Skip to content

Commit 75d5437

Browse files
Make CoreSchema a plain schema (not a proxy) [ECR-3865] (#1303)
Make CoreSchema a plain schema — a factory of Exonum indexes. The existing ITs in BlockchainIntegrationTest adequately cover the operations now implemented in Java CoreSchema. Also, push CoreSchema#getLastBlock to Blockchain.
1 parent 633bd38 commit 75d5437

6 files changed

Lines changed: 49 additions & 176 deletions

File tree

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

Lines changed: 0 additions & 107 deletions
This file was deleted.

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
mod core_schema;
1615
mod db;
1716
mod entry;
1817
mod fork;
@@ -27,7 +26,6 @@ mod raw_proof_map_index;
2726
mod temporarydb;
2827
mod value_set_index;
2928

30-
pub use self::core_schema::*;
3129
pub use self::db::Java_com_exonum_binding_core_storage_database_Views_nativeFree;
3230
pub(crate) use self::db::View;
3331
pub use self::entry::*;

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.exonum.binding.core.blockchain;
1818

1919
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkState;
2021

2122
import com.exonum.binding.common.blockchain.TransactionLocation;
2223
import com.exonum.binding.common.hash.HashCode;
@@ -42,18 +43,18 @@
4243
*/
4344
public final class Blockchain {
4445

45-
private final CoreSchemaProxy schema;
46+
private final CoreSchema schema;
4647

4748
@VisibleForTesting
48-
Blockchain(CoreSchemaProxy schema) {
49+
Blockchain(CoreSchema schema) {
4950
this.schema = schema;
5051
}
5152

5253
/**
5354
* Constructs a new blockchain instance for the given database view.
5455
*/
5556
public static Blockchain newInstance(View view) {
56-
CoreSchemaProxy coreSchema = CoreSchemaProxy.newInstance(view);
57+
CoreSchema coreSchema = CoreSchema.newInstance(view);
5758
return new Blockchain(coreSchema);
5859
}
5960

@@ -225,10 +226,14 @@ public Optional<Block> findBlock(HashCode blockHash) {
225226
/**
226227
* Returns the latest committed block.
227228
*
228-
* @throws RuntimeException if the "genesis block" was not created
229+
* @throws IllegalStateException if the "genesis block" was not created
229230
*/
230231
public Block getLastBlock() {
231-
return schema.getLastBlock();
232+
ListIndex<HashCode> blockHashes = getBlockHashes();
233+
checkState(!blockHashes.isEmpty(),
234+
"No genesis block created yet (block hashes list is empty)");
235+
HashCode lastBlockHash = blockHashes.getLast();
236+
return getBlocks().get(lastBlockHash);
232237
}
233238

234239
/**

exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/CoreSchemaProxy.java renamed to exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/CoreSchema.java

Lines changed: 18 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@
2727
import com.exonum.binding.common.serialization.StandardSerializers;
2828
import com.exonum.binding.core.blockchain.serialization.BlockSerializer;
2929
import com.exonum.binding.core.blockchain.serialization.TransactionLocationSerializer;
30-
import com.exonum.binding.core.proxy.Cleaner;
31-
import com.exonum.binding.core.proxy.NativeHandle;
32-
import com.exonum.binding.core.proxy.ProxyDestructor;
3330
import com.exonum.binding.core.storage.database.View;
3431
import com.exonum.binding.core.storage.indices.EntryIndexProxy;
3532
import com.exonum.binding.core.storage.indices.KeySetIndexProxy;
@@ -39,24 +36,22 @@
3936
import com.exonum.binding.core.storage.indices.MapIndexProxy;
4037
import com.exonum.binding.core.storage.indices.ProofListIndexProxy;
4138
import com.exonum.binding.core.storage.indices.ProofMapIndexProxy;
42-
import com.exonum.binding.core.util.LibraryLoader;
4339
import com.exonum.core.messages.Blockchain.Config;
4440
import com.exonum.core.messages.Runtime.ExecutionStatus;
4541
import java.nio.ByteBuffer;
4642
import java.nio.ByteOrder;
4743

4844
/**
49-
* A proxy class for the blockchain::Schema struct maintained by Exonum core.
50-
* Please refer to the
51-
* <a href="https://docs.rs/exonum/latest/exonum/blockchain/struct.Schema.html">doc</a> for details.
45+
* Information schema for indices maintained by the Exonum core logic.
46+
*
47+
* <p>Indices defined by this schema are present in the blockchain regardless of the deployed
48+
* services and store general-purpose information, such as committed transactions.
49+
*
50+
* @see <a href="https://docs.rs/exonum/latest/exonum/blockchain/struct.Schema.html">
51+
* Definition in Exonum</a>
5252
*/
53-
final class CoreSchemaProxy {
53+
final class CoreSchema {
5454

55-
static {
56-
LibraryLoader.load();
57-
}
58-
59-
private final NativeHandle nativeHandle;
6055
private final View dbView;
6156
private static final Serializer<Block> BLOCK_SERIALIZER = BlockSerializer.INSTANCE;
6257
private static final Serializer<TransactionLocation> TRANSACTION_LOCATION_SERIALIZER =
@@ -68,32 +63,28 @@ final class CoreSchemaProxy {
6863
private static final Serializer<Config> CONSENSUS_CONFIG_SERIALIZER =
6964
StandardSerializers.protobuf(Config.class);
7065

71-
private CoreSchemaProxy(NativeHandle nativeHandle, View dbView) {
72-
this.nativeHandle = nativeHandle;
66+
private CoreSchema(View dbView) {
7367
this.dbView = dbView;
7468
}
7569

7670
/**
77-
* Constructs a schema proxy for a given dbView.
71+
* Constructs a schema for a given dbView.
7872
*/
79-
static CoreSchemaProxy newInstance(View dbView) {
80-
long nativePointer = nativeCreate(dbView.getViewNativeHandle());
81-
NativeHandle nativeHandle = new NativeHandle(nativePointer);
82-
83-
Cleaner cleaner = dbView.getCleaner();
84-
ProxyDestructor.newRegistered(cleaner, nativeHandle, CoreSchemaProxy.class,
85-
CoreSchemaProxy::nativeFree);
86-
87-
return new CoreSchemaProxy(nativeHandle, dbView);
73+
static CoreSchema newInstance(View dbView) {
74+
return new CoreSchema(dbView);
8875
}
8976

9077
/**
9178
* Returns the height of the latest committed block.
9279
*
93-
* @throws RuntimeException if the "genesis block" was not created
80+
* @throws IllegalStateException if the "genesis block" was not created
9481
*/
9582
long getHeight() {
96-
return nativeGetHeight(nativeHandle.get());
83+
// The blockchain height is equal to the number of blocks (incl. genesis) minus one
84+
ListIndex<HashCode> blockHashes = getBlockHashes();
85+
checkState(!blockHashes.isEmpty(),
86+
"No genesis block created yet (block hashes list is empty)");
87+
return blockHashes.size() - 1;
9788
}
9889

9990
/**
@@ -131,15 +122,6 @@ MapIndex<HashCode, Block> getBlocks() {
131122
CoreIndex.BLOCKS, dbView, StandardSerializers.hash(), BLOCK_SERIALIZER);
132123
}
133124

134-
/**
135-
* Returns the latest committed block.
136-
*
137-
* @throws RuntimeException if the "genesis block" was not created
138-
*/
139-
Block getLastBlock() {
140-
return BLOCK_SERIALIZER.fromBytes(nativeGetLastBlock(nativeHandle.get()));
141-
}
142-
143125
/**
144126
* Returns a map of transaction messages identified by their SHA-256 hashes.
145127
*/
@@ -191,19 +173,6 @@ Config getConsensusConfiguration() {
191173
return configEntry.get();
192174
}
193175

194-
private static native long nativeCreate(long viewNativeHandle);
195-
196-
private static native void nativeFree(long nativeHandle);
197-
198-
private static native long nativeGetHeight(long nativeHandle);
199-
200-
/**
201-
* Returns the latest committed block.
202-
*
203-
* @throws RuntimeException if the "genesis block" was not created
204-
*/
205-
private static native byte[] nativeGetLastBlock(long nativeHandle);
206-
207176
private byte[] toCoreStorageKey(long value) {
208177
return ByteBuffer.allocate(Long.BYTES)
209178
.order(ByteOrder.BIG_ENDIAN)

exonum-java-binding/core/src/test/java/com/exonum/binding/core/blockchain/CoreSchemaProxyIntegrationTest.java renamed to exonum-java-binding/core/src/test/java/com/exonum/binding/core/blockchain/CoreSchemaIntegrationTest.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@
3333
import org.junit.jupiter.api.Test;
3434

3535
@RequiresNativeLibrary
36-
class CoreSchemaProxyIntegrationTest {
36+
class CoreSchemaIntegrationTest {
3737

3838
@Test
3939
void getHeightBeforeGenesisBlockTest() {
40-
assertSchema((schema) -> assertThrows(RuntimeException.class, schema::getHeight));
40+
assertSchema((schema) -> assertThrows(IllegalStateException.class, schema::getHeight));
4141
}
4242

4343
@Test
@@ -49,10 +49,9 @@ void getAllBlockHashesTest() {
4949
void getBlockTransactionsTest() {
5050
assertSchema((schema) -> {
5151
long height = 0L;
52-
Exception e = assertThrows(RuntimeException.class,
52+
Exception e = assertThrows(IllegalStateException.class,
5353
() -> schema.getBlockTransactions(height));
54-
assertThat(e).hasMessageContaining("An attempt to get the actual `height` "
55-
+ "during creating the genesis block");
54+
assertThat(e).hasMessageContaining("No genesis block created");
5655
});
5756
}
5857

@@ -67,11 +66,6 @@ void getBlocksTest() {
6766
assertSchema((schema) -> assertTrue(schema.getBlocks().isEmpty()));
6867
}
6968

70-
@Test
71-
void getLastBlockBeforeGenesisBlockTest() {
72-
assertSchema((schema) -> assertThrows(RuntimeException.class, schema::getLastBlock));
73-
}
74-
7569
@Test
7670
void getTxMessagesTest() {
7771
assertSchema((schema) -> assertTrue(schema.getTxMessages().isEmpty()));
@@ -95,10 +89,10 @@ void getTransactionPool() {
9589
});
9690
}
9791

98-
private static void assertSchema(Consumer<CoreSchemaProxy> assertion) {
92+
private static void assertSchema(Consumer<CoreSchema> assertion) {
9993
try (TemporaryDb db = TemporaryDb.newInstance(); Cleaner cleaner = new Cleaner()) {
10094
Snapshot view = db.createSnapshot(cleaner);
101-
assertion.accept(CoreSchemaProxy.newInstance(view));
95+
assertion.accept(CoreSchema.newInstance(view));
10296
} catch (CloseFailuresException e) {
10397
fail(e.getLocalizedMessage());
10498
}

exonum-java-binding/integration-tests/src/test/java/com/exonum/binding/test/BlockchainIntegrationTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
import org.junit.jupiter.api.BeforeEach;
6060
import org.junit.jupiter.api.Nested;
6161
import org.junit.jupiter.api.Test;
62+
import org.junit.jupiter.params.ParameterizedTest;
63+
import org.junit.jupiter.params.provider.ValueSource;
6264

6365
class BlockchainIntegrationTest {
6466

@@ -147,7 +149,7 @@ void containsBlock() {
147149
@Test
148150
void getHeight() {
149151
testKitTest((blockchain) -> {
150-
long expectedHeight = 1;
152+
long expectedHeight = 1L;
151153
assertThat(blockchain.getHeight()).isEqualTo(expectedHeight);
152154
});
153155
}
@@ -209,6 +211,18 @@ void getBlockTransactionsByInvalidHeight() {
209211
});
210212
}
211213

214+
@ParameterizedTest
215+
@ValueSource(longs = {-1, -2, Long.MIN_VALUE})
216+
void getBlockTransactionsByNegativeHeight(long height) {
217+
testKitTest((blockchain) -> {
218+
Exception e = assertThrows(IllegalArgumentException.class,
219+
() -> blockchain.getBlockTransactions(height));
220+
assertThat(e.getMessage()).containsIgnoringCase("negative")
221+
.containsIgnoringCase("height")
222+
.contains(Long.toString(height));
223+
});
224+
}
225+
212226
@Test
213227
void getBlockTransactionsByHash() {
214228
testKitTest((blockchain) -> {

0 commit comments

Comments
 (0)