Merge pull request #13 from nicksavers/master
Fix Trie to generate the expected rootHash
This commit is contained in:
commit
7645262d55
|
@ -1,6 +1,5 @@
|
||||||
package org.ethereum.gui;
|
package org.ethereum.gui;
|
||||||
|
|
||||||
import org.ethereum.config.SystemProperties;
|
|
||||||
import org.ethereum.core.Transaction;
|
import org.ethereum.core.Transaction;
|
||||||
import org.ethereum.manager.MainData;
|
import org.ethereum.manager.MainData;
|
||||||
import org.ethereum.net.client.ClientPeer;
|
import org.ethereum.net.client.ClientPeer;
|
||||||
|
@ -9,26 +8,19 @@ import org.ethereum.net.submit.TransactionTask;
|
||||||
import org.ethereum.wallet.AddressState;
|
import org.ethereum.wallet.AddressState;
|
||||||
import org.spongycastle.util.BigIntegers;
|
import org.spongycastle.util.BigIntegers;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
import samples.Main;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.*;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.Border;
|
|
||||||
import javax.swing.border.CompoundBorder;
|
|
||||||
import javax.swing.border.EmptyBorder;
|
|
||||||
|
|
||||||
import static org.ethereum.config.SystemProperties.CONFIG;
|
import static org.ethereum.config.SystemProperties.CONFIG;
|
||||||
|
|
||||||
|
@ -44,11 +36,11 @@ class PayOutDialog extends JDialog {
|
||||||
AddressState addressState = null;
|
AddressState addressState = null;
|
||||||
JLabel statusMsg = null;
|
JLabel statusMsg = null;
|
||||||
|
|
||||||
public PayOutDialog(Frame parent, final AddressState addressState) {
|
public PayOutDialog(Frame parent, final AddressState addressState) {
|
||||||
super(parent, "Payout details: ", false);
|
super(parent, "Payout details: ", false);
|
||||||
dialog = this;
|
dialog = this;
|
||||||
|
|
||||||
this.addressState = addressState;
|
this.addressState = addressState;
|
||||||
|
|
||||||
final JTextField receiverInput = new JTextField(18);
|
final JTextField receiverInput = new JTextField(18);
|
||||||
GUIUtils.addStyle(receiverInput, "Pay to:");
|
GUIUtils.addStyle(receiverInput, "Pay to:");
|
||||||
|
@ -86,14 +78,12 @@ class PayOutDialog extends JDialog {
|
||||||
rejectLabel.setBounds(260, 145, 45, 45);
|
rejectLabel.setBounds(260, 145, 45, 45);
|
||||||
this.getContentPane().add(rejectLabel);
|
this.getContentPane().add(rejectLabel);
|
||||||
rejectLabel.setVisible(true);
|
rejectLabel.setVisible(true);
|
||||||
rejectLabel.addMouseListener(
|
rejectLabel.addMouseListener(new MouseAdapter() {
|
||||||
new MouseAdapter() {
|
@Override
|
||||||
@Override
|
public void mouseClicked(MouseEvent e) {
|
||||||
public void mouseClicked(MouseEvent e) {
|
dialog.dispose();
|
||||||
|
}
|
||||||
dialog.dispose();
|
});
|
||||||
}}
|
|
||||||
);
|
|
||||||
|
|
||||||
URL approveIconURL = ClassLoader.getSystemResource("buttons/approve.png");
|
URL approveIconURL = ClassLoader.getSystemResource("buttons/approve.png");
|
||||||
ImageIcon approveIcon = new ImageIcon(approveIconURL);
|
ImageIcon approveIcon = new ImageIcon(approveIconURL);
|
||||||
|
@ -105,53 +95,45 @@ class PayOutDialog extends JDialog {
|
||||||
this.getContentPane().add(approveLabel);
|
this.getContentPane().add(approveLabel);
|
||||||
approveLabel.setVisible(true);
|
approveLabel.setVisible(true);
|
||||||
|
|
||||||
approveLabel.addMouseListener(
|
approveLabel.addMouseListener(new MouseAdapter() {
|
||||||
new MouseAdapter() {
|
@Override
|
||||||
@Override
|
public void mouseClicked(MouseEvent e) {
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
|
|
||||||
|
BigInteger fee = new BigInteger(feeInput.getText());
|
||||||
|
BigInteger value = new BigInteger(amountInput.getText());
|
||||||
|
byte[] address = Hex.decode(receiverInput.getText());
|
||||||
|
|
||||||
BigInteger fee = new BigInteger(feeInput.getText());
|
// Client
|
||||||
BigInteger value = new BigInteger(amountInput.getText());
|
ClientPeer peer = MainData.instance.getActivePeer();
|
||||||
byte[] address = Hex.decode( receiverInput.getText());
|
|
||||||
|
|
||||||
|
if (peer == null) {
|
||||||
|
dialog.alertStatusMsg("Not connected to any peer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Client
|
byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes();
|
||||||
ClientPeer peer = MainData.instance.getActivePeer();
|
|
||||||
|
|
||||||
if (peer == null){
|
byte[] nonce = addressState.getNonce() == BigInteger.ZERO ? null : addressState.getNonce().toByteArray();
|
||||||
dialog.alertStatusMsg("Not connected to any peer");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] senderPrivKey = addressState.getEcKey().getPrivKeyBytes();
|
// todo: in the future it should be retrieved from the block
|
||||||
|
byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
|
||||||
|
|
||||||
byte[] nonce = addressState.getNonce() == BigInteger.ZERO ?
|
Transaction tx = new Transaction(nonce, gasPrice, BigIntegers
|
||||||
null : addressState.getNonce().toByteArray();
|
.asUnsignedByteArray(fee), address, BigIntegers
|
||||||
|
.asUnsignedByteArray(value), null);
|
||||||
|
|
||||||
// todo: in the future it should be retrieved from the block
|
try {
|
||||||
byte[] gasPrice = new BigInteger("10000000000000").toByteArray();
|
tx.sign(senderPrivKey);
|
||||||
|
} catch (Exception e1) {
|
||||||
|
|
||||||
Transaction tx = new Transaction(nonce, gasPrice,
|
dialog.alertStatusMsg("Failed to sign the transaction");
|
||||||
BigIntegers.asUnsignedByteArray(fee),
|
return;
|
||||||
address,
|
}
|
||||||
BigIntegers.asUnsignedByteArray(value), null);
|
// SwingWorker
|
||||||
|
DialogWorker worker = new DialogWorker(tx);
|
||||||
try {
|
worker.execute();
|
||||||
tx.sign(senderPrivKey);
|
}
|
||||||
} catch (Exception e1) {
|
});
|
||||||
|
|
||||||
dialog.alertStatusMsg("Failed to sign the transaction");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwingWorker
|
|
||||||
|
|
||||||
DialogWorker worker = new DialogWorker(tx);
|
|
||||||
worker.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
feeInput.setText("1000");
|
feeInput.setText("1000");
|
||||||
amountInput.setText("0");
|
amountInput.setText("0");
|
||||||
|
@ -185,7 +167,6 @@ class PayOutDialog extends JDialog {
|
||||||
this.setSize(500, 255);
|
this.setSize(500, 255);
|
||||||
this.setVisible(true);
|
this.setVisible(true);
|
||||||
|
|
||||||
|
|
||||||
return rootPane;
|
return rootPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,8 +198,7 @@ class PayOutDialog extends JDialog {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DialogWorker extends SwingWorker {
|
||||||
class DialogWorker extends SwingWorker{
|
|
||||||
|
|
||||||
Transaction tx;
|
Transaction tx;
|
||||||
|
|
||||||
|
@ -252,17 +232,12 @@ class PayOutDialog extends JDialog {
|
||||||
MainData.instance.getWallet().applyTransaction(tx);
|
MainData.instance.getWallet().applyTransaction(tx);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
|
|
||||||
AddressState as = new AddressState();
|
AddressState as = new AddressState();
|
||||||
|
|
||||||
PayOutDialog pod = new PayOutDialog(null, as);
|
PayOutDialog pod = new PayOutDialog(null, as);
|
||||||
pod.setVisible(true);
|
pod.setVisible(true);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.ethereum.crypto.HashUtil;
|
||||||
import org.ethereum.util.Value;
|
import org.ethereum.util.Value;
|
||||||
import org.iq80.leveldb.DB;
|
import org.iq80.leveldb.DB;
|
||||||
import org.iq80.leveldb.Options;
|
import org.iq80.leveldb.Options;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
|
|
||||||
public class Cache {
|
public class Cache {
|
||||||
|
|
||||||
|
@ -40,8 +39,8 @@ public class Cache {
|
||||||
Value value = new Value(o);
|
Value value = new Value(o);
|
||||||
byte[] enc = value.encode();
|
byte[] enc = value.encode();
|
||||||
if (enc.length >= 32) {
|
if (enc.length >= 32) {
|
||||||
byte[] sha = Hex.encode(HashUtil.sha3(enc));
|
byte[] sha = HashUtil.sha3(enc);
|
||||||
this.nodes.put(sha, new Node(sha, value, true));
|
this.nodes.put(sha, new Node(value, true));
|
||||||
this.isDirty = true;
|
this.isDirty = true;
|
||||||
return sha;
|
return sha;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +56,7 @@ public class Cache {
|
||||||
byte[] data = this.db.get(key);
|
byte[] data = this.db.get(key);
|
||||||
Value value = new Value(data);
|
Value value = new Value(data);
|
||||||
// Create caching node
|
// Create caching node
|
||||||
this.nodes.put(key, new Node(key, value, false));
|
this.nodes.put(key, new Node(value, false));
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,13 @@ package org.ethereum.trie;
|
||||||
|
|
||||||
import org.ethereum.util.Value;
|
import org.ethereum.util.Value;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Node in a Merkle Patricia Tree is one of the following:
|
* A Node in a Merkle Patricia Tree is one of the following:
|
||||||
*
|
*
|
||||||
* - NULL (represented as the empty string)
|
* - NULL (represented as the empty string)
|
||||||
* - A two-item array [ key, value ]
|
* - A two-item array [ key, value ] (1 key for 2-item array)
|
||||||
* - A 17-item array [ v0 ... v15, vt ]
|
* - A 17-item array [ v0 ... v15, vt ] (16 keys for 17-item array)
|
||||||
*
|
*
|
||||||
* The idea is that in the event that there is a long path of nodes
|
* The idea is that in the event that there is a long path of nodes
|
||||||
* each with only one element, we shortcut the descent by setting up
|
* each with only one element, we shortcut the descent by setting up
|
||||||
|
@ -21,41 +22,40 @@ import org.ethereum.util.Value;
|
||||||
* N N
|
* N N
|
||||||
* / \ / \
|
* / \ / \
|
||||||
* L L L L
|
* L L L L
|
||||||
*
|
* *
|
||||||
*
|
|
||||||
* Also, we add another conceptual change: internal nodes can no longer
|
* Also, we add another conceptual change: internal nodes can no longer
|
||||||
* have values, only leaves with no children of their own can; however,
|
* have values, only leaves with no children of their own can; however,
|
||||||
* since to be fully generic we want the key/value store to be able to
|
* since to be fully generic we want the key/value store to be able to
|
||||||
* store keys like 'dog' and 'doge' at the same time, we simply add
|
* store keys like 'dog' and 'doge' at the same time, we simply add
|
||||||
* a terminator symbol (16) to the alphabet so there is never a value
|
* a terminator symbol (16) to the alphabet so there is never a value
|
||||||
* "en-route" to another value. Where a node is referenced inside a node,
|
* "en-route" to another value.
|
||||||
* what is included is H(rlp.encode(x)) where H(x) = sha3(x) if len(x) >= 32 else x
|
*
|
||||||
* and rlp.encode is the RLP encoding function.
|
* Where a node is referenced inside a node, what is included is:
|
||||||
|
*
|
||||||
|
* H(rlp.encode(x)) where H(x) = sha3(x) if len(x) >= 32 else x
|
||||||
*
|
*
|
||||||
* Note that when updating a trie, you will need to store the key/value pair (sha3(x), x)
|
* Note that when updating a trie, you will need to store the key/value pair (sha3(x), x)
|
||||||
* in a persistent lookup table when you create a node with length >= 32,
|
* in a persistent lookup table when you create a node with length >= 32,
|
||||||
* but if the node is shorter than that then you do not need to store anything
|
* but if the node is shorter than that then you do not need to store anything
|
||||||
* when length < 32 for the obvious reason that the function f(x) = x is reversible.
|
* when length < 32 for the obvious reason that the function f(x) = x is reversible.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class Node {
|
public class Node {
|
||||||
|
|
||||||
private byte[] key; // 1 key for 2-item array, 16 keys for 17-item array
|
/* RLP encoded value of the Trie-node */
|
||||||
private Value value;
|
private Value value;
|
||||||
private boolean dirty;
|
private boolean dirty;
|
||||||
|
|
||||||
public Node(byte[] key, Value val) {
|
public Node(Value val) {
|
||||||
this(key, val, false);
|
this(val, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Node(byte[] key, Value val, boolean dirty) {
|
public Node(Value val, boolean dirty) {
|
||||||
this.key = key;
|
|
||||||
this.value = val;
|
this.value = val;
|
||||||
this.dirty = dirty;
|
this.dirty = dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Node copy() {
|
public Node copy() {
|
||||||
return new Node(this.key.clone(), this.value, this.dirty);
|
return new Node(this.value, this.dirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDirty() {
|
public boolean isDirty() {
|
||||||
|
@ -66,15 +66,7 @@ public class Node {
|
||||||
this.dirty = ditry;
|
this.dirty = ditry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Value getValue() {
|
public Value getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] getItems() {
|
|
||||||
return new Object[] { key, value };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,26 +2,37 @@ package org.ethereum.trie;
|
||||||
|
|
||||||
import static java.util.Arrays.copyOfRange;
|
import static java.util.Arrays.copyOfRange;
|
||||||
import static org.spongycastle.util.Arrays.concatenate;
|
import static org.spongycastle.util.Arrays.concatenate;
|
||||||
|
import static org.ethereum.util.CompactEncoder.*;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.ethereum.util.CompactEncoder;
|
import org.ethereum.crypto.HashUtil;
|
||||||
import org.ethereum.util.Value;
|
import org.ethereum.util.Value;
|
||||||
import org.iq80.leveldb.DB;
|
import org.iq80.leveldb.DB;
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import com.cedarsoftware.util.DeepEquals;
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Please note that the data isn't persisted unless `sync` is explicitly called.
|
||||||
|
*/
|
||||||
public class Trie {
|
public class Trie {
|
||||||
|
|
||||||
private static byte PAIR_SIZE = 2;
|
private static byte PAIR_SIZE = 2;
|
||||||
private static byte LIST_SIZE = 17;
|
private static byte LIST_SIZE = 17;
|
||||||
|
|
||||||
// A (modified) Radix Trie implementation. The Trie implements
|
|
||||||
// a caching mechanism and will used 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.
|
|
||||||
private Object prevRoot;
|
private Object prevRoot;
|
||||||
private Object root;
|
private Object root;
|
||||||
private Cache cache;
|
private Cache cache;
|
||||||
|
@ -73,7 +84,7 @@ public class Trie {
|
||||||
public void update(String key, String value) {
|
public void update(String key, String value) {
|
||||||
if (key == null)
|
if (key == null)
|
||||||
throw new NullPointerException("Key should not be blank");
|
throw new NullPointerException("Key should not be blank");
|
||||||
byte[] k = CompactEncoder.hexDecode(key.getBytes());
|
byte[] k = binToNibbles(key.getBytes());
|
||||||
this.root = this.insertOrDelete(this.root, k, value.getBytes());
|
this.root = this.insertOrDelete(this.root, k, value.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +95,7 @@ public class Trie {
|
||||||
* @return value
|
* @return value
|
||||||
*/
|
*/
|
||||||
public String get(String key) {
|
public String get(String key) {
|
||||||
byte[] k = CompactEncoder.hexDecode(key.getBytes());
|
byte[] k = binToNibbles(key.getBytes());
|
||||||
Value c = new Value( this.get(this.root, k) );
|
Value c = new Value( this.get(this.root, k) );
|
||||||
return c.asString();
|
return c.asString();
|
||||||
}
|
}
|
||||||
|
@ -110,11 +121,10 @@ public class Trie {
|
||||||
}
|
}
|
||||||
|
|
||||||
Value currentNode = this.getNode(node);
|
Value currentNode = this.getNode(node);
|
||||||
int length = currentNode.length();
|
|
||||||
|
if (currentNode.length() == PAIR_SIZE) {
|
||||||
if (length == PAIR_SIZE) {
|
|
||||||
// Decode the key
|
// Decode the key
|
||||||
byte[] k = CompactEncoder.decode(currentNode.get(0).asBytes());
|
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||||
Object v = currentNode.get(1).asObj();
|
Object v = currentNode.get(1).asObj();
|
||||||
|
|
||||||
if (key.length >= k.length && Arrays.equals(k, copyOfRange(key, 0, k.length))) {
|
if (key.length >= k.length && Arrays.equals(k, copyOfRange(key, 0, k.length))) {
|
||||||
|
@ -122,12 +132,9 @@ public class Trie {
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
} else if (length == LIST_SIZE) {
|
} else {
|
||||||
return this.get(currentNode.get(key[0]).asObj(), copyOfRange(key, 1, key.length));
|
return this.get(currentNode.get(key[0]).asObj(), copyOfRange(key, 1, key.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
// It shouldn't come this far
|
|
||||||
throw new RuntimeException("Unexpected Node length: " + length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object insertOrDelete(Object node, byte[] key, byte[] value) {
|
private Object insertOrDelete(Object node, byte[] key, byte[] value) {
|
||||||
|
@ -149,7 +156,7 @@ public class Trie {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEmptyNode(node)) {
|
if (isEmptyNode(node)) {
|
||||||
Object[] newNode = new Object[] { CompactEncoder.encode(key), value };
|
Object[] newNode = new Object[] { packNibbles(key), value };
|
||||||
return this.put(newNode);
|
return this.put(newNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,13 +165,12 @@ public class Trie {
|
||||||
// Check for "special" 2 slice type node
|
// Check for "special" 2 slice type node
|
||||||
if (currentNode.length() == PAIR_SIZE) {
|
if (currentNode.length() == PAIR_SIZE) {
|
||||||
// Decode the key
|
// Decode the key
|
||||||
|
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||||
byte[] k = CompactEncoder.decode(currentNode.get(0).asBytes());
|
|
||||||
Object v = currentNode.get(1).asObj();
|
Object v = currentNode.get(1).asObj();
|
||||||
|
|
||||||
// Matching key pair (ie. there's already an object with this key)
|
// Matching key pair (ie. there's already an object with this key)
|
||||||
if (Arrays.equals(k, key)) {
|
if (Arrays.equals(k, key)) {
|
||||||
Object[] newNode = new Object[] {CompactEncoder.encode(key), value};
|
Object[] newNode = new Object[] {packNibbles(key), value};
|
||||||
return this.put(newNode);
|
return this.put(newNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,9 +178,10 @@ public class Trie {
|
||||||
int matchingLength = matchingNibbleLength(key, k);
|
int matchingLength = matchingNibbleLength(key, k);
|
||||||
if (matchingLength == k.length) {
|
if (matchingLength == k.length) {
|
||||||
// Insert the hash, creating a new node
|
// Insert the hash, creating a new node
|
||||||
newHash = this.insert(v, copyOfRange(key, matchingLength, key.length), value);
|
byte[] remainingKeypart = copyOfRange(key, matchingLength, key.length);
|
||||||
} else {
|
newHash = this.insert(v, remainingKeypart, value);
|
||||||
// Expand the 2 length slice to a 17 length slice
|
} 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 oldNode = this.insert("", copyOfRange(k, matchingLength+1, k.length), v);
|
||||||
Object newNode = this.insert("", copyOfRange(key, matchingLength+1, key.length), value);
|
Object newNode = this.insert("", copyOfRange(key, matchingLength+1, key.length), value);
|
||||||
// Create an expanded slice
|
// Create an expanded slice
|
||||||
|
@ -189,7 +196,7 @@ public class Trie {
|
||||||
// End of the chain, return
|
// End of the chain, return
|
||||||
return newHash;
|
return newHash;
|
||||||
} else {
|
} else {
|
||||||
Object[] newNode = new Object[] {CompactEncoder.encode(copyOfRange(key, 0, matchingLength)), newHash};
|
Object[] newNode = new Object[] { packNibbles(copyOfRange(key, 0, matchingLength)), newHash};
|
||||||
return this.put(newNode);
|
return this.put(newNode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -213,7 +220,7 @@ public class Trie {
|
||||||
// Check for "special" 2 slice type node
|
// Check for "special" 2 slice type node
|
||||||
if (currentNode.length() == PAIR_SIZE) {
|
if (currentNode.length() == PAIR_SIZE) {
|
||||||
// Decode the key
|
// Decode the key
|
||||||
byte[] k = CompactEncoder.decode(currentNode.get(0).asBytes());
|
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||||
Object v = currentNode.get(1).asObj();
|
Object v = currentNode.get(1).asObj();
|
||||||
|
|
||||||
// Matching key pair (ie. there's already an object with this key)
|
// Matching key pair (ie. there's already an object with this key)
|
||||||
|
@ -225,8 +232,8 @@ public class Trie {
|
||||||
|
|
||||||
Object newNode;
|
Object newNode;
|
||||||
if (child.length() == PAIR_SIZE) {
|
if (child.length() == PAIR_SIZE) {
|
||||||
byte[] newKey = concatenate(k, CompactEncoder.decode(child.get(0).asBytes()));
|
byte[] newKey = concatenate(k, unpackToNibbles(child.get(0).asBytes()));
|
||||||
newNode = new Object[] {CompactEncoder.encode(newKey), child.get(1).asObj()};
|
newNode = new Object[] {packNibbles(newKey), child.get(1).asObj()};
|
||||||
} else {
|
} else {
|
||||||
newNode = new Object[] {currentNode.get(0).asString(), hash};
|
newNode = new Object[] {currentNode.get(0).asString(), hash};
|
||||||
}
|
}
|
||||||
|
@ -240,6 +247,7 @@ public class Trie {
|
||||||
|
|
||||||
// Replace the first nibble in the key
|
// Replace the first nibble in the key
|
||||||
itemList[key[0]] = this.delete(itemList[key[0]], copyOfRange(key, 1, key.length));
|
itemList[key[0]] = this.delete(itemList[key[0]], copyOfRange(key, 1, key.length));
|
||||||
|
|
||||||
byte amount = -1;
|
byte amount = -1;
|
||||||
for (byte i = 0; i < LIST_SIZE; i++) {
|
for (byte i = 0; i < LIST_SIZE; i++) {
|
||||||
if (itemList[i] != "") {
|
if (itemList[i] != "") {
|
||||||
|
@ -253,14 +261,14 @@ public class Trie {
|
||||||
|
|
||||||
Object[] newNode = null;
|
Object[] newNode = null;
|
||||||
if (amount == 16) {
|
if (amount == 16) {
|
||||||
newNode = new Object[] { CompactEncoder.encode(new byte[] {16} ), itemList[amount]};
|
newNode = new Object[] { packNibbles(new byte[] {16} ), itemList[amount]};
|
||||||
} else if (amount >= 0) {
|
} else if (amount >= 0) {
|
||||||
Value child = this.getNode(itemList[amount]);
|
Value child = this.getNode(itemList[amount]);
|
||||||
if (child.length() == PAIR_SIZE) {
|
if (child.length() == PAIR_SIZE) {
|
||||||
key = concatenate(new byte[]{amount}, CompactEncoder.decode(child.get(0).asBytes()));
|
key = concatenate(new byte[]{amount}, unpackToNibbles(child.get(0).asBytes()));
|
||||||
newNode = new Object[] {CompactEncoder.encode(key), child.get(1).asObj()};
|
newNode = new Object[] {packNibbles(key), child.get(1).asObj()};
|
||||||
} else if (child.length() == LIST_SIZE) {
|
} else if (child.length() == LIST_SIZE) {
|
||||||
newNode = new Object[] { CompactEncoder.encode(new byte[]{amount}), itemList[amount]};
|
newNode = new Object[] { packNibbles(new byte[]{amount}), itemList[amount]};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newNode = itemList;
|
newNode = itemList;
|
||||||
|
@ -276,30 +284,23 @@ public class Trie {
|
||||||
* @param node
|
* @param node
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private Value getNode(Object node) {
|
private Value getNode(Object node) {
|
||||||
Value n = new Value(node);
|
Value n = new Value(node);
|
||||||
|
|
||||||
if (!n.get(0).isNull()) {
|
if (!n.get(0).isNull()) {
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
String str = n.asString();
|
byte[] str = n.asBytes();
|
||||||
if (str.length() == 0) {
|
if (str.length == 0) {
|
||||||
return n;
|
return n;
|
||||||
} else if (str.length() < 32) {
|
} else if (str.length < 32) {
|
||||||
return new Value(str.getBytes());
|
return new Value(str);
|
||||||
}
|
}
|
||||||
return this.cache.get(n.asBytes());
|
return this.cache.get(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object put(Object node) {
|
private Object put(Object node) {
|
||||||
/* TODO?
|
|
||||||
c := Conv(t.Root)
|
|
||||||
fmt.Println(c.Type(), c.Length())
|
|
||||||
if c.Type() == reflect.String && c.AsString() == "" {
|
|
||||||
return enc
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return this.cache.put(node);
|
return this.cache.put(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,9 +320,9 @@ public class Trie {
|
||||||
return itemList;
|
return itemList;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple compare function which creates a rlp value out of the evaluated objects
|
// Simple compare function which compared the tries based on their stateRoot
|
||||||
public boolean cmp(Trie trie) {
|
public boolean cmp(Trie trie) {
|
||||||
return DeepEquals.deepEquals(this.root, trie.getRoot());
|
return this.getRootHash().equals(trie.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the cached value to the database.
|
// Save the cached value to the database.
|
||||||
|
@ -358,6 +359,7 @@ public class Trie {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Created an array of empty elements of requred length
|
||||||
private Object[] emptyStringSlice(int l) {
|
private Object[] emptyStringSlice(int l) {
|
||||||
Object[] slice = new Object[l];
|
Object[] slice = new Object[l];
|
||||||
for (int i = 0; i < l; i++) {
|
for (int i = 0; i < l; i++) {
|
||||||
|
@ -365,4 +367,20 @@ public class Trie {
|
||||||
}
|
}
|
||||||
return slice;
|
return slice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRootHash() {
|
||||||
|
Object root = this.getRoot();
|
||||||
|
if (root == null
|
||||||
|
|| (root instanceof byte[] && ((byte[]) root).length == 0)
|
||||||
|
|| (root instanceof String && "".equals((String) root))) {
|
||||||
|
return "";
|
||||||
|
} else if (root instanceof byte[]) {
|
||||||
|
return Hex.toHexString((byte[])this.getRoot());
|
||||||
|
} else {
|
||||||
|
Value rootValue = new Value(this.getRoot());
|
||||||
|
byte[] val = rootValue.encode();
|
||||||
|
byte[] key = HashUtil.sha3(val);
|
||||||
|
return Hex.toHexString(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ package org.ethereum.trie;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.ethereum.util.CompactEncoder;
|
|
||||||
import org.ethereum.util.Value;
|
import org.ethereum.util.Value;
|
||||||
|
import static org.ethereum.util.CompactEncoder.unpackToNibbles;
|
||||||
|
|
||||||
public class TrieIterator {
|
public class TrieIterator {
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ public class TrieIterator {
|
||||||
// XXX Note to self, IsSlice == inline node. Str == sha3 to node
|
// XXX Note to self, IsSlice == inline node. Str == sha3 to node
|
||||||
private void workNode(Value currentNode) {
|
private void workNode(Value currentNode) {
|
||||||
if (currentNode.length() == 2) {
|
if (currentNode.length() == 2) {
|
||||||
byte[] k = CompactEncoder.decode(currentNode.get(0).asBytes());
|
byte[] k = unpackToNibbles(currentNode.get(0).asBytes());
|
||||||
|
|
||||||
if (currentNode.get(1).asString() == "") {
|
if (currentNode.get(1).asString() == "") {
|
||||||
this.workNode(currentNode.get(1));
|
this.workNode(currentNode.get(1));
|
||||||
|
|
|
@ -9,8 +9,7 @@ import static org.ethereum.util.ByteUtil.appendByte;
|
||||||
import static org.spongycastle.util.Arrays.concatenate;
|
import static org.spongycastle.util.Arrays.concatenate;
|
||||||
import static org.spongycastle.util.encoders.Hex.toHexString;
|
import static org.spongycastle.util.encoders.Hex.toHexString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Compact encoding of hex sequence with optional terminator
|
* Compact encoding of hex sequence with optional terminator
|
||||||
*
|
*
|
||||||
* The traditional compact way of encoding a hex string is to convert it into binary
|
* The traditional compact way of encoding a hex string is to convert it into binary
|
||||||
|
@ -49,36 +48,43 @@ public class CompactEncoder {
|
||||||
private final static byte TERMINATOR = 16;
|
private final static byte TERMINATOR = 16;
|
||||||
private final static String hexBase = "0123456789abcdef";
|
private final static String hexBase = "0123456789abcdef";
|
||||||
|
|
||||||
public static byte[] encode(byte[] hexSlice) {
|
/**
|
||||||
|
* Pack nibbles to binary
|
||||||
|
*
|
||||||
|
* @param nibbles sequence. may have a terminator
|
||||||
|
* @return hex-encoded byte array
|
||||||
|
*/
|
||||||
|
public static byte[] packNibbles(byte[] nibbles) {
|
||||||
int terminator = 0;
|
int terminator = 0;
|
||||||
|
|
||||||
if (hexSlice[hexSlice.length-1] == TERMINATOR) {
|
if (nibbles[nibbles.length-1] == TERMINATOR) {
|
||||||
terminator = 1;
|
terminator = 1;
|
||||||
hexSlice = copyOf(hexSlice, hexSlice.length-1);
|
nibbles = copyOf(nibbles, nibbles.length-1);
|
||||||
}
|
}
|
||||||
|
int oddlen = nibbles.length % 2;
|
||||||
int oddlen = hexSlice.length % 2;
|
|
||||||
int flag = 2*terminator + oddlen;
|
int flag = 2*terminator + oddlen;
|
||||||
if (oddlen != 0) {
|
if (oddlen != 0) {
|
||||||
byte[] flags = new byte[] { (byte) flag};
|
byte[] flags = new byte[] { (byte) flag};
|
||||||
hexSlice = concatenate(flags, hexSlice);
|
nibbles = concatenate(flags, nibbles);
|
||||||
} else {
|
} else {
|
||||||
byte[] flags = new byte[] { (byte) flag, 0};
|
byte[] flags = new byte[] { (byte) flag, 0};
|
||||||
hexSlice = concatenate(flags, hexSlice);
|
nibbles = concatenate(flags, nibbles);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
for (int i = 0; i < hexSlice.length; i += 2) {
|
for (int i = 0; i < nibbles.length; i += 2) {
|
||||||
buffer.write( 16*hexSlice[i] + hexSlice[i+1] );
|
buffer.write( 16*nibbles[i] + nibbles[i+1] );
|
||||||
}
|
}
|
||||||
return buffer.toByteArray();
|
return buffer.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strips hex slices
|
* Unpack a binary string to its nibbles equivalent
|
||||||
|
*
|
||||||
|
* @param string of binary data
|
||||||
|
* @return array of nibbles in byte-format
|
||||||
*/
|
*/
|
||||||
public static byte[] decode(byte[] str) {
|
public static byte[] unpackToNibbles(byte[] str) {
|
||||||
byte[] base = hexDecode(str);
|
byte[] base = binToNibbles(str);
|
||||||
base = copyOf(base, base.length-1);
|
base = copyOf(base, base.length-1);
|
||||||
if (base[0] >= 2) {
|
if (base[0] >= 2) {
|
||||||
base = appendByte(base, TERMINATOR);
|
base = appendByte(base, TERMINATOR);
|
||||||
|
@ -92,10 +98,11 @@ public class CompactEncoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a binary array to hexadecimal format,
|
* Transforms a binary array to hexadecimal format + terminator
|
||||||
* returns array with each individual nibble adding a terminator at the end
|
*
|
||||||
*/
|
* @return array with each individual nibble adding a terminator at the end
|
||||||
public static byte[] hexDecode(byte[] str) {
|
*/
|
||||||
|
public static byte[] binToNibbles(byte[] str) {
|
||||||
byte[] hexSlice = new byte[0];
|
byte[] hexSlice = new byte[0];
|
||||||
String hexEncoded = toHexString(str);
|
String hexEncoded = toHexString(str);
|
||||||
for (char value : hexEncoded.toCharArray()) {
|
for (char value : hexEncoded.toCharArray()) {
|
||||||
|
|
|
@ -5,11 +5,10 @@ import static org.junit.Assert.*;
|
||||||
import org.ethereum.trie.Trie;
|
import org.ethereum.trie.Trie;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.cedarsoftware.util.DeepEquals;
|
|
||||||
|
|
||||||
public class TrieTest {
|
public class TrieTest {
|
||||||
|
|
||||||
private static String LONG_STRING = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ";
|
private static String LONG_STRING = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
private static String ROOT_HASH_EMPTY = "";
|
||||||
|
|
||||||
private static String c = "c";
|
private static String c = "c";
|
||||||
private static String ca = "ca";
|
private static String ca = "ca";
|
||||||
|
@ -33,215 +32,305 @@ public class TrieTest {
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyKey() {
|
public void testEmptyKey() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update("", dog);
|
trie.update("", dog);
|
||||||
String result = trie.get("");
|
assertEquals(dog, trie.get(""));
|
||||||
assertEquals(dog, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertShortString() {
|
public void testInsertShortString() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
String result = trie.get(cat);
|
assertEquals(dog, trie.get(cat));
|
||||||
assertEquals(dog, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertLongString() {
|
public void testInsertLongString() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, LONG_STRING);
|
trie.update(cat, LONG_STRING);
|
||||||
String result = trie.get(cat);
|
assertEquals(LONG_STRING, trie.get(cat));
|
||||||
assertEquals(LONG_STRING, result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertMultipleItems1() {
|
public void testInsertMultipleItems1() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
trie.update(ca, dude);
|
trie.update(ca, dude);
|
||||||
|
assertEquals(dude, trie.get(ca));
|
||||||
|
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
|
assertEquals(dog, trie.get(cat));
|
||||||
|
|
||||||
trie.update(dog, test);
|
trie.update(dog, test);
|
||||||
|
assertEquals(test, trie.get(dog));
|
||||||
|
|
||||||
trie.update(doge, LONG_STRING);
|
trie.update(doge, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(doge));
|
||||||
|
|
||||||
trie.update(test, LONG_STRING);
|
trie.update(test, LONG_STRING);
|
||||||
String result1 = trie.get(ca);
|
assertEquals(LONG_STRING, trie.get(test));
|
||||||
String result2 = trie.get(cat);
|
|
||||||
String result3 = trie.get(dog);
|
// Test if everything is still there
|
||||||
String result4 = trie.get(doge);
|
assertEquals(dude, trie.get(ca));
|
||||||
String result5 = trie.get(test);
|
assertEquals(dog, trie.get(cat));
|
||||||
assertEquals(dude, result1);
|
assertEquals(test, trie.get(dog));
|
||||||
assertEquals(dog, result2);
|
assertEquals(LONG_STRING, trie.get(doge));
|
||||||
assertEquals(test, result3);
|
assertEquals(LONG_STRING, trie.get(test));
|
||||||
assertEquals(LONG_STRING, result4);
|
|
||||||
assertEquals(LONG_STRING, result5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertMultipleItems2() {
|
public void testInsertMultipleItems2() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
|
assertEquals(dog, trie.get(cat));
|
||||||
|
|
||||||
trie.update(ca, dude);
|
trie.update(ca, dude);
|
||||||
|
assertEquals(dude, trie.get(ca));
|
||||||
|
|
||||||
trie.update(doge, LONG_STRING);
|
trie.update(doge, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(doge));
|
||||||
|
|
||||||
trie.update(dog, test);
|
trie.update(dog, test);
|
||||||
|
assertEquals(test, trie.get(dog));
|
||||||
|
|
||||||
trie.update(test, LONG_STRING);
|
trie.update(test, LONG_STRING);
|
||||||
String result1 = trie.get(cat);
|
assertEquals(LONG_STRING, trie.get(test));
|
||||||
String result2 = trie.get(ca);
|
|
||||||
String result3 = trie.get(doge);
|
// Test if everything is still there
|
||||||
String result4 = trie.get(dog);
|
assertEquals(dog, trie.get(cat));
|
||||||
String result5 = trie.get(test);
|
assertEquals(dude, trie.get(ca));
|
||||||
assertEquals(dog, result1);
|
assertEquals(LONG_STRING, trie.get(doge));
|
||||||
assertEquals(dude, result2);
|
assertEquals(test, trie.get(dog));
|
||||||
assertEquals(LONG_STRING, result3);
|
assertEquals(LONG_STRING, trie.get(test));
|
||||||
assertEquals(test, result4);
|
|
||||||
assertEquals(LONG_STRING, result5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateShortToShortString() {
|
public void testUpdateShortToShortString() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
|
assertEquals(dog, trie.get(cat));
|
||||||
|
|
||||||
trie.update(cat, dog+"1");
|
trie.update(cat, dog+"1");
|
||||||
String result = trie.get(cat);
|
assertEquals(dog+"1", trie.get(cat));
|
||||||
assertEquals(dog+"1", result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateLongToLongString() {
|
public void testUpdateLongToLongString() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
trie.update(cat, LONG_STRING);
|
trie.update(cat, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(cat));
|
||||||
trie.update(cat, LONG_STRING+"1");
|
trie.update(cat, LONG_STRING+"1");
|
||||||
String result = trie.get(cat);
|
assertEquals(LONG_STRING+"1", trie.get(cat));
|
||||||
assertEquals(LONG_STRING+"1", result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateShortToLongString() {
|
public void testUpdateShortToLongString() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
|
assertEquals(dog, trie.get(cat));
|
||||||
|
|
||||||
trie.update(cat, LONG_STRING+"1");
|
trie.update(cat, LONG_STRING+"1");
|
||||||
String result = trie.get(cat);
|
assertEquals(LONG_STRING+"1", trie.get(cat));
|
||||||
assertEquals(LONG_STRING+"1", result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateLongToShortString() {
|
public void testUpdateLongToShortString() {
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, LONG_STRING);
|
trie.update(cat, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(cat));
|
||||||
|
|
||||||
trie.update(cat, dog+"1");
|
trie.update(cat, dog+"1");
|
||||||
String result = trie.get(cat);
|
assertEquals(dog+"1", trie.get(cat));
|
||||||
assertEquals(dog+"1", result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteShortString1() {
|
public void testDeleteShortString1() {
|
||||||
|
String ROOT_HASH_BEFORE = "a9539c810cc2e8fa20785bdd78ec36cc1dab4b41f0d531e80a5e5fd25c3037ee";
|
||||||
|
String ROOT_HASH_AFTER = "fc5120b4a711bca1f5bb54769525b11b3fb9a8d6ac0b8bf08cbb248770521758";
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(dog, trie.get(cat));
|
||||||
|
|
||||||
trie.update(ca, dude);
|
trie.update(ca, dude);
|
||||||
|
assertEquals(dude, trie.get(ca));
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(ca);
|
trie.delete(ca);
|
||||||
Object result = trie.getRoot();
|
assertEquals("", trie.get(ca));
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
assertEquals(ROOT_HASH_AFTER, trie.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteShortString2() {
|
public void testDeleteShortString2() {
|
||||||
|
String ROOT_HASH_BEFORE = "a9539c810cc2e8fa20785bdd78ec36cc1dab4b41f0d531e80a5e5fd25c3037ee";
|
||||||
|
String ROOT_HASH_AFTER = "b25e1b5be78dbadf6c4e817c6d170bbb47e9916f8f6cc4607c5f3819ce98497b";
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(ca, dude);
|
trie.update(ca, dude);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(dude, trie.get(ca));
|
||||||
|
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
|
assertEquals(dog, trie.get(cat));
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(cat);
|
trie.delete(cat);
|
||||||
Object result = trie.getRoot();
|
assertEquals("", trie.get(cat));
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
assertEquals(ROOT_HASH_AFTER, trie.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteShortString3() {
|
public void testDeleteShortString3() {
|
||||||
|
String ROOT_HASH_BEFORE = "778ab82a7e8236ea2ff7bb9cfa46688e7241c1fd445bf2941416881a6ee192eb";
|
||||||
|
String ROOT_HASH_AFTER = "05875807b8f3e735188d2479add82f96dee4db5aff00dc63f07a7e27d0deab65";
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, dude);
|
trie.update(cat, dude);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(dude, trie.get(cat));
|
||||||
|
|
||||||
trie.update(dog, test);
|
trie.update(dog, test);
|
||||||
|
assertEquals(test, trie.get(dog));
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(dog);
|
trie.delete(dog);
|
||||||
Object result = trie.getRoot();
|
assertEquals("", trie.get(dog));
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
assertEquals(ROOT_HASH_AFTER, trie.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteLongString1() {
|
public void testDeleteLongString1() {
|
||||||
|
String ROOT_HASH_BEFORE = "318961a1c8f3724286e8e80d312352f01450bc4892c165cc7614e1c2e5a0012a";
|
||||||
|
String ROOT_HASH_AFTER = "63356ecf33b083e244122fca7a9b128cc7620d438d5d62e4f8b5168f1fb0527b";
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, LONG_STRING);
|
trie.update(cat, LONG_STRING);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(LONG_STRING, trie.get(cat));
|
||||||
|
|
||||||
trie.update(dog, LONG_STRING);
|
trie.update(dog, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(dog));
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(dog);
|
trie.delete(dog);
|
||||||
Object result = trie.getRoot();
|
assertEquals("", trie.get(dog));
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
assertEquals(ROOT_HASH_AFTER, trie.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteLongString2() {
|
public void testDeleteLongString2() {
|
||||||
|
String ROOT_HASH_BEFORE = "e020de34ca26f8d373ff2c0a8ac3a4cb9032bfa7a194c68330b7ac3584a1d388";
|
||||||
|
String ROOT_HASH_AFTER = "334511f0c4897677b782d13a6fa1e58e18de6b24879d57ced430bad5ac831cb2";
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(ca, LONG_STRING);
|
trie.update(ca, LONG_STRING);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(LONG_STRING, trie.get(ca));
|
||||||
|
|
||||||
trie.update(cat, LONG_STRING);
|
trie.update(cat, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(cat));
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(cat);
|
trie.delete(cat);
|
||||||
Object result = trie.getRoot();
|
assertEquals("", trie.get(cat));
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
assertEquals(ROOT_HASH_AFTER, trie.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteLongString3() {
|
public void testDeleteLongString3() {
|
||||||
|
String ROOT_HASH_BEFORE = "e020de34ca26f8d373ff2c0a8ac3a4cb9032bfa7a194c68330b7ac3584a1d388";
|
||||||
|
String ROOT_HASH_AFTER = "63356ecf33b083e244122fca7a9b128cc7620d438d5d62e4f8b5168f1fb0527b";
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, LONG_STRING);
|
trie.update(cat, LONG_STRING);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(LONG_STRING, trie.get(cat));
|
||||||
|
|
||||||
trie.update(ca, LONG_STRING);
|
trie.update(ca, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(ca));
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(ca);
|
trie.delete(ca);
|
||||||
Object result = trie.getRoot();
|
assertEquals("", trie.get(ca));
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
assertEquals(ROOT_HASH_AFTER, trie.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteMultipleItems1() {
|
public void testDeleteMultipleItems1() {
|
||||||
|
String ROOT_HASH_BEFORE = "3a784eddf1936515f0313b073f99e3bd65c38689021d24855f62a9601ea41717";
|
||||||
|
String ROOT_HASH_AFTER1 = "60a2e75cfa153c4af2783bd6cb48fd6bed84c6381bc2c8f02792c046b46c0653";
|
||||||
|
String ROOT_HASH_AFTER2 = "a84739b4762ddf15e3acc4e6957e5ab2bbfaaef00fe9d436a7369c6f058ec90d";
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
|
assertEquals(dog, trie.get(cat));
|
||||||
|
|
||||||
trie.update(ca, dude);
|
trie.update(ca, dude);
|
||||||
|
assertEquals(dude, trie.get(ca));
|
||||||
|
|
||||||
trie.update(doge, LONG_STRING);
|
trie.update(doge, LONG_STRING);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(LONG_STRING, trie.get(doge));
|
||||||
|
|
||||||
trie.update(dog, test);
|
trie.update(dog, test);
|
||||||
|
assertEquals(test, trie.get(dog));
|
||||||
|
|
||||||
trie.update(test, LONG_STRING);
|
trie.update(test, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(test));
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(dog);
|
trie.delete(dog);
|
||||||
|
assertEquals("", trie.get(dog));
|
||||||
|
assertEquals(ROOT_HASH_AFTER1, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(test);
|
trie.delete(test);
|
||||||
Object result = trie.getRoot();
|
assertEquals("", trie.get(test));
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
assertEquals(ROOT_HASH_AFTER2, trie.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteMultipleItems2() {
|
public void testDeleteMultipleItems2() {
|
||||||
|
String ROOT_HASH_BEFORE = "cf1ed2b6c4b6558f70ef0ecf76bfbee96af785cb5d5e7bfc37f9804ad8d0fb56";
|
||||||
|
String ROOT_HASH_AFTER1 = "f586af4a476ba853fca8cea1fbde27cd17d537d18f64269fe09b02aa7fe55a9e";
|
||||||
|
String ROOT_HASH_AFTER2 = "c59fdc16a80b11cc2f7a8b107bb0c954c0d8059e49c760ec3660eea64053ac91";
|
||||||
|
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
trie.update(c, LONG_STRING);
|
trie.update(c, LONG_STRING);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(LONG_STRING, trie.get(c));
|
||||||
|
|
||||||
trie.update(ca, LONG_STRING);
|
trie.update(ca, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(ca));
|
||||||
|
|
||||||
trie.update(cat, LONG_STRING);
|
trie.update(cat, LONG_STRING);
|
||||||
|
assertEquals(LONG_STRING, trie.get(cat));
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(ca);
|
trie.delete(ca);
|
||||||
|
assertEquals("", trie.get(ca));
|
||||||
|
assertEquals(ROOT_HASH_AFTER1, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(cat);
|
trie.delete(cat);
|
||||||
Object result = trie.getRoot();
|
assertEquals("", trie.get(cat));
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
assertEquals(ROOT_HASH_AFTER2, trie.getRootHash());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteAll() {
|
public void testDeleteAll() {
|
||||||
|
String ROOT_HASH_BEFORE = "a84739b4762ddf15e3acc4e6957e5ab2bbfaaef00fe9d436a7369c6f058ec90d";
|
||||||
Trie trie = new Trie(mockDb);
|
Trie trie = new Trie(mockDb);
|
||||||
Object expected = trie.getRoot();
|
assertEquals(ROOT_HASH_EMPTY, trie.getRootHash());
|
||||||
|
|
||||||
trie.update(ca, dude);
|
trie.update(ca, dude);
|
||||||
trie.update(cat, dog);
|
trie.update(cat, dog);
|
||||||
trie.update(doge, LONG_STRING);
|
trie.update(doge, LONG_STRING);
|
||||||
|
assertEquals(ROOT_HASH_BEFORE, trie.getRootHash());
|
||||||
|
|
||||||
trie.delete(ca);
|
trie.delete(ca);
|
||||||
trie.delete(cat);
|
trie.delete(cat);
|
||||||
trie.delete(doge);
|
trie.delete(doge);
|
||||||
Object result = trie.getRoot();
|
assertEquals(ROOT_HASH_EMPTY, trie.getRootHash());
|
||||||
assertTrue("Tries are not equal", DeepEquals.deepEquals(expected, result));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTrieCmp() {
|
public void testTrieCmp() {
|
||||||
Trie trie1 = new Trie(mockDb);
|
Trie trie1 = new Trie(mockDb);
|
||||||
|
@ -250,10 +339,12 @@ public class TrieTest {
|
||||||
trie1.update(doge, LONG_STRING);
|
trie1.update(doge, LONG_STRING);
|
||||||
trie2.update(doge, LONG_STRING);
|
trie2.update(doge, LONG_STRING);
|
||||||
assertTrue("Expected tries to be equal", trie1.cmp(trie2));
|
assertTrue("Expected tries to be equal", trie1.cmp(trie2));
|
||||||
|
assertEquals(trie1.getRootHash(), trie2.getRootHash());
|
||||||
|
|
||||||
trie1.update(dog, LONG_STRING);
|
trie1.update(dog, LONG_STRING);
|
||||||
trie2.update(cat, LONG_STRING);
|
trie2.update(cat, LONG_STRING);
|
||||||
assertFalse("Expected tries not to be equal", trie1.cmp(trie2));
|
assertFalse("Expected tries not to be equal", trie1.cmp(trie2));
|
||||||
|
assertNotEquals(trie1.getRootHash(), trie2.getRootHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -302,4 +393,84 @@ public class TrieTest {
|
||||||
public void testTrieUndo() {
|
public void testTrieUndo() {
|
||||||
fail("To be implemented");
|
fail("To be implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using tests from: https://github.com/ethereum/tests/blob/master/trietest.json
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleItem() {
|
||||||
|
Trie trie = new Trie(mockDb);
|
||||||
|
trie.update("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
|
||||||
|
assertEquals("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab", trie.getRootHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDogs() {
|
||||||
|
Trie trie = new Trie(mockDb);
|
||||||
|
trie.update("doe", "reindeer");
|
||||||
|
assertEquals("11a0327cfcc5b7689b6b6d727e1f5f8846c1137caaa9fc871ba31b7cce1b703e", trie.getRootHash());
|
||||||
|
|
||||||
|
trie.update("dog", "puppy");
|
||||||
|
assertEquals("05ae693aac2107336a79309e0c60b24a7aac6aa3edecaef593921500d33c63c4", trie.getRootHash());
|
||||||
|
|
||||||
|
trie.update("dogglesworth", "cat");
|
||||||
|
assertEquals("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3", trie.getRootHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPuppy() {
|
||||||
|
Trie trie = new Trie(mockDb);
|
||||||
|
trie.update("do", "verb");
|
||||||
|
trie.update("horse", "stallion");
|
||||||
|
trie.update("doge", "coin");
|
||||||
|
trie.update("dog", "puppy");
|
||||||
|
|
||||||
|
assertEquals("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84", trie.getRootHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptyValues() {
|
||||||
|
Trie trie = new Trie(mockDb);
|
||||||
|
trie.update("do", "verb");
|
||||||
|
trie.update("ether", "wookiedoo");
|
||||||
|
trie.update("horse", "stallion");
|
||||||
|
trie.update("shaman", "horse");
|
||||||
|
trie.update("doge", "coin");
|
||||||
|
trie.update("ether", "");
|
||||||
|
trie.update("dog", "puppy");
|
||||||
|
trie.update("shaman", "");
|
||||||
|
|
||||||
|
assertEquals("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84", trie.getRootHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFoo() {
|
||||||
|
Trie trie = new Trie(mockDb);
|
||||||
|
trie.update("foo", "bar");
|
||||||
|
trie.update("food", "bat");
|
||||||
|
trie.update("food", "bass");
|
||||||
|
|
||||||
|
assertEquals("17beaa1648bafa633cda809c90c04af50fc8aed3cb40d16efbddee6fdf63c4c3", trie.getRootHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSmallValues() {
|
||||||
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
|
trie.update("be", "e");
|
||||||
|
trie.update("dog", "puppy");
|
||||||
|
trie.update("bed", "d");
|
||||||
|
assertEquals("3f67c7a47520f79faa29255d2d3c084a7a6df0453116ed7232ff10277a8be68b", trie.getRootHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTesty() {
|
||||||
|
Trie trie = new Trie(mockDb);
|
||||||
|
|
||||||
|
trie.update("test", "test");
|
||||||
|
assertEquals("85d106d4edff3b7a4889e91251d0a87d7c17a1dda648ebdba8c6060825be23b8", trie.getRootHash());
|
||||||
|
|
||||||
|
trie.update("te", "testy");
|
||||||
|
assertEquals("8452568af70d8d140f58d941338542f645fcca50094b20f3c3d8c3df49337928", trie.getRootHash());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,62 +13,62 @@ public class CompactEncoderTest {
|
||||||
public void testCompactEncodeOddCompact() {
|
public void testCompactEncodeOddCompact() {
|
||||||
byte[] test = new byte[] { 1, 2, 3, 4, 5 };
|
byte[] test = new byte[] { 1, 2, 3, 4, 5 };
|
||||||
byte[] expectedData = new byte[] { 0x11, 0x23, 0x45 };
|
byte[] expectedData = new byte[] { 0x11, 0x23, 0x45 };
|
||||||
assertArrayEquals("odd compact encode fail", expectedData, CompactEncoder.encode(test));
|
assertArrayEquals("odd compact encode fail", expectedData, CompactEncoder.packNibbles(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompactEncodeEvenCompact() {
|
public void testCompactEncodeEvenCompact() {
|
||||||
byte[] test = new byte[] { 0, 1, 2, 3, 4, 5 };
|
byte[] test = new byte[] { 0, 1, 2, 3, 4, 5 };
|
||||||
byte[] expectedData = new byte[] { 0x00, 0x01, 0x23, 0x45 };
|
byte[] expectedData = new byte[] { 0x00, 0x01, 0x23, 0x45 };
|
||||||
assertArrayEquals("even compact encode fail", expectedData, CompactEncoder.encode(test));
|
assertArrayEquals("even compact encode fail", expectedData, CompactEncoder.packNibbles(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompactEncodeEvenTerminated() {
|
public void testCompactEncodeEvenTerminated() {
|
||||||
byte[] test = new byte[] { 0, 15, 1, 12, 11, 8, T };
|
byte[] test = new byte[] { 0, 15, 1, 12, 11, 8, T };
|
||||||
byte[] expectedData = new byte[] { 0x20, 0x0f, 0x1c, (byte) 0xb8 };
|
byte[] expectedData = new byte[] { 0x20, 0x0f, 0x1c, (byte) 0xb8 };
|
||||||
assertArrayEquals("even terminated compact encode fail", expectedData, CompactEncoder.encode(test));
|
assertArrayEquals("even terminated compact encode fail", expectedData, CompactEncoder.packNibbles(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompactEncodeOddTerminated() {
|
public void testCompactEncodeOddTerminated() {
|
||||||
byte[] test = new byte[] { 15, 1, 12, 11, 8, T };
|
byte[] test = new byte[] { 15, 1, 12, 11, 8, T };
|
||||||
byte[] expectedData = new byte[] { 0x3f, 0x1c, (byte) 0xb8 };
|
byte[] expectedData = new byte[] { 0x3f, 0x1c, (byte) 0xb8 };
|
||||||
assertArrayEquals("odd terminated compact encode fail", expectedData, CompactEncoder.encode(test));
|
assertArrayEquals("odd terminated compact encode fail", expectedData, CompactEncoder.packNibbles(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompactDecodeOddCompact() {
|
public void testCompactDecodeOddCompact() {
|
||||||
byte[] test = new byte[] { 0x11, 0x23, 0x45 };
|
byte[] test = new byte[] { 0x11, 0x23, 0x45 };
|
||||||
byte[] expected = new byte[] {1, 2, 3, 4, 5};
|
byte[] expected = new byte[] {1, 2, 3, 4, 5};
|
||||||
assertArrayEquals("odd compact decode fail", expected, CompactEncoder.decode(test));
|
assertArrayEquals("odd compact decode fail", expected, CompactEncoder.unpackToNibbles(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompactDecodeEvenCompact() {
|
public void testCompactDecodeEvenCompact() {
|
||||||
byte[] test = new byte[] { 0x00, 0x01, 0x23, 0x45 };
|
byte[] test = new byte[] { 0x00, 0x01, 0x23, 0x45 };
|
||||||
byte[] expected = new byte[] {0, 1, 2, 3, 4, 5};
|
byte[] expected = new byte[] {0, 1, 2, 3, 4, 5};
|
||||||
assertArrayEquals("even compact decode fail", expected, CompactEncoder.decode(test));
|
assertArrayEquals("even compact decode fail", expected, CompactEncoder.unpackToNibbles(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompactDecodeEvenTerminated() {
|
public void testCompactDecodeEvenTerminated() {
|
||||||
byte[] test = new byte[] { 0x20, 0x0f, 0x1c, (byte) 0xb8 };
|
byte[] test = new byte[] { 0x20, 0x0f, 0x1c, (byte) 0xb8 };
|
||||||
byte[] expected = new byte[] {0, 15, 1, 12, 11, 8, T};
|
byte[] expected = new byte[] {0, 15, 1, 12, 11, 8, T};
|
||||||
assertArrayEquals("even terminated compact decode fail", expected, CompactEncoder.decode(test));
|
assertArrayEquals("even terminated compact decode fail", expected, CompactEncoder.unpackToNibbles(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompactDecodeOddTerminated() {
|
public void testCompactDecodeOddTerminated() {
|
||||||
byte[] test = new byte[] { 0x3f, 0x1c, (byte) 0xb8 };
|
byte[] test = new byte[] { 0x3f, 0x1c, (byte) 0xb8 };
|
||||||
byte[] expected = new byte[] {15, 1, 12, 11, 8, T};
|
byte[] expected = new byte[] {15, 1, 12, 11, 8, T};
|
||||||
assertArrayEquals("odd terminated compact decode fail", expected, CompactEncoder.decode(test));
|
assertArrayEquals("odd terminated compact decode fail", expected, CompactEncoder.unpackToNibbles(test));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompactHexDecode() {
|
public void testCompactHexDecode() {
|
||||||
byte[] test = "stallion".getBytes();
|
byte[] test = "stallion".getBytes();
|
||||||
byte[] result = new byte[] { 7, 3, 7, 4, 6, 1, 6, 12, 6, 12, 6, 9, 6, 15, 6, 14, T };
|
byte[] result = new byte[] { 7, 3, 7, 4, 6, 1, 6, 12, 6, 12, 6, 9, 6, 15, 6, 14, T };
|
||||||
assertArrayEquals(result, CompactEncoder.hexDecode(test));
|
assertArrayEquals(result, CompactEncoder.binToNibbles(test));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue