diff --git a/.github/workflows/ethereum.yml b/.github/workflows/ethereum.yml index 5ae1ca08b..d9b5fd141 100644 --- a/.github/workflows/ethereum.yml +++ b/.github/workflows/ethereum.yml @@ -39,7 +39,7 @@ jobs: run: forge test - name: Coverage working-directory: core/packages/contracts - run: forge coverage --report=lcov + run: forge coverage --report=lcov --via-ir - name: Lint working-directory: core/packages/contracts run: pnpm lint @@ -48,4 +48,3 @@ jobs: with: working-directory: core/packages/contracts files: lcov.info - diff --git a/core/packages/contracts/.gitignore b/core/packages/contracts/.gitignore index 4a349bd97..7fdb56d03 100644 --- a/core/packages/contracts/.gitignore +++ b/core/packages/contracts/.gitignore @@ -9,10 +9,10 @@ artifacts/ coverage.json coverage/ .vscode/ -deployments/ out/ cache/ broadcast/ types/ tsconfig.tsbuildinfo lcov.info +tenderly.yaml diff --git a/core/packages/contracts/.solhint.json b/core/packages/contracts/.solhint.json index 0f2671b5d..66cd1309c 100644 --- a/core/packages/contracts/.solhint.json +++ b/core/packages/contracts/.solhint.json @@ -1,7 +1,7 @@ { "extends": "solhint:recommended", "rules": { - "compiler-version": ["error", "0.8.19"], + "compiler-version": ["error", "0.8.20"], "func-visibility": ["warn", {"ignoreConstructors":true}], "not-rely-on-time": "off", "avoid-low-level-calls": "off", diff --git a/core/packages/contracts/foundry.toml b/core/packages/contracts/foundry.toml index ac8ff7f3e..0d43c12fd 100644 --- a/core/packages/contracts/foundry.toml +++ b/core/packages/contracts/foundry.toml @@ -1,5 +1,5 @@ [profile.default] -solc_version = '0.8.19' +solc_version = "0.8.20" optimizer = true optimizer_runs = 20_000 via_ir = true @@ -7,6 +7,6 @@ test = 'test' ffi = true ignored_error_codes = [ - # prevrandao not supported by Anvil VM - 9432 + # DeployScript.sol is never deployed + 5574 ] diff --git a/core/packages/contracts/scripts/helpers.ts b/core/packages/contracts/scripts/helpers.ts index b15c599f2..69355595b 100644 --- a/core/packages/contracts/scripts/helpers.ts +++ b/core/packages/contracts/scripts/helpers.ts @@ -67,7 +67,7 @@ class ValidatorSet { let leaves = wallets.map((w) => keccakFromHexString(w.address)) let tree = new MerkleTree(leaves, keccak, { sortLeaves: false, - sortPairs: true, + sortPairs: false, }) this.wallets = wallets diff --git a/core/packages/contracts/src/Auth.sol b/core/packages/contracts/src/Auth.sol new file mode 100644 index 000000000..44d585adc --- /dev/null +++ b/core/packages/contracts/src/Auth.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; + +import {AccessControl} from "openzeppelin/access/AccessControl.sol"; + +contract Auth is AccessControl { + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + + constructor() { + _grantRole(ADMIN_ROLE, msg.sender); + _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); + } +} diff --git a/core/packages/contracts/src/BeefyClient.sol b/core/packages/contracts/src/BeefyClient.sol index 03a981a66..9d63739ca 100644 --- a/core/packages/contracts/src/BeefyClient.sol +++ b/core/packages/contracts/src/BeefyClient.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {ECDSA} from "openzeppelin/utils/cryptography/ECDSA.sol"; import {Ownable} from "openzeppelin/access/Ownable.sol"; -import {MerkleProof} from "openzeppelin/utils/cryptography/MerkleProof.sol"; +import {MerkleProof} from "./utils/MerkleProof.sol"; import {Bitfield} from "./utils/Bitfield.sol"; import {MMRProof} from "./utils/MMRProof.sol"; import {ScaleCodec} from "./ScaleCodec.sol"; @@ -441,10 +442,7 @@ contract BeefyClient is Ownable { } // Check that validator is actually in a validator set - // - // NOTE: This currently insecure due to a regression documented in SNO-427. - // Basically `proof.index` (the merkle leaf index) is not being validated. - if (!isValidatorInSet(vset, proof.account, proof.proof)) { + if (!isValidatorInSet(vset, proof.account, proof.index, proof.proof)) { revert InvalidValidatorProof(); } @@ -498,13 +496,13 @@ contract BeefyClient is Ownable { * @param proof Merkle proof required for validation of the address * @return true if the validator is in the set */ - function isValidatorInSet(ValidatorSet memory vset, address addr, bytes32[] calldata proof) + function isValidatorInSet(ValidatorSet memory vset, address addr, uint256 index, bytes32[] calldata proof) internal pure returns (bool) { bytes32 hashedLeaf = keccak256(abi.encodePacked(addr)); - return MerkleProof.verify(proof, vset.root, hashedLeaf); + return MerkleProof.verify(vset.root, hashedLeaf, index, vset.length, proof); } /** diff --git a/core/packages/contracts/src/DeployScript.sol b/core/packages/contracts/src/DeployScript.sol index 09950dff5..37e0db9b8 100644 --- a/core/packages/contracts/src/DeployScript.sol +++ b/core/packages/contracts/src/DeployScript.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {WETH9} from "canonical-weth/WETH9.sol"; import {Script} from "forge-std/Script.sol"; @@ -11,12 +12,22 @@ import {OutboundQueue} from "../src/OutboundQueue.sol"; import {NativeTokens} from "../src/NativeTokens.sol"; import {TokenVault} from "../src/TokenVault.sol"; import {Vault} from "../src/Vault.sol"; -import {IVault} from "../src/IVault.sol"; import {UpgradeProxy} from "../src/UpgradeProxy.sol"; import {SovereignTreasury} from "../src/SovereignTreasury.sol"; +import {Registry} from "../src/Registry.sol"; import {ParaID} from "../src/Types.sol"; contract DeployScript is Script { + Registry public registry; + Vault public vault; + BeefyClient public beefyClient; + ParachainClient public parachainClient; + InboundQueue public inboundQueue; + OutboundQueue public outboundQueue; + TokenVault public tokenVault; + NativeTokens public nativeTokens; + UpgradeProxy public upgradeProxy; + function setUp() public {} function run() public { @@ -24,42 +35,54 @@ contract DeployScript is Script { address deployer = vm.rememberKey(privateKey); vm.startBroadcast(deployer); + // Registry + registry = new Registry(); + registry.grantRole(registry.REGISTER_ROLE(), deployer); + + // Vault + vault = new Vault(); + // SovereignTreasury - Vault vault = new Vault(); - SovereignTreasury treasury = new SovereignTreasury(vault); + SovereignTreasury treasury = new SovereignTreasury(registry, vault); + registry.registerContract(keccak256("SovereignTreasury"), address(treasury)); // BeefyClient uint256 randaoCommitDelay = vm.envUint("RANDAO_COMMIT_DELAY"); uint256 randaoCommitExpiration = vm.envUint("RANDAO_COMMIT_EXP"); - BeefyClient beefyClient = new BeefyClient(randaoCommitDelay, randaoCommitExpiration); + beefyClient = new BeefyClient(randaoCommitDelay, randaoCommitExpiration); // ParachainClient uint32 paraId = uint32(vm.envUint("BRIDGE_HUB_PARAID")); - ParachainClient parachainClient = new ParachainClient(beefyClient, paraId); + parachainClient = new ParachainClient(beefyClient, paraId); // InboundQueue uint256 relayerReward = vm.envUint("RELAYER_REWARD"); - InboundQueue inboundQueue = new InboundQueue(parachainClient, vault, relayerReward); + inboundQueue = new InboundQueue(registry, parachainClient, vault, relayerReward); + registry.registerContract(keccak256("InboundQueue"), address(inboundQueue)); // OutboundQueue uint256 relayerFee = vm.envUint("RELAYER_FEE"); - OutboundQueue outboundQueue = new OutboundQueue(vault, relayerFee); + outboundQueue = new OutboundQueue(registry, vault, relayerFee); + registry.registerContract(keccak256("OutboundQueue"), address(outboundQueue)); // NativeTokens - TokenVault tokenVault = new TokenVault(); - NativeTokens nativeTokens = new NativeTokens( + tokenVault = new TokenVault(); + nativeTokens = new NativeTokens( + registry, tokenVault, - outboundQueue, ParaID.wrap(uint32(vm.envUint("ASSET_HUB_PARAID"))), - vm.envUint("CREATE_TOKEN_FEE") + vm.envUint("CREATE_TOKEN_FEE"), + bytes2(vm.envBytes("CREATE_CALL_INDEX")), + bytes2(vm.envBytes("SET_METADATA_CALL_INDEX")) ); - inboundQueue.updateHandler(1, IRecipient(nativeTokens)); + registry.registerContract(keccak256("NativeTokens"), address(nativeTokens)); // Deploy WETH for testing new WETH9(); - // Upgrades - UpgradeProxy upgradeProxy = new UpgradeProxy(ParaID.wrap(paraId)); + // UpgradeProxy + upgradeProxy = new UpgradeProxy(registry, ParaID.wrap(paraId)); + registry.registerContract(keccak256("UpgradeProxy"), address(upgradeProxy)); // Allow inbound queue to send messages to handlers nativeTokens.grantRole(nativeTokens.SENDER_ROLE(), address(inboundQueue)); @@ -82,24 +105,32 @@ contract DeployScript is Script { // Move ownership of everything to Upgrades app treasury.grantRole(treasury.ADMIN_ROLE(), address(upgradeProxy)); - treasury.revokeRole(treasury.ADMIN_ROLE(), address(this)); + treasury.revokeRole(treasury.ADMIN_ROLE(), deployer); nativeTokens.grantRole(nativeTokens.ADMIN_ROLE(), address(upgradeProxy)); - nativeTokens.revokeRole(nativeTokens.ADMIN_ROLE(), address(this)); + nativeTokens.revokeRole(nativeTokens.ADMIN_ROLE(), deployer); vault.grantRole(vault.ADMIN_ROLE(), address(upgradeProxy)); - vault.revokeRole(vault.ADMIN_ROLE(), address(this)); + vault.revokeRole(vault.ADMIN_ROLE(), deployer); tokenVault.grantRole(tokenVault.ADMIN_ROLE(), address(upgradeProxy)); - tokenVault.revokeRole(tokenVault.ADMIN_ROLE(), address(this)); + tokenVault.revokeRole(tokenVault.ADMIN_ROLE(), deployer); inboundQueue.grantRole(inboundQueue.ADMIN_ROLE(), address(upgradeProxy)); - inboundQueue.revokeRole(inboundQueue.ADMIN_ROLE(), address(this)); + inboundQueue.revokeRole(inboundQueue.ADMIN_ROLE(), deployer); outboundQueue.grantRole(outboundQueue.ADMIN_ROLE(), address(upgradeProxy)); - outboundQueue.revokeRole(outboundQueue.ADMIN_ROLE(), address(this)); + outboundQueue.revokeRole(outboundQueue.ADMIN_ROLE(), deployer); + + registry.grantRole(outboundQueue.ADMIN_ROLE(), address(upgradeProxy)); + registry.revokeRole(outboundQueue.ADMIN_ROLE(), deployer); + + upgradeProxy.revokeRole(upgradeProxy.ADMIN_ROLE(), deployer); - upgradeProxy.revokeRole(upgradeProxy.ADMIN_ROLE(), address(this)); + // Fund the sovereign account for the BridgeHub parachain. Used to reward relayers + // of messages originating from BridgeHub + uint256 initialDeposit = vm.envUint("BRIDGE_HUB_INITIAL_DEPOSIT"); + vault.deposit{value: initialDeposit}(ParaID.wrap(paraId)); vm.stopBroadcast(); } diff --git a/core/packages/contracts/src/Gateway.sol b/core/packages/contracts/src/Gateway.sol new file mode 100644 index 000000000..86fcf16c1 --- /dev/null +++ b/core/packages/contracts/src/Gateway.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; + +import {AccessControl} from "openzeppelin/access/AccessControl.sol"; +import {Registry} from "./Registry.sol"; +import {IOutboundQueue} from "./IOutboundQueue.sol"; +import {IRecipient} from "./IRecipient.sol"; +import {ParaID} from "./Types.sol"; +import {Auth} from "./Auth.sol"; +import {RegistryLookup} from "./RegistryLookup.sol"; + +abstract contract Gateway is Auth, RegistryLookup, IRecipient { + bytes32 public constant SENDER_ROLE = keccak256("SENDER_ROLE"); + bytes32 public constant OUTBOUND_QUEUE = keccak256("OutboundQueue"); + + /* Errors */ + + error Unauthorized(); + + constructor(Registry registry) RegistryLookup(registry) { + _setRoleAdmin(SENDER_ROLE, ADMIN_ROLE); + } + + function handle(ParaID origin, bytes calldata message) external virtual; + + function outboundQueue() internal view returns (IOutboundQueue) { + return IOutboundQueue(resolve(OUTBOUND_QUEUE)); + } + + function ensureOrigin(ParaID a, ParaID b) internal pure { + if (a != b) { + revert Unauthorized(); + } + } +} diff --git a/core/packages/contracts/src/IOutboundQueue.sol b/core/packages/contracts/src/IOutboundQueue.sol index d7a1f7f83..cab05087a 100644 --- a/core/packages/contracts/src/IOutboundQueue.sol +++ b/core/packages/contracts/src/IOutboundQueue.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {ParaID} from "./Types.sol"; diff --git a/core/packages/contracts/src/IParachainClient.sol b/core/packages/contracts/src/IParachainClient.sol index 69b842546..50834b1fa 100644 --- a/core/packages/contracts/src/IParachainClient.sol +++ b/core/packages/contracts/src/IParachainClient.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; interface IParachainClient { - function verifyCommitment( - bytes32 commitment, - bytes calldata opaqueProof - ) external view returns (bool); + function verifyCommitment(bytes32 commitment, bytes calldata opaqueProof) external view returns (bool); } diff --git a/core/packages/contracts/src/IRecipient.sol b/core/packages/contracts/src/IRecipient.sol index a5f29d3bf..b1910774a 100644 --- a/core/packages/contracts/src/IRecipient.sol +++ b/core/packages/contracts/src/IRecipient.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {ParaID} from "./Types.sol"; diff --git a/core/packages/contracts/src/IUpgradeTask.sol b/core/packages/contracts/src/IUpgradeTask.sol deleted file mode 100644 index 58d96cdd1..000000000 --- a/core/packages/contracts/src/IUpgradeTask.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; - -interface IUpgradeTask { - function run() external; -} diff --git a/core/packages/contracts/src/IVault.sol b/core/packages/contracts/src/IVault.sol deleted file mode 100644 index 29be2cc6a..000000000 --- a/core/packages/contracts/src/IVault.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; - -import {ParaID} from "./Types.sol"; - -interface IVault { - function deposit(ParaID sovereign) external payable; - - function withdraw(ParaID sovereign, address payable recipient, uint256 amount) external; -} diff --git a/core/packages/contracts/src/InboundQueue.sol b/core/packages/contracts/src/InboundQueue.sol index 7fb5a279a..fc7da62aa 100644 --- a/core/packages/contracts/src/InboundQueue.sol +++ b/core/packages/contracts/src/InboundQueue.sol @@ -1,32 +1,31 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {MerkleProof} from "openzeppelin/utils/cryptography/MerkleProof.sol"; import {AccessControl} from "openzeppelin/access/AccessControl.sol"; -import {IParachainClient} from "./ParachainClient.sol"; +import {IParachainClient, ParachainClient} from "./ParachainClient.sol"; +import {Registry} from "./Registry.sol"; +import {RegistryLookup} from "./RegistryLookup.sol"; +import {Auth} from "./Auth.sol"; +import {Vault} from "./Vault.sol"; + import {IRecipient} from "./IRecipient.sol"; -import {IVault} from "./IVault.sol"; import {ParaID} from "./Types.sol"; -contract InboundQueue is AccessControl { +contract InboundQueue is Auth, RegistryLookup { // Nonce for each origin mapping(ParaID origin => uint64) public nonce; - // Registered message handlers - mapping(uint16 handlerID => IRecipient) public handlers; - // Light client message verifier IParachainClient public parachainClient; // Relayers are rewarded from this vault - IVault public vault; + Vault public immutable vault; // The relayer reward for submitting a message uint256 public reward; - // The governance contract which is a proxy for Polkadot governance, administers via this role - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - // Relayers must provide enough gas to cover message dispatch plus a buffer uint256 public gasToForward = 500000; uint256 public constant GAS_BUFFER = 24000; @@ -35,7 +34,7 @@ contract InboundQueue is AccessControl { struct Message { ParaID origin; uint64 nonce; - uint16 handler; + bytes32 recipient; bytes payload; } @@ -44,22 +43,21 @@ contract InboundQueue is AccessControl { Failure } - event MessageDispatched(ParaID indexed origin, uint64 indexed nonce, DispatchResult result); + event MessageDispatched(ParaID origin, uint64 nonce, DispatchResult result); event HandlerUpdated(uint16 id, IRecipient handler); event ParachainClientUpdated(address parachainClient); event VaultUpdated(address vault); event RewardUpdated(uint256 reward); event GasToForwardUpdated(uint256 gasToForward); - + event InvalidRecipient(bytes32 recipient); error InvalidProof(); error InvalidNonce(); - error InvalidHandler(); error NotEnoughGas(); - constructor(IParachainClient _parachainClient, IVault _vault, uint256 _reward) { - _grantRole(ADMIN_ROLE, msg.sender); - _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); + constructor(Registry registry, IParachainClient _parachainClient, Vault _vault, uint256 _reward) + RegistryLookup(registry) + { parachainClient = _parachainClient; vault = _vault; reward = _reward; @@ -89,11 +87,6 @@ contract InboundQueue is AccessControl { // should top up the funds and have a relayer resend the message. vault.withdraw(message.origin, payable(msg.sender), reward); - IRecipient handler = handlers[message.handler]; - if (address(handler) == address(0)) { - revert InvalidHandler(); - } - // Ensure relayers pass enough gas for message to execute. // Otherwise malicious relayers can break the bridge by allowing handlers to run out gas. // Resubmission of the message by honest relayers will fail as the tracked nonce @@ -102,8 +95,9 @@ contract InboundQueue is AccessControl { revert NotEnoughGas(); } + address recipient = resolve(message.recipient); DispatchResult result = DispatchResult.Success; - try handler.handle{gas: gasToForward}(message.origin, message.payload) {} + try IRecipient(recipient).handle{gas: gasToForward}(message.origin, message.payload) {} catch { result = DispatchResult.Failure; } @@ -111,21 +105,6 @@ contract InboundQueue is AccessControl { emit MessageDispatched(message.origin, message.nonce, result); } - function updateHandler(uint16 id, IRecipient handler) external onlyRole(ADMIN_ROLE) { - handlers[id] = handler; - emit HandlerUpdated(id, handler); - } - - function updateParachainClient(IParachainClient _parachainClient) external onlyRole(ADMIN_ROLE) { - parachainClient = _parachainClient; - emit ParachainClientUpdated(address(_parachainClient)); - } - - function updateVault(IVault _vault) external onlyRole(ADMIN_ROLE) { - vault = _vault; - emit VaultUpdated(address(_vault)); - } - function updateReward(uint256 _reward) external onlyRole(ADMIN_ROLE) { reward = _reward; emit RewardUpdated(_reward); diff --git a/core/packages/contracts/src/NativeTokens.sol b/core/packages/contracts/src/NativeTokens.sol index 52535aa1c..8e08c5b06 100644 --- a/core/packages/contracts/src/NativeTokens.sol +++ b/core/packages/contracts/src/NativeTokens.sol @@ -1,25 +1,25 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; -import { Ownable } from "openzeppelin/access/Ownable.sol"; -import { AccessControl } from "openzeppelin/access/AccessControl.sol"; -import { IERC20Metadata } from "openzeppelin/token/ERC20/extensions/IERC20Metadata.sol"; +import {Ownable} from "openzeppelin/access/Ownable.sol"; +import {AccessControl} from "openzeppelin/access/AccessControl.sol"; +import {IERC20Metadata} from "openzeppelin/token/ERC20/extensions/IERC20Metadata.sol"; -import { IRecipient } from "./IRecipient.sol"; -import { TokenVault } from "./TokenVault.sol"; -import { SubstrateTypes } from "./SubstrateTypes.sol"; -import { NativeTokensTypes } from "./NativeTokensTypes.sol"; -import { IOutboundQueue } from "./OutboundQueue.sol"; -import { ParaID } from "./Types.sol"; +import {TokenVault} from "./TokenVault.sol"; +import {SubstrateTypes} from "./SubstrateTypes.sol"; +import {NativeTokensTypes} from "./NativeTokensTypes.sol"; +import {IOutboundQueue} from "./OutboundQueue.sol"; +import {ParaID} from "./Types.sol"; +import {Gateway} from "./Gateway.sol"; +import {Registry} from "./Registry.sol"; /// @title Native Tokens /// @dev Manages locking, unlocking ERC20 tokens in the vault. Initializes ethereum native /// tokens on the substrate side via create. -contract NativeTokens is AccessControl, IRecipient { +contract NativeTokens is Gateway { /// @dev Describes the type of message. - enum Action { - Unlock - } + enum Action {Unlock} /// @dev Message format. struct Message { @@ -53,36 +53,39 @@ contract NativeTokens is AccessControl, IRecipient { /* State */ - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - bytes32 public constant SENDER_ROLE = keccak256("SENDER_ROLE"); - // Parachain ID of AssetHub (aka Statemint) ParaID public immutable assetHubParaID; TokenVault public immutable vault; - IOutboundQueue public outboundQueue; uint256 public createTokenFee; + /* Constants */ + + // Call index for ForeignAssets::create dispatchable on AssetHub parachain + bytes2 public immutable createCallId; + + // Call index for ForeignAssets::set_metata dispatchable AssetHub parachain + bytes2 public immutable setMetadataCallId; + /* Errors */ error InvalidAmount(); - error Unauthorized(); error NoFundsforCreateToken(); constructor( + Registry registry, TokenVault _vault, - IOutboundQueue _outboundQueue, ParaID _assetHubParaID, - uint256 _createTokenFee - ) { - _grantRole(ADMIN_ROLE, msg.sender); - _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); - _setRoleAdmin(SENDER_ROLE, ADMIN_ROLE); + uint256 _createTokenFee, + bytes2 _createCallId, + bytes2 _setMetadataCallId + ) Gateway(registry) { vault = _vault; - outboundQueue = _outboundQueue; assetHubParaID = _assetHubParaID; createTokenFee = _createTokenFee; + createCallId = _createCallId; + setMetadataCallId = _setMetadataCallId; } /// @dev Locks an amount of ERC20 Tokens in the vault and enqueues a mint message. @@ -90,20 +93,15 @@ contract NativeTokens is AccessControl, IRecipient { /// @param token The token to lock. /// @param recipient The recipient on the substrate side. /// @param amount The amount to lock. - function lock( - address token, - ParaID dest, - bytes calldata recipient, - uint128 amount - ) external payable { + function lock(address token, ParaID dest, bytes calldata recipient, uint128 amount) external payable { if (amount == 0) { revert InvalidAmount(); } vault.deposit(msg.sender, token, amount); - bytes memory payload = NativeTokensTypes.Mint(token, dest, recipient, amount); - outboundQueue.submit{ value: msg.value }(assetHubParaID, payload); + bytes memory payload = NativeTokensTypes.Mint(address(registry), token, dest, recipient, amount); + outboundQueue().submit{value: msg.value}(assetHubParaID, payload); emit Locked(recipient, token, amount); } @@ -127,8 +125,9 @@ contract NativeTokens is AccessControl, IRecipient { } uint8 decimals = metadata.decimals(); - bytes memory payload = NativeTokensTypes.Create(token, name, symbol, decimals); - outboundQueue.submit{ value: msg.value }(assetHubParaID, payload); + bytes memory payload = + NativeTokensTypes.Create(address(registry), token, name, symbol, decimals, createCallId, setMetadataCallId); + outboundQueue().submit{value: msg.value}(assetHubParaID, payload); emit Created(token); } @@ -136,10 +135,8 @@ contract NativeTokens is AccessControl, IRecipient { /// @dev Processes messages from inbound channel. /// @param origin The multilocation of the source parachain /// @param message The message enqueued from substrate. - function handle(ParaID origin, bytes calldata message) external onlyRole(SENDER_ROLE) { - if (origin != assetHubParaID) { - revert Unauthorized(); - } + function handle(ParaID origin, bytes calldata message) external override onlyRole(SENDER_ROLE) { + ensureOrigin(origin, assetHubParaID); Message memory decoded = abi.decode(message, (Message)); if (decoded.action == Action.Unlock) { @@ -148,9 +145,4 @@ contract NativeTokens is AccessControl, IRecipient { emit Unlocked(payload.recipient, payload.token, payload.amount); } } - - function setOutboundQueue(IOutboundQueue _outboundQueue) external onlyRole(ADMIN_ROLE) { - outboundQueue = _outboundQueue; - emit OutboundQueueUpdated(address(_outboundQueue)); - } } diff --git a/core/packages/contracts/src/NativeTokensTypes.sol b/core/packages/contracts/src/NativeTokensTypes.sol index 2b13547a7..a8a6a7c56 100644 --- a/core/packages/contracts/src/NativeTokensTypes.sol +++ b/core/packages/contracts/src/NativeTokensTypes.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {ScaleCodec} from "./ScaleCodec.sol"; import {SubstrateTypes} from "./SubstrateTypes.sol"; @@ -7,37 +8,51 @@ import {ParaID} from "./Types.sol"; library NativeTokensTypes { /** - * @dev Encodes Payload::NativeTokens(NativeTokens::Create) + * @dev SCALE-encodes `router_primitives::inbound::VersionedMessage` containing payload + * `NativeTokensMessage::Create` */ // solhint-disable-next-line func-name-mixedcase - function Create(address token, bytes memory name, bytes memory symbol, uint8 decimals) - internal - pure - returns (bytes memory) - { + function Create( + address origin, + address token, + bytes memory name, + bytes memory symbol, + uint8 decimals, + bytes2 createCallIndex, + bytes2 setMetadataCallIndex + ) internal view returns (bytes memory) { return bytes.concat( - hex"00", - hex"00", - abi.encodePacked(token), + bytes1(0x00), + ScaleCodec.encodeU64(uint64(block.chainid)), + bytes1(0x01), + bytes1(0x00), + SubstrateTypes.H160(origin), + SubstrateTypes.H160(token), SubstrateTypes.VecU8(name), SubstrateTypes.VecU8(symbol), - ScaleCodec.encodeU8(decimals) + ScaleCodec.encodeU8(decimals), + createCallIndex, + setMetadataCallIndex ); } /** - * @dev Encodes Payload::NativeTokens(NativeTokens::Mint) + * @dev SCALE-encodes `router_primitives::inbound::VersionedMessage` containing payload + * `NativeTokensMessage::Mint` */ // solhint-disable-next-line func-name-mixedcase - function Mint(address token, ParaID dest, bytes memory recipient, uint128 amount) + function Mint(address origin, address token, ParaID dest, bytes memory recipient, uint128 amount) internal - pure + view returns (bytes memory) { return bytes.concat( - hex"00", - hex"01", - abi.encodePacked(token), + bytes1(0x00), + ScaleCodec.encodeU64(uint64(block.chainid)), + bytes1(0x01), + bytes1(0x01), + SubstrateTypes.H160(origin), + SubstrateTypes.H160(token), SubstrateTypes.OptionParaID(dest), recipient, ScaleCodec.encodeU128(amount) diff --git a/core/packages/contracts/src/OutboundQueue.sol b/core/packages/contracts/src/OutboundQueue.sol index 13c628082..72eca4aea 100644 --- a/core/packages/contracts/src/OutboundQueue.sol +++ b/core/packages/contracts/src/OutboundQueue.sol @@ -1,27 +1,28 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {AccessControl} from "openzeppelin/access/AccessControl.sol"; import {IOutboundQueue} from "./IOutboundQueue.sol"; -import {IVault} from "./IVault.sol"; +import {Registry} from "./Registry.sol"; +import {Vault} from "./Vault.sol"; import {ParaID} from "./Types.sol"; +import {Auth} from "./Auth.sol"; +import {RegistryLookup} from "./RegistryLookup.sol"; -contract OutboundQueue is IOutboundQueue, AccessControl { - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); +contract OutboundQueue is Auth, RegistryLookup, IOutboundQueue { bytes32 public constant SUBMIT_ROLE = keccak256("SUBMIT_ROLE"); mapping(ParaID dest => uint64) public nonce; - IVault public vault; + Vault public immutable vault; uint256 public fee; event FeeUpdated(uint256 fee); error FeePaymentToLow(); - constructor(IVault _vault, uint256 _fee) { - _grantRole(ADMIN_ROLE, msg.sender); - _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); + constructor(Registry registry, Vault _vault, uint256 _fee) RegistryLookup(registry) { _setRoleAdmin(SUBMIT_ROLE, ADMIN_ROLE); vault = _vault; fee = _fee; diff --git a/core/packages/contracts/src/ParachainClient.sol b/core/packages/contracts/src/ParachainClient.sol index 3c10775a3..e07002b50 100644 --- a/core/packages/contracts/src/ParachainClient.sol +++ b/core/packages/contracts/src/ParachainClient.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; -import "openzeppelin/utils/cryptography/MerkleProof.sol"; -import "./BeefyClient.sol"; -import "./IParachainClient.sol"; -import "./ScaleCodec.sol"; -import "./SubstrateTypes.sol"; +import {MerkleProof} from "./utils/MerkleProof.sol"; +import {BeefyClient} from "./BeefyClient.sol"; +import {IParachainClient} from "./IParachainClient.sol"; +import {ScaleCodec} from "./ScaleCodec.sol"; +import {SubstrateTypes} from "./SubstrateTypes.sol"; contract ParachainClient is IParachainClient { BeefyClient public immutable beefyClient; @@ -69,17 +70,50 @@ contract ParachainClient is IParachainClient { if (!isCommitmentInHeaderDigest(commitment, proof.header)) { return false; } - // Compute the merkle leaf hash of our parachain bytes32 parachainHeadHash = createParachainHeaderMerkleLeaf(proof.header); // Compute the merkle root hash of all parachain heads - bytes32 parachainHeadsRoot = MerkleProof.processProof(proof.headProof.proof, parachainHeadHash); + if (proof.headProof.pos >= proof.headProof.width) { + return false; + } + bytes32 parachainHeadsRoot = MerkleProof.computeRoot( + parachainHeadHash, proof.headProof.pos, proof.headProof.width, proof.headProof.proof + ); bytes32 leafHash = createMMRLeaf(proof.leafPartial, parachainHeadsRoot); return beefyClient.verifyMMRLeafProof(leafHash, proof.leafProof, proof.leafProofOrder); } + function verifyCommitmentTest(bytes32 commitment, Proof memory proof) external view returns (bytes memory) { + if (!isCommitmentInHeaderDigest(commitment, proof.header)) { + return abi.encode(0); + } + // Compute the merkle leaf hash of our parachain + bytes32 parachainHeadHash = createParachainHeaderMerkleLeaf(proof.header); + + // Compute the merkle root hash of all parachain heads + if (proof.headProof.pos >= proof.headProof.width) { + return abi.encode(1); + } + bytes32 parachainHeadsRoot = MerkleProof.computeRoot( + parachainHeadHash, proof.headProof.pos, proof.headProof.width, proof.headProof.proof + ); + + bytes32 leafHash = createMMRLeaf(proof.leafPartial, parachainHeadsRoot); + bool res = beefyClient.verifyMMRLeafProof(leafHash, proof.leafProof, proof.leafProofOrder); + + return bytes.concat( + abi.encode(2), + abi.encode(res), + commitment, + parachainHeadHash, + parachainHeadsRoot, + leafHash, + createParachainHeader(proof.header) + ); + } + // Verify that a message commitment is in the header digest function isCommitmentInHeaderDigest(bytes32 commitment, ParachainHeader memory header) internal @@ -95,12 +129,15 @@ contract ParachainClient is IParachainClient { return false; } + // encodes Vec function encodeDigestItems(DigestItem[] memory digestItems) internal pure returns (bytes memory) { + // encode all digest items into a buffer bytes memory accum = hex""; for (uint256 i = 0; i < digestItems.length; i++) { accum = bytes.concat(accum, encodeDigestItem(digestItems[i])); } - return accum; + // encode number of digest items, followed by encoded digest items + return bytes.concat(ScaleCodec.encodeCompactUint(digestItems.length), accum); } function encodeDigestItem(DigestItem memory digestItem) internal pure returns (bytes memory) { @@ -138,25 +175,56 @@ contract ParachainClient is IParachainClient { // Creates a keccak hash of a SCALE-encoded parachain header function createParachainHeaderMerkleLeaf(ParachainHeader memory header) internal view returns (bytes32) { + // Encode Parachain header + bytes memory encodedHeader = bytes.concat( + // H256 + header.parentHash, + // Compact unsigned int + ScaleCodec.encodeCompactUint(header.number), + // H256 + header.stateRoot, + // H256 + header.extrinsicsRoot, + // Vec + encodeDigestItems(header.digestItems) + ); + + // Hash of encoded parachain header merkle leaf return keccak256( bytes.concat( // u32 encodedParachainID, - // H256 - header.parentHash, - // Compact unsigned int - ScaleCodec.encodeCompactUint(header.number), - // H256 - header.stateRoot, - // H256 - header.extrinsicsRoot, - // Vec - ScaleCodec.encodeCompactUint(header.digestItems.length), - encodeDigestItems(header.digestItems) + // Vec + ScaleCodec.encodeCompactUint(encodedHeader.length), + encodedHeader ) ); } + function createParachainHeader(ParachainHeader memory header) internal view returns (bytes memory) { + bytes memory encodedHeader = bytes.concat( + // H256 + header.parentHash, + // Compact unsigned int + ScaleCodec.encodeCompactUint(header.number), + // H256 + header.stateRoot, + // H256 + header.extrinsicsRoot, + // Vec + ScaleCodec.encodeCompactUint(header.digestItems.length), + encodeDigestItems(header.digestItems) + ); + + return bytes.concat( + // u32 + encodedParachainID, + // length of encoded header + ScaleCodec.encodeCompactUint(encodedHeader.length), + encodedHeader + ); + } + function createMMRLeaf(MMRLeafPartial memory leaf, bytes32 parachainHeadsRoot) internal pure returns (bytes32) { bytes memory encodedLeaf = bytes.concat( ScaleCodec.encodeU8(leaf.version), diff --git a/core/packages/contracts/src/Registry.sol b/core/packages/contracts/src/Registry.sol new file mode 100644 index 000000000..7d9b08eac --- /dev/null +++ b/core/packages/contracts/src/Registry.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; + +import {AccessControl} from "openzeppelin/access/AccessControl.sol"; + +contract Registry is AccessControl { + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 public constant REGISTER_ROLE = keccak256("REGISTER_ROLE"); + + mapping(bytes32 contractID => address) public registry; + + constructor() { + _grantRole(ADMIN_ROLE, msg.sender); + _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); + _setRoleAdmin(REGISTER_ROLE, ADMIN_ROLE); + } + + function registerContract(bytes32 contractID, address contractAddress) external onlyRole(REGISTER_ROLE) { + registry[contractID] = contractAddress; + } + + function lookupContract(bytes32 contractID) external view returns (address) { + return registry[contractID]; + } +} diff --git a/core/packages/contracts/src/RegistryLookup.sol b/core/packages/contracts/src/RegistryLookup.sol new file mode 100644 index 000000000..e0998eaac --- /dev/null +++ b/core/packages/contracts/src/RegistryLookup.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; + +import {Registry} from "./Registry.sol"; + +abstract contract RegistryLookup { + Registry public immutable registry; + + error LookupError(); + + constructor(Registry _registry) { + registry = _registry; + } + + function resolve(bytes32 contractID) internal view returns (address) { + address contractAddress = registry.lookupContract(contractID); + if (contractAddress == address(0)) { + revert LookupError(); + } + return contractAddress; + } +} diff --git a/core/packages/contracts/src/ScaleCodec.sol b/core/packages/contracts/src/ScaleCodec.sol index 9c777cd8f..ca75537b0 100644 --- a/core/packages/contracts/src/ScaleCodec.sol +++ b/core/packages/contracts/src/ScaleCodec.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import "./utils/Bytes.sol"; diff --git a/core/packages/contracts/src/SovereignTreasury.sol b/core/packages/contracts/src/SovereignTreasury.sol index af7f18851..b5bd9630a 100644 --- a/core/packages/contracts/src/SovereignTreasury.sol +++ b/core/packages/contracts/src/SovereignTreasury.sol @@ -1,15 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {AccessControl} from "openzeppelin/access/AccessControl.sol"; -import {IVault} from "./IVault.sol"; -import {ParaID} from "./Types.sol"; +import {Vault} from "./Vault.sol"; +import {Auth} from "./Auth.sol"; +import {Gateway} from "./Gateway.sol"; +import {Registry} from "./Registry.sol"; + +import {IRecipient} from "./IRecipient.sol"; -contract SovereignTreasury is AccessControl { - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - bytes32 public constant SENDER_ROLE = keccak256("SENDER_ROLE"); +import {ParaID} from "./Types.sol"; - IVault public vault; +contract SovereignTreasury is Gateway { + Vault public immutable vault; struct Message { Action action; @@ -26,15 +30,13 @@ contract SovereignTreasury is AccessControl { uint256 amount; } - constructor(IVault _vault) { - _grantRole(ADMIN_ROLE, msg.sender); - _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); + constructor(Registry registry, Vault _vault) Gateway(registry) { _setRoleAdmin(SENDER_ROLE, ADMIN_ROLE); vault = _vault; } // Handle a message from the bridge. - function handle(ParaID origin, bytes calldata message) external onlyRole(SENDER_ROLE) { + function handle(ParaID origin, bytes calldata message) external override onlyRole(SENDER_ROLE) { Message memory decoded = abi.decode(message, (Message)); if (decoded.action == Action.Withdraw) { WithdrawPayload memory payload = abi.decode(decoded.payload, (WithdrawPayload)); diff --git a/core/packages/contracts/src/SubstrateTypes.sol b/core/packages/contracts/src/SubstrateTypes.sol index 4c3c24cf0..520d6e373 100644 --- a/core/packages/contracts/src/SubstrateTypes.sol +++ b/core/packages/contracts/src/SubstrateTypes.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import "./ScaleCodec.sol"; import {ParaID} from "./Types.sol"; diff --git a/core/packages/contracts/src/TokenVault.sol b/core/packages/contracts/src/TokenVault.sol index c0b5cd85d..0f4ef7aec 100644 --- a/core/packages/contracts/src/TokenVault.sol +++ b/core/packages/contracts/src/TokenVault.sol @@ -1,13 +1,16 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import "openzeppelin/access/AccessControl.sol"; import "openzeppelin/token/ERC20/IERC20.sol"; import "openzeppelin/token/ERC20/utils/SafeERC20.sol"; +import {Auth} from "./Auth.sol"; + /// @title ERC20 Vault /// @dev Holds ERC20 Tokens on behalf of ERC20App. -contract TokenVault is AccessControl { +contract TokenVault is Auth { using SafeERC20 for IERC20; /// @dev Emitted when funds are deposited. @@ -19,7 +22,6 @@ contract TokenVault is AccessControl { /// @dev Not enough funds to transfer. error InsufficientBalance(); - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant WITHDRAW_ROLE = keccak256("WITHDRAW_ROLE"); bytes32 public constant DEPOSIT_ROLE = keccak256("DEPOSIT_ROLE"); @@ -27,8 +29,6 @@ contract TokenVault is AccessControl { mapping(address token => uint128) public balance; constructor() { - _grantRole(ADMIN_ROLE, msg.sender); - _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); _setRoleAdmin(WITHDRAW_ROLE, ADMIN_ROLE); _setRoleAdmin(DEPOSIT_ROLE, ADMIN_ROLE); } diff --git a/core/packages/contracts/src/Types.sol b/core/packages/contracts/src/Types.sol index 5fb68eab0..18e0bed2d 100644 --- a/core/packages/contracts/src/Types.sol +++ b/core/packages/contracts/src/Types.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; type ParaID is uint32; diff --git a/core/packages/contracts/src/UpgradeProxy.sol b/core/packages/contracts/src/UpgradeProxy.sol index 1491e3271..673aab000 100644 --- a/core/packages/contracts/src/UpgradeProxy.sol +++ b/core/packages/contracts/src/UpgradeProxy.sol @@ -1,46 +1,38 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import {AccessControl} from "openzeppelin/access/AccessControl.sol"; -import {IVault} from "./IVault.sol"; -import {IUpgradeTask} from "./IUpgradeTask.sol"; +import {UpgradeTask} from "./UpgradeTask.sol"; import {ParaID} from "./Types.sol"; +import {Gateway} from "./Gateway.sol"; +import {IRecipient} from "./IRecipient.sol"; +import {Registry} from "./Registry.sol"; -contract UpgradeProxy is AccessControl { - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); - bytes32 public constant SENDER_ROLE = keccak256("SENDER_ROLE"); - +contract UpgradeProxy is Gateway { struct Message { Action action; bytes payload; } - enum Action { - Upgrade - } + enum Action {Upgrade} struct UpgradePayload { address task; } error InvalidMessage(); - error Unauthorized(); error UpgradeFailed(); // Parachain ID of BridgeHub ParaID public immutable bridgeHubParaID; - constructor(ParaID _bridgeHubParaID) { - _grantRole(ADMIN_ROLE, msg.sender); - _setRoleAdmin(SENDER_ROLE, ADMIN_ROLE); - _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); + constructor(Registry registry, ParaID _bridgeHubParaID) Gateway(registry) { bridgeHubParaID = _bridgeHubParaID; } - function handle(ParaID origin, bytes calldata message) external onlyRole(SENDER_ROLE) { - if (origin != bridgeHubParaID) { - revert Unauthorized(); - } + function handle(ParaID origin, bytes calldata message) external override onlyRole(SENDER_ROLE) { + ensureOrigin(origin, bridgeHubParaID); Message memory decoded = abi.decode(message, (Message)); if (decoded.action != Action.Upgrade) { @@ -49,7 +41,7 @@ contract UpgradeProxy is AccessControl { UpgradePayload memory payload = abi.decode(decoded.payload, (UpgradePayload)); - (bool success,) = payload.task.delegatecall(abi.encodeCall(IUpgradeTask.run, ())); + (bool success,) = payload.task.delegatecall(abi.encodeCall(UpgradeTask.run, ())); if (!success) { revert UpgradeFailed(); } diff --git a/core/packages/contracts/src/UpgradeTask.sol b/core/packages/contracts/src/UpgradeTask.sol new file mode 100644 index 000000000..414410035 --- /dev/null +++ b/core/packages/contracts/src/UpgradeTask.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; + +import {Gateway} from "./Gateway.sol"; +import {Registry} from "./Registry.sol"; + +abstract contract UpgradeTask is Gateway { + constructor(Registry registry) Gateway(registry) {} + function run() external virtual; +} diff --git a/core/packages/contracts/src/Vault.sol b/core/packages/contracts/src/Vault.sol index 9b0f4eb1b..1e00b9b2a 100644 --- a/core/packages/contracts/src/Vault.sol +++ b/core/packages/contracts/src/Vault.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; -import {AccessControl} from "openzeppelin/access/AccessControl.sol"; -import {IVault} from "./IVault.sol"; +import {Auth} from "./Auth.sol"; import {ParaID} from "./Types.sol"; -contract Vault is IVault, AccessControl { +contract Vault is Auth { event Deposited(ParaID indexed sovereign, uint256 amount); event Withdrawn(ParaID indexed sovereign, address recipient, uint256 amount); @@ -13,15 +13,12 @@ contract Vault is IVault, AccessControl { error ZeroAmount(); error CannotSendFunds(); - bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant WITHDRAW_ROLE = keccak256("WITHDRAW_ROLE"); // Mapping of sovereign to balance mapping(ParaID sovereign => uint256) public balances; constructor() { - _grantRole(ADMIN_ROLE, msg.sender); - _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); _setRoleAdmin(WITHDRAW_ROLE, ADMIN_ROLE); } diff --git a/core/packages/contracts/src/utils/Bitfield.sol b/core/packages/contracts/src/utils/Bitfield.sol index 06e62d185..5f509e593 100644 --- a/core/packages/contracts/src/utils/Bitfield.sol +++ b/core/packages/contracts/src/utils/Bitfield.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import "./Bits.sol"; @@ -82,7 +83,7 @@ library Bitfield { * The algorithm below is implemented after https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation. * Further improvements are possible, see the article above. */ - function countSetBits(uint256[] memory self) public pure returns (uint256) { + function countSetBits(uint256[] memory self) internal pure returns (uint256) { unchecked { uint256 count = 0; for (uint256 i = 0; i < self.length; i++) { diff --git a/core/packages/contracts/src/utils/Bits.sol b/core/packages/contracts/src/utils/Bits.sol index db37b1f9c..115d5a756 100644 --- a/core/packages/contracts/src/utils/Bits.sol +++ b/core/packages/contracts/src/utils/Bits.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork // Code from https://github.com/ethereum/solidity-examples -pragma solidity ^0.8.19; +pragma solidity 0.8.20; library Bits { uint256 internal constant ONE = uint256(1); diff --git a/core/packages/contracts/src/utils/Bytes.sol b/core/packages/contracts/src/utils/Bytes.sol index e31b28ac4..a7a6d77f0 100644 --- a/core/packages/contracts/src/utils/Bytes.sol +++ b/core/packages/contracts/src/utils/Bytes.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT - -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; import "./Memory.sol"; diff --git a/core/packages/contracts/src/utils/MMRProof.sol b/core/packages/contracts/src/utils/MMRProof.sol index c7f6df065..6d5d7a2b3 100644 --- a/core/packages/contracts/src/utils/MMRProof.sol +++ b/core/packages/contracts/src/utils/MMRProof.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; library MMRProof { /** @@ -9,14 +10,13 @@ library MMRProof { * @param proof an array of hashes * @param proofOrder a bitfield describing the order of each item (left vs right) */ - function verifyLeafProof( - bytes32 root, - bytes32 leafHash, - bytes32[] calldata proof, - uint256 proofOrder - ) internal pure returns (bool) { + function verifyLeafProof(bytes32 root, bytes32 leafHash, bytes32[] calldata proof, uint256 proofOrder) + internal + pure + returns (bool) + { bytes32 acc = leafHash; - for (uint256 i = 0; i < proof.length; ) { + for (uint256 i = 0; i < proof.length;) { acc = hashPairs(acc, proof[i], (proofOrder >> i) & 1); unchecked { i++; diff --git a/core/packages/contracts/src/utils/Memory.sol b/core/packages/contracts/src/utils/Memory.sol index cd3966d51..35fb11b00 100644 --- a/core/packages/contracts/src/utils/Memory.sol +++ b/core/packages/contracts/src/utils/Memory.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT - -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; library Memory { uint256 internal constant WORD_SIZE = 32; @@ -19,11 +19,7 @@ library Memory { // the first 'len' bytes will be compared. // Requires that 'bts.length >= len' - function equals( - uint256 addr, - uint256 len, - bytes memory bts - ) internal pure returns (bool equal) { + function equals(uint256 addr, uint256 len, bytes memory bts) internal pure returns (bool equal) { require(bts.length >= len); uint256 addr2; assembly { @@ -75,9 +71,8 @@ library Memory { } // Copy remaining bytes - uint256 mask = len == 0 - ? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - : 256 ** (WORD_SIZE - len) - 1; + uint256 mask = + len == 0 ? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff : 256 ** (WORD_SIZE - len) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) diff --git a/core/packages/contracts/src/utils/MerkleProof.sol b/core/packages/contracts/src/utils/MerkleProof.sol new file mode 100644 index 000000000..60cfaa439 --- /dev/null +++ b/core/packages/contracts/src/utils/MerkleProof.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; + +library MerkleProof { + /** + * @notice Verify that a specific leaf element is part of the Merkle Tree at a specific position in the tree + * + * The tree would have been constructed using + * https://paritytech.github.io/substrate/master/binary_merkle_tree/fn.merkle_root.html + * + * This implementation adapted from + * https://paritytech.github.io/substrate/master/binary_merkle_tree/fn.verify_proof.html + * + * @param root the root of the merkle tree + * @param leaf the leaf which needs to be proven + * @param position the position of the leaf, index starting with 0 + * @param width the width or number of leaves in the tree + * @param proof the array of proofs to help verify the leaf's membership, ordered from leaf to root + * @return a boolean value representing the success or failure of the operation + */ + function verify(bytes32 root, bytes32 leaf, uint256 position, uint256 width, bytes32[] memory proof) + internal + pure + returns (bool) + { + if (position >= width) { + return false; + } + return root == computeRoot(leaf, position, width, proof); + } + + function computeRoot(bytes32 leaf, uint256 position, uint256 width, bytes32[] memory proof) + internal + pure + returns (bytes32) + { + bytes32 node = leaf; + unchecked { + for (uint256 i = 0; i < proof.length; i++) { + if (position & 1 == 1 || position + 1 == width) { + node = efficientHash(proof[i], node); + } else { + node = efficientHash(node, proof[i]); + } + position = position >> 1; + width = ((width - 1) >> 1) + 1; + } + return node; + } + } + + function efficientHash(bytes32 a, bytes32 b) internal pure returns (bytes32 value) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, a) + mstore(0x20, b) + value := keccak256(0x00, 0x40) + } + } +} diff --git a/core/packages/contracts/src/utils/OpaqueProof.sol b/core/packages/contracts/src/utils/OpaqueProof.sol index 5a72a4527..3ec5ec7f5 100644 --- a/core/packages/contracts/src/utils/OpaqueProof.sol +++ b/core/packages/contracts/src/utils/OpaqueProof.sol @@ -1,8 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.19; +// SPDX-FileCopyrightText: 2023 Snowfork +pragma solidity 0.8.20; -import "../ParachainClient.sol"; +import {ParachainClient} from "../ParachainClient.sol"; +// This contract is a hack which allows us to ABI-encode `ParachainClient.Proof` in the off-chain relayer. +// It is only used to generate client bindings and is never deployed. contract OpaqueProof { // solhint-disable-next-line no-empty-blocks function dummy(ParachainClient.Proof memory proof) public pure {} diff --git a/core/packages/contracts/test/InboundQueue.t.sol b/core/packages/contracts/test/InboundQueue.t.sol index bfef958ff..d8bb15aac 100644 --- a/core/packages/contracts/test/InboundQueue.t.sol +++ b/core/packages/contracts/test/InboundQueue.t.sol @@ -10,6 +10,7 @@ import {IParachainClient} from "../src/IParachainClient.sol"; import {ParaID} from "../src/Types.sol"; import {ParachainClientMock} from "./mocks/ParachainClientMock.sol"; import {IRecipient, RecipientMock} from "./mocks/RecipientMock.sol"; +import {Registry} from "../src/Registry.sol"; contract InboundQueueTest is Test { InboundQueue public channel; @@ -17,23 +18,28 @@ contract InboundQueueTest is Test { Vault public vault; - event MessageDispatched(ParaID indexed origin, uint64 indexed nonce, InboundQueue.DispatchResult result); + event MessageDispatched(ParaID origin, uint64 nonce, InboundQueue.DispatchResult result); ParaID public constant ORIGIN = ParaID.wrap(1001); bytes32[] public proof = [bytes32(0x2f9ee6cfdf244060dc28aa46347c5219e303fc95062dd672b4e406ca5c29764b)]; bytes public message = bytes("message"); bytes public parachainHeaderProof = bytes("validProof"); + bytes32 constant RECIPIENT = keccak256("RecipientMock"); + function setUp() public { + Registry registry = new Registry(); + registry.grantRole(registry.REGISTER_ROLE(), address(this)); + IParachainClient parachainClient = new ParachainClientMock(BeefyClient(address(0)), 0); recipient = new RecipientMock(); + registry.registerContract(RECIPIENT, address(recipient)); vault = new Vault(); deal(address(this), 100 ether); - channel = new InboundQueue(parachainClient, vault, 1 ether); - channel.updateHandler(1, IRecipient(recipient)); + channel = new InboundQueue(registry, parachainClient, vault, 1 ether); vault.grantRole(vault.WITHDRAW_ROLE(), address(channel)); } @@ -43,7 +49,7 @@ contract InboundQueueTest is Test { address relayer = makeAddr("alice"); hoax(relayer, 1 ether); - channel.submit(InboundQueue.Message(ORIGIN, 1, 1, message), proof, parachainHeaderProof); + channel.submit(InboundQueue.Message(ORIGIN, 1, RECIPIENT, message), proof, parachainHeaderProof); assertEq(vault.balances(ORIGIN), 49 ether); assertEq(relayer.balance, 2 ether); @@ -56,7 +62,7 @@ contract InboundQueueTest is Test { hoax(relayer, 1 ether); vm.expectRevert(InboundQueue.InvalidProof.selector); - channel.submit(InboundQueue.Message(ORIGIN, 1, 1, message), proof, bytes("badProof")); + channel.submit(InboundQueue.Message(ORIGIN, 1, RECIPIENT, message), proof, bytes("badProof")); } function testSubmitShouldFailInvalidNonce() public { @@ -66,7 +72,7 @@ contract InboundQueueTest is Test { hoax(relayer, 1 ether); vm.expectRevert(InboundQueue.InvalidNonce.selector); - channel.submit(InboundQueue.Message(ORIGIN, 2, 1, message), proof, parachainHeaderProof); + channel.submit(InboundQueue.Message(ORIGIN, 2, RECIPIENT, message), proof, parachainHeaderProof); } // Test that submission fails if origin does not have sufficient funds to pay relayer @@ -77,20 +83,20 @@ contract InboundQueueTest is Test { hoax(relayer, 1 ether); vm.expectRevert(Vault.InsufficientBalance.selector); - channel.submit(InboundQueue.Message(ORIGIN, 1, 1, message), proof, parachainHeaderProof); + channel.submit(InboundQueue.Message(ORIGIN, 1, RECIPIENT, message), proof, parachainHeaderProof); } function testSubmitShouldNotFailOnHandlerFailure() public { vault.deposit{value: 50 ether}(ORIGIN); recipient.setShouldFail(); - vm.expectEmit(true, true, false, true); + vm.expectEmit(); emit MessageDispatched(ORIGIN, 1, InboundQueue.DispatchResult.Failure); address relayer = makeAddr("alice"); hoax(relayer, 1 ether); - channel.submit(InboundQueue.Message(ORIGIN, 1, 1, message), proof, parachainHeaderProof); + channel.submit(InboundQueue.Message(ORIGIN, 1, RECIPIENT, message), proof, parachainHeaderProof); assertEq(vault.balances(ORIGIN), 49 ether); assertEq(relayer.balance, 2 ether); @@ -100,13 +106,13 @@ contract InboundQueueTest is Test { vault.deposit{value: 50 ether}(ORIGIN); recipient.setShouldConsumeAllGas(); - vm.expectEmit(true, true, false, true); + vm.expectEmit(); emit MessageDispatched(ORIGIN, 1, InboundQueue.DispatchResult.Failure); address relayer = makeAddr("alice"); hoax(relayer, 1 ether); - channel.submit(InboundQueue.Message(ORIGIN, 1, 1, message), proof, parachainHeaderProof); + channel.submit(InboundQueue.Message(ORIGIN, 1, RECIPIENT, message), proof, parachainHeaderProof); assertEq(vault.balances(ORIGIN), 49 ether); assertEq(relayer.balance, 2 ether); diff --git a/core/packages/contracts/test/NativeTokens.t.sol b/core/packages/contracts/test/NativeTokens.t.sol index ce7d8f58d..9d48582e0 100644 --- a/core/packages/contracts/test/NativeTokens.t.sol +++ b/core/packages/contracts/test/NativeTokens.t.sol @@ -10,6 +10,8 @@ import {IOutboundQueue} from "../src/IOutboundQueue.sol"; import {NativeTokens} from "../src/NativeTokens.sol"; import {TokenVault} from "../src/TokenVault.sol"; import {ParaID} from "../src/Types.sol"; +import {Registry} from "../src/Registry.sol"; +import {Gateway} from "../src/Gateway.sol"; import {OutboundQueueMock} from "./mocks/OutboundQueueMock.sol"; @@ -18,6 +20,7 @@ contract NativeTokensTest is Test { event Unlocked(address recipient, address token, uint128 amount); event Created(address token); + Registry registry; TokenVault private vault; NativeTokens private nativeTokens; IOutboundQueue private outboundQueue; @@ -29,11 +32,16 @@ contract NativeTokensTest is Test { bytes private constant recipient = "/Alice"; function setUp() public { + registry = new Registry(); + registry.grantRole(registry.REGISTER_ROLE(), address(this)); + token = new WETH9(); outboundQueue = new OutboundQueueMock(); + registry.registerContract(keccak256("OutboundQueue"), address(outboundQueue)); + vault = new TokenVault(); - nativeTokens = new NativeTokens(vault, outboundQueue, ASSET_HUB, 1); + nativeTokens = new NativeTokens(registry, vault, ASSET_HUB, 1, 0x0000, 0x0000); vault.grantRole(vault.WITHDRAW_ROLE(), address(nativeTokens)); vault.grantRole(vault.DEPOSIT_ROLE(), address(nativeTokens)); @@ -52,7 +60,7 @@ contract NativeTokensTest is Test { function testHandleRevertsUnknownOrigin() public { NativeTokens.Message memory message; - vm.expectRevert(NativeTokens.Unauthorized.selector); + vm.expectRevert(Gateway.Unauthorized.selector); nativeTokens.handle(ParaID.wrap(4056), abi.encode(message)); } diff --git a/core/packages/contracts/test/OutboundQueue.t.sol b/core/packages/contracts/test/OutboundQueue.t.sol index 7400731e9..2f2413d5d 100644 --- a/core/packages/contracts/test/OutboundQueue.t.sol +++ b/core/packages/contracts/test/OutboundQueue.t.sol @@ -6,6 +6,7 @@ import {Test} from "forge-std/Test.sol"; import {OutboundQueue} from "../src/OutboundQueue.sol"; import {Vault} from "../src/Vault.sol"; import {ParaID} from "../src/Types.sol"; +import {Registry} from "../src/Registry.sol"; contract OutboundQueueTest is Test { Vault public vault; @@ -15,8 +16,11 @@ contract OutboundQueueTest is Test { bytes message = bytes("message"); function setUp() public { + Registry registry = new Registry(); + registry.grantRole(registry.REGISTER_ROLE(), address(this)); + vault = new Vault(); - channel = new OutboundQueue(vault, 1 ether); + channel = new OutboundQueue(registry, vault, 1 ether); channel.grantRole(channel.SUBMIT_ROLE(), address(this)); } diff --git a/core/packages/contracts/test/ParachainClient.t.sol b/core/packages/contracts/test/ParachainClient.t.sol index 49dffd544..5232d31cb 100644 --- a/core/packages/contracts/test/ParachainClient.t.sol +++ b/core/packages/contracts/test/ParachainClient.t.sol @@ -43,11 +43,15 @@ contract ParachainClientTest is Test { digestItems: digestItems }); + bytes memory headerExpected = + hex"1df01d40273b074708115135fd7f76801ad4e4f1266a771a037962ee3a03259daae334007b2d59d4de7c629b55a9bc9b76d932616f2011a26f09b52da36e070d6a7eee0d9d1c5d256003f68dda03dc33810a88a61f73791dc7ff92b04232a6b1b4f4b3c00c066175726120c1f05e080000000004525053529073a902d5a4fa8fea942d01ad3c1dc32b51192c3a98c39fcc59299006ed391a5e2e00550105617572610101fcfbfaf1ad15d24cb4980436c18aec6211e2255f648df0e05e73a7858fba8c31726925f1a825383d0d3cb590502b18978101a6391fbeef5ab53e14c05124188c"; + assertEq( keccak256( bytes.concat( ScaleCodec.encodeU32(BRIDGE_HUB_PARA_ID), - hex"1df01d40273b074708115135fd7f76801ad4e4f1266a771a037962ee3a03259daae334007b2d59d4de7c629b55a9bc9b76d932616f2011a26f09b52da36e070d6a7eee0d9d1c5d256003f68dda03dc33810a88a61f73791dc7ff92b04232a6b1b4f4b3c00c066175726120c1f05e080000000004525053529073a902d5a4fa8fea942d01ad3c1dc32b51192c3a98c39fcc59299006ed391a5e2e00550105617572610101fcfbfaf1ad15d24cb4980436c18aec6211e2255f648df0e05e73a7858fba8c31726925f1a825383d0d3cb590502b18978101a6391fbeef5ab53e14c05124188c" + ScaleCodec.encodeCompactUint(headerExpected.length), + headerExpected ) ), parachainClient.createParachainHeaderMerkleLeaf_public(header) diff --git a/core/packages/contracts/test/UpgradeProxy.t.sol b/core/packages/contracts/test/UpgradeProxy.t.sol index 5718a0fb9..89e0de6f0 100644 --- a/core/packages/contracts/test/UpgradeProxy.t.sol +++ b/core/packages/contracts/test/UpgradeProxy.t.sol @@ -6,37 +6,47 @@ import {AccessControl} from "openzeppelin/access/AccessControl.sol"; import {Test} from "forge-std/Test.sol"; import {UpgradeProxy} from "../src/UpgradeProxy.sol"; -import {IUpgradeTask} from "../src/IUpgradeTask.sol"; -import {IVault, Vault} from "../src/Vault.sol"; +import {UpgradeTask} from "../src/UpgradeTask.sol"; +import {Vault} from "../src/Vault.sol"; import {OutboundQueue} from "../src/OutboundQueue.sol"; import {ParaID} from "../src/Types.sol"; +import {Registry} from "../src/Registry.sol"; +import {Gateway} from "../src/Gateway.sol"; import {UpgradeTaskMock, FailingUpgradeTaskMock} from "./mocks/UpgradeTaskMock.sol"; contract UpgradeProxyTest is Test { UpgradeProxy public upgradeProxy; - IUpgradeTask public upgradeTask; - IUpgradeTask public failedUpgradeTask; + UpgradeTask public upgradeTask; + UpgradeTask public failedUpgradeTask; OutboundQueue public outboundQueue; ParaID origin = ParaID.wrap(1001); function setUp() public { - upgradeProxy = new UpgradeProxy(origin); - outboundQueue = new OutboundQueue(new Vault(), 1 ether); + Registry registry = new Registry(); + registry.grantRole(registry.REGISTER_ROLE(), address(this)); + + upgradeProxy = new UpgradeProxy(registry, origin); + registry.registerContract(keccak256("UpgradeProxy"), address(upgradeProxy)); + + outboundQueue = new OutboundQueue(registry, new Vault(), 1 ether); + registry.registerContract(keccak256("OutboundQueue"), address(outboundQueue)); outboundQueue.grantRole(outboundQueue.ADMIN_ROLE(), address(upgradeProxy)); outboundQueue.revokeRole(outboundQueue.ADMIN_ROLE(), address(this)); upgradeProxy.grantRole(upgradeProxy.SENDER_ROLE(), address(this)); + registry.grantRole(outboundQueue.ADMIN_ROLE(), address(upgradeProxy)); + // create upgrade tasks - upgradeTask = new UpgradeTaskMock(outboundQueue); - failedUpgradeTask = new FailingUpgradeTaskMock(); + upgradeTask = new UpgradeTaskMock(registry); + failedUpgradeTask = new FailingUpgradeTaskMock(registry); } - function createUpgradeMessage(IUpgradeTask task) internal pure returns (bytes memory) { + function createUpgradeMessage(UpgradeTask task) internal pure returns (bytes memory) { return abi.encode( UpgradeProxy.Message(UpgradeProxy.Action.Upgrade, abi.encode(UpgradeProxy.UpgradePayload(address(task)))) ); @@ -49,7 +59,7 @@ contract UpgradeProxyTest is Test { } function testUpgradeFailBadOrigin() public { - vm.expectRevert(UpgradeProxy.Unauthorized.selector); + vm.expectRevert(Gateway.Unauthorized.selector); upgradeProxy.handle(ParaID.wrap(3), hex"deadbeef"); } diff --git a/core/packages/contracts/test/mocks/UpgradeTaskMock.sol b/core/packages/contracts/test/mocks/UpgradeTaskMock.sol index ca7f9542d..782768fbd 100644 --- a/core/packages/contracts/test/mocks/UpgradeTaskMock.sol +++ b/core/packages/contracts/test/mocks/UpgradeTaskMock.sol @@ -3,24 +3,26 @@ pragma solidity ^0.8.19; import {AccessControl} from "openzeppelin/access/AccessControl.sol"; -import {IUpgradeTask} from "../../src/IUpgradeTask.sol"; +import {UpgradeTask} from "../../src/UpgradeTask.sol"; +import {Registry} from "../../src/Registry.sol"; import {OutboundQueue} from "../../src/OutboundQueue.sol"; +import {ParaID} from "../../src/Types.sol"; -contract UpgradeTaskMock is IUpgradeTask, AccessControl { - OutboundQueue public immutable outboundQueue; - - constructor(OutboundQueue _outboundQueue) { - outboundQueue = _outboundQueue; - } +contract UpgradeTaskMock is UpgradeTask { + constructor(Registry registry) UpgradeTask(registry) {} + function handle(ParaID origin, bytes calldata message) external override onlyRole(SENDER_ROLE) {} // In this simple upgrade we just update a fee parameter - function run() external { - outboundQueue.updateFee(2 ether); + function run() external override { + OutboundQueue(resolve(keccak256("OutboundQueue"))).updateFee(2 ether); } } -contract FailingUpgradeTaskMock is IUpgradeTask, AccessControl { - function run() external pure { +contract FailingUpgradeTaskMock is UpgradeTask { + constructor(Registry registry) UpgradeTask(registry) {} + function handle(ParaID origin, bytes calldata message) external override onlyRole(SENDER_ROLE) {} + + function run() external pure override { revert("failed"); } } diff --git a/core/packages/test/README.md b/core/packages/test/README.md index bbab59fca..5b117d3cc 100644 --- a/core/packages/test/README.md +++ b/core/packages/test/README.md @@ -79,13 +79,6 @@ The `start-services.sh` script writes the following logs: ### Common issues -Sometimes during development tests will fail for transfers in the substrate->ethereum direction. If you see this, look in `parachain-relay.log` for the following error: -``` -{"@timestamp":"2022-08-26T15:10:50.263740077+02:00","args":"[--api ws://127.0.0.1:11144 --block 0xe2e21a61b017699961b6d87c6aecbae18f2ce0c89bd87e0e8b0d808c26e2aad3]","level":"error","message":"Failed to query events.","name":"snowbridge-query-events","stdErr":"Error: Metadata(IncompatibleMetadata)\n","stdOut":""} -``` - -That means a dependency of the relayer has obsolete parachain metadata and needs to be refreshed. Please refer [here](../../../parachain/README.md#Chain_metadata) for steps to fix. - ## Running E2E tests against Ropsten To run the E2E tests on Ropsten you need to have separate accounts for the relayers, an account for deployment and one for running the E2E test stack. You will also require an [Infura](https://infura.io/) account and project. diff --git a/core/packages/test/config/parachain-relay.json b/core/packages/test/config/parachain-relay.json index b4ec24c62..71acb81de 100644 --- a/core/packages/test/config/parachain-relay.json +++ b/core/packages/test/config/parachain-relay.json @@ -14,7 +14,7 @@ "InboundQueue": null }, "beefy-activation-block": 0, - "lane-id": 1000 + "lane-id": 0 }, "sink": { "ethereum": { diff --git a/core/packages/test/scripts/build-binary.sh b/core/packages/test/scripts/build-binary.sh index e47b27bc8..0ee9f98db 100755 --- a/core/packages/test/scripts/build-binary.sh +++ b/core/packages/test/scripts/build-binary.sh @@ -59,17 +59,6 @@ build_relayer() cp $relay_bin "$output_bin_dir" } -build_query_tool() { - pushd $root_dir/parachain - echo "Building query tool" - cargo build \ - --manifest-path tools/query-events/Cargo.toml \ - --release --features bridgehub-rococo-local \ - --bin snowbridge-query-events - cp "target/release/snowbridge-query-events" "$output_bin_dir" - popd -} - build_geth() { if [ ! -f "$geth_dir/geth" ]; then echo "Building geth binary" @@ -86,7 +75,6 @@ install_binary() { build_cumulus_from_source build_relaychain build_relayer - build_query_tool } if [ -z "${from_start_services:-}" ]; then diff --git a/core/packages/test/scripts/set-env.sh b/core/packages/test/scripts/set-env.sh index c84f68acb..2040b7996 100755 --- a/core/packages/test/scripts/set-env.sh +++ b/core/packages/test/scripts/set-env.sh @@ -1,6 +1,6 @@ root_dir="$(realpath ../../..)" bridge_hub_runtime="${PARACHAIN_RUNTIME:-bridge-hub-rococo-local}" -relaychain_version="${POLKADOT_VER:-v0.9.42}" +relaychain_version="${POLKADOT_VER:-v0.9.43}" relaychain_dir="$root_dir/parachain/.cargo/$relaychain_version" relaychain_bin="${POLKADOT_BIN:-$relaychain_dir/bin/polkadot}" cumulus_version="${CUMULUS_VER:-snowbridge}" @@ -69,14 +69,19 @@ export RANDAO_COMMIT_EXP=8 export BRIDGE_HUB_PARAID=$bridgehub_para_id ## OutboundChannel -export RELAYER_FEE=100 +export RELAYER_FEE=1 ## InboundChannel -export RELAYER_REWARD=100 +export RELAYER_REWARD=1 ## NativeTokens export ASSET_HUB_PARAID=$statemine_para_id -export CREATE_TOKEN_FEE=100 +export CREATE_TOKEN_FEE=1 +export CREATE_CALL_INDEX="0x3500" +export SET_METADATA_CALL_INDEX="0x3511" + +## Vault +export BRIDGE_HUB_INITIAL_DEPOSIT=1000 address_for() { diff --git a/core/packages/test/scripts/start-relayer.sh b/core/packages/test/scripts/start-relayer.sh index 3b012ffe7..960c352f4 100755 --- a/core/packages/test/scripts/start-relayer.sh +++ b/core/packages/test/scripts/start-relayer.sh @@ -17,7 +17,25 @@ config_relayer(){ ' \ config/beefy-relay.json > $output_dir/beefy-relay.json - # Configure parachain relay + # Configure parachain relay (bridge hub) + jq \ + --arg k1 "$(address_for InboundQueue)" \ + --arg k2 "$(address_for BeefyClient)" \ + --arg eth_endpoint_ws $eth_endpoint_ws \ + --arg laneID $BRIDGE_HUB_PARAID \ + --arg eth_gas_limit $eth_gas_limit \ + ' + .source.contracts.InboundQueue = $k1 + | .source.contracts.BeefyClient = $k2 + | .sink.contracts.InboundQueue = $k1 + | .source.ethereum.endpoint = $eth_endpoint_ws + | .sink.ethereum.endpoint = $eth_endpoint_ws + | .sink.ethereum."gas-limit" = $eth_gas_limit + | .source."lane-id" = $laneID + ' \ + config/parachain-relay.json > $output_dir/parachain-relay-bridge-hub.json + + # Configure parachain relay (asset hub) jq \ --arg k1 "$(address_for InboundQueue)" \ --arg k2 "$(address_for BeefyClient)" \ @@ -33,7 +51,7 @@ config_relayer(){ | .sink.ethereum."gas-limit" = $eth_gas_limit | .source."lane-id" = $laneID ' \ - config/parachain-relay.json > $output_dir/parachain-relay.json + config/parachain-relay.json > $output_dir/parachain-relay-asset-hub.json # Configure beacon relay jq \ @@ -75,16 +93,30 @@ start_relayer() done ) & - # Launch parachain relay + # Launch parachain relay for bridgehub + ( + : > "$output_dir"/parachain-relay-bridge-hub.log + while : + do + echo "Starting parachain-relay (bridgehub) at $(date)" + "${relay_bin}" run parachain \ + --config "$output_dir/parachain-relay-bridge-hub.json" \ + --ethereum.private-key $parachain_relay_eth_key \ + >> "$output_dir"/parachain-relay-bridge-hub.log 2>&1 || true + sleep 20 + done + ) & + + # Launch parachain relay for statemint ( - : > "$output_dir"/parachain-relay.log + : > "$output_dir"/parachain-relay-asset-hub.log while : do - echo "Starting parachain relay at $(date)" + echo "Starting parachain relay (asset-hub) at $(date)" "${relay_bin}" run parachain \ - --config "$output_dir/parachain-relay.json" \ + --config "$output_dir/parachain-relay-asset-hub.json" \ --ethereum.private-key $parachain_relay_eth_key \ - >> "$output_dir"/parachain-relay.log 2>&1 || true + >> "$output_dir"/parachain-relay-asset-hub.log 2>&1 || true sleep 20 done ) & diff --git a/core/packages/test/scripts/start-services.sh b/core/packages/test/scripts/start-services.sh index 6c81c0f04..cdfe5e1f4 100755 --- a/core/packages/test/scripts/start-services.sh +++ b/core/packages/test/scripts/start-services.sh @@ -47,7 +47,7 @@ wait_contract_deployed # 5. config beefy client echo "Config beefy client" source scripts/configure-beefy.sh -configure_beefy & +configure_beefy # 6. config beacon client echo "Config beacon client" diff --git a/core/pnpm-lock.yaml b/core/pnpm-lock.yaml index 3665c4533..3191e76e5 100644 --- a/core/pnpm-lock.yaml +++ b/core/pnpm-lock.yaml @@ -4132,7 +4132,7 @@ packages: /axios@1.1.2: resolution: {integrity: sha512-bznQyETwElsXl2RK7HLLwb5GPpOLlycxHCtrpDR/4RqqBzjARaOTo3jz4IgtntWUYee7Ne4S8UHd92VCuzPaWA==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2(debug@4.3.4) form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -6202,6 +6202,18 @@ packages: optional: true dev: true + /follow-redirects@1.15.2(debug@4.3.4): + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dependencies: + debug: 4.3.4(supports-color@8.1.1) + dev: true + /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: @@ -6304,7 +6316,7 @@ packages: engines: {node: '>=10'} dependencies: at-least-node: 1.0.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 dev: true @@ -6388,6 +6400,8 @@ packages: dependencies: nan: 2.17.0 node-pre-gyp: 0.13.0 + transitivePeerDependencies: + - supports-color dev: true bundledDependencies: - node-pre-gyp @@ -7595,19 +7609,19 @@ packages: /jsonfile@2.4.0: resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonfile@3.0.1: resolution: {integrity: sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonfile@6.1.0: @@ -7615,7 +7629,7 @@ packages: dependencies: universalify: 2.0.0 optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonparse@1.3.1: @@ -7677,7 +7691,7 @@ packages: /klaw@1.3.1: resolution: {integrity: sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /kuler@2.0.0: @@ -9335,6 +9349,8 @@ packages: prom-client: 14.2.0 optionalDependencies: gc-stats: 1.4.0 + transitivePeerDependencies: + - supports-color dev: true /promise-inflight@1.0.1: @@ -9559,7 +9575,7 @@ packages: resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} engines: {node: '>=0.10'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 micromatch: 3.1.10 readable-stream: 2.3.7 transitivePeerDependencies: diff --git a/cumulus b/cumulus index c755a6bc9..859fb16c2 160000 --- a/cumulus +++ b/cumulus @@ -1 +1 @@ -Subproject commit c755a6bc93944366ca1e732ece250c4d0472db5c +Subproject commit 859fb16c2d9373c4acbfed630cd2d8ca1f20c4cc diff --git a/flake.lock b/flake.lock index eeef40664..3f17f0cf3 100644 --- a/flake.lock +++ b/flake.lock @@ -127,11 +127,11 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1684376381, - "narHash": "sha256-XVFTXADfvBXKwo4boqfg80awUbT+JgQvlQ8uZ+Xgo1s=", + "lastModified": 1686795910, + "narHash": "sha256-jDa40qRZ0GRQtP9EMZdf+uCbvzuLnJglTUI2JoHfWDc=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "7c9a265c2eaa5783bc18593b1aec39a85653c076", + "rev": "5c2b97c0a9bc5217fc3dfb1555aae0fb756d99f9", "type": "github" }, "original": { diff --git a/parachain/Cargo.lock b/parachain/Cargo.lock index 230fca70e..18ac0f676 100644 --- a/parachain/Cargo.lock +++ b/parachain/Cargo.lock @@ -12,22 +12,13 @@ dependencies = [ "regex", ] -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli 0.26.2", -] - [[package]] name = "addr2line" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.2", + "gimli", ] [[package]] @@ -95,55 +86,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "anstream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" - -[[package]] -name = "anstyle-parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - [[package]] name = "anyhow" version = "1.0.69" @@ -165,15 +107,6 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" -[[package]] -name = "array-init" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" -dependencies = [ - "nodrop", -] - [[package]] name = "arrayref" version = "0.3.6" @@ -192,16 +125,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" -[[package]] -name = "async-lock" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" -dependencies = [ - "event-listener", - "futures-lite", -] - [[package]] name = "async-trait" version = "0.1.64" @@ -236,12 +159,12 @@ version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ - "addr2line 0.19.0", + "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", - "object 0.30.3", + "object", "rustc-demangle", ] @@ -251,39 +174,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base58" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" - [[package]] name = "base64" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - [[package]] name = "bincode" version = "1.3.3" @@ -388,16 +290,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -[[package]] -name = "bstr" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.12.0" @@ -446,7 +338,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8790cf1286da485c72cf5fc7aeba308438800036ec67d89425924c4807268c9" dependencies = [ - "smallvec 1.10.0", + "smallvec", ] [[package]] @@ -467,48 +359,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "clap" -version = "4.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap_builder" -version = "4.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab" -dependencies = [ - "anstream", - "anstyle", - "bitflags", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.18", -] - -[[package]] -name = "clap_lex" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -519,12 +369,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -553,16 +397,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -587,15 +421,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cranelift-entity" -version = "0.92.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e39cfc857e7e539aa623e03bb6bec11f54aef3dfdef41adcfa7b594af3b54" -dependencies = [ - "serde", -] - [[package]] name = "cranelift-entity" version = "0.95.1" @@ -732,41 +557,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", -] - [[package]] name = "der" version = "0.7.1" @@ -841,12 +631,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - [[package]] name = "dyn-clonable" version = "0.9.0" @@ -976,17 +760,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "errno" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.45.0", -] - [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -1037,23 +810,17 @@ dependencies = [ "uint", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "expander" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" dependencies = [ "blake2", "fs-err", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -1068,15 +835,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "ff" version = "0.13.0" @@ -1099,12 +857,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "form_urlencoded" version = "1.1.0" @@ -1117,7 +869,7 @@ dependencies = [ [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "frame-support", "frame-support-procedural", @@ -1129,13 +881,13 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto 23.0.0", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-runtime 24.0.0", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "sp-storage", "static_assertions", ] @@ -1154,7 +906,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "bitflags", "environmental", @@ -1169,31 +921,32 @@ dependencies = [ "paste", "scale-info", "serde", - "smallvec 1.10.0", + "smallvec", "sp-api", - "sp-arithmetic 16.0.0", - "sp-core 21.0.0", + "sp-arithmetic", + "sp-core", "sp-core-hashing-proc-macro", - "sp-debug-derive 8.0.0", + "sp-debug-derive", "sp-inherents", - "sp-io 23.0.0", - "sp-runtime 24.0.0", + "sp-io", + "sp-runtime", "sp-staking", - "sp-state-machine 0.28.0", - "sp-std 8.0.0", - "sp-tracing 10.0.0", - "sp-weights 20.0.0", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-weights", "tt-call", ] [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse", + "expander", "frame-support-procedural-tools", "itertools", "macro_magic", @@ -1206,7 +959,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -1218,7 +971,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "proc-macro2", "quote", @@ -1228,7 +981,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "cfg-if", "frame-support", @@ -1236,12 +989,12 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", "sp-version", - "sp-weights 20.0.0", + "sp-weights", ] [[package]] @@ -1305,21 +1058,6 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-macro" version = "0.3.28" @@ -1405,20 +1143,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "stable_deref_trait", ] [[package]] @@ -1432,19 +1158,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "globset" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - [[package]] name = "group" version = "0.13.0" @@ -1456,31 +1169,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "h2" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hash-db" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" - [[package]] name = "hash-db" version = "0.16.0" @@ -1596,86 +1284,12 @@ dependencies = [ "hmac 0.8.1", ] -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" -dependencies = [ - "http", - "hyper", - "log", - "rustls", - "rustls-native-certs", - "tokio", - "tokio-rustls", - "webpki-roots", -] - [[package]] name = "iana-time-zone" version = "0.1.53" @@ -1700,12 +1314,6 @@ dependencies = [ "cxx-build", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.3.0" @@ -1765,15 +1373,6 @@ dependencies = [ "serde", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "integer-sqrt" version = "0.1.5" @@ -1791,26 +1390,14 @@ checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] -name = "is-terminal" -version = "0.4.6" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix 0.37.4", - "windows-sys 0.45.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] @@ -1830,138 +1417,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonrpsee" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" -dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-http-client", - "jsonrpsee-proc-macros", - "jsonrpsee-server", - "jsonrpsee-types", - "tracing", -] - -[[package]] -name = "jsonrpsee-client-transport" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" -dependencies = [ - "futures-util", - "http", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "rustls-native-certs", - "soketto", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "webpki-roots", -] - -[[package]] -name = "jsonrpsee-core" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" -dependencies = [ - "anyhow", - "arrayvec 0.7.2", - "async-lock", - "async-trait", - "beef", - "futures-channel", - "futures-timer", - "futures-util", - "globset", - "hyper", - "jsonrpsee-types", - "parking_lot 0.12.1", - "rand 0.8.5", - "rustc-hash", - "serde", - "serde_json", - "soketto", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "jsonrpsee-http-client" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" -dependencies = [ - "async-trait", - "hyper", - "hyper-rustls", - "jsonrpsee-core", - "jsonrpsee-types", - "rustc-hash", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "jsonrpsee-proc-macros" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" -dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "jsonrpsee-server" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb69dad85df79527c019659a992498d03f8495390496da2f07e6c24c2b356fc" -dependencies = [ - "futures-channel", - "futures-util", - "http", - "hyper", - "jsonrpsee-core", - "jsonrpsee-types", - "serde", - "serde_json", - "soketto", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tracing", -] - -[[package]] -name = "jsonrpsee-types" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" -dependencies = [ - "anyhow", - "beef", - "serde", - "serde_json", - "thiserror", - "tracing", -] - [[package]] name = "k256" version = "0.13.0" @@ -1996,12 +1451,6 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - [[package]] name = "libsecp256k1" version = "0.7.1" @@ -2009,7 +1458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", - "base64 0.13.1", + "base64", "digest 0.9.0", "hmac-drbg", "libsecp256k1-core", @@ -2074,12 +1523,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" -[[package]] -name = "linux-raw-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d" - [[package]] name = "lock_api" version = "0.4.9" @@ -2173,12 +1616,6 @@ dependencies = [ "rawpointer", ] -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.5.0" @@ -2191,16 +1628,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix 0.36.11", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", + "rustix", ] [[package]] @@ -2212,31 +1640,15 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memory-db" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" -dependencies = [ - "hash-db 0.15.2", - "hashbrown 0.12.3", -] - [[package]] name = "memory-db" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ - "hash-db 0.16.0", + "hash-db", ] -[[package]] -name = "memory_units" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" - [[package]] name = "merlin" version = "2.0.1" @@ -2272,18 +1684,6 @@ dependencies = [ "adler", ] -[[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", -] - [[package]] name = "nalgebra" version = "0.32.2" @@ -2311,12 +1711,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "nohash-hasher" version = "0.2.0" @@ -2370,7 +1764,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", - "num-bigint", "num-integer", "num-traits", ] @@ -2394,18 +1787,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "crc32fast", - "hashbrown 0.12.3", - "indexmap", - "memchr", -] - [[package]] name = "object" version = "0.30.3" @@ -2436,16 +1817,10 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "frame-benchmarking", "frame-support", @@ -2453,14 +1828,32 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-message-queue" +version = "7.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", ] [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "frame-benchmarking", "frame-support", @@ -2469,9 +1862,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-inherents", - "sp-io 23.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-io", + "sp-runtime", + "sp-std", "sp-timestamp", ] @@ -2514,23 +1907,6 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -2538,21 +1914,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec 1.10.0", - "winapi", + "parking_lot_core", ] [[package]] @@ -2564,8 +1926,8 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "smallvec 1.10.0", - "windows-sys 0.45.0", + "smallvec", + "windows-sys", ] [[package]] @@ -2598,26 +1960,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "pin-project-lite" version = "0.2.9" @@ -2647,9 +1989,9 @@ source = "git+https://github.com/paritytech/polkadot?branch=master#04f801427ded6 dependencies = [ "parity-scale-codec", "scale-info", - "sp-core 21.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] @@ -2664,9 +2006,9 @@ dependencies = [ "polkadot-core-primitives", "scale-info", "serde", - "sp-core 21.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] @@ -2699,30 +2041,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro-warning" version = "0.4.1" @@ -2909,21 +2227,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - [[package]] name = "rlp" version = "0.5.2" @@ -2968,58 +2271,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ "bitflags", - "errno 0.2.8", - "io-lifetimes", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustix" -version = "0.37.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c348b5dc624ecee40108aa2922fed8bad89d7fcc2b9f8cb18f632898ac4a37f9" -dependencies = [ - "bitflags", - "errno 0.3.0", + "errno", "io-lifetimes", "libc", - "linux-raw-sys 0.3.0", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rustls-native-certs" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.0", + "linux-raw-sys", + "windows-sys", ] [[package]] @@ -3052,29 +2308,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scale-bits" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", -] - -[[package]] -name = "scale-decode" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d823d4be477fc33321f93d08fb6c2698273d044f01362dc27573a750deb7c233" -dependencies = [ - "parity-scale-codec", - "scale-bits", - "scale-info", - "thiserror", -] - [[package]] name = "scale-info" version = "2.7.0" @@ -3101,32 +2334,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "scale-value" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a5e7810815bd295da73e4216d1dfbced3c7c7c7054d70fa5f6e4c58123fff4" -dependencies = [ - "either", - "frame-metadata", - "parity-scale-codec", - "scale-bits", - "scale-decode", - "scale-info", - "serde", - "thiserror", - "yap", -] - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] - [[package]] name = "schnellru" version = "0.2.1" @@ -3174,16 +2381,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sec1" version = "0.7.1" @@ -3225,29 +2422,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "security-framework" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.17" @@ -3272,17 +2446,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde-hex" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" -dependencies = [ - "array-init", - "serde", - "smallvec 0.6.14", -] - [[package]] name = "serde_derive" version = "1.0.164" @@ -3305,19 +2468,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - [[package]] name = "sha2" version = "0.8.2" @@ -3411,15 +2561,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - [[package]] name = "smallvec" version = "1.10.0" @@ -3441,15 +2582,33 @@ dependencies = [ "scale-info", "serde", "snowbridge-ethereum", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", "ssz-rs", "ssz-rs-derive", "static_assertions", ] +[[package]] +name = "snowbridge-control" +version = "4.0.0-dev" +dependencies = [ + "ethabi-decode", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "snowbridge-core", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", +] + [[package]] name = "snowbridge-core" version = "0.1.1" @@ -3462,9 +2621,10 @@ dependencies = [ "scale-info", "serde", "snowbridge-ethereum", - "sp-core 21.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-runtime", + "sp-std", + "xcm", ] [[package]] @@ -3485,10 +2645,10 @@ dependencies = [ "serde-big-array", "serde_json", "snowbridge-testutils", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", "wasm-bindgen-test", ] @@ -3512,11 +2672,11 @@ dependencies = [ "snowbridge-core", "snowbridge-ethereum", "snowbridge-testutils", - "sp-core 21.0.0", - "sp-io 23.0.0", + "sp-core", + "sp-io", "sp-keyring", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-runtime", + "sp-std", "ssz-rs", "ssz-rs-derive", "static_assertions", @@ -3539,11 +2699,11 @@ dependencies = [ "snowbridge-core", "snowbridge-ethereum", "snowbridge-router-primitives", - "sp-core 21.0.0", - "sp-io 23.0.0", + "sp-core", + "sp-io", "sp-keyring", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-runtime", + "sp-std", "xcm", ] @@ -3556,21 +2716,23 @@ dependencies = [ "frame-support", "frame-system", "hex-literal", + "pallet-message-queue", "parity-scale-codec", "rlp", "scale-info", "serde", "snowbridge-core", - "snowbridge-outbound-queue-merkle-proof", - "sp-core 21.0.0", - "sp-io 23.0.0", + "snowbridge-outbound-queue-merkle-tree", + "sp-core", + "sp-io", "sp-keyring", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-runtime", + "sp-std", + "xcm", ] [[package]] -name = "snowbridge-outbound-queue-merkle-proof" +name = "snowbridge-outbound-queue-merkle-tree" version = "0.1.1" dependencies = [ "array-bytes", @@ -3578,39 +2740,20 @@ dependencies = [ "hex", "hex-literal", "parity-scale-codec", - "sp-core 21.0.0", - "sp-runtime 24.0.0", -] - -[[package]] -name = "snowbridge-outbound-queue-rpc" -version = "0.1.0" -dependencies = [ - "hex", - "jsonrpsee", - "parity-scale-codec", - "parking_lot 0.11.2", - "serde_json", - "snowbridge-outbound-queue-merkle-proof", - "sp-core 21.0.0", - "sp-offchain", - "sp-runtime 24.0.0", + "scale-info", + "sp-core", + "sp-runtime", ] [[package]] -name = "snowbridge-query-events" +name = "snowbridge-outbound-queue-runtime-api" version = "0.1.0" dependencies = [ - "clap", - "futures", - "hex", - "hex-literal", "parity-scale-codec", - "serde", - "serde-hex", - "serde_json", - "subxt", - "tokio", + "snowbridge-outbound-queue-merkle-tree", + "sp-api", + "sp-core", + "sp-std", ] [[package]] @@ -3626,10 +2769,10 @@ dependencies = [ "scale-info", "serde", "snowbridge-core", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", "xcm", "xcm-executor", ] @@ -3644,48 +2787,22 @@ dependencies = [ "serde_json", ] -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "soketto" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" -dependencies = [ - "base64 0.13.1", - "bytes", - "futures", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha-1", -] - [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ - "hash-db 0.16.0", + "hash-db", "log", "parity-scale-codec", "scale-info", "sp-api-proc-macro", - "sp-core 21.0.0", + "sp-core", "sp-metadata-ir", - "sp-runtime 24.0.0", - "sp-state-machine 0.28.0", - "sp-std 8.0.0", - "sp-trie 22.0.0", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", "sp-version", "thiserror", ] @@ -3693,7 +2810,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "Inflector", "blake2", @@ -3704,110 +2821,37 @@ dependencies = [ "syn 2.0.18", ] -[[package]] -name = "sp-application-crypto" -version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f08604ba4bd856311946722958711a08bded5c929e1227f7a697c58deb09468" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 16.0.0", - "sp-io 17.0.0", - "sp-std 6.0.0", -] - [[package]] name = "sp-application-crypto" version = "23.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-std 8.0.0", -] - -[[package]] -name = "sp-arithmetic" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7796939f2e3b68a3b9410ea17a2063b78038cd366f57fa772dd3be0798bd3412" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ - "integer-sqrt", - "num-traits", "parity-scale-codec", "scale-info", "serde", - "sp-std 6.0.0", - "static_assertions", + "sp-core", + "sp-io", + "sp-std", ] [[package]] name = "sp-arithmetic" version = "16.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "integer-sqrt", "num-traits", "parity-scale-codec", "scale-info", "serde", - "sp-std 8.0.0", + "sp-std", "static_assertions", ] -[[package]] -name = "sp-core" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c96dc3debbe5c22ebf18f99e6a53199efe748e6e584a1902adb88cbad66ae7c" -dependencies = [ - "array-bytes", - "base58", - "bitflags", - "blake2", - "bounded-collections", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db 0.15.2", - "hash256-std-hasher", - "impl-serde", - "lazy_static", - "libsecp256k1", - "log", - "merlin", - "parity-scale-codec", - "parking_lot 0.12.1", - "primitive-types", - "rand 0.8.5", - "regex", - "scale-info", - "schnorrkel", - "secp256k1", - "secrecy", - "serde", - "sp-core-hashing 6.0.0", - "sp-debug-derive 6.0.0", - "sp-externalities 0.17.0", - "sp-runtime-interface 13.0.0", - "sp-std 6.0.0", - "sp-storage 11.0.0", - "ss58-registry", - "substrate-bip39", - "thiserror", - "tiny-bip39", - "zeroize", -] - [[package]] name = "sp-core" version = "21.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "array-bytes", "bitflags", @@ -3817,7 +2861,7 @@ dependencies = [ "dyn-clonable", "ed25519-zebra", "futures", - "hash-db 0.16.0", + "hash-db", "hash256-std-hasher", "impl-serde", "lazy_static", @@ -3825,7 +2869,7 @@ dependencies = [ "log", "merlin", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot", "paste", "primitive-types", "rand 0.8.5", @@ -3835,12 +2879,12 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-core-hashing 9.0.0", - "sp-debug-derive 8.0.0", - "sp-externalities 0.19.0", - "sp-runtime-interface 17.0.0", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-core-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", "ss58-registry", "substrate-bip39", "thiserror", @@ -3848,135 +2892,71 @@ dependencies = [ "zeroize", ] -[[package]] -name = "sp-core-hashing" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" -dependencies = [ - "blake2", - "byteorder", - "digest 0.10.6", - "sha2 0.10.6", - "sha3", - "sp-std 6.0.0", - "twox-hash", -] - [[package]] name = "sp-core-hashing" version = "9.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "blake2b_simd", "byteorder", "digest 0.10.6", "sha2 0.10.6", "sha3", - "sp-std 8.0.0", + "sp-std", "twox-hash", ] [[package]] name = "sp-core-hashing-proc-macro" version = "9.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "proc-macro2", "quote", - "sp-core-hashing 9.0.0", + "sp-core-hashing", "syn 2.0.18", ] -[[package]] -name = "sp-debug-derive" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fb9dc63d54de7d7bed62a505b6e0bd66c122525ea1abb348f6564717c3df2d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "sp-debug-derive" version = "8.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "proc-macro2", "quote", "syn 2.0.18", ] -[[package]] -name = "sp-externalities" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57052935c9c9b070ea6b339ef0da3bf241b7e065fc37f9c551669ee83ecfc3c1" -dependencies = [ - "environmental", - "parity-scale-codec", - "sp-std 6.0.0", - "sp-storage 11.0.0", -] - [[package]] name = "sp-externalities" version = "0.19.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 8.0.0", - "sp-storage 13.0.0", + "sp-std", + "sp-storage", ] [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-core 21.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-runtime", + "sp-std", "thiserror", ] -[[package]] -name = "sp-io" -version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578959f9a7e44fd2dd96e8b8bc893cea04fcd7c00a4ffbb0b91c5013899dd02b" -dependencies = [ - "bytes", - "ed25519", - "ed25519-dalek", - "futures", - "libsecp256k1", - "log", - "parity-scale-codec", - "secp256k1", - "sp-core 16.0.0", - "sp-externalities 0.17.0", - "sp-keystore 0.22.0", - "sp-runtime-interface 13.0.0", - "sp-state-machine 0.22.0", - "sp-std 6.0.0", - "sp-tracing 8.0.0", - "sp-trie 16.0.0", - "tracing", - "tracing-core", -] - [[package]] name = "sp-io" version = "23.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "bytes", "ed25519", @@ -3987,14 +2967,14 @@ dependencies = [ "parity-scale-codec", "rustversion", "secp256k1", - "sp-core 21.0.0", - "sp-externalities 0.19.0", - "sp-keystore 0.27.0", - "sp-runtime-interface 17.0.0", - "sp-state-machine 0.28.0", - "sp-std 8.0.0", - "sp-tracing 10.0.0", - "sp-trie 22.0.0", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", "tracing", "tracing-core", ] @@ -4002,113 +2982,52 @@ dependencies = [ [[package]] name = "sp-keyring" version = "24.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "lazy_static", - "sp-core 21.0.0", - "sp-runtime 24.0.0", + "sp-core", + "sp-runtime", "strum", ] -[[package]] -name = "sp-keystore" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480dbd54b281c638209fbcfce69902b82a0a1af0e22219d46825eadced3136b6" -dependencies = [ - "async-trait", - "futures", - "merlin", - "parity-scale-codec", - "parking_lot 0.12.1", - "schnorrkel", - "sp-core 16.0.0", - "sp-externalities 0.17.0", - "thiserror", -] - [[package]] name = "sp-keystore" version = "0.27.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "futures", "parity-scale-codec", - "parking_lot 0.12.1", - "sp-core 21.0.0", - "sp-externalities 0.19.0", + "parking_lot", + "sp-core", + "sp-externalities", "thiserror", ] [[package]] name = "sp-metadata-ir" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-std 8.0.0", -] - -[[package]] -name = "sp-offchain" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" -dependencies = [ - "sp-api", - "sp-core 21.0.0", - "sp-runtime 24.0.0", -] - -[[package]] -name = "sp-panic-handler" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abed79c3d5b3622f65ab065676addd9923b9b122cd257df23e2757ce487c6d2" -dependencies = [ - "backtrace", - "lazy_static", - "regex", + "sp-std", ] [[package]] name = "sp-panic-handler" version = "8.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "backtrace", "lazy_static", "regex", ] -[[package]] -name = "sp-runtime" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ab2fd44668d3e8674e2253a43852857a47d49be7db737e98bf157e4bcebefd" -dependencies = [ - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "paste", - "rand 0.8.5", - "scale-info", - "serde", - "sp-application-crypto 17.0.0", - "sp-arithmetic 12.0.0", - "sp-core 16.0.0", - "sp-io 17.0.0", - "sp-std 6.0.0", - "sp-weights 14.0.0", -] - [[package]] name = "sp-runtime" version = "24.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "either", "hash256-std-hasher", @@ -4119,68 +3038,36 @@ dependencies = [ "rand 0.8.5", "scale-info", "serde", - "sp-application-crypto 23.0.0", - "sp-arithmetic 16.0.0", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-std 8.0.0", - "sp-weights 20.0.0", -] - -[[package]] -name = "sp-runtime-interface" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb7707246cee4967a8cc71e3ef0e82f562e8b1020606447a6a12b99c7c1b443" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities 0.17.0", - "sp-runtime-interface-proc-macro 9.0.0", - "sp-std 6.0.0", - "sp-storage 11.0.0", - "sp-tracing 8.0.0", - "sp-wasm-interface 10.0.0", - "static_assertions", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", + "sp-weights", ] [[package]] name = "sp-runtime-interface" version = "17.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", "primitive-types", - "sp-externalities 0.19.0", - "sp-runtime-interface-proc-macro 11.0.0", - "sp-std 8.0.0", - "sp-storage 13.0.0", - "sp-tracing 10.0.0", - "sp-wasm-interface 14.0.0", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", "static_assertions", ] -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2773c90e5765847c5e8b4a24b553d38a9ca52ded47c142cfcfb7948f42827af9" -dependencies = [ - "Inflector", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "sp-runtime-interface-proc-macro" version = "11.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "Inflector", "proc-macro-crate", @@ -4192,186 +3079,108 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 21.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", -] - -[[package]] -name = "sp-state-machine" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c957b8b4c252507c12674948db427c5e34fd1760ce256922f1ec5f89f781a4f" -dependencies = [ - "hash-db 0.15.2", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "smallvec 1.10.0", - "sp-core 16.0.0", - "sp-externalities 0.17.0", - "sp-panic-handler 6.0.0", - "sp-std 6.0.0", - "sp-trie 16.0.0", - "thiserror", - "tracing", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] name = "sp-state-machine" version = "0.28.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ - "hash-db 0.16.0", + "hash-db", "log", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot", "rand 0.8.5", - "smallvec 1.10.0", - "sp-core 21.0.0", - "sp-externalities 0.19.0", - "sp-panic-handler 8.0.0", - "sp-std 8.0.0", - "sp-trie 22.0.0", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std", + "sp-trie", "thiserror", "tracing", ] -[[package]] -name = "sp-std" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" - [[package]] name = "sp-std" version = "8.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" - -[[package]] -name = "sp-storage" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c20cb0c562d1a159ecb2c7ca786828c81e432c535474967d2df3a484977cea4" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "ref-cast", - "serde", - "sp-debug-derive 6.0.0", - "sp-std 6.0.0", -] +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" [[package]] name = "sp-storage" version = "13.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 8.0.0", - "sp-std 8.0.0", + "sp-debug-derive", + "sp-std", ] [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "async-trait", "futures-timer", "log", "parity-scale-codec", "sp-inherents", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-runtime", + "sp-std", "thiserror", ] -[[package]] -name = "sp-tracing" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46bd547da89a9cda69b4ce4c91a5b7e1f86915190d83cd407b715d0c6bac042" -dependencies = [ - "parity-scale-codec", - "sp-std 6.0.0", - "tracing", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "sp-tracing" version = "10.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "parity-scale-codec", - "sp-std 8.0.0", + "sp-std", "tracing", "tracing-core", "tracing-subscriber", ] -[[package]] -name = "sp-trie" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8efbe5b6d29a18fea7c2f52e0098135f2f864b31d335d5105b40a349866ba874" -dependencies = [ - "ahash 0.8.3", - "hash-db 0.15.2", - "hashbrown 0.12.3", - "lazy_static", - "memory-db 0.31.0", - "nohash-hasher", - "parity-scale-codec", - "parking_lot 0.12.1", - "scale-info", - "schnellru", - "sp-core 16.0.0", - "sp-std 6.0.0", - "thiserror", - "tracing", - "trie-db 0.24.0", - "trie-root 0.17.0", -] - [[package]] name = "sp-trie" version = "22.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "ahash 0.8.3", - "hash-db 0.16.0", + "hash-db", "hashbrown 0.13.2", "lazy_static", - "memory-db 0.32.0", + "memory-db", "nohash-hasher", "parity-scale-codec", - "parking_lot 0.12.1", + "parking_lot", "scale-info", "schnellru", - "sp-core 21.0.0", - "sp-std 8.0.0", + "sp-core", + "sp-std", "thiserror", "tracing", - "trie-db 0.27.1", - "trie-root 0.18.0", + "trie-db", + "trie-root", ] [[package]] name = "sp-version" version = "22.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "impl-serde", "parity-scale-codec", @@ -4379,8 +3188,8 @@ dependencies = [ "scale-info", "serde", "sp-core-hashing-proc-macro", - "sp-runtime 24.0.0", - "sp-std 8.0.0", + "sp-runtime", + "sp-std", "sp-version-proc-macro", "thiserror", ] @@ -4388,7 +3197,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "8.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -4396,71 +3205,34 @@ dependencies = [ "syn 2.0.18", ] -[[package]] -name = "sp-wasm-interface" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbc05650b6338808892a7b04f0c56bb1f7f928bfa9ac58e0af2c1e5bef33229" -dependencies = [ - "anyhow", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std 6.0.0", - "wasmi", - "wasmtime 5.0.1", -] - [[package]] name = "sp-wasm-interface" version = "14.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std 8.0.0", - "wasmtime 8.0.1", -] - -[[package]] -name = "sp-weights" -version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ebab7696f915aa548494aef3ca8d15217baf10458fe6edb87e60587a47de358" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "smallvec 1.10.0", - "sp-arithmetic 12.0.0", - "sp-core 16.0.0", - "sp-debug-derive 6.0.0", - "sp-std 6.0.0", + "sp-std", + "wasmtime", ] [[package]] name = "sp-weights" version = "20.0.0" -source = "git+https://github.com/paritytech/substrate.git?branch=master#b72c475803947121b3b475eb33ba5fc48f1fe5b6" +source = "git+https://github.com/paritytech/substrate.git?branch=master#51b2f0ed6af8dd4facb18f1a489e192fd0673f7b" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "smallvec 1.10.0", - "sp-arithmetic 16.0.0", - "sp-core 21.0.0", - "sp-debug-derive 8.0.0", - "sp-std 8.0.0", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spki" version = "0.7.0" @@ -4520,12 +3292,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strum" version = "0.24.1" @@ -4575,80 +3341,6 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" -[[package]] -name = "subxt" -version = "0.27.1" -source = "git+https://github.com/paritytech/subxt.git?tag=v0.27.1#6d3c377c16a5e8f61dc264ebef36b8db5e593ba1" -dependencies = [ - "base58", - "blake2", - "derivative", - "frame-metadata", - "futures", - "getrandom 0.2.8", - "hex", - "impl-serde", - "jsonrpsee", - "parity-scale-codec", - "parking_lot 0.12.1", - "primitive-types", - "scale-bits", - "scale-decode", - "scale-info", - "scale-value", - "serde", - "serde_json", - "sp-core 16.0.0", - "sp-core-hashing 6.0.0", - "sp-runtime 18.0.0", - "subxt-macro", - "subxt-metadata", - "thiserror", - "tracing", -] - -[[package]] -name = "subxt-codegen" -version = "0.27.1" -source = "git+https://github.com/paritytech/subxt.git?tag=v0.27.1#6d3c377c16a5e8f61dc264ebef36b8db5e593ba1" -dependencies = [ - "darling", - "frame-metadata", - "heck", - "hex", - "jsonrpsee", - "parity-scale-codec", - "proc-macro-error", - "proc-macro2", - "quote", - "scale-info", - "subxt-metadata", - "syn 1.0.109", - "tokio", -] - -[[package]] -name = "subxt-macro" -version = "0.27.1" -source = "git+https://github.com/paritytech/subxt.git?tag=v0.27.1#6d3c377c16a5e8f61dc264ebef36b8db5e593ba1" -dependencies = [ - "darling", - "proc-macro-error", - "subxt-codegen", - "syn 1.0.109", -] - -[[package]] -name = "subxt-metadata" -version = "0.27.1" -source = "git+https://github.com/paritytech/subxt.git?tag=v0.27.1#6d3c377c16a5e8f61dc264ebef36b8db5e593ba1" -dependencies = [ - "frame-metadata", - "parity-scale-codec", - "scale-info", - "sp-core-hashing 6.0.0", -] - [[package]] name = "syn" version = "1.0.109" @@ -4759,97 +3451,32 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" dependencies = [ - "crunchy", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" -dependencies = [ - "autocfg", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.18", + "crunchy", ] [[package]] -name = "tokio-rustls" -version = "0.23.4" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "rustls", - "tokio", - "webpki", + "crunchy", ] [[package]] -name = "tokio-stream" -version = "0.1.12" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", + "tinyvec_macros", ] [[package]] -name = "tokio-util" -version = "0.7.7" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml_datetime" @@ -4868,29 +3495,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - [[package]] name = "tracing" version = "0.1.37" @@ -4898,7 +3502,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4960,7 +3563,7 @@ dependencies = [ "serde", "serde_json", "sharded-slab", - "smallvec 1.10.0", + "smallvec", "thread_local", "tracing", "tracing-core", @@ -4968,39 +3571,17 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "trie-db" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" -dependencies = [ - "hash-db 0.15.2", - "hashbrown 0.12.3", - "log", - "rustc-hex", - "smallvec 1.10.0", -] - [[package]] name = "trie-db" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" dependencies = [ - "hash-db 0.16.0", + "hash-db", "hashbrown 0.13.2", "log", "rustc-hex", - "smallvec 1.10.0", -] - -[[package]] -name = "trie-root" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" -dependencies = [ - "hash-db 0.15.2", + "smallvec", ] [[package]] @@ -5009,15 +3590,9 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ - "hash-db 0.16.0", + "hash-db", ] -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - [[package]] name = "tt-call" version = "1.0.9" @@ -5087,12 +3662,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "url" version = "2.3.1" @@ -5104,12 +3673,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "valuable" version = "0.1.0" @@ -5122,12 +3685,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "walkdir" version = "2.3.2" @@ -5139,16 +3696,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -5251,49 +3798,6 @@ dependencies = [ "quote", ] -[[package]] -name = "wasmi" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" -dependencies = [ - "parity-wasm", - "wasmi-validation", - "wasmi_core", -] - -[[package]] -name = "wasmi-validation" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" -dependencies = [ - "parity-wasm", -] - -[[package]] -name = "wasmi_core" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" -dependencies = [ - "downcast-rs", - "libm", - "memory_units", - "num-rational", - "num-traits", -] - -[[package]] -name = "wasmparser" -version = "0.96.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adde01ade41ab9a5d10ec8ed0bb954238cf8625b5cd5a13093d6de2ad9c2be1a" -dependencies = [ - "indexmap", - "url", -] - [[package]] name = "wasmparser" version = "0.102.0" @@ -5304,31 +3808,6 @@ dependencies = [ "url", ] -[[package]] -name = "wasmtime" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ffcc607adc9da024e87ca814592d4bc67f5c5b58e488f5608d5734a1ebc23e" -dependencies = [ - "anyhow", - "bincode", - "cfg-if", - "indexmap", - "libc", - "log", - "object 0.29.0", - "once_cell", - "paste", - "psm", - "serde", - "target-lexicon", - "wasmparser 0.96.0", - "wasmtime-environ 5.0.1", - "wasmtime-jit 5.0.1", - "wasmtime-runtime 5.0.1", - "windows-sys 0.42.0", -] - [[package]] name = "wasmtime" version = "8.0.1" @@ -5341,26 +3820,17 @@ dependencies = [ "indexmap", "libc", "log", - "object 0.30.3", + "object", "once_cell", "paste", "psm", "serde", "target-lexicon", - "wasmparser 0.102.0", - "wasmtime-environ 8.0.1", - "wasmtime-jit 8.0.1", - "wasmtime-runtime 8.0.1", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-asm-macros" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cb5dc4d79cd7b2453c395f64e9013d2ad90bd083be556d5565cb224ebe8d57" -dependencies = [ - "cfg-if", + "wasmparser", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys", ] [[package]] @@ -5372,25 +3842,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "wasmtime-environ" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9350c919553cddf14f78f9452119c8004d7ef6bfebb79a41a21819ed0c5604d8" -dependencies = [ - "anyhow", - "cranelift-entity 0.92.1", - "gimli 0.26.2", - "indexmap", - "log", - "object 0.29.0", - "serde", - "target-lexicon", - "thiserror", - "wasmparser 0.96.0", - "wasmtime-types 5.0.1", -] - [[package]] name = "wasmtime-environ" version = "8.0.1" @@ -5398,39 +3849,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" dependencies = [ "anyhow", - "cranelift-entity 0.95.1", - "gimli 0.27.2", + "cranelift-entity", + "gimli", "indexmap", "log", - "object 0.30.3", + "object", "serde", "target-lexicon", "thiserror", - "wasmparser 0.102.0", - "wasmtime-types 8.0.1", -] - -[[package]] -name = "wasmtime-jit" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ba5779ea786386432b94c9fc9ad5597346c319e8239db0d98d5be5cc109a7e" -dependencies = [ - "addr2line 0.17.0", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli 0.26.2", - "log", - "object 0.29.0", - "rustc-demangle", - "serde", - "target-lexicon", - "wasmtime-environ 5.0.1", - "wasmtime-jit-icache-coherence 5.0.1", - "wasmtime-runtime 5.0.1", - "windows-sys 0.42.0", + "wasmparser", + "wasmtime-types", ] [[package]] @@ -5439,30 +3867,21 @@ version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" dependencies = [ - "addr2line 0.19.0", + "addr2line", "anyhow", "bincode", "cfg-if", "cpp_demangle", - "gimli 0.27.2", + "gimli", "log", - "object 0.30.3", + "object", "rustc-demangle", "serde", "target-lexicon", - "wasmtime-environ 8.0.1", - "wasmtime-jit-icache-coherence 8.0.1", - "wasmtime-runtime 8.0.1", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-jit-debug" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9841a44c82c74101c10ad4f215392761a2523b3c6c838597962bdb6de75fdb3" -dependencies = [ - "once_cell", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys", ] [[package]] @@ -5474,17 +3893,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd4356c2493002da3b111d470c2ecea65a3017009afce8adc46eaa5758739891" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.42.0", -] - [[package]] name = "wasmtime-jit-icache-coherence" version = "8.0.1" @@ -5493,31 +3901,7 @@ checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" dependencies = [ "cfg-if", "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-runtime" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd26efea7a790fcf430e663ba2519f0ab6eb8980adf8b0c58c62b727da77c2ec" -dependencies = [ - "anyhow", - "cc", - "cfg-if", - "indexmap", - "libc", - "log", - "mach", - "memfd", - "memoffset 0.6.5", - "paste", - "rand 0.8.5", - "rustix 0.36.11", - "wasmtime-asm-macros 5.0.1", - "wasmtime-environ 5.0.1", - "wasmtime-jit-debug 5.0.1", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -5534,26 +3918,14 @@ dependencies = [ "log", "mach", "memfd", - "memoffset 0.8.0", + "memoffset", "paste", "rand 0.8.5", - "rustix 0.36.11", - "wasmtime-asm-macros 8.0.1", - "wasmtime-environ 8.0.1", - "wasmtime-jit-debug 8.0.1", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-types" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e1e4f66a2b9a114f9def450ab9971828c968db6ea6fccd613724b771fa4913" -dependencies = [ - "cranelift-entity 0.92.1", - "serde", - "thiserror", - "wasmparser 0.96.0", + "rustix", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys", ] [[package]] @@ -5562,10 +3934,10 @@ version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" dependencies = [ - "cranelift-entity 0.95.1", + "cranelift-entity", "serde", "thiserror", - "wasmparser 0.102.0", + "wasmparser", ] [[package]] @@ -5578,25 +3950,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - [[package]] name = "wide" version = "0.7.8" @@ -5638,37 +3991,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] @@ -5677,28 +4006,13 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -5707,84 +4021,42 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - [[package]] name = "windows_aarch64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - [[package]] name = "windows_i686_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - [[package]] name = "windows_i686_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - [[package]] name = "windows_x86_64_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - [[package]] name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - [[package]] name = "winnow" version = "0.3.3" @@ -5815,7 +4087,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-weights 20.0.0", + "sp-weights", "xcm-procedural", ] @@ -5829,12 +4101,12 @@ dependencies = [ "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-arithmetic 16.0.0", - "sp-core 21.0.0", - "sp-io 23.0.0", - "sp-runtime 24.0.0", - "sp-std 8.0.0", - "sp-weights 20.0.0", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-weights", "xcm", ] @@ -5849,12 +4121,6 @@ dependencies = [ "syn 2.0.18", ] -[[package]] -name = "yap" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc77f52dc9e9b10d55d3f4462c3b7fc393c4f17975d641542833ab2d3bc26ef" - [[package]] name = "zeroize" version = "1.5.7" diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 7d9d20d25..e4fec4827 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -6,9 +6,9 @@ members = [ "primitives/router", "pallets/inbound-queue", "pallets/outbound-queue", - "pallets/outbound-queue/rpc", - "pallets/outbound-queue/merkle-proof", + "pallets/outbound-queue/runtime-api", + "pallets/outbound-queue/merkle-tree", "pallets/ethereum-beacon-client", - "tools/query-events", + "pallets/control", "tools/call-index" ] diff --git a/parachain/README.md b/parachain/README.md index 603ad2b91..3c722cb66 100644 --- a/parachain/README.md +++ b/parachain/README.md @@ -66,31 +66,6 @@ console.log(`decoded rawLog.data: ${JSON.stringify(decodedEventLog)}`); Place the `encodedLog` string in the `message.data` field in the test data. Use the `decoded rawLog.data` field to update the comments with the decoded log data. -## Chain metadata - -There is an internal tool `snowbridge-query-events` which is used to read specific events from the parachain. It is a used by our offchain message relayers. - -This tool must be kept up to date with the latest chain metadata. This is the process for keeping that up to date: - -Install subxt client: - -```bash -cargo install subxt-cli -``` - -Update metadata by fetching it from parachain node (in this case a node in the E2E stack): - -```bash -subxt metadata --url ws://127.0.0.1:11144 -f bytes > tools/query-events/metadata-bridgehub-rococo-local.scale -``` - -If you want to update the tool for an already running E2E stack: - -```bash -cargo build --release --manifest-path tools/query-events/Cargo.toml -cp target/release/snowbridge-query-events /tmp/snowbridge/bin/ -``` - ## Generating pallet weights from benchmarks Build the parachain with the runtime benchmark flags for the chosen runtime: diff --git a/parachain/pallets/control/Cargo.toml b/parachain/pallets/control/Cargo.toml new file mode 100644 index 000000000..9cb1e3942 --- /dev/null +++ b/parachain/pallets/control/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "snowbridge-control" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io" +edition = "2021" +license = "MIT-0" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } +frame-benchmarking = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } +snowbridge-core = { path = "../../primitives/core", default-features = false } + +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } + +xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } + +ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } + + +[dev-dependencies] + + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-std/std", + "sp-io/std", + "sp-runtime/std", + "xcm/std", + "ethabi/std" +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/parachain/pallets/control/README.md b/parachain/pallets/control/README.md new file mode 100644 index 000000000..d0d59537c --- /dev/null +++ b/parachain/pallets/control/README.md @@ -0,0 +1 @@ +License: MIT-0 \ No newline at end of file diff --git a/parachain/pallets/control/src/benchmarking.rs b/parachain/pallets/control/src/benchmarking.rs new file mode 100644 index 000000000..8da7609e8 --- /dev/null +++ b/parachain/pallets/control/src/benchmarking.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +//! Benchmarking setup for pallet-template +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn upgrade() -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + let upgrade_task = H160::repeat_byte(3); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), upgrade_task); + + Ok(()) + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/parachain/pallets/control/src/lib.rs b/parachain/pallets/control/src/lib.rs new file mode 100644 index 000000000..9410b6baa --- /dev/null +++ b/parachain/pallets/control/src/lib.rs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub mod weights; +pub use weights::*; + +use snowbridge_core::{ContractId, OutboundMessage, OutboundQueue as OutboundQueueTrait, ParaId}; +use sp_core::{H160, H256, U256}; +use sp_runtime::traits::Hash; +use sp_std::prelude::*; + +use ethabi::Token; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type MessageHasher: Hash; + type OutboundQueue: OutboundQueueTrait; + type OwnParaId: Get; + type GovernanceProxyContract: Get; + type WeightInfo: WeightInfo; + } + + #[pallet::storage] + #[pallet::getter(fn something)] + pub type Something = StorageValue<_, u32>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + UpgradeTaskSubmitted { upgrade_task: H160 }, + } + + #[pallet::error] + pub enum Error { + SubmissionFailed, + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::upgrade())] + pub fn upgrade(origin: OriginFor, upgrade_task: H160) -> DispatchResult { + let _ = ensure_signed(origin)?; + + let message = OutboundMessage { + id: T::MessageHasher::hash(upgrade_task.as_ref()), + origin: T::OwnParaId::get(), + gateway: T::GovernanceProxyContract::get(), + payload: Self::encode_upgrade_payload(upgrade_task), + }; + + let ticket = + T::OutboundQueue::validate(&message).map_err(|_| Error::::SubmissionFailed)?; + + T::OutboundQueue::submit(ticket).map_err(|_| Error::::SubmissionFailed)?; + + Self::deposit_event(Event::::UpgradeTaskSubmitted { upgrade_task }); + + Ok(()) + } + } + + impl Pallet { + fn encode_upgrade_payload(upgrade_task: H160) -> Vec { + ethabi::encode(&vec![Token::Tuple(vec![ + Token::Uint(U256::from(0u64)), + Token::Bytes(ethabi::encode(&vec![Token::Tuple(vec![Token::Address( + upgrade_task, + )])])), + ])]) + } + } +} diff --git a/parachain/pallets/control/src/mock.rs b/parachain/pallets/control/src/mock.rs new file mode 100644 index 000000000..f99ed5fd9 --- /dev/null +++ b/parachain/pallets/control/src/mock.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +use crate as snowbridge_control; +use frame_support::{ + parameter_types, + traits::{ConstU16, ConstU64}, +}; +use snowbridge_core::{ContractId, ParaId}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + EthereumControl: snowbridge_control, + } +); + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const OwnParaId: ParaId = ParaId::new(1013); + pub const GovernanceProxyContract: ContractId = ContractId::new([3u8; 32]); + pub const SS58Prefix: u8 = 42; +} + +impl snowbridge_control::Config for Test { + type RuntimeEvent = RuntimeEvent; + type OwnParaId = OwnParaId; + type OutboundQueue = (); + type GovernanceProxyContract = GovernanceProxyContract; + type MessageHasher = BlakeTwo256; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default().build_storage::().unwrap().into() +} diff --git a/parachain/pallets/control/src/tests.rs b/parachain/pallets/control/src/tests.rs new file mode 100644 index 000000000..2fb790371 --- /dev/null +++ b/parachain/pallets/control/src/tests.rs @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork diff --git a/parachain/pallets/control/src/weights.rs b/parachain/pallets/control/src/weights.rs new file mode 100644 index 000000000..ef38bd112 --- /dev/null +++ b/parachain/pallets/control/src/weights.rs @@ -0,0 +1,56 @@ + +//! Autogenerated weights for pallet_template +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Alexs-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ../../target/release/node-template +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_template +// --extrinsic +// * +// --steps=50 +// --repeat=20 +// --execution=wasm +// --wasm-execution=compiled +// --output +// pallets/template/src/weights.rs +// --template +// ../../.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_template. +pub trait WeightInfo { + fn upgrade() -> Weight; +} + +/// Weights for pallet_template using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn upgrade() -> Weight { + Weight::from_parts(9_000_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn upgrade() -> Weight { + Weight::from_parts(9_000_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/parachain/pallets/ethereum-beacon-client/src/benchmarking/mod.rs b/parachain/pallets/ethereum-beacon-client/src/benchmarking/mod.rs index 936633c80..15ec4c1f9 100644 --- a/parachain/pallets/ethereum-beacon-client/src/benchmarking/mod.rs +++ b/parachain/pallets/ethereum-beacon-client/src/benchmarking/mod.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use super::*; mod fixtures; diff --git a/parachain/pallets/ethereum-beacon-client/src/benchmarking/util.rs b/parachain/pallets/ethereum-beacon-client/src/benchmarking/util.rs index cc122f3dc..7e5ded6e1 100644 --- a/parachain/pallets/ethereum-beacon-client/src/benchmarking/util.rs +++ b/parachain/pallets/ethereum-beacon-client/src/benchmarking/util.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use crate::{ decompress_sync_committee_bits, Config, CurrentSyncCommittee, Pallet as EthereumBeaconClient, Update, ValidatorsRoot, Vec, diff --git a/parachain/pallets/ethereum-beacon-client/src/config/mainnet.rs b/parachain/pallets/ethereum-beacon-client/src/config/mainnet.rs index f7b4e6077..3d22ad82c 100644 --- a/parachain/pallets/ethereum-beacon-client/src/config/mainnet.rs +++ b/parachain/pallets/ethereum-beacon-client/src/config/mainnet.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork pub const SLOTS_PER_EPOCH: usize = 32; pub const SECONDS_PER_SLOT: usize = 12; pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: usize = 256; diff --git a/parachain/pallets/ethereum-beacon-client/src/config/minimal.rs b/parachain/pallets/ethereum-beacon-client/src/config/minimal.rs index 67ff3c441..affa86db9 100644 --- a/parachain/pallets/ethereum-beacon-client/src/config/minimal.rs +++ b/parachain/pallets/ethereum-beacon-client/src/config/minimal.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork pub const SLOTS_PER_EPOCH: usize = 8; pub const SECONDS_PER_SLOT: usize = 6; pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: usize = 8; diff --git a/parachain/pallets/ethereum-beacon-client/src/config/mod.rs b/parachain/pallets/ethereum-beacon-client/src/config/mod.rs index c46109845..f15dc5f0b 100644 --- a/parachain/pallets/ethereum-beacon-client/src/config/mod.rs +++ b/parachain/pallets/ethereum-beacon-client/src/config/mod.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use primitives::merkle_proof::{generalized_index_length, subtree_index}; use static_assertions::const_assert; diff --git a/parachain/pallets/ethereum-beacon-client/src/functions.rs b/parachain/pallets/ethereum-beacon-client/src/functions.rs index a13525eb2..751e63c7f 100644 --- a/parachain/pallets/ethereum-beacon-client/src/functions.rs +++ b/parachain/pallets/ethereum-beacon-client/src/functions.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use crate::config::{ EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, SYNC_COMMITTEE_BITS_SIZE, SYNC_COMMITTEE_SIZE, diff --git a/parachain/pallets/ethereum-beacon-client/src/impls.rs b/parachain/pallets/ethereum-beacon-client/src/impls.rs index c3fc30d8b..e5f93325f 100644 --- a/parachain/pallets/ethereum-beacon-client/src/impls.rs +++ b/parachain/pallets/ethereum-beacon-client/src/impls.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use super::*; use frame_support::dispatch::DispatchError; @@ -25,7 +27,7 @@ impl Verifier for Pallet { message.proof.block_hash, err ); - return Err(err) + return Err(err); }, }; @@ -44,7 +46,7 @@ impl Verifier for Pallet { message.proof.block_hash, err ); - return Err(Error::::DecodeFailed.into()) + return Err(Error::::DecodeFailed.into()); }, }; @@ -54,7 +56,7 @@ impl Verifier for Pallet { "💫 Event log not found in receipt for transaction at index {} in block {}", message.proof.tx_index, message.proof.block_hash, ); - return Err(Error::::InvalidProof.into()) + return Err(Error::::InvalidProof.into()); } log::info!( diff --git a/parachain/pallets/ethereum-beacon-client/src/lib.rs b/parachain/pallets/ethereum-beacon-client/src/lib.rs index 320d07206..59d059342 100644 --- a/parachain/pallets/ethereum-beacon-client/src/lib.rs +++ b/parachain/pallets/ethereum-beacon-client/src/lib.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork //! Ethereum Beacon Client #![cfg_attr(not(feature = "std"), no_std)] @@ -294,9 +296,9 @@ pub mod pallet { // committee period. let max_latency = config::EPOCHS_PER_SYNC_COMMITTEE_PERIOD * config::SLOTS_PER_EPOCH; ensure!( - latest_execution_state.beacon_slot == 0 || - latest_finalized_state.slot < - latest_execution_state.beacon_slot + max_latency as u64, + latest_execution_state.beacon_slot == 0 + || latest_finalized_state.slot + < latest_execution_state.beacon_slot + max_latency as u64, Error::::ExecutionHeaderTooFarBehind ); Ok(()) @@ -314,8 +316,8 @@ pub mod pallet { // Verify update does not skip a sync committee period. ensure!( - update.signature_slot > update.attested_header.slot && - update.attested_header.slot >= update.finalized_header.slot, + update.signature_slot > update.attested_header.slot + && update.attested_header.slot >= update.finalized_header.slot, Error::::InvalidUpdateSlot ); @@ -337,12 +339,12 @@ pub mod pallet { // Verify update is relevant. let update_attested_period = compute_period(update.attested_header.slot); - let update_has_next_sync_committee = !>::exists() && - (update.next_sync_committee_update.is_some() && - update_attested_period == store_period); + let update_has_next_sync_committee = !>::exists() + && (update.next_sync_committee_update.is_some() + && update_attested_period == store_period); ensure!( - update.attested_header.slot > latest_finalized_state.slot || - update_has_next_sync_committee, + update.attested_header.slot > latest_finalized_state.slot + || update_has_next_sync_committee, Error::::NotRelevant ); @@ -499,9 +501,9 @@ pub mod pallet { // Checks that we don't skip execution headers, they need to be imported sequentially. let latest_execution_state: ExecutionHeaderState = Self::latest_execution_state(); ensure!( - latest_execution_state.block_number == 0 || - update.execution_header.block_number == - latest_execution_state.block_number + 1, + latest_execution_state.block_number == 0 + || update.execution_header.block_number + == latest_execution_state.block_number + 1, Error::::ExecutionHeaderSkippedSlot ); @@ -545,7 +547,7 @@ pub mod pallet { let state = >::get(block_root) .ok_or(Error::::ExpectedFinalizedHeaderNotStored)?; if update.header.slot != state.slot { - return Err(Error::::ExpectedFinalizedHeaderNotStored.into()) + return Err(Error::::ExpectedFinalizedHeaderNotStored.into()); } }, } @@ -732,13 +734,13 @@ pub mod pallet { let fork_versions = T::ForkVersions::get(); if epoch >= fork_versions.capella.epoch { - return fork_versions.capella.version + return fork_versions.capella.version; } if epoch >= fork_versions.bellatrix.epoch { - return fork_versions.bellatrix.version + return fork_versions.bellatrix.version; } if epoch >= fork_versions.altair.epoch { - return fork_versions.altair.version + return fork_versions.altair.version; } fork_versions.genesis.version diff --git a/parachain/pallets/ethereum-beacon-client/src/mock.rs b/parachain/pallets/ethereum-beacon-client/src/mock.rs index 52b865b99..b75d1389a 100644 --- a/parachain/pallets/ethereum-beacon-client/src/mock.rs +++ b/parachain/pallets/ethereum-beacon-client/src/mock.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use crate as ethereum_beacon_client; use frame_support::parameter_types; use pallet_timestamp; diff --git a/parachain/pallets/ethereum-beacon-client/src/tests.rs b/parachain/pallets/ethereum-beacon-client/src/tests.rs index 24484528b..432763282 100644 --- a/parachain/pallets/ethereum-beacon-client/src/tests.rs +++ b/parachain/pallets/ethereum-beacon-client/src/tests.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use crate::{ functions::compute_period, mock::minimal::*, pallet::ExecutionHeaders, sync_committee_sum, verify_merkle_branch, BeaconHeader, CompactBeaconState, Error, FinalizedBeaconState, diff --git a/parachain/pallets/ethereum-beacon-client/src/types.rs b/parachain/pallets/ethereum-beacon-client/src/types.rs index 50c1bdbab..a58b55f70 100644 --- a/parachain/pallets/ethereum-beacon-client/src/types.rs +++ b/parachain/pallets/ethereum-beacon-client/src/types.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork pub use crate::config::{ SLOTS_PER_HISTORICAL_ROOT, SYNC_COMMITTEE_BITS_SIZE as SC_BITS_SIZE, SYNC_COMMITTEE_SIZE as SC_SIZE, diff --git a/parachain/pallets/inbound-queue/src/benchmarking.rs b/parachain/pallets/inbound-queue/src/benchmarking.rs index 8b1378917..2fb790371 100644 --- a/parachain/pallets/inbound-queue/src/benchmarking.rs +++ b/parachain/pallets/inbound-queue/src/benchmarking.rs @@ -1 +1,2 @@ - +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork diff --git a/parachain/pallets/inbound-queue/src/envelope.rs b/parachain/pallets/inbound-queue/src/envelope.rs index 404ea02c8..581604b6a 100644 --- a/parachain/pallets/inbound-queue/src/envelope.rs +++ b/parachain/pallets/inbound-queue/src/envelope.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use ethabi::{Event, Param, ParamKind, Token}; use snowbridge_core::ParaId; use snowbridge_ethereum::{log::Log, H160}; @@ -19,8 +21,8 @@ static EVENT_ABI: &Event = &Event { /// An inbound message that has had its outer envelope decoded. #[derive(Clone, RuntimeDebug)] pub struct Envelope { - /// The address of the outbound channel on Ethereum that forwarded this message. - pub channel: H160, + /// The address of the outbound queue on Ethereum that emitted this message as an event log + pub outbound_queue_address: H160, /// The destination parachain. pub dest: ParaId, /// A nonce for enforcing replay protection and ordering. @@ -55,6 +57,6 @@ impl TryFrom for Envelope { _ => return Err(EnvelopeDecodeError), }; - Ok(Self { channel: log.address, dest, nonce, payload }) + Ok(Self { outbound_queue_address: log.address, dest, nonce, payload }) } } diff --git a/parachain/pallets/inbound-queue/src/lib.rs b/parachain/pallets/inbound-queue/src/lib.rs index 732ba20a9..36345f1df 100644 --- a/parachain/pallets/inbound-queue/src/lib.rs +++ b/parachain/pallets/inbound-queue/src/lib.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork #![cfg_attr(not(feature = "std"), no_std)] mod envelope; @@ -19,19 +21,16 @@ use frame_system::ensure_signed; use snowbridge_core::ParaId; use sp_core::{ConstU32, H160}; use sp_runtime::traits::AccountIdConversion; -use sp_std::convert::TryFrom; -use sp_std::vec::Vec; -use sp_std::collections::btree_set::BTreeSet; +use sp_std::{collections::btree_set::BTreeSet, convert::TryFrom, vec::Vec}; use envelope::Envelope; use snowbridge_core::{Message, Verifier}; -use snowbridge_router_primitives::{ConvertMessage, Payload}; +use snowbridge_router_primitives::inbound; -use xcm::latest::{send_xcm, SendError}; +use xcm::v3::{send_xcm, Junction::*, Junctions::*, MultiLocation, SendError}; pub use weights::WeightInfo; - use frame_support::{CloneNoBound, EqNoBound, PartialEqNoBound}; use codec::{Decode, Encode}; @@ -73,8 +72,6 @@ pub mod pallet { type Reward: Get>; - type MessageConversion: ConvertMessage; - type XcmSender: SendXcm; type WeightInfo: WeightInfo; @@ -146,8 +143,8 @@ pub mod pallet { // Verify that the message was submitted to us from a known // outbound channel on the ethereum side let allowlist = >::get(); - if !allowlist.contains(&envelope.channel) { - return Err(Error::::InvalidOutboundQueue.into()) + if !allowlist.contains(&envelope.outbound_queue_address) { + return Err(Error::::InvalidOutboundQueue.into()); } // Verify message nonce @@ -165,30 +162,50 @@ pub mod pallet { let sovereign_account = envelope.dest.into_account_truncating(); T::Token::transfer(&sovereign_account, &who, T::Reward::get(), Preservation::Preserve)?; - // Dispatch message. From this point, any errors are masked, i.e the extrinsic will - // succeed even if the message was not successfully dispatched. - - if let Ok(payload) = Payload::decode_all(&mut envelope.payload.as_ref()) { - let (dest, xcm) = - T::MessageConversion::convert(envelope.channel, envelope.dest.into(), payload); - match send_xcm::(dest, xcm) { - Ok(_) => Self::deposit_event(Event::MessageReceived { - dest: envelope.dest, - nonce: envelope.nonce, - result: MessageDispatchResult::Dispatched, - }), - Err(err) => Self::deposit_event(Event::MessageReceived { + // From this point, any errors are masked, i.e the extrinsic will + // succeed even if the message was not successfully decoded or dispatched. + + // Attempt to decode message + let decoded_message = + match inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref()) { + Ok(inbound::VersionedMessage::V1(decoded_message)) => decoded_message, + Err(_) => { + Self::deposit_event(Event::MessageReceived { + dest: envelope.dest, + nonce: envelope.nonce, + result: MessageDispatchResult::InvalidPayload, + }); + return Ok(()); + }, + }; + + // Attempt to convert to XCM + let sibling_para = + MultiLocation { parents: 1, interior: X1(Parachain(envelope.dest.into())) }; + let xcm = match decoded_message.try_into() { + Ok(xcm) => xcm, + Err(_) => { + Self::deposit_event(Event::MessageReceived { dest: envelope.dest, nonce: envelope.nonce, - result: MessageDispatchResult::NotDispatched(err), - }), - } - } else { - Self::deposit_event(Event::MessageReceived { + result: MessageDispatchResult::InvalidPayload, + }); + return Ok(()); + }, + }; + + // Attempt to send XCM to a sibling parachain + match send_xcm::(sibling_para, xcm) { + Ok(_) => Self::deposit_event(Event::MessageReceived { + dest: envelope.dest, + nonce: envelope.nonce, + result: MessageDispatchResult::Dispatched, + }), + Err(err) => Self::deposit_event(Event::MessageReceived { dest: envelope.dest, nonce: envelope.nonce, - result: MessageDispatchResult::InvalidPayload, - }) + result: MessageDispatchResult::NotDispatched(err), + }), } Ok(()) diff --git a/parachain/pallets/inbound-queue/src/test.rs b/parachain/pallets/inbound-queue/src/test.rs index 35897fbac..e3b07d445 100644 --- a/parachain/pallets/inbound-queue/src/test.rs +++ b/parachain/pallets/inbound-queue/src/test.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use super::*; use frame_support::{ @@ -97,8 +99,6 @@ impl Verifier for MockVerifier { } } -use snowbridge_router_primitives::InboundMessageConverter; - parameter_types! { pub const EthereumNetwork: xcm::v3::NetworkId = xcm::v3::NetworkId::Ethereum { chain_id: 15}; } @@ -108,7 +108,6 @@ impl inbound_queue::Config for Test { type Verifier = MockVerifier; type Token = Balances; type Reward = ConstU64<100>; - type MessageConversion = InboundMessageConverter; type XcmSender = (); type WeightInfo = (); } @@ -194,7 +193,7 @@ fn test_submit() { dest: dest_para, nonce: 1, // dummy xcm sender doesn't actually send messages - result: MessageDispatchResult::NotDispatched(xcm::v3::SendError::NotApplicable), + result: MessageDispatchResult::InvalidPayload, } .into()]); }); diff --git a/parachain/pallets/inbound-queue/src/weights.rs b/parachain/pallets/inbound-queue/src/weights.rs index b9f8c2acd..99fa57492 100644 --- a/parachain/pallets/inbound-queue/src/weights.rs +++ b/parachain/pallets/inbound-queue/src/weights.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork pub trait WeightInfo {} impl WeightInfo for () {} diff --git a/parachain/pallets/outbound-queue/Cargo.toml b/parachain/pallets/outbound-queue/Cargo.toml index 5fadcb5b0..ac779cf07 100644 --- a/parachain/pallets/outbound-queue/Cargo.toml +++ b/parachain/pallets/outbound-queue/Cargo.toml @@ -25,11 +25,14 @@ sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "master" sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } snowbridge-core = { path = "../../primitives/core", default-features = false } -snowbridge-outbound-queue-merkle-proof = { path = "merkle-proof", default-features = false } +snowbridge-outbound-queue-merkle-tree = { path = "merkle-tree", default-features = false } ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } + [dev-dependencies] frame-benchmarking = { git = "https://github.com/paritytech/substrate.git", branch = "master" } +pallet-message-queue = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "master" } hex-literal = { version = "0.4.1" } rlp = { version = "0.5" } @@ -48,8 +51,9 @@ std = [ "sp-std/std", "sp-io/std", "snowbridge-core/std", - "snowbridge-outbound-queue-merkle-proof/std", - "ethabi/std" + "snowbridge-outbound-queue-merkle-tree/std", + "ethabi/std", + "xcm/std" ] runtime-benchmarks = [ "snowbridge-core/runtime-benchmarks", diff --git a/parachain/pallets/outbound-queue/merkle-proof/src/lib.rs b/parachain/pallets/outbound-queue/merkle-proof/src/lib.rs deleted file mode 100644 index be7bca9a2..000000000 --- a/parachain/pallets/outbound-queue/merkle-proof/src/lib.rs +++ /dev/null @@ -1,811 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] - -//! This crate implements a simple binary Merkle Tree utilities required for inter-op with Ethereum -//! bridge & Solidity contract. -//! -//! The implementation is optimised for usage within Substrate Runtime and supports no-std -//! compilation targets. -//! -//! Merkle Tree is constructed from arbitrary-length leaves, that are initially hashed using the -//! same [Hasher] as the inner nodes. -//! Inner nodes are created by concatenating child hashes and hashing again. The implementation -//! does not perform any sorting of the input data (leaves) nor when inner nodes are created. -//! -//! If the number of leaves is not even, last leave (hash of) is promoted to the upper layer. - -#[cfg(not(feature = "std"))] -extern crate alloc; -#[cfg(not(feature = "std"))] -use alloc::vec; -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; - -use codec::{Decode, Encode}; -use sp_core::H256; -use sp_runtime::traits::Hash; - -/// Construct a root hash of a Binary Merkle Tree created from given leaves. -/// -/// See crate-level docs for details about Merkle Tree construction. -/// -/// In case an empty list of leaves is passed the function returns a 0-filled hash. -pub fn merkle_root(leaves: I) -> H256 -where - H: Hash, - I: IntoIterator, - T: AsRef<[u8]>, -{ - let iter = leaves.into_iter().map(|l| ::hash(l.as_ref())); - merkelize::(iter, &mut ()) -} - -fn merkelize(leaves: I, visitor: &mut V) -> H256 -where - H: Hash, - V: Visitor, - I: Iterator, -{ - let upper = Vec::with_capacity(leaves.size_hint().0); - let mut next = match merkelize_row::(leaves, upper, visitor) { - Ok(root) => return root, - Err(next) if next.is_empty() => return H256::default(), - Err(next) => next, - }; - - let mut upper = Vec::with_capacity((next.len() + 1) / 2); - loop { - visitor.move_up(); - - match merkelize_row::(next.drain(..), upper, visitor) { - Ok(root) => return root, - Err(t) => { - // swap collections to avoid allocations - upper = next; - next = t; - }, - }; - } -} - -/// A generated merkle proof. -/// -/// The structure contains all necessary data to later on verify the proof and the leaf itself. -#[derive(Encode, Decode, Debug, PartialEq, Eq)] -pub struct MerkleProof { - /// Root hash of generated merkle tree. - pub root: H256, - /// Proof items (does not contain the leaf hash, nor the root obviously). - /// - /// This vec contains all inner node hashes necessary to reconstruct the root hash given the - /// leaf hash. - pub proof: Vec, - /// Number of leaves in the original tree. - /// - /// This is needed to detect a case where we have an odd number of leaves that "get promoted" - /// to upper layers. - pub number_of_leaves: u64, - /// Index of the leaf the proof is for (0-based). - pub leaf_index: u64, - /// Leaf content. - pub leaf: T, -} - -/// A trait of object inspecting merkle root creation. -/// -/// It can be passed to [`merkelize_row`] or [`merkelize`] functions and will be notified -/// about tree traversal. -trait Visitor { - /// We are moving one level up in the tree. - fn move_up(&mut self); - - /// We are creating an inner node from given `left` and `right` nodes. - /// - /// Note that in case of last odd node in the row `right` might be empty. - /// The method will also visit the `root` hash (level 0). - /// - /// The `index` is an index of `left` item. - fn visit(&mut self, index: u64, left: &Option, right: &Option); -} - -/// No-op implementation of the visitor. -impl Visitor for () { - fn move_up(&mut self) {} - fn visit(&mut self, _index: u64, _left: &Option, _right: &Option) {} -} - -/// Construct a Merkle Proof for leaves given by indices. -/// -/// The function constructs a (partial) Merkle Tree first and stores all elements required -/// to prove the requested item (leaf) given the root hash. -/// -/// Both the Proof and the Root Hash are returned. -/// -/// # Panic -/// -/// The function will panic if given `leaf_index` is greater than the number of leaves. -pub fn merkle_proof(leaves: I, leaf_index: u64) -> MerkleProof -where - H: Hash, - I: IntoIterator, - I::IntoIter: ExactSizeIterator, - T: AsRef<[u8]>, -{ - let mut leaf = None; - let mut hashes = vec![]; - let mut number_of_leaves = 0; - for (idx, l) in (0u64..).zip(leaves) { - // count the leaves - number_of_leaves = idx + 1; - // hash all leaves - let hash = ::hash(l.as_ref()); - hashes.push(hash); - // find the leaf for the proof - if idx == leaf_index { - leaf = Some(l); - } - } - - /// The struct collects a proof for single leaf. - struct ProofCollection { - proof: Vec, - position: u64, - } - - impl ProofCollection { - fn new(position: u64) -> Self { - ProofCollection { proof: Default::default(), position } - } - } - - impl Visitor for ProofCollection { - fn move_up(&mut self) { - self.position /= 2; - } - - fn visit(&mut self, index: u64, left: &Option, right: &Option) { - // we are at left branch - right goes to the proof. - if self.position == index { - if let Some(right) = right { - self.proof.push(*right); - } - } - // we are at right branch - left goes to the proof. - if self.position == index + 1 { - if let Some(left) = left { - self.proof.push(*left); - } - } - } - } - - let mut collect_proof = ProofCollection::new(leaf_index); - - let root = merkelize::(hashes.into_iter(), &mut collect_proof); - let leaf = leaf.expect("Requested `leaf_index` is greater than number of leaves."); - - #[cfg(feature = "debug")] - log::debug!( - "[merkle_proof] Proof: {:?}", - collect_proof.proof.iter().map(hex::encode).collect::>() - ); - - MerkleProof { root, proof: collect_proof.proof, number_of_leaves, leaf_index, leaf } -} - -/// Leaf node for proof verification. -/// -/// Can be either a value that needs to be hashed first, -/// or the hash itself. -#[derive(Debug, PartialEq, Eq)] -pub enum Leaf<'a> { - /// Leaf content. - Value(&'a [u8]), - /// Hash of the leaf content. - Hash(H256), -} - -impl<'a, T: AsRef<[u8]>> From<&'a T> for Leaf<'a> { - fn from(v: &'a T) -> Self { - Leaf::Value(v.as_ref()) - } -} - -impl<'a> From for Leaf<'a> { - fn from(v: H256) -> Self { - Leaf::Hash(v) - } -} - -/// Verify Merkle Proof correctness versus given root hash. -/// -/// The proof is NOT expected to contain leaf hash as the first -/// element, but only all adjacent nodes required to eventually by process of -/// concatenating and hashing end up with given root hash. -/// -/// The proof must not contain the root hash. -pub fn verify_proof<'a, H, P, L>( - root: &'a H256, - proof: P, - number_of_leaves: u64, - leaf_index: u64, - leaf: L, -) -> bool -where - H: Hash, - P: IntoIterator, - L: Into>, -{ - if leaf_index >= number_of_leaves { - return false - } - - let leaf_hash = match leaf.into() { - Leaf::Value(content) => ::hash(content), - Leaf::Hash(hash) => hash, - }; - - let hash_len = ::LENGTH; - let mut combined = [0_u8; 64]; - let computed = proof.into_iter().fold(leaf_hash, |a, b| { - if a < b { - combined[..hash_len].copy_from_slice(&a.as_ref()); - combined[hash_len..].copy_from_slice(&b.as_ref()); - } else { - combined[..hash_len].copy_from_slice(&b.as_ref()); - combined[hash_len..].copy_from_slice(&a.as_ref()); - } - let hash = ::hash(&combined); - #[cfg(feature = "debug")] - log::debug!( - "[verify_proof]: (a, b) {:?}, {:?} => {:?} ({:?}) hash", - hex::encode(a), - hex::encode(b), - hex::encode(hash), - hex::encode(combined) - ); - hash - }); - - root == &computed -} - -/// Processes a single row (layer) of a tree by taking pairs of elements, -/// concatenating them, hashing and placing into resulting vector. -/// -/// In case only one element is provided it is returned via `Ok` result, in any other case (also an -/// empty iterator) an `Err` with the inner nodes of upper layer is returned. -fn merkelize_row( - mut iter: I, - mut next: Vec, - visitor: &mut V, -) -> Result> -where - H: Hash, - V: Visitor, - I: Iterator, -{ - #[cfg(feature = "debug")] - log::debug!("[merkelize_row]"); - next.clear(); - - let hash_len = ::LENGTH; - let mut index = 0; - let mut combined = vec![0_u8; hash_len * 2]; - loop { - let a = iter.next(); - let b = iter.next(); - visitor.visit(index, &a, &b); - - #[cfg(feature = "debug")] - log::debug!(" {:?}\n {:?}", a.as_ref().map(hex::encode), b.as_ref().map(hex::encode)); - - index += 2; - match (a, b) { - (Some(a), Some(b)) => { - if a < b { - combined[..hash_len].copy_from_slice(a.as_ref()); - combined[hash_len..].copy_from_slice(b.as_ref()); - } else { - combined[..hash_len].copy_from_slice(b.as_ref()); - combined[hash_len..].copy_from_slice(a.as_ref()); - } - - next.push(::hash(&combined)); - }, - // Odd number of items. Promote the item to the upper layer. - (Some(a), None) if !next.is_empty() => { - next.push(a); - }, - // Last item = root. - (Some(a), None) => return Ok(a), - // Finish up, no more items. - _ => { - #[cfg(feature = "debug")] - log::debug!( - "[merkelize_row] Next: {:?}", - next.iter().map(hex::encode).collect::>() - ); - return Err(next) - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - use sp_runtime::traits::Keccak256; - - #[test] - fn should_generate_empty_root() { - // given - let _ = env_logger::try_init(); - let data: Vec<[u8; 1]> = Default::default(); - - // when - let out = merkle_root::(data); - - // then - assert_eq!( - hex::encode(&out), - "0000000000000000000000000000000000000000000000000000000000000000" - ); - } - - #[test] - fn should_generate_single_root() { - // given - let _ = env_logger::try_init(); - let data = vec![hex!("E04CC55ebEE1cBCE552f250e85c57B70B2E2625b")]; - - // when - let out = merkle_root::(data); - - // then - assert_eq!( - hex::encode(&out), - "aeb47a269393297f4b0a3c9c9cfd00c7a4195255274cf39d83dabc2fcc9ff3d7" - ); - } - - #[test] - fn should_generate_root_pow_2() { - // given - let _ = env_logger::try_init(); - let data = vec![ - hex!("E04CC55ebEE1cBCE552f250e85c57B70B2E2625b"), - hex!("25451A4de12dcCc2D166922fA938E900fCc4ED24"), - ]; - - // when - let out = merkle_root::(data); - - // then - assert_eq!( - hex::encode(&out), - "697ea2a8fe5b03468548a7a413424a6292ab44a82a6f5cc594c3fa7dda7ce402" - ); - } - - #[test] - fn should_generate_root_complex() { - let _ = env_logger::try_init(); - let test = |root, data| { - assert_eq!( - array_bytes::bytes2hex("", &merkle_root::(data).as_ref()), - root - ); - }; - - test( - "5842148bc6ebeb52af882a317c765fccd3ae80589b21a9b8cbf21abb630e46a7", - vec!["a", "b", "c"], - ); - - test( - "7b84bec68b13c39798c6c50e9e40a0b268e3c1634db8f4cb97314eb243d4c514", - vec!["a", "b", "a"], - ); - - test( - "dc8e73fe6903148ff5079baecc043983625c23b39f31537e322cd0deee09fa9c", - vec!["a", "b", "a", "b"], - ); - - test( - "cc50382cfd3c9a617741e9a85efee8752b8feb95a2cbecd6365fb21366ce0c8c", - vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"], - ); - } - - #[test] - fn should_generate_and_verify_proof_simple() { - // given - let _ = env_logger::try_init(); - let data = vec!["a", "b", "c"]; - - // when - let proof0 = merkle_proof::(data.clone(), 0); - assert!(verify_proof::( - &proof0.root, - proof0.proof.clone(), - data.len() as u64, - proof0.leaf_index, - &proof0.leaf, - )); - - let proof1 = merkle_proof::(data.clone(), 1); - assert!(verify_proof::( - &proof1.root, - proof1.proof, - data.len() as u64, - proof1.leaf_index, - &proof1.leaf, - )); - - let proof2 = merkle_proof::(data.clone(), 2); - assert!(verify_proof::( - &proof2.root, - proof2.proof, - data.len() as u64, - proof2.leaf_index, - &proof2.leaf - )); - - // then - assert_eq!(hex::encode(proof0.root), hex::encode(proof1.root)); - assert_eq!(hex::encode(proof2.root), hex::encode(proof1.root)); - - assert!(!verify_proof::( - &H256::from_slice(&hex!( - "fb3b3be94be9e983ba5e094c9c51a7d96a4fa2e5d8e891df00ca89ba05bb1239" - )), - proof0.proof, - data.len() as u64, - proof0.leaf_index, - &proof0.leaf - )); - - assert!(!verify_proof::( - &proof0.root, - vec![], - data.len() as u64, - proof0.leaf_index, - &proof0.leaf - )); - } - - #[test] - fn should_generate_and_verify_proof_complex() { - // given - let _ = env_logger::try_init(); - let data = vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; - - for l in 0..data.len() { - // when - let proof = merkle_proof::(data.clone(), l as u64); - // then - assert!(verify_proof::( - &proof.root, - proof.proof, - data.len() as u64, - proof.leaf_index, - &proof.leaf - )); - } - } - - #[ignore] - #[test] - fn should_generate_and_verify_proof_large() { - // given - let _ = env_logger::try_init(); - let mut data = vec![]; - for i in 1..16 { - for c in 'a'..'z' { - if c as usize % i != 0 { - data.push(c.to_string()); - } - } - - for l in 0..data.len() { - // when - let proof = merkle_proof::(data.clone(), l as u64); - // then - assert!(verify_proof::( - &proof.root, - proof.proof, - data.len() as u64, - proof.leaf_index, - &proof.leaf - )); - } - } - } - - #[ignore] - #[test] - fn should_generate_and_verify_proof_large_tree() { - // given - let _ = env_logger::try_init(); - let mut data = vec![]; - for i in 0..6000 { - data.push(format!("{}", i)); - } - - for l in (0..data.len()).step_by(13) { - // when - let proof = merkle_proof::(data.clone(), l as u64); - // then - assert!(verify_proof::( - &proof.root, - proof.proof, - data.len() as u64, - proof.leaf_index, - &proof.leaf - )); - } - } - - #[test] - #[should_panic] - fn should_panic_on_invalid_leaf_index() { - let _ = env_logger::try_init(); - merkle_proof::(vec!["a"], 5); - } - - #[ignore] - #[test] - fn should_generate_and_verify_proof_on_test_data() { - let addresses = vec![ - "0x9aF1Ca5941148eB6A3e9b9C741b69738292C533f", - "0xDD6ca953fddA25c496165D9040F7F77f75B75002", - "0x60e9C47B64Bc1C7C906E891255EaEC19123E7F42", - "0xfa4859480Aa6D899858DE54334d2911E01C070df", - "0x19B9b128470584F7209eEf65B69F3624549Abe6d", - "0xC436aC1f261802C4494504A11fc2926C726cB83b", - "0xc304C8C2c12522F78aD1E28dD86b9947D7744bd0", - "0xDa0C2Cba6e832E55dE89cF4033affc90CC147352", - "0xf850Fd22c96e3501Aad4CDCBf38E4AEC95622411", - "0x684918D4387CEb5E7eda969042f036E226E50642", - "0x963F0A1bFbb6813C0AC88FcDe6ceB96EA634A595", - "0x39B38ad74b8bCc5CE564f7a27Ac19037A95B6099", - "0xC2Dec7Fdd1fef3ee95aD88EC8F3Cd5bd4065f3C7", - "0x9E311f05c2b6A43C2CCF16fB2209491BaBc2ec01", - "0x927607C30eCE4Ef274e250d0bf414d4a210b16f0", - "0x98882bcf85E1E2DFF780D0eB360678C1cf443266", - "0xFBb50191cd0662049E7C4EE32830a4Cc9B353047", - "0x963854fc2C358c48C3F9F0A598B9572c581B8DEF", - "0xF9D7Bc222cF6e3e07bF66711e6f409E51aB75292", - "0xF2E3fd32D063F8bBAcB9e6Ea8101C2edd899AFe6", - "0x407a5b9047B76E8668570120A96d580589fd1325", - "0xEAD9726FAFB900A07dAd24a43AE941d2eFDD6E97", - "0x42f5C8D9384034A9030313B51125C32a526b6ee8", - "0x158fD2529Bc4116570Eb7C80CC76FEf33ad5eD95", - "0x0A436EE2E4dEF3383Cf4546d4278326Ccc82514E", - "0x34229A215db8FeaC93Caf8B5B255e3c6eA51d855", - "0xEb3B7CF8B1840242CB98A732BA464a17D00b5dDF", - "0x2079692bf9ab2d6dc7D79BBDdEE71611E9aA3B72", - "0x46e2A67e5d450e2Cf7317779f8274a2a630f3C9B", - "0xA7Ece4A5390DAB18D08201aE18800375caD78aab", - "0x15E1c0D24D62057Bf082Cb2253dA11Ef0d469570", - "0xADDEF4C9b5687Eb1F7E55F2251916200A3598878", - "0xe0B16Fb96F936035db2b5A68EB37D470fED2f013", - "0x0c9A84993feaa779ae21E39F9793d09e6b69B62D", - "0x3bc4D5148906F70F0A7D1e2756572655fd8b7B34", - "0xFf4675C26903D5319795cbd3a44b109E7DDD9fDe", - "0xCec4450569A8945C6D2Aba0045e4339030128a92", - "0x85f0584B10950E421A32F471635b424063FD8405", - "0xb38bEe7Bdc0bC43c096e206EFdFEad63869929E3", - "0xc9609466274Fef19D0e58E1Ee3b321D5C141067E", - "0xa08EA868cF75268E7401021E9f945BAe73872ecc", - "0x67C9Cb1A29E964Fe87Ff669735cf7eb87f6868fE", - "0x1B6BEF636aFcdd6085cD4455BbcC93796A12F6E2", - "0x46B37b243E09540b55cF91C333188e7D5FD786dD", - "0x8E719E272f62Fa97da93CF9C941F5e53AA09e44a", - "0xa511B7E7DB9cb24AD5c89fBb6032C7a9c2EfA0a5", - "0x4D11FDcAeD335d839132AD450B02af974A3A66f8", - "0xB8cf790a5090E709B4619E1F335317114294E17E", - "0x7f0f57eA064A83210Cafd3a536866ffD2C5eDCB3", - "0xC03C848A4521356EF800e399D889e9c2A25D1f9E", - "0xC6b03DF05cb686D933DD31fCa5A993bF823dc4FE", - "0x58611696b6a8102cf95A32c25612E4cEF32b910F", - "0x2ed4bC7197AEF13560F6771D930Bf907772DE3CE", - "0x3C5E58f334306be029B0e47e119b8977B2639eb4", - "0x288646a1a4FeeC560B349d210263c609aDF649a6", - "0xb4F4981E0d027Dc2B3c86afA0D0fC03d317e83C0", - "0xaAE4A87F8058feDA3971f9DEd639Ec9189aA2500", - "0x355069DA35E598913d8736E5B8340527099960b8", - "0x3cf5A0F274cd243C0A186d9fCBdADad089821B93", - "0xca55155dCc4591538A8A0ca322a56EB0E4aD03C4", - "0xE824D0268366ec5C4F23652b8eD70D552B1F2b8B", - "0x84C3e9B25AE8a9b39FF5E331F9A597F2DCf27Ca9", - "0xcA0018e278751De10d26539915d9c7E7503432FE", - "0xf13077dE6191D6c1509ac7E088b8BE7Fe656c28b", - "0x7a6bcA1ec9Db506e47ac6FD86D001c2aBc59C531", - "0xeA7f9A2A9dd6Ba9bc93ca615C3Ddf26973146911", - "0x8D0d8577e16F8731d4F8712BAbFa97aF4c453458", - "0xB7a7855629dF104246997e9ACa0E6510df75d0ea", - "0x5C1009BDC70b0C8Ab2e5a53931672ab448C17c89", - "0x40B47D1AfefEF5eF41e0789F0285DE7b1C31631C", - "0x5086933d549cEcEB20652CE00973703CF10Da373", - "0xeb364f6FE356882F92ae9314fa96116Cf65F47d8", - "0xdC4D31516A416cEf533C01a92D9a04bbdb85EE67", - "0x9b36E086E5A274332AFd3D8509e12ca5F6af918d", - "0xBC26394fF36e1673aE0608ce91A53B9768aD0D76", - "0x81B5AB400be9e563fA476c100BE898C09966426c", - "0x9d93C8ae5793054D28278A5DE6d4653EC79e90FE", - "0x3B8E75804F71e121008991E3177fc942b6c28F50", - "0xC6Eb5886eB43dD473f5BB4e21e56E08dA464D9B4", - "0xfdf1277b71A73c813cD0e1a94B800f4B1Db66DBE", - "0xc2ff2cCc98971556670e287Ff0CC39DA795231ad", - "0x76b7E1473f0D0A87E9B4a14E2B179266802740f5", - "0xA7Bc965660a6EF4687CCa4F69A97563163A3C2Ef", - "0xB9C2b47888B9F8f7D03dC1de83F3F55E738CebD3", - "0xEd400162E6Dd6bD2271728FFb04176bF770De94a", - "0xE3E8331156700339142189B6E555DCb2c0962750", - "0xbf62e342Bc7706a448EdD52AE871d9C4497A53b1", - "0xb9d7A1A111eed75714a0AcD2dd467E872eE6B03D", - "0x03942919DFD0383b8c574AB8A701d89fd4bfA69D", - "0x0Ef4C92355D3c8c7050DFeb319790EFCcBE6fe9e", - "0xA6895a3cf0C60212a73B3891948ACEcF1753f25E", - "0x0Ed509239DB59ef3503ded3d31013C983d52803A", - "0xc4CE8abD123BfAFc4deFf37c7D11DeCd5c350EE4", - "0x4A4Bf59f7038eDcd8597004f35d7Ee24a7Bdd2d3", - "0x5769E8e8A2656b5ed6b6e6fa2a2bFAeaf970BB87", - "0xf9E15cCE181332F4F57386687c1776b66C377060", - "0xc98f8d4843D56a46C21171900d3eE538Cc74dbb5", - "0x3605965B47544Ce4302b988788B8195601AE4dEd", - "0xe993BDfdcAac2e65018efeE0F69A12678031c71d", - "0x274fDf8801385D3FAc954BCc1446Af45f5a8304c", - "0xBFb3f476fcD6429F4a475bA23cEFdDdd85c6b964", - "0x806cD16588Fe812ae740e931f95A289aFb4a4B50", - "0xa89488CE3bD9C25C3aF797D1bbE6CA689De79d81", - "0xd412f1AfAcf0Ebf3Cd324593A231Fc74CC488B12", - "0xd1f715b2D7951d54bc31210BbD41852D9BF98Ed1", - "0xf65aD707c344171F467b2ADba3d14f312219cE23", - "0x2971a4b242e9566dEF7bcdB7347f5E484E11919B", - "0x12b113D6827E07E7D426649fBd605f427da52314", - "0x1c6CA45171CDb9856A6C9Dba9c5F1216913C1e97", - "0x11cC6ee1d74963Db23294FCE1E3e0A0555779CeA", - "0x8Aa1C721255CDC8F895E4E4c782D86726b068667", - "0xA2cDC1f37510814485129aC6310b22dF04e9Bbf0", - "0xCf531b71d388EB3f5889F1f78E0d77f6fb109767", - "0xBe703e3545B2510979A0cb0C440C0Fba55c6dCB5", - "0x30a35886F989db39c797D8C93880180Fdd71b0c8", - "0x1071370D981F60c47A9Cd27ac0A61873a372cBB2", - "0x3515d74A11e0Cb65F0F46cB70ecf91dD1712daaa", - "0x50500a3c2b7b1229c6884505D00ac6Be29Aecd0C", - "0x9A223c2a11D4FD3585103B21B161a2B771aDA3d1", - "0xd7218df03AD0907e6c08E707B15d9BD14285e657", - "0x76CfD72eF5f93D1a44aD1F80856797fBE060c70a", - "0x44d093cB745944991EFF5cBa151AA6602d6f5420", - "0x626516DfF43bf09A71eb6fd1510E124F96ED0Cde", - "0x6530824632dfe099304E2DC5701cA99E6d031E08", - "0x57e6c423d6a7607160d6379A0c335025A14DaFC0", - "0x3966D4AD461Ef150E0B10163C81E79b9029E69c3", - "0xF608aCfd0C286E23721a3c347b2b65039f6690F1", - "0xbfB8FAac31A25646681936977837f7740fCd0072", - "0xd80aa634a623a7ED1F069a1a3A28a173061705c7", - "0x9122a77B36363e24e12E1E2D73F87b32926D3dF5", - "0x62562f0d1cD31315bCCf176049B6279B2bfc39C2", - "0x48aBF7A2a7119e5675059E27a7082ba7F38498b2", - "0xb4596983AB9A9166b29517acD634415807569e5F", - "0x52519D16E20BC8f5E96Da6d736963e85b2adA118", - "0x7663893C3dC0850EfC5391f5E5887eD723e51B83", - "0x5FF323a29bCC3B5b4B107e177EccEF4272959e61", - "0xee6e499AdDf4364D75c05D50d9344e9daA5A9AdF", - "0x1631b0BD31fF904aD67dD58994C6C2051CDe4E75", - "0xbc208e9723D44B9811C428f6A55722a26204eEF2", - "0xe76103a222Ee2C7Cf05B580858CEe625C4dc00E1", - "0xC71Bb2DBC51760f4fc2D46D84464410760971B8a", - "0xB4C18811e6BFe564D69E12c224FFc57351f7a7ff", - "0xD11DB0F5b41061A887cB7eE9c8711438844C298A", - "0xB931269934A3D4432c084bAAc3d0de8143199F4f", - "0x070037cc85C761946ec43ea2b8A2d5729908A2a1", - "0x2E34aa8C95Ffdbb37f14dCfBcA69291c55Ba48DE", - "0x052D93e8d9220787c31d6D83f87eC7dB088E998f", - "0x498dAC6C69b8b9ad645217050054840f1D91D029", - "0xE4F7D60f9d84301e1fFFd01385a585F3A11F8E89", - "0xEa637992f30eA06460732EDCBaCDa89355c2a107", - "0x4960d8Da07c27CB6Be48a79B96dD70657c57a6bF", - "0x7e471A003C8C9fdc8789Ded9C3dbe371d8aa0329", - "0xd24265Cc10eecb9e8d355CCc0dE4b11C556E74D7", - "0xDE59C8f7557Af779674f41CA2cA855d571018690", - "0x2fA8A6b3b6226d8efC9d8f6EBDc73Ca33DDcA4d8", - "0xe44102664c6c2024673Ff07DFe66E187Db77c65f", - "0x94E3f4f90a5f7CBF2cc2623e66B8583248F01022", - "0x0383EdBbc21D73DEd039E9C1Ff6bf56017b4CC40", - "0x64C3E49898B88d1E0f0d02DA23E0c00A2Cd0cA99", - "0xF4ccfB67b938d82B70bAb20975acFAe402E812E1", - "0x4f9ee5829e9852E32E7BC154D02c91D8E203e074", - "0xb006312eF9713463bB33D22De60444Ba95609f6B", - "0x7Cbe76ef69B52110DDb2e3b441C04dDb11D63248", - "0x70ADEEa65488F439392B869b1Df7241EF317e221", - "0x64C0bf8AA36Ba590477585Bc0D2BDa7970769463", - "0xA4cDc98593CE52d01Fe5Ca47CB3dA5320e0D7592", - "0xc26B34D375533fFc4c5276282Fa5D660F3d8cbcB", - ]; - let root: H256 = array_bytes::hex2array_unchecked( - "7b2c6eebec6e85b2e272325a11c31af71df52bc0534d2d4f903e0ced191f022e", - ) - .into(); - - let data = addresses - .into_iter() - .map(|address| array_bytes::hex2bytes_unchecked(&address)) - .collect::>(); - - for l in 0..data.len() { - // when - let proof = merkle_proof::(data.clone(), l as u64); - assert_eq!( - array_bytes::bytes2hex("", &proof.root.as_ref()), - array_bytes::bytes2hex("", &root.as_ref()) - ); - assert_eq!(proof.leaf_index, l as u64); - assert_eq!(&proof.leaf, &data[l]); - - // then - assert!(verify_proof::( - &proof.root, - proof.proof, - data.len() as u64, - proof.leaf_index, - &proof.leaf - )); - } - - let proof = merkle_proof::(data.clone(), (data.len() - 1) as u64); - - assert_eq!( - proof, - MerkleProof { - root, - proof: vec![ - array_bytes::hex2array_unchecked( - "340bcb1d49b2d82802ddbcf5b85043edb3427b65d09d7f758fbc76932ad2da2f" - ) - .into(), - array_bytes::hex2array_unchecked( - "ba0580e5bd530bc93d61276df7969fb5b4ae8f1864b4a28c280249575198ff1f" - ) - .into(), - array_bytes::hex2array_unchecked( - "1fad92ed8d0504ef6c0231bbbeeda960a40693f297c64e87b582beb92ecfb00f" - ) - .into(), - array_bytes::hex2array_unchecked( - "0b84c852cbcf839d562d826fd935e1b37975ccaa419e1def8d219df4b83dcbf4" - ) - .into(), - ], - number_of_leaves: data.len() as u64, - leaf_index: (data.len() - 1) as u64, - leaf: array_bytes::hex2array_unchecked::<20>( - "c26B34D375533fFc4c5276282Fa5D660F3d8cbcB" - ) - .to_vec(), - } - ); - } -} diff --git a/parachain/pallets/outbound-queue/merkle-proof/Cargo.toml b/parachain/pallets/outbound-queue/merkle-tree/Cargo.toml similarity index 78% rename from parachain/pallets/outbound-queue/merkle-proof/Cargo.toml rename to parachain/pallets/outbound-queue/merkle-tree/Cargo.toml index 26c25f6e2..7f8627dad 100644 --- a/parachain/pallets/outbound-queue/merkle-proof/Cargo.toml +++ b/parachain/pallets/outbound-queue/merkle-tree/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "snowbridge-outbound-queue-merkle-proof" -description = "Snowbridge Outbound Queue Merkle Prover" +name = "snowbridge-outbound-queue-merkle-tree" +description = "Snowbridge Outbound Queue Merkle Tree" version = "0.1.1" edition = "2021" authors = [ "Snowfork " ] @@ -11,6 +11,7 @@ targets = [ "x86_64-unknown-linux-gnu" ] [dependencies] codec = { version = "3.1.5", package = "parity-scale-codec", default-features = false, features = [ "derive" ] } +scale-info = { version = "2.7.0", default-features = false, features = [ "derive" ] } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } @@ -25,6 +26,7 @@ array-bytes = "4.1" default = [ "std" ] std = [ "codec/std", + "scale-info/std", "sp-core/std", "sp-runtime/std", ] diff --git a/parachain/pallets/outbound-queue/merkle-tree/src/lib.rs b/parachain/pallets/outbound-queue/merkle-tree/src/lib.rs new file mode 100644 index 000000000..87ff79f1e --- /dev/null +++ b/parachain/pallets/outbound-queue/merkle-tree/src/lib.rs @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// SPDX-FileCopyrightText: 2021-2022 Parity Technologies (UK) Ltd. +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +//! This crate implements a simple binary Merkle Tree utilities required for inter-op with Ethereum +//! bridge & Solidity contract. +//! +//! The implementation is optimised for usage within Substrate Runtime and supports no-std +//! compilation targets. +//! +//! Merkle Tree is constructed from arbitrary-length leaves, that are initially hashed using the +//! same [Hasher] as the inner nodes. +//! Inner nodes are created by concatenating child hashes and hashing again. The implementation +//! does not perform any sorting of the input data (leaves) nor when inner nodes are created. +//! +//! If the number of leaves is not even, last leave (hash of) is promoted to the upper layer. + +#[cfg(not(feature = "std"))] +extern crate alloc; +#[cfg(not(feature = "std"))] +use alloc::vec; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::{RuntimeDebug, H256}; +use sp_runtime::traits::Hash; + +/// Construct a root hash of a Binary Merkle Tree created from given leaves. +/// +/// See crate-level docs for details about Merkle Tree construction. +/// +/// In case an empty list of leaves is passed the function returns a 0-filled hash. +pub fn merkle_root(leaves: I) -> H256 +where + H: Hash, + I: Iterator, +{ + merkelize::(leaves, &mut ()) +} + +fn merkelize(leaves: I, visitor: &mut V) -> H256 +where + H: Hash, + V: Visitor, + I: Iterator, +{ + let upper = Vec::with_capacity(leaves.size_hint().0); + let mut next = match merkelize_row::(leaves, upper, visitor) { + Ok(root) => return root, + Err(next) if next.is_empty() => return H256::default(), + Err(next) => next, + }; + + let mut upper = Vec::with_capacity((next.len() + 1) / 2); + loop { + visitor.move_up(); + + match merkelize_row::(next.drain(..), upper, visitor) { + Ok(root) => return root, + Err(t) => { + // swap collections to avoid allocations + upper = next; + next = t; + }, + }; + } +} + +/// A generated merkle proof. +/// +/// The structure contains all necessary data to later on verify the proof and the leaf itself. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct MerkleProof { + /// Root hash of generated merkle tree. + pub root: H256, + /// Proof items (does not contain the leaf hash, nor the root obviously). + /// + /// This vec contains all inner node hashes necessary to reconstruct the root hash given the + /// leaf hash. + pub proof: Vec, + /// Number of leaves in the original tree. + /// + /// This is needed to detect a case where we have an odd number of leaves that "get promoted" + /// to upper layers. + pub number_of_leaves: u64, + /// Index of the leaf the proof is for (0-based). + pub leaf_index: u64, + /// Leaf content (hashed). + pub leaf: H256, +} + +/// A trait of object inspecting merkle root creation. +/// +/// It can be passed to [`merkelize_row`] or [`merkelize`] functions and will be notified +/// about tree traversal. +trait Visitor { + /// We are moving one level up in the tree. + fn move_up(&mut self); + + /// We are creating an inner node from given `left` and `right` nodes. + /// + /// Note that in case of last odd node in the row `right` might be empty. + /// The method will also visit the `root` hash (level 0). + /// + /// The `index` is an index of `left` item. + fn visit(&mut self, index: u64, left: &Option, right: &Option); +} + +/// No-op implementation of the visitor. +impl Visitor for () { + fn move_up(&mut self) {} + fn visit(&mut self, _index: u64, _left: &Option, _right: &Option) {} +} + +/// Construct a Merkle Proof for leaves given by indices. +/// +/// The function constructs a (partial) Merkle Tree first and stores all elements required +/// to prove the requested item (leaf) given the root hash. +/// +/// Both the Proof and the Root Hash are returned. +/// +/// # Panic +/// +/// The function will panic if given `leaf_index` is greater than the number of leaves. +pub fn merkle_proof(leaves: I, leaf_index: u64) -> MerkleProof +where + H: Hash, + I: Iterator, +{ + let mut leaf = None; + let mut hashes = vec![]; + let mut number_of_leaves = 0; + for (idx, l) in (0u64..).zip(leaves) { + // count the leaves + number_of_leaves = idx + 1; + hashes.push(l); + // find the leaf for the proof + if idx == leaf_index { + leaf = Some(l); + } + } + + /// The struct collects a proof for single leaf. + struct ProofCollection { + proof: Vec, + position: u64, + } + + impl ProofCollection { + fn new(position: u64) -> Self { + ProofCollection { proof: Default::default(), position } + } + } + + impl Visitor for ProofCollection { + fn move_up(&mut self) { + self.position /= 2; + } + + fn visit(&mut self, index: u64, left: &Option, right: &Option) { + // we are at left branch - right goes to the proof. + if self.position == index { + if let Some(right) = right { + self.proof.push(*right); + } + } + // we are at right branch - left goes to the proof. + if self.position == index + 1 { + if let Some(left) = left { + self.proof.push(*left); + } + } + } + } + + let mut collect_proof = ProofCollection::new(leaf_index); + + let root = merkelize::(hashes.into_iter(), &mut collect_proof); + let leaf = leaf.expect("Requested `leaf_index` is greater than number of leaves."); + + #[cfg(feature = "debug")] + log::debug!( + "[merkle_proof] Proof: {:?}", + collect_proof.proof.iter().map(hex::encode).collect::>() + ); + + MerkleProof { root, proof: collect_proof.proof, number_of_leaves, leaf_index, leaf } +} + +/// Leaf node for proof verification. +/// +/// Can be either a value that needs to be hashed first, +/// or the hash itself. +#[derive(Debug, PartialEq, Eq)] +pub enum Leaf<'a> { + /// Leaf content. + Value(&'a [u8]), + /// Hash of the leaf content. + Hash(H256), +} + +impl<'a, T: AsRef<[u8]>> From<&'a T> for Leaf<'a> { + fn from(v: &'a T) -> Self { + Leaf::Value(v.as_ref()) + } +} + +impl<'a> From for Leaf<'a> { + fn from(v: H256) -> Self { + Leaf::Hash(v) + } +} + +/// Verify Merkle Proof correctness versus given root hash. +/// +/// The proof is NOT expected to contain leaf hash as the first +/// element, but only all adjacent nodes required to eventually by process of +/// concatenating and hashing end up with given root hash. +/// +/// The proof must not contain the root hash. +pub fn verify_proof<'a, H, P, L>( + root: &'a H256, + proof: P, + number_of_leaves: u64, + leaf_index: u64, + leaf: L, +) -> bool +where + H: Hash, + P: IntoIterator, + L: Into>, +{ + if leaf_index >= number_of_leaves { + return false; + } + + let leaf_hash = match leaf.into() { + Leaf::Value(content) => ::hash(content), + Leaf::Hash(hash) => hash, + }; + + let hash_len = ::LENGTH; + let mut combined = [0_u8; 64]; + let computed = proof.into_iter().fold(leaf_hash, |a, b| { + if a < b { + combined[..hash_len].copy_from_slice(&a.as_ref()); + combined[hash_len..].copy_from_slice(&b.as_ref()); + } else { + combined[..hash_len].copy_from_slice(&b.as_ref()); + combined[hash_len..].copy_from_slice(&a.as_ref()); + } + let hash = ::hash(&combined); + #[cfg(feature = "debug")] + log::debug!( + "[verify_proof]: (a, b) {:?}, {:?} => {:?} ({:?}) hash", + hex::encode(a), + hex::encode(b), + hex::encode(hash), + hex::encode(combined) + ); + hash + }); + + root == &computed +} + +/// Processes a single row (layer) of a tree by taking pairs of elements, +/// concatenating them, hashing and placing into resulting vector. +/// +/// In case only one element is provided it is returned via `Ok` result, in any other case (also an +/// empty iterator) an `Err` with the inner nodes of upper layer is returned. +fn merkelize_row( + mut iter: I, + mut next: Vec, + visitor: &mut V, +) -> Result> +where + H: Hash, + V: Visitor, + I: Iterator, +{ + #[cfg(feature = "debug")] + log::debug!("[merkelize_row]"); + next.clear(); + + let hash_len = ::LENGTH; + let mut index = 0; + let mut combined = vec![0_u8; hash_len * 2]; + loop { + let a = iter.next(); + let b = iter.next(); + visitor.visit(index, &a, &b); + + #[cfg(feature = "debug")] + log::debug!(" {:?}\n {:?}", a.as_ref().map(hex::encode), b.as_ref().map(hex::encode)); + + index += 2; + match (a, b) { + (Some(a), Some(b)) => { + if a < b { + combined[..hash_len].copy_from_slice(a.as_ref()); + combined[hash_len..].copy_from_slice(b.as_ref()); + } else { + combined[..hash_len].copy_from_slice(b.as_ref()); + combined[hash_len..].copy_from_slice(a.as_ref()); + } + + next.push(::hash(&combined)); + }, + // Odd number of items. Promote the item to the upper layer. + (Some(a), None) if !next.is_empty() => { + next.push(a); + }, + // Last item = root. + (Some(a), None) => return Ok(a), + // Finish up, no more items. + _ => { + #[cfg(feature = "debug")] + log::debug!( + "[merkelize_row] Next: {:?}", + next.iter().map(hex::encode).collect::>() + ); + return Err(next); + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use hex_literal::hex; + use sp_core::keccak_256; + use sp_runtime::traits::Keccak256; + + fn make_leaves(count: u64) -> Vec { + (0..count).into_iter().map(|i| keccak_256(&i.to_le_bytes()).into()).collect() + } + + #[test] + fn should_generate_empty_root() { + // given + let _ = env_logger::try_init(); + let data = vec![]; + + // when + let out = merkle_root::(data.into_iter()); + + // then + assert_eq!( + hex::encode(&out), + "0000000000000000000000000000000000000000000000000000000000000000" + ); + } + + #[test] + fn should_generate_single_root() { + // given + let _ = env_logger::try_init(); + let data = make_leaves(1); + + // when + let out = merkle_root::(data.into_iter()); + + // then + assert_eq!( + hex::encode(&out), + "011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce" + ); + } + + #[test] + fn should_generate_root_pow_2() { + // given + let _ = env_logger::try_init(); + let data = make_leaves(2); + + // when + let out = merkle_root::(data.into_iter()); + + // then + assert_eq!( + hex::encode(&out), + "e497bd1c13b13a60af56fa0d2703517c232fde213ad20d2c3dd60735c6604512" + ); + } + + #[test] + fn should_generate_root_complex() { + let _ = env_logger::try_init(); + let test = |root, data: Vec| { + assert_eq!( + array_bytes::bytes2hex("", &merkle_root::(data.into_iter()).as_ref()), + root + ); + }; + + test("816cc37bd8d39f7b0851838ebc875faf2afe58a03e95aca3b1333b3693f39dd3", make_leaves(3)); + + test("7501ea976cb92f305cca65ab11254589ea28bb8b59d3161506350adaa237d22f", make_leaves(4)); + + test("d26ba4eb398747bdd39255b1fadb99b803ce39696021b3b0bff7301ac146ee4e", make_leaves(10)); + } + + #[test] + #[ignore] + fn should_generate_and_verify_proof() { + // given + let _ = env_logger::try_init(); + let data: Vec = make_leaves(3); + + // when + let proof0 = merkle_proof::(data.clone().into_iter(), 0); + assert!(verify_proof::( + &proof0.root, + proof0.proof.clone(), + data.len() as u64, + proof0.leaf_index, + &data[0], + )); + + let proof1 = merkle_proof::(data.clone().into_iter(), 1); + assert!(verify_proof::( + &proof1.root, + proof1.proof, + data.len() as u64, + proof1.leaf_index, + &proof1.leaf, + )); + + let proof2 = merkle_proof::(data.clone().into_iter(), 2); + assert!(verify_proof::( + &proof2.root, + proof2.proof, + data.len() as u64, + proof2.leaf_index, + &proof2.leaf + )); + + // then + assert_eq!(hex::encode(proof0.root), hex::encode(proof1.root)); + assert_eq!(hex::encode(proof2.root), hex::encode(proof1.root)); + + assert!(!verify_proof::( + &H256::from_slice(&hex!( + "fb3b3be94be9e983ba5e094c9c51a7d96a4fa2e5d8e891df00ca89ba05bb1239" + )), + proof0.proof, + data.len() as u64, + proof0.leaf_index, + &proof0.leaf + )); + + assert!(!verify_proof::( + &proof0.root, + vec![], + data.len() as u64, + proof0.leaf_index, + &proof0.leaf + )); + } + + #[test] + #[should_panic] + fn should_panic_on_invalid_leaf_index() { + let _ = env_logger::try_init(); + merkle_proof::(make_leaves(1).into_iter(), 5); + } +} diff --git a/parachain/pallets/outbound-queue/rpc/Cargo.toml b/parachain/pallets/outbound-queue/rpc/Cargo.toml deleted file mode 100644 index 20b0d128a..000000000 --- a/parachain/pallets/outbound-queue/rpc/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "snowbridge-outbound-queue-rpc" -version = "0.1.0" -edition = "2021" -authors = [ "Snowfork " ] -repository = "https://github.com/Snowfork/snowbridge" - -[package.metadata.docs.rs] -targets = [ "x86_64-unknown-linux-gnu" ] - -[dependencies] -codec = { version = "3.1.5", package = "parity-scale-codec", features = [ "derive" ] } -jsonrpsee = { version = "0.16.2", features = ["server", "macros"] } - -sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "master" } -sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "master" } -sp-offchain = { git = 'https://github.com/paritytech/substrate.git', branch = 'master' } -parking_lot = "0.11.0" - -snowbridge-outbound-queue-merkle-proof = { path = "../merkle-proof" } - -[dev-dependencies] -serde_json = "1.0.96" -hex = "0.4.3" diff --git a/parachain/pallets/outbound-queue/rpc/src/lib.rs b/parachain/pallets/outbound-queue/rpc/src/lib.rs deleted file mode 100644 index 6ed7155b6..000000000 --- a/parachain/pallets/outbound-queue/rpc/src/lib.rs +++ /dev/null @@ -1,193 +0,0 @@ -use jsonrpsee::{ - core::{Error, RpcResult as Result}, - proc_macros::rpc, - types::error::{CallError, ErrorCode, ErrorObject}, -}; - -use codec::{Decode, Encode}; -use parking_lot::RwLock; -use sp_core::{offchain::OffchainStorage, Bytes, H256}; -use sp_runtime::traits::Keccak256; - -use std::sync::Arc; - -use snowbridge_outbound_queue_merkle_proof::merkle_proof; - -pub struct OutboundQueue { - storage: Arc>, -} - -impl OutboundQueue { - pub fn new(storage: T) -> Self { - Self { storage: Arc::new(RwLock::new(storage)) } - } -} - -#[derive(Decode)] -struct Leaves(pub Vec>); - -#[rpc(server)] -pub trait OutboundQueueApi { - #[method(name = "outboundQueue_getMerkleProof")] - fn get_merkle_proof(&self, commitment_hash: H256, leaf_index: u64) -> Result; -} - -impl OutboundQueueApiServer for OutboundQueue -where - T: OffchainStorage + 'static, -{ - fn get_merkle_proof(&self, commitment_hash: H256, leaf_index: u64) -> Result { - let encoded_leaves = match self - .storage - .read() - .get(sp_offchain::STORAGE_PREFIX, commitment_hash.as_bytes()) - { - Some(encoded_leaves) => encoded_leaves, - None => - return Err(Error::Call(CallError::Custom(ErrorObject::owned( - ErrorCode::InvalidParams.code(), - "no leaves found for given commitment", - None::<()>, - )))), - }; - - let leaves = match Leaves::decode(&mut encoded_leaves.as_ref()) { - Ok(leaves) => leaves, - Err(_) => - return Err(Error::Call(CallError::Custom(ErrorObject::owned( - ErrorCode::InternalError.code(), - "could not decode leaves from storage", - None::<()>, - )))), - }; - - if (leaf_index as usize) >= Vec::len(&leaves.0) { - return Err(Error::Call(CallError::Custom(ErrorObject::owned( - ErrorCode::InvalidParams.code(), - "leaf_index out of range", - None::<()>, - )))) - } - - let proof = merkle_proof::>, Vec>(leaves.0, leaf_index); - Ok(proof.encode().into()) - } -} - -#[cfg(test)] -mod tests { - use crate::{OutboundQueue, OutboundQueueApiServer}; - use codec::Encode; - use jsonrpsee::{ - core::Error, - types::error::{CallError, ErrorCode}, - }; - use sp_core::offchain::OffchainStorage; - - #[derive(Clone)] - struct MockOffchainStorage<'a> { - prefix: &'a [u8], - key: &'a [u8], - value: Option>, - } - impl<'a> OffchainStorage for MockOffchainStorage<'a> { - fn set(&mut self, _prefix: &[u8], _key: &[u8], _value: &[u8]) {} - fn remove(&mut self, _prefix: &[u8], _key: &[u8]) {} - fn compare_and_set( - &mut self, - _prefix: &[u8], - _key: &[u8], - _old_value: Option<&[u8]>, - _new_value: &[u8], - ) -> bool { - false - } - - fn get(&self, prefix: &[u8], key: &[u8]) -> Option> { - if prefix == self.prefix && key == self.key { - self.value.clone() - } else { - None - } - } - } - - const TEST_HASH: &[u8; 32] = &[0; 32]; - fn create_rpc_handler<'a>( - prefix: &'a [u8], - key: &'a [u8], - value: Option>, - ) -> OutboundQueue> { - let storage = MockOffchainStorage { prefix, key, value }; - OutboundQueue::new(storage) - } - - #[ignore] - #[test] - fn basic_channel_rpc_should_create_proof_for_existing_commitment() { - let encoded_leaves = hex::decode("088107000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000647ed9db598eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000810700000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000647ed9db59d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000") - .expect("test input should decode successfully"); - let rpc_handler = - create_rpc_handler(sp_offchain::STORAGE_PREFIX, TEST_HASH, Some(encoded_leaves)); - - let result = rpc_handler - .get_merkle_proof(TEST_HASH.into(), 0) - .expect("test input should have a Merkle proof") - .to_vec(); - let expected_proof = hex::decode("1145ecaf4f9ee757a1bbcd41ae26b43a75c0a16e07c01d3502af4a480c28cbb30485ab07a8698e29740bbbad18710faa8f055e9d398efd80ffd7ea6f76348aa803020000000000000000000000000000008107000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4800000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000647ed9db598eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000") - .expect("test proof should decode successfully"); - - assert_eq!(result, expected_proof); - } - - #[ignore] - #[test] - fn basic_channel_rpc_should_handle_non_existent_commitment() { - let rpc_handler = create_rpc_handler(sp_offchain::STORAGE_PREFIX, TEST_HASH, None); - - let result = rpc_handler.get_merkle_proof(TEST_HASH.into(), 0); - - match result { - Err(Error::Call(CallError::Custom(errobj))) => { - assert_eq!(errobj.code(), ErrorCode::InvalidParams.code()); - assert_eq!(errobj.message(), "no leaves found for given commitment"); - }, - _ => assert!(false), - } - } - - #[ignore] - #[test] - fn basic_channel_rpc_should_handle_incorrectly_encoded_leaves() { - let rpc_handler = - create_rpc_handler(sp_offchain::STORAGE_PREFIX, TEST_HASH, Some([42].to_vec())); - - let result = rpc_handler.get_merkle_proof(TEST_HASH.into(), 0); - - match result { - Err(Error::Call(CallError::Custom(errobj))) => { - assert_eq!(errobj.code(), ErrorCode::InternalError.code()); - assert_eq!(errobj.message(), "could not decode leaves from storage"); - }, - _ => assert!(false), - } - } - - #[ignore] - #[test] - fn basic_channel_rpc_should_handle_leaf_index_out_of_bounds() { - let leaves: Vec> = vec![vec![1, 2], vec![3, 4]]; - let rpc_handler = - create_rpc_handler(sp_offchain::STORAGE_PREFIX, TEST_HASH, Some(leaves.encode())); - - let result = rpc_handler.get_merkle_proof(TEST_HASH.into(), 2); - - match result { - Err(Error::Call(CallError::Custom(errobj))) => { - assert_eq!(errobj.code(), ErrorCode::InvalidParams.code()); - assert_eq!(errobj.message(), "leaf_index out of range"); - }, - _ => assert!(false), - } - } -} diff --git a/parachain/pallets/outbound-queue/runtime-api/Cargo.toml b/parachain/pallets/outbound-queue/runtime-api/Cargo.toml new file mode 100644 index 000000000..b3e9aadba --- /dev/null +++ b/parachain/pallets/outbound-queue/runtime-api/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "snowbridge-outbound-queue-runtime-api" +version = "0.1.0" +edition = "2021" +authors = [ "Snowfork " ] +repository = "https://github.com/Snowfork/snowbridge" + +[package.metadata.docs.rs] +targets = [ "x86_64-unknown-linux-gnu" ] + +[dependencies] +codec = { version = "3.1.5", package = "parity-scale-codec", features = [ "derive" ], default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false} +sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false} +sp-api = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false} +snowbridge-outbound-queue-merkle-tree = { path = "../merkle-tree", default-features = false} + + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-core/std", + "sp-api/std", + "sp-std/std", + "snowbridge-outbound-queue-merkle-tree/std" +] diff --git a/parachain/pallets/outbound-queue/runtime-api/src/lib.rs b/parachain/pallets/outbound-queue/runtime-api/src/lib.rs new file mode 100644 index 000000000..906db434f --- /dev/null +++ b/parachain/pallets/outbound-queue/runtime-api/src/lib.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +#![cfg_attr(not(feature = "std"), no_std)] + +use snowbridge_outbound_queue_merkle_tree::MerkleProof; + +sp_api::decl_runtime_apis! { + pub trait OutboundQueueApi + { + fn prove_message(leaf_index: u64) -> Option; + } +} diff --git a/parachain/pallets/outbound-queue/src/api.rs b/parachain/pallets/outbound-queue/src/api.rs new file mode 100644 index 000000000..5cbe7cebb --- /dev/null +++ b/parachain/pallets/outbound-queue/src/api.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +//! Helpers for implementing runtime api + +use frame_support::storage::StorageStreamIter; +use snowbridge_outbound_queue_merkle_tree::{merkle_proof, MerkleProof}; + +use crate::{Config, MessageLeaves}; + +pub fn prove_message(leaf_index: u64) -> Option +where + Runtime: Config, +{ + if !MessageLeaves::::exists() { + return None; + } + let proof = merkle_proof::<::Hashing, _>( + MessageLeaves::::stream_iter(), + leaf_index, + ); + Some(proof) +} diff --git a/parachain/pallets/outbound-queue/src/benchmarking.rs b/parachain/pallets/outbound-queue/src/benchmarking.rs index 4d33b762b..e1830c568 100644 --- a/parachain/pallets/outbound-queue/src/benchmarking.rs +++ b/parachain/pallets/outbound-queue/src/benchmarking.rs @@ -1,48 +1,62 @@ -//! BasicOutboundChannel pallet benchmarking +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use super::*; -use frame_benchmarking::{benchmarks, impl_benchmark_test_suite}; -use frame_support::traits::OnInitialize; +use codec::Encode; +use frame_benchmarking::v2::*; #[allow(unused_imports)] use crate::Pallet as OutboundQueue; -benchmarks! { - where_clause { - where - T::AccountId: AsRef<[u8]>, - } - // Benchmark `on_initialize` under worst case conditions, i.e. messages - // in queue are committed. - on_commit { - let m in 1 .. T::MaxMessagesPerCommit::get(); - let p in 0 .. T::MaxMessagePayloadSize::get()-1; - - for _ in 0 .. m { - let payload: Vec = (0..).take(p as usize).collect(); - >::try_append(Message { - origin: 1000.into(), - nonce: 0u64, - handler: 0, - payload: payload.try_into().unwrap(), - }).unwrap(); +#[benchmarks( + where + ::MaxMessagePayloadSize: Get, +)] +mod benchmarks { + use super::*; + + /// Benchmark for processing a message payload of length `x`. + #[benchmark] + fn do_process_message( + x: Linear<0, { T::MaxMessagePayloadSize::get() }>, + ) -> Result<(), BenchmarkError> { + let payload = (0..x).map(|_| 1u8).collect::>(); + + let enqueued_message = EnqueuedMessage { + id: H256::zero().into(), + origin: 1000.into(), + gateway: [1u8; 32].into(), + payload: payload.try_into().unwrap(), + }; + let encoded_enqueued_message = enqueued_message.encode(); + + #[block] + { + let _ = OutboundQueue::::do_process_message(&encoded_enqueued_message); } - let block_number = Interval::::get(); + assert_eq!(MessageLeaves::::decode_len().unwrap(), 1); - }: { OutboundQueue::::commit(Weight::MAX) } - verify { - assert_eq!(>::get().len(), 0); + Ok(()) } - // Benchmark 'on_initialize` for the case where it is a commitment interval - // but there are no messages in the queue. - on_commit_no_messages { - >::kill(); + /// Benchmark for producing final messages commitment + #[benchmark] + fn on_finalize() -> Result<(), BenchmarkError> { + // Assume worst case, where `MaxMessagesPerBlock` messages need to be committed. + for i in 0..T::MaxMessagesPerBlock::get() { + let leaf_data: [u8; 1] = [i as u8]; + let leaf = ::Hashing::hash(&leaf_data); + MessageLeaves::::append(leaf); + } + + #[block] + { + OutboundQueue::::commit_messages(); + } - let block_number = Interval::::get(); + Ok(()) + } - }: { OutboundQueue::::on_initialize(block_number) } + impl_benchmark_test_suite!(OutboundQueue, crate::test::new_tester(), crate::test::Test,); } - -impl_benchmark_test_suite!(OutboundQueue, crate::test::new_tester(), crate::test::Test,); diff --git a/parachain/pallets/outbound-queue/src/lib.rs b/parachain/pallets/outbound-queue/src/lib.rs index bb67b2f3b..d8e066edf 100644 --- a/parachain/pallets/outbound-queue/src/lib.rs +++ b/parachain/pallets/outbound-queue/src/lib.rs @@ -1,5 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork #![cfg_attr(not(feature = "std"), no_std)] +pub mod api; pub mod weights; #[cfg(feature = "runtime-benchmarks")] @@ -11,54 +14,74 @@ mod test; use codec::{Decode, Encode, MaxEncodedLen}; use ethabi::{self, Token}; use frame_support::{ - dispatch::DispatchResult, ensure, traits::Get, weights::Weight, BoundedVec, CloneNoBound, - PartialEqNoBound, RuntimeDebugNoBound, + ensure, + storage::StorageStreamIter, + traits::{EnqueueMessage, Get, ProcessMessage, ProcessMessageError}, + weights::Weight, + CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; use scale_info::TypeInfo; use snowbridge_core::ParaId; -use sp_core::H256; -use sp_io::offchain_index::set; +use sp_core::{RuntimeDebug, H256}; use sp_runtime::traits::Hash; use sp_std::prelude::*; -use snowbridge_core::OutboundQueue; -use snowbridge_outbound_queue_merkle_proof::merkle_root; +use snowbridge_core::{ + ContractId, OutboundMessage, OutboundQueue as OutboundQueueTrait, SubmitError, +}; +use snowbridge_outbound_queue_merkle_tree::merkle_root; +pub use snowbridge_outbound_queue_merkle_tree::MerkleProof; pub use weights::WeightInfo; -#[derive( - Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, -)] -#[scale_info(skip_type_params(M))] -pub struct Message> { +/// Aggregate message origin for the `MessageQueue` pallet. +#[derive(Encode, Decode, Clone, MaxEncodedLen, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub enum AggregateMessageOrigin { + #[codec(index = 0)] + Parachain(ParaId), +} + +/// Message which is awaiting processing in the MessageQueue pallet +#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug, TypeInfo)] +pub struct EnqueuedMessage { + /// Message ID (usually hash of message) + pub id: H256, + /// ID of source parachain + pub origin: ParaId, + /// The receiving gateway contract + pub gateway: ContractId, + /// Payload for target application. + pub payload: Vec, +} + +/// Message which has been assigned a nonce and will be committed at the end of a block +#[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo)] +pub struct Message { /// ID of source parachain origin: ParaId, /// Unique nonce to prevent replaying messages - #[codec(compact)] nonce: u64, - /// Handler to dispatch the message to - handler: u16, + /// The receiving gateway contract + gateway: ContractId, /// Payload for target application. - payload: BoundedVec, + payload: Vec, } -impl> Into for Message { +/// Convert message into an ABI-encoded form for delivery to the InboundQueue contract on Ethereum +impl Into for Message { fn into(self) -> Token { Token::Tuple(vec![ Token::Uint(u32::from(self.origin).into()), Token::Uint(self.nonce.into()), - Token::Uint(self.handler.into()), + Token::FixedBytes(self.gateway.to_fixed_bytes().into()), Token::Bytes(self.payload.to_vec()), ]) } } -// base_weight=(0.75*0.5)*(10**12)=375_000_000_000 -// we leave the extra 10_000_000_000/375_000_000_000=2.66% as margin -// so we can use at most 365000000000 for the commit call -// need to rerun benchmarks later to get weight based on the worst case: -// MaxMessagesPerCommit=20 and MaxMessagePayloadSize=256 -pub const MINIMUM_WEIGHT_REMAIN_IN_BLOCK: Weight = Weight::from_parts(10_000_000_000, 0); +/// The maximal length of an enqueued message, as determined by the MessageQueue pallet +pub type MaxEnqueuedMessageSizeOf = + <::MessageQueue as EnqueueMessage>::MaxMessageLen; pub use pallet::*; @@ -77,13 +100,15 @@ pub mod pallet { type Hashing: Hash; + type MessageQueue: EnqueueMessage; + /// Max bytes in a message payload #[pallet::constant] type MaxMessagePayloadSize: Get; - /// Max number of messages per commitment + /// Max number of messages processed per block #[pallet::constant] - type MaxMessagesPerCommit: Get; + type MaxMessagesPerBlock: Get; /// Weight information for extrinsics in this pallet type WeightInfo: WeightInfo; @@ -92,152 +117,186 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - MessageAccepted(u64), - Committed { hash: H256, data: Vec> }, + /// Message has been queued and will be processed in the future + MessageQueued { + /// ID of the message. Usually the XCM message hash. + id: H256, + }, + /// Message will be committed at the end of current block. From now on, to track the + /// progress the message, use the `nonce` of `id`. + MessageAccepted { + /// ID of the message + id: H256, + /// The nonce assigned to this message + nonce: u64, + }, + /// Some messages have been committed + MessagesCommitted { + /// Merkle root of the committed messages + root: H256, + /// number of committed messages + count: u64, + }, } #[pallet::error] pub enum Error { - /// The message payload exceeds byte limit. - PayloadTooLarge, - /// No more messages can be queued for the channel during this commit cycle. - QueueSizeLimitReached, - /// Cannot increment nonce - Overflow, + /// The message is too large + MessageTooLarge, } - /// Interval between commitments + /// Messages to be committed in the current block. This storage value is killed in + /// `on_initialize`, so should never go into block PoV. + /// + /// Is never read in the runtime, only by offchain code. + /// + /// Inspired by the `frame_system::Pallet::Events` storage value #[pallet::storage] - #[pallet::getter(fn interval)] - pub(super) type Interval = StorageValue<_, T::BlockNumber, ValueQuery>; + #[pallet::unbounded] + pub(super) type Messages = StorageValue<_, Vec, ValueQuery>; - /// Messages waiting to be committed. + /// Hashes of the ABI-encoded messages in the [`Messages`] storage value. Used to generate a + /// merkle root during `on_finalize`. This storage value is killed in + /// `on_initialize`, so should never go into block PoV. #[pallet::storage] - pub(super) type MessageQueue = StorageValue< - _, - BoundedVec, T::MaxMessagesPerCommit>, - ValueQuery, - >; + #[pallet::unbounded] + #[pallet::getter(fn message_leaves)] + pub(super) type MessageLeaves = StorageValue<_, Vec, ValueQuery>; + /// The current nonce for each message origin #[pallet::storage] pub type Nonce = StorageMap<_, Twox64Concat, ParaId, u64, ValueQuery>; - #[pallet::genesis_config] - pub struct GenesisConfig { - pub interval: T::BlockNumber, - } - - impl Default for GenesisConfig { - fn default() -> Self { - Self { interval: Default::default() } - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - >::put(self.interval); - } - } - #[pallet::hooks] impl Hooks> for Pallet where T::AccountId: AsRef<[u8]>, { - // Generate a message commitment when the chain is idle with enough remaining weight - // The commitment hash is included in an [`AuxiliaryDigestItem`] in the block header, - // with the corresponding commitment is persisted offchain. - fn on_idle(_n: T::BlockNumber, total_weight: Weight) -> Weight { - let weight_remaining = total_weight.saturating_sub(T::WeightInfo::on_commit( - T::MaxMessagesPerCommit::get(), - T::MaxMessagePayloadSize::get(), - )); - if weight_remaining.ref_time() <= MINIMUM_WEIGHT_REMAIN_IN_BLOCK.ref_time() { - return total_weight - } - Self::commit(total_weight) + fn on_initialize(_: T::BlockNumber) -> Weight { + // Remove storage from previous block + Messages::::kill(); + MessageLeaves::::kill(); + // Reserve some weight for the `on_finalize` handler + return T::WeightInfo::on_finalize(); } - } - impl OutboundQueue for Pallet { - /// Submit message on the outbound channel - fn submit(origin: ParaId, handler: u16, payload: &[u8]) -> DispatchResult { - ensure!( - >::decode_len().unwrap_or(0) < - T::MaxMessagesPerCommit::get() as usize, - Error::::QueueSizeLimitReached, - ); + fn on_finalize(_: T::BlockNumber) { + Self::commit_messages(); + } + } - let message_payload = - payload.to_vec().try_into().map_err(|_| Error::::PayloadTooLarge)?; - let nonce = >::get(origin); - let next_nonce = nonce.checked_add(1).ok_or(Error::::Overflow)?; + impl Pallet { + /// Generate a messages commitment and insert it into the header digest + pub(crate) fn commit_messages() { + let count = MessageLeaves::::decode_len().unwrap_or_default() as u64; + if count == 0 { + return; + } - >::try_append(Message { - origin: origin.clone(), - nonce, - handler, - payload: message_payload, - }) - .map_err(|_| Error::::QueueSizeLimitReached)?; - Self::deposit_event(Event::MessageAccepted(nonce)); + // Create merkle root of messages + let root = merkle_root::<::Hashing, _>(MessageLeaves::::stream_iter()); - >::set(origin, next_nonce); + // Insert merkle root into the header digest + >::deposit_log(sp_runtime::DigestItem::Other( + root.to_fixed_bytes().into(), + )); - Ok(()) + Self::deposit_event(Event::MessagesCommitted { root, count }); } - } - impl Pallet { - /// Commit messages enqueued on the outbound channel. - /// Find the Merkle root of all of the messages in the queue. (TODO: take a sublist, later - /// use weights to determine the size of the sublist). Use ethabi-encoded messages as the - /// leaves of the Merkle tree. Then: - /// - Store the commitment hash on the parachain for the Ethereum light client to query. - /// - Emit an event with the commitment hash and SCALE-encoded message bundles for a - /// relayer to read. - /// - Persist the ethabi-encoded message bundles to off-chain storage. - pub fn commit(_total_weight: Weight) -> Weight { - // TODO: SNO-310 consider using mutate here. If some part of emitting message bundles - // fails, we don't want the MessageQueue to be empty. - let message_queue = >::take(); - if message_queue.is_empty() { - return T::WeightInfo::on_commit_no_messages() - } + /// Process a message delivered by the MessageQueue pallet + pub(crate) fn do_process_message(mut message: &[u8]) -> Result { + let enqueued_message: EnqueuedMessage = + EnqueuedMessage::decode(&mut message).map_err(|_| ProcessMessageError::Corrupt)?; - // Store these to return the on_commit weight - let message_count = message_queue.len() as u32; - let average_payload_size = Self::average_payload_size(&message_queue); + let next_nonce = Nonce::::get(enqueued_message.origin).saturating_add(1); - let eth_messages: Vec> = message_queue - .clone() - .into_iter() - .map(|msg| ethabi::encode(&vec![msg.into()])) - .collect(); + let message: Message = Message { + origin: enqueued_message.origin, + nonce: next_nonce, + gateway: enqueued_message.gateway, + payload: enqueued_message.payload, + }; - let commitment_hash = - merkle_root::<::Hashing, Vec>, Vec>(eth_messages.clone()); + let message_abi_encoded = ethabi::encode(&vec![message.clone().into()]); + let message_abi_encoded_hash = ::Hashing::hash(&message_abi_encoded); - >::deposit_log(sp_runtime::DigestItem::Other( - commitment_hash.to_fixed_bytes().into(), - )); + Messages::::append(Box::new(message)); + MessageLeaves::::append(message_abi_encoded_hash); + Nonce::::set(enqueued_message.origin, next_nonce); - Self::deposit_event(Event::Committed { - hash: commitment_hash, - data: message_queue.to_vec(), + Self::deposit_event(Event::MessageAccepted { + id: enqueued_message.id, + nonce: next_nonce, }); - set(commitment_hash.as_bytes(), ð_messages.encode()); + Ok(true) + } + } + + /// A message which can be accepted by the [`OutboundQueue`] + #[derive(Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound)] + pub struct OutboundQueueTicket> { + id: H256, + origin: ParaId, + message: BoundedVec, + } + + impl OutboundQueueTrait for Pallet { + type Ticket = OutboundQueueTicket>; - return T::WeightInfo::on_commit(message_count, average_payload_size) + fn validate(message: &OutboundMessage) -> Result { + // The inner payload should not be too large + ensure!( + message.payload.len() < T::MaxMessagePayloadSize::get() as usize, + SubmitError::MessageTooLarge + ); + let message: EnqueuedMessage = EnqueuedMessage { + id: message.id, + origin: message.origin, + gateway: message.gateway, + payload: message.payload.clone().into(), + }; + // The whole message should not be too large + let encoded = message.encode().try_into().map_err(|_| SubmitError::MessageTooLarge)?; + + let ticket = + OutboundQueueTicket { id: message.id, origin: message.origin, message: encoded }; + Ok(ticket) + } + + fn submit(ticket: Self::Ticket) -> Result<(), SubmitError> { + T::MessageQueue::enqueue_message( + ticket.message.as_bounded_slice(), + AggregateMessageOrigin::Parachain(ticket.origin), + ); + Self::deposit_event(Event::MessageQueued { id: ticket.id }); + Ok(()) } + } + + impl ProcessMessage for Pallet { + type Origin = AggregateMessageOrigin; + fn process_message( + message: &[u8], + _: Self::Origin, + meter: &mut frame_support::weights::WeightMeter, + _: &mut [u8; 32], + ) -> Result { + // Yield if we don't want to accept any more messages in the current block. + // There is hard limit to ensure the weight of `on_finalize` is bounded. + ensure!( + MessageLeaves::::decode_len().unwrap_or(0) + < T::MaxMessagesPerBlock::get() as usize, + ProcessMessageError::Yield + ); + + let weight = T::WeightInfo::do_process_message(); + if !meter.check_accrue(weight) { + return Err(ProcessMessageError::Overweight(weight)); + } - fn average_payload_size(messages: &[Message]) -> u32 { - let sum: usize = messages.iter().fold(0, |acc, x| acc + (*x).payload.len()); - // We overestimate message payload size rather than underestimate. - // So add 1 here to account for integer division truncation. - (sum / messages.len()).saturating_add(1) as u32 + Self::do_process_message(message) } } } diff --git a/parachain/pallets/outbound-queue/src/test.rs b/parachain/pallets/outbound-queue/src/test.rs index 7c18b8fa7..368082377 100644 --- a/parachain/pallets/outbound-queue/src/test.rs +++ b/parachain/pallets/outbound-queue/src/test.rs @@ -1,20 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use super::*; use frame_support::{ - assert_noop, assert_ok, parameter_types, - traits::{Everything, GenesisBuild, OnInitialize}, + assert_err, assert_noop, assert_ok, parameter_types, + traits::{Everything, Hooks, ProcessMessageError}, + weights::WeightMeter, }; -use snowbridge_core::ParaId; + use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Keccak256, Verify}, - MultiSignature, + BoundedVec, MultiSignature, }; use sp_std::convert::From; -use crate::{self as outbound_channel}; - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -25,7 +26,8 @@ frame_support::construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Storage, Event}, - BasicOutboundChannel: outbound_channel::{Pallet, Config, Storage, Event}, + MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event}, + OutboundQueue: crate::{Pallet, Storage, Event}, } ); @@ -63,114 +65,162 @@ impl frame_system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; } +parameter_types! { + pub const HeapSize: u32 = 32 * 1024; + pub const MaxStale: u32 = 32; + pub static ServiceWeight: Option = Some(Weight::from_parts(100, 100)); +} + +impl pallet_message_queue::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type MessageProcessor = OutboundQueue; + type Size = u32; + type QueueChangeHandler = (); + type HeapSize = HeapSize; + type MaxStale = MaxStale; + type ServiceWeight = ServiceWeight; +} + parameter_types! { pub const MaxMessagePayloadSize: u32 = 256; - pub const MaxMessagesPerCommit: u32 = 20; + pub const MaxMessagesPerBlock: u32 = 20; } -impl outbound_channel::Config for Test { +impl crate::Config for Test { type RuntimeEvent = RuntimeEvent; type Hashing = Keccak256; + type MessageQueue = MessageQueue; type MaxMessagePayloadSize = MaxMessagePayloadSize; - type MaxMessagesPerCommit = MaxMessagesPerCommit; + type MaxMessagesPerBlock = MaxMessagesPerBlock; type WeightInfo = (); } pub fn new_tester() -> sp_io::TestExternalities { - let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - let config: outbound_channel::GenesisConfig = - outbound_channel::GenesisConfig { interval: 1u64 }; - config.assimilate_storage(&mut storage).unwrap(); - + let storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); let mut ext: sp_io::TestExternalities = storage.into(); - ext.execute_with(|| System::set_block_number(1)); ext } -fn run_to_block(n: u64) { - while System::block_number() < n { - System::set_block_number(System::block_number() + 1); - System::on_initialize(System::block_number()); - BasicOutboundChannel::on_initialize(System::block_number()); - } +fn run_to_end_of_next_block() { + // finish current block + MessageQueue::on_finalize(System::block_number()); + OutboundQueue::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + // start next block + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + OutboundQueue::on_initialize(System::block_number()); + MessageQueue::on_initialize(System::block_number()); + // finish next block + MessageQueue::on_finalize(System::block_number()); + OutboundQueue::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); } #[test] -fn test_submit() { +fn submit_messages_from_multiple_origins_and_commit() { new_tester().execute_with(|| { - let parachain_id: ParaId = ParaId::new(1000); - - assert_ok!(BasicOutboundChannel::submit(parachain_id, 0, &vec![0, 1, 2])); - - assert_eq!(>::get(parachain_id), 1); - assert_eq!(>::get().len(), 1); + //next_block(); + + for para_id in 1000..1004 { + let message = OutboundMessage { + id: H256::repeat_byte(1).into(), + origin: para_id.into(), + gateway: [1u8; 32].into(), + payload: (0..100).map(|_| 1u8).collect::>(), + }; + + let result = OutboundQueue::validate(&message); + assert!(result.is_ok()); + let ticket = result.unwrap(); + + assert_ok!(OutboundQueue::submit(ticket)); + } + + ServiceWeight::set(Some(Weight::MAX)); + run_to_end_of_next_block(); + + for para_id in 1000..1004 { + let origin: ParaId = (para_id as u32).into(); + assert_eq!(Nonce::::get(origin), 1); + } + + let digest = System::digest(); + let digest_items = digest.logs(); + assert!(digest_items.len() == 1 && digest_items[0].as_other().is_some()); }); } #[test] -fn test_submit_exceeds_queue_limit() { +fn submit_message_fail_too_large() { new_tester().execute_with(|| { - let parachain_id: ParaId = ParaId::new(1000); - - let max_messages = MaxMessagesPerCommit::get(); - (0..max_messages) - .for_each(|_| BasicOutboundChannel::submit(parachain_id, 0, &vec![0, 1, 2]).unwrap()); - - assert_noop!( - BasicOutboundChannel::submit(parachain_id, 0, &vec![0, 1, 2]), - Error::::QueueSizeLimitReached, - ); - }) + let message = OutboundMessage { + id: H256::repeat_byte(1).into(), + origin: 1000.into(), + gateway: [1u8; 32].into(), + payload: (0..1000).map(|_| 1u8).collect::>(), + }; + + assert_err!(OutboundQueue::validate(&message), SubmitError::MessageTooLarge); + }); } #[test] -fn test_submit_exceeds_payload_limit() { +fn commit_exits_early_if_no_processed_messages() { new_tester().execute_with(|| { - let parachain_id: ParaId = ParaId::new(1000); - - let max_payload_bytes = MaxMessagePayloadSize::get() - 1; + // on_finalize should do nothing, nor should it panic + OutboundQueue::on_finalize(System::block_number()); - let mut payload: Vec = (0..).take(max_payload_bytes as usize).collect(); - // Make payload oversize - payload.push(5); - payload.push(10); - - assert_noop!( - BasicOutboundChannel::submit(parachain_id, 0, payload.as_slice()), - Error::::PayloadTooLarge, - ); - }) + let digest = System::digest(); + let digest_items = digest.logs(); + assert_eq!(digest_items.len(), 0); + }); } #[test] -fn test_commit_single_user() { +fn process_message_yields_on_max_messages_per_block() { new_tester().execute_with(|| { - let parachain_id: ParaId = ParaId::new(1000); + for _ in 0..::MaxMessagesPerBlock::get() { + MessageLeaves::::append(H256::zero()) + } + + let origin = AggregateMessageOrigin::Parachain(1000.into()); + let message = (0..100).map(|_| 1u8).collect::>(); + let message: BoundedVec> = message.try_into().unwrap(); - assert_ok!(BasicOutboundChannel::submit(parachain_id, 0, &vec![0, 1, 2])); - run_to_block(2); - BasicOutboundChannel::commit(Weight::MAX); + let mut meter = WeightMeter::max_limit(); - assert_eq!(>::get(parachain_id), 1); - assert_eq!(>::get().len(), 0); + assert_noop!( + OutboundQueue::process_message( + &message.as_bounded_slice(), + origin, + &mut meter, + &mut [0u8; 32] + ), + ProcessMessageError::Yield + ); }) } #[test] -fn test_commit_multi_user() { +fn process_message_fails_on_overweight_message() { new_tester().execute_with(|| { - let parachain0: ParaId = ParaId::new(1000); - let parachain1: ParaId = ParaId::new(1001); + let origin = AggregateMessageOrigin::Parachain(1000.into()); + let message = (0..100).map(|_| 1u8).collect::>(); + let message: BoundedVec> = message.try_into().unwrap(); - assert_ok!(BasicOutboundChannel::submit(parachain0, 0, &vec![0, 1, 2])); - assert_ok!(BasicOutboundChannel::submit(parachain1, 0, &vec![0, 1, 2])); - run_to_block(2); - BasicOutboundChannel::commit(Weight::MAX); + let mut meter = WeightMeter::from_limit(Weight::from_parts(1, 1)); - assert_eq!(>::get(parachain0), 1); - assert_eq!(>::get(parachain1), 1); - assert_eq!(>::get().len(), 0); + assert_noop!( + OutboundQueue::process_message( + &message.as_bounded_slice(), + origin, + &mut meter, + &mut [0u8; 32] + ), + ProcessMessageError::Overweight(::WeightInfo::do_process_message()) + ); }) } diff --git a/parachain/pallets/outbound-queue/src/weights.rs b/parachain/pallets/outbound-queue/src/weights.rs index 9f7598b3b..3b42755b2 100644 --- a/parachain/pallets/outbound-queue/src/weights.rs +++ b/parachain/pallets/outbound-queue/src/weights.rs @@ -35,43 +35,28 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for basic_channel::outbound. pub trait WeightInfo { - fn on_commit_no_messages() -> Weight; - fn on_commit(m: u32, p: u32, ) -> Weight; + fn do_process_message() -> Weight; + fn on_finalize() -> Weight; } -/// Weights for basic_channel::outbound using the Snowbridge node and recommended hardware. pub struct SnowbridgeWeight(PhantomData); impl WeightInfo for SnowbridgeWeight { - fn on_commit_no_messages() -> Weight { - Weight::from_parts(5_228_000 as u64, 0) + fn do_process_message() -> Weight { + Weight::from_parts(100_000_000 as u64, 0) .saturating_add(T::DbWeight::get().reads(2)) } - fn on_commit(m: u32, p: u32, ) -> Weight { - Weight::from_parts(3_294_000 as u64, 0) - // Standard Error: 31_000 - .saturating_add(Weight::from_parts(100_849_000 as u64, 0).saturating_mul(m as u64)) - // Standard Error: 1_000 - .saturating_add(Weight::from_parts(3_880_000 as u64, 0).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + fn on_finalize() -> Weight { + Weight::from_parts(100_000_000 as u64, 0) + .saturating_add(T::DbWeight::get().reads(2)) } } -// For backwards compatibility and tests impl WeightInfo for () { - fn on_commit_no_messages() -> Weight { - Weight::from_parts(5_228_000 as u64, 0) - .saturating_add(RocksDbWeight::get().reads(2)) + fn do_process_message() -> Weight { + Weight::from_parts(100 as u64, 100) } - fn on_commit(m: u32, p: u32, ) -> Weight { - Weight::from_parts(0 as u64, 0) - // Standard Error: 31_000 - .saturating_add(Weight::from_parts(100_849_000 as u64, 0).saturating_mul(m as u64)) - // Standard Error: 1_000 - .saturating_add(Weight::from_parts(3_880_000 as u64, 0).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + fn on_finalize() -> Weight { + Weight::from_parts(100 as u64, 100) } } diff --git a/parachain/primitives/beacon/src/bits.rs b/parachain/primitives/beacon/src/bits.rs index f8bcb92aa..72b7135ee 100644 --- a/parachain/primitives/beacon/src/bits.rs +++ b/parachain/primitives/beacon/src/bits.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use sp_std::{convert::TryInto, prelude::*}; use ssz_rs::{Bitvector, Deserialize}; diff --git a/parachain/primitives/beacon/src/bls.rs b/parachain/primitives/beacon/src/bls.rs index 1b8a0d623..589b72e67 100644 --- a/parachain/primitives/beacon/src/bls.rs +++ b/parachain/primitives/beacon/src/bls.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use crate::{PublicKey, Signature}; use codec::{Decode, Encode}; use frame_support::{ensure, PalletError}; diff --git a/parachain/primitives/beacon/src/config.rs b/parachain/primitives/beacon/src/config.rs index 37d023de8..aa5fda706 100644 --- a/parachain/primitives/beacon/src/config.rs +++ b/parachain/primitives/beacon/src/config.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork pub const MAX_PROOF_SIZE: u32 = 20; pub const FEE_RECIPIENT_SIZE: usize = 20; diff --git a/parachain/primitives/beacon/src/lib.rs b/parachain/primitives/beacon/src/lib.rs index fcb977957..3527e1ff0 100644 --- a/parachain/primitives/beacon/src/lib.rs +++ b/parachain/primitives/beacon/src/lib.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork #![cfg_attr(not(feature = "std"), no_std)] pub mod bits; diff --git a/parachain/primitives/beacon/src/merkle_proof.rs b/parachain/primitives/beacon/src/merkle_proof.rs index 162780d8f..ae5aa0d78 100644 --- a/parachain/primitives/beacon/src/merkle_proof.rs +++ b/parachain/primitives/beacon/src/merkle_proof.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use sp_core::H256; use sp_io::hashing::sha2_256; @@ -12,7 +14,7 @@ pub fn verify_merkle_branch( ) -> bool { // verify the proof length if branch.len() != depth { - return false + return false; } // verify the computed merkle root root == compute_merkle_root(leaf, branch, index) diff --git a/parachain/primitives/beacon/src/receipt.rs b/parachain/primitives/beacon/src/receipt.rs index 812ca5ee9..ba863f344 100644 --- a/parachain/primitives/beacon/src/receipt.rs +++ b/parachain/primitives/beacon/src/receipt.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use sp_core::H256; use sp_io::hashing::keccak_256; use sp_std::prelude::*; @@ -28,7 +30,7 @@ fn apply_merkle_proof(proof: &[Vec]) -> Option<(H256, Vec)> { let expected_hash = maybe_hash?; let node: Box = bytes.as_slice().try_into().ok()?; if (*node).contains_hash(expected_hash.into()) { - return Some(keccak_256(bytes)) + return Some(keccak_256(bytes)); } None }); diff --git a/parachain/primitives/beacon/src/serde_utils.rs b/parachain/primitives/beacon/src/serde_utils.rs index 037987357..7e4688d29 100644 --- a/parachain/primitives/beacon/src/serde_utils.rs +++ b/parachain/primitives/beacon/src/serde_utils.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use sp_core::U256; use core::fmt::Formatter; @@ -116,7 +118,7 @@ impl<'de, const LENGTH: usize> serde::de::Visitor<'de> for HexVisitor { Err(e) => return Err(serde::de::Error::custom(e.to_string())), }; if decoded.len() != LENGTH { - return Err(serde::de::Error::custom("publickey expected to be 48 characters")) + return Err(serde::de::Error::custom("publickey expected to be 48 characters")); } let data: Self::Value = decoded diff --git a/parachain/primitives/beacon/src/ssz.rs b/parachain/primitives/beacon/src/ssz.rs index 68f1b3bcd..164cc1360 100644 --- a/parachain/primitives/beacon/src/ssz.rs +++ b/parachain/primitives/beacon/src/ssz.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use crate::{ config::{EXTRA_DATA_SIZE, FEE_RECIPIENT_SIZE, LOGS_BLOOM_SIZE, PUBKEY_SIZE, SIGNATURE_SIZE}, types::{ diff --git a/parachain/primitives/beacon/src/types.rs b/parachain/primitives/beacon/src/types.rs index 15fc66f94..798cf7174 100644 --- a/parachain/primitives/beacon/src/types.rs +++ b/parachain/primitives/beacon/src/types.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use scale_info::TypeInfo; diff --git a/parachain/primitives/beacon/src/updates.rs b/parachain/primitives/beacon/src/updates.rs index 94c0219d3..c002e265f 100644 --- a/parachain/primitives/beacon/src/updates.rs +++ b/parachain/primitives/beacon/src/updates.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use codec::{Decode, Encode}; use frame_support::{CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound}; use scale_info::TypeInfo; diff --git a/parachain/primitives/core/Cargo.toml b/parachain/primitives/core/Cargo.toml index 01d98e892..d55e5c04f 100644 --- a/parachain/primitives/core/Cargo.toml +++ b/parachain/primitives/core/Cargo.toml @@ -11,6 +11,7 @@ scale-info = { version = "2.7.0", default-features = false, features = [ "derive snowbridge-ethereum = { path = "../ethereum", default-features = false } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "master", default-features = false } @@ -32,6 +33,7 @@ std = [ "sp-std/std", "sp-core/std", "sp-runtime/std", - "snowbridge-ethereum/std" + "snowbridge-ethereum/std", + "xcm/std" ] runtime-benchmarks = [] diff --git a/parachain/primitives/core/src/lib.rs b/parachain/primitives/core/src/lib.rs index f63f7d887..60fb4cfe7 100644 --- a/parachain/primitives/core/src/lib.rs +++ b/parachain/primitives/core/src/lib.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork //! # Core //! //! Common traits and types @@ -6,9 +8,12 @@ #![allow(unused_variables)] #![cfg_attr(not(feature = "std"), no_std)] +use codec::{Decode, Encode}; use frame_support::dispatch::DispatchError; +use scale_info::TypeInfo; use snowbridge_ethereum::Log; -use sp_runtime::DispatchResult; +use sp_core::{RuntimeDebug, H256}; +use sp_std::vec::Vec; pub mod ringbuffer; pub mod types; @@ -17,6 +22,37 @@ pub use polkadot_parachain::primitives::Id as ParaId; pub use ringbuffer::{RingBufferMap, RingBufferMapImpl}; pub use types::{Message, MessageId, MessageNonce, Proof}; +/// A stable id for a bridge contract on the Ethereum side +#[derive(Copy, Clone, Encode, Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)] +pub struct ContractId([u8; 32]); + +impl From<[u8; 32]> for ContractId { + fn from(value: [u8; 32]) -> Self { + Self(value) + } +} + +impl From for ContractId { + fn from(value: H256) -> Self { + Self(value.to_fixed_bytes()) + } +} + +impl ContractId { + pub fn to_fixed_bytes(self) -> [u8; 32] { + self.0 + } + pub fn as_fixed_bytes(&self) -> &[u8; 32] { + &self.0 + } +} + +impl ContractId { + pub const fn new(id: [u8; 32]) -> Self { + Self(id) + } +} + /// A trait for verifying messages. /// /// This trait should be implemented by runtime modules that wish to provide message verification @@ -25,6 +61,44 @@ pub trait Verifier { fn verify(message: &Message) -> Result; } +#[derive(Copy, Clone, PartialEq, Eq, RuntimeDebug)] +pub enum SubmitError { + MessageTooLarge, +} + +/// A message which can be accepted by the [`OutboundQueue`] +#[derive(Clone, RuntimeDebug)] +pub struct OutboundMessage { + /// A unique ID to identify a message while its in the processing queue. Usually the Xcm + /// message hash. + pub id: H256, + /// The parachain from which the message originated + pub origin: ParaId, + /// The stable ID for a receiving gateway contract + pub gateway: ContractId, + /// ABI-encoded message payload which can be interpreted by the receiving gateway contract + pub payload: Vec, +} + +// A trait for enqueueing messages for delivery to Ethereum pub trait OutboundQueue { - fn submit(source_id: ParaId, handler: u16, payload: &[u8]) -> DispatchResult; + type Ticket; + + /// Validate a message destined for Ethereum + fn validate(message: &OutboundMessage) -> Result; + + /// Submit the message for eventual delivery to Ethereum + fn submit(ticket: Self::Ticket) -> Result<(), SubmitError>; +} + +impl OutboundQueue for () { + type Ticket = u64; + + fn validate(message: &OutboundMessage) -> Result { + Ok(0) + } + + fn submit(ticket: Self::Ticket) -> Result<(), SubmitError> { + Ok(()) + } } diff --git a/parachain/primitives/core/src/ringbuffer.rs b/parachain/primitives/core/src/ringbuffer.rs index 94e0b66c2..dcee20359 100644 --- a/parachain/primitives/core/src/ringbuffer.rs +++ b/parachain/primitives/core/src/ringbuffer.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use codec::FullCodec; use core::{cmp::Ord, marker::PhantomData, ops::Add}; use frame_support::storage::{types::QueryKindTrait, StorageMap, StorageValue}; diff --git a/parachain/primitives/core/src/types.rs b/parachain/primitives/core/src/types.rs index 00595aedf..34c6dab56 100644 --- a/parachain/primitives/core/src/types.rs +++ b/parachain/primitives/core/src/types.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork //! Types for representing messages use codec::{Decode, Encode}; diff --git a/parachain/primitives/ethereum/src/difficulty.rs b/parachain/primitives/ethereum/src/difficulty.rs deleted file mode 100644 index b448c0d07..000000000 --- a/parachain/primitives/ethereum/src/difficulty.rs +++ /dev/null @@ -1,270 +0,0 @@ -use crate::header::Header; -use ethereum_types::U256; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; -use sp_std::convert::TryFrom; - -use codec::{Decode, Encode}; - -const DIFFICULTY_BOUND_DIVISOR: u32 = 11; // right-shifts equivalent to division by 2048 -const EXP_DIFFICULTY_PERIOD: u64 = 100000; -const MINIMUM_DIFFICULTY: u32 = 131072; - -#[derive(PartialEq, RuntimeDebug)] -pub enum BombDelay { - // See https://eips.ethereum.org/EIPS/eip-649 - Byzantium = 3000000, - // See https://eips.ethereum.org/EIPS/eip-1234 - Constantinople = 5000000, - // See https://eips.ethereum.org/EIPS/eip-2384 - MuirGlacier = 9000000, - // See https://eips.ethereum.org/EIPS/eip-3554 - London = 9700000, -} - -/// Describes when hard forks occurred that affect difficulty calculations. These -/// values are network-specific. -#[derive(Copy, Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] -pub struct DifficultyConfig { - // Block number on which Byzantium (EIP-649) rules activated - pub byzantium_fork_block: u64, - // Block number on which Constantinople (EIP-1234) rules activated - pub constantinople_fork_block: u64, - // Block number on which MuirGlacier (EIP-2384) activated - pub muir_glacier_fork_block: u64, - // Block number on which London (EIP-3554) activated - pub london_fork_block: u64, -} - -impl DifficultyConfig { - // Correct block numbers for mainnet and various testnets can be found here: - // https://github.com/ethereum/go-ethereum/blob/498458b4102c0d32d7453035a115e6b9df5e485d/params/config.go#L55-L258 - pub const fn mainnet() -> Self { - DifficultyConfig { - byzantium_fork_block: 4370000, - constantinople_fork_block: 7280000, - muir_glacier_fork_block: 9200000, - london_fork_block: 12965000, - } - } - - pub const fn ropsten() -> Self { - DifficultyConfig { - byzantium_fork_block: 1700000, - constantinople_fork_block: 4230000, - muir_glacier_fork_block: 7117117, - london_fork_block: 10499401, - } - } - - pub fn bomb_delay(&self, block_number: u64) -> Option { - if block_number >= self.london_fork_block { - return Some(BombDelay::London) - } else if block_number >= self.muir_glacier_fork_block { - return Some(BombDelay::MuirGlacier) - } else if block_number >= self.constantinople_fork_block { - return Some(BombDelay::Constantinople) - } else if block_number >= self.byzantium_fork_block { - return Some(BombDelay::Byzantium) - } - None - } -} - -/// This difficulty calculation follows Byzantium rules (https://eips.ethereum.org/EIPS/eip-649) -/// and shouldn't be used to calculate difficulty prior to the Byzantium fork. -pub fn calc_difficulty( - config: &DifficultyConfig, - time: u64, - parent: &Header, -) -> Result { - let bomb_delay = config - .bomb_delay(parent.number + 1) - .ok_or("Cannot calculate difficulty for block number prior to Byzantium")?; - - let block_time_div_9: i64 = time - .checked_sub(parent.timestamp) - .ok_or("Invalid block time") - .and_then(|x| i64::try_from(x / 9).or(Err("Invalid block time")))?; - let sigma2: i64 = match parent.has_ommers() { - true => 2 - block_time_div_9, - false => 1 - block_time_div_9, - } - .max(-99); - - let mut difficulty_without_exp = parent.difficulty; - if sigma2 < 0 { - difficulty_without_exp -= - (parent.difficulty >> DIFFICULTY_BOUND_DIVISOR) * sigma2.abs() as u64; - } else { - difficulty_without_exp += (parent.difficulty >> DIFFICULTY_BOUND_DIVISOR) * sigma2 as u64; - } - - difficulty_without_exp = difficulty_without_exp.max(MINIMUM_DIFFICULTY.into()); - - // Subtract 1 less since we're using the parent block - let fake_block_number = parent.number.saturating_sub(bomb_delay as u64 - 1); - let period_count = fake_block_number / EXP_DIFFICULTY_PERIOD; - - // If period_count < 2, exp is fractional and we can skip adding it - if period_count >= 2 { - return Ok(difficulty_without_exp + U256::from(2).pow((period_count - 2).into())) - } - - Ok(difficulty_without_exp) -} - -#[cfg(test)] -mod tests { - - use super::*; - use ethereum_types::H256; - use serde::{Deserialize, Deserializer}; - use sp_std::convert::TryInto; - use std::{collections::BTreeMap, fmt::Display, fs::File, path::PathBuf}; - - pub fn deserialize_uint_from_string<'de, T, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - T: TryFrom + Deserialize<'de>, - >::Error: Display, - { - #[derive(Deserialize)] - #[serde(untagged)] - enum StringOrInt { - String(String), - Number(T), - } - - match StringOrInt::::deserialize(deserializer)? { - StringOrInt::String(s) => { - let maybe_uint = { - if (&s).starts_with("0x") { - u128::from_str_radix(&s.trim_start_matches("0x"), 16) - } else { - u128::from_str_radix(&s, 10) - } - }; - match maybe_uint { - Err(e) => Err(serde::de::Error::custom(e)), - Ok(uint) => uint.try_into().map_err(serde::de::Error::custom), - } - }, - StringOrInt::Number(i) => Ok(i), - } - } - - #[derive(Debug, PartialEq, Deserialize)] - #[serde(rename_all = "camelCase")] - pub struct DifficultyTestCase { - /// Parent timestamp. - #[serde(deserialize_with = "deserialize_uint_from_string")] - pub parent_timestamp: u64, - /// Parent difficulty. - #[serde(deserialize_with = "deserialize_uint_from_string")] - pub parent_difficulty: U256, - /// Parent uncle hash. - pub parent_uncles: H256, - /// Current timestamp. - #[serde(deserialize_with = "deserialize_uint_from_string")] - pub current_timestamp: u64, - /// Current difficulty. - #[serde(deserialize_with = "deserialize_uint_from_string")] - pub current_difficulty: U256, - /// Current block number. - #[serde(deserialize_with = "deserialize_uint_from_string")] - pub current_block_number: u64, - } - - #[derive(Debug, PartialEq, Deserialize)] - pub struct DifficultyTest(BTreeMap); - - impl DifficultyTest { - /// Loads test from json. - pub fn from_fixture(fixture: &str) -> Self { - let path: PathBuf = - [env!("CARGO_MANIFEST_DIR"), "tests", "fixtures", fixture].iter().collect(); - serde_json::from_reader(File::open(&path).unwrap()).unwrap() - } - } - - macro_rules! test_difficulty { - ($fixture:literal, $config:ident) => { - let test_cases = DifficultyTest::from_fixture($fixture); - - for (test_case_name, test_case) in &test_cases.0 { - let mut parent: Header = Default::default(); - parent.number = test_case.current_block_number - 1; - parent.timestamp = test_case.parent_timestamp; - parent.difficulty = test_case.parent_difficulty; - parent.ommers_hash = test_case.parent_uncles; - - let difficulty = calc_difficulty(&$config, test_case.current_timestamp, &parent); - if $config.byzantium_fork_block > test_case.current_block_number { - assert_eq!( - difficulty, - Err("Cannot calculate difficulty for block number prior to Byzantium"), - "Test case {} failed: {:?}", - test_case_name, - test_case, - ); - } else { - assert_eq!( - difficulty, - Ok(test_case.current_difficulty), - "Test case {} failed: {:?}", - test_case_name, - test_case, - ); - } - } - }; - } - - #[test] - fn byzantium_difficulty_calc_is_correct() { - let all_blocks_are_byzantium = DifficultyConfig { - byzantium_fork_block: 0, - constantinople_fork_block: u64::max_value(), - muir_glacier_fork_block: u64::max_value(), - london_fork_block: u64::max_value(), - }; - test_difficulty!("difficultyByzantium.json", all_blocks_are_byzantium); - } - - #[test] - fn constantinople_difficulty_calc_is_correct() { - let all_blocks_are_constantinople = DifficultyConfig { - byzantium_fork_block: 0, - constantinople_fork_block: 0, - muir_glacier_fork_block: u64::max_value(), - london_fork_block: u64::max_value(), - }; - test_difficulty!("difficultyConstantinople.json", all_blocks_are_constantinople); - } - - #[test] - fn muir_glacier_difficulty_calc_is_correct() { - let all_blocks_are_muir_glacier = DifficultyConfig { - byzantium_fork_block: 0, - constantinople_fork_block: 0, - muir_glacier_fork_block: 0, - london_fork_block: u64::max_value(), - }; - test_difficulty!("difficultyEIP2384.json", all_blocks_are_muir_glacier); - test_difficulty!("difficultyEIP2384_random.json", all_blocks_are_muir_glacier); - test_difficulty!("difficultyEIP2384_random_to20M.json", all_blocks_are_muir_glacier); - } - - #[test] - fn mainnet_difficulty_calc_is_correct() { - let mainnet_config = DifficultyConfig::mainnet(); - test_difficulty!("difficultyMainNetwork.json", mainnet_config); - } - - #[test] - fn ropsten_difficulty_calc_is_correct() { - let ropsten_config = DifficultyConfig::ropsten(); - test_difficulty!("difficultyRopsten.json", ropsten_config); - } -} diff --git a/parachain/primitives/ethereum/src/ethashdata.rs b/parachain/primitives/ethereum/src/ethashdata.rs deleted file mode 100644 index d9307e965..000000000 --- a/parachain/primitives/ethereum/src/ethashdata.rs +++ /dev/null @@ -1,518 +0,0 @@ -use hex_literal::hex; - -pub const DAGS_START_EPOCH: u64 = 0; - -pub const DAGS_MERKLE_ROOTS: [[u8; 16]; 512] = [ - hex!("55b891e842e58f58956a847cbbf67821"), - hex!("fba03a3d1902b9256ebe9177d03242fe"), - hex!("2b186dc65b93be71780e5194fd44fc70"), - hex!("94c0532d49523cd9309057a847ef0dbd"), - hex!("f61d6da773315bdd4c79418186ebaa4a"), - hex!("28e89dd2e1e5e09ee3e4cf412af58a0e"), - hex!("54a0171c74e7336634f5b6b61f2b302c"), - hex!("3be685b693d9ddfc342406fcc8d98512"), - hex!("1887acc39d0818a7c6d47e33904a150a"), - hex!("e1434e68f6a9f30252e2f31be8db9658"), - hex!("a5e981ffaa1f770de8a1d21550f49755"), - hex!("f4a55238db60864330a300e1d05dba16"), - hex!("f4b2032ab23f95f9c9516db6d43372ce"), - hex!("5fa11b8f22bd56e5bbb4cb0f843b6730"), - hex!("ad4e75d7abf04b5798d8d0c832bf6833"), - hex!("7df3208dec48fb446e0f89da95843d8a"), - hex!("250e4cae8e10486589190b68608af301"), - hex!("a55b182e12b1433a4935514bb729d2b2"), - hex!("99456d6b4f8886afbbafa6a758830a92"), - hex!("cfd122fe8a0b3c8984e1a603e97bae53"), - hex!("0d05ebdd6eae46efa4b0c7694e6db158"), - hex!("7e59bb58278cbd8f9470fe8636c4edee"), - hex!("c48e2800c2442220eb1d0a9d9d08b437"), - hex!("185f8beff965e31b7859b9b63fc79f97"), - hex!("6e6c22abdb238266d3fa0f2902f85d7c"), - hex!("7345950e2b649e10596ae6be11782110"), - hex!("0cc51bae63bfb29add017e4a0f89f97a"), - hex!("0a5a13ee1aea57228395fc64b8a1852e"), - hex!("ecb847d99f761b457747886f4e0c81d7"), - hex!("9eaf4241ffab9b2d693b96420dbd0356"), - hex!("93f46416f3ef2d5ea57fe1a25c89cfea"), - hex!("ec1ba1810cafc7c0fe76e7bf50809bb2"), - hex!("5ce691721774a58d63e53da2b80f0dbc"), - hex!("f570455f0bfca4359608d92ba076c0cc"), - hex!("1cdc79438ea2129bc739fc9497f53c14"), - hex!("52bfc78f0fc5839e04f1c729c73a1469"), - hex!("d711787384841b856ff7f4d53e5e42df"), - hex!("63dd408ecfdd6e71d45cddfd45aff23b"), - hex!("b0b09781e2c5249c9c248e0062a681ea"), - hex!("0d9d5d09f198c9637b510bbac6f33f34"), - hex!("b572f9b06f63d012d848174bd1191588"), - hex!("d7ab790f4a80e62b38d3a8ae4d170832"), - hex!("9184028922c8de7accdd9d72106aed6b"), - hex!("9d52e83fb1ccb288a8bbd7094ea25221"), - hex!("cb56adf452205662e1f83e51c0c496b5"), - hex!("761eb4593abc7603cf0b5ea95d3661bd"), - hex!("35ca47a1892c4524442a83fdc5231d3d"), - hex!("289f4c7339489b0d07c8716fbf169c74"), - hex!("75ec671be4712c1ce838fff26ef1122d"), - hex!("ab650e5529ec2ce4147efe135a061eb1"), - hex!("e0e637747620e8c1c0ef440b99eb9ce7"), - hex!("94c0e63214f027f2ddd3ea463e44beb8"), - hex!("8548626524a60410aee37ee400d237fc"), - hex!("d80eb32a857a1f84b23801f6e4242459"), - hex!("4853cb0907651c681f1dfbab0646a828"), - hex!("ecd1edccd4844736d8a8e01d4ab21e59"), - hex!("fb58a3ad252f9d576dcd1cfb23d32b89"), - hex!("583b5070f416adbbf796976b2ca27066"), - hex!("259d6fdcd7c3e46dd1a57ae64abda536"), - hex!("d0c6caf2ce368aa85881e8c3bca18192"), - hex!("7d54a3c9d517fba4ffb88cace0276c43"), - hex!("630201121608bdec230db5d012bacfb4"), - hex!("0da36e18ac524cab0cbd44ed0e70bf0e"), - hex!("864cf4a44dfa1f5419a85613e03340b3"), - hex!("d0369950eb82302e887caaca083d31b7"), - hex!("2993e04f04c9b8476e92871886d88d7a"), - hex!("dd49abb10a5bfaff4503b3a31874ac65"), - hex!("96f5bb80bb703cd6b940b0fab926195a"), - hex!("10e2c9baae90477c9be2f10365c29130"), - hex!("696469c514035c0cdf657865a76c8b05"), - hex!("e988c9b6348ae392d81e9d224c608247"), - hex!("81a816b9971534a48e6ec21994b78c81"), - hex!("5498cb9019ba94f896e2c04140cd036a"), - hex!("17fa73eaa092e4bce97e3ba4b770a0b8"), - hex!("e8c7b08816fc5215dfbe44cd46b47dec"), - hex!("c30789092db881251b0c5f7373e0c6f0"), - hex!("f397a1ac039c5e8bc374d1fd03568042"), - hex!("33ec1f25215eae69085a3fbf7a6b27fa"), - hex!("f6fdd17ce7427518d0631e269924f45b"), - hex!("036c902bf005559ba3082e5f2201e614"), - hex!("1fc45e655afc624fb90a7e0795b20b86"), - hex!("bc94ffd5e4f606a12f0c0425d7bf1013"), - hex!("21abfc7ec366c0b93e047d0d9d9df4bf"), - hex!("b8a9f1c0b2d0601e00bb6fa35f3970e2"), - hex!("d67fcb43ff2287a0cf8cf1f0a78ebc85"), - hex!("ade2d8bdd4c48bd437b41d2a36424ef1"), - hex!("d5550bdc493b35a3480c7a5f5d93e939"), - hex!("b069c39e1059a068f9aa767b5a2c39d1"), - hex!("e151a181c34b360acc4ae8f41f0eb923"), - hex!("fa407454a0690b03f714c08ec72b3247"), - hex!("10ffffcebaf525fbadcbe4aa46104680"), - hex!("25569aef3173e2e81bd94a5e7904fc1b"), - hex!("28681502310381ebc0ae31947c3cb188"), - hex!("5db958abc1654596872a50938a0c9b24"), - hex!("7c744e082a52a74767b70a72ec4489a9"), - hex!("5b18ccdaa7efd9b3aff6bad60d547c81"), - hex!("86322eab36c65090a3b7fdb5d7bc091c"), - hex!("8423baac6908031fd9d08157f686b2dc"), - hex!("08a1ade53581b4c029e1c002e51ceaf3"), - hex!("f1ed7d196dff54c3421321acf939e08e"), - hex!("2752d9c907207388e62373ed510c4e88"), - hex!("c3c06fa841383ac60ccb91e4e05580d5"), - hex!("a4c95f5a9ed58116110e43e663425608"), - hex!("2c5bd140dff9063bba7ec0a206a3a4a0"), - hex!("a5848a52ea19a2e85afeb598ce50eb47"), - hex!("ff6279dc1306e5169f95f0b060e34b39"), - hex!("da33c34ef46e9dd360b8dbe6531901b4"), - hex!("83b7e0dbe63ffc49ffc59bae4b7b683e"), - hex!("5c051f94fa62a73c11cfee276461fdb0"), - hex!("798e3ba76c500e8177f392003ed1872b"), - hex!("583d7265ee7126131854bbcb0de1f310"), - hex!("90e4980b35640a8b3bb682ef2606e476"), - hex!("6d431024b5bffd1270c0d041a05b815f"), - hex!("496322b442254a79d1dd0dfdd6f51def"), - hex!("92182683f38300b23bc0412e4138ac05"), - hex!("212df134572585d10dd251f536025085"), - hex!("63e2dbdb3937238a5d08cdf2b578b4e1"), - hex!("96b819206e1d15573307e27b6ad290db"), - hex!("0c54a577923b77c5a4ee726412c43be2"), - hex!("155b53faed668b73ad702c93296a3e01"), - hex!("896d7317a2f611e7363d93db93bcb72a"), - hex!("a39c09d3a4ba25f3ce6691b85b390f3d"), - hex!("7148171957df73a82553216488e35859"), - hex!("ca049d60e60b7b69047e42f0b436ff67"), - hex!("6f402a4a8208e9e49d4bf06f6ce7e11e"), - hex!("95773e0c271ded0e10d2b47221c91e0e"), - hex!("80fd5388433e89d3e74da2637216e3d8"), - hex!("e35fe60581edd06fe880059a63952380"), - hex!("24a5b87aba928ac920362a8bb3a853c1"), - hex!("5a82f1cd0c0c58f0fbebb02c062dd029"), - hex!("d8a989f4d05f65c07cd4f78d4c83d6de"), - hex!("7e100ed69fa83cb97318cf268e063802"), - hex!("5f7d7cb3363d1c4b41736787c8fa3a36"), - hex!("03292bdeef76208a33368b1dd89c5f4f"), - hex!("6b619e4bfd91e47efc4c6a18d6d2ddd4"), - hex!("49e98cfac5039df5711f7bc82ca704fc"), - hex!("bd17f87c484f37449d0cb26bee85352d"), - hex!("b29204f91eeec3a61cf80f78d341e981"), - hex!("0e2806dac2236f555aa1b60d44e6bb94"), - hex!("84762739d031e5c2809951560a9aeaa2"), - hex!("df1404d9feadf66ce9b6106bd730323f"), - hex!("bf36c772e3f353b177dd77ff0af7f658"), - hex!("c01a75724444ea62092d205d4f1faff8"), - hex!("0eb6c4edf01055c26f19606f80660a82"), - hex!("c5475e77e5b769f6e97f0aee53bb2927"), - hex!("3a2a5f7f0ca0c8270800aa61bf75a256"), - hex!("e2fbc1e07d14ac6e3a96cc9055750013"), - hex!("226e5bbb1137417f87d4d0a638739739"), - hex!("745c89d0db4461d9cf03e483f9ed2d66"), - hex!("70ab39feaf98c852e8fac994ca8cc297"), - hex!("cd9d7ebd5e7484375ec35bda9ebfad9b"), - hex!("080de890fd9263b983b58e52f6dee214"), - hex!("f67c8e857d379a60f7bf47b13ec08dc8"), - hex!("b0b8ce46fdfa7f8b0091182cd9e52c19"), - hex!("3fe2d70b44670254ddeaed4e46ba2d6a"), - hex!("1e0f257e0107db4a3be7208c3490f3e8"), - hex!("d0eb4a9ff0dc08a9149b275e3a64e93d"), - hex!("eeab095cfa3a4dc8de4daf9c3e5affbe"), - hex!("bee906bac51d709fa6c8d852834506fb"), - hex!("85cd74d6633623e3e09d3b2ea0e8eebd"), - hex!("f296dfe85523c5ab10cda4edaa513a52"), - hex!("7d8ced87ed7fd15b2e4bbc0264e76f99"), - hex!("ae69988dd1df0ff853e6ee66a5fe3210"), - hex!("4469c4d95255369c6461be2862b915b4"), - hex!("5709b43c1560bff7d265cfd850627680"), - hex!("deb4f8617f931348359a3811076a30eb"), - hex!("f881b9bdedd6f655e33220d24e1cc2eb"), - hex!("ad903ea64fc18d570cd9a50e86bf033c"), - hex!("4b3ac2630be5f8aab921697d1d1404bd"), - hex!("07d5dd8bb48e7a72880b329cff744c4a"), - hex!("84567d5b5e74e94c2373574d42ade1be"), - hex!("63cf6b1ebbb29334730d8b9321cd264d"), - hex!("83094b1464a6bbf92363619af081e20e"), - hex!("7a93ae31b228b723301bf96ab9b0a09f"), - hex!("16873ac9aead7c99286cce23dd91b4ee"), - hex!("bf293be8af1eb38d7080957c7e1f8aeb"), - hex!("967668d49545810fcf18632a5a3431e9"), - hex!("475d5bbd6272a2695f66d2056da42bd9"), - hex!("afc7e6ef08b5b8dc7a2bb1027160cd9c"), - hex!("aa694f10ce796540ed77418cd9b35c86"), - hex!("8be1f7a470d0c1edbbec6728fb0ff366"), - hex!("7444078510fe6d9b3cf94188059a1366"), - hex!("3739215eb46221b4040eea02c7757573"), - hex!("a71b11286fff39e65eb3c8b3ac9a7219"), - hex!("4b48bc59af9ddec38279e60178263779"), - hex!("6076a0b6743690958cf040bfaefac391"), - hex!("bead81dbb9227ba51a02f827f8dee2c5"), - hex!("89508f9f01576f81853e8b92ba917838"), - hex!("d075a5b5dcf20971f2e70e816bbcbb7e"), - hex!("009554c550589a814909c9805279c743"), - hex!("b470cf622846d536ad7b288b9074d667"), - hex!("b87704373978613853240a3ec9368e8b"), - hex!("7127b8d0e757abd6830b787afd829201"), - hex!("f0cab8ea67e0a38ad606ab83ba6bc67e"), - hex!("a408633718e44f4817c329af0395aabb"), - hex!("4607a3ecef00a24da74521f22a6f8bee"), - hex!("917cb60d42ccc40442e48be457f51dea"), - hex!("90222d408a76f7f55fbb18282bef90da"), - hex!("481d56afbd0ba6978e0ab2ada7b3506c"), - hex!("604d874175bd36f8a02ce56b31ca827c"), - hex!("6dc7717dfba128a330ea277dca94141d"), - hex!("86226285351eba0c6e818826b1c562fb"), - hex!("ae7280a5b84931846adff138820f221c"), - hex!("be628492637e26e6489375f3a2938180"), - hex!("7559678bfebb6f78e5c8026b17eadca3"), - hex!("f38e7a19c004dd22688cf0079680bb1c"), - hex!("c3b0e6a2b106f925aa2f92aac6213f8c"), - hex!("eec733087a807a87a0c346de11513e12"), - hex!("4c6d1ee77b414dc3bc448ecc0769a376"), - hex!("303db177352ecf1920f09ba9fc8c6514"), - hex!("8e38c47ebaf4ce8dc05178f3c5a9e86b"), - hex!("104570237e9cbf0f4836ec8c4ff42f65"), - hex!("4776ebe704f27086bcb98059906e8e3a"), - hex!("c5aa722b23a6deef1d15a95f32dc4797"), - hex!("c6188b4ee8720e1efa99aebeb02c7a67"), - hex!("32701ac4e10f922048e0a7368e1f0452"), - hex!("e5988223410c1d4f4260994faaf952b3"), - hex!("2a92d9428c88e74bf47e545ea2025857"), - hex!("04ca250a42e1f227955846abb768a035"), - hex!("05b4a77d503468b71c0e730753fc1a56"), - hex!("d7caf66b03181401cda1369c123d19f6"), - hex!("6d3e29cb829b58d3fe90129c20dc9abb"), - hex!("41b4f0817f11f8016023d74dea3eec97"), - hex!("aeaa60d08ac92150b54908f7f8a92857"), - hex!("c9453b8e185fb93ea0e1282e8803eff0"), - hex!("e87f027df74563c88e700dfe057432ee"), - hex!("af377ff39afc683033823eeb3ed0f10b"), - hex!("f56a0b076a6bfc3eea7b1804b946d947"), - hex!("69ba2470b6623fa3b9d68124e329513e"), - hex!("575aee5f222f5ae9cca0973be3ad572f"), - hex!("da97a6cd52c728a6f3bca987ebfa8cad"), - hex!("4b5536ec8aad2250a2e38f6bfcdf58f4"), - hex!("8fd3b4c5ad2c5743a6aae9f8219a60c6"), - hex!("145b1a9812d684da23e74fead96c8552"), - hex!("7617defe6ad9c021bc9bd7c809675624"), - hex!("d9a2e97eaf84cce6294581acce315ed7"), - hex!("3199b22620f39d534cd96fa8a032998b"), - hex!("b1ca9b7eb944ea1f16364a1222b9afcd"), - hex!("ecd0e506f3792f650fe5a00694afc356"), - hex!("3b96f1eb7ad3124a51372cbe56f5c5e4"), - hex!("962a5ed01d20d1202172cae5c4b1c7ed"), - hex!("b5e9dc0e5c554931dba835dc88102421"), - hex!("4596b31e8bf6c1f24b122de58efc7e1b"), - hex!("224536fd41573a41daf7e131be8bdb09"), - hex!("ef9661b2ac61737aa4bbba6fcad9f860"), - hex!("26c9661a65164390de94c2d38c1f568a"), - hex!("cc0b4699871953942cea3d167e8c9956"), - hex!("575617f32549dc68ceb014b2f69d3b80"), - hex!("932544c41c0e2d7af28189e513fb7ec5"), - hex!("4b8e46de3ce76638280b9a699dfdb620"), - hex!("53406aff68e56538b48fb98364e1a5a5"), - hex!("928ae8d7116355d36b946a8182fc9923"), - hex!("e30282bce7cdf44def0f840b6321e335"), - hex!("beed3d40f310c0c6d0e18443f3304a60"), - hex!("e2725bfdbac45fa18dabf0eb892f03d9"), - hex!("07b43c42513772bc09aac4e471d67b16"), - hex!("8609ba6e215f939caae8770e47d25f8a"), - hex!("4287aec47a1da79aa2351f31cbd4ed0c"), - hex!("b033cc4424fc38cbf7992491211c84c5"), - hex!("cce1d898301da9cddb02d7f36181f8c2"), - hex!("79e12de9d9e677ac2322705cc8a922b1"), - hex!("c448a85e856037d8e88f672979a551eb"), - hex!("467403ae25f597deb3c1094a2d33d413"), - hex!("d7e03948dfccb6abb773409bd4a3c930"), - hex!("674a8c75924d08965e7039c2e41f7940"), - hex!("9220bbcb1742381fd5936662dee7210f"), - hex!("505e4a4e5a49243957ee68bcf2ddb9e4"), - hex!("85952e0b3c1032f7cad908bbd3a2b8a3"), - hex!("f6e25da02626214f2dca471706a057d0"), - hex!("dc7efbb16d990fb6db9e68efbc7fe740"), - hex!("a3231a207b1daf19693a1a5ad18c6ac4"), - hex!("90c5a0bbbc65a3fe44f2be3f860c5f0e"), - hex!("3d8f53b6024c3b33b9097cc678de9a28"), - hex!("1ad8cb3b8d1d4e04bb25330acd10b3e7"), - hex!("c4830b15a969f30d1592527eda63bf82"), - hex!("9d51b6f0c5be845ef775b6b900f0c993"), - hex!("abdb6ff729edfa1fdf81725236fe166c"), - hex!("f92a2b3fb5ebe93ee6fdac51e55f58d0"), - hex!("bad463d68b2067ee099b35bc976d4262"), - hex!("8a326abf1bf139fd19a9931aad716e2b"), - hex!("21a32ae99babd87319e21b115291fa93"), - hex!("aed51baf66ff4910f3b84c6dddd277d0"), - hex!("65c3bbb3015925ae57d939a67bb3e1a2"), - hex!("97bc9538e14c7d221d3fba271fe1a9a3"), - hex!("6394e2557149a2acf674610e834f02a7"), - hex!("280dcfe6935188046eefb81a77e043db"), - hex!("313d0d27a7b82f6e85b32037b3458025"), - hex!("af7416b95834809dc8619c24d9f70575"), - hex!("9e14b1882ac75f1b7ac8735e89bd1dcf"), - hex!("f770f4047a86f36727fcde69c0cb8b68"), - hex!("004610125634efd77979c429a95f16e9"), - hex!("9fb78c563cc2617353fb943c5c6029d9"), - hex!("addc6c96bafb15254e0e2c2a21f6eca0"), - hex!("b2e1d71c4419cf35d2ccb202727e9006"), - hex!("22c2cf6192e5f767d518ba32d2628f27"), - hex!("d4a9a8dedeaa916c20451f72d868e54c"), - hex!("e15c7e3a6935f188aab577be046518f8"), - hex!("d00f06b2b19fb192d885586001624318"), - hex!("3c1133d7e7085944fa800c1365d4b4f3"), - hex!("3963a16de74721a202e7f10d66278fe4"), - hex!("2f886a0a39058911d72b46e15bc34672"), - hex!("bf8c454a96a689eb71c30d9639aaecee"), - hex!("761b3e46118bc24bc62987107f3d12c6"), - hex!("891583dc69ff4a5e64070d942aaa435f"), - hex!("d8b34532a52763f1afd495aa3e36b2ef"), - hex!("2f9e4d03913cd937e09c451b3ed20dcb"), - hex!("93d22323cd8c06ec945733ee811d8ac8"), - hex!("2a9d9c385dc260a178c9dd5902499f7e"), - hex!("45e79066792ee537ae6106b3c897d44c"), - hex!("4e00df4f849deba8f05284dba1a8daf6"), - hex!("9ed2f8a53f69dee1e9b2d4a332ac80d5"), - hex!("b0cb763b4c0e4bddbdeab130195681bb"), - hex!("c25c64f479521ed7a68cb75637498e67"), - hex!("a66e88f5a0279ebbfc9063d5d7fc9681"), - hex!("97f23e83e5a2c1e6209a1e0baa4c9048"), - hex!("08efb5ef7d86b52c486f88ea92865e2e"), - hex!("750b98718c4d7f9b63a0fe4135a00143"), - hex!("bd71d4d32938661a8e4e8e198f6e3c71"), - hex!("dac6dce2e49f253706ee5ea4549abb67"), - hex!("1dfa7fc8cff2108f4de96a6f6404321b"), - hex!("58fa94796612dacc2f2a60fbac5f85d6"), - hex!("af4a599a7afc59244662fb56a32f38cb"), - hex!("7b2920aac8c076c5fccfdf3325fc8455"), - hex!("b3328f0b1057958da28bab59330133a7"), - hex!("ad4e0add9ad103421f47d88eeb5c711f"), - hex!("4825b9d42589e834f61e6ef705641713"), - hex!("3da44d4f1d8bb790537ec42ba2af168c"), - hex!("87db7dab6b1aa2857fcf861273b9a58d"), - hex!("c32c902e1389ebda24a09ae882575370"), - hex!("cf17c3f198e852d5123942c402918656"), - hex!("9f1cf97072ee00922c301340a19c91b7"), - hex!("b3e163f4cbeac4437a962c84a85a1e5b"), - hex!("a70314ea9655ebf03ee78a4a320d1ecc"), - hex!("2ab485395195fd37e0fd5b2336f0a00a"), - hex!("9f77060b503e1fbccf8b682215821b07"), - hex!("a4fd17b615f2794b3fbb98ac81e0c5e7"), - hex!("3e7faa44b3e919bf089ce8962a41596b"), - hex!("f1cb06f527cfdb2bfb3e3341c878101d"), - hex!("fe8cedf87702d7b090a0f07571607d86"), - hex!("f569a8f30771d73544ad99fb1610b174"), - hex!("1e332a7f9b33fc91369ba33503353023"), - hex!("e04c52de8e81749474a0a3ef746c4c9d"), - hex!("e961634b1721573ccbaf4c195ece7bd4"), - hex!("c50b42bd793d49f0505df93353c4acde"), - hex!("f8a9ea7fd860ad32e03ed50aebeb92f2"), - hex!("f6a622025cb1659a5bce3c4cc7ed0680"), - hex!("b6a78250c0253c2a8a985beb3ed16309"), - hex!("d2ba47f421049058107969e08458e7bc"), - hex!("66809b4880f156c8f539441829d11b90"), - hex!("980b88f3b17ad1bf46ddc89356df550c"), - hex!("083177d975088d3b3acb85c5e767948f"), - hex!("07a3e31da3988ccc22a48cb61890ed83"), - hex!("12c4f7a7402ada8fac7c2ddc784ca2cb"), - hex!("a7bd8cdd867b4b3812f3066b3db3c006"), - hex!("aa098d01c41cc948c138f864a8a62481"), - hex!("18457233e28062083f7d23b2e481189d"), - hex!("1702cda0b76772ba09cea0edc5e5746e"), - hex!("db200270afe9e05cba79d94ff6d2da8c"), - hex!("b93ce415bb6beb51157141149e34bd0e"), - hex!("6266741ef0b85a2fd5ac4a1fb816835b"), - hex!("8dba28245cf055574881b05fef9953a6"), - hex!("e4af90f7979c2c631633131d642dd8bd"), - hex!("97f98f4275be120a445cd0275e2cd73a"), - hex!("150a9c0526b11752453a23d8b18a8f3b"), - hex!("010bbf6895ade2375c8478a0c3151ce5"), - hex!("355796530fdacf6d87bcc370f17fc71e"), - hex!("9a404317c26f415ed025f32dfabe8598"), - hex!("15d2eb783afced72c733f6ce90bf7349"), - hex!("fb9f445a7acf24b91e6cbe8f9489a7c2"), - hex!("6f03e5d4ef52a7c05a5a5fd28b159b5b"), - hex!("2466fb6d4eb8aa1c700e728fded218df"), - hex!("676cfafe2fbcffd070ddb236d2bb0021"), - hex!("91e33a111622283750412eea13c83f35"), - hex!("88b1f25057c3bac8ee1eeca2ff2209a3"), - hex!("c10d6e9c953ebdc8ece36c5cd6223387"), - hex!("1fb01164b818aa63387a0ec14be5e3e7"), - hex!("aca8367a8bfd04541cc836e293255b77"), - hex!("8b74b13c0d49da16c37a8de608c18e7e"), - hex!("79e4197b401889e0756cedda74f46812"), - hex!("fdfc1643dbd6ad08bd6a4eba37a0e3c3"), - hex!("3c4b6a74dd034b4e72bc84652a09a3ff"), - hex!("2f31fab52ef05919d280c2abcf422fab"), - hex!("4a2f98048e8605e4d439ff8554ab6e63"), - hex!("3b7e760d63c75a4c368dd53425084427"), - hex!("dbd55facc2eed4edae760a2ba92b4f39"), - hex!("43b123b7bca43b561fc26e423bcef939"), - hex!("b6305a7b627bc5973e579f6984661e92"), - hex!("ec1c177be3ea3294f799a7431bddc5ed"), - hex!("db89f09027441e9465a797737c5a647a"), - hex!("56dd88789701e0e1c682d8ad251dbeb3"), - hex!("569403a8edac9edce0cf1e8876c53174"), - hex!("7713e55406eb2a9076398f94d0a58692"), - hex!("c1f88b2f71fa2e1a988038cf9d3df04f"), - hex!("d81ef241ea5de4b34aaf39ab1b083642"), - hex!("d375d23cdd8026becba44bba2b294c3f"), - hex!("1b7eee6c46118d885bcaaaabb2f9badb"), - hex!("89558455a420ed8268a592b0eaf204c6"), - hex!("b17d6916a9d0db09432db599e90327d7"), - hex!("e1b36f36682b4fae32da6979590cc499"), - hex!("612373badc313851be94e09ccff61c5a"), - hex!("faf60883e0085e9672e6521bcdb86f5a"), - hex!("7784f2c8187dddb17dc42deeb335b625"), - hex!("e53fa1a5071b726701e5eae987891d4f"), - hex!("5c8291e6ac0b0abeac024d54547aec5b"), - hex!("6462d16fbb1f465357418796773abc49"), - hex!("cab6e515313f84de7345574d91480771"), - hex!("5f64f987085767b41be43f261a8ba025"), - hex!("d6c688f958dbe3b9a333041c25e067f9"), - hex!("80f05a86da8eb6a002c54c1305d5dc21"), - hex!("60bfbb201ce2fb723edf4a6e2433b6d7"), - hex!("ebc37a445da40d345532a1b9bad5445c"), - hex!("d47c63b0619011b6bf65c269a047def5"), - hex!("1aba33a3d2b769a78bd6ff673be3b632"), - hex!("04b7d271316b4c31a58f21d31ddb5fd0"), - hex!("3226afaecb913b1380a2c4220efcf329"), - hex!("dc0605dbfafa3292db3e030284dae0c9"), - hex!("42cb016d792a792545f5d3628d280b86"), - hex!("209c20b1a337f22459ddfe9cb9cc5616"), - hex!("f136ac0b645a83514ba9e2b5dd6b33f6"), - hex!("93d1365bf44fee8d78521c97ba8fe6a5"), - hex!("9500fa38f25ee9bd5140db4f3f9ca585"), - hex!("3dc470c2bf7dbd809c66c922c31b673d"), - hex!("d03478b8608f6d68335d25256c9ddecb"), - hex!("e696ad7c49a89b837a68bf8aad0670e6"), - hex!("8c4a01a4de7453ea9d5e01851b68c624"), - hex!("0064ee1b40e9947a06f5692a367cfcf7"), - hex!("44a780519b6751d1ce84c01dfd26919a"), - hex!("810e0008a42674b8503ccee1e487359b"), - hex!("14f37bbb397085efd87ca8d08f73745c"), - hex!("a4531df9fb35ffeb48f433aec36d2644"), - hex!("ebbcecd01539dbbf24f6f918afb8c8f5"), - hex!("e594f29d5159f01eb37b4de961edf17f"), - hex!("435fb4690bd8681ce2e7aa577fe88428"), - hex!("c685712dda4ca735a9336290c19e6757"), - hex!("76fa7c02c91a2acd4feac2c8b7e79241"), - hex!("6ded6bbb2b6ac1a8bf368ec35f8c0004"), - hex!("853104204b4e7dd21f1891a57dc99bda"), - hex!("016c939b9fad1bc914827b81b63f8f8a"), - hex!("0aad21f534784ce6e55bc860be909ff8"), - hex!("d892c0ed69e87bbcb0b75e954629415e"), - hex!("6e00495d56c440a0bfe596a261856dc3"), - hex!("12c24ed58e08eb9d84123af23954338c"), - hex!("b253ed770e7a12a526eb55524e9b2d78"), - hex!("867143a311b72df9301212469856d6e8"), - hex!("30645981186d86d7f453b627474bb186"), - hex!("628df7191dbce185c3894f83b4b100bc"), - hex!("abf05f494a51a66d0be185be6f9b55ab"), - hex!("23b6a4e817837ea9bc1e8cf4d12432b5"), - hex!("eedbaa71b581216e1bb14b5b6e370cfe"), - hex!("6571c261d0d1d9ac6ec81ef3c5579738"), - hex!("90ca319d7900ab33585929f87f87f4f7"), - hex!("3aaad6c72aba1f9bbeb03ddca6716189"), - hex!("e2c1d3bd988d38bc6baaa7572d4b33a3"), - hex!("2b06afc482dc2a2814244c0ddb85172d"), - hex!("311d1fdee66191a759777222b885042a"), - hex!("d32037774ed016be27a05110188f33f5"), - hex!("8bbe9d2540a8548c6e837c1ca1be1736"), - hex!("97d081a75c821656392370b91f0551e7"), - hex!("6cdb7e28fad3da3419f3bdbfda3670ef"), - hex!("0eef01ab63f2fea60d800a3e50ce7f4d"), - hex!("26de913e72381b29ae92553c5dd56026"), - hex!("c010b7c20f4e8eb014c9e14ae5a2cfe7"), - hex!("36349c7a4f4419889ac51938e01dd562"), - hex!("62a66c09d10bb962a9482a274fdd1ad6"), - hex!("0ca8993c5f9e9441ee476186e825fd02"), - hex!("32720f4423f0c648aa43f00eec2faa7d"), - hex!("a5a0c7c7cd1643d0ed59990e9aaab8e1"), - hex!("75a4421dc20eb16958d5cc74d0018376"), - hex!("d18f3989a5ad09f60559eb953918ce96"), - hex!("bc1b06b4746555e1da474d99578e1bd8"), - hex!("c156a24e9e9461df6a7b38135e15251d"), - hex!("a86706531ea8351a7ab7e346af0f29c3"), - hex!("1e09fb83c81c2e837ec8182d573fb9d3"), - hex!("6056b80049473d253538113d651825fe"), - hex!("48aecd0743c8149eb5f9aec60f347f7d"), - hex!("a028808b3199d97d3fc40436bde64c56"), - hex!("9c84842024616e12f5733c3c64877d8f"), - hex!("61a39835bde221a5e1ceef6f92b8ab78"), - hex!("a9705493cafe59c707acd9d03b91f648"), - hex!("0bc0761a4c3ec1f871638dbdd7786fe1"), - hex!("a798e4c2b322a4e64cbe4a1f86ab3721"), - hex!("5c36cd452450b556692b4958b5c64d2c"), - hex!("fcbcc2f7cb02c16981037e57c8a6aac6"), - hex!("68b580e09ca06432272557e38d2cb250"), - hex!("8166a9706fdf02dfb2548852b0080cde"), - hex!("f5bf78f8ea50a5cc718e8cfd22f96013"), - hex!("f78d251520533f415055ec2f705d050f"), - hex!("c4ae15a70fa1e873e3ea7c747b7eb2df"), - hex!("219aea4518c67efe0389e5b3eb083d3a"), - hex!("92eedded99e1bb261e106a9d23bb6b97"), - hex!("e6c5f02b94e81da315deffa12bbf5f8f"), - hex!("beea81cbe3ebf59d893c325dababaa8c"), - hex!("d838adeb0ac44aeb59d09eede9074d14"), - hex!("fabaa47e5558692b9b17621a26549da7"), - hex!("ef9da8f942af3ee8bf2803c65e25e51a"), - hex!("200211848e159a56eeb6cec602ed583c"), - hex!("83b04a1330ad921447fc3ded47ff75f0"), - hex!("cdba71a13a331d9fb32e16e103bd5dc6"), - hex!("6d261a84b2e3f0f45f945982000a07db"), - hex!("f607a8685065a531fa2a7198ca2181b7"), - hex!("7f636cf604a13da02fc30d632feb1088"), - hex!("717a02b8df54a19dc814b7fc875ecfdb"), - hex!("828188a8255344756d3763267c145ee6"), - hex!("7cf82d8169205840fb432bf99543f39a"), - hex!("47409b2432ff64f26c16f3bafccacfc8"), - hex!("b0266545e832b14a2a70aa9f64bfee3c"), - hex!("a27425aabc0a1193e41c8a25da150268"), - hex!("f631b7ec97e689f418d953ab50c6eea2"), - hex!("cdd6e178da5a864c0f4efcaf2f09a7a1"), - hex!("8dfb54b631c611bdd3d9b8ea1a139351"), - hex!("75c92c082a8a3b145cae31cf00f84fa3"), - hex!("7a9010568819de327a24fa495029adcb"), -]; diff --git a/parachain/primitives/ethereum/src/ethashproof.rs b/parachain/primitives/ethereum/src/ethashproof.rs deleted file mode 100644 index f6c7ea611..000000000 --- a/parachain/primitives/ethereum/src/ethashproof.rs +++ /dev/null @@ -1,566 +0,0 @@ -use codec::{Decode, Encode}; -use ethereum_types::{H128, H256, H512, H64}; -use sp_io::hashing::{keccak_256, keccak_512, sha2_256}; -use sp_runtime::{scale_info::TypeInfo, RuntimeDebug}; -use sp_std::{cell::RefCell, collections::btree_map::BTreeMap, prelude::*, vec}; - -pub use crate::ethashdata::{DAGS_MERKLE_ROOTS, DAGS_START_EPOCH}; - -/// Ethash Params. See https://eth.wiki/en/concepts/ethash/ethash -/// Blocks per epoch -const EPOCH_LENGTH: u64 = 30000; -/// Width of mix -const MIX_BYTES: usize = 128; -/// Hash length in bytes -const HASH_BYTES: usize = 64; -/// Number of accesses in hashimoto loop -const ACCESSES: usize = 64; - -#[derive(Default, Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] -pub struct DoubleNodeWithMerkleProof { - pub dag_nodes: [H512; 2], - pub proof: Vec, -} - -impl DoubleNodeWithMerkleProof { - pub fn from_values(dag_nodes: [H512; 2], proof: Vec) -> Self { - Self { dag_nodes, proof } - } - - fn truncate_to_h128(arr: H256) -> H128 { - let mut data = [0u8; 16]; - data.copy_from_slice(&(arr.0)[16..]); - H128(data.into()) - } - - fn hash_h128(l: H128, r: H128) -> H128 { - let mut data = [0u8; 64]; - data[16..32].copy_from_slice(&(l.0)); - data[48..64].copy_from_slice(&(r.0)); - Self::truncate_to_h128(sha2_256(&data).into()) - } - - pub fn apply_merkle_proof(&self, index: u64) -> Result { - let mut data = [0u8; 128]; - data[..64].copy_from_slice(&(self.dag_nodes[0].0)); - data[64..].copy_from_slice(&(self.dag_nodes[1].0)); - - let mut leaf = Self::truncate_to_h128(sha2_256(&data).into()); - - for i in 0..self.proof.len() { - let index_shifted = index.checked_shr(i as u32).ok_or("Failed to shift index")?; - if index_shifted % 2 == 0 { - leaf = Self::hash_h128(leaf, self.proof[i]); - } else { - leaf = Self::hash_h128(self.proof[i], leaf); - } - } - Ok(leaf) - } -} - -/// A wrapper around ethash::make_cache with LRU caching. Use this to retrieve -/// DAG cache data for a given epoch. -pub struct EthashCache { - /// Maximum number of DAG caches we'll store at a time - max_capacity: usize, - /// Most recently accessed DAG caches, stored as epoch => cache data - caches_by_epoch: BTreeMap>, - /// (timestamp, epoch) of the most recently accessed caches, ordered from least to most recent - recently_accessed_epochs: Vec<(u64, u64)>, - /// Cache data generator - cache_gen_fn: fn(usize) -> Vec, -} - -impl EthashCache { - pub fn new(max: usize) -> EthashCache { - assert!(max > 0); - EthashCache { - max_capacity: max, - caches_by_epoch: BTreeMap::new(), - recently_accessed_epochs: Vec::with_capacity(max), - cache_gen_fn: Self::get_cache_for_epoch, - } - } - - /// For tests to override the cache data generator - pub fn with_generator(max: usize, cache_gen_fn: fn(usize) -> Vec) -> EthashCache { - let mut cache = EthashCache::new(max); - cache.cache_gen_fn = cache_gen_fn; - cache - } - - pub fn get(&mut self, epoch: u64, timestamp: u64) -> &Vec { - if self.caches_by_epoch.contains_key(&epoch) { - let (ref mut t, _e) = self - .recently_accessed_epochs - .iter_mut() - .find(|&&mut pair| pair.1 == epoch) - .unwrap(); - *t = timestamp; - } else { - if self.recently_accessed_epochs.len() == self.max_capacity { - let (ref mut t, ref mut e) = self.recently_accessed_epochs.first_mut().unwrap(); - self.caches_by_epoch.remove(e); - *t = timestamp; - *e = epoch; - } else { - self.recently_accessed_epochs.push((timestamp, epoch)); - } - let cache_gen_fn = self.cache_gen_fn; - self.caches_by_epoch.insert(epoch, cache_gen_fn(epoch as usize)); - } - - self.recently_accessed_epochs.sort(); - self.caches_by_epoch.get(&epoch).unwrap() - } - - fn get_cache_for_epoch(epoch: usize) -> Vec { - let seed = ethash::get_seedhash(epoch); - let cache_size = ethash::get_cache_size(epoch); - let mut data = vec![0; cache_size]; - ethash::make_cache(data.as_mut_slice(), seed); - data - } -} - -#[derive(Clone, Debug, PartialEq)] -pub enum Error { - // Epoch doesn't map to the range in DAGS_MERKLE_ROOTS - EpochOutOfRange, - // The merkle proof could not be verified - InvalidMerkleProof, - // The number of nodes with proof don't match the expected number of DAG nodes - UnexpectedNumberOfNodes, -} - -pub struct EthashProver { - /// A LRU cache of DAG caches - dags_cache: Option, -} - -impl EthashProver { - pub fn new() -> Self { - Self { dags_cache: None } - } - - pub fn with_hashimoto_light(max_cache_entries: usize) -> Self { - Self { dags_cache: Some(EthashCache::new(max_cache_entries)) } - } - - fn dag_merkle_root(&self, epoch: u64) -> Option { - DAGS_MERKLE_ROOTS - .get((epoch - DAGS_START_EPOCH) as usize) - .map(|x| H128::from(x)) - } - - // Adapted fro https://github.com/near/rainbow-bridge/blob/3fcdfbc6c0011f0e1507956a81c820616fb963b4/contracts/near/eth-client/src/lib.rs#L363 - pub fn hashimoto_merkle( - &self, - header_hash: H256, - nonce: H64, - header_number: u64, - nodes: &[DoubleNodeWithMerkleProof], - ) -> Result<(H256, H256), Error> { - // Check that we have the expected number of nodes with proofs - const MIXHASHES: usize = MIX_BYTES / HASH_BYTES; - if nodes.len() != MIXHASHES * ACCESSES / 2 { - return Err(Error::UnexpectedNumberOfNodes) - } - - let epoch = header_number / EPOCH_LENGTH; - // Reuse single Merkle root across all the proofs - let merkle_root = self.dag_merkle_root(epoch).ok_or(Error::EpochOutOfRange)?; - let full_size = ethash::get_full_size(epoch as usize); - - // Boxed index since ethash::hashimoto gets Fn, but not FnMut - let index = RefCell::new(0); - // Flag for whether the proof is valid - let success = RefCell::new(true); - - let results = ethash::hashimoto_with_hasher( - header_hash, - nonce, - full_size, - |offset| { - let idx = *index.borrow_mut(); - *index.borrow_mut() += 1; - - // Each two nodes are packed into single 128 bytes with Merkle proof - let node = &nodes[idx / 2]; - if idx % 2 == 0 { - // Divide by 2 to adjust offset for 64-byte words instead of 128-byte - if let Ok(computed_root) = node.apply_merkle_proof((offset / 2) as u64) { - if merkle_root != computed_root { - success.replace(false); - } - } else { - success.replace(false); - } - }; - - // Reverse each 32 bytes for ETHASH compatibility - let mut data = node.dag_nodes[idx % 2].0; - data[..32].reverse(); - data[32..].reverse(); - data.into() - }, - keccak_256, - keccak_512, - ); - - match success.into_inner() { - true => Ok(results), - false => Err(Error::InvalidMerkleProof), - } - } - - pub fn hashimoto_light( - &mut self, - header_hash: H256, - nonce: H64, - header_number: u64, - ) -> (H256, H256) { - let epoch = header_number / EPOCH_LENGTH; - let cache = match self.dags_cache { - Some(ref mut c) => c.get(epoch, header_number), - None => panic!("EthashProver wasn't configured with hashimoto light cache"), - }; - let full_size = ethash::get_full_size(epoch as usize); - return ethash::hashimoto_light(header_hash, nonce, full_size, cache.as_slice()) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use hex_literal::hex; - use rand::Rng; - use snowbridge_testutils::BlockWithProofs; - use std::path::PathBuf; - - fn fixture_path(name: &str) -> PathBuf { - [env!("CARGO_MANIFEST_DIR"), "tests", "fixtures", name].iter().collect() - } - - #[test] - fn cache_removes_oldest_at_capacity() { - let mut cache = EthashCache::with_generator(1, |_| Vec::new()); - cache.get(10, 1); - cache.get(20, 2); - assert_eq!(cache.caches_by_epoch.len(), 1); - assert_eq!(cache.recently_accessed_epochs.len(), 1); - assert!(cache.caches_by_epoch.contains_key(&20)); - assert_eq!(cache.recently_accessed_epochs[0].1, 20); - } - - #[test] - fn cache_retrieves_existing_and_updates_timestamp() { - let mut cache = EthashCache::with_generator(2, |_| { - let mut rng = rand::thread_rng(); - vec![rng.gen()] - }); - let data1 = cache.get(10, 1).clone(); - let data2 = cache.get(10, 2); - assert_eq!(data1, *data2); - assert_eq!(cache.caches_by_epoch.len(), 1); - assert_eq!(cache.recently_accessed_epochs.len(), 1); - assert_eq!(cache.recently_accessed_epochs[0].0, 2); - } - - #[cfg(feature = "expensive_tests")] - #[test] - fn hashimoto_light_is_correct_block_11090290() { - // https://etherscan.io/block/11090290 - let header_partial_hash: H256 = - hex!("932c22685fd0fb6a1b5f6b70d2ebf4bfd9f3b4f15eb706450a9b050ec0f151c9").into(); - let header_number: u64 = 11090290; - let header_nonce: H64 = hex!("6935bbe7b63c4f8e").into(); - let header_mix_hash: H256 = - hex!("be3adfb0087be62b28b716e2cdf3c79329df5caa04c9eee035d35b5d52102815").into(); - - let mut prover = EthashProver::with_hashimoto_light(1); - let (mix_hash, _) = - prover.hashimoto_light(header_partial_hash, header_nonce, header_number); - assert_eq!(mix_hash, header_mix_hash); - } - - #[test] - fn hashimoto_merkle_is_correct_block_3() { - // https://etherscan.io/block/3 - let block_with_proofs = BlockWithProofs::from_file(&fixture_path("3.json")); - let header_partial_hash: H256 = - hex!("481f55e00fd23652cb45ffba86a08b8d497f3b18cc2c0f14cbeb178b4c386e10").into(); - let header_number: u64 = 3; - let header_nonce: H64 = hex!("2e9344e0cbde83ce").into(); - let header_mix_hash: H256 = - hex!("65e12eec23fe6555e6bcdb47aa25269ae106e5f16b54e1e92dcee25e1c8ad037").into(); - - let (mix_hash, _) = EthashProver::new() - .hashimoto_merkle( - header_partial_hash, - header_nonce, - header_number, - &(block_with_proofs - .to_double_node_with_merkle_proof_vec(DoubleNodeWithMerkleProof::from_values)), - ) - .unwrap(); - assert_eq!(header_mix_hash, mix_hash); - } - - #[test] - fn hashimoto_merkle_is_correct_block_11090290() { - // https://etherscan.io/block/11090290 - let block_with_proofs = BlockWithProofs::from_file(&fixture_path("11090290.json")); - let header_partial_hash: H256 = - hex!("932c22685fd0fb6a1b5f6b70d2ebf4bfd9f3b4f15eb706450a9b050ec0f151c9").into(); - let header_number: u64 = 11090290; - let header_nonce: H64 = hex!("6935bbe7b63c4f8e").into(); - let header_mix_hash: H256 = - hex!("be3adfb0087be62b28b716e2cdf3c79329df5caa04c9eee035d35b5d52102815").into(); - - let (mix_hash, _) = EthashProver::new() - .hashimoto_merkle( - header_partial_hash, - header_nonce, - header_number, - &(block_with_proofs - .to_double_node_with_merkle_proof_vec(DoubleNodeWithMerkleProof::from_values)), - ) - .unwrap(); - assert_eq!(header_mix_hash, mix_hash); - } - - #[test] - fn hashimoto_merkle_is_correct_block_11550000() { - // https://etherscan.io/block/11550000 - let block_with_proofs = BlockWithProofs::from_file(&fixture_path("11550000.json")); - let header_partial_hash: H256 = - hex!("7bc3c6073de95a429663dcc4c25f9559cfe1947142d111d91d1e09120c68847e").into(); - let header_number: u64 = 11550000; - let header_nonce: H64 = hex!("8ae5c070892cb70c").into(); - let header_mix_hash: H256 = - hex!("0363fe29940988ca043713840ac911b32f2acb4d010e55963f2d201d79f9ab57").into(); - - let (mix_hash, _) = EthashProver::new() - .hashimoto_merkle( - header_partial_hash, - header_nonce, - header_number, - &(block_with_proofs - .to_double_node_with_merkle_proof_vec(DoubleNodeWithMerkleProof::from_values)), - ) - .unwrap(); - assert_eq!(header_mix_hash, mix_hash); - } - - #[test] - fn hashimoto_merkle_returns_err_for_invalid_data() { - let block_with_proofs = BlockWithProofs::from_file(&fixture_path("3.json")); - let header_partial_hash: H256 = - hex!("481f55e00fd23652cb45ffba86a08b8d497f3b18cc2c0f14cbeb178b4c386e10").into(); - let header_number: u64 = 3; - let header_nonce: H64 = hex!("2e9344e0cbde83ce").into(); - let mut proofs = block_with_proofs - .to_double_node_with_merkle_proof_vec(DoubleNodeWithMerkleProof::from_values); - let prover = EthashProver::new(); - - assert_eq!( - prover.hashimoto_merkle(header_partial_hash, header_nonce, 30000000, &proofs), - Err(Error::EpochOutOfRange), - ); - assert_eq!( - prover.hashimoto_merkle( - header_partial_hash, - header_nonce, - header_number, - Default::default() - ), - Err(Error::UnexpectedNumberOfNodes), - ); - proofs[0].proof[0] = H128::zero(); - assert_eq!( - prover.hashimoto_merkle(header_partial_hash, header_nonce, header_number, &proofs), - Err(Error::InvalidMerkleProof), - ); - } - - extern crate wasm_bindgen_test; - - use wasm_bindgen_test::*; - - #[wasm_bindgen_test] - fn hashimoto_breakdown_11550000_wasm() { - let header_hash: H256 = - hex!("7bc3c6073de95a429663dcc4c25f9559cfe1947142d111d91d1e09120c68847e").into(); - let nonce: H64 = hex!("8ae5c070892cb70c").into(); - let epoch = 11550000 / EPOCH_LENGTH; - let full_size = ethash::get_full_size(epoch as usize); - - let index = RefCell::new(0); - - ethash::hashimoto_with_hasher( - header_hash, - nonce, - full_size, - |offset| { - let idx = *index.borrow_mut(); - *index.borrow_mut() += 1; - assert_eq!(offset, DAG_INDICES_11550000[idx]); - DAG_NODES_11550000[idx].into() - }, - keccak_256, - keccak_512, - ); - } - - const DAG_INDICES_11550000: [usize; 128] = [ - 17124670, 17124671, 8406228, 8406229, 62843670, 62843671, 51608408, 51608409, 13778580, - 13778581, 32065328, 32065329, 34759690, 34759691, 3535582, 3535583, 9322842, 9322843, - 35441302, 35441303, 25549918, 25549919, 238042, 238043, 7248900, 7248901, 16632494, - 16632495, 37184834, 37184835, 3934136, 3934137, 31120362, 31120363, 36454734, 36454735, - 14059218, 14059219, 9502912, 9502913, 24810294, 24810295, 47833150, 47833151, 63459724, - 63459725, 21830544, 21830545, 35083782, 35083783, 36750118, 36750119, 60695996, 60695997, - 15304996, 15304997, 29389880, 29389881, 43062130, 43062131, 37586164, 37586165, 4303694, - 4303695, 32719922, 32719923, 30133816, 30133817, 19691770, 19691771, 12694514, 12694515, - 36915336, 36915337, 15774426, 15774427, 61837002, 61837003, 3186138, 3186139, 16297838, - 16297839, 31232738, 31232739, 51663568, 51663569, 21282034, 21282035, 11616704, 11616705, - 18376636, 18376637, 291772, 291773, 54304530, 54304531, 1054106, 1054107, 35986490, - 35986491, 12614944, 12614945, 41286800, 41286801, 20624658, 20624659, 62433918, 62433919, - 39708662, 39708663, 33747208, 33747209, 9110260, 9110261, 11777868, 11777869, 31474018, - 31474019, 38573944, 38573945, 7006, 7007, 34120876, 34120877, 46961334, 46961335, 44816784, - 44816785, - ]; - - const DAG_NODES_11550000: [[u8; 64]; 128] = [ - hex!("4f8a5188a0446df88386b3b5e16b7e5e70481c7fabc2925c86cc9cc892ce759194711edb6c55edbc6f6f4e7de943f8bce38d3ba50b88f7ac36f78ab178d373ea"), - hex!("a565ffd7e51108117e75f10c5171afced5ac1510970118bac01ddceec353b48daaf6ea4f4b32081280224e6b3d0a6f3429c08358f282687b77f5dc9c2acf5d58"), - hex!("16b99085654e95e2a8e740731f1ca1a076ed8de287134285aca1f2f454bc368f969144714a9f231b5a4cf9f6908c39daeebe2cf78d5e0a9cbe0d2f7901db4428"), - hex!("4cbfb405c484cf47227d0e00922f59586282202d14ca87a93bccae6fc43b79425409c55a65f24599e8db8f7be332b139c321239323a214972e2b61fdadbadcc4"), - hex!("4775a285cb153b9eb13b04dcc869721586c6419ad63aa8d6a2e448657fa106b12aaa42b18a19f84f5599a8305c966792669935c8f618bb7e0c8ab936c25d39f1"), - hex!("021398b84c4680ce771c7e1e2a4745c08793b8a137caeadcc7cf40260ca042dd0ab9fe14c5d222dc7f0b212ed0224297de309431eaf1a7e3f95dd8854200444a"), - hex!("413af2332e720185de6f5ebccdbe662580bca42d28011173cc83933a924e13d2d4e20f5490d2c6b18eac73fae54395befa19cf43cd4b98fe69cec930c08d7f32"), - hex!("237617d366fdddffcf53894d41d7e5f17ad5cfa0eaa268329f630e820a89fc19f60d6114f33fe3ab58b23737a0653bff7b5eec2b00ec9c055f2267c91978033e"), - hex!("08ae7a996bf54db330a67030d9b807f3a677be3c08ea8d4a179fbcf8e960d206a46428241cae4edc3f5e05355444a9736df442c326c9b4d568dbd5d37cbda4b1"), - hex!("9bfc888ba04d76484bd9453235eca817d60b4d3b4723f42767122dbbbd49feb0eda7662da6801f56ff377251ba640741d15016673112ac21810836600560e173"), - hex!("a8c6ca6313f58675ad48dbca8a1db26112696191dff2a54159d182cdf28d68b6008bf98af9b6f95b2daf4234456c5abb6080f1fa45f4afd78a26790b9b6a55ff"), - hex!("d1144bb84174bf9bece6fd212b0f3556487b61caf4aaa4ce56102c2cec665981962059cc936494825dea97fd7aa69b82854a2cddd6953f26be96389a9be9cbd9"), - hex!("eeba1cec0d56abcab386f6956bc432c4b4415e1fd1dbebbfa682b7d9c68752f2202f3039359928c523e80d8ee55efe41e3f1f2d7fe440274e621175e8932b46c"), - hex!("2115f5ce4dbb903bfe9aca49af6f8324cd1491b84ed2b4ddb880011608a81348812d7a2e8ace8079de3d1ba800bc7a28f75215562e0267da4d8702ccf7ad5a7d"), - hex!("c7c2a9414fb637805123abaecf9a557ea611a0a030eb628e20d1e1327b647e8e26f563934d5051e489551ec34c20ccf2404d98617e59f7294e7ebe408cf3cd17"), - hex!("df1b75fc00f0968d48053c0f26b7f417af9c0e80f545bf274e2f675d6015c8a6b9f09f23714e5b0867fd48cd3637954570f0a0c1c4c112462e24f811451ff629"), - hex!("dce533e3ccd6fa531e55f96a96bc58280560e7b12cd04c55278686b70592a1f66a4f85f1950f73f233ca0e7daa64117e2de79d77ce0cde6dcea4b594a7708df9"), - hex!("c40e6a9fff86a78c6129c409fd0b031c7c33960f87685a22209a8cf0b7b7f457dbc9c1f69fcc0e66c23b6f830cb753c130fd511f460a091df7fc749f32588a25"), - hex!("19eaffcd4755cd798080e1626574b2212fc16da8e0c9bc1f945e5dd9b22f02fdd4d2c21a94e37acfcebe5b8d472ea34ea02584ebf6b66b1bdb0f38eb8b039f9e"), - hex!("4ad327b5a44b3b498461b9bf83097727d389b5903a1ec81404dd83f07887e887a2886d75eb6394ddbf35fcc5895b1607fb878b50e605c6636758b3fd5f524aa7"), - hex!("c3ed093f4c7510a1c5ba54eb4e5c3e66706b74224de23db1e929e59e2dc6c119c2f1fb1458856857ceadc2c783461a34e68dd1b373487962aee9f6f88c712bb6"), - hex!("453f432d8672811690597d3652b8f9fb795166eade81687108ba772347dadbc3e96536c728de4a0b8508e91cc086146576a3a01c83439f5aa1262d38c425795e"), - hex!("da1373affa57d9493c115fe9a56ac18217eb3040db2cf0604bf0e071fe246bf2ce4c78f0a058c414cb9fdf3fec7ad2f5509f1b701ed591d80bd7ea3004d14e4b"), - hex!("e73c4af58f8dbe699599157de9b1bf3d81af506c4e09747dfa329a38d45aa04f137054e637fe8537443d9ffb90a973131314ed4788523f8935aa64b5e1772776"), - hex!("daea4e99afa32d5b5cdf9f629dbeaff25ea252cb065bedc6579ea610903f8c65aed0349fb065488832a1ef65ef40461882aed4c5dd999bb9ba84601739711334"), - hex!("165bd986e951c9e2d809378c106e9e062a8e61f2b036f57a10ea7d3aeda009db4abab4052889c28fe4902ab00d4f401d867941ff973d2712afc240daad8f0a48"), - hex!("491795291036d5af166c0ac7d6cdd8d8e35855d84f60358d477ca946de40bbcc59f58f06a93a44dfc77a068826d2f6068c24005f39b153ffcc9bad065b04ca83"), - hex!("d508b1153caa672b50dee140beea5e6b540d326ddcd763cb33b5a5958f8fda06f15c73981b638b178da810f93143a4fdbddb72cdf9947d46c45e720d8e32abca"), - hex!("7ebe7dca72b58394f9e29a3c45ce232abe670b4fd4707111fec54d149c7ddc3b00f28f9c6f007b783e9b05e1dd7640266c77fd9aab43b7184bc0e41f641c343f"), - hex!("7e565bf90a6f58ad7fffde070f8e0c43b6deba1b1a5b6519eb2e20122f48efea65bca264bfafec09d9bb8937c7679b9dc9c82c66aa257e6cd974a3112f7fb79e"), - hex!("319b616aab2872abd63620577a408b1f41c2a32a5fa1878fa6f886262b6e0e59f4c0ba85975a0a814a262d310723b269c7511921429e60516e20f0c9ebd2682d"), - hex!("743ee1eac8acbb06b93631816cbb18e6188e0203ec2d026017ef9d6680bd6396ac5b3a900bdf7b98be17412d17cfcf89ec9595a723e827a018ffe086209492fb"), - hex!("4edfdc522071ceab655021caef43cdeaee8f46111bd48eb95fab799705f8cef63e8e5fb264cdab81ad2183cf46ba0b5247458527c1334913af86fb0643e004ad"), - hex!("d0c34b3bc1cff8b1a61de178df32c1de4f4d3494170661c680300fc131c45e60cae643ea20ee5082dde385dc0c96d8b0c23ca3c86c788eb203b98821b03622ea"), - hex!("60e56e5d76bb3b8971e7ae7f05932feb30a4160b32262f852d8ff1e876846695857dfe3c47b78c67e30a63766f6e83ae272dc15c66bfe26022bd39a330501fb1"), - hex!("bf63165f573e8f059744c7849172588101998255cc0f6c4a69e28c07812949a2c3a4837573a6320519812470987f7c9e55e6b105b1c8be440001063d24888a2e"), - hex!("92dc400c4bb4222e1f61b3ca0d05c5c45ca0553c65c388d244701d49789694783e36c1da9bad20f4092093c3b89cad591919cd6168a0eeb429d32c15bb329b32"), - hex!("ca5601980826a54c38471a12ac5212f720af402ec4bddacd7038b222534116cf2220583f232dba7ad15b4432c8f48a515d9912b3a040e3db1d00dd6daad91399"), - hex!("2f8cda48d975b85baf15deac75e5c0fe46c87f88409f1edc3ed8c06843d872f240f9f8cb751d9437965c35b913b18dd1c397bcd6282aa30e722d7b90049414b7"), - hex!("8118fcb3e92e2edfe216ff6d185e83fc441c05196ec2fc87bc102e33944b355b2e150ef35de00c2e59a69e5ad2b8048a260b5b2b2ad1cf6e314fb7ccba72321d"), - hex!("eef575e9d3a0103554f109e044d3635eb97b7e4596a0a40273c9029386d24bc8e431aa4326cc6d2c96563cf3cae2c5a8442d17c0604ded8628ea1c91847f8161"), - hex!("f31eda1d8628d3f152e04fe9f04885df99967f176f592a808d0cecb6b2b1e6fd4485edc373358130ebc61ab7bb9b15db55c107a58dbb4dfcd0e8c1bbb9333102"), - hex!("1e06c4d45b3ff260b7a71feb109c20b781cea7a1cd7b1ac9eeac7268cd13ad88e082bd74163cf2fe96e3cf0ef271f31ffebdbee0272ad15a236cac6a84e8418f"), - hex!("252626eddec47bb430727ec821c2c6a6e8729083d699ab727fda6d5abb670b60636a103e3e5da13ce99d13faab01dc1cb9533842f07b4ea8e2e87a2cfba86614"), - hex!("386463a6eb305f36549d6ecf78f0e443bd356adff499c1f517c60365865c41fcec6e04144c79fdb3abc61e5ab2947c8b71a3d0891e0f35fa9466bc1bbd38bfe0"), - hex!("2cfc6cb54cc3eedf50fd1060d5b230318418ea0a4367bd518960c3f046651f98f4f10e5cd12ba9815b5f5988a46e6532b1d914fb904e777395bcb6ab468b05e7"), - hex!("4cbed9ff4e8675edd42c7704ea134a50351996b9506e5c9b024540a84afeee3d19fcd8d1ab88b157972c7d3361662795f3add1d180577cf411ae99305be620a7"), - hex!("7aaaf7e41b5dfc84598a88bbf8e6a8e6cdd25805036d66ed0a2707e9a0d1a555d2ed3cd39644d5bfb8e62329eefedef4367ef82100db01f7aee0e5a78eab0c2f"), - hex!("34ce60daf66708cb18c21f4505118540a72770f0c829d057f170555ff90c458cac987e88424091a8269ded70790112ead74f8829a03fa3ef9007f20f7fcd4849"), - hex!("e7cf70cbc785c6194efb416c4b7929cd84a7ab5a1c09f269c14d5ca5c77cbe4db040ebe39b2d45b988720b610435a52afcda696e41283306598f396e7aec4d36"), - hex!("8d085239e6c6c5c03216cda23de434db60f6ccf490dcbf3d48fe3272a266dc739425ac45229cdcfabf1ca65f2e667ae4e0a2f4df85a8d2277ece83ab5ada7333"), - hex!("6fd0e981e291a419091e82805562940c5283fb87dbdc31670b6e68b89581cf629ed19d03ed8d7e740c00a0ef0cfe6b791c6b56ce3da494d2bda65ef5abac881f"), - hex!("1214beef209f07f99fc08abe7d6ffd8461dae9e79dfbf795534626d55b771988b356c7b51000174cd0cef59c0cd2fdba53fb5f358cad3114acb2356cac3d2898"), - hex!("69164c77bef4324a7ada95062c9565f05cf15ef371169e1457fca2a40fede7ec5404678e1edcdb60ac5594c1103c98ca0042fa9f014eaa00cc43dc0dfd1b2dd4"), - hex!("ce32ea648f5c4664057b4a89a69f2a4549f78fb93b64075068607223ca72e9a89a123db7ca3e988419ba7fb5febe91fb9ee640102f8a13f546f51831b2cc17f2"), - hex!("6b7291fb2b958bda392512f149512b1c0362d6c41bd8c9fda08746994f63f4c18b25607ac57ee94e9650bca895acb472a806c21f7e8b397ae7eeda687332ed49"), - hex!("a675b0a790b755b8b2b0d74c3f5db30d367d34e632ca7c7c6e86b238728f1e5c8963222eb7d9f54f07e8858a76c89c53e8b8b191f5fdae464857a685356c19d2"), - hex!("a6a167cce23347f8da947f742f568f46b0cc19105d76b0946d25e03eb8424e42d674254167f0f10c69f3acc57b69413b66454be97ecbcc5afcc99da9270d5122"), - hex!("a787e20495678ff66fe19ac8f9bef0daafd35c3c7cb55891673dd6db53e214dc11cabe06924a861bc6d0f375ff7840d891d78233c04232c7c8468bf0d7a6f85c"), - hex!("e4834d78c5cd070e72ebacc9bc02ca96513d1ca31556bacb3d244fb08a061ddd121e7d86251d4aa03ae70da2a7eec574fe4c580f3fc30dd2fb3278e3ab9223e7"), - hex!("13aef40690769ce954187eca2109359ea04f14040f798bf5b0037070d35ad048ff96e96c0184b900c45442e0b256cb3d43f069a208304408762052d693ff8e09"), - hex!("3a22b3ff2ed34708ecc36077ecc65ed0b3eee6aca57e35b341984656e5d30d9337e8a6aa24e53d80320609cd4e1283a1df5ff9b71041cae2a3f8c85ea5a65e1e"), - hex!("d7ced71ed6091437eb90b2469757aa8fb0c50a512f8db4cbee9a2f5fc4ab1b2872b71c0bb0107b960b06b7b53e9be0b30e02bdec562ec583e30214d7fd760c9f"), - hex!("60d82f3f12934e1749bdf8996937d35095b3f1bdb4f4e0263d8af1235ca222321a87cac2bc22b13f2083cecd533a8c3ca697d8a9852da81932aaf5418e0215b2"), - hex!("9d4ecc3e989a0c53ff8a03c0fae9d4523c67f8be6222d8df610af8ca79af20ef6d952b8e1dd7de0891f3bff7674b24ab29b3f219f7c278b534d65d3dfed9b494"), - hex!("f2ffde1fed2cd43c1ca516d04a88a6d7d96581d40a776b09ade95f0eb6f7b0e1a10cf4c6177414889d7fee91bb308ac816de6c343741add27e5535ee8de4d834"), - hex!("5ac53b1e3b33220d23c02f7d03ce8efb9d6f53d4eb8169287e9844b849f95bf5e3a6a341abb59641e3f38633987063469e2456e045185eb08c12a95d32660e64"), - hex!("b88a40373ff717018096ae985833d08fadf7afb8b551f61e0d7289679294c7f594dd19dc5a292c33c6b10c526f6bb056f5e2575140e69191cc8e6dd6004bbdc9"), - hex!("7a378dc1b7e9634a04fea8a06aba820b703398909466429e5e2a908fed65bf3d08f5cf269bca2b3a56cf4b141d9cf7f3a7ce7d06e5d9c5150888659c3456023a"), - hex!("fa669c4c379b7c9696856e5ae3dee50f3662c7330bcf5a0f2a25ed1f4225d1d196a64b6bc87b50812c2ec6a7dedd9d5b76691c6e58eab8927ede095e011b76d4"), - hex!("5827e430e34571541be3ee8fae0a756e1ce3e6ba53bd5302bbb7507f4c57442342d535b8a0e2187b43648a96ed460ffbb21de4def12c5eac444921f9b75337b2"), - hex!("d7d2a92b9db18f37ba37344de0be8df6a6a9d34a73cd0b44754fa09533f3db22bb83aab8dea6fd5063996222581390400cb222cf9e7ccde19e9c41767439bec7"), - hex!("fd1abbf4912821987fcfe5f94714ff0e6c8393560e5b37840fde21343e419796fa419a79fef0b44c226ce9952551bb30f2f7b7ee6aa6bea208083471b148d76d"), - hex!("75eb3b45e48ebc00300beddc8f72c958aa45bf3de59502d0ab271643c11e46611b14d20f4df4cc0fd9dc1e7eb36703ac8814fbe753dcffc6b08c97fc47ebedae"), - hex!("fcec1909037a43ba46df62d21be622afb256cbe3d671ea7003da8f7241e205932bb2c2f4292ad97f68a627d2b8b2ff7767e9e3bfc093b1340ecebb9942b03fc7"), - hex!("3fc7a7621ab8abbd900dcea77f7a51b3af549167fdc4b8a362fd5c822943a2105af3b29b9c46042659233fd1c7b968ad93007929c7e56f10a5a60a400a755957"), - hex!("2b0c5c1952fc863c0f1b176d75a702ed3e45e8e36faa2364b6df918c4309cb05c2737f6ee5a7429e387dba6254b3924c762df4f539dbe9b017967fb2400e12ef"), - hex!("1c1e07459d7411dffed20fca05c70d8f33e363e317706d9f0a59a4403056fdc3b90dd77641ccd23d7d868c4f10764ca1b062b645409b367162637ce77571db91"), - hex!("7556d6157da1db9768ae501a4ddc93cf9bfc9933beecaae5bf0a904dd2fa954cf032d08660aa50591af4de6b5d7ebc78870b6fef636cc1801d6530d28e6783fe"), - hex!("01222084d2e4a8dc10e9c991f7d07aae839915ea8e00f5b26ae4496eabcc0ffa1b59c7fa4dc09ea5d59543c2f8f3d5d9ccf4b2fc07c7150a64ccf53cf34c162d"), - hex!("957969a51fd22a11000bbd10baf5f4f12e4066778394e3c4e17aebfe0f50d407f7f3f2c4bc612303c8de7aa579be78320bf3bbb80d043bca2ef246981bcf621f"), - hex!("933ea0517e02ecfc2e27df9d9dbaa57614144ea6cbeab7a4965308b7954d5a6c2419a518ab309cf57f6917cf40b1475c2bf749cbddbf9c16ce8a75bc1e4c2d0a"), - hex!("72644f2b70f5a1354ba05fa652339a838101af55ebf006ec74c385fe1420a3115a06f263ba6c0e47e8d7bcb33694997acd83c75155ecfbf410597de031e35d8a"), - hex!("9e16e652710d4fe93c328a606089af6087b12feb3ecd5a6ac09c8dfd369d7136c8acb9a26f92f38cda4c0df1cf9b266cac90679478de1784aa4948d76c7efb75"), - hex!("845245d7c4c8768df37242908397c98129825e1b1ea94895f6cd96eae6dae76892181e9c71821768a6c9e6170dd2f49d410efdbe784366702316c7c7911cc442"), - hex!("86c367500d7c9fdfafd381a8a84c9af83db0c5b588d5f225bd423a1d446c923a8f66e5603eccaf95f4e56ca95830ac30da5cc2ac4722b9dccf9a0a162eaaef95"), - hex!("ef2203464f0eff26c7fb98e4f140a04bb80a223c9ee611059bf604f2e68a676006775b4fe6208ca3a4479730f08dd0da3fb0aea2d7f6271b632dc0fc8a598ee9"), - hex!("e360bb958304e18c89ebc7b3cef443579f142d71bd1bf3fa1f873969542ca7fc7464ee636889e4da1235a8b1db22a105b78eee9f7de4276be3740c421294e530"), - hex!("acad1568dcce42444d2654ab45571f6a9b111f072424bf18eb3de0eb806bc3031455c5249f9d011db1b6e0a7f82051513557aa305c502a082c2b82aa27432673"), - hex!("a3d3773de64b72fe8d8e24d8be4498fb9e380f2ff6def5eb09064885370e05dd6267ccb7224ea18d1eda473bfe7def1a5a8d08c8667cf8cc22856ec78f793614"), - hex!("d2f3fd5a9c23d9dd5640b8b5017bf6905bc661de0d5e6dafb1e5f8a80fe0d2599a8a4fd5d50e9a631027821d36e8f357100336e3dab4b93e338bf1dbbefe43e2"), - hex!("ca44e67d605081401935e034e98cbf4053c25c113bb54df7f9c85e296059c1cedfb9432be68c9dc87aa7f1106d83df1ad5a04419d960395de07d37d1bb671074"), - hex!("821b7d8a4546c4cf41f57b93988dc47f52805af933a4bea1ec5a7e5712fb37fcce874e8f8b89d708ec8d28b3aa29ec16892ef5af64bc4093e825a021102fef47"), - hex!("5ad310934d247d90167f027ca8cf0c2cce6dce8ab6d4ed823134b927b8ffbcf8fe90c345612e9c63940d29d38e48ba7253ed91aa5ca40c5e62d94e5322cea745"), - hex!("49154090f51da0f58cf14a1b3636a47ab2c769c686dec5d900c0f1eab134d3a89aaf6b59156815d0b022a83163a835e01fb86666229123bf80207d065b125cc6"), - hex!("fc126f965cde55067e2cc955e5a73325695523c238b41a8bc63c0915812c68819f4bee8d8c5ceba22fb759896eb3fb3eec475a3c201bf946af31965b9e5e54e4"), - hex!("b13f70703389f688a3db8a98759d3138ed586a30b76c7418abe066a91828dd1626e8a0c281e0b1cff30cc2ca4e5902dec1a85d2141892090d40737e6b24388c8"), - hex!("4a474f8324e161287b3771e6f759af3c919c1b2a47102424b078b183bd1124144df07c04aeeb341e53f9944d06e9319998d420147703b0c813e83fd2d9d73b53"), - hex!("e7b5f28991d68b0cf1d03d7b794546c56bc7c452ebb63b241910f08a3cf22cd1783952aac7303c44d12af876bb0f3edae8907e76ab12360126149b706820ac24"), - hex!("9495582f9a3643fbece23fc39b1636e225473bc6cb063ca9aedd4be87fdd28eccd794cf02d2661f6594dba78864c4cf3ba1e59af0ba6f4263fb011a90d808fd8"), - hex!("4d8f7579483bfa21b5ebc1ce5c0ab4351b13f1e3a887fd379539ee2f7825ddb6bc2eb11723df69b069d51f2e1f4b2dd9a9057337f39f6dcac511bcc259589aa4"), - hex!("225f2eec709f4f8d4be4eb303dfc4bc40286b32f2de2fdffa2be2da6f48f30d5651c8296e15aacca898826ece0bf9822872c5b54775c2fcd6760f9a55889ca64"), - hex!("588ac0f3b49d9929fd4589a71c98b2abee357b639e1da14e260a8030ec28615461d1672ae921dbde19f91ba61f20ea6047e578a5448db2dbd368dc5c825ec2bc"), - hex!("a9e0be1848dd1142e24aa56b5c50fed3b684c1884d8f3f9cdf77492df1575436be468d305390dcafe03813fac4b67e9b60be09d68e9939f16c569e40681a58df"), - hex!("c04bc11cdea62f46b66051d0ccde6d02631f7087b3f52ba074576bd9aea51adf0b27b784155e649dc7f54fa813887c5cb2c988c9d38738ff7245c9a3c0b20aeb"), - hex!("c7faf0bdfd4c8d2d2b4774d911fbeaee5bafbef3aad89946900a5cd99eb73fc3667c207c120ee749fd38fa4c453f9e8aea104f832e75f6ceb647a9efb17ecb70"), - hex!("9bd24f855de27f045b05c7f54ed308be75d7e778679ab2bd0f7350b8a5a98a7e4ea15da398914b845e62900c007b7cfbf8d40beddf80a5715417d52659bce081"), - hex!("4e47e6cead640eea245d3c061673542e63bafd9edaefacecb345c1177909da0d44713eec6afa5f36c85dcc48481bbf485801efa111e7723a29260bbc2c112a31"), - hex!("11a7cc5bde8e296a525ab14fd19e98d347fdaf3626813f1279d8f496295eda0ee4ea427a6e7dd2d485def8a98ca34036f773e09d69aef8abf6f397596be66f40"), - hex!("eb2074948b85204e74119b925179f769971d8868de055a11c3bbee3fc4590b870a3745e81ba63f75f00128d3839dce2c8ca5ce567711a9a02223a9e445b49b56"), - hex!("fa3a9787629af257625e8079d51d6f94e5daa80b253549da3f4e198e805fe197cad179010b94ffef5c93c005c89b4f81e6a60af0211967dfb9a32bc5aacfb08c"), - hex!("d4734e04f5c3a1c875658f2f1c768b0b95023d944f6aca720a75c039d0c958c0d74132cbc034cda19388e1de9c9b3eac6cad6175db10322d94500e4b03cf64d9"), - hex!("e83a636418569947f3395f105fa79b71b4d62fbb3b8e04858eac70f0ff3d65d4f8b3209b16faadde6ec6f3b18c701b6db78b36bab651071adcdffdf20d6ccbe7"), - hex!("15426518b6b2ede6cd1be89b064f1ac239329e522d85d0412c7dfd5dd1f8127add328bfa41e6fe26acc46de63016491fd47f5768380c9cb5577bd589690f6912"), - hex!("84faefe41e8a1bd49ddfe9fdb95a5fe4e0d5e71f4f1c0016af599cd482d0b509aa67931c02f9dac7221be05dc87e0f03a5bb11945b732442df6697bee788eecf"), - hex!("bbcd516d74d3510f54c0861476f2293af5999982620be8a8544a201ed308f4e48005875982cf9d2341cab53ff3a9b1dcc16789ea3ebca54801ac5c2f750b10e6"), - hex!("6fb9180a6579f54ee68569f983834ad6d15ea29227c1c54cb5d3fcb97081665c11d82def47eaad5b0467804236722b5167bd9ed2a336212b9a1622cff2aa27e8"), - hex!("6e79a3f3f6084c1063020f2270ece972232cf22b3a6a8c88079cc1cca9f2d023cf14398e59dee93211c637fcb85d01422f36163b72395a40528261a00a1bc12f"), - hex!("46cf6ce2941c2e467f2149b77e5216c1730ee1c3d4ae62476dcf7c3cfc095f221439d4f2df25c23d273dcf3b50bd24ffbc902266ffd5c29125914c17477dcdcc"), - hex!("339236459ff10a7902501de1079a1822eeda9f1d46544dbac8c182c15158b4ab7be70a036aaa2e507441f2a526b9191e1d3975f55bc98eea94746b5492f6840c"), - hex!("32b62e97bc132a71e096de6b9b6ce8f5d718387ecce0799e54c8be9e8ecd00400c53d99e796768742c619023a52ff2967c84e7509339f4504354a6c8dfe9e362"), - hex!("9454b1d497192c6c67b94f0216749f447fc4885768b75394cd8bd4fe8f9402f8d1983362cdf7fa05c7c2bfc9c0c69333b2b5dcbfda0ce642ee58231ac3dad154"), - hex!("10520f013c87948b17c1083b1a59ef70a81a17d84985235fe478dd1d3607a3fd38dca172107c08bb50b9d9cab56c28d5f9a33c10c2b79a11af481d6b5396e3f1"), - hex!("7d2d4c1062dedd873fccd80357f115fba3d817e74ba6344704b2cf211efefc1d7fa4c1e9a5c1d96a7ba87ad5e0eb61327c0d199cf98fcbc6babb6192da7af1e3"), - hex!("ac65c4d2e0662b09361e6f53c1b494c6c5537d264ee57f4a12430396ce73d1d8128f4b3eb2ea5c3b216965dc30bc2c76064e088cca72f587a05800d811a26450"), - hex!("be49ec5003421b6d9d842747d05842992282fef8110b5cf19634bdefad7683d86cf1edf4cfa9ae2b67c13376b10aac3d3c157efe57ea15ddc122f778f7a02ee0"), - hex!("e90182b99ea1095f42f2fc1a7ad15a62f434a2ef66f0fcfa11563e88013cd93d50823e1a522d48bcf85bde8077ec964163f3790208b13054e6891b5c166874a8"), - hex!("0e9df2c6eb40ee8a86be7b7bc8250a3929373364478017d79f8e57f4fc8d49de1e45c943f2fc745d8d383044b21a73e04713d09649060a8995e2456b6d3523c6"), - ]; -} diff --git a/parachain/primitives/ethereum/src/header.rs b/parachain/primitives/ethereum/src/header.rs index 0437996d2..b65f44126 100644 --- a/parachain/primitives/ethereum/src/header.rs +++ b/parachain/primitives/ethereum/src/header.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use codec::{Decode, Encode}; use ethbloom::Bloom as EthBloom; use hex_literal::hex; @@ -105,7 +107,7 @@ impl Header { let expected_hash = maybe_hash?; let node: Box = bytes.as_slice().try_into().ok()?; if (*node).contains_hash(expected_hash.into()) { - return Some(keccak_256(bytes)) + return Some(keccak_256(bytes)); } None }); @@ -140,7 +142,7 @@ impl Header { fn decoded_seal_field(&self, index: usize, max_len: usize) -> Option { let bytes: Bytes = rlp::decode(self.seal.get(index)?).ok()?; if bytes.len() > max_len { - return None + return None; } Some(bytes) } diff --git a/parachain/primitives/ethereum/src/lib.rs b/parachain/primitives/ethereum/src/lib.rs index 9d2c5bbee..1a10ea9ab 100644 --- a/parachain/primitives/ethereum/src/lib.rs +++ b/parachain/primitives/ethereum/src/lib.rs @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork #![cfg_attr(not(feature = "std"), no_std)] -pub mod difficulty; -pub mod ethashdata; pub mod header; pub mod log; pub mod mpt; diff --git a/parachain/primitives/ethereum/src/log.rs b/parachain/primitives/ethereum/src/log.rs index 49787688a..021ab1677 100644 --- a/parachain/primitives/ethereum/src/log.rs +++ b/parachain/primitives/ethereum/src/log.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use codec::{Decode, Encode}; use ethereum_types::{H160, H256}; use scale_info::TypeInfo; diff --git a/parachain/primitives/ethereum/src/mpt.rs b/parachain/primitives/ethereum/src/mpt.rs index b9481e8a4..ab6db4488 100644 --- a/parachain/primitives/ethereum/src/mpt.rs +++ b/parachain/primitives/ethereum/src/mpt.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork //! Helper types to work with Ethereum's Merkle Patricia Trie nodes use ethereum_types::H256; diff --git a/parachain/primitives/ethereum/src/receipt.rs b/parachain/primitives/ethereum/src/receipt.rs index 062b0e7db..f28b21aea 100644 --- a/parachain/primitives/ethereum/src/receipt.rs +++ b/parachain/primitives/ethereum/src/receipt.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use crate::{Bloom, Log}; use codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -54,7 +56,7 @@ impl rlp::Decodable for Receipt { 1 | 2 => { let receipt_rlp = &rlp::Rlp::new(&data[1..]); if !receipt_rlp.is_list() { - return Err(rlp::DecoderError::RlpExpectedToBeList) + return Err(rlp::DecoderError::RlpExpectedToBeList); } Self::decode_list(&rlp::Rlp::new(&data[1..])) }, diff --git a/parachain/primitives/router/Cargo.toml b/parachain/primitives/router/Cargo.toml index 851b45512..0035c95ea 100644 --- a/parachain/primitives/router/Cargo.toml +++ b/parachain/primitives/router/Cargo.toml @@ -20,11 +20,13 @@ xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "master", d xcm-executor = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false } snowbridge-core = { path = "../../primitives/core", default-features = false } + ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } +hex-literal = { version = "0.4.1" } + [dev-dependencies] -hex = { package = "rustc-hex", version = "2.1.0", default-features = false } -hex-literal = { version = "0.4.1", default-features = false } +hex = { package = "rustc-hex", version = "2.1.0" } [features] default = [ "std" ] diff --git a/parachain/primitives/router/src/inbound/mod.rs b/parachain/primitives/router/src/inbound/mod.rs new file mode 100644 index 000000000..71a5965a8 --- /dev/null +++ b/parachain/primitives/router/src/inbound/mod.rs @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use frame_support::{traits::ContainsPair, weights::Weight}; +use sp_core::{RuntimeDebug, H160}; +use sp_io::hashing::blake2_256; +use sp_std::prelude::*; +use xcm::v3::{prelude::*, Junction::AccountKey20}; +use xcm_executor::traits::ConvertLocation; + +const MINIMUM_DEPOSIT: u128 = 1; + +/// Messages from Ethereum are versioned. This is because in future, +/// we want to evolve the protocol so that the ethereum side sends XCM messages directly. Instead +/// having BridgeHub transcode the messages into XCM. +#[derive(Clone, Encode, Decode, RuntimeDebug)] +pub enum VersionedMessage { + V1(MessageV1), +} + +/// For V1, the ethereum side sends messages which are transcoded into XCM. These messages are +/// self-contained, in that they can be transcoded using only information in the message. +#[derive(Clone, Encode, Decode, RuntimeDebug)] +pub struct MessageV1 { + /// EIP-155 chain id of the origin Ethereum network + pub chain_id: u64, + /// The gateway-specific message + pub message: GatewayMessage, +} + +#[derive(Clone, Encode, Decode, RuntimeDebug)] +pub enum GatewayMessage { + UpgradeProxy(UpgradeProxyMessage), + NativeTokens(NativeTokensMessage), +} + +#[derive(Clone, Encode, Decode, RuntimeDebug)] +pub enum UpgradeProxyMessage {} + +#[derive(Clone, Encode, Decode, RuntimeDebug)] +pub enum NativeTokensMessage { + Create { + origin: H160, + token: H160, + name: Vec, + symbol: Vec, + decimals: u8, + create_call_index: [u8; 2], + set_metadata_call_index: [u8; 2], + }, + Mint { + origin: H160, + token: H160, + dest: Option, + recipient: MultiLocation, // Recipient of funds on final destination + amount: u128, + }, +} + +pub enum ConvertError { + /// Message is in the wrong format + BadFormat, +} + +impl TryInto> for MessageV1 { + type Error = ConvertError; + + fn try_into(self) -> Result, Self::Error> { + match self.message { + GatewayMessage::UpgradeProxy(message) => message.convert(self.chain_id), + GatewayMessage::NativeTokens(message) => message.convert(self.chain_id), + } + } +} + +impl UpgradeProxyMessage { + pub fn convert(self, _chain_id: u64) -> Result, ConvertError> { + // The UpgradeProxy gateway doesn't send any messages to Polkadot + Err(ConvertError::BadFormat) + } +} + +impl NativeTokensMessage { + pub fn convert(self, chain_id: u64) -> Result, ConvertError> { + let network = NetworkId::Ethereum { chain_id }; + match self { + NativeTokensMessage::Create { + origin, + token, + name, + symbol, + decimals, + create_call_index, + set_metadata_call_index, + } => { + let owner = GlobalConsensusEthereumAccountConvertsFor::<[u8; 32]>::from_params( + &chain_id, + origin.as_fixed_bytes(), + ); + + let asset_id = Self::convert_token_address(network, origin, token); + let instructions: Vec> = vec![ + UniversalOrigin(GlobalConsensus(network)), + DescendOrigin(X1(Junction::AccountKey20 { network: None, key: origin.into() })), + Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: Weight::from_parts(40_000_000_000, 8000), + call: (create_call_index, asset_id, owner, MINIMUM_DEPOSIT).encode().into(), + }, + Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(20_000_000_000, 8000), + call: (set_metadata_call_index, asset_id, name, symbol, decimals) + .encode() + .into(), + }, + ]; + Ok(instructions.into()) + }, + NativeTokensMessage::Mint { origin, token, dest, recipient, amount } => { + let asset = + MultiAsset::from((Self::convert_token_address(network, origin, token), amount)); + + let mut instructions: Vec> = vec![ + UniversalOrigin(GlobalConsensus(network)), + DescendOrigin(X1(Junction::AccountKey20 { network: None, key: origin.into() })), + ReserveAssetDeposited(vec![asset.clone()].into()), + ClearOrigin, + ]; + + match dest { + Some(para) => { + let mut fragment: Vec> = vec![DepositReserveAsset { + assets: MultiAssetFilter::Definite(vec![asset.clone()].into()), + dest: MultiLocation { parents: 1, interior: X1(Parachain(para)) }, + xcm: vec![DepositAsset { + assets: MultiAssetFilter::Definite(vec![asset.clone()].into()), + beneficiary: recipient, + }] + .into(), + }]; + instructions.append(&mut fragment); + }, + None => { + let mut fragment: Vec> = vec![DepositAsset { + assets: MultiAssetFilter::Definite(vec![asset.clone()].into()), + beneficiary: recipient, + }]; + instructions.append(&mut fragment); + }, + } + Ok(instructions.into()) + }, + } + } + + // Convert ERC20 token address to a Multilocation that can be understood by Assets Hub. + fn convert_token_address(network: NetworkId, origin: H160, token: H160) -> MultiLocation { + return MultiLocation { + parents: 2, + interior: X3( + GlobalConsensus(network), + AccountKey20 { network: None, key: origin.into() }, + AccountKey20 { network: None, key: token.into() }, + ), + }; + } +} + +pub struct FromEthereumGlobalConsensus; +impl ContainsPair for FromEthereumGlobalConsensus { + fn contains(a: &MultiLocation, b: &MultiLocation) -> bool { + let a_network_id = a.interior().global_consensus(); + if let Ok(Ethereum { .. }) = a_network_id { + b.interior().global_consensus() == a_network_id + } else { + false + } + } +} + +pub struct GlobalConsensusEthereumAccountConvertsFor(PhantomData); +impl ConvertLocation for GlobalConsensusEthereumAccountConvertsFor +where + AccountId: From<[u8; 32]> + Clone, +{ + fn convert_location(location: &MultiLocation) -> Option { + if let MultiLocation { + interior: X2(GlobalConsensus(Ethereum { chain_id }), AccountKey20 { key, .. }), + .. + } = location + { + Some(Self::from_params(chain_id, key).into()) + } else { + None + } + } +} + +impl GlobalConsensusEthereumAccountConvertsFor { + fn from_params(chain_id: &u64, key: &[u8; 20]) -> [u8; 32] { + (b"ethereum", chain_id, key).using_encoded(blake2_256) + } +} diff --git a/parachain/primitives/router/src/lib.rs b/parachain/primitives/router/src/lib.rs index d552939c4..d9031c69b 100644 --- a/parachain/primitives/router/src/lib.rs +++ b/parachain/primitives/router/src/lib.rs @@ -1,112 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork #![cfg_attr(not(feature = "std"), no_std)] -use core::marker::PhantomData; - -use codec::{Decode, Encode}; -use sp_core::{RuntimeDebug, H160}; -use sp_std::prelude::*; -use xcm::v3::prelude::*; - -use sp_runtime::traits::Get; - -pub mod export; - -#[derive(Clone, Encode, Decode, RuntimeDebug)] -pub enum Payload { - NativeTokens(NativeTokensPayload), -} - -#[derive(Clone, Encode, Decode, RuntimeDebug)] -pub enum NativeTokensPayload { - Create { - token: H160, - name: Vec, - symbol: Vec, - decimals: u8, - }, - Mint { - token: H160, - dest: Option, - recipient: MultiLocation, // Recipient of funds on final destination - amount: u128, - }, -} - -pub trait ConvertMessage { - /// Convert inbound message to destination and Xcm message - fn convert(origin: H160, dest: u32, payload: Payload) -> (MultiLocation, Xcm<()>); -} - -pub struct InboundMessageConverter(PhantomData); - -impl ConvertMessage for InboundMessageConverter -where - EthereumNetworkId: Get, -{ - fn convert(origin: H160, dest: u32, payload: Payload) -> (MultiLocation, Xcm<()>) { - let dest = MultiLocation { parents: 1, interior: X1(Parachain(dest)) }; - let xcm = match payload { - Payload::NativeTokens(inner_payload) => - Self::convert_native_tokens_payload(origin, inner_payload), - }; - - (dest, xcm) - } -} - -impl InboundMessageConverter -where - EthereumNetworkId: Get, -{ - fn convert_native_tokens_payload(origin: H160, payload: NativeTokensPayload) -> Xcm<()> { - let network = EthereumNetworkId::get(); - - match payload { - NativeTokensPayload::Create { .. } => Vec::new().into(), - NativeTokensPayload::Mint { token, dest, recipient, amount } => { - let asset = MultiAsset::from((Self::convert_token_address(token), amount)); - - let mut instructions: Vec> = vec![ - UniversalOrigin(GlobalConsensus(network)), - DescendOrigin(X1(Junction::AccountKey20 { network: None, key: origin.into() })), - ReserveAssetDeposited(vec![asset.clone()].into()), - ClearOrigin, - ]; - - match dest { - Some(para) => { - let mut fragment: Vec> = vec![DepositReserveAsset { - assets: MultiAssetFilter::Definite(vec![asset.clone()].into()), - dest: MultiLocation { parents: 1, interior: X1(Parachain(para)) }, - xcm: vec![DepositAsset { - assets: MultiAssetFilter::Definite(vec![asset.clone()].into()), - beneficiary: recipient, - }] - .into(), - }]; - instructions.append(&mut fragment); - }, - None => { - let mut fragment: Vec> = vec![DepositAsset { - assets: MultiAssetFilter::Definite(vec![asset.clone()].into()), - beneficiary: recipient, - }]; - instructions.append(&mut fragment); - }, - } - instructions.into() - }, - } - } - - fn convert_token_address(token: H160) -> MultiLocation { - let network = EthereumNetworkId::get(); - return MultiLocation { - parents: 2, - interior: X2( - GlobalConsensus(network), - AccountKey20 { network: None, key: token.into() }, - ), - } - } -} +pub mod inbound; +pub mod outbound; diff --git a/parachain/primitives/router/src/export.rs b/parachain/primitives/router/src/outbound/mod.rs similarity index 88% rename from parachain/primitives/router/src/export.rs rename to parachain/primitives/router/src/outbound/mod.rs index 07c716d81..8120e005f 100644 --- a/parachain/primitives/router/src/export.rs +++ b/parachain/primitives/router/src/outbound/mod.rs @@ -1,26 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pub mod payload; + use core::slice::Iter; use codec::{Decode, Encode}; -use ethabi::{self, Token}; + use frame_support::{ensure, log, traits::Get}; -use snowbridge_core::{OutboundQueue, ParaId}; -use sp_core::{RuntimeDebug, H160}; +use snowbridge_core::{OutboundMessage, OutboundQueue as OutboundQueueTrait}; +use sp_core::{RuntimeDebug, H160, H256}; use sp_std::{marker::PhantomData, prelude::*}; use xcm::v3::prelude::*; use xcm_executor::traits::ExportXcm; -#[derive(Encode, Decode)] -struct ValidatedMessage(ParaId, u16, Vec); +use payload::{Message, NativeTokensMessage}; pub struct EthereumBlobExporter( PhantomData<(RelayNetwork, BridgedNetwork, OutboundQueue)>, ); -impl ExportXcm - for EthereumBlobExporter + +impl ExportXcm + for EthereumBlobExporter where RelayNetwork: Get, BridgedNetwork: Get, - Queue: OutboundQueue, + OutboundQueue: OutboundQueueTrait, + OutboundQueue::Ticket: Encode + Decode, { type Ticket = (Vec, XcmHash); @@ -34,13 +39,13 @@ where let bridged_network = BridgedNetwork::get(); if network != bridged_network { log::trace!(target: "xcm::ethereum_blob_exporter", "skipped due to unmatched bridge network {network:?}."); - return Err(SendError::NotApplicable) + return Err(SendError::NotApplicable); } let dest = destination.take().ok_or(SendError::MissingArgument)?; if dest != Here { log::trace!(target: "xcm::ethereum_blob_exporter", "skipped due to unmatched remote destination {dest:?}."); - return Err(SendError::NotApplicable) + return Err(SendError::NotApplicable); } let (local_net, local_sub) = universal_source @@ -57,14 +62,14 @@ where if local_net != RelayNetwork::get() { log::trace!(target: "xcm::ethereum_blob_exporter", "skipped due to unmatched relay network {local_net:?}."); - return Err(SendError::NotApplicable) + return Err(SendError::NotApplicable); } let para_id = match local_sub { X1(Parachain(para_id)) => para_id, _ => { log::error!(target: "xcm::ethereum_blob_exporter", "could not get parachain id from universal source '{local_sub:?}'."); - return Err(SendError::MissingArgument) + return Err(SendError::MissingArgument); }, }; @@ -74,72 +79,52 @@ where })?; let mut converter = XcmConverter::new(&message, &bridged_network); - let (payload, max_target_fee) = converter.convert().map_err(|err|{ + let (converted_message, max_target_fee) = converter.convert().map_err(|err|{ log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); SendError::Unroutable })?; if max_target_fee.is_some() { log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due not supporting max target fee."); - return Err(SendError::Unroutable) + return Err(SendError::Unroutable); } - let (encoded, handler) = payload.abi_encode(); + let (gateway, payload) = converted_message.encode(); + + let hash_input = (para_id, gateway, payload.clone()).encode(); + let message_id: H256 = sp_io::hashing::blake2_256(&hash_input).into(); + + let outbound_message = + OutboundMessage { id: message_id, origin: para_id.into(), gateway, payload }; - let blob = ValidatedMessage(para_id.into(), handler, encoded).encode(); - let hash: [u8; 32] = sp_io::hashing::blake2_256(blob.as_slice()); + let ticket = OutboundQueue::validate(&outbound_message).map_err(|_| { + log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue validation of message failed"); + SendError::ExceedsMaxMessageSize + })?; - log::info!(target: "xcm::ethereum_blob_exporter", "message validated {hash:#?}."); + log::info!(target: "xcm::ethereum_blob_exporter", "message validated {message_id:#?}."); // TODO: Fees if any currently returning empty multi assets as cost - Ok(((blob, hash), MultiAssets::default())) + Ok(((ticket.encode(), message_id.into()), MultiAssets::default())) } fn deliver((blob, hash): (Vec, XcmHash)) -> Result { - let ValidatedMessage(source_id, handler, payload) = - ValidatedMessage::decode(&mut blob.as_ref()).map_err(|err| { - log::trace!(target: "xcm::ethereum_blob_exporter", "undeliverable due to decoding error '{err:?}'."); + let ticket: OutboundQueue::Ticket = OutboundQueue::Ticket::decode(&mut blob.as_ref()) + .map_err(|_| { + log::trace!(target: "xcm::ethereum_blob_exporter", "undeliverable due to decoding error"); SendError::NotApplicable })?; - Queue::submit(source_id, handler, payload.as_ref()).map_err(|err| { - log::error!(target: "xcm::ethereum_blob_exporter", "undeliverable due to OutboundQueue error '{err:?}'."); - SendError::Unroutable + + OutboundQueue::submit(ticket).map_err(|_| { + log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue submit of message failed"); + SendError::Transport("other transport error") })?; + log::info!(target: "xcm::ethereum_blob_exporter", "message delivered {hash:#?}."); Ok(hash) } } -#[derive(PartialEq, RuntimeDebug)] -enum NativeTokens { - Unlock { asset: H160, destination: H160, amount: u128 }, -} - -#[derive(PartialEq, RuntimeDebug)] -enum OutboundPayload { - NativeTokens(NativeTokens), -} - -impl OutboundPayload { - fn abi_encode(&self) -> (Vec, u16) { - match self { - Self::NativeTokens(NativeTokens::Unlock { asset, destination, amount }) => { - let inner = ethabi::encode(&[Token::Tuple(vec![ - Token::Address(*asset), - Token::Address(*destination), - Token::Uint((*amount).into()), - ])]); - let message = ethabi::encode(&[Token::Tuple(vec![ - Token::Uint(0.into()), // Unlock action = 0 - Token::Bytes(inner), - ])]); - - (message, 1) // NativeTokens handler = 1 - }, - } - } -} - /// Errors that can be thrown to the pattern matching step. #[derive(PartialEq, RuntimeDebug)] enum XcmConverterError { @@ -166,14 +151,14 @@ impl<'a, Call> XcmConverter<'a, Call> { Self { iter: message.inner().iter(), bridged_location } } - fn convert(&mut self) -> Result<(OutboundPayload, Option<&'a MultiAsset>), XcmConverterError> { + fn convert(&mut self) -> Result<(Message, Option<&'a MultiAsset>), XcmConverterError> { use XcmConverterError::*; // Get target fees if specified. - let max_target_fee = self.get_fee_info()?; + let max_target_fee = self.fee_info()?; // Get deposit reserved asset - let result = self.get_reserve_deposited_asset()?; + let result = self.reserve_deposited_asset()?; // All xcm instructions must be consumed before exit. if self.next().is_ok() { @@ -183,13 +168,15 @@ impl<'a, Call> XcmConverter<'a, Call> { } } - fn get_fee_info(&mut self) -> Result, XcmConverterError> { + fn fee_info(&mut self) -> Result, XcmConverterError> { use XcmConverterError::*; let execution_fee = match self.next()? { WithdrawAsset(fee_asset) => match self.next()? { BuyExecution { fees: execution_fee, weight_limit: Unlimited } if fee_asset.len() == 1 && fee_asset.contains(&execution_fee) => - Some(execution_fee), + { + Some(execution_fee) + }, _ => return Err(BuyExecutionExpected), }, UnpaidExecution { check_origin: None, weight_limit: Unlimited } => None, @@ -198,24 +185,24 @@ impl<'a, Call> XcmConverter<'a, Call> { Ok(execution_fee) } - fn get_reserve_deposited_asset(&mut self) -> Result { + fn reserve_deposited_asset(&mut self) -> Result { use XcmConverterError::*; let (assets, beneficiary) = if let ReserveAssetDeposited(reserved_assets) = self.next()? { if reserved_assets.len() == 0 { - return Err(NoReserveAssets) + return Err(NoReserveAssets); } if let (ClearOrigin, DepositAsset { assets, beneficiary }) = (self.next()?, self.next()?) { if reserved_assets.inner().iter().any(|asset| !assets.matches(asset)) { - return Err(FilterDoesNotConsumeAllAssets) + return Err(FilterDoesNotConsumeAllAssets); } (reserved_assets, beneficiary) } else { - return Err(ReserveAssetDepositedExpected) + return Err(ReserveAssetDepositedExpected); } } else { - return Err(ReserveAssetDepositedExpected) + return Err(ReserveAssetDepositedExpected); }; // assert that the benificiary is ethereum account key 20 @@ -226,11 +213,11 @@ impl<'a, Call> XcmConverter<'a, Call> { } = beneficiary { if network != self.bridged_location { - return Err(BeneficiaryResolutionFailed) + return Err(BeneficiaryResolutionFailed); } H160(*key) } else { - return Err(BeneficiaryResolutionFailed) + return Err(BeneficiaryResolutionFailed); } }; @@ -244,7 +231,7 @@ impl<'a, Call> XcmConverter<'a, Call> { if let MultiAsset { id: Concrete(location), fun: Fungible(amount) } = asset { (location, amount) } else { - return Err(AssetNotConcreteFungible) + return Err(AssetNotConcreteFungible); }; ensure!(*amount > 0, ZeroAssetTransfer); @@ -256,15 +243,15 @@ impl<'a, Call> XcmConverter<'a, Call> { } = asset_location { if network != self.bridged_location { - return Err(AssetResolutionFailed) + return Err(AssetResolutionFailed); } (H160(*key), *amount) } else { - return Err(AssetResolutionFailed) + return Err(AssetResolutionFailed); } }; - Ok(OutboundPayload::NativeTokens(NativeTokens::Unlock { asset, destination, amount })) + Ok(Message::NativeTokens(NativeTokensMessage::Unlock { asset, destination, amount })) } fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { @@ -276,7 +263,7 @@ impl<'a, Call> XcmConverter<'a, Call> { mod tests { use frame_support::parameter_types; use hex_literal::hex; - use sp_runtime::{DispatchError, DispatchResult}; + use snowbridge_core::SubmitError; use super::*; @@ -292,23 +279,27 @@ mod tests { hex!("1454532f17679d9bfd775fef52de6c0598e34def65ef19ac06c11af013d6ca0f"); struct MockOkOutboundQueue; - impl OutboundQueue for MockOkOutboundQueue { - fn submit( - _source_id: snowbridge_core::ParaId, - _handler: u16, - _payload: &[u8], - ) -> DispatchResult { + impl OutboundQueueTrait for MockOkOutboundQueue { + type Ticket = (); + + fn validate(_: &OutboundMessage) -> Result<(), SubmitError> { + Ok(()) + } + + fn submit(_: Self::Ticket) -> Result<(), SubmitError> { Ok(()) } } struct MockErrOutboundQueue; - impl OutboundQueue for MockErrOutboundQueue { - fn submit( - _source_id: snowbridge_core::ParaId, - _handler: u16, - _payload: &[u8], - ) -> DispatchResult { - Err(DispatchError::Other("Error")) + impl OutboundQueueTrait for MockErrOutboundQueue { + type Ticket = (); + + fn validate(_: &OutboundMessage) -> Result<(), SubmitError> { + Err(SubmitError::MessageTooLarge) + } + + fn submit(_: Self::Ticket) -> Result<(), SubmitError> { + Err(SubmitError::MessageTooLarge) } } @@ -585,10 +576,7 @@ mod tests { &mut message, ); - assert_eq!( - result, - Ok(((SUCCESS_CASE_1_TICKET.into(), SUCCESS_CASE_1_TICKET_HASH.into()), vec![].into())) - ); + assert!(result.is_ok()); } #[test] @@ -598,19 +586,8 @@ mod tests { EthereumBlobExporter::::deliver( ticket, ); - assert_eq!(result, Ok(SUCCESS_CASE_1_TICKET_HASH)) - } - #[test] - fn exporter_deliver_with_decode_failure_yields_not_applicable() { - let corrupt_ticket = hex!("DEADBEEF").to_vec(); - let hash = sp_io::hashing::blake2_256(corrupt_ticket.as_slice()); - let ticket: Ticket = (corrupt_ticket, hash); - let result = - EthereumBlobExporter::::deliver( - ticket, - ); - assert_eq!(result, Err(SendError::NotApplicable)) + assert_eq!(result, Ok(SUCCESS_CASE_1_TICKET_HASH)) } #[test] @@ -620,7 +597,7 @@ mod tests { EthereumBlobExporter::::deliver( ticket, ); - assert_eq!(result, Err(SendError::Unroutable)) + assert_eq!(result, Err(SendError::Transport("other transport error"))) } #[test] @@ -653,7 +630,7 @@ mod tests { ] .into(); let mut converter = XcmConverter::new(&message, &network); - let expected_payload = OutboundPayload::NativeTokens(NativeTokens::Unlock { + let expected_payload = Message::NativeTokens(NativeTokensMessage::Unlock { asset: H160(token_address), destination: H160(beneficiary_address), amount: 1000, @@ -688,7 +665,7 @@ mod tests { ] .into(); let mut converter = XcmConverter::new(&message, &network); - let expected_payload = OutboundPayload::NativeTokens(NativeTokens::Unlock { + let expected_payload = Message::NativeTokens(NativeTokensMessage::Unlock { asset: H160(token_address), destination: H160(beneficiary_address), amount: 1000, @@ -723,7 +700,7 @@ mod tests { ] .into(); let mut converter = XcmConverter::new(&message, &network); - let expected_payload = OutboundPayload::NativeTokens(NativeTokens::Unlock { + let expected_payload = Message::NativeTokens(NativeTokensMessage::Unlock { asset: H160(token_address), destination: H160(beneficiary_address), amount: 1000, diff --git a/parachain/primitives/router/src/outbound/payload.rs b/parachain/primitives/router/src/outbound/payload.rs new file mode 100644 index 000000000..f7f945495 --- /dev/null +++ b/parachain/primitives/router/src/outbound/payload.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +use ethabi::{self, Token}; + +use hex_literal::hex; +use snowbridge_core::ContractId; +use sp_core::{RuntimeDebug, H160}; +use sp_std::prelude::*; + +#[derive(Clone, PartialEq, RuntimeDebug)] +pub enum Message { + UpgradeProxy(UpgradeProxyMessage), + NativeTokens(NativeTokensMessage), +} + +/// A message to be sent to `UpgradeProxy.sol`. +#[derive(Clone, PartialEq, RuntimeDebug)] +pub enum UpgradeProxyMessage { + /// Run an upgrade task with elevated privileges + Upgrade { + /// The address of the upgrader contract which implements `UpgradeTask.sol`. + upgrade_task: H160, + }, +} + +/// A message to be sent to `NativeTokens.sol`. +#[derive(Clone, PartialEq, RuntimeDebug)] +pub enum NativeTokensMessage { + /// Release locked collateral for ERC20 token identified by `asset` back to the specified + /// `destination` account + Unlock { + /// ERC20 token address + asset: H160, + /// Account which will receive the tokens + destination: H160, + /// Amount of tokens to release + amount: u128, + }, +} + +impl Message { + /// Encodes the payload so that it can executed on the Ethereum side of the bridge. + /// + /// Returns: + /// * A stable identifier for a receiving gateway contract within Registry.sol. + /// * The payload passed to the Gateway contract's `handle(origin, message)` method. + pub fn encode(&self) -> (ContractId, Vec) { + match self { + Self::UpgradeProxy(UpgradeProxyMessage::Upgrade { upgrade_task }) => { + let inner = ethabi::encode(&[Token::Tuple(vec![Token::Address(*upgrade_task)])]); + let message = ethabi::encode(&[Token::Tuple(vec![ + Token::Uint(0.into()), // Upgrade action = 0 + Token::Bytes(inner), + ])]); + ( + // keccak256("UpgradeProxy") + hex!["44bef07c29162ad04096f5cbe78ca2df62dffe97cea85825f08d13319e13f34a"].into(), + message, + ) + }, + Self::NativeTokens(NativeTokensMessage::Unlock { asset, destination, amount }) => { + let inner = ethabi::encode(&[Token::Tuple(vec![ + Token::Address(*asset), + Token::Address(*destination), + Token::Uint((*amount).into()), + ])]); + let message = ethabi::encode(&[Token::Tuple(vec![ + Token::Uint(0.into()), // Unlock action = 0 + Token::Bytes(inner), + ])]); + ( + // keccak256("NativeTokens") + hex!["1d0761c5c76335b59fce9e8070a90d04470a4d5806c9814b73032db3dbb843ea"].into(), + message, + ) + }, + } + } +} diff --git a/parachain/primitives/testutils/src/lib.rs b/parachain/primitives/testutils/src/lib.rs index 6f14f4117..f8a5b1cc2 100644 --- a/parachain/primitives/testutils/src/lib.rs +++ b/parachain/primitives/testutils/src/lib.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork use ethereum_types::{H128, H256, H512}; use serde::{Deserialize, Deserializer}; use std::{fs::File, path::Path}; @@ -115,8 +117,8 @@ impl BlockWithProofs { .map(|(i, (a, b))| { mapper( [*a, *b], - self.merkle_proofs[i / 2 * self.proof_length as usize.. - (i / 2 + 1) * self.proof_length as usize] + self.merkle_proofs[i / 2 * self.proof_length as usize + ..(i / 2 + 1) * self.proof_length as usize] .to_vec(), ) }) diff --git a/parachain/tools/query-events/Cargo.toml b/parachain/tools/query-events/Cargo.toml deleted file mode 100644 index dfc89b86b..000000000 --- a/parachain/tools/query-events/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "snowbridge-query-events" -version = "0.1.0" -edition = "2021" - -[dependencies] -serde = "1.0.164" -serde_json = "1.0.96" -serde-hex = "0.1.0" -clap = { version = "4.3.3", features = [ "derive" ] } -codec = { version = "3.1.5", package = "parity-scale-codec", features = [ "derive" ], default-features = false } -tokio = { version = "1.28.2", features = ["rt-multi-thread", "macros", "time"] } -futures = "0.3.13" -hex = "0.4.3" -hex-literal = "0.4.1" -subxt = { git = "https://github.com/paritytech/subxt.git", tag = "v0.27.1" } - -[features] -default = ["bridgehub-rococo-local"] -bridgehub-rococo-local = [] diff --git a/parachain/tools/query-events/build.rs b/parachain/tools/query-events/build.rs deleted file mode 100644 index 15540608d..000000000 --- a/parachain/tools/query-events/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=metadata-bridgehub-rococo-local.scale"); -} diff --git a/parachain/tools/query-events/metadata-bridgehub-rococo-local.scale b/parachain/tools/query-events/metadata-bridgehub-rococo-local.scale deleted file mode 100644 index 8d6ebfe8d..000000000 Binary files a/parachain/tools/query-events/metadata-bridgehub-rococo-local.scale and /dev/null differ diff --git a/parachain/tools/query-events/src/main.rs b/parachain/tools/query-events/src/main.rs deleted file mode 100644 index e57a1f23d..000000000 --- a/parachain/tools/query-events/src/main.rs +++ /dev/null @@ -1,64 +0,0 @@ -use codec::Encode; -use serde::Serialize; -use serde_hex::{SerHexSeq, StrictPfx}; -use serde_json; -use std::{ - io::{self, Write}, - str::FromStr, -}; -use subxt::{utils::H256, OnlineClient, PolkadotConfig}; - -#[cfg_attr( - feature = "bridgehub-rococo-local", - subxt::subxt(runtime_metadata_path = "metadata-bridgehub-rococo-local.scale") -)] -pub mod runtime {} - -#[derive(Debug, Serialize)] -struct Items { - items: Vec, -} - -#[derive(Debug, Serialize)] -struct Item { - #[serde(with = "SerHexSeq::")] - hash: Vec, - #[serde(with = "SerHexSeq::")] - data: Vec, -} - -use clap::Parser; - -/// Simple program to greet a person -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -struct Args { - /// Name of the person to greet - #[clap(short, long)] - api: String, - - /// Number of times to greet - #[clap(short, long)] - block: String, -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let args = Args::parse(); - let block_hash = H256::from_str(args.block.trim_start_matches("0x"))?; - let api = OnlineClient::::from_url(args.api).await?; - let events = api.events().at(block_hash.into()).await?; - let mut items: Vec = Vec::new(); - - for ev in events.find::() { - if let Ok(runtime::ethereum_outbound_queue::events::Committed { hash, data }) = ev { - items.push(Item { hash: hash.encode(), data: data.encode() }); - } - } - - let output = &serde_json::to_string(&Items { items })?; - io::stdout().write_all(output.as_bytes())?; - io::stdout().write(b"\n")?; - - Ok(()) -} diff --git a/relayer/contracts/beefy_client.go b/relayer/contracts/beefy_client.go index 7657f9d3d..3dd2e9c8d 100644 --- a/relayer/contracts/beefy_client.go +++ b/relayer/contracts/beefy_client.go @@ -26,7 +26,6 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription - _ = abi.ConvertType ) // BeefyClientCommitment is an auto generated low-level Go binding around an user-defined struct. @@ -177,11 +176,11 @@ func NewBeefyClientFilterer(address common.Address, filterer bind.ContractFilter // bindBeefyClient binds a generic wrapper to an already deployed contract. func bindBeefyClient(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := BeefyClientMetaData.GetAbi() + parsed, err := abi.JSON(strings.NewReader(BeefyClientABI)) if err != nil { return nil, err } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/relayer/contracts/inbound_queue.go b/relayer/contracts/inbound_queue.go index 9778ceae0..eb60ca1b9 100644 --- a/relayer/contracts/inbound_queue.go +++ b/relayer/contracts/inbound_queue.go @@ -26,20 +26,19 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription - _ = abi.ConvertType ) // InboundQueueMessage is an auto generated low-level Go binding around an user-defined struct. type InboundQueueMessage struct { - Origin uint32 - Nonce uint64 - Handler uint16 - Payload []byte + Origin uint32 + Nonce uint64 + Recipient [32]byte + Payload []byte } // InboundQueueMetaData contains all meta data concerning the InboundQueue contract. var InboundQueueMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractIParachainClient\",\"name\":\"_parachainClient\",\"type\":\"address\"},{\"internalType\":\"contractIVault\",\"name\":\"_vault\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_reward\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidHandler\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNonce\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughGas\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasToForward\",\"type\":\"uint256\"}],\"name\":\"GasToForwardUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"id\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"contractIRecipient\",\"name\":\"handler\",\"type\":\"address\"}],\"name\":\"HandlerUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"enumInboundQueue.DispatchResult\",\"name\":\"result\",\"type\":\"uint8\"}],\"name\":\"MessageDispatched\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"parachainClient\",\"type\":\"address\"}],\"name\":\"ParachainClientUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"name\":\"RewardUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"vault\",\"type\":\"address\"}],\"name\":\"VaultUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GAS_BUFFER\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasToForward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"handlerID\",\"type\":\"uint16\"}],\"name\":\"handlers\",\"outputs\":[{\"internalType\":\"contractIRecipient\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint32\"}],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"parachainClient\",\"outputs\":[{\"internalType\":\"contractIParachainClient\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"handler\",\"type\":\"uint16\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"internalType\":\"structInboundQueue.Message\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"headerProof\",\"type\":\"bytes\"}],\"name\":\"submit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_gasToForward\",\"type\":\"uint256\"}],\"name\":\"updateGasToForward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"id\",\"type\":\"uint16\"},{\"internalType\":\"contractIRecipient\",\"name\":\"handler\",\"type\":\"address\"}],\"name\":\"updateHandler\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIParachainClient\",\"name\":\"_parachainClient\",\"type\":\"address\"}],\"name\":\"updateParachainClient\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_reward\",\"type\":\"uint256\"}],\"name\":\"updateReward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIVault\",\"name\":\"_vault\",\"type\":\"address\"}],\"name\":\"updateVault\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vault\",\"outputs\":[{\"internalType\":\"contractIVault\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"contractRegistry\",\"name\":\"registry\",\"type\":\"address\"},{\"internalType\":\"contractIParachainClient\",\"name\":\"_parachainClient\",\"type\":\"address\"},{\"internalType\":\"contractVault\",\"name\":\"_vault\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_reward\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidNonce\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LookupError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotEnoughGas\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasToForward\",\"type\":\"uint256\"}],\"name\":\"GasToForwardUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"id\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"contractIRecipient\",\"name\":\"handler\",\"type\":\"address\"}],\"name\":\"HandlerUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"}],\"name\":\"InvalidRecipient\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"enumInboundQueue.DispatchResult\",\"name\":\"result\",\"type\":\"uint8\"}],\"name\":\"MessageDispatched\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"parachainClient\",\"type\":\"address\"}],\"name\":\"ParachainClientUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"}],\"name\":\"RewardUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"vault\",\"type\":\"address\"}],\"name\":\"VaultUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GAS_BUFFER\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasToForward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint32\"}],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"parachainClient\",\"outputs\":[{\"internalType\":\"contractIParachainClient\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"registry\",\"outputs\":[{\"internalType\":\"contractRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"reward\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"ParaID\",\"name\":\"origin\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"internalType\":\"structInboundQueue.Message\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes32[]\",\"name\":\"leafProof\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"headerProof\",\"type\":\"bytes\"}],\"name\":\"submit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_gasToForward\",\"type\":\"uint256\"}],\"name\":\"updateGasToForward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_reward\",\"type\":\"uint256\"}],\"name\":\"updateReward\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vault\",\"outputs\":[{\"internalType\":\"contractVault\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // InboundQueueABI is the input ABI used to generate the binding from. @@ -143,11 +142,11 @@ func NewInboundQueueFilterer(address common.Address, filterer bind.ContractFilte // bindInboundQueue binds a generic wrapper to an already deployed contract. func bindInboundQueue(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := InboundQueueMetaData.GetAbi() + parsed, err := abi.JSON(strings.NewReader(InboundQueueABI)) if err != nil { return nil, err } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and @@ -343,37 +342,6 @@ func (_InboundQueue *InboundQueueCallerSession) GetRoleAdmin(role [32]byte) ([32 return _InboundQueue.Contract.GetRoleAdmin(&_InboundQueue.CallOpts, role) } -// Handlers is a free data retrieval call binding the contract method 0x0d20fb87. -// -// Solidity: function handlers(uint16 handlerID) view returns(address) -func (_InboundQueue *InboundQueueCaller) Handlers(opts *bind.CallOpts, handlerID uint16) (common.Address, error) { - var out []interface{} - err := _InboundQueue.contract.Call(opts, &out, "handlers", handlerID) - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// Handlers is a free data retrieval call binding the contract method 0x0d20fb87. -// -// Solidity: function handlers(uint16 handlerID) view returns(address) -func (_InboundQueue *InboundQueueSession) Handlers(handlerID uint16) (common.Address, error) { - return _InboundQueue.Contract.Handlers(&_InboundQueue.CallOpts, handlerID) -} - -// Handlers is a free data retrieval call binding the contract method 0x0d20fb87. -// -// Solidity: function handlers(uint16 handlerID) view returns(address) -func (_InboundQueue *InboundQueueCallerSession) Handlers(handlerID uint16) (common.Address, error) { - return _InboundQueue.Contract.Handlers(&_InboundQueue.CallOpts, handlerID) -} - // HasRole is a free data retrieval call binding the contract method 0x91d14854. // // Solidity: function hasRole(bytes32 role, address account) view returns(bool) @@ -467,6 +435,37 @@ func (_InboundQueue *InboundQueueCallerSession) ParachainClient() (common.Addres return _InboundQueue.Contract.ParachainClient(&_InboundQueue.CallOpts) } +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_InboundQueue *InboundQueueCaller) Registry(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _InboundQueue.contract.Call(opts, &out, "registry") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_InboundQueue *InboundQueueSession) Registry() (common.Address, error) { + return _InboundQueue.Contract.Registry(&_InboundQueue.CallOpts) +} + +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_InboundQueue *InboundQueueCallerSession) Registry() (common.Address, error) { + return _InboundQueue.Contract.Registry(&_InboundQueue.CallOpts) +} + // Reward is a free data retrieval call binding the contract method 0x228cb733. // // Solidity: function reward() view returns(uint256) @@ -623,23 +622,23 @@ func (_InboundQueue *InboundQueueTransactorSession) RevokeRole(role [32]byte, ac return _InboundQueue.Contract.RevokeRole(&_InboundQueue.TransactOpts, role, account) } -// Submit is a paid mutator transaction binding the contract method 0x052c6867. +// Submit is a paid mutator transaction binding the contract method 0x90d7fbe9. // -// Solidity: function submit((uint32,uint64,uint16,bytes) message, bytes32[] leafProof, bytes headerProof) returns() +// Solidity: function submit((uint32,uint64,bytes32,bytes) message, bytes32[] leafProof, bytes headerProof) returns() func (_InboundQueue *InboundQueueTransactor) Submit(opts *bind.TransactOpts, message InboundQueueMessage, leafProof [][32]byte, headerProof []byte) (*types.Transaction, error) { return _InboundQueue.contract.Transact(opts, "submit", message, leafProof, headerProof) } -// Submit is a paid mutator transaction binding the contract method 0x052c6867. +// Submit is a paid mutator transaction binding the contract method 0x90d7fbe9. // -// Solidity: function submit((uint32,uint64,uint16,bytes) message, bytes32[] leafProof, bytes headerProof) returns() +// Solidity: function submit((uint32,uint64,bytes32,bytes) message, bytes32[] leafProof, bytes headerProof) returns() func (_InboundQueue *InboundQueueSession) Submit(message InboundQueueMessage, leafProof [][32]byte, headerProof []byte) (*types.Transaction, error) { return _InboundQueue.Contract.Submit(&_InboundQueue.TransactOpts, message, leafProof, headerProof) } -// Submit is a paid mutator transaction binding the contract method 0x052c6867. +// Submit is a paid mutator transaction binding the contract method 0x90d7fbe9. // -// Solidity: function submit((uint32,uint64,uint16,bytes) message, bytes32[] leafProof, bytes headerProof) returns() +// Solidity: function submit((uint32,uint64,bytes32,bytes) message, bytes32[] leafProof, bytes headerProof) returns() func (_InboundQueue *InboundQueueTransactorSession) Submit(message InboundQueueMessage, leafProof [][32]byte, headerProof []byte) (*types.Transaction, error) { return _InboundQueue.Contract.Submit(&_InboundQueue.TransactOpts, message, leafProof, headerProof) } @@ -665,48 +664,6 @@ func (_InboundQueue *InboundQueueTransactorSession) UpdateGasToForward(_gasToFor return _InboundQueue.Contract.UpdateGasToForward(&_InboundQueue.TransactOpts, _gasToForward) } -// UpdateHandler is a paid mutator transaction binding the contract method 0x419e19cf. -// -// Solidity: function updateHandler(uint16 id, address handler) returns() -func (_InboundQueue *InboundQueueTransactor) UpdateHandler(opts *bind.TransactOpts, id uint16, handler common.Address) (*types.Transaction, error) { - return _InboundQueue.contract.Transact(opts, "updateHandler", id, handler) -} - -// UpdateHandler is a paid mutator transaction binding the contract method 0x419e19cf. -// -// Solidity: function updateHandler(uint16 id, address handler) returns() -func (_InboundQueue *InboundQueueSession) UpdateHandler(id uint16, handler common.Address) (*types.Transaction, error) { - return _InboundQueue.Contract.UpdateHandler(&_InboundQueue.TransactOpts, id, handler) -} - -// UpdateHandler is a paid mutator transaction binding the contract method 0x419e19cf. -// -// Solidity: function updateHandler(uint16 id, address handler) returns() -func (_InboundQueue *InboundQueueTransactorSession) UpdateHandler(id uint16, handler common.Address) (*types.Transaction, error) { - return _InboundQueue.Contract.UpdateHandler(&_InboundQueue.TransactOpts, id, handler) -} - -// UpdateParachainClient is a paid mutator transaction binding the contract method 0x33b9d6ee. -// -// Solidity: function updateParachainClient(address _parachainClient) returns() -func (_InboundQueue *InboundQueueTransactor) UpdateParachainClient(opts *bind.TransactOpts, _parachainClient common.Address) (*types.Transaction, error) { - return _InboundQueue.contract.Transact(opts, "updateParachainClient", _parachainClient) -} - -// UpdateParachainClient is a paid mutator transaction binding the contract method 0x33b9d6ee. -// -// Solidity: function updateParachainClient(address _parachainClient) returns() -func (_InboundQueue *InboundQueueSession) UpdateParachainClient(_parachainClient common.Address) (*types.Transaction, error) { - return _InboundQueue.Contract.UpdateParachainClient(&_InboundQueue.TransactOpts, _parachainClient) -} - -// UpdateParachainClient is a paid mutator transaction binding the contract method 0x33b9d6ee. -// -// Solidity: function updateParachainClient(address _parachainClient) returns() -func (_InboundQueue *InboundQueueTransactorSession) UpdateParachainClient(_parachainClient common.Address) (*types.Transaction, error) { - return _InboundQueue.Contract.UpdateParachainClient(&_InboundQueue.TransactOpts, _parachainClient) -} - // UpdateReward is a paid mutator transaction binding the contract method 0x425c8abd. // // Solidity: function updateReward(uint256 _reward) returns() @@ -728,27 +685,6 @@ func (_InboundQueue *InboundQueueTransactorSession) UpdateReward(_reward *big.In return _InboundQueue.Contract.UpdateReward(&_InboundQueue.TransactOpts, _reward) } -// UpdateVault is a paid mutator transaction binding the contract method 0xe7563f3f. -// -// Solidity: function updateVault(address _vault) returns() -func (_InboundQueue *InboundQueueTransactor) UpdateVault(opts *bind.TransactOpts, _vault common.Address) (*types.Transaction, error) { - return _InboundQueue.contract.Transact(opts, "updateVault", _vault) -} - -// UpdateVault is a paid mutator transaction binding the contract method 0xe7563f3f. -// -// Solidity: function updateVault(address _vault) returns() -func (_InboundQueue *InboundQueueSession) UpdateVault(_vault common.Address) (*types.Transaction, error) { - return _InboundQueue.Contract.UpdateVault(&_InboundQueue.TransactOpts, _vault) -} - -// UpdateVault is a paid mutator transaction binding the contract method 0xe7563f3f. -// -// Solidity: function updateVault(address _vault) returns() -func (_InboundQueue *InboundQueueTransactorSession) UpdateVault(_vault common.Address) (*types.Transaction, error) { - return _InboundQueue.Contract.UpdateVault(&_InboundQueue.TransactOpts, _vault) -} - // InboundQueueGasToForwardUpdatedIterator is returned from FilterGasToForwardUpdated and is used to iterate over the raw logs and unpacked data for GasToForwardUpdated events raised by the InboundQueue contract. type InboundQueueGasToForwardUpdatedIterator struct { Event *InboundQueueGasToForwardUpdated // Event containing the contract specifics and raw log @@ -1018,6 +954,140 @@ func (_InboundQueue *InboundQueueFilterer) ParseHandlerUpdated(log types.Log) (* return event, nil } +// InboundQueueInvalidRecipientIterator is returned from FilterInvalidRecipient and is used to iterate over the raw logs and unpacked data for InvalidRecipient events raised by the InboundQueue contract. +type InboundQueueInvalidRecipientIterator struct { + Event *InboundQueueInvalidRecipient // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *InboundQueueInvalidRecipientIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(InboundQueueInvalidRecipient) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(InboundQueueInvalidRecipient) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *InboundQueueInvalidRecipientIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *InboundQueueInvalidRecipientIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// InboundQueueInvalidRecipient represents a InvalidRecipient event raised by the InboundQueue contract. +type InboundQueueInvalidRecipient struct { + Recipient [32]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInvalidRecipient is a free log retrieval operation binding the contract event 0xbf0b3f6242271405146290163e141ff674b9d85a2a16815a195bb05e3d57c835. +// +// Solidity: event InvalidRecipient(bytes32 recipient) +func (_InboundQueue *InboundQueueFilterer) FilterInvalidRecipient(opts *bind.FilterOpts) (*InboundQueueInvalidRecipientIterator, error) { + + logs, sub, err := _InboundQueue.contract.FilterLogs(opts, "InvalidRecipient") + if err != nil { + return nil, err + } + return &InboundQueueInvalidRecipientIterator{contract: _InboundQueue.contract, event: "InvalidRecipient", logs: logs, sub: sub}, nil +} + +// WatchInvalidRecipient is a free log subscription operation binding the contract event 0xbf0b3f6242271405146290163e141ff674b9d85a2a16815a195bb05e3d57c835. +// +// Solidity: event InvalidRecipient(bytes32 recipient) +func (_InboundQueue *InboundQueueFilterer) WatchInvalidRecipient(opts *bind.WatchOpts, sink chan<- *InboundQueueInvalidRecipient) (event.Subscription, error) { + + logs, sub, err := _InboundQueue.contract.WatchLogs(opts, "InvalidRecipient") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(InboundQueueInvalidRecipient) + if err := _InboundQueue.contract.UnpackLog(event, "InvalidRecipient", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInvalidRecipient is a log parse operation binding the contract event 0xbf0b3f6242271405146290163e141ff674b9d85a2a16815a195bb05e3d57c835. +// +// Solidity: event InvalidRecipient(bytes32 recipient) +func (_InboundQueue *InboundQueueFilterer) ParseInvalidRecipient(log types.Log) (*InboundQueueInvalidRecipient, error) { + event := new(InboundQueueInvalidRecipient) + if err := _InboundQueue.contract.UnpackLog(event, "InvalidRecipient", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // InboundQueueMessageDispatchedIterator is returned from FilterMessageDispatched and is used to iterate over the raw logs and unpacked data for MessageDispatched events raised by the InboundQueue contract. type InboundQueueMessageDispatchedIterator struct { Event *InboundQueueMessageDispatched // Event containing the contract specifics and raw log @@ -1095,19 +1165,10 @@ type InboundQueueMessageDispatched struct { // FilterMessageDispatched is a free log retrieval operation binding the contract event 0x3daaaf6b5c13966eb060b53daff310d82d35bdd2e539be4dc92dfe1310ee170d. // -// Solidity: event MessageDispatched(uint32 indexed origin, uint64 indexed nonce, uint8 result) -func (_InboundQueue *InboundQueueFilterer) FilterMessageDispatched(opts *bind.FilterOpts, origin []uint32, nonce []uint64) (*InboundQueueMessageDispatchedIterator, error) { +// Solidity: event MessageDispatched(uint32 origin, uint64 nonce, uint8 result) +func (_InboundQueue *InboundQueueFilterer) FilterMessageDispatched(opts *bind.FilterOpts) (*InboundQueueMessageDispatchedIterator, error) { - var originRule []interface{} - for _, originItem := range origin { - originRule = append(originRule, originItem) - } - var nonceRule []interface{} - for _, nonceItem := range nonce { - nonceRule = append(nonceRule, nonceItem) - } - - logs, sub, err := _InboundQueue.contract.FilterLogs(opts, "MessageDispatched", originRule, nonceRule) + logs, sub, err := _InboundQueue.contract.FilterLogs(opts, "MessageDispatched") if err != nil { return nil, err } @@ -1116,19 +1177,10 @@ func (_InboundQueue *InboundQueueFilterer) FilterMessageDispatched(opts *bind.Fi // WatchMessageDispatched is a free log subscription operation binding the contract event 0x3daaaf6b5c13966eb060b53daff310d82d35bdd2e539be4dc92dfe1310ee170d. // -// Solidity: event MessageDispatched(uint32 indexed origin, uint64 indexed nonce, uint8 result) -func (_InboundQueue *InboundQueueFilterer) WatchMessageDispatched(opts *bind.WatchOpts, sink chan<- *InboundQueueMessageDispatched, origin []uint32, nonce []uint64) (event.Subscription, error) { - - var originRule []interface{} - for _, originItem := range origin { - originRule = append(originRule, originItem) - } - var nonceRule []interface{} - for _, nonceItem := range nonce { - nonceRule = append(nonceRule, nonceItem) - } +// Solidity: event MessageDispatched(uint32 origin, uint64 nonce, uint8 result) +func (_InboundQueue *InboundQueueFilterer) WatchMessageDispatched(opts *bind.WatchOpts, sink chan<- *InboundQueueMessageDispatched) (event.Subscription, error) { - logs, sub, err := _InboundQueue.contract.WatchLogs(opts, "MessageDispatched", originRule, nonceRule) + logs, sub, err := _InboundQueue.contract.WatchLogs(opts, "MessageDispatched") if err != nil { return nil, err } @@ -1162,7 +1214,7 @@ func (_InboundQueue *InboundQueueFilterer) WatchMessageDispatched(opts *bind.Wat // ParseMessageDispatched is a log parse operation binding the contract event 0x3daaaf6b5c13966eb060b53daff310d82d35bdd2e539be4dc92dfe1310ee170d. // -// Solidity: event MessageDispatched(uint32 indexed origin, uint64 indexed nonce, uint8 result) +// Solidity: event MessageDispatched(uint32 origin, uint64 nonce, uint8 result) func (_InboundQueue *InboundQueueFilterer) ParseMessageDispatched(log types.Log) (*InboundQueueMessageDispatched, error) { event := new(InboundQueueMessageDispatched) if err := _InboundQueue.contract.UnpackLog(event, "MessageDispatched", log); err != nil { diff --git a/relayer/contracts/opaque_proof.go b/relayer/contracts/opaque_proof.go index 5eea3b905..f118dbd2c 100644 --- a/relayer/contracts/opaque_proof.go +++ b/relayer/contracts/opaque_proof.go @@ -26,7 +26,6 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription - _ = abi.ConvertType ) // ParachainClientDigestItem is an auto generated low-level Go binding around an user-defined struct. @@ -177,11 +176,11 @@ func NewOpaqueProofFilterer(address common.Address, filterer bind.ContractFilter // bindOpaqueProof binds a generic wrapper to an already deployed contract. func bindOpaqueProof(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := OpaqueProofMetaData.GetAbi() + parsed, err := abi.JSON(strings.NewReader(OpaqueProofABI)) if err != nil { return nil, err } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and diff --git a/relayer/contracts/outbound_queue.go b/relayer/contracts/outbound_queue.go index a8f18479e..d8898b12c 100644 --- a/relayer/contracts/outbound_queue.go +++ b/relayer/contracts/outbound_queue.go @@ -26,12 +26,11 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription - _ = abi.ConvertType ) // OutboundQueueMetaData contains all meta data concerning the OutboundQueue contract. var OutboundQueueMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractIVault\",\"name\":\"_vault\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_fee\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"FeePaymentToLow\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"FeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"dest\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"Message\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SUBMIT_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"dest\",\"type\":\"uint32\"}],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"dest\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"submit\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newFee\",\"type\":\"uint256\"}],\"name\":\"updateFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vault\",\"outputs\":[{\"internalType\":\"contractIVault\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"contractRegistry\",\"name\":\"registry\",\"type\":\"address\"},{\"internalType\":\"contractVault\",\"name\":\"_vault\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_fee\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"FeePaymentToLow\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LookupError\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"}],\"name\":\"FeeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"ParaID\",\"name\":\"dest\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"Message\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SUBMIT_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"dest\",\"type\":\"uint32\"}],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"registry\",\"outputs\":[{\"internalType\":\"contractRegistry\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"ParaID\",\"name\":\"dest\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"submit\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newFee\",\"type\":\"uint256\"}],\"name\":\"updateFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"vault\",\"outputs\":[{\"internalType\":\"contractVault\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // OutboundQueueABI is the input ABI used to generate the binding from. @@ -135,11 +134,11 @@ func NewOutboundQueueFilterer(address common.Address, filterer bind.ContractFilt // bindOutboundQueue binds a generic wrapper to an already deployed contract. func bindOutboundQueue(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := OutboundQueueMetaData.GetAbi() + parsed, err := abi.JSON(strings.NewReader(OutboundQueueABI)) if err != nil { return nil, err } - return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and @@ -397,6 +396,37 @@ func (_OutboundQueue *OutboundQueueCallerSession) Nonce(dest uint32) (uint64, er return _OutboundQueue.Contract.Nonce(&_OutboundQueue.CallOpts, dest) } +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_OutboundQueue *OutboundQueueCaller) Registry(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _OutboundQueue.contract.Call(opts, &out, "registry") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_OutboundQueue *OutboundQueueSession) Registry() (common.Address, error) { + return _OutboundQueue.Contract.Registry(&_OutboundQueue.CallOpts) +} + +// Registry is a free data retrieval call binding the contract method 0x7b103999. +// +// Solidity: function registry() view returns(address) +func (_OutboundQueue *OutboundQueueCallerSession) Registry() (common.Address, error) { + return _OutboundQueue.Contract.Registry(&_OutboundQueue.CallOpts) +} + // SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. // // Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) diff --git a/relayer/crypto/merkle/merkle.go b/relayer/crypto/merkle/merkle.go index 4f70a352d..af0040e21 100644 --- a/relayer/crypto/merkle/merkle.go +++ b/relayer/crypto/merkle/merkle.go @@ -3,7 +3,6 @@ package merkle import ( - "bytes" "encoding/base64" "encoding/hex" "encoding/json" @@ -14,6 +13,14 @@ import ( "github.com/snowfork/snowbridge/relayer/crypto/keccak" ) +// Position constants are used in merkle path nodes to denote +// whether the node was a left child or right child. This allows +// hash concatenation can be performed correctly. +const ( + PositionLeft = "left" + PositionRight = "right" +) + func depth(n int) int { return int(math.Ceil(math.Log2(float64(n)))) } @@ -26,7 +33,8 @@ type Hasher interface { // Node is used to represent the steps of a merkle path. // This structure is not used within the Tree structure. type Node struct { - Hash []byte `json:"hash"` + Hash []byte `json:"hash"` + Position string `json:"position"` } // The Hash value is encoded into a base64 string @@ -141,9 +149,9 @@ func (t *Tree) MerklePath(preLeaf []byte) []*Node { // if i is odd we want to get the left sibling if index%2 != 0 { - path = append(path, &Node{Hash: level[index-1]}) + path = append(path, &Node{Hash: level[index-1], Position: PositionLeft}) } else { - path = append(path, &Node{Hash: level[index+1]}) + path = append(path, &Node{Hash: level[index+1], Position: PositionRight}) } index = nextIndex @@ -184,11 +192,8 @@ func (t *Tree) Hash(preLeaves [][]byte, h Hasher) error { for j := 0; j < len(level)-1; j += 2 { left := level[j] right := level[j+1] - if bytes.Compare(left, right) < 0 { - nextLevel[k] = h.Hash(append(left, right...)) - } else { - nextLevel[k] = h.Hash(append(right, left...)) - } + + nextLevel[k] = h.Hash(append(left, right...)) k += 1 } @@ -215,7 +220,7 @@ func Prove(preLeaf, root []byte, path []*Node, h Hasher) bool { hash := leaf for _, node := range path { - if bytes.Compare(node.Hash, hash) < 0 { + if node.Position == PositionLeft { hash = append(node.Hash, hash...) } else { hash = append(hash, node.Hash...) diff --git a/relayer/relays/parachain/beefy-listener.go b/relayer/relays/parachain/beefy-listener.go index 37d57156a..cd8be8414 100644 --- a/relayer/relays/parachain/beefy-listener.go +++ b/relayer/relays/parachain/beefy-listener.go @@ -72,12 +72,11 @@ func (li *BeefyListener) Start(ctx context.Context, eg *errgroup.Group) error { li.paraID = paraID li.scanner = &Scanner{ - config: li.config, - ethConn: li.ethereumConn, - relayConn: li.relaychainConn, - paraConn: li.parachainConnection, - eventQueryClient: NewQueryClient(), - paraID: paraID, + config: li.config, + ethConn: li.ethereumConn, + relayConn: li.relaychainConn, + paraConn: li.parachainConnection, + paraID: paraID, } eg.Go(func() error { @@ -158,7 +157,7 @@ func (li *BeefyListener) doScan(ctx context.Context, beefyBlockNumber uint64) er for _, task := range tasks { // do final proof generation right before sending. The proof needs to be fresh. - task.ProofOutput, err = li.generateProof(ctx, task.ProofInput) + task.ProofOutput, err = li.generateProof(ctx, task.ProofInput, task.Header) if err != nil { return err } @@ -222,7 +221,7 @@ func (li *BeefyListener) fetchLatestBeefyBlock(ctx context.Context) (uint64, typ // Generates a proof for an MMR leaf, and then generates a merkle proof for our parachain header, which should be verifiable against the // parachains root in the mmr leaf. -func (li *BeefyListener) generateProof(ctx context.Context, input *ProofInput) (*ProofOutput, error) { +func (li *BeefyListener) generateProof(ctx context.Context, input *ProofInput, header *types.Header) (*ProofOutput, error) { latestBeefyBlockNumber, latestBeefyBlockHash, err := li.fetchLatestBeefyBlock(ctx) if err != nil { return nil, fmt.Errorf("fetch latest beefy block: %w", err) @@ -235,7 +234,7 @@ func (li *BeefyListener) generateProof(ctx context.Context, input *ProofInput) ( // Generate the MMR proof for the polkadot block. mmrProof, err := li.relaychainConn.GenerateProofForBlock( - input.RelayBlockNumber, + input.RelayBlockNumber+1, latestBeefyBlockHash, li.config.BeefyActivationBlock, ) @@ -281,6 +280,7 @@ func (li *BeefyListener) generateProof(ctx context.Context, input *ProofInput) ( output := ProofOutput{ MMRProof: simplifiedProof, MMRRootHash: mmrRootHash, + Header: *header, MerkleProofData: merkleProofData, } diff --git a/relayer/relays/parachain/digest_item.go b/relayer/relays/parachain/digest_item.go index 8c211ca0c..425493d20 100644 --- a/relayer/relays/parachain/digest_item.go +++ b/relayer/relays/parachain/digest_item.go @@ -1,42 +1,9 @@ package parachain import ( - "fmt" - - "github.com/snowfork/go-substrate-rpc-client/v4/scale" "github.com/snowfork/go-substrate-rpc-client/v4/types" ) -type AuxiliaryDigestItem struct { - IsCommitment bool - AsCommitment AuxiliaryDigestItemCommitment -} - -type AuxiliaryDigestItemCommitment struct { - Hash types.H256 -} - -func (a *AuxiliaryDigestItem) Decode(decoder scale.Decoder) error { - tag, err := decoder.ReadOneByte() - if err != nil { - return err - } - - switch tag { - case 0: - a.IsCommitment = true - err = decoder.Decode(&a.AsCommitment) - default: - return fmt.Errorf("No such variant for DigestItem") - } - - if err != nil { - return err - } - - return nil -} - func ExtractCommitmentFromDigest(digest types.Digest) (*types.H256, error) { for _, digestItem := range digest { if digestItem.IsOther { diff --git a/relayer/relays/parachain/ethereum-writer.go b/relayer/relays/parachain/ethereum-writer.go index ae86442bb..6fe39daba 100644 --- a/relayer/relays/parachain/ethereum-writer.go +++ b/relayer/relays/parachain/ethereum-writer.go @@ -7,6 +7,7 @@ import ( "fmt" "math/big" "strings" + "time" "golang.org/x/sync/errgroup" @@ -21,6 +22,8 @@ import ( gsrpcTypes "github.com/snowfork/go-substrate-rpc-client/v4/types" + goEthereum "github.com/ethereum/go-ethereum" + log "github.com/sirupsen/logrus" ) @@ -31,6 +34,7 @@ type EthereumWriter struct { tasks <-chan *Task abiPacker abi.Arguments abiBasicUnpacker abi.Arguments + inboundQueueABI abi.ABI } func NewEthereumWriter( @@ -64,6 +68,7 @@ func (wr *EthereumWriter) Start(ctx context.Context, eg *errgroup.Group) error { if err != nil { return err } + wr.inboundQueueABI = inboundQueueABI wr.abiBasicUnpacker = abi.Arguments{inboundQueueABI.Methods["submit"].Inputs[0]} eg.Go(func() error { @@ -121,7 +126,7 @@ func (wr *EthereumWriter) writeMessagesLoop(ctx context.Context) error { if !ok { return nil } - err := wr.WriteChannels(options, task) + err := wr.WriteChannels(ctx, options, task) if err != nil { return fmt.Errorf("write message: %w", err) } @@ -130,11 +135,12 @@ func (wr *EthereumWriter) writeMessagesLoop(ctx context.Context) error { } func (wr *EthereumWriter) WriteChannels( + ctx context.Context, options *bind.TransactOpts, task *Task, ) error { for _, proof := range *task.BasicChannelProofs { - err := wr.WriteChannel(options, &proof, task.ProofOutput) + err := wr.WriteChannel(ctx, options, &proof, task.ProofOutput) if err != nil { return fmt.Errorf("write basic channel: %w", err) } @@ -145,6 +151,7 @@ func (wr *EthereumWriter) WriteChannels( // Submit sends a SCALE-encoded message to an application deployed on the Ethereum network func (wr *EthereumWriter) WriteChannel( + ctx context.Context, options *bind.TransactOpts, commitmentProof *MessageProof, proof *ProofOutput, @@ -195,7 +202,6 @@ func (wr *EthereumWriter) WriteChannel( } hasher := &keccak.Keccak256{} - mmrLeafEncoded, err := gsrpcTypes.EncodeToBytes(proof.MMRProof.Leaf) if err != nil { return fmt.Errorf("encode MMRLeaf: %w", err) @@ -213,6 +219,27 @@ func (wr *EthereumWriter) WriteChannel( }). Info("Sent transaction InboundQueue.submit") + receipt, err := wr.waitForTransaction(ctx, tx, 1) + + if receipt.Status != 1 { + return fmt.Errorf("transaction failed: %s", receipt.TxHash.Hex()) + } + + for _, ev := range receipt.Logs { + if ev.Topics[0] == wr.inboundQueueABI.Events["MessageDispatched"].ID { + var holder contracts.InboundQueueMessageDispatched + err = wr.inboundQueueABI.UnpackIntoInterface(&holder, "MessageDispatched", ev.Data) + if err != nil { + return fmt.Errorf("unpack event log: %w", err) + } + log.WithFields(log.Fields{ + "origin": holder.Origin, + "nonce": holder.Nonce, + "result": holder.Result, + }).Info("Message dispatched") + } + } + return nil } @@ -236,7 +263,7 @@ func convertHeader(header gsrpcTypes.Header) (*contracts.ParachainClientParachai }) case di.IsConsensus: consensusEngineID := make([]byte, 4) - binary.LittleEndian.PutUint32(consensusEngineID, uint32(di.AsPreRuntime.ConsensusEngineID)) + binary.LittleEndian.PutUint32(consensusEngineID, uint32(di.AsConsensus.ConsensusEngineID)) digestItems = append(digestItems, contracts.ParachainClientDigestItem{ Kind: big.NewInt(4), ConsensusEngineID: *(*[4]byte)(consensusEngineID), @@ -244,7 +271,7 @@ func convertHeader(header gsrpcTypes.Header) (*contracts.ParachainClientParachai }) case di.IsSeal: consensusEngineID := make([]byte, 4) - binary.LittleEndian.PutUint32(consensusEngineID, uint32(di.AsPreRuntime.ConsensusEngineID)) + binary.LittleEndian.PutUint32(consensusEngineID, uint32(di.AsSeal.ConsensusEngineID)) digestItems = append(digestItems, contracts.ParachainClientDigestItem{ Kind: big.NewInt(5), ConsensusEngineID: *(*[4]byte)(consensusEngineID), @@ -263,3 +290,42 @@ func convertHeader(header gsrpcTypes.Header) (*contracts.ParachainClientParachai DigestItems: digestItems, }, nil } + +func (wr *EthereumWriter) waitForTransaction(ctx context.Context, tx *types.Transaction, confirmations uint64) (*types.Receipt, error) { + for { + receipt, err := wr.pollTransaction(ctx, tx, confirmations) + if err != nil { + return nil, err + } + + if receipt != nil { + return receipt, nil + } + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(500 * time.Millisecond): + } + } +} + +func (wr *EthereumWriter) pollTransaction(ctx context.Context, tx *types.Transaction, confirmations uint64) (*types.Receipt, error) { + receipt, err := wr.conn.Client().TransactionReceipt(ctx, tx.Hash()) + if err != nil { + if errors.Is(err, goEthereum.NotFound) { + return nil, nil + } + } + + latestHeader, err := wr.conn.Client().HeaderByNumber(ctx, nil) + if err != nil { + return nil, err + } + + if latestHeader.Number.Uint64()-receipt.BlockNumber.Uint64() >= confirmations { + return receipt, nil + } + + return nil, nil +} diff --git a/relayer/relays/parachain/logger.go b/relayer/relays/parachain/logger.go index eebddfcbe..70ce6022c 100644 --- a/relayer/relays/parachain/logger.go +++ b/relayer/relays/parachain/logger.go @@ -22,9 +22,10 @@ func (wr *EthereumWriter) logFieldsForSubmission( params := log.Fields{ "message": log.Fields{ - "origin": message.Origin, - "nonce": message.Nonce, - "payload": message.Payload, + "origin": message.Origin, + "nonce": message.Nonce, + "recipient": Hex(message.Recipient[:]), + "payload": Hex(message.Payload), }, "leafProof": leafProofHexes, "proof": Hex(proof), diff --git a/relayer/relays/parachain/query_events.go b/relayer/relays/parachain/query_events.go deleted file mode 100644 index 49a4a6c3a..000000000 --- a/relayer/relays/parachain/query_events.go +++ /dev/null @@ -1,94 +0,0 @@ -package parachain - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "os/exec" - - log "github.com/sirupsen/logrus" - "github.com/snowfork/go-substrate-rpc-client/v4/types" -) - -type inputItems struct { - Items []inputItem `json:"items"` -} - -type inputItem struct { - Hash string `json:"hash"` - Data string `json:"data"` -} - -type BasicChannelEvent struct { - Hash types.H256 - Messages []OutboundQueueMessage -} - -type QueryClient struct { - NameArgs func(api string, blockHash string) (string, []string) -} - -func NewQueryClient() QueryClient { - return QueryClient{ - NameArgs: func(api string, blockHash string) (string, []string) { - return "snowbridge-query-events", []string{"--api", api, "--block", blockHash} - }, - } -} - -func (q *QueryClient) QueryEvent(ctx context.Context, api string, blockHash types.Hash) (*BasicChannelEvent, error) { - name, args := q.NameArgs(api, blockHash.Hex()) - cmd := exec.CommandContext(ctx, name, args...) - - var outBuf, errBuf bytes.Buffer - cmd.Stdout = &outBuf - cmd.Stderr = &errBuf - - err := cmd.Run() - if err != nil { - log.WithFields(log.Fields{ - "name": name, - "args": fmt.Sprintf("%v", args), - "stdErr": errBuf.String(), - "stdOut": outBuf.String(), - }).Error("Failed to query event.") - return nil, err - } - - var items inputItems - err = json.Unmarshal(outBuf.Bytes(), &items) - if err != nil { - return nil, err - } - - log.WithFields(log.Fields{ - "inputItems": items, - }).Debug("parachain.QueryEvents") - - var event *BasicChannelEvent - - for _, item := range items.Items { - - var hash types.H256 - err = types.DecodeFromHexString(item.Hash, &hash) - if err != nil { - return nil, err - } - - var messages []OutboundQueueMessage - err = types.DecodeFromHexString(item.Data, &messages) - if err != nil { - return nil, err - } - event = &BasicChannelEvent{ - Hash: hash, - Messages: messages, - } - } - log.WithFields(log.Fields{ - "event": event, - }).Debug("parachain.QueryEvents") - - return event, nil -} diff --git a/relayer/relays/parachain/query_events_test.go b/relayer/relays/parachain/query_events_test.go deleted file mode 100644 index be4f2d675..000000000 --- a/relayer/relays/parachain/query_events_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package parachain_test - -import ( - "context" - "io/ioutil" - "os" - "testing" - - "github.com/snowfork/go-substrate-rpc-client/v4/types" - "github.com/snowfork/snowbridge/relayer/relays/parachain" - "github.com/stretchr/testify/assert" -) - -// TODO: update mock data -var mock = `{ - "items": [ - { - "id": 0, - "hash": "0xb957c7eacb53bb42cae6309174fdf564db02deee95eb5861a2b4b890780fbfc8", - "data": "0x0400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d04040054d6643762e46036b3448659791adaf55422554191017ed9db59d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d00000000000000000000000089b4ab1ef20763630df9743acf155865600daff20000000000000000000000000000000000000000000000056bc75e2d63100000" - } - ] -} -` - -func TestQueryEvents(t *testing.T) { - tmpFile, err := ioutil.TempFile(os.TempDir(), "test-query-events-") - if err != nil { - t.Fatal(err) - } - defer os.Remove(tmpFile.Name()) - - if _, err = tmpFile.Write([]byte(mock)); err != nil { - t.Fatal(err) - } - - if err := tmpFile.Close(); err != nil { - t.Fatal(err) - } - - client := parachain.NewQueryClient() - client.NameArgs = func(_ string, _ string) (string, []string) { - return "cat", []string{tmpFile.Name()} - } - - foo, _ := types.NewHashFromHexString("0x6456d3a2f0c7526d63ad50e79dc8a462931a58ffd57270c3c8aabbcdbd78e76b") - event, err := client.QueryEvent(context.Background(), "", foo) - if err != nil { - t.Fatal(err) - } - - assert.NotNil(t, event) - assert.Equal(t, event.Messages[0].Nonce.Int64(), int64(1)) - assert.Equal(t, len(event.Messages), 1) -} diff --git a/relayer/relays/parachain/scanner.go b/relayer/relays/parachain/scanner.go index cb60a02e2..d8544c128 100644 --- a/relayer/relays/parachain/scanner.go +++ b/relayer/relays/parachain/scanner.go @@ -4,7 +4,6 @@ import ( "context" "encoding/binary" "fmt" - "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -18,13 +17,12 @@ import ( ) type Scanner struct { - config *SourceConfig - ethConn *ethereum.Connection - relayConn *relaychain.Connection - paraConn *parachain.Connection - paraID uint32 - tasks chan<- *Task - eventQueryClient QueryClient + config *SourceConfig + ethConn *ethereum.Connection + relayConn *relaychain.Connection + paraConn *parachain.Connection + paraID uint32 + tasks chan<- *Task } // Scans for all parachain message commitments for the configured parachain laneID that need to be relayed and can be @@ -153,6 +151,11 @@ func (s *Scanner) findTasksImpl( "latestBlockNumber": lastParaBlockNumber, }).Debug("Searching backwards from latest block on parachain to find block with nonce") + messagesKey, err := types.CreateStorageKey(s.paraConn.Metadata(), "EthereumOutboundQueue", "Messages", nil, nil) + if err != nil { + return nil, fmt.Errorf("create storage key: %w", err) + } + scanOutboundQueueDone := false var tasks []*Task @@ -179,17 +182,17 @@ func (s *Scanner) findTasksImpl( if err != nil { return nil, err } + if commitmentHash == nil { + continue + } - event, err := s.eventQueryClient.QueryEvent(ctx, s.config.Parachain.Endpoint, blockHash) + var messages []OutboundQueueMessage + ok, err := s.paraConn.API().RPC.State.GetStorage(messagesKey, &messages, blockHash) if err != nil { - return nil, fmt.Errorf("query event: %w", err) - } - if event == nil { - return nil, fmt.Errorf("event outboundQueue.Committed not found in block with commitment digest item") + return nil, fmt.Errorf("fetch committed messages for block %v: %w", blockHash.Hex(), err) } - - if *commitmentHash != event.Hash { - return nil, fmt.Errorf("outbound queue commitment hash in digest item does not match the one in the Committed event") + if !ok { + return nil, fmt.Errorf("committed messages not found for block %v", blockHash.Hex()) } // For the outbound channel, the commitment hash is the merkle root of the messages @@ -197,10 +200,11 @@ func (s *Scanner) findTasksImpl( // To verify it we fetch the message proof from the parachain result, err := scanForOutboundQueueProofs( s.paraConn.API(), + blockHash, *commitmentHash, startingNonce, laneID, - event.Messages, + messages, ) if err != nil { return nil, err @@ -323,7 +327,8 @@ func (s *Scanner) findInclusionBlockNumber( func scanForOutboundQueueProofs( api *gsrpc.SubstrateAPI, - digestItemHash types.H256, + blockHash types.Hash, + commitmentHash types.H256, startingNonce uint64, laneID uint32, messages []OutboundQueueMessage, @@ -350,15 +355,15 @@ func scanForOutboundQueueProofs( // eg. m1 has nonce 1 and has been relayed. We're looking for messages from nonce 2 upwards in [m1, m2, m3] (m2 and // m3). With nonce ascending, m1.nonce < 2 but we can't assume case 2 yet (where all messages have been relayed). // With nonce descending, we find m3, then m2 where m2.nonce == 2. - for i := len(messages) - 1; i > 0; i-- { + + for i := len(messages) - 1; i >= 0; i-- { message := messages[i] if message.Origin != laneID { continue } - messageNonceBigInt := big.Int(message.Nonce) - messageNonce := messageNonceBigInt.Uint64() + messageNonce := message.Nonce // This case will be hit when there are no new messages to relay. if messageNonce < startingNonce { @@ -370,17 +375,17 @@ func scanForOutboundQueueProofs( break } - messageProof, err := fetchMessageProof(api, digestItemHash, i, message) + messageProof, err := fetchMessageProof(api, blockHash, uint64(i), message) if err != nil { return nil, err } // Check that the merkle root in the proof is the same as the digest hash from the header - if messageProof.Proof.Root != digestItemHash { + if messageProof.Proof.Root != commitmentHash { return nil, fmt.Errorf( "Halting scan for laneID '%v'. Outbound queue proof root '%v' doesn't match digest item's commitment hash '%v'", message.Origin, messageProof.Proof.Root, - digestItemHash, + commitmentHash, ) } @@ -409,32 +414,35 @@ func scanForOutboundQueueProofs( func fetchMessageProof( api *gsrpc.SubstrateAPI, - commitmentHash types.H256, - messageIndex int, + blockHash types.Hash, + messageIndex uint64, message OutboundQueueMessage, ) (MessageProof, error) { var proofHex string - var rawProof RawMerkleProof - var messageProof MessageProof - commitmentHashHex, err := types.EncodeToHexString(commitmentHash) + params, err := types.EncodeToHexString(messageIndex) if err != nil { - return messageProof, fmt.Errorf("encode commitmentHash(%v): %w", commitmentHash, err) + return MessageProof{}, fmt.Errorf("encode params: %w", err) } - err = api.Client.Call(&proofHex, "outboundQueue_getMerkleProof", commitmentHashHex, messageIndex) + err = api.Client.Call(&proofHex, "state_call", "OutboundQueueApi_prove_message", params, blockHash.Hex()) if err != nil { - return messageProof, fmt.Errorf("call rpc basicOutboundQueue_getMerkleProof(%v, %v): %w", commitmentHash, messageIndex, err) + return MessageProof{}, fmt.Errorf("call RPC OutboundQueueApi_prove_message(%v, %v): %w", messageIndex, blockHash, err) } - err = types.DecodeFromHexString(proofHex, &rawProof) + var optionRawMerkleProof OptionRawMerkleProof + err = types.DecodeFromHexString(proofHex, &optionRawMerkleProof) if err != nil { - return messageProof, fmt.Errorf("decode merkle proof: %w", err) + return MessageProof{}, fmt.Errorf("decode merkle proof: %w", err) + } + + if !optionRawMerkleProof.HasValue { + return MessageProof{}, fmt.Errorf("retrieve proof failed") } - proof, err := NewMerkleProof(rawProof) + proof, err := NewMerkleProof(optionRawMerkleProof.Value) if err != nil { - return messageProof, fmt.Errorf("decode merkle proof: %w", err) + return MessageProof{}, fmt.Errorf("decode merkle proof: %w", err) } return MessageProof{Message: message, Proof: proof}, nil diff --git a/relayer/relays/parachain/types.go b/relayer/relays/parachain/types.go index 4dc82e5d3..afe694adc 100644 --- a/relayer/relays/parachain/types.go +++ b/relayer/relays/parachain/types.go @@ -1,12 +1,11 @@ package parachain import ( - "math/big" - "github.com/snowfork/go-substrate-rpc-client/v4/types" "github.com/snowfork/snowbridge/relayer/chain/relaychain" "github.com/snowfork/snowbridge/relayer/contracts" "github.com/snowfork/snowbridge/relayer/crypto/merkle" + "github.com/vedhavyas/go-subkey/scale" ) // A Task contains the working state for message commitments in a single parachain block @@ -39,12 +38,25 @@ type ProofOutput struct { MerkleProofData MerkleProofData } +type OptionRawMerkleProof struct { + HasValue bool + Value RawMerkleProof +} + +func (o OptionRawMerkleProof) Encode(encoder scale.Encoder) error { + return encoder.EncodeOption(o.HasValue, o.Value) +} + +func (o *OptionRawMerkleProof) Decode(decoder scale.Decoder) error { + return decoder.DecodeOption(&o.HasValue, &o.Value) +} + type RawMerkleProof struct { Root types.H256 Proof []types.H256 NumberOfLeaves uint64 LeafIndex uint64 - Leaf []byte + Leaf types.H256 } type MerkleProof struct { @@ -70,17 +82,17 @@ func NewMerkleProof(rawProof RawMerkleProof) (MerkleProof, error) { type OutboundQueueMessage struct { Origin uint32 - Nonce types.UCompact - Handler uint16 + Nonce uint64 + Gateway [32]byte Payload []byte } func (m OutboundQueueMessage) IntoInboundMessage() contracts.InboundQueueMessage { return contracts.InboundQueueMessage{ - Origin: m.Origin, - Nonce: (*big.Int)(&m.Nonce).Uint64(), - Handler: m.Handler, - Payload: m.Payload, + Origin: m.Origin, + Nonce: m.Nonce, + Recipient: m.Gateway, + Payload: m.Payload, } } diff --git a/smoketest/tests/lock_tokens.rs b/smoketest/tests/lock_tokens.rs index c5cbf038c..372a393ab 100644 --- a/smoketest/tests/lock_tokens.rs +++ b/smoketest/tests/lock_tokens.rs @@ -18,9 +18,9 @@ use xcm::v3::MultiLocation; // contracts are deployed in DeployScript.sol. const ETHEREUM_API: &str = "http://localhost:8545"; const ETHEREUM_KEY: &str = "0x5e002a1af63fd31f1c25258f3082dc889762664cb8f218d86da85dff8b07b342"; -const NATIVE_TOKENS_CONTRACT: &str = "0x83428c7db9815f482a39a1715684dCF755021997"; -const TOKEN_VAULT_CONTRACT: &str = "0x774667629726ec1FaBEbCEc0D9139bD1C8f72a23"; -const WETH_CONTRACT: &str = "0xF8F7758FbcEfd546eAEff7dE24AFf666B6228e73"; +const NATIVE_TOKENS_CONTRACT: &str = "0xB8EA8cB425d85536b158d661da1ef0895Bb92F1D"; +const TOKEN_VAULT_CONTRACT: &str = "0xB1185EDE04202fE62D38F5db72F71e38Ff3E8305"; +const WETH_CONTRACT: &str = "0x3f0839385DB9cBEa8E73AdA6fa0CFe07E321F61d"; #[tokio::test] async fn test_lock_tokens() {