【quorum源碼】quorum tessera源碼剖析

概述

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>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章