Switch to consistent interface name for Trie
This commit is contained in:
parent
6c1de3e483
commit
ce38ac5e89
|
@ -2,6 +2,7 @@ package org.ethereum.core;
|
|||
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.trie.TrieImpl;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.ethereum.util.RLPElement;
|
||||
|
@ -286,7 +287,7 @@ public class Block {
|
|||
|
||||
private void parseTxs(byte[] expectedRoot, RLPList txReceipts) {
|
||||
|
||||
this.txsState = new Trie(null);
|
||||
this.txsState = new TrieImpl(null);
|
||||
for (int i = 0; i < txReceipts.size(); i++) {
|
||||
RLPElement rlpTxReceipt = txReceipts.get(i);
|
||||
RLPElement txData = ((RLPList)rlpTxReceipt).get(0);
|
||||
|
|
|
@ -44,12 +44,10 @@ import static org.ethereum.core.Denomination.SZABO;
|
|||
* </ol>
|
||||
* See <a href="https://github.com/ethereum/wiki/wiki/White-Paper#blockchain-and-mining">Ethereum Whitepaper</a>
|
||||
*
|
||||
*
|
||||
* www.ethereumJ.com
|
||||
* @authors: Roman Mandeleil,
|
||||
* Nick Savers
|
||||
* Created on: 20/05/2014 10:44
|
||||
*
|
||||
*/
|
||||
public class BlockchainImpl implements Blockchain {
|
||||
|
||||
|
@ -214,7 +212,6 @@ public class BlockchainImpl implements Blockchain {
|
|||
logger.info("*** Last block added [ #{} ]", block.getNumber());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply the transaction to the world state.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.ethereum.core;
|
|||
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.trie.TrieImpl;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -64,7 +65,7 @@ public class Genesis extends Block {
|
|||
NUMBER, MIN_GAS_PRICE, GAS_LIMIT, GAS_USED, TIMESTAMP,
|
||||
EXTRA_DATA, NONCE, null, null);
|
||||
|
||||
Trie state = new Trie(null);
|
||||
Trie state = new TrieImpl(null);
|
||||
// The proof-of-concept series include a development premine, making the state root hash
|
||||
// some value stateRoot. The latest documentation should be consulted for the value of the state root.
|
||||
for (String address : premine) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.trie.TrieImpl;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.ethereum.util.RLPElement;
|
||||
import org.ethereum.util.RLPItem;
|
||||
|
@ -28,7 +29,7 @@ public class ContractDetails {
|
|||
|
||||
private byte[] code;
|
||||
|
||||
private Trie storageTrie = new Trie(null);
|
||||
private Trie storageTrie = new TrieImpl(null);
|
||||
|
||||
public ContractDetails() {
|
||||
}
|
||||
|
@ -88,7 +89,7 @@ public class ContractDetails {
|
|||
|
||||
public byte[] getStorageHash() {
|
||||
|
||||
storageTrie = new Trie(null);
|
||||
storageTrie = new TrieImpl(null);
|
||||
// calc the trie for root hash
|
||||
for (int i = 0; i < storageKeys.size(); ++i){
|
||||
storageTrie.update(storageKeys.get(i).getData(), RLP
|
||||
|
|
|
@ -12,8 +12,8 @@ import org.ethereum.json.JSONHelper;
|
|||
import org.ethereum.listener.EthereumListener;
|
||||
import org.ethereum.manager.WorldManager;
|
||||
import org.ethereum.trie.TrackTrie;
|
||||
import org.ethereum.trie.TrieImpl;
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.trie.TrieFacade;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.ethereum.vm.DataWord;
|
||||
import org.iq80.leveldb.DBIterator;
|
||||
|
@ -84,7 +84,7 @@ public class RepositoryImpl implements Repository {
|
|||
detailsDB = new DatabaseImpl(detailsDbName);
|
||||
contractDetailsDB = new TrackDatabase(detailsDB);
|
||||
stateDB = new DatabaseImpl(stateDbName);
|
||||
worldState = new Trie(stateDB.getDb());
|
||||
worldState = new TrieImpl(stateDB.getDb());
|
||||
accountStateDB = new TrackTrie(worldState);
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ public class RepositoryImpl implements Repository {
|
|||
return state;
|
||||
}
|
||||
|
||||
public TrieFacade getWorldState() {
|
||||
public Trie getWorldState() {
|
||||
return worldState;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,6 @@ package org.ethereum.facade;
|
|||
|
||||
import org.ethereum.core.Block;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 06/09/2014 08:31
|
||||
*/
|
||||
|
||||
public interface Blockchain {
|
||||
|
||||
public int getSize();
|
||||
|
|
|
@ -27,7 +27,6 @@ public interface Ethereum {
|
|||
*/
|
||||
public PeerData findOnlinePeer(PeerData excludePeer) ;
|
||||
|
||||
|
||||
/**
|
||||
* Find an online peer but not from excluded list
|
||||
*
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.ethereum.core.AccountState;
|
|||
import org.ethereum.core.Block;
|
||||
import org.ethereum.core.BlockchainImpl;
|
||||
import org.ethereum.db.ContractDetails;
|
||||
import org.ethereum.trie.TrieFacade;
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.vm.DataWord;
|
||||
import org.iq80.leveldb.DBIterator;
|
||||
|
||||
|
@ -144,7 +144,7 @@ public interface Repository {
|
|||
*
|
||||
* @return the <code>Trie</code> representing the entire current state
|
||||
*/
|
||||
public TrieFacade getWorldState();
|
||||
public Trie getWorldState();
|
||||
|
||||
/**
|
||||
* Load the blockchain into cache memory
|
||||
|
|
|
@ -12,7 +12,7 @@ import java.util.Set;
|
|||
* Created on: 29/08/2014 10:46
|
||||
*/
|
||||
|
||||
public class CollectFullSetOfNodes implements Trie.ScanAction {
|
||||
public class CollectFullSetOfNodes implements TrieImpl.ScanAction {
|
||||
Set<byte[]> nodes = new HashSet<>();
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.ethereum.util.Value;
|
|||
* Created on: 29/08/2014 10:46
|
||||
*/
|
||||
|
||||
public class CountAllNodes implements Trie.ScanAction {
|
||||
public class CountAllNodes implements TrieImpl.ScanAction {
|
||||
|
||||
int counted = 0;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import org.spongycastle.util.encoders.Hex;
|
|||
* Created on: 29/08/2014 10:46
|
||||
*/
|
||||
|
||||
public class TraceAllNodes implements Trie.ScanAction {
|
||||
public class TraceAllNodes implements TrieImpl.ScanAction {
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
|
|
|
@ -9,20 +9,23 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
* The TrackTrie is a wrapper around and actual Modified Merkle Patricia Trie
|
||||
* to keep track of changes which can be rolled back or committed down into
|
||||
* the original trie after successful execution of a transaction.
|
||||
*
|
||||
* www.ethereumJ.com
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 11/06/2014 19:47
|
||||
*/
|
||||
public class TrackTrie implements TrieFacade {
|
||||
public class TrackTrie implements Trie {
|
||||
|
||||
private TrieFacade trie;
|
||||
private Trie trie;
|
||||
|
||||
private boolean trackingChanges = false;
|
||||
private Map<ByteArrayWrapper, byte[]> changes;
|
||||
private List<ByteArrayWrapper> deletes;
|
||||
|
||||
public TrackTrie(TrieFacade trie) {
|
||||
public TrackTrie(Trie trie) {
|
||||
this.trie = trie;
|
||||
}
|
||||
|
||||
|
@ -33,12 +36,10 @@ public class TrackTrie implements TrieFacade {
|
|||
}
|
||||
|
||||
public void commitTrack() {
|
||||
for (ByteArrayWrapper key : changes.keySet()) {
|
||||
for (ByteArrayWrapper key : changes.keySet())
|
||||
trie.update(key.getData(), changes.get(key));
|
||||
}
|
||||
for (ByteArrayWrapper key : deletes) {
|
||||
for (ByteArrayWrapper key : deletes)
|
||||
trie.update(key.getData(), ByteUtil.EMPTY_BYTE_ARRAY);
|
||||
}
|
||||
changes = null;
|
||||
trackingChanges = false;
|
||||
}
|
||||
|
@ -52,12 +53,11 @@ public class TrackTrie implements TrieFacade {
|
|||
|
||||
@Override
|
||||
public void update(byte[] key, byte[] value) {
|
||||
if (trackingChanges) {
|
||||
if (trackingChanges)
|
||||
changes.put(new ByteArrayWrapper(key), value);
|
||||
} else {
|
||||
else
|
||||
trie.update(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] get(byte[] key) {
|
||||
|
@ -77,10 +77,9 @@ public class TrackTrie implements TrieFacade {
|
|||
if (trackingChanges) {
|
||||
ByteArrayWrapper wKey = new ByteArrayWrapper(key);
|
||||
deletes.add(wKey);
|
||||
} else {
|
||||
} else
|
||||
trie.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRootHash() {
|
||||
|
@ -91,4 +90,14 @@ public class TrackTrie implements TrieFacade {
|
|||
public String getTrieDump() {
|
||||
return trie.getTrieDump();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRoot(byte[] root) {
|
||||
trie.setRoot(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync() {
|
||||
trie.sync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,517 +1,52 @@
|
|||
package org.ethereum.trie;
|
||||
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
import static org.ethereum.util.ByteUtil.matchingNibbleLength;
|
||||
import static org.ethereum.util.CompactEncoder.binToNibbles;
|
||||
import static org.ethereum.util.CompactEncoder.packNibbles;
|
||||
import static org.ethereum.util.CompactEncoder.unpackToNibbles;
|
||||
import static org.spongycastle.util.Arrays.concatenate;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
import org.ethereum.util.Value;
|
||||
import org.iq80.leveldb.DB;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
/**
|
||||
* Trie interface for the main data structure in Ethereum
|
||||
* which is used to store both the account state and storage of each account.
|
||||
*/
|
||||
public interface Trie {
|
||||
|
||||
/**
|
||||
* The modified Merkle Patricia tree (trie) provides a persistent data structure
|
||||
* to map between arbitrary-length binary data (byte arrays). It is defined in terms of
|
||||
* a mutable data structure to map between 256-bit binary fragments and arbitrary-length
|
||||
* binary data, typically implemented as a database. The core of the trie, and its sole
|
||||
* requirement in terms of the protocol specification is to provide a single value that
|
||||
* identifies a given set of key-value pairs, which may either a 32 byte sequence or
|
||||
* the empty byte sequence. It is left as an implementation consideration to store and
|
||||
* maintain the structure of the trie in a manner the allows effective and efficient
|
||||
* realisation of the protocol.
|
||||
* Adds or updates a value to the trie for the specified key
|
||||
*
|
||||
* 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
|
||||
* @author: Nick Savers
|
||||
* Created on: 20/05/2014 10:44
|
||||
* @param key - any length byte array
|
||||
* @param value - an rlp encoded byte representation of the object to store
|
||||
*/
|
||||
public class Trie implements TrieFacade {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger("trie");
|
||||
|
||||
private static byte PAIR_SIZE = 2;
|
||||
private static byte LIST_SIZE = 17;
|
||||
|
||||
private Object prevRoot;
|
||||
private Object root;
|
||||
private Cache cache;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public Object getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public void setRoot(Object root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* Public (query) interface functions *
|
||||
**************************************/
|
||||
public void update(byte[] key, byte[] value);
|
||||
|
||||
/**
|
||||
* Insert key/value pair into trie
|
||||
* Gets a value from the trie for a given key
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @param key - any length byte array
|
||||
* @return value - an rlp encoded byte array of the stored object
|
||||
*/
|
||||
public void update(String key, String value) {
|
||||
this.update(key.getBytes(), value.getBytes());
|
||||
}
|
||||
public byte[] get(byte[] key);
|
||||
|
||||
/**
|
||||
* Insert key/value pair into trie
|
||||
* Deletes a value from the trie for a given key
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @param key - any length byte array
|
||||
*/
|
||||
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()));
|
||||
}
|
||||
}
|
||||
public void delete(byte[] key);
|
||||
|
||||
/**
|
||||
* Retrieve a value from a node
|
||||
* Returns a SHA-3 hash from the top node of the trie
|
||||
*
|
||||
* @param key
|
||||
* @return value
|
||||
* @return 32-byte SHA-3 hash representing the entire contents of the trie.
|
||||
*/
|
||||
public byte[] get(String key) {
|
||||
return this.get(key.getBytes());
|
||||
}
|
||||
public byte[] getRootHash();
|
||||
|
||||
/**
|
||||
* Retrieve a value from a node
|
||||
* Set the top node of the trie
|
||||
*
|
||||
* @param key
|
||||
* @return value
|
||||
* @param root - 32-byte SHA-3 hash of the root node
|
||||
*/
|
||||
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 == null)? null : c.asBytes();
|
||||
}
|
||||
public void setRoot(byte[] root);
|
||||
|
||||
/**
|
||||
* Delete a key/value pair from the trie
|
||||
*
|
||||
* @param key
|
||||
* Commit all the changes until now
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
Value currentNode = this.getNode(node);
|
||||
if (currentNode == null) return null;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (isEmptyNode(node)) {
|
||||
Object[] newNode = new Object[] { packNibbles(key), value };
|
||||
return this.putToCache(newNode);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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.putToCache(newNode);
|
||||
}
|
||||
|
||||
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 putToCache 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.putToCache(scaledSlice);
|
||||
}
|
||||
|
||||
if (matchingLength == 0) {
|
||||
// End of the chain, return
|
||||
return newHash;
|
||||
} else {
|
||||
Object[] newNode = new Object[] { packNibbles(copyOfRange(key, 0, matchingLength)), newHash};
|
||||
return this.putToCache(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);
|
||||
return this.putToCache(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
private Object delete(Object node, byte[] key) {
|
||||
|
||||
if (key.length == 0 || isEmptyNode(node)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// 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);
|
||||
|
||||
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.putToCache(newNode);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
} else {
|
||||
// Copy the current node over to a new node
|
||||
Object[] itemList = copyNode(currentNode);
|
||||
|
||||
// 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.putToCache(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 val = new Value(node);
|
||||
|
||||
// in that case we got a node
|
||||
// so no need to encode it
|
||||
if (!val.isBytes()) {
|
||||
return val;
|
||||
}
|
||||
|
||||
byte[] keyBytes = val.asBytes();
|
||||
if (keyBytes.length == 0) {
|
||||
return val;
|
||||
} else if (keyBytes.length < 32) {
|
||||
return new Value(keyBytes);
|
||||
}
|
||||
return this.cache.get(keyBytes);
|
||||
}
|
||||
|
||||
private Object putToCache(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 *
|
||||
*******************************/
|
||||
|
||||
// Created an array of empty elements of required 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() {
|
||||
if (root == null
|
||||
|| (root instanceof byte[] && ((byte[]) root).length == 0)
|
||||
|| (root instanceof String && "".equals((String) root))) {
|
||||
return ByteUtil.EMPTY_BYTE_ARRAY;
|
||||
} else if (root instanceof byte[]) {
|
||||
return (byte[]) this.getRoot();
|
||||
} else {
|
||||
Value rootValue = new Value(this.getRoot());
|
||||
byte[] val = rootValue.encode();
|
||||
return HashUtil.sha3(val);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* insert/delete operations on a Trie structure
|
||||
* leaves unnecessary nodes, this method scans the
|
||||
* cache and removes them. The method is not thread
|
||||
* safe, the tree should not be modified during the
|
||||
* cleaning process.
|
||||
*/
|
||||
public void cleanCacheGarbage() {
|
||||
|
||||
CollectFullSetOfNodes collectAction = new CollectFullSetOfNodes();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
this.scanTree(this.getRootHash(), collectAction);
|
||||
|
||||
Set<byte[]> hashSet = collectAction.getCollectedHashes();
|
||||
Map<ByteArrayWrapper, Node> nodes = this.getCache().getNodes();
|
||||
Set<ByteArrayWrapper> toRemoveSet = new HashSet<>();
|
||||
|
||||
for (ByteArrayWrapper key : nodes.keySet()) {
|
||||
if (!hashSet.contains(key.getData())) {
|
||||
toRemoveSet.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (ByteArrayWrapper key : toRemoveSet) {
|
||||
|
||||
this.getCache().delete(key.getData());
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
logger.trace("Garbage collected node: [ {} ]",
|
||||
Hex.toHexString( key.getData() ));
|
||||
}
|
||||
logger.info("Garbage collected node list, size: [ {} ]", toRemoveSet.size());
|
||||
logger.info("Garbage collection time: [ {}ms ]", System.currentTimeMillis() - startTime);
|
||||
}
|
||||
|
||||
public void scanTree(byte[] hash, ScanAction scanAction) {
|
||||
|
||||
Value node = this.getCache().get(hash);
|
||||
if (node == null) return;
|
||||
|
||||
if (node.isList()) {
|
||||
List<Object> siblings = node.asList();
|
||||
if (siblings.size() == PAIR_SIZE) {
|
||||
Value val = new Value(siblings.get(1));
|
||||
if (val.isHashCode())
|
||||
scanTree(val.asBytes(), scanAction);
|
||||
} else {
|
||||
for (int j = 0; j < LIST_SIZE; ++j) {
|
||||
Value val = new Value(siblings.get(j));
|
||||
if (val.isHashCode())
|
||||
scanTree(val.asBytes(), scanAction);
|
||||
}
|
||||
}
|
||||
scanAction.doOnNode(hash, node);
|
||||
}
|
||||
}
|
||||
|
||||
public String getTrieDump() {
|
||||
|
||||
String root = "";
|
||||
TraceAllNodes traceAction = new TraceAllNodes();
|
||||
this.scanTree(this.getRootHash(), traceAction);
|
||||
|
||||
if (this.getRoot() instanceof Value) {
|
||||
root = "root: " + Hex.toHexString(getRootHash()) + " => " + this.getRoot() + "\n";
|
||||
} else {
|
||||
root = "root: " + Hex.toHexString(getRootHash()) + "\n";
|
||||
}
|
||||
return root + traceAction.getOutput();
|
||||
}
|
||||
|
||||
public interface ScanAction {
|
||||
public void doOnNode(byte[] hash, Value node);
|
||||
}
|
||||
public void sync();
|
||||
|
||||
public String getTrieDump();
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package org.ethereum.trie;
|
||||
|
||||
/**
|
||||
* www.ethereumJ.com
|
||||
*
|
||||
* @author: Roman Mandeleil
|
||||
* Created on: 11/06/2014 19:44
|
||||
*/
|
||||
public interface TrieFacade {
|
||||
|
||||
public void update(byte[] key, byte[] value);
|
||||
public byte[] get(byte[] key);
|
||||
public void delete(byte[] key);
|
||||
|
||||
public byte[] getRootHash();
|
||||
|
||||
public String getTrieDump();
|
||||
}
|
|
@ -0,0 +1,517 @@
|
|||
package org.ethereum.trie;
|
||||
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
import static org.ethereum.util.ByteUtil.matchingNibbleLength;
|
||||
import static org.ethereum.util.CompactEncoder.binToNibbles;
|
||||
import static org.ethereum.util.CompactEncoder.packNibbles;
|
||||
import static org.ethereum.util.CompactEncoder.unpackToNibbles;
|
||||
import static org.spongycastle.util.Arrays.concatenate;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.db.ByteArrayWrapper;
|
||||
import org.ethereum.util.ByteUtil;
|
||||
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
|
||||
* to map between arbitrary-length binary data (byte arrays). It is defined in terms of
|
||||
* a mutable data structure to map between 256-bit binary fragments and arbitrary-length
|
||||
* binary data, typically implemented as a database. The core of the trie, and its sole
|
||||
* requirement in terms of the protocol specification is to provide a single value that
|
||||
* identifies a given set of key-value pairs, which may either a 32 byte sequence or
|
||||
* the empty byte sequence. It is left as an implementation consideration to store and
|
||||
* maintain the structure of the trie in a manner the allows effective and efficient
|
||||
* realisation of the protocol.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* <b>Note:</b> the data isn't persisted unless `sync` is explicitly called.
|
||||
*
|
||||
* www.ethereumJ.com
|
||||
* @author: Nick Savers
|
||||
* Created on: 20/05/2014 10:44
|
||||
*/
|
||||
public class TrieImpl implements Trie {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger("trie");
|
||||
|
||||
private static byte PAIR_SIZE = 2;
|
||||
private static byte LIST_SIZE = 17;
|
||||
|
||||
private Object prevRoot;
|
||||
private Object root;
|
||||
private Cache cache;
|
||||
|
||||
public TrieImpl(DB db) {
|
||||
this(db, "");
|
||||
}
|
||||
|
||||
public TrieImpl(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;
|
||||
}
|
||||
|
||||
public Object getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public void setRoot(byte[] root) {
|
||||
this.root = 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
|
||||
*
|
||||
* @param key
|
||||
* @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 == null)? null : c.asBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a key/value pair from the trie
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
Value currentNode = this.getNode(node);
|
||||
if (currentNode == null) return null;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (isEmptyNode(node)) {
|
||||
Object[] newNode = new Object[] { packNibbles(key), value };
|
||||
return this.putToCache(newNode);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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.putToCache(newNode);
|
||||
}
|
||||
|
||||
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 putToCache 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.putToCache(scaledSlice);
|
||||
}
|
||||
|
||||
if (matchingLength == 0) {
|
||||
// End of the chain, return
|
||||
return newHash;
|
||||
} else {
|
||||
Object[] newNode = new Object[] { packNibbles(copyOfRange(key, 0, matchingLength)), newHash};
|
||||
return this.putToCache(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);
|
||||
return this.putToCache(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
private Object delete(Object node, byte[] key) {
|
||||
|
||||
if (key.length == 0 || isEmptyNode(node)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// 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);
|
||||
|
||||
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.putToCache(newNode);
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
} else {
|
||||
// Copy the current node over to a new node
|
||||
Object[] itemList = copyNode(currentNode);
|
||||
|
||||
// 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.putToCache(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 val = new Value(node);
|
||||
|
||||
// in that case we got a node
|
||||
// so no need to encode it
|
||||
if (!val.isBytes()) {
|
||||
return val;
|
||||
}
|
||||
|
||||
byte[] keyBytes = val.asBytes();
|
||||
if (keyBytes.length == 0) {
|
||||
return val;
|
||||
} else if (keyBytes.length < 32) {
|
||||
return new Value(keyBytes);
|
||||
}
|
||||
return this.cache.get(keyBytes);
|
||||
}
|
||||
|
||||
private Object putToCache(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(TrieImpl 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 TrieImpl copy() {
|
||||
TrieImpl trie = new TrieImpl(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 *
|
||||
*******************************/
|
||||
|
||||
// Created an array of empty elements of required 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() {
|
||||
if (root == null
|
||||
|| (root instanceof byte[] && ((byte[]) root).length == 0)
|
||||
|| (root instanceof String && "".equals((String) root))) {
|
||||
return ByteUtil.EMPTY_BYTE_ARRAY;
|
||||
} else if (root instanceof byte[]) {
|
||||
return (byte[]) this.getRoot();
|
||||
} else {
|
||||
Value rootValue = new Value(this.getRoot());
|
||||
byte[] val = rootValue.encode();
|
||||
return HashUtil.sha3(val);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* insert/delete operations on a Trie structure
|
||||
* leaves unnecessary nodes, this method scans the
|
||||
* cache and removes them. The method is not thread
|
||||
* safe, the tree should not be modified during the
|
||||
* cleaning process.
|
||||
*/
|
||||
public void cleanCacheGarbage() {
|
||||
|
||||
CollectFullSetOfNodes collectAction = new CollectFullSetOfNodes();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
this.scanTree(this.getRootHash(), collectAction);
|
||||
|
||||
Set<byte[]> hashSet = collectAction.getCollectedHashes();
|
||||
Map<ByteArrayWrapper, Node> nodes = this.getCache().getNodes();
|
||||
Set<ByteArrayWrapper> toRemoveSet = new HashSet<>();
|
||||
|
||||
for (ByteArrayWrapper key : nodes.keySet()) {
|
||||
if (!hashSet.contains(key.getData())) {
|
||||
toRemoveSet.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (ByteArrayWrapper key : toRemoveSet) {
|
||||
|
||||
this.getCache().delete(key.getData());
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
logger.trace("Garbage collected node: [ {} ]",
|
||||
Hex.toHexString( key.getData() ));
|
||||
}
|
||||
logger.info("Garbage collected node list, size: [ {} ]", toRemoveSet.size());
|
||||
logger.info("Garbage collection time: [ {}ms ]", System.currentTimeMillis() - startTime);
|
||||
}
|
||||
|
||||
public void scanTree(byte[] hash, ScanAction scanAction) {
|
||||
|
||||
Value node = this.getCache().get(hash);
|
||||
if (node == null) return;
|
||||
|
||||
if (node.isList()) {
|
||||
List<Object> siblings = node.asList();
|
||||
if (siblings.size() == PAIR_SIZE) {
|
||||
Value val = new Value(siblings.get(1));
|
||||
if (val.isHashCode())
|
||||
scanTree(val.asBytes(), scanAction);
|
||||
} else {
|
||||
for (int j = 0; j < LIST_SIZE; ++j) {
|
||||
Value val = new Value(siblings.get(j));
|
||||
if (val.isHashCode())
|
||||
scanTree(val.asBytes(), scanAction);
|
||||
}
|
||||
}
|
||||
scanAction.doOnNode(hash, node);
|
||||
}
|
||||
}
|
||||
|
||||
public String getTrieDump() {
|
||||
|
||||
String root = "";
|
||||
TraceAllNodes traceAction = new TraceAllNodes();
|
||||
this.scanTree(this.getRootHash(), traceAction);
|
||||
|
||||
if (this.getRoot() instanceof Value) {
|
||||
root = "root: " + Hex.toHexString(getRootHash()) + " => " + this.getRoot() + "\n";
|
||||
} else {
|
||||
root = "root: " + Hex.toHexString(getRootHash()) + "\n";
|
||||
}
|
||||
return root + traceAction.getOutput();
|
||||
}
|
||||
|
||||
public interface ScanAction {
|
||||
public void doOnNode(byte[] hash, Value node);
|
||||
}
|
||||
}
|
|
@ -12,14 +12,14 @@ import static org.ethereum.util.CompactEncoder.unpackToNibbles;
|
|||
*/
|
||||
public class TrieIterator {
|
||||
|
||||
private Trie trie;
|
||||
private TrieImpl trie;
|
||||
private String key;
|
||||
private String value;
|
||||
|
||||
private List<byte[]> shas;
|
||||
private List<String> values;
|
||||
|
||||
public TrieIterator(Trie t) {
|
||||
public TrieIterator(TrieImpl t) {
|
||||
this.trie = t;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.io.Serializable;
|
|||
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class DecodeResult implements Serializable {
|
||||
|
||||
private int pos;
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.math.BigInteger;
|
|||
import org.ethereum.crypto.HashUtil;
|
||||
import org.ethereum.db.MockDB;
|
||||
import org.ethereum.trie.Trie;
|
||||
import org.ethereum.trie.TrieImpl;
|
||||
import org.ethereum.util.RLP;
|
||||
import org.junit.Test;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
@ -42,7 +43,7 @@ public class StateTest {
|
|||
|
||||
TransactionReceipt tr = new TransactionReceipt(tx, postTxState, cumGas);
|
||||
|
||||
Trie trie = new Trie(new MockDB());
|
||||
Trie trie = new TrieImpl(new MockDB());
|
||||
trie.update(RLP.encodeInt(0), tr.getEncoded());
|
||||
String txTrieRoot = Hex.toHexString(trie.getRootHash());
|
||||
assertEquals(expected, txTrieRoot);
|
||||
|
@ -160,7 +161,7 @@ public class StateTest {
|
|||
|
||||
private Trie generateGenesisState() {
|
||||
|
||||
Trie trie = new Trie(new MockDB());
|
||||
Trie trie = new TrieImpl(new MockDB());
|
||||
for (String address : Genesis.getPremine()) {
|
||||
AccountState acct = new AccountState(BigInteger.ZERO, BigInteger.valueOf(2).pow(200));
|
||||
trie.update(Hex.decode(address), acct.getEncoded());
|
||||
|
|
|
@ -59,7 +59,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testEmptyKey() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update("", dog);
|
||||
assertEquals(dog, new String(trie.get("")));
|
||||
|
@ -67,7 +67,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testInsertShortString() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, dog);
|
||||
assertEquals(dog, new String(trie.get(cat)));
|
||||
|
@ -75,7 +75,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testInsertLongString() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, LONG_STRING);
|
||||
assertEquals(LONG_STRING, new String(trie.get(cat)));
|
||||
|
@ -83,7 +83,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testInsertMultipleItems1() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update(ca, dude);
|
||||
assertEquals(dude, new String(trie.get(ca)));
|
||||
|
||||
|
@ -109,7 +109,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testInsertMultipleItems2() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, dog);
|
||||
assertEquals(dog, new String(trie.get(cat)));
|
||||
|
@ -136,7 +136,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateShortToShortString() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, dog);
|
||||
assertEquals(dog, new String(trie.get(cat)));
|
||||
|
@ -147,7 +147,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateLongToLongString() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update(cat, LONG_STRING);
|
||||
assertEquals(LONG_STRING, new String(trie.get(cat)));
|
||||
trie.update(cat, LONG_STRING+"1");
|
||||
|
@ -156,7 +156,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateShortToLongString() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, dog);
|
||||
assertEquals(dog, new String(trie.get(cat)));
|
||||
|
@ -167,7 +167,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testUpdateLongToShortString() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, LONG_STRING);
|
||||
assertEquals(LONG_STRING, new String(trie.get(cat)));
|
||||
|
@ -180,7 +180,7 @@ public class TrieTest {
|
|||
public void testDeleteShortString1() {
|
||||
String ROOT_HASH_BEFORE = "a9539c810cc2e8fa20785bdd78ec36cc1dab4b41f0d531e80a5e5fd25c3037ee";
|
||||
String ROOT_HASH_AFTER = "fc5120b4a711bca1f5bb54769525b11b3fb9a8d6ac0b8bf08cbb248770521758";
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, dog);
|
||||
assertEquals(dog, new String(trie.get(cat)));
|
||||
|
@ -198,7 +198,7 @@ public class TrieTest {
|
|||
public void testDeleteShortString2() {
|
||||
String ROOT_HASH_BEFORE = "a9539c810cc2e8fa20785bdd78ec36cc1dab4b41f0d531e80a5e5fd25c3037ee";
|
||||
String ROOT_HASH_AFTER = "b25e1b5be78dbadf6c4e817c6d170bbb47e9916f8f6cc4607c5f3819ce98497b";
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(ca, dude);
|
||||
assertEquals(dude, new String(trie.get(ca)));
|
||||
|
@ -216,7 +216,7 @@ public class TrieTest {
|
|||
public void testDeleteShortString3() {
|
||||
String ROOT_HASH_BEFORE = "778ab82a7e8236ea2ff7bb9cfa46688e7241c1fd445bf2941416881a6ee192eb";
|
||||
String ROOT_HASH_AFTER = "05875807b8f3e735188d2479add82f96dee4db5aff00dc63f07a7e27d0deab65";
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, dude);
|
||||
assertEquals(dude, new String(trie.get(cat)));
|
||||
|
@ -234,7 +234,7 @@ public class TrieTest {
|
|||
public void testDeleteLongString1() {
|
||||
String ROOT_HASH_BEFORE = "318961a1c8f3724286e8e80d312352f01450bc4892c165cc7614e1c2e5a0012a";
|
||||
String ROOT_HASH_AFTER = "63356ecf33b083e244122fca7a9b128cc7620d438d5d62e4f8b5168f1fb0527b";
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, LONG_STRING);
|
||||
assertEquals(LONG_STRING, new String(trie.get(cat)));
|
||||
|
@ -252,7 +252,7 @@ public class TrieTest {
|
|||
public void testDeleteLongString2() {
|
||||
String ROOT_HASH_BEFORE = "e020de34ca26f8d373ff2c0a8ac3a4cb9032bfa7a194c68330b7ac3584a1d388";
|
||||
String ROOT_HASH_AFTER = "334511f0c4897677b782d13a6fa1e58e18de6b24879d57ced430bad5ac831cb2";
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(ca, LONG_STRING);
|
||||
assertEquals(LONG_STRING, new String(trie.get(ca)));
|
||||
|
@ -270,7 +270,7 @@ public class TrieTest {
|
|||
public void testDeleteLongString3() {
|
||||
String ROOT_HASH_BEFORE = "e020de34ca26f8d373ff2c0a8ac3a4cb9032bfa7a194c68330b7ac3584a1d388";
|
||||
String ROOT_HASH_AFTER = "63356ecf33b083e244122fca7a9b128cc7620d438d5d62e4f8b5168f1fb0527b";
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, LONG_STRING);
|
||||
assertEquals(LONG_STRING, new String(trie.get(cat)));
|
||||
|
@ -289,7 +289,7 @@ public class TrieTest {
|
|||
String ROOT_HASH_BEFORE = "3a784eddf1936515f0313b073f99e3bd65c38689021d24855f62a9601ea41717";
|
||||
String ROOT_HASH_AFTER1 = "60a2e75cfa153c4af2783bd6cb48fd6bed84c6381bc2c8f02792c046b46c0653";
|
||||
String ROOT_HASH_AFTER2 = "a84739b4762ddf15e3acc4e6957e5ab2bbfaaef00fe9d436a7369c6f058ec90d";
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, dog);
|
||||
assertEquals(dog, new String(trie.get(cat)));
|
||||
|
@ -322,7 +322,7 @@ public class TrieTest {
|
|||
String ROOT_HASH_AFTER1 = "f586af4a476ba853fca8cea1fbde27cd17d537d18f64269fe09b02aa7fe55a9e";
|
||||
String ROOT_HASH_AFTER2 = "c59fdc16a80b11cc2f7a8b107bb0c954c0d8059e49c760ec3660eea64053ac91";
|
||||
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update(c, LONG_STRING);
|
||||
assertEquals(LONG_STRING, new String(trie.get(c)));
|
||||
|
||||
|
@ -345,7 +345,7 @@ public class TrieTest {
|
|||
@Test
|
||||
public void testDeleteAll() {
|
||||
String ROOT_HASH_BEFORE = "a84739b4762ddf15e3acc4e6957e5ab2bbfaaef00fe9d436a7369c6f058ec90d";
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
assertEquals(ROOT_HASH_EMPTY, Hex.toHexString(trie.getRootHash()));
|
||||
|
||||
trie.update(ca, dude);
|
||||
|
@ -361,8 +361,8 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testTrieCmp() {
|
||||
Trie trie1 = new Trie(mockDb);
|
||||
Trie trie2 = new Trie(mockDb);
|
||||
TrieImpl trie1 = new TrieImpl(mockDb);
|
||||
TrieImpl trie2 = new TrieImpl(mockDb);
|
||||
|
||||
trie1.update(doge, LONG_STRING);
|
||||
trie2.update(doge, LONG_STRING);
|
||||
|
@ -377,7 +377,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testTrieSync() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(dog, LONG_STRING);
|
||||
assertEquals("Expected no data in database", mockDb.getAddedItems(), 0);
|
||||
|
@ -388,7 +388,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void TestTrieDirtyTracking() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update(dog, LONG_STRING);
|
||||
assertTrue("Expected trie to be dirty", trie.getCache().isDirty());
|
||||
|
||||
|
@ -402,7 +402,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void TestTrieReset() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update(cat, LONG_STRING);
|
||||
assertNotEquals("Expected cached nodes", 0, trie.getCache().getNodes().size());
|
||||
|
@ -414,9 +414,9 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testTrieCopy() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update("doe", "reindeer");
|
||||
Trie trie2 = trie.copy();
|
||||
TrieImpl trie2 = trie.copy();
|
||||
assertFalse(trie.equals(trie2)); // avoid possibility that its just a reference copy
|
||||
assertEquals(Hex.toHexString(trie.getRootHash()), Hex.toHexString(trie2.getRootHash()));
|
||||
assertTrue(trie.cmp(trie2));
|
||||
|
@ -424,7 +424,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testTrieUndo() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update("doe", "reindeer");
|
||||
assertEquals("11a0327cfcc5b7689b6b6d727e1f5f8846c1137caaa9fc871ba31b7cce1b703e", Hex.toHexString(trie.getRootHash()));
|
||||
trie.sync();
|
||||
|
@ -440,7 +440,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testSingleItem() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
|
||||
assertEquals("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab", Hex.toHexString(trie.getRootHash()));
|
||||
|
@ -448,7 +448,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testDogs() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update("doe", "reindeer");
|
||||
assertEquals("11a0327cfcc5b7689b6b6d727e1f5f8846c1137caaa9fc871ba31b7cce1b703e", Hex.toHexString(trie.getRootHash()));
|
||||
|
||||
|
@ -461,7 +461,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testPuppy() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update("do", "verb");
|
||||
trie.update("doge", "coin");
|
||||
trie.update("horse", "stallion");
|
||||
|
@ -472,7 +472,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testEmptyValues() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update("do", "verb");
|
||||
trie.update("ether", "wookiedoo");
|
||||
trie.update("horse", "stallion");
|
||||
|
@ -487,7 +487,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testFoo() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
trie.update("foo", "bar");
|
||||
trie.update("food", "bat");
|
||||
trie.update("food", "bass");
|
||||
|
@ -497,7 +497,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testSmallValues() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update("be", "e");
|
||||
trie.update("dog", "puppy");
|
||||
|
@ -507,7 +507,7 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testTesty() {
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update("test", "test");
|
||||
assertEquals("85d106d4edff3b7a4889e91251d0a87d7c17a1dda648ebdba8c6060825be23b8", Hex.toHexString(trie.getRootHash()));
|
||||
|
@ -526,7 +526,7 @@ public class TrieTest {
|
|||
List<String> randomWords = Arrays.asList(randomDictionary.split(","));
|
||||
HashMap<String, String> testerMap = new HashMap<>();
|
||||
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
Random generator = new Random();
|
||||
|
||||
// Random insertion
|
||||
|
@ -583,7 +583,7 @@ public class TrieTest {
|
|||
// *** Part - 1 ***
|
||||
// 1. load the data from massive-upload.dmp
|
||||
// which includes deletes/upadtes (5000 operations)
|
||||
Trie trieSingle = new Trie(mockDb_2);
|
||||
TrieImpl trieSingle = new TrieImpl(mockDb_2);
|
||||
for (int i = 0; i < strData.size() ; ++i){
|
||||
|
||||
String[] keyVal= strData.get(i).split("=");
|
||||
|
@ -603,7 +603,7 @@ public class TrieTest {
|
|||
// 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);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
for (int i = 0; i < 2000; ++i){
|
||||
|
||||
|
@ -618,7 +618,7 @@ public class TrieTest {
|
|||
trie.cleanCacheGarbage();
|
||||
trie.sync();
|
||||
|
||||
Trie trie2 = new Trie(mockDb, trie.getRootHash());
|
||||
TrieImpl trie2 = new TrieImpl(mockDb, trie.getRootHash());
|
||||
|
||||
for (int i = 2000; i < strData.size(); ++i){
|
||||
|
||||
|
@ -644,7 +644,7 @@ public class TrieTest {
|
|||
List<String> randomWords = Arrays.asList(randomDictionary.split(","));
|
||||
HashMap<String, String> testerMap = new HashMap<>();
|
||||
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
Random generator = new Random();
|
||||
|
||||
// Random insertion
|
||||
|
@ -674,7 +674,7 @@ public class TrieTest {
|
|||
Assert.assertEquals(mapWord2, treeWord2);
|
||||
}
|
||||
|
||||
Trie trie2 = new Trie(mockDb, trie.getRootHash());
|
||||
TrieImpl trie2 = new TrieImpl(mockDb, trie.getRootHash());
|
||||
|
||||
// Assert the result now
|
||||
keys = testerMap.keySet().iterator();
|
||||
|
@ -693,7 +693,7 @@ public class TrieTest {
|
|||
@Test
|
||||
public void testRollbackTrie() throws URISyntaxException, IOException {
|
||||
|
||||
Trie trieSingle = new Trie(mockDb);
|
||||
TrieImpl trieSingle = new TrieImpl(mockDb);
|
||||
|
||||
URL massiveUpload_1 = ClassLoader
|
||||
.getSystemResource("trie/massive-upload.dmp");
|
||||
|
@ -745,10 +745,10 @@ public class TrieTest {
|
|||
|
||||
@Test
|
||||
public void testGetFromRootNode() {
|
||||
Trie trie1 = new Trie(mockDb);
|
||||
TrieImpl trie1 = new TrieImpl(mockDb);
|
||||
trie1.update(cat, LONG_STRING);
|
||||
trie1.sync();
|
||||
Trie trie2 = new Trie(mockDb, trie1.getRootHash());
|
||||
TrieImpl trie2 = new TrieImpl(mockDb, trie1.getRootHash());
|
||||
assertEquals(LONG_STRING, new String(trie2.get(cat)));
|
||||
}
|
||||
|
||||
|
@ -774,7 +774,7 @@ public class TrieTest {
|
|||
byte[] val3 = Hex.decode("94412e0c4f0102f3f0ac63f0a125bce36ca75d4e0d");
|
||||
byte[] val4 = Hex.decode("01");
|
||||
|
||||
Trie storage = new Trie(new org.ethereum.db.MockDB());
|
||||
TrieImpl storage = new TrieImpl(new org.ethereum.db.MockDB());
|
||||
storage.update(key1, val1);
|
||||
storage.update(key2, val2);
|
||||
storage.update(key3, val3);
|
||||
|
@ -815,7 +815,7 @@ public class TrieTest {
|
|||
|
||||
// TEST: load trie out of this run up to block#33
|
||||
byte[] rootNode = Hex.decode("bb690805d24882bc7ccae6fc0f80ac146274d5b81c6a6e9c882cd9b0a649c9c7");
|
||||
Trie trie = new Trie(db.getDb(), rootNode);
|
||||
TrieImpl trie = new TrieImpl(db.getDb(), rootNode);
|
||||
|
||||
// first key added in genesis
|
||||
byte[] val1 = trie.get(Hex.decode("51ba59315b3a95761d0863b05ccc7a7f54703d99"));
|
||||
|
@ -844,7 +844,7 @@ public class TrieTest {
|
|||
// each time dump the entire trie
|
||||
public void testSample_1(){
|
||||
|
||||
Trie trie = new Trie(mockDb);
|
||||
TrieImpl trie = new TrieImpl(mockDb);
|
||||
|
||||
trie.update("dog", "puppy");
|
||||
String dmp = trie.getTrieDump();
|
||||
|
|
|
@ -6,7 +6,6 @@ import org.ethereum.vm.Program.OutOfGasException;
|
|||
import org.ethereum.vm.Program.PcOverflowException;
|
||||
import org.ethereum.vm.Program.StackTooSmallException;
|
||||
import org.junit.FixMethodOrder;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runners.MethodSorters;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
@ -1524,7 +1523,6 @@ public class VMTest {
|
|||
}
|
||||
|
||||
@Test // SSTORE OP
|
||||
@Ignore
|
||||
public void testSSTORE_1() {
|
||||
|
||||
VM vm = new VM();
|
||||
|
@ -1546,7 +1544,6 @@ public class VMTest {
|
|||
}
|
||||
|
||||
@Test // SSTORE OP
|
||||
@Ignore
|
||||
public void testSSTORE_2() {
|
||||
|
||||
VM vm = new VM();
|
||||
|
@ -1586,7 +1583,6 @@ public class VMTest {
|
|||
}
|
||||
|
||||
@Test // SLOAD OP
|
||||
@Ignore
|
||||
public void testSLOAD_1() {
|
||||
|
||||
VM vm = new VM();
|
||||
|
@ -1601,7 +1597,6 @@ public class VMTest {
|
|||
}
|
||||
|
||||
@Test // SLOAD OP
|
||||
@Ignore
|
||||
public void testSLOAD_2() {
|
||||
|
||||
VM vm = new VM();
|
||||
|
@ -1619,7 +1614,6 @@ public class VMTest {
|
|||
}
|
||||
|
||||
@Test // SLOAD OP
|
||||
@Ignore
|
||||
public void testSLOAD_3() {
|
||||
|
||||
VM vm = new VM();
|
||||
|
@ -1667,7 +1661,6 @@ public class VMTest {
|
|||
|
||||
|
||||
@Test // PC OP
|
||||
@Ignore
|
||||
public void testPC_2() {
|
||||
|
||||
VM vm = new VM();
|
||||
|
|
Loading…
Reference in New Issue