PolygonLightClient is an implementation of the polygon Light Client in Solidity as an MapoContract.
Polygon is a Proof-of-stake system. Anyone can stake their Matic token on Ethereum smart-contract, "staking contract", and become a validator for the system.Once validators are active on Heimdall they get selected as producers through bor module.
Bor consensus is inspired by Clique consensus: https://eips.ethereum.org/EIPS/eip-225. Clique works with multiple pre-defined producers. All producers vote on new producers using Clique APIs. They take turns creating blocks.
Bor fetches new producers through span and sprint management mechanism.
The validators changes every epoch,each selected validator address is written to the epoch block in the extraData field of the block header. next block begins production and validation of the block.These validators participate in the consensus protocol by signing blocks that contain cryptographic signatures signed by each validator's private key.
If we want to validate a transaction, we need to validate the block header that the transaction is in,to validate a block header and we need to validate the signature of the block header.
by tracking validators changes light node can verify all transations.
How to verify
updateBlockHeader
keep track of the validator's changes by continuously submitting epoch block headers to light client.The submitted epoch block must be signed with the private key by one of the validator's submitted in the previous epoch. so we initialize an epoch and store the validator's address can keep committing the next epoch block over and over again.to improve certainty, multiple blocks need to be submitted as confirms.
functionupdateBlockHeader(bytesmemory_blockHeadersBytes ) externaloverridewhenNotPaused { Verify.BlockHeader[] memory _blockHeaders = abi.decode( _blockHeadersBytes, (Verify.BlockHeader[]) );require(confirms >0," not initialize");require(_blockHeaders.length == confirms,"not enough"); _lastSyncedBlock += EPOCH_NUM;require( _blockHeaders[0].number == _lastSyncedBlock,"invalid syncing block" );// index 0 header verify by pre validators others by index 0 getValidators validators[(_lastSyncedBlock +1) / EPOCH_NUM] = Verify.getValidators( _blockHeaders[0].extraData );require(_verifyBlockHeaders(_blockHeaders),"blocks verify fail");emitUpdateBlockHeader(tx.origin, _blockHeaders[0].number); }
updateBlockHeader take a few steps
1.check that the first committed block is the next epoch block.
2.check that the number of blocks submitted is sufficient.
functionrecoverSigner(BlockHeadermemory_header ) internalpurereturns (address) { (bytesmemory signature,bytesmemory extraData) =splitExtra( _header.extraData );bytes32hash=keccak256(encodeSigHeader(_header, extraData));bytes32 r;bytes32 s;uint8 v;// ecrecover takes the signature parameters, and the only way to get them// currently is to use assembly.assembly { r :=mload(add(signature,0x20)) s :=mload(add(signature,0x40)) v :=byte(0,mload(add(signature,0x60))) }if (v <=1) { v = v +27; }address signer =ecrecover(hash, v, r, s);return signer; }
check if siger is in the corresponding validatorSet.
The light client can verify the epoch blocks after it has the epoch validatorSet.to verify the receipt should first veriy the block transation receipt in.verify the block is similar to the update block,won't go into it again.
we know that receipts from block transactions form a receipt patricia-merkle-trie. the block field receiptsRoot is the root of the tree. after we verify the block we can trust the receiptsRoot.
so we can build proof of the transation receipt off chain submit to light client to proof transaton receipts.