From 503042c4e1c07047978648d373cadf2a96fbfbfc Mon Sep 17 00:00:00 2001 From: Vincent Geddes Date: Tue, 9 Mar 2021 22:48:59 +0200 Subject: [PATCH] Refactor channel architecture (#308) --- ethereum/contracts/BasicOutboundChannel.sol | 13 +- .../contracts/{BaseDOTApp.sol => DOTApp.sol} | 57 +- ethereum/contracts/DOTAppDecimals10.sol | 24 - ethereum/contracts/DOTAppDecimals12.sol | 24 - ethereum/contracts/ERC20App.sol | 2 +- ethereum/contracts/ETHApp.sol | 2 +- .../contracts/IncentivizedOutboundChannel.sol | 16 +- ethereum/contracts/OutboundChannel.sol | 20 +- ethereum/contracts/WrappedToken.sol | 4 +- ethereum/migrations/2_next.js | 8 +- ethereum/test/helpers.js | 44 +- ethereum/test/test_basic_outbound_channel.js | 10 +- ethereum/test/test_dot_app.js | 13 +- ethereum/test/test_erc20_app.js | 7 +- ethereum/test/test_eth_app.js | 7 +- .../test_incentivized_outbound_channel.js | 21 +- parachain/Cargo.lock | 65 +- parachain/Cargo.toml | 3 +- parachain/pallets/basic-channel/Cargo.toml | 45 + .../src/inbound}/envelope.rs | 0 .../pallets/basic-channel/src/inbound/mod.rs | 93 ++ .../pallets/basic-channel/src/inbound/test.rs | 234 +++ parachain/pallets/basic-channel/src/lib.rs | 4 + .../pallets/basic-channel/src/outbound/mod.rs | 55 + .../basic-channel/src/outbound/test.rs | 98 ++ .../pallets/bridge/src/channel/inbound.rs | 132 -- parachain/pallets/bridge/src/channel/mod.rs | 2 - .../pallets/bridge/src/channel/outbound.rs | 118 -- parachain/pallets/bridge/src/lib.rs | 153 -- parachain/pallets/bridge/src/mock.rs | 159 -- parachain/pallets/bridge/src/primitives.rs | 31 - parachain/pallets/bridge/src/tests.rs | 242 --- parachain/pallets/dot-app/src/lib.rs | 6 +- parachain/pallets/dot-app/src/mock.rs | 165 ++- parachain/pallets/dot-app/src/tests.rs | 1 - parachain/pallets/erc20-app/src/lib.rs | 7 +- parachain/pallets/erc20-app/src/mock.rs | 17 +- parachain/pallets/erc20-app/src/tests.rs | 24 +- parachain/pallets/eth-app/src/lib.rs | 6 +- parachain/pallets/eth-app/src/mock.rs | 19 +- parachain/pallets/eth-app/src/tests.rs | 22 +- .../Cargo.toml | 8 +- .../src/inbound/envelope.rs | 114 ++ .../incentivized-channel/src/inbound/mod.rs | 108 ++ .../incentivized-channel/src/inbound/test.rs | 272 ++++ .../pallets/incentivized-channel/src/lib.rs | 4 + .../incentivized-channel/src/outbound/mod.rs | 55 + .../incentivized-channel/src/outbound/test.rs | 98 ++ parachain/primitives/core/src/lib.rs | 7 +- parachain/primitives/core/src/types.rs | 21 +- parachain/runtime/Cargo.toml | 7 +- parachain/runtime/src/lib.rs | 96 +- parachain/src/chain_spec.rs | 19 +- relayer/chain/chain.go | 5 +- relayer/chain/ethereum/chain.go | 4 +- relayer/chain/ethereum/listener.go | 110 +- relayer/chain/ethereum/message.go | 36 +- relayer/chain/ethereum/message_test.go | 20 +- relayer/chain/substrate/writer.go | 3 +- relayer/chain/substrate/writer_test.go | 22 +- relayer/cmd/fetch_messages.go | 126 +- relayer/contracts/generate.go | 3 +- relayer/contracts/outbound/basic.go | 360 +++++ relayer/contracts/outbound/contract.go | 360 ----- relayer/contracts/outbound/incentivized.go | 1304 +++++++++++++++++ test/src/ethclient/index.js | 4 +- 66 files changed, 3480 insertions(+), 1659 deletions(-) rename ethereum/contracts/{BaseDOTApp.sol => DOTApp.sol} (69%) delete mode 100644 ethereum/contracts/DOTAppDecimals10.sol delete mode 100644 ethereum/contracts/DOTAppDecimals12.sol create mode 100644 parachain/pallets/basic-channel/Cargo.toml rename parachain/pallets/{bridge/src => basic-channel/src/inbound}/envelope.rs (100%) create mode 100644 parachain/pallets/basic-channel/src/inbound/mod.rs create mode 100644 parachain/pallets/basic-channel/src/inbound/test.rs create mode 100644 parachain/pallets/basic-channel/src/lib.rs create mode 100644 parachain/pallets/basic-channel/src/outbound/mod.rs create mode 100644 parachain/pallets/basic-channel/src/outbound/test.rs delete mode 100644 parachain/pallets/bridge/src/channel/inbound.rs delete mode 100644 parachain/pallets/bridge/src/channel/mod.rs delete mode 100644 parachain/pallets/bridge/src/channel/outbound.rs delete mode 100644 parachain/pallets/bridge/src/lib.rs delete mode 100644 parachain/pallets/bridge/src/mock.rs delete mode 100644 parachain/pallets/bridge/src/primitives.rs delete mode 100644 parachain/pallets/bridge/src/tests.rs rename parachain/pallets/{bridge => incentivized-channel}/Cargo.toml (93%) create mode 100644 parachain/pallets/incentivized-channel/src/inbound/envelope.rs create mode 100644 parachain/pallets/incentivized-channel/src/inbound/mod.rs create mode 100644 parachain/pallets/incentivized-channel/src/inbound/test.rs create mode 100644 parachain/pallets/incentivized-channel/src/lib.rs create mode 100644 parachain/pallets/incentivized-channel/src/outbound/mod.rs create mode 100644 parachain/pallets/incentivized-channel/src/outbound/test.rs create mode 100644 relayer/contracts/outbound/basic.go delete mode 100644 relayer/contracts/outbound/contract.go create mode 100644 relayer/contracts/outbound/incentivized.go diff --git a/ethereum/contracts/BasicOutboundChannel.sol b/ethereum/contracts/BasicOutboundChannel.sol index 3084d9668a51e..3aef1d9ae6f87 100644 --- a/ethereum/contracts/BasicOutboundChannel.sol +++ b/ethereum/contracts/BasicOutboundChannel.sol @@ -6,14 +6,19 @@ import "./OutboundChannel.sol"; // BasicOutboundChannel is a basic channel that just sends messages with a nonce. contract BasicOutboundChannel is OutboundChannel { - constructor() { - nonce = 0; - } + + uint64 public nonce; + + event Message( + address source, + uint64 nonce, + bytes payload + ); /** * @dev Sends a message across the channel */ - function submit(bytes memory payload) public override { + function submit(address, bytes calldata payload) external override { nonce = nonce + 1; emit Message(msg.sender, nonce, payload); } diff --git a/ethereum/contracts/BaseDOTApp.sol b/ethereum/contracts/DOTApp.sol similarity index 69% rename from ethereum/contracts/BaseDOTApp.sol rename to ethereum/contracts/DOTApp.sol index 3d4f38fdde811..9904e6da11a62 100644 --- a/ethereum/contracts/BaseDOTApp.sol +++ b/ethereum/contracts/DOTApp.sol @@ -2,19 +2,31 @@ pragma solidity >=0.7.6; pragma experimental ABIEncoderV2; +import "@openzeppelin/contracts/access/AccessControl.sol"; import "./WrappedToken.sol"; import "./ScaleCodec.sol"; import "./OutboundChannel.sol"; enum ChannelId {Basic, Incentivized} -abstract contract BaseDOTApp { +contract DOTApp is AccessControl { using ScaleCodec for uint128; + bytes32 public constant FEE_BURNER_ROLE = keccak256("FEE_BURNER_ROLE"); + mapping(ChannelId => Channel) public channels; bytes2 constant UNLOCK_CALL = 0x0e01; + /* + * Smallest part of DOT/KSM/ROC that is not divisible when increasing + * precision to 18 decimal places. + * + * This is used for converting between native and wrapped + * representations of DOT/KSM/ROC. + */ + uint256 private granularity; + WrappedToken public token; struct Channel { @@ -25,6 +37,7 @@ abstract contract BaseDOTApp { constructor( string memory _name, string memory _symbol, + uint256 _decimals, Channel memory _basic, Channel memory _incentivized ) { @@ -38,6 +51,10 @@ abstract contract BaseDOTApp { Channel storage c2 = channels[ChannelId.Incentivized]; c2.inbound = _incentivized.inbound; c2.outbound = _incentivized.outbound; + + _setupRole(FEE_BURNER_ROLE, _incentivized.outbound); + + granularity = 10 ** (18 - _decimals); } function burn(bytes32 _recipient, uint256 _amount, ChannelId _channelId) public { @@ -46,14 +63,14 @@ abstract contract BaseDOTApp { _channelId == ChannelId.Incentivized, "Invalid channel ID" ); - require(_amount % granularity() == 0, "Invalid Granularity"); + require(_amount % granularity == 0, "Invalid Granularity"); token.burn(msg.sender, _amount, abi.encodePacked(_recipient)); OutboundChannel channel = OutboundChannel(channels[_channelId].outbound); bytes memory call = encodeCall(msg.sender, _recipient, unwrap(_amount)); - channel.submit(call); + channel.submit(msg.sender, call); } function mint(bytes32 _sender, address _recipient, uint128 _amount) public { @@ -61,6 +78,13 @@ abstract contract BaseDOTApp { token.mint(_recipient, wrap(_amount), abi.encodePacked(_sender)); } + function burnFee(address _account, uint256 _amount) external returns (uint128) { + require(hasRole(FEE_BURNER_ROLE, msg.sender), "ACCESS_FORBIDDEN"); + require(_amount % granularity == 0, "INVALID_GRANULARITY"); + token.burn(_account, _amount, ""); + return unwrap(_amount); + } + function encodeCall(address _sender, bytes32 _recipient, uint128 _amount) private pure @@ -79,9 +103,8 @@ abstract contract BaseDOTApp { /* * Convert native DOT/KSM/ROC to the wrapped equivalent. * - * SAFETY: No need for SafeMath.mul since its impossible to overflow - * when 0 <= granularity <= 10 ^ 8, as specified by DOTAppDecimals10.sol - * and DOTAppDecimals12.sol. + * SAFETY: No need for SafeMath.mul as overflow is not possible for + * 0 <= granularity <= 10 ^ 8. * * Can verify in Rust using this snippet: * @@ -89,27 +112,17 @@ abstract contract BaseDOTApp { * U256::from(u128::MAX).checked_mul(granularity).unwrap(); * */ - function wrap(uint128 _value) pure internal returns (uint256) { - return uint256(_value) * granularity(); + function wrap(uint128 _value) view internal returns (uint256) { + return uint256(_value) * granularity; } /* * Convert wrapped DOT/KSM/ROC to its native equivalent. * - * SAFETY: No need for SafeMath.div since granularity() resolves to a non-zero - * constant (See DOTAppDecimals10.sol and DOTAppDecimals12.sol) + * SAFETY: No need for SafeMath.div since granularity is + * configured to be non-zero. */ - function unwrap(uint256 _value) pure internal returns (uint128) { - return uint128(_value / granularity()); + function unwrap(uint256 _value) view internal returns (uint128) { + return uint128(_value / granularity); } - - /** - * Smallest part of DOT/KSM/ROC that is not divisible when increasing - * precision to 18 decimal places. - * - * This is used for converting between native and wrapped - * representations of DOT/KSM/ROC. - */ - function granularity() pure internal virtual returns (uint256); - } diff --git a/ethereum/contracts/DOTAppDecimals10.sol b/ethereum/contracts/DOTAppDecimals10.sol deleted file mode 100644 index f95d546581f14..0000000000000 --- a/ethereum/contracts/DOTAppDecimals10.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.7.6; -pragma experimental ABIEncoderV2; - -import "./BaseDOTApp.sol"; - -contract DOTAppDecimals10 is BaseDOTApp { - // Polkadot (DOT) has 10 decimal places - uint256 constant internal DECIMALS = 10; - uint256 constant internal GRANULARITY = 10 ** (18 - DECIMALS); - - constructor( - string memory _name, - string memory _symbol, - Channel memory _basic, - Channel memory _incentivized - ) - BaseDOTApp(_name, _symbol, _basic, _incentivized) - { } - - function granularity() pure internal override returns (uint256) { - return GRANULARITY; - } -} diff --git a/ethereum/contracts/DOTAppDecimals12.sol b/ethereum/contracts/DOTAppDecimals12.sol deleted file mode 100644 index 9ba6903b63f20..0000000000000 --- a/ethereum/contracts/DOTAppDecimals12.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.7.6; -pragma experimental ABIEncoderV2; - -import "./BaseDOTApp.sol"; - -contract DOTAppDecimals12 is BaseDOTApp { - // Kusama (KSM) and Rococo (ROC) have 12 decimal places - uint256 constant internal DECIMALS = 12; - uint256 constant internal GRANULARITY = 10 ** (18 - DECIMALS); - - constructor( - string memory _name, - string memory _symbol, - Channel memory _basic, - Channel memory _incentivized - ) - BaseDOTApp(_name, _symbol, _basic, _incentivized) - { } - - function granularity() pure internal override returns (uint256) { - return GRANULARITY; - } -} diff --git a/ethereum/contracts/ERC20App.sol b/ethereum/contracts/ERC20App.sol index caa2a27fbedce..dc2f8f07e1948 100644 --- a/ethereum/contracts/ERC20App.sol +++ b/ethereum/contracts/ERC20App.sol @@ -72,7 +72,7 @@ contract ERC20App { OutboundChannel channel = OutboundChannel(channels[_channelId].outbound); - channel.submit(call); + channel.submit(msg.sender, call); } function unlock( diff --git a/ethereum/contracts/ETHApp.sol b/ethereum/contracts/ETHApp.sol index 26f6952d0f045..fdf5398888f2b 100644 --- a/ethereum/contracts/ETHApp.sol +++ b/ethereum/contracts/ETHApp.sol @@ -55,7 +55,7 @@ contract ETHApp { OutboundChannel channel = OutboundChannel(channels[_channelId].outbound); - channel.submit(call); + channel.submit(msg.sender, call); } function unlock( diff --git a/ethereum/contracts/IncentivizedOutboundChannel.sol b/ethereum/contracts/IncentivizedOutboundChannel.sol index 3c7febacfe342..2808141e8b992 100644 --- a/ethereum/contracts/IncentivizedOutboundChannel.sol +++ b/ethereum/contracts/IncentivizedOutboundChannel.sol @@ -7,17 +7,19 @@ import "./OutboundChannel.sol"; // IncentivizedOutboundChannel is a channel that sends ordered messages with an increasing nonce. It will have incentivization too. contract IncentivizedOutboundChannel is OutboundChannel { - constructor() { - nonce = 0; - } + // Nonce for last submitted message + uint64 public nonce; + + event Message( + address source, + uint64 nonce, + bytes payload + ); /** * @dev Sends a message across the channel */ - function submit(bytes memory payload) - public - override - { + function submit(address, bytes calldata payload) external override { nonce = nonce + 1; emit Message(msg.sender, nonce, payload); } diff --git a/ethereum/contracts/OutboundChannel.sol b/ethereum/contracts/OutboundChannel.sol index 1714eb09bf87c..d3bcec4fab4e2 100644 --- a/ethereum/contracts/OutboundChannel.sol +++ b/ethereum/contracts/OutboundChannel.sol @@ -2,22 +2,6 @@ pragma solidity >=0.7.6; pragma experimental ABIEncoderV2; -// OutboundChannel contains methods that all outgoing channels must implement -abstract contract OutboundChannel { - - // Nonce for last submitted message - uint64 public nonce; - - event Message( - address source, - uint64 nonce, - bytes payload - ); - - /** - * @dev Sends a message across the channel - */ - function submit(bytes memory payload) - public - virtual; +interface OutboundChannel { + function submit(address origin, bytes calldata payload) external; } diff --git a/ethereum/contracts/WrappedToken.sol b/ethereum/contracts/WrappedToken.sol index e19ccc8e41eb0..4af6602677fb6 100644 --- a/ethereum/contracts/WrappedToken.sol +++ b/ethereum/contracts/WrappedToken.sol @@ -23,8 +23,8 @@ contract WrappedToken is ERC777, Ownable { _mint(recipient, amount, data, ""); } - // Don't allow users to directly burn their SnowDOT via the IERC777 burn API, as it won't have - // the desired effect. + // Don't allow users to directly burn their wrapped tokens via the IERC777 burn API, as it won't redeem + // the native tokens on substrate. function burn(uint256, bytes memory) public pure override { revert("not-supported"); diff --git a/ethereum/migrations/2_next.js b/ethereum/migrations/2_next.js index ddc13dcd545c4..392b59820269b 100644 --- a/ethereum/migrations/2_next.js +++ b/ethereum/migrations/2_next.js @@ -1,8 +1,7 @@ const ScaleCodec = artifacts.require("ScaleCodec"); const ETHApp = artifacts.require("ETHApp"); const ERC20App = artifacts.require("ERC20App"); -const DOTAppDecimals10 = artifacts.require("DOTAppDecimals10"); -const DOTAppDecimals12 = artifacts.require("DOTAppDecimals12"); +const DOTApp = artifacts.require("DOTApp"); const TestToken = artifacts.require("TestToken"); const channels = { @@ -37,7 +36,7 @@ module.exports = function(deployer, network, accounts) { // Link libraries to applications await deployer.deploy(ScaleCodec); - deployer.link(ScaleCodec, [ETHApp, ERC20App, DOTAppDecimals10, DOTAppDecimals12]); + deployer.link(ScaleCodec, [ETHApp, ERC20App, DOTApp]); // Deploy applications await deployer.deploy( @@ -78,9 +77,10 @@ module.exports = function(deployer, network, accounts) { // only deploy this contract to non-development networks. The unit tests deploy this contract themselves. if (network === 'ropsten' || network === 'e2e_test') { await deployer.deploy( - DOTAppDecimals12, // On Kusama and Rococo, KSM/ROC tokens have 12 decimal places + DOTApp, "Snowfork DOT", "SnowDOT", + 12, // On Kusama and Rococo, KSM/ROC tokens have 12 decimal places { inbound: channels.basic.inbound.instance.address, outbound: channels.basic.outbound.instance.address, diff --git a/ethereum/test/helpers.js b/ethereum/test/helpers.js index 5231d311436fb..e696d7586654b 100644 --- a/ethereum/test/helpers.js +++ b/ethereum/test/helpers.js @@ -1,6 +1,8 @@ -const ethers = require("ethers"); const BigNumber = require('bignumber.js'); const rlp = require("rlp"); +const { ethers } = require("ethers"); + +const assert = require('chai').assert; const channelContracts = { basic: { @@ -13,32 +15,35 @@ const channelContracts = { }, }; -const confirmChannelSend = (channelEvent, channelAddress, sendingAppAddress, expectedNonce = 0, expectedPayload) => { - outChannelLogFields = [ - { - type: 'address', - name: 'source' - }, - { - type: 'uint64', - name: 'nonce' - }, - { - type: 'bytes', - name: 'payload', - } - ]; +const confirmBasicChannelSend = (channelEvent, channelAddress, sendingAppAddress, expectedNonce = 0, expectedPayload) => { + var abi = ["event Message(address source, uint64 nonce, bytes payload)"]; + var iface = new ethers.utils.Interface(abi); + let decodedEvent = iface.decodeEventLog('Message(address,uint64,bytes)', channelEvent.data, channelEvent.topics); - const decodedEvent = web3.eth.abi.decodeLog(outChannelLogFields, channelEvent.data, channelEvent.topics); + channelEvent.address.should.be.equal(channelAddress); + decodedEvent.source.should.be.equal(sendingAppAddress); + + assert(decodedEvent.nonce.eq(ethers.BigNumber.from(expectedNonce))); + if (expectedPayload) { + decodedEvent.payload.should.be.equal(expectedPayload); + } +}; + +const confirmIncentivizedChannelSend = (channelEvent, channelAddress, sendingAppAddress, expectedNonce = 0, expectedPayload) => { + var abi = ["event Message(address source, uint64 nonce, bytes payload)"]; + var iface = new ethers.utils.Interface(abi); + let decodedEvent = iface.decodeEventLog('Message(address,uint64,bytes)', channelEvent.data, channelEvent.topics); channelEvent.address.should.be.equal(channelAddress); decodedEvent.source.should.be.equal(sendingAppAddress); - decodedEvent.nonce.should.be.equal('' + expectedNonce); + + assert(decodedEvent.nonce.eq(ethers.BigNumber.from(expectedNonce))); if (expectedPayload) { decodedEvent.payload.should.be.equal(expectedPayload); } }; + const confirmUnlock = (rawEvent, ethAppAddress, expectedRecipient, expectedAmount) => { unlockLogFields = [ { @@ -198,7 +203,8 @@ const encodeLog = (log) => { } module.exports = { - confirmChannelSend, + confirmBasicChannelSend, + confirmIncentivizedChannelSend, confirmUnlock, confirmUnlockTokens, confirmMessageDispatched, diff --git a/ethereum/test/test_basic_outbound_channel.js b/ethereum/test/test_basic_outbound_channel.js index e4cc314b322e9..62202063da1c5 100644 --- a/ethereum/test/test_basic_outbound_channel.js +++ b/ethereum/test/test_basic_outbound_channel.js @@ -4,7 +4,7 @@ const Web3Utils = require("web3-utils"); const ethers = require("ethers"); const BigNumber = web3.BigNumber; -const { confirmChannelSend } = require("./helpers"); +const { confirmBasicChannelSend } = require("./helpers"); require("chai") .use(require("chai-as-promised")) @@ -13,7 +13,8 @@ require("chai") contract("BasicOutboundChannel", function (accounts) { // Accounts - const userOne = accounts[1]; + const appAddress = accounts[1]; + const origin = accounts[2]; const payload = ethers.utils.formatBytes32String("arbitrary-payload"); describe("submit messages", function () { @@ -23,12 +24,13 @@ contract("BasicOutboundChannel", function (accounts) { it("should send messages out with the correct event and fields", async function () { const tx = await this.channel.submit( + origin, payload, - { from: userOne, value: 0 } + { from: appAddress, value: 0 } ).should.be.fulfilled; const rawLog = tx.receipt.rawLogs[0]; - confirmChannelSend(rawLog, this.channel.address, userOne, 1, payload) + confirmBasicChannelSend(rawLog, this.channel.address, appAddress, 1, payload) }); }); diff --git a/ethereum/test/test_dot_app.js b/ethereum/test/test_dot_app.js index f58d027dc946e..4f0acf1d53300 100644 --- a/ethereum/test/test_dot_app.js +++ b/ethereum/test/test_dot_app.js @@ -3,7 +3,8 @@ const { ethers } = require("ethers"); const BigNumber = require('bignumber.js'); const AssertionError = require('assert').AssertionError; const { - confirmChannelSend, + confirmBasicChannelSend, + confirmIncentivizedChannelSend, confirmUnlock, deployAppContractWithChannels, addressBytes, @@ -16,7 +17,7 @@ require("chai") .use(require("chai-bignumber")(BigNumber)) .should(); -const DOTAppDecimals10 = artifacts.require("DOTAppDecimals10"); +const DOTApp = artifacts.require("DOTApp"); const Token = artifacts.require("WrappedToken"); const DOT_DECIMALS = 10; @@ -53,7 +54,7 @@ contract("DOTApp", function (accounts) { describe("minting", function () { beforeEach(async function () { this.erc1820 = await singletons.ERC1820Registry(owner); - [this.channels, this.app] = await deployAppContractWithChannels(DOTAppDecimals10, "Snowfork DOT", "SnowDOT"); + [this.channels, this.app] = await deployAppContractWithChannels(DOTApp, "Snowfork DOT", "SnowDOT", 10); this.token = await Token.at(await this.app.token()); }); @@ -93,7 +94,7 @@ contract("DOTApp", function (accounts) { describe("burning", function () { beforeEach(async function () { this.erc1820 = await singletons.ERC1820Registry(owner); - [this.channels, this.app] = await deployAppContractWithChannels(DOTAppDecimals10, "Snowfork DOT", "SnowDOT"); + [this.channels, this.app] = await deployAppContractWithChannels(DOTApp, "Snowfork DOT", "SnowDOT", 10); this.token = await Token.at(await this.app.token()); // Mint 2 wrapped DOT @@ -145,13 +146,13 @@ contract("DOTApp", function (accounts) { it("should send payload to the basic outbound channel", async function () { const amountWrapped = wrapped(BigNumber("10000000000")); let { receipt } = await burnTokens(this.app, user, POLKADOT_ADDRESS, amountWrapped, ChannelId.Basic).should.be.fulfilled; - confirmChannelSend(receipt.rawLogs[2], this.channels.basic.outbound.address, this.app.address, 1) + confirmBasicChannelSend(receipt.rawLogs[2], this.channels.basic.outbound.address, this.app.address, 1) }); it("should send payload to the incentivized outbound channel", async function () { const amountWrapped = wrapped(BigNumber("10000000000")); let { receipt } = await burnTokens(this.app, user, POLKADOT_ADDRESS, amountWrapped, ChannelId.Incentivized).should.be.fulfilled; - confirmChannelSend(receipt.rawLogs[2], this.channels.incentivized.outbound.address, this.app.address, 1) + confirmIncentivizedChannelSend(receipt.rawLogs[2], this.channels.incentivized.outbound.address, this.app.address, 1) }); }); }); diff --git a/ethereum/test/test_erc20_app.js b/ethereum/test/test_erc20_app.js index c822312404ddd..8ac98d6169536 100644 --- a/ethereum/test/test_erc20_app.js +++ b/ethereum/test/test_erc20_app.js @@ -1,6 +1,7 @@ const BigNumber = require('bignumber.js'); const { - confirmChannelSend, + confirmBasicChannelSend, + confirmIncentivizedChannelSend, confirmUnlockTokens, deployAppContractWithChannels, addressBytes, @@ -89,7 +90,7 @@ contract("ERC20App", function (accounts) { let tx = await lockupFunds(this.app, this.token, userOne, POLKADOT_ADDRESS, amount, ChannelId.Basic) .should.be.fulfilled; - confirmChannelSend(tx.receipt.rawLogs[3], this.channels.basic.outbound.address, this.app.address, 1) + confirmBasicChannelSend(tx.receipt.rawLogs[3], this.channels.basic.outbound.address, this.app.address, 1) }); it("should send payload to the incentivized outbound channel", async function () { @@ -101,7 +102,7 @@ contract("ERC20App", function (accounts) { let tx = await lockupFunds(this.app, this.token, userOne, POLKADOT_ADDRESS, amount, ChannelId.Incentivized) .should.be.fulfilled; - confirmChannelSend(tx.receipt.rawLogs[3], this.channels.incentivized.outbound.address, this.app.address, 1) + confirmIncentivizedChannelSend(tx.receipt.rawLogs[3], this.channels.incentivized.outbound.address, this.app.address, 1) }); }) diff --git a/ethereum/test/test_eth_app.js b/ethereum/test/test_eth_app.js index 37c5b0c98d509..28a90c0b58916 100644 --- a/ethereum/test/test_eth_app.js +++ b/ethereum/test/test_eth_app.js @@ -1,6 +1,7 @@ const BigNumber = require('bignumber.js'); const { - confirmChannelSend, + confirmBasicChannelSend, + confirmIncentivizedChannelSend, confirmUnlock, deployAppContractWithChannels, addressBytes, @@ -77,7 +78,7 @@ contract("ETHApp", function (accounts) { // console.log(tx.receipt.rawLogs[1]) // console.log(encodeLog(tx.receipt.rawLogs[1])); - confirmChannelSend(tx.receipt.rawLogs[1], this.channels.basic.outbound.address, this.app.address, 1) + confirmBasicChannelSend(tx.receipt.rawLogs[1], this.channels.basic.outbound.address, this.app.address, 1) }); it("should send payload to the incentivized outbound channel", async function () { @@ -86,7 +87,7 @@ contract("ETHApp", function (accounts) { const tx = await lockupFunds(this.app, userOne, POLKADOT_ADDRESS, amount, ChannelId.Incentivized) .should.be.fulfilled; - confirmChannelSend(tx.receipt.rawLogs[1], this.channels.incentivized.outbound.address, this.app.address, 1) + confirmIncentivizedChannelSend(tx.receipt.rawLogs[1], this.channels.incentivized.outbound.address, this.app.address, 1) }); }) diff --git a/ethereum/test/test_incentivized_outbound_channel.js b/ethereum/test/test_incentivized_outbound_channel.js index 6261ed23c60ac..184ce7484773a 100644 --- a/ethereum/test/test_incentivized_outbound_channel.js +++ b/ethereum/test/test_incentivized_outbound_channel.js @@ -4,7 +4,7 @@ const Web3Utils = require("web3-utils"); const ethers = require("ethers"); const BigNumber = web3.BigNumber; -const { confirmChannelSend } = require("./helpers"); +const { confirmIncentivizedChannelSend } = require("./helpers"); require("chai") .use(require("chai-as-promised")) @@ -13,7 +13,8 @@ require("chai") contract("IncentivizedOutboundChannel", function (accounts) { // Accounts - const userOne = accounts[1]; + const appAddress = accounts[1]; + const origin = accounts[2]; const testPayload = ethers.utils.formatBytes32String("arbitrary-payload"); describe("deployment and initialization", function () { @@ -29,32 +30,36 @@ contract("IncentivizedOutboundChannel", function (accounts) { it("should send messages out with the correct event and fields", async function () { const tx = await this.channel.submit( + origin, testPayload, - { from: userOne, value: 0 } + { from: appAddress, value: 0 } ).should.be.fulfilled; const rawLog = tx.receipt.rawLogs[0]; - confirmChannelSend(rawLog, this.channel.address, userOne, 1, testPayload) + confirmIncentivizedChannelSend(rawLog, this.channel.address, appAddress, 1, testPayload) }); it("should increment nonces correctly", async function () { const tx = await this.channel.submit( + origin, testPayload, - { from: userOne, value: 0 } + { from: appAddress, value: 0 } ).should.be.fulfilled; const tx2 = await this.channel.submit( + origin, testPayload, - { from: userOne, value: 0 } + { from: appAddress, value: 0 } ).should.be.fulfilled; const tx3 = await this.channel.submit( + origin, testPayload, - { from: userOne, value: 0 } + { from: appAddress, value: 0 } ).should.be.fulfilled; const rawLog = tx3.receipt.rawLogs[0]; - confirmChannelSend(rawLog, this.channel.address, userOne, 3, testPayload) + confirmIncentivizedChannelSend(rawLog, this.channel.address, appAddress, 3, testPayload) }); }); diff --git a/parachain/Cargo.lock b/parachain/Cargo.lock index 80ea0be1d06ae..d31b0c17502a5 100644 --- a/parachain/Cargo.lock +++ b/parachain/Cargo.lock @@ -221,6 +221,26 @@ dependencies = [ "sp-std", ] +[[package]] +name = "artemis-basic-channel" +version = "0.1.1" +dependencies = [ + "artemis-core", + "artemis-ethereum", + "ethabi-decode", + "frame-support", + "frame-system", + "hex-literal", + "parity-scale-codec", + "rlp", + "serde", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std", +] + [[package]] name = "artemis-commitments" version = "0.1.0" @@ -369,17 +389,40 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "artemis-incentivized-channel" +version = "0.1.1" +dependencies = [ + "artemis-core", + "artemis-ethereum", + "ethabi-decode", + "frame-support", + "frame-system", + "hex-literal", + "pallet-balances", + "parity-scale-codec", + "rlp", + "serde", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std", +] + [[package]] name = "artemis-runtime" version = "0.1.1" dependencies = [ "artemis-assets", + "artemis-basic-channel", "artemis-commitments", "artemis-core", "artemis-dispatch", "artemis-dot-app", "artemis-erc20-app", "artemis-eth-app", + "artemis-incentivized-channel", "artemis-transfer", "artemis-xcm-support", "cumulus-pallet-parachain-system", @@ -394,7 +437,6 @@ dependencies = [ "getrandom 0.2.2", "hex-literal", "pallet-balances", - "pallet-bridge", "pallet-randomness-collective-flip", "pallet-timestamp", "pallet-transaction-payment", @@ -4497,27 +4539,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-bridge" -version = "0.1.1" -dependencies = [ - "artemis-core", - "artemis-ethereum", - "ethabi-decode", - "frame-support", - "frame-system", - "hex-literal", - "pallet-balances", - "parity-scale-codec", - "rlp", - "serde", - "sp-core", - "sp-io", - "sp-keyring", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-collective" version = "3.0.0" diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index ff823cf6e5183..9dbad6298583f 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -91,7 +91,8 @@ members = [ "primitives/ethereum", "primitives/testutils", "primitives/xcm-support", - "pallets/bridge", + "pallets/basic-channel", + "pallets/incentivized-channel", "pallets/dispatch", "pallets/assets", "pallets/verifier-lightclient", diff --git a/parachain/pallets/basic-channel/Cargo.toml b/parachain/pallets/basic-channel/Cargo.toml new file mode 100644 index 0000000000000..2fcbe63a62fef --- /dev/null +++ b/parachain/pallets/basic-channel/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "artemis-basic-channel" +description = "Artemis Basic Channel" +version = "0.1.1" +edition = "2018" +authors = ["Snowfork "] +repository = "https://github.com/Snowfork/polkadot-ethereum" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +serde = { version = "1.0.101", optional = true } +codec = { version = "2.0.0", package = "parity-scale-codec", default-features = false, features = ["derive"] } + +frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } + +artemis-core = { path = "../../primitives/core", default-features = false } +artemis-ethereum = { path = "../../primitives/ethereum", default-features = false } +ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false } + +[dev-dependencies] +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } +hex-literal = { version = "0.3.1" } +rlp = { version = "0.5" } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "frame-support/std", + "frame-system/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "artemis-core/std", + "artemis-ethereum/std", + "ethabi/std" +] diff --git a/parachain/pallets/bridge/src/envelope.rs b/parachain/pallets/basic-channel/src/inbound/envelope.rs similarity index 100% rename from parachain/pallets/bridge/src/envelope.rs rename to parachain/pallets/basic-channel/src/inbound/envelope.rs diff --git a/parachain/pallets/basic-channel/src/inbound/mod.rs b/parachain/pallets/basic-channel/src/inbound/mod.rs new file mode 100644 index 0000000000000..b97090fc5bc8e --- /dev/null +++ b/parachain/pallets/basic-channel/src/inbound/mod.rs @@ -0,0 +1,93 @@ +use frame_support::{ + decl_error, decl_event, decl_module, decl_storage, + dispatch::DispatchResult, +}; +use frame_system::{self as system, ensure_signed}; +use sp_core::H160; +use sp_std::prelude::*; +use sp_std::convert::TryFrom; +use artemis_core::{ + ChannelId, Message, MessageId, + MessageDispatch, Verifier, +}; + +use envelope::Envelope; + +#[cfg(test)] +mod test; + +mod envelope; + +pub trait Config: system::Config { + type Event: From + Into<::Event>; + + /// Verifier module for message verification. + type Verifier: Verifier; + + /// Verifier module for message verification. + type MessageDispatch: MessageDispatch; +} + +decl_storage! { + trait Store for Module as BasicInboundModule { + pub SourceChannel get(fn source_channel) config(): H160; + pub Nonce: u64; + } +} + +decl_event! { + pub enum Event { + + } +} + +decl_error! { + pub enum Error for Module { + /// Message came from an invalid outbound channel on the Ethereum side. + InvalidSourceChannel, + /// Message has an invalid envelope. + InvalidEnvelope, + /// Message has an unexpected nonce. + InvalidNonce, + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + + type Error = Error; + + fn deposit_event() = default; + + #[weight = 0] + pub fn submit(origin, message: Message) -> DispatchResult { + ensure_signed(origin)?; + // submit message to verifier for verification + let log = T::Verifier::verify(&message)?; + + // Decode log into an Envelope + let envelope = Envelope::try_from(log).map_err(|_| Error::::InvalidEnvelope)?; + + // Verify that the message was submitted to us from a known + // outbound channel on the ethereum side + if envelope.channel != SourceChannel::get() { + return Err(Error::::InvalidSourceChannel.into()) + } + + // Verify message nonce + Nonce::try_mutate(|nonce| -> DispatchResult { + if envelope.nonce != *nonce + 1 { + Err(Error::::InvalidNonce.into()) + } else { + *nonce += 1; + Ok(()) + } + })?; + + let message_id = MessageId::new(ChannelId::Basic, envelope.nonce); + T::MessageDispatch::dispatch(envelope.source, message_id, &envelope.payload); + + Ok(()) + } + } +} diff --git a/parachain/pallets/basic-channel/src/inbound/test.rs b/parachain/pallets/basic-channel/src/inbound/test.rs new file mode 100644 index 0000000000000..66a5538ce9d5b --- /dev/null +++ b/parachain/pallets/basic-channel/src/inbound/test.rs @@ -0,0 +1,234 @@ + +use super::*; + +use sp_core::{H160, H256}; +use frame_support::{ + assert_ok, assert_noop, + parameter_types, + dispatch::DispatchError +}; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup, IdentifyAccount, Verify}, testing::Header, MultiSignature +}; +use sp_keyring::AccountKeyring as Keyring; +use sp_std::convert::From; + +use artemis_core::{MessageDispatch, ChannelId, Message, Proof}; +use artemis_ethereum::Log; + +use hex_literal::hex; + +use crate::inbound::Error; + +use crate::inbound as basic_inbound_channel; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Module, Call, Storage, Event}, + BasicInboundChannel: basic_inbound_channel::{Module, Call, Storage, Event}, + } +); + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl system::Config for Test { + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); +} +// Mock verifier +pub struct MockVerifier; + +impl Verifier for MockVerifier { + fn verify(message: &Message) -> Result { + let log: Log = rlp::decode(&message.data).unwrap(); + Ok(log) + } +} + +// Mock Dispatch +pub struct MockMessageDispatch; + +impl MessageDispatch for MockMessageDispatch { + fn dispatch(_: H160, _: MessageId, _: &[u8]) {} +} + +impl basic_inbound_channel::Config for Test { + type Event = Event; + type Verifier = MockVerifier; + type MessageDispatch = MockMessageDispatch; +} + +pub fn new_tester(source_channel: H160) -> sp_io::TestExternalities { + new_tester_with_config(basic_inbound_channel::GenesisConfig { + source_channel, + }) +} + +pub fn new_tester_with_config(config: basic_inbound_channel::GenesisConfig) -> sp_io::TestExternalities { + let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + config.assimilate_storage(&mut storage).unwrap(); + + let mut ext: sp_io::TestExternalities = storage.into(); + ext.execute_with(|| System::set_block_number(1)); + ext +} + + +// The originating channel address for the messages below +const SOURCE_CHANNEL_ADDR: [u8; 20] = hex!["2d02f2234d0B6e35D8d8fD77705f535ACe681327"]; + +// Ethereum Log: +// address: 0xe4ab635d0bdc5668b3fcb4eaee1dec587998f4af (outbound channel contract) +// topics: ... +// data: +// source: 0x8f5acf5f15d4c3d654a759b96bb674a236c8c0f3 (ETH bank contract) +// nonce: 1 +// payload ... +const MESSAGE_DATA_0: [u8; 284] = hex!(" + f90119942d02f2234d0b6e35d8d8fd77705f535ace681327e1a0779b38144a38 + cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15bb8e000000000 + 00000000000000000a42cba2b7960a0ce216ade5d6a82574257023d800000000 + 0000000000000000000000000000000000000000000000000000000100000000 + 0000000000000000000000000000000000000000000000000000006000000000 + 000000000000000000000000000000000000000000000000000000570c018213 + dae5f9c236beab905c8305cb159c5fa1aae500d43593c715fdd31c61141abd04 + a99fd6822c8558854ccde39a5684e7a56da27d0000d9e9ac2d78030000000000 + 00000000000000000000000000000000000000000000000000000000 +"); + +// Ethereum Log: +// address: 0xe4ab635d0bdc5668b3fcb4eaee1dec587998f4af (outbound channel contract) +// topics: ... +// data: +// source: 0x8f5acf5f15d4c3d654a759b96bb674a236c8c0f3 (ETH bank contract) +// nonce: 1 +// payload ... +const MESSAGE_DATA_1: [u8; 284] = hex!(" + f90119942d02f2234d0b6e35d8d8fd77705f535ace681327e1a0779b38144a38 + cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15bb8e000000000 + 00000000000000000a42cba2b7960a0ce216ade5d6a82574257023d800000000 + 0000000000000000000000000000000000000000000000000000000200000000 + 0000000000000000000000000000000000000000000000000000006000000000 + 000000000000000000000000000000000000000000000000000000570c018213 + dae5f9c236beab905c8305cb159c5fa1aae500d43593c715fdd31c61141abd04 + a99fd6822c8558854ccde39a5684e7a56da27d0000d9e9ac2d78030000000000 + 00000000000000000000000000000000000000000000000000000000 +"); + +#[test] +fn test_submit_with_invalid_source_channel() { + new_tester(H160::zero()).execute_with(|| { + let relayer: AccountId = Keyring::Bob.into(); + let origin = Origin::signed(relayer); + + // Submit message + let message = Message { + data: MESSAGE_DATA_0.into(), + proof: Proof { + block_hash: Default::default(), + tx_index: Default::default(), + data: Default::default() + }, + }; + assert_noop!( + BasicInboundChannel::submit(origin.clone(), message.clone()), + Error::::InvalidSourceChannel + ); + }); +} + +#[test] +fn test_submit() { + new_tester(SOURCE_CHANNEL_ADDR.into()).execute_with(|| { + let chan_id = ChannelId::Basic; + let relayer: AccountId = Keyring::Bob.into(); + let origin = Origin::signed(relayer); + + // Submit message 1 + let message_1 = Message { + data: MESSAGE_DATA_0.into(), + proof: Proof { + block_hash: Default::default(), + tx_index: Default::default(), + data: Default::default() + }, + }; + assert_ok!(BasicInboundChannel::submit(origin.clone(), message_1)); + let nonce: u64 = Nonce::get(); + assert_eq!(nonce, 1); + + // Submit message 2 + let message_2 = Message { + data: MESSAGE_DATA_1.into(), + proof: Proof { + block_hash: Default::default(), + tx_index: Default::default(), + data: Default::default() + }, + }; + assert_ok!(BasicInboundChannel::submit(origin.clone(), message_2)); + let nonce: u64 = Nonce::get(); + assert_eq!(nonce, 2); + }); +} + +#[test] +fn test_submit_with_invalid_nonce() { + new_tester(SOURCE_CHANNEL_ADDR.into()).execute_with(|| { + let chan_id = ChannelId::Basic; + let relayer: AccountId = Keyring::Bob.into(); + let origin = Origin::signed(relayer); + + // Submit message + let message = Message { + data: MESSAGE_DATA_0.into(), + proof: Proof { + block_hash: Default::default(), + tx_index: Default::default(), + data: Default::default() + }, + }; + assert_ok!(BasicInboundChannel::submit(origin.clone(), message.clone())); + let nonce: u64 = Nonce::get(); + assert_eq!(nonce, 1); + + // Submit the same again + assert_noop!( + BasicInboundChannel::submit(origin.clone(), message.clone()), + Error::::InvalidNonce + ); + }); +} diff --git a/parachain/pallets/basic-channel/src/lib.rs b/parachain/pallets/basic-channel/src/lib.rs new file mode 100644 index 0000000000000..7a086a937a35b --- /dev/null +++ b/parachain/pallets/basic-channel/src/lib.rs @@ -0,0 +1,4 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod inbound; +pub mod outbound; diff --git a/parachain/pallets/basic-channel/src/outbound/mod.rs b/parachain/pallets/basic-channel/src/outbound/mod.rs new file mode 100644 index 0000000000000..39fff0bd85a11 --- /dev/null +++ b/parachain/pallets/basic-channel/src/outbound/mod.rs @@ -0,0 +1,55 @@ +use frame_support::{decl_error, decl_event, decl_module, decl_storage, + dispatch::DispatchResult, +}; +use frame_system::{self as system}; +use sp_core::H160; +use sp_std::prelude::*; +use artemis_core::{ + ChannelId, MessageNonce, MessageCommitment, +}; + +#[cfg(test)] +mod test; + +pub trait Config: system::Config { + type Event: From + Into<::Event>; + + /// Used by outbound channels to persist messages for outbound delivery. + type MessageCommitment: MessageCommitment; + +} + +decl_storage! { + trait Store for Module as BasicOutboundModule { + pub Nonce: u64; + } +} + +decl_event! { + pub enum Event { + MessageAccepted(MessageNonce), + } +} + +decl_error! { + pub enum Error for Module { + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + fn deposit_event() = default; + } +} + +impl Module { + pub fn submit(_: &T::AccountId, target: H160, payload: &[u8]) -> DispatchResult { + Nonce::try_mutate(|nonce| -> DispatchResult { + *nonce += 1; + T::MessageCommitment::add(ChannelId::Basic, target, *nonce, payload)?; + >::deposit_event(Event::MessageAccepted(*nonce)); + Ok(()) + }) + } +} diff --git a/parachain/pallets/basic-channel/src/outbound/test.rs b/parachain/pallets/basic-channel/src/outbound/test.rs new file mode 100644 index 0000000000000..2972cffd7db6e --- /dev/null +++ b/parachain/pallets/basic-channel/src/outbound/test.rs @@ -0,0 +1,98 @@ +use super::*; + +use sp_core::{H160, H256}; +use frame_support::{ + assert_ok, + parameter_types, +}; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup, IdentifyAccount, Verify}, testing::Header, MultiSignature +}; +use sp_keyring::AccountKeyring as Keyring; +use sp_std::convert::From; + +use artemis_core::MessageCommitment; + +use crate::outbound as basic_outbound_channel; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Module, Call, Storage, Event}, + BasicOutboundChannel: basic_outbound_channel::{Module, Call, Storage, Event}, + } +); + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl system::Config for Test { + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); +} + +// Mock Commitments +pub struct MockMessageCommitment; + +impl MessageCommitment for MockMessageCommitment { + fn add(_: ChannelId, _: H160, _: u64, _: &[u8]) -> DispatchResult { + Ok(()) + } +} + +impl basic_outbound_channel::Config for Test { + type Event = Event; + type MessageCommitment = MockMessageCommitment; +} + +pub fn new_tester() -> sp_io::TestExternalities { + 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 +} + +#[test] +fn test_submit() { + new_tester().execute_with(|| { + let target = H160::zero(); + let who: AccountId = Keyring::Bob.into(); + + assert_ok!(BasicOutboundChannel::submit(&who, target, &vec![0, 1, 2])); + assert_eq!(Nonce::get(), 1); + + assert_ok!(BasicOutboundChannel::submit(&who, target, &vec![0, 1, 2])); + assert_eq!(Nonce::get(), 2); + }); +} diff --git a/parachain/pallets/bridge/src/channel/inbound.rs b/parachain/pallets/bridge/src/channel/inbound.rs deleted file mode 100644 index f9ff9895e4ddd..0000000000000 --- a/parachain/pallets/bridge/src/channel/inbound.rs +++ /dev/null @@ -1,132 +0,0 @@ -use frame_support::{dispatch::{DispatchError, DispatchResult}, traits::Get, storage::StorageMap}; -use sp_std::{cell::Cell, marker::PhantomData, boxed::Box}; -use artemis_core::{ChannelId, MessageId, MessageDispatch}; -use artemis_core::rewards::RewardRelayer; -use crate::{ - Config, Error, InboundChannels, - envelope::Envelope, primitives::{InboundChannel, InboundChannelData} -}; - -/// Construct an inbound channel object -pub fn make_inbound_channel(channel_id: ChannelId) -> Box> -where - T: Config -{ - match channel_id { - ChannelId::Basic => Box::new(BasicInboundChannel::::new()), - ChannelId::Incentivized => Box::new(IncentivizedInboundChannel::::new()), - } -} - -/// Basic Channel -struct BasicInboundChannel { - channel_id: ChannelId, - storage: Storage -} - -impl BasicInboundChannel { - fn new() -> Self { - Self { - channel_id: ChannelId::Basic, - storage: Storage::new(ChannelId::Basic) - } - } -} - -impl InboundChannel for BasicInboundChannel { - fn submit(&self, relayer: &T::AccountId, envelope: &Envelope) -> DispatchResult { - self.storage.try_mutate::<_,DispatchError,_>(|data| { - if envelope.nonce != data.nonce + 1 { - return Err(Error::::BadNonce.into()) - } - data.nonce += 1; - Ok(()) - })?; - - let message_id = MessageId::new(self.channel_id, envelope.nonce); - T::MessageDispatch::dispatch(envelope.source, message_id, &envelope.payload); - - Ok(()) - } -} - -/// Incentivized Channel -struct IncentivizedInboundChannel { - channel_id: ChannelId, - storage: Storage -} - -impl IncentivizedInboundChannel { - fn new() -> Self { - Self { - channel_id: ChannelId::Incentivized, - storage: Storage::new(ChannelId::Incentivized) - } - } -} - -impl InboundChannel for IncentivizedInboundChannel { - fn submit(&self, relayer: &T::AccountId, envelope: &Envelope) -> DispatchResult { - self.storage.try_mutate::<_,DispatchError,_>(|data| { - if envelope.nonce != data.nonce + 1 { - return Err(Error::::BadNonce.into()) - } - data.nonce += 1; - Ok(()) - })?; - - T::RewardRelayer::pay_relayer(&T::RewardsAccount::get(), relayer, 0.into()); - - let message_id = MessageId::new(self.channel_id, envelope.nonce); - T::MessageDispatch::dispatch(envelope.source, message_id, &envelope.payload); - - Ok(()) - } -} - -struct Storage { - channel_id: ChannelId, - cached_data: Cell>, - phantom: PhantomData -} - -impl Storage { - fn new(channel_id: ChannelId) -> Self { - Storage { - channel_id, - cached_data: Cell::new(None), - phantom: PhantomData - } - } - - #[allow(dead_code)] - fn get(&self) -> InboundChannelData { - match self.cached_data.get() { - Some(data) => data, - None => { - let data = InboundChannels::get(self.channel_id); - self.cached_data.set(Some(data)); - data - } - } - } - - #[allow(dead_code)] - fn set(&self, data: InboundChannelData) { - self.cached_data.set(Some(data)); - InboundChannels::insert(self.channel_id, data) - } - - #[allow(dead_code)] - fn try_mutate(&self, f: F) -> Result - where - F: FnOnce(&mut InboundChannelData) -> Result - { - let mut data = self.get(); - let result = f(&mut data); - if result.is_ok() { - self.set(data); - } - result - } -} diff --git a/parachain/pallets/bridge/src/channel/mod.rs b/parachain/pallets/bridge/src/channel/mod.rs deleted file mode 100644 index 5756441b7897f..0000000000000 --- a/parachain/pallets/bridge/src/channel/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod inbound; -pub mod outbound; diff --git a/parachain/pallets/bridge/src/channel/outbound.rs b/parachain/pallets/bridge/src/channel/outbound.rs deleted file mode 100644 index 4929109125d45..0000000000000 --- a/parachain/pallets/bridge/src/channel/outbound.rs +++ /dev/null @@ -1,118 +0,0 @@ -use frame_support::{ - dispatch::DispatchResult, - storage::StorageMap, -}; -use sp_core::H160; -use sp_std::{cell::Cell, marker::PhantomData, boxed::Box}; -use artemis_core::{ChannelId, MessageCommitment}; -use crate::{ - Event, - Module, - Config, - OutboundChannels, - primitives::{OutboundChannel, OutboundChannelData} -}; - -/// Construct an Outbound channel object -pub fn make_outbound_channel(channel_id: ChannelId) -> Box { - match channel_id { - ChannelId::Basic => Box::new(BasicOutboundChannel::::new()), - ChannelId::Incentivized => Box::new(IncentivizedOutboundChannel::::new()), - } -} - -struct BasicOutboundChannel { - id: ChannelId, - storage: Storage -} - -impl BasicOutboundChannel { - fn new() -> Self { - Self { - id: ChannelId::Basic, - storage: Storage::new(ChannelId::Basic) - } - } -} - -impl OutboundChannel for BasicOutboundChannel { - // This implementation is a WIP! - fn submit(&self, target: H160, payload: &[u8]) -> DispatchResult { - self.storage.try_mutate(|data| { - data.nonce += 1; - T::MessageCommitment::add(self.id, target, data.nonce, payload)?; - >::deposit_event(Event::MessageAccepted(self.id, data.nonce)); - Ok(()) - }) - } -} - -struct IncentivizedOutboundChannel { - id: ChannelId, - storage: Storage -} - -impl IncentivizedOutboundChannel { - fn new() -> Self { - Self { - id: ChannelId::Incentivized, - storage: Storage::new(ChannelId::Incentivized) - } - } -} - -impl OutboundChannel for IncentivizedOutboundChannel { - // This implementation is a WIP! - fn submit(&self, target: H160, payload: &[u8]) -> DispatchResult { - self.storage.try_mutate(|data| { - data.nonce += 1; - T::MessageCommitment::add(self.id, target, data.nonce, payload)?; - >::deposit_event(Event::MessageAccepted(self.id, data.nonce)); - Ok(()) - }) - } -} - -struct Storage { - channel_id: ChannelId, - cached_data: Cell>, - phantom: PhantomData -} - -impl Storage { - fn new(channel_id: ChannelId) -> Self { - Storage { - channel_id, - cached_data: Cell::new(None), - phantom: PhantomData - } - } - - fn get(&self) -> OutboundChannelData { - match self.cached_data.get() { - Some(data) => data, - None => { - let data = OutboundChannels::get(self.channel_id); - self.cached_data.set(Some(data)); - data - } - } - } - - fn set(&self, data: OutboundChannelData) { - self.cached_data.set(Some(data)); - OutboundChannels::insert(self.channel_id, data) - } - - fn try_mutate(&self, f: F) -> Result - where - F: FnOnce(&mut OutboundChannelData) -> Result - { - let mut data = self.get(); - let result = f(&mut data); - if result.is_ok() { - self.set(data); - } - result - } -} diff --git a/parachain/pallets/bridge/src/lib.rs b/parachain/pallets/bridge/src/lib.rs deleted file mode 100644 index 24f14df8ae62a..0000000000000 --- a/parachain/pallets/bridge/src/lib.rs +++ /dev/null @@ -1,153 +0,0 @@ -//! # Bridge -//! -//! The Bridge module is the primary interface for submitting external messages to the parachain. -//! -//! ## Implementation -//! -//! Before a [`Message`] is dispatched to a target [`Application`], it is submitted to a [`Verifier`] for verification. -//! -//! ## Interface -//! -//! ### Dispatchable Calls -//! -//! - `submit`: Submit a message for verification and dispatch. -//! - -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(unused_variables)] - -use frame_support::{ - decl_error, decl_event, decl_module, decl_storage, - dispatch::DispatchResult, - storage::StorageMap, - traits::Get, - Parameter, -}; -use frame_system::{self as system, ensure_signed}; -use sp_core::H160; -use sp_std::prelude::*; -use sp_std::convert::TryFrom; -use artemis_core::{ - ChannelId, SubmitOutbound, Message, MessageId, - MessageCommitment, MessageDispatch, Verifier, - SourceChannelConfig, - rewards::RewardRelayer, -}; -use channel::inbound::make_inbound_channel; -use channel::outbound::make_outbound_channel; -use primitives::{InboundChannelData, OutboundChannelData}; -use envelope::Envelope; - -use sp_runtime::traits::Zero; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -mod channel; -mod primitives; -mod envelope; - -type MessageNonce = u64; - -pub trait Config: system::Config { - type Event: From + Into<::Event>; - - /// Verifier module for message verification. - type Verifier: Verifier; - - /// Used by outbound channels to persist messages for outbound delivery. - type MessageCommitment: MessageCommitment; - - /// Verifier module for message verification. - type MessageDispatch: MessageDispatch; - - /// Source of funds to pay relayers - type RewardsAccount: Get; - - /// Fee type - type InboundMessageFee: PartialOrd + Parameter + Zero + From; - - type RewardRelayer: RewardRelayer; -} - -decl_storage! { - trait Store for Module as BridgeModule { - /// Outbound (source) channels on Ethereum from whom we will accept messages. - pub SourceChannels: map hasher(identity) H160 => Option; - /// Storage for inbound channels. - pub InboundChannels: map hasher(identity) ChannelId => InboundChannelData; - /// Storage for outbound channels. - pub OutboundChannels: map hasher(identity) ChannelId => OutboundChannelData; - } - add_extra_genesis { - config(source_channels): SourceChannelConfig; - build(|config: &GenesisConfig| { - let sources = config.source_channels; - SourceChannels::insert(sources.basic.address, ChannelId::Basic); - SourceChannels::insert(sources.incentivized.address, ChannelId::Incentivized); - }); - } -} - -decl_event! { - /// Events for the Bridge module. - pub enum Event { - /// Message has been accepted by an outbound channel - MessageAccepted(ChannelId, MessageNonce), - } -} - -decl_error! { - pub enum Error for Module { - /// Message came from an invalid outbound channel on the Ethereum side. - InvalidSourceChannel, - /// Message has an invalid envelope. - InvalidEnvelope, - /// Message has an unexpected nonce. - BadNonce, - /// Target application not found. - AppNotFound, - } -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - - type Error = Error; - - fn deposit_event() = default; - - #[weight = 0] - pub fn submit(origin, message: Message) -> DispatchResult { - let relayer = ensure_signed(origin)?; - // submit message to verifier for verification - let log = T::Verifier::verify(&message)?; - - // Decode log into an Envelope - let envelope = Envelope::try_from(log).map_err(|_| Error::::InvalidEnvelope)?; - - // Verify that the message was submitted to us from a known - // outbound channel on the ethereum side - let channel_id = SourceChannels::get(envelope.channel) - .ok_or(Error::::InvalidSourceChannel)?; - - // Submit to an inbound channel for further processing - let channel = make_inbound_channel::(channel_id); - channel.submit(&relayer, &envelope) - } - } -} - -impl SubmitOutbound for Module { - - // Submit a message to to Ethereum, taking into account the desired - // channel for delivery. - fn submit(channel_id: ChannelId, target: H160, payload: &[u8]) -> DispatchResult { - // Construct channel object from storage - let channel = make_outbound_channel::(channel_id); - channel.submit(target, payload) - } -} diff --git a/parachain/pallets/bridge/src/mock.rs b/parachain/pallets/bridge/src/mock.rs deleted file mode 100644 index 1d6e514731dc5..0000000000000 --- a/parachain/pallets/bridge/src/mock.rs +++ /dev/null @@ -1,159 +0,0 @@ - -use super::*; - -use sp_core::H256; -use frame_support::{parameter_types, - dispatch::DispatchError -}; -use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup, IdentifyAccount, Verify}, testing::Header, MultiSignature -}; -use sp_keyring::AccountKeyring as Keyring; -use sp_std::convert::From; - -use artemis_core::{MessageCommitment, MessageDispatch, ChannelId, SourceChannel, SourceChannelConfig, rewards::InstantRewards}; -use artemis_ethereum::Log; - -use crate as bridge; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Module, Call, Storage, Event}, - Balances: pallet_balances::{Module, Call, Storage, Event}, - Bridge: bridge::{Module, Call, Storage, Event}, - } -); - -pub type Signature = MultiSignature; -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; -pub type Balance = u128; - -parameter_types! { - pub const BlockHashCount: u64 = 250; -} - -impl system::Config for Test { - type BaseCallFilter = (); - type BlockWeights = (); - type BlockLength = (); - type Origin = Origin; - type Call = Call; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); -} - - -parameter_types! { - pub const ExistentialDeposit: u128 = 1; - pub const MaxLocks: u32 = 50; -} - -impl pallet_balances::Config for Test { - /// The ubiquitous event type. - type Event = Event; - type MaxLocks = MaxLocks; - /// The type for recording an account's balance. - type Balance = Balance; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); -} - -// Mock verifier -pub struct MockVerifier; - -impl Verifier for MockVerifier { - fn verify(message: &Message) -> Result { - let log: Log = rlp::decode(&message.data).unwrap(); - Ok(log) - } -} - -// Mock Commitments -pub struct MockMessageCommitment; - -impl MessageCommitment for MockMessageCommitment { - fn add(channel_id: ChannelId, target: H160, nonce: u64, payload: &[u8]) -> DispatchResult { - Ok(()) - } -} - -// Mock Dispatch -pub struct MockMessageDispatch; - -impl MessageDispatch for MockMessageDispatch { - fn dispatch(_: H160, _: MessageId, _: &[u8]) {} -} - -parameter_types! { - pub RewardsAccount: AccountId = Keyring::Eve.into(); -} - -impl bridge::Config for Test { - type Event = Event; - type Verifier = MockVerifier; - type MessageCommitment = MockMessageCommitment; - type MessageDispatch = MockMessageDispatch; - type RewardsAccount = RewardsAccount; - type InboundMessageFee = Balance; - type RewardRelayer = InstantRewards; -} - -pub fn new_tester() -> sp_io::TestExternalities { - new_tester_with_config(bridge::GenesisConfig { - source_channels: SourceChannelConfig { - basic: SourceChannel { - address: H160::zero(), - }, - incentivized: SourceChannel { - address: H160::zero(), - } - } - }) -} - -pub fn new_tester_with_source_channels(basic: H160, incentivized: H160) -> sp_io::TestExternalities { - new_tester_with_config(bridge::GenesisConfig { - source_channels: SourceChannelConfig { - basic: SourceChannel { - address: basic, - }, - incentivized: SourceChannel { - address: incentivized, - } - } - }) -} - -pub fn new_tester_with_config(config: bridge::GenesisConfig) -> sp_io::TestExternalities { - let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - config.assimilate_storage(&mut storage).unwrap(); - - let mut ext: sp_io::TestExternalities = storage.into(); - ext.execute_with(|| System::set_block_number(1)); - ext -} diff --git a/parachain/pallets/bridge/src/primitives.rs b/parachain/pallets/bridge/src/primitives.rs deleted file mode 100644 index 1f40965db7e7d..0000000000000 --- a/parachain/pallets/bridge/src/primitives.rs +++ /dev/null @@ -1,31 +0,0 @@ -use frame_support::dispatch::DispatchResult; -use sp_runtime::RuntimeDebug; -use sp_core::H160; -use codec::{Encode, Decode}; - -use crate::envelope::Envelope; - - -/// Persistent storage for inbound channels -#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, Default, RuntimeDebug)] -pub struct InboundChannelData { - pub nonce: u64 -} -/// Persistent storage for outbound channels -#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, Default, RuntimeDebug)] -pub struct OutboundChannelData { - pub nonce: u64 -} - -/// Handles messages inbound from Ethereum -pub trait InboundChannel -{ - /// Submit a message envelope for processing - fn submit(&self, relayer: &AccountId, envelope: &Envelope) -> DispatchResult; -} - -/// Handles messages outbound to Ethereum -pub trait OutboundChannel { - /// Submit a message payload for processing - fn submit(&self, target: H160, payload: &[u8]) -> DispatchResult; -} diff --git a/parachain/pallets/bridge/src/tests.rs b/parachain/pallets/bridge/src/tests.rs deleted file mode 100644 index dc9cbaff63489..0000000000000 --- a/parachain/pallets/bridge/src/tests.rs +++ /dev/null @@ -1,242 +0,0 @@ -use frame_support::{assert_ok, assert_noop}; -use frame_support::storage::StorageMap; -use sp_keyring::AccountKeyring as Keyring; -use sp_core::H160; - -use artemis_core::{ChannelId, Message, Proof}; - -use hex_literal::hex; - -use crate::{ - Error, - mock::{new_tester, new_tester_with_source_channels, Test, Bridge, AccountId, Origin}, - OutboundChannels, InboundChannels, - channel::outbound::make_outbound_channel, - primitives::{OutboundChannelData, InboundChannelData} -}; - -#[test] -fn test_submit_outbound_basic() { - new_tester().execute_with(|| { - let chan_id = ChannelId::Basic; - let target = H160::zero(); - let channel = make_outbound_channel::(chan_id); - - assert_ok!(channel.submit(target, &vec![0, 1, 2])); - - let data: OutboundChannelData = OutboundChannels::get(chan_id); - assert_eq!(data.nonce, 1); - - assert_ok!(channel.submit(target, &vec![0, 1, 2])); - - let data: OutboundChannelData = OutboundChannels::get(chan_id); - assert_eq!(data.nonce, 2); - }); -} - - -#[test] -fn test_submit_outbound_incentivized() { - new_tester().execute_with(|| { - let chan_id = ChannelId::Incentivized; - let target = H160::zero(); - let channel = make_outbound_channel::(chan_id); - - assert_ok!(channel.submit(target, &vec![0, 1, 2])); - - let data: OutboundChannelData = OutboundChannels::get(chan_id); - assert_eq!(data.nonce, 1); - - assert_ok!(channel.submit(target, &vec![0, 1, 2])); - - let data: OutboundChannelData = OutboundChannels::get(chan_id); - assert_eq!(data.nonce, 2); - }); -} - -// The originating channel address for the messages below -const SOURCE_CHANNEL_ADDR: [u8; 20] = hex!["2d02f2234d0B6e35D8d8fD77705f535ACe681327"]; - -// Ethereum Log: -// address: 0xe4ab635d0bdc5668b3fcb4eaee1dec587998f4af (outbound channel contract) -// topics: ... -// data: -// source: 0x8f5acf5f15d4c3d654a759b96bb674a236c8c0f3 (ETH bank contract) -// nonce: 1 -// payload ... -const MESSAGE_DATA_0: [u8; 284] = hex!(" - f90119942d02f2234d0b6e35d8d8fd77705f535ace681327e1a0779b38144a38 - cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15bb8e000000000 - 00000000000000000a42cba2b7960a0ce216ade5d6a82574257023d800000000 - 0000000000000000000000000000000000000000000000000000000100000000 - 0000000000000000000000000000000000000000000000000000006000000000 - 000000000000000000000000000000000000000000000000000000570c018213 - dae5f9c236beab905c8305cb159c5fa1aae500d43593c715fdd31c61141abd04 - a99fd6822c8558854ccde39a5684e7a56da27d0000d9e9ac2d78030000000000 - 00000000000000000000000000000000000000000000000000000000 -"); - -// Ethereum Log: -// address: 0xe4ab635d0bdc5668b3fcb4eaee1dec587998f4af (outbound channel contract) -// topics: ... -// data: -// source: 0x8f5acf5f15d4c3d654a759b96bb674a236c8c0f3 (ETH bank contract) -// nonce: 1 -// payload ... -const MESSAGE_DATA_1: [u8; 284] = hex!(" - f90119942d02f2234d0b6e35d8d8fd77705f535ace681327e1a0779b38144a38 - cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15bb8e000000000 - 00000000000000000a42cba2b7960a0ce216ade5d6a82574257023d800000000 - 0000000000000000000000000000000000000000000000000000000200000000 - 0000000000000000000000000000000000000000000000000000006000000000 - 000000000000000000000000000000000000000000000000000000570c018213 - dae5f9c236beab905c8305cb159c5fa1aae500d43593c715fdd31c61141abd04 - a99fd6822c8558854ccde39a5684e7a56da27d0000d9e9ac2d78030000000000 - 00000000000000000000000000000000000000000000000000000000 -"); - -#[test] -fn test_submit_inbound_invalid_source_channel() { - new_tester_with_source_channels(H160::zero(), H160::zero()).execute_with(|| { - let relayer: AccountId = Keyring::Bob.into(); - let origin = Origin::signed(relayer); - - // Submit message - let message = Message { - data: MESSAGE_DATA_0.into(), - proof: Proof { - block_hash: Default::default(), - tx_index: Default::default(), - data: Default::default() - }, - }; - assert_noop!( - Bridge::submit(origin.clone(), message.clone()), - Error::::InvalidSourceChannel - ); - }); -} - -#[test] -fn test_submit_inbound_basic() { - new_tester_with_source_channels(SOURCE_CHANNEL_ADDR.into(), H160::zero()).execute_with(|| { - let chan_id = ChannelId::Basic; - let relayer: AccountId = Keyring::Bob.into(); - let origin = Origin::signed(relayer); - - // Submit message 1 - let message_1 = Message { - data: MESSAGE_DATA_0.into(), - proof: Proof { - block_hash: Default::default(), - tx_index: Default::default(), - data: Default::default() - }, - }; - assert_ok!(Bridge::submit(origin.clone(), message_1)); - let data: InboundChannelData = InboundChannels::get(chan_id); - assert_eq!(data.nonce, 1); - - // Submit message 2 - let message_2 = Message { - data: MESSAGE_DATA_1.into(), - proof: Proof { - block_hash: Default::default(), - tx_index: Default::default(), - data: Default::default() - }, - }; - assert_ok!(Bridge::submit(origin.clone(), message_2)); - let data: InboundChannelData = InboundChannels::get(chan_id); - assert_eq!(data.nonce, 2); - }); -} -#[test] -fn test_submit_inbound_basic_bad_nonce() { - new_tester_with_source_channels(SOURCE_CHANNEL_ADDR.into(), H160::zero()).execute_with(|| { - let chan_id = ChannelId::Basic; - let relayer: AccountId = Keyring::Bob.into(); - let origin = Origin::signed(relayer); - - // Submit message - let message = Message { - data: MESSAGE_DATA_0.into(), - proof: Proof { - block_hash: Default::default(), - tx_index: Default::default(), - data: Default::default() - }, - }; - assert_ok!(Bridge::submit(origin.clone(), message.clone())); - let data: InboundChannelData = InboundChannels::get(chan_id); - assert_eq!(data.nonce, 1); - - // Submit the same again - assert_noop!( - Bridge::submit(origin.clone(), message.clone()), - Error::::BadNonce - ); - }); -} - -#[test] -fn test_submit_inbound_incentivized() { - new_tester_with_source_channels(H160::zero(), SOURCE_CHANNEL_ADDR.into()).execute_with(|| { - let chan_id = ChannelId::Incentivized; - let relayer: AccountId = Keyring::Bob.into(); - let origin = Origin::signed(relayer); - - // Submit message 1 - let message_1 = Message { - data: MESSAGE_DATA_0.into(), - proof: Proof { - block_hash: Default::default(), - tx_index: Default::default(), - data: Default::default() - }, - }; - assert_ok!(Bridge::submit(origin.clone(), message_1)); - let data: InboundChannelData = InboundChannels::get(chan_id); - assert_eq!(data.nonce, 1); - - // Submit message 2 - let message_2 = Message { - data: MESSAGE_DATA_1.into(), - proof: Proof { - block_hash: Default::default(), - tx_index: Default::default(), - data: Default::default() - }, - }; - assert_ok!(Bridge::submit(origin.clone(), message_2)); - let data: InboundChannelData = InboundChannels::get(chan_id); - assert_eq!(data.nonce, 2); - }); -} -#[test] -fn test_submit_inbound_incentivized_bad_nonce() { - new_tester_with_source_channels(H160::zero(), SOURCE_CHANNEL_ADDR.into()).execute_with(|| { - let chan_id = ChannelId::Incentivized; - let relayer: AccountId = Keyring::Bob.into(); - let origin = Origin::signed(relayer); - - // Submit message - let message = Message { - data: MESSAGE_DATA_0.into(), - proof: Proof { - block_hash: Default::default(), - tx_index: Default::default(), - data: Default::default() - }, - }; - assert_ok!(Bridge::submit(origin.clone(), message.clone())); - let data: InboundChannelData = InboundChannels::get(chan_id); - assert_eq!(data.nonce, 1); - - // Submit the same again - assert_noop!( - Bridge::submit(origin.clone(), message.clone()), - Error::::BadNonce - ); - }); -} diff --git a/parachain/pallets/dot-app/src/lib.rs b/parachain/pallets/dot-app/src/lib.rs index 8c0859dee8129..7822b318f668d 100644 --- a/parachain/pallets/dot-app/src/lib.rs +++ b/parachain/pallets/dot-app/src/lib.rs @@ -20,7 +20,7 @@ use sp_runtime::{ SaturatedConversion, }; -use artemis_core::{ChannelId, SubmitOutbound}; +use artemis_core::{ChannelId, OutboundRouter}; mod payload; use payload::OutboundPayload; @@ -38,7 +38,7 @@ pub trait Config: system::Config { type Currency: Currency; - type SubmitOutbound: SubmitOutbound; + type OutboundRouter: OutboundRouter; type CallOrigin: EnsureOrigin; @@ -90,7 +90,7 @@ decl_module! { amount: amount.saturated_into::(), }; - T::SubmitOutbound::submit(channel_id, Address::get(), &message.encode())?; + T::OutboundRouter::submit(channel_id, &who, Address::get(), &message.encode())?; Self::deposit_event(RawEvent::Locked(who.clone(), recipient, amount)); Ok(()) } diff --git a/parachain/pallets/dot-app/src/mock.rs b/parachain/pallets/dot-app/src/mock.rs index bd85f59f140ee..6fd06d21e28ea 100644 --- a/parachain/pallets/dot-app/src/mock.rs +++ b/parachain/pallets/dot-app/src/mock.rs @@ -1,18 +1,19 @@ // Mock runtime -use sp_core::{H160, H256}; +use sp_std::marker::PhantomData; + use frame_support::{ - parameter_types, - dispatch::{DispatchError, DispatchResult}, + dispatch::{DispatchError, DispatchResult}, + parameter_types, }; +use frame_system as system; +use sp_core::{H160, H256}; use sp_runtime::{ - traits::{ - BlakeTwo256, IdentityLookup, IdentifyAccount, Verify, - }, testing::Header, MultiSignature, - ModuleId, + testing::Header, + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + ModuleId, MultiSignature, }; -use frame_system as system; -use artemis_core::{ChannelId, SubmitOutbound}; +use artemis_core::{ChannelId, OutboundRouter}; use crate as dot_app; @@ -20,16 +21,16 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Module, Call, Storage, Event}, - Balances: pallet_balances::{Module, Call, Storage, Event}, - Dispatch: artemis_dispatch::{Module, Call, Storage, Origin, Event}, - DOTApp: dot_app::{Module, Call, Config, Storage, Event}, - } + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Module, Call, Storage, Event}, + Balances: pallet_balances::{Module, Call, Storage, Event}, + Dispatch: artemis_dispatch::{Module, Call, Storage, Origin, Event}, + DOTApp: dot_app::{Module, Call, Config, Storage, Event}, + } ); pub type Signature = MultiSignature; @@ -39,91 +40,93 @@ pub type AccountId = <::Signer as IdentifyAccount>::Account pub type Balance = u128; parameter_types! { - pub const BlockHashCount: u64 = 250; + pub const BlockHashCount: u64 = 250; } impl system::Config for Test { - type BaseCallFilter = (); - type BlockWeights = (); - type BlockLength = (); - type Origin = Origin; - type Call = Call; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type DbWeight = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); } impl artemis_dispatch::Config for Test { - type Origin = Origin; - type Event = Event; - type MessageId = u64; - type Call = Call; - type CallFilter = (); + type Origin = Origin; + type Event = Event; + type MessageId = u64; + type Call = Call; + type CallFilter = (); } -pub struct MockSubmitOutbound; +pub struct MockOutboundRouter(PhantomData); -impl SubmitOutbound for MockSubmitOutbound { - fn submit(channel: ChannelId, _: H160, _: &[u8]) -> DispatchResult { - if channel == ChannelId::Basic { - return Err(DispatchError::Other("some error!")) - } +impl OutboundRouter for MockOutboundRouter { + fn submit(channel: ChannelId, _: &AccountId, _: H160, _: &[u8]) -> DispatchResult { + if channel == ChannelId::Basic { + return Err(DispatchError::Other("some error!")); + } Ok(()) - } + } } parameter_types! { - pub const ExistentialDeposit: u128 = 1; - pub const MaxLocks: u32 = 50; + pub const ExistentialDeposit: u128 = 1; + pub const MaxLocks: u32 = 50; } impl pallet_balances::Config for Test { - type MaxLocks = MaxLocks; - /// The type for recording an account's balance. - type Balance = Balance; - /// The ubiquitous event type. - type Event = Event; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); + type MaxLocks = MaxLocks; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); } parameter_types! { - pub const DotModuleId: ModuleId = ModuleId(*b"s/dotapp"); + pub const DotModuleId: ModuleId = ModuleId(*b"s/dotapp"); } impl dot_app::Config for Test { - type Event = Event; - type Currency = Balances; - type SubmitOutbound = MockSubmitOutbound; - type CallOrigin = artemis_dispatch::EnsureEthereumAccount; - type ModuleId = DotModuleId; + type Event = Event; + type Currency = Balances; + type OutboundRouter = MockOutboundRouter; + type CallOrigin = artemis_dispatch::EnsureEthereumAccount; + type ModuleId = DotModuleId; } pub fn new_tester() -> sp_io::TestExternalities { - let mut storage = system::GenesisConfig::default().build_storage::().unwrap(); - - let config = dot_app::GenesisConfig { - address: H160::repeat_byte(1), - }; - config.assimilate_storage(&mut storage).unwrap(); - - let mut ext: sp_io::TestExternalities = storage.into(); - ext.execute_with(|| System::set_block_number(1)); - ext + let mut storage = system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let config = dot_app::GenesisConfig { + address: H160::repeat_byte(1), + }; + config.assimilate_storage(&mut storage).unwrap(); + + let mut ext: sp_io::TestExternalities = storage.into(); + ext.execute_with(|| System::set_block_number(1)); + ext } diff --git a/parachain/pallets/dot-app/src/tests.rs b/parachain/pallets/dot-app/src/tests.rs index fd4888ccee9e8..0b93e4af9f3d7 100644 --- a/parachain/pallets/dot-app/src/tests.rs +++ b/parachain/pallets/dot-app/src/tests.rs @@ -122,7 +122,6 @@ fn should_not_lock_on_add_commitment_failure() { }); } - // Used to prove safety of conversion from DOT to wrapped DOT (See BaseDOTApp.sol) #[test] fn should_max_dot_convert_to_wrapped_dot() { diff --git a/parachain/pallets/erc20-app/src/lib.rs b/parachain/pallets/erc20-app/src/lib.rs index f42410efd9d55..39f290a891819 100644 --- a/parachain/pallets/erc20-app/src/lib.rs +++ b/parachain/pallets/erc20-app/src/lib.rs @@ -27,7 +27,7 @@ use sp_runtime::traits::StaticLookup; use sp_std::prelude::*; use sp_core::{H160, U256}; -use artemis_core::{ChannelId, SubmitOutbound, AssetId, MultiAsset}; +use artemis_core::{ChannelId, OutboundRouter, AssetId, MultiAsset}; mod payload; use payload::OutboundPayload; @@ -37,12 +37,13 @@ mod mock; #[cfg(test)] mod tests; + pub trait Config: system::Config { type Event: From> + Into<::Event>; type Assets: MultiAsset<::AccountId>; - type SubmitOutbound: SubmitOutbound; + type OutboundRouter: OutboundRouter; type CallOrigin: EnsureOrigin; } @@ -95,7 +96,7 @@ decl_module! { amount: amount }; - T::SubmitOutbound::submit(channel_id, Address::get(), &message.encode())?; + T::OutboundRouter::submit(channel_id, &who, Address::get(), &message.encode())?; Self::deposit_event(RawEvent::Burned(token, who.clone(), recipient, amount)); Ok(()) diff --git a/parachain/pallets/erc20-app/src/mock.rs b/parachain/pallets/erc20-app/src/mock.rs index 36f59594e63dd..1e626c2d572af 100644 --- a/parachain/pallets/erc20-app/src/mock.rs +++ b/parachain/pallets/erc20-app/src/mock.rs @@ -1,8 +1,10 @@ // Mock runtime +use sp_std::marker::PhantomData; + use sp_core::{H160, H256}; use frame_support::{ parameter_types, - dispatch::DispatchResult, + dispatch::{DispatchResult, DispatchError}, }; use sp_runtime::{ traits::{ @@ -11,7 +13,7 @@ use sp_runtime::{ }; use frame_system as system; -use artemis_core::{ChannelId, AssetId, SubmitOutbound}; +use artemis_core::{ChannelId, AssetId, OutboundRouter}; use crate as erc20_app; @@ -76,10 +78,13 @@ impl artemis_dispatch::Config for Test { type CallFilter = (); } -pub struct MockSubmitOutbound; +pub struct MockOutboundRouter(PhantomData); -impl SubmitOutbound for MockSubmitOutbound { - fn submit(_: ChannelId, _: H160, _: &[u8]) -> DispatchResult { +impl OutboundRouter for MockOutboundRouter { + fn submit(channel: ChannelId, _: &AccountId, _: H160, _: &[u8]) -> DispatchResult { + if channel == ChannelId::Basic { + return Err(DispatchError::Other("some error!")); + } Ok(()) } } @@ -91,7 +96,7 @@ parameter_types! { impl erc20_app::Config for Test { type Event = Event; type Assets = Assets; - type SubmitOutbound = MockSubmitOutbound; + type OutboundRouter = MockOutboundRouter; type CallOrigin = artemis_dispatch::EnsureEthereumAccount; } diff --git a/parachain/pallets/erc20-app/src/tests.rs b/parachain/pallets/erc20-app/src/tests.rs index d216003e0822f..5078059631f16 100644 --- a/parachain/pallets/erc20-app/src/tests.rs +++ b/parachain/pallets/erc20-app/src/tests.rs @@ -1,5 +1,5 @@ use crate::mock::{new_tester, Event, System, AccountId, Origin, Assets, ERC20App}; -use frame_support::{assert_ok}; +use frame_support::{assert_ok, assert_noop, dispatch::DispatchError}; use sp_keyring::AccountKeyring as Keyring; use sp_core::H160; use artemis_core::{ChannelId, AssetId, MultiAsset}; @@ -57,3 +57,25 @@ fn burn_should_emit_bridge_event() { ); }); } + +#[test] +fn should_not_burn_on_commitment_failure() { + new_tester().execute_with(|| { + let token_id = H160::repeat_byte(1); + let sender: AccountId = Keyring::Bob.into(); + let recipient = H160::repeat_byte(9); + + Assets::deposit(AssetId::Token(token_id), &sender, 500.into()).unwrap(); + + assert_noop!( + ERC20App::burn( + Origin::signed(sender.clone()), + ChannelId::Basic, + token_id, + recipient.clone(), + 20.into() + ), + DispatchError::Other("some error!") + ); + }); +} diff --git a/parachain/pallets/eth-app/src/lib.rs b/parachain/pallets/eth-app/src/lib.rs index 171aea7a6ea4e..84067c870e4b7 100644 --- a/parachain/pallets/eth-app/src/lib.rs +++ b/parachain/pallets/eth-app/src/lib.rs @@ -27,7 +27,7 @@ use sp_runtime::traits::StaticLookup; use sp_std::prelude::*; use sp_core::{H160, U256}; -use artemis_core::{ChannelId, SubmitOutbound, SingleAsset}; +use artemis_core::{ChannelId, SingleAsset, OutboundRouter}; mod payload; use payload::OutboundPayload; @@ -43,7 +43,7 @@ pub trait Config: system::Config { type Asset: SingleAsset<::AccountId>; - type SubmitOutbound: SubmitOutbound; + type OutboundRouter: OutboundRouter; type CallOrigin: EnsureOrigin; } @@ -95,7 +95,7 @@ decl_module! { amount: amount }; - T::SubmitOutbound::submit(channel_id, Address::get(), &message.encode())?; + T::OutboundRouter::submit(channel_id, &who, Address::get(), &message.encode())?; Self::deposit_event(RawEvent::Burned(who.clone(), recipient, amount)); Ok(()) diff --git a/parachain/pallets/eth-app/src/mock.rs b/parachain/pallets/eth-app/src/mock.rs index b34db78bbedf1..b1d665a7a5b74 100644 --- a/parachain/pallets/eth-app/src/mock.rs +++ b/parachain/pallets/eth-app/src/mock.rs @@ -1,8 +1,10 @@ +use sp_std::marker::PhantomData; + // Mock runtime use sp_core::{H160, H256}; use frame_support::{ parameter_types, - dispatch::DispatchResult, + dispatch::{DispatchError, DispatchResult}, }; use sp_runtime::{ traits::{ @@ -11,10 +13,10 @@ use sp_runtime::{ }; use frame_system as system; -use artemis_core::{ChannelId, AssetId, SubmitOutbound}; +use artemis_core::{ChannelId, AssetId, OutboundRouter}; use artemis_assets::SingleAssetAdaptor; -use crate as eth_app; +use crate as eth_app; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -77,10 +79,13 @@ impl artemis_dispatch::Config for Test { type CallFilter = (); } -pub struct MockSubmitOutbound; +pub struct MockOutboundRouter(PhantomData); -impl SubmitOutbound for MockSubmitOutbound { - fn submit(_: ChannelId, _: H160, _: &[u8]) -> DispatchResult { +impl OutboundRouter for MockOutboundRouter { + fn submit(channel: ChannelId, _: &AccountId, _: H160, _: &[u8]) -> DispatchResult { + if channel == ChannelId::Basic { + return Err(DispatchError::Other("some error!")); + } Ok(()) } } @@ -92,7 +97,7 @@ parameter_types! { impl eth_app::Config for Test { type Event = Event; type Asset = Asset; - type SubmitOutbound = MockSubmitOutbound; + type OutboundRouter = MockOutboundRouter; type CallOrigin = artemis_dispatch::EnsureEthereumAccount; } diff --git a/parachain/pallets/eth-app/src/tests.rs b/parachain/pallets/eth-app/src/tests.rs index 260e1b37c2e89..ee611750abe02 100644 --- a/parachain/pallets/eth-app/src/tests.rs +++ b/parachain/pallets/eth-app/src/tests.rs @@ -1,5 +1,5 @@ use crate::mock::{new_tester, AccountId, Origin, Event, System, Asset, ETHApp}; -use frame_support::{assert_ok}; +use frame_support::{assert_ok, assert_noop, dispatch::DispatchError}; use sp_keyring::AccountKeyring as Keyring; use sp_core::H160; use crate::RawEvent; @@ -53,3 +53,23 @@ fn burn_should_emit_bridge_event() { ); }); } + +#[test] +fn should_not_burn_on_commitment_failure() { + new_tester().execute_with(|| { + let sender: AccountId = Keyring::Bob.into(); + let recipient = H160::repeat_byte(9); + + Asset::deposit(&sender, 500.into()).unwrap(); + + assert_noop!( + ETHApp::burn( + Origin::signed(sender.clone()), + ChannelId::Basic, + recipient.clone(), + 20.into() + ), + DispatchError::Other("some error!") + ); + }); +} diff --git a/parachain/pallets/bridge/Cargo.toml b/parachain/pallets/incentivized-channel/Cargo.toml similarity index 93% rename from parachain/pallets/bridge/Cargo.toml rename to parachain/pallets/incentivized-channel/Cargo.toml index 24392625f30f8..0f91186b660f4 100644 --- a/parachain/pallets/bridge/Cargo.toml +++ b/parachain/pallets/incentivized-channel/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "pallet-bridge" -description = "Artemis Bridge Pallet" +name = "artemis-incentivized-channel" +description = "Artemis Incentivized Channel" version = "0.1.1" edition = "2018" authors = ["Snowfork "] @@ -17,7 +17,6 @@ frame-support = { git = "https://github.com/paritytech/substrate.git", branch = frame-system = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1", default-features = false } artemis-core = { path = "../../primitives/core", default-features = false } @@ -27,10 +26,10 @@ ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "eth [dev-dependencies] pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "rococo-v1" } hex-literal = { version = "0.3.1" } rlp = { version = "0.5" } - [features] default = ["std"] std = [ @@ -39,7 +38,6 @@ std = [ "frame-support/std", "frame-system/std", "sp-core/std", - "sp-io/std", "sp-runtime/std", "sp-std/std", "artemis-core/std", diff --git a/parachain/pallets/incentivized-channel/src/inbound/envelope.rs b/parachain/pallets/incentivized-channel/src/inbound/envelope.rs new file mode 100644 index 0000000000000..ec5003b28a713 --- /dev/null +++ b/parachain/pallets/incentivized-channel/src/inbound/envelope.rs @@ -0,0 +1,114 @@ +use ethabi::{Event, Param, ParamKind, Token}; +use artemis_ethereum::{log::Log, H160}; + +use sp_core::RuntimeDebug; +use sp_std::prelude::*; +use sp_std::convert::TryFrom; + +// Used to decode a raw Ethereum log into an [`Envelope`]. +static EVENT_ABI: &Event = &Event { + signature: "Message(address,uint64,bytes)", + inputs: &[ + Param { kind: ParamKind::Address, indexed: false }, + Param { kind: ParamKind::Uint(64), indexed: false }, + Param { kind: ParamKind::Bytes, indexed: false }, + ], + anonymous: false +}; + +/// An inbound message that has had its outer envelope decoded. +#[derive(Clone, PartialEq, Eq, RuntimeDebug)] +pub struct Envelope { + /// The address of the outbound channel on Ethereum that forwarded this message. + pub channel: H160, + /// The application on Ethereum where the message originated from. + pub source: H160, + /// A nonce for enforcing replay protection and ordering. + pub nonce: u64, + /// Fee paid by user for relaying the message +// pub fee: u128, + /// The inner payload generated from the source application. + pub payload: Vec, +} + +#[derive(Copy, Clone, PartialEq, Eq, RuntimeDebug)] +pub struct EnvelopeDecodeError; + +impl TryFrom for Envelope { + type Error = EnvelopeDecodeError; + + fn try_from(log: Log) -> Result { + let tokens = EVENT_ABI.decode(log.topics, log.data) + .map_err(|_| EnvelopeDecodeError)?; + + let mut iter = tokens.into_iter(); + + let source = match iter.next().ok_or(EnvelopeDecodeError)? { + Token::Address(source) => source, + _ => return Err(EnvelopeDecodeError) + }; + + let nonce = match iter.next().ok_or(EnvelopeDecodeError)? { + Token::Uint(value) => { + value.low_u64() + } + _ => return Err(EnvelopeDecodeError) + }; + + // let fee = match iter.next().ok_or(EnvelopeDecodeError)? { + // Token::Uint(value) => { + // value.low_u128() + // } + // _ => return Err(EnvelopeDecodeError) + // }; + + let payload = match iter.next().ok_or(EnvelopeDecodeError)? { + Token::Bytes(payload) => payload, + _ => return Err(EnvelopeDecodeError) + }; + + Ok(Self { + channel: log.address, + source, + nonce, + payload, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use hex_literal::hex; + + const LOG: [u8; 284] = hex!(" + f901199430d2da52e36f80b17fe2694a5e4900b81cf26344e1a0779b38144a38 + cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15bb8e000000000 + 0000000000000000abe98e5ef4dc7a5c4f317823986fe48649f0edbb00000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000006000000000 + 000000000000000000000000000000000000000000000000000000541ed28b61 + 269a6d3d28d07b1fd834ebe4e703368ed43593c715fdd31c61141abd04a99fd6 + 822c8558854ccde39a5684e7a56da27d00010000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000 + "); + + #[test] + fn test_try_from_log() { + let log: Log = rlp::decode(&LOG).unwrap(); + let envelope = Envelope::try_from(log).unwrap(); + + assert_eq!(envelope, + Envelope { + channel: hex!["30d2da52e36f80b17fe2694a5e4900b81cf26344"].into(), + source: hex!["abe98e5ef4dc7a5c4f317823986fe48649f0edbb"].into(), + nonce: 0, +// fee: 0, + payload: hex!(" + 1ed28b61269a6d3d28d07b1fd834ebe4e703368ed43593c715fdd31c61141abd + 04a99fd6822c8558854ccde39a5684e7a56da27d000100000000000000000000 + 0000000000000000000000000000000000000000" + ).into(), + }) + } +} diff --git a/parachain/pallets/incentivized-channel/src/inbound/mod.rs b/parachain/pallets/incentivized-channel/src/inbound/mod.rs new file mode 100644 index 0000000000000..fbf1bfd9a12b7 --- /dev/null +++ b/parachain/pallets/incentivized-channel/src/inbound/mod.rs @@ -0,0 +1,108 @@ +use frame_support::{ + decl_error, decl_event, decl_module, decl_storage, + dispatch::DispatchResult, + traits::Get, + Parameter, +}; +use frame_system::{self as system, ensure_signed}; +use sp_core::H160; +use sp_std::prelude::*; +use sp_std::convert::TryFrom; +use artemis_core::{ + ChannelId, Message, MessageId, + MessageDispatch, Verifier, + rewards::RewardRelayer, +}; + +use envelope::Envelope; + +use sp_runtime::traits::Zero; + +#[cfg(test)] +mod test; + +mod envelope; + +pub trait Config: system::Config { + type Event: From + Into<::Event>; + + /// Verifier module for message verification. + type Verifier: Verifier; + + /// Verifier module for message verification. + type MessageDispatch: MessageDispatch; + + /// Source of funds to pay relayers + type RewardsAccount: Get; + + /// Fee type + type InboundMessageFee: PartialOrd + Parameter + Zero + From; + + type RewardRelayer: RewardRelayer; +} + +decl_storage! { + trait Store for Module as IncentivizedInboundModule { + pub SourceChannel get(fn source_channel) config(): H160; + pub Nonce: u64; + } +} + +decl_event! { + pub enum Event { + + } +} + +decl_error! { + pub enum Error for Module { + /// Message came from an invalid outbound channel on the Ethereum side. + InvalidSourceChannel, + /// Message has an invalid envelope. + InvalidEnvelope, + /// Message has an unexpected nonce. + InvalidNonce, + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + + type Error = Error; + + fn deposit_event() = default; + + #[weight = 0] + pub fn submit(origin, message: Message) -> DispatchResult { + let relayer = ensure_signed(origin)?; + // submit message to verifier for verification + let log = T::Verifier::verify(&message)?; + + // Decode log into an Envelope + let envelope = Envelope::try_from(log).map_err(|_| Error::::InvalidEnvelope)?; + + // Verify that the message was submitted to us from a known + // outbound channel on the ethereum side + if envelope.channel != SourceChannel::get() { + return Err(Error::::InvalidSourceChannel.into()) + } + + // Verify message nonce + Nonce::try_mutate(|nonce| -> DispatchResult { + if envelope.nonce != *nonce + 1 { + Err(Error::::InvalidNonce.into()) + } else { + *nonce += 1; + Ok(()) + } + })?; + + T::RewardRelayer::pay_relayer(&T::RewardsAccount::get(), &relayer, 0.into()); + + let message_id = MessageId::new(ChannelId::Incentivized, envelope.nonce); + T::MessageDispatch::dispatch(envelope.source, message_id, &envelope.payload); + + Ok(()) + } + } +} diff --git a/parachain/pallets/incentivized-channel/src/inbound/test.rs b/parachain/pallets/incentivized-channel/src/inbound/test.rs new file mode 100644 index 0000000000000..ad0570ed5287e --- /dev/null +++ b/parachain/pallets/incentivized-channel/src/inbound/test.rs @@ -0,0 +1,272 @@ + +use super::*; + +use sp_core::{H160, H256}; +use frame_support::{ + assert_ok, assert_noop, + parameter_types, + dispatch::DispatchError +}; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup, IdentifyAccount, Verify}, testing::Header, MultiSignature +}; +use sp_keyring::AccountKeyring as Keyring; +use sp_std::convert::From; + +use artemis_core::{MessageCommitment, MessageDispatch, ChannelId, Message, Proof, rewards::InstantRewards}; +use artemis_ethereum::Log; + +use hex_literal::hex; + +use crate::inbound::Error; + +use crate::inbound as incentivized_inbound_channel; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Module, Call, Storage, Event}, + Balances: pallet_balances::{Module, Call, Storage, Event}, + IncentivizedInboundChannel: incentivized_inbound_channel::{Module, Call, Storage, Event}, + } +); + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl system::Config for Test { + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); +} + + +parameter_types! { + pub const ExistentialDeposit: u128 = 1; + pub const MaxLocks: u32 = 50; +} + +impl pallet_balances::Config for Test { + /// The ubiquitous event type. + type Event = Event; + type MaxLocks = MaxLocks; + /// The type for recording an account's balance. + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +// Mock verifier +pub struct MockVerifier; + +impl Verifier for MockVerifier { + fn verify(message: &Message) -> Result { + let log: Log = rlp::decode(&message.data).unwrap(); + Ok(log) + } +} + +// Mock Commitments +pub struct MockMessageCommitment; + +impl MessageCommitment for MockMessageCommitment { + fn add(channel_id: ChannelId, target: H160, nonce: u64, payload: &[u8]) -> DispatchResult { + Ok(()) + } +} + +// Mock Dispatch +pub struct MockMessageDispatch; + +impl MessageDispatch for MockMessageDispatch { + fn dispatch(_: H160, _: MessageId, _: &[u8]) {} +} + +parameter_types! { + pub RewardsAccount: AccountId = Keyring::Eve.into(); +} + + +impl incentivized_inbound_channel::Config for Test { + type Event = Event; + type Verifier = MockVerifier; + type MessageDispatch = MockMessageDispatch; + type RewardsAccount = RewardsAccount; + type InboundMessageFee = Balance; + type RewardRelayer = InstantRewards; +} + +pub fn new_tester(source_channel: H160) -> sp_io::TestExternalities { + new_tester_with_config(incentivized_inbound_channel::GenesisConfig { + source_channel, + }) +} + +pub fn new_tester_with_config(config: incentivized_inbound_channel::GenesisConfig) -> sp_io::TestExternalities { + let mut storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + config.assimilate_storage(&mut storage).unwrap(); + + let mut ext: sp_io::TestExternalities = storage.into(); + ext.execute_with(|| System::set_block_number(1)); + ext +} + + +// The originating channel address for the messages below +const SOURCE_CHANNEL_ADDR: [u8; 20] = hex!["2d02f2234d0B6e35D8d8fD77705f535ACe681327"]; + +// Ethereum Log: +// address: 0xe4ab635d0bdc5668b3fcb4eaee1dec587998f4af (outbound channel contract) +// topics: ... +// data: +// source: 0x8f5acf5f15d4c3d654a759b96bb674a236c8c0f3 (ETH bank contract) +// nonce: 1 +// payload ... +const MESSAGE_DATA_0: [u8; 284] = hex!(" + f90119942d02f2234d0b6e35d8d8fd77705f535ace681327e1a0779b38144a38 + cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15bb8e000000000 + 00000000000000000a42cba2b7960a0ce216ade5d6a82574257023d800000000 + 0000000000000000000000000000000000000000000000000000000100000000 + 0000000000000000000000000000000000000000000000000000006000000000 + 000000000000000000000000000000000000000000000000000000570c018213 + dae5f9c236beab905c8305cb159c5fa1aae500d43593c715fdd31c61141abd04 + a99fd6822c8558854ccde39a5684e7a56da27d0000d9e9ac2d78030000000000 + 00000000000000000000000000000000000000000000000000000000 +"); + +// Ethereum Log: +// address: 0xe4ab635d0bdc5668b3fcb4eaee1dec587998f4af (outbound channel contract) +// topics: ... +// data: +// source: 0x8f5acf5f15d4c3d654a759b96bb674a236c8c0f3 (ETH bank contract) +// nonce: 1 +// payload ... +const MESSAGE_DATA_1: [u8; 284] = hex!(" + f90119942d02f2234d0b6e35d8d8fd77705f535ace681327e1a0779b38144a38 + cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15bb8e000000000 + 00000000000000000a42cba2b7960a0ce216ade5d6a82574257023d800000000 + 0000000000000000000000000000000000000000000000000000000200000000 + 0000000000000000000000000000000000000000000000000000006000000000 + 000000000000000000000000000000000000000000000000000000570c018213 + dae5f9c236beab905c8305cb159c5fa1aae500d43593c715fdd31c61141abd04 + a99fd6822c8558854ccde39a5684e7a56da27d0000d9e9ac2d78030000000000 + 00000000000000000000000000000000000000000000000000000000 +"); + +#[test] +fn test_submit_with_invalid_source_channel() { + new_tester(H160::zero()).execute_with(|| { + let relayer: AccountId = Keyring::Bob.into(); + let origin = Origin::signed(relayer); + + // Submit message + let message = Message { + data: MESSAGE_DATA_0.into(), + proof: Proof { + block_hash: Default::default(), + tx_index: Default::default(), + data: Default::default() + }, + }; + assert_noop!( + IncentivizedInboundChannel::submit(origin.clone(), message.clone()), + Error::::InvalidSourceChannel + ); + }); +} + +#[test] +fn test_submit() { + new_tester(SOURCE_CHANNEL_ADDR.into()).execute_with(|| { + let chan_id = ChannelId::Basic; + let relayer: AccountId = Keyring::Bob.into(); + let origin = Origin::signed(relayer); + + // Submit message 1 + let message_1 = Message { + data: MESSAGE_DATA_0.into(), + proof: Proof { + block_hash: Default::default(), + tx_index: Default::default(), + data: Default::default() + }, + }; + assert_ok!(IncentivizedInboundChannel::submit(origin.clone(), message_1)); + let nonce: u64 = Nonce::get(); + assert_eq!(nonce, 1); + + // Submit message 2 + let message_2 = Message { + data: MESSAGE_DATA_1.into(), + proof: Proof { + block_hash: Default::default(), + tx_index: Default::default(), + data: Default::default() + }, + }; + assert_ok!(IncentivizedInboundChannel::submit(origin.clone(), message_2)); + let nonce: u64 = Nonce::get(); + assert_eq!(nonce, 2); + }); +} + +#[test] +fn test_submit_with_invalid_nonce() { + new_tester(SOURCE_CHANNEL_ADDR.into()).execute_with(|| { + let chan_id = ChannelId::Basic; + let relayer: AccountId = Keyring::Bob.into(); + let origin = Origin::signed(relayer); + + // Submit message + let message = Message { + data: MESSAGE_DATA_0.into(), + proof: Proof { + block_hash: Default::default(), + tx_index: Default::default(), + data: Default::default() + }, + }; + assert_ok!(IncentivizedInboundChannel::submit(origin.clone(), message.clone())); + let nonce: u64 = Nonce::get(); + assert_eq!(nonce, 1); + + // Submit the same again + assert_noop!( + IncentivizedInboundChannel::submit(origin.clone(), message.clone()), + Error::::InvalidNonce + ); + }); +} diff --git a/parachain/pallets/incentivized-channel/src/lib.rs b/parachain/pallets/incentivized-channel/src/lib.rs new file mode 100644 index 0000000000000..7a086a937a35b --- /dev/null +++ b/parachain/pallets/incentivized-channel/src/lib.rs @@ -0,0 +1,4 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod inbound; +pub mod outbound; diff --git a/parachain/pallets/incentivized-channel/src/outbound/mod.rs b/parachain/pallets/incentivized-channel/src/outbound/mod.rs new file mode 100644 index 0000000000000..93053dd7c2004 --- /dev/null +++ b/parachain/pallets/incentivized-channel/src/outbound/mod.rs @@ -0,0 +1,55 @@ +use frame_support::{decl_error, decl_event, decl_module, decl_storage, + dispatch::DispatchResult, +}; +use frame_system::{self as system}; +use sp_core::H160; +use sp_std::prelude::*; +use artemis_core::{ + ChannelId, MessageNonce, MessageCommitment, +}; + +#[cfg(test)] +mod test; + +pub trait Config: system::Config { + type Event: From + Into<::Event>; + + /// Used by outbound channels to persist messages for outbound delivery. + type MessageCommitment: MessageCommitment; + +} + +decl_storage! { + trait Store for Module as IncentivizedOutboundModule { + pub Nonce: u64; + } +} + +decl_event! { + pub enum Event { + MessageAccepted(MessageNonce), + } +} + +decl_error! { + pub enum Error for Module { + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + fn deposit_event() = default; + } +} + +impl Module { + pub fn submit(_: &T::AccountId, target: H160, payload: &[u8]) -> DispatchResult { + Nonce::try_mutate(|nonce| -> DispatchResult { + *nonce += 1; + T::MessageCommitment::add(ChannelId::Incentivized, target, *nonce, payload)?; + >::deposit_event(Event::MessageAccepted(*nonce)); + Ok(()) + }) + } +} diff --git a/parachain/pallets/incentivized-channel/src/outbound/test.rs b/parachain/pallets/incentivized-channel/src/outbound/test.rs new file mode 100644 index 0000000000000..93c40b2993daf --- /dev/null +++ b/parachain/pallets/incentivized-channel/src/outbound/test.rs @@ -0,0 +1,98 @@ +use super::*; + +use sp_core::{H160, H256}; +use frame_support::{ + assert_ok, + parameter_types, +}; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup, IdentifyAccount, Verify}, testing::Header, MultiSignature +}; +use sp_keyring::AccountKeyring as Keyring; +use sp_std::convert::From; + +use artemis_core::MessageCommitment; + +use crate::outbound as incentivized_outbound_channel; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Module, Call, Storage, Event}, + IncentivizedOutboundChannel: incentivized_outbound_channel::{Module, Call, Storage, Event}, + } +); + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl system::Config for Test { + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); +} + +// Mock Commitments +pub struct MockMessageCommitment; + +impl MessageCommitment for MockMessageCommitment { + fn add(_: ChannelId, _: H160, _: u64, _: &[u8]) -> DispatchResult { + Ok(()) + } +} + +impl incentivized_outbound_channel::Config for Test { + type Event = Event; + type MessageCommitment = MockMessageCommitment; +} + +pub fn new_tester() -> sp_io::TestExternalities { + 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 +} + +#[test] +fn test_submit() { + new_tester().execute_with(|| { + let target = H160::zero(); + let who: AccountId = Keyring::Bob.into(); + + assert_ok!(IncentivizedOutboundChannel::submit(&who, target, &vec![0, 1, 2])); + assert_eq!(Nonce::get(), 1); + + assert_ok!(IncentivizedOutboundChannel::submit(&who, target, &vec![0, 1, 2])); + assert_eq!(Nonce::get(), 2); + }); +} diff --git a/parachain/primitives/core/src/lib.rs b/parachain/primitives/core/src/lib.rs index 50abe4a32e46d..7098ffa5ad286 100644 --- a/parachain/primitives/core/src/lib.rs +++ b/parachain/primitives/core/src/lib.rs @@ -19,8 +19,7 @@ pub use types::{ Proof, ChannelId, MessageId, - SourceChannelConfig, - SourceChannel, + MessageNonce, }; pub use assets::{AssetId, MultiAsset, SingleAsset}; @@ -32,8 +31,8 @@ pub trait Verifier { } /// Outbound submission for applications -pub trait SubmitOutbound { - fn submit(channel_id: ChannelId, target: H160, payload: &[u8]) -> DispatchResult; +pub trait OutboundRouter { + fn submit(channel_id: ChannelId, who: &AccountId, target: H160, payload: &[u8]) -> DispatchResult; } /// Add a message to a commitment diff --git a/parachain/primitives/core/src/types.rs b/parachain/primitives/core/src/types.rs index 5e59315f93334..e62fd2478195f 100644 --- a/parachain/primitives/core/src/types.rs +++ b/parachain/primitives/core/src/types.rs @@ -2,14 +2,10 @@ use frame_support::RuntimeDebug; use sp_std::vec::Vec; -use sp_core::{H160, H256}; +use sp_core::H256; use enum_iterator::IntoEnumIterator; use codec::{Encode, Decode}; -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; - - #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] pub struct MessageId { pub channel_id: ChannelId, @@ -24,6 +20,8 @@ impl MessageId { } } +pub type MessageNonce = u64; + #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, IntoEnumIterator, RuntimeDebug)] pub enum ChannelId { Basic, @@ -52,16 +50,3 @@ pub struct Proof { // Proof keys and values pub data: (Vec>, Vec>), } - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct SourceChannelConfig { - pub basic: SourceChannel, - pub incentivized: SourceChannel, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct SourceChannel { - pub address: H160 -} diff --git a/parachain/runtime/Cargo.toml b/parachain/runtime/Cargo.toml index 7d516fc479fb6..31a347bfdf8d1 100644 --- a/parachain/runtime/Cargo.toml +++ b/parachain/runtime/Cargo.toml @@ -43,11 +43,11 @@ xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "rococo-v1" xcm-executor = { git = "https://github.com/paritytech/polkadot.git", branch = "rococo-v1", default-features = false } xcm-builder = { git = "https://github.com/paritytech/polkadot.git", branch = "rococo-v1", default-features = false } polkadot-parachain = { git = "https://github.com/paritytech/polkadot.git", branch = "rococo-v1", default-features = false } - artemis-core = { path = "../primitives/core", default-features = false } artemis-transfer = { path = "../pallets/transfer", default-features = false } artemis-xcm-support = { path = "../primitives/xcm-support", default-features = false } -bridge = { path = "../pallets/bridge", package = "pallet-bridge", default-features = false } +basic-channel = { path = "../pallets/basic-channel", package = "artemis-basic-channel", default-features = false } +incentivized-channel = { path = "../pallets/incentivized-channel", package = "artemis-incentivized-channel", default-features = false } dispatch = { path = "../pallets/dispatch", package = "artemis-dispatch", default-features = false } verifier-lightclient = { path = "../pallets/verifier-lightclient", package = "pallet-verifier-lightclient", default-features = false } assets = { path = "../pallets/assets", package = "artemis-assets", default-features = false } @@ -96,7 +96,8 @@ std = [ "xcm-executor/std", "xcm-builder/std", "polkadot-parachain/std", - "bridge/std", + "basic-channel/std", + "incentivized-channel/std", "verifier-lightclient/std", "assets/std", "dispatch/std", diff --git a/parachain/runtime/src/lib.rs b/parachain/runtime/src/lib.rs index de5572cd0f00f..c1ac478c5277b 100644 --- a/parachain/runtime/src/lib.rs +++ b/parachain/runtime/src/lib.rs @@ -30,7 +30,9 @@ pub use pallet_timestamp::Call as TimestampCall; pub use pallet_balances::Call as BalancesCall; pub use sp_runtime::{Permill, Perbill, ModuleId, traits::AccountIdConversion}; pub use frame_support::{ - construct_runtime, parameter_types, StorageValue, + construct_runtime, + dispatch::DispatchResult, + parameter_types, StorageValue, traits::{KeyOwnerProofSystem, Randomness, Filter}, weights::{ Weight, IdentityFee, @@ -40,7 +42,7 @@ pub use frame_support::{ use pallet_transaction_payment::FeeDetails; use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; -pub use artemis_core::{AssetId, ChannelId, MessageId, rewards::InstantRewards}; +pub use artemis_core::{AssetId, OutboundRouter, ChannelId, MessageId, rewards::InstantRewards}; use dispatch::EnsureEthereumAccount; pub use verifier_lightclient::{EthereumHeader, EthereumDifficultyConfig}; @@ -365,16 +367,54 @@ parameter_types! { pub RewardsAccount: AccountId = DotModuleId::get().into_account(); } -impl bridge::Config for Runtime { +use basic_channel::inbound as basic_channel_inbound; +use incentivized_channel::inbound as incentivized_channel_inbound; +use basic_channel::outbound as basic_channel_outbound; +use incentivized_channel::outbound as incentivized_channel_outbound; + + +impl basic_channel_inbound::Config for Runtime { type Event = Event; type Verifier = verifier_lightclient::Module; + type MessageDispatch = dispatch::Module; +} + +impl basic_channel_outbound::Config for Runtime { + type Event = Event; type MessageCommitment = commitments::Module; +} + +impl incentivized_channel_inbound::Config for Runtime { + type Event = Event; + type Verifier = verifier_lightclient::Module; type MessageDispatch = dispatch::Module; type RewardsAccount = RewardsAccount; type InboundMessageFee = Balance; type RewardRelayer = InstantRewards; } +impl incentivized_channel_outbound::Config for Runtime { + type Event = Event; + type MessageCommitment = commitments::Module; +} + +use sp_std::marker::PhantomData; +use sp_core::H160; + +pub struct SimpleOutboundRouter(PhantomData); + +impl OutboundRouter for SimpleOutboundRouter +where + T: basic_channel_outbound::Config + incentivized_channel_outbound::Config +{ + fn submit(channel_id: ChannelId, who: &T::AccountId, target: H160, payload: &[u8]) -> DispatchResult { + match channel_id { + ChannelId::Basic => basic_channel_outbound::Module::::submit(who, target, payload), + ChannelId::Incentivized => incentivized_channel_outbound::Module::::submit(who, target, payload), + } + } +} + pub const ROPSTEN_DIFFICULTY_CONFIG: EthereumDifficultyConfig = EthereumDifficultyConfig { byzantium_fork_block: 1700000, constantinople_fork_block: 4230000, @@ -424,14 +464,14 @@ parameter_types! { impl eth_app::Config for Runtime { type Event = Event; type Asset = assets::SingleAssetAdaptor; - type SubmitOutbound = bridge::Module; + type OutboundRouter = SimpleOutboundRouter; type CallOrigin = EnsureEthereumAccount; } impl erc20_app::Config for Runtime { type Event = Event; type Assets = assets::Module; - type SubmitOutbound = bridge::Module; + type OutboundRouter = SimpleOutboundRouter; type CallOrigin = EnsureEthereumAccount; } @@ -442,7 +482,7 @@ parameter_types! { impl dot_app::Config for Runtime { type Event = Event; type Currency = Balances; - type SubmitOutbound = bridge::Module; + type OutboundRouter = SimpleOutboundRouter; type CallOrigin = EnsureEthereumAccount; type ModuleId = DotModuleId; } @@ -453,26 +493,30 @@ construct_runtime!( NodeBlock = opaque::Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: frame_system::{Module, Call, Config, Storage, Event}, - Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent}, - Balances: pallet_balances::{Module, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Module, Storage}, - RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage}, - - ParachainInfo: parachain_info::{Module, Storage, Config}, - ParachainSystem: cumulus_pallet_parachain_system::{Module, Call, Storage, Inherent, Event}, - - Bridge: bridge::{Module, Call, Config, Storage, Event}, - Dispatch: dispatch::{Module, Call, Storage, Event, Origin}, - Commitments: commitments::{Module, Call, Config, Storage, Event}, - VerifierLightclient: verifier_lightclient::{Module, Call, Storage, Event, Config}, - Assets: assets::{Module, Call, Config, Storage, Event}, - ETH: eth_app::{Module, Call, Config, Storage, Event}, - ERC20: erc20_app::{Module, Call, Config, Storage, Event}, - DOT: dot_app::{Module, Call, Config, Storage, Event}, - - LocalXcmHandler: cumulus_pallet_xcm_handler::{Module, Event, Origin}, - Transfer: artemis_transfer::{Module, Call, Event}, + System: frame_system::{Module, Call, Config, Storage, Event} = 0, + Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent} = 1, + Balances: pallet_balances::{Module, Call, Storage, Config, Event} = 2, + TransactionPayment: pallet_transaction_payment::{Module, Storage} = 3, + RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Module, Call, Storage} = 4, + + ParachainInfo: parachain_info::{Module, Storage, Config} = 5, + ParachainSystem: cumulus_pallet_parachain_system::{Module, Call, Storage, Inherent, Event} = 6, + + BasicInboundChannel: basic_channel_inbound::{Module, Call, Config, Storage, Event} = 7, + BasicOutboundChannel: basic_channel_outbound::{Module, Storage, Event} = 8, + IncentivizedInboundChannel: incentivized_channel_inbound::{Module, Call, Config, Storage, Event} = 9, + IncentivizedOutboundChannel: incentivized_channel_outbound::{Module, Storage, Event} = 10, + Dispatch: dispatch::{Module, Call, Storage, Event, Origin} = 11, + Commitments: commitments::{Module, Call, Config, Storage, Event} = 15, + VerifierLightclient: verifier_lightclient::{Module, Call, Storage, Event, Config} = 16, + Assets: assets::{Module, Call, Config, Storage, Event} = 17, + + LocalXcmHandler: cumulus_pallet_xcm_handler::{Module, Event, Origin} = 18, + Transfer: artemis_transfer::{Module, Call, Event} = 19, + + ETH: eth_app::{Module, Call, Config, Storage, Event} = 12, + ERC20: erc20_app::{Module, Call, Config, Storage, Event} = 13, + DOT: dot_app::{Module, Call, Config, Storage, Event} = 14, } ); diff --git a/parachain/src/chain_spec.rs b/parachain/src/chain_spec.rs index 45a384eaecef3..12b7ad128fab6 100644 --- a/parachain/src/chain_spec.rs +++ b/parachain/src/chain_spec.rs @@ -4,7 +4,8 @@ use artemis_runtime::{ AccountId, EthereumHeader, BalancesConfig, GenesisConfig, SystemConfig, VerifierLightclientConfig, - BridgeConfig, ETHConfig, ERC20Config, DOTConfig, AssetsConfig, + BasicInboundChannelConfig, IncentivizedInboundChannelConfig, + ETHConfig, ERC20Config, DOTConfig, AssetsConfig, CommitmentsConfig, ParachainInfoConfig, WASM_BINARY, Signature, @@ -18,7 +19,7 @@ use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec; -use artemis_core::{AssetId, SourceChannelConfig, SourceChannel}; +use artemis_core::AssetId; /// Helper function to generate a crypto pair from seed pub fn get_from_seed(seed: &str) -> ::Public { @@ -111,15 +112,11 @@ fn testnet_genesis( // Configure endowed accounts with initial balance of 1 << 60. balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), }), - bridge: Some(BridgeConfig { - source_channels: SourceChannelConfig { - basic: SourceChannel { - address: hex!["2ffa5ecdbe006d30397c7636d3e015eee251369f"].into(), - }, - incentivized: SourceChannel { - address: hex!["eda338e4dc46038493b885327842fd3e301cab39"].into(), - } - }, + basic_channel_inbound: Some(BasicInboundChannelConfig { + source_channel: hex!["2ffa5ecdbe006d30397c7636d3e015eee251369f"].into(), + }), + incentivized_channel_inbound: Some(IncentivizedInboundChannelConfig { + source_channel: hex!["eda338e4dc46038493b885327842fd3e301cab39"].into(), }), assets: Some(AssetsConfig { balances: vec![ diff --git a/relayer/chain/chain.go b/relayer/chain/chain.go index 4480d548f7ad6..b33d864a846e3 100644 --- a/relayer/chain/chain.go +++ b/relayer/chain/chain.go @@ -21,7 +21,10 @@ type SubstrateOutboundMessage struct { } // Message from ethereum -type EthereumOutboundMessage substrate.Message +type EthereumOutboundMessage struct { + Call string + Args []interface{} +} type Header struct { HeaderData interface{} diff --git a/relayer/chain/ethereum/chain.go b/relayer/chain/ethereum/chain.go index 1a6411c1aec6f..ed0ac185af26b 100644 --- a/relayer/chain/ethereum/chain.go +++ b/relayer/chain/ethereum/chain.go @@ -9,7 +9,6 @@ import ( "github.com/snowfork/polkadot-ethereum/relayer/chain" "github.com/snowfork/polkadot-ethereum/relayer/contracts/inbound" - "github.com/snowfork/polkadot-ethereum/relayer/contracts/outbound" "github.com/snowfork/polkadot-ethereum/relayer/substrate" "golang.org/x/sync/errgroup" @@ -59,8 +58,7 @@ func (ch *Chain) SetReceiver(subMessages <-chan []chain.Message, _ <-chan chain. } func (ch *Chain) SetSender(ethMessages chan<- []chain.Message, ethHeaders chan<- chain.Header) error { - var contracts []*outbound.Contract - listener, err := NewListener(ch.config, ch.conn, ethMessages, ethHeaders, contracts, ch.log) + listener, err := NewListener(ch.config, ch.conn, ethMessages, ethHeaders, ch.log) if err != nil { return err } diff --git a/relayer/chain/ethereum/listener.go b/relayer/chain/ethereum/listener.go index 86860b78ce2a4..881eaf579bada 100644 --- a/relayer/chain/ethereum/listener.go +++ b/relayer/chain/ethereum/listener.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + etypes "github.com/ethereum/go-ethereum/core/types" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/sirupsen/logrus" @@ -22,22 +23,26 @@ const MaxMessagesPerSend = 10 // Listener streams the Ethereum blockchain for application events type Listener struct { - config *Config - conn *Connection - contracts []*outbound.Contract - messages chan<- []chain.Message - headers chan<- chain.Header - log *logrus.Entry + config *Config + conn *Connection + basicOutboundChannel *outbound.BasicOutboundChannel + incentivizedOutboundChannel *outbound.IncentivizedOutboundChannel + mapping map[common.Address]string + messages chan<- []chain.Message + headers chan<- chain.Header + log *logrus.Entry } -func NewListener(config *Config, conn *Connection, messages chan<- []chain.Message, headers chan<- chain.Header, contracts []*outbound.Contract, log *logrus.Entry) (*Listener, error) { +func NewListener(config *Config, conn *Connection, messages chan<- []chain.Message, headers chan<- chain.Header, log *logrus.Entry) (*Listener, error) { return &Listener{ - config: config, - conn: conn, - contracts: contracts, - messages: messages, - headers: headers, - log: log, + config: config, + conn: conn, + basicOutboundChannel: nil, + incentivizedOutboundChannel: nil, + mapping: make(map[common.Address]string), + messages: messages, + headers: headers, + log: log, }, nil } @@ -52,17 +57,20 @@ func (li *Listener) Start(cxt context.Context, eg *errgroup.Group, initBlockHeig return err } - contract, err := outbound.NewContract(common.HexToAddress(li.config.Channels.Basic.Outbound), li.conn.client) + basicOutboundChannel, err := outbound.NewBasicOutboundChannel(common.HexToAddress(li.config.Channels.Basic.Outbound), li.conn.client) if err != nil { return err } - li.contracts = append(li.contracts, contract) + li.basicOutboundChannel = basicOutboundChannel - contract, err = outbound.NewContract(common.HexToAddress(li.config.Channels.Incentivized.Outbound), li.conn.client) + incentivizedOutboundChannel, err := outbound.NewIncentivizedOutboundChannel(common.HexToAddress(li.config.Channels.Incentivized.Outbound), li.conn.client) if err != nil { return err } - li.contracts = append(li.contracts, contract) + li.incentivizedOutboundChannel = incentivizedOutboundChannel + + li.mapping[common.HexToAddress(li.config.Channels.Basic.Outbound)] = "BasicInboundChannel.submit" + li.mapping[common.HexToAddress(li.config.Channels.Incentivized.Outbound)] = "IncentivizedInboundChannel.submit" eg.Go(func() error { return li.pollEventsAndHeaders(cxt, initBlockHeight, descendantsUntilFinal, hcs) @@ -117,27 +125,31 @@ func (li *Listener) pollEventsAndHeaders( } finalizedBlockNumber := gethheader.Number.Uint64() - descendantsUntilFinal - var events []*outbound.ContractMessage + var events []*etypes.Log + + filterOptions := bind.FilterOpts{Start: finalizedBlockNumber, End: &finalizedBlockNumber, Context: ctx} - for _, channelContract := range li.contracts { - channelEvents, err := li.queryEvents(ctx, channelContract, finalizedBlockNumber, &finalizedBlockNumber) - if err != nil { - li.log.WithError(err).Error("Failure fetching event logs") - } + basicEvents, err := li.queryBasicEvents(li.basicOutboundChannel, &filterOptions) + if err != nil { + li.log.WithError(err).Error("Failure fetching event logs") + } + events = append(events, basicEvents...) - events = append(events, channelEvents...) + incentivizedEvents, err := li.queryIncentivizedEvents(li.incentivizedOutboundChannel, &filterOptions) + if err != nil { + li.log.WithError(err).Error("Failure fetching event logs") } + events = append(events, incentivizedEvents...) li.forwardEvents(ctx, hcs, events) } } } -func (li *Listener) queryEvents(ctx context.Context, contract *outbound.Contract, start uint64, end *uint64) ([]*outbound.ContractMessage, error) { - var events []*outbound.ContractMessage - filterOps := bind.FilterOpts{Start: start, End: end, Context: ctx} +func (li *Listener) queryBasicEvents(contract *outbound.BasicOutboundChannel, options *bind.FilterOpts) ([]*etypes.Log, error) { + var events []*etypes.Log - iter, err := contract.FilterMessage(&filterOps) + iter, err := contract.FilterMessage(options) if err != nil { return nil, err } @@ -151,34 +163,54 @@ func (li *Listener) queryEvents(ctx context.Context, contract *outbound.Contract } break } + events = append(events, &iter.Event.Raw) + } + return events, nil +} + +func (li *Listener) queryIncentivizedEvents(contract *outbound.IncentivizedOutboundChannel, options *bind.FilterOpts) ([]*etypes.Log, error) { + var events []*etypes.Log - events = append(events, iter.Event) + iter, err := contract.FilterMessage(options) + if err != nil { + return nil, err } + for { + more := iter.Next() + if !more { + err = iter.Error() + if err != nil { + return nil, err + } + break + } + events = append(events, &iter.Event.Raw) + } return events, nil } -func (li *Listener) forwardEvents(ctx context.Context, hcs *HeaderCacheState, events []*outbound.ContractMessage) { +func (li *Listener) forwardEvents(ctx context.Context, hcs *HeaderCacheState, events []*etypes.Log) { messages := make([]chain.Message, len(events)) for i, event := range events { - receiptTrie, err := hcs.GetReceiptTrie(ctx, event.Raw.BlockHash) + receiptTrie, err := hcs.GetReceiptTrie(ctx, event.BlockHash) if err != nil { li.log.WithFields(logrus.Fields{ - "blockHash": event.Raw.BlockHash.Hex(), - "blockNumber": event.Raw.BlockNumber, - "txHash": event.Raw.TxHash.Hex(), + "blockHash": event.BlockHash.Hex(), + "blockNumber": event.BlockNumber, + "txHash": event.TxHash.Hex(), }).WithError(err).Error("Failed to get receipt trie for event") return } - msg, err := MakeMessageFromEvent(event, receiptTrie, li.log) + msg, err := MakeMessageFromEvent(li.mapping, event, receiptTrie, li.log) if err != nil { li.log.WithFields(logrus.Fields{ - "address": event.Raw.Address.Hex(), - "blockHash": event.Raw.BlockHash.Hex(), - "blockNumber": event.Raw.BlockNumber, - "txHash": event.Raw.TxHash.Hex(), + "address": event.Address.Hex(), + "blockHash": event.BlockHash.Hex(), + "blockNumber": event.BlockNumber, + "txHash": event.TxHash.Hex(), }).WithError(err).Error("Failed to generate message from ethereum event") return } diff --git a/relayer/chain/ethereum/message.go b/relayer/chain/ethereum/message.go index a06808b9477f2..b97e9f899d288 100644 --- a/relayer/chain/ethereum/message.go +++ b/relayer/chain/ethereum/message.go @@ -7,24 +7,25 @@ import ( "bytes" "encoding/hex" + "github.com/ethereum/go-ethereum/common" + etypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" etrie "github.com/ethereum/go-ethereum/trie" "github.com/sirupsen/logrus" "github.com/snowfork/go-substrate-rpc-client/v2/types" "github.com/snowfork/polkadot-ethereum/relayer/chain" - "github.com/snowfork/polkadot-ethereum/relayer/contracts/outbound" "github.com/snowfork/polkadot-ethereum/relayer/substrate" ) -func MakeMessageFromEvent(event *outbound.ContractMessage, receiptsTrie *etrie.Trie, log *logrus.Entry) (*chain.EthereumOutboundMessage, error) { +func MakeMessageFromEvent(mapping map[common.Address]string, event *etypes.Log, receiptsTrie *etrie.Trie, log *logrus.Entry) (*chain.EthereumOutboundMessage, error) { // RLP encode event log's Address, Topics, and Data var buf bytes.Buffer - err := event.Raw.EncodeRLP(&buf) + err := event.EncodeRLP(&buf) if err != nil { return nil, err } - receiptKey, err := rlp.EncodeToBytes(event.Raw.TxIndex) + receiptKey, err := rlp.EncodeToBytes(event.TxIndex) if err != nil { return nil, err } @@ -35,23 +36,34 @@ func MakeMessageFromEvent(event *outbound.ContractMessage, receiptsTrie *etrie.T return nil, err } - message := substrate.Message{ + m := substrate.Message{ Data: buf.Bytes(), Proof: substrate.Proof{ - BlockHash: types.NewH256(event.Raw.BlockHash.Bytes()), - TxIndex: types.NewU32(uint32(event.Raw.TxIndex)), + BlockHash: types.NewH256(event.BlockHash.Bytes()), + TxIndex: types.NewU32(uint32(event.TxIndex)), Data: proof, }, } - value := hex.EncodeToString(message.Data) + value := hex.EncodeToString(m.Data) log.WithFields(logrus.Fields{ "payload": value, - "blockHash": message.Proof.BlockHash.Hex(), - "eventIndex": message.Proof.TxIndex, + "blockHash": m.Proof.BlockHash.Hex(), + "eventIndex": m.Proof.TxIndex, }).Debug("Generated message from Ethereum log") - msg := chain.EthereumOutboundMessage(message) + var args []interface{} + args = append(args, m) - return &msg, nil + call, ok := mapping[event.Address] + if !ok { + return nil, err + } + + message := chain.EthereumOutboundMessage{ + Call: call, + Args: args, + } + + return &message, nil } diff --git a/relayer/chain/ethereum/message_test.go b/relayer/chain/ethereum/message_test.go index 4fa5d97b2d8a5..35c2c13f1719c 100644 --- a/relayer/chain/ethereum/message_test.go +++ b/relayer/chain/ethereum/message_test.go @@ -5,11 +5,11 @@ import ( "fmt" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" gethTrie "github.com/ethereum/go-ethereum/trie" "github.com/sirupsen/logrus/hooks/test" "github.com/snowfork/polkadot-ethereum/relayer/chain/ethereum" - "github.com/snowfork/polkadot-ethereum/relayer/contracts/outbound" "github.com/snowfork/polkadot-ethereum/relayer/substrate" "github.com/stretchr/testify/assert" ) @@ -38,7 +38,7 @@ func TestMessage_Proof(t *testing.T) { // We'll prove inclusion for this event by proving inclusion for // the encapsulating receipt - event5_5 := outbound.ContractMessage{Raw: *receipts[5].Logs[5]} + event5_5 := receipts[5].Logs[5] receipt5Encoded, err := rlp.EncodeToBytes(receipts[5]) if err != nil { @@ -58,16 +58,24 @@ func TestMessage_Proof(t *testing.T) { } logger, _ := test.NewNullLogger() - msg, err := ethereum.MakeMessageFromEvent(&event5_5, receiptTrie, logger.WithField("test", "ing")) + mapping := make(map[common.Address]string) + mapping[event5_5.Address] = "InboundChannel.submit" + + msg, err := ethereum.MakeMessageFromEvent(mapping, event5_5, receiptTrie, logger.WithField("test", "ing")) assert.Nil(t, err) assert.NotNil(t, msg) - assert.Equal(t, block.Hash().Hex(), msg.Proof.BlockHash.Hex()) - key, err := rlp.EncodeToBytes(uint(msg.Proof.TxIndex)) + msgInner, ok := msg.Args[0].(substrate.Message) + if !ok { + panic("unexpected type") + } + + assert.Equal(t, block.Hash().Hex(), msgInner.Proof.BlockHash.Hex()) + key, err := rlp.EncodeToBytes(uint(msgInner.Proof.TxIndex)) if err != nil { panic(err) } - proofNodes := TestProof(*msg.Proof.Data) + proofNodes := TestProof(*msgInner.Proof.Data) provenReceipt, err := gethTrie.VerifyProof(block.ReceiptHash(), key, &proofNodes) assert.Nil(t, err) assert.Equal(t, provenReceipt, receipt5Encoded) diff --git a/relayer/chain/substrate/writer.go b/relayer/chain/substrate/writer.go index 21772ceb75b86..4eed003867023 100644 --- a/relayer/chain/substrate/writer.go +++ b/relayer/chain/substrate/writer.go @@ -14,7 +14,6 @@ import ( "github.com/snowfork/go-substrate-rpc-client/v2/types" "github.com/snowfork/polkadot-ethereum/relayer/chain" "github.com/snowfork/polkadot-ethereum/relayer/chain/ethereum" - "github.com/snowfork/polkadot-ethereum/relayer/substrate" ) type Writer struct { @@ -170,7 +169,7 @@ func (wr *Writer) write(ctx context.Context, c types.Call) error { func (wr *Writer) WriteMessages(ctx context.Context, msgs []*chain.EthereumOutboundMessage) error { for _, msg := range msgs { - c, err := types.NewCall(&wr.conn.metadata, "Bridge.submit", substrate.Message(*msg)) + c, err := types.NewCall(&wr.conn.metadata, msg.Call, msg.Args...) if err != nil { return err } diff --git a/relayer/chain/substrate/writer_test.go b/relayer/chain/substrate/writer_test.go index ba9a415e01374..7889afe4249b3 100644 --- a/relayer/chain/substrate/writer_test.go +++ b/relayer/chain/substrate/writer_test.go @@ -48,14 +48,22 @@ func TestWrite(t *testing.T) { t.Fatal(err) } - message := chain.EthereumOutboundMessage(chainTypes.Message{ - Data: []byte{1, 2, 3}, - Proof: chainTypes.Proof{ - BlockHash: types.NewH256([]byte{1, 2, 3}), - TxIndex: 1, - Data: chainTypes.NewProofData(), + var args []interface{} + args = append(args, + chainTypes.Message{ + Data: []byte{1, 2, 3}, + Proof: chainTypes.Proof{ + BlockHash: types.NewH256([]byte{1, 2, 3}), + TxIndex: 1, + Data: chainTypes.NewProofData(), + }, }, - }) + ) + + message := chain.EthereumOutboundMessage{ + Call: "BasicInboundChannel.submit", + Args: args, + } err = writer.WriteMessages(ctx, []*chain.EthereumOutboundMessage{&message}) if err != nil { diff --git a/relayer/cmd/fetch_messages.go b/relayer/cmd/fetch_messages.go index 14feb69f85c15..cf39fafd373db 100644 --- a/relayer/cmd/fetch_messages.go +++ b/relayer/cmd/fetch_messages.go @@ -15,9 +15,9 @@ import ( "github.com/spf13/cobra" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rlp" gethTrie "github.com/ethereum/go-ethereum/trie" "github.com/snowfork/go-substrate-rpc-client/v2/types" @@ -63,22 +63,25 @@ func FetchMessagesFn(cmd *cobra.Command, _ []string) error { return err } - contractEvents, trie, err := getEthContractEventsAndTrie(&config.Eth, blockHash, index) + mapping := make(map[common.Address]string) + + contractEvents, trie, err := getEthContractEventsAndTrie(&config.Eth, mapping, blockHash, index) if err != nil { return err } for _, event := range contractEvents { - printEthContractEventForSub(event, trie) + printEthContractEventForSub(mapping, event, trie) } return nil } func getEthContractEventsAndTrie( config *ethereum.Config, + mapping map[common.Address]string, blockHash gethCommon.Hash, index uint64, -) ([]*outbound.ContractMessage, *gethTrie.Trie, error) { +) ([]*gethTypes.Log, *gethTrie.Trie, error) { ctx := context.Background() kp, err := secp256k1.NewKeypairFromString(config.PrivateKey) if err != nil { @@ -92,6 +95,19 @@ func getEthContractEventsAndTrie( } defer conn.Close() + basicOutboundChannel, err := outbound.NewBasicOutboundChannel(common.HexToAddress(config.Channels.Basic.Outbound), conn.GetClient()) + if err != nil { + return nil, nil, err + } + + incentivizedOutboundChannel, err := outbound.NewIncentivizedOutboundChannel(common.HexToAddress(config.Channels.Incentivized.Outbound), conn.GetClient()) + if err != nil { + return nil, nil, err + } + + mapping[common.HexToAddress(config.Channels.Basic.Outbound)] = "BasicInboundChannel.submit" + mapping[common.HexToAddress(config.Channels.Incentivized.Outbound)] = "IncentivizedInboundChannel.submit" + loader := ethereum.DefaultBlockLoader{Conn: conn} block, err := loader.GetBlock(ctx, blockHash) if err != nil { @@ -104,75 +120,99 @@ func getEthContractEventsAndTrie( } trie := makeTrie(receipts) - contracts, err := getEthContractsFromConfig(config, conn.GetClient()) + allEvents := make([]*gethTypes.Log, 0) + + basicEvents, err := getEthBasicMessages(ctx, basicOutboundChannel, block.NumberU64(), index) if err != nil { return nil, nil, err } + allEvents = append(allEvents, basicEvents...) - contractMessages, err := getEthContractMessages(ctx, contracts, block.NumberU64(), index) + incentivizedEvents, err := getEthIncentivizedMessages(ctx, incentivizedOutboundChannel, block.NumberU64(), index) if err != nil { return nil, nil, err } + allEvents = append(allEvents, incentivizedEvents...) - return contractMessages, trie, nil + return allEvents, trie, nil } -func getEthContractsFromConfig(config *ethereum.Config, client *ethclient.Client) ([]*outbound.Contract, error) { - contractBasic, err := outbound.NewContract(gethCommon.HexToAddress(config.Channels.Basic.Outbound), client) +func getEthBasicMessages( + ctx context.Context, + contract *outbound.BasicOutboundChannel, + blockNumber uint64, + index uint64, +) ([]*gethTypes.Log, error) { + events := make([]*gethTypes.Log, 0) + filterOps := bind.FilterOpts{Start: blockNumber, End: &blockNumber, Context: ctx} + + iter, err := contract.FilterMessage(&filterOps) if err != nil { return nil, err } - contractIncentivized, err := outbound.NewContract(gethCommon.HexToAddress(config.Channels.Incentivized.Outbound), client) - if err != nil { - return nil, err + for { + more := iter.Next() + if !more { + err = iter.Error() + if err != nil { + return nil, err + } + break + } + + if uint64(iter.Event.Raw.TxIndex) != index { + continue + } + events = append(events, &iter.Event.Raw) } - return []*outbound.Contract{contractBasic, contractIncentivized}, nil + return events, nil } -func getEthContractMessages( +func getEthIncentivizedMessages( ctx context.Context, - contracts []*outbound.Contract, + contract *outbound.IncentivizedOutboundChannel, blockNumber uint64, index uint64, -) ([]*outbound.ContractMessage, error) { - events := make([]*outbound.ContractMessage, 0) +) ([]*gethTypes.Log, error) { + events := make([]*gethTypes.Log, 0) filterOps := bind.FilterOpts{Start: blockNumber, End: &blockNumber, Context: ctx} - for _, contract := range contracts { - iter, err := contract.FilterMessage(&filterOps) - if err != nil { - return nil, err - } - for { - more := iter.Next() - if !more { - err = iter.Error() - if err != nil { - return nil, err - } - break - } + iter, err := contract.FilterMessage(&filterOps) + if err != nil { + return nil, err + } - if uint64(iter.Event.Raw.TxIndex) != index { - continue + for { + more := iter.Next() + if !more { + err = iter.Error() + if err != nil { + return nil, err } - events = append(events, iter.Event) + break } + + if uint64(iter.Event.Raw.TxIndex) != index { + continue + } + events = append(events, &iter.Event.Raw) } return events, nil } -func printEthContractEventForSub(contractMsg *outbound.ContractMessage, trie *gethTrie.Trie) error { - message, err := ethereum.MakeMessageFromEvent(contractMsg, trie, logrus.WithField("chain", "Ethereum")) +func printEthContractEventForSub(mapping map[common.Address]string, event *gethTypes.Log, trie *gethTrie.Trie) error { + message, err := ethereum.MakeMessageFromEvent(mapping, event, trie, logrus.WithField("chain", "Ethereum")) if err != nil { return err } - messageForSub := substrate.Message(*message) - proof := messageForSub.Proof + msgInner, ok := message.Args[0].(substrate.Message) + if !ok { + return err + } formatProofVec := func(data []types.Bytes) string { hexRep := make([]string, len(data)) @@ -197,11 +237,11 @@ func printEthContractEventForSub(contractMsg *outbound.ContractMessage, trie *ge ), }, }`, - hex.EncodeToString(messageForSub.Data), - proof.BlockHash, - proof.TxIndex, - formatProofVec(proof.Data.Keys), - formatProofVec(proof.Data.Values), + hex.EncodeToString(msgInner.Data), + msgInner.Proof.BlockHash, + msgInner.Proof.TxIndex, + formatProofVec(msgInner.Proof.Data.Keys), + formatProofVec(msgInner.Proof.Data.Values), ) fmt.Println("") return nil diff --git a/relayer/contracts/generate.go b/relayer/contracts/generate.go index 915fb5c0cebff..d6795d9732643 100644 --- a/relayer/contracts/generate.go +++ b/relayer/contracts/generate.go @@ -1,4 +1,5 @@ //go:generate bash -c "jq .abi ../../ethereum/build/contracts/InboundChannel.json | abigen --abi - --type contract --pkg inbound --out inbound/contract.go" -//go:generate bash -c "jq .abi ../../ethereum/build/contracts/OutboundChannel.json | abigen --abi - --type contract --pkg outbound --out outbound/contract.go" +//go:generate bash -c "jq .abi ../../ethereum/build/contracts/BasicOutboundChannel.json | abigen --abi - --type BasicOutboundChannel --pkg outbound --out outbound/basic.go" +//go:generate bash -c "jq .abi ../../ethereum/build/contracts/IncentivizedOutboundChannel.json | abigen --abi - --type IncentivizedOutboundChannel --pkg outbound --out outbound/incentivized.go" package contracts diff --git a/relayer/contracts/outbound/basic.go b/relayer/contracts/outbound/basic.go new file mode 100644 index 0000000000000..8619dd1df630a --- /dev/null +++ b/relayer/contracts/outbound/basic.go @@ -0,0 +1,360 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package outbound + +import ( + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// BasicOutboundChannelABI is the input ABI used to generate the binding from. +const BasicOutboundChannelABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"Message\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"submit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + +// BasicOutboundChannel is an auto generated Go binding around an Ethereum contract. +type BasicOutboundChannel struct { + BasicOutboundChannelCaller // Read-only binding to the contract + BasicOutboundChannelTransactor // Write-only binding to the contract + BasicOutboundChannelFilterer // Log filterer for contract events +} + +// BasicOutboundChannelCaller is an auto generated read-only Go binding around an Ethereum contract. +type BasicOutboundChannelCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BasicOutboundChannelTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BasicOutboundChannelTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BasicOutboundChannelFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BasicOutboundChannelFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BasicOutboundChannelSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BasicOutboundChannelSession struct { + Contract *BasicOutboundChannel // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BasicOutboundChannelCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BasicOutboundChannelCallerSession struct { + Contract *BasicOutboundChannelCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BasicOutboundChannelTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BasicOutboundChannelTransactorSession struct { + Contract *BasicOutboundChannelTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BasicOutboundChannelRaw is an auto generated low-level Go binding around an Ethereum contract. +type BasicOutboundChannelRaw struct { + Contract *BasicOutboundChannel // Generic contract binding to access the raw methods on +} + +// BasicOutboundChannelCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BasicOutboundChannelCallerRaw struct { + Contract *BasicOutboundChannelCaller // Generic read-only contract binding to access the raw methods on +} + +// BasicOutboundChannelTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BasicOutboundChannelTransactorRaw struct { + Contract *BasicOutboundChannelTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBasicOutboundChannel creates a new instance of BasicOutboundChannel, bound to a specific deployed contract. +func NewBasicOutboundChannel(address common.Address, backend bind.ContractBackend) (*BasicOutboundChannel, error) { + contract, err := bindBasicOutboundChannel(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &BasicOutboundChannel{BasicOutboundChannelCaller: BasicOutboundChannelCaller{contract: contract}, BasicOutboundChannelTransactor: BasicOutboundChannelTransactor{contract: contract}, BasicOutboundChannelFilterer: BasicOutboundChannelFilterer{contract: contract}}, nil +} + +// NewBasicOutboundChannelCaller creates a new read-only instance of BasicOutboundChannel, bound to a specific deployed contract. +func NewBasicOutboundChannelCaller(address common.Address, caller bind.ContractCaller) (*BasicOutboundChannelCaller, error) { + contract, err := bindBasicOutboundChannel(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BasicOutboundChannelCaller{contract: contract}, nil +} + +// NewBasicOutboundChannelTransactor creates a new write-only instance of BasicOutboundChannel, bound to a specific deployed contract. +func NewBasicOutboundChannelTransactor(address common.Address, transactor bind.ContractTransactor) (*BasicOutboundChannelTransactor, error) { + contract, err := bindBasicOutboundChannel(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BasicOutboundChannelTransactor{contract: contract}, nil +} + +// NewBasicOutboundChannelFilterer creates a new log filterer instance of BasicOutboundChannel, bound to a specific deployed contract. +func NewBasicOutboundChannelFilterer(address common.Address, filterer bind.ContractFilterer) (*BasicOutboundChannelFilterer, error) { + contract, err := bindBasicOutboundChannel(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BasicOutboundChannelFilterer{contract: contract}, nil +} + +// bindBasicOutboundChannel binds a generic wrapper to an already deployed contract. +func bindBasicOutboundChannel(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(BasicOutboundChannelABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BasicOutboundChannel *BasicOutboundChannelRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BasicOutboundChannel.Contract.BasicOutboundChannelCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BasicOutboundChannel *BasicOutboundChannelRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BasicOutboundChannel.Contract.BasicOutboundChannelTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BasicOutboundChannel *BasicOutboundChannelRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BasicOutboundChannel.Contract.BasicOutboundChannelTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BasicOutboundChannel *BasicOutboundChannelCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BasicOutboundChannel.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BasicOutboundChannel *BasicOutboundChannelTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BasicOutboundChannel.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BasicOutboundChannel *BasicOutboundChannelTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BasicOutboundChannel.Contract.contract.Transact(opts, method, params...) +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint64) +func (_BasicOutboundChannel *BasicOutboundChannelCaller) Nonce(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _BasicOutboundChannel.contract.Call(opts, &out, "nonce") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint64) +func (_BasicOutboundChannel *BasicOutboundChannelSession) Nonce() (uint64, error) { + return _BasicOutboundChannel.Contract.Nonce(&_BasicOutboundChannel.CallOpts) +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint64) +func (_BasicOutboundChannel *BasicOutboundChannelCallerSession) Nonce() (uint64, error) { + return _BasicOutboundChannel.Contract.Nonce(&_BasicOutboundChannel.CallOpts) +} + +// Submit is a paid mutator transaction binding the contract method 0x76846edd. +// +// Solidity: function submit(address , bytes payload) returns() +func (_BasicOutboundChannel *BasicOutboundChannelTransactor) Submit(opts *bind.TransactOpts, arg0 common.Address, payload []byte) (*types.Transaction, error) { + return _BasicOutboundChannel.contract.Transact(opts, "submit", arg0, payload) +} + +// Submit is a paid mutator transaction binding the contract method 0x76846edd. +// +// Solidity: function submit(address , bytes payload) returns() +func (_BasicOutboundChannel *BasicOutboundChannelSession) Submit(arg0 common.Address, payload []byte) (*types.Transaction, error) { + return _BasicOutboundChannel.Contract.Submit(&_BasicOutboundChannel.TransactOpts, arg0, payload) +} + +// Submit is a paid mutator transaction binding the contract method 0x76846edd. +// +// Solidity: function submit(address , bytes payload) returns() +func (_BasicOutboundChannel *BasicOutboundChannelTransactorSession) Submit(arg0 common.Address, payload []byte) (*types.Transaction, error) { + return _BasicOutboundChannel.Contract.Submit(&_BasicOutboundChannel.TransactOpts, arg0, payload) +} + +// BasicOutboundChannelMessageIterator is returned from FilterMessage and is used to iterate over the raw logs and unpacked data for Message events raised by the BasicOutboundChannel contract. +type BasicOutboundChannelMessageIterator struct { + Event *BasicOutboundChannelMessage // 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 *BasicOutboundChannelMessageIterator) 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(BasicOutboundChannelMessage) + 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(BasicOutboundChannelMessage) + 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 *BasicOutboundChannelMessageIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BasicOutboundChannelMessageIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BasicOutboundChannelMessage represents a Message event raised by the BasicOutboundChannel contract. +type BasicOutboundChannelMessage struct { + Source common.Address + Nonce uint64 + Payload []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMessage is a free log retrieval operation binding the contract event 0x779b38144a38cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15b. +// +// Solidity: event Message(address source, uint64 nonce, bytes payload) +func (_BasicOutboundChannel *BasicOutboundChannelFilterer) FilterMessage(opts *bind.FilterOpts) (*BasicOutboundChannelMessageIterator, error) { + + logs, sub, err := _BasicOutboundChannel.contract.FilterLogs(opts, "Message") + if err != nil { + return nil, err + } + return &BasicOutboundChannelMessageIterator{contract: _BasicOutboundChannel.contract, event: "Message", logs: logs, sub: sub}, nil +} + +// WatchMessage is a free log subscription operation binding the contract event 0x779b38144a38cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15b. +// +// Solidity: event Message(address source, uint64 nonce, bytes payload) +func (_BasicOutboundChannel *BasicOutboundChannelFilterer) WatchMessage(opts *bind.WatchOpts, sink chan<- *BasicOutboundChannelMessage) (event.Subscription, error) { + + logs, sub, err := _BasicOutboundChannel.contract.WatchLogs(opts, "Message") + 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(BasicOutboundChannelMessage) + if err := _BasicOutboundChannel.contract.UnpackLog(event, "Message", 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 +} + +// ParseMessage is a log parse operation binding the contract event 0x779b38144a38cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15b. +// +// Solidity: event Message(address source, uint64 nonce, bytes payload) +func (_BasicOutboundChannel *BasicOutboundChannelFilterer) ParseMessage(log types.Log) (*BasicOutboundChannelMessage, error) { + event := new(BasicOutboundChannelMessage) + if err := _BasicOutboundChannel.contract.UnpackLog(event, "Message", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/relayer/contracts/outbound/contract.go b/relayer/contracts/outbound/contract.go deleted file mode 100644 index dbbdcd297b1e5..0000000000000 --- a/relayer/contracts/outbound/contract.go +++ /dev/null @@ -1,360 +0,0 @@ -// Code generated - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package outbound - -import ( - "math/big" - "strings" - - ethereum "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/event" -) - -// Reference imports to suppress errors if they are not otherwise used. -var ( - _ = big.NewInt - _ = strings.NewReader - _ = ethereum.NotFound - _ = bind.Bind - _ = common.Big1 - _ = types.BloomLookup - _ = event.NewSubscription -) - -// ContractABI is the input ABI used to generate the binding from. -const ContractABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"Message\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"submit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" - -// Contract is an auto generated Go binding around an Ethereum contract. -type Contract struct { - ContractCaller // Read-only binding to the contract - ContractTransactor // Write-only binding to the contract - ContractFilterer // Log filterer for contract events -} - -// ContractCaller is an auto generated read-only Go binding around an Ethereum contract. -type ContractCaller struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// ContractTransactor is an auto generated write-only Go binding around an Ethereum contract. -type ContractTransactor struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// ContractFilterer is an auto generated log filtering Go binding around an Ethereum contract events. -type ContractFilterer struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// ContractSession is an auto generated Go binding around an Ethereum contract, -// with pre-set call and transact options. -type ContractSession struct { - Contract *Contract // Generic contract binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// ContractCallerSession is an auto generated read-only Go binding around an Ethereum contract, -// with pre-set call options. -type ContractCallerSession struct { - Contract *ContractCaller // Generic contract caller binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session -} - -// ContractTransactorSession is an auto generated write-only Go binding around an Ethereum contract, -// with pre-set transact options. -type ContractTransactorSession struct { - Contract *ContractTransactor // Generic contract transactor binding to set the session for - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// ContractRaw is an auto generated low-level Go binding around an Ethereum contract. -type ContractRaw struct { - Contract *Contract // Generic contract binding to access the raw methods on -} - -// ContractCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. -type ContractCallerRaw struct { - Contract *ContractCaller // Generic read-only contract binding to access the raw methods on -} - -// ContractTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. -type ContractTransactorRaw struct { - Contract *ContractTransactor // Generic write-only contract binding to access the raw methods on -} - -// NewContract creates a new instance of Contract, bound to a specific deployed contract. -func NewContract(address common.Address, backend bind.ContractBackend) (*Contract, error) { - contract, err := bindContract(address, backend, backend, backend) - if err != nil { - return nil, err - } - return &Contract{ContractCaller: ContractCaller{contract: contract}, ContractTransactor: ContractTransactor{contract: contract}, ContractFilterer: ContractFilterer{contract: contract}}, nil -} - -// NewContractCaller creates a new read-only instance of Contract, bound to a specific deployed contract. -func NewContractCaller(address common.Address, caller bind.ContractCaller) (*ContractCaller, error) { - contract, err := bindContract(address, caller, nil, nil) - if err != nil { - return nil, err - } - return &ContractCaller{contract: contract}, nil -} - -// NewContractTransactor creates a new write-only instance of Contract, bound to a specific deployed contract. -func NewContractTransactor(address common.Address, transactor bind.ContractTransactor) (*ContractTransactor, error) { - contract, err := bindContract(address, nil, transactor, nil) - if err != nil { - return nil, err - } - return &ContractTransactor{contract: contract}, nil -} - -// NewContractFilterer creates a new log filterer instance of Contract, bound to a specific deployed contract. -func NewContractFilterer(address common.Address, filterer bind.ContractFilterer) (*ContractFilterer, error) { - contract, err := bindContract(address, nil, nil, filterer) - if err != nil { - return nil, err - } - return &ContractFilterer{contract: contract}, nil -} - -// bindContract binds a generic wrapper to an already deployed contract. -func bindContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(ContractABI)) - if err != nil { - return nil, err - } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_Contract *ContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _Contract.Contract.ContractCaller.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_Contract *ContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _Contract.Contract.ContractTransactor.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_Contract *ContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _Contract.Contract.ContractTransactor.contract.Transact(opts, method, params...) -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_Contract *ContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _Contract.Contract.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_Contract *ContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _Contract.Contract.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_Contract *ContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _Contract.Contract.contract.Transact(opts, method, params...) -} - -// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. -// -// Solidity: function nonce() view returns(uint64) -func (_Contract *ContractCaller) Nonce(opts *bind.CallOpts) (uint64, error) { - var out []interface{} - err := _Contract.contract.Call(opts, &out, "nonce") - - if err != nil { - return *new(uint64), err - } - - out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) - - return out0, err - -} - -// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. -// -// Solidity: function nonce() view returns(uint64) -func (_Contract *ContractSession) Nonce() (uint64, error) { - return _Contract.Contract.Nonce(&_Contract.CallOpts) -} - -// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. -// -// Solidity: function nonce() view returns(uint64) -func (_Contract *ContractCallerSession) Nonce() (uint64, error) { - return _Contract.Contract.Nonce(&_Contract.CallOpts) -} - -// Submit is a paid mutator transaction binding the contract method 0xef7fa71b. -// -// Solidity: function submit(bytes payload) returns() -func (_Contract *ContractTransactor) Submit(opts *bind.TransactOpts, payload []byte) (*types.Transaction, error) { - return _Contract.contract.Transact(opts, "submit", payload) -} - -// Submit is a paid mutator transaction binding the contract method 0xef7fa71b. -// -// Solidity: function submit(bytes payload) returns() -func (_Contract *ContractSession) Submit(payload []byte) (*types.Transaction, error) { - return _Contract.Contract.Submit(&_Contract.TransactOpts, payload) -} - -// Submit is a paid mutator transaction binding the contract method 0xef7fa71b. -// -// Solidity: function submit(bytes payload) returns() -func (_Contract *ContractTransactorSession) Submit(payload []byte) (*types.Transaction, error) { - return _Contract.Contract.Submit(&_Contract.TransactOpts, payload) -} - -// ContractMessageIterator is returned from FilterMessage and is used to iterate over the raw logs and unpacked data for Message events raised by the Contract contract. -type ContractMessageIterator struct { - Event *ContractMessage // 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 *ContractMessageIterator) 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(ContractMessage) - 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(ContractMessage) - 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 *ContractMessageIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *ContractMessageIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// ContractMessage represents a Message event raised by the Contract contract. -type ContractMessage struct { - Source common.Address - Nonce uint64 - Payload []byte - Raw types.Log // Blockchain specific contextual infos -} - -// FilterMessage is a free log retrieval operation binding the contract event 0x779b38144a38cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15b. -// -// Solidity: event Message(address source, uint64 nonce, bytes payload) -func (_Contract *ContractFilterer) FilterMessage(opts *bind.FilterOpts) (*ContractMessageIterator, error) { - - logs, sub, err := _Contract.contract.FilterLogs(opts, "Message") - if err != nil { - return nil, err - } - return &ContractMessageIterator{contract: _Contract.contract, event: "Message", logs: logs, sub: sub}, nil -} - -// WatchMessage is a free log subscription operation binding the contract event 0x779b38144a38cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15b. -// -// Solidity: event Message(address source, uint64 nonce, bytes payload) -func (_Contract *ContractFilterer) WatchMessage(opts *bind.WatchOpts, sink chan<- *ContractMessage) (event.Subscription, error) { - - logs, sub, err := _Contract.contract.WatchLogs(opts, "Message") - 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(ContractMessage) - if err := _Contract.contract.UnpackLog(event, "Message", 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 -} - -// ParseMessage is a log parse operation binding the contract event 0x779b38144a38cfc4351816442048b17fe24ba2b0e0c63446b576e8281160b15b. -// -// Solidity: event Message(address source, uint64 nonce, bytes payload) -func (_Contract *ContractFilterer) ParseMessage(log types.Log) (*ContractMessage, error) { - event := new(ContractMessage) - if err := _Contract.contract.UnpackLog(event, "Message", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} diff --git a/relayer/contracts/outbound/incentivized.go b/relayer/contracts/outbound/incentivized.go new file mode 100644 index 0000000000000..6b60896c6abc4 --- /dev/null +++ b/relayer/contracts/outbound/incentivized.go @@ -0,0 +1,1304 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package outbound + +import ( + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// IncentivizedOutboundChannelABI is the input ABI used to generate the binding from. +const IncentivizedOutboundChannelABI = "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint128\",\"name\":\"fee\",\"type\":\"uint128\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"Message\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousFee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newFee\",\"type\":\"uint256\"}],\"name\":\"RelayFeeUpdated\",\"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\":\"CONFIG_UPDATE_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\":[{\"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\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRoleMember\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleMemberCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"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\":[],\"name\":\"nonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"relayFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"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\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"setDOTApp\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"updateRelayFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"origin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"submit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + +// IncentivizedOutboundChannel is an auto generated Go binding around an Ethereum contract. +type IncentivizedOutboundChannel struct { + IncentivizedOutboundChannelCaller // Read-only binding to the contract + IncentivizedOutboundChannelTransactor // Write-only binding to the contract + IncentivizedOutboundChannelFilterer // Log filterer for contract events +} + +// IncentivizedOutboundChannelCaller is an auto generated read-only Go binding around an Ethereum contract. +type IncentivizedOutboundChannelCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IncentivizedOutboundChannelTransactor is an auto generated write-only Go binding around an Ethereum contract. +type IncentivizedOutboundChannelTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IncentivizedOutboundChannelFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type IncentivizedOutboundChannelFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IncentivizedOutboundChannelSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type IncentivizedOutboundChannelSession struct { + Contract *IncentivizedOutboundChannel // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// IncentivizedOutboundChannelCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type IncentivizedOutboundChannelCallerSession struct { + Contract *IncentivizedOutboundChannelCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// IncentivizedOutboundChannelTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type IncentivizedOutboundChannelTransactorSession struct { + Contract *IncentivizedOutboundChannelTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// IncentivizedOutboundChannelRaw is an auto generated low-level Go binding around an Ethereum contract. +type IncentivizedOutboundChannelRaw struct { + Contract *IncentivizedOutboundChannel // Generic contract binding to access the raw methods on +} + +// IncentivizedOutboundChannelCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type IncentivizedOutboundChannelCallerRaw struct { + Contract *IncentivizedOutboundChannelCaller // Generic read-only contract binding to access the raw methods on +} + +// IncentivizedOutboundChannelTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type IncentivizedOutboundChannelTransactorRaw struct { + Contract *IncentivizedOutboundChannelTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewIncentivizedOutboundChannel creates a new instance of IncentivizedOutboundChannel, bound to a specific deployed contract. +func NewIncentivizedOutboundChannel(address common.Address, backend bind.ContractBackend) (*IncentivizedOutboundChannel, error) { + contract, err := bindIncentivizedOutboundChannel(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannel{IncentivizedOutboundChannelCaller: IncentivizedOutboundChannelCaller{contract: contract}, IncentivizedOutboundChannelTransactor: IncentivizedOutboundChannelTransactor{contract: contract}, IncentivizedOutboundChannelFilterer: IncentivizedOutboundChannelFilterer{contract: contract}}, nil +} + +// NewIncentivizedOutboundChannelCaller creates a new read-only instance of IncentivizedOutboundChannel, bound to a specific deployed contract. +func NewIncentivizedOutboundChannelCaller(address common.Address, caller bind.ContractCaller) (*IncentivizedOutboundChannelCaller, error) { + contract, err := bindIncentivizedOutboundChannel(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannelCaller{contract: contract}, nil +} + +// NewIncentivizedOutboundChannelTransactor creates a new write-only instance of IncentivizedOutboundChannel, bound to a specific deployed contract. +func NewIncentivizedOutboundChannelTransactor(address common.Address, transactor bind.ContractTransactor) (*IncentivizedOutboundChannelTransactor, error) { + contract, err := bindIncentivizedOutboundChannel(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannelTransactor{contract: contract}, nil +} + +// NewIncentivizedOutboundChannelFilterer creates a new log filterer instance of IncentivizedOutboundChannel, bound to a specific deployed contract. +func NewIncentivizedOutboundChannelFilterer(address common.Address, filterer bind.ContractFilterer) (*IncentivizedOutboundChannelFilterer, error) { + contract, err := bindIncentivizedOutboundChannel(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannelFilterer{contract: contract}, nil +} + +// bindIncentivizedOutboundChannel binds a generic wrapper to an already deployed contract. +func bindIncentivizedOutboundChannel(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(IncentivizedOutboundChannelABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IncentivizedOutboundChannel.Contract.IncentivizedOutboundChannelCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.IncentivizedOutboundChannelTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.IncentivizedOutboundChannelTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IncentivizedOutboundChannel.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.contract.Transact(opts, method, params...) +} + +// CONFIGUPDATEROLE is a free data retrieval call binding the contract method 0xa2d6c6e5. +// +// Solidity: function CONFIG_UPDATE_ROLE() view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCaller) CONFIGUPDATEROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _IncentivizedOutboundChannel.contract.Call(opts, &out, "CONFIG_UPDATE_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// CONFIGUPDATEROLE is a free data retrieval call binding the contract method 0xa2d6c6e5. +// +// Solidity: function CONFIG_UPDATE_ROLE() view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) CONFIGUPDATEROLE() ([32]byte, error) { + return _IncentivizedOutboundChannel.Contract.CONFIGUPDATEROLE(&_IncentivizedOutboundChannel.CallOpts) +} + +// CONFIGUPDATEROLE is a free data retrieval call binding the contract method 0xa2d6c6e5. +// +// Solidity: function CONFIG_UPDATE_ROLE() view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerSession) CONFIGUPDATEROLE() ([32]byte, error) { + return _IncentivizedOutboundChannel.Contract.CONFIGUPDATEROLE(&_IncentivizedOutboundChannel.CallOpts) +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCaller) DEFAULTADMINROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _IncentivizedOutboundChannel.contract.Call(opts, &out, "DEFAULT_ADMIN_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) DEFAULTADMINROLE() ([32]byte, error) { + return _IncentivizedOutboundChannel.Contract.DEFAULTADMINROLE(&_IncentivizedOutboundChannel.CallOpts) +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerSession) DEFAULTADMINROLE() ([32]byte, error) { + return _IncentivizedOutboundChannel.Contract.DEFAULTADMINROLE(&_IncentivizedOutboundChannel.CallOpts) +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCaller) GetRoleAdmin(opts *bind.CallOpts, role [32]byte) ([32]byte, error) { + var out []interface{} + err := _IncentivizedOutboundChannel.contract.Call(opts, &out, "getRoleAdmin", role) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) GetRoleAdmin(role [32]byte) ([32]byte, error) { + return _IncentivizedOutboundChannel.Contract.GetRoleAdmin(&_IncentivizedOutboundChannel.CallOpts, role) +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerSession) GetRoleAdmin(role [32]byte) ([32]byte, error) { + return _IncentivizedOutboundChannel.Contract.GetRoleAdmin(&_IncentivizedOutboundChannel.CallOpts, role) +} + +// GetRoleMember is a free data retrieval call binding the contract method 0x9010d07c. +// +// Solidity: function getRoleMember(bytes32 role, uint256 index) view returns(address) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCaller) GetRoleMember(opts *bind.CallOpts, role [32]byte, index *big.Int) (common.Address, error) { + var out []interface{} + err := _IncentivizedOutboundChannel.contract.Call(opts, &out, "getRoleMember", role, index) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetRoleMember is a free data retrieval call binding the contract method 0x9010d07c. +// +// Solidity: function getRoleMember(bytes32 role, uint256 index) view returns(address) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) GetRoleMember(role [32]byte, index *big.Int) (common.Address, error) { + return _IncentivizedOutboundChannel.Contract.GetRoleMember(&_IncentivizedOutboundChannel.CallOpts, role, index) +} + +// GetRoleMember is a free data retrieval call binding the contract method 0x9010d07c. +// +// Solidity: function getRoleMember(bytes32 role, uint256 index) view returns(address) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerSession) GetRoleMember(role [32]byte, index *big.Int) (common.Address, error) { + return _IncentivizedOutboundChannel.Contract.GetRoleMember(&_IncentivizedOutboundChannel.CallOpts, role, index) +} + +// GetRoleMemberCount is a free data retrieval call binding the contract method 0xca15c873. +// +// Solidity: function getRoleMemberCount(bytes32 role) view returns(uint256) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCaller) GetRoleMemberCount(opts *bind.CallOpts, role [32]byte) (*big.Int, error) { + var out []interface{} + err := _IncentivizedOutboundChannel.contract.Call(opts, &out, "getRoleMemberCount", role) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetRoleMemberCount is a free data retrieval call binding the contract method 0xca15c873. +// +// Solidity: function getRoleMemberCount(bytes32 role) view returns(uint256) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) GetRoleMemberCount(role [32]byte) (*big.Int, error) { + return _IncentivizedOutboundChannel.Contract.GetRoleMemberCount(&_IncentivizedOutboundChannel.CallOpts, role) +} + +// GetRoleMemberCount is a free data retrieval call binding the contract method 0xca15c873. +// +// Solidity: function getRoleMemberCount(bytes32 role) view returns(uint256) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerSession) GetRoleMemberCount(role [32]byte) (*big.Int, error) { + return _IncentivizedOutboundChannel.Contract.GetRoleMemberCount(&_IncentivizedOutboundChannel.CallOpts, role) +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCaller) HasRole(opts *bind.CallOpts, role [32]byte, account common.Address) (bool, error) { + var out []interface{} + err := _IncentivizedOutboundChannel.contract.Call(opts, &out, "hasRole", role, account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) HasRole(role [32]byte, account common.Address) (bool, error) { + return _IncentivizedOutboundChannel.Contract.HasRole(&_IncentivizedOutboundChannel.CallOpts, role, account) +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerSession) HasRole(role [32]byte, account common.Address) (bool, error) { + return _IncentivizedOutboundChannel.Contract.HasRole(&_IncentivizedOutboundChannel.CallOpts, role, account) +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint64) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCaller) Nonce(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _IncentivizedOutboundChannel.contract.Call(opts, &out, "nonce") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint64) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) Nonce() (uint64, error) { + return _IncentivizedOutboundChannel.Contract.Nonce(&_IncentivizedOutboundChannel.CallOpts) +} + +// Nonce is a free data retrieval call binding the contract method 0xaffed0e0. +// +// Solidity: function nonce() view returns(uint64) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerSession) Nonce() (uint64, error) { + return _IncentivizedOutboundChannel.Contract.Nonce(&_IncentivizedOutboundChannel.CallOpts) +} + +// RelayFee is a free data retrieval call binding the contract method 0x71d30863. +// +// Solidity: function relayFee() view returns(uint256) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCaller) RelayFee(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IncentivizedOutboundChannel.contract.Call(opts, &out, "relayFee") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// RelayFee is a free data retrieval call binding the contract method 0x71d30863. +// +// Solidity: function relayFee() view returns(uint256) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) RelayFee() (*big.Int, error) { + return _IncentivizedOutboundChannel.Contract.RelayFee(&_IncentivizedOutboundChannel.CallOpts) +} + +// RelayFee is a free data retrieval call binding the contract method 0x71d30863. +// +// Solidity: function relayFee() view returns(uint256) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelCallerSession) RelayFee() (*big.Int, error) { + return _IncentivizedOutboundChannel.Contract.RelayFee(&_IncentivizedOutboundChannel.CallOpts) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactor) GrantRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.contract.Transact(opts, "grantRole", role, account) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) GrantRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.GrantRole(&_IncentivizedOutboundChannel.TransactOpts, role, account) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactorSession) GrantRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.GrantRole(&_IncentivizedOutboundChannel.TransactOpts, role, account) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactor) RenounceRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.contract.Transact(opts, "renounceRole", role, account) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) RenounceRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.RenounceRole(&_IncentivizedOutboundChannel.TransactOpts, role, account) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactorSession) RenounceRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.RenounceRole(&_IncentivizedOutboundChannel.TransactOpts, role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactor) RevokeRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.contract.Transact(opts, "revokeRole", role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) RevokeRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.RevokeRole(&_IncentivizedOutboundChannel.TransactOpts, role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactorSession) RevokeRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.RevokeRole(&_IncentivizedOutboundChannel.TransactOpts, role, account) +} + +// SetDOTApp is a paid mutator transaction binding the contract method 0x41cfb5c6. +// +// Solidity: function setDOTApp(address _address) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactor) SetDOTApp(opts *bind.TransactOpts, _address common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.contract.Transact(opts, "setDOTApp", _address) +} + +// SetDOTApp is a paid mutator transaction binding the contract method 0x41cfb5c6. +// +// Solidity: function setDOTApp(address _address) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) SetDOTApp(_address common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.SetDOTApp(&_IncentivizedOutboundChannel.TransactOpts, _address) +} + +// SetDOTApp is a paid mutator transaction binding the contract method 0x41cfb5c6. +// +// Solidity: function setDOTApp(address _address) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactorSession) SetDOTApp(_address common.Address) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.SetDOTApp(&_IncentivizedOutboundChannel.TransactOpts, _address) +} + +// Submit is a paid mutator transaction binding the contract method 0x76846edd. +// +// Solidity: function submit(address origin, bytes payload) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactor) Submit(opts *bind.TransactOpts, origin common.Address, payload []byte) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.contract.Transact(opts, "submit", origin, payload) +} + +// Submit is a paid mutator transaction binding the contract method 0x76846edd. +// +// Solidity: function submit(address origin, bytes payload) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) Submit(origin common.Address, payload []byte) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.Submit(&_IncentivizedOutboundChannel.TransactOpts, origin, payload) +} + +// Submit is a paid mutator transaction binding the contract method 0x76846edd. +// +// Solidity: function submit(address origin, bytes payload) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactorSession) Submit(origin common.Address, payload []byte) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.Submit(&_IncentivizedOutboundChannel.TransactOpts, origin, payload) +} + +// UpdateRelayFee is a paid mutator transaction binding the contract method 0xee3c4664. +// +// Solidity: function updateRelayFee(uint256 _amount) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactor) UpdateRelayFee(opts *bind.TransactOpts, _amount *big.Int) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.contract.Transact(opts, "updateRelayFee", _amount) +} + +// UpdateRelayFee is a paid mutator transaction binding the contract method 0xee3c4664. +// +// Solidity: function updateRelayFee(uint256 _amount) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelSession) UpdateRelayFee(_amount *big.Int) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.UpdateRelayFee(&_IncentivizedOutboundChannel.TransactOpts, _amount) +} + +// UpdateRelayFee is a paid mutator transaction binding the contract method 0xee3c4664. +// +// Solidity: function updateRelayFee(uint256 _amount) returns() +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelTransactorSession) UpdateRelayFee(_amount *big.Int) (*types.Transaction, error) { + return _IncentivizedOutboundChannel.Contract.UpdateRelayFee(&_IncentivizedOutboundChannel.TransactOpts, _amount) +} + +// IncentivizedOutboundChannelMessageIterator is returned from FilterMessage and is used to iterate over the raw logs and unpacked data for Message events raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelMessageIterator struct { + Event *IncentivizedOutboundChannelMessage // 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 *IncentivizedOutboundChannelMessageIterator) 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(IncentivizedOutboundChannelMessage) + 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(IncentivizedOutboundChannelMessage) + 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 *IncentivizedOutboundChannelMessageIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *IncentivizedOutboundChannelMessageIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// IncentivizedOutboundChannelMessage represents a Message event raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelMessage struct { + Source common.Address + Nonce uint64 + Fee *big.Int + Payload []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMessage is a free log retrieval operation binding the contract event 0xd04534302d9e419f66f67d941e1f8d81819fae1128b18140c547c8d19d33ce1d. +// +// Solidity: event Message(address source, uint64 nonce, uint128 fee, bytes payload) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) FilterMessage(opts *bind.FilterOpts) (*IncentivizedOutboundChannelMessageIterator, error) { + + logs, sub, err := _IncentivizedOutboundChannel.contract.FilterLogs(opts, "Message") + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannelMessageIterator{contract: _IncentivizedOutboundChannel.contract, event: "Message", logs: logs, sub: sub}, nil +} + +// WatchMessage is a free log subscription operation binding the contract event 0xd04534302d9e419f66f67d941e1f8d81819fae1128b18140c547c8d19d33ce1d. +// +// Solidity: event Message(address source, uint64 nonce, uint128 fee, bytes payload) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) WatchMessage(opts *bind.WatchOpts, sink chan<- *IncentivizedOutboundChannelMessage) (event.Subscription, error) { + + logs, sub, err := _IncentivizedOutboundChannel.contract.WatchLogs(opts, "Message") + 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(IncentivizedOutboundChannelMessage) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "Message", 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 +} + +// ParseMessage is a log parse operation binding the contract event 0xd04534302d9e419f66f67d941e1f8d81819fae1128b18140c547c8d19d33ce1d. +// +// Solidity: event Message(address source, uint64 nonce, uint128 fee, bytes payload) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) ParseMessage(log types.Log) (*IncentivizedOutboundChannelMessage, error) { + event := new(IncentivizedOutboundChannelMessage) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "Message", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// IncentivizedOutboundChannelRelayFeeUpdatedIterator is returned from FilterRelayFeeUpdated and is used to iterate over the raw logs and unpacked data for RelayFeeUpdated events raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelRelayFeeUpdatedIterator struct { + Event *IncentivizedOutboundChannelRelayFeeUpdated // 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 *IncentivizedOutboundChannelRelayFeeUpdatedIterator) 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(IncentivizedOutboundChannelRelayFeeUpdated) + 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(IncentivizedOutboundChannelRelayFeeUpdated) + 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 *IncentivizedOutboundChannelRelayFeeUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *IncentivizedOutboundChannelRelayFeeUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// IncentivizedOutboundChannelRelayFeeUpdated represents a RelayFeeUpdated event raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelRelayFeeUpdated struct { + PreviousFee *big.Int + NewFee *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRelayFeeUpdated is a free log retrieval operation binding the contract event 0x8d84c62d6bf3d81941ad828eb675118ad55652fc8dc7acafcebb37497222df31. +// +// Solidity: event RelayFeeUpdated(uint256 previousFee, uint256 newFee) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) FilterRelayFeeUpdated(opts *bind.FilterOpts) (*IncentivizedOutboundChannelRelayFeeUpdatedIterator, error) { + + logs, sub, err := _IncentivizedOutboundChannel.contract.FilterLogs(opts, "RelayFeeUpdated") + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannelRelayFeeUpdatedIterator{contract: _IncentivizedOutboundChannel.contract, event: "RelayFeeUpdated", logs: logs, sub: sub}, nil +} + +// WatchRelayFeeUpdated is a free log subscription operation binding the contract event 0x8d84c62d6bf3d81941ad828eb675118ad55652fc8dc7acafcebb37497222df31. +// +// Solidity: event RelayFeeUpdated(uint256 previousFee, uint256 newFee) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) WatchRelayFeeUpdated(opts *bind.WatchOpts, sink chan<- *IncentivizedOutboundChannelRelayFeeUpdated) (event.Subscription, error) { + + logs, sub, err := _IncentivizedOutboundChannel.contract.WatchLogs(opts, "RelayFeeUpdated") + 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(IncentivizedOutboundChannelRelayFeeUpdated) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "RelayFeeUpdated", 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 +} + +// ParseRelayFeeUpdated is a log parse operation binding the contract event 0x8d84c62d6bf3d81941ad828eb675118ad55652fc8dc7acafcebb37497222df31. +// +// Solidity: event RelayFeeUpdated(uint256 previousFee, uint256 newFee) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) ParseRelayFeeUpdated(log types.Log) (*IncentivizedOutboundChannelRelayFeeUpdated, error) { + event := new(IncentivizedOutboundChannelRelayFeeUpdated) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "RelayFeeUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// IncentivizedOutboundChannelRoleAdminChangedIterator is returned from FilterRoleAdminChanged and is used to iterate over the raw logs and unpacked data for RoleAdminChanged events raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelRoleAdminChangedIterator struct { + Event *IncentivizedOutboundChannelRoleAdminChanged // 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 *IncentivizedOutboundChannelRoleAdminChangedIterator) 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(IncentivizedOutboundChannelRoleAdminChanged) + 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(IncentivizedOutboundChannelRoleAdminChanged) + 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 *IncentivizedOutboundChannelRoleAdminChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *IncentivizedOutboundChannelRoleAdminChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// IncentivizedOutboundChannelRoleAdminChanged represents a RoleAdminChanged event raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelRoleAdminChanged struct { + Role [32]byte + PreviousAdminRole [32]byte + NewAdminRole [32]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleAdminChanged is a free log retrieval operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) FilterRoleAdminChanged(opts *bind.FilterOpts, role [][32]byte, previousAdminRole [][32]byte, newAdminRole [][32]byte) (*IncentivizedOutboundChannelRoleAdminChangedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var previousAdminRoleRule []interface{} + for _, previousAdminRoleItem := range previousAdminRole { + previousAdminRoleRule = append(previousAdminRoleRule, previousAdminRoleItem) + } + var newAdminRoleRule []interface{} + for _, newAdminRoleItem := range newAdminRole { + newAdminRoleRule = append(newAdminRoleRule, newAdminRoleItem) + } + + logs, sub, err := _IncentivizedOutboundChannel.contract.FilterLogs(opts, "RoleAdminChanged", roleRule, previousAdminRoleRule, newAdminRoleRule) + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannelRoleAdminChangedIterator{contract: _IncentivizedOutboundChannel.contract, event: "RoleAdminChanged", logs: logs, sub: sub}, nil +} + +// WatchRoleAdminChanged is a free log subscription operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) WatchRoleAdminChanged(opts *bind.WatchOpts, sink chan<- *IncentivizedOutboundChannelRoleAdminChanged, role [][32]byte, previousAdminRole [][32]byte, newAdminRole [][32]byte) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var previousAdminRoleRule []interface{} + for _, previousAdminRoleItem := range previousAdminRole { + previousAdminRoleRule = append(previousAdminRoleRule, previousAdminRoleItem) + } + var newAdminRoleRule []interface{} + for _, newAdminRoleItem := range newAdminRole { + newAdminRoleRule = append(newAdminRoleRule, newAdminRoleItem) + } + + logs, sub, err := _IncentivizedOutboundChannel.contract.WatchLogs(opts, "RoleAdminChanged", roleRule, previousAdminRoleRule, newAdminRoleRule) + 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(IncentivizedOutboundChannelRoleAdminChanged) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "RoleAdminChanged", 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 +} + +// ParseRoleAdminChanged is a log parse operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) ParseRoleAdminChanged(log types.Log) (*IncentivizedOutboundChannelRoleAdminChanged, error) { + event := new(IncentivizedOutboundChannelRoleAdminChanged) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "RoleAdminChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// IncentivizedOutboundChannelRoleGrantedIterator is returned from FilterRoleGranted and is used to iterate over the raw logs and unpacked data for RoleGranted events raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelRoleGrantedIterator struct { + Event *IncentivizedOutboundChannelRoleGranted // 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 *IncentivizedOutboundChannelRoleGrantedIterator) 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(IncentivizedOutboundChannelRoleGranted) + 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(IncentivizedOutboundChannelRoleGranted) + 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 *IncentivizedOutboundChannelRoleGrantedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *IncentivizedOutboundChannelRoleGrantedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// IncentivizedOutboundChannelRoleGranted represents a RoleGranted event raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelRoleGranted struct { + Role [32]byte + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleGranted is a free log retrieval operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) FilterRoleGranted(opts *bind.FilterOpts, role [][32]byte, account []common.Address, sender []common.Address) (*IncentivizedOutboundChannelRoleGrantedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _IncentivizedOutboundChannel.contract.FilterLogs(opts, "RoleGranted", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannelRoleGrantedIterator{contract: _IncentivizedOutboundChannel.contract, event: "RoleGranted", logs: logs, sub: sub}, nil +} + +// WatchRoleGranted is a free log subscription operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) WatchRoleGranted(opts *bind.WatchOpts, sink chan<- *IncentivizedOutboundChannelRoleGranted, role [][32]byte, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _IncentivizedOutboundChannel.contract.WatchLogs(opts, "RoleGranted", roleRule, accountRule, senderRule) + 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(IncentivizedOutboundChannelRoleGranted) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "RoleGranted", 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 +} + +// ParseRoleGranted is a log parse operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) ParseRoleGranted(log types.Log) (*IncentivizedOutboundChannelRoleGranted, error) { + event := new(IncentivizedOutboundChannelRoleGranted) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "RoleGranted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// IncentivizedOutboundChannelRoleRevokedIterator is returned from FilterRoleRevoked and is used to iterate over the raw logs and unpacked data for RoleRevoked events raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelRoleRevokedIterator struct { + Event *IncentivizedOutboundChannelRoleRevoked // 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 *IncentivizedOutboundChannelRoleRevokedIterator) 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(IncentivizedOutboundChannelRoleRevoked) + 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(IncentivizedOutboundChannelRoleRevoked) + 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 *IncentivizedOutboundChannelRoleRevokedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *IncentivizedOutboundChannelRoleRevokedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// IncentivizedOutboundChannelRoleRevoked represents a RoleRevoked event raised by the IncentivizedOutboundChannel contract. +type IncentivizedOutboundChannelRoleRevoked struct { + Role [32]byte + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleRevoked is a free log retrieval operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) FilterRoleRevoked(opts *bind.FilterOpts, role [][32]byte, account []common.Address, sender []common.Address) (*IncentivizedOutboundChannelRoleRevokedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _IncentivizedOutboundChannel.contract.FilterLogs(opts, "RoleRevoked", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return &IncentivizedOutboundChannelRoleRevokedIterator{contract: _IncentivizedOutboundChannel.contract, event: "RoleRevoked", logs: logs, sub: sub}, nil +} + +// WatchRoleRevoked is a free log subscription operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) WatchRoleRevoked(opts *bind.WatchOpts, sink chan<- *IncentivizedOutboundChannelRoleRevoked, role [][32]byte, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _IncentivizedOutboundChannel.contract.WatchLogs(opts, "RoleRevoked", roleRule, accountRule, senderRule) + 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(IncentivizedOutboundChannelRoleRevoked) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "RoleRevoked", 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 +} + +// ParseRoleRevoked is a log parse operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_IncentivizedOutboundChannel *IncentivizedOutboundChannelFilterer) ParseRoleRevoked(log types.Log) (*IncentivizedOutboundChannelRoleRevoked, error) { + event := new(IncentivizedOutboundChannelRoleRevoked) + if err := _IncentivizedOutboundChannel.contract.UnpackLog(event, "RoleRevoked", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/test/src/ethclient/index.js b/test/src/ethclient/index.js index 177d2ed21eea3..a4864441125d4 100644 --- a/test/src/ethclient/index.js +++ b/test/src/ethclient/index.js @@ -5,7 +5,7 @@ const ETHApp = require('../../../ethereum/build/contracts/ETHApp.json'); const ERC20App = require('../../../ethereum/build/contracts/ERC20App.json'); const ERC20 = require('../../../ethereum/build/contracts/ERC20.json'); const TestToken = require('../../../ethereum/build/contracts/TestToken.json'); -const DOTAppDecimals12 = require('../../../ethereum/build/contracts/DOTAppDecimals12.json'); +const DOTApp = require('../../../ethereum/build/contracts/DOTApp.json'); const WrappedToken = require('../../../ethereum/build/contracts/WrappedToken.json'); @@ -31,7 +31,7 @@ class EthClient { const appERC20 = new this.web3.eth.Contract(ERC20App.abi, ERC20App.networks[networkID].address); this.appERC20 = appERC20; - const appDOT = new this.web3.eth.Contract(DOTAppDecimals12.abi, DOTAppDecimals12.networks[networkID].address); + const appDOT = new this.web3.eth.Contract(DOTApp.abi, DOTApp.networks[networkID].address); this.appDOT = appDOT; };