@@ -18,26 +18,39 @@ public class MerkleDataNodeEncoderTest { | |||
@Test | |||
public void testEnocoderV0() { | |||
MerkleDataNodeEncoder encoderV0 = new MerkleDataNodeEncoder_V0(); | |||
Random rand = new Random(); | |||
byte[] data = new byte[512]; | |||
byte[] key = new byte[256]; | |||
byte[] keyBytes = new byte[256]; | |||
rand.nextBytes(data); | |||
rand.nextBytes(key); | |||
rand.nextBytes(keyBytes); | |||
Bytes key = new Bytes(keyBytes); | |||
long sn = 1024; | |||
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()); | |||
assertEquals(sn, nodeV0.getSN()); | |||
assertEquals(version, nodeV0.getVersion()); | |||
assertEquals(new Bytes(key), nodeV0.getKey()); | |||
assertEquals(key, nodeV0.getKey()); | |||
byte[] nodeBytes = nodeV0.toBytes(); | |||
@@ -47,37 +60,47 @@ public class MerkleDataNodeEncoderTest { | |||
assertEquals(nodeV0.getNodeHash(), nodeV0_reversed.getNodeHash()); | |||
assertEquals(encoderV0.getFormatVersion(), nodeBytes[0]); | |||
assertEquals(sn, nodeV0_reversed.getSN()); | |||
assertEquals(version, nodeV0_reversed.getVersion()); | |||
assertEquals(new Bytes(key), nodeV0_reversed.getKey()); | |||
assertEquals(key, nodeV0_reversed.getKey()); | |||
} | |||
@Test | |||
public void testEnocoderV1() { | |||
MerkleDataNodeEncoder encoderV1 = new MerkleDataNodeEncoder_V1(); | |||
Random rand = new Random(); | |||
byte[] data = new byte[512]; | |||
byte[] key = new byte[256]; | |||
byte[] keyBytes = new byte[256]; | |||
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 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(sn, node.getSN()); | |||
assertEquals(version, node.getVersion()); | |||
assertEquals(new Bytes(key), node.getKey()); | |||
assertEquals(key, node.getKey()); | |||
byte[] nodeBytes = node.toBytes(); | |||
@@ -89,8 +112,42 @@ public class MerkleDataNodeEncoderTest { | |||
assertEquals(sn, node_reversed.getSN()); | |||
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; | |||
} | |||
} |