Near Protocol
How it works
NearProtocolLightClient is an implementation of the NEAR light client in Solidity as an Mapo contract. The state of the light client is defined by:
BlockHeaderInnerLiteView
for the current head (which containsheight
,epoch_id
,next_epoch_id
,prev_state_root
,outcome_root
,timestamp
, the hash of the block producers set for the next epochnext_bp_hash
, and the merkle root of all the block hashesblock_merkle_root
);The set of block producers for the current and next epochs.
The epoch_id
refers to the epoch to which the block that is the current known head belongs, and next_epoch_id
is the epoch that will follow.
Light clients operate by periodically fetching instances of LightClientBlockView
via particular RPC end-point described here.
Light client doesn't need to receive LightClientBlockView
for all the blocks. Having the LightClientBlockView
for block B
is sufficient to be able to verify any statement about state or outcomes in any block in the ancestry of B
(including B
itself). In particular, having the LightClientBlockView
for the head is sufficient to locally verify any statement about state or outcomes in any block on the canonical chain.
However, to verify the validity of a particular LightClientBlockView
, the light client must have verified a LightClientBlockView
for at least one block in the preceding epoch, thus to sync to the head the light client will have to fetch and verify a LightClientBlockView
per epoch passed.
How to verify
updateBlockHeader
The fields prev_block_hash
, next_block_inner_hash
and inner_rest_hash
are used to reconstruct the hashes of the current and next block, and the approvals that will be signed, in the following way (where block_view
is an instance of LightClientBlockView
):
The light client updates its head with the information from LightClientBlockView
iff:
The height of the block is higher than the height of the current head;
The epoch of the block is equal to the
next_epoch_id
known for the current head;If the epoch of the block is equal to the
next_epoch_id
of the head, thennext_bps
is notNone
;approvals_after_next
contain valid signatures onapproval_message
from the block producers of the corresponding epoch (see next section);The signatures present in
approvals_after_next
correspond to more than 2/3 of the total stake (see next section).If
next_bps
is not none,sha256(borsh(next_bps))
corresponds to thenext_bp_hash
ininner_lite
.To simplify the protocol we require that the next block and the block after next are both in the same epoch as the block that
LightClientBlockView
corresponds to. It is guaranteed that each epoch has at least one final block for which the next two blocks that build on top of it are in the same epoch.By construction by the time the
LightClientBlockView
is being validated, the block producers set for its epoch is known. Specifically, when the first light client block view of the previous epoch was processed, due to (3) above thenext_bps
was notNone
, and due to (6) above it was corresponding to thenext_bp_hash
in the block header.The sum of all the stakes of
next_bps
in the previous epoch istotal_stake
referred to in (5) above.The signatures in the
LightClientBlockView::approvals_after_next
are signatures onapproval_message
. Thei
-th signature inapprovals_after_next
, if present, must validate against thei
-th public key innext_bps
from the previous epoch.approvals_after_next
can contain fewer elements thannext_bps
in the previous epoch.approvals_after_next
can also contain more signatures than the length ofnext_bps
in the previous epoch. This is due to the fact that, as per consensus specification, the last blocks in each epoch contain signatures from both the block producers of the current epoch, and the next epoch. The trailing signatures can be safely ignored by the light client implementation.
verifyProofData
To verify that a transaction or receipt happens on chain, a light client can request a proof through rpc by providing receipt_id and the block hash of light client head. The rpc will return the following struct
which includes everything that a light client needs to prove the execution outcome of the given transaction or receipt.
The proof verification can be broken down into two steps, execution outcome root verification and block merkle root verification.
Execution Outcome Root Verification
If the outcome root of the transaction or receipt is included in block H
, then outcome_proof
includes the block hash of H
, as well as the merkle proof of the execution outcome in its given shard. The outcome root in H
can be reconstructed by
This outcome root must match the outcome root in block_header_lite.inner_lite
.
Block Merkle Root Verification
Recall that block hash can be computed from LightClientBlockLiteView
by
The expected block merkle root can be computed by
which must match the block merkle root in the light client block of the light client head.
Proof
LightClientBlock
Proof of transation
Last updated