/*
* Copyright (c) [2016] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.trie;
import org.ethereum.core.AccountState;
import org.ethereum.crypto.HashUtil;
import org.ethereum.datasource.*;
import org.ethereum.datasource.inmem.HashMapDB;
import org.ethereum.datasource.inmem.HashMapDBSimple;
import org.ethereum.util.Value;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;
import static org.ethereum.crypto.HashUtil.sha3;
import static org.ethereum.util.ByteUtil.intToBytes;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
public class TrieTest {
private static final Logger logger = LoggerFactory.getLogger("test");
private static String LONG_STRING = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ";
private static String ROOT_HASH_EMPTY = Hex.toHexString(EMPTY_TRIE_HASH);
private static String c = "c";
private static String ca = "ca";
private static String cat = "cat";
private static String dog = "dog";
private static String doge = "doge";
private static String test = "test";
private static String dude = "dude";
public class NoDoubleDeleteMapDB extends HashMapDB<byte[]> {
@Override
public synchronized void delete(byte[] key) {
if (storage.get(key) == null) {
throw new RuntimeException("Trying delete non-existing entry: " + Hex.toHexString(key));
}
super.delete(key);
}
public NoDoubleDeleteMapDB getDb() {return this;}
};
public class TrieCache extends SourceCodec<byte[], Value, byte[], byte[]> {
public TrieCache() {
super(new NoDoubleDeleteMapDB(), new Serializers.Identity<byte[]>(), Serializers.TrieNodeSerializer);
}
public NoDoubleDeleteMapDB getDb() {return (NoDoubleDeleteMapDB) getSource();}
}
// public TrieCache mockDb = new TrieCache();
// public TrieCache mockDb_2 = new TrieCache();
public NoDoubleDeleteMapDB mockDb = new NoDoubleDeleteMapDB();
public NoDoubleDeleteMapDB mockDb_2 = new NoDoubleDeleteMapDB();
// ROOT: [ '\x16', A ]
// A: [ '', '', '', '', B, '', '', '', C, '', '', '', '', '', '', '', '' ]
// B: [ '\x00\x6f', D ]
// D: [ '', '', '', '', '', '', E, '', '', '', '', '', '', '', '', '', 'verb' ]
// E: [ '\x17', F ]
// F: [ '', '', '', '', '', '', G, '', '', '', '', '', '', '', '', '', 'puppy' ]
// G: [ '\x35', 'coin' ]
// C: [ '\x20\x6f\x72\x73\x65', 'stallion' ]
@After
public void closeMockDb() throws IOException {
}
private static Serializer<String, byte[]> STR_SERIALIZER = new Serializer<String, byte[]>() {
public byte[] serialize(String object) {return object == null ? null : object.getBytes();}
public String deserialize(byte[] stream) {return stream == null ? null : new String(stream);}
};
// private static class StringTrie extends SourceCodec<String, String, byte[], byte[]> {
// public StringTrie(Source<byte[], Value> src) {
// this(src, null);
// }
// public StringTrie(Source<byte[], Value> src, byte[] root) {
// super(new TrieImpl(new NoDeleteSource<>(src), root), STR_SERIALIZER, STR_SERIALIZER);
// }
//
// public byte[] getRootHash() {
// return ((TrieImpl) getSource()).getRootHash();
// }
//
// public String getTrieDump() {
// return ((TrieImpl) getSource()).getTrieDump();
// }
//
// @Override
// public boolean equals(Object obj) {
// return getSource().equals(((StringTrie) obj).getSource());
// }
// }
private static class StringTrie extends SourceCodec<String, String, byte[], byte[]> {
public StringTrie(Source<byte[], byte[]> src) {
this(src, null);
}
public StringTrie(Source<byte[], byte[]> src, byte[] root) {
super(new TrieImpl(new NoDeleteSource<>(src), root), STR_SERIALIZER, STR_SERIALIZER);
}
public byte[] getRootHash() {
return ((TrieImpl) getSource()).getRootHash();
}
public String getTrieDump() {
return ((TrieImpl) getSource()).dumpTrie();
}
public String dumpStructure() {
return ((TrieImpl) getSource()).dumpStructure();
}
@Override
public String get(String s) {
String ret = super.get(s);
return ret == null ? "" : ret;
}
@Override
public void put(String s, String val) {
if (val == null || val.isEmpty()) {
super.delete(s);
} else {
super.put(s, val);
}
}
@Override
public boolean equals(Object obj) {
return getSource().equals(((StringTrie) obj).getSource());
}
}
@Test
public void testEmptyKey() {
Source<String, String> trie = new StringTrie(mockDb, null);
trie.put("", dog);
assertEquals(dog, new String(trie.get("")));
}
@Test
public void testInsertShortString() {
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, dog);
assertEquals(dog, new String(trie.get(cat)));
}
@Test
public void testInsertLongString() {
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(cat)));
}
@Test
public void testInsertMultipleItems1() {
StringTrie trie = new StringTrie(mockDb);
trie.put(ca, dude);
assertEquals(dude, new String(trie.get(ca)));
trie.put(cat, dog);
assertEquals(dog, new String(trie.get(cat)));
trie.put(dog, test);
assertEquals(test, new String(trie.get(dog)));
trie.put(doge, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(doge)));
trie.put(test, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(test)));
// Test if everything is still there
assertEquals(dude, new String(trie.get(ca)));
assertEquals(dog, new String(trie.get(cat)));
assertEquals(test, new String(trie.get(dog)));
assertEquals(LONG_STRING, new String(trie.get(doge)));
assertEquals(LONG_STRING, new String(trie.get(test)));
}
@Test
public void testInsertMultipleItems2() {
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, dog);
assertEquals(dog, trie.get(cat));
System.out.println(trie.getTrieDump());
trie.put(ca, dude);
assertEquals(dude, trie.get(ca));
System.out.println(trie.getTrieDump());
trie.put(doge, LONG_STRING);
assertEquals(LONG_STRING, trie.get(doge));
System.out.println(trie.getTrieDump());
trie.put(dog, test);
assertEquals(test, trie.get(dog));
trie.put(test, LONG_STRING);
assertEquals(LONG_STRING, trie.get(test));
// Test if everything is still there
assertEquals(dog, trie.get(cat));
assertEquals(dude, trie.get(ca));
assertEquals(LONG_STRING, trie.get(doge));
assertEquals(test, trie.get(dog));
assertEquals(LONG_STRING, trie.get(test));
System.out.println(trie.getTrieDump());
TrieImpl trieNew = new TrieImpl(mockDb.getDb(), trie.getRootHash());
assertEquals(dog, new String(trieNew.get(cat.getBytes())));
assertEquals(dude, new String(trieNew.get(ca.getBytes())));
assertEquals(LONG_STRING, new String(trieNew.get(doge.getBytes())));
assertEquals(test, new String(trieNew.get(dog.getBytes())));
assertEquals(LONG_STRING, new String(trieNew.get(test.getBytes())));
}
@Test
public void newImplTest() {
HashMapDBSimple<byte[]> db = new HashMapDBSimple<>();
TrieImpl btrie = new TrieImpl(db, null);
Source<String, String> trie = new SourceCodec<>(btrie, STR_SERIALIZER, STR_SERIALIZER);
trie.put("cat", "dog");
trie.put("ca", "dude");
assertEquals(trie.get("cat"), "dog");
assertEquals(trie.get("ca"), "dude");
trie.put(doge, LONG_STRING);
System.out.println(btrie.dumpStructure());
System.out.println(btrie.dumpTrie());
assertEquals(LONG_STRING, trie.get(doge));
assertEquals(dog, trie.get(cat));
trie.put(dog, test);
assertEquals(test, trie.get(dog));
assertEquals(dog, trie.get(cat));
trie.put(test, LONG_STRING);
assertEquals(LONG_STRING, trie.get(test));
System.out.println(btrie.dumpStructure());
System.out.println(btrie.dumpTrie());
assertEquals(dog, trie.get(cat));
assertEquals(dude, trie.get(ca));
assertEquals(LONG_STRING, trie.get(doge));
assertEquals(test, trie.get(dog));
assertEquals(LONG_STRING, trie.get(test));
}
@Test
public void testUpdateShortToShortString() {
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, dog);
assertEquals(dog, new String(trie.get(cat)));
trie.put(cat, dog + "1");
assertEquals(dog + "1", new String(trie.get(cat)));
}
@Test
public void testUpdateLongToLongString() {
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(cat)));
trie.put(cat, LONG_STRING + "1");
assertEquals(LONG_STRING + "1", new String(trie.get(cat)));
}
@Test
public void testUpdateShortToLongString() {
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, dog);
assertEquals(dog, new String(trie.get(cat)));
trie.put(cat, LONG_STRING + "1");
assertEquals(LONG_STRING + "1", new String(trie.get(cat)));
}
@Test
public void testUpdateLongToShortString() {
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(cat)));
trie.put(cat, dog + "1");
assertEquals(dog + "1", new String(trie.get(cat)));
}
@Test
public void testDeleteShortString1() {
String ROOT_HASH_BEFORE = "a9539c810cc2e8fa20785bdd78ec36cc1dab4b41f0d531e80a5e5fd25c3037ee";
String ROOT_HASH_AFTER = "fc5120b4a711bca1f5bb54769525b11b3fb9a8d6ac0b8bf08cbb248770521758";
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, dog);
assertEquals(dog, new String(trie.get(cat)));
trie.put(ca, dude);
assertEquals(dude, new String(trie.get(ca)));
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
trie.delete(ca);
assertEquals("", new String(trie.get(ca)));
assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testDeleteShortString2() {
String ROOT_HASH_BEFORE = "a9539c810cc2e8fa20785bdd78ec36cc1dab4b41f0d531e80a5e5fd25c3037ee";
String ROOT_HASH_AFTER = "b25e1b5be78dbadf6c4e817c6d170bbb47e9916f8f6cc4607c5f3819ce98497b";
StringTrie trie = new StringTrie(mockDb);
trie.put(ca, dude);
assertEquals(dude, new String(trie.get(ca)));
trie.put(cat, dog);
assertEquals(dog, new String(trie.get(cat)));
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
trie.delete(cat);
assertEquals("", new String(trie.get(cat)));
assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testDeleteShortString3() {
String ROOT_HASH_BEFORE = "778ab82a7e8236ea2ff7bb9cfa46688e7241c1fd445bf2941416881a6ee192eb";
String ROOT_HASH_AFTER = "05875807b8f3e735188d2479add82f96dee4db5aff00dc63f07a7e27d0deab65";
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, dude);
assertEquals(dude, new String(trie.get(cat)));
trie.put(dog, test);
assertEquals(test, new String(trie.get(dog)));
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
trie.delete(dog);
assertEquals("", new String(trie.get(dog)));
assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testDeleteLongString1() {
String ROOT_HASH_BEFORE = "318961a1c8f3724286e8e80d312352f01450bc4892c165cc7614e1c2e5a0012a";
String ROOT_HASH_AFTER = "63356ecf33b083e244122fca7a9b128cc7620d438d5d62e4f8b5168f1fb0527b";
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(cat)));
trie.put(dog, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(dog)));
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
trie.delete(dog);
assertEquals("", new String(trie.get(dog)));
assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testDeleteLongString2() {
String ROOT_HASH_BEFORE = "e020de34ca26f8d373ff2c0a8ac3a4cb9032bfa7a194c68330b7ac3584a1d388";
String ROOT_HASH_AFTER = "334511f0c4897677b782d13a6fa1e58e18de6b24879d57ced430bad5ac831cb2";
StringTrie trie = new StringTrie(mockDb);
trie.put(ca, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(ca)));
trie.put(cat, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(cat)));
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
trie.delete(cat);
assertEquals("", new String(trie.get(cat)));
assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testDeleteLongString3() {
String ROOT_HASH_BEFORE = "e020de34ca26f8d373ff2c0a8ac3a4cb9032bfa7a194c68330b7ac3584a1d388";
String ROOT_HASH_AFTER = "63356ecf33b083e244122fca7a9b128cc7620d438d5d62e4f8b5168f1fb0527b";
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(cat)));
trie.put(ca, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(ca)));
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
trie.delete(ca);
assertEquals("", new String(trie.get(ca)));
assertEquals(ROOT_HASH_AFTER, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testDeleteCompletellyDiferentItems() {
TrieImpl trie = new TrieImpl(mockDb);
String val_1 = "1000000000000000000000000000000000000000000000000000000000000000";
String val_2 = "2000000000000000000000000000000000000000000000000000000000000000";
String val_3 = "3000000000000000000000000000000000000000000000000000000000000000";
trie.put(Hex.decode(val_1), Hex.decode(val_1));
trie.put(Hex.decode(val_2), Hex.decode(val_2));
String root1 = Hex.toHexString(trie.getRootHash());
trie.put(Hex.decode(val_3), Hex.decode(val_3));
trie.delete(Hex.decode(val_3));
String root1_ = Hex.toHexString(trie.getRootHash());
Assert.assertEquals(root1, root1_);
}
@Test
public void testDeleteMultipleItems1() {
String ROOT_HASH_BEFORE = "3a784eddf1936515f0313b073f99e3bd65c38689021d24855f62a9601ea41717";
String ROOT_HASH_AFTER1 = "60a2e75cfa153c4af2783bd6cb48fd6bed84c6381bc2c8f02792c046b46c0653";
String ROOT_HASH_AFTER2 = "a84739b4762ddf15e3acc4e6957e5ab2bbfaaef00fe9d436a7369c6f058ec90d";
StringTrie trie = new StringTrie(mockDb);
trie.put(cat, dog);
assertEquals(dog, new String(trie.get(cat)));
trie.put(ca, dude);
assertEquals(dude, new String(trie.get(ca)));
trie.put(doge, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(doge)));
trie.put(dog, test);
assertEquals(test, new String(trie.get(dog)));
trie.put(test, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(test)));
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
System.out.println(trie.dumpStructure());
trie.delete(dog);
System.out.println(trie.dumpStructure());
assertEquals("", trie.get(dog));
assertEquals(ROOT_HASH_AFTER1, Hex.toHexString(trie.getRootHash()));
trie.delete(test);
System.out.println(trie.dumpStructure());
assertEquals("", trie.get(test));
assertEquals(ROOT_HASH_AFTER2, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testDeleteMultipleItems2() {
String ROOT_HASH_BEFORE = "cf1ed2b6c4b6558f70ef0ecf76bfbee96af785cb5d5e7bfc37f9804ad8d0fb56";
String ROOT_HASH_AFTER1 = "f586af4a476ba853fca8cea1fbde27cd17d537d18f64269fe09b02aa7fe55a9e";
String ROOT_HASH_AFTER2 = "c59fdc16a80b11cc2f7a8b107bb0c954c0d8059e49c760ec3660eea64053ac91";
StringTrie trie = new StringTrie(mockDb);
trie.put(c, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(c)));
trie.put(ca, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(ca)));
trie.put(cat, LONG_STRING);
assertEquals(LONG_STRING, new String(trie.get(cat)));
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
trie.delete(ca);
assertEquals("", new String(trie.get(ca)));
assertEquals(ROOT_HASH_AFTER1, Hex.toHexString(trie.getRootHash()));
trie.delete(cat);
assertEquals("", new String(trie.get(cat)));
assertEquals(ROOT_HASH_AFTER2, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testMassiveDelete() {
TrieImpl trie = new TrieImpl(mockDb);
byte[] rootHash1 = null;
for (int i = 0; i < 11000; i++) {
trie.put(HashUtil.sha3(intToBytes(i)), HashUtil.sha3(intToBytes(i + 1000000)));
if (i == 10000) {
rootHash1 = trie.getRootHash();
}
}
for (int i = 10001; i < 11000; i++) {
trie.delete(HashUtil.sha3(intToBytes(i)));
}
byte[] rootHash2 = trie.getRootHash();
assertArrayEquals(rootHash1, rootHash2);
}
@Test
public void testDeleteAll() {
String ROOT_HASH_BEFORE = "a84739b4762ddf15e3acc4e6957e5ab2bbfaaef00fe9d436a7369c6f058ec90d";
StringTrie trie = new StringTrie(mockDb);
assertEquals(ROOT_HASH_EMPTY, Hex.toHexString(trie.getRootHash()));
trie.put(ca, dude);
trie.put(cat, dog);
trie.put(doge, LONG_STRING);
assertEquals(ROOT_HASH_BEFORE, Hex.toHexString(trie.getRootHash()));
trie.delete(ca);
trie.delete(cat);
trie.delete(doge);
assertEquals(ROOT_HASH_EMPTY, Hex.toHexString(trie.getRootHash()));
}
@Test
public void testTrieEquals() {
StringTrie trie1 = new StringTrie(mockDb);
StringTrie trie2 = new StringTrie(mockDb);
trie1.put(doge, LONG_STRING);
trie2.put(doge, LONG_STRING);
assertTrue("Expected tries to be equal", trie1.equals(trie2));
assertEquals(Hex.toHexString(trie1.getRootHash()), Hex.toHexString(trie2.getRootHash()));
trie1.put(dog, LONG_STRING);
trie2.put(cat, LONG_STRING);
assertFalse("Expected tries not to be equal", trie1.equals(trie2));
assertNotEquals(Hex.toHexString(trie1.getRootHash()), Hex.toHexString(trie2.getRootHash()));
}
// Using tests from: https://github.com/ethereum/tests/blob/master/trietest.json
@Test
public void testSingleItem() {
StringTrie trie = new StringTrie(mockDb);
trie.put("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
assertEquals("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab", Hex.toHexString(trie.getRootHash()));
}
@Test
public void testDogs() {
StringTrie trie = new StringTrie(mockDb);
trie.put("doe", "reindeer");
assertEquals("11a0327cfcc5b7689b6b6d727e1f5f8846c1137caaa9fc871ba31b7cce1b703e", Hex.toHexString(trie.getRootHash()));
trie.put("dog", "puppy");
assertEquals("05ae693aac2107336a79309e0c60b24a7aac6aa3edecaef593921500d33c63c4", Hex.toHexString(trie.getRootHash()));
trie.put("dogglesworth", "cat");
assertEquals("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3", Hex.toHexString(trie.getRootHash()));
}
@Test
public void testPuppy() {
StringTrie trie = new StringTrie(mockDb);
trie.put("do", "verb");
trie.put("doge", "coin");
trie.put("horse", "stallion");
trie.put("dog", "puppy");
assertEquals("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84", Hex.toHexString(trie.getRootHash()));
}
@Test
public void testEmptyValues() {
StringTrie trie = new StringTrie(mockDb);
trie.put("do", "verb");
trie.put("ether", "wookiedoo");
trie.put("horse", "stallion");
trie.put("shaman", "horse");
trie.put("doge", "coin");
trie.put("ether", "");
trie.put("dog", "puppy");
trie.put("shaman", "");
assertEquals("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84", Hex.toHexString(trie.getRootHash()));
}
@Test
public void testFoo() {
StringTrie trie = new StringTrie(mockDb);
trie.put("foo", "bar");
trie.put("food", "bat");
trie.put("food", "bass");
assertEquals("17beaa1648bafa633cda809c90c04af50fc8aed3cb40d16efbddee6fdf63c4c3", Hex.toHexString(trie.getRootHash()));
}
@Test
public void testSmallValues() {
StringTrie trie = new StringTrie(mockDb);
trie.put("be", "e");
trie.put("dog", "puppy");
trie.put("bed", "d");
assertEquals("3f67c7a47520f79faa29255d2d3c084a7a6df0453116ed7232ff10277a8be68b", Hex.toHexString(trie.getRootHash()));
}
@Test
public void testTesty() {
StringTrie trie = new StringTrie(mockDb);
trie.put("test", "test");
assertEquals("85d106d4edff3b7a4889e91251d0a87d7c17a1dda648ebdba8c6060825be23b8", Hex.toHexString(trie.getRootHash()));
trie.put("te", "testy");
assertEquals("8452568af70d8d140f58d941338542f645fcca50094b20f3c3d8c3df49337928", Hex.toHexString(trie.getRootHash()));
}
private final String randomDictionary = "spinneries, archipenko, prepotency, herniotomy, preexpress, relaxative, insolvably, debonnaire, apophysate, virtuality, cavalryman, utilizable, diagenesis, vitascopic, governessy, abranchial, cyanogenic, gratulated, signalment, predicable, subquality, crystalize, prosaicism, oenologist, repressive, impanelled, cockneyism, bordelaise, compigne, konstantin, predicated, unsublimed, hydrophane, phycomyces, capitalise, slippingly, untithable, unburnable, deoxidizer, misteacher, precorrect, disclaimer, solidified, neuraxitis, caravaning, betelgeuse, underprice, uninclosed, acrogynous, reirrigate, dazzlingly, chaffiness, corybantes, intumesced, intentness, superexert, abstrusely, astounding, pilgrimage, posttarsal, prayerless, nomologist, semibelted, frithstool, unstinging, ecalcarate, amputating, megascopic, graphalloy, platteland, adjacently, mingrelian, valentinus, appendical, unaccurate, coriaceous, waterworks, sympathize, doorkeeper, overguilty, flaggingly, admonitory, aeriferous, normocytic, parnellism, catafalque, odontiasis, apprentice, adulterous, mechanisma, wilderness, undivorced, reinterred, effleurage, pretrochal, phytogenic, swirlingly, herbarized, unresolved, classifier, diosmosing, microphage, consecrate, astarboard, predefying, predriving, lettergram, ungranular, overdozing, conferring, unfavorite, peacockish, coinciding, erythraeum, freeholder, zygophoric, imbitterer, centroidal, appendixes, grayfishes, enological, indiscreet, broadcloth, divulgated, anglophobe, stoopingly, bibliophil, laryngitis, separatist, estivating, bellarmine, greasiness, typhlology, xanthation, mortifying, endeavorer, aviatrices, unequalise, metastatic, leftwinger, apologizer, quatrefoil, nonfouling, bitartrate, outchiding, undeported, poussetted, haemolysis, asantehene, montgomery, unjoinable, cedarhurst, unfastener, nonvacuums, beauregard, animalized, polyphides, cannizzaro, gelatinoid, apologised, unscripted, tracheidal, subdiscoid, gravelling, variegated, interabang, inoperable, immortelle, laestrygon, duplicatus, proscience, deoxidised, manfulness, channelize, nondefense, ectomorphy, unimpelled, headwaiter, hexaemeric, derivation, prelexical, limitarian, nonionized, prorefugee, invariably, patronizer, paraplegia, redivision, occupative, unfaceable, hypomnesia, psalterium, doctorfish, gentlefolk, overrefine, heptastich, desirously, clarabelle, uneuphonic, autotelism, firewarden, timberjack, fumigation, drainpipes, spathulate, novelvelle, bicorporal, grisliness, unhesitant, supergiant, unpatented, womanpower, toastiness, multichord, paramnesia, undertrick, contrarily, neurogenic, gunmanship, settlement, brookville, gradualism, unossified, villanovan, ecospecies, organising, buckhannon, prefulfill, johnsonese, unforegone, unwrathful, dunderhead, erceldoune, unwadeable, refunction, understuff, swaggering, freckliest, telemachus, groundsill, outslidden, bolsheviks, recognizer, hemangioma, tarantella, muhammedan, talebearer, relocation, preemption, chachalaca, septuagint, ubiquitous, plexiglass, humoresque, biliverdin, tetraploid, capitoline, summerwood, undilating, undetested, meningitic, petrolatum, phytotoxic, adiphenine, flashlight, protectory, inwreathed, rawishness, tendrillar, hastefully, bananaquit, anarthrous, unbedimmed, herborized, decenniums, deprecated, karyotypic, squalidity, pomiferous, petroglyph, actinomere, peninsular, trigonally, androgenic, resistance, unassuming, frithstool, documental, eunuchised, interphone, thymbraeus, confirmand, expurgated, vegetation, myographic, plasmagene, spindrying, unlackeyed, foreknower, mythically, albescence, rebudgeted, implicitly, unmonastic, torricelli, mortarless, labialized, phenacaine, radiometry, sluggishly, understood, wiretapper, jacobitely, unbetrayed, stadholder, directress, emissaries, corelation, sensualize, uncurbable, permillage, tentacular, thriftless, demoralize, preimagine, iconoclast, acrobatism, firewarden, transpired, bluethroat, wanderjahr, groundable, pedestrian, unulcerous, preearthly, freelanced, sculleries, avengingly, visigothic, preharmony, bressummer, acceptable, unfoolable, predivider, overseeing, arcosolium, piriformis, needlecord, homebodies, sulphation, phantasmic, unsensible, unpackaged, isopiestic, cytophagic, butterlike, frizzliest, winklehawk, necrophile, mesothorax, cuchulainn, unrentable, untangible, unshifting, unfeasible, poetastric, extermined, gaillardia, nonpendent, harborside, pigsticker, infanthood, underrower, easterling, jockeyship, housebreak, horologium, undepicted, dysacousma, incurrable, editorship, unrelented, peritricha, interchaff, frothiness, underplant, proafrican, squareness, enigmatise, reconciled, nonnumeral, nonevident, hamantasch, victualing, watercolor, schrdinger, understand, butlerlike, hemiglobin, yankeeland";
@Test
public void testMasiveUpdate() {
boolean massiveUpdateTestEnabled = false;
if (massiveUpdateTestEnabled) {
List<String> randomWords = Arrays.asList(randomDictionary.split(","));
HashMap<String, String> testerMap = new HashMap<>();
StringTrie trie = new StringTrie(mockDb);
Random generator = new Random();
// Random insertion
for (int i = 0; i < 100000; ++i) {
int randomIndex1 = generator.nextInt(randomWords.size());
int randomIndex2 = generator.nextInt(randomWords.size());
String word1 = randomWords.get(randomIndex1).trim();
String word2 = randomWords.get(randomIndex2).trim();
trie.put(word1, word2);
testerMap.put(word1, word2);
}
int half = testerMap.size() / 2;
for (int r = 0; r < half; ++r) {
int randomIndex = generator.nextInt(randomWords.size());
String word1 = randomWords.get(randomIndex).trim();
testerMap.remove(word1);
trie.delete(word1);
}
// Assert the result now
Iterator<String> keys = testerMap.keySet().iterator();
while (keys.hasNext()) {
String mapWord1 = keys.next();
String mapWord2 = testerMap.get(mapWord1);
String treeWord2 = new String(trie.get(mapWord1));
Assert.assertEquals(mapWord2, treeWord2);
}
}
}
@Ignore
@Test
public void testMasiveDetermenisticUpdate() throws IOException, URISyntaxException {
// should be root: cfd77c0fcb037adefce1f4e2eb94381456a4746379d2896bb8f309c620436d30
URL massiveUpload_1 = ClassLoader
.getSystemResource("trie/massive-upload.dmp");
File file = new File(massiveUpload_1.toURI());
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
// *** Part - 1 ***
// 1. load the data from massive-upload.dmp
// which includes deletes/upadtes (5000 operations)
StringTrie trieSingle = new StringTrie(mockDb_2);
for (String aStrData : strData) {
String[] keyVal = aStrData.split("=");
if (keyVal[0].equals("*"))
trieSingle.delete(keyVal[1].trim());
else
trieSingle.put(keyVal[0].trim(), keyVal[1].trim());
}
System.out.println("root_1: => " + Hex.toHexString(trieSingle.getRootHash()));
// *** Part - 2 ***
// pre. we use the same data from massive-upload.dmp
// which includes deletes/upadtes (100000 operations)
// 1. part of the data loaded
// 2. the trie cache sync to the db
// 3. the rest of the data loaded with part of the trie not in the cache
StringTrie trie = new StringTrie(mockDb);
for (int i = 0; i < 2000; ++i) {
String[] keyVal = strData.get(i).split("=");
if (keyVal[0].equals("*"))
trie.delete(keyVal[1].trim());
else
trie.put(keyVal[0].trim(), keyVal[1].trim());
}
StringTrie trie2 = new StringTrie(mockDb, trie.getRootHash());
for (int i = 2000; i < strData.size(); ++i) {
String[] keyVal = strData.get(i).split("=");
if (keyVal[0].equals("*"))
trie2.delete(keyVal[1].trim());
else
trie2.put(keyVal[0].trim(), keyVal[1].trim());
}
System.out.println("root_2: => " + Hex.toHexString(trie2.getRootHash()));
assertEquals(trieSingle.getRootHash(), trie2.getRootHash());
}
@Test // tests saving keys to the file //
public void testMasiveUpdateFromDB() {
boolean massiveUpdateFromDBEnabled = false;
if (massiveUpdateFromDBEnabled) {
List<String> randomWords = Arrays.asList(randomDictionary.split(","));
Map<String, String> testerMap = new HashMap<>();
StringTrie trie = new StringTrie(mockDb);
Random generator = new Random();
// Random insertion
for (int i = 0; i < 50000; ++i) {
int randomIndex1 = generator.nextInt(randomWords.size());
int randomIndex2 = generator.nextInt(randomWords.size());
String word1 = randomWords.get(randomIndex1).trim();
String word2 = randomWords.get(randomIndex2).trim();
trie.put(word1, word2);
testerMap.put(word1, word2);
}
// trie.cleanCache();
// trie.sync();
// Assert the result now
Iterator<String> keys = testerMap.keySet().iterator();
while (keys.hasNext()) {
String mapWord1 = keys.next();
String mapWord2 = testerMap.get(mapWord1);
String treeWord2 = new String(trie.get(mapWord1));
Assert.assertEquals(mapWord2, treeWord2);
}
TrieImpl trie2 = new TrieImpl(mockDb, trie.getRootHash());
// Assert the result now
keys = testerMap.keySet().iterator();
while (keys.hasNext()) {
String mapWord1 = keys.next();
String mapWord2 = testerMap.get(mapWord1);
String treeWord2 = new String(trie2.get(mapWord1.getBytes()));
Assert.assertEquals(mapWord2, treeWord2);
}
}
}
@Test
public void testRollbackTrie() throws URISyntaxException, IOException {
StringTrie trieSingle = new StringTrie(mockDb);
URL massiveUpload_1 = ClassLoader
.getSystemResource("trie/massive-upload.dmp");
File file = new File(massiveUpload_1.toURI());
List<String> strData = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
List<byte[]> roots = new ArrayList<>();
Map<String, String> trieDumps = new HashMap<>();
for (int i = 0; i < 100; ++i) {
String[] keyVal = strData.get(i).split("=");
if (keyVal[0].equals("*"))
trieSingle.delete(keyVal[1].trim());
else
trieSingle.put(keyVal[0].trim(), keyVal[1].trim());
byte[] hash = trieSingle.getRootHash();
roots.add(hash);
String key = Hex.toHexString(hash);
String dump = trieSingle.getTrieDump();
trieDumps.put(key, dump);
}
// compare all 100 rollback dumps and
// the originaly saved dumps
for (int i = 1; i < roots.size(); ++i) {
byte[] root = roots.get(i);
logger.info("rollback over root : {}", Hex.toHexString(root));
trieSingle = new StringTrie(mockDb, root);
String currDump = trieSingle.getTrieDump();
String originDump = trieDumps.get(Hex.toHexString(root));
Assert.assertEquals(currDump, originDump);
}
}
@Test
public void testGetFromRootNode() {
StringTrie trie1 = new StringTrie(mockDb);
trie1.put(cat, LONG_STRING);
TrieImpl trie2 = new TrieImpl(mockDb, trie1.getRootHash());
assertEquals(LONG_STRING, new String(trie2.get(cat.getBytes())));
}
/*
0x7645b9fbf1b51e6b980801fafe6bbc22d2ebe218 0x517eaccda568f3fa24915fed8add49d3b743b3764c0bc495b19a47c54dbc3d62 0x 0x1
0x0000000000000000000000000000000000000000000000000000000000000010 0x947e70f9460402290a3e487dae01f610a1a8218fda
0x0000000000000000000000000000000000000000000000000000000000000014 0x40
0x0000000000000000000000000000000000000000000000000000000000000016 0x94412e0c4f0102f3f0ac63f0a125bce36ca75d4e0d
0x0000000000000000000000000000000000000000000000000000000000000017 0x01
*/
@Test
public void storageHashCalc_1() {
byte[] key1 = Hex.decode("0000000000000000000000000000000000000000000000000000000000000010");
byte[] key2 = Hex.decode("0000000000000000000000000000000000000000000000000000000000000014");
byte[] key3 = Hex.decode("0000000000000000000000000000000000000000000000000000000000000016");
byte[] key4 = Hex.decode("0000000000000000000000000000000000000000000000000000000000000017");
byte[] val1 = Hex.decode("947e70f9460402290a3e487dae01f610a1a8218fda");
byte[] val2 = Hex.decode("40");
byte[] val3 = Hex.decode("94412e0c4f0102f3f0ac63f0a125bce36ca75d4e0d");
byte[] val4 = Hex.decode("01");
TrieImpl storage = new TrieImpl();
storage.put(key1, val1);
storage.put(key2, val2);
storage.put(key3, val3);
storage.put(key4, val4);
String hash = Hex.toHexString(storage.getRootHash());
System.out.println(hash);
Assert.assertEquals("517eaccda568f3fa24915fed8add49d3b743b3764c0bc495b19a47c54dbc3d62", hash);
}
@Test
public void testFromDump_1() throws URISyntaxException, IOException, ParseException {
// LOAD: real dump from real state run
URL dbDump = ClassLoader
.getSystemResource("dbdump/dbdump.json");
File dbDumpFile = new File(dbDump.toURI());
byte[] testData = Files.readAllBytes(dbDumpFile.toPath());
String testSrc = new String(testData);
JSONParser parser = new JSONParser();
JSONArray dbDumpJSONArray = (JSONArray) parser.parse(testSrc);
// KeyValueDataSource keyValueDataSource = new LevelDbDataSource("testState");
// keyValueDataSource.init();
HashMapDB<byte[]> dataSource = new HashMapDB<>();
for (Object aDbDumpJSONArray : dbDumpJSONArray) {
JSONObject obj = (JSONObject) aDbDumpJSONArray;
byte[] key = Hex.decode(obj.get("key").toString());
byte[] val = Hex.decode(obj.get("val").toString());
dataSource.put(key, val);
}
// TEST: load trie out of this run up to block#33
byte[] rootNode = Hex.decode("bb690805d24882bc7ccae6fc0f80ac146274d5b81c6a6e9c882cd9b0a649c9c7");
TrieImpl trie = new TrieImpl(dataSource, rootNode);
// first key added in genesis
byte[] val1 = trie.get(Hex.decode("51ba59315b3a95761d0863b05ccc7a7f54703d99"));
AccountState accountState1 = new AccountState(val1);
assertEquals(BigInteger.valueOf(2).pow(200), accountState1.getBalance());
assertEquals("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", Hex.toHexString(accountState1.getCodeHash()));
assertEquals(BigInteger.ZERO, accountState1.getNonce());
assertEquals(null, accountState1.getStateRoot());
// last key added up to block#33
byte[] val2 = trie.get(Hex.decode("a39c2067eb45bc878818946d0f05a836b3da44fa"));
AccountState accountState2 = new AccountState(val2);
assertEquals(new BigInteger("1500000000000000000"), accountState2.getBalance());
assertEquals("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", Hex.toHexString(accountState2.getCodeHash()));
assertEquals(BigInteger.ZERO, accountState2.getNonce());
assertEquals(null, accountState2.getStateRoot());
// keyValueDataSource.close();
}
@Test // update the trie with blog key/val
// each time dump the entire trie
public void testSample_1() {
StringTrie trie = new StringTrie(mockDb);
trie.put("dog", "puppy");
String dmp = trie.getTrieDump();
System.out.println(dmp);
System.out.println();
Assert.assertEquals("ed6e08740e4a267eca9d4740f71f573e9aabbcc739b16a2fa6c1baed5ec21278", Hex.toHexString(trie.getRootHash()));
trie.put("do", "verb");
dmp = trie.getTrieDump();
System.out.println(dmp);
System.out.println();
Assert.assertEquals("779db3986dd4f38416bfde49750ef7b13c6ecb3e2221620bcad9267e94604d36", Hex.toHexString(trie.getRootHash()));
trie.put("doggiestan", "aeswome_place");
dmp = trie.getTrieDump();
System.out.println(dmp);
System.out.println();
Assert.assertEquals("8bd5544747b4c44d1274aa99a6293065fe319b3230e800203317e4c75a770099", Hex.toHexString(trie.getRootHash()));
}
@Test
public void testSecureTrie(){
Trie trie = new SecureTrie(mockDb);
byte[] k1 = "do".getBytes();
byte[] v1 = "verb".getBytes();
byte[] k2 = "ether".getBytes();
byte[] v2 = "wookiedoo".getBytes();
byte[] k3 = "horse".getBytes();
byte[] v3 = "stallion".getBytes();
byte[] k4 = "shaman".getBytes();
byte[] v4 = "horse".getBytes();
byte[] k5 = "doge".getBytes();
byte[] v5 = "coin".getBytes();
byte[] k6 = "ether".getBytes();
byte[] v6 = "".getBytes();
byte[] k7 = "dog".getBytes();
byte[] v7 = "puppy".getBytes();
byte[] k8 = "shaman".getBytes();
byte[] v8 = "".getBytes();
trie.put(k1, v1);
trie.put(k2, v2);
trie.put(k3, v3);
trie.put(k4, v4);
trie.put(k5, v5);
trie.put(k6, v6);
trie.put(k7, v7);
trie.put(k8, v8);
byte[] root = trie.getRootHash();
logger.info("root: " + Hex.toHexString(root));
Assert.assertEquals("29b235a58c3c25ab83010c327d5932bcf05324b7d6b1185e650798034783ca9d",Hex.toHexString(root));
}
// this case relates to a bug which led us to conflict on Morden network (block #486248)
// first part of the new Value was converted to String by #asString() during key deletion
// and some lines after String.getBytes() returned byte array which differed to array before converting
@Test
public void testBugFix() throws ParseException, IOException, URISyntaxException {
Map<String, String> dataMap = new HashMap<>();
dataMap.put("6e929251b981389774af84a07585724c432e2db487381810719c3dd913192ae2", "00000000000000000000000000000000000000000000000000000000000000be");
dataMap.put("6e92718d00dae27b2a96f6853a0bf11ded08bc658b2e75904ca0344df5aff9ae", "00000000000000000000000000000000000000000000002f0000000000000000");
TrieImpl trie = new TrieImpl();
for (Map.Entry<String, String> e : dataMap.entrySet()) {
trie.put(Hex.decode(e.getKey()), Hex.decode(e.getValue()));
}
assertArrayEquals(trie.get(Hex.decode("6e929251b981389774af84a07585724c432e2db487381810719c3dd913192ae2")),
Hex.decode("00000000000000000000000000000000000000000000000000000000000000be"));
assertArrayEquals(trie.get(Hex.decode("6e92718d00dae27b2a96f6853a0bf11ded08bc658b2e75904ca0344df5aff9ae")),
Hex.decode("00000000000000000000000000000000000000000000002f0000000000000000"));
trie.delete(Hex.decode("6e9286c946c6dd1f5d97f35683732dc8a70dc511133a43d416892f527dfcd243"));
assertArrayEquals(trie.get(Hex.decode("6e929251b981389774af84a07585724c432e2db487381810719c3dd913192ae2")),
Hex.decode("00000000000000000000000000000000000000000000000000000000000000be"));
assertArrayEquals(trie.get(Hex.decode("6e92718d00dae27b2a96f6853a0bf11ded08bc658b2e75904ca0344df5aff9ae")),
Hex.decode("00000000000000000000000000000000000000000000002f0000000000000000"));
}
@Ignore
@Test
public void perfTestGet() {
HashMapDBSimple<byte[]> db = new HashMapDBSimple<>();
TrieCache trieCache = new TrieCache();
TrieImpl trie = new TrieImpl(db);
byte[][] keys = new byte[100000][];
System.out.println("Filling trie...");
for (int i = 0; i < 100_000; i++) {
byte[] k = sha3(intToBytes(i));
trie.put(k, k);
if (i < keys.length) {
keys[i] = k;
}
}
// Trie trie1 = new TrieImpl(new ReadCache.BytesKey<>(trieCache), trie.getRootHash());
// Trie trie1 = new TrieImpl(trieCache.getDb(), trie.getRootHash());
System.out.println("Benching...");
while (true) {
long s = System.nanoTime();
for (int j = 0; j < 5; j++) {
for (int k = 0; k < 100; k++) {
// Trie trie1 = new TrieImpl(new ReadCache.BytesKey<>(trieCache), trie.getRootHash());
// Trie trie1 = new TrieImpl(trieCache.getDb(), trie.getRootHash());
for (int i = 0; i < 1000; i++) {
Trie trie1 = new TrieImpl(trieCache.getDb(), trie.getRootHash());
// Trie trie1 = new TrieImpl(trieCache, trie.getRootHash());
trie1.get(keys[k * 100 + i]);
}
}
}
System.out.println((System.nanoTime() - s) / 1_000_000 + " ms");
}
}
@Ignore
@Test
public void perfTestRoot() {
while(true) {
HashMapDB<byte[]> db = new HashMapDB<>();
TrieCache trieCache = new TrieCache();
// TrieImpl trie = new TrieImpl(trieCache);
TrieImpl trie = new TrieImpl(db, null);
trie.setAsync(true);
// System.out.println("Filling trie...");
long s = System.nanoTime();
for (int i = 0; i < 200_000; i++) {
byte[] k = sha3(intToBytes(i));
trie.put(k, new byte[512]);
}
long s1 = System.nanoTime();
// System.out.println("Calculating root...");
System.out.println(Hex.toHexString(trie.getRootHash()));
System.out.println((System.nanoTime() - s) / 1_000_000 + " ms, root: " + (System.nanoTime() - s1) / 1_000_000 + " ms");
}
}
}