编译器
0.8.25+commit.b61c2a91
文件 1 的 11:BeefyClient.sol
pragma solidity 0.8.25;
import {ECDSA} from "openzeppelin/utils/cryptography/ECDSA.sol";
import {SubstrateMerkleProof} from "./utils/SubstrateMerkleProof.sol";
import {Bitfield} from "./utils/Bitfield.sol";
import {Uint16Array, createUint16Array} from "./utils/Uint16Array.sol";
import {Math} from "./utils/Math.sol";
import {MMRProof} from "./utils/MMRProof.sol";
import {ScaleCodec} from "./utils/ScaleCodec.sol";
contract BeefyClient {
using Math for uint16;
using Math for uint256;
event NewMMRRoot(bytes32 mmrRoot, uint64 blockNumber);
event NewTicket(address relayer, uint64 blockNumber);
struct Commitment {
uint32 blockNumber;
uint64 validatorSetID;
PayloadItem[] payload;
}
struct PayloadItem {
bytes2 payloadID;
bytes data;
}
struct ValidatorProof {
uint8 v;
bytes32 r;
bytes32 s;
uint256 index;
address account;
bytes32[] proof;
}
struct Ticket {
uint64 blockNumber;
uint32 validatorSetLen;
uint32 numRequiredSignatures;
uint256 prevRandao;
bytes32 bitfieldHash;
}
struct MMRLeaf {
uint8 version;
uint32 parentNumber;
bytes32 parentHash;
uint64 nextAuthoritySetID;
uint32 nextAuthoritySetLen;
bytes32 nextAuthoritySetRoot;
bytes32 parachainHeadsRoot;
}
struct ValidatorSet {
uint128 id;
uint128 length;
bytes32 root;
}
struct ValidatorSetState {
uint128 id;
uint128 length;
bytes32 root;
Uint16Array usageCounters;
}
bytes32 public latestMMRRoot;
uint64 public latestBeefyBlock;
ValidatorSetState public currentValidatorSet;
ValidatorSetState public nextValidatorSet;
mapping(bytes32 ticketID => Ticket) public tickets;
bytes2 public constant MMR_ROOT_ID = bytes2("mh");
uint256 public immutable randaoCommitDelay;
uint256 public immutable randaoCommitExpiration;
uint256 public immutable minNumRequiredSignatures;
error InvalidBitfield();
error InvalidBitfieldLength();
error InvalidCommitment();
error InvalidMMRLeaf();
error InvalidMMRLeafProof();
error InvalidMMRRootLength();
error InvalidSignature();
error InvalidTicket();
error InvalidValidatorProof();
error InvalidValidatorProofLength();
error CommitmentNotRelevant();
error NotEnoughClaims();
error PrevRandaoAlreadyCaptured();
error PrevRandaoNotCaptured();
error StaleCommitment();
error TicketExpired();
error WaitPeriodNotOver();
constructor(
uint256 _randaoCommitDelay,
uint256 _randaoCommitExpiration,
uint256 _minNumRequiredSignatures,
uint64 _initialBeefyBlock,
ValidatorSet memory _initialValidatorSet,
ValidatorSet memory _nextValidatorSet
) {
if (_nextValidatorSet.id != _initialValidatorSet.id + 1) {
revert("invalid-constructor-params");
}
randaoCommitDelay = _randaoCommitDelay;
randaoCommitExpiration = _randaoCommitExpiration;
minNumRequiredSignatures = _minNumRequiredSignatures;
latestBeefyBlock = _initialBeefyBlock;
currentValidatorSet.id = _initialValidatorSet.id;
currentValidatorSet.length = _initialValidatorSet.length;
currentValidatorSet.root = _initialValidatorSet.root;
currentValidatorSet.usageCounters = createUint16Array(currentValidatorSet.length);
nextValidatorSet.id = _nextValidatorSet.id;
nextValidatorSet.length = _nextValidatorSet.length;
nextValidatorSet.root = _nextValidatorSet.root;
nextValidatorSet.usageCounters = createUint16Array(nextValidatorSet.length);
}
function submitInitial(Commitment calldata commitment, uint256[] calldata bitfield, ValidatorProof calldata proof)
external
{
if (commitment.blockNumber <= latestBeefyBlock) {
revert StaleCommitment();
}
ValidatorSetState storage vset;
uint16 signatureUsageCount;
if (commitment.validatorSetID == currentValidatorSet.id) {
signatureUsageCount = currentValidatorSet.usageCounters.get(proof.index);
currentValidatorSet.usageCounters.set(proof.index, signatureUsageCount.saturatingAdd(1));
vset = currentValidatorSet;
} else if (commitment.validatorSetID == nextValidatorSet.id) {
signatureUsageCount = nextValidatorSet.usageCounters.get(proof.index);
nextValidatorSet.usageCounters.set(proof.index, signatureUsageCount.saturatingAdd(1));
vset = nextValidatorSet;
} else {
revert InvalidCommitment();
}
if (!isValidatorInSet(vset, proof.account, proof.index, proof.proof) || !Bitfield.isSet(bitfield, proof.index))
{
revert InvalidValidatorProof();
}
bytes32 commitmentHash = keccak256(encodeCommitment(commitment));
if (ECDSA.recover(commitmentHash, proof.v, proof.r, proof.s) != proof.account) {
revert InvalidSignature();
}
if (Bitfield.countSetBits(bitfield) < computeQuorum(vset.length)) {
revert NotEnoughClaims();
}
tickets[createTicketID(msg.sender, commitmentHash)] = Ticket({
blockNumber: uint64(block.number),
validatorSetLen: uint32(vset.length),
numRequiredSignatures: uint32(
computeNumRequiredSignatures(vset.length, signatureUsageCount, minNumRequiredSignatures)
),
prevRandao: 0,
bitfieldHash: keccak256(abi.encodePacked(bitfield))
});
emit NewTicket(msg.sender, commitment.blockNumber);
}
function commitPrevRandao(bytes32 commitmentHash) external {
bytes32 ticketID = createTicketID(msg.sender, commitmentHash);
Ticket storage ticket = tickets[ticketID];
if (ticket.blockNumber == 0) {
revert InvalidTicket();
}
if (ticket.prevRandao != 0) {
revert PrevRandaoAlreadyCaptured();
}
if (block.number < ticket.blockNumber + randaoCommitDelay) {
revert WaitPeriodNotOver();
}
if (block.number > ticket.blockNumber + randaoCommitDelay + randaoCommitExpiration) {
delete tickets[ticketID];
revert TicketExpired();
}
ticket.prevRandao = block.prevrandao;
}
function submitFinal(
Commitment calldata commitment,
uint256[] calldata bitfield,
ValidatorProof[] calldata proofs,
MMRLeaf calldata leaf,
bytes32[] calldata leafProof,
uint256 leafProofOrder
) external {
bytes32 commitmentHash = keccak256(encodeCommitment(commitment));
bytes32 ticketID = createTicketID(msg.sender, commitmentHash);
validateTicket(ticketID, commitment, bitfield);
bool is_next_session = false;
ValidatorSetState storage vset;
if (commitment.validatorSetID == nextValidatorSet.id) {
is_next_session = true;
vset = nextValidatorSet;
} else if (commitment.validatorSetID == currentValidatorSet.id) {
vset = currentValidatorSet;
} else {
revert InvalidCommitment();
}
verifyCommitment(commitmentHash, ticketID, bitfield, vset, proofs);
bytes32 newMMRRoot = ensureProvidesMMRRoot(commitment);
if (is_next_session) {
if (leaf.nextAuthoritySetID != nextValidatorSet.id + 1) {
revert InvalidMMRLeaf();
}
bool leafIsValid =
MMRProof.verifyLeafProof(newMMRRoot, keccak256(encodeMMRLeaf(leaf)), leafProof, leafProofOrder);
if (!leafIsValid) {
revert InvalidMMRLeafProof();
}
currentValidatorSet = nextValidatorSet;
nextValidatorSet.id = leaf.nextAuthoritySetID;
nextValidatorSet.length = leaf.nextAuthoritySetLen;
nextValidatorSet.root = leaf.nextAuthoritySetRoot;
nextValidatorSet.usageCounters = createUint16Array(leaf.nextAuthoritySetLen);
}
latestMMRRoot = newMMRRoot;
latestBeefyBlock = commitment.blockNumber;
delete tickets[ticketID];
emit NewMMRRoot(newMMRRoot, commitment.blockNumber);
}
function verifyMMRLeafProof(bytes32 leafHash, bytes32[] calldata proof, uint256 proofOrder)
external
view
returns (bool)
{
return MMRProof.verifyLeafProof(latestMMRRoot, leafHash, proof, proofOrder);
}
function createInitialBitfield(uint256[] calldata bitsToSet, uint256 length)
external
pure
returns (uint256[] memory)
{
if (length < bitsToSet.length) {
revert InvalidBitfieldLength();
}
return Bitfield.createBitfield(bitsToSet, length);
}
function createFinalBitfield(bytes32 commitmentHash, uint256[] calldata bitfield)
external
view
returns (uint256[] memory)
{
Ticket storage ticket = tickets[createTicketID(msg.sender, commitmentHash)];
if (ticket.bitfieldHash != keccak256(abi.encodePacked(bitfield))) {
revert InvalidBitfield();
}
return Bitfield.subsample(ticket.prevRandao, bitfield, ticket.numRequiredSignatures, ticket.validatorSetLen);
}
function createTicketID(address account, bytes32 commitmentHash) internal pure returns (bytes32 value) {
assembly {
mstore(0x00, account)
mstore(0x20, commitmentHash)
value := keccak256(0x0, 0x40)
}
}
function computeNumRequiredSignatures(
uint256 validatorSetLen,
uint256 signatureUsageCount,
uint256 minRequiredSignatures
) internal pure returns (uint256) {
uint256 numRequiredSignatures = minRequiredSignatures;
numRequiredSignatures += Math.log2(validatorSetLen, Math.Rounding.Ceil);
numRequiredSignatures += 1 + (2 * Math.log2(signatureUsageCount, Math.Rounding.Ceil));
return Math.min(numRequiredSignatures, computeQuorum(validatorSetLen));
}
function computeQuorum(uint256 numValidators) internal pure returns (uint256) {
return numValidators - (numValidators - 1) / 3;
}
function verifyCommitment(
bytes32 commitmentHash,
bytes32 ticketID,
uint256[] calldata bitfield,
ValidatorSetState storage vset,
ValidatorProof[] calldata proofs
) internal view {
Ticket storage ticket = tickets[ticketID];
uint256 numRequiredSignatures = ticket.numRequiredSignatures;
if (proofs.length != numRequiredSignatures) {
revert InvalidValidatorProofLength();
}
uint256[] memory finalbitfield =
Bitfield.subsample(ticket.prevRandao, bitfield, numRequiredSignatures, vset.length);
for (uint256 i = 0; i < proofs.length; i++) {
ValidatorProof calldata proof = proofs[i];
if (!Bitfield.isSet(finalbitfield, proof.index)) {
revert InvalidValidatorProof();
}
if (!isValidatorInSet(vset, proof.account, proof.index, proof.proof)) {
revert InvalidValidatorProof();
}
if (ECDSA.recover(commitmentHash, proof.v, proof.r, proof.s) != proof.account) {
revert InvalidSignature();
}
Bitfield.unset(finalbitfield, proof.index);
}
}
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) {
revert InvalidMMRRootLength();
} else {
return bytes32(commitment.payload[i].data);
}
}
}
revert CommitmentNotRelevant();
}
function encodeCommitment(Commitment calldata commitment) internal pure returns (bytes memory) {
return bytes.concat(
encodeCommitmentPayload(commitment.payload),
ScaleCodec.encodeU32(commitment.blockNumber),
ScaleCodec.encodeU64(commitment.validatorSetID)
);
}
function encodeCommitmentPayload(PayloadItem[] calldata items) internal pure returns (bytes memory) {
bytes memory payload = ScaleCodec.checkedEncodeCompactU32(items.length);
for (uint256 i = 0; i < items.length; i++) {
payload = bytes.concat(
payload, items[i].payloadID, ScaleCodec.checkedEncodeCompactU32(items[i].data.length), items[i].data
);
}
return payload;
}
function encodeMMRLeaf(MMRLeaf calldata leaf) internal pure returns (bytes memory) {
return bytes.concat(
ScaleCodec.encodeU8(leaf.version),
ScaleCodec.encodeU32(leaf.parentNumber),
leaf.parentHash,
ScaleCodec.encodeU64(leaf.nextAuthoritySetID),
ScaleCodec.encodeU32(leaf.nextAuthoritySetLen),
leaf.nextAuthoritySetRoot,
leaf.parachainHeadsRoot
);
}
function isValidatorInSet(ValidatorSetState storage vset, address account, uint256 index, bytes32[] calldata proof)
internal
view
returns (bool)
{
bytes32 hashedLeaf = keccak256(abi.encodePacked(account));
return SubstrateMerkleProof.verify(vset.root, hashedLeaf, index, vset.length, proof);
}
function validateTicket(bytes32 ticketID, Commitment calldata commitment, uint256[] calldata bitfield)
internal
view
{
Ticket storage ticket = tickets[ticketID];
if (ticket.blockNumber == 0) {
revert InvalidTicket();
}
if (ticket.prevRandao == 0) {
revert PrevRandaoNotCaptured();
}
if (commitment.blockNumber <= latestBeefyBlock) {
revert StaleCommitment();
}
if (ticket.bitfieldHash != keccak256(abi.encodePacked(bitfield))) {
revert InvalidBitfield();
}
}
}
文件 2 的 11:Bitfield.sol
pragma solidity 0.8.25;
import {Bits} from "./Bits.sol";
library Bitfield {
using Bits for uint256;
uint256 internal constant M1 = 0x5555555555555555555555555555555555555555555555555555555555555555;
uint256 internal constant M2 = 0x3333333333333333333333333333333333333333333333333333333333333333;
uint256 internal constant M4 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 internal constant M8 = 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff;
uint256 internal constant M16 = 0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff;
uint256 internal constant M32 = 0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff;
uint256 internal constant M64 = 0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff;
uint256 internal constant M128 = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;
uint256 internal constant ONE = uint256(1);
function subsample(uint256 seed, uint256[] memory prior, uint256 n, uint256 length)
internal
pure
returns (uint256[] memory bitfield)
{
bitfield = new uint256[](prior.length);
uint256 found = 0;
for (uint256 i = 0; found < n;) {
uint256 index = makeIndex(seed, i, length);
if (!isSet(prior, index) || isSet(bitfield, index)) {
unchecked {
i++;
}
continue;
}
set(bitfield, index);
unchecked {
found++;
i++;
}
}
return bitfield;
}
function createBitfield(uint256[] calldata bitsToSet, uint256 length)
internal
pure
returns (uint256[] memory bitfield)
{
uint256 arrayLength = (length + 255) / 256;
bitfield = new uint256[](arrayLength);
for (uint256 i = 0; i < bitsToSet.length; i++) {
set(bitfield, bitsToSet[i]);
}
return bitfield;
}
function countSetBits(uint256[] memory self) internal pure returns (uint256) {
unchecked {
uint256 count = 0;
for (uint256 i = 0; i < self.length; i++) {
uint256 x = self[i];
x = (x & M1) + ((x >> 1) & M1);
x = (x & M2) + ((x >> 2) & M2);
x = (x & M4) + ((x >> 4) & M4);
x = (x & M8) + ((x >> 8) & M8);
x = (x & M16) + ((x >> 16) & M16);
x = (x & M32) + ((x >> 32) & M32);
x = (x & M64) + ((x >> 64) & M64);
x = (x & M128) + ((x >> 128) & M128);
count += x;
}
return count;
}
}
function isSet(uint256[] memory self, uint256 index) internal pure returns (bool) {
uint256 element = index >> 8;
return self[element].bit(uint8(index)) == 1;
}
function set(uint256[] memory self, uint256 index) internal pure {
uint256 element = index >> 8;
self[element] = self[element].setBit(uint8(index));
}
function unset(uint256[] memory self, uint256 index) internal pure {
uint256 element = index >> 8;
self[element] = self[element].clearBit(uint8(index));
}
function makeIndex(uint256 seed, uint256 iteration, uint256 length) internal pure returns (uint256 index) {
assembly {
mstore(0x00, seed)
mstore(0x20, iteration)
index := mod(keccak256(0x00, 0x40), length)
}
}
}
文件 3 的 11:Bits.sol
pragma solidity 0.8.25;
library Bits {
uint256 internal constant ONE = uint256(1);
uint256 internal constant ONES = type(uint256).max;
function setBit(uint256 self, uint8 index) internal pure returns (uint256) {
return self | (ONE << index);
}
function clearBit(uint256 self, uint8 index) internal pure returns (uint256) {
return self & ~(ONE << index);
}
function toggleBit(uint256 self, uint8 index) internal pure returns (uint256) {
return self ^ (ONE << index);
}
function bit(uint256 self, uint8 index) internal pure returns (uint8) {
return uint8((self >> index) & 1);
}
function bitSet(uint256 self, uint8 index) internal pure returns (bool) {
return (self >> index) & 1 == 1;
}
function bitEqual(uint256 self, uint256 other, uint8 index) internal pure returns (bool) {
return ((self ^ other) >> index) & 1 == 0;
}
function bitNot(uint256 self, uint8 index) internal pure returns (uint8) {
return uint8(1 - ((self >> index) & 1));
}
function bitAnd(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) {
return uint8(((self & other) >> index) & 1);
}
function bitOr(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) {
return uint8(((self | other) >> index) & 1);
}
function bitXor(uint256 self, uint256 other, uint8 index) internal pure returns (uint8) {
return uint8(((self ^ other) >> index) & 1);
}
function bits(uint256 self, uint8 startIndex, uint16 numBits) internal pure returns (uint256) {
require(0 < numBits && startIndex < 256 && startIndex + numBits <= 256, "out of bounds");
return (self >> startIndex) & (ONES >> (256 - numBits));
}
function highestBitSet(uint256 self) internal pure returns (uint8 highest) {
require(self != 0, "should not be zero");
uint256 val = self;
for (uint8 i = 128; i >= 1; i >>= 1) {
if (val & (((ONE << i) - 1) << i) != 0) {
highest += i;
val >>= i;
}
}
}
function lowestBitSet(uint256 self) internal pure returns (uint8 lowest) {
require(self != 0, "should not be zero");
uint256 val = self;
for (uint8 i = 128; i >= 1; i >>= 1) {
if (val & ((ONE << i) - 1) == 0) {
lowest += i;
val >>= i;
}
}
}
}
文件 4 的 11:ECDSA.sol
pragma solidity ^0.8.0;
import "../Strings.sol";
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return;
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}
文件 5 的 11:MMRProof.sol
pragma solidity 0.8.25;
library MMRProof {
error ProofSizeExceeded();
uint256 internal constant MAXIMUM_PROOF_SIZE = 256;
function verifyLeafProof(bytes32 root, bytes32 leafHash, bytes32[] calldata proof, uint256 proofOrder)
internal
pure
returns (bool)
{
if (proof.length > MAXIMUM_PROOF_SIZE) {
revert ProofSizeExceeded();
}
bytes32 acc = leafHash;
for (uint256 i = 0; i < proof.length; i++) {
acc = hashPairs(acc, proof[i], (proofOrder >> i) & 1);
}
return root == acc;
}
function hashPairs(bytes32 x, bytes32 y, uint256 order) internal pure returns (bytes32 value) {
assembly {
switch order
case 0 {
mstore(0x00, x)
mstore(0x20, y)
}
default {
mstore(0x00, y)
mstore(0x20, x)
}
value := keccak256(0x0, 0x40)
}
}
}
文件 6 的 11:Math.sol
pragma solidity 0.8.25;
library Math {
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
function saturatingAdd(uint16 a, uint16 b) internal pure returns (uint16) {
unchecked {
uint16 c = a + b;
if (c < a) {
return 0xFFFF;
}
return c;
}
}
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
if (b >= a) {
return 0;
}
return a - b;
}
}
}
文件 7 的 11:ScaleCodec.sol
pragma solidity 0.8.25;
library ScaleCodec {
error UnsupportedCompactEncoding();
uint256 internal constant MAX_COMPACT_ENCODABLE_UINT = 2 ** 30 - 1;
function reverse256(uint256 input) internal pure returns (uint256 v) {
v = input;
v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8)
| ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16)
| ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32)
| ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);
v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64)
| ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);
v = (v >> 128) | (v << 128);
}
function reverse128(uint128 input) internal pure returns (uint128 v) {
v = input;
v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000) >> 32) | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF) << 32);
v = (v >> 64) | (v << 64);
}
function reverse64(uint64 input) internal pure returns (uint64 v) {
v = input;
v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8);
v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16);
v = (v >> 32) | (v << 32);
}
function reverse32(uint32 input) internal pure returns (uint32 v) {
v = input;
v = ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8);
v = (v >> 16) | (v << 16);
}
function reverse16(uint16 input) internal pure returns (uint16 v) {
v = input;
v = (v >> 8) | (v << 8);
}
function encodeU256(uint256 input) internal pure returns (bytes32) {
return bytes32(reverse256(input));
}
function encodeU128(uint128 input) internal pure returns (bytes16) {
return bytes16(reverse128(input));
}
function encodeU64(uint64 input) internal pure returns (bytes8) {
return bytes8(reverse64(input));
}
function encodeU32(uint32 input) internal pure returns (bytes4) {
return bytes4(reverse32(input));
}
function encodeU16(uint16 input) internal pure returns (bytes2) {
return bytes2(reverse16(input));
}
function encodeU8(uint8 input) internal pure returns (bytes1) {
return bytes1(input);
}
function encodeCompactU32(uint32 value) internal pure returns (bytes memory) {
if (value <= 2 ** 6 - 1) {
return abi.encodePacked(uint8(value << 2));
} else if (value <= 2 ** 14 - 1) {
return abi.encodePacked(ScaleCodec.reverse16(uint16(((value << 2) + 1))));
} else if (value <= 2 ** 30 - 1) {
return abi.encodePacked(ScaleCodec.reverse32(uint32((value << 2)) + 2));
} else {
return abi.encodePacked(uint8(3), ScaleCodec.reverse32(value));
}
}
function checkedEncodeCompactU32(uint256 value) internal pure returns (bytes memory) {
if (value > type(uint32).max) {
revert UnsupportedCompactEncoding();
}
return encodeCompactU32(uint32(value));
}
}
文件 8 的 11:SignedMath.sol
pragma solidity ^0.8.0;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 9 的 11:Strings.sol
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
文件 10 的 11:SubstrateMerkleProof.sol
pragma solidity 0.8.25;
library SubstrateMerkleProof {
function verify(bytes32 root, bytes32 leaf, uint256 position, uint256 width, bytes32[] calldata proof)
internal
pure
returns (bool)
{
if (position >= width) {
return false;
}
return root == computeRoot(leaf, position, width, proof);
}
function computeRoot(bytes32 leaf, uint256 position, uint256 width, bytes32[] calldata proof)
internal
pure
returns (bytes32)
{
bytes32 node = leaf;
unchecked {
for (uint256 i = 0; i < proof.length; i++) {
if (position & 1 == 1 || position + 1 == width) {
node = efficientHash(proof[i], node);
} else {
node = efficientHash(node, proof[i]);
}
position = position >> 1;
width = ((width - 1) >> 1) + 1;
}
return node;
}
}
function efficientHash(bytes32 a, bytes32 b) internal pure returns (bytes32 value) {
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
文件 11 的 11:Uint16Array.sol
pragma solidity 0.8.25;
using {get, set} for Uint16Array global;
error IndexOutOfBounds();
struct Uint16Array {
uint256[] data;
uint256 length;
}
function createUint16Array(uint256 length) pure returns (Uint16Array memory) {
uint256 bufferLength = length / 16 + (length % 16 == 0 ? 0 : 1);
return Uint16Array({data: new uint256[](bufferLength), length: length});
}
function get(Uint16Array storage self, uint256 index) view returns (uint16) {
if (index >= self.length) {
revert IndexOutOfBounds();
}
uint256 element = index >> 4;
uint8 inside = uint8(index) & 0x0F;
return uint16((self.data[element] >> (16 * inside)) & 0xFFFF);
}
function set(Uint16Array storage self, uint256 index, uint16 value) {
if (index >= self.length) {
revert IndexOutOfBounds();
}
uint256 element = index >> 4;
uint8 inside = uint8(index) & 0x0F;
uint256 zero = ~(uint256(0xFFFF) << (16 * inside));
uint256 shiftedValue = uint256(value) << (16 * inside);
self.data[element] = self.data[element] & zero | shiftedValue;
}
{
"compilationTarget": {
"src/BeefyClient.sol": "BeefyClient"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 20000
},
"remappings": [
":@prb/test/=lib/prb-math/lib/prb-test/src/",
":canonical-weth/=lib/canonical-weth/contracts/",
":ds-test/=lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/",
":prb-math/=lib/prb-math/src/",
":prb-test/=lib/prb-math/lib/prb-test/src/",
":prb/math/=lib/prb-math/"
],
"viaIR": true
}
[{"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":"struct BeefyClient.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":"struct BeefyClient.ValidatorSet","name":"_nextValidatorSet","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CommitmentNotRelevant","type":"error"},{"inputs":[],"name":"IndexOutOfBounds","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":"InvalidValidatorProofLength","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"relayer","type":"address"},{"indexed":false,"internalType":"uint64","name":"blockNumber","type":"uint64"}],"name":"NewTicket","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":"uint256","name":"length","type":"uint256"}],"internalType":"struct Uint16Array","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":"uint256","name":"length","type":"uint256"}],"internalType":"struct Uint16Array","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":"struct BeefyClient.PayloadItem[]","name":"payload","type":"tuple[]"}],"internalType":"struct BeefyClient.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":"struct BeefyClient.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":"struct BeefyClient.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":"struct BeefyClient.PayloadItem[]","name":"payload","type":"tuple[]"}],"internalType":"struct BeefyClient.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":"struct BeefyClient.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"}]