本文主要介绍LedgerDB内的数据可信性验证。同时提供可信性验证的方法,用户可根据业务需求,自行对LedgerDB内的数据进行可信性验证。

什么是数据可信性?

指写入Ledger实例的数据,具有不可篡改和不可抵赖性,同时可追溯和溯源,并且支持可信性的验证。

  • 不可篡改:任何数据写入Ledger实例后,任何人都无能力修改和删除,包括LedgerDB服务提供方。
  • 数据不可抵赖:任何数据的写入动作,都需要数据的写入方使用自己的私钥为待写入的数据进行签名,配合“不可篡改”能力,任何写入Ledger实例的数据都不能被抵赖。
  • 数据溯源(追溯):由于LedgerDB在数据记录时采用类似Journal模式,所有数据的非读操作都会被写入对应的Ledger实例中,因此LedgerDB在数据库层面提供原生的数据溯源能力。

什么是数据可信性验证?

数据可信性验证,是指通过密码学算法来验证存入LedgerDB内的数据是否被篡改。

控制台可信性验证

LedgerDB底层基于新型的默克尔累加器进行研发,所以在数据可信性验证时,会涉及到默克尔树的相关概念。

用户可对指定账本的指定数据进行可信性验证。

JSON字段解释

result 验证结果。成功/失败(成功表示数据可信性验证通过)
MemberId 被验证数据的写入方ID
LedgerId 被验证数据所属LedgerID
JournalSequence 被验证数据在所属Ledger中的记录编号
RootHash 被验证数据的父节点哈希值
WriterPubKey 被验证数据的写入方公钥
ProofPath 进行可信性验证的辅助数据。用户可利用此数据和RootHash,通过本文提供的验证方法自行进行验证
Timestamp 被验证数据的写入时间戳

数据可信性验证方法

用户可根据业务需求,通过此方法自行验证数据的可信性。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.common.hash.Hashing;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.util.StringUtils;


public class ProofDemo {

  @Test
  public void test() {
    // 来自于从LedgerDB控制台>数据可信性验证>JSON复制功能。同时也可以自行拼装 
    // 对应ProofPath字段
    String proofPath = "******";
    
    // 来自于从LedgerDB控制台>数据可信性验证>JSON复制功能 
    // 对应RootHash字段
    String rootHash = "来自于从LedgerDB控制台>数据可信性验证>JSON复制功能";
      
    Assert.assertTrue(verifyProofPathV1(rootHash, proofPath));
  }

  public static byte[] calculateRoot(JSONArray proofPath) {
    byte[][] childHashes = new byte[proofPath.size()][];
    for (int i = 0; i < proofPath.size(); i++) {
      Object child = proofPath.get(i);
      byte[] childHash = null;  
      if (child instanceof String) {
        // 叶子节点  
        childHash = Hex.decode((String) child);
      } else {
        // 分支节点  
        childHash = calculateRoot(proofPath.getJSONArray(i));
      }
      childHashes[i] = childHash;
    }
    // 父节点hash为子节点的hash的拼接作为输入计算而得  
    byte[] input = Arrays.concatenate(childHashes);
    return Hashing.sha256().hashBytes(input).asBytes();
  }

  public static boolean verifyProofPathV1(String rootHash, String proofPath) {
    JSONArray path = JSON.parseArray(proofPath);
    if (StringUtils.isEmpty(rootHash)) {
      if (path.size() == 0) {
        return true;
      } else {
        return false;
      }
    }
    if (path.size() == 1) {
      return rootHash.equals(path.getString(0));
    }
    byte[] rootCalculated = calculateRoot(path);
    return rootHash.equalsIgnoreCase(Hex.toHexString(rootCalculated));
  }
}