Skip to content

Commit ec6bed2

Browse files
Add block and index proofs [ECR-4011] (#1355)
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.
1 parent bfc6cf7 commit ec6bed2

File tree

22 files changed

+587
-71
lines changed

22 files changed

+587
-71
lines changed

exonum-java-binding/CHANGELOG.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1616
## [Unreleased]
1717

1818
### Added
19+
- Support of creation of various blockchain proofs:
20+
- Block Proof
21+
- Transaction Execution Proof
22+
- Call Result Proof
23+
- Service Data Proof.
24+
25+
See [`Blockchain`][blockchain-proofs], `BlockProof` and `IndexProof`
26+
for details. (#1355)
1927
- Support of creation of Protobuf-based proofs for maps and lists.
2028
Such proofs can be easily serialized using Protocol Buffers
2129
and sent to the light clients.
22-
See `ProofMapIndexProxy#getProof` and `MapProof`;
23-
`ProofListIndexProxy.getProof`, `ProofListIndexProxy.getRangeProof` and
24-
`ListProof`.
30+
See:
31+
- `ProofMapIndexProxy#getProof` and `MapProof`;
32+
- `ProofListIndexProxy.getProof`, `ProofListIndexProxy.getRangeProof` and
33+
`ListProof`;
34+
- [`Blockchain`][blockchain-proofs].
2535
- `ProofEntryIndexProxy` collection.
2636
- `supervisor-mode` CLI parameter added for `generate-template` command. It
2737
allows to configure the mode of the Supervisor service. Possible values are
2838
"simple" and "decentralized". (#1361)
2939

40+
[blockchain-proofs]: https://exonum.com/doc/api/java-binding/0.10.0-SNAPSHOT/com/exonum/binding/core/blockchain/Blockchain.html#proofs
41+
3042
### Changed
3143
- Transactions are now implemented as service methods annotated with
3244
`@Transaction(TX_ID)`, instead of classes implementing

exonum-java-binding/common/src/main/java/com/exonum/binding/common/message/SignedMessage.java

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616

1717
package com.exonum.binding.common.message;
1818

19+
import static com.exonum.binding.common.hash.Hashing.sha256;
20+
1921
import com.exonum.binding.common.crypto.PublicKey;
2022
import com.exonum.binding.common.hash.HashCode;
21-
import com.exonum.binding.common.hash.Hashing;
2223
import com.exonum.core.messages.Consensus;
2324
import com.exonum.core.messages.Consensus.ExonumMessage;
2425
import com.google.protobuf.ByteString;
@@ -31,7 +32,7 @@
3132
* <p>It currently does not support verification of the signature against the author's public
3233
* key — such functionality may be added later if needed.
3334
*/
34-
final class SignedMessage {
35+
public final class SignedMessage {
3536

3637
private final ExonumMessage payload;
3738
private final PublicKey authorPk;
@@ -56,10 +57,11 @@ private SignedMessage(ExonumMessage payload, PublicKey authorPk,
5657
* {@link Consensus.SignedMessage}; or if the payload of the message is not
5758
* {@link Consensus.ExonumMessage}
5859
*/
59-
static SignedMessage parseFrom(byte[] messageBytes) throws InvalidProtocolBufferException {
60+
public static SignedMessage parseFrom(byte[] messageBytes) throws InvalidProtocolBufferException {
6061
// Try to decode the SignedMessage container
62+
HashCode hash = sha256().hashBytes(messageBytes);
6163
Consensus.SignedMessage message = Consensus.SignedMessage.parseFrom(messageBytes);
62-
return fromProto(message);
64+
return fromProto(message, hash);
6365
}
6466

6567
/**
@@ -69,26 +71,28 @@ static SignedMessage parseFrom(byte[] messageBytes) throws InvalidProtocolBuffer
6971
* @throws InvalidProtocolBufferException if a signed message does not contain a valid payload
7072
* that is a serialized {@link Consensus.ExonumMessage}
7173
*/
72-
static SignedMessage fromProto(Consensus.SignedMessage message)
74+
public static SignedMessage fromProto(Consensus.SignedMessage message)
7375
throws InvalidProtocolBufferException {
76+
HashCode hash = sha256().hashBytes(message.toByteArray());
77+
return fromProto(message, hash);
78+
}
79+
80+
private static SignedMessage fromProto(Consensus.SignedMessage message,
81+
HashCode messageHash) throws InvalidProtocolBufferException {
7482
// Try to decode the payload, which is stored as bytes. It is expected to be an ExonumMessage
7583
ByteString payloadBytes = message.getPayload();
7684
ExonumMessage payload = ExonumMessage.parseFrom(payloadBytes);
77-
7885
PublicKey authorPk = PublicKey.fromBytes(message.getAuthor()
7986
.getData()
8087
.toByteArray());
8188
ByteString signature = message.getSignature().getData();
82-
83-
HashCode hash = Hashing.sha256().hashBytes(message.toByteArray());
84-
85-
return new SignedMessage(payload, authorPk, signature, hash);
89+
return new SignedMessage(payload, authorPk, signature, messageHash);
8690
}
8791

8892
/**
8993
* Returns the message payload.
9094
*/
91-
Consensus.ExonumMessage getPayload() {
95+
public Consensus.ExonumMessage getPayload() {
9296
return payload;
9397
}
9498

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

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

116120
/**
117121
* Returns the hash of the signed message, which is the hash of the protobuf-serialized
118122
* representation.
119123
*/
120-
HashCode hash() {
124+
public HashCode hash() {
121125
return hash;
122126
}
123127
}

exonum-java-binding/core/rust/src/storage/blockchain.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use {
2020
/// - index is not initialized (index have not been used before calling the method)
2121
/// - index is not Merkelized
2222
#[no_mangle]
23-
pub extern "system" fn Java_com_exonum_binding_core_blockchain_Blockchain_nativeCreateIndexProof(
23+
pub extern "system" fn Java_com_exonum_binding_core_blockchain_BlockchainProofs_nativeCreateIndexProof(
2424
env: JNIEnv,
2525
_: JObject,
2626
snapshot_handle: jlong,
@@ -50,7 +50,7 @@ pub extern "system" fn Java_com_exonum_binding_core_blockchain_Blockchain_native
5050
/// - there is no such block
5151
/// - passed `snapshot_handle` is Fork handle
5252
#[no_mangle]
53-
pub extern "system" fn Java_com_exonum_binding_core_blockchain_Blockchain_nativeCreateBlockProof(
53+
pub extern "system" fn Java_com_exonum_binding_core_blockchain_BlockchainProofs_nativeCreateBlockProof(
5454
env: JNIEnv,
5555
_: JObject,
5656
snapshot_handle: jlong,

exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/Block.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

1717
package com.exonum.binding.core.blockchain;
1818

19+
import static com.exonum.binding.common.hash.Hashing.sha256;
1920
import static com.google.common.base.Preconditions.checkState;
2021

2122
import com.exonum.binding.common.hash.HashCode;
23+
import com.exonum.binding.core.blockchain.serialization.BlockSerializer;
2224
import com.exonum.binding.core.blockchain.serialization.CoreTypeAdapterFactory;
2325
import com.exonum.binding.core.service.Schema;
2426
import com.google.auto.value.AutoValue;
@@ -95,7 +97,7 @@ public final boolean isEmpty() {
9597
/**
9698
* Root hash of exceptions occurred in the block.
9799
*
98-
* @see Blockchain#getCallErrors()
100+
* @see Blockchain#getCallErrors(long)
99101
*/
100102
public abstract HashCode getErrorHash();
101103

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

127+
/**
128+
* Creates a block from the block message.
129+
* @param blockMessage a block
130+
*/
131+
public static Block fromMessage(com.exonum.core.messages.Blockchain.Block blockMessage) {
132+
// Such implementation prevents a redundant deserialization of Block message
133+
// (in BlockSerializer#fromBytes).
134+
HashCode blockHash = sha256().hashBytes(blockMessage.toByteArray());
135+
return BlockSerializer.newBlockInternal(blockMessage, blockHash);
136+
}
137+
138+
/**
139+
* Creates a block from the serialized block message.
140+
* @param serializedBlock a serialized block message
141+
* @throws IllegalArgumentException if the block bytes are not a serialized
142+
* {@link com.exonum.core.messages.Blockchain.Block}
143+
*/
144+
public static Block parseFrom(byte[] serializedBlock) {
145+
return BlockSerializer.INSTANCE.fromBytes(serializedBlock);
146+
}
147+
125148
/**
126149
* Creates a new block builder.
127150
*/
@@ -172,7 +195,7 @@ public abstract static class Builder {
172195
* Sets the blockchain state hash at the moment this block was committed. The blockchain
173196
* state hash reflects the state of each service in the database.
174197
*
175-
* @see Schema#getStateHashes()
198+
* @see Schema
176199
*/
177200
public abstract Builder stateHash(HashCode blockchainStateHash);
178201

0 commit comments

Comments
 (0)