diff --git a/docs/pages/developers/evm/dispatching.mdx b/docs/pages/developers/evm/dispatching.mdx index 447f31de6..8c3960fa6 100644 --- a/docs/pages/developers/evm/dispatching.mdx +++ b/docs/pages/developers/evm/dispatching.mdx @@ -9,7 +9,7 @@ A POST request is simply a cross-chain message to be executed by some `IIsmpModu A post request is created using the `DispatchPost` struct ```solidity showLineNumbers -// An object for dispatching post requests to the IsmpDispatcher +// An object for dispatching post requests struct DispatchPost { // Use the StateMachine library to create this bytes dest; @@ -19,14 +19,15 @@ struct DispatchPost { bytes body; // timeout for this request in seconds uint64 timeout; - // The amount put up to be paid to the relayer, this is in $DAI and charged to tx.origin + // The amount put up to be paid to the relayer, + // this is in DAI and charged to msg.sender uint256 fee; // who pays for this request? address payer; } ``` -### Dispatch Parameters: +### Dispatch Parameters - `dest`: Destination chain, for this you'll use the `StateMachine` library eg `StateMachine.evm(1)` for Ethereum Mainnet. @@ -38,7 +39,8 @@ struct DispatchPost { - `body`: Serialized byte representation of the message (to be decoded by the receiving contract).
-- `timeout`: Relative time in seconds for message validity. Messages exceeding this timeout cannot be processed on the destination and require user action (timeout message) to revert changes. +- `timeout`: Time in seconds for message validity eg 3600 for a timeout of 1 hour, or 0 for no timeout. ie Messages will never expire. If the timeout is set to a non-zero value, messages that have exceeded this timeout will be rejected on the destination and require user action + (timeout message) to revert changes.
- `fee`: Optional relayer fees, this can also be set to zero if the application developers prefer to self-relay.
@@ -71,7 +73,7 @@ Dispatching a POST response, going by it's name is, well, a response to a previo A post response dispatch has the following fields: ```solidity showLineNumbers -// An object for dispatching post responses to the IsmpDispatcher +// An object for dispatching post responses struct DispatchPostResponse { // The request that initiated this response PostRequest request; @@ -79,14 +81,15 @@ struct DispatchPostResponse { bytes response; // timeout for this response in seconds uint64 timeout; - // the amount put up to be paid to the relayer, this is in $DAI and charged to tx.origin + // The amount put up to be paid to the relayer, + // this is in DAI and charged to msg.sender uint256 fee; // who pays for this request? address payer; } ``` -### Dispatch Parameters: +### Dispatch Parameters - `request`: The request object that was previously received. @@ -96,7 +99,7 @@ struct DispatchPostResponse {
-- `timeout`: Relative time in seconds for message validity. Messages exceeding this timeout cannot be processed on the destination and require user action +- `timeout`: Time in seconds for message validity eg 3600 for a timeout of 1 hour, or 0 for no timeout. ie Messages will never expire. If the timeout is set to a non-zero value, messages that have exceeded this timeout will be rejected on the destination and require user action (timeout message) to revert changes.
@@ -136,7 +139,7 @@ When dispatching get requests, you specify the storage keys you need to read and the block height at which you need to read these storage entries. ```solidity -// An object for dispatching get requests to the IsmpDispatcher +// An object for dispatching get requests struct DispatchGet { // bytes representation of the destination state machine bytes dest; @@ -153,7 +156,7 @@ struct DispatchGet { } ``` -### Dispatch Parameters: +### Dispatch Parameters - `dest`: The chain whose database should be read (e.g., `StateMachine.evm(1)` for Ethereum Mainnet). @@ -167,8 +170,8 @@ struct DispatchGet {
-- `timeout`: Relative time in seconds for message validity. Responses exceeding this timeout cannot be processed on the source and require user action (timeout - message) to revert changes. +- `timeout`: Time in seconds for message validity eg 3600 for a timeout of 1 hour, or 0 for no timeout. ie Messages will never expire. If the timeout is set to a non-zero value, messages that have exceeded this timeout will be rejected on the destination and require user action + (timeout message) to revert changes.
diff --git a/docs/pages/developers/evm/receiving.mdx b/docs/pages/developers/evm/receiving.mdx index 3dea5071f..83379d8d7 100644 --- a/docs/pages/developers/evm/receiving.mdx +++ b/docs/pages/developers/evm/receiving.mdx @@ -4,10 +4,9 @@ To receive ISMP messages a contract must implement the [`IIsmpModule`](https://g The required methods for the [`IIsmpModule`](https://github.com/polytope-labs/ismp-solidity/blob/main/interfaces/IIsmpModule.sol#L42) is described in detail below: - ## `onAccept` -This is the callback method for new POST requests that have been verified by Hyperbridge. After the `IHandler` verifies the neccessary proofs of this request, they are passed on to the host, which in turn calls the `onAccept` method for the intended modules. The arguments provided `IncomingPostRequest` holds both the request object itself and the account that initally called the Handler contract, this may be either a 3rd party relayer or a user who is self-relaying. +This is the callback method for new POST requests that have been verified by Hyperbridge. After the `IHandler` verifies the necessary proofs of this request, they are passed on to the host, which in turn calls the `onAccept` method for the intended modules. The arguments provided `IncomingPostRequest` holds both the request object itself and the account that initally called the Handler contract, this may be either a 3rd party relayer or a user who is self-relaying. `IIsmpModule`'s should ensure that is method is only callable by the `host` or risk critical vulnerabilies from unauthorized calls to this method by malicious actors. A modifier `onlyHost` is provided as part of the `BaseIsmpModule` for this. @@ -15,11 +14,11 @@ This is the callback method for new POST requests that have been verified by Hyp In the event that some initially dispatched request was unable to be delivered. Whether as a result of insufficient fees provided to the relayers, Or a revert during request execution on the destination chain. Then Hyperbridge allows this request to gracefully timeout, and this timeout can be reported back to the sending module on the source chain. -This callback is provided as a way to execute some logic in the event that some request times out. This can be seen as a *catch* block in a try/catch for cross-chain messages. Typically you'll want to revert any state changes that were made prior to dispatching the request. +This callback is provided as a way to execute some logic in the event that some request times out. This can be seen as a _catch_ block in a try/catch for cross-chain messages. Typically you'll want to revert any state changes that were made prior to dispatching the request. ## `onPostResponse` -This is the callback method for new POST responses that have been verified by Hyperbridge. After the `IHandler` verifies the neccessary proofs of this response, they are passed on to the host, which in turn calls the `onPostResponse` method for the intended modules. The arguments provided `IncomingPostResponse` holds both the resopnse object itself and the account that initally called the Handler contract, this may be either a 3rd party relayer or a user who is self-relaying. +This is the callback method for new POST responses that have been verified by Hyperbridge. After the `IHandler` verifies the necessary proofs of this response, they are passed on to the host, which in turn calls the `onPostResponse` method for the intended modules. The arguments provided `IncomingPostResponse` holds both the resopnse object itself and the account that initally called the Handler contract, this may be either a 3rd party relayer or a user who is self-relaying. `IIsmpModule`'s should ensure that is method is only callable by the `host` or risk critical vulnerabilies from unauthorized calls to this method by malicious actors. A modifier `onlyHost` is provided as part of the `BaseIsmpModule` for this. @@ -27,11 +26,11 @@ This is the callback method for new POST responses that have been verified by Hy In the event that some initially dispatched response was unable to be delivered. Whether as a result of insufficient fees provided to the relayers, Or a revert during response execution on the destination chain. Then Hyperbridge allows this response to gracefully timeout, and this timeout can be reported back to the sending module on the source chain. -This callback is provided as a way to execute some logic in the event that some response times out. This can be seen as a *catch* block in a try/catch for cross-chain messages. Typically you'll want to revert any state changes that were made prior to dispatching the response. +This callback is provided as a way to execute some logic in the event that some response times out. This can be seen as a _catch_ block in a try/catch for cross-chain messages. Typically you'll want to revert any state changes that were made prior to dispatching the response. ## `onGetResponse` -This is the callback method for new GET responses that have been verified by Hyperbridge. After the `IHandler` verifies the neccessary proofs of this response, they are passed on to the host, which in turn calls the `onGetResponse` method for the intended modules. The arguments provided `IncomingGetResponse` holds both the resopnse object itself and the account that initally called the Handler contract, this may be either a 3rd party relayer or a user who is self-relaying. +This is the callback method for new GET responses that have been verified by Hyperbridge. After the `IHandler` verifies the necessary proofs of this response, they are passed on to the host, which in turn calls the `onGetResponse` method for the intended modules. The arguments provided `IncomingGetResponse` holds both the resopnse object itself and the account that initally called the Handler contract, this may be either a 3rd party relayer or a user who is self-relaying. `IIsmpModule`'s should ensure that is method is only callable by the `host` or risk critical vulnerabilies from unauthorized calls to this method by malicious actors. A modifier `onlyHost` is provided as part of the `BaseIsmpModule` for this. @@ -39,8 +38,7 @@ This is the callback method for new GET responses that have been verified by Hyp In the event that some GET request is unable to be processed. Likely as a result of insufficient fees provided. Then Hyperbridge allows this request to gracefully timeout, and this timeout can be reported back to the sending module on the source chain. -This callback is provided as a way to execute some logic in the event that some request times out. This can be seen as a *catch* block in a try/catch for cross-chain messages. Typically you'll want to revert any state changes that were made prior to dispatching the request. - +This callback is provided as a way to execute some logic in the event that some request times out. This can be seen as a _catch_ block in a try/catch for cross-chain messages. Typically you'll want to revert any state changes that were made prior to dispatching the request. ## Example `IIsmpModule` @@ -90,43 +88,66 @@ contract Example is BaseIsmpModule { return IDispatcher(host).dispatch{value: msg.value}(post); } - function onAccept(IncomingPostRequest memory incoming) external override onlyHost { + function onAccept(IncomingPostRequest memory incoming) + external + override + onlyHost + { // decode request body - // Check that decoded value can be executed successfully - // Make state changes + // make any necessary state changes emit PostReceived(); } - function onPostRequestTimeout(PostRequest memory request) external override onlyHost { - // revert any state changes made when post request was dispatched + function onPostRequestTimeout(PostRequest memory request) + external + override + onlyHost + { + // revert any state changes made + // when post request was dispatched emit PostTimeoutReceived(); } - function onPostResponse(IncomingPostResponse memory) external override onlyHost { + function onPostResponse(IncomingPostResponse memory) + external + override + onlyHost + { // decode response - // Check that decoded value can be executed successfully - // Make state changes + // make any necessary state changes emit PostResponseReceived(); } - function onPostResponseTimeout(PostResponse memory) external override onlyHost { - // revert any state changes made when post response was dispatched + function onPostResponseTimeout(PostResponse memory) + external + override + onlyHost + { + // revert any state changes made + // when post response was dispatched emit PostResponseTimeoutReceived(); } - function onGetResponse(IncomingGetResponse memory) external override onlyHost { + function onGetResponse(IncomingGetResponse memory) + external + override + onlyHost + { emit GetResponseReceived(); } - function onGetTimeout(GetRequest memory) external override onlyHost { - // revert any state changes made when get request was dispatched + function onGetTimeout(GetRequest memory) + external + override + onlyHost + { + // revert any state changes + // made when get request was dispatched emit GetTimeoutReceived(); } } ``` - ## Security Considerations -* Limit the caller of these functions to the `IIsmpHost` contract only. This prevents unauthorized messages from being executed. - +- Limit the caller of these functions to the `IIsmpHost` contract only. This prevents unauthorized messages from being executed. diff --git a/docs/pages/developers/explore/index.mdx b/docs/pages/developers/explore/index.mdx index fc7b68bc6..2bc44ce19 100644 --- a/docs/pages/developers/explore/index.mdx +++ b/docs/pages/developers/explore/index.mdx @@ -9,5 +9,5 @@ Hyperbridge brings together advanced cryptographic and mechanistic protocols to The protocol is facilitated by: - - A permissionless set of relayers who transmit cross-chain messages authenticated by cryptographic proofs. - - Hyperbridge Nexus, a blockchain that serves a crypto-economic coprocessor for aggregating interoperability proofs. + - A permissionless set of relayers that transmit cross-chain messages authenticated by cryptographic proofs. + - Hyperbridge Nexus, a blockchain that serves as a crypto-economic coprocessor for aggregating interoperability proofs. diff --git a/docs/pages/developers/network/node.mdx b/docs/pages/developers/network/node.mdx index 260a42cd4..2732b9f41 100644 --- a/docs/pages/developers/network/node.mdx +++ b/docs/pages/developers/network/node.mdx @@ -102,7 +102,8 @@ rustup component add rust-src Download a local copy of the repo and checkout the latest release tag ```bash -export LATEST_TAG=hyperbridge-v0.5.0 +# fetch the latest tag from docker hub +LATEST_TAG=$(curl -s https://hub.docker.com/v2/repositories/polytopelabs/hyperbridge/tags\?page_size\=1\&page\=2 | jq -r '.results[0].name') git clone https://github.com/polytope-labs/hyperbridge.git cd ./hyperbridge git checkout ${LATEST_TAG} @@ -162,28 +163,6 @@ hyperbridge \ --sync=fast-unsafe ``` -### Kusama - -Hyperbridge is live on Kusama with a chainId of `messier` and ParaId of `3340`. We do not recommend joining the kusama network at this time. - -```bash -export PUBLIC_IP_ADDRESS= -hyperbridge \ - --base-path=$HOME/.hyperbridge \ - --pruning=archive \ - --name="Your node name here" \ - --rpc-cors=all \ - --rpc-port=9944 \ - --unsafe-rpc-external \ - --rpc-methods=unsafe \ - --chain=messier \ - --no-mdns \ - --listen-addr=/ip4/0.0.0.0/tcp/30333 \ - --listen-addr=/ip6/::/tcp/30333 \ - --public-addr=/ip4/$PUBLIC_IP_ADDRESS/tcp/30333 \ - --out-peers=32 -``` - ### Polkadot Hyperbridge is also live on Polkadot with a chainId of `nexus` and ParaId of `3367`. diff --git a/docs/pages/developers/network/relayer.mdx b/docs/pages/developers/network/relayer.mdx index 17cda3e94..54922645f 100644 --- a/docs/pages/developers/network/relayer.mdx +++ b/docs/pages/developers/network/relayer.mdx @@ -5,7 +5,7 @@ description: Running the Hyperbridge relayer # Running a Relayer -The Hyperbridge relayer (tesseract) can be obtained through a variety of ways. For now only release artifacts for x86 linux environments are officially distributed. You can also build the relayer from source if you prefer. +The Hyperbridge relayer (tesseract) can be obtained through a variety of ways. For now only release artifacts for x86 linux environments are officially distributed. You can also build the relayer from source if you prefer. ## Prebuilt binaries @@ -84,13 +84,14 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ### Clone the repo -Download a local copy of the repo and checkout the latest release tag +Download a local copy of the repo and checkout the latest release version. ```bash -export LATEST_TAG=tesseract-v0.3.3 +# fetch the latest tag from docker hub +LATEST_TAG=$(curl -s https://hub.docker.com/v2/repositories/polytopelabs/tesseract/tags\?page_size\=1\&page\=2 | jq -r '.results[0].name') git clone https://github.com/polytope-labs/hyperbridge.git cd ./hyperbridge -git checkout ${LATEST_TAG} +git checkout tesseract-${LATEST_TAG} ``` ### Build the tesseract relayer @@ -102,7 +103,9 @@ cargo build --release -p tesseract The Tesseract relayer will now be available at `target/release/tesseract`, You can move the binary to your `$PATH` so you can run it directly.
-Update your path to include `${HOME}/.local/bin`. If you are using Bash, run the following. Alternatively, replace `${HOME}/.bashrc` with `${HOME}/.zshrc` if using Zsh. Replace `source` with `.` if necessary. +Update your path to include `${HOME}/.local/bin`. If you are using Bash, run the +following. Alternatively, replace `${HOME}/.bashrc` with `${HOME}/.zshrc` if +using Zsh. Replace `source` with `.` if necessary. ```bash # add .local/bin to path if it doesn't exist @@ -146,7 +149,7 @@ At the minimum, the hyperbridge relayer should be run on a machine with at least A community member has graciously provided their ansible playbook for running the hyperbridge relayer. You can find it here: - - [schmiatz/hyperbridge-relayer](https://github.com/schmiatz/hyperbridge-relayer) +- [schmiatz/hyperbridge-relayer](https://github.com/schmiatz/hyperbridge-relayer) ## Configuration @@ -188,7 +191,7 @@ delivery_endpoints = [ [ethereum] # configuration type can be either "evm" or "substrate" type = "evm" -# State machine identifier for the this evm chain. +# State machine identifier for this evm chain. # Must be specified as: # - "EVM-{chainId}" @@ -314,91 +317,91 @@ tesseract --config=$HOME/config.toml --db=$HOME/tesseract.db accumulate-fees --w The relayer also runs background tasks for automatic fee accumulation and withdrawals. Whenever a batch of messages is successfully delivered, the fee accumulation task receives the delivery receipts and starts the process of accumulating the fees on hyperbridge. This process happens concurrently for all successfully delivered message batches. For redundancy, the delivery receipts are stored in the database prior to accumulation so they can be retried manually if any error is encountered.
-Withdrawing fees from hyperbridge is triggered at fixed intervals based on the configured -`withdrawal_frequency` and `minimum_withdrawal_amount`. Feel free to the adjust these -values as desired. The task will only make a withdrawal attempt if your balance on -hyperbridge is greater than or equal to the configured `minimum_withdrawal_amount`. -Any failed withdrawal attempts will be retried each time the withdrawal task is triggered. -The manual processes described in the previous sections can be used as fallbacks -when errors are encountered by their automated conterparts. +Withdrawing fees from hyperbridge is triggered at fixed intervals based on the +configured `withdrawal_frequency` and `minimum_withdrawal_amount`. Feel free to +the adjust these values as desired. The task will only make a withdrawal attempt +if your balance on hyperbridge is greater than or equal to the configured +`minimum_withdrawal_amount`. Any failed withdrawal attempts will be retried each +time the withdrawal task is triggered. The manual processes described in the +previous sections can be used as fallbacks when errors are encountered by their +automated conterparts. ## Relayer Errors -List of commonly seen errors with their explanation and tips on how to fix them. +List of commonly seen errors with their explanation and tips on how to fix them. -```manpage +``` ERROR tesseract_messaging: Failed to submit transaction to Evm(42161): Middleware error: (code: -32000, message: insufficient funds for gas * price + value: address have 0 want 323200000000000, data: None ``` -```manpage +``` ERROR tesseract_evm::provider: Debug trace frame not found! ``` -```manpage +``` ERROR tesseract_messaging: Error while handling POLKADOT-3367 on Evm(8453): Invalid name: please ensure the contract and method you're calling exist! failed to decode empty bytes. if you're using jsonrpc this is likely due to jsonrpc returning `0x` in case contract or method don't exist ``` -```manpage +``` ERROR tesseract: Messaging task Evm(56)->Polkadot(3367) encountered an error: StreamError("Error encountered while querying state_machine_update_time Middleware error: (code: -32000, message: header not found, data: None)") ``` -```manpage +``` ERROR tesseract_messaging: Failed to submit transaction to Polkadot(3367): Failed to submit unsigned extrinsic ``` -```manpage +``` ERROR tesseract_messaging: Error while handling POLKADOT-3367 on Evm(56): Middleware error: (code: -32601, message: The method debug_traceCall does not exist/is not available, data: None) ``` -```manpage +``` ERROR tesseract: Error waiting for challenge period in Evm(56) - Polkadot(3367) update stream ``` -```manpage +``` ERROR tesseract_messaging: Failed to submit transaction to Evm(10): Transaction reverted ``` -```manpage +``` ERROR tesseract_messaging: Error while handling POLKADOT-3367 on Evm(100): Middleware error: (code: -32053, message: API key is not allowed to access method, data: None) ``` -```manpage +``` ERROR tesseract: Messaging task Evm(56)->Polkadot(3367) encountered an error: StreamError("Error fetching latest block height on Evm(56) JsonRpcClientError(MiddlewareError(Middleware((code: -32603, message: request failed or timed out, data: None))))") ``` -```manpage +``` ERROR tesseract_evm::provider: Error while querying events in range 43812202..43812204 from Evm(56): Middleware error: (code: -32603, message: request failed or timed out, data: None) ``` -```manpage +``` ERROR tesseract_evm::provider: Error while querying events in range 43816145..43816147 from Evm(56): Middleware error: (code: -32603, message: internal server error, data: None) ``` -```manpage +``` ERROR tesseract: Messaging task Evm(56)->Polkadot(3367) encountered an error: StreamError("Error encountered while querying state_machine_update_time Invalid name: please ensure the contract and method you're calling exist! failed to decode empty bytes. if you're using jsonrpc this is likely due to jsonrpc returning `0x` in case contract or method don't exist") ``` -```manpage +``` ERROR tesseract_messaging: Error while handling POLKADOT-3367 on Evm(1): Middleware error: Deserialization Error: response must be either a success/error or notification object at line 1 column 237. Response: {"jsonrpc":"2.0","id":31327,"result":null,"error":{"code":-32000,"message":"tracing failed: insufficient funds for gas * price + value: address
have 411893340457544628 want 525945389500000000"}} ``` -```manpage +``` ERROR tesseract_messaging: Error accummulating some fees, receipts have been stored in the db, you can try again manually ``` -```manpage +``` ERROR tesseract: Messaging task Evm(56)->Polkadot(3367) encountered an error: StreamError("Error fetching latest block height on Evm(56) JsonRpcClientError(MiddlewareError(Middleware(Deserialization Error: expected value at line 1 column 1. Response: \r\n504 Gateway Time-out\r\n\r\n

504 Gateway Time-out

\r\n\r\n\r\n)))") ``` -```manpage +``` ERROR tesseract_evm::provider: Error while querying events in range 43876757..43876796 from Evm(56): Middleware error: Deserialization Error: expected value at line 1 column 1. Response: ``` -```manpage +``` ERROR tesseract_messaging: Error while handling EVM-56 on Polkadot(3367): Middleware error: (code: -32000, message: missing trie node baf71a6410ba54b32b02056415efe6b22060c20aff9c399d9b6308d0a88c3d09 (path ) state 0xbaf71a6410ba54b32b02056415efe6b22060c20aff9c399d9b6308d0a88c3d09 is not available, data: None) ``` -```manpage +``` ERROR tesseract::cli: Disconnected from telemetry with: Text( ``` - diff --git a/docs/pages/developers/polkadot/getting-started.mdx b/docs/pages/developers/polkadot/getting-started.mdx new file mode 100644 index 000000000..3d62375c2 --- /dev/null +++ b/docs/pages/developers/polkadot/getting-started.mdx @@ -0,0 +1,83 @@ +--- +title: Getting Started +description: Hyperbridge is built on a cross-chain interoperability protocol referred to as the [Interoperable State Machine Protocol](/protocol/ismp). This protocol implementation lives in the [Hyperbridge Monorepo](https://github.com/polytope-labs/hyperbridge/tree/main/modules/ismp). +--- + +# ISMP SDK + +Hyperbridge is built on a cross-chain interoperability protocol referred to as the [Interoperable State Machine Protocol](/protocol/ismp). This protocol implementation lives in the [Hyperbridge Monorepo](https://github.com/polytope-labs/hyperbridge/tree/main/modules/ismp). Polkadot-SDK based chains whether parachains or solochains can be connected to Hyperbridge and consequently all of Hyperbridge's connected networks through the ISMP protocol. + +The connection between Hyperbridge and Polkadot-SDK chains is mediated by a few modules that we will introduce below. + +## Pallet ISMP + +This is the core module of the Interoperable state machine protocol for Polkadot-SDK chains. It exposes APIs and calls that allow the runtime to send and receive ISMP messages respectively. You can add it to your runtime like so + +``` +cargo add pallet-ismp +``` + +### Pallet ISMP Runtime API + +This ISMP runtime API exposes necessary storage items to the client subsystems, specifically in this case the RPC subsystem. But you can also build custom client subsytems that leverage this runtime API. You can add it to your runtime like so + +``` +cargo add pallet-ismp-runtime-api +``` + +### Pallet ISMP RPC + +The Pallet ISMP RPC module exposes the necessary RPC APIs that are required by the [tesseract relayer](/developers/network/relayer) and other alternative relayer implementations. This is required for any kind of offchain relayer process. You can add it to your runtime like so + +``` +cargo add pallet-ismp-rpc +``` + +## Parachain Consensus Client + +For parachain runtimes that want to connect to Hyperbridge, They will do so by means of the parachain consensus client. Which is leverages the relay chain as the source of truth for finalized sibling parachain state commitments. You can add it to your runtime like so + +``` +cargo add pallet-ismp-rpc +``` + + +### Parachain Inherent + +The parachain inherent greatly simplifies the infrastructure required for parachains to exchange messages with Hyperbridge by turning collators into consensus relayers. Specifically this inherent will automatically include sibling parachain headers and their proofs into every block keeping the parachain up to date with the latest finalized state of Hyperbridge and any other sibling parachain that is configured. Without this, a seperate consensus relayer will need to be run offchain. You can add it to your runtime like so + +``` +cargo add ismp-parachain-inherent +``` + +### Parachain Runtime API + +The Parachain inherent provider requires some runtime APIs to access which parachains are configured by the runtime to be included in the inherent.ou can add it to your runtime like so + +``` +cargo add ismp-parachain-runtime-api +``` + +## GRANDPA Consensus Client + +Solochains that want to connect to Hyperbridge will do so by means of the GRANDPA consensus client. This consensus client is capable of verifying GRANDPA consensus proofs of standalone chains as well as relay chains. You can add it to your runtime like so + +``` +cargo add ismp-grandpa +``` + +## Pallet Hyperbridge + +The Pallet Hyperbridge provides an implementation of the `IsmpDispatcher` which collects the Hyperbridge protocol fees, as well as an `IsmpModule` for processing cross-chain messages from Hyperbridge. These cross-chain messages may either be withdrawal requests may be for either relayer fees, or protocol fees. The IsmpModule may also receive cross-chain messages from Hyperbridge to adjust it's protocol fees as decided by governance. This module is optional and is only needed if Polkadot-SDK chains opt to pay Hyperbridge protocol fees onchain, they may also do so offchain by running their own relayers and paying Hyperbridge it's native token when they relay messages to Hyperbridge. You can add it to your runtime like so + +``` +cargo add pallet-hyperbridge +``` + +## Pallet Token Gateway + +The Pallet Token Gateway is an application-layer module that leverages Hyperbridge for token bridging. It works with any implementation of the `fungibles::*` traits so `pallet-assets` or `orml-tokens`. Allowing runtimes to send and receive assets from any of Hyperbridge's connected chains even EVM ones. You can add it to your runtime like so + +``` +cargo add pallet-token-gateway +``` diff --git a/docs/pages/developers/polkadot/ismp-parachain-inherent.mdx b/docs/pages/developers/polkadot/ismp-parachain-inherent.mdx new file mode 100644 index 000000000..af0d8ad5f --- /dev/null +++ b/docs/pages/developers/polkadot/ismp-parachain-inherent.mdx @@ -0,0 +1,59 @@ + +# ISMP Parachain Inherent Provider + + +This module leverages the [`ProvideInherent`](https://docs.rs/frame-support/latest/frame_support/inherent/trait.ProvideInherent.html) functionality to submit consensus update messages. This approach offers a significant advantage: + + - **Eliminating Off-chain Consensus Relayer** : By utilizing inherent messages for receiving consensus messages about finalized parachain block headers, the need for a separate off-chain consensus relayer is eliminated. + - **Simplified Architecture** : This reduces the overall system complexity by removing an external component (the off-chain consensus relayer). + - **Improved Efficiency** : Inherents are a built-in mechanism within the polkadot-sdk, allowing the collator to act as the consensus relayer. + +The inherent provider module needs to be added to the collator's client subsystems as shown in the code below + +```rust showLineNumbers +fn start_consensus( + client: Arc, + backend: Arc, + block_import: ParachainBlockImport, + prometheus_registry: Option<&Registry>, + telemetry: Option, + task_manager: &TaskManager, + relay_chain_interface: Arc, + transaction_pool: Arc>, + sync_oracle: Arc>, + keystore: KeystorePtr, + relay_chain_slot_duration: Duration, + para_id: ParaId, + collator_key: CollatorPair, + overseer_handle: OverseerHandle, + announce_block: Arc>) + Send + Sync>, +) { + // .. omitted calls + + let (client_clone, relay_chain_interface_clone) = + (client.clone(), relay_chain_interface.clone()); + let params = lookahead::Params { + create_inherent_data_providers: move |parent, ()| { + let client = client_clone.clone(); + let relay_chain_interface = relay_chain_interface_clone.clone(); + async move { + let inherent = ismp_parachain_inherent::ConsensusInherentProvider::create( + parent, + client, + relay_chain_interface, + ).await?; + + Ok(inherent) + } + }, + ..Default::default() + // omitted fields + }; + + // ..omitted calls +} +``` + +## Implementation + + - [ismp-parachain-inherent](https://github.com/polytope-labs/hyperbridge/blob/main/modules/ismp/clients/parachain/inherent/src/lib.rs) diff --git a/docs/pages/developers/polkadot/ismp-parachain-runtime-api.mdx b/docs/pages/developers/polkadot/ismp-parachain-runtime-api.mdx new file mode 100644 index 000000000..08420df2a --- /dev/null +++ b/docs/pages/developers/polkadot/ismp-parachain-runtime-api.mdx @@ -0,0 +1,25 @@ +# ISMP Parachain Runtime API + +This is required by the `ismp-parachain-inherent`. It is used to access the whitelist of sibling parachains to produce consensus proofs for. + +To use this, Include the `ismp-parachain-runtime-api` implementation in your `impl_runtime_apis` section. + +```rust showLineNumbers [runtime.rs] +use ismp_parachain_runtime_api::IsmpParachainApi; + +impl_runtime_apis! { + impl IsmpParachainApi for Runtime { + fn para_ids() -> Vec { + ismp_parachain::Pallet::::para_ids() + } + + fn current_relay_chain_state() -> RelayChainState { + ismp_parachain::Pallet::::current_relay_chain_state() + } + } +} +``` + +## Implementation + + - [ismp-parachain-runtime-api](https://github.com/polytope-labs/hyperbridge/blob/main/modules/ismp/clients/parachain/runtime-api/src/lib.rs) \ No newline at end of file diff --git a/docs/pages/developers/polkadot/rpc.mdx b/docs/pages/developers/polkadot/pallet-ismp-rpc.mdx similarity index 99% rename from docs/pages/developers/polkadot/rpc.mdx rename to docs/pages/developers/polkadot/pallet-ismp-rpc.mdx index e795f7e68..762f4ffb7 100644 --- a/docs/pages/developers/polkadot/rpc.mdx +++ b/docs/pages/developers/polkadot/pallet-ismp-rpc.mdx @@ -18,7 +18,7 @@ Query results obtained through this interface are formatted in JSON (JavaScript For offchain components that need to read the state of pallet-ismp, an rpc client is provided, this should be added to the rpc server in the substrate node. -```rust showLineNumbers +```rust showLineNumbers [rpc.rs] // RPC API Implementation for pallet-ismp /// Full client dependencies pub struct FullDeps { diff --git a/docs/pages/developers/polkadot/pallet-ismp-runtime-api.mdx b/docs/pages/developers/polkadot/pallet-ismp-runtime-api.mdx new file mode 100644 index 000000000..d6eb47693 --- /dev/null +++ b/docs/pages/developers/polkadot/pallet-ismp-runtime-api.mdx @@ -0,0 +1,65 @@ +# Pallet ISMP Runtime API + + +The `pallet-ismp-runtime-api` provides methods that allow the rpc client read the runtime state, this methods include querying requests and responses, generating proofs, among others. The runtime api can be easily added to the runtime as follows: + +```rust showLineNumbers [runtime.rs] +impl pallet_ismp_runtime_api::IsmpRuntimeApi::Hash> for Runtime { + fn host_state_machine() -> StateMachine { + ::HostStateMachine::get() + } + + fn challenge_period(state_machine_id: StateMachineId) -> Option { + pallet_ismp::Pallet::::challenge_period(state_machine_id) + } + + /// Generate a proof for the provided leaf indices + fn generate_proof( + keys: ProofKeys + ) -> Result<(Vec, Proof<::Hash>), sp_mmr_primitives::Error> { + pallet_ismp::Pallet::::generate_proof(keys) + } + + /// Fetch all ISMP events in the block, should only be called from runtime-api. + fn block_events() -> Vec<::ismp::events::Event> { + pallet_ismp::Pallet::::block_events() + } + + /// Fetch all ISMP events and their extrinsic metadata, should only be called from runtime-api. + fn block_events_with_metadata() -> Vec<(::ismp::events::Event, Option)> { + pallet_ismp::Pallet::::block_events_with_metadata() + } + + /// Return the scale encoded consensus state + fn consensus_state(id: ConsensusClientId) -> Option> { + pallet_ismp::Pallet::::consensus_states(id) + } + + /// Return the timestamp this client was last updated in seconds + fn state_machine_update_time(height: StateMachineHeight) -> Option { + pallet_ismp::Pallet::::state_machine_update_time(height) + } + + /// Return the latest height of the state machine + fn latest_state_machine_height(id: StateMachineId) -> Option { + pallet_ismp::Pallet::::latest_state_machine_height(id) + } + + + /// Get actual requests + fn requests(commitments: Vec) -> Vec { + pallet_ismp::Pallet::::requests(commitments) + } + + /// Get actual requests + fn responses(commitments: Vec) -> Vec { + pallet_ismp::Pallet::::responses(commitments) + } + } +``` + + + +## Implementation + + - [pallet-ismp-runtime-api](https://github.com/polytope-labs/hyperbridge/blob/main/modules/ismp/pallets/runtime-api/src/lib.rs) \ No newline at end of file diff --git a/docs/pages/developers/polkadot/integration.mdx b/docs/pages/developers/polkadot/pallet-ismp.mdx similarity index 53% rename from docs/pages/developers/polkadot/integration.mdx rename to docs/pages/developers/polkadot/pallet-ismp.mdx index d8bee0d88..52b4d8136 100644 --- a/docs/pages/developers/polkadot/integration.mdx +++ b/docs/pages/developers/polkadot/pallet-ismp.mdx @@ -2,151 +2,22 @@ This is the implementation of ISMP for substrate chains. It is the foundational component that allows communication over ISMP. It correctly composes the various ISMP components in the runtime. -## Pallet Config - -The Pallet has the following Config trait - -```rust showLineNumbers -#[pallet::config] -pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - /// Admin origin for privileged actions such as adding new consensus clients as well as - /// modifying existing consensus clients (eg. challenge period, unbonding period) - type AdminOrigin: EnsureOrigin; - - /// Timestamp interface [`UnixTime`] for querying the current timestamp. This is used within - /// the various ISMP sub-protocols. - type TimestampProvider: UnixTime; - - /// The balance of an account. - type Balance: Parameter - + Member - + AtLeast32BitUnsigned - + Codec - + Default - + Copy - + MaybeSerializeDeserialize - + Debug - + MaxEncodedLen - + TypeInfo - + FixedPointOperand; - - /// The currency that is offered to relayers as payment for request delivery - /// and execution. This should ideally be a stablecoin of some kind to guarantee - /// predictable and stable revenue for relayers. - /// - /// This can also be used with pallet-assets through the - /// [ItemOf](frame_support::traits::tokens::fungible::ItemOf) implementation - type Currency: Mutate; - - /// The state machine identifier for the host chain. This is the identifier that will be - /// used to accept requests that are addressed to this state machine. Remote chains - /// will also use this identifier to accept requests originating from this state - /// machine. - type HostStateMachine: Get; - - /// The coprocessor is a state machine which proxies requests on our behalf. The coprocessor - /// does this by performing the costly consensus and state proof verification needed to - /// verify requests/responses that are addressed to this host state machine. - /// - /// The ISMP framework permits the coprocessor to aggregate messages from potentially - /// multiple state machines. Finally producing much cheaper proofs of consensus and state - /// needed to verify the legitimacy of the messages. - type Coprocessor: Get>; - - /// [`IsmpRouter`] implementation for routing requests & responses to their appropriate - /// modules. - type Router: IsmpRouter + Default; - - /// This should provide a list of [`ConsenusClient`](ismp::consensus::ConsensusClient)s - /// which should be used to validate incoming requests or responses. There should be - /// at least one consensus client present to allow messages be processed by the ISMP - /// subsystems. - type ConsensusClients: ConsensusClientProvider; - - /// This implementation should provide the weight consumed by `IsmpModule` callbacks from - /// their benchmarks. - type WeightProvider: WeightProvider; - - /// Merkle mountain range overlay tree implementation. Outgoing requests and responses are - /// inserted in this "overlay tree" to enable cheap proofs for messages. - /// - /// State machines that do not need this can simply use the `NoOpMmrTree` - type Mmr: MerkleMountainRangeTree; -} -``` - -## Interfaces - -The `pallet-ismp` implements the neccessary interfaces for the ISMP framework. These are: - - - [`IsmpHost`](/protocol/ismp/host): Pallet ISMP implements `IsmpHost` interface providing all the storage and cryptographic requirements for the ISMP handlers. Modules that need to interact with the low-level ISMP framework can use this interface to access the necessary storage items they wish to read. - - :::danger[IMPORTANT] - It is **not recommended** for modules to alter the storage items directly, unless you absolutely know what you are doing. - ::: - - - [`IsmpDispatcher`](/protocol/ismp/dispatcher): It implements `IsmpDispatcher` allowing it to dispatch requests and responses. This is the low-level ISMP framework dispatcher. It can be used to dispatch requests that are not addressed to Hyperbridge and perhaps meant for other state machines. Dispatching requests to be Hyperbridge should be done throught the [`pallet-hyperbridge`](/developers/polkadot/fees#pallet-hyperbridge) module. Which also implements the `IsmpDispatcher` interface but collects the necessary fees. - - :::danger[IMPORTANT] - Requests/Responses that are not dispatched by `pallet-hyperbridge` will not be processed by Hyperbridge. They will fail transaction pool validation and will not be included in blocks. - ::: - - -## Calls - -* `create_consensus_client` -This is a priviledged call used to initialize the consensus state of a consensus client. Consensus clients must to be initialized with a trusted state, so this call must only be called by a trusted party. - -* `update_consensus_state` -This is a priviledged call used to update the unbonding period or challenge_period for a consensus client. It must only be called by a trusted parties to prevent consensus exploits. - -* `handle_unsigned` -Execute the provided batch of ISMP messages, this will short-circuit and revert if any of the provided messages are invalid. This is an unsigned extrinsic that permits anyone -execute ISMP messages for free, provided they have valid proofs and the messages havenot been previously processed. -The dispatch origin for this call must be an unsigned one. -Emits different message events based on the Message received if successful. -Only available when the pallet is built with the `unsigned` feature flag. - -* `handle` -Execute the provided batch of ISMP messages. This call will short-circuit and revert if any of the provided messages are invalid. -The dispatch origin for this call must be a signed one. -Emits different message events based on the Message received if successful. -Only available when the `unsigned` feature flag is disabled. - -* `fund_message` -During periods of high transaction fees on the destination chain, you can increase the relayer fee for in-flight requests and responses to incentivize their delivery. -Simply call this function with the request/response commitment and the desired fee increase amount. -Should not be called on a message that has been completed (delivered or timed-out) as those funds will be lost forever. - -## Transaction fees - -Hyperbridge offers a cost-effective approach to delivering ISMP messages: - - - `No Fees for Valid Messages`: Hyperbridge itself doesn't charge any transaction fees for delivering valid ISMP messages to the chain. This reduces the overall cost burden for users. - - - `Fees Paid on Source Chain`: Protocol fees are collected on the source chain (where the message originates) before it's submitted. This upfront payment system ensures that every delivered message has been "paid for" before valid proofs can be generated. - -Malformed messages or those with invalid proofs are filtered out by the transaction pool validation logic preventing unnecessary processing and potential network congestion. - -On parachains and solochains integrating ISMP transaction fees will be collected as the `handle_unsigned` extrinsic is disabled by default. - ## Runtime Integration Including `pallet-ismp` in a substrate runtime requires implementing the pallet config. -```rust showLineNumbers +```rust showLineNumbers [runtime.rs] parameter_types! { - // The hyperbridge parachain on Polkadot + // For example, the hyperbridge parachain on Polkadot pub const Coprocessor: Option = Some(StateMachine::Polkadot(3367)); - // The host state machine of this pallet - pub const HostStateMachine: StateMachine = StateMachine::Polkadot(1000); // your paraId here + // The host state machine of this pallet, your state machine id goes here + pub const HostStateMachine: StateMachine = StateMachine::Polkadot(1000); // polkadot + // pub const HostStateMachine: StateMachine = StateMachine::Kusama(1000); // kusama + // pub const HostStateMachine: StateMachine = StateMachine::Substrate(*b"MYID"); // solochain } impl pallet_ismp::Config for Runtime { - // configure the runtime event + // Configure the runtime event type RuntimeEvent = RuntimeEvent; // Permissioned origin who can create or update consensus clients type AdminOrigin = EnsureRoot; @@ -155,6 +26,7 @@ impl pallet_ismp::Config for Runtime { // The pallet_timestamp pallet type TimestampProvider = Timestamp; // The currency implementation that is offered to relayers + // this could also be `frame_support::traits::tokens::fungible::ItemOf` type Currency = Balances; // The balance type for the currency implementation type Balance = Balance; @@ -169,7 +41,7 @@ impl pallet_ismp::Config for Runtime { ); // Optional merkle mountain range overlay tree, for cheaper outgoing request proofs. // You most likely don't need it, just use the `NoOpMmrTree` - type Mmr = NoOpMmrTree; + type Mmr = pallet_ismp::NoOpMmrTree; // Weight provider for local modules type WeightProvider = (); } @@ -184,23 +56,31 @@ construct_runtime! { Let's go through some of the ISMP specific components of the configuration. -* `HostStateMachine` +* `HostStateMachine`: This is the state machine identifier for your chain, it will be used as the **source value for all requests that are dispatched from this chain** For parachains, this should be your parachain id e.g `StateMachine::Polkadot(1000)`. +
-* `Coprocessor` +* `Coprocessor`: ISMP is built around the idea of a coprocessor that aggregates consensus and state proofs from multiple state machines into a more succinct proof that is cheaply verifiable. This component defines the state machine identifier of the supported coprocessor, Hyperbridge is a coprocessor for ISMP. +
-* `ConsensusClients` +* `ConsensusClients`: This is a tuple of types that implement the `ConsensusClient` interface, it defines all the consensus algorithms supported by this deployment of the protocol. +
-* `Mmr` +* `Mmr`: This type allows us to use mmr tree as an overlay for cheaper proofs for requests and responses instead of the merkle patricia trie proofs. +
-* `Router` +* `Router`: The router is a type that provides an `IsmpModule` implementation for a module id. -```rust showLineNumbers +### Router + +The `IsmpRouter` is a module which produces an `IsmpModule` implementation for a given module identifier. + +```rust showLineNumbers [runtime.rs] #[derive(Default)] struct Router; @@ -244,9 +124,11 @@ impl IsmpModule for YourModule { } ``` -`WeightProvider`: This type allows providing the static benchmarks for all ismp modules, it should identify modules by their id and return the weights for each `IsmpModule` callback +### WeightProvider -```rust showLineNumbers +This type allows providing the static benchmarks for all ismp modules, it should identify modules by their id and return the weights for each `IsmpModule` callback + +```rust showLineNumbers [runtime.rs] struct YourModuleBenchmarks; impl pallet_ismp::weights::IsmpModuleWeight for YourModuleBenchmarks { @@ -279,72 +161,91 @@ impl pallet_ismp::WeightProvider for ModuleWeightProvider { } ``` -### Runtime API - -`pallet-ismp-runtime-api` provides methods that allow the rpc client read the runtime state, this methods include querying requests and responses, generating proofs, among others. The runtime api can be easily added to the runtime as follows: - -```rust showLineNumbers -impl pallet_ismp_runtime_api::IsmpRuntimeApi::Hash> for Runtime { - fn host_state_machine() -> StateMachine { - ::HostStateMachine::get() - } - - fn challenge_period(state_machine_id: StateMachineId) -> Option { - Ismp::challenge_period(state_machine_id) - } - - /// Generate a proof for the provided leaf indices - fn generate_proof( - keys: ProofKeys - ) -> Result<(Vec, Proof<::Hash>), sp_mmr_primitives::Error> { - Ismp::generate_proof(keys) - } - - /// Fetch all ISMP events in the block, should only be called from runtime-api. - fn block_events() -> Vec<::ismp::events::Event> { - Ismp::block_events() - } - - /// Fetch all ISMP events and their extrinsic metadata, should only be called from runtime-api. - fn block_events_with_metadata() -> Vec<(::ismp::events::Event, Option)> { - Ismp::block_events_with_metadata() - } - - /// Return the scale encoded consensus state - fn consensus_state(id: ConsensusClientId) -> Option> { - Ismp::consensus_states(id) - } - - /// Return the timestamp this client was last updated in seconds - fn state_machine_update_time(height: StateMachineHeight) -> Option { - Ismp::state_machine_update_time(height) - } - - /// Return the latest height of the state machine - fn latest_state_machine_height(id: StateMachineId) -> Option { - Ismp::latest_state_machine_height(id) - } - - - /// Get actual requests - fn requests(commitments: Vec) -> Vec { - Ismp::requests(commitments) - } - - /// Get actual requests - fn responses(commitments: Vec) -> Vec { - Ismp::responses(commitments) - } - } -``` -While ISMP can be used independently, connecting to hyperbridge provides access to all its connected chains. In the next sections we'll look into how you can integrate with hyperbridge as a parachain or solochain. +## Interfaces + +The `pallet_ismp::Pallet` implements the neccessary interfaces for the ISMP framework. These are: + + - [`IsmpHost`](/protocol/ismp/host): Pallet ISMP implements `IsmpHost` interface providing all the storage and cryptographic requirements for the ISMP handlers. Modules that need to interact with the low-level ISMP framework can use this interface to access the necessary storage items they wish to read. + + :::danger[IMPORTANT] + It is **not recommended** for 3rd-party modules to alter the storage items directly, unless you absolutely know what you are doing. + ::: + + - [`IsmpDispatcher`](/protocol/ismp/dispatcher): It implements `IsmpDispatcher` allowing it to dispatch requests and responses. This is the low-level ISMP framework dispatcher. It can be used to dispatch requests that are not addressed to Hyperbridge and perhaps meant for other state machines. Dispatching requests to be Hyperbridge should be done throught the [`pallet-hyperbridge`](/developers/polkadot/fees#pallet-hyperbridge) module. Which also implements the `IsmpDispatcher` interface but collects the necessary fees. + + :::danger[IMPORTANT] + Once the Hyperbridge token goes live, requests or responses that are not dispatched by `pallet-hyperbridge` will not be processed by Hyperbridge. They will fail transaction pool validation and will not be included in blocks. + ::: + + +## Calls + +* ### `create_consensus_client` +This is a priviledged call used to initialize the consensus state of a consensus client. Consensus clients must to be initialized with a trusted state, so this call must only be called by a trusted party. +
+ + +* ### `update_consensus_state` +This is a priviledged call used to update the unbonding period or challenge_period for a consensus client. It must only be called by a trusted parties to prevent consensus exploits. +
+ +* ### `handle_unsigned` +Execute the provided batch of ISMP messages, this will short-circuit and revert if any of the provided messages are invalid. This is an unsigned extrinsic that permits anyone +execute ISMP messages for free, provided they have valid proofs and the messages havenot been previously processed. +The dispatch origin for this call must be an unsigned one. +Emits different message events based on the Message received if successful. +Only available when the pallet is built with the `unsigned` feature flag. +
+ +* ### `handle` +Execute the provided batch of ISMP messages. This call will short-circuit and revert if any of the provided messages are invalid. +The dispatch origin for this call must be a signed one. +Emits different message events based on the Message received if successful. +Only available when the `unsigned` feature flag is disabled. +
+ +* ### `fund_message` +During periods of high transaction fees on the destination chain, you can increase the relayer fee for in-flight requests and responses to incentivize their delivery. +Simply call this function with the request/response commitment and the desired fee increase amount. +Should not be called on a message that has been completed (delivered or timed-out) as those funds will be lost forever. + +## Transaction fees + +Pallet ISMP offers a two different approaches to transaction fees. + +### Unsigned + +This essentially means all cross-chain messages received are executed for free as unsigned transactions. The upside to this is that it cannot be exploited as a spam vector, since the transaction pool will check if the submitted extrinsics are valid before they are included in the pool. This validity check ensures that the transaction can be successfully executed and contains valid proofs. Malformed messages or those with invalid proofs are filtered out by the transaction pool validation logic preventing unnecessary processing and potential network congestion. + +### Signed + +In this method, relayers and users will need to pay the native token for executing cross-chain messages. This is likely more preferrable but requires that the token be widely available. + + +## Miscellaneous + +### Offchain Indexing + +The `pallet-ismp` only stores "commitments" of hashes of requests onchain for storage proofs, while the full requests are stored offchain and using the [offchain indexing api](https://docs.rs/sp-io/38.0.0/sp_io/offchain/index.html). It would be prudent to enable offchain indexing by default in the node, so all nodes on the network store all requests offchain. You can do this in your `run` function in `command.rs`. [Here's an example](https://github.com/polytope-labs/hyperbridge/blob/main/parachain/node/src/command.rs#L168-L170) + +```rust showLineNumbers [command.rs] +/// Parse command line arguments into service configuration. +pub fn run() -> Result<()> { + let mut cli = Cli::from_args(); + + // all full nodes should store request/responses, otherwise they'd basically be useless without + // it. + cli.run.base.offchain_worker_params.indexing_enabled = true; // [!code hl] + // .. other stuff +} +``` ### Signed Extensions The teseract messaging relayer expects the following signed extensions to be present in the runtime in the same order listed below -```rust +```rust showLineNumbers [runtime.rs] /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( frame_system::CheckNonZeroSender, @@ -362,4 +263,3 @@ pub type SignedExtra = ( ## Implementation - [pallet-ismp](https://github.com/polytope-labs/hyperbridge/blob/main/modules/ismp/pallets/pallet/src/lib.rs) - - [pallet-ismp-runtime-api](https://github.com/polytope-labs/hyperbridge/blob/main/modules/ismp/pallets/runtime-api/src/lib.rs) diff --git a/docs/pages/developers/polkadot/parachains.mdx b/docs/pages/developers/polkadot/parachains.mdx index 01a160180..a2f27267d 100644 --- a/docs/pages/developers/polkadot/parachains.mdx +++ b/docs/pages/developers/polkadot/parachains.mdx @@ -1,69 +1,10 @@ # Parachain Integration -Parachains that want to leverage the Hyperbridge protocol for secure cross-chain interoperability can do so by integrating the `ismp-parachain` pallet. This pallet empowers parachains to interoperate with whitelisted sibling parachains through the ISMP framework. Here's how it works: +Parachains that want to leverage the Hyperbridge protocol for secure cross-chain interoperability can do so by integrating the `ismp-parachain` pallet. This pallet empowers parachains to interoperate with whitelisted sibling parachains through the ISMP framework. - - **Registration** : A parachain can register the IDs of sibling parachains whose state commitments it wants to learn. - - **Inherent Mechanism** : The functionality relies on an inherent provider and inherent extrinsics: - * **Whitelisted Parachains** : This inherent provider reads the `ismp-parachain` pallet for a list of currently whitelisted parachain IDs. - * **State Proof Fetching** : The provider then retrieves the latest headers from the relay chain for each supported parachain. Additionally, it fetches state proofs for these headers, which act as cryptographic evidence of the header's validity. - * **Consensus Message Submission** : Finally, the provider submits a consensus message as an inherent extrinsic. This message includes the retrieved state proofs. - * **Verification** : The `ParachainConsensusClient`, integrated with `pallet-ismp`, verifies the submitted consensus message and its proofs. +## Runtime Integration -## Pallet Config - -The pallet has the following configuration - -```rust showLineNumbers -#[pallet::config] -pub trait Config: - frame_system::Config - + pallet_ismp::Config - + cumulus_pallet_parachain_system::Config -{ - /// The overarching event type - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - /// The underlying [`IsmpHost`] implementation - type IsmpHost: IsmpHost + Default; -} -``` -## Consensus Updates via Inherents - -This pallet leverages the `ProvideInherent` functionality to submit consensus update messages. This approach offers a significant advantage: - -**Eliminating Off-chain Consensus Relayer** : By utilizing inherent messages for receiving consensus messages about finalized parachain block headers, the need for a separate off-chain consensus relayer is eliminated. - -**Benefits** : - - **Simplified Architecture** : This reduces the overall system complexity by removing an external component (the off-chain consensus relayer). - - **Improved Efficiency** : Inherents are a built-in mechanism within the polkadot-sdk, allowing the collator to act as the consensus relayer. - -## Calls - -* `update_parachain_consensus` -This is an inherent call that is used by the collator to include the consensus update message for the parachain consensus client. - -* `add_parachain` -This call allows a priviledged origin to add a new parachain to the list of supported parachains. whenever a new parachain is added, the inherent provider will add state proofs of the parachain's latest header in subsequent consensus messages. - -* `remove_parachain` -This priviledged call removes a parachain from the list of supported parachains, thereby preventing any future state updates from such parachain. - -## LifeCycle - -### OnInitialize - -In the OnInitialize hook, the consensus state for the parachain consensus client will be initialized if it does not exist. - -### OnFinalize - -This hook is used to read the current relay chain state in order to get the state root of the finalized relay chain block, so it can be used to verify subsequent parachain consensus update messages. - -## Integration - -For a parachain to establish a connection with Hyperbridge and leverage its message-passing capabilities, two key steps are required: - -**Include pallet-ismp** : The first step involves integrating the pallet-ismp module into the parachain's runtime environment as described in a previous section. This module provides the foundational functionalities for ISMP message handling. -**Include ismp-parachain** : Subsequently, the parachain needs to include the ismp-parachain pallet. This additional module grants the parachain access to the state commitments of whitelisted parachains. Access to these commitments is essential for verifying the validity of messages that the parachain intends to send or receive to/from Hyperbridge. +To establish a connection with Hyperbridge and utilize its message-passing capabilities, a parachain must complete three essential steps. First, it must integrate the `pallet-ismp` module into its runtime environment, providing the core functionalities for ISMP message handling. Next, the parachain must include the `ismp-parachain` pallet, which provides a parachain consensus client for verifying proofs of finalized parachain headers. Finally, the parachain must whitelist the Hyperbridge parachain on the `ismp-parachain` pallet. ### Parachain Consensus Client @@ -100,81 +41,36 @@ construct_runtime! { } ``` -Include the `ismp-parachain-runtime-api` implementation in your `impl_runtime_apis` section. +### Whitelisting Parachains -```rust showLineNumbers -impl_runtime_apis! { - impl ismp_parachain_runtime_api::IsmpParachainApi for Runtime { - fn para_ids() -> Vec { - IsmpParachain::para_ids() - } - - fn current_relay_chain_state() -> RelayChainState { - IsmpParachain::current_relay_chain_state() - } - } -} -``` +To use the parachain consensus client to communicate with sibling parachains, Hyperbridge included. You will need to first whitelist these parachains using the `ismp_parachain::Call::::add_parachain` extrinsic. Once added consenus relayers like the ismp parachain inherent will begin including finality proofs of these parachains in your runtime. Opening the doors of secure cross-chain communication. -### Inherent Provider + -The inherent provider needs to be added to the collator params as shown in the code below -```rust showLineNumbers -fn start_consensus( - client: Arc, - backend: Arc, - block_import: ParachainBlockImport, - prometheus_registry: Option<&Registry>, - telemetry: Option, - task_manager: &TaskManager, - relay_chain_interface: Arc, - transaction_pool: Arc>, - sync_oracle: Arc>, - keystore: KeystorePtr, - relay_chain_slot_duration: Duration, - para_id: ParaId, - collator_key: CollatorPair, - overseer_handle: OverseerHandle, - announce_block: Arc>) + Send + Sync>, -) { - // .. omitted calls - - let (client_clone, relay_chain_interface_clone) = - (client.clone(), relay_chain_interface.clone()); - let params = lookahead::Params { - create_inherent_data_providers: move |parent, ()| { - let client = client_clone.clone(); - let relay_chain_interface = relay_chain_interface_clone.clone(); - async move { - let inherent = ismp_parachain_inherent::ConsensusInherentProvider::create( - parent, - client, - relay_chain_interface, - ).await?; - - Ok(inherent) - } - }, - ..Default::default() - // omitted fields - }; - - // ..omitted calls -} -``` +## Calls + +* ### `update_parachain_consensus` +This is an inherent call that is used by the collator to include the consensus proofs for newly finalized parachain headers. + +* ### `add_parachain` + +This call allows a priviledged origin to add a new parachain to the list of supported parachains. whenever a new parachain is added, the inherent provider will add state proofs of the parachain's latest header in subsequent consensus messages. -### Configure the Coprocessor +* ### `remove_parachain` +This priviledged call removes a parachain from the list of supported parachains, thereby preventing any future state updates from such parachain. + +## Hooks -Integrating your parachain with Hyperbridge is a straightforward process. Here's what you need to do: +### on_initialize -**Add Hyperbridge's Parachain ID** : Include Hyperbridge's unique parachain identifier (ID), which is `3367`, in the list of supported parachain IDs within the `ismp-parachain` pallet. This grants your parachain the ability to communicate with Hyperbridge.You can do this by executing the `add_parachain` call with the admin origin setup in `palet-ismp`. This action officially adds Hyperbridge to your list of supported parachains. +In the OnInitialize hook, the consensus state for the parachain consensus client will be initialized if it does not exist. -Once these steps are completed, your parachain will be able to send and receive messages to and from Hyperbridge, enabling seamless cross-chain communication. +### on_finalize + +This hook is used to read the current relay chain state in order to get the state root of the finalized relay chain block, so it can be used to verify subsequent parachain consensus update messages. ## Implementation - [ismp-parachain](https://github.com/polytope-labs/hyperbridge/blob/main/modules/ismp/clients/parachain/client/src/lib.rs) - - [ismp-parachain-inherent](https://github.com/polytope-labs/hyperbridge/blob/main/modules/ismp/clients/parachain/inherent/src/lib.rs) - - [ismp-parachain-runtime-api](https://github.com/polytope-labs/hyperbridge/blob/main/modules/ismp/clients/parachain/runtime-api/src/lib.rs) diff --git a/docs/pages/developers/polkadot/solochains.mdx b/docs/pages/developers/polkadot/solochains.mdx index da5975f56..b9fc5b180 100644 --- a/docs/pages/developers/polkadot/solochains.mdx +++ b/docs/pages/developers/polkadot/solochains.mdx @@ -1,63 +1,170 @@ # Solochain Integration -For a solochain that wants to integrate with hyperbridge all that is required is to configure either the BEEFY consensus client or GRANDPA consensus client and set hyperbridge as the coprocessor. +For solochains that want to integrate with hyperbridge a GRANDPA consensus client is provided as a way to track the finality of the Hyperbridge parachain through Polkadot. A consensus relayer is also provided, which is responsible for exchanging finality proofs of cross-chain messages. + +## Runtime Integration + +In your runtime, you should configure Hyperbridge as the coprocessor and add a GRANDPA consensus client to the list of consensus clients. The host state machine should be assigned a unique value for each solochain connected to Hyperbridge. + +Every other configuration detail remains unchanged as described in the previous sections + +```rust showLineNumbers [runtime.rs] +parameter_types! { + // The hyperbridge parachain on Polkadot + pub const Coprocessor: Option = Some(StateMachine::Polkadot(3367)); + // The host state machine of this pallet, this must be unique to all every solochain + pub const HostStateMachine: StateMachine = StateMachine::Substrate(*b"solo"); // your unique chain id here +} + +impl pallet_ismp::Config for Runtime { + // ... + type Coprocessor = Coprocessor; + // Supported consensus clients + type ConsensusClients = ( + // Add the grandpa consensus client here + ismp_grandpa::GrandpaConsensusClient, + ); + // ... +} + +construct_runtime! { + // ... + Ismp: pallet_ismp + IsmpGrandpa: ismp_grandpa +} +``` + +### Whitelisting State Machines + +The `ismp-grandpa` pallet requires state machines to be whitelisted before any consensus proofs can be accepted, Hyperbridge included. This ensures that solochains are not spammed with consensus proofs of unwanted chains. + + + +## Calls + +* ### `add_state_machines` + +This adds new state machines which produce GRANDPA consensus proofs to the pallet whitelist, enabling trustless cross-chain communication with the state machines in question. + +* ### `remove_state_machines` + +This removes the provided state machines from the pallet whitelist, terminating the trustless connection with the state machines in question. They can always be re-added. + ## Consensus Relayer -A solochain requires a consensus relayer to send consensus updates between Hyperbridge and itself. Tesseract consensus relayer is built to handle this. +Solochains require an offchain consensus relayer to exchange consensus proofs with Hyperbridge. The Tesseract consensus relayer is built to handle this. This version of the relayer, is different from the publicly available messaging relayer and is closed-source at this time, but is available as a docker image. + +### Docker -## Beefy or Grandpa +You can pull in the tesseract consensus relayer like so -Choosing between Beefy and GRANDPA is just a matter of preference, BEEFY is a much leaner consensus client to maintain with a little finality lag since it piggy packs on top of GRANDPA. -A GRANDPA consensus client would offer reduced latency as GRANDPA finalizes before BEEFY. +```bash +docker pull polytopelabs/tesseract-consensus:latest +``` -## Connecting to Hyperbrige as a Solochain +### Configuring the relayer -In your runtime you should add Hyperbridge as a Coprocessor and add a GRANDPA or BEEFY Consensus client to the list of supported clients, whichever client is added would determine the mode the consensus -relayer would operate. +The tesseract consensus relayer must be configured to relay consensus messages between a solochain and Hyperbridge. The configuration file is a `toml` that should look like: -The host state machine should reflect the consensus protocol of the solochain either GRANDPA or BEEFY and it must be unique to every solochain connected to Hyperbridge. -Every other configuration detail remains unchanged as described in the previous sections +```toml [consensus.toml] +# Required +[hyperbridge] +type = "grandpa" + +[hyperbridge.grandpa] +# Hyperbridge's relay chain websocket RPC +rpc = "" +# Hyperbridge's slot duration +slot_duration = 6 +# How frequently to exchange consensus proofs +consensus_update_frequency = 60 +# Hyperbridge's paraId on the provided relay chain +# For Paseo Testnet: para_ids = [4009] +# For Polkadot Mainnet: para_ids = [3367] +para_ids = [] + +[hyperbridge.substrate] +# Hyperbridge's relay chain websocket RPC +rpc_ws = "" +# Hyperbridge's hashing algorithm +hashing = "Keccak" +# Hyperbridge's consensus state id +# For Paseo Testnet: PAS0 +# For Polkadot Mainnet: DOT0 +consensus_state_id = "" +# Hyperbridge's state machine ID +# For Paseo Testnet: KUSAMA-4009 +# For Polkadot Mainnet: POLKADOT-3367 +state_machine = "" + +# can use any key here +[YourSolochain] +type = "grandpa" + +[YourSolochain.substrate] +# Solochains's websocket RPC +rpc_ws = "" +# Hashing can be "Keccak" or "Blake2" +hashing = "Blake2" +# Solochains's consensus state id on Hyperbridge +# should be 4 utf-8 chars chosen by solochain +consensus_state_id = "" +# Solochains's state machine id. eg +state_machine = "SUBSTRATE-myid" -```rust showLineNumbers - parameter_types! { - // The hyperbridge parachain on Polkadot - pub const Coprocessor: Option = Some(StateMachine::Polkadot(3367)); - // The host state machine of this pallet, this must be unique to all every solochain - pub const HostStateMachine: StateMachine = StateMachine::Grandpa(*b"solo"); // your unique chain id here - } - - impl pallet_ismp::Config for Runtime { - // configure the runtime event - type RuntimeEvent = RuntimeEvent; - // Permissioned origin who can create or update consensus clients - type AdminOrigin = EnsureRoot; - // The state machine identifier for this state machine - type HostStateMachine = HostStateMachine; - // The pallet_timestamp pallet - type TimestampProvider = Timestamp; - // The currency implementation that is offered to relayers - type Currency = Balances; - // The balance type for the currency implementation - type Balance = Balance; - // Router implementation for routing requests/responses to their respective modules - type Router = Router; - // Optional coprocessor for incoming requests/responses - type Coprocessor = Coprocessor; - // Supported consensus clients - type ConsensusClients = ( - // Add the grandpa or beefy consensus client here - ismp_grandpa::GrandpaConsensusClient, - ); - // Optional merkle mountain range overlay tree, for cheaper outgoing request proofs. - // You most likely don't need it, just use the `NoOpMmrTree` - type Mmr = NoOpMmrTree; - // Weight provider for local modules - type WeightProvider = (); - } - - construct_runtime! { - // ... - Ismp: pallet_ismp - } +[YourSolochain.grandpa] +# Solochains's websocket RPC +rpc = "" +# Solochains's slot duration +slot_duration = 6 +# How frequently to exchange consensus proofs +consensus_update_frequency = 60 +# Any para ids to prove if solochain is actually a relay chain +para_ids = [] ``` + +### Consensus State Initialization + +Before running the relayer, you must first initialize Hyperbridge's consensus state on your solochain. The consensus relayer can query Hyperbridge to retrieve this initial state for you. For instance, here's how you can log the consensus state of the Hyperbridge testnet on Paseo: + +```bash +docker run -v /path/to/consensus.toml:/root/consensus.toml polyopelabs/tesseract-consensus:latest --config=/root/consensus.toml log-consensus-state=KUSAMA-4009 +``` + +This should print out a potentially long hex string. Next you'll use this hex string to initialize the Hyperbridge consensus state on your solochain through an extrinsic as shown below. + + + +- **`consensusState`**: This is the initial, trusted consensus state for the Hyperbridge consensus on your solochain. It contains the current and next validator set keys, as well as the latest finalized block. It is the value printed by the `log-consensus-state` subcommand on the tesseract consensus relayer. + +- **`consensusClientId`**: This is the consensus client identifier for the GRANDPA consensus client. It will always be `GRNP` + +- **`consensusStateId`**: This is the consensus state id for Hyperbridge on your solochain, refer to the configuration section on what this value should be. + +- **`unbondingPeriod`**: This is the unbonding period for the Hyperbridge relay chain and is used to [mitigate long fork attacks](/protocol/interoperability/consensus-proofs#long-range-attacks). + +- **`challengePeriod`**: This is period you want state commitments to stay pending for, even after they have been verified by the consensus client, allowing fishermen to check for byzantine behaviour and submit fraud proofs, if any. Polkadot has high economic security which disincentivizes this sort of behaviour so for Hyperbridge, you can leave set this to zero. + + +Once this is completed on your solochain, the same will need to also be executed on Hyperbridge. In similar fashion you can log the initial consensus of your solochain and [create an issue on our github](https://github.com/polytope-labs/hyperbridge/issues/new) to be added to the Hyperbridge testnet. On Hyperbridge mainnet, you'll instead submit a governance proposal for this. + +### Running the relayer + +Once all consensus states are set up, running the relayer is as easy as: + +```bash +docker run -v /path/to/consensus.toml:/root/consensus.toml polyopelabs/tesseract-consensus:latest --config=/root/consensus.toml log-consensus-state=KUSAMA-4009 +``` + +You will of course need to pair this with the [messaging relayer](/developers/network/relayer) which actually relays cross-chain messages. + +## Miscallenous + +Some random things to note + +### Slot Numbers + +The GRANDPA consensus client relies on digest items in the header to communicate the current slot number. These digest items must be identified as either `BABE` or `AURA`. Solochains using custom block production algorithms must include the `AURA` digest item to ensure compatibility with the GRANDPA consensus client for producing valid consensus proofs. + + diff --git a/docs/pages/protocol/consensus/casper-ffg.mdx b/docs/pages/protocol/consensus/casper-ffg.mdx index d60514ce0..34ac84951 100644 --- a/docs/pages/protocol/consensus/casper-ffg.mdx +++ b/docs/pages/protocol/consensus/casper-ffg.mdx @@ -115,7 +115,7 @@ $$ L(x) = \sum^{n-1}_{i = 0} \mathcal{L}_i(x) $$ -This allows us create a KZG commitment to this polynomial $\textmd{g}^{L(s)}$ as part of the KZG set up ceremony. Since we know that $L(x_i) - \mathcal{L}_i(x_i) = 0$, thus the prover can can compute a KZG proof for the $x_i$-th coordinate using the quotient. +This allows us create a KZG commitment to this polynomial $\textmd{g}^{L(s)}$ as part of the KZG set up ceremony. Since we know that $L(x_i) - \mathcal{L}_i(x_i) = 0$, thus the prover can compute a KZG proof for the $x_i$-th coordinate using the quotient. $$ \psi(x) = \frac{L(x) - \mathcal{L}_i(x_i)}{(x - x_i)} diff --git a/docs/pages/protocol/consensus/sync-committee.mdx b/docs/pages/protocol/consensus/sync-committee.mdx index 58a91b7bc..7b91fbf8c 100644 --- a/docs/pages/protocol/consensus/sync-committee.mdx +++ b/docs/pages/protocol/consensus/sync-committee.mdx @@ -10,7 +10,7 @@ import Algorithm from '../../../components/Algorithm'; This technical specification assumes that you’re already aware of [the sync committee protocol](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#introduction) introduced in altair, the first hard fork of the ethereum beacon chain. If not, tl;dr: The original attestation protocol unfortunately did not include succint BLS public key aggregation, which would’ve made it cheap to verify by light clients given that there are now almost [500k authorities](https://beaconscan.com/) actively validating blocks on the beacon chain. -This hypothetical succint BLS public key aggreation scheme, would’ve allowed us to verify the Casper FFG attestation protocol directly by aggregating all 500k validators’ public keys into a smaller set of BLS public keys. But unfortunately, it doesn’t exist. +This hypothetical succicnt BLS public key aggregation scheme, would’ve allowed us to verify the Casper FFG attestation protocol directly by aggregating all 500k validators’ public keys into a smaller set of BLS public keys. But unfortunately, it doesn’t exist. Some napkin math reveals: diff --git a/docs/public/add_parachain.png b/docs/public/add_parachain.png new file mode 100644 index 000000000..8f2c5e0c3 Binary files /dev/null and b/docs/public/add_parachain.png differ diff --git a/docs/public/add_state_machine.png b/docs/public/add_state_machine.png new file mode 100644 index 000000000..ac4f26eac Binary files /dev/null and b/docs/public/add_state_machine.png differ diff --git a/docs/public/grnp_init.png b/docs/public/grnp_init.png new file mode 100644 index 000000000..e734cc6d8 Binary files /dev/null and b/docs/public/grnp_init.png differ diff --git a/docs/vocs.config.tsx b/docs/vocs.config.tsx index e2e96b111..5ea7f7b14 100644 --- a/docs/vocs.config.tsx +++ b/docs/vocs.config.tsx @@ -337,7 +337,7 @@ export default defineConfig({ link: "/developers/evm/getting-started", }, { - text: "Dispatching", + text: "Dispatching Messages", link: "/developers/evm/dispatching", }, { @@ -345,7 +345,7 @@ export default defineConfig({ link: "/developers/evm/fees", }, { - text: "Receiving", + text: "Receiving Messages", link: "/developers/evm/receiving", }, { @@ -353,11 +353,11 @@ export default defineConfig({ collapsed: false, items: [ { - text: "Mainnet Addresses", + text: "Mainnet", link: "/developers/evm/contracts/mainnet", }, { - text: "Testnet Addresses", + text: "Testnet", link: "/developers/evm/contracts/testnet", }, ], @@ -368,14 +368,46 @@ export default defineConfig({ text: "Polkadot Sdk", collapsed: true, items: [ + { + text: "Getting Started", + link: "/developers/polkadot/getting-started", + }, + { text: "Pallet ISMP", - link: "/developers/polkadot/integration", + link: "/developers/polkadot/pallet-ismp", + items: [ + { + text: "Runtime API", + link: "/developers/polkadot/pallet-ismp-runtime-api", + }, + { + text: "RPC Interface", + link: "/developers/polkadot/pallet-ismp-rpc", + }, + ] }, + { text: "Parachains", link: "/developers/polkadot/parachains", + items: [ + { + text: "Runtime API", + link: "/developers/polkadot/ismp-parachain-runtime-api", + }, + { + text: "Inherent Provider", + link: "/developers/polkadot/ismp-parachain-inherent", + }, + ] + }, + + + { + text: "Solochains (GRANDPA)", + link: "/developers/polkadot/solochains", }, // { @@ -384,13 +416,8 @@ export default defineConfig({ // }, { - text: "ISMP Modules", - link: "/developers/polkadot/modules", - }, - - { - text: "RPC Interface", - link: "/developers/polkadot/rpc", + text: "Dispatching Messages", + link: "/developers/polkadot/delivery", }, { @@ -399,8 +426,8 @@ export default defineConfig({ }, { - text: "Message Delivery", - link: "/developers/polkadot/delivery", + text: "Receiving Messages", + link: "/developers/polkadot/modules", }, {