上篇讲过双层网络,这篇主要讲解轻节点验证和收据证明。

上次文章写了下双层网络,收到了一些好评也遇到了些问题。这篇文章将主要讲解轻节点证明和收据证明,只有节点可靠即节点MMR验证通过才会去获取收据证明。轻节点证明中

这是之前硬编码到代码中轻节点CHT验证需要的数据,后来感觉每次升级要更改这个和安全性太低升级为验证人合约。

    MainnetCheckpointOracle = &CheckpointOracleConfig{         Address: common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"),         Signers: []common.Address{             common.HexToAddress("0x1b2C260efc720BE89101890E4Db589b44E950527"), // Peter             common.HexToAddress("0x78d1aD571A1A09D60D9BBf25894b44e4C8859595"), // Martin             common.HexToAddress("0x286834935f4A8Cfb4FF4C77D5770C2775aE2b0E7"), // Zsolt             common.HexToAddress("0xb86e2B0Ab5A4B1373e40c51A7C712c70Ba2f9f8E"), // Gary             common.HexToAddress("0x0DF8fa387C602AE62559cC4aFa4972A7045d6707"), // Guillaume         },     }

下面来看下这个轻节点简化验证流程

    header := new(types.Header)     if err := rlp.DecodeBytes(headerEnc, header); err != nil      // Verify the CHT     value, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)     var node light.ChtNode     if err := rlp.DecodeBytes(value, &node); err != nil      if node.Hash != header.Hash()     if r.BlockNum != header.Number.Uint64()     }

可以看到,首先如下验证

  • CHT的proof证明是否可信,成功获取到叶子节点value
  • 轻节点获取的区块头HASH方法和value中获取ChtNode中hash比较
  • 比较CHT的高度是否和区块头的一致

经过如上证明,可知对方节点当前可信,这是之后验证的起点,之前的几百万区块不需要下载,这样轻节点在手机,浏览器才有可能运行。

MMR证明

讲解了如上CHT证明,我们来看下MMR证明。MMR证明需要在区块头里面加MMR的根,每挖到新区块的时候都需要更新MMR的根

    n := ulvp.NewNode(b.Hash(), d, new(big.Int).Set( b.Difficulty()), big.NewInt(0), time)     mmr.Push(n)

MMR主要是靠采样,根据安全精度决对采样数据的数量,只需要logN的数据即可证明节点的安全性,被称为超轻节点。网上有很多这方面的介绍,这里主要说下如何实现。有两个地方需要更改

  • 矿工挖矿需要更新区块头MMR根调用PushBlockInMMR
  • 验证节点校验MMR并更新树

MMR第一次验证

节点第一次握手的时候,需要校验对方是否为可信的对链节点

            proof, err := uLVP.PushFirstMsg()             errc <- p2p.Send(p.rw, StatusMsg, &statusData{                 ProtocolVersion: uint32(p.version),                 NetworkID:       network,                 TD:              td,                 Head:            head,                 Genesis:         genesis,                 Proof:           [][]byte{proof},             })

需要将MMR Proof证明发给对方节点,证明包含如下部分

    Right, heads := getRightDifficult(uv.localChain, curNum, new(big.Int).Set(cur.Difficulty))     proof, _, _ := uv.MmrInfo.CreateNewProof(Right)     heads = append([]*types.Header{genesis.Header(), cur}, heads...)     res := &ulvp.ChainHeaderProofMsg{         Proof:  proof,Header: heads,Right:  Right,}

对链节点握手验证

func (p *peer) readOtherStatus(network uint64, status *statusData, genesis common.Hash, uLVP *core.SimpleULVP) error {     msg, err := p.rw.ReadMsg()     // Decode the handshake and make sure everything matches     if err := msg.Decode(&status); err != nil {         return errResp(ErrDecode, "msg %v: %v", msg, err)     }     if status.Genesis != genesis      if err := uLVP.VerifyFirstMsg(status.Proof[0]); err != nil      return nil }

创世校验后调用MMR验证流程。

收据证明

网络中收到跨链交易后,将交易转发给矿工模块。

    if ev, ok := ev.Data.(core.NewOtherTxsEvent); ok {         for _, tx := range ev.Txs {             if w.insertCM(tx) && !request {                 request = true                 w.requestCrossTxProof(tx.Hash())             }         }     }

矿工将交易插入缓存并去重,调用请求收据proof证明。

BestPeer请求证明

请求证明的过程涉及到找哪一个Peer请求数据的流程

    if ev, ok := obj.Data.(core.NewRequestTxProofEvent); ok {         peer := pm.peersOther.BestPeer()         if peer != nil {             peer.RequestMMRReceipts([]common.Hash{ev.TxHash})         }     }

通过寻找难度最高的节点请求数据是最好的,如果难度最高的节点验证失败,找次之节点继续验证

对链Hash通知

本链每个区块的Hash和难度都会通过NewOtherBlockHashesMsg广播到对链,这样对链有了一个缓存本链哪个Peer的区块是最新的机制。

MMR第二次请求

当节点收到GetMMRReceiptProofMsg消息,为对链查询跨链消息的证明。

    case msg.Code == GetMMRReceiptProofMsg:         var query getBlockMMRData         if err := msg.Decode(&query)         var mtProof ulvp.SimpleUlvpProof         receiptRep, receipt, err := pm.ulVP.GetReceiptProof(query.TxHash)         data, err := pm.ulVP.HandleSimpleUlvpMsgReq(pm.ulVP.GetSimpleUlvpMsgReq([]uint64{receipt.BlockNumber.Uint64(), pm.blockchain.CurrentBlock().NumberU64()}))         mtProof.Result = true         mtProof.ReceiptProof = receiptRep         mtProof.ChainProof = &ulvp.UlvpChainProof{Res: data}         mtProof.Header = pm.blockchain.GetHeaderByHash(receipt.BlockHash)         mtProof.End = pm.blockchain.CurrentBlock().Number()         mtProof.TxHash = query.TxHash

首先拿到对链请求的Txhash,查找此交易收据是否存在,生成收据的proof证明。

func (uv *SimpleULVP) GetReceiptProof(txHash common.Hash) (*ulvp.ReceiptTrieResps, *types.Receipt, error) {     lookup := uv.localChain.GetTransactionLookup(txHash)     receipts := uv.localChain.GetReceiptsByHash(lookup.BlockHash)      tri := types.DeriveShaHasher(receipts, new(trie.Trie))     keybuf := new(bytes.Buffer)     keybuf.Reset()     rlp.Encode(keybuf, lookup.Index)     proofs := types.NewNodeSet()     tri.Prove(keybuf.Bytes(), 0, proofs)     return &ulvp.ReceiptTrieResps{Proofs: proofs.NodeList(), Index: lookup.Index, ReceiptHash: block.ReceiptHash()}, receipt, nil }

根据交易所在高度生成MMR证明,由于高度发生改变,需要重新采样,故此时需要新的MMR证明。

data, err := pm.ulVP.HandleSimpleUlvpMsgReq(pm.ulVP.GetSimpleUlvpMsgReq([]uint64{receipt.BlockNumber.Uint64(), pm.blockchain.CurrentBlock().NumberU64()}))

将收据所在高度和当前区块的高度传进去生成证明。将两个证明拼在一起发给对方节点。

节点收据校验

节点收到验证数据,验证MMR和收据证明是否正确,校验失败将把本peer踢掉,然后通知矿工从新请求。

    case msg.Code == MMRReceiptProofMsg:         var request *ulvp.SimpleUlvpProof         if err := msg.Decode(&request); err != nil {             return errResp(ErrDecode, "%v: %v", msg, err)         }         find := false         if !request.Result {             find = true         } else if _, err := request.VerifyULVPTXMsg(request.TxHash); err != nil {             find = true         }         if find {             pm.removeOtherPeer(p.id)             request.Result = false         }         pm.eventMux.Post(core.NewProofEvent{MRProof: request})

如果验证成功,矿工需要转换跨链为新的铸币交易,这部分在下篇继续梳理。目前已把节点证明和收据证明梳理完。

Mouse和Duck跨链github链接

感兴趣的朋友欢迎一起讨论。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注