Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added BlockHashISM #5276

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions solidity/contracts/isms/blockhash/AbstractBlockHashIsm.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

// ============ External Imports ============
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

// ============ Internal Imports ============
import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol";
import {IAggregationIsm} from "../../interfaces/isms/IAggregationIsm.sol";
import {AggregationIsmMetadata} from "../../isms/libs/AggregationIsmMetadata.sol";
import {PackageVersioned} from "../../PackageVersioned.sol";
import {IBlockHashOracle} from "./IBlockHashOracle.sol";
import {Message} from "../../libs/Message.sol";

/**
* @title AggregationIsm
* @notice Manages per-domain m-of-n ISM sets that are used to verify
* interchain messages.
*/
abstract contract AbstractBlockHashIsm is IAggregationIsm, PackageVersioned {
using Message for bytes;
// ============ Constants ============
// Use Blockhash oracle address
IBlockHashOracle public blockHashOracle = IBlockHashOracle(address(0));

// solhint-disable-next-line const-name-snakecase
uint8 public constant moduleType =
uint8(IInterchainSecurityModule.Types.AGGREGATION);

// ============ Virtual Functions ============
// ======= OVERRIDE THESE TO IMPLEMENT =======

/**
* @notice Returns the set of ISMs responsible for verifying _message
* and the number of ISMs that must verify
* @dev Can change based on the content of _message
* @param _message Hyperlane formatted interchain message
* @return modules The array of ISM addresses
* @return threshold The number of ISMs needed to verify
*/
function modulesAndThreshold(
bytes calldata _message
) public view virtual returns (address[] memory, uint8);

// ============ Public Functions ============]

/**
* @notice Requires that m-of-n ISMs verify the provided interchain message.
* @param _metadata ABI encoded module metadata (see AggregationIsmMetadata.sol)
* @param _message Formatted Hyperlane message (see Message.sol).
*/
function verify(
bytes calldata _metadata,
bytes calldata _message
) public returns (bool) {
// Step 1: Verify the message using the aggregation ISM logic
(address[] memory _isms, uint8 _threshold) = modulesAndThreshold(
_message
);
uint256 _count = _isms.length;
for (uint8 i = 0; i < _count; i++) {
if (!AggregationIsmMetadata.hasMetadata(_metadata, i)) continue;
IInterchainSecurityModule _ism = IInterchainSecurityModule(
_isms[i]
);
require(
_ism.verify(
AggregationIsmMetadata.metadataAt(_metadata, i),
_message
),
"!verify"
);
_threshold -= 1;
}

// Step 2: Decode the message to extract fields
uint32 originDomain = _message.origin();
uint32 expectedOriginDomain = blockHashOracle.origin();

// Step 3: Extract block information from the message
(uint256 blockHash, uint256 blockHeight) = _extractBlockInfo(
_message.body()
);

uint256 expectedBlockHash = blockHashOracle.blockHash(blockHeight);
// Step 4: Verify the origin domain matches the expected origin domain and the threshold is zero
require(
_threshold == 0 &&
expectedOriginDomain == originDomain &&
expectedBlockHash == blockHash,
"validation_error"
);

// Step 4: Return true if all checks pass
return true;
}

function _extractBlockInfo(
bytes calldata _messageBody
) internal view returns (uint256 hash, uint256 height) {
require(_messageBody.length >= 64, "Invalid message body");

(hash, height) = abi.decode(_messageBody[:64], (uint256, uint256));
}
}
81 changes: 81 additions & 0 deletions solidity/contracts/isms/blockhash/BlockHashISM.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

// ============ Internal Imports ============
import {AbstractBlockHashIsm} from "./AbstractBlockHashIsm.sol";
import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol";
import {IThresholdAddressFactory} from "../../interfaces/IThresholdAddressFactory.sol";
import {MinimalProxy} from "../../libs/MinimalProxy.sol";
import {PackageVersioned} from "../../PackageVersioned.sol";

// ============ External Imports ============
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";

contract BlockHashISM is AbstractBlockHashIsm, Ownable2StepUpgradeable {
address[] public modules;
uint8 public threshold;

event ModulesAndThresholdSet(address[] modules, uint8 threshold);

constructor(
address[] memory _modules,
uint8 _threshold
) Ownable2StepUpgradeable() {
modules = _modules;
threshold = _threshold;
_disableInitializers();
}

function initialize(
address _owner,
address[] memory _modules,
uint8 _threshold
) external initializer {
__Ownable2Step_init();
setModulesAndThreshold(_modules, _threshold);
_transferOwnership(_owner);
}

function setModulesAndThreshold(
address[] memory _modules,
uint8 _threshold
) public onlyOwner {
require(
0 < _threshold && _threshold <= _modules.length,
"Invalid threshold"
);
modules = _modules;
threshold = _threshold;
emit ModulesAndThresholdSet(_modules, _threshold);
}

function modulesAndThreshold(
bytes calldata /* _message */
) public view override returns (address[] memory, uint8) {
return (modules, threshold);
}
}

contract BlockHashISMFactory is IThresholdAddressFactory, PackageVersioned {
address public immutable implementation;

constructor() {
implementation = address(new BlockHashISM(new address[](1), 1));
}

/**
* @notice Emitted when a multisig module is deployed
* @param module The deployed ISM
*/
event ModuleDeployed(address module);

// ============ External Functions ============
function deploy(
address[] calldata _modules,
uint8 _threshold
) external returns (address ism) {
ism = MinimalProxy.create(implementation);
emit ModuleDeployed(ism);
BlockHashISM(ism).initialize(msg.sender, _modules, _threshold);
}
}
8 changes: 8 additions & 0 deletions solidity/contracts/isms/blockhash/IBlockHashOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

interface IBlockHashOracle {
function origin() external view returns (uint32);

function blockHash(uint256 height) external view returns (uint256);
}
Loading