目录
- 概述
- 1. 项目结构
- 2. 数据库结构
- 3. 主要流程
- 3.1 服务启动
- 3.2 交易处理
- 3.3 加密交易
- 4. Restful API
- 4.1 Q2TRestApp
- 4.2 ThirdPartyRestApp
- 4.3 P2PRestApp
- 4.4 EnclaveApplication
- 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-config.json 示例
- tessera-dist/tessera-app/src/main/resources/tessera-spring.xml
- tessera-core/src/main/resources/tessera-core-spring.xml
- tessera-partyinfo/src/main/resources/tessera-partyinfo-spring.xml
概述
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);@Overridedefault void start() {}@Overridedefault 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.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsdhttp://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>