use of com.jd.blockchain.ledger.MerkleDataNode in project jdchain-core by blockchain-jd-com.
the class MerkleSequenceTreeTest method testMerkleReload.
/**
* 测试从存储重新加载 Merkle 树的正确性;
*/
/**
*/
// TODO: 暂时注释掉默克尔证明相关的内容;
// @Test
public void testMerkleReload() {
CryptoSetting setting = Mockito.mock(CryptoSetting.class);
when(setting.getHashAlgorithm()).thenReturn(ClassicAlgorithm.SHA256.code());
when(setting.getAutoVerifyHash()).thenReturn(true);
// 保存所有写入的数据节点的 SN-Hash 映射表;
TreeMap<Long, HashDigest> expectedDataNodes = new TreeMap<>();
MerkleNode nd;
// 测试从空的树开始,顺序增加数据节点;
ExistancePolicyKVStorageMap storage = new ExistancePolicyKVStorageMap();
// 创建空的的树;
MerkleSequenceTree mkt = new MerkleSequenceTree(setting, keyPrefix, storage);
long sn = 0;
// 加入 4097 条数据记录,预期构成以一颗 4 层 16 叉树;
int count = 4097;
byte[] dataBuf = new byte[16];
Random rand = new Random();
for (int i = 0; i < count; i++) {
rand.nextBytes(dataBuf);
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf);
expectedDataNodes.put(sn, nd.getNodeHash());
sn++;
}
mkt.commit();
// 记录一次提交的根哈希以及部分节点信息,用于后续的加载校验;
HashDigest r1_rootHash = mkt.getRootHash();
long r1_dataCount = mkt.getDataCount();
long r1_maxSN = mkt.getMaxSn();
long r1_sn1 = r1_maxSN;
String r1_proof1 = mkt.getProof(r1_sn1).toString();
long r1_sn2 = 1024;
String r1_proof2 = mkt.getProof(r1_sn2).toString();
{
// 检查节点数;
assertEquals(count, mkt.getDataCount());
// 检查最大序列号的正确性;
long maxSN = mkt.getMaxSn();
// count-1;
long expectedMaxSN = 4096;
assertEquals(expectedMaxSN, maxSN);
// 预期扩展到 4 层;
assertEquals(4, mkt.getLevel());
// 路径节点 + 数据节点;
// 预期扩展为 4 层16叉树,由 3 层满16叉树扩展 1 新分支(4个路径节点)而形成;
long expectedNodes = getMaxPathNodeCount(3) + 4 + 4097;
assertEquals(expectedNodes, storage.getCount());
// 重新加载,判断数据是否正确;
MerkleSequenceTree r1_mkt = new MerkleSequenceTree(r1_rootHash, setting, keyPrefix, storage, true);
{
// 验证每一个数据节点都产生了存在性证明;
MerkleProof proof = null;
HashDigest expectedNodeHash = null;
MerkleDataNode reallyDataNode = null;
for (long n = 0; n < maxSN; n++) {
expectedNodeHash = expectedDataNodes.get(n);
reallyDataNode = r1_mkt.getData(n);
assertEquals(expectedNodeHash, reallyDataNode.getNodeHash());
proof = r1_mkt.getProof(n);
assertNotNull(proof);
assertEquals(expectedNodeHash, proof.getDataHash());
}
}
}
// 覆盖到每一路分支修改数据节点;
int storageDataCountBefore = storage.getCount();
// maxSn = 4096;
long maxSN = mkt.getMaxSn();
int i;
for (i = 0; i <= maxSN; i += 16) {
rand.nextBytes(dataBuf);
sn = i;
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf);
expectedDataNodes.put(sn, nd.getNodeHash());
}
mkt.commit();
// 记录一次提交的根哈希以及部分节点信息,用于后续的加载校验;
HashDigest r2_rootHash = mkt.getRootHash();
long r2_dataCount = mkt.getDataCount();
long r2_maxSN = mkt.getMaxSn();
long r2_sn1 = r1_sn1;
String r2_proof1 = mkt.getProof(r2_sn1).toString();
long r2_sn2 = r1_sn2;
String r2_proof2 = mkt.getProof(r2_sn2).toString();
{
// 检查节点数;
assertEquals(count, mkt.getDataCount());
assertEquals(r1_dataCount, r2_dataCount);
// 检查最大序列号的正确性;
maxSN = mkt.getMaxSn();
// count-1;
long expectedMaxSN = 4096;
assertEquals(expectedMaxSN, maxSN);
// 由于覆盖到每一个分支节点,全部分支节点都重新生成,因此:
// 新增节点数=修改的数据节点数 + 全部分支节点数;
long addCounts = i / 16 + getMaxPathNodeCount(3) + 4;
assertEquals(storageDataCountBefore + addCounts, storage.getCount());
}
// 新插入数据;
final int NEW_INSERTED_COUNT = 18;
for (i = 0; i < NEW_INSERTED_COUNT; i++) {
rand.nextBytes(dataBuf);
sn = maxSN + 1 + i;
nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf);
expectedDataNodes.put(sn, nd.getNodeHash());
}
mkt.commit();
{
// 验证每一个数据节点都产生了存在性证明;
MerkleProof proof = null;
for (Long n : expectedDataNodes.keySet()) {
proof = mkt.getProof(n.longValue());
assertNotNull(proof);
assertEquals(expectedDataNodes.get(n), proof.getDataHash());
}
}
// 记录一次提交的根哈希以及部分节点信息,用于后续的加载校验;
HashDigest r3_rootHash = mkt.getRootHash();
long r3_maxSN = mkt.getMaxSn();
long r3_sn1 = r2_sn1;
String r3_proof1 = mkt.getProof(r3_sn1).toString();
long r3_sn2 = r2_sn2;
String r3_proof2 = mkt.getProof(r3_sn2).toString();
long r3_sn3 = 4096 + NEW_INSERTED_COUNT - 5;
String r3_proof3 = mkt.getProof(r3_sn3).toString();
{
// 检查节点数;
assertEquals(count + NEW_INSERTED_COUNT, mkt.getDataCount());
// 检查最大序列号的正确性;
maxSN = mkt.getMaxSn();
// count-1;
long expectedMaxSN = 4096 + NEW_INSERTED_COUNT;
assertEquals(expectedMaxSN, maxSN);
}
// --------------------
// 重新从存储加载生成新的 MerkleTree 实例,验证与初始实例的一致性;
// 从第 2 轮提交的 Merkle 根哈希加载;
MerkleSequenceTree r1_mkt = new MerkleSequenceTree(r1_rootHash, setting, keyPrefix, storage, true);
assertEquals(r1_maxSN, r1_mkt.getMaxSn());
assertEquals(r1_rootHash, r1_mkt.getRootHash());
assertEquals(r1_dataCount, r1_mkt.getDataCount());
assertEquals(r1_proof1, r1_mkt.getProof(r1_sn1).toString());
assertEquals(r1_proof2, r1_mkt.getProof(r1_sn2).toString());
// 从第 2 轮提交的 Merkle 根哈希加载;
// 第 2 轮生成的 Merkle 树是对第 1 轮的数据的全部节点的修改,因此同一个 SN 的节点的证明是不同的;
MerkleSequenceTree r2_mkt = new MerkleSequenceTree(r2_rootHash, setting, keyPrefix, storage, true);
assertEquals(r1_maxSN, r2_mkt.getMaxSn());
assertEquals(r1_dataCount, r2_mkt.getDataCount());
assertNotEquals(r1_rootHash, r2_mkt.getRootHash());
assertNotEquals(r1_proof1, r2_mkt.getProof(r1_sn1).toString());
assertNotEquals(r1_proof2, r2_mkt.getProof(r1_sn2).toString());
assertEquals(r2_maxSN, r2_mkt.getMaxSn());
assertEquals(r2_rootHash, r2_mkt.getRootHash());
assertEquals(r2_dataCount, r2_mkt.getDataCount());
assertEquals(r2_proof1, r2_mkt.getProof(r2_sn1).toString());
assertEquals(r2_proof2, r2_mkt.getProof(r2_sn2).toString());
// 从第 3 轮提交的 Merkle 根哈希加载;
// 第 3 轮生成的 Merkle 树是在第 2 轮的数据基础上做新增,因此非新增的同一个 SN 的节点的Merkle证明是相同的;
MerkleSequenceTree r3_mkt = new MerkleSequenceTree(r3_rootHash, setting, keyPrefix, storage, true);
assertEquals(r2_maxSN + NEW_INSERTED_COUNT, r3_mkt.getMaxSn());
assertNotEquals(r2_rootHash, r3_mkt.getRootHash());
assertEquals(r2_dataCount + NEW_INSERTED_COUNT, r3_mkt.getDataCount());
assertEquals(r3_maxSN, r3_mkt.getMaxSn());
assertEquals(r3_rootHash, r3_mkt.getRootHash());
assertEquals(r3_proof1, r3_mkt.getProof(r3_sn1).toString());
assertEquals(r3_proof2, r3_mkt.getProof(r3_sn2).toString());
assertEquals(r3_proof3, r3_mkt.getProof(r3_sn3).toString());
// 验证每一个数据节点都产生了存在性证明;
{
MerkleProof proof = null;
for (Long n : expectedDataNodes.keySet()) {
proof = r3_mkt.getProof(n.longValue());
assertNotNull(proof);
assertEquals(expectedDataNodes.get(n), proof.getDataHash());
}
}
}
use of com.jd.blockchain.ledger.MerkleDataNode in project jdchain-core by blockchain-jd-com.
the class MerkleSequenceDataset method getLatestDataEntries.
public DataEntry<Bytes, byte[]>[] getLatestDataEntries(long fromIndex, int count) {
if (count > LedgerConsts.MAX_LIST_COUNT) {
throw new IllegalArgumentException("Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!");
}
if (fromIndex < 0 || (fromIndex + count) > merkleTree.getDataCount()) {
throw new IllegalArgumentException("Index out of bound!");
}
if (count == 0) {
return EMPTY_ENTRIES;
}
@SuppressWarnings("unchecked") DataEntry<Bytes, byte[]>[] values = new DataEntry[count];
byte[] bytesValue;
for (int i = 0; i < count; i++) {
MerkleDataNode dataNode = merkleTree.getData(fromIndex + i);
Bytes dataKey = encodeDataKey(dataNode.getKey());
bytesValue = valueStorage.get(dataKey, dataNode.getVersion());
values[i] = new VersioningKVData<Bytes, byte[]>(dataNode.getKey(), dataNode.getVersion(), bytesValue);
}
return values;
}
use of com.jd.blockchain.ledger.MerkleDataNode in project jdchain-core by blockchain-jd-com.
the class MerkleSequenceDataset method getLatestDataEntry.
public DataEntry<Bytes, byte[]> getLatestDataEntry(long index) {
if (index < 0 || index + 1 > merkleTree.getDataCount()) {
throw new IllegalArgumentException("Index out of bound!");
}
byte[] bytesValue;
MerkleDataNode dataNode = merkleTree.getData(index);
Bytes dataKey = encodeDataKey(dataNode.getKey());
bytesValue = valueStorage.get(dataKey, dataNode.getVersion());
DataEntry<Bytes, byte[]> entry = new VersioningKVData<Bytes, byte[]>(dataNode.getKey(), dataNode.getVersion(), bytesValue);
return entry;
}
use of com.jd.blockchain.ledger.MerkleDataNode in project jdchain-core by blockchain-jd-com.
the class MerkleSequenceDataset method getValuesAtIndex.
/**
* get the data at the specific index;
*
* @param fromIndex
* @return
*/
public byte[] getValuesAtIndex(int fromIndex) {
MerkleDataNode dataNode = merkleTree.getData(fromIndex);
Bytes dataKey = encodeDataKey(dataNode.getKey());
return valueStorage.get(dataKey, dataNode.getVersion());
}
use of com.jd.blockchain.ledger.MerkleDataNode in project jdchain-core by blockchain-jd-com.
the class MerkleSequenceDataset method getLatestValues.
public byte[][] getLatestValues(long fromIndex, int count) {
if (count > LedgerConsts.MAX_LIST_COUNT) {
throw new IllegalArgumentException("Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!");
}
if (fromIndex < 0 || (fromIndex + count) > merkleTree.getDataCount()) {
throw new IllegalArgumentException("Index out of bound!");
}
byte[][] values = new byte[count][];
for (int i = 0; i < count; i++) {
MerkleDataNode dataNode = merkleTree.getData(fromIndex + i);
Bytes dataKey = encodeDataKey(dataNode.getKey());
values[i] = valueStorage.get(dataKey, dataNode.getVersion());
}
return values;
}
Aggregations