+fixing the bug of loading Trie from the db
 +Tracing full tree improved - now it traverse the trie recursively from the root
This commit is contained in:
romanman 2014-08-30 16:53:40 +03:00
parent da50b99394
commit cee2e3cb20
7 changed files with 100110 additions and 26 deletions

View File

@ -1,5 +1,7 @@
package org.ethereum.trie; package org.ethereum.trie;
import org.ethereum.util.Value;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -14,7 +16,7 @@ public class CollectFullSetOfNodes implements Trie.ScanAction {
Set<byte[]> nodes = new HashSet<>(); Set<byte[]> nodes = new HashSet<>();
@Override @Override
public void doOnNode(byte[] hash) { public void doOnNode(byte[] hash, Value node) {
nodes.add(hash); nodes.add(hash);
} }

View File

@ -1,5 +1,7 @@
package org.ethereum.trie; package org.ethereum.trie;
import org.ethereum.util.Value;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -15,7 +17,7 @@ public class CountAllNodes implements Trie.ScanAction {
int counted = 0; int counted = 0;
@Override @Override
public void doOnNode(byte[] hash) { public void doOnNode(byte[] hash, Value node) {
++counted; ++counted;
} }

View File

@ -0,0 +1,26 @@
package org.ethereum.trie;
import org.ethereum.util.Value;
import org.spongycastle.util.encoders.Hex;
/**
* www.ethereumJ.com
*
* @author: Roman Mandeleil
* Created on: 29/08/2014 10:46
*/
public class TraceAllNodes implements Trie.ScanAction {
StringBuilder output = new StringBuilder();
@Override
public void doOnNode(byte[] hash, Value node) {
output.append(Hex.toHexString(hash)).append(" ==> ").append(node.toString()).append("\n");
}
public String getOutput() {
return output.toString();
}
}

View File

@ -504,40 +504,23 @@ public class Trie implements TrieFacade {
scanTree(val.asBytes(), scanAction); scanTree(val.asBytes(), scanAction);
} }
} }
scanAction.doOnNode(hash, node);
scanAction.doOnNode(hash);
} }
} }
public String getTrieDump(){ public String getTrieDump(){
Set<ByteArrayWrapper> keys = getCache().getNodes().keySet(); TraceAllNodes traceAction = new TraceAllNodes();
StringBuilder output = new StringBuilder(); this.scanTree(this.getRootHash(), traceAction);
Value rootVal = new Value(root); String root = "root: " + Hex.toHexString(getRootHash()) + "\n";
if ( ! Arrays.equals(rootVal.asBytes() , this.getRootHash())){
output.append("root: ").append(Hex.toHexString( this.getRootHash() )).append(" => "). return root + traceAction.getOutput();
append( rootVal.toString()).append("\n");
} else {
output.append("root: ").append(Hex.toHexString( this.getRootHash() )).append("\n");
}
for (ByteArrayWrapper key : keys){
Node value = getCache().getNodes().get(key);
byte[] hash = HashUtil.sha3( value.getValue().encode() );
output.append(Hex.toHexString(hash)).append(" ==> ").append(value.getValue().toString()).append("\n");
}
return output.toString();
} }
public interface ScanAction{ public interface ScanAction{
public void doOnNode(byte[] hash); public void doOnNode(byte[] hash, Value node);
} }
} }

View File

@ -680,7 +680,7 @@ public class RLP {
} }
int prefix = data[pos] & 0xFF; int prefix = data[pos] & 0xFF;
if (prefix == OFFSET_SHORT_ITEM) { if (prefix == OFFSET_SHORT_ITEM) {
return new DecodeResult(pos+1, new byte[0]); // means no length or 0 return new DecodeResult(pos+1, ""); // means no length or 0
} else if (prefix < OFFSET_SHORT_ITEM) { } else if (prefix < OFFSET_SHORT_ITEM) {
return new DecodeResult(pos+1, new byte[] { data[pos] }); // byte is its own RLP encoding return new DecodeResult(pos+1, new byte[] { data[pos] }); // byte is its own RLP encoding
} else if (prefix < OFFSET_LONG_ITEM) { } else if (prefix < OFFSET_LONG_ITEM) {

View File

@ -11,6 +11,7 @@ import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.*; import java.util.*;
@ -44,6 +45,7 @@ public class TrieTest {
private static String dude = "dude"; private static String dude = "dude";
private MockDB mockDb = new MockDB(); private MockDB mockDb = new MockDB();
private MockDB mockDb_2 = new MockDB();
// ROOT: [ '\x16', A ] // ROOT: [ '\x16', A ]
// A: [ '', '', '', '', B, '', '', '', C, '', '', '', '', '', '', '', '' ] // A: [ '', '', '', '', B, '', '', '', C, '', '', '', '', '', '', '', '' ]
@ -57,6 +59,7 @@ public class TrieTest {
@After @After
public void closeMockDb() throws IOException { public void closeMockDb() throws IOException {
mockDb.close(); mockDb.close();
mockDb_2.close();
} }
@Test @Test
@ -566,6 +569,74 @@ public class TrieTest {
} }
} }
@Test
public void testMasiveDetermenisticUpdate() throws IOException, URISyntaxException {
// should be root: 89bb4c4051774dd9039305bdad93b7fb61fcf2e6c0905d1acf2658b9c00b35c6
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 (100000 operations)
Trie trieSingle = new Trie(mockDb_2);
for (int i = 0; i < strData.size() ; ++i){
String[] keyVal= strData.get(i).split("=");
if (keyVal[0].equals("*"))
trieSingle.delete(keyVal[1].trim());
else
trieSingle.update(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
Trie trie = new Trie(mockDb);
for (int i = 0; i < 10000; ++i){
String[] keyVal= strData.get(i).split("=");
if (keyVal[0].equals("*"))
trie.delete(keyVal[1].trim());
else
trie.update(keyVal[0].trim(), keyVal[1].trim());
}
trie.cleanCacheGarbage();
trie.sync();
Trie trie2 = new Trie(mockDb, trie.getRootHash());
for (int i = 10000; i < strData.size(); ++i){
String[] keyVal= strData.get(i).split("=");
if (keyVal[0].equals("*"))
trie2.delete(keyVal[1].trim());
else
trie2.update(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 // @Test // tests saving keys to the file //
public void testMasiveUpdateFromDB(){ public void testMasiveUpdateFromDB(){

File diff suppressed because it is too large Load Diff