diff --git a/contracts/src/BeefyClient.sol b/contracts/src/BeefyClient.sol index 58656fff0b..0dc8ef7696 100644 --- a/contracts/src/BeefyClient.sol +++ b/contracts/src/BeefyClient.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.22; import {ECDSA} from "openzeppelin/utils/cryptography/ECDSA.sol"; import {SubstrateMerkleProof} from "./utils/SubstrateMerkleProof.sol"; import {Bitfield} from "./utils/Bitfield.sol"; -import {Counter} from "./utils/Counter.sol"; +import {Uint16Array} from "./utils/Uint16Array.sol"; import {Math} from "./utils/Math.sol"; import {MMRProof} from "./utils/MMRProof.sol"; import {ScaleCodec} from "./utils/ScaleCodec.sol"; @@ -15,23 +15,15 @@ import {ScaleCodec} from "./utils/ScaleCodec.sol"; * * High-level documentation at https://docs.snowbridge.network/architecture/verification/polkadot * - * To submit new commitments signed by the current validator set, relayers must call - * the following methods sequentially: - * 1. submitInitial - * 2. commitPrevRandao - * 3. createFinalBitfield (this is just a call, not a transaction, to generate the validator subsampling) - * 4. submitFinal (with signature proofs specified by (3)) - * - * If the a commitment is signed by the next validator set, relayers must call - * the following methods sequentially: - * 1. submitInitialWithHandover - * 2. commitPrevRandao - * 3. createFinalBitfield (this is just a call, not a transaction, to generate the validator subsampling) - * 4. submitFinalWithHandover (with signature proofs specified by (3)) + * To submit new commitments, relayers must call the following methods sequentially: + * 1. submitInitial: Setup the session for the interactive submission + * 2. commitPrevRandao: Commit to a random seed for generating a validator subsampling + * 3. createFinalBitfield: Generate the validator subsampling + * 4. submitFinal: Complete submission after providing the request validator signatures * */ contract BeefyClient { - using Counter for uint256[]; + using Uint16Array for Uint16Array.Array; using Math for uint16; using Math for uint256; @@ -50,111 +42,122 @@ contract BeefyClient { * @dev The Commitment, with its payload, is the core thing we are trying to verify with * this contract. It contains an MMR root that commits to the polkadot history, including * past blocks and parachain blocks and can be used to verify both polkadot and parachain blocks. - * @param blockNumber relay chain block number - * @param validatorSetID id of the validator set that signed the commitment - * @param payload the payload of the new commitment in beefy justifications (in - * our case, this is a new MMR root for all past polkadot blocks) */ struct Commitment { + // Relay chain block number uint32 blockNumber; + // ID of the validator set that signed the commitment uint64 validatorSetID; + // The payload of the new commitment in beefy justifications (in + // our case, this is a new MMR root for all past polkadot blocks) PayloadItem[] payload; } /** * @dev Each PayloadItem is a piece of data signed by validators at a particular block. - * This includes the relay chain's MMR root. - * @param payloadID an ID that references a description of the data in the payload item. - * Known payload ids can be found [upstream](https://github.com/paritytech/substrate/blob/fe1f8ba1c4f23931ae89c1ada35efb3d908b50f5/primitives/consensus/beefy/src/payload.rs#L27). - * @param data the contents of the payload item. */ struct PayloadItem { + // An ID that references a description of the data in the payload item. + // Known payload ids can be found [upstream](https://github.com/paritytech/substrate/blob/fe1f8ba1c4f23931ae89c1ada35efb3d908b50f5/primitives/consensus/beefy/src/payload.rs#L27). bytes2 payloadID; + // The contents of the payload item bytes data; } /** * @dev The ValidatorProof is a proof used to verify a commitment signature - * @param v the parity bit to specify the intended solution - * @param r the x component on the secp256k1 curve - * @param s the challenge solution - * @param index index of the validator address in the merkle tree - * @param account validator address - * @param proof merkle proof for the validator */ struct ValidatorProof { + // The parity bit to specify the intended solution uint8 v; + // The x component on the secp256k1 curve bytes32 r; + // The challenge solution bytes32 s; + // Leaf index of the validator address in the merkle tree uint256 index; + // Validator address address account; + // Merkle proof for the validator bytes32[] proof; } /** * @dev A ticket tracks working state for the interactive submission of new commitments - * @param sender the sender of the initial transaction - * @param bitfield a bitfield signalling which validators they claim have signed - * @param blockNumber the block number for this commitment - * @param validatorSetLen the length of the validator set for this commitment - * @param numRequiredSignatures the number of signatures required - * @param bitfield a bitfield signalling which validators they claim have signed */ struct Ticket { + // The block number this ticket was issued uint64 blockNumber; + // Length of the validator set that signed the commitment uint32 validatorSetLen; + // The number of signatures required uint32 numRequiredSignatures; + // The PREVRANDAO seed selected for this ticket session uint256 prevRandao; + // Hash of a bitfield claiming which validators have signed bytes32 bitfieldHash; } - /** - * @dev The MMRLeaf is the structure of each leaf in each MMR that each commitment's payload commits to. - * @param version version of the leaf type - * @param parentNumber parent number of the block this leaf describes - * @param parentHash parent hash of the block this leaf describes - * @param parachainHeadsRoot merkle root of all parachain headers in this block - * @param nextAuthoritySetID validator set id that will be part of consensus for the next block - * @param nextAuthoritySetLen length of that validator set - * @param nextAuthoritySetRoot merkle root of all public keys in that validator set - */ + /// @dev The MMRLeaf describes the leaf structure of the MMR struct MMRLeaf { + // Version of the leaf type uint8 version; + // Parent number of the block this leaf describes uint32 parentNumber; + // Parent hash of the block this leaf describes bytes32 parentHash; + // Validator set id that will be part of consensus for the next block uint64 nextAuthoritySetID; + // Length of that validator set uint32 nextAuthoritySetLen; + // Merkle root of all public keys in that validator set bytes32 nextAuthoritySetRoot; + // Merkle root of all parachain headers in this block bytes32 parachainHeadsRoot; } /** * @dev The ValidatorSet describes a BEEFY validator set - * @param id identifier for the set - * @param length number of validators in the set - * @param root Merkle root of BEEFY validator addresses */ struct ValidatorSet { + // Identifier for the set uint128 id; + // Number of validators in the set uint128 length; + // Merkle root of BEEFY validator addresses bytes32 root; } + /** + * @dev The ValidatorSetState describes a BEEFY validator set along with signature usage counters + */ + struct ValidatorSetState { + // Identifier for the set + uint128 id; + // Number of validators in the set + uint128 length; + // Merkle root of BEEFY validator addresses + bytes32 root; + // Number of times a validator signature has been used + Uint16Array.Array usageCounters; + } + /* State */ - // The latest verified MMRRoot and corresponding BlockNumber from the Polkadot relay chain + /// @dev The latest verified MMR root bytes32 public latestMMRRoot; + + /// @dev The block number in the relay chain in which the latest MMR root was emitted uint64 public latestBeefyBlock; - ValidatorSet public currentValidatorSet; - ValidatorSet public nextValidatorSet; + /// @dev State of the current validator set + ValidatorSetState public currentValidatorSet; - // The validator signature counters for the current and next validator sets. - uint256[] currentValidatorSetCounters; - uint256[] nextValidatorSetCounters; + /// @dev State of the next validator set + ValidatorSetState public nextValidatorSet; - // Currently pending tickets for commitment submission - mapping(bytes32 => Ticket) public tickets; + /// @dev Pending tickets for commitment submission + mapping(bytes32 ticketID => Ticket) public tickets; /* Constants */ @@ -196,7 +199,7 @@ contract BeefyClient { error InvalidSignature(); error InvalidTicket(); error InvalidValidatorProof(); - error NoMMRRootInCommitment(); + error CommitmentNotRelevant(); error NotEnoughClaims(); error PrevRandaoAlreadyCaptured(); error PrevRandaoNotCaptured(); @@ -216,10 +219,14 @@ contract BeefyClient { randaoCommitExpiration = _randaoCommitExpiration; minNumRequiredSignatures = _minNumRequiredSignatures; latestBeefyBlock = _initialBeefyBlock; - currentValidatorSet = _initialValidatorSet; - currentValidatorSetCounters = Counter.createCounter(currentValidatorSet.length); - nextValidatorSet = _nextValidatorSet; - nextValidatorSetCounters = Counter.createCounter(nextValidatorSet.length); + currentValidatorSet.id = _initialValidatorSet.id; + currentValidatorSet.length = _initialValidatorSet.length; + currentValidatorSet.root = _initialValidatorSet.root; + currentValidatorSet.usageCounters = Uint16Array.create(currentValidatorSet.length); + nextValidatorSet.id = _nextValidatorSet.id; + nextValidatorSet.length = _nextValidatorSet.length; + nextValidatorSet.root = _nextValidatorSet.root; + nextValidatorSet.usageCounters = Uint16Array.create(nextValidatorSet.length); } /* External Functions */ @@ -233,15 +240,15 @@ contract BeefyClient { function submitInitial(Commitment calldata commitment, uint256[] calldata bitfield, ValidatorProof calldata proof) external { - ValidatorSet memory vset; + ValidatorSetState storage vset; uint16 signatureUsageCount; if (commitment.validatorSetID == currentValidatorSet.id) { - signatureUsageCount = currentValidatorSetCounters.get(proof.index); - currentValidatorSetCounters.set(proof.index, signatureUsageCount.saturatingAdd(1)); + signatureUsageCount = currentValidatorSet.usageCounters.get(proof.index); + currentValidatorSet.usageCounters.set(proof.index, signatureUsageCount.saturatingAdd(1)); vset = currentValidatorSet; } else if (commitment.validatorSetID == nextValidatorSet.id) { - signatureUsageCount = nextValidatorSetCounters.get(proof.index); - nextValidatorSetCounters.set(proof.index, signatureUsageCount.saturatingAdd(1)); + signatureUsageCount = nextValidatorSet.usageCounters.get(proof.index); + nextValidatorSet.usageCounters.set(proof.index, signatureUsageCount.saturatingAdd(1)); vset = nextValidatorSet; } else { revert InvalidCommitment(); @@ -325,10 +332,12 @@ contract BeefyClient { bytes32[] calldata leafProof, uint256 leafProofOrder ) external { - (bytes32 commitmentHash, bytes32 ticketID) = validate(commitment, bitfield); + bytes32 commitmentHash = keccak256(encodeCommitment(commitment)); + bytes32 ticketID = createTicketID(msg.sender, commitmentHash); + validateTicket(ticketID, commitment, bitfield); bool is_next_session = false; - ValidatorSet memory vset; + ValidatorSetState storage vset; if (commitment.validatorSetID == nextValidatorSet.id) { is_next_session = true; vset = nextValidatorSet; @@ -340,7 +349,7 @@ contract BeefyClient { verifyCommitment(commitmentHash, ticketID, bitfield, vset, proofs); - bytes32 newMMRRoot = getFirstMMRRoot(commitment); + bytes32 newMMRRoot = ensureProvidesMMRRoot(commitment); if (is_next_session) { if (leaf.nextAuthoritySetID != nextValidatorSet.id + 1) { @@ -355,15 +364,14 @@ contract BeefyClient { nextValidatorSet.id = leaf.nextAuthoritySetID; nextValidatorSet.length = leaf.nextAuthoritySetLen; nextValidatorSet.root = leaf.nextAuthoritySetRoot; - currentValidatorSetCounters = nextValidatorSetCounters; - nextValidatorSetCounters = Counter.createCounter(leaf.nextAuthoritySetLen); + nextValidatorSet.usageCounters = Uint16Array.create(leaf.nextAuthoritySetLen); } - uint64 newBeefyBlock = commitment.blockNumber; latestMMRRoot = newMMRRoot; - latestBeefyBlock = newBeefyBlock; + latestBeefyBlock = commitment.blockNumber; delete tickets[ticketID]; - emit NewMMRRoot(newMMRRoot, newBeefyBlock); + + emit NewMMRRoot(newMMRRoot, commitment.blockNumber); } /** @@ -425,15 +433,14 @@ contract BeefyClient { } /** - * @dev Calculates the number of signature samples required for `submitFinal`. - * @param validatorSetLen The validator set length. - * @param signatureUsageCount The count of how many times a validators signature was previously used in a call to `submitInitial`. - * @param minRequiredSignatures The minimum amount of signatures to verify. - * - * ceil(log2(validatorSetLen)) + 1 * 2 ceil(log2(signatureUsageCount)) - * - * See https://hackmd.io/9OedC7icR5m-in_moUZ_WQ for full analysis. + * @dev Calculates the number of required signatures for `submitFinal`. + * @param validatorSetLen The length of the validator set + * @param signatureUsageCount A counter of the number of times the validator signature was previously used in a call to `submitInitial` within the session. + * @param minRequiredSignatures The minimum amount of signatures to verify */ + // For more details on the calculation, read the following: + // 1. https://docs.snowbridge.network/architecture/verification/polkadot#signature-sampling + // 2. https://hackmd.io/9OedC7icR5m-in_moUZ_WQ function computeNumRequiredSignatures( uint256 validatorSetLen, uint256 signatureUsageCount, @@ -442,7 +449,7 @@ contract BeefyClient { // Start with the minimum number of signatures. uint256 numRequiredSignatures = minRequiredSignatures; - // We must substrate minimumSignatures from the number of validators or we might end up + // We must substract minimumSignatures from the number of validators or we might end up // requiring more signatures than there are validators. uint256 extraValidatorsLen = validatorSetLen.saturatingSub(minRequiredSignatures); if (extraValidatorsLen > 1) { @@ -453,24 +460,18 @@ contract BeefyClient { numRequiredSignatures += 1; } - // To address the concurrency issue specified in the link below: - // https://hackmd.io/wsVcL0tZQA-Ks3b5KJilFQ?view#Solution-2-Signature-Checks-dynamically-depend-on-the-No-of-initial-Claims-per-session - // It must be harder for a malicious relayer to spam submitInitial to bias the RANDAO. - // If we detect that a signature is used many times (spam), we increase the number of signature samples required on submitFinal. if (signatureUsageCount > 0) { - // Based on formula provided here: https://hackmd.io/9OedC7icR5m-in_moUZ_WQ numRequiredSignatures += 1 + 2 * Math.log2(signatureUsageCount, Math.Rounding.Ceil); } - // Never require more signatures than 2/3 majority. - uint256 validatorSetQuorum = computeQuorum(validatorSetLen); - return Math.min(numRequiredSignatures, validatorSetQuorum); + // Never require more signatures than a 2/3 majority + return Math.min(numRequiredSignatures, computeQuorum(validatorSetLen)); } + /** * @dev Calculates 2/3 majority required for quorum for a given number of validators. * @param numValidators The number of validators in the validator set. */ - function computeQuorum(uint256 numValidators) internal pure returns (uint256) { return numValidators - (numValidators - 1) / 3; } @@ -482,7 +483,7 @@ contract BeefyClient { bytes32 commitmentHash, bytes32 ticketID, uint256[] calldata bitfield, - ValidatorSet memory vset, + ValidatorSetState storage vset, ValidatorProof[] calldata proofs ) internal view { Ticket storage ticket = tickets[ticketID]; @@ -496,7 +497,7 @@ contract BeefyClient { uint256[] memory finalbitfield = Bitfield.subsample(ticket.prevRandao, bitfield, numRequiredSignatures, vset.length); - for (uint256 i = 0; i < proofs.length;) { + for (uint256 i = 0; i < proofs.length; i++) { ValidatorProof calldata proof = proofs[i]; // Check that validator is in bitfield @@ -516,14 +517,11 @@ contract BeefyClient { // Ensure no validator can appear more than once in bitfield Bitfield.unset(finalbitfield, proof.index); - - unchecked { - i++; - } } } - function getFirstMMRRoot(Commitment calldata commitment) internal pure returns (bytes32) { + // Ensure that the commitment provides a new MMR root + function ensureProvidesMMRRoot(Commitment calldata commitment) internal pure returns (bytes32) { for (uint256 i = 0; i < commitment.payload.length; i++) { if (commitment.payload[i].payloadID == MMR_ROOT_ID) { if (commitment.payload[i].data.length != 32) { @@ -533,8 +531,7 @@ contract BeefyClient { } } } - - revert NoMMRRootInCommitment(); + revert CommitmentNotRelevant(); } function encodeCommitment(Commitment calldata commitment) internal pure returns (bytes memory) { @@ -579,41 +576,43 @@ contract BeefyClient { * @param proof Merkle proof required for validation of the address * @return true if the validator is in the set */ - function isValidatorInSet(ValidatorSet memory vset, address account, uint256 index, bytes32[] calldata proof) + function isValidatorInSet(ValidatorSetState storage vset, address account, uint256 index, bytes32[] calldata proof) internal - pure + view returns (bool) { bytes32 hashedLeaf = keccak256(abi.encodePacked(account)); return SubstrateMerkleProof.verify(vset.root, hashedLeaf, index, vset.length, proof); } - // Basic checks for commitment - function validate(Commitment calldata commitment, uint256[] calldata bitfield) + /** + * @dev Basic validation of a ticket for submitFinal + */ + function validateTicket(bytes32 ticketID, Commitment calldata commitment, uint256[] calldata bitfield) internal view - returns (bytes32, bytes32) { - bytes32 commitmentHash = keccak256(encodeCommitment(commitment)); - bytes32 ticketID = createTicketID(msg.sender, commitmentHash); Ticket storage ticket = tickets[ticketID]; if (ticket.blockNumber == 0) { - // Zero value ticket: submitInitial hasn't run for this commitment + // submitInitial hasn't been called yet revert InvalidTicket(); } if (ticket.prevRandao == 0) { + // commitPrevRandao hasn't been called yet revert PrevRandaoNotCaptured(); } if (commitment.blockNumber <= latestBeefyBlock) { + // ticket is obsolete revert StaleCommitment(); } if (ticket.bitfieldHash != keccak256(abi.encodePacked(bitfield))) { + // The provided claims bitfield isn't the same one that was + // passed to submitInitial revert InvalidBitfield(); } - return (commitmentHash, ticketID); } } diff --git a/contracts/src/Verification.sol b/contracts/src/Verification.sol index e9e71829f2..5c99fce8f3 100644 --- a/contracts/src/Verification.sol +++ b/contracts/src/Verification.sol @@ -11,11 +11,11 @@ library Verification { /// @dev Merkle proof for parachain header finalized by BEEFY /// Reference: https://github.com/paritytech/polkadot/blob/09b61286da11921a3dda0a8e4015ceb9ef9cffca/runtime/rococo/src/lib.rs#L1312 struct HeadProof { - /// @dev The leaf index of the parachain being proven + // The leaf index of the parachain being proven uint256 pos; - /// @dev The number of leaves in the merkle tree + // The number of leaves in the merkle tree uint256 width; - /// @dev The proof items + // The proof items bytes32[] proof; } @@ -54,15 +54,15 @@ library Verification { /// @dev A chain of proofs struct Proof { - /// @dev The parachain header containing the message commitment as a digest item + // The parachain header containing the message commitment as a digest item ParachainHeader header; - /// @dev The proof used to generate a merkle root of parachain heads + // The proof used to generate a merkle root of parachain heads HeadProof headProof; - /// @dev The MMR leaf to be proven + // The MMR leaf to be proven MMRLeafPartial leafPartial; - /// @dev The MMR leaf prove + // The MMR leaf prove bytes32[] leafProof; - /// @dev The order in which proof items should be combined + // The order in which proof items should be combined uint256 leafProofOrder; } diff --git a/contracts/src/utils/Bitfield.sol b/contracts/src/utils/Bitfield.sol index 2e60fd9d2c..f311ec006a 100644 --- a/contracts/src/utils/Bitfield.sol +++ b/contracts/src/utils/Bitfield.sol @@ -74,11 +74,8 @@ library Bitfield { bitfield = new uint256[](arrayLength); - for (uint256 i = 0; i < bitsToSet.length;) { + for (uint256 i = 0; i < bitsToSet.length; i++) { set(bitfield, bitsToSet[i]); - unchecked { - ++i; - } } return bitfield; diff --git a/contracts/src/utils/MMRProof.sol b/contracts/src/utils/MMRProof.sol index bdff0cab52..5c10ca9074 100644 --- a/contracts/src/utils/MMRProof.sol +++ b/contracts/src/utils/MMRProof.sol @@ -25,11 +25,8 @@ library MMRProof { } bytes32 acc = leafHash; - for (uint256 i = 0; i < proof.length;) { + for (uint256 i = 0; i < proof.length; i++) { acc = hashPairs(acc, proof[i], (proofOrder >> i) & 1); - unchecked { - i++; - } } return root == acc; } diff --git a/contracts/src/utils/Math.sol b/contracts/src/utils/Math.sol index ca6215e382..0a634a8e42 100644 --- a/contracts/src/utils/Math.sol +++ b/contracts/src/utils/Math.sol @@ -95,7 +95,9 @@ library Math { function saturatingAdd(uint16 a, uint16 b) internal pure returns (uint16) { unchecked { uint16 c = a + b; - if (c < a) return 0xFFFF; + if (c < a) { + return 0xFFFF; + } return c; } } @@ -105,7 +107,9 @@ library Math { */ function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { - if (b > a) return 0; + if (b > a) { + return 0; + } return a - b; } } diff --git a/contracts/src/utils/Counter.sol b/contracts/src/utils/Uint16Array.sol similarity index 82% rename from contracts/src/utils/Counter.sol rename to contracts/src/utils/Uint16Array.sol index 44fdfdbe63..fcffa22fbc 100644 --- a/contracts/src/utils/Counter.sol +++ b/contracts/src/utils/Uint16Array.sol @@ -29,40 +29,43 @@ pragma solidity 0.8.22; * above table counter XX is at logical index 22. It will convert to a physical index of 1 in the array and * then to bit-index 128 to 143 of uint256[1]. */ -library Counter { +library Uint16Array { + struct Array { + uint256[] data; + } /** * @dev Creates a new counter which can store at least `length` counters. * @param length The amount of counters. */ - function createCounter(uint256 length) internal pure returns (uint256[] memory) { - // create space for length counters and round up if needed. - uint256 counterLength = length / 16 + (length % 16 == 0 ? 0 : 1); - // Counters are returned zeroed. - return new uint256[](counterLength); + + function create(uint256 length) internal pure returns (Array memory) { + // create space for `length` elements and round up if needed. + uint256 bufferLength = length / 16 + (length % 16 == 0 ? 0 : 1); + return Array({data: new uint256[](bufferLength)}); } /** * @dev Gets the counter at the logical index - * @param self The counter array. + * @param self The array. * @param index The logical index. */ - function get(uint256[] storage self, uint256 index) internal view returns (uint16) { + function get(Array storage self, uint256 index) internal view returns (uint16) { // Right-shift the index by 4. This truncates the first 4 bits (bit-index) leaving us with the index // into the array. uint256 element = index >> 4; // Mask out the first 4 bytes of the logical index to give us the bit-index. uint8 inside = uint8(index) & 0x0F; // find the element in the array, shift until its bit index and mask to only take the first 16 bits. - return uint16((self[element] >> (16 * inside)) & 0xFFFF); + return uint16((self.data[element] >> (16 * inside)) & 0xFFFF); } /** * @dev Sets the counter at the logical index. - * @param self The counter array. + * @param self The array. * @param index The logical index of the counter in the array. * @param value The value to set the counter to. */ - function set(uint256[] storage self, uint256 index, uint16 value) internal { + function set(Array storage self, uint256 index, uint16 value) internal { // Right-shift the index by 4. This truncates the first 4 bits (bit-index) leaving us with the index // into the array. uint256 element = index >> 4; @@ -73,6 +76,6 @@ library Counter { // Shift the value to the bit index. uint256 shiftedValue = uint256(value) << (16 * inside); // Take the element, apply the zero mask to clear the existing value, and then apply the shifted value with bitwise or. - self[element] = self[element] & zero | shiftedValue; + self.data[element] = self.data[element] & zero | shiftedValue; } } diff --git a/contracts/test/BeefyClient.t.sol b/contracts/test/BeefyClient.t.sol index 2c68d02681..2a8fe719f0 100644 --- a/contracts/test/BeefyClient.t.sol +++ b/contracts/test/BeefyClient.t.sol @@ -704,4 +704,8 @@ contract BeefyClientTest is Test { result = beefyClient.computeNumRequiredSignatures_public(1, 0, 0); assertEq(1, result, "C"); } + + function testStorageToStorageCopies() public { + beefyClient.copyCounters(); + } } diff --git a/contracts/test/Counter.t.sol b/contracts/test/Uint16Array.t.sol similarity index 78% rename from contracts/test/Counter.t.sol rename to contracts/test/Uint16Array.t.sol index 87518caa3f..b28009f2f8 100644 --- a/contracts/test/Counter.t.sol +++ b/contracts/test/Uint16Array.t.sol @@ -4,12 +4,12 @@ pragma solidity 0.8.22; import "forge-std/Test.sol"; import "forge-std/console.sol"; -import {Counter} from "../src/utils/Counter.sol"; +import {Uint16Array} from "../src/utils/Uint16Array.sol"; -contract CounterTest is Test { - using Counter for uint256[]; +contract Uint16ArrayTest is Test { + using Uint16Array for Uint16Array.Array; - uint256[] counters; + Uint16Array.Array counters; function setUp() public { delete counters; @@ -18,8 +18,8 @@ contract CounterTest is Test { function testCounterCreatedInitializationRoundsUp() public { // 33 uint16s will require 3 uint256s uint256[] memory expected = new uint256[](3); - counters = Counter.createCounter(33); - assertEq(counters, expected); + counters = Uint16Array.create(33); + assertEq(counters.data, expected); } function testCounterWithLengthNotMultipleOf16() public { @@ -27,17 +27,17 @@ contract CounterTest is Test { uint256[] memory expected = new uint256[](3); expected[2] = 1; - counters = Counter.createCounter(33); + counters = Uint16Array.create(33); counters.set(32, counters.get(32) + 1); - assertEq(counters, expected); + assertEq(counters.data, expected); } function testCounterCreatedAsZeroed() public { uint256[] memory expected = new uint256[](2); - counters = Counter.createCounter(16); - counters[0] = 0xABABABAB; - counters = Counter.createCounter(32); - assertEq(counters, expected); + counters = Uint16Array.create(16); + counters.data[0] = 0xABABABAB; + counters = Uint16Array.create(32); + assertEq(counters.data, expected); } function testCounterSet() public { @@ -46,23 +46,23 @@ contract CounterTest is Test { // Manually set the 16th index to 2. expected[1] = 2; - counters = Counter.createCounter(32); + counters = Uint16Array.create(32); counters.set(16, 2); - assertEq(counters, expected); + assertEq(counters.data, expected); } function testCounterGet() public { - counters = Counter.createCounter(32); + counters = Uint16Array.create(32); // Manually set the 16th index to 2. - counters[1] = 2; + counters.data[1] = 2; assertEq(counters.get(16), 2); } function testCounterGetAndSetAlongEntireRange() public { - counters = Counter.createCounter(32); + counters = Uint16Array.create(32); for (uint16 index = 0; index < 32; index++) { // Should be zero as the initial value. uint16 value = counters.get(index); @@ -93,7 +93,7 @@ contract CounterTest is Test { } function testCounterGetAndSetWithTwoIterations() public { - counters = Counter.createCounter(300); + counters = Uint16Array.create(300); uint256 index = 0; uint16 value = 11; counters.set(index, value); diff --git a/contracts/test/mocks/BeefyClientMock.sol b/contracts/test/mocks/BeefyClientMock.sol index 8d35a2a2e0..7a70b65730 100644 --- a/contracts/test/mocks/BeefyClientMock.sol +++ b/contracts/test/mocks/BeefyClientMock.sol @@ -2,10 +2,11 @@ pragma solidity 0.8.22; import {BeefyClient} from "../../src/BeefyClient.sol"; -import {Counter} from "../../src/utils/Counter.sol"; +import {Uint16Array} from "../../src/utils/Uint16Array.sol"; +import "forge-std/console.sol"; contract BeefyClientMock is BeefyClient { - using Counter for uint256[]; + using Uint16Array for Uint16Array.Array; constructor(uint256 randaoCommitDelay, uint256 randaoCommitExpiration, uint256 minNumRequiredSignatures) BeefyClient( @@ -36,17 +37,40 @@ contract BeefyClientMock is BeefyClient { ValidatorSet calldata _nextValidatorSet ) external { latestBeefyBlock = _initialBeefyBlock; - currentValidatorSet = _initialValidatorSet; - currentValidatorSetCounters = Counter.createCounter(currentValidatorSet.length); - nextValidatorSet = _nextValidatorSet; - nextValidatorSetCounters = Counter.createCounter(nextValidatorSet.length); + currentValidatorSet.id = _initialValidatorSet.id; + currentValidatorSet.length = _initialValidatorSet.length; + currentValidatorSet.root = _initialValidatorSet.root; + currentValidatorSet.usageCounters = Uint16Array.create(currentValidatorSet.length); + nextValidatorSet.id = _nextValidatorSet.id; + nextValidatorSet.length = _nextValidatorSet.length; + nextValidatorSet.root = _nextValidatorSet.root; + nextValidatorSet.usageCounters = Uint16Array.create(nextValidatorSet.length); + console.log(currentValidatorSet.usageCounters.data.length); + } + + // Used to verify integrity of storage to storage copies + function copyCounters() external { + currentValidatorSet.usageCounters = Uint16Array.create(1000); + for (uint256 i = 0; i < 1000; i++) { + currentValidatorSet.usageCounters.set(i, 5); + } + nextValidatorSet.usageCounters = Uint16Array.create(800); + for (uint256 i = 0; i < 800; i++) { + nextValidatorSet.usageCounters.set(i, 7); + } + + // Perform the copy + currentValidatorSet = nextValidatorSet; + + assert(currentValidatorSet.usageCounters.data.length == nextValidatorSet.usageCounters.data.length); + assert(currentValidatorSet.usageCounters.get(799) == 7); } function getValidatorCounter(bool next, uint256 index) public view returns (uint16) { if (next) { - return nextValidatorSetCounters.get(index); + return nextValidatorSet.usageCounters.get(index); } - return currentValidatorSetCounters.get(index); + return currentValidatorSet.usageCounters.get(index); } function computeNumRequiredSignatures_public( diff --git a/docs/architecture/verification/polkadot/README.md b/docs/architecture/verification/polkadot/README.md index 70232937a7..48e618981d 100644 --- a/docs/architecture/verification/polkadot/README.md +++ b/docs/architecture/verification/polkadot/README.md @@ -18,11 +18,11 @@ In collaboration with W3F, we have designed a protocol where the light client ne In the EVM there is no cryptographically secure source of randomness. Instead, we make our update protocol crypto-economically secure through an interactive update protocol. In this protocol, a candidate commitment is verified over 3 transactions. At a high level it works like this: -1. `submitInitial` - In the [first transaction](../../../../contracts/src/BeefyClient.sol#L233), the relayer submits the commitment, a randomly selected validator signature, and an initial bitfield claiming which validators have signed the commitment. +1. `submitInitial` - In the first transaction, the relayer submits the commitment, a randomly selected validator signature, and an initial bitfield claiming which validators have signed the commitment. 2. The relayer must then wait [MAX\_SEED\_LOOKAHEAD](https://eth2book.info/bellatrix/part3/config/preset/#max\_seed\_lookahead) blocks. -3. `commitPrevRandao` - The relayer submits a [second transaction](../../../../contracts/src/BeefyClient.sol#L288) to reveal and commit to a random seed, derived from Ethereum's [RANDAO](https://eips.ethereum.org/EIPS/eip-4399). -4. The relayer [requests](../../../../contracts/src/BeefyClient.sol#L404) from the light client a bitfield with $$N$$randomly chosen validators sampled from the initial bitfield.​ -5. `submitFinal` - The relayer sends a [third transaction](../../../../contracts/src/BeefyClient.sol#L320) with signatures for all the validators specified in the final bitfield +3. `commitPrevRandao` - The relayer submits a second transaction to reveal and commit to a random seed, derived from Ethereum's [RANDAO](https://eips.ethereum.org/EIPS/eip-4399). +4. The relayer requests from the light client a bitfield with $$N$$randomly chosen validators sampled from the initial bitfield.​ +5. `submitFinal` - The relayer sends a third and final transaction with signatures for all the validators specified in the final bitfield 6. The light client verifies all validator signatures in the third transaction to ensure: 1. The provided validators are in the current validator set 2. The provided validators are in the final bitfield @@ -48,9 +48,9 @@ $$ From the list above 1 and 2 are known in the light client and can be calculated on-chain. Variables 3, 4.1, and 4.2 are not known by the light client and are instead calculated off-chain and set as a minimum number of required signatures during the initialization of the light client. This minimum is immutable for the life time of the light client. -* [Minimum required signatures.](../../../../contracts/src/BeefyClient.sol#L182-L187) -* [Dynamic signature calculation.](../../../../contracts/src/BeefyClient.sol#L437) -* [Python implementation of required signatures.](../../../../scripts/beefy\_signature\_sampling.py#L9) +* [Minimum required signatures](../../../../contracts/src/BeefyClient.sol#L185-L190) +* [Dynamic signature calculation](../../../../contracts/src/BeefyClient.sol#L444) +* [Python implementation of required signatures](../../../../scripts/beefy\_signature\_sampling.py#L9) ## Message Verification diff --git a/relayer/contracts/beefy_client.go b/relayer/contracts/beefy_client.go index 1005c5f886..698f751b78 100644 --- a/relayer/contracts/beefy_client.go +++ b/relayer/contracts/beefy_client.go @@ -70,9 +70,14 @@ type BeefyClientValidatorSet struct { Root [32]byte } +// Uint16ArrayArray is an auto generated low-level Go binding around an user-defined struct. +type Uint16ArrayArray struct { + Data []*big.Int +} + // BeefyClientMetaData contains all meta data concerning the BeefyClient contract. var BeefyClientMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_randaoCommitDelay\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_randaoCommitExpiration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_minNumRequiredSignatures\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"_initialBeefyBlock\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.ValidatorSet\",\"name\":\"_initialValidatorSet\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.ValidatorSet\",\"name\":\"_nextValidatorSet\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidBitfield\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBitfieldLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRLeaf\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRLeafProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRRootLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTicket\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidValidatorProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoMMRRootInCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughClaims\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PrevRandaoAlreadyCaptured\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PrevRandaoNotCaptured\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ProofSizeExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TicketExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedCompactEncoding\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WaitPeriodNotOver\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"mmrRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"}],\"name\":\"NewMMRRoot\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MMR_ROOT_ID\",\"outputs\":[{\"internalType\":\"bytes2\",\"name\":\"\",\"type\":\"bytes2\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"commitmentHash\",\"type\":\"bytes32\"}],\"name\":\"commitPrevRandao\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"commitmentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"}],\"name\":\"createFinalBitfield\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"bitsToSet\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"createInitialBitfield\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentValidatorSet\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestBeefyBlock\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestMMRRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minNumRequiredSignatures\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextValidatorSet\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"randaoCommitDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"randaoCommitExpiration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"parentNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.MMRLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"leafProofOrder\",\"type\":\"uint256\"}],\"name\":\"submitFinal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof\",\"name\":\"proof\",\"type\":\"tuple\"}],\"name\":\"submitInitial\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"tickets\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"validatorSetLen\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numRequiredSignatures\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"prevRandao\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"bitfieldHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"leafHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofOrder\",\"type\":\"uint256\"}],\"name\":\"verifyMMRLeafProof\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_randaoCommitDelay\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_randaoCommitExpiration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_minNumRequiredSignatures\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"_initialBeefyBlock\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.ValidatorSet\",\"name\":\"_initialValidatorSet\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.ValidatorSet\",\"name\":\"_nextValidatorSet\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CommitmentNotRelevant\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBitfield\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBitfieldLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRLeaf\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRLeafProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMMRRootLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTicket\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidValidatorProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughClaims\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PrevRandaoAlreadyCaptured\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PrevRandaoNotCaptured\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ProofSizeExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TicketExpired\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedCompactEncoding\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WaitPeriodNotOver\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"mmrRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"}],\"name\":\"NewMMRRoot\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MMR_ROOT_ID\",\"outputs\":[{\"internalType\":\"bytes2\",\"name\":\"\",\"type\":\"bytes2\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"commitmentHash\",\"type\":\"bytes32\"}],\"name\":\"commitPrevRandao\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"commitmentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"}],\"name\":\"createFinalBitfield\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"bitsToSet\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"createInitialBitfield\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentValidatorSet\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256[]\",\"name\":\"data\",\"type\":\"uint256[]\"}],\"internalType\":\"structUint16Array.Array\",\"name\":\"usageCounters\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestBeefyBlock\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestMMRRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minNumRequiredSignatures\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextValidatorSet\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"id\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"length\",\"type\":\"uint128\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256[]\",\"name\":\"data\",\"type\":\"uint256[]\"}],\"internalType\":\"structUint16Array.Array\",\"name\":\"usageCounters\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"randaoCommitDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"randaoCommitExpiration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof[]\",\"name\":\"proofs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"parentNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"parachainHeadsRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structBeefyClient.MMRLeaf\",\"name\":\"leaf\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"leafProofOrder\",\"type\":\"uint256\"}],\"name\":\"submitFinal\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"validatorSetID\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"payloadID\",\"type\":\"bytes2\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structBeefyClient.PayloadItem[]\",\"name\":\"payload\",\"type\":\"tuple[]\"}],\"internalType\":\"structBeefyClient.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"},{\"internalType\":\"uint256[]\",\"name\":\"bitfield\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"internalType\":\"structBeefyClient.ValidatorProof\",\"name\":\"proof\",\"type\":\"tuple\"}],\"name\":\"submitInitial\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"ticketID\",\"type\":\"bytes32\"}],\"name\":\"tickets\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"validatorSetLen\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numRequiredSignatures\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"prevRandao\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"bitfieldHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"leafHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofOrder\",\"type\":\"uint256\"}],\"name\":\"verifyMMRLeafProof\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // BeefyClientABI is the input ABI used to generate the binding from. @@ -316,19 +321,21 @@ func (_BeefyClient *BeefyClientCallerSession) CreateInitialBitfield(bitsToSet [] // CurrentValidatorSet is a free data retrieval call binding the contract method 0x2cdea717. // -// Solidity: function currentValidatorSet() view returns(uint128 id, uint128 length, bytes32 root) +// Solidity: function currentValidatorSet() view returns(uint128 id, uint128 length, bytes32 root, (uint256[]) usageCounters) func (_BeefyClient *BeefyClientCaller) CurrentValidatorSet(opts *bind.CallOpts) (struct { - Id *big.Int - Length *big.Int - Root [32]byte + Id *big.Int + Length *big.Int + Root [32]byte + UsageCounters Uint16ArrayArray }, error) { var out []interface{} err := _BeefyClient.contract.Call(opts, &out, "currentValidatorSet") outstruct := new(struct { - Id *big.Int - Length *big.Int - Root [32]byte + Id *big.Int + Length *big.Int + Root [32]byte + UsageCounters Uint16ArrayArray }) if err != nil { return *outstruct, err @@ -337,6 +344,7 @@ func (_BeefyClient *BeefyClientCaller) CurrentValidatorSet(opts *bind.CallOpts) outstruct.Id = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) outstruct.Length = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) outstruct.Root = *abi.ConvertType(out[2], new([32]byte)).(*[32]byte) + outstruct.UsageCounters = *abi.ConvertType(out[3], new(Uint16ArrayArray)).(*Uint16ArrayArray) return *outstruct, err @@ -344,22 +352,24 @@ func (_BeefyClient *BeefyClientCaller) CurrentValidatorSet(opts *bind.CallOpts) // CurrentValidatorSet is a free data retrieval call binding the contract method 0x2cdea717. // -// Solidity: function currentValidatorSet() view returns(uint128 id, uint128 length, bytes32 root) +// Solidity: function currentValidatorSet() view returns(uint128 id, uint128 length, bytes32 root, (uint256[]) usageCounters) func (_BeefyClient *BeefyClientSession) CurrentValidatorSet() (struct { - Id *big.Int - Length *big.Int - Root [32]byte + Id *big.Int + Length *big.Int + Root [32]byte + UsageCounters Uint16ArrayArray }, error) { return _BeefyClient.Contract.CurrentValidatorSet(&_BeefyClient.CallOpts) } // CurrentValidatorSet is a free data retrieval call binding the contract method 0x2cdea717. // -// Solidity: function currentValidatorSet() view returns(uint128 id, uint128 length, bytes32 root) +// Solidity: function currentValidatorSet() view returns(uint128 id, uint128 length, bytes32 root, (uint256[]) usageCounters) func (_BeefyClient *BeefyClientCallerSession) CurrentValidatorSet() (struct { - Id *big.Int - Length *big.Int - Root [32]byte + Id *big.Int + Length *big.Int + Root [32]byte + UsageCounters Uint16ArrayArray }, error) { return _BeefyClient.Contract.CurrentValidatorSet(&_BeefyClient.CallOpts) } @@ -459,19 +469,21 @@ func (_BeefyClient *BeefyClientCallerSession) MinNumRequiredSignatures() (*big.I // NextValidatorSet is a free data retrieval call binding the contract method 0x36667513. // -// Solidity: function nextValidatorSet() view returns(uint128 id, uint128 length, bytes32 root) +// Solidity: function nextValidatorSet() view returns(uint128 id, uint128 length, bytes32 root, (uint256[]) usageCounters) func (_BeefyClient *BeefyClientCaller) NextValidatorSet(opts *bind.CallOpts) (struct { - Id *big.Int - Length *big.Int - Root [32]byte + Id *big.Int + Length *big.Int + Root [32]byte + UsageCounters Uint16ArrayArray }, error) { var out []interface{} err := _BeefyClient.contract.Call(opts, &out, "nextValidatorSet") outstruct := new(struct { - Id *big.Int - Length *big.Int - Root [32]byte + Id *big.Int + Length *big.Int + Root [32]byte + UsageCounters Uint16ArrayArray }) if err != nil { return *outstruct, err @@ -480,6 +492,7 @@ func (_BeefyClient *BeefyClientCaller) NextValidatorSet(opts *bind.CallOpts) (st outstruct.Id = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) outstruct.Length = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) outstruct.Root = *abi.ConvertType(out[2], new([32]byte)).(*[32]byte) + outstruct.UsageCounters = *abi.ConvertType(out[3], new(Uint16ArrayArray)).(*Uint16ArrayArray) return *outstruct, err @@ -487,22 +500,24 @@ func (_BeefyClient *BeefyClientCaller) NextValidatorSet(opts *bind.CallOpts) (st // NextValidatorSet is a free data retrieval call binding the contract method 0x36667513. // -// Solidity: function nextValidatorSet() view returns(uint128 id, uint128 length, bytes32 root) +// Solidity: function nextValidatorSet() view returns(uint128 id, uint128 length, bytes32 root, (uint256[]) usageCounters) func (_BeefyClient *BeefyClientSession) NextValidatorSet() (struct { - Id *big.Int - Length *big.Int - Root [32]byte + Id *big.Int + Length *big.Int + Root [32]byte + UsageCounters Uint16ArrayArray }, error) { return _BeefyClient.Contract.NextValidatorSet(&_BeefyClient.CallOpts) } // NextValidatorSet is a free data retrieval call binding the contract method 0x36667513. // -// Solidity: function nextValidatorSet() view returns(uint128 id, uint128 length, bytes32 root) +// Solidity: function nextValidatorSet() view returns(uint128 id, uint128 length, bytes32 root, (uint256[]) usageCounters) func (_BeefyClient *BeefyClientCallerSession) NextValidatorSet() (struct { - Id *big.Int - Length *big.Int - Root [32]byte + Id *big.Int + Length *big.Int + Root [32]byte + UsageCounters Uint16ArrayArray }, error) { return _BeefyClient.Contract.NextValidatorSet(&_BeefyClient.CallOpts) } @@ -571,8 +586,8 @@ func (_BeefyClient *BeefyClientCallerSession) RandaoCommitExpiration() (*big.Int // Tickets is a free data retrieval call binding the contract method 0xdf0dd0d5. // -// Solidity: function tickets(bytes32 ) view returns(uint64 blockNumber, uint32 validatorSetLen, uint32 numRequiredSignatures, uint256 prevRandao, bytes32 bitfieldHash) -func (_BeefyClient *BeefyClientCaller) Tickets(opts *bind.CallOpts, arg0 [32]byte) (struct { +// Solidity: function tickets(bytes32 ticketID) view returns(uint64 blockNumber, uint32 validatorSetLen, uint32 numRequiredSignatures, uint256 prevRandao, bytes32 bitfieldHash) +func (_BeefyClient *BeefyClientCaller) Tickets(opts *bind.CallOpts, ticketID [32]byte) (struct { BlockNumber uint64 ValidatorSetLen uint32 NumRequiredSignatures uint32 @@ -580,7 +595,7 @@ func (_BeefyClient *BeefyClientCaller) Tickets(opts *bind.CallOpts, arg0 [32]byt BitfieldHash [32]byte }, error) { var out []interface{} - err := _BeefyClient.contract.Call(opts, &out, "tickets", arg0) + err := _BeefyClient.contract.Call(opts, &out, "tickets", ticketID) outstruct := new(struct { BlockNumber uint64 @@ -605,28 +620,28 @@ func (_BeefyClient *BeefyClientCaller) Tickets(opts *bind.CallOpts, arg0 [32]byt // Tickets is a free data retrieval call binding the contract method 0xdf0dd0d5. // -// Solidity: function tickets(bytes32 ) view returns(uint64 blockNumber, uint32 validatorSetLen, uint32 numRequiredSignatures, uint256 prevRandao, bytes32 bitfieldHash) -func (_BeefyClient *BeefyClientSession) Tickets(arg0 [32]byte) (struct { +// Solidity: function tickets(bytes32 ticketID) view returns(uint64 blockNumber, uint32 validatorSetLen, uint32 numRequiredSignatures, uint256 prevRandao, bytes32 bitfieldHash) +func (_BeefyClient *BeefyClientSession) Tickets(ticketID [32]byte) (struct { BlockNumber uint64 ValidatorSetLen uint32 NumRequiredSignatures uint32 PrevRandao *big.Int BitfieldHash [32]byte }, error) { - return _BeefyClient.Contract.Tickets(&_BeefyClient.CallOpts, arg0) + return _BeefyClient.Contract.Tickets(&_BeefyClient.CallOpts, ticketID) } // Tickets is a free data retrieval call binding the contract method 0xdf0dd0d5. // -// Solidity: function tickets(bytes32 ) view returns(uint64 blockNumber, uint32 validatorSetLen, uint32 numRequiredSignatures, uint256 prevRandao, bytes32 bitfieldHash) -func (_BeefyClient *BeefyClientCallerSession) Tickets(arg0 [32]byte) (struct { +// Solidity: function tickets(bytes32 ticketID) view returns(uint64 blockNumber, uint32 validatorSetLen, uint32 numRequiredSignatures, uint256 prevRandao, bytes32 bitfieldHash) +func (_BeefyClient *BeefyClientCallerSession) Tickets(ticketID [32]byte) (struct { BlockNumber uint64 ValidatorSetLen uint32 NumRequiredSignatures uint32 PrevRandao *big.Int BitfieldHash [32]byte }, error) { - return _BeefyClient.Contract.Tickets(&_BeefyClient.CallOpts, arg0) + return _BeefyClient.Contract.Tickets(&_BeefyClient.CallOpts, ticketID) } // VerifyMMRLeafProof is a free data retrieval call binding the contract method 0xa401662b.