Skip to content

Commit

Permalink
Introduce audit report (#252)
Browse files Browse the repository at this point in the history
  • Loading branch information
seunlanlege authored Jul 1, 2024
1 parent 7cccc53 commit 8e12979
Show file tree
Hide file tree
Showing 17 changed files with 176 additions and 59 deletions.
Binary file not shown.
1 change: 1 addition & 0 deletions evm/abi/src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ impl TryFrom<EvmHostEvents> for ismp::events::Event {
fisherman: vetoed.fisherman.as_bytes().to_vec(),
})),
EvmHostEvents::HostFrozenFilter(_) |
EvmHostEvents::HostWithdrawalFilter(_) |
EvmHostEvents::HostParamsUpdatedFilter(_) |
EvmHostEvents::PostResponseFundedFilter(_) |
EvmHostEvents::RequestFundedFilter(_) => Err(anyhow!("Unsupported Event!"))?,
Expand Down
56 changes: 55 additions & 1 deletion evm/abi/src/generated/evm_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1569,6 +1569,29 @@ pub mod evm_host {
},
],
),
(
::std::borrow::ToOwned::to_owned("HostWithdrawal"),
::std::vec![
::ethers::core::abi::ethabi::Event {
name: ::std::borrow::ToOwned::to_owned("HostWithdrawal"),
inputs: ::std::vec![
::ethers::core::abi::ethabi::EventParam {
name: ::std::borrow::ToOwned::to_owned("amount"),
kind: ::ethers::core::abi::ethabi::ParamType::Uint(
256usize,
),
indexed: false,
},
::ethers::core::abi::ethabi::EventParam {
name: ::std::borrow::ToOwned::to_owned("beneficiary"),
kind: ::ethers::core::abi::ethabi::ParamType::Address,
indexed: false,
},
],
anonymous: false,
},
],
),
(
::std::borrow::ToOwned::to_owned("PostRequestEvent"),
::std::vec![
Expand Down Expand Up @@ -2462,6 +2485,12 @@ pub mod evm_host {
) -> ::ethers::contract::builders::Event<::std::sync::Arc<M>, M, HostParamsUpdatedFilter> {
self.0.event()
}
///Gets the contract's `HostWithdrawal` event
pub fn host_withdrawal_filter(
&self,
) -> ::ethers::contract::builders::Event<::std::sync::Arc<M>, M, HostWithdrawalFilter> {
self.0.event()
}
///Gets the contract's `PostRequestEvent` event
pub fn post_request_event_filter(
&self,
Expand Down Expand Up @@ -2959,6 +2988,21 @@ pub mod evm_host {
Eq,
Hash,
)]
#[ethevent(name = "HostWithdrawal", abi = "HostWithdrawal(uint256,address)")]
pub struct HostWithdrawalFilter {
pub amount: ::ethers::core::types::U256,
pub beneficiary: ::ethers::core::types::Address,
}
#[derive(
Clone,
::ethers::contract::EthEvent,
::ethers::contract::EthDisplay,
Default,
Debug,
PartialEq,
Eq,
Hash,
)]
#[ethevent(
name = "PostRequestEvent",
abi = "PostRequestEvent(bytes,bytes,bytes,bytes,uint256,uint256,bytes,uint256)"
Expand Down Expand Up @@ -3140,6 +3184,7 @@ pub mod evm_host {
GetRequestTimeoutHandledFilter(GetRequestTimeoutHandledFilter),
HostFrozenFilter(HostFrozenFilter),
HostParamsUpdatedFilter(HostParamsUpdatedFilter),
HostWithdrawalFilter(HostWithdrawalFilter),
PostRequestEventFilter(PostRequestEventFilter),
PostRequestHandledFilter(PostRequestHandledFilter),
PostRequestTimeoutHandledFilter(PostRequestTimeoutHandledFilter),
Expand Down Expand Up @@ -3170,6 +3215,9 @@ pub mod evm_host {
if let Ok(decoded) = HostParamsUpdatedFilter::decode_log(log) {
return Ok(EvmHostEvents::HostParamsUpdatedFilter(decoded));
}
if let Ok(decoded) = HostWithdrawalFilter::decode_log(log) {
return Ok(EvmHostEvents::HostWithdrawalFilter(decoded));
}
if let Ok(decoded) = PostRequestEventFilter::decode_log(log) {
return Ok(EvmHostEvents::PostRequestEventFilter(decoded));
}
Expand Down Expand Up @@ -3212,6 +3260,7 @@ pub mod evm_host {
::core::fmt::Display::fmt(element, f),
Self::HostFrozenFilter(element) => ::core::fmt::Display::fmt(element, f),
Self::HostParamsUpdatedFilter(element) => ::core::fmt::Display::fmt(element, f),
Self::HostWithdrawalFilter(element) => ::core::fmt::Display::fmt(element, f),
Self::PostRequestEventFilter(element) => ::core::fmt::Display::fmt(element, f),
Self::PostRequestHandledFilter(element) => ::core::fmt::Display::fmt(element, f),
Self::PostRequestTimeoutHandledFilter(element) =>
Expand Down Expand Up @@ -3252,6 +3301,11 @@ pub mod evm_host {
Self::HostParamsUpdatedFilter(value)
}
}
impl ::core::convert::From<HostWithdrawalFilter> for EvmHostEvents {
fn from(value: HostWithdrawalFilter) -> Self {
Self::HostWithdrawalFilter(value)
}
}
impl ::core::convert::From<PostRequestEventFilter> for EvmHostEvents {
fn from(value: PostRequestEventFilter) -> Self {
Self::PostRequestEventFilter(value)
Expand Down Expand Up @@ -4999,7 +5053,7 @@ pub mod evm_host {
pub un_staking_period: ::ethers::core::types::U256,
pub challenge_period: ::ethers::core::types::U256,
pub consensus_client: ::ethers::core::types::Address,
pub state_machine_whitelist: ::std::vec::Vec<::ethers::core::types::U256>,
pub state_machines: ::std::vec::Vec<::ethers::core::types::U256>,
pub fishermen: ::std::vec::Vec<::ethers::core::types::Address>,
pub hyperbridge: ::ethers::core::types::Bytes,
}
Expand Down
4 changes: 2 additions & 2 deletions evm/abi/src/generated/host_manager.rs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions evm/script/DeployIsmp.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ contract DeployScript is Script {

// Host manager
HostManager manager = new HostManager{salt: salt}(HostManagerParams({admin: admin, host: address(0)}));
uint256[] memory stateMachineWhitelist = new uint256[](1);
stateMachineWhitelist[0] = paraId;
uint256[] memory stateMachines = new uint256[](1);
stateMachines[0] = paraId;

// EvmHost
address[] memory fishermen = new address[](0);
Expand All @@ -85,7 +85,7 @@ contract DeployScript is Script {
perByteFee: 3 * 1e15, // $0.003/byte
hyperbridge: StateMachine.kusama(paraId),
feeToken: address(feeToken),
stateMachineWhitelist: stateMachineWhitelist
stateMachines: stateMachines
});

address hostAddress = initHost(params);
Expand Down
75 changes: 52 additions & 23 deletions evm/src/hosts/EvmHost.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,46 @@ import {StateCommitment, StateMachineHeight} from "ismp/IConsensusClient.sol";
import {IHandler} from "ismp/IHandler.sol";
import {PostRequest, PostResponse, GetRequest, GetResponse, PostTimeout, Message} from "ismp/Message.sol";

// The IsmpHost parameters
// The EvmHost protocol parameters
struct HostParams {
// default timeout in seconds for requests.
// The default timeout in seconds for requests. If requests are dispatched
// with a timeout value lower than this this value will be used instead
uint256 defaultTimeout;
// cost of cross-chain requests in the fee token per byte
// The cost of cross-chain requests in the feetoken per-byte,
// this is charged to the application initiating a request or response
uint256 perByteFee;
// The fee token contract. This will typically be DAI.
// The fee token contract address. This will typically be DAI.
// but we allow it to be configurable to prevent future regrets.
address feeToken;
// admin account, this only has the rights to freeze, or unfreeze the bridge
// The admin account, this only has the rights to freeze, or unfreeze the bridge
address admin;
// Ismp request/response handler
// Ismp message handler contract. This performs all verification logic
// needed to validate cross-chain messages before they are dispatched to local modules
address handler;
// the authorized host manager contract
// The authorized host manager contract, is itself an `IIsmpModule`
// which receives governance requests from the Hyperbridge chain to either
// withdraw revenue from the host or update its protocol parameters
address hostManager;
// unstaking period
// The unstaking period of Polkadot's validators. In order to prevent long-range attacks
uint256 unStakingPeriod;
// minimum challenge period in seconds;
// Minimum challenge period for state commitments in seconds;
uint256 challengePeriod;
// consensus client contract
// The consensus client contract which handles consensus proof verification
address consensusClient;
// whitelisted state machines
uint256[] stateMachineWhitelist;
// white list of fishermen accounts
// State machines whose state commitments are accepted
uint256[] stateMachines;
// Priviledged set of fishermen accounts
address[] fishermen;
// state machine identifier for hyperbridge
// The state machine identifier for hyperbridge
bytes hyperbridge;
}

// The host manager interface. This provides methods for modifying the host's params or withdrawing bridge revenue.
// Can only be called used by the HostManager module.
/**
* @title The Host Manager Interface. This provides methods for
* modifying the host's params or withdrawing bridge revenue.
*
* @dev Can only be called used by the HostManager module.
*/
interface IHostManager {
/**
* @dev Updates IsmpHost params
Expand All @@ -81,8 +90,16 @@ struct WithdrawParams {
uint256 amount;
}

/// IsmpHost implementation for Evm hosts. Refer to the official ISMP specification.
/// https://docs.hyperbridge.network/protocol/ismp
/**
* @title EvmHost. IsmpHost and IsmpDispatcher implementation for EVM-compatible chains
* Refer to the official ISMP specification. https://docs.hyperbridge.network/protocol/ismp
*
* @dev The IsmpHost provides the neccessary storage interface for the ISMP handlers to process
* ISMP messages, the IsmpDispatcher provides the interfaces applications use for dispatching requests
* and responses. This host implementation delegates all verification logic to the IHandler contract.
* It is only responsible for dispatching incoming & outgoing requests/responses. As well as managing
* the state of the ISMP protocol.
*/
abstract contract EvmHost is IIsmpHost, IHostManager, Context {
using Bytes for bytes;
using Message for PostResponse;
Expand Down Expand Up @@ -218,22 +235,33 @@ abstract contract EvmHost is IIsmpHost, IHostManager, Context {
// Emitted when the host params is updated
event HostParamsUpdated(HostParams oldParams, HostParams newParams);

// Emitted when the host processes a withdrawal
event HostWithdrawal(uint256 amount, address beneficiary);

// Account is unauthorized to perform requested action
error UnauthorizedAccount();

// Provided address didn't fit address type size
error InvalidAddressLength();

// Provided request was unknown
error UnknownRequest();

// Provided response was unknown
error UnknownResponse();

// Action breaks protocol invariants and is therefore unauthorized
error UnauthorizedAction();

// Application is attempting to respond to a request it did not receive
error UnauthorizedResponse();

// Response has already been provided for this request
error DuplicateResponse();

// Cannot exceed max fishermen count
error MaxFishermanCountExceeded(uint256 provided);

// Host manager address was zero or not a contract
error InvalidHostManagerAddress();

Expand Down Expand Up @@ -423,9 +451,9 @@ abstract contract EvmHost is IIsmpHost, IHostManager, Context {
revert UnauthorizedAction();
}

uint256 whitelistLength = params.stateMachineWhitelist.length;
uint256 whitelistLength = params.stateMachines.length;
for (uint256 i = 0; i < whitelistLength; ++i) {
delete _latestStateMachineHeight[params.stateMachineWhitelist[i]];
delete _latestStateMachineHeight[params.stateMachines[i]];
}
updateHostParamsInternal(params);
}
Expand Down Expand Up @@ -464,11 +492,11 @@ abstract contract EvmHost is IIsmpHost, IHostManager, Context {
}

// add whitelisted state machines
uint256 whitelistLength = params.stateMachineWhitelist.length;
uint256 whitelistLength = params.stateMachines.length;
for (uint256 i = 0; i < whitelistLength; ++i) {
// create if it doesn't already exist
if (_latestStateMachineHeight[params.stateMachineWhitelist[i]] == 0) {
_latestStateMachineHeight[params.stateMachineWhitelist[i]] = 1;
if (_latestStateMachineHeight[params.stateMachines[i]] == 0) {
_latestStateMachineHeight[params.stateMachines[i]] = 1;
}
}
}
Expand All @@ -479,6 +507,7 @@ abstract contract EvmHost is IIsmpHost, IHostManager, Context {
*/
function withdraw(WithdrawParams memory params) external restrict(_hostParams.hostManager) {
SafeERC20.safeTransfer(IERC20(feeToken()), params.beneficiary, params.amount);
emit HostWithdrawal({beneficiary: params.beneficiary, amount: params.amount});
}

/**
Expand Down
14 changes: 13 additions & 1 deletion evm/src/modules/HandlerV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ bytes constant REQUEST_RECEIPTS_STORAGE_PREFIX = hex"526571756573745265636569707
// Storage prefix for request receipts in the pallet-ismp child trie
bytes constant RESPONSE_RECEIPTS_STORAGE_PREFIX = hex"526573706f6e73655265636569707473";

/// Entry point for the hyperbridge. Implementation of the ISMP handler protocol
/**
* @title The ISMP Message Handler. Responsible for verifying the cryptographic proofs
* needed to confirm the validity of incoming requests/responses.
*/
contract HandlerV1 is IHandler, Context {
using Bytes for bytes;
using Message for PostResponse;
Expand All @@ -50,22 +53,31 @@ contract HandlerV1 is IHandler, Context {
// The cosensus client has now expired to mitigate
// long fork attacks, this is unrecoverable.
error ConsensusClientExpired();

// The IsmpHost has been frozen by the admin
error HostFrozen();

// Challenge period has not yet elapsed
error ChallengePeriodNotElapsed();

// The requested state commitment does not exist
error StateCommitmentNotFound();

// The message destination is not intended for this host
error InvalidMessageDestination();

// The provided message has now timed-out
error MessageTimedOut();

// The provided message has not timed-out
error MessageNotTimedOut();

// The message has been previously processed
error DuplicateMessage();

// The provided message is unknown to the host
error UnknownMessage();

// The provided proof is invalid
error InvalidProof();

Expand Down
6 changes: 4 additions & 2 deletions evm/src/modules/HostManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ struct HostManagerParams {
address host;
}

/// Manages the IsmpHost, allows cross-chain governance to configure params
/// and withdraw accrued revenue
/**
* @title The ISMP Host Manager. Allows cross-chain governance actions
* for updating the ISMP Host parameters or withdrawing bridge revenue.
*/
contract HostManager is BaseIsmpModule {
using Bytes for bytes;

Expand Down
15 changes: 14 additions & 1 deletion evm/src/modules/Registrar.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ struct RegistrarParams {
uint256 baseFee;
}

/**
* @title The Token Registrar. Serves as a form of gas abstraction for token
* registration. By collecting fees on any chain and permitting token creation on the
* Hyperbridge chain.
*/
contract TokenRegistrar is BaseIsmpModule {
using Bytes for bytes;

Expand All @@ -61,12 +66,16 @@ contract TokenRegistrar is BaseIsmpModule {

// Unexpected state
error InconsistentState();

// Requested action is unauthorized
error UnauthorizedAction();

// A user has initiated the asset registration process
event RegistrationBegun(bytes32 indexed assetId, address indexed owner);

// Governance has updated the registrar parameters
event ParamsUpdated(RegistrarParams oldParams, RegistrarParams newParams);

// restricts call to the provided `caller`
modifier restrict(address caller) {
if (msg.sender != caller) revert UnauthorizedAction();
Expand Down Expand Up @@ -148,6 +157,10 @@ contract TokenRegistrar is BaseIsmpModule {
// only hyperbridge can do this
if (!incoming.request.source.equals(IIsmpHost(_params.host).hyperbridge())) revert UnauthorizedAction();

_params = abi.decode(incoming.request.body, (RegistrarParams));
RegistrarParams memory update = abi.decode(incoming.request.body, (RegistrarParams));

emit ParamsUpdated({oldParams: _params, newParams: update});

_params = update;
}
}
Loading

0 comments on commit 8e12979

Please sign in to comment.