Merge with 518f9f53d57f5b2b9989a2b55e76a82534daaf16
This commit is contained in:
parent
2be0364145
commit
d2d36317d6
|
@ -67,15 +67,6 @@ public class Repository {
|
|||
accountStateDB = new TrackTrie(worldState);
|
||||
}
|
||||
|
||||
public Repository(byte[] stateRoot) {
|
||||
detailsDB = new DatabaseImpl("details");
|
||||
contractDetailsDB = new TrackDatabase(detailsDB);
|
||||
stateDB = new DatabaseImpl("state");
|
||||
worldState = new Trie(stateDB.getDb());
|
||||
worldState.setRoot(stateRoot);
|
||||
accountStateDB = new TrackTrie(worldState);
|
||||
}
|
||||
|
||||
|
||||
private Repository(TrackTrie accountStateDB, TrackDatabase contractDetailsDB) {
|
||||
this.accountStateDB = accountStateDB;
|
||||
|
|
|
@ -10,6 +10,9 @@ import org.ethereum.crypto.HashUtil;
|
|||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.ethereum.util.Value;
|
||||
import org.iq80.leveldb.DB;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
/**
|
||||
* The modified Merkle Patricia tree (trie) provides a persistent data structure
|
||||
|
@ -25,7 +28,7 @@ import org.iq80.leveldb.DB;
|
|||
* The trie implements a caching mechanism and will use cached values if they are present.
|
||||
* If a node is not present in the cache it will try to fetch it from the database and
|
||||
* store the cached value.
|
||||
*
|
||||
*
|
||||
* Please note that the data isn't persisted unless `sync` is explicitly called.
|
||||
*
|
||||
* www.ethereumJ.com
|
||||
|
@ -34,83 +37,85 @@ import org.iq80.leveldb.DB;
|
|||
*/
|
||||
public class Trie implements TrieFacade{
|
||||
|
||||
private static byte PAIR_SIZE = 2;
|
||||
private static byte LIST_SIZE = 17;
|
||||
|
||||
private Object prevRoot;
|
||||
private Object root;
|
||||
private Cache cache;
|
||||
private Logger logger = LoggerFactory.getLogger("trie");
|
||||
|
||||
public Trie(DB db) {
|
||||
this(db, "");
|
||||
}
|
||||
|
||||
public Trie(DB db, Object root) {
|
||||
this.cache = new Cache(db);
|
||||
this.root = root;
|
||||
this.prevRoot = root;
|
||||
}
|
||||
|
||||
public TrieIterator getIterator() {
|
||||
return new TrieIterator(this);
|
||||
}
|
||||
|
||||
public Cache getCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
public Object getPrevRoot() {
|
||||
return prevRoot;
|
||||
}
|
||||
private static byte PAIR_SIZE = 2;
|
||||
private static byte LIST_SIZE = 17;
|
||||
|
||||
public Object getRoot() {
|
||||
return root;
|
||||
}
|
||||
private Object prevRoot;
|
||||
private Object root;
|
||||
private Cache cache;
|
||||
|
||||
public void setRoot(byte[] root) {
|
||||
this.root = root;
|
||||
}
|
||||
public Trie(DB db) {
|
||||
this(db, "");
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
public Trie(DB db, Object root) {
|
||||
this.cache = new Cache(db);
|
||||
this.root = root;
|
||||
this.prevRoot = root;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* Public (query) interface functions *
|
||||
**************************************/
|
||||
|
||||
/**
|
||||
* Insert key/value pair into trie
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void update(String key, String value) {
|
||||
this.update(key.getBytes(), value.getBytes());
|
||||
}
|
||||
public TrieIterator getIterator() {
|
||||
return new TrieIterator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert key/value pair into trie
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void update(byte[] key, byte[] value) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key should not be blank");
|
||||
byte[] k = binToNibbles(key);
|
||||
this.root = this.insertOrDelete(this.root, k, value);
|
||||
}
|
||||
public Cache getCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a value from a node
|
||||
*
|
||||
* @param key
|
||||
* @return value
|
||||
*/
|
||||
public byte[] get(String key) {
|
||||
return this.get(key.getBytes());
|
||||
}
|
||||
public Object getPrevRoot() {
|
||||
return prevRoot;
|
||||
}
|
||||
|
||||
public Object getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* Public (query) interface functions *
|
||||
**************************************/
|
||||
|
||||
/**
|
||||
* Insert key/value pair into trie
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void update(String key, String value) {
|
||||
this.update(key.getBytes(), value.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert key/value pair into trie
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void update(byte[] key, byte[] value) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key should not be blank");
|
||||
byte[] k = binToNibbles(key);
|
||||
this.root = this.insertOrDelete(this.root, k, value);
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("Added key {} and value {}", Hex.toHexString(key), Hex.toHexString(value));
|
||||
logger.debug("New root-hash: {}", Hex.toHexString(this.getRootHash()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a value from a node
|
||||
*
|
||||
* @param key
|
||||
* @return value
|
||||
*/
|
||||
public byte[] get(String key) {
|
||||
return this.get(key.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a value from a node
|
||||
|
@ -119,6 +124,9 @@ public class Trie implements TrieFacade{
|
|||
* @return value
|
||||
*/
|
||||
public byte[] get(byte[] key) {
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("Retrieving key {}", Hex.toHexString(key));
|
||||
}
|
||||
byte[] k = binToNibbles(key);
|
||||
Value c = new Value( this.get(this.root, k) );
|
||||
return c.asBytes();
|
||||
|
@ -131,288 +139,292 @@ public class Trie implements TrieFacade{
|
|||
*/
|
||||
public void delete(byte[] key) {
|
||||
delete(new String(key));
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug("Deleted value for key {}", Hex.toHexString(key));
|
||||
logger.debug("New root-hash: {}", Hex.toHexString(this.getRootHash()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a key/value pair from the trie
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public void delete(String key) {
|
||||
this.update(key.getBytes(), "".getBytes());
|
||||
}
|
||||
/**
|
||||
* Delete a key/value pair from the trie
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public void delete(String key) {
|
||||
this.update(key.getBytes(), "".getBytes());
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* Private functions *
|
||||
****************************************/
|
||||
|
||||
private Object get(Object node, byte[] key) {
|
||||
|
||||
// Return the node if key is empty (= found)
|
||||
if (key.length == 0 || isEmptyNode(node)) {
|
||||
return node;
|
||||
}
|
||||
/****************************************
|
||||
* Private functions *
|
||||
****************************************/
|
||||
|
||||
Value currentNode = this.getNode(node);
|
||||
|
||||
if (currentNode.length() == PAIR_SIZE) {
|
||||
// Decode the key
|
||||
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||
Object v = currentNode.get(1).asObj();
|
||||
private Object get(Object node, byte[] key) {
|
||||
|
||||
if (key.length >= k.length && Arrays.equals(k, copyOfRange(key, 0, k.length))) {
|
||||
return this.get(v, copyOfRange(key, k.length, key.length));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return this.get(currentNode.get(key[0]).asObj(), copyOfRange(key, 1, key.length));
|
||||
}
|
||||
}
|
||||
// Return the node if key is empty (= found)
|
||||
if (key.length == 0 || isEmptyNode(node)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
private Object insertOrDelete(Object node, byte[] key, byte[] value) {
|
||||
if (value.length != 0) {
|
||||
return this.insert(node, key, value);
|
||||
} else {
|
||||
return this.delete(node, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update or add the item inside a node
|
||||
* return the updated node with rlp encoded
|
||||
*/
|
||||
private Object insert(Object node, byte[] key, Object value) {
|
||||
Value currentNode = this.getNode(node);
|
||||
|
||||
if (currentNode.length() == PAIR_SIZE) {
|
||||
// Decode the key
|
||||
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||
Object v = currentNode.get(1).asObj();
|
||||
|
||||
if (key.length >= k.length && Arrays.equals(k, copyOfRange(key, 0, k.length))) {
|
||||
return this.get(v, copyOfRange(key, k.length, key.length));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
return this.get(currentNode.get(key[0]).asObj(), copyOfRange(key, 1, key.length));
|
||||
}
|
||||
}
|
||||
|
||||
private Object insertOrDelete(Object node, byte[] key, byte[] value) {
|
||||
if (value.length != 0) {
|
||||
return this.insert(node, key, value);
|
||||
} else {
|
||||
return this.delete(node, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update or add the item inside a node
|
||||
* return the updated node with rlp encoded
|
||||
*/
|
||||
private Object insert(Object node, byte[] key, Object value) {
|
||||
|
||||
if (key.length == 0) {
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
if (isEmptyNode(node)) {
|
||||
Object[] newNode = new Object[] { packNibbles(key), value };
|
||||
return this.put(newNode);
|
||||
}
|
||||
|
||||
Value currentNode = this.getNode(node);
|
||||
Object[] newNode = new Object[] { packNibbles(key), value };
|
||||
return this.put(newNode);
|
||||
}
|
||||
|
||||
// Check for "special" 2 slice type node
|
||||
if (currentNode.length() == PAIR_SIZE) {
|
||||
// Decode the key
|
||||
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||
Object v = currentNode.get(1).asObj();
|
||||
Value currentNode = this.getNode(node);
|
||||
|
||||
// Matching key pair (ie. there's already an object with this key)
|
||||
if (Arrays.equals(k, key)) {
|
||||
Object[] newNode = new Object[] {packNibbles(key), value};
|
||||
return this.put(newNode);
|
||||
}
|
||||
// Check for "special" 2 slice type node
|
||||
if (currentNode.length() == PAIR_SIZE) {
|
||||
// Decode the key
|
||||
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||
Object v = currentNode.get(1).asObj();
|
||||
|
||||
Object newHash;
|
||||
int matchingLength = matchingNibbleLength(key, k);
|
||||
if (matchingLength == k.length) {
|
||||
// Insert the hash, creating a new node
|
||||
byte[] remainingKeypart = copyOfRange(key, matchingLength, key.length);
|
||||
newHash = this.insert(v, remainingKeypart, value);
|
||||
} else { // Expand the 2 length slice to a 17 length slice
|
||||
// Create two nodes to put into the new 17 length node
|
||||
Object oldNode = this.insert("", copyOfRange(k, matchingLength+1, k.length), v);
|
||||
Object newNode = this.insert("", copyOfRange(key, matchingLength+1, key.length), value);
|
||||
// Create an expanded slice
|
||||
Object[] scaledSlice = emptyStringSlice(17);
|
||||
// Set the copied and new node
|
||||
scaledSlice[k[matchingLength]] = oldNode;
|
||||
scaledSlice[key[matchingLength]] = newNode;
|
||||
newHash = this.put(scaledSlice);
|
||||
}
|
||||
// Matching key pair (ie. there's already an object with this key)
|
||||
if (Arrays.equals(k, key)) {
|
||||
Object[] newNode = new Object[] {packNibbles(key), value};
|
||||
return this.put(newNode);
|
||||
}
|
||||
|
||||
if (matchingLength == 0) {
|
||||
// End of the chain, return
|
||||
return newHash;
|
||||
} else {
|
||||
Object[] newNode = new Object[] { packNibbles(copyOfRange(key, 0, matchingLength)), newHash};
|
||||
return this.put(newNode);
|
||||
}
|
||||
} else {
|
||||
// Copy the current node over to the new node
|
||||
Object[] newNode = copyNode(currentNode);
|
||||
// Replace the first nibble in the key
|
||||
newNode[key[0]] = this.insert(currentNode.get(key[0]).asObj(), copyOfRange(key, 1, key.length), value);
|
||||
Object newHash;
|
||||
int matchingLength = matchingNibbleLength(key, k);
|
||||
if (matchingLength == k.length) {
|
||||
// Insert the hash, creating a new node
|
||||
byte[] remainingKeypart = copyOfRange(key, matchingLength, key.length);
|
||||
newHash = this.insert(v, remainingKeypart, value);
|
||||
} else { // Expand the 2 length slice to a 17 length slice
|
||||
// Create two nodes to put into the new 17 length node
|
||||
Object oldNode = this.insert("", copyOfRange(k, matchingLength+1, k.length), v);
|
||||
Object newNode = this.insert("", copyOfRange(key, matchingLength+1, key.length), value);
|
||||
// Create an expanded slice
|
||||
Object[] scaledSlice = emptyStringSlice(17);
|
||||
// Set the copied and new node
|
||||
scaledSlice[k[matchingLength]] = oldNode;
|
||||
scaledSlice[key[matchingLength]] = newNode;
|
||||
newHash = this.put(scaledSlice);
|
||||
}
|
||||
|
||||
return this.put(newNode);
|
||||
}
|
||||
}
|
||||
if (matchingLength == 0) {
|
||||
// End of the chain, return
|
||||
return newHash;
|
||||
} else {
|
||||
Object[] newNode = new Object[] { packNibbles(copyOfRange(key, 0, matchingLength)), newHash};
|
||||
return this.put(newNode);
|
||||
}
|
||||
} else {
|
||||
// Copy the current node over to the new node
|
||||
Object[] newNode = copyNode(currentNode);
|
||||
// Replace the first nibble in the key
|
||||
newNode[key[0]] = this.insert(currentNode.get(key[0]).asObj(), copyOfRange(key, 1, key.length), value);
|
||||
|
||||
private Object delete(Object node, byte[] key) {
|
||||
|
||||
if (key.length == 0 || isEmptyNode(node)) {
|
||||
return "";
|
||||
}
|
||||
return this.put(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
// New node
|
||||
Value currentNode = this.getNode(node);
|
||||
// Check for "special" 2 slice type node
|
||||
if (currentNode.length() == PAIR_SIZE) {
|
||||
// Decode the key
|
||||
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||
Object v = currentNode.get(1).asObj();
|
||||
private Object delete(Object node, byte[] key) {
|
||||
|
||||
// Matching key pair (ie. there's already an object with this key)
|
||||
if (Arrays.equals(k, key)) {
|
||||
return "";
|
||||
} else if (Arrays.equals(copyOfRange(key, 0, k.length), k)) {
|
||||
Object hash = this.delete(v, copyOfRange(key, k.length, key.length));
|
||||
Value child = this.getNode(hash);
|
||||
if (key.length == 0 || isEmptyNode(node)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
Object newNode;
|
||||
if (child.length() == PAIR_SIZE) {
|
||||
byte[] newKey = concatenate(k, unpackToNibbles(child.get(0).asBytes()));
|
||||
newNode = new Object[] {packNibbles(newKey), child.get(1).asObj()};
|
||||
} else {
|
||||
newNode = new Object[] {currentNode.get(0).asString(), hash};
|
||||
}
|
||||
return this.put(newNode);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
} else {
|
||||
// Copy the current node over to a new node
|
||||
Object[] itemList = copyNode(currentNode);
|
||||
// New node
|
||||
Value currentNode = this.getNode(node);
|
||||
// Check for "special" 2 slice type node
|
||||
if (currentNode.length() == PAIR_SIZE) {
|
||||
// Decode the key
|
||||
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||
Object v = currentNode.get(1).asObj();
|
||||
|
||||
// Replace the first nibble in the key
|
||||
itemList[key[0]] = this.delete(itemList[key[0]], copyOfRange(key, 1, key.length));
|
||||
|
||||
byte amount = -1;
|
||||
for (byte i = 0; i < LIST_SIZE; i++) {
|
||||
if (itemList[i] != "") {
|
||||
if (amount == -1) {
|
||||
amount = i;
|
||||
} else {
|
||||
amount = -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object[] newNode = null;
|
||||
if (amount == 16) {
|
||||
newNode = new Object[] { packNibbles(new byte[] {16} ), itemList[amount]};
|
||||
} else if (amount >= 0) {
|
||||
Value child = this.getNode(itemList[amount]);
|
||||
if (child.length() == PAIR_SIZE) {
|
||||
key = concatenate(new byte[]{amount}, unpackToNibbles(child.get(0).asBytes()));
|
||||
newNode = new Object[] {packNibbles(key), child.get(1).asObj()};
|
||||
} else if (child.length() == LIST_SIZE) {
|
||||
newNode = new Object[] { packNibbles(new byte[]{amount}), itemList[amount]};
|
||||
}
|
||||
} else {
|
||||
newNode = itemList;
|
||||
}
|
||||
return this.put(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to retrieve the actual node
|
||||
* If the node is not a list and length is > 32 bytes get the actual node from the db
|
||||
*
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private Value getNode(Object node) {
|
||||
Value n = new Value(node);
|
||||
|
||||
if (!n.get(0).isNull()) {
|
||||
return n;
|
||||
}
|
||||
// Matching key pair (ie. there's already an object with this key)
|
||||
if (Arrays.equals(k, key)) {
|
||||
return "";
|
||||
} else if (Arrays.equals(copyOfRange(key, 0, k.length), k)) {
|
||||
Object hash = this.delete(v, copyOfRange(key, k.length, key.length));
|
||||
Value child = this.getNode(hash);
|
||||
|
||||
byte[] str = n.asBytes();
|
||||
if (str.length == 0) {
|
||||
return n;
|
||||
} else if (str.length < 32) {
|
||||
return new Value(str);
|
||||
}
|
||||
return this.cache.get(str);
|
||||
}
|
||||
|
||||
private Object put(Object node) {
|
||||
return this.cache.put(node);
|
||||
}
|
||||
Object newNode;
|
||||
if (child.length() == PAIR_SIZE) {
|
||||
byte[] newKey = concatenate(k, unpackToNibbles(child.get(0).asBytes()));
|
||||
newNode = new Object[] {packNibbles(newKey), child.get(1).asObj()};
|
||||
} else {
|
||||
newNode = new Object[] {currentNode.get(0).asString(), hash};
|
||||
}
|
||||
return this.put(newNode);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
} else {
|
||||
// Copy the current node over to a new node
|
||||
Object[] itemList = copyNode(currentNode);
|
||||
|
||||
private boolean isEmptyNode(Object node) {
|
||||
Value n = new Value(node);
|
||||
return (node == null || (n.isString() && (n.asString() == "" || n.get(0).isNull())) || n.length() == 0);
|
||||
}
|
||||
// Replace the first nibble in the key
|
||||
itemList[key[0]] = this.delete(itemList[key[0]], copyOfRange(key, 1, key.length));
|
||||
|
||||
private Object[] copyNode(Value currentNode) {
|
||||
Object[] itemList = emptyStringSlice(LIST_SIZE);
|
||||
for (int i = 0; i < LIST_SIZE; i++) {
|
||||
Object cpy = currentNode.get(i).asObj();
|
||||
if (cpy != null) {
|
||||
itemList[i] = cpy;
|
||||
}
|
||||
}
|
||||
return itemList;
|
||||
}
|
||||
byte amount = -1;
|
||||
for (byte i = 0; i < LIST_SIZE; i++) {
|
||||
if (itemList[i] != "") {
|
||||
if (amount == -1) {
|
||||
amount = i;
|
||||
} else {
|
||||
amount = -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple compare function which compared the tries based on their stateRoot
|
||||
public boolean cmp(Trie trie) {
|
||||
return Arrays.equals(this.getRootHash(), trie.getRootHash());
|
||||
}
|
||||
|
||||
// Save the cached value to the database.
|
||||
public void sync() {
|
||||
this.cache.commit();
|
||||
this.prevRoot = this.root;
|
||||
}
|
||||
Object[] newNode = null;
|
||||
if (amount == 16) {
|
||||
newNode = new Object[] { packNibbles(new byte[] {16} ), itemList[amount]};
|
||||
} else if (amount >= 0) {
|
||||
Value child = this.getNode(itemList[amount]);
|
||||
if (child.length() == PAIR_SIZE) {
|
||||
key = concatenate(new byte[]{amount}, unpackToNibbles(child.get(0).asBytes()));
|
||||
newNode = new Object[] {packNibbles(key), child.get(1).asObj()};
|
||||
} else if (child.length() == LIST_SIZE) {
|
||||
newNode = new Object[] { packNibbles(new byte[]{amount}), itemList[amount]};
|
||||
}
|
||||
} else {
|
||||
newNode = itemList;
|
||||
}
|
||||
return this.put(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
public void undo() {
|
||||
this.cache.undo();
|
||||
this.root = this.prevRoot;
|
||||
}
|
||||
/**
|
||||
* Helper method to retrieve the actual node
|
||||
* If the node is not a list and length is > 32 bytes get the actual node from the db
|
||||
*
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private Value getNode(Object node) {
|
||||
Value n = new Value(node);
|
||||
|
||||
// Returns a copy of this trie
|
||||
public Trie copy() {
|
||||
Trie trie = new Trie(this.cache.getDb(), this.root);
|
||||
for (ByteArrayWrapper key : this.cache.getNodes().keySet()) {
|
||||
Node node = this.cache.getNodes().get(key);
|
||||
trie.cache.getNodes().put(key, node.copy());
|
||||
}
|
||||
return trie;
|
||||
}
|
||||
if (!n.get(0).isNull()) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Utility functions *
|
||||
*******************************/
|
||||
|
||||
// Returns the amount of nibbles that match each other from 0 ...
|
||||
private int matchingNibbleLength(byte[] a, byte[] b) {
|
||||
int i = 0;
|
||||
while (Arrays.equals(copyOfRange(a, 0, i+1), copyOfRange(b, 0, i+1)) && i < b.length) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// Created an array of empty elements of requred length
|
||||
private Object[] emptyStringSlice(int l) {
|
||||
Object[] slice = new Object[l];
|
||||
for (int i = 0; i < l; i++) {
|
||||
slice[i] = "";
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
byte[] str = n.asBytes();
|
||||
if (str.length == 0) {
|
||||
return n;
|
||||
} else if (str.length < 32) {
|
||||
return new Value(str);
|
||||
}
|
||||
return this.cache.get(str);
|
||||
}
|
||||
|
||||
public byte[] getRootHash() {
|
||||
Object root = this.getRoot();
|
||||
if (root == null
|
||||
|| (root instanceof byte[] && ((byte[]) root).length == 0)
|
||||
|| (root instanceof String && "".equals((String) root))) {
|
||||
return new byte[0];
|
||||
} else if (root instanceof byte[]) {
|
||||
return (byte[]) this.getRoot();
|
||||
} else {
|
||||
Value rootValue = new Value(this.getRoot());
|
||||
byte[] val = rootValue.encode();
|
||||
return HashUtil.sha3(val);
|
||||
}
|
||||
}
|
||||
private Object put(Object node) {
|
||||
return this.cache.put(node);
|
||||
}
|
||||
|
||||
private boolean isEmptyNode(Object node) {
|
||||
Value n = new Value(node);
|
||||
return (node == null || (n.isString() && (n.asString() == "" || n.get(0).isNull())) || n.length() == 0);
|
||||
}
|
||||
|
||||
private Object[] copyNode(Value currentNode) {
|
||||
Object[] itemList = emptyStringSlice(LIST_SIZE);
|
||||
for (int i = 0; i < LIST_SIZE; i++) {
|
||||
Object cpy = currentNode.get(i).asObj();
|
||||
if (cpy != null) {
|
||||
itemList[i] = cpy;
|
||||
}
|
||||
}
|
||||
return itemList;
|
||||
}
|
||||
|
||||
// Simple compare function which compared the tries based on their stateRoot
|
||||
public boolean cmp(Trie trie) {
|
||||
return Arrays.equals(this.getRootHash(), trie.getRootHash());
|
||||
}
|
||||
|
||||
// Save the cached value to the database.
|
||||
public void sync() {
|
||||
this.cache.commit();
|
||||
this.prevRoot = this.root;
|
||||
}
|
||||
|
||||
public void undo() {
|
||||
this.cache.undo();
|
||||
this.root = this.prevRoot;
|
||||
}
|
||||
|
||||
// Returns a copy of this trie
|
||||
public Trie copy() {
|
||||
Trie trie = new Trie(this.cache.getDb(), this.root);
|
||||
for (ByteArrayWrapper key : this.cache.getNodes().keySet()) {
|
||||
Node node = this.cache.getNodes().get(key);
|
||||
trie.cache.getNodes().put(key, node.copy());
|
||||
}
|
||||
return trie;
|
||||
}
|
||||
|
||||
/********************************
|
||||
* Utility functions *
|
||||
*******************************/
|
||||
|
||||
// Returns the amount of nibbles that match each other from 0 ...
|
||||
private int matchingNibbleLength(byte[] a, byte[] b) {
|
||||
int i = 0;
|
||||
while (Arrays.equals(copyOfRange(a, 0, i+1), copyOfRange(b, 0, i+1)) && i < b.length) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// Created an array of empty elements of requred length
|
||||
private Object[] emptyStringSlice(int l) {
|
||||
Object[] slice = new Object[l];
|
||||
for (int i = 0; i < l; i++) {
|
||||
slice[i] = "";
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
|
||||
public byte[] getRootHash() {
|
||||
Object root = this.getRoot();
|
||||
if (root == null
|
||||
|| (root instanceof byte[] && ((byte[]) root).length == 0)
|
||||
|| (root instanceof String && "".equals((String) root))) {
|
||||
return new byte[0];
|
||||
} else if (root instanceof byte[]) {
|
||||
return (byte[]) this.getRoot();
|
||||
} else {
|
||||
Value rootValue = new Value(this.getRoot());
|
||||
byte[] val = rootValue.encode();
|
||||
return HashUtil.sha3(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue