diff --git a/bridges/relays/ethereum/res/substrate-bridge-abi.json b/bridges/relays/ethereum/res/substrate-bridge-abi.json index 169b000680..b7d7b4b915 100644 --- a/bridges/relays/ethereum/res/substrate-bridge-abi.json +++ b/bridges/relays/ethereum/res/substrate-bridge-abi.json @@ -69,11 +69,26 @@ "inputs": [ { "internalType": "bytes", - "name": "rawHeader", + "name": "rawHeader1", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "rawHeader2", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "rawHeader3", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "rawHeader4", "type": "bytes" } ], - "name": "importHeader", + "name": "importHeaders", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -100,16 +115,31 @@ "inputs": [ { "internalType": "bytes", - "name": "rawHeader", + "name": "rawHeader1", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "rawHeader2", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "rawHeader3", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "rawHeader4", "type": "bytes" } ], - "name": "isIncompleteHeader", + "name": "isIncompleteHeaders", "outputs": [ { - "internalType": "bool", + "internalType": "uint256", "name": "", - "type": "bool" + "type": "uint256" } ], "stateMutability": "view", diff --git a/bridges/relays/ethereum/res/substrate-bridge-bytecode.hex b/bridges/relays/ethereum/res/substrate-bridge-bytecode.hex index 0a0973699d..6dd6a33046 100644 --- a/bridges/relays/ethereum/res/substrate-bridge-bytecode.hex +++ b/bridges/relays/ethereum/res/substrate-bridge-bytecode.hex @@ -1 +1 @@ -60806040523480156200001157600080fd5b506040516200142e3803806200142e833981810160405260608110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b506040818152602083015192018051929491939192846401000000008211156200010f57600080fd5b9083019060208201858111156200012557600080fd5b82516401000000008111828201881017156200014057600080fd5b82525081516020918201929091019080838360005b838110156200016f57818101518382015260200162000155565b50505050905090810190601f1680156200019d5780820380516001836020036101000a031916815260200191505b50604052505050620001ae620003d5565b620001c2846001600160e01b03620002dc16565b805160008181556002918255604080840180516001908155825160e08101845281815260208088015181830190815293518286019081526080808a0151606085019081526001600160401b038e169185019190915260a0840188905260c084018890528951885260078352959096208251815460ff191690151517815593519284019290925593519482019490945590518051949550919390926200026f9260038501929101906200040a565b506080820151600482810180546001600160401b03199081166001600160401b039485161790915560a0850151600585015560c09094015160069093019290925560038054909316908616179091558251620002d1919060208501906200040a565b5050505050620004af565b620002e6620003d5565b60008060008060008651602088016040516020810160208101602081016020810160a08588886010600019fa6200031c57600080fd5b84519b5083519a50825199508151985080519750505050505050506060816001600160401b03811180156200035057600080fd5b506040519080825280601f01601f1916602001820160405280156200037c576020820181803683370190505b5090508115620003a85787516020890160208301848184846011600019fa620003a457600080fd5b5050505b6040805160a081018252968752602087019590955293850192909252606084015250608082015292915050565b6040518060a0016040528060008019168152602001600080191681526020016000815260200160008152602001606081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200044d57805160ff19168380011785556200047d565b828001600101855582156200047d579182015b828111156200047d57825182559160200191906001019062000460565b506200048b9291506200048f565b5090565b620004ac91905b808211156200048b576000815560010162000496565b90565b610f6f80620004bf6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c8063374c2c2614610067578063871ebe18146101085780638dc1f05c14610139578063d96a2deb146101df578063e7af077914610200578063fae71ae8146102a8575b600080fd5b61006f61035a565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100b357818101518382015260200161009b565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156100f25781810151838201526020016100da565b5050505090500194505050505060405180910390f35b6101256004803603602081101561011e57600080fd5b503561045f565b604080519115158252519081900360200190f35b6101256004803603602081101561014f57600080fd5b81019060208101813564010000000081111561016a57600080fd5b82018360208201111561017c57600080fd5b8035906020019184600183028401116401000000008311171561019e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610474945050505050565b6101e761049d565b6040805192835260208301919091528051918290030190f35b6102a66004803603602081101561021657600080fd5b81019060208101813564010000000081111561023157600080fd5b82018360208201111561024357600080fd5b8035906020019184600183028401116401000000008311171561026557600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506104b5945050505050565b005b6102a6600480360360608110156102be57600080fd5b8135916020810135918101906060810160408201356401000000008111156102e557600080fd5b8201836020820111156102f757600080fd5b8035906020019184600183028401116401000000008311171561031957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610607945050505050565b6005546060908190818167ffffffffffffffff8111801561037a57600080fd5b506040519080825280602002602001820160405280156103a4578160200160208202803683370190505b50905060005b828110156103ff5760076000600583815481106103c357fe5b90600052602060002001548152602001908152602001600020600201548282815181106103ec57fe5b60209081029190910101526001016103aa565b508060058080548060200260200160405190810160405280929190818152602001828054801561044e57602002820191906000526020600020905b81548152602001906001019080831161043a575b505050505090509350935050509091565b60009081526007602052604090205460ff1690565b600061047e610d28565b60006104898461086c565b604090930151909214945050505050919050565b60008054808252600760205260409091206002015491565b6104bd610d28565b60008060006104cb8561086c565b93509350935093508360400151811415610526576005805485516001820180845560009384527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0909201558551825260066020526040909120555b6040805160e0810182526001808252602087810151818401908152888501518486019081526080808b01516060870190815267ffffffffffffffff8b169187019190915260a0860189905260c086018890528a51600090815260078552969096208551815460ff19169015151781559151938201939093559151600283015592518051929391926105bd9260038501920190610d5d565b50608082015160048201805467ffffffffffffffff191667ffffffffffffffff90921691909117905560a0820151600582015560c090910151600690910155505090516000555050565b60008281526007602052604090206002015483146106565760405162461bcd60e51b815260040180806020018281038252602f815260200180610eda602f913960400191505060405180910390fd5b60028054600354600480546040805160206101006001851615026000190190931696909604601f81018390048302870183019091528086529394600094610707948a948a9467ffffffffffffffff909216939290918301828280156106fc5780601f106106d1576101008083540402835291602001916106fc565b820191906000526020600020905b8154815290600101906020018083116106df57829003601f168201915b505050505087610af3565b600081815260076020526040902060028281558101546001559091505b828214610864575060008181526007602090815260408083206001810154600690935292205490929080156107e65760055460001991820191810182146107b25760006005600183038154811061077757fe5b90600052602060002001549050806005848154811061079257fe5b600091825260208083209091019290925591825260069052604090208290555b60058054806107bd57fe5b600082815260208082208301600019908101839055909201909255848252600690526040812055505b82600601548360020154141561085d57600583015460009081526007602052604090206003805467ffffffffffffffff198116600167ffffffffffffffff92831681019092161782559082018054610854926004929160026101009282161592909202600019011604610ddb565b50505050610864565b5050610724565b505050505050565b610874610d28565b6000806000610881610d28565b61088a86610c35565b805160009081526007602052604090205490915060ff16156108f3576040805162461bcd60e51b815260206004820152601760248201527f48656164657220697320616c7265616479206b6e6f776e000000000000000000604482015290519081900360640190fd5b6001548160400151116109375760405162461bcd60e51b8152600401808060200182810382526025815260200180610e6e6025913960400191505060405180910390fd5b6020808201516000908152600790915260409020805460ff168015610966575060018260400151038160020154145b6109a15760405162461bcd60e51b8152600401808060200182810382526026815260200180610e936026913960400191505060405180910390fd5b6006810154158015906109bb575080600201548160060154145b15610a0457816020015160025414610a045760405162461bcd60e51b8152600401808060200182810382526031815260200180610f096031913960400191505060405180910390fd5b60048101546005820154600683015460808501515167ffffffffffffffff9093169215610ae25767ffffffffffffffff8381161415610a745760405162461bcd60e51b8152600401808060200182810382526021815260200180610eb96021913960400191505060405180910390fd5b84604001518110610acc576040805162461bcd60e51b815260206004820152601960248201527f4f7665726c617070696e67207369676e616c7320666f756e6400000000000000604482015290519081900360640190fd5b5050825160608401516040850151600190930192015b939991985096509194509092505050565b600060608686868686604051602001808681526020018581526020018467ffffffffffffffff1667ffffffffffffffff1681526020018060200180602001838103835285818151815260200191508051906020019080838360005b83811015610b66578181015183820152602001610b4e565b50505050905090810190601f168015610b935780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610bc6578181015183820152602001610bae565b50505050905090810190601f168015610bf35780820380516001836020036101000a031916815260200191505b50975050505050505050604051602081830303815290604052905080516020820160008083836012600019fa610c2857600080fd5b5095979650505050505050565b610c3d610d28565b60008060008060008651602088016040516020810160208101602081016020810160a08588886010600019fa610c7257600080fd5b84519b5083519a508251995081519850805197505050505050505060608167ffffffffffffffff81118015610ca657600080fd5b506040519080825280601f01601f191660200182016040528015610cd1576020820181803683370190505b5090508115610cfb5787516020890160208301848184846011600019fa610cf757600080fd5b5050505b6040805160a081018252968752602087019590955293850192909252606084015250608082015292915050565b6040518060a0016040528060008019168152602001600080191681526020016000815260200160008152602001606081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610d9e57805160ff1916838001178555610dcb565b82800160010185558215610dcb579182015b82811115610dcb578251825591602001919060010190610db0565b50610dd7929150610e50565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610e145780548555610dcb565b82800160010185558215610dcb57600052602060002091601f016020900482015b82811115610dcb578254825591600101919060010190610e35565b610e6a91905b80821115610dd75760008155600101610e56565b9056fe547279696e6720746f20696d706f7274206e6f6e2d63616e6f6e6963616c206865616465724d697373696e6720706172656e74206865616465722066726f6d207468652073746f7261676552656163686564206d6178696d616c2076616c696461746f7273207365742069644d697373696e672066696e616c69747920746172676574206865616465722066726f6d207468652073746f726167654d697373696e672072657175697265642066696e616c6974792070726f6f6620666f7220706172656e7420686561646572a2646970667358221220430a1b51f4ade85190a1817f6d8dfc5ffc46019627c95d768106e998f360e8be64736f6c63430006060033 \ No newline at end of file +60806040523480156200001157600080fd5b5060405162001af838038062001af8833981810160405260608110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b506040818152602083015192018051929491939192846401000000008211156200010f57600080fd5b9083019060208201858111156200012557600080fd5b82516401000000008111828201881017156200014057600080fd5b82525081516020918201929091019080838360005b838110156200016f57818101518382015260200162000155565b50505050905090810190601f1680156200019d5780820380516001836020036101000a031916815260200191505b50604052505050620001ae620003d5565b620001c2846001600160e01b03620002dc16565b805160008181556002918255604080840180516001908155825160e08101845281815260208088015181830190815293518286019081526080808a0151606085019081526001600160401b038e169185019190915260a0840188905260c084018890528951885260078352959096208251815460ff191690151517815593519284019290925593519482019490945590518051949550919390926200026f9260038501929101906200040a565b506080820151600482810180546001600160401b03199081166001600160401b039485161790915560a0850151600585015560c09094015160069093019290925560038054909316908616179091558251620002d1919060208501906200040a565b5050505050620004af565b620002e6620003d5565b60008060008060008651602088016040516020810160208101602081016020810160a08588886010600019fa6200031c57600080fd5b84519b5083519a50825199508151985080519750505050505050506060816001600160401b03811180156200035057600080fd5b506040519080825280601f01601f1916602001820160405280156200037c576020820181803683370190505b5090508115620003a85787516020890160208301848184846011600019fa620003a457600080fd5b5050505b6040805160a081018252968752602087019590955293850192909252606084015250608082015292915050565b6040518060a0016040528060008019168152602001600080191681526020016000815260200160008152602001606081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200044d57805160ff19168380011785556200047d565b828001600101855582156200047d579182015b828111156200047d57825182559160200191906001019062000460565b506200048b9291506200048f565b5090565b620004ac91905b808211156200048b576000815560010162000496565b90565b61163980620004bf6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c8063374c2c26146100675780636a742c0914610108578063871ebe181461033d578063d96a2deb1461036e578063e8ffbe841461038f578063fae71ae8146105d4575b600080fd5b61006f610684565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100b357818101518382015260200161009b565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156100f25781810151838201526020016100da565b5050505090500194505050505060405180910390f35b61033b6004803603608081101561011e57600080fd5b810190602081018135600160201b81111561013857600080fd5b82018360208201111561014a57600080fd5b803590602001918460018302840111600160201b8311171561016b57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b8111156101bd57600080fd5b8201836020820111156101cf57600080fd5b803590602001918460018302840111600160201b831117156101f057600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b81111561024257600080fd5b82018360208201111561025457600080fd5b803590602001918460018302840111600160201b8311171561027557600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b8111156102c757600080fd5b8201836020820111156102d957600080fd5b803590602001918460018302840111600160201b831117156102fa57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610789945050505050565b005b61035a6004803603602081101561035357600080fd5b50356107e5565b604080519115158252519081900360200190f35b6103766107fd565b6040805192835260208301919091528051918290030190f35b6105c2600480360360808110156103a557600080fd5b810190602081018135600160201b8111156103bf57600080fd5b8201836020820111156103d157600080fd5b803590602001918460018302840111600160201b831117156103f257600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b81111561044457600080fd5b82018360208201111561045657600080fd5b803590602001918460018302840111600160201b8311171561047757600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b8111156104c957600080fd5b8201836020820111156104db57600080fd5b803590602001918460018302840111600160201b831117156104fc57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295949360208101935035915050600160201b81111561054e57600080fd5b82018360208201111561056057600080fd5b803590602001918460018302840111600160201b8311171561058157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610815945050505050565b60408051918252519081900360200190f35b61033b600480360360608110156105ea57600080fd5b813591602081013591810190606081016040820135600160201b81111561061057600080fd5b82018360208201111561062257600080fd5b803590602001918460018302840111600160201b8311171561064357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610b28945050505050565b6005546060908190818167ffffffffffffffff811180156106a457600080fd5b506040519080825280602002602001820160405280156106ce578160200160208202803683370190505b50905060005b828110156107295760076000600583815481106106ed57fe5b906000526020600020015481526020019081526020016000206002015482828151811061071657fe5b60209081029190910101526001016106d4565b508060058080548060200260200160405190810160405280929190818152602001828054801561077857602002820191906000526020600020905b815481526020019060010190808311610764575b505050505090509350935050509091565b61079284610d8d565b61079b576107df565b8251156107b4576107ab83610d8d565b6107b4576107df565b8151156107cd576107c482610d8d565b6107cd576107df565b8051156107df576107dd81610d8d565b505b50505050565b60008181526007602052604090205460ff165b919050565b60008054808252600760205260409091206002015491565b600061081f611454565b61082886610f0e565b9050610832611489565b602082810151600090815260078252604090819020815160e081018352815460ff1615158152600180830154828601526002808401548386015260038401805486516101009482161594909402600019011691909104601f81018790048702830187019095528482529194929360608601939192918301828280156108f85780601f106108cd576101008083540402835291602001916108f8565b820191906000526020600020905b8154815290600101906020018083116108db57829003601f168201915b5050509183525050600482015467ffffffffffffffff1660208201526005820154604082015260069091015460609091015290506000806109398484611001565b945050505091506000600681111561094d57fe5b82600681111561095957fe5b146109ab576040805162461bcd60e51b815260206004820152601860248201527f43616e277420696d706f727420616e7920686561646572730000000000000000604482015290519081900360640190fd5b83604001518114156109c4576001945050505050610b20565b87516109d7576000945050505050610b20565b6109df611489565b6109e98585611171565b90506109f3611454565b6109fc8a610f0e565b90506000610a0a8284611001565b9450505050508160400151811415610a2c576002975050505050505050610b20565b8951610a42576000975050505050505050610b20565b610a4a611489565b610a548388611171565b9050610a5e611454565b610a678c610f0e565b90506000610a758284611001565b9450505050508160400151811415610a9a5760039a5050505050505050505050610b20565b8b51610ab35760009a5050505050505050505050610b20565b610abb611489565b610ac5838b611171565b9050610acf611454565b610ad88e610f0e565b90506000610ae68284611001565b9450505050508160400151811415610b0e5760049d5050505050505050505050505050610b20565b60009d50505050505050505050505050505b949350505050565b6000828152600760205260409020600201548314610b775760405162461bcd60e51b815260040180806020018281038252602f8152602001806115d5602f913960400191505060405180910390fd5b60028054600354600480546040805160206101006001851615026000190190931696909604601f81018390048302870183019091528086529394600094610c28948a948a9467ffffffffffffffff90921693929091830182828015610c1d5780601f10610bf257610100808354040283529160200191610c1d565b820191906000526020600020905b815481529060010190602001808311610c0057829003601f168201915b5050505050876111d0565b600081815260076020526040902060028281558101546001559091505b828214610d8557506000818152600760209081526040808320600181015460069093529220549092908015610d07576005546000199182019181018214610cd357600060056001830381548110610c9857fe5b906000526020600020015490508060058481548110610cb357fe5b600091825260208083209091019290925591825260069052604090208290555b6005805480610cde57fe5b600082815260208082208301600019908101839055909201909255848252600690526040812055505b826006015483600201541415610d7e57600583015460009081526007602052604090206003805467ffffffffffffffff198116600167ffffffffffffffff92831681019092161782559082018054610d759260049291600261010092821615929092026000190116046114c4565b50505050610d85565b5050610c45565b505050505050565b600080610d98611454565b6000806000610da687611312565b9398509196509450925090506000856006811115610dc057fe5b14610dd3576000955050505050506107f8565b604084015181148015610e27576005805486516001820180845560009384527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0909201558651825260066020526040909120555b6040805160e0810182526001808252602088810151818401908152898501518486019081526080808c01516060870190815267ffffffffffffffff8c169187019190915260a086018a905260c086018990528b51600090815260078552969096208551815460ff1916901515178155915193820193909355915160028301559251805192939192610ebe9260038501920190611549565b50608082015160048201805467ffffffffffffffff191667ffffffffffffffff90921691909117905560a0820151600582015560c090910151600690910155935160005550509015949350505050565b610f16611454565b60008060008060008651602088016040516020810160208101602081016020810160a08588886010600019fa610f4b57600080fd5b84519b5083519a508251995081519850805197505050505050505060608167ffffffffffffffff81118015610f7f57600080fd5b506040519080825280601f01601f191660200182016040528015610faa576020820181803683370190505b5090508115610fd45787516020890160208301848184846011600019fa610fd057600080fd5b5050505b6040805160a081018252968752602087019590955293850192909252606084015250608082015292915050565b600061100b611454565b83516000908152600760205260408120548190819060ff161561103d5750600193508592506000915081905080611167565b60015487604001511161105f5750600293508592506000915081905080611167565b8551158061107857506001876040015103866040015114155b156110925750600393508592506000915081905080611167565b60c0860151158015906110ac575085604001518660c00151145b156110d3578660200151600254146110d35750600493508592506000915081905080611167565b60808087015160a088015160c0890151928a01515191929091156111585767ffffffffffffffff838116141561111d57506005965088955060009450849350839250611167915050565b8960400151811061114257506006965088955060009450849350839250611167915050565b50508751606089015160408a0151600190930192015b60009750899650919450925090505b9295509295909350565b611179611489565b506040805160e08101825260018082528451602083015293820151909301908301526060818101519083015260808082015167ffffffffffffffff169083015260a0808201519083015260c0908101519082015290565b600060608686868686604051602001808681526020018581526020018467ffffffffffffffff1667ffffffffffffffff1681526020018060200180602001838103835285818151815260200191508051906020019080838360005b8381101561124357818101518382015260200161122b565b50505050905090810190601f1680156112705780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b838110156112a357818101518382015260200161128b565b50505050905090810190601f1680156112d05780820380516001836020036101000a031916815260200191505b50975050505050505050604051602081830303815290604052905080516020820160008083836012600019fa61130557600080fd5b5095979650505050505050565b600061131c611454565b6000806000611329611454565b61133287610f0e565b905061133c611489565b602082810151600090815260078252604090819020815160e081018352815460ff1615158152600180830154828601526002808401548386015260038401805486516101009482161594909402600019011691909104601f81018790048702830187019095528482529194929360608601939192918301828280156114025780601f106113d757610100808354040283529160200191611402565b820191906000526020600020905b8154815290600101906020018083116113e557829003601f168201915b5050509183525050600482015467ffffffffffffffff1660208201526005820154604082015260069091015460609091015290506114408282611001565b939c929b5090995097509095509350505050565b6040518060a0016040528060008019168152602001600080191681526020016000815260200160008152602001606081525090565b6040805160e0810182526000808252602082018190529181018290526060808201526080810182905260a0810182905260c081019190915290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106114fd5780548555611539565b8280016001018555821561153957600052602060002091601f016020900482015b8281111561153957825482559160010191906001019061151e565b506115459291506115b7565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061158a57805160ff1916838001178555611539565b82800160010185558215611539579182015b8281111561153957825182559160200191906001019061159c565b6115d191905b8082111561154557600081556001016115bd565b9056fe4d697373696e672066696e616c69747920746172676574206865616465722066726f6d207468652073746f72616765a2646970667358221220edcaec08f93f74ce5be00b81da5d6b2276138571a33f1cfdca50e5047f854e6e64736f6c63430006060033 \ No newline at end of file diff --git a/bridges/relays/ethereum/res/substrate-bridge-metadata.txt b/bridges/relays/ethereum/res/substrate-bridge-metadata.txt index 60b8d022fc..13b7daa9a8 100644 --- a/bridges/relays/ethereum/res/substrate-bridge-metadata.txt +++ b/bridges/relays/ethereum/res/substrate-bridge-metadata.txt @@ -1,5 +1,5 @@ -Last Change Date: 2020-07-03 -Solc version: 0.6.6+commit.6c089d02 -Source hash (keccak256): 0x3e6339beefe6786f4f26b408d4f727e03c6fd9630d692af9a7f6b46143fa308f -Source gist: https://github.com/svyatonik/substrate-bridge-sol/blob/1d0fa475a2ba3a70a47ed2dd870568c42ec16c8c/substrate-bridge.sol +Last Change Date: 2020-07-30 +Solc version: 0.6.6+commit.6c089d02.Linux.g++ +Source hash (keccak256): 0xea5d6d744f69157adc2857166792aca139c0b5b186ba89c1011358fbcad90d7e +Source gist: https://github.com/svyatonik/substrate-bridge-sol/blob/6456d3e016c95cd5e6d5e817c23e9e69e739aa78/substrate-bridge.sol Compiler flags used (command to produce the file): `docker run -i ethereum/solc:0.6.6 --optimize --bin - < substrate-bridge.sol` \ No newline at end of file diff --git a/bridges/relays/ethereum/src/ethereum_client.rs b/bridges/relays/ethereum/src/ethereum_client.rs index b489cab522..ae7e630c33 100644 --- a/bridges/relays/ethereum/src/ethereum_client.rs +++ b/bridges/relays/ethereum/src/ethereum_client.rs @@ -32,12 +32,12 @@ use jsonrpsee::transport::http::HttpTransportClient; use jsonrpsee::Client; use parity_crypto::publickey::KeyPair; -use std::collections::{HashSet, VecDeque}; +use std::collections::HashSet; // to encode/decode contract calls ethabi_contract::use_contract!(bridge_contract, "res/substrate-bridge-abi.json"); -type Result = std::result::Result; +type RpcResult = std::result::Result; /// Ethereum connection params. #[derive(Debug, Clone)] @@ -104,15 +104,15 @@ impl EthereumRpcClient { #[async_trait] impl EthereumRpc for EthereumRpcClient { - async fn estimate_gas(&self, call_request: CallRequest) -> Result { + async fn estimate_gas(&self, call_request: CallRequest) -> RpcResult { Ok(Ethereum::estimate_gas(&self.client, call_request).await?) } - async fn best_block_number(&self) -> Result { + async fn best_block_number(&self) -> RpcResult { Ok(Ethereum::block_number(&self.client).await?.as_u64()) } - async fn header_by_number(&self, block_number: u64) -> Result
{ + async fn header_by_number(&self, block_number: u64) -> RpcResult
{ let get_full_tx_objects = false; let header = Ethereum::get_block_by_number(&self.client, block_number, get_full_tx_objects).await?; match header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some() { @@ -121,7 +121,7 @@ impl EthereumRpc for EthereumRpcClient { } } - async fn header_by_hash(&self, hash: H256) -> Result
{ + async fn header_by_hash(&self, hash: H256) -> RpcResult
{ let get_full_tx_objects = false; let header = Ethereum::get_block_by_hash(&self.client, hash, get_full_tx_objects).await?; match header.number.is_some() && header.hash.is_some() && header.logs_bloom.is_some() { @@ -130,7 +130,7 @@ impl EthereumRpc for EthereumRpcClient { } } - async fn header_by_number_with_transactions(&self, number: u64) -> Result { + async fn header_by_number_with_transactions(&self, number: u64) -> RpcResult { let get_full_tx_objects = true; let header = Ethereum::get_block_by_number_with_transactions(&self.client, number, get_full_tx_objects).await?; @@ -147,7 +147,7 @@ impl EthereumRpc for EthereumRpcClient { Ok(header) } - async fn header_by_hash_with_transactions(&self, hash: H256) -> Result { + async fn header_by_hash_with_transactions(&self, hash: H256) -> RpcResult { let get_full_tx_objects = true; let header = Ethereum::get_block_by_hash_with_transactions(&self.client, hash, get_full_tx_objects).await?; @@ -164,24 +164,26 @@ impl EthereumRpc for EthereumRpcClient { Ok(header) } - async fn transaction_by_hash(&self, hash: H256) -> Result> { + async fn transaction_by_hash(&self, hash: H256) -> RpcResult> { Ok(Ethereum::transaction_by_hash(&self.client, hash).await?) } - async fn transaction_receipt(&self, transaction_hash: H256) -> Result { + async fn transaction_receipt(&self, transaction_hash: H256) -> RpcResult { Ok(Ethereum::get_transaction_receipt(&self.client, transaction_hash).await?) } - async fn account_nonce(&self, address: Address) -> Result { + async fn account_nonce(&self, address: Address) -> RpcResult { Ok(Ethereum::get_transaction_count(&self.client, address).await?) } - async fn submit_transaction(&self, signed_raw_tx: SignedRawTx) -> Result { + async fn submit_transaction(&self, signed_raw_tx: SignedRawTx) -> RpcResult { let transaction = Bytes(signed_raw_tx); - Ok(Ethereum::submit_transaction(&self.client, transaction).await?) + let tx_hash = Ethereum::submit_transaction(&self.client, transaction).await?; + log::trace!(target: "bridge", "Sent transaction to Ethereum node: {:?}", tx_hash); + Ok(tx_hash) } - async fn eth_call(&self, call_transaction: CallRequest) -> Result { + async fn eth_call(&self, call_transaction: CallRequest) -> RpcResult { Ok(Ethereum::call(&self.client, call_transaction).await?) } } @@ -191,14 +193,14 @@ impl EthereumRpc for EthereumRpcClient { #[async_trait] pub trait EthereumHighLevelRpc: EthereumRpc { /// Returns best Substrate block that PoA chain knows of. - async fn best_substrate_block(&self, contract_address: Address) -> Result; + async fn best_substrate_block(&self, contract_address: Address) -> RpcResult; /// Returns true if Substrate header is known to Ethereum node. async fn substrate_header_known( &self, contract_address: Address, id: SubstrateHeaderId, - ) -> Result<(SubstrateHeaderId, bool)>; + ) -> RpcResult<(SubstrateHeaderId, bool)>; /// Submits Substrate headers to Ethereum contract. async fn submit_substrate_headers( @@ -209,7 +211,7 @@ pub trait EthereumHighLevelRpc: EthereumRpc { ) -> SubmittedHeaders; /// Returns ids of incomplete Substrate headers. - async fn incomplete_substrate_headers(&self, contract_address: Address) -> Result>; + async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult>; /// Complete Substrate header. async fn complete_substrate_header( @@ -218,7 +220,7 @@ pub trait EthereumHighLevelRpc: EthereumRpc { contract_address: Address, id: SubstrateHeaderId, justification: GrandpaJustification, - ) -> Result; + ) -> RpcResult; /// Submit ethereum transaction. async fn submit_ethereum_transaction( @@ -228,19 +230,19 @@ pub trait EthereumHighLevelRpc: EthereumRpc { nonce: Option, double_gas: bool, encoded_call: Vec, - ) -> Result<()>; + ) -> RpcResult<()>; /// Retrieve transactions receipts for given block. async fn transaction_receipts( &self, id: EthereumHeaderId, transactions: Vec, - ) -> Result<(EthereumHeaderId, Vec)>; + ) -> RpcResult<(EthereumHeaderId, Vec)>; } #[async_trait] impl EthereumHighLevelRpc for EthereumRpcClient { - async fn best_substrate_block(&self, contract_address: Address) -> Result { + async fn best_substrate_block(&self, contract_address: Address) -> RpcResult { let (encoded_call, call_decoder) = bridge_contract::functions::best_known_header::call(); let call_request = CallRequest { to: Some(contract_address), @@ -263,7 +265,7 @@ impl EthereumHighLevelRpc for EthereumRpcClient { &self, contract_address: Address, id: SubstrateHeaderId, - ) -> Result<(SubstrateHeaderId, bool)> { + ) -> RpcResult<(SubstrateHeaderId, bool)> { let (encoded_call, call_decoder) = bridge_contract::functions::is_known_header::call(id.1); let call_request = CallRequest { to: Some(contract_address), @@ -313,7 +315,7 @@ impl EthereumHighLevelRpc for EthereumRpcClient { .await } - async fn incomplete_substrate_headers(&self, contract_address: Address) -> Result> { + async fn incomplete_substrate_headers(&self, contract_address: Address) -> RpcResult> { let (encoded_call, call_decoder) = bridge_contract::functions::incomplete_headers::call(); let call_request = CallRequest { to: Some(contract_address), @@ -346,7 +348,7 @@ impl EthereumHighLevelRpc for EthereumRpcClient { contract_address: Address, id: SubstrateHeaderId, justification: GrandpaJustification, - ) -> Result { + ) -> RpcResult { let _ = self .submit_ethereum_transaction( ¶ms, @@ -367,7 +369,7 @@ impl EthereumHighLevelRpc for EthereumRpcClient { nonce: Option, double_gas: bool, encoded_call: Vec, - ) -> Result<()> { + ) -> RpcResult<()> { let nonce = if let Some(n) = nonce { n } else { @@ -400,7 +402,7 @@ impl EthereumHighLevelRpc for EthereumRpcClient { &self, id: EthereumHeaderId, transactions: Vec, - ) -> Result<(EthereumHeaderId, Vec)> { + ) -> RpcResult<(EthereumHeaderId, Vec)> { let mut transaction_receipts = Vec::with_capacity(transactions.len()); for transaction in transactions { let transaction_receipt = self.transaction_receipt(transaction).await?; @@ -410,18 +412,115 @@ impl EthereumHighLevelRpc for EthereumRpcClient { } } +/// Max number of headers which can be sent to Solidity contract. +pub const HEADERS_BATCH: usize = 4; + +/// Substrate headers to send to the Ethereum light client. +/// +/// The Solidity contract can only accept a fixed number of headers in one go. +/// This struct is meant to encapsulate this limitation. +#[derive(Debug)] +#[cfg_attr(test, derive(Clone))] +pub struct HeadersBatch { + pub header1: QueuedSubstrateHeader, + pub header2: Option, + pub header3: Option, + pub header4: Option, +} + +impl HeadersBatch { + /// Create new headers from given header & ids collections. + /// + /// This method will pop `HEADERS_BATCH` items from both collections + /// and construct `Headers` object and a vector of `SubstrateheaderId`s. + pub fn pop_from( + headers: &mut Vec, + ids: &mut Vec, + ) -> Result<(Self, Vec), ()> { + if headers.len() != ids.len() { + log::error!(target: "bridge", "Collection size mismatch ({} vs {})", headers.len(), ids.len()); + return Err(()); + } + + let header1 = headers.pop().ok_or(())?; + let header2 = headers.pop(); + let header3 = headers.pop(); + let header4 = headers.pop(); + + let mut submitting_ids = Vec::with_capacity(HEADERS_BATCH); + for _ in 0..HEADERS_BATCH { + submitting_ids.extend(ids.pop().iter()); + } + + Ok(( + Self { + header1, + header2, + header3, + header4, + }, + submitting_ids, + )) + } + + /// Returns unified array of headers. + /// + /// The first element is always `Some`. + fn headers(&self) -> [Option<&QueuedSubstrateHeader>; HEADERS_BATCH] { + [ + Some(&self.header1), + self.header2.as_ref(), + self.header3.as_ref(), + self.header4.as_ref(), + ] + } + + /// Encodes all headers. If header is not present an empty vector will be returned. + pub fn encode(&self) -> [Vec; HEADERS_BATCH] { + let encode = |h: &QueuedSubstrateHeader| h.header().encode(); + let headers = self.headers(); + [ + headers[0].map(encode).unwrap_or_default(), + headers[1].map(encode).unwrap_or_default(), + headers[2].map(encode).unwrap_or_default(), + headers[3].map(encode).unwrap_or_default(), + ] + } + /// Returns number of contained headers. + pub fn len(&self) -> usize { + let is_set = |h: &Option<&QueuedSubstrateHeader>| if h.is_some() { 1 } else { 0 }; + self.headers().iter().map(is_set).sum() + } + + /// Remove headers starting from `idx` (0-based) from this collection. + /// + /// The collection will be left with `[0, idx)` headers. + /// Returns `Err` when `idx == 0`, since `Headers` must contain at least one header, + /// or when `idx > HEADERS_BATCH`. + pub fn split_off(&mut self, idx: usize) -> Result<(), ()> { + if idx == 0 || idx > HEADERS_BATCH { + return Err(()); + } + let mut vals: [_; HEADERS_BATCH] = [&mut None, &mut self.header2, &mut self.header3, &mut self.header4]; + for val in vals.iter_mut().skip(idx) { + **val = None; + } + Ok(()) + } +} + /// Substrate headers submitter API. #[async_trait] trait HeadersSubmitter { - /// Returns Ok(true) if not-yet-imported header is incomplete. - /// Returns Ok(false) if not-yet-imported header is complete. + /// Returns Ok(0) if all given not-yet-imported headers are complete. + /// Returns Ok(index != 0) where index is 1-based index of first header that is incomplete. /// - /// Returns Err(()) if contract has rejected header. This probably means - /// that the header is already imported by the contract. - async fn is_header_incomplete(&self, header: &QueuedSubstrateHeader) -> Result; + /// Returns Err(()) if contract has rejected headers. This means that the contract is + /// unable to import first header (e.g. it may already be imported). + async fn is_headers_incomplete(&self, headers: &HeadersBatch) -> RpcResult; - /// Submit given header to Ethereum node. - async fn submit_header(&mut self, header: QueuedSubstrateHeader) -> Result<()>; + /// Submit given headers to Ethereum node. + async fn submit_headers(&mut self, headers: HeadersBatch) -> RpcResult<()>; } /// Implementation of Substrate headers submitter that sends headers to running Ethereum node. @@ -434,9 +533,9 @@ struct EthereumHeadersSubmitter { #[async_trait] impl HeadersSubmitter for EthereumHeadersSubmitter { - async fn is_header_incomplete(&self, header: &QueuedSubstrateHeader) -> Result { - let (encoded_call, call_decoder) = - bridge_contract::functions::is_incomplete_header::call(header.header().encode()); + async fn is_headers_incomplete(&self, headers: &HeadersBatch) -> RpcResult { + let [h1, h2, h3, h4] = headers.encode(); + let (encoded_call, call_decoder) = bridge_contract::functions::is_incomplete_headers::call(h1, h2, h3, h4); let call_request = CallRequest { to: Some(self.contract_address), data: Some(encoded_call.into()), @@ -444,12 +543,16 @@ impl HeadersSubmitter for EthereumHeadersSubmitter { }; let call_result = self.client.eth_call(call_request).await?; - let is_incomplete = call_decoder.decode(&call_result.0)?; + let incomplete_index: U256 = call_decoder.decode(&call_result.0)?; + if incomplete_index > HEADERS_BATCH.into() { + return Err(RpcError::Ethereum(EthereumNodeError::InvalidIncompleteIndex)); + } - Ok(is_incomplete) + Ok(incomplete_index.low_u32() as _) } - async fn submit_header(&mut self, header: QueuedSubstrateHeader) -> Result<()> { + async fn submit_headers(&mut self, headers: HeadersBatch) -> RpcResult<()> { + let [h1, h2, h3, h4] = headers.encode(); let result = self .client .submit_ethereum_transaction( @@ -457,7 +560,7 @@ impl HeadersSubmitter for EthereumHeadersSubmitter { Some(self.contract_address), Some(self.nonce), false, - bridge_contract::functions::import_header::encode_input(header.header().encode()), + bridge_contract::functions::import_headers::encode_input(h1, h2, h3, h4), ) .await; @@ -472,16 +575,23 @@ impl HeadersSubmitter for EthereumHeadersSubmitter { /// Submit multiple Substrate headers. async fn submit_substrate_headers( mut header_submitter: impl HeadersSubmitter, - headers: Vec, + mut headers: Vec, ) -> SubmittedHeaders { - let mut ids = headers.iter().map(|header| header.id()).collect::>(); let mut submitted_headers = SubmittedHeaders::default(); - for header in headers { - let id = ids.pop_front().expect("both collections have same size; qed"); + + let mut ids = headers.iter().map(|header| header.id()).rev().collect::>(); + headers.reverse(); + + while !headers.is_empty() { + let (headers, submitting_ids) = + HeadersBatch::pop_from(&mut headers, &mut ids).expect("Headers and ids are not empty; qed"); + submitted_headers.fatal_error = - submit_substrate_header(&mut header_submitter, &mut submitted_headers, id, header).await; + submit_substrate_headers_batch(&mut header_submitter, &mut submitted_headers, submitting_ids, headers) + .await; if submitted_headers.fatal_error.is_some() { + ids.reverse(); submitted_headers.rejected.extend(ids); break; } @@ -490,28 +600,31 @@ async fn submit_substrate_headers( submitted_headers } -/// Submit single Substrate header. -async fn submit_substrate_header( +/// Submit 4 Substrate headers in single PoA transaction. +async fn submit_substrate_headers_batch( header_submitter: &mut impl HeadersSubmitter, submitted_headers: &mut SubmittedHeaders, - id: SubstrateHeaderId, - header: QueuedSubstrateHeader, + mut ids: Vec, + mut headers: HeadersBatch, ) -> Option { - // if parent of this header is either incomplete, or rejected, we assume that contract + debug_assert_eq!(ids.len(), headers.len(),); + + // if parent of first header is either incomplete, or rejected, we assume that contract // will reject this header as well - let parent_id = header.parent_id(); + let parent_id = headers.header1.parent_id(); if submitted_headers.rejected.contains(&parent_id) || submitted_headers.incomplete.contains(&parent_id) { - submitted_headers.rejected.push(id); + submitted_headers.rejected.extend(ids); return None; } - // check if this header is incomplete - let is_header_incomplete = match header_submitter.is_header_incomplete(&header).await { - Ok(true) => true, - Ok(false) => false, + // check if headers are incomplete + let incomplete_header_index = match header_submitter.is_headers_incomplete(&headers).await { + // All headers valid + Ok(0) => None, + Ok(incomplete_header_index) => Some(incomplete_header_index), Err(error) => { - // contract has rejected this header => we do not want to submit it - submitted_headers.rejected.push(id); + // contract has rejected all headers => we do not want to submit it + submitted_headers.rejected.extend(ids); if error.is_connection_error() { return Some(error); } else { @@ -520,17 +633,30 @@ async fn submit_substrate_header( } }; - // submit header and update submitted headers - match header_submitter.submit_header(header).await { + // Modify `ids` and `headers` to only contain values that are going to be accepted. + let rejected = if let Some(idx) = incomplete_header_index { + let len = std::cmp::min(idx, ids.len()); + headers + .split_off(len) + .expect("len > 0, the case where all headers are valid is converted to None; qed"); + ids.split_off(len) + } else { + Vec::new() + }; + let submitted = ids; + let submit_result = header_submitter.submit_headers(headers).await; + match submit_result { Ok(_) => { - submitted_headers.submitted.push(id); - if is_header_incomplete { - submitted_headers.incomplete.push(id); + if incomplete_header_index.is_some() { + submitted_headers.incomplete.extend(submitted.iter().last().cloned()); } + submitted_headers.submitted.extend(submitted); + submitted_headers.rejected.extend(rejected); None } Err(error) => { - submitted_headers.rejected.push(id); + submitted_headers.rejected.extend(submitted); + submitted_headers.rejected.extend(rejected); Some(error) } } @@ -549,16 +675,16 @@ mod tests { #[async_trait] impl HeadersSubmitter for TestHeadersSubmitter { - async fn is_header_incomplete(&self, header: &QueuedSubstrateHeader) -> Result { - if self.incomplete.iter().any(|i| i.0 == header.id().0) { - Ok(true) + async fn is_headers_incomplete(&self, headers: &HeadersBatch) -> RpcResult { + if self.incomplete.iter().any(|i| i.0 == headers.header1.id().0) { + Ok(1) } else { - Ok(false) + Ok(0) } } - async fn submit_header(&mut self, header: QueuedSubstrateHeader) -> Result<()> { - if self.failed.iter().any(|i| i.0 == header.id().0) { + async fn submit_headers(&mut self, headers: HeadersBatch) -> RpcResult<()> { + if self.failed.iter().any(|i| i.0 == headers.header1.id().0) { Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber)) } else { Ok(()) @@ -600,13 +726,102 @@ mod tests { let submitted_headers = async_std::task::block_on(submit_substrate_headers( TestHeadersSubmitter { incomplete: vec![], - failed: vec![header(6).id()], + failed: vec![header(9).id()], }, - vec![header(5), header(6), header(7)], + vec![ + header(5), + header(6), + header(7), + header(8), + header(9), + header(10), + header(11), + ], )); - assert_eq!(submitted_headers.submitted, vec![header(5).id()]); + assert_eq!( + submitted_headers.submitted, + vec![header(5).id(), header(6).id(), header(7).id(), header(8).id()] + ); assert_eq!(submitted_headers.incomplete, vec![]); - assert_eq!(submitted_headers.rejected, vec![header(6).id(), header(7).id()]); + assert_eq!( + submitted_headers.rejected, + vec![header(9).id(), header(10).id(), header(11).id(),] + ); assert!(submitted_headers.fatal_error.is_some()); } + + fn headers_batch() -> HeadersBatch { + let mut init_headers = vec![header(1), header(2), header(3), header(4), header(5)]; + init_headers.reverse(); + let mut init_ids = init_headers.iter().map(|h| h.id()).collect(); + let (headers, ids) = HeadersBatch::pop_from(&mut init_headers, &mut init_ids).unwrap(); + assert_eq!(init_headers, vec![header(5)]); + assert_eq!(init_ids, vec![header(5).id()]); + assert_eq!( + ids, + vec![header(1).id(), header(2).id(), header(3).id(), header(4).id()] + ); + headers + } + + #[test] + fn headers_batch_len() { + let headers = headers_batch(); + assert_eq!(headers.len(), 4); + } + + #[test] + fn headers_batch_encode() { + let headers = headers_batch(); + assert_eq!( + headers.encode(), + [ + header(1).header().encode(), + header(2).header().encode(), + header(3).header().encode(), + header(4).header().encode(), + ] + ); + } + + #[test] + fn headers_batch_split_off() { + // given + let mut headers = headers_batch(); + + // when + assert!(headers.split_off(0).is_err()); + assert_eq!(headers.header1, header(1)); + assert!(headers.header2.is_some()); + assert!(headers.header3.is_some()); + assert!(headers.header4.is_some()); + + // when + let mut h = headers.clone(); + h.split_off(1).unwrap(); + assert!(h.header2.is_none()); + assert!(h.header3.is_none()); + assert!(h.header4.is_none()); + + // when + let mut h = headers.clone(); + h.split_off(2).unwrap(); + assert!(h.header2.is_some()); + assert!(h.header3.is_none()); + assert!(h.header4.is_none()); + + // when + let mut h = headers.clone(); + h.split_off(3).unwrap(); + assert!(h.header2.is_some()); + assert!(h.header3.is_some()); + assert!(h.header4.is_none()); + + // when + let mut h = headers; + h.split_off(4).unwrap(); + assert!(h.header2.is_some()); + assert!(h.header3.is_some()); + assert!(h.header4.is_some()); + } } diff --git a/bridges/relays/ethereum/src/exchange_loop.rs b/bridges/relays/ethereum/src/exchange_loop.rs index 4d9a031ec9..0da29d2324 100644 --- a/bridges/relays/ethereum/src/exchange_loop.rs +++ b/bridges/relays/ethereum/src/exchange_loop.rs @@ -157,7 +157,7 @@ async fn run_loop_iteration( ) -> Result<(), ()> { let best_finalized_header_id = match target_client.best_finalized_header_id().await { Ok(best_finalized_header_id) => { - log::trace!( + log::debug!( target: "bridge", "Got best finalized {} block from {} node: {:?}", P::SOURCE_NAME, diff --git a/bridges/relays/ethereum/src/rpc_errors.rs b/bridges/relays/ethereum/src/rpc_errors.rs index aa3bcbde09..26206bdc1d 100644 --- a/bridges/relays/ethereum/src/rpc_errors.rs +++ b/bridges/relays/ethereum/src/rpc_errors.rs @@ -100,6 +100,8 @@ pub enum EthereumNodeError { /// An invalid Substrate block number was received from /// an Ethereum node. InvalidSubstrateBlockNumber, + /// An invalid index has been received from an Ethereum node. + InvalidIncompleteIndex, } impl ToString for EthereumNodeError { @@ -112,6 +114,7 @@ impl ToString for EthereumNodeError { } Self::IncompleteTransaction => "Incomplete Ethereum Transaction (missing required field - raw)".to_string(), Self::InvalidSubstrateBlockNumber => "Received an invalid Substrate block from Ethereum Node".to_string(), + Self::InvalidIncompleteIndex => "Received an invalid incomplete index from Ethereum Node".to_string(), } } } diff --git a/bridges/relays/ethereum/src/substrate_client.rs b/bridges/relays/ethereum/src/substrate_client.rs index f40d3a5472..4970587d34 100644 --- a/bridges/relays/ethereum/src/substrate_client.rs +++ b/bridges/relays/ethereum/src/substrate_client.rs @@ -39,7 +39,7 @@ const ETH_API_BEST_FINALIZED_BLOCK: &str = "RialtoHeaderApi_finalized_block"; const EXCH_API_FILTER_TRANSACTION_PROOF: &str = "RialtoCurrencyExchangeApi_filter_transaction_proof"; const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; -type Result = std::result::Result; +type RpcResult = std::result::Result; type GrandpaAuthorityList = Vec; /// Substrate connection params. @@ -93,7 +93,7 @@ pub struct SubstrateRpcClient { impl SubstrateRpcClient { /// Returns client that is able to call RPCs on Substrate node. - pub async fn new(params: SubstrateConnectionParams, instance: Box) -> Result { + pub async fn new(params: SubstrateConnectionParams, instance: Box) -> RpcResult { let uri = format!("http://{}:{}", params.host, params.port); let transport = HttpTransportClient::new(&uri); let raw_client = RawClient::new(transport); @@ -112,32 +112,32 @@ impl SubstrateRpcClient { #[async_trait] impl SubstrateRpc for SubstrateRpcClient { - async fn best_header(&self) -> Result { + async fn best_header(&self) -> RpcResult { Ok(Substrate::chain_get_header(&self.client, None).await?) } - async fn get_block(&self, block_hash: Option) -> Result { + async fn get_block(&self, block_hash: Option) -> RpcResult { Ok(Substrate::chain_get_block(&self.client, block_hash).await?) } - async fn header_by_hash(&self, block_hash: Hash) -> Result { + async fn header_by_hash(&self, block_hash: Hash) -> RpcResult { Ok(Substrate::chain_get_header(&self.client, block_hash).await?) } - async fn block_hash_by_number(&self, number: Number) -> Result { + async fn block_hash_by_number(&self, number: Number) -> RpcResult { Ok(Substrate::chain_get_block_hash(&self.client, number).await?) } - async fn header_by_number(&self, block_number: Number) -> Result { + async fn header_by_number(&self, block_number: Number) -> RpcResult { let block_hash = Self::block_hash_by_number(self, block_number).await?; Ok(Self::header_by_hash(self, block_hash).await?) } - async fn next_account_index(&self, account: node_primitives::AccountId) -> Result { + async fn next_account_index(&self, account: node_primitives::AccountId) -> RpcResult { Ok(Substrate::system_account_next_index(&self.client, account).await?) } - async fn best_ethereum_block(&self) -> Result { + async fn best_ethereum_block(&self) -> RpcResult { let call = ETH_API_BEST_BLOCK.to_string(); let data = Bytes(Vec::new()); @@ -148,7 +148,7 @@ impl SubstrateRpc for SubstrateRpcClient { Ok(best_header_id) } - async fn best_ethereum_finalized_block(&self) -> Result { + async fn best_ethereum_finalized_block(&self) -> RpcResult { let call = ETH_API_BEST_FINALIZED_BLOCK.to_string(); let data = Bytes(Vec::new()); @@ -159,7 +159,7 @@ impl SubstrateRpc for SubstrateRpcClient { Ok(best_header_id) } - async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> Result { + async fn ethereum_receipts_required(&self, header: SubstrateEthereumHeader) -> RpcResult { let call = ETH_API_IMPORT_REQUIRES_RECEIPTS.to_string(); let data = Bytes(header.encode()); @@ -175,7 +175,7 @@ impl SubstrateRpc for SubstrateRpcClient { // But when we read the best header from Substrate next time, we will know that // there's a better header. This Orphan will either be marked as synced, or // eventually pruned. - async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> Result { + async fn ethereum_header_known(&self, header_id: EthereumHeaderId) -> RpcResult { let call = ETH_API_IS_KNOWN_BLOCK.to_string(); let data = Bytes(header_id.1.encode()); @@ -185,11 +185,13 @@ impl SubstrateRpc for SubstrateRpcClient { Ok(is_known_block) } - async fn submit_extrinsic(&self, transaction: Bytes) -> Result { - Ok(Substrate::author_submit_extrinsic(&self.client, transaction).await?) + async fn submit_extrinsic(&self, transaction: Bytes) -> RpcResult { + let tx_hash = Substrate::author_submit_extrinsic(&self.client, transaction).await?; + log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash); + Ok(tx_hash) } - async fn grandpa_authorities_set(&self, block: Hash) -> Result { + async fn grandpa_authorities_set(&self, block: Hash) -> RpcResult { let call = SUB_API_GRANDPA_AUTHORITIES.to_string(); let data = Bytes(Vec::new()); @@ -312,13 +314,13 @@ pub trait SubmitEthereumExchangeTransactionProof: SubstrateRpc { async fn verify_exchange_transaction_proof( &self, proof: bridge_node_runtime::exchange::EthereumTransactionInclusionProof, - ) -> Result; + ) -> RpcResult; /// Submits Ethereum exchange transaction proof to Substrate runtime. async fn submit_exchange_transaction_proof( &self, params: SubstrateSigningParams, proof: bridge_node_runtime::exchange::EthereumTransactionInclusionProof, - ) -> Result<()>; + ) -> RpcResult<()>; } #[async_trait] @@ -326,7 +328,7 @@ impl SubmitEthereumExchangeTransactionProof for SubstrateRpcClient { async fn verify_exchange_transaction_proof( &self, proof: bridge_node_runtime::exchange::EthereumTransactionInclusionProof, - ) -> Result { + ) -> RpcResult { let call = EXCH_API_FILTER_TRANSACTION_PROOF.to_string(); let data = Bytes(proof.encode()); @@ -340,7 +342,7 @@ impl SubmitEthereumExchangeTransactionProof for SubstrateRpcClient { &self, params: SubstrateSigningParams, proof: bridge_node_runtime::exchange::EthereumTransactionInclusionProof, - ) -> Result<()> { + ) -> RpcResult<()> { let account_id = params.signer.public().as_array_ref().clone().into(); let nonce = self.next_account_index(account_id).await?; diff --git a/bridges/relays/ethereum/src/sync.rs b/bridges/relays/ethereum/src/sync.rs index 9ff7a0733f..2538b04c32 100644 --- a/bridges/relays/ethereum/src/sync.rs +++ b/bridges/relays/ethereum/src/sync.rs @@ -91,6 +91,8 @@ pub struct HeadersSync { target_best_header: Option>, /// Headers queue. headers: QueuedHeaders

, + /// Pause headers submission. + pause_submit: bool, } impl HeadersSync

{ @@ -101,6 +103,7 @@ impl HeadersSync

{ params, source_best_number: None, target_best_header: None, + pause_submit: false, } } @@ -191,6 +194,11 @@ impl HeadersSync

{ /// Select headers that need to be submitted to the target node. pub fn select_headers_to_submit(&self, stalled: bool) -> Option>> { + // maybe we have paused new headers submit? + if self.pause_submit { + return None; + } + // if we operate in backup mode, we only submit headers when sync has stalled if self.params.target_tx_mode == TargetTransactionMode::Backup && !stalled { return None; @@ -260,14 +268,40 @@ impl HeadersSync

{ // finally remember the best header itself self.target_best_header = Some(best_header); + // we are ready to submit headers again + if self.pause_submit { + log::debug!( + target: "bridge", + "Ready to submit {} headers to {} node again!", + P::SOURCE_NAME, + P::TARGET_NAME, + ); + + self.pause_submit = false; + } + true } + /// Pause headers submit until best header will be updated on target node. + pub fn pause_submit(&mut self) { + log::debug!( + target: "bridge", + "Stopping submitting {} headers to {} node. Waiting for {} submitted headers to be accepted", + P::SOURCE_NAME, + P::TARGET_NAME, + self.headers.headers_in_status(HeaderStatus::Submitted), + ); + + self.pause_submit = true; + } + /// Restart synchronization. pub fn restart(&mut self) { self.source_best_number = None; self.target_best_header = None; self.headers.clear(); + self.pause_submit = false; } } @@ -481,4 +515,35 @@ pub mod tests { // ensure that headers are not submitted when sync is stalled assert_eq!(eth_sync.select_headers_to_submit(true), Some(vec![&header(101)])); } + + #[test] + fn does_not_select_new_headers_to_submit_when_submit_is_paused() { + let mut eth_sync = HeadersSync::new(default_sync_params()); + eth_sync.params.max_headers_in_submitted_status = 1; + + // ethereum reports best header #102 and substrate is at #100 + eth_sync.source_best_header_number_response(102); + eth_sync.target_best_header_response(id(100)); + + // let's prepare #101 and #102 for submitting + eth_sync.headers.header_response(header(101).header().clone()); + eth_sync.headers.maybe_extra_response(&id(101), false); + eth_sync.headers.header_response(header(102).header().clone()); + eth_sync.headers.maybe_extra_response(&id(102), false); + + // when submit is not paused, we're ready to submit #101 + assert_eq!(eth_sync.select_headers_to_submit(false), Some(vec![&header(101)])); + + // when submit is paused, we're not ready to submit anything + eth_sync.pause_submit(); + assert_eq!(eth_sync.select_headers_to_submit(false), None); + + // if best header on substrate node isn't updated, we still not submitting anything + eth_sync.target_best_header_response(id(100)); + assert_eq!(eth_sync.select_headers_to_submit(false), None); + + // but after it is actually updated, we are ready to submit + eth_sync.target_best_header_response(id(101)); + assert_eq!(eth_sync.select_headers_to_submit(false), Some(vec![&header(102)])); + } } diff --git a/bridges/relays/ethereum/src/sync_loop.rs b/bridges/relays/ethereum/src/sync_loop.rs index a62c712f22..12fdf0d01b 100644 --- a/bridges/relays/ethereum/src/sync_loop.rs +++ b/bridges/relays/ethereum/src/sync_loop.rs @@ -266,8 +266,7 @@ pub fn run>( false => { log::info!( target: "bridge", - "Possible {} fork detected. Restarting {} headers synchronization.", - P::TARGET_NAME, + "Sync has stalled. Restarting {} headers synchronization.", P::SOURCE_NAME, ); stall_countdown = None; @@ -308,16 +307,21 @@ pub fn run>( // following line helps Rust understand the type of `submitted_headers` :/ let submitted_headers: SubmittedHeaders, TC::Error> = submitted_headers; let submitted_headers_str = format!("{}", submitted_headers); + let all_headers_rejected = submitted_headers.submitted.is_empty() + && submitted_headers.incomplete.is_empty(); + let has_submitted_headers = sync.headers().headers_in_status(HeaderStatus::Submitted) != 0; + let maybe_fatal_error = match submitted_headers.fatal_error { Some(fatal_error) => Err(StringifiedMaybeConnectionError::new( fatal_error.is_connection_error(), format!("{:?}", fatal_error), )), - None if submitted_headers.submitted.is_empty() && submitted_headers.incomplete.is_empty() => + None if all_headers_rejected && !has_submitted_headers => Err(StringifiedMaybeConnectionError::new(false, "All headers were rejected".into())), None => Ok(()), }; + let no_fatal_error = maybe_fatal_error.is_ok(); target_client_is_online = process_future_result( maybe_fatal_error, &mut target_retry_backoff, @@ -331,6 +335,12 @@ pub fn run>( sync.headers_mut().headers_submitted(submitted_headers.submitted); sync.headers_mut().add_incomplete_headers(submitted_headers.incomplete); + + // when there's no fatal error, but node has rejected all our headers we may + // want to pause until our submitted headers will be accepted + if no_fatal_error && all_headers_rejected && has_submitted_headers { + sync.pause_submit(); + } }, target_complete_header_result = target_complete_header_future => { target_client_is_online = process_future_result(