目錄
- 概述
- 1. 項目結構
- 2. 數據庫結構
- 3. 主要流程
- 4. Restful API
- 5. 一些核心接口
- App、Enclave相關的類圖:
- com.quorum.tessera.server.TesseraServer
- com.quorum.tessera.key.generation.KeyGenerator
- com.quorum.tessera.encryption.Encryptor
- com.quorum.tessera.enclave.Enclave
- com.quorum.tessera.context.RuntimeContext
- com.quorum.tessera.partyinfo.PartyInfoService
- com.quorum.tessera.transaction.TransactionManager
- com.quorum.tessera.config.AppType
- 6. 主要配置文件
概述
tessera是quorum的一種隱私管理器實現,使用Java語言編寫,用於對quorum隱私交易的加密、解密和分發。
原理參考:Quorum工作原理
1. 項目結構
tessera
├── argon2
hash函數庫,類似的函數還有pbkdf2、bcrypt、 scrypt
├── cli
使用picocli實現的命令行tessera(包含子命令keygen|keyupdate|admin)
├── config
配置數據模型,給各個模塊使用的配置
├── config-migration
提供命令行可以將Constellation TOML轉換成Tessera JSON
├── data-migration
創建表結構
├── ddls
ddl語句,包含兩張表(支持mysq、oracle、postgresql、h2、hsql、sqllite)
├── enclave
提供加密、解密接口/Restful api(調用encryption模塊)
├── encryption
生成主密鑰、共享密鑰、加密解密payload、生成隨機數等.實現有ec、jnacl、kalium三種
├── key-generation
公鑰私鑰生成,包含aws、azure、hashcorp三種實現
├── key-vault
密鑰保險箱 ,有aws、azure、hashcorp三種實現,可將密鑰保存在這些在線服務上
├── security
ssl通信相關工具
├── server
包含兩個TesseraServer的實現:1、使用Jersey和Jetty 實現的RestServer;2 WebSocketServer
├── service-locator
獲取服務實例,默認使用spring 配置文件 tessera-spring.xml中的定義(不使用註解?)
├── shared
大雜燴,包含一些工具類:控制檯密碼讀取、ReflectCallback、JaxbCallback等CallBack
├── tessera-context
上下文,見下面的RuntimeContext
├── tessera-core
主要包含TransactionManager
├── tessera-data
主要包含EncryptedTransactionDAO、EncryptedRawTransactionDAO的實現
├── tessera-dist
系統launcher入口,包含tessera-spring.xml配置文件
├── tessera-jaxrs
系統RESTful API(OpenAPI)定義
├── tessera-partyinfo
參與者之間的服務發現、p2p連接、推送EncodedPayload到其他節點
├── tessera-sync
peer節點之間Transaction的同步
├── test-utils
測試mock工具
└── tests
測試用例
參考下面的接口和類
2. 數據庫結構
存儲hash
和加密playload
對應關係
CREATE TABLE ENCRYPTED_TRANSACTION (
ENCODED_PAYLOAD BLOB NOT NULL,
HASH VARBINARY(100) NOT NULL,
TIMESTAMP BIGINT, PRIMARY KEY (HASH)
);
CREATE TABLE ENCRYPTED_RAW_TRANSACTION (
ENCRYPTED_KEY BLOB NOT NULL,
ENCRYPTED_PAYLOAD BLOB NOT NULL,
NONCE BLOB NOT NULL,
SENDER BLOB NOT NULL,
TIMESTAMP BIGINT,
HASH VARBINARY(100) NOT NULL, PRIMARY KEY (HASH)
);
3. 主要流程
3.1 服務啓動
a. 首先通過cli從配置文件tessera-config.json讀取配置,根據配置創建運行時上下文(上下文持有當前節點公私鑰對,peers列表等引用)
b. 再將當前partyInfo保存到集合中(內存)
c. 根據的serverConfigs循環創建ThirdPartyRestApp、P2PRestApp、Q2TRestApp(未包含EnclaveApplication)Restful服務
d 啓動服務監聽
順序圖:
主要代碼:
main方法
PicoCliDelegate picoCliDelegate = new PicoCliDelegate();
LOGGER.debug("Execute PicoCliDelegate with args [{}]",String.join(",",args));
final CliResult cliResult = picoCliDelegate.execute(args);
LOGGER.debug("Executed PicoCliDelegate with args [{}].",String.join(",",args));
CliDelegate.instance().setConfig(cliResult.getConfig().orElse(null));
if (cliResult.isSuppressStartup()) {
System.exit(0);
}
if (cliResult.getStatus() != 0) {
System.exit(cliResult.getStatus());
}
final Config config =
cliResult
.getConfig()
.orElseThrow(() -> new NoSuchElementException("No config found. Tessera will not run."));
RuntimeContext runtimeContext = RuntimeContextFactory.newFactory().create(config);
LOGGER.debug("Creating service locator");
ServiceLocator serviceLocator = ServiceLocator.create();
LOGGER.debug("Created service locator {}",serviceLocator);
Set<Object> services = serviceLocator.getServices();
LOGGER.debug("Created {} services",services.size());
services.forEach(o -> LOGGER.debug("Service : {}",o));
services.stream()
.filter(PartyInfoService.class::isInstance)
.map(PartyInfoService.class::cast)
.findAny()
.ifPresent(p -> p.populateStore());
runWebServer(config);
runWebServer
final List<TesseraServer> servers =
config.getServerConfigs().stream()
.filter(server -> !AppType.ENCLAVE.equals(server.getApp()))
.map(
conf -> {
Object app =
TesseraAppFactory.create(conf.getCommunicationType(), conf.getApp())
.orElseThrow(
() ->
new IllegalStateException(
"Cant create app for " + conf.getApp()));
return TesseraServerFactory.create(conf.getCommunicationType())
.createServer(conf, Collections.singleton(app));
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
for (TesseraServer ts : servers) {
ts.start();
}
3.2 交易處理
a.收到交易請求後,將請求交給TransactionManager處理,TransactionManager調用Enclave加密tx(詳見下一個流程【加密交易】),根據加密的payload,調用MessageHashFactory生成tx Hash,
b. 調用DAO將數據保存到數據庫
c. 循環接收者列表,將加密了的playload推送給其他Tessera節點處理
d.將tx hash使用base64編碼後返回給quorum幾點
主要代碼:
public SendResponse send(SendRequest sendRequest) {
final String sender = sendRequest.getFrom();
final PublicKey senderPublicKey =
Optional.ofNullable(sender)
.map(base64Decoder::decode)
.map(PublicKey::from)
.orElseGet(enclave::defaultPublicKey);
final byte[][] recipients =
Stream.of(sendRequest)
.filter(sr -> Objects.nonNull(sr.getTo()))
.flatMap(s -> Stream.of(s.getTo()))
.map(base64Decoder::decode)
.toArray(byte[][]::new);
final List<PublicKey> recipientList = Stream.of(recipients).map(PublicKey::from).collect(Collectors.toList());
recipientList.add(senderPublicKey);
recipientList.addAll(enclave.getForwardingKeys());
final List<PublicKey> recipientListNoDuplicate =
recipientList.stream().distinct().collect(Collectors.toList());
final byte[] raw = sendRequest.getPayload();
final EncodedPayload payload = enclave.encryptPayload(raw, senderPublicKey, recipientListNoDuplicate);
final MessageHash transactionHash =
Optional.of(payload)
.map(EncodedPayload::getCipherText)
.map(messageHashFactory::createFromCipherText)
.get();
final EncryptedTransaction newTransaction =
new EncryptedTransaction(transactionHash, this.payloadEncoder.encode(payload));
this.encryptedTransactionDAO.save(newTransaction);
recipientListNoDuplicate.forEach(
recipient -> {
final EncodedPayload outgoing = payloadEncoder.forRecipient(payload, recipient);
partyInfoService.publishPayload(outgoing, recipient);
});
final byte[] key = transactionHash.getHashBytes();
final String encodedKey = base64Decoder.encodeToString(key);
return new SendResponse(encodedKey);
}
3.3 加密交易
a. 生成隨機主密鑰(RMK:NonceMasterKey)和隨機數Nonce、接收者隨機數Nonce
b.使用步驟a的隨機數Nonce和RMK加密message(Transaction Payload)。
c. 根據發送者的公鑰從keymanager中獲取發送者私鑰
d.遍歷接收者列表:根據發送者的私鑰和接收者的公鑰生成共享祕鑰,根據共享密鑰和接收者隨機數加密RMK,最後返回RMK列表
e.返回加密的playload、隨機數、RMKs給Transaction Manager
注:圖中使用的Encryptor實現是EllipticalCurveEncryptor
主要代碼:
public EncodedPayload encryptPayload(
final RawTransaction rawTransaction, final List<PublicKey> recipientPublicKeys) {
final MasterKey masterKey =
this.getMasterKey(
rawTransaction.getFrom(), rawTransaction.getFrom(),
rawTransaction.getNonce(), rawTransaction.getEncryptedKey());
final Nonce recipientNonce = encryptor.randomNonce();
final List<byte[]> encryptedMasterKeys =
buildRecipientMasterKeys(rawTransaction.getFrom(), recipientPublicKeys, recipientNonce, masterKey);
return EncodedPayload.Builder.create()
.withSenderKey(rawTransaction.getFrom())
.withCipherText(rawTransaction.getEncryptedPayload())
.withCipherTextNonce(rawTransaction.getNonce())
.withRecipientBoxes(encryptedMasterKeys)
.withRecipientNonce(recipientNonce)
.withRecipientKeys(recipientPublicKeys)
.build();
}
private List<byte[]> buildRecipientMasterKeys(
final PublicKey senderPublicKey,
final List<PublicKey> recipientPublicKeys,
final Nonce recipientNonce,
final MasterKey masterKey) {
final PrivateKey privateKey = keyManager.getPrivateKeyForPublicKey(senderPublicKey);
return recipientPublicKeys.stream()
.map(publicKey -> encryptor.computeSharedKey(publicKey, privateKey))
.map(sharedKey -> encryptor.sealAfterPrecomputation(masterKey.getKeyBytes(), recipientNonce, sharedKey))
.collect(Collectors.toList());
}
4. Restful API
4.1 Q2TRestApp
quorum節點和tessera之間的數據交換
api | method | 功能 |
---|---|---|
send | post | Send private transaction payload |
sendRaw | post | Send private transaction payload |
sendsignedtx | post | Send private raw transaction payload |
receive | get | Submit keys to retrieve payload and decrypt it |
receiveRaw | get | Submit keys to retrieve payload and decrypt it |
transaction/{hash} | get | Returns decrypted payload back to Quorum |
transaction/{key} | delete | Delete single transaction from P2PRestApp node |
upcheck | get | Node is up? |
version | get | Node’s version |
storeraw | post | Store raw private transaction payload |
4.2 ThirdPartyRestApp
api | method | 功能 |
---|---|---|
key | get | Fetch local public keys managed by the enclave |
partyinfo/key | get | Fetch network/peer public keys |
storeraw | post | Store raw private transaction payload |
4.3 P2PRestApp
tessera節點之間的數據交換
api | method | 功能 |
---|---|---|
resend | post | Resend transactions for given key or message hash/recipient |
push | post | Transmit encrypted payload between P2PRestApp Nodes |
partyinfo | post | Request public key/url of other nodes |
partyinfo | get | Fetch network/peer information |
partyinfo/validate | post | validate network/peer |
4.4 EnclaveApplication
提供main方法,可以獨立啓動成web服務提供Restful API,也可以走內部調用(默認)
api | method | 功能 |
---|---|---|
ping | get | 獲取Encalve服務狀態 |
default | get | 獲取默認的公鑰(第一個) |
forwarding | get | 獲取要轉發的公鑰列表 |
public | get | 獲取公鑰 |
encrypt | post | 加密playload |
encrypt/raw | post | 加密rawplayload |
encrypt/toraw | post | playload轉換成rawplayload |
unencrypt | post | 解密Payload |
addRecipient | post | 添加收件人 |
5. 一些核心接口
App、Enclave相關的類圖:
某些接口手工加了成員變量
com.quorum.tessera.server.TesseraServer
public interface TesseraServer {
void start() throws Exception;
void stop() throws Exception;
}
com.quorum.tessera.key.generation.KeyGenerator
public interface KeyGenerator {
ConfigKeyPair generate(String filename, ArgonOptions encryptionOptions, KeyVaultOptions keyVaultOptions);
}
com.quorum.tessera.encryption.Encryptor
/**
* The API provided to the application that all implementation of this API
* module should extend
* <p>
* Provides all function relating to encrypting and decrypting messages
* using public/private and symmetric keys.
*/
public interface Encryptor {
/**
* Compute the shared key from a public/private key combination
* The keys must be from different keysets.
* Providing the public key for the corresponding private key (and vice versa) results in an error
* <p>
* The shared key for a public/private key combo is the same as if the private/public corresponding keys
* were provided.
* i.e. public1/private2 == private1/public2
*
* @param publicKey A public key from the first keyset
* @param privateKey A private key from the second keyset
* @return The shared key for this key pair.
*/
SharedKey computeSharedKey(PublicKey publicKey, PrivateKey privateKey);
/**
* Encrypt a payload directly using the given public/private key pair for the sender/recipient
*
* @param message The payload to be encrypted
* @param nonce A unique nonce for this public/private pair
* @param publicKey The key from either sender or recipient
* @param privateKey The other key from either sender or recipient
* @return The encrypted payload
*/
byte[] seal(byte[] message, Nonce nonce, PublicKey publicKey, PrivateKey privateKey);
/**
* Decrypt a payload directly using the given public/private key pair for the sender/recipient
*
* @param cipherText The payload to be encrypted
* @param nonce A unique nonce for this public/private pair
* @param publicKey The key from either sender or recipient
* @param privateKey The other key from either sender or recipient
* @return The encrypted payload
*/
byte[] open(byte[] cipherText, Nonce nonce, PublicKey publicKey, PrivateKey privateKey);
/**
* Encrypt a payload using the given public/private key pair for the sender/recipient
*
* @param message The payload to be encrypted
* @param nonce A unique nonce for this public/private pair
* @param sharedKey The shared key between the sender and recipient of the payload
* @return The encrypted payload
*/
byte[] sealAfterPrecomputation(byte[] message, Nonce nonce, SharedKey sharedKey);
default byte[] sealAfterPrecomputation(byte[] message, Nonce nonce, MasterKey masterKey) {
SharedKey sharedKey = SharedKey.from(masterKey.getKeyBytes());
return sealAfterPrecomputation(message, nonce, sharedKey);
}
/**
* Decrypts a payload using the shared key between the sender and recipient
*
* @param cipherText The encrypted payload
* @param nonce The nonce that was used to encrypt this payload
* @param sharedKey The shared key for the sender and recipient
* @return The decrypted payload
*/
byte[] openAfterPrecomputation(byte[] cipherText, Nonce nonce, SharedKey sharedKey);
/**
* Generates a new random nonce of the correct size
*
* @return a {@link Nonce} containing random data to be used as a nonce
*/
Nonce randomNonce();
/**
* Generates a new public and private keypair
*
* @return A pair of public and private keys
*/
KeyPair generateNewKeys();
/**
* Creates a single standalone key
*
* @return The randomly generated key
*/
SharedKey createSingleKey();
/**
* Create a randomly generated {@link MasterKey}
*
* @return a random {@link MasterKey}
*/
default MasterKey createMasterKey() {
SharedKey sharedKey = createSingleKey();
return MasterKey.from(sharedKey.getKeyBytes());
}
/**
* Decrypts a payload using the given {@link MasterKey}
*
* @param cipherText the ciphertext to decrypt
* @param cipherTextNonce the nonce that was used to encrypt the payload
* @param masterKey the key used to encrypt the payload
* @return the decrypted payload
* @see Encryptor#openAfterPrecomputation(byte[], Nonce, SharedKey)
*/
default byte[] openAfterPrecomputation(byte[] cipherText, Nonce cipherTextNonce, MasterKey masterKey) {
SharedKey sharedKey = SharedKey.from(masterKey.getKeyBytes());
return openAfterPrecomputation(cipherText, cipherTextNonce, sharedKey);
}
}
com.quorum.tessera.enclave.Enclave
/**
* An {@link Enclave} provides encryption/decryption functions and keeps hold
* of all the nodes private keys so the do not leak into other services.
*/
public interface Enclave extends Service {
private final Encryptor encryptor;
private final KeyManager keyManager;
/**
* Retrieves the public key to use if no key is specified for an operation
* There is no guarantee this key remains the same between runs of the Enclave.
*
* @return the public key that has been assigned as the default
*/
PublicKey defaultPublicKey();
/**
* Returns a set of public keys that should be included as recipients to
* all transactions produced by this node. The keys are not be managed by
* this node.
*
* @return the set of public keys to be added to transactions
*/
Set<PublicKey> getForwardingKeys();
/**
* Returns all the public keys that are managed by this Enclave.
*
* @return all public keys managed by this Enclave
*/
Set<PublicKey> getPublicKeys();
/**
* Encrypts a message using the specified sender and a list of recipients.
* Returns a {@link EncodedPayload} which contains all the encrypted
* information, including the recipients and their encrypted master keys.
*
* @param message the message to be encrypted
* @param senderPublicKey the public key which this enclave manages
* @param recipientPublicKeys the recipients to encrypt this message for
* @return the encrypted information, represented by an {@link EncodedPayload}
*/
EncodedPayload encryptPayload(byte[] message, PublicKey senderPublicKey, List<PublicKey> recipientPublicKeys);
/**
* Decrypts a {@link RawTransaction} so that it can be re-encrypted into a
* {@link EncodedPayload} with the given recipient list
*
* @param rawTransaction the transactiopn to decrypt and re-encrypt with recipients
* @param recipientPublicKeys the recipients to encrypt the transaction for
* @return the encrypted information, represented by an {@link EncodedPayload}
*/
EncodedPayload encryptPayload(RawTransaction rawTransaction, List<PublicKey> recipientPublicKeys);
/**
* Encrypt a payload without any recipients that can be retrieved later.
* The payload is encrypted using the private key that is related to the
* given public key.
*
* @param message the message to be encrypted
* @param sender the sender's public key to encrypt the transaction with
* @return the encrypted transaction
*/
RawTransaction encryptRawPayload(byte[] message, PublicKey sender);
/**
* Decrypt a transaction and fetch the original message using the given
* public key. Throws an {@link com.quorum.tessera.nacl.NaclException} if
* the provided public key OR one of the Enclave's managed keys cannot be
* used to decrypt the payload
*
* @param payload the encrypted payload
* @param providedKey the key to use for decryption, if the payload wasn't sent by this Enclave
* @return the original, decrypted message
*/
byte[] unencryptTransaction(EncodedPayload payload, PublicKey providedKey);
/**
* Creates a new recipient box for the payload, for which we must be the originator.
* At least one recipient must already be available to be able to decrypt the master key.
*
* @param payload the payload to add a recipient to
* @param recipientKey the new recipient key to add
*/
byte[] createNewRecipientBox(EncodedPayload payload, PublicKey recipientKey);
@Override
default void start() {
}
@Override
default void stop() {
}
}
com.quorum.tessera.context.RuntimeContext
public interface RuntimeContext {
List<KeyPair> getKeys();
KeyEncryptor getKeyEncryptor();
List<PublicKey> getAlwaysSendTo();
List<URI> getPeers();
Client getP2pClient();
boolean isRemoteKeyValidation();
URI getP2pServerUri();
static RuntimeContext getInstance() {
return ContextHolder.INSTANCE.getContext().get();
}
boolean isDisablePeerDiscovery();
default Set<PublicKey> getPublicKeys() {
return getKeys().stream().map(KeyPair::getPublicKey).collect(Collectors.toSet());
}
boolean isUseWhiteList();
}
com.quorum.tessera.partyinfo.PartyInfoService
public interface PartyInfoService {
/**
* Request PartyInfo data from all remote nodes that this node is aware of
*
* @return PartyInfo object
*/
PartyInfo getPartyInfo();
/**
* Update the PartyInfo data store with the provided encoded data.This can happen when endpoint /partyinfo is
* triggered, or by a response from this node hitting another node /partyinfo endpoint
*
* @param partyInfo
* @return updated PartyInfo object
*/
PartyInfo updatePartyInfo(PartyInfo partyInfo);
// Set<String> getUrlsForKey(PublicKey key);
PartyInfo removeRecipient(String uri);
/**
* Formats, encodes and publishes encrypted messages using the target public key as the identifier, instead of the
* URL
*
* @param payload the pre-formatted payload object (i.e. with all recipients still present)
* @param recipientKey the target public key to publish the payload to
* @throws com.quorum.tessera.encryption.KeyNotFoundException if the target public key is not known
*/
void publishPayload(EncodedPayload payload, PublicKey recipientKey);
// TODO: Added as lifecycle call once RuntimeContext has been created.
void populateStore();
}
com.quorum.tessera.transaction.TransactionManager
public interface TransactionManager {
private final PayloadEncoder payloadEncoder;
private final Base64Decoder base64Decoder;
private final EncryptedTransactionDAO encryptedTransactionDAO;
private final EncryptedRawTransactionDAO encryptedRawTransactionDAO;
private final PartyInfoService partyInfoService;
private final Enclave enclave;
private final ResendManager resendManager;
private final MessageHashFactory messageHashFactory = MessageHashFactory.create();
private int resendFetchSize;
SendResponse send(SendRequest sendRequest);
SendResponse sendSignedTransaction(SendSignedRequest sendRequest);
void delete(DeleteRequest request);
ResendResponse resend(ResendRequest request);
MessageHash storePayload(byte[] toByteArray);
ReceiveResponse receive(ReceiveRequest request);
StoreRawResponse store(StoreRawRequest storeRequest);
}
com.quorum.tessera.config.AppType
public enum AppType {
P2P(CommunicationType.REST),
Q2T(CommunicationType.REST),
THIRD_PARTY(CommunicationType.REST),
ENCLAVE(CommunicationType.REST),
ADMIN(CommunicationType.REST);
}
6. 主要配置文件
tessera-config.json 示例
{
"useWhiteList": false,
"jdbc": {
"username": "sa",
"password": "",
"url": "jdbc:h2:test/db;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0",
"autoCreateTables": true
},
"serverConfigs":[
{
"app":"ThirdParty",
"enabled": true,
"serverAddress": "http://$$(hostname -i):9080",
"communicationType" : "REST"
},
{
"app":"Q2T",
"enabled": true,
"serverAddress": "unix:$${DDIR}/tm.ipc",
"communicationType" : "REST"
},
{
"app":"P2P",
"enabled": true,
"serverAddress": "http://$$(hostname -i):9000",
"sslConfig": {
"tls": "OFF"
},
"communicationType" : "REST"
}
],
"peer": [
{
"url": "http://txmanager1:9000"
},
{
"url": "http://txmanager2:9000"
},
{
"url": "http://txmanager3:9000"
},
{
"url": "http://txmanager4:9000"
},
{
"url": "http://txmanager5:9000"
},
{
"url": "http://txmanager6:9000"
},
{
"url": "http://txmanager7:9000"
}
],
"keys": {
"passwords": [],
"keyData": [
{
"config": "tm.key",
"publicKey": "tm.pub"
}
]
},
"alwaysSendTo": []
}
tessera-dist/tessera-app/src/main/resources/tessera-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:/tessera-core-spring.xml" />
<import resource="classpath:/tessera-partyinfo-spring.xml" />
</beans>
tessera-core/src/main/resources/tessera-core-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<tx:annotation-driven transaction-manager="jpaTransactionManager"/>
<context:component-scan base-package="com.quorum.tessera"/>
<bean id="enclaveFactory" class="com.quorum.tessera.enclave.EnclaveFactory" factory-method="create" />
<bean id="enclave" factory-bean="enclaveFactory" factory-method="create">
<constructor-arg ref="config" />
</bean>
<bean class="com.quorum.tessera.service.ServiceContainer">
<constructor-arg ref="enclave" />
</bean>
<bean id="transactionManager" class="com.quorum.tessera.transaction.TransactionManagerImpl">
<constructor-arg ref="encryptedTransactionDAO" />
<constructor-arg ref="enclave" />
<constructor-arg ref="encryptedRawTransactionDAO" />
<constructor-arg ref="resendManager" />
<constructor-arg ref="partyInfoService" />
<constructor-arg value="#{config.getJdbcConfig().getFetchSize() > 0 ? config.getJdbcConfig().getFetchSize() : 1000}"/>
</bean>
<bean id="cliDelegate" class="com.quorum.tessera.cli.CliDelegate" factory-method="instance"/>
<bean id="config" factory-bean="cliDelegate" factory-method="getConfig"/>
<bean name="encryptedTransactionDAO" class="com.quorum.tessera.data.EncryptedTransactionDAOImpl"/>
<bean name="encryptedRawTransactionDAO" class="com.quorum.tessera.data.EncryptedRawTransactionDAOImpl"/>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="jdbcUrl" value="#{ config.getJdbcConfig().getUrl() }" />
<property name="username" value="#{ config.getJdbcConfig().getUsername() }" />
<property name="password" value="#{ resolver.resolve(config.getJdbcConfig().getPassword()) }" />
</bean>
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="tessera"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter" />
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect"/>
</property>
<property name="jpaPropertyMap">
<props>
<prop key="eclipselink.weaving">false</prop>
<prop key="eclipselink.session-name">tessera</prop>
<prop key="eclipselink.logging.logger">org.eclipse.persistence.logging.slf4j.SLF4JLogger</prop>
<prop key="eclipselink.logging.session">false</prop>
<prop key="javax.persistence.schema-generation.database.action">#{config.getJdbcConfig().isAutoCreateTables() ? 'create' : 'none'}</prop>
</props>
</property>
</bean>
<bean id="resolver" class="com.quorum.tessera.config.util.EncryptedStringResolver"/>
</beans>
tessera-partyinfo/src/main/resources/tessera-partyinfo-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="p2pClientFactory" class="com.quorum.tessera.partyinfo.P2pClientFactory" factory-method="newFactory">
<constructor-arg ref="config" />
</bean>
<bean id="p2pClient" factory-bean="p2pClientFactory" factory-method="create">
<constructor-arg ref="config"/>
</bean>
<bean id="resendClientFactory" class="com.quorum.tessera.sync.ResendClientFactory" factory-method="newFactory">
<constructor-arg ref="config"/>
</bean>
<bean id="resendClient" factory-bean="resendClientFactory" factory-method="create">
<constructor-arg ref="config"/>
</bean>
<bean id="payloadPublisherFactory" class="com.quorum.tessera.partyinfo.PayloadPublisherFactory" factory-method="newFactory">
<constructor-arg ref="config" />
</bean>
<bean id="payloadPublisher" class="com.quorum.tessera.partyinfo.PayloadPublisher"
factory-bean="payloadPublisherFactory" factory-method="create">
<constructor-arg ref="config" />
</bean>
<!-- Party Info management -->
<bean name="partyInfoStore" class="com.quorum.tessera.partyinfo.PartyInfoStore">
<constructor-arg value="#{config.getP2PServerConfig().getServerUri()}"/>
</bean>
<bean name="partyInfoService" class="com.quorum.tessera.partyinfo.PartyInfoServiceImpl">
<constructor-arg ref="partyInfoStore"/>
<constructor-arg ref="enclave"/>
<constructor-arg ref="payloadPublisher"/>
</bean>
<bean name="partyInfoPoller" class="com.quorum.tessera.partyinfo.PartyInfoPoller">
<constructor-arg ref="partyInfoService"/>
<constructor-arg ref="p2pClient"/>
</bean>
<bean name="propertyHelper" class="com.quorum.tessera.config.util.IntervalPropertyHelper">
<constructor-arg value="#{config.getP2PServerConfig().getProperties()}"/>
</bean>
<bean name="partyInfoPollExecutor" class="com.quorum.tessera.threading.TesseraScheduledExecutor">
<constructor-arg>
<bean class="java.util.concurrent.Executors" factory-method="newSingleThreadScheduledExecutor"/>
</constructor-arg>
<constructor-arg ref="partyInfoPoller"/>
<constructor-arg value="#{propertyHelper.partyInfoInterval()}"/>
<constructor-arg value="5000"/>
</bean>
<bean id="resendManager" class="com.quorum.tessera.partyinfo.ResendManagerImpl">
<constructor-arg ref="encryptedTransactionDAO" />
<constructor-arg ref="enclave" />
</bean>
<!-- Local key sync -->
<bean name="enclaveKeySynchroniser" class="com.quorum.tessera.partyinfo.EnclaveKeySynchroniser">
<constructor-arg ref="enclave" />
<constructor-arg ref="partyInfoStore" />
<constructor-arg value="#{config.getP2PServerConfig().getServerUri()}" />
</bean>
<bean name="enclaveKeySynchroniserExecutor" class="com.quorum.tessera.threading.TesseraScheduledExecutor">
<constructor-arg>
<bean class="java.util.concurrent.Executors" factory-method="newSingleThreadScheduledExecutor"/>
</constructor-arg>
<constructor-arg ref="enclaveKeySynchroniser"/>
<constructor-arg value="#{propertyHelper.enclaveKeySyncInterval()}"/>
<constructor-arg value="5000"/>
</bean>
<!-- Node synchronization management-->
<beans profile="enable-sync-poller">
<bean name="resendPartyStore" class="com.quorum.tessera.sync.ResendPartyStoreImpl"/>
<bean name="transactionRequester" class="com.quorum.tessera.sync.TransactionRequesterImpl">
<constructor-arg ref="enclave" />
<constructor-arg ref="resendClient" />
</bean>
<bean name="syncPoller" class="com.quorum.tessera.sync.SyncPoller">
<constructor-arg ref="resendPartyStore" />
<constructor-arg ref="transactionRequester" />
<constructor-arg ref="partyInfoService"/>
<constructor-arg ref="p2pClient"/>
</bean>
<bean class="com.quorum.tessera.threading.TesseraScheduledExecutor">
<constructor-arg>
<bean class="java.util.concurrent.Executors" factory-method="newSingleThreadScheduledExecutor"/>
</constructor-arg>
<constructor-arg ref="syncPoller"/>
<constructor-arg value="#{propertyHelper.syncInterval()}"/>
<constructor-arg value="5000"/>
</bean>
</beans>
</beans>