diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiAccount.java index a9f85cce9ee..d9f515b81f1 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiAccount.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiAccount.java @@ -173,6 +173,19 @@ public String toString() { + '}'; } + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final BonsaiAccount that = (BonsaiAccount) o; + return super.equals(o) && storageRoot.equals(that.storageRoot); + } + + @Override + public int hashCode() { + return Objects.hash(nonce, balance, storageRoot, codeHash); + } + /** * Throws an exception if the two accounts represent different stored states * diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedAccount.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedAccount.java index 2b81ebfbab1..599edd5f42f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedAccount.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/common/DiffBasedAccount.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Objects; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -226,4 +227,17 @@ public String toString() { + codeHash + '}'; } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final DiffBasedAccount that = (DiffBasedAccount) o; + return nonce == that.nonce && balance.equals(that.balance) && codeHash.equals(that.codeHash); + } + + @Override + public int hashCode() { + return Objects.hash(nonce, balance, codeHash); + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiAccountTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiAccountTest.java index 53cf9e1ca12..2289c97e1d7 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiAccountTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/trie/diffbased/bonsai/BonsaiAccountTest.java @@ -30,6 +30,7 @@ public class BonsaiAccountTest { @Mock BonsaiWorldState bonsaiWorldState; + @Mock BonsaiWorldState bonsaiWorldStateAlternate; @Test void shouldCopyTrackedBonsaiAccountCorrectly() { @@ -71,4 +72,171 @@ void shouldCopyBonsaiAccountCorrectly() { assertThat(new BonsaiAccount(account, bonsaiWorldState, true)) .isEqualToComparingFieldByField(account); } + + @Test + void shouldBeEqualIfAttributesMatch() { + // Basic equality test + final BonsaiAccount account1 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 0, + Wei.ONE, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY, + true); + final BonsaiAccount account2 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 0, + Wei.ONE, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY, + true); + assertThat(account1.equals(account2)).isTrue(); + + // Another equality test + final BonsaiAccount account3 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.of(UInt256.fromHexString("0x123")), + Hash.hash(Bytes.fromHexString("0xabcdef")), + Hash.hash(Bytes.fromHexString("0x9876")), + true); + final BonsaiAccount account4 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.of(UInt256.fromHexString("0x123")), + Hash.hash(Bytes.fromHexString("0xabcdef")), + Hash.hash(Bytes.fromHexString("0x9876")), + true); + assertThat(account3.equals(account4)).isTrue(); + + // Context and mutability should not affect equality + final BonsaiAccount account5 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 0, + Wei.ONE, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY, + true); + final BonsaiAccount account6 = + new BonsaiAccount( + bonsaiWorldStateAlternate, + Address.ZERO, + Hash.hash(Address.ZERO), + 0, + Wei.ONE, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY, + false); + assertThat(account5.equals(account6)).isTrue(); + } + + @Test + void shouldBeUnEqualIfAnyAttributesDiffer() { + // Nonce + final BonsaiAccount account1 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.ONE, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY, + true); + final BonsaiAccount account2 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 100, + Wei.ONE, + Hash.EMPTY_TRIE_HASH, + Hash.EMPTY, + true); + assertThat(account1.equals(account2)).isFalse(); + + // Balance + final BonsaiAccount account3 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.of(UInt256.fromHexString("0x123")), + Hash.hash(Bytes.fromHexString("0xabcdef")), + Hash.hash(Bytes.fromHexString("0x9876")), + true); + final BonsaiAccount account4 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.of(UInt256.fromHexString("0x456")), + Hash.hash(Bytes.fromHexString("0xabcdef")), + Hash.hash(Bytes.fromHexString("0x9876")), + true); + assertThat(account3.equals(account4)).isFalse(); + + // Storage root + final BonsaiAccount account5 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.of(UInt256.fromHexString("0x123")), + Hash.hash(Bytes.fromHexString("0xabcdef")), + Hash.hash(Bytes.fromHexString("0x9876")), + true); + final BonsaiAccount account6 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.of(UInt256.fromHexString("0x456")), + Hash.hash(Bytes.fromHexString("0xfedcba")), + Hash.hash(Bytes.fromHexString("0x9876")), + true); + assertThat(account5.equals(account6)).isFalse(); + + // Code hash + final BonsaiAccount account7 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.of(UInt256.fromHexString("0x123")), + Hash.hash(Bytes.fromHexString("0xabcdef")), + Hash.hash(Bytes.fromHexString("0x6789")), + true); + final BonsaiAccount account8 = + new BonsaiAccount( + bonsaiWorldState, + Address.ZERO, + Hash.hash(Address.ZERO), + 99, + Wei.of(UInt256.fromHexString("0x456")), + Hash.hash(Bytes.fromHexString("0xabcdef")), + Hash.hash(Bytes.fromHexString("0x9876")), + true); + assertThat(account7.equals(account8)).isFalse(); + } }