diff --git a/ethereumj-core/src/main/java/org/ethereum/trie/TrieImpl.java b/ethereumj-core/src/main/java/org/ethereum/trie/TrieImpl.java index 6e7cf38f9a..1050b852c1 100644 --- a/ethereumj-core/src/main/java/org/ethereum/trie/TrieImpl.java +++ b/ethereumj-core/src/main/java/org/ethereum/trie/TrieImpl.java @@ -93,6 +93,7 @@ public Node(byte[] hashOrRlp) { private Node(RLP.LList parsedRlp) { this.parsedRlp = parsedRlp; + this.rlp = parsedRlp.getEncoded(); } private Node(Object[] children) { diff --git a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java index dd793ccb71..7f4c1f28c2 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/RLP.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/RLP.java @@ -710,6 +710,14 @@ public LList(byte[] rlp) { this.rlp = rlp; } + public byte[] getEncoded() { + byte encoded[][] = new byte[cnt][]; + for (int i = 0; i < cnt; i++) { + encoded[i] = encodeElement(getBytes(i)); + } + return encodeList(encoded); + } + public void add(int off, int len, boolean isList) { offsets[cnt] = off; lens[cnt] = isList ? (-1 - len) : len; diff --git a/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java b/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java index faf3dfdf11..1a9be3cfca 100644 --- a/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/trie/TrieTest.java @@ -1078,6 +1078,34 @@ public void testBugFix() throws ParseException, IOException, URISyntaxException Hex.decode("00000000000000000000000000000000000000000000002f0000000000000000")); } + @Test + public void testBugFix2() throws ParseException, IOException, URISyntaxException { + + Source src = new HashMapDB<>(); + + // Create trie: root -> BranchNode (..., NodeValue (less than 32 bytes), ...) + TrieImpl trie = new TrieImpl(src); + trie.put(Hex.decode("0000000000000000000000000000000000000000000000000000000000000011"), Hex.decode("11")); + trie.put(Hex.decode("0000000000000000000000000000000000000000000000000000000000000022"), Hex.decode("22")); + trie.flush(); + + // Reset trie to refresh the nodes + trie = new TrieImpl(src, trie.getRootHash()); + + // Update trie: root -> dirty BranchNode (..., NodeValue (less than 32 bytes), ..., dirty NodeValue, ...) + trie.put(Hex.decode("0000000000000000000000000000000000000000000000000000000000000033"), Hex.decode("33")); + + // BUG: + // In that case NodeValue (encoded as plain RLP list) isn't dirty + // while both rlp and hash fields are null, Node has been initialized with parsedRLP only + // Therefore any subsequent call to BranchNode.encode() fails with NPE + + // FIX: + // Supply Node initialization with raw rlp value + + assertEquals("36e350d9a1d9c02d5bc4539a05e51890784ea5d2b675a0b26725dbbdadb4d6e2", Hex.toHexString(trie.getRootHash())); + } + @Ignore @Test public void perfTestGet() {