Skip to content

Commit

Permalink
Add block and index proofs [ECR-4011] (#1355)
Browse files Browse the repository at this point in the history
Added block and index proofs support. They shall be created through Blockchain.

Documented the various proof types: what they prove, what comprises
them, how to create them. Added an example for a commonly used proof type.

Also:
- Made SignedMessage public to be able to use it in tests; 
   got rid of redundant serialization in #parseFrom.
- Added Block#parseFrom and Block#fromMessage.
  • Loading branch information
dmitry-timofeev authored Jan 17, 2020
1 parent bfc6cf7 commit ec6bed2
Show file tree
Hide file tree
Showing 22 changed files with 587 additions and 71 deletions.
18 changes: 15 additions & 3 deletions exonum-java-binding/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]

### Added
- Support of creation of various blockchain proofs:
- Block Proof
- Transaction Execution Proof
- Call Result Proof
- Service Data Proof.

See [`Blockchain`][blockchain-proofs], `BlockProof` and `IndexProof`
for details. (#1355)
- Support of creation of Protobuf-based proofs for maps and lists.
Such proofs can be easily serialized using Protocol Buffers
and sent to the light clients.
See `ProofMapIndexProxy#getProof` and `MapProof`;
`ProofListIndexProxy.getProof`, `ProofListIndexProxy.getRangeProof` and
`ListProof`.
See:
- `ProofMapIndexProxy#getProof` and `MapProof`;
- `ProofListIndexProxy.getProof`, `ProofListIndexProxy.getRangeProof` and
`ListProof`;
- [`Blockchain`][blockchain-proofs].
- `ProofEntryIndexProxy` collection.
- `supervisor-mode` CLI parameter added for `generate-template` command. It
allows to configure the mode of the Supervisor service. Possible values are
"simple" and "decentralized". (#1361)

[blockchain-proofs]: https://exonum.com/doc/api/java-binding/0.10.0-SNAPSHOT/com/exonum/binding/core/blockchain/Blockchain.html#proofs

### Changed
- Transactions are now implemented as service methods annotated with
`@Transaction(TX_ID)`, instead of classes implementing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

package com.exonum.binding.common.message;

import static com.exonum.binding.common.hash.Hashing.sha256;

import com.exonum.binding.common.crypto.PublicKey;
import com.exonum.binding.common.hash.HashCode;
import com.exonum.binding.common.hash.Hashing;
import com.exonum.core.messages.Consensus;
import com.exonum.core.messages.Consensus.ExonumMessage;
import com.google.protobuf.ByteString;
Expand All @@ -31,7 +32,7 @@
* <p>It currently does not support verification of the signature against the author's public
* key — such functionality may be added later if needed.
*/
final class SignedMessage {
public final class SignedMessage {

private final ExonumMessage payload;
private final PublicKey authorPk;
Expand All @@ -56,10 +57,11 @@ private SignedMessage(ExonumMessage payload, PublicKey authorPk,
* {@link Consensus.SignedMessage}; or if the payload of the message is not
* {@link Consensus.ExonumMessage}
*/
static SignedMessage parseFrom(byte[] messageBytes) throws InvalidProtocolBufferException {
public static SignedMessage parseFrom(byte[] messageBytes) throws InvalidProtocolBufferException {
// Try to decode the SignedMessage container
HashCode hash = sha256().hashBytes(messageBytes);
Consensus.SignedMessage message = Consensus.SignedMessage.parseFrom(messageBytes);
return fromProto(message);
return fromProto(message, hash);
}

/**
Expand All @@ -69,26 +71,28 @@ static SignedMessage parseFrom(byte[] messageBytes) throws InvalidProtocolBuffer
* @throws InvalidProtocolBufferException if a signed message does not contain a valid payload
* that is a serialized {@link Consensus.ExonumMessage}
*/
static SignedMessage fromProto(Consensus.SignedMessage message)
public static SignedMessage fromProto(Consensus.SignedMessage message)
throws InvalidProtocolBufferException {
HashCode hash = sha256().hashBytes(message.toByteArray());
return fromProto(message, hash);
}

private static SignedMessage fromProto(Consensus.SignedMessage message,
HashCode messageHash) throws InvalidProtocolBufferException {
// Try to decode the payload, which is stored as bytes. It is expected to be an ExonumMessage
ByteString payloadBytes = message.getPayload();
ExonumMessage payload = ExonumMessage.parseFrom(payloadBytes);

PublicKey authorPk = PublicKey.fromBytes(message.getAuthor()
.getData()
.toByteArray());
ByteString signature = message.getSignature().getData();

HashCode hash = Hashing.sha256().hashBytes(message.toByteArray());

return new SignedMessage(payload, authorPk, signature, hash);
return new SignedMessage(payload, authorPk, signature, messageHash);
}

/**
* Returns the message payload.
*/
Consensus.ExonumMessage getPayload() {
public Consensus.ExonumMessage getPayload() {
return payload;
}

Expand All @@ -98,7 +102,7 @@ Consensus.ExonumMessage getPayload() {
* <p>The correctness of the signature is <strong>not</strong> verified against this key
* and must be done separately if needed.
*/
PublicKey getAuthorPk() {
public PublicKey getAuthorPk() {
return authorPk;
}

Expand All @@ -109,15 +113,15 @@ PublicKey getAuthorPk() {
* <p>The correctness of the signature is <strong>not</strong> verified against this key
* and must be done separately if needed.
*/
byte[] getSignature() {
public byte[] getSignature() {
return signature.toByteArray();
}

/**
* Returns the hash of the signed message, which is the hash of the protobuf-serialized
* representation.
*/
HashCode hash() {
public HashCode hash() {
return hash;
}
}
4 changes: 2 additions & 2 deletions exonum-java-binding/core/rust/src/storage/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use {
/// - index is not initialized (index have not been used before calling the method)
/// - index is not Merkelized
#[no_mangle]
pub extern "system" fn Java_com_exonum_binding_core_blockchain_Blockchain_nativeCreateIndexProof(
pub extern "system" fn Java_com_exonum_binding_core_blockchain_BlockchainProofs_nativeCreateIndexProof(
env: JNIEnv,
_: JObject,
snapshot_handle: jlong,
Expand Down Expand Up @@ -50,7 +50,7 @@ pub extern "system" fn Java_com_exonum_binding_core_blockchain_Blockchain_native
/// - there is no such block
/// - passed `snapshot_handle` is Fork handle
#[no_mangle]
pub extern "system" fn Java_com_exonum_binding_core_blockchain_Blockchain_nativeCreateBlockProof(
pub extern "system" fn Java_com_exonum_binding_core_blockchain_BlockchainProofs_nativeCreateBlockProof(
env: JNIEnv,
_: JObject,
snapshot_handle: jlong,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

package com.exonum.binding.core.blockchain;

import static com.exonum.binding.common.hash.Hashing.sha256;
import static com.google.common.base.Preconditions.checkState;

import com.exonum.binding.common.hash.HashCode;
import com.exonum.binding.core.blockchain.serialization.BlockSerializer;
import com.exonum.binding.core.blockchain.serialization.CoreTypeAdapterFactory;
import com.exonum.binding.core.service.Schema;
import com.google.auto.value.AutoValue;
Expand Down Expand Up @@ -95,7 +97,7 @@ public final boolean isEmpty() {
/**
* Root hash of exceptions occurred in the block.
*
* @see Blockchain#getCallErrors()
* @see Blockchain#getCallErrors(long)
*/
public abstract HashCode getErrorHash();

Expand All @@ -122,6 +124,27 @@ public static TypeAdapter<Block> typeAdapter(Gson gson) {
return new AutoValue_Block.GsonTypeAdapter(gson);
}

/**
* Creates a block from the block message.
* @param blockMessage a block
*/
public static Block fromMessage(com.exonum.core.messages.Blockchain.Block blockMessage) {
// Such implementation prevents a redundant deserialization of Block message
// (in BlockSerializer#fromBytes).
HashCode blockHash = sha256().hashBytes(blockMessage.toByteArray());
return BlockSerializer.newBlockInternal(blockMessage, blockHash);
}

/**
* Creates a block from the serialized block message.
* @param serializedBlock a serialized block message
* @throws IllegalArgumentException if the block bytes are not a serialized
* {@link com.exonum.core.messages.Blockchain.Block}
*/
public static Block parseFrom(byte[] serializedBlock) {
return BlockSerializer.INSTANCE.fromBytes(serializedBlock);
}

/**
* Creates a new block builder.
*/
Expand Down Expand Up @@ -172,7 +195,7 @@ public abstract static class Builder {
* Sets the blockchain state hash at the moment this block was committed. The blockchain
* state hash reflects the state of each service in the database.
*
* @see Schema#getStateHashes()
* @see Schema
*/
public abstract Builder stateHash(HashCode blockchainStateHash);

Expand Down
Loading

0 comments on commit ec6bed2

Please sign in to comment.