Class Blockchain


  • public final class Blockchain
    extends Object
    Provides read-only access to the subset of blockchain::Schema features in the Core API: blocks, transaction messages, execution results.

    Proofs

    Blockchain allows creating cryptographic proofs that some data is indeed stored in the database. Exonum supports the following types of proofs:

    • Block Proof
    • Transaction Execution Proof
    • Call Result Proof
    • Service Data Proof

    Block Proof

    A block proof proves correctness of a blockchain block. It can be created with createBlockProof(long) for any committed block. See also BlockProof.

    Transaction Execution Proof

    A transaction execution proof proves that a transaction with a given message hash was executed in a block at a certain height at a certain location. It consists of a block proof, and a list proof from getBlockTransactions(long). It may be extended to a call result proof — read the next section.

    Call Result Proof

    A call result proof proves that a given service call completed with a particular result in a block at a certain height. It consists of a block proof and a map proof from the call errors registry, which index hash is recorded in the block header as Block.getErrorHash().

    To construct such a proof, access the call records for a particular block with getCallRecords(long); and use CallRecords.getProof(CallInBlock).

    Service Data Proof

    A service data proof proves that some service index contains certain data as of the last committed block. It includes:

    • An index proof: a block proof + a proof from the aggregating collection.
    • A proof from the service index.

    An index proof is created with createIndexProof(String).

    Example

    Consider a simple timestamping service that keeps timestamps for event ids, and supports proofs of their authenticity.

    First, create a message definition for a proof:

       message TimestampProof {
         MapProof timestamp = 1;
         IndexProof indexProof = 2;
       }
     

    Then create the two components: timestamp proof from a service index and index proof for that index from the blockchain:

       TimestampProof createTimestampProof(Snapshot s,
                                           String eventId) {
         // 1. Create a timestamp proof
         // The literal is for illustrative purposes —
         // usually the service name is prepended elsewhere
         var fullIndexName = "timestamping.timestamp";
         var timestamps = ProofMapIndexProxy.newInstance(fullIndexName, s,
             string(), timestamp());
         var tsProof = timestamps.getProof(eventId);
    
         // 2. Create an index proof
         var blockchain = Blockchain.newInstance(s);
         var indexProof = blockchain.createIndexProof(fullIndexName);
    
         // 3. Create a complete service data proof
         return TimestampProof.newBuilder()
           .setTimestamp(tsProof.getAsMessage())
           .setIndexProof(indexProof.getAsMessage())
           .build();
       }
     

    Finally, serialize the proof and send it to the client.


    All method arguments are non-null by default.

    • Method Summary

      All Methods Static Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      boolean containsBlock​(Block block)
      Returns true if the blockchain contains exactly the same block as the passed value; false if it does not contain such block.
      BlockProof createBlockProof​(long blockHeight)
      Creates a proof for the block at the given height.
      IndexProof createIndexProof​(String fullIndexName)
      Creates a proof for a single index in the database.
      Optional<Block> findBlock​(com.exonum.binding.common.hash.HashCode blockHash)
      Returns a block object for given block hash.
      Block getBlock​(long height)
      Returns the block at the given height.
      ListIndex<com.exonum.binding.common.hash.HashCode> getBlockHashes()
      Returns a list of all block hashes, indexed by the block height.
      MapIndex<com.exonum.binding.common.hash.HashCode,​Block> getBlocks()
      Returns a map that stores a block object for every block hash.
      ProofListIndexProxy<com.exonum.binding.common.hash.HashCode> getBlockTransactions​(long height)
      Returns a proof list of transaction hashes committed in the block at the given height.
      ProofListIndexProxy<com.exonum.binding.common.hash.HashCode> getBlockTransactions​(com.exonum.binding.common.hash.HashCode blockId)
      Returns a proof list of transaction hashes committed in the block with the given id.
      ProofListIndexProxy<com.exonum.binding.common.hash.HashCode> getBlockTransactions​(Block block)
      Returns a proof list of transaction hashes committed in the given block.
      CallRecords getCallRecords​(long blockHeight)
      Returns the call records, containing the execution errors that occurred in the block at the given height.
      com.exonum.messages.core.Blockchain.Config getConsensusConfiguration()
      Returns the current consensus configuration of the network.
      long getHeight()
      Returns the blockchain height which is the height of the latest committed block in the blockchain.
      Block getLastBlock()
      Returns the latest committed block.
      long getNextHeight()
      Returns the blockchain height of the next block to be committed.
      long getNumTransactions()
      Returns the total number of transactions committed to the blockchain.
      KeySetIndexProxy<com.exonum.binding.common.hash.HashCode> getTransactionPool()
      Returns a set of uncommitted (in-pool) transaction hashes; empty in case of no transactions.
      Optional<com.exonum.binding.common.blockchain.TransactionLocation> getTxLocation​(com.exonum.binding.common.hash.HashCode messageHash)
      Returns transaction position inside the blockchain for given message hash.
      MapIndex<com.exonum.binding.common.hash.HashCode,​com.exonum.binding.common.blockchain.TransactionLocation> getTxLocations()
      Returns a map that keeps the transaction position inside the blockchain for every transaction hash.
      MapIndex<com.exonum.binding.common.hash.HashCode,​com.exonum.binding.common.message.TransactionMessage> getTxMessages()
      Returns a map of transaction messages identified by their SHA-256 hashes.
      Optional<com.exonum.messages.core.runtime.Errors.ExecutionStatus> getTxResult​(com.exonum.binding.common.hash.HashCode messageHash)
      Returns a transaction execution result for the given message hash.
      static Blockchain newInstance​(Access access)
      Constructs a new blockchain instance for the given database access.
    • Method Detail

      • createBlockProof

        public BlockProof createBlockProof​(long blockHeight)
        Creates a proof for the block at the given height.

        It allows creating genesis block proofs, but they make little sense, as a genesis block is supposed to be a "root of trust", hence, well-known to the clients verifying any subsequent proofs coming from the blockchain.

        If you need to create a proof for a service index, use createIndexProof(String).

        Parameters:
        blockHeight - a height of the block for which to create a proof
        Throws:
        IndexOutOfBoundsException - if the height is not valid
        See Also:
        createIndexProof(String)
      • createIndexProof

        public IndexProof createIndexProof​(String fullIndexName)
        Creates a proof for a single index in the database. It is usually a part of a Service Data Proof.
        Parameters:
        fullIndexName - the full index name for which to create a proof
        Throws:
        IllegalStateException - if the access is not a snapshot, because a state of a service index can be proved only for the latest committed block, not for any intermediate state during transaction processing
        IllegalArgumentException - if the index with the given name does not exist; or is not Merkelized. An index does not exist until it is initialized — created for the first time with a Fork. Depending on the service logic, an index may remain uninitialized indefinitely. Therefore, if proofs for an empty index need to be created, it must be initialized early in the service lifecycle (e.g., in Service.initialize(ExecutionContext, Configuration).
      • containsBlock

        public boolean containsBlock​(Block block)
        Returns true if the blockchain contains exactly the same block as the passed value; false if it does not contain such block. Please note that all block fields are compared, not only its hash.
        Parameters:
        block - a value to check for presence in the blockchain
      • getHeight

        public long getHeight()
        Returns the blockchain height which is the height of the latest committed block in the blockchain. The block height is a distance between the last block and the "genesis", or initial, block. Therefore, the blockchain height is equal to the number of blocks plus one.

        For example, the "genesis" block has height h = 0. The latest committed block has height h = getBlockHashes().size() - 1.

        Throws:
        RuntimeException - if the "genesis block" was not created yet; consider using getNextHeight() in service methods that might be invoked before the genesis block commit
      • getNextHeight

        public long getNextHeight()
        Returns the blockchain height of the next block to be committed.
        See Also:
        getHeight()
      • getBlockHashes

        public ListIndex<com.exonum.binding.common.hash.HashCode> getBlockHashes()
        Returns a list of all block hashes, indexed by the block height. For example, the "genesis block" will be at index 0, the block at height h = 10 — at index 10. The last committed block will be at height h = getBlockHashes().size() - 1.
      • getBlockTransactions

        public ProofListIndexProxy<com.exonum.binding.common.hash.HashCode> getBlockTransactions​(long height)
        Returns a proof list of transaction hashes committed in the block at the given height.

        The index hash of this index is recorded in the block header as Block.getTxRootHash(). That allows constructing proofs that a transaction with a certain message hash was executed at a certain location: (block_height, tx_index_in_block) pair.

        Parameters:
        height - block height starting from 0
        Throws:
        IllegalArgumentException - if the height is invalid: negative or exceeding the blockchain height
      • getBlockTransactions

        public ProofListIndexProxy<com.exonum.binding.common.hash.HashCode> getBlockTransactions​(com.exonum.binding.common.hash.HashCode blockId)
        Returns a proof list of transaction hashes committed in the block with the given id.
        Parameters:
        blockId - id of the block
        Throws:
        IllegalArgumentException - if there is no block with given id
        See Also:
        getBlockTransactions(long)
      • getBlockTransactions

        public ProofListIndexProxy<com.exonum.binding.common.hash.HashCode> getBlockTransactions​(Block block)
        Returns a proof list of transaction hashes committed in the given block. The given block must match exactly the block that is stored in the database.
        Parameters:
        block - block of which list of transaction hashes should be returned
        Throws:
        IllegalArgumentException - if there is no such block in the blockchain
        See Also:
        getBlockTransactions(long)
      • getTxMessages

        public MapIndex<com.exonum.binding.common.hash.HashCode,​com.exonum.binding.common.message.TransactionMessage> getTxMessages()
        Returns a map of transaction messages identified by their SHA-256 hashes. Both committed and in-pool (not yet processed) transactions are returned.
      • getCallRecords

        public CallRecords getCallRecords​(long blockHeight)
        Returns the call records, containing the execution errors that occurred in the block at the given height.
        Parameters:
        blockHeight - the height of the block
        Throws:
        IllegalArgumentException - if the height is invalid: negative or exceeding the blockchain height
      • getTxResult

        public Optional<com.exonum.messages.core.runtime.Errors.ExecutionStatus> getTxResult​(com.exonum.binding.common.hash.HashCode messageHash)
        Returns a transaction execution result for the given message hash.
        Returns:
        a transaction execution result, or Optional.empty() if this transaction is unknown or was not yet executed
      • getTxLocations

        public MapIndex<com.exonum.binding.common.hash.HashCode,​com.exonum.binding.common.blockchain.TransactionLocation> getTxLocations()
        Returns a map that keeps the transaction position inside the blockchain for every transaction hash.
      • getTxLocation

        public Optional<com.exonum.binding.common.blockchain.TransactionLocation> getTxLocation​(com.exonum.binding.common.hash.HashCode messageHash)
        Returns transaction position inside the blockchain for given message hash.
        Returns:
        a transaction execution result, or Optional.empty() if this transaction is unknown or was not yet executed
      • getBlocks

        public MapIndex<com.exonum.binding.common.hash.HashCode,​Block> getBlocks()
        Returns a map that stores a block object for every block hash.
      • getBlock

        public Block getBlock​(long height)
        Returns the block at the given height.
        Parameters:
        height - the height of the block; must be non-negative and less than or equal to the current blockchain height
        Returns:
        a block at the height
        Throws:
        IndexOutOfBoundsException - if the height is not valid
      • findBlock

        public Optional<Block> findBlock​(com.exonum.binding.common.hash.HashCode blockHash)
        Returns a block object for given block hash.
        Returns:
        a corresponding block, or Optional.empty() if there is no block with given block hash
      • getLastBlock

        public Block getLastBlock()
        Returns the latest committed block.
        Throws:
        IllegalStateException - if the "genesis block" was not created
      • getConsensusConfiguration

        public com.exonum.messages.core.Blockchain.Config getConsensusConfiguration()
        Returns the current consensus configuration of the network.
        Throws:
        IllegalStateException - if the "genesis block" was not created
        See Also:
        Exonum configuration for consensus configuration information.
      • getTransactionPool

        public KeySetIndexProxy<com.exonum.binding.common.hash.HashCode> getTransactionPool()
        Returns a set of uncommitted (in-pool) transaction hashes; empty in case of no transactions. Note that this pool represents the state as of the current snapshot, and its state is volatile even between block commits.
        See Also:
        Pool of Unconfirmed Transactions
      • getNumTransactions

        public long getNumTransactions()
        Returns the total number of transactions committed to the blockchain.