@@ -18,26 +18,39 @@ public class MerkleDataNodeEncoderTest { | |||||
@Test | @Test | ||||
public void testEnocoderV0() { | public void testEnocoderV0() { | ||||
MerkleDataNodeEncoder encoderV0 = new MerkleDataNodeEncoder_V0(); | |||||
Random rand = new Random(); | Random rand = new Random(); | ||||
byte[] data = new byte[512]; | byte[] data = new byte[512]; | ||||
byte[] key = new byte[256]; | |||||
byte[] keyBytes = new byte[256]; | |||||
rand.nextBytes(data); | rand.nextBytes(data); | ||||
rand.nextBytes(key); | |||||
rand.nextBytes(keyBytes); | |||||
Bytes key = new Bytes(keyBytes); | |||||
long sn = 1024; | long sn = 1024; | ||||
long version = 1; | long version = 1; | ||||
DataNode nodeV0 = encoderV0.create(ClassicAlgorithm.SHA256.code(), sn, new Bytes(key), version, data); | |||||
doTestV0(sn, version, key, data); | |||||
sn = 0; | |||||
version = 1000; | |||||
doTestV0(sn, version, key, data); | |||||
sn = (1 << 56) -1; | |||||
version = 1000; | |||||
doTestV0(sn, version, key, data); | |||||
} | |||||
private void doTestV0(long sn, long version, Bytes key, byte[] data) { | |||||
MerkleDataNodeEncoder encoderV0 = new MerkleDataNodeEncoder_V0(); | |||||
DataNode nodeV0 = encoderV0.create(ClassicAlgorithm.SHA256.code(), sn, key, version, data); | |||||
assertNull(nodeV0.getValueHash()); | assertNull(nodeV0.getValueHash()); | ||||
assertEquals(sn, nodeV0.getSN()); | assertEquals(sn, nodeV0.getSN()); | ||||
assertEquals(version, nodeV0.getVersion()); | assertEquals(version, nodeV0.getVersion()); | ||||
assertEquals(new Bytes(key), nodeV0.getKey()); | |||||
assertEquals(key, nodeV0.getKey()); | |||||
byte[] nodeBytes = nodeV0.toBytes(); | byte[] nodeBytes = nodeV0.toBytes(); | ||||
@@ -47,37 +60,47 @@ public class MerkleDataNodeEncoderTest { | |||||
assertEquals(nodeV0.getNodeHash(), nodeV0_reversed.getNodeHash()); | assertEquals(nodeV0.getNodeHash(), nodeV0_reversed.getNodeHash()); | ||||
assertEquals(encoderV0.getFormatVersion(), nodeBytes[0]); | assertEquals(encoderV0.getFormatVersion(), nodeBytes[0]); | ||||
assertEquals(sn, nodeV0_reversed.getSN()); | assertEquals(sn, nodeV0_reversed.getSN()); | ||||
assertEquals(version, nodeV0_reversed.getVersion()); | assertEquals(version, nodeV0_reversed.getVersion()); | ||||
assertEquals(new Bytes(key), nodeV0_reversed.getKey()); | |||||
assertEquals(key, nodeV0_reversed.getKey()); | |||||
} | } | ||||
@Test | @Test | ||||
public void testEnocoderV1() { | public void testEnocoderV1() { | ||||
MerkleDataNodeEncoder encoderV1 = new MerkleDataNodeEncoder_V1(); | |||||
Random rand = new Random(); | Random rand = new Random(); | ||||
byte[] data = new byte[512]; | byte[] data = new byte[512]; | ||||
byte[] key = new byte[256]; | |||||
byte[] keyBytes = new byte[256]; | |||||
rand.nextBytes(data); | rand.nextBytes(data); | ||||
rand.nextBytes(key); | |||||
HashFunction hashFunc = Crypto.getHashFunction(ClassicAlgorithm.SHA256); | |||||
HashDigest dataHash = hashFunc.hash(data); | |||||
rand.nextBytes(keyBytes); | |||||
Bytes key = new Bytes(keyBytes); | |||||
long sn = 1024; | long sn = 1024; | ||||
long version = 1; | long version = 1; | ||||
DataNode node = encoderV1.create(ClassicAlgorithm.SHA256.code(), sn, new Bytes(key), version, data); | |||||
doTestV1(sn, version, key, data); | |||||
sn = 0; | |||||
version = 10088; | |||||
doTestV1(sn, version, key, data); | |||||
sn = (1 << 56) -1; | |||||
version = 1000; | |||||
doTestV1(sn, version, key, data); | |||||
} | |||||
private void doTestV1(long sn, long version, Bytes key, byte[] data) { | |||||
HashFunction hashFunc = Crypto.getHashFunction(ClassicAlgorithm.SHA256); | |||||
HashDigest dataHash = hashFunc.hash(data); | |||||
MerkleDataNodeEncoder encoderV1 = new MerkleDataNodeEncoder_V1(); | |||||
DataNode node = encoderV1.create(ClassicAlgorithm.SHA256.code(), sn, key, version, data); | |||||
assertEquals(dataHash, node.getValueHash()); | assertEquals(dataHash, node.getValueHash()); | ||||
assertEquals(sn, node.getSN()); | assertEquals(sn, node.getSN()); | ||||
assertEquals(version, node.getVersion()); | assertEquals(version, node.getVersion()); | ||||
assertEquals(new Bytes(key), node.getKey()); | |||||
assertEquals(key, node.getKey()); | |||||
byte[] nodeBytes = node.toBytes(); | byte[] nodeBytes = node.toBytes(); | ||||
@@ -89,8 +112,42 @@ public class MerkleDataNodeEncoderTest { | |||||
assertEquals(sn, node_reversed.getSN()); | assertEquals(sn, node_reversed.getSN()); | ||||
assertEquals(version, node_reversed.getVersion()); | assertEquals(version, node_reversed.getVersion()); | ||||
assertEquals(new Bytes(key), node_reversed.getKey()); | |||||
assertEquals(key, node_reversed.getKey()); | |||||
} | } | ||||
@Test | |||||
public void testCompatibility() { | |||||
Random rand = new Random(); | |||||
byte[] data = new byte[512]; | |||||
byte[] keyBytes = new byte[256]; | |||||
rand.nextBytes(data); | |||||
rand.nextBytes(keyBytes); | |||||
Bytes key = new Bytes(keyBytes); | |||||
long sn = 1024; | |||||
long version = 1; | |||||
PreviousDataNode pdataNode = PreviousDataNode.newDataNode(ClassicAlgorithm.SHA256.code(), sn, key, version, | |||||
data); | |||||
MerkleDataNodeEncoder encoderV0 = new MerkleDataNodeEncoder_V0(); | |||||
DataNode dataNode = encoderV0.create(ClassicAlgorithm.SHA256.code(), sn, key, version, data); | |||||
assertEquals(pdataNode.getNodeHash(), dataNode.getNodeHash()); | |||||
assertEquals(pdataNode.getSN(), dataNode.getSN()); | |||||
assertEquals(pdataNode.getVersion(), dataNode.getVersion()); | |||||
assertEquals(pdataNode.getKey(), dataNode.getKey()); | |||||
DataNode dataNode_reversed = encoderV0.resolve(pdataNode.toBytes()); | |||||
assertNull(dataNode_reversed.getValueHash()); | |||||
assertEquals(pdataNode.getNodeHash(), dataNode_reversed.getNodeHash()); | |||||
assertEquals(pdataNode.getSN(), dataNode_reversed.getSN()); | |||||
assertEquals(pdataNode.getVersion(), dataNode_reversed.getVersion()); | |||||
assertEquals(pdataNode.getKey(), dataNode_reversed.getKey()); | |||||
} | |||||
} | } |
@@ -0,0 +1,191 @@ | |||||
package com.jd.blockchain.ledger.core; | |||||
import com.jd.blockchain.crypto.Crypto; | |||||
import com.jd.blockchain.crypto.CryptoAlgorithm; | |||||
import com.jd.blockchain.crypto.HashDigest; | |||||
import com.jd.blockchain.crypto.HashFunction; | |||||
import com.jd.blockchain.utils.Bytes; | |||||
import com.jd.blockchain.utils.io.BytesUtils; | |||||
import com.jd.blockchain.utils.io.NumberMask; | |||||
/** | |||||
* A copy of previous version of com.jd.blockchain.ledger.core.MerkleTree.DataNode; | |||||
* | |||||
* @author huanghaiquan | |||||
* | |||||
*/ | |||||
public class PreviousDataNode { | |||||
private HashDigest nodeHash; | |||||
private long sn; | |||||
private Bytes key; | |||||
private long version; | |||||
private byte[] dataNodeBytes; | |||||
private PreviousDataNode(long sn, Bytes key, long version, HashDigest dataHash, byte[] dataBytes) { | |||||
this.sn = sn; | |||||
this.key = key; | |||||
this.version = version; | |||||
this.nodeHash = dataHash; | |||||
this.dataNodeBytes = dataBytes; | |||||
} | |||||
static PreviousDataNode newDataNode(CryptoAlgorithm hashAlgorithm, long sn, Bytes key, long version, | |||||
byte[] hashedData) { | |||||
return newDataNode(hashAlgorithm.code(), sn, key, version, hashedData); | |||||
} | |||||
static PreviousDataNode newDataNode(short hashAlgorithm, long sn, Bytes key, long version, byte[] hashedData) { | |||||
// byte[] keyStrBytes = BytesUtils.toBytes(key); | |||||
// int maskSize = NumberMask.SHORT.getMaskLength(keyStrBytes.length); | |||||
int keySize = key.size(); | |||||
int maskSize = NumberMask.SHORT.getMaskLength(keySize); | |||||
// int bodySize = 8 + maskSize + keyStrBytes.length + 8;// sn + key + version; | |||||
int bodySize = 8 + maskSize + keySize + 8;// sn + key + version; | |||||
byte[] bodyBytes = new byte[bodySize]; | |||||
int offset = 0; | |||||
offset += BytesUtils.toBytes(sn, bodyBytes, 0); | |||||
// NumberMask.SHORT.writeMask(keyStrBytes.length, bodyBytes, offset); | |||||
NumberMask.SHORT.writeMask(keySize, bodyBytes, offset); | |||||
offset += maskSize; | |||||
// System.arraycopy(keyStrBytes, 0, bodyBytes, offset, keyStrBytes.length); | |||||
// System.arraycopy(keyStrBytes, 0, bodyBytes, offset, keyStrBytes.length); | |||||
// offset += keyStrBytes.length; | |||||
offset += key.copyTo(bodyBytes, offset, keySize); | |||||
// TODO: version; | |||||
offset += BytesUtils.toBytes(version, bodyBytes, offset); | |||||
byte[] dataBytes = BytesUtils.concat(bodyBytes, hashedData); | |||||
HashFunction hashFunc = Crypto.getHashFunction(hashAlgorithm); | |||||
HashDigest dataHash = hashFunc.hash(dataBytes); | |||||
int hashMaskSize = NumberMask.TINY.getMaskLength(dataHash.size()); | |||||
int dataNodeSize = bodySize + hashMaskSize + dataHash.size(); | |||||
byte[] dataNodeBytes = new byte[dataNodeSize]; | |||||
offset = 0; | |||||
System.arraycopy(bodyBytes, 0, dataNodeBytes, offset, bodySize); | |||||
offset += bodySize; | |||||
NumberMask.TINY.writeMask(dataHash.size(), dataNodeBytes, offset); | |||||
offset += hashMaskSize; | |||||
System.arraycopy(dataHash.toBytes(), 0, dataNodeBytes, offset, dataHash.size()); | |||||
return new PreviousDataNode(sn, key, version, dataHash, dataNodeBytes); | |||||
} | |||||
public HashDigest getNodeHash() { | |||||
return nodeHash; | |||||
} | |||||
protected long getStartingSN() { | |||||
return sn; | |||||
} | |||||
protected long getDataCount() { | |||||
return 1; | |||||
} | |||||
/* | |||||
* (non-Javadoc) | |||||
* | |||||
* @see com.jd.blockchain.ledger.core.MerkleDataNode#getLevel() | |||||
*/ | |||||
public int getLevel() { | |||||
return 0; | |||||
} | |||||
/* | |||||
* (non-Javadoc) | |||||
* | |||||
* @see com.jd.blockchain.ledger.core.MerkleDataNode#getSN() | |||||
*/ | |||||
public long getSN() { | |||||
return sn; | |||||
} | |||||
/* | |||||
* (non-Javadoc) | |||||
* | |||||
* @see com.jd.blockchain.ledger.core.MerkleDataNode#getKey() | |||||
*/ | |||||
public Bytes getKey() { | |||||
return key; | |||||
} | |||||
/* | |||||
* (non-Javadoc) | |||||
* | |||||
* @see com.jd.blockchain.ledger.core.MerkleDataNode#getVersion() | |||||
*/ | |||||
public long getVersion() { | |||||
return version; | |||||
} | |||||
public byte[] toBytes() { | |||||
return dataNodeBytes; | |||||
} | |||||
static PreviousDataNode parse(byte[] bytes) { | |||||
// InputStream in = new ByteArrayInputStream(bytes); | |||||
int offset = 0; | |||||
long sn = BytesUtils.toLong(bytes, offset); | |||||
offset += 8; | |||||
// byte[] keyBytes = BytesEncoding.read(NumberMask.SHORT, in); | |||||
// String key = BytesUtils.toString(keyBytes); | |||||
int keySize = NumberMask.SHORT.resolveMaskedNumber(bytes, offset); | |||||
offset += NumberMask.SHORT.getMaskLength(keySize); | |||||
byte[] keyBytes = new byte[keySize]; | |||||
System.arraycopy(bytes, offset, keyBytes, 0, keySize); | |||||
offset += keySize; | |||||
// String key = BytesUtils.toString(keyBytes); | |||||
Bytes key = new Bytes(keyBytes); | |||||
// long version = BytesUtils.readLong(in); | |||||
long version = BytesUtils.toLong(bytes, offset); | |||||
offset += 8; | |||||
// byte[] dataHashBytes = BytesEncoding.read(NumberMask.SHORT, in); | |||||
int hashSize = NumberMask.TINY.resolveMaskedNumber(bytes, offset); | |||||
offset += NumberMask.TINY.getMaskLength(hashSize); | |||||
byte[] dataHashBytes = new byte[hashSize]; | |||||
System.arraycopy(bytes, offset, dataHashBytes, 0, hashSize); | |||||
offset += hashSize; | |||||
HashDigest dataHash = new HashDigest(dataHashBytes); | |||||
return new PreviousDataNode(sn, key, version, dataHash, bytes); | |||||
} | |||||
@Override | |||||
public int hashCode() { | |||||
return nodeHash.hashCode(); | |||||
} | |||||
@Override | |||||
public boolean equals(Object obj) { | |||||
if (obj == null) { | |||||
return false; | |||||
} | |||||
if (obj == this) { | |||||
return true; | |||||
} | |||||
if (obj instanceof PreviousDataNode) { | |||||
PreviousDataNode node1 = (PreviousDataNode) obj; | |||||
return this.nodeHash.equals(node1.nodeHash); | |||||
} | |||||
return false; | |||||
} | |||||
} |