From 67f2ce5d3b47bd79ee483e0304d7358eeca44b44 Mon Sep 17 00:00:00 2001 From: Ozan Nacitarhan Date: Sun, 24 Apr 2022 18:21:39 +0300 Subject: [PATCH 1/5] implements merkle snapshot --- src/main/java/model/codec/EntityType.java | 2 + src/main/java/model/lightchain/Account.java | 24 +++++- .../modules/ads/merkletree/MerkleTree.java | 8 ++ src/main/java/state/Snapshot.java | 3 +- .../java/state/merkle/MerkleSnapshot.java | 82 +++++++++++++++++++ .../java/state/merkle/MerkleSnapshotTest.java | 61 ++++++++++++++ 6 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 src/main/java/state/merkle/MerkleSnapshot.java create mode 100644 src/test/java/state/merkle/MerkleSnapshotTest.java diff --git a/src/main/java/model/codec/EntityType.java b/src/main/java/model/codec/EntityType.java index 2be05de7..f0b3e0c8 100644 --- a/src/main/java/model/codec/EntityType.java +++ b/src/main/java/model/codec/EntityType.java @@ -9,4 +9,6 @@ public class EntityType { public static final String TYPE_VALIDATED_TRANSACTION = "type-lightchain-validated-transaction"; public static final String TYPE_VALIDATED_BLOCK = "type-lightchain-validated-block"; public static final String TYPE_ECDSA_SIGNATURE = "type-lightchain-ecdsa-signature"; + public static final String TYPE_ACCOUNT = "type-lightchain-account"; + } diff --git a/src/main/java/model/lightchain/Account.java b/src/main/java/model/lightchain/Account.java index df046c0b..9ec3b9fa 100644 --- a/src/main/java/model/lightchain/Account.java +++ b/src/main/java/model/lightchain/Account.java @@ -1,11 +1,13 @@ package model.lightchain; +import model.Entity; +import model.codec.EntityType; import model.crypto.PublicKey; /** * Represents a LightChain account which is the constituent of the SnapShot. */ -public class Account { +public class Account extends Entity { /** * Unique identifier of the account. */ @@ -72,4 +74,24 @@ public Identifier getLastBlockId() { public int getStake() { return this.stake; } + + /** + * Type of this entity. + * + * @return type of this entity. + */ + @Override + public String type() { + return EntityType.TYPE_ACCOUNT; + } + + /** + * Identifier of this Account + * + * @return identifier of this Account + */ + @Override + public Identifier id() { + return this.identifier; + } } diff --git a/src/main/java/modules/ads/merkletree/MerkleTree.java b/src/main/java/modules/ads/merkletree/MerkleTree.java index 2c194c4a..a8d56ff0 100644 --- a/src/main/java/modules/ads/merkletree/MerkleTree.java +++ b/src/main/java/modules/ads/merkletree/MerkleTree.java @@ -133,4 +133,12 @@ private void buildMerkleTree() { public int size() { return this.size; } + + public ArrayList all() { + ArrayList entities = new ArrayList<>(); + for(Identifier id : entityHashTable.keySet()) { + entities.add(entityHashTable.get(id)); + } + return entities; + } } diff --git a/src/main/java/state/Snapshot.java b/src/main/java/state/Snapshot.java index eaaa01db..d672b63b 100644 --- a/src/main/java/state/Snapshot.java +++ b/src/main/java/state/Snapshot.java @@ -2,6 +2,7 @@ import java.util.ArrayList; +import model.Entity; import model.lightchain.Account; import model.lightchain.Identifier; @@ -30,7 +31,7 @@ public interface Snapshot { * @return account corresponding to the given identifier at this snapshot, or null if such an account * does not exist. */ - Account getAccount(Identifier identifier); + Entity getAccount(Identifier identifier); /** * The list of accounts in this snapshot. diff --git a/src/main/java/state/merkle/MerkleSnapshot.java b/src/main/java/state/merkle/MerkleSnapshot.java new file mode 100644 index 00000000..7a90feec --- /dev/null +++ b/src/main/java/state/merkle/MerkleSnapshot.java @@ -0,0 +1,82 @@ +package state.merkle; + +import java.util.ArrayList; + +import model.Entity; +import model.lightchain.Account; +import model.lightchain.Identifier; +import modules.ads.merkletree.MerkleTree; + +public class MerkleSnapshot implements state.Snapshot { + private final Identifier rootBlockId; + private final long rootBlockHeight; + private MerkleTree merkleTree; + + /** + * Constructor of MerkleSnapShot. + * + * @param rootBlockId root block id representing this snapshot. + * @param rootBlockHeight root block height of this snapshot. + */ + public MerkleSnapshot(Identifier rootBlockId, long rootBlockHeight) { + this.rootBlockId = rootBlockId; + this.rootBlockHeight = rootBlockHeight; + this.merkleTree = new MerkleTree(); + } + + /** + * The identifier of finalized block that this snapshot represents. + * + * @return the identifier of finalized block that this snapshot represents. + */ + @Override + public Identifier getReferenceBlockId() { + return rootBlockId; + } + + /** + * The height of the reference block that this snapshot represents. + * + * @return height of the reference block that this snapshot represents. + */ + @Override + public long getReferenceBlockHeight() { + return rootBlockHeight; + } + + /** + * Fetches account corresponding to an identifier at the given snapshot. + * + * @param identifier identifier of an account of interest. + * @return account corresponding to the given identifier at this snapshot, or null if such an account + * does not exist. + */ + @Override + public Account getAccount(Identifier identifier) { + return (Account) merkleTree.get(identifier).getEntity(); + } + + /** + * Adds an account to the snapshot. + * + * @param identifier Identifier of the account to add. + * @param account Account to add. + */ + public void addAccount(Identifier identifier, Account account) { + this.merkleTree.put(account); + } + + /** + * The list of accounts in this snapshot. + * + * @return the list of accounts in this snapshot. + */ + @Override + public ArrayList all() { + ArrayList accounts = new ArrayList<>(); + for (Entity entity : merkleTree.all()) { + accounts.add((Account) entity); + } + return accounts; + } +} diff --git a/src/test/java/state/merkle/MerkleSnapshotTest.java b/src/test/java/state/merkle/MerkleSnapshotTest.java new file mode 100644 index 00000000..64d307ce --- /dev/null +++ b/src/test/java/state/merkle/MerkleSnapshotTest.java @@ -0,0 +1,61 @@ +package state.merkle; + +import java.util.ArrayList; + +import model.lightchain.Account; +import model.lightchain.Identifier; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import protocol.Parameters; +import state.table.TableSnapshot; +import unittest.fixtures.AccountFixture; +import unittest.fixtures.IdentifierFixture; + +/** + * Encapsulates tests for hash table implementation of state snapshot. + */ + +public class MerkleSnapshotTest { + /** + * Evaluates the correctness of getters in TableSnapshot. + */ + @Test + public void testMerkleSnapshot() { + // Arrange + /// Snapshot and Lists + Identifier rootBlockId = IdentifierFixture.newIdentifier(); + MerkleSnapshot merkleSnapshot = new MerkleSnapshot(rootBlockId, 10L); + ArrayList identifiers = new ArrayList<>(); + ArrayList accounts = new ArrayList<>(); + + /// Staked and Unstaked Accounts + for (int i = 0; i < 2; i++) { + // staked + for (int j = 0; j < 5; j++) { + Identifier accountId = IdentifierFixture.newIdentifier(); + Account account = AccountFixture.newAccount(accountId, Parameters.MINIMUM_STAKE + j); + merkleSnapshot.addAccount(accountId, account); + identifiers.add(accountId); + accounts.add(account); + } + for (int k = 0; k < 5; k++) { + // unstaked + Identifier accountId = IdentifierFixture.newIdentifier(); + Account account = AccountFixture.newAccount(accountId, Parameters.MINIMUM_STAKE - 2); + merkleSnapshot.addAccount(accountId, account); + identifiers.add(accountId); + accounts.add(account); + } + } + + // Assert + for (int i = 0; i < identifiers.size(); i++) { + Assertions.assertEquals(merkleSnapshot.getAccount(identifiers.get(i)), accounts.get(i)); + } + Assertions.assertEquals(merkleSnapshot.getReferenceBlockId(), rootBlockId); + Assertions.assertEquals(merkleSnapshot.getReferenceBlockHeight(), 10L); + Assertions.assertEquals(merkleSnapshot.all().size(), 20); + Assertions.assertTrue(merkleSnapshot.all().containsAll(accounts) + && accounts.containsAll(merkleSnapshot.all())); + } +} From f854bd9dceb482d96d775af20dc4dc2a10c8e10f Mon Sep 17 00:00:00 2001 From: Ozan Nacitarhan Date: Sun, 24 Apr 2022 18:23:15 +0300 Subject: [PATCH 2/5] fixes a wrong type decleration --- src/main/java/state/Snapshot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/state/Snapshot.java b/src/main/java/state/Snapshot.java index d672b63b..a1730693 100644 --- a/src/main/java/state/Snapshot.java +++ b/src/main/java/state/Snapshot.java @@ -31,7 +31,7 @@ public interface Snapshot { * @return account corresponding to the given identifier at this snapshot, or null if such an account * does not exist. */ - Entity getAccount(Identifier identifier); + Account getAccount(Identifier identifier); /** * The list of accounts in this snapshot. From ece287748a088a0dc2759aadebd8ef8eff2a908b Mon Sep 17 00:00:00 2001 From: Ozan Nacitarhan Date: Sun, 24 Apr 2022 20:03:05 +0300 Subject: [PATCH 3/5] implements MerkleSnapshot and makes MerkleTree tests generic --- src/main/java/model/lightchain/Account.java | 2 +- .../modules/ads/merkletree/MerkleTree.java | 9 +- src/main/java/state/Snapshot.java | 1 - .../java/state/merkle/MerkleSnapshot.java | 3 + src/test/java/modules/ads/MerkleTreeTest.java | 216 +++++++++++++----- .../java/state/merkle/MerkleSnapshotTest.java | 88 ++++++- 6 files changed, 253 insertions(+), 66 deletions(-) diff --git a/src/main/java/model/lightchain/Account.java b/src/main/java/model/lightchain/Account.java index 9ec3b9fa..0ba06901 100644 --- a/src/main/java/model/lightchain/Account.java +++ b/src/main/java/model/lightchain/Account.java @@ -86,7 +86,7 @@ public String type() { } /** - * Identifier of this Account + * Identifier of this account. * * @return identifier of this Account */ diff --git a/src/main/java/modules/ads/merkletree/MerkleTree.java b/src/main/java/modules/ads/merkletree/MerkleTree.java index a8d56ff0..e8a6d633 100644 --- a/src/main/java/modules/ads/merkletree/MerkleTree.java +++ b/src/main/java/modules/ads/merkletree/MerkleTree.java @@ -134,10 +134,15 @@ public int size() { return this.size; } + /** + * returns all entities in the merkle tree. + * + * @return all entities in the merkle tree. + */ public ArrayList all() { ArrayList entities = new ArrayList<>(); - for(Identifier id : entityHashTable.keySet()) { - entities.add(entityHashTable.get(id)); + for (Entity e : entityHashTable.values()) { + entities.add(e); } return entities; } diff --git a/src/main/java/state/Snapshot.java b/src/main/java/state/Snapshot.java index a1730693..eaaa01db 100644 --- a/src/main/java/state/Snapshot.java +++ b/src/main/java/state/Snapshot.java @@ -2,7 +2,6 @@ import java.util.ArrayList; -import model.Entity; import model.lightchain.Account; import model.lightchain.Identifier; diff --git a/src/main/java/state/merkle/MerkleSnapshot.java b/src/main/java/state/merkle/MerkleSnapshot.java index 7a90feec..f07301ab 100644 --- a/src/main/java/state/merkle/MerkleSnapshot.java +++ b/src/main/java/state/merkle/MerkleSnapshot.java @@ -7,6 +7,9 @@ import model.lightchain.Identifier; import modules.ads.merkletree.MerkleTree; +/** + * Implements a simplified merkle tree based model of the protocol snapshot at a given block. + */ public class MerkleSnapshot implements state.Snapshot { private final Identifier rootBlockId; private final long rootBlockHeight; diff --git a/src/test/java/modules/ads/MerkleTreeTest.java b/src/test/java/modules/ads/MerkleTreeTest.java index 95a290dc..0b012ddc 100644 --- a/src/test/java/modules/ads/MerkleTreeTest.java +++ b/src/test/java/modules/ads/MerkleTreeTest.java @@ -6,6 +6,7 @@ import java.util.concurrent.atomic.AtomicInteger; import model.Entity; +import model.codec.EntityType; import model.crypto.Sha3256Hash; import model.lightchain.Identifier; import modules.ads.merkletree.MerkleProof; @@ -14,9 +15,7 @@ import modules.ads.merkletree.MerkleTreeAuthenticatedEntityVerifier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import unittest.fixtures.EntityFixture; -import unittest.fixtures.MerkleTreeFixture; -import unittest.fixtures.Sha3256HashFixture; +import unittest.fixtures.*; /** * Encapsulates tests for an authenticated and concurrent implementation of MerkleTree ADS. @@ -24,14 +23,89 @@ public class MerkleTreeTest { /** - * A basic test for one sequential put and get operations. + * Test putting and verifying a random entity in a random merkle tree. */ @Test - public void testVerification() { - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(5); - Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. + public void testVerificationNoArg() { + testVerification(null, null); + } + + /** + * Tests both putting and getting the same random entity gives same proof and putting + * another entity gives different proofs. + */ + @Test + public void testPutGetSameProofNoArg() { + testPutGetSameProof(null, null); + } + + /** + * Tests putting an existing entity does not change the proof with a random entity and merkle tree. + */ + @Test + public void testPutExistingEntityNoArg() { + testPutExistingEntity(null, null); + } + + /** + * Concurrently puts and gets entities and checks their proofs are correct (thread safety check). + */ + @Test + public void testConcurrentPutGetNoArg() { + testConcurrentPutGet(null, null); + } + + /** + * Tests getting an entity that does not exist in the merkle tree throws IllegalArgumentException + * with a random entity and merkle tree. + */ + @Test + public void testGetNonExistingEntityNoArg() { + testGetNonExistingEntity(null, null); + } + + /** + * Tests inserting null throws IllegalArgumentException with a random merkle tree. + */ + @Test + public void testNullInsertionNoArg() { + testNullInsertion(null); + } + + /** + * Tests the proof verification fails when root is changed with random entities and merkle tree. + */ + @Test + public void testManipulatedRootNoArg() { + testManipulatedRoot(null, null); + } + + /** + * Tests the proof verification fails when entity is changed with random entity and merkle tree. + */ + @Test + public void testManipulatedEntityNoArg() { + testManipulatedEntity(null, null); + } - Entity entity = new EntityFixture(); + /** + * Tests the proof verification fails when entity is changed with random entity and merkle tree. + */ + @Test + public void testManipulatedProofNoArg() { + testManipulatedProof(null, null); + } + + /** + * Generic function to test putting and verifying an entity in a merkle tree. + * + * @param entity the entity to put in the merkle tree + * @param merkleTree the merkle tree to put the entity in + */ + public static void testVerification(Entity entity, MerkleTree merkleTree) { + entity = entity != null ? entity : new EntityFixture(); + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); + Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. merkleTree.put(entity); Assertions.assertEquals(merkleTree.size(), 6); @@ -41,14 +115,16 @@ public void testVerification() { } /** - * Tests both putting and getting the same entity gives same proof + * Generic function to test both putting and getting the same entity gives same proof * and putting another entity gives different proofs. + * + * @param e1 the entity to put in the merkle tree + * @param merkleTree the merkle tree to put the entity in */ - @Test - public void testPutGetSameProof() { - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(5); + public static void testPutGetSameProof(Entity e1, MerkleTree merkleTree) { + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. - Entity e1 = new EntityFixture(); + e1 = e1 != null ? e1 : new EntityFixture(); // putting e1 AuthenticatedEntity authenticatedEntityPut = merkleTree.put(e1); @@ -75,13 +151,15 @@ public void testPutGetSameProof() { } /** - * Tests putting an existing entity does not change the proof. + * Generic function which tests putting an existing entity does not change the proof. + * + * @param entity the entity to put in the merkle tree + * @param merkleTree the merkle tree to put the entity in */ - @Test - public void testPutExistingEntity() { - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(5); + public static void testPutExistingEntity(Entity entity, MerkleTree merkleTree) { + entity = entity != null ? entity : new EntityFixture(); + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. - Entity entity = new EntityFixture(); // first time put AuthenticatedEntity authenticatedEntityPut = merkleTree.put(entity); @@ -98,10 +176,13 @@ public void testPutExistingEntity() { } /** - * Concurrently puts and gets entities and checks their proofs are correct (thread safety check). + * Generic function which concurrently puts and gets entities and checks their proofs are correct + * (thread safety check). + * + * @param type the type of entity which is put in the merkle tree + * @param merkleTree the merkle tree to put the entity in */ - @Test - public void testConcurrentPutGet() { + public static void testConcurrentPutGet(String type, MerkleTree merkleTree) { int concurrencyDegree = 100; ArrayList entities = new ArrayList<>(); ArrayList ids = new ArrayList<>(); @@ -113,11 +194,17 @@ public void testConcurrentPutGet() { Thread[] putThreads = new Thread[concurrencyDegree]; Thread[] getThreads = new Thread[concurrencyDegree]; - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(0); + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(0); + MerkleTree finalMerkleTree = merkleTree; Assertions.assertEquals(merkleTree.size(), 0); // fixture sanity check. for (int i = 0; i < concurrencyDegree; i++) { - Entity entity = new EntityFixture(); + Entity entity; + if (type == EntityType.TYPE_ACCOUNT) { + entity = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); + } else { + entity = new EntityFixture(); + } entities.add(entity); ids.add(entity.id()); } @@ -127,7 +214,7 @@ public void testConcurrentPutGet() { Entity entity = entities.get(i); putThreads[i] = new Thread(() -> { try { - merkleTree.put(entity); + finalMerkleTree.put(entity); putDone.countDown(); } catch (Exception e) { threadError.getAndIncrement(); @@ -149,7 +236,7 @@ public void testConcurrentPutGet() { Identifier id = ids.get(i); getThreads[i] = new Thread(() -> { try { - AuthenticatedEntity authenticatedEntity = merkleTree.get(id); + AuthenticatedEntity authenticatedEntity = finalMerkleTree.get(id); MerkleTreeAuthenticatedEntityVerifier verifier = new MerkleTreeAuthenticatedEntityVerifier(); if (!verifier.verify(authenticatedEntity)) { threadError.getAndIncrement(); @@ -174,48 +261,56 @@ public void testConcurrentPutGet() { } /** - * Tests getting an entity that does not exist in the merkle tree throws IllegalArgumentException. + * Generic function which tests getting an entity that does not exist in the merkle tree + * throws IllegalArgumentException. + * + * @param entity the entity to put in the merkle tree + * @param merkleTree the merkle tree to put the entity in */ - @Test - public void testGetNonExistingEntity() { - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(5); + public static void testGetNonExistingEntity(Entity entity, MerkleTree merkleTree) { + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. - Entity entity = new EntityFixture(); - + entity = entity != null ? entity : new EntityFixture(); + Entity finalEntity = entity; + MerkleTree finalMerkleTree = merkleTree; Assertions.assertThrows(IllegalArgumentException.class, () -> { - merkleTree.get(entity.id()); + finalMerkleTree.get(finalEntity.id()); }); } /** - * Tests inserting null throws IllegalArgumentException. + * Generic function which tests inserting null throws IllegalArgumentException. + * + * @param merkleTree the merkle tree to put null */ - @Test - public void testNullInsertion() { - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(5); + public static void testNullInsertion(MerkleTree merkleTree) { + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. + MerkleTree finalMerkleTree = merkleTree; Assertions.assertThrows(IllegalArgumentException.class, () -> { - merkleTree.put(null); + finalMerkleTree.put(null); }); } /** - * Tests the proof verification fails when root is changed. + * Generic function which tests the proof verification fails when root is changed. + * + * @param entity the entity to put in the merkle tree + * @param merkleTree the merkle tree to put the entity in */ - @Test - public void testManipulatedRoot() { - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(5); + public static void testManipulatedRoot(Entity entity, MerkleTree merkleTree) { + entity = entity != null ? entity : new EntityFixture(); + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. - Entity entity = new EntityFixture(); AuthenticatedEntity authenticatedEntity = merkleTree.put(entity); MembershipProof proof = authenticatedEntity.getMembershipProof(); // creates a tampered proof with random root. MerkleProof tamperedProof = new MerkleProof(proof.getPath(), new Sha3256Hash(new byte[32]), proof.getIsLeftNode()); AuthenticatedEntity tamperedAuthenticatedEntity = new MerkleTreeAuthenticatedEntity( - tamperedProof, - authenticatedEntity.type(), - authenticatedEntity.getEntity()); + tamperedProof, + authenticatedEntity.type(), + authenticatedEntity.getEntity()); MerkleTreeAuthenticatedEntityVerifier verifier = new MerkleTreeAuthenticatedEntityVerifier(); // authenticated entity must be verified. @@ -225,19 +320,21 @@ public void testManipulatedRoot() { } /** - * Tests the proof verification fails when entity is changed. + * Generic function which tests the proof verification fails when entity is changed. + * + * @param entity the entity to put in the merkle tree + * @param merkleTree the merkle tree to put the entity in */ - @Test - public void testManipulatedEntity() { - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(5); + public static void testManipulatedEntity(Entity entity, MerkleTree merkleTree) { + entity = entity != null ? entity : new EntityFixture(); + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. - Entity entity = new EntityFixture(); AuthenticatedEntity authenticatedEntity = merkleTree.put(entity); AuthenticatedEntity tamperedEntity = new MerkleTreeAuthenticatedEntity( - (MerkleProof) authenticatedEntity.getMembershipProof(), - authenticatedEntity.type(), - new EntityFixture()); + (MerkleProof) authenticatedEntity.getMembershipProof(), + authenticatedEntity.type(), + new EntityFixture()); MerkleTreeAuthenticatedEntityVerifier verifier = new MerkleTreeAuthenticatedEntityVerifier(); Assertions.assertTrue(verifier.verify(authenticatedEntity)); // original authenticated entity passes verification. @@ -245,14 +342,15 @@ public void testManipulatedEntity() { } /** - * Tests the proof fails verification when proof part of authenticated entity is changed. + * Generic function which tests the proof fails verification when proof part of authenticated entity is changed. + * + * @param entity the entity to put in the merkle tree + * @param merkleTree the merkle tree to put the entity in */ - @Test - public void testManipulatedProof() { - MerkleTree merkleTree = MerkleTreeFixture.createMerkleTree(5); + public static void testManipulatedProof(Entity entity, MerkleTree merkleTree) { + entity = entity != null ? entity : new EntityFixture(); + merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. - - Entity entity = new EntityFixture(); AuthenticatedEntity authenticatedEntity = merkleTree.put(entity); MembershipProof proof = authenticatedEntity.getMembershipProof(); diff --git a/src/test/java/state/merkle/MerkleSnapshotTest.java b/src/test/java/state/merkle/MerkleSnapshotTest.java index 64d307ce..b410bdb3 100644 --- a/src/test/java/state/merkle/MerkleSnapshotTest.java +++ b/src/test/java/state/merkle/MerkleSnapshotTest.java @@ -2,22 +2,24 @@ import java.util.ArrayList; +import model.codec.EntityType; import model.lightchain.Account; import model.lightchain.Identifier; +import modules.ads.MerkleTreeTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import protocol.Parameters; -import state.table.TableSnapshot; import unittest.fixtures.AccountFixture; import unittest.fixtures.IdentifierFixture; /** - * Encapsulates tests for hash table implementation of state snapshot. + * Encapsulates tests for Merkle Tree implementation of state snapshot. */ public class MerkleSnapshotTest { + /** - * Evaluates the correctness of getters in TableSnapshot. + * Evaluates the correctness of getters in MerkleSnapshot. */ @Test public void testMerkleSnapshot() { @@ -58,4 +60,84 @@ public void testMerkleSnapshot() { Assertions.assertTrue(merkleSnapshot.all().containsAll(accounts) && accounts.containsAll(merkleSnapshot.all())); } + + /** + * Tests putting and verifying an account in a merkle tree. + */ + @Test + public void testVerification() { + Account account = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); + MerkleTreeTest.testVerification(account, null); + } + + /** + * Tests both putting and getting the same account gives same proof and putting + * another account gives different proofs. + */ + @Test + public void testPutGetSameProof() { + Account account = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); + MerkleTreeTest.testPutGetSameProof(account, null); + } + + /** + * Tests putting an existing account does not change the proof. + */ + @Test + public void testPutExistingAccount() { + Account account = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); + MerkleTreeTest.testPutExistingEntity(account, null); + } + + /** + * Concurrently puts and gets accounts and checks their proofs are correct. + */ + @Test + public void testConcurrentPutGet() { + MerkleTreeTest.testConcurrentPutGet(EntityType.TYPE_ACCOUNT, null); + } + + /** + * Tests getting an account that does not exist in the merkle tree throws IllegalArgumentException. + */ + @Test + public void testGetNonExistingAccount() { + Account account = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); + MerkleTreeTest.testGetNonExistingEntity(account, null); + } + + /** + * Tests inserting null throws IllegalArgumentException. + */ + @Test + public void testNullInsertion() { + MerkleTreeTest.testNullInsertion(null); + } + + /** + * Tests the proof verification fails when root is changed. + */ + @Test + public void testManipulatedRoot() { + Account account = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); + MerkleTreeTest.testManipulatedRoot(account, null); + } + + /** + * Tests the proof verification fails when an account is changed. + */ + @Test + public void testManipulatedAccount() { + Account account = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); + MerkleTreeTest.testManipulatedEntity(account, null); + } + + /** + * Tests the proof fails verification when proof part of authenticated entity of an account is changed. + */ + @Test + public void testManipulatedProof() { + Account account = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); + MerkleTreeTest.testManipulatedProof(account, null); + } } From ea256ab00fd113023d518eb4b9ea5bb8bd6494fb Mon Sep 17 00:00:00 2001 From: Ozan Nacitarhan Date: Sun, 24 Apr 2022 20:19:44 +0300 Subject: [PATCH 4/5] improves a few lines of the code --- src/test/java/modules/ads/MerkleTreeTest.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/test/java/modules/ads/MerkleTreeTest.java b/src/test/java/modules/ads/MerkleTreeTest.java index 0b012ddc..ccc75080 100644 --- a/src/test/java/modules/ads/MerkleTreeTest.java +++ b/src/test/java/modules/ads/MerkleTreeTest.java @@ -200,7 +200,7 @@ public static void testConcurrentPutGet(String type, MerkleTree merkleTree) { for (int i = 0; i < concurrencyDegree; i++) { Entity entity; - if (type == EntityType.TYPE_ACCOUNT) { + if (type.equals(EntityType.TYPE_ACCOUNT)) { entity = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); } else { entity = new EntityFixture(); @@ -273,9 +273,7 @@ public static void testGetNonExistingEntity(Entity entity, MerkleTree merkleTree entity = entity != null ? entity : new EntityFixture(); Entity finalEntity = entity; MerkleTree finalMerkleTree = merkleTree; - Assertions.assertThrows(IllegalArgumentException.class, () -> { - finalMerkleTree.get(finalEntity.id()); - }); + Assertions.assertThrows(IllegalArgumentException.class, () -> finalMerkleTree.get(finalEntity.id())); } /** @@ -287,9 +285,7 @@ public static void testNullInsertion(MerkleTree merkleTree) { merkleTree = merkleTree != null ? merkleTree : MerkleTreeFixture.createMerkleTree(5); Assertions.assertEquals(merkleTree.size(), 5); // fixture sanity check. MerkleTree finalMerkleTree = merkleTree; - Assertions.assertThrows(IllegalArgumentException.class, () -> { - finalMerkleTree.put(null); - }); + Assertions.assertThrows(IllegalArgumentException.class, () -> finalMerkleTree.put(null)); } /** From 0b70e7e968250ea90668e24824653ce770ce86d8 Mon Sep 17 00:00:00 2001 From: Ozan Nacitarhan Date: Sun, 24 Apr 2022 22:50:07 +0300 Subject: [PATCH 5/5] fixes spotbugs a issue --- src/test/java/modules/ads/MerkleTreeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/modules/ads/MerkleTreeTest.java b/src/test/java/modules/ads/MerkleTreeTest.java index ccc75080..19d98eef 100644 --- a/src/test/java/modules/ads/MerkleTreeTest.java +++ b/src/test/java/modules/ads/MerkleTreeTest.java @@ -200,7 +200,7 @@ public static void testConcurrentPutGet(String type, MerkleTree merkleTree) { for (int i = 0; i < concurrencyDegree; i++) { Entity entity; - if (type.equals(EntityType.TYPE_ACCOUNT)) { + if (type == EntityType.TYPE_ACCOUNT) { entity = AccountFixture.newAccount(IdentifierFixture.newIdentifier()); } else { entity = new EntityFixture();