// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; /** * Format of metadata: * [ 0: 32] Merkle root * [ 32: 64] Root index * [ 64: 96] Origin mailbox address * [ 96:1120] Merkle proof * [1120:1152] Threshold * [1152:????] Validator signatures, 65 bytes each, length == Threshold * [????:????] Addresses of the entire validator set, left padded to bytes32 */ library MultisigIsmMetadata { uint256 private constant MERKLE_ROOT_OFFSET = 0; uint256 private constant MERKLE_INDEX_OFFSET = 32; uint256 private constant ORIGIN_MAILBOX_OFFSET = 64; uint256 private constant MERKLE_PROOF_OFFSET = 96; uint256 private constant THRESHOLD_OFFSET = 1120; uint256 private constant SIGNATURES_OFFSET = 1152; uint256 private constant SIGNATURE_LENGTH = 65; /** * @notice Returns the merkle root of the signed checkpoint. * @param _metadata ABI encoded Multisig ISM metadata. * @return Merkle root of the signed checkpoint */ function root(bytes calldata _metadata) internal pure returns (bytes32) { return bytes32(_metadata[MERKLE_ROOT_OFFSET:MERKLE_INDEX_OFFSET]); } /** * @notice Returns the index of the signed checkpoint. * @param _metadata ABI encoded Multisig ISM metadata. * @return Index of the signed checkpoint */ function index(bytes calldata _metadata) internal pure returns (uint256) { return uint256( bytes32(_metadata[MERKLE_INDEX_OFFSET:ORIGIN_MAILBOX_OFFSET]) ); } /** * @notice Returns the origin mailbox of the signed checkpoint as bytes32. * @param _metadata ABI encoded Multisig ISM metadata. * @return Origin mailbox of the signed checkpoint as bytes32 */ function originMailbox(bytes calldata _metadata) internal pure returns (bytes32) { return bytes32(_metadata[ORIGIN_MAILBOX_OFFSET:MERKLE_PROOF_OFFSET]); } /** * @notice Returns the merkle proof branch of the message. * @dev This appears to be more gas efficient than returning a calldata * slice and using that. * @param _metadata ABI encoded Multisig ISM metadata. * @return Merkle proof branch of the message. */ function proof(bytes calldata _metadata) internal pure returns (bytes32[32] memory) { return abi.decode( _metadata[MERKLE_PROOF_OFFSET:THRESHOLD_OFFSET], (bytes32[32]) ); } /** * @notice Returns the number of required signatures. Verified against * the commitment stored in the module. * @param _metadata ABI encoded Multisig ISM metadata. * @return The number of required signatures. */ function threshold(bytes calldata _metadata) internal pure returns (uint256) { return uint256(bytes32(_metadata[THRESHOLD_OFFSET:SIGNATURES_OFFSET])); } /** * @notice Returns the validator ECDSA signature at `_index`. * @dev Assumes signatures are sorted by validator * @dev Assumes `_metadata` encodes `threshold` signatures. * @dev Assumes `_index` is less than `threshold` * @param _metadata ABI encoded Multisig ISM metadata. * @param _index The index of the signature to return. * @return The validator ECDSA signature at `_index`. */ function signatureAt(bytes calldata _metadata, uint256 _index) internal pure returns (bytes calldata) { uint256 _start = SIGNATURES_OFFSET + (_index * SIGNATURE_LENGTH); uint256 _end = _start + SIGNATURE_LENGTH; return _metadata[_start:_end]; } /** * @notice Returns the validator address at `_index`. * @dev Assumes `_index` is less than the number of validators * @param _metadata ABI encoded Multisig ISM metadata. * @param _index The index of the validator to return. * @return The validator address at `_index`. */ function validatorAt(bytes calldata _metadata, uint256 _index) internal pure returns (address) { // Validator addresses are left padded to bytes32 in order to match // abi.encodePacked(address[]). uint256 _start = _validatorsOffset(_metadata) + (_index * 32) + 12; uint256 _end = _start + 20; return address(bytes20(_metadata[_start:_end])); } /** * @notice Returns the validator set encoded as bytes. Verified against the * commitment stored in the module. * @dev Validator addresses are encoded as tightly packed array of bytes32, * sorted to match the enumerable set stored by the module. * @param _metadata ABI encoded Multisig ISM metadata. * @return The validator set encoded as bytes. */ function validators(bytes calldata _metadata) internal pure returns (bytes calldata) { return _metadata[_validatorsOffset(_metadata):]; } /** * @notice Returns the size of the validator set encoded in the metadata * @dev Validator addresses are encoded as tightly packed array of bytes32, * sorted to match the enumerable set stored by the module. * @param _metadata ABI encoded Multisig ISM metadata. * @return The size of the validator set encoded in the metadata */ function commitment(bytes calldata _metadata) internal pure returns (uint256) { return (_metadata.length - _validatorsOffset(_metadata)) / 32; } /** * @notice Returns the size of the validator set encoded in the metadata * @dev Validator addresses are encoded as tightly packed array of bytes32, * sorted to match the enumerable set stored by the module. * @param _metadata ABI encoded Multisig ISM metadata. * @return The size of the validator set encoded in the metadata */ function validatorCount(bytes calldata _metadata) internal pure returns (uint256) { return (_metadata.length - _validatorsOffset(_metadata)) / 32; } /** * @notice Returns the offset in bytes of the list of validators within * `_metadata`. * @param _metadata ABI encoded Multisig ISM metadata. * @return The index at which the list of validators starts */ function _validatorsOffset(bytes calldata _metadata) private pure returns (uint256) { return SIGNATURES_OFFSET + (threshold(_metadata) * SIGNATURE_LENGTH); } }