From edff69aaed6960baec35a429177ae1c9b7a6a3b9 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Tue, 27 Feb 2024 09:53:24 +0100 Subject: [PATCH] Tesseract Documentation (#101) Co-authored-by: David Salami --- Cargo.lock | 17 +- Cargo.toml | 6 +- docs/package-lock.json | 10 + docs/package.json | 3 +- docs/pages/evm/integration.mdx | 20 ++ docs/pages/network/node.mdx | 6 +- docs/pages/network/relayer.mdx | 207 +++++++++++++++++++- docs/public/relayer.webp | Bin 0 -> 44832 bytes modules/ismp/clients/bsc-pos/src/lib.rs | 4 +- modules/ismp/clients/polygon-pos/src/lib.rs | 4 +- parachain/chainspec/gargantua.json | 3 +- parachain/chainspec/messier.json | 2 +- parachain/node/Cargo.toml | 2 +- parachain/node/src/cli.rs | 2 + parachain/node/src/command.rs | 1 - parachain/node/src/service.rs | 116 ++++------- scripts/zombienet/local-testnet.toml | 22 +-- 17 files changed, 297 insertions(+), 128 deletions(-) create mode 100644 docs/public/relayer.webp diff --git a/Cargo.lock b/Cargo.lock index a7572a122..080a7898e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5305,7 +5305,7 @@ dependencies = [ [[package]] name = "hyperbridge" -version = "0.3.2" +version = "0.3.3" dependencies = [ "clap", "cumulus-client-cli", @@ -10668,21 +10668,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "polygon-pos-prover" -version = "0.1.0" -dependencies = [ - "anyhow", - "dotenv", - "ethers", - "geth-primitives", - "ismp", - "polygon-pos-verifier", - "primitive-types", - "sp-core 28.0.0", - "tokio", -] - [[package]] name = "polygon-pos-verifier" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0455f6bb8..a8ab62242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ members = [ "modules/ismp/core", "modules/ismp/testsuite", "modules/ismp/clients/sync-committee", - "modules/ismp/clients/polygon-pos", +# "modules/ismp/clients/polygon-pos", "modules/ismp/clients/casper-ffg", "modules/ismp/pallet/state-machine", @@ -26,8 +26,8 @@ members = [ "modules/consensus/sync-committee/prover", "modules/consensus/sync-committee/verifier", "modules/consensus/sync-committee/primitives", - "modules/consensus/polygon-pos/verifier", - "modules/consensus/polygon-pos/prover", +# "modules/consensus/polygon-pos/verifier", +# "modules/consensus/polygon-pos/prover", "modules/consensus/beefy/primitives", "modules/consensus/beefy/prover", "modules/consensus/geth-primitives", diff --git a/docs/package-lock.json b/docs/package-lock.json index f4d1d7d5e..aac32fde5 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,6 +8,7 @@ "name": "hyperbridge", "version": "0.0.0", "dependencies": { + "i": "^0.3.7", "prettier": "^3.2.2", "react": "latest", "react-dom": "latest", @@ -526,6 +527,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { @@ -4359,6 +4361,14 @@ "node": ">=10.17.0" } }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", diff --git a/docs/package.json b/docs/package.json index 9bbc6d97a..9fdf7906b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -3,12 +3,13 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vocs dev", + "dev": "vocs dev --host", "build": "vocs build", "preview": "vocs preview", "prettier": "npm run prettier" }, "dependencies": { + "i": "^0.3.7", "prettier": "^3.2.2", "react": "latest", "react-dom": "latest", diff --git a/docs/pages/evm/integration.mdx b/docs/pages/evm/integration.mdx index e69de29bb..ac088a07e 100644 --- a/docs/pages/evm/integration.mdx +++ b/docs/pages/evm/integration.mdx @@ -0,0 +1,20 @@ +## Gargantua V1 (Deprecated) + + +| Network | `HandlerV1` | `IsmpHost` | +|:-------------------|:--------------|:-----| +|Ethereum Sepolia | [`0x577efa5c6184E10D54fdA5eB195f7ecA30644082`](https://sepolia.etherscan.io/address/0x577efa5c6184E10D54fdA5eB195f7ecA30644082) | [`0x5b5F63C8f3985CaFE1CE53E6374f42AB60dE5a6B`](https://sepolia.etherscan.io/address/0x5b5F63C8f3985CaFE1CE53E6374f42AB60dE5a6B) | +|Arbitrum Sepolia | [`0x4221D52aa25d80Bb4741D430f16f5769cA99bc58`](https://sepolia.arbiscan.io/address/0x4221D52aa25d80Bb4741D430f16f5769cA99bc58) | [`0x43E136611Cf74E165116a47e6F9C58AFCc80Ec54`](https://sepolia.arbiscan.io/address/0x43E136611Cf74E165116a47e6F9C58AFCc80Ec54) | +|Optimism Sepolia | [`0x4221D52aa25d80Bb4741D430f16f5769cA99bc58`](https://sepolia-optimism.etherscan.io/address/0x4221D52aa25d80Bb4741D430f16f5769cA99bc58) | [`0x0124f458900FCd101c4CE31A9772fD2c5e6d65BF`](https://sepolia-optimism.etherscan.io/address/0x0124f458900FCd101c4CE31A9772fD2c5e6d65BF) | +|Base Sepolia | [`0x3cfb5eE8D00c2620e0A63FD25deAA2d7a671F449`](https://sepolia.basescan.org/address/0x3cfb5eE8D00c2620e0A63FD25deAA2d7a671F449) | [`0x87825f839d95c6021c0e821917F93aDB299eD6F8`](https://sepolia.basescan.org/address/0x87825f839d95c6021c0e821917F93aDB299eD6F8) | + + +## Gargantua V2 + +| Network | `HandlerV1` | `IsmpHost` | +|:-------------------|:--------------|:-----| +|Ethereum Sepolia | [`0xa873b742084F81811693eD86C041583D37c05b43`](https://sepolia.etherscan.io/address/0xa873b742084F81811693eD86C041583D37c05b43) | [`0x9DF353352b469782AB1B0F2CbBFEC41bF1FDbDb3`](https://sepolia.etherscan.io/address/0x9DF353352b469782AB1B0F2CbBFEC41bF1FDbDb3) | +|Arbitrum Sepolia | [`0xFb9CDe6EBA0bBfA0E7ed2603Ea659E3511A98B41`](https://sepolia.arbiscan.io/address/0xFb9CDe6EBA0bBfA0E7ed2603Ea659E3511A98B41) | [`0x424e6971EB1C693cf4296d4bdb42aa0F32a0dd9e`](https://sepolia.arbiscan.io/address/0x424e6971EB1C693cf4296d4bdb42aa0F32a0dd9e) | +|Optimism Sepolia | [`0xa25151598Dc180fc03635858f37bDF8427f47845`](https://sepolia-optimism.etherscan.io/address/0xa25151598Dc180fc03635858f37bDF8427f47845) | [`0x1B58A47e61Ca7604b634CBB00b4e275cCd7c9E95`](https://sepolia-optimism.etherscan.io/address/0x1B58A47e61Ca7604b634CBB00b4e275cCd7c9E95) | +|Base Sepolia | [`0x4B0c9cDA104D131aF12280Bb074d6B456F467C47`](https://sepolia.basescan.org/address/0x4B0c9cDA104D131aF12280Bb074d6B456F467C47) | [`0x4c876500A13cc3825D343b5Ac791d3A4913bF14f`](https://sepolia.basescan.org/address/0x4c876500A13cc3825D343b5Ac791d3A4913bF14f) | +|Bsc Testnet | [`0x43a0BcC347894303f93905cE137CB3b804bE990d`](https://testnet.bscscan.com/address/0x43a0BcC347894303f93905cE137CB3b804bE990d) | [`0x022DDE07A21d8c553978b006D93CDe68ac83e677`](https://testnet.bscscan.com/address/0x022DDE07A21d8c553978b006D93CDe68ac83e677) | diff --git a/docs/pages/network/node.mdx b/docs/pages/network/node.mdx index 20a7e9e74..61b7e59b0 100644 --- a/docs/pages/network/node.mdx +++ b/docs/pages/network/node.mdx @@ -24,7 +24,7 @@ docker run polytopelabs/hyperbridge:latest --chain=messier You can install a prebuilt binary for the hyperbridge node with the following bash script ```bash -wget -q --show-progress https://github.com/polytope-labs/hyperbridge/releases/download/v0.2.0/hyperbridge-x86_64-unknown-linux-gnu.tar.gz +wget -q --show-progress https://github.com/polytope-labs/hyperbridge/releases/download/v0.3.2/hyperbridge-x86_64-unknown-linux-gnu.tar.gz tar -xvzf hyperbridge-x86_64-unknown-linux-gnu.tar.gz # copy to $PATH cp hyperbridge-x86_64-unknown-linux-gnu/hyperbridge $HOME/.local/bin/ @@ -33,7 +33,7 @@ cp hyperbridge-x86_64-unknown-linux-gnu/hyperbridge $HOME/.local/bin/ or a 1-liner shell script ```bash -curl --proto '=https' --tlsv1.2 -LsSf https://github.com/polytope-labs/hyperbridge/releases/download/v0.2.0/hyperbridge-installer.sh | sh +curl --proto '=https' --tlsv1.2 -LsSf https://github.com/polytope-labs/hyperbridge/releases/download/v0.3.2/hyperbridge-installer.sh | sh ``` ## Building from source @@ -101,7 +101,7 @@ Download a local copy of the repo and checkout the latest release tag ```bash git clone https://github.com/polytope-labs/hyperbridge.git cd ./hyperbidge -git checkout v0.2.0 +git checkout v0.3.2 ``` ### Build the node diff --git a/docs/pages/network/relayer.mdx b/docs/pages/network/relayer.mdx index 784a1c5e0..169484d3a 100644 --- a/docs/pages/network/relayer.mdx +++ b/docs/pages/network/relayer.mdx @@ -5,7 +5,38 @@ description: Running the tesseract relayer # Relayers -Critical to the function of Hyperbridge are relayers who transmit requests & responses across it's connected chains. These relayers much like node operators earn rewards for relaying requests. Since request execution fees are paid for ahead of time on the source chain by the initiating application. Relayers can relay and pay for the execution of these requests on the destination chain and redeem their fees on the source chain. This works by the relayer showing state proofs of request delivery to the hyperbridge parachain. Hyperbridge then authorizes the release of the fees to the relayer on the source chain through an ISMP request. +
+ Hyperbridge relayers +
Hyperbridge relayers
+
+ +Critical to the function of Hyperbridge are the relayers who transmit requests & responses across it's connected chains. Unlike popular interoperability protocols, Hyperbridge relayers are fully permissionless. This means, relayers will not need to be staked or whitelisted in order to transmit cross-chain messages. This is as a result of it's first-of-it's kind, fully trust-free design. This service of-course, will not be for free. Hyperbridge permits the applications that leverage it’s secure message passing infrastructure to pay ahead of time for the message delivery & execution. They do so on the source chain of the initiating application. +
+Fees for cross-chain requests are to be paid in stable coins, We’ve chosen the DAI stablecoin for it’s decentralized and censorship-resistant properties. This means users will estimate the cost for message execution & provide what they believe to be a fair amount in DAI to the Hyperbridge contract. (Specifically, the [EvmHost](https://github.com/polytope-labs/hyperbridge/blob/main/evm/src/hosts/EvmHost.sol#L533)) at the time of dispatching a request. Once the request has been finalized by the originating chain and these finality proofs made available to the destination chain. The request becomes eligible for delivery & execution. A relayer will estimate the cost of request delivery and query the fee that has been put up for it’s delivery on the source chain. Depending on their profitability configuration, they’ll either choose to deliver the request or ignore it. +
+If the relayers chooses to deliver the request, then this means they will cover the cost of message delivery and execution. This cost of message delivery is the cost of proof verification of requests & responses. The cost of message execution is the cost of executing the message on the destination module. In the current version of the protocol, relayers are tasked with both delivering and executing the messages. But it is entirely possible in future versions of the protocol that we unbundle message delivery and execution as this may enable more exotic applications. +
+In our chosen design, relayers race to deliver a batch of cross-chain messages to their respective destination chains. This has a few benefits, this effectively mitigates any liveness issues that may arise as a result of some whitelisted relayers going down. This free market design presents a much better UX to end users and applications. The winning relayer, can be decided by a few factors. +
+1. Block notification. + +Relayer are mostly idle, and will only check for new messages to be delivered when the chains they’re interested in receive new consensus messages. The consensus messages, contain proofs that finalizes a new set of block heights. The new block heights, may or may not contain new messages to be delivered from the hyperbridge parachain. Therefore, whichever relayer sees the block where these consensus messages were processed on the destination chain first, can begin the check before everyone else and as a result, deliver the new batch of requests before anyone else. + +2. Query throughput + +We can for the purposes of this criteria assume that every single relayer has received the block notification at the same time. Now they have to do a few things, first they’ll query for the new requests available, next they’ll need to query the fees associated with these requests, and finally query the cost of executing these requests by simulating them individually as a transaction on the full node for it’s destination chain. + +If the relayer is able to query high-performance nodes and has a very fast internet connection, or even better is colocated the nodes in question. You can already see how this provides an edge in being the first to deliver the requests. + +3. MEV + +An unfortunate development in blockchain protocols are validators who auction their blockspace. They do so in order to earn even more money than the underlying protocols already pay them. It’s not impossible that relayers may integrate with validators who auction their blockspace using proposer-builder networks in order to have their bundles always be first in the block. Of-course these relayers would have to pay high priority fees for such shenanigans. But we fully expect relayer competition to eventually reach these levels. +
+ +Once a relayer successfully delivers a batch of requests, they can immediately start claiming the associated fees for these requests. They’ll do so by submitting state proofs of the messages delivered on the destination chain to hyperbridge. Once hyperbridge verifies these proofs, it will make the fees available to the relayer by issuing a request to it’s contracts on the source chain. The relayer will be responsible for delivering this request as it will have no fee attached and will hence be ignored by other relayers. ## Installation @@ -13,20 +44,186 @@ The tesseract relayer is available through the following methods ### Prebuilt binaries -Unavailable at this time +Unavailable at this time. ### Docker Tesseract is available at the official docker repository [`polytopelabs/tesseract`](https://hub.docker.com/r/polytopelabs/tesseract) ```bash -docker run polytopelabs/tesseract:latest --config=/path/to/config +docker run polytopelabs/tesseract:latest --config=/path/to/config --db=/path/to/database ``` ### Building from source? -Unfortunately, tesseract is closed-souce for now. +Unfortunately, tesseract is closed-souce for now. Tesseract source-code will be available on Hyperbridge mainnet launch. + + +## System Requirements + +At the minimum, the hyperbridge relayer should be run on a machine with at least 4GB of RAM and a quad-core cpu. This relayer should also have at least a 100Mb/s connection if it is to query nodes over the internet. + ## Running the relayer -### Config +The tesseract relayer command line interface expects two arguments, which are the paths to it's configuration file and database file. Because it resides in a docker container, you will need to [map the directory](https://docs.docker.com/storage/bind-mounts/#start-a-container-with-a-bind-mount) where you've stored the configuration files on your host into the docker conatiner. Tesseract will write it's database to that directory once it's initialized. + + +```bash +docker run -v /dir/on/host:/home/root polytopelabs/tesseract:latest --config=/home/root/config.toml --db=/home/root/dev.db + +``` + +### Configuration + +The configuration file is a toml file that at the moment, expects the following configuration options: + + +```toml +# Hyperbridge config, required +[hyperbridge] +# Hyperbridge chain spec, either one of Dev, Gargantua or Messier +chain = "Dev" +# Hyperbidge node ws rpc endpoint. +rpc_ws = "ws://127.0.0.1:9944" # example endpoint + +[ethereum] +# configuration type +type = "ethereum_sepolia" +# State machine identifier +state_machine = { Ethereum = "ExecutionLayer" } +# http(s) rpc url for sepolia +rpc_url = "" +# consensus state identifier for sepolia on hyperbridge +consensus_state_id = "ETH0" +# etherscan api key for querying Ethereum token price +etherscan_api_key = "" +# Contract address of the HandlerV1 contract +handler = "" +# Contract address of the IsmpHost contract +ismp_host = "" +# hex-encoded private key for the relayer +signer = "" + +[arbitrum] +# configuration type +type = "arbitrum" +# State machine identifier +state_machine = { Ethereum = "Arbitrum" } +# http(s) rpc url for arbitrum +rpc_url = "" +# consensus state identifier for arbitrum on hyperbridge, L2s use ethereum as their consensus oracle +consensus_state_id = "ETH0" +# etherscan api key for querying Ethereum token price +etherscan_api_key = "" +# Contract address of the HandlerV1 contract +handler = "" +# Contract address of the IsmpHost contract +ismp_host = "" +# hex-encoded private key for the relayer +signer = "" + +[optimism] +# configuration type +type = "optimism" +# State machine identifier +state_machine = { Ethereum = "Optimism" } +# http(s) rpc url for optimism +rpc_url = "" +# consensus state identifier for optimism on hyperbridge, L2s use ethereum as their consensus oracle +consensus_state_id = "ETH0" +# etherscan api key for querying Ethereum token price +etherscan_api_key = "" +# Contract address of the HandlerV1 contract +handler = "" +# Contract address of the IsmpHost contract +ismp_host = "" +# hex-encoded private key for the relayer +signer = "" + +[base] +# configuration type +type = "base" +# State machine identifier +state_machine = { Ethereum = "Base" } +# http(s) rpc url for base +rpc_url = "" +# consensus state identifier for base on hyperbridge, L2s use ethereum as their consensus oracle +consensus_state_id = "ETH0" +# etherscan api key for querying Ethereum token price +etherscan_api_key = "" +# Contract address of the HandlerV1 contract +handler = "" +# Contract address of the IsmpHost contract +ismp_host = "" +# hex-encoded private key for the relayer +signer = "" + +[bsc] +# configuration type +type = "bsc" +# State machine identifier +state_machine = "Bsc" +# http(s) rpc url for binance smart chain +rpc_url = "" +# consensus state identifier for binance smart chain on hyperbridge +consensus_state_id = "BSC0" +# etherscan api key for querying BNB token price +etherscan_api_key = "" +# Contract address of the HandlerV1 contract +handler = "" +# Contract address of the IsmpHost contract +ismp_host = "" +# hex-encoded private key for the relayer +signer = "" + +# Relayer config, required +[relayer] +# Hyperbridge chain spec, either one of Dev, Gargantua or Messier +chain = "Dev" +# Define your profitability configuration. 0 -> 0% i.e relay all requests, even unprofitable ones. 1 -> 1%. ie fees provided for requests must be profitable by at least 1%. etc. +minimum_profit_percentage = 0 +# (Optional) If not empty, will filter requests to be delivered by originating module identifier (eg contract address) +module_filter = [] +# (Optional) If not empty, only deliver to the specied state-machines +delivery_endpoints = [ + { Ethereum = "ExecutionLayer" }, + { Ethereum = "Arbitrum" }, + { Ethereum = "Optimism" }, + { Ethereum = "Base" }, + "Bsc" +] +``` + +It is optional to provide the configuration option for any of the connected chains, The only consequence is your relayer will not deliver requests from the ommitted chain as it has no way of querying the associated fees for requests originating from this chain. + +You can find the up-to-date contract addresses for the `Handler` & `IsmpHost` contracts [here](/evm/integration#gargantua-v2) + +### Withdrawing Fees + +Tesseract maintains a local sqlite database where it keeps track of successfully delivered messages. The withdrawal process happens in two phases, the first phase is the fee accumulation on hyperbridge, then withdrawal on any of the connected chains. +
+In the fee accumulation phase, tesseract submits state proofs of messages all delivered to hyperbridge using the `accumulate_fees` extrinsic on the `RelayerFees` pallet. If the proof verification is successful, the total amount of unclaimed fees for the relayer address will be recorded. +
+The relayer can submit a withdrawal request from hyperbridge to any of the chains where it has unclaimed fees, an ISMP Post request with instructions to credit the relayer on the destination chain is dispatched, the request has a timeout of zero allowing it to be submitted to the destination at any time in the future without any risk of timing out. + +#### Accumulating fees on Hyperbridge +Stop any running relayer instances, run the following command in the terminal and wait for it to complete. The command will submit proofs for messages delivered to all chains present in the config file. + +```bash + docker run -v /dir/on/host:/home/root polytopelabs/tesseract:latest --config=/home/root/config.toml --db=/home/root/dev.db accumulate-fees +``` + +#### Initiating withdrawal from hyperbridge +To initiate a withdrawal from hyperbridge, a relayer needs to submit a `withdraw_fees` extrinsic from the `RelayerFees` pallet on hyperbridge, The transaction takes a `WithdrawalInputData`, this value contains, the amount to be withdrawn, the destination chain and a signature that proves ownership of the account that will be debited. +
+It is possible to use the relayer software to accumulate fees and initiate the withdrawal process with one command, for this to work, the relayer account must have sufficient funds to pay the transaction delivery fees on the withdrawal destination chains. +
+To use this feature run the following command: + +```bash + docker run -v /dir/on/host:/home/root polytopelabs/tesseract:latest --config=/home/root/config.toml --db=/home/root/dev.db accumulate-fees --withdraw +``` + +You can find the code that implements fee accumulation and withdrawals [here](https://github.com/polytope-labs/hyperbridge/blob/c3e940c60b718eaf5ac02a96c4801814a555dcf7/modules/relayer-fees/src/lib.rs#L120) + diff --git a/docs/public/relayer.webp b/docs/public/relayer.webp new file mode 100644 index 0000000000000000000000000000000000000000..b77c68f1b9a5c053e74895e73613d1c8c65c9381 GIT binary patch literal 44832 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 z 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 qt<+Q}D3E4YRV5+o%FU+>w{?3Nbgs~AEJ StateMachineClient for EvmStateMachine { } fn state_trie_key(&self, items: RequestResponse) -> Vec> { - req_res_to_key::(items) + req_res_receipt_keys::(items) } fn verify_state_proof( diff --git a/modules/ismp/clients/polygon-pos/src/lib.rs b/modules/ismp/clients/polygon-pos/src/lib.rs index 5fbc8ca55..44f6c9571 100644 --- a/modules/ismp/clients/polygon-pos/src/lib.rs +++ b/modules/ismp/clients/polygon-pos/src/lib.rs @@ -21,7 +21,7 @@ use ismp::{ messaging::{Proof, StateCommitmentHeight}, router::RequestResponse, }; -use ismp_sync_committee::{utils::req_res_to_key, verify_membership, verify_state_proof}; +use ismp_sync_committee::{utils::req_res_receipt_keys, verify_membership, verify_state_proof}; use pallet::{Config, Headers}; use polygon_pos_verifier::{ primitives::{SPAN_LENGTH, SPRINT_LENGTH}, @@ -354,7 +354,7 @@ impl StateMachineClient for EvmStateMachine { } fn state_trie_key(&self, items: RequestResponse) -> Vec> { - req_res_to_key::(items) + req_res_receipt_keys::(items) } fn verify_state_proof( diff --git a/parachain/chainspec/gargantua.json b/parachain/chainspec/gargantua.json index 0a5424b15..ef726b338 100644 --- a/parachain/chainspec/gargantua.json +++ b/parachain/chainspec/gargantua.json @@ -3,7 +3,6 @@ "id": "gargantua", "chainType": "Live", "bootNodes": [ - "/ip4/34.78.130.110/tcp/30333/p2p/12D3KooWMRRsnAgJ1hjxCj3uJXPkauHqhP2BbbP4M5XF8xVJpeLX", "/ip4/34.22.173.252/tcp/30333/p2p/12D3KooWGuMWMXSj7GUGMRdJAceRHoSuSkWW53b6nebzybUEoauB" ], "telemetryEndpoints": [ @@ -20,7 +19,7 @@ "properties": { "ss58Format": 42, "tokenDecimals": 12, - "tokenSymbol": "XOR" + "tokenSymbol": "tNAND" }, "relay_chain": "rococo", "para_id": 4374, diff --git a/parachain/chainspec/messier.json b/parachain/chainspec/messier.json index 44a613fac..900d3dc02 100644 --- a/parachain/chainspec/messier.json +++ b/parachain/chainspec/messier.json @@ -10,7 +10,7 @@ "properties": { "ss58Format": 42, "tokenDecimals": 12, - "tokenSymbol": "CORE" + "tokenSymbol": "NAND" }, "relay_chain": "kusama", "para_id": 3340, diff --git a/parachain/node/Cargo.toml b/parachain/node/Cargo.toml index 7a0052906..b950c801b 100644 --- a/parachain/node/Cargo.toml +++ b/parachain/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hyperbridge" -version = "0.3.2" +version = "0.3.3" authors = ["Polytope Labs "] description = "The Hyperbridge coprocessor node" repository = "https://github.com/polytope-labs/hyperbridge" diff --git a/parachain/node/src/cli.rs b/parachain/node/src/cli.rs index 0bf4651fc..238616ca0 100644 --- a/parachain/node/src/cli.rs +++ b/parachain/node/src/cli.rs @@ -83,6 +83,8 @@ pub struct Cli { pub no_hardware_benchmarks: bool, /// Should we enable async-backing? + /// + /// NOTE: DEPRECATED, async-backing is enabled by default. #[arg(long)] pub async_backing: bool, diff --git a/parachain/node/src/command.rs b/parachain/node/src/command.rs index 2c96eb90d..e0b43f571 100644 --- a/parachain/node/src/command.rs +++ b/parachain/node/src/command.rs @@ -371,7 +371,6 @@ pub fn run() -> Result<()> { collator_options, id, hwbench, - cli, ) .await .map_err(Into::into) diff --git a/parachain/node/src/service.rs b/parachain/node/src/service.rs index f6136c930..976d571d4 100644 --- a/parachain/node/src/service.rs +++ b/parachain/node/src/service.rs @@ -24,6 +24,7 @@ use crate::runtime_api::{opaque, BaseHostRuntimeApis}; // Cumulus Imports use cumulus_client_collator::service::CollatorService; +use cumulus_client_consensus_aura::collators::lookahead; use cumulus_client_consensus_common::ParachainBlockImport as TParachainBlockImport; use cumulus_client_consensus_proposer::Proposer; use cumulus_client_service::{ @@ -34,7 +35,6 @@ use cumulus_primitives_core::{relay_chain::CollatorPair, ParaId}; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; use polkadot_primitives::ValidationCode; // Substrate Imports -use crate::cli::Cli; use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use sc_client_api::Backend; use sc_consensus::ImportQueue; @@ -194,7 +194,6 @@ async fn start_node_impl( collator_options: CollatorOptions, para_id: ParaId, hwbench: Option, - cli: Cli, ) -> sc_service::error::Result where Runtime: @@ -380,7 +379,6 @@ where collator_key.expect("Command line arguments do not allow this. qed"), overseer_handle, announce_block, - cli, )?; } @@ -445,7 +443,6 @@ fn start_consensus( collator_key: CollatorPair, overseer_handle: OverseerHandle, announce_block: Arc>) + Send + Sync>, - cli: Cli, ) -> Result<(), sc_service::Error> where Runtime: @@ -477,80 +474,42 @@ where client.clone(), ); - if cli.async_backing { - use cumulus_client_consensus_aura::collators::lookahead; + let params = lookahead::Params { + create_inherent_data_providers: move |_, ()| async move { Ok(()) }, + block_import, + para_client: client.clone(), + para_backend: backend, + relay_client: relay_chain_interface, + code_hash_provider: move |hash| { + client.code_at(hash).ok().map(ValidationCode).map(|c| c.hash()) + }, + sync_oracle, + keystore, + collator_key, + para_id, + overseer_handle, + slot_duration, + relay_chain_slot_duration, + proposer, + collator_service, + // Async backing time + authoring_duration: Duration::from_millis(1500), + }; - let params = lookahead::Params { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - para_backend: backend, - relay_client: relay_chain_interface, - code_hash_provider: move |hash| { - client.code_at(hash).ok().map(ValidationCode).map(|c| c.hash()) - }, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - // Async backing time - authoring_duration: Duration::from_millis(1500), - }; - - let fut = lookahead::run::< - opaque::Block, - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - _, - _, - _, - _, - >(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); - } else { - use cumulus_client_consensus_aura::collators::basic; - - let params = basic::Params { - create_inherent_data_providers: move |_, ()| async move { Ok(()) }, - block_import, - para_client: client.clone(), - relay_client: relay_chain_interface, - sync_oracle, - keystore, - collator_key, - para_id, - overseer_handle, - slot_duration, - relay_chain_slot_duration, - proposer, - collator_service, - // Very limited proposal time. - authoring_duration: Duration::from_millis(500), - collation_request_receiver: None, - }; - - let fut = basic::run::< - opaque::Block, - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - _, - _, - >(params); - task_manager.spawn_essential_handle().spawn("aura", None, fut); - } + let fut = lookahead::run::< + opaque::Block, + sp_consensus_aura::sr25519::AuthorityPair, + _, + _, + _, + _, + _, + _, + _, + _, + _, + >(params); + task_manager.spawn_essential_handle().spawn("aura", None, fut); Ok(()) } @@ -562,7 +521,6 @@ pub async fn start_parachain_node( collator_options: CollatorOptions, para_id: ParaId, hwbench: Option, - cli: Cli, ) -> sc_service::error::Result { match parachain_config.chain_spec.id() { chain if chain.contains("gargantua") => @@ -572,7 +530,6 @@ pub async fn start_parachain_node( collator_options, para_id, hwbench, - cli, ) .await, chain if chain.contains("messier") => @@ -582,7 +539,6 @@ pub async fn start_parachain_node( collator_options, para_id, hwbench, - cli, ) .await, chain => panic!("Unknown chain with id: {}", chain), diff --git a/scripts/zombienet/local-testnet.toml b/scripts/zombienet/local-testnet.toml index 0363bebbf..93301185a 100644 --- a/scripts/zombienet/local-testnet.toml +++ b/scripts/zombienet/local-testnet.toml @@ -6,8 +6,8 @@ chain = "rococo-local" [[relaychain.nodes]] name = "alice" validator = true -rpc_port = 9932 -ws_port = 9944 +rpc_port = 9912 +ws_port = 9922 args = [ "--enable-offchain-indexing=true", "--pruning=archive", ] @@ -15,22 +15,22 @@ args = [ [[relaychain.nodes]] name = "bob" validator = true -rpc_port = 9933 -ws_port = 9945 +rpc_port = 9913 +ws_port = 9923 args = [] [[relaychain.nodes]] name = "charlie" validator = true -rpc_port = 9934 -ws_port = 9946 +rpc_port = 9914 +ws_port = 9924 args = [] [[relaychain.nodes]] name = "dave" validator = true -rpc_port = 9935 -ws_port = 9947 +rpc_port = 9915 +ws_port = 9925 args = [] [[parachains]] @@ -42,9 +42,9 @@ cumulus_based = true [[parachains.collators]] name = "alice" validator = true -rpc_port = 9955 -ws_port = 9988 -port = 30337 +rpc_port = 8955 +ws_port = 9990 +port = 40337 command = "./target/release/hyperbridge" args = [ "--async-backing -lbasic-authorship=trace,pallet-ismp=trace", "--enable-offchain-indexing=true", "--pruning=archive"