MAPO Developer Docs
LearnDevelopRunWhitePaperBRC-201
English
English
  • Overview
  • Learn
    • About MAP Protocol
      • Background
      • Introduction of MAP Protocol
      • History of MAP Protocol
    • Comparison with other cross-chain models
      • MAP Protocol vs Cosmos & Polkadot
      • MAP vs other cross-chain solutions without relay chain
    • Technical Mechanism
      • MAP Protocol's Technical Mechanism
      • Three-layer Architecture
      • MAP Protocol Layer
        • Concept
        • Isomorphism with All Chains
        • Verification & Maintenance Network
      • MAP Omnichain Service (MOS) Layer
      • MAPO Application Layer
      • Peer-to-Peer Cross-chain Technology with ZK-enabled Light Clients
      • Interact with Bitcoin
    • Gas Fee Model
    • DAO
    • Construction of MAP Protocol
      • Developers
      • Validators
      • Maintainers
      • Messengers
      • Liquidity providers
      • End users
    • Tokenomics
    • Purchase $MAP
  • Develop
    • MAP Relay Chain
      • Getting Started
        • Build
        • Make Private Chain
        • How To Vote
        • How To Withdraw
        • Integrate an Exchange
        • Integrate MAP Relay Chain with EVM-Compatible Chains
      • Consensus
        • Overview
        • Proof-of-Stake
        • Validator
          • Validator
          • Locked MAP
        • Election
        • Rewards
        • Aggregated Seal
      • Contracts
        • Precompiled Contracts
        • Genesis Contracts
          • ABI
            • AccountsABI
            • ElectionABI
            • EpochRewardsABI
            • LockedGoldABI
            • ValidatorsABI
          • Deploy
      • Marker
        • Genesis
        • Validator
        • Vote
        • ContractOwner
        • Common
      • Account-Abstraction
    • Light Client
      • Verification based on Light Client
      • MAPO Light Client
        • EVM Chains
        • Near
      • Light Clients
        • Client Manager
        • BNB Smart Chain
        • Near Protocol
        • Polygon(Matic)
        • Ethereum 2.0
        • Klaytn
        • Conflux)
      • Maintainer
    • MAP Omnichain Service (MOS)
      • MOS Message Guides
        • How It Works
        • How To Use
        • EVM Chains Contract
      • MCS Guides
        • How It Works
        • How To Use
          • How To On Evm Chains
          • How To On Near Protocol
        • Relay Chain Contract
        • EVM Chains Contract
        • Near Protocol Contract
      • Messenger
      • API
    • OmniChain Examples
      • OmniApp
      • OmniDictionary
    • API & SDK
      • Butter SDK
      • Atlas JSON RPC
      • Atlas Consensus API
      • MAP Scan API
    • Connected Chains and Corresponding Addresses
  • Run
    • How To Become A New Validator
    • How To Become A New Validator[advanced]
    • Withdraw
Powered by GitBook
On this page
  • How it works
  • How to verify
  • Proof

Was this helpful?

  1. Develop
  2. Light Client
  3. Light Clients

Polygon(Matic)

PreviousNear ProtocolNextEthereum 2.0

Last updated 2 years ago

Was this helpful?

How it works

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.

consensus is inspired by Clique consensus: . 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.

    struct BlockHeader {
        bytes parentHash;
        bytes sha3Uncles;
        address miner;
        bytes stateRoot;
        bytes transactionsRoot;
        bytes receiptsRoot;
        bytes logsBloom;
        uint256 difficulty;
        uint256 number;
        uint256 gasLimit;
        uint256 gasUsed;
        uint256 timestamp;
        bytes extraData;
        bytes mixHash;
        bytes nonce;
        uint256 baseFeePerGas;
    }

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.

 function updateBlockHeader(
        bytes memory _blockHeadersBytes
    ) external override whenNotPaused {
        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");

        emit UpdateBlockHeader(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.

3.verify each submitted block separately.

  • validate the field of the block

  function validateHeader(
        BlockHeader memory _header,
        uint256 _minEpochBlockExtraDataLen,
        BlockHeader memory _parent
    ) internal pure returns (bool) {
        if (_header.extraData.length < (EXTRA_VANITY + EXTRASEAL)) {
            return false;
        }
        //Epoch block
        if ((_header.number + 1) % EPOCH_NUM == 0) {
            if (_header.extraData.length < _minEpochBlockExtraDataLen) {
                return false;
            }
        }

        if (_header.difficulty > 200) {
            return false;
        }

        if (_header.parentHash.length != 32) {
            return false;
        }

        if (_header.miner != address(0)) {
            return false;
        }

        if (
            _header.sha3Uncles.length != 32 ||
            bytes32(_header.sha3Uncles) != SHA3_UNCLES
        ) {
            return false;
        }

        if (_header.nonce.length != 8 || bytes8(_header.nonce) != NONCE) {
            return false;
        }

        if (
            _header.mixHash.length != 32 || bytes32(_header.mixHash) != MIX_HASH
        ) {
            return false;
        }
        //2**63 - 1 maxGasLimit minGasLimit 5000
        if (
            _header.gasLimit > 2 ** 63 - 1 ||
            _header.gasLimit < MIN_GAS_LIMIT ||
            _header.gasLimit < _header.gasUsed
        ) {
            return false;
        }

        if (_header.number != _parent.number) {
        
            if(_header.timestamp <= _parent.timestamp) {
                return false;
            }
            uint256 diff = _parent.gasLimit > _header.gasLimit
                ? _parent.gasLimit - _header.gasLimit
                : _header.gasLimit - _parent.gasLimit;
      
            if (diff >= _parent.gasLimit / 1024) {
                return false;
            }

            uint256 expectedBaseFee = calcBaseFee(
                _parent.gasUsed,
                _parent.gasLimit,
                _parent.baseFeePerGas
            );

            if (_header.baseFeePerGas != expectedBaseFee) {
                return false;
            }
        }

        return true;
    }
  • verify the signature of the block

        function recoverSigner(
            BlockHeader memory _header
        ) internal pure returns (address) {
            (bytes memory signature, bytes memory extraData) = splitExtra(
                _header.extraData
            );
    
            bytes32 hash = 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.

                address signer = Verify.recoverSigner(_blockHeaders[i]);
                require(
                    Verify.containValidator(
                        validators[_blockHeaders[i].number / EPOCH_NUM],
                        signer
                    ),
                    "invalid block header singer"
                );

4.store validatorSet.

        validators[(_lastSyncedBlock + 1) / EPOCH_NUM] = Verify.getValidators(
            _blockHeaders[0].extraData
        );

verify receipt

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.

so we can build proof of the transation receipt off chain submit to light client to proof transaton receipts.

    function validateProof(
        bytes32 _receiptsRoot,
        ReceiptProof memory _receipt,
        address _mptVerify
    ) internal pure returns (bool success, bytes memory logs) {
        bytes memory bytesReceipt = encodeReceipt(_receipt.txReceipt);
        bytes memory expectedValue = bytesReceipt;
        if (_receipt.txReceipt.receiptType > 0) {
            expectedValue = abi.encodePacked(
                bytes1(uint8(_receipt.txReceipt.receiptType)),
                bytesReceipt
            );
        }

        success = IMPTVerify(_mptVerify).verifyTrieProof(
            _receiptsRoot,
            _receipt.keyIndex,
            _receipt.proof,
            expectedValue
        );

        if (success) logs = bytesReceipt.toRlpItem().toList()[3].toRlpBytes(); // list length must be 4
    }

Proof

    struct ProofData {
        Verify.BlockHeader[] headers;   // proof block headers
        Verify.ReceiptProof receiptProof; 
    }

    struct ReceiptProof {
        TxReceipt txReceipt;
        bytes keyIndex;
        bytes[] proof;
    } 

    struct TxReceipt {
        uint256 receiptType;
        bytes postStateOrStatus;
        uint256 cumulativeGasUsed;
        bytes bloom;
        TxLog[] logs;
    }

    struct TxLog {
        address addr;
        bytes[] topics;
        bytes data;
    }

we know that receipts from block transactions form a receipt . the block field receiptsRoot is the root of the tree. after we verify the block we can trust the receiptsRoot.

Bor
https://eips.ethereum.org/EIPS/eip-225
patricia-merkle-trie