NEAR light client

1. 引言

NEAR官方在2020年10月发布了一则 Succinct Proof for NEAR light client的悬赏。

NEAR采用的是ed25519签名。
NEAR light client应:

  • 压缩the light block verification 到 仅需验证一个单一的proof,这样可快速同步区块并用于Rainbow bridge中。

NEAR light client希望达成的目标有:

  • 1)Light client proof generator, that fetches specific block by height from the node over RPC and generates a succinct proof that takes block hash and a block from previous epoch as arguments。
  • 2)提供多种blocks proofs的测试用例,包括但不限于:epoch的最后一个区块、epoch的第一个区块、epoch的中间区块、无效区块(如无效data,无效签名等)、扫描NEAR主网所有区块生成proof并verify。
  • 3)Solidity携程的Proof verifier合约,输入为某previous epoch的某block hash和block,输出为a proof。
  • 4)从创世区块到指定区块的recursively proof。

2. Light client

NEAR light client state定义为:

  • 当前区块头BlockHeaderInnerLiteView:包含了heightepoch_idnext_epoch_idprev_state_rootoutcome_roottimestamp
  • 下一epoch的block producers set的hash值next_bp_hash
  • 所有区块hashes的merkle root block_merkle_root
  • 当前epoch的block producers set。
  • 下一epoch的block producers set。

Light client需定期通过特殊的RPC end-point来获取LightClientBlockView instances。

Light client并不需要收所有区块的LightClientBlockView。具有区块 B B BLightClientBlockView,就足以验证区块B及其之前任意区块的state或outcome statement。

但是,为了验证某特定LightClientBlockView的有效性,light client必须验证a LightClientBlockView for at least one block in the preceding epoch,从而,在每个epoch需向light client至少同步并验证一个LightClientBlockView

2.1 Validating light client block views

相关结构体定义为:

pub enum ApprovalInner {
    Endorsement(CryptoHash),
    Skip(BlockHeight)
}

pub struct ValidatorStakeView {
    pub account_id: AccountId,
    pub public_key: PublicKey,
    pub stake: Balance,
}

pub struct BlockHeaderInnerLiteView {
    pub height: BlockHeight,
    pub epoch_id: CryptoHash,
    pub next_epoch_id: CryptoHash,
    pub prev_state_root: CryptoHash,
    pub outcome_root: CryptoHash,
    pub timestamp: u64,
    pub next_bp_hash: CryptoHash,
    pub block_merkle_root: CryptoHash,
}

pub struct LightClientBlockLiteView {
    pub prev_block_hash: CryptoHash,
    pub inner_rest_hash: CryptoHash,
    pub inner_lite: BlockHeaderInnerLiteView,
}


pub struct LightClientBlockView {
    pub prev_block_hash: CryptoHash,
    pub next_block_inner_hash: CryptoHash,
    pub inner_lite: BlockHeaderInnerLiteView,
    pub inner_rest_hash: CryptoHash,
    pub next_bps: Option<Vec<ValidatorStakeView>>,
    pub approvals_after_next: Vec<Option<Signature>>,
}

block hash的计算方式为:

sha256(concat(
    sha256(concat(
        sha256(borsh(inner_lite)),
        sha256(borsh(inner_rest))
    )),
    prev_hash
))

可根据prev_block_hashnext_block_inner_hashinner_rest_hash 来重构当前区块hash和下一区块hash。approval_message为待签名消息,其中block_view为an instance of LightClientBlockView

def reconstruct_light_client_block_view_fields(block_view):
    current_block_hash = sha256(concat(
        sha256(concat(
            sha256(borsh(block_view.inner_lite)),
            block_view.inner_rest_hash,
        )),
        block_view.prev_block_hash
    ))

    next_block_hash = sha256(concat(
        block_view.next_block_inner_hash,
        current_block_hash
    ))

    approval_message = concat(
        borsh(ApprovalInner::Endorsement(next_block_hash)),
        little_endian(block_view.inner_lite.height + 2)
    )

    return (current_block_hash, next_block_hash, approval_message)

当且仅当满足以下条件时,light client才会根据LightClientBlockView来更新区块头:

  • 1)区块高度 高于 当前高度;
  • 2)区块中的epoch 等于当前head中的epoch_idnext_epoch_id
  • 3)若区块中的epoch 等于当前head中的next_epoch_id,则next_bps不为None
  • 4)approvals_after_next中包含了来自于相应epoch的block producers对approval_message的有效签名;
  • 5)approvals_after_next中的签名对应more than 2/3 of the total stake;
  • 6)若next_bps不为none,则sha256(borsh(next_bps))对应为inner_lite中的next_bp_hash
def validate_and_update_head(block_view):
    global head
    global epoch_block_producers_map

    current_block_hash, next_block_hash, approval_message = reconstruct_light_client_block_view_fields(block_view)

    # (1)
    if block_view.inner_lite.height <= head.inner_lite.height:
        return False

    # (2)
    if block_view.inner_lite.epoch_id not in [head.inner_lite.epoch_id, head.inner_lite.next_epoch_id]:
        return False

    # (3)
    if block_view.inner_lite.epoch_id == head.inner_lite.next_epoch_id and block_view.next_bps is None:
        return False

    # (4) and (5)
    total_stake = 0
    approved_stake = 0

    epoch_block_producers = epoch_block_producers_map[block_view.inner_lite.epoch_id]
    for maybe_signature, block_producer in zip(block_view.approvals_after_next, epoch_block_producers):
        total_stake += block_producer.stake

        if maybe_signature is None:
            continue

        approved_stake += block_producer.stake
        if not verify_signature(
            public_key: block_producer.public_key,
            signature: maybe_signature,
            message: approval_message
        ):
            return False

    threshold = total_stake * 2 // 3
    if approved_stake <= threshold:
        return False

    # (6)
    if block_view.next_bps is not None:
        if sha256(borsh(block_view.next_bps)) != block_view.inner_lite.next_bp_hash:
            return False

        epoch_block_producers_map[block_view.inner_lite.next_epoch_id] = block_view.next_bps

    head = block_view

2.2 验签

为了简化,此时要求LightClientBlockView对应的区块、下一区块、下下个区块 均属于同一epoch。从而保证每个epoch至少有一个final block for which the next two blocks that build on top of it are in the same epoch。

LightClientBlockView验证通过时,该epoch的block producer set即已知。当前一epoch的第一个light client block view处理完毕时,根据以上第(3)条可知next_bps不为None,根据以上第(6)条可知其对应block header中的next_bp_hash,根据以上第(5)条可知,前一epoch的next_bps的所有质押之和为total_stake

LightClientBlockView::approvals_after_next中的签名为对approval_message的签名。approvals_after_next中第 i i i个签名若存在,则必须可由前一epoch的next_bps中的第 i i i个公钥验签通过。approvals_after_next中的元素数可少于前一epoch的next_bps中的元素数。

approvals_after_next中的元素数也可多于前一epoch的next_bps中的元素数。因为根据 NEAR共识机制 可知,每个epoch的最后一个区块包含了来自当前epoch以及下一epoch的block producers的签名。多余的签名light client可安全地直接忽略。

2.3 Proof Verification

2.3.1 Transaction Outcome Proofs

为了验证某交易或receipt在链上已发生,light client必须通过RPC接口通过提供id和light client head的block hash 来获取proof,id类型为:

pub enum TransactionOrReceiptId {
    Transaction { hash: CryptoHash, sender: AccountId },
    Receipt { id: CryptoHash, receiver: AccountId },
}

RPC接口会返回如下结构体:

pub struct RpcLightClientExecutionProofResponse {
    /// Proof of execution outcome
    pub outcome_proof: ExecutionOutcomeWithIdView,
    /// Proof of shard execution outcome root
    pub outcome_root_proof: MerklePath,
    /// A light weight representation of block that contains the outcome root
    pub block_header_lite: LightClientBlockLiteView,
    /// Proof of the existence of the block in the block merkle tree,
    /// which consists of blocks up to the light client head
    pub block_proof: MerklePath,
}

包含了light client证明the execution outcome of the given transaction or receipt的所有信息。
其中ExecutionOutcomeWithIdView定义为:

pub struct ExecutionOutcomeWithIdView {
    /// Proof of the execution outcome
    pub proof: MerklePath,
    /// Block hash of the block that contains the outcome root
    pub block_hash: CryptoHash,
    /// Id of the execution (transaction or receipt)
    pub id: CryptoHash,
    /// The actual outcome
    pub outcome: ExecutionOutcomeView,
}

proof verification可分为2步:

  • 1)execution outcome root verification
  • 2)block merkle root verification

(1)execution outcome root verification:
若区块 H H H中包含了the outcome root of the transaction or receipt,则outcome_proof中更包含了 H H H的区块hash,以及其所属shard的the merkle proof of the execution outcome。 H H H的outcome root可按如下方式重构:

shard_outcome_root = compute_root(sha256(borsh(execution_outcome)), outcome_proof.proof)
block_outcome_root = compute_root(sha256(borsh(shard_outcome_root)), outcome_root_proof)

该outcome root必须匹配block_header_lite.inner_lite中的outcome root。

(2)block merkle root verification
根据LightClientBlockLiteView计算block hash的方法为:

sha256(concat(
    sha256(concat(
        sha256(borsh(inner_lite)),
        sha256(borsh(inner_rest))
    )),
    prev_hash
))

期待的block merkle root可按如下方式计算:

block_hash = compute_block_hash(block_header_lite)
block_merkle_root = compute_root(block_hash, block_proof)

其必须匹配the block merkle root in the light client block of the light client head。

2.4 RPC接口

2.4.1 Light client block

NEAR的每个全节点为light client提供了接口来获取新的LightClientBlockView

http post http://127.0.0.1:3030/ jsonrpc=2.0 method=next_light_client_block params:="[<last known hash>]" id="dontcare"

该RPC接口会返回LightClientBlock for the block as far into the future from the last known hash as possible for the light client to still accept it。特别地,其要么返回下一epoch的最后一个final block,要么返回the last final known block。当没有更新的final block than the one the light client knows about,该RPC接口会返回empty result。

A standalone light client would bootstrap by requesting next blocks until it receives an empty result, and then periodically request the next light client block.

A smart contract-based light client that enables a bridge to NEAR on a different blockchain naturally cannot request blocks itself. Instead external oracles query the next light client block from one of the full nodes, and submit it to the light client smart contract. The smart contract-based light client performs the same checks described above, so the oracle doesn’t need to be trusted.

2.4.2 Light client proof

以下RPC接口可返回light client验证execution outcomes所需的RpcLightClientExecutionProofResponse

对于transaction execution outcome,相应的RPC接口为:

http post http://127.0.0.1:3030/ jsonrpc=2.0 method=EXPERIMENTAL_light_client_proof params:="{"type": "transaction", "transaction_hash": <transaction_hash>, "sender_id": <sender_id>, "light_client_head": <light_client_head>}" id="dontcare"

对于receipt execution outcome,相应的RPC接口为:

http post http://127.0.0.1:3030/ jsonrpc=2.0 method=EXPERIMENTAL_light_client_proof params:="{"type": "receipt", "receipt_id": <receipt_id>, "receiver_id": <receiver_id>, "light_client_head": <light_client_head>}" id="dontcare"

参考资料

[1] https://nomicon.io/ChainSpec/LightClient.html
[2] Rainbow Upgrade Idea: Using zk-SNARKS to cheaply verify ED25519 signatures on Ethereum mainnet

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值