diff --git a/bridges/relays/ethereum/Cargo.toml b/bridges/relays/ethereum/Cargo.toml index 6d50e0d857979..fd7c69429bdff 100644 --- a/bridges/relays/ethereum/Cargo.toml +++ b/bridges/relays/ethereum/Cargo.toml @@ -24,7 +24,7 @@ num-traits = "0.2" parity-crypto = { version = "0.6", features = ["publickey"] } parking_lot = "0.11.0" rustc-hex = "2.0.1" -serde = { version = "1.0.114", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.55" sp-bridge-eth-poa = { path = "../../primitives/ethereum-poa" } time = "0.2" diff --git a/bridges/relays/ethereum/res/substrate-bridge-abi.json b/bridges/relays/ethereum/res/substrate-bridge-abi.json index bd65f6b7921f1..169b000680218 100644 --- a/bridges/relays/ethereum/res/substrate-bridge-abi.json +++ b/bridges/relays/ethereum/res/substrate-bridge-abi.json @@ -96,6 +96,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "rawHeader", + "type": "bytes" + } + ], + "name": "isIncompleteHeader", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/bridges/relays/ethereum/res/substrate-bridge-bytecode.hex b/bridges/relays/ethereum/res/substrate-bridge-bytecode.hex index 6b3b74df48b0d..0a0973699d463 100644 --- a/bridges/relays/ethereum/res/substrate-bridge-bytecode.hex +++ b/bridges/relays/ethereum/res/substrate-bridge-bytecode.hex @@ -1 +1 @@ -60806040523480156200001157600080fd5b506040516200131938038062001319833981810160405260608110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b506040818152602083015192018051929491939192846401000000008211156200010f57600080fd5b9083019060208201858111156200012557600080fd5b82516401000000008111828201881017156200014057600080fd5b82525081516020918201929091019080838360005b838110156200016f57818101518382015260200162000155565b50505050905090810190601f1680156200019d5780820380516001836020036101000a031916815260200191505b50604052505050620001ae620003d5565b620001c2846001600160e01b03620002dc16565b805160008181556002918255604080840180516001908155825160e08101845281815260208088015181830190815293518286019081526080808a0151606085019081526001600160401b038e169185019190915260a0840188905260c084018890528951885260078352959096208251815460ff191690151517815593519284019290925593519482019490945590518051949550919390926200026f9260038501929101906200040a565b506080820151600482810180546001600160401b03199081166001600160401b039485161790915560a0850151600585015560c09094015160069093019290925560038054909316908616179091558251620002d1919060208501906200040a565b5050505050620004af565b620002e6620003d5565b60008060008060008651602088016040516020810160208101602081016020810160a08588886010600019fa6200031c57600080fd5b84519b5083519a50825199508151985080519750505050505050506060816001600160401b03811180156200035057600080fd5b506040519080825280601f01601f1916602001820160405280156200037c576020820181803683370190505b5090508115620003a85787516020890160208301848184846011600019fa620003a457600080fd5b5050505b6040805160a081018252968752602087019590955293850192909252606084015250608082015292915050565b6040518060a0016040528060008019168152602001600080191681526020016000815260200160008152602001606081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200044d57805160ff19168380011785556200047d565b828001600101855582156200047d579182015b828111156200047d57825182559160200191906001019062000460565b506200048b9291506200048f565b5090565b620004ac91905b808211156200048b576000815560010162000496565b90565b610e5a80620004bf6000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063374c2c261461005c578063871ebe18146100fd578063d96a2deb1461012e578063e7af07791461014f578063fae71ae8146101f7575b600080fd5b6100646102a9565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100a8578181015183820152602001610090565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156100e75781810151838201526020016100cf565b5050505090500194505050505060405180910390f35b61011a6004803603602081101561011357600080fd5b50356103ae565b604080519115158252519081900360200190f35b6101366103c3565b6040805192835260208301919091528051918290030190f35b6101f56004803603602081101561016557600080fd5b81019060208101813564010000000081111561018057600080fd5b82018360208201111561019257600080fd5b803590602001918460018302840111640100000000831117156101b457600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506103db945050505050565b005b6101f56004803603606081101561020d57600080fd5b81359160208101359181019060608101604082013564010000000081111561023457600080fd5b82018360208201111561024657600080fd5b8035906020019184600183028401116401000000008311171561026857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610779945050505050565b6005546060908190818167ffffffffffffffff811180156102c957600080fd5b506040519080825280602002602001820160405280156102f3578160200160208202803683370190505b50905060005b8281101561034e57600760006005838154811061031257fe5b906000526020600020015481526020019081526020016000206002015482828151811061033b57fe5b60209081029190910101526001016102f9565b508060058080548060200260200160405190810160405280929190818152602001828054801561039d57602002820191906000526020600020905b815481526020019060010190808311610389575b505050505090509350935050509091565b60009081526007602052604090205460ff1690565b60008054808252600760205260409091206002015491565b6103e3610c13565b6103ec826109de565b805160009081526007602052604090205490915060ff1615610455576040805162461bcd60e51b815260206004820152601760248201527f48656164657220697320616c7265616479206b6e6f776e000000000000000000604482015290519081900360640190fd5b6001548160400151116104995760405162461bcd60e51b8152600401808060200182810382526025815260200180610d596025913960400191505060405180910390fd5b6020808201516000908152600790915260409020805460ff1680156104c8575060018260400151038160020154145b6105035760405162461bcd60e51b8152600401808060200182810382526026815260200180610d7e6026913960400191505060405180910390fd5b60068101541580159061051d575080600201548160060154145b15610566578160200151600254146105665760405162461bcd60e51b8152600401808060200182810382526031815260200180610df46031913960400191505060405180910390fd5b60048101546005820154600683015460808501515167ffffffffffffffff90931692156106445767ffffffffffffffff83811614156105d65760405162461bcd60e51b8152600401808060200182810382526021815260200180610da46021913960400191505060405180910390fd5b8460400151811061062e576040805162461bcd60e51b815260206004820152601960248201527f4f7665726c617070696e67207369676e616c7320666f756e6400000000000000604482015290519081900360640190fd5b5050825160608401516040850151600190930192015b8460400151811415610697576005805486516001820180845560009384527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0909201558651825260066020526040909120555b6040805160e0810182526001808252602088810151818401908152898501518486019081526080808c01516060870190815267ffffffffffffffff8b169187019190915260a0860189905260c086018890528b51600090815260078552969096208551815460ff191690151517815591519382019390935591516002830155925180519293919261072e9260038501920190610c48565b50608082015160048201805467ffffffffffffffff191667ffffffffffffffff90921691909117905560a0820151600582015560c09091015160069091015550509151600055505050565b60008281526007602052604090206002015483146107c85760405162461bcd60e51b815260040180806020018281038252602f815260200180610dc5602f913960400191505060405180910390fd5b60028054600354600480546040805160206101006001851615026000190190931696909604601f81018390048302870183019091528086529394600094610879948a948a9467ffffffffffffffff9092169392909183018282801561086e5780601f106108435761010080835404028352916020019161086e565b820191906000526020600020905b81548152906001019060200180831161085157829003601f168201915b505050505087610ad1565b600081815260076020526040902060028281558101546001559091505b8282146109d657506000818152600760209081526040808320600181015460069093529220549092908015610958576005546000199182019181018214610924576000600560018303815481106108e957fe5b90600052602060002001549050806005848154811061090457fe5b600091825260208083209091019290925591825260069052604090208290555b600580548061092f57fe5b600082815260208082208301600019908101839055909201909255848252600690526040812055505b8260060154836002015414156109cf57600583015460009081526007602052604090206003805467ffffffffffffffff198116600167ffffffffffffffff928316810190921617825590820180546109c6926004929160026101009282161592909202600019011604610cc6565b505050506109d6565b5050610896565b505050505050565b6109e6610c13565b60008060008060008651602088016040516020810160208101602081016020810160a08588886010600019fa610a1b57600080fd5b84519b5083519a508251995081519850805197505050505050505060608167ffffffffffffffff81118015610a4f57600080fd5b506040519080825280601f01601f191660200182016040528015610a7a576020820181803683370190505b5090508115610aa45787516020890160208301848184846011600019fa610aa057600080fd5b5050505b6040805160a081018252968752602087019590955293850192909252606084015250608082015292915050565b600060608686868686604051602001808681526020018581526020018467ffffffffffffffff1667ffffffffffffffff1681526020018060200180602001838103835285818151815260200191508051906020019080838360005b83811015610b44578181015183820152602001610b2c565b50505050905090810190601f168015610b715780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610ba4578181015183820152602001610b8c565b50505050905090810190601f168015610bd15780820380516001836020036101000a031916815260200191505b50975050505050505050604051602081830303815290604052905080516020820160008083836012600019fa610c0657600080fd5b5095979650505050505050565b6040518060a0016040528060008019168152602001600080191681526020016000815260200160008152602001606081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610c8957805160ff1916838001178555610cb6565b82800160010185558215610cb6579182015b82811115610cb6578251825591602001919060010190610c9b565b50610cc2929150610d3b565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610cff5780548555610cb6565b82800160010185558215610cb657600052602060002091601f016020900482015b82811115610cb6578254825591600101919060010190610d20565b610d5591905b80821115610cc25760008155600101610d41565b9056fe547279696e6720746f20696d706f7274206e6f6e2d63616e6f6e6963616c206865616465724d697373696e6720706172656e74206865616465722066726f6d207468652073746f7261676552656163686564206d6178696d616c2076616c696461746f7273207365742069644d697373696e672066696e616c69747920746172676574206865616465722066726f6d207468652073746f726167654d697373696e672072657175697265642066696e616c6974792070726f6f6620666f7220706172656e7420686561646572a26469706673582212206a13d7fad990f5ff64cfe638f258f1cf2a4c72a3161a28ec200db444a3d61c2e64736f6c63430006060033 \ No newline at end of file +60806040523480156200001157600080fd5b506040516200142e3803806200142e833981810160405260608110156200003757600080fd5b81019080805160405193929190846401000000008211156200005857600080fd5b9083019060208201858111156200006e57600080fd5b82516401000000008111828201881017156200008957600080fd5b82525081516020918201929091019080838360005b83811015620000b85781810151838201526020016200009e565b50505050905090810190601f168015620000e65780820380516001836020036101000a031916815260200191505b506040818152602083015192018051929491939192846401000000008211156200010f57600080fd5b9083019060208201858111156200012557600080fd5b82516401000000008111828201881017156200014057600080fd5b82525081516020918201929091019080838360005b838110156200016f57818101518382015260200162000155565b50505050905090810190601f1680156200019d5780820380516001836020036101000a031916815260200191505b50604052505050620001ae620003d5565b620001c2846001600160e01b03620002dc16565b805160008181556002918255604080840180516001908155825160e08101845281815260208088015181830190815293518286019081526080808a0151606085019081526001600160401b038e169185019190915260a0840188905260c084018890528951885260078352959096208251815460ff191690151517815593519284019290925593519482019490945590518051949550919390926200026f9260038501929101906200040a565b506080820151600482810180546001600160401b03199081166001600160401b039485161790915560a0850151600585015560c09094015160069093019290925560038054909316908616179091558251620002d1919060208501906200040a565b5050505050620004af565b620002e6620003d5565b60008060008060008651602088016040516020810160208101602081016020810160a08588886010600019fa6200031c57600080fd5b84519b5083519a50825199508151985080519750505050505050506060816001600160401b03811180156200035057600080fd5b506040519080825280601f01601f1916602001820160405280156200037c576020820181803683370190505b5090508115620003a85787516020890160208301848184846011600019fa620003a457600080fd5b5050505b6040805160a081018252968752602087019590955293850192909252606084015250608082015292915050565b6040518060a0016040528060008019168152602001600080191681526020016000815260200160008152602001606081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200044d57805160ff19168380011785556200047d565b828001600101855582156200047d579182015b828111156200047d57825182559160200191906001019062000460565b506200048b9291506200048f565b5090565b620004ac91905b808211156200048b576000815560010162000496565b90565b610f6f80620004bf6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c8063374c2c2614610067578063871ebe18146101085780638dc1f05c14610139578063d96a2deb146101df578063e7af077914610200578063fae71ae8146102a8575b600080fd5b61006f61035a565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156100b357818101518382015260200161009b565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156100f25781810151838201526020016100da565b5050505090500194505050505060405180910390f35b6101256004803603602081101561011e57600080fd5b503561045f565b604080519115158252519081900360200190f35b6101256004803603602081101561014f57600080fd5b81019060208101813564010000000081111561016a57600080fd5b82018360208201111561017c57600080fd5b8035906020019184600183028401116401000000008311171561019e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610474945050505050565b6101e761049d565b6040805192835260208301919091528051918290030190f35b6102a66004803603602081101561021657600080fd5b81019060208101813564010000000081111561023157600080fd5b82018360208201111561024357600080fd5b8035906020019184600183028401116401000000008311171561026557600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506104b5945050505050565b005b6102a6600480360360608110156102be57600080fd5b8135916020810135918101906060810160408201356401000000008111156102e557600080fd5b8201836020820111156102f757600080fd5b8035906020019184600183028401116401000000008311171561031957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610607945050505050565b6005546060908190818167ffffffffffffffff8111801561037a57600080fd5b506040519080825280602002602001820160405280156103a4578160200160208202803683370190505b50905060005b828110156103ff5760076000600583815481106103c357fe5b90600052602060002001548152602001908152602001600020600201548282815181106103ec57fe5b60209081029190910101526001016103aa565b508060058080548060200260200160405190810160405280929190818152602001828054801561044e57602002820191906000526020600020905b81548152602001906001019080831161043a575b505050505090509350935050509091565b60009081526007602052604090205460ff1690565b600061047e610d28565b60006104898461086c565b604090930151909214945050505050919050565b60008054808252600760205260409091206002015491565b6104bd610d28565b60008060006104cb8561086c565b93509350935093508360400151811415610526576005805485516001820180845560009384527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0909201558551825260066020526040909120555b6040805160e0810182526001808252602087810151818401908152888501518486019081526080808b01516060870190815267ffffffffffffffff8b169187019190915260a0860189905260c086018890528a51600090815260078552969096208551815460ff19169015151781559151938201939093559151600283015592518051929391926105bd9260038501920190610d5d565b50608082015160048201805467ffffffffffffffff191667ffffffffffffffff90921691909117905560a0820151600582015560c090910151600690910155505090516000555050565b60008281526007602052604090206002015483146106565760405162461bcd60e51b815260040180806020018281038252602f815260200180610eda602f913960400191505060405180910390fd5b60028054600354600480546040805160206101006001851615026000190190931696909604601f81018390048302870183019091528086529394600094610707948a948a9467ffffffffffffffff909216939290918301828280156106fc5780601f106106d1576101008083540402835291602001916106fc565b820191906000526020600020905b8154815290600101906020018083116106df57829003601f168201915b505050505087610af3565b600081815260076020526040902060028281558101546001559091505b828214610864575060008181526007602090815260408083206001810154600690935292205490929080156107e65760055460001991820191810182146107b25760006005600183038154811061077757fe5b90600052602060002001549050806005848154811061079257fe5b600091825260208083209091019290925591825260069052604090208290555b60058054806107bd57fe5b600082815260208082208301600019908101839055909201909255848252600690526040812055505b82600601548360020154141561085d57600583015460009081526007602052604090206003805467ffffffffffffffff198116600167ffffffffffffffff92831681019092161782559082018054610854926004929160026101009282161592909202600019011604610ddb565b50505050610864565b5050610724565b505050505050565b610874610d28565b6000806000610881610d28565b61088a86610c35565b805160009081526007602052604090205490915060ff16156108f3576040805162461bcd60e51b815260206004820152601760248201527f48656164657220697320616c7265616479206b6e6f776e000000000000000000604482015290519081900360640190fd5b6001548160400151116109375760405162461bcd60e51b8152600401808060200182810382526025815260200180610e6e6025913960400191505060405180910390fd5b6020808201516000908152600790915260409020805460ff168015610966575060018260400151038160020154145b6109a15760405162461bcd60e51b8152600401808060200182810382526026815260200180610e936026913960400191505060405180910390fd5b6006810154158015906109bb575080600201548160060154145b15610a0457816020015160025414610a045760405162461bcd60e51b8152600401808060200182810382526031815260200180610f096031913960400191505060405180910390fd5b60048101546005820154600683015460808501515167ffffffffffffffff9093169215610ae25767ffffffffffffffff8381161415610a745760405162461bcd60e51b8152600401808060200182810382526021815260200180610eb96021913960400191505060405180910390fd5b84604001518110610acc576040805162461bcd60e51b815260206004820152601960248201527f4f7665726c617070696e67207369676e616c7320666f756e6400000000000000604482015290519081900360640190fd5b5050825160608401516040850151600190930192015b939991985096509194509092505050565b600060608686868686604051602001808681526020018581526020018467ffffffffffffffff1667ffffffffffffffff1681526020018060200180602001838103835285818151815260200191508051906020019080838360005b83811015610b66578181015183820152602001610b4e565b50505050905090810190601f168015610b935780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610bc6578181015183820152602001610bae565b50505050905090810190601f168015610bf35780820380516001836020036101000a031916815260200191505b50975050505050505050604051602081830303815290604052905080516020820160008083836012600019fa610c2857600080fd5b5095979650505050505050565b610c3d610d28565b60008060008060008651602088016040516020810160208101602081016020810160a08588886010600019fa610c7257600080fd5b84519b5083519a508251995081519850805197505050505050505060608167ffffffffffffffff81118015610ca657600080fd5b506040519080825280601f01601f191660200182016040528015610cd1576020820181803683370190505b5090508115610cfb5787516020890160208301848184846011600019fa610cf757600080fd5b5050505b6040805160a081018252968752602087019590955293850192909252606084015250608082015292915050565b6040518060a0016040528060008019168152602001600080191681526020016000815260200160008152602001606081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610d9e57805160ff1916838001178555610dcb565b82800160010185558215610dcb579182015b82811115610dcb578251825591602001919060010190610db0565b50610dd7929150610e50565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610e145780548555610dcb565b82800160010185558215610dcb57600052602060002091601f016020900482015b82811115610dcb578254825591600101919060010190610e35565b610e6a91905b80821115610dd75760008155600101610e56565b9056fe547279696e6720746f20696d706f7274206e6f6e2d63616e6f6e6963616c206865616465724d697373696e6720706172656e74206865616465722066726f6d207468652073746f7261676552656163686564206d6178696d616c2076616c696461746f7273207365742069644d697373696e672066696e616c69747920746172676574206865616465722066726f6d207468652073746f726167654d697373696e672072657175697265642066696e616c6974792070726f6f6620666f7220706172656e7420686561646572a2646970667358221220430a1b51f4ade85190a1817f6d8dfc5ffc46019627c95d768106e998f360e8be64736f6c63430006060033 \ 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 dcf50c5cfa462..60b8d022fc4b4 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-05-01 +Last Change Date: 2020-07-03 Solc version: 0.6.6+commit.6c089d02 -Source hash (keccak256): 0x36403636ad41082ca6c937c60ab06446cd9ef7036c178fa2f04d7c8286544d39 -Source gist: https://github.com/svyatonik/substrate-bridge-sol/blob/8b54f5f648f8685fecd52b7af1deb277922b0fc3/substrate-bridge.sol +Source hash (keccak256): 0x3e6339beefe6786f4f26b408d4f727e03c6fd9630d692af9a7f6b46143fa308f +Source gist: https://github.com/svyatonik/substrate-bridge-sol/blob/1d0fa475a2ba3a70a47ed2dd870568c42ec16c8c/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 fcd627549a076..ffc14f77584e4 100644 --- a/bridges/relays/ethereum/src/ethereum_client.rs +++ b/bridges/relays/ethereum/src/ethereum_client.rs @@ -20,7 +20,7 @@ use crate::ethereum_types::{ use crate::rpc::{Ethereum, EthereumRpc}; use crate::rpc_errors::{EthereumNodeError, RpcError}; use crate::substrate_types::{GrandpaJustification, Hash as SubstrateHash, QueuedSubstrateHeader, SubstrateHeaderId}; -use crate::sync_types::HeaderId; +use crate::sync_types::{HeaderId, MaybeConnectionError, SubmittedHeaders}; use async_trait::async_trait; use codec::{Decode, Encode}; @@ -30,7 +30,7 @@ use jsonrpsee::transport::http::HttpTransportClient; use jsonrpsee::Client; use parity_crypto::publickey::KeyPair; -use std::collections::HashSet; +use std::collections::{HashSet, VecDeque}; // to encode/decode contract calls ethabi_contract::use_contract!(bridge_contract, "res/substrate-bridge-abi.json"); @@ -170,7 +170,7 @@ pub trait EthereumHighLevelRpc: EthereumRpc { params: EthereumSigningParams, contract_address: Address, headers: Vec, - ) -> Result>; + ) -> SubmittedHeaders; /// Returns ids of incomplete Substrate headers. async fn incomplete_substrate_headers(&self, contract_address: Address) -> Result>; @@ -246,25 +246,35 @@ impl EthereumHighLevelRpc for EthereumRpcClient { params: EthereumSigningParams, contract_address: Address, headers: Vec, - ) -> Result> { + ) -> SubmittedHeaders { + // read nonce of signer let address: Address = params.signer.address().as_fixed_bytes().into(); - let mut nonce = self.account_nonce(address).await?; - - let ids = headers.iter().map(|header| header.id()).collect(); - for header in headers { - self.submit_ethereum_transaction( - ¶ms, - Some(contract_address), - Some(nonce), - false, - bridge_contract::functions::import_header::encode_input(header.header().encode()), - ) - .await?; - - nonce += 1.into(); - } + let nonce = match self.account_nonce(address).await { + Ok(nonce) => nonce, + Err(error) => { + return SubmittedHeaders { + submitted: Vec::new(), + incomplete: Vec::new(), + rejected: headers.iter().rev().map(|header| header.id()).collect(), + fatal_error: Some(error), + } + } + }; - Ok(ids) + // submit headers. Note that we're cloning self here. It is ok, because + // cloning `jsonrpsee::Client` only clones reference to background threads + submit_substrate_headers( + EthereumHeadersSubmitter { + client: EthereumRpcClient { + client: self.client.clone(), + }, + params, + contract_address, + nonce, + }, + headers, + ) + .await } async fn incomplete_substrate_headers(&self, contract_address: Address) -> Result> { @@ -363,3 +373,204 @@ impl EthereumHighLevelRpc for EthereumRpcClient { Ok((id, transaction_receipts)) } } + +/// 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 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; + + /// Submit given header to Ethereum node. + async fn submit_header(&mut self, header: QueuedSubstrateHeader) -> Result<()>; +} + +/// Implementation of Substrate headers submitter that sends headers to running Ethereum node. +struct EthereumHeadersSubmitter { + client: EthereumRpcClient, + params: EthereumSigningParams, + contract_address: Address, + nonce: U256, +} + +#[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()); + let call_request = CallRequest { + to: Some(self.contract_address), + data: Some(encoded_call.into()), + ..Default::default() + }; + + let call_result = self.client.eth_call(call_request).await?; + let is_incomplete = call_decoder.decode(&call_result.0)?; + + Ok(is_incomplete) + } + + async fn submit_header(&mut self, header: QueuedSubstrateHeader) -> Result<()> { + let result = self + .client + .submit_ethereum_transaction( + &self.params, + Some(self.contract_address), + Some(self.nonce), + false, + bridge_contract::functions::import_header::encode_input(header.header().encode()), + ) + .await; + + if result.is_ok() { + self.nonce += U256::one(); + } + + result + } +} + +/// Submit multiple Substrate headers. +async fn submit_substrate_headers( + mut header_submitter: impl HeadersSubmitter, + 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"); + submitted_headers.fatal_error = + submit_substrate_header(&mut header_submitter, &mut submitted_headers, id, header).await; + + if submitted_headers.fatal_error.is_some() { + submitted_headers.rejected.extend(ids); + break; + } + } + + submitted_headers +} + +/// Submit single Substrate header. +async fn submit_substrate_header( + header_submitter: &mut impl HeadersSubmitter, + submitted_headers: &mut SubmittedHeaders, + id: SubstrateHeaderId, + header: QueuedSubstrateHeader, +) -> Option { + // if parent of this header is either incomplete, or rejected, we assume that contract + // will reject this header as well + let parent_id = header.parent_id(); + if submitted_headers.rejected.contains(&parent_id) || submitted_headers.incomplete.contains(&parent_id) { + submitted_headers.rejected.push(id); + 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, + Err(error) => { + // contract has rejected this header => we do not want to submit it + submitted_headers.rejected.push(id); + if error.is_connection_error() { + return Some(error); + } else { + return None; + } + } + }; + + // submit header and update submitted headers + match header_submitter.submit_header(header).await { + Ok(_) => { + submitted_headers.submitted.push(id); + if is_header_incomplete { + submitted_headers.incomplete.push(id); + } + None + } + Err(error) => { + submitted_headers.rejected.push(id); + Some(error) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::substrate_types::{Header as SubstrateHeader, Number as SubstrateBlockNumber}; + use sp_runtime::traits::Header; + + struct TestHeadersSubmitter { + incomplete: Vec, + failed: Vec, + } + + #[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) + } else { + Ok(false) + } + } + + async fn submit_header(&mut self, header: QueuedSubstrateHeader) -> Result<()> { + if self.failed.iter().any(|i| i.0 == header.id().0) { + Err(RpcError::Ethereum(EthereumNodeError::InvalidSubstrateBlockNumber)) + } else { + Ok(()) + } + } + } + + fn header(number: SubstrateBlockNumber) -> QueuedSubstrateHeader { + QueuedSubstrateHeader::new(SubstrateHeader::new( + number, + Default::default(), + Default::default(), + if number == 0 { + Default::default() + } else { + header(number - 1).id().1 + }, + Default::default(), + )) + } + + #[test] + fn descendants_of_incomplete_headers_are_not_submitted() { + let submitted_headers = async_std::task::block_on(submit_substrate_headers( + TestHeadersSubmitter { + incomplete: vec![header(5).id()], + failed: vec![], + }, + vec![header(5), header(6)], + )); + assert_eq!(submitted_headers.submitted, vec![header(5).id()]); + assert_eq!(submitted_headers.incomplete, vec![header(5).id()]); + assert_eq!(submitted_headers.rejected, vec![header(6).id()]); + assert!(submitted_headers.fatal_error.is_none()); + } + + #[test] + fn headers_after_fatal_error_are_not_submitted() { + let submitted_headers = async_std::task::block_on(submit_substrate_headers( + TestHeadersSubmitter { + incomplete: vec![], + failed: vec![header(6).id()], + }, + vec![header(5), header(6), header(7)], + )); + assert_eq!(submitted_headers.submitted, vec![header(5).id()]); + assert_eq!(submitted_headers.incomplete, vec![]); + assert_eq!(submitted_headers.rejected, vec![header(6).id(), header(7).id()]); + assert!(submitted_headers.fatal_error.is_some()); + } +} diff --git a/bridges/relays/ethereum/src/ethereum_sync_loop.rs b/bridges/relays/ethereum/src/ethereum_sync_loop.rs index 26804d4de0cb7..65d53946ebbe3 100644 --- a/bridges/relays/ethereum/src/ethereum_sync_loop.rs +++ b/bridges/relays/ethereum/src/ethereum_sync_loop.rs @@ -26,7 +26,7 @@ use crate::substrate_client::{ use crate::substrate_types::into_substrate_ethereum_header; use crate::sync::{HeadersSyncParams, TargetTransactionMode}; use crate::sync_loop::{SourceClient, TargetClient}; -use crate::sync_types::SourceHeader; +use crate::sync_types::{SourceHeader, SubmittedHeaders}; use async_trait::async_trait; use web3::types::H256; @@ -155,7 +155,10 @@ impl TargetClient for SubstrateHeadersTarget { Ok((id, self.client.ethereum_header_known(id).await?)) } - async fn submit_headers(&self, headers: Vec) -> Result, Self::Error> { + async fn submit_headers( + &self, + headers: Vec, + ) -> SubmittedHeaders { let (sign_params, sign_transactions) = (self.sign_params.clone(), self.sign_transactions.clone()); self.client .submit_ethereum_headers(sign_params, headers, sign_transactions) diff --git a/bridges/relays/ethereum/src/headers.rs b/bridges/relays/ethereum/src/headers.rs index a75937da8cfb3..c1dcc86444d67 100644 --- a/bridges/relays/ethereum/src/headers.rs +++ b/bridges/relays/ethereum/src/headers.rs @@ -62,6 +62,8 @@ pub struct QueuedHeaders { incomplete_headers: LinkedHashMap, Option>, /// Headers that are waiting to be completed at target node. Auto-sorted by insertion time. completion_data: LinkedHashMap, P::Completion>, + /// Best synced block number. + best_synced_number: P::Number, /// Pruned blocks border. We do not store or accept any blocks with number less than /// this number. prune_border: P::Number, @@ -90,6 +92,7 @@ impl QueuedHeaders

{ known_headers: KnownHeaders::

::new(), incomplete_headers: LinkedHashMap::new(), completion_data: LinkedHashMap::new(), + best_synced_number: Zero::zero(), prune_border: Zero::zero(), } } @@ -158,6 +161,12 @@ impl QueuedHeaders

{ ) } + /// Returns number of best synced block we have ever seen. It is either less + /// than `best_queued_number()`, or points to last synced block if queue is empty. + pub fn best_synced_number(&self) -> P::Number { + self.best_synced_number + } + /// Returns synchronization status of the header. pub fn status(&self, id: &HeaderId) -> HeaderStatus { self.known_headers @@ -328,10 +337,23 @@ impl QueuedHeaders

{ pub fn completion_response(&mut self, id: &HeaderId, completion: Option) { let completion = match completion { Some(completion) => completion, - None => return, // we'll try refetch later + None => { + log::debug!( + target: "bridge", + "{} Node is still missing completion data for header: {:?}. Will retry later.", + P::SOURCE_NAME, + id, + ); + + return; + } }; - if self.incomplete_headers.remove(id).is_some() { + // do not remove from `incomplete_headers` here, because otherwise we'll miss + // completion 'notification' + // this could lead to duplicate completion retrieval (if completion transaction isn't mined + // for too long) + if self.incomplete_headers.get(id).is_some() { log::debug!( target: "bridge", "Received completion data from {} for header: {:?}", @@ -381,15 +403,8 @@ impl QueuedHeaders

{ } } - /// When incomplete headers ids are receved from target node. - pub fn incomplete_headers_response(&mut self, ids: HashSet>) { - // all new incomplete headers are marked Synced and all their descendants - // are moved from Ready/Submitted to Incomplete queue - let new_incomplete_headers = ids - .iter() - .filter(|id| !self.incomplete_headers.contains_key(id) && !self.completion_data.contains_key(id)) - .cloned() - .collect::>(); + /// Marks given headers incomplete. + pub fn add_incomplete_headers(&mut self, new_incomplete_headers: Vec>) { for new_incomplete_header in new_incomplete_headers { self.header_synced(&new_incomplete_header); move_header_descendants::

( @@ -408,6 +423,18 @@ impl QueuedHeaders

{ self.incomplete_headers.insert(new_incomplete_header, None); } + } + + /// When incomplete headers ids are receved from target node. + pub fn incomplete_headers_response(&mut self, ids: HashSet>) { + // all new incomplete headers are marked Synced and all their descendants + // are moved from Ready/Submitted to Incomplete queue + let new_incomplete_headers = ids + .iter() + .filter(|id| !self.incomplete_headers.contains_key(id) && !self.completion_data.contains_key(id)) + .cloned() + .collect::>(); + self.add_incomplete_headers(new_incomplete_headers); // for all headers that were incompleted previously, but now are completed, we move // all descendants from incomplete to ready @@ -487,6 +514,7 @@ impl QueuedHeaders

{ self.incomplete.clear(); self.submitted.clear(); self.known_headers.clear(); + self.best_synced_number = Zero::zero(); self.prune_border = Zero::zero(); } @@ -519,6 +547,9 @@ impl QueuedHeaders

{ /// When we receive new Synced header from target node. fn header_synced(&mut self, id: &HeaderId) { + // update best synced block number + self.best_synced_number = std::cmp::max(self.best_synced_number, id.0); + // all ancestors of this header are now synced => let's remove them from // queues let mut current = *id; @@ -1272,7 +1303,7 @@ pub(crate) mod tests { // when response is Some, we're scheduling completion queue.completion_response(&id(200), Some(())); - assert_eq!(queue.incomplete_headers.len(), 1); + assert_eq!(queue.incomplete_headers.len(), 2); assert_eq!(queue.completion_data.len(), 1); assert!(queue.incomplete_headers.contains_key(&id(100))); assert!(queue.completion_data.contains_key(&id(200))); diff --git a/bridges/relays/ethereum/src/substrate_client.rs b/bridges/relays/ethereum/src/substrate_client.rs index cd77545b266bd..43549292cef6d 100644 --- a/bridges/relays/ethereum/src/substrate_client.rs +++ b/bridges/relays/ethereum/src/substrate_client.rs @@ -21,7 +21,7 @@ use crate::substrate_types::{ into_substrate_ethereum_header, into_substrate_ethereum_receipts, Hash, Header as SubstrateHeader, Number, SignedBlock as SignedSubstrateBlock, }; -use crate::sync_types::HeaderId; +use crate::sync_types::{HeaderId, SubmittedHeaders}; use async_trait::async_trait; use codec::{Decode, Encode}; @@ -32,6 +32,7 @@ use num_traits::Zero; use sp_bridge_eth_poa::Header as SubstrateEthereumHeader; use sp_core::crypto::Pair; use sp_runtime::traits::IdentifyAccount; +use std::collections::VecDeque; const ETH_API_IMPORT_REQUIRES_RECEIPTS: &str = "EthereumHeadersApi_is_import_requires_receipts"; const ETH_API_IS_KNOWN_BLOCK: &str = "EthereumHeadersApi_is_known_block"; @@ -193,20 +194,20 @@ pub trait SubmitEthereumHeaders: SubstrateRpc { params: SubstrateSigningParams, headers: Vec, sign_transactions: bool, - ) -> Result>; + ) -> SubmittedHeaders; /// Submits signed Ethereum header to Substrate runtime. async fn submit_signed_ethereum_headers( &self, params: SubstrateSigningParams, headers: Vec, - ) -> Result>; + ) -> SubmittedHeaders; /// Submits unsigned Ethereum header to Substrate runtime. async fn submit_unsigned_ethereum_headers( &self, headers: Vec, - ) -> Result>; + ) -> SubmittedHeaders; } #[async_trait] @@ -216,7 +217,7 @@ impl SubmitEthereumHeaders for SubstrateRpcClient { params: SubstrateSigningParams, headers: Vec, sign_transactions: bool, - ) -> Result> { + ) -> SubmittedHeaders { if sign_transactions { self.submit_signed_ethereum_headers(params, headers).await } else { @@ -228,29 +229,55 @@ impl SubmitEthereumHeaders for SubstrateRpcClient { &self, params: SubstrateSigningParams, headers: Vec, - ) -> Result> { + ) -> SubmittedHeaders { let ids = headers.iter().map(|header| header.id()).collect(); + let submission_result = async { + let account_id = params.signer.public().as_array_ref().clone().into(); + let nonce = self.next_account_index(account_id).await?; - let account_id = params.signer.public().as_array_ref().clone().into(); - let nonce = self.next_account_index(account_id).await?; - - let transaction = create_signed_submit_transaction(headers, ¶ms.signer, nonce, self.genesis_hash); - let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; - - Ok(ids) + let transaction = create_signed_submit_transaction(headers, ¶ms.signer, nonce, self.genesis_hash); + let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; + Ok(()) + } + .await; + + match submission_result { + Ok(_) => SubmittedHeaders { + submitted: ids, + incomplete: Vec::new(), + rejected: Vec::new(), + fatal_error: None, + }, + Err(error) => SubmittedHeaders { + submitted: Vec::new(), + incomplete: Vec::new(), + rejected: ids, + fatal_error: Some(error), + }, + } } async fn submit_unsigned_ethereum_headers( &self, headers: Vec, - ) -> Result> { - let ids = headers.iter().map(|header| header.id()).collect(); + ) -> 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 transaction = create_unsigned_submit_transaction(header); - let _ = self.submit_extrinsic(Bytes(transaction.encode())).await?; + match self.submit_extrinsic(Bytes(transaction.encode())).await { + Ok(_) => submitted_headers.submitted.push(id), + Err(error) => { + submitted_headers.rejected.push(id); + submitted_headers.rejected.extend(ids); + submitted_headers.fatal_error = Some(error); + break; + } + } } - Ok(ids) + submitted_headers } } diff --git a/bridges/relays/ethereum/src/substrate_sync_loop.rs b/bridges/relays/ethereum/src/substrate_sync_loop.rs index ede547a9b5d5f..4da5fcb1290af 100644 --- a/bridges/relays/ethereum/src/substrate_sync_loop.rs +++ b/bridges/relays/ethereum/src/substrate_sync_loop.rs @@ -28,7 +28,7 @@ use crate::substrate_types::{ }; use crate::sync::{HeadersSyncParams, TargetTransactionMode}; use crate::sync_loop::{SourceClient, TargetClient}; -use crate::sync_types::SourceHeader; +use crate::sync_types::{SourceHeader, SubmittedHeaders}; use async_trait::async_trait; @@ -165,7 +165,10 @@ impl TargetClient for EthereumHeadersTarget { self.client.substrate_header_known(self.contract, id).await } - async fn submit_headers(&self, headers: Vec) -> Result, Self::Error> { + async fn submit_headers( + &self, + headers: Vec, + ) -> SubmittedHeaders { self.client .submit_substrate_headers(self.sign_params.clone(), self.contract, headers) .await diff --git a/bridges/relays/ethereum/src/sync.rs b/bridges/relays/ethereum/src/sync.rs index 7f15865cc5049..4cfd3b4ab92f4 100644 --- a/bridges/relays/ethereum/src/sync.rs +++ b/bridges/relays/ethereum/src/sync.rs @@ -113,7 +113,10 @@ impl HeadersSync

{ } // we assume that there were no reorgs if we have already downloaded best header - let best_downloaded_number = std::cmp::max(self.headers.best_queued_number(), target_best_header.0); + let best_downloaded_number = std::cmp::max( + std::cmp::max(self.headers.best_queued_number(), self.headers.best_synced_number()), + target_best_header.0, + ); if best_downloaded_number == source_best_number { return None; } @@ -257,6 +260,26 @@ mod tests { assert_eq!(eth_sync.select_new_header_to_download(), None); } + #[test] + fn select_new_header_to_download_works_with_empty_queue() { + let mut eth_sync = HeadersSync::::new(default_sync_params()); + eth_sync.source_best_header_number_response(100); + + // when queue is not empty => everything goes as usually + eth_sync.target_best_header_response(header(10).id()); + eth_sync.headers_mut().header_response(header(11).header().clone()); + eth_sync.headers_mut().maybe_extra_response(&header(11).id(), false); + assert_eq!(eth_sync.select_new_header_to_download(), Some(12)); + + // but then queue is drained + eth_sync.headers_mut().target_best_header_response(&header(11).id()); + + // even though it's empty, we know that header#11 is synced + assert_eq!(eth_sync.headers().best_queued_number(), 0); + assert_eq!(eth_sync.headers().best_synced_number(), 11); + assert_eq!(eth_sync.select_new_header_to_download(), Some(12)); + } + #[test] fn sync_without_reorgs_works() { let mut eth_sync = HeadersSync::new(default_sync_params()); diff --git a/bridges/relays/ethereum/src/sync_loop.rs b/bridges/relays/ethereum/src/sync_loop.rs index 28ee40650f32a..e13b52469dfef 100644 --- a/bridges/relays/ethereum/src/sync_loop.rs +++ b/bridges/relays/ethereum/src/sync_loop.rs @@ -15,7 +15,9 @@ // along with Parity Bridges Common. If not, see . use crate::sync::HeadersSyncParams; -use crate::sync_types::{HeaderId, HeaderStatus, HeadersSyncPipeline, MaybeConnectionError, QueuedHeader}; +use crate::sync_types::{ + HeaderId, HeaderStatus, HeadersSyncPipeline, MaybeConnectionError, QueuedHeader, SubmittedHeaders, +}; use async_trait::async_trait; use futures::{future::FutureExt, stream::StreamExt}; @@ -91,7 +93,7 @@ pub trait TargetClient: Sized { async fn submit_headers( &self, headers: Vec>, - ) -> Result>, Self::Error>; + ) -> SubmittedHeaders, Self::Error>; /// Returns ID of headers that require to be 'completed' before children can be submitted. async fn incomplete_headers_ids(&self) -> Result>, Self::Error>; @@ -111,10 +113,10 @@ pub trait TargetClient: Sized { } /// Run headers synchronization. -pub fn run( +pub fn run>( source_client: impl SourceClient

, source_tick: Duration, - target_client: impl TargetClient

, + target_client: TC, target_tick: Duration, sync_params: HeadersSyncParams, ) { @@ -288,14 +290,21 @@ pub fn run( || format!("Error retrieving existence status from {} node", P::TARGET_NAME), ); }, - target_submit_header_result = target_submit_header_future => { + submitted_headers = target_submit_header_future => { + // following line helps Rust understand the type of `submitted_headers` :/ + let submitted_headers: SubmittedHeaders, TC::Error> = submitted_headers; + let maybe_fatal_error = submitted_headers.fatal_error.map(Err).unwrap_or(Ok(())); + target_client_is_online = process_future_result( - target_submit_header_result, - |submitted_headers| sync.headers_mut().headers_submitted(submitted_headers), + maybe_fatal_error, + |_| {}, &mut target_go_offline_future, || async_std::task::sleep(CONNECTION_ERROR_DELAY), || format!("Error submitting headers to {} node", P::TARGET_NAME), ); + + sync.headers_mut().headers_submitted(submitted_headers.submitted); + sync.headers_mut().add_incomplete_headers(submitted_headers.incomplete); }, target_complete_header_result = target_complete_header_future => { target_client_is_online = process_future_result( diff --git a/bridges/relays/ethereum/src/sync_types.rs b/bridges/relays/ethereum/src/sync_types.rs index 391d18afab390..c300d25e84bbd 100644 --- a/bridges/relays/ethereum/src/sync_types.rs +++ b/bridges/relays/ethereum/src/sync_types.rs @@ -161,3 +161,30 @@ impl QueuedHeader

{ &self.extra } } + +/// Headers submission result. +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] +pub struct SubmittedHeaders { + /// IDs of headers that have been submitted to target node. + pub submitted: Vec, + /// IDs of incomplete headers. These headers were submitted (so this id is also in `submitted` vec), + /// but all descendants are not. + pub incomplete: Vec, + /// IDs of ignored headers that we have decided not to submit (they're either rejected by + /// target node immediately, or they're descendants of incomplete headers). + pub rejected: Vec, + /// Fatal target node error, if it has occured during submission. + pub fatal_error: Option, +} + +impl Default for SubmittedHeaders { + fn default() -> Self { + SubmittedHeaders { + submitted: Vec::new(), + incomplete: Vec::new(), + rejected: Vec::new(), + fatal_error: None, + } + } +}