From efd6f4d497baedd5d8240c0c5265f0ca19d22637 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 3 Dec 2024 17:51:28 +0900 Subject: [PATCH 01/60] CCCP-295, chore: update abi --- abi/abi.socket.bitcoin.json | 42 +++++++++---------- abi/abi.socket.merged.json | 84 ++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/abi/abi.socket.bitcoin.json b/abi/abi.socket.bitcoin.json index 5216a789..17ac6bf8 100644 --- a/abi/abi.socket.bitcoin.json +++ b/abi/abi.socket.bitcoin.json @@ -105,8 +105,8 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { @@ -132,13 +132,13 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { - "internalType": "RBCmethod", - "name": "method", + "internalType": "bytes16", + "name": "RBCmethod", "type": "bytes16" } ], @@ -149,12 +149,12 @@ { "components": [ { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX0", "type": "bytes32" }, { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX1", "type": "bytes32" }, @@ -234,8 +234,8 @@ "name": "BFC_MAIN", "outputs": [ { - "internalType": "ChainIndex", - "name": "", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" } ], @@ -247,8 +247,8 @@ "name": "BTC_ON_BTC", "outputs": [ { - "internalType": "Asset_Index", - "name": "", + "internalType": "bytes32", + "name": "Asset_Index", "type": "bytes32" } ], @@ -829,8 +829,8 @@ { "inputs": [ { - "internalType": "Asset_Index", - "name": "_index", + "internalType": "bytes32", + "name": "Asset_Index", "type": "bytes32" } ], @@ -842,8 +842,8 @@ { "inputs": [ { - "internalType": "ChainIndex", - "name": "_chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" } ], @@ -939,8 +939,8 @@ "name": "this_chain", "outputs": [ { - "internalType": "ChainIndex", - "name": "", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" } ], @@ -1007,8 +1007,8 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { @@ -1039,4 +1039,4 @@ "deployedBytecode": "0x60806040526004361061030c5760003560e01c80637e926b4b1161019a578063be19d11f116100e1578063d92e233d1161008a578063e1c7392a11610064578063e1c7392a14610a47578063e45deb9814610a5c578063f2fde38b14610a7c5761031b565b8063d92e233d146109fd578063dd420fa014610a12578063dfbe0ece14610a275761031b565b8063c462342c116100bb578063c462342c146109a8578063cc22678e146109c8578063cf2fd14d146109dd5761031b565b8063be19d11f14610953578063bf7e214f14610968578063c38c5813146109885761031b565b806393e548bf11610143578063a4298ab61161011d578063a4298ab6146108e8578063a58edf09146108fd578063a762eb8c1461091d5761031b565b806393e548bf146107b5578063986ba392146107d55780639bed25d8146108d35761031b565b80638818024e116101745780638818024e1461074c57806389c06cb7146107685780638b35c07a146107955761031b565b80637e926b4b146106b55780637f2d00bd146106d55780638794b69d146107375761031b565b80632f2ba2c31161025e57806356d2cad2116102075780636d151101116101e15780636d151101146106555780636daadb11146106755780637a9e5e4b146106955761031b565b806356d2cad214610609578063592e8f2e1461061f5780635f48f3931461063f5761031b565b80634e71e0c8116102385780634e71e0c81461059c5780634fe47f70146105b1578063529d15cc146105d15761031b565b80632f2ba2c3146105235780633c12375e146105455780633e5e5411146105835761031b565b80631e10b201116102c05780632708d2a21161029a5780632708d2a2146104a45780632be3968f146104b95780632d345670146105035761031b565b80631e10b201146103f45780631e5eb1d01461042e578063249d8fba146104845761031b565b80631220c6ed116102f15780631220c6ed1461038057806314bfd6d0146103b05780631df4ccfc146103d05761031b565b8063025e7c271461032357806308391f70146103605761031b565b3661031b57610319610a9c565b005b610319610a9c565b34801561032f57600080fd5b5061034361033e3660046127c8565b610aae565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561036c57600080fd5b5061031961037b3660046127f8565b610ad8565b34801561038c57600080fd5b506103a061039b366004612835565b610efb565b6040519015158152602001610357565b3480156103bc57600080fd5b506103436103cb3660046127c8565b610f61565b3480156103dc57600080fd5b506103e6600b5481565b604051908152602001610357565b34801561040057600080fd5b5060065461041590600160a01b900460e01b81565b6040516001600160e01b03199091168152602001610357565b34801561043a57600080fd5b5060085460095461045e916001600160801b0380821692600160801b909204169083565b604080516001600160801b03948516815293909216602084015290820152606001610357565b34801561049057600080fd5b5061031961049f366004612867565b610f71565b3480156104b057600080fd5b506103a0610fda565b3480156104c557600080fd5b506103a06104d43660046128dd565b6000828152600d602090815260408083206001600160a01b038516845260040190915290205460ff1692915050565b34801561050f57600080fd5b506103a061051e366004612835565b611054565b34801561052f57600080fd5b506105386110a5565b6040516103579190612909565b34801561055157600080fd5b506000546105659062010000900460601b81565b6040516bffffffffffffffffffffffff199091168152602001610357565b34801561058f57600080fd5b506104156102ff60e21b81565b3480156105a857600080fd5b506103a0611107565b3480156105bd57600080fd5b506103196105cc3660046127c8565b61117b565b3480156105dd57600080fd5b50600a546105f1906001600160801b031681565b6040516001600160801b039091168152602001610357565b34801561061557600080fd5b506103e660075481565b34801561062b57600080fd5b5061031961063a366004612956565b6111c2565b34801561064b57600080fd5b506103e6600c5481565b34801561066157600080fd5b506103a0610670366004612835565b611240565b34801561068157600080fd5b50610319610690366004612835565b61129a565b3480156106a157600080fd5b506103196106b0366004612835565b6112e8565b3480156106c157600080fd5b506103a06106d036600461298e565b61134c565b3480156106e157600080fd5b506103e66106f03660046127f8565b60408051602080820196909652808201949094526001600160a01b039290921660608401526080808401919091528151808403909101815260a09092019052805191012090565b34801561074357600080fd5b50610538611424565b34801561075857600080fd5b506103e66001600160f81b031981565b34801561077457600080fd5b506103e6610783366004612835565b60016020526000908152604090205481565b3480156107a157600080fd5b506103196107b0366004612835565b611484565b3480156107c157600080fd5b506103196107d03660046127c8565b6114cf565b3480156107e157600080fd5b506108766107f03660046127c8565b600d60209081526000918252604091829020805460018201546002830154855160608101875260039094015460e081901b6001600160e01b0319168552640100000000810467ffffffffffffffff16958501959095526c010000000000000000000000009094046001600160801b0316948301949094526001600160a01b031692919084565b604080516001600160a01b0390951685526020808601949094528481019290925280516001600160e01b03191660608501529182015167ffffffffffffffff16608084015201516001600160801b031660a082015260c001610357565b3480156108df57600080fd5b506103a0611516565b3480156108f457600080fd5b5061053861157c565b34801561090957600080fd5b506103436109183660046127c8565b6115dc565b34801561092957600080fd5b506103e6610938366004612835565b6001600160a01b031660009081526001602052604090205490565b34801561095f57600080fd5b506103a06115ec565b34801561097457600080fd5b50600654610343906001600160a01b031681565b34801561099457600080fd5b506103a06109a3366004612835565b611643565b3480156109b457600080fd5b506103196109c33660046129ab565b611694565b3480156109d457600080fd5b506103a0611779565b3480156109e957600080fd5b506103a06109f8366004612835565b6117d0565b348015610a0957600080fd5b50610343600081565b348015610a1e57600080fd5b5061053861182a565b348015610a3357600080fd5b50610343610a423660046127c8565b61188a565b348015610a5357600080fd5b5061031961189a565b348015610a6857600080fd5b506103a0610a77366004612835565b611a98565b348015610a8857600080fd5b506103a0610a97366004612835565b611af2565b610aac610aa7611ba4565b611bb3565b565b60028181548110610abe57600080fd5b6000918252602090912001546001600160a01b0316905081565b6006546040517f976a75f10000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b039091169063976a75f190602401602060405180830381865afa158015610b39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b5d91906129da565b610bae5760405162461bcd60e51b815260206004820152600c60248201527f6f6e6c792072656c61796572000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6040805160208082018790528183018690526001600160a01b038516606083015260808083018590528351808403909101815260a090920183528151918101919091206000908152600d825282812033825260048101909252919091205460ff1615610c5c5760405162461bcd60e51b815260206004820152601560248201527f72656c6179657220616c726561647920766f74656400000000000000000000006044820152606401610ba5565b3360009081526004820160205260408120805460ff1916600117905560028201805491610c8883612a0d565b91905055508060010154600003610d25576008546001600160801b031682118015610cb55750600c548211155b610d015760405162461bcd60e51b815260206004820152600c60248201527f616d6f756e7420636865636b00000000000000000000000000000000000000006044820152606401610ba5565b80546001600160a01b0319166001600160a01b038416178155600101819055610ef5565b60038101546c0100000000000000000000000090046001600160801b031615610d905760405162461bcd60e51b815260206004820152601360248201527f747820616c7265616479206578656375746564000000000000000000000000006044820152606401610ba5565b6006546040517fd2ea63fb000000000000000000000000000000000000000000000000000000008152600160048201526000916001600160a01b03169063d2ea63fb90602401602060405180830381865afa158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e179190612a26565b905080826002015410610ef2576000610e308585611bdc565b8051805160038601805460208401516040948501516001600160801b03166c01000000000000000000000000027fffffffff00000000000000000000000000000000ffffffffffffffffffffffff67ffffffffffffffff909216640100000000026bffffffffffffffffffffffff1990931660e09590951c949094179190911716919091179055519091507f918454f530580823dd0d8cf59cacb45a6eb7cc62f222d7129efba5821e77f19190610ee8908390612aee565b60405180910390a1505b50505b50505050565b6000610f09335b6003611cf7565b610f425760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b610f4b82611d21565b610f586002836003611d79565b5060015b919050565b60048181548110610abe57600080fd5b610f7a33610f02565b610fb35760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b805160208201516001600160801b03908116600160801b0291161760085560400151600955565b6000610fe8335b6002611cf7565b6110345760405162461bcd60e51b815260206004820152601260248201527f4f6e6c792050656e64696e672041646d696e00000000000000000000000000006044820152606401610ba5565b6110416004336001611d79565b61104e6005336002611de1565b50600190565b600061105f33610f02565b6110985760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b610f586004836001611de1565b606060058054806020026020016040519081016040528092919081815260200182805480156110fd57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116110df575b5050505050905090565b6000611115335b6004611cf7565b6111615760405162461bcd60e51b815260206004820152601260248201527f4f6e6c792050656e64696e67204f776e657200000000000000000000000000006044820152606401610ba5565b61116e6002336003611d79565b61104e6003336004611de1565b61118433610f02565b6111bd5760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b600c55565b6111cb33610f02565b6112045760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b6006805460e09290921c600160a01b027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b600061124b33610f02565b6112845760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b61128d82611d21565b610f586002836003611de1565b6112a333610f02565b6112dc5760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b6112e581611e49565b50565b6112f133610f02565b61132a5760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b600680546001600160a01b0319166001600160a01b0392909216919091179055565b600061135733610f02565b6113905760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b816114165760006001600160a01b031660036000815481106113b4576113b4612bbd565b6000918252602090912001546001600160a01b0316036114165760405162461bcd60e51b815260206004820181905260248201527f666174616c3a205468657265206973206e6f2070656e64696e67206f776e65726044820152606401610ba5565b610f586002335b6003611de1565b606060048054806020026020016040519081016040528092919081815260200182805480156110fd576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116110df575050505050905090565b61148d33610f02565b6114c65760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b6112e581611e89565b6114d833610f02565b6115115760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b600755565b6000611523336001611cf7565b61156f5760405162461bcd60e51b815260206004820152600a60248201527f4f6e6c792041646d696e000000000000000000000000000000000000000000006044820152606401610ba5565b61104e6004336001611de1565b606060028054806020026020016040519081016040528092919081815260200182805480156110fd576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116110df575050505050905090565b60058181548110610abe57600080fd5b60006115f73361110e565b61116e5760405162461bcd60e51b815260206004820152601260248201527f4f6e6c792050656e64696e67204f776e657200000000000000000000000000006044820152606401610ba5565b600061164e33610f02565b6116875760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b610f586004836001611d79565b61169d33610f02565b6116d65760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b60005462010000900460601b6bffffffffffffffffffffffff19161561173e5760405162461bcd60e51b815260206004820152600960248201527f6f6e6c79206f6e636500000000000000000000000000000000000000000000006044820152606401610ba5565b6000805460609290921c62010000027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909216919091179055565b600061178433610fe1565b6110415760405162461bcd60e51b815260206004820152601260248201527f4f6e6c792050656e64696e672041646d696e00000000000000000000000000006044820152606401610ba5565b60006117db33610f02565b6118145760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b61181d82611d21565b610f586003836004611d79565b606060038054806020026020016040519081016040528092919081815260200182805480156110fd576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116110df575050505050905090565b60038181548110610abe57600080fd5b600054610100900460ff16158080156118ba5750600054600160ff909116105b806118d45750303b1580156118d4575060005460ff166001145b6119465760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610ba5565b6000805460ff191660011790558015611969576000805461ff0019166101001790555b611974336003611edd565b6002805460018082019092557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0180546001600160a01b03199081163317909155600380548084019091557fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01805482169055600480548084019091557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b018054821690556005805492830181556000527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db09091018054909116905580156112e5576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b6000611aa333610f02565b611adc5760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b611ae582611d21565b610f586005836002611d79565b6000611afd33610f02565b611b365760405162461bcd60e51b815260206004820152600a60248201526927b7363c9027bbb732b960b11b6044820152606401610ba5565b611b3f82611d21565b611b4a60023361141d565b611b576002836003611d79565b6040516001600160a01b0383169033907f5c486528ec3e3f0ea91181cff8116f02bfa350e03b8b6f12e00765adbb5af85c90600090a3506001919050565b6001600160a01b03163b151590565b6000611bae611f9d565b905090565b3660008037600080366000845af43d6000803e808015611bd2573d6000f35b3d6000fd5b505050565b6040805160e0810182526000608080830182815260a080850184905260c08086018590529185526020808601859052865180880188528581528082018690528688015286519283018752848352820184905294810183905260608181018490529181019290925292810183905291810191909152611c58611fd0565b815260016020808301919091526040805180820182526102ff60e21b81527f03010102000000000000000000000000000000000000000000000000000000008184015281840152805160c08101825260075481526000928101929092526001600160a01b038516908201819052606082015260808101611cd7846120f1565b815260408051602081810190925260008152910152606082015292915050565b6001600160a01b038216600090815260016020526040812054611d1a90836121dc565b9392505050565b336001600160a01b038216036112e55760405162461bcd60e51b815260206004820152600960248201527f417574682053656c6600000000000000000000000000000000000000000000006044820152606401610ba5565b611d828261223c565b611d8c8282611edd565b611d968383612292565b806004811115611da857611da8612a3f565b6040516001600160a01b038416907f16648684a169742a8946057cc0e413441c39b8c40d8a0b329547a5b58d0fad3390600090a3505050565b611dea8261223c565b611df48282612340565b611dfe8383612405565b806004811115611e1057611e10612a3f565b6040516001600160a01b038416907f4c122a44c01f7baedf7841724cfb4cecb281d5d9f46ddfe8bcace2d9d121fb5a90600090a3505050565b611e528161244f565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f611eb2612510565b604080516001600160a01b03928316815291841660208301520160405180910390a16112e581612538565b6001600160a01b038216600090815260016020526040902054611f0081836121dc565b15611f4d5760405162461bcd60e51b815260206004820152601260248201527f636f6e666c696374206175746820666c616700000000000000000000000000006044820152606401610ba5565b816004811115611f5f57611f5f612a3f565b611f6a906008612bd3565b6001600160a01b0390931660009081526001602052604090206001600160f81b031960ff949094169390931c1790915550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b546001600160a01b0316919050565b6040805160608101825260008082526020820181905291810191909152604080516060810182526006546001600160e01b0319600160a01b820460e01b16825282517f6f31dd98000000000000000000000000000000000000000000000000000000008152925191926020808501936001600160a01b0390931692636f31dd989260048082019392918290030181865afa158015612072573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120969190612a26565b63ffffffff168152600a80546020909201916000906120bd906001600160801b0316612bfc565b91906101000a8154816001600160801b0302191690836001600160801b0316021790556001600160801b0316815250905090565b600080670de0b6b3a76400006008600101548461210e9190612c22565b6121189190612c41565b6008549091506001600160801b031681101561213c57506008546001600160801b03165b600854600160801b90046001600160801b031681111561216b5750600854600160801b90046001600160801b03165b8083116121ba5760405162461bcd60e51b815260206004820152600760248201527f73756220666565000000000000000000000000000000000000000000000000006044820152606401610ba5565b80600b60008282546121cc9190612c63565b90915550611d1a90508184612c7b565b60006001600160f81b0319838360048111156121fa576121fa612a3f565b60ff166020811061220d5761220d612bbd565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614905092915050565b6001600160a01b0381166112e55760405162461bcd60e51b815260206004820152600b60248201527f5a65726f416464726573730000000000000000000000000000000000000000006044820152606401610ba5565b60006001600160a01b0316826000815481106122b0576122b0612bbd565b6000918252602090912001546001600160a01b0316036123105780826000815481106122de576122de612bbd565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505050565b81546001810183556000838152602090200180546001600160a01b0319166001600160a01b0383161790555b5050565b6001600160a01b03821660009081526001602052604090205461236381836121dc565b6123af5760405162461bcd60e51b815260206004820152600f60248201527f6e6f7420666c61676564206175746800000000000000000000000000000000006044820152606401610ba5565b8160048111156123c1576123c1612a3f565b6123cc906008612bd3565b6001600160a01b0393909316600090815260016020526040902080546001600160f81b031960ff9095169490941c199093169092555050565b6124468282846001868054905061241c9190612c7b565b8154811061242c5761242c612bbd565b6000918252602090912001546001600160a01b03166125db565b61233c826126a0565b6001600160a01b0381163b6124cc5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e7472616374000000000000000000000000000000000000006064820152608401610ba5565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103611fc1565b6001600160a01b0381166125b45760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610ba5565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036124ef565b806001600160a01b0316826001600160a01b0316036125fe57611bd78383612724565b825460005b8181101561269957836001600160a01b031685828154811061262757612627612bbd565b6000918252602090912001546001600160a01b031603612687578285828154811061265457612654612bbd565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550612699565b8061269181612a0d565b915050612603565b5050505050565b80546001036126ef576000816000815481106126be576126be612bbd565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555050565b808054806126ff576126ff612c92565b600082815260209020810160001990810180546001600160a01b031916905501905550565b815460005b8181101561277f57826001600160a01b031684828154811061274d5761274d612bbd565b6000918252602090912001546001600160a01b03160361276d5750505050565b8061277781612a0d565b915050612729565b5060405162461bcd60e51b815260206004820152600b60248201527f6e6f7420636f6e7461696e0000000000000000000000000000000000000000006044820152606401610ba5565b6000602082840312156127da57600080fd5b5035919050565b80356001600160a01b0381168114610f5c57600080fd5b6000806000806080858703121561280e57600080fd5b8435935060208501359250612825604086016127e1565b9396929550929360600135925050565b60006020828403121561284757600080fd5b611d1a826127e1565b80356001600160801b0381168114610f5c57600080fd5b60006060828403121561287957600080fd5b6040516060810181811067ffffffffffffffff821117156128aa57634e487b7160e01b600052604160045260246000fd5b6040526128b683612850565b81526128c460208401612850565b6020820152604083013560408201528091505092915050565b600080604083850312156128f057600080fd5b82359150612900602084016127e1565b90509250929050565b6020808252825182820181905260009190848201906040850190845b8181101561294a5783516001600160a01b031683529284019291840191600101612925565b50909695505050505050565b60006020828403121561296857600080fd5b81356001600160e01b031981168114611d1a57600080fd5b80151581146112e557600080fd5b6000602082840312156129a057600080fd5b8135611d1a81612980565b6000602082840312156129bd57600080fd5b81356bffffffffffffffffffffffff1981168114611d1a57600080fd5b6000602082840312156129ec57600080fd5b8151611d1a81612980565b634e487b7160e01b600052601160045260246000fd5b600060018201612a1f57612a1f6129f7565b5060010190565b600060208284031215612a3857600080fd5b5051919050565b634e487b7160e01b600052602160045260246000fd5b8051825260006020808301518185015260408301516001600160a01b03808216604087015280606086015116606087015250506080830151608085015260a083015160c060a086015280518060c087015260005b81811015612ac55782810184015187820160e001528301612aa9565b81811115612ad757600060e083890101525b50601f01601f19169490940160e001949350505050565b60208152612b3160208201835180516001600160e01b031916825260208082015167ffffffffffffffff16908301526040908101516001600160801b0316910152565b60006020830151600b8110612b5657634e487b7160e01b600052602160045260246000fd5b6080830152604083015180516001600160e01b03191660a0840152602001517fffffffffffffffffffffffffffffffff000000000000000000000000000000001660c0830152606083015160e080840152612bb5610100840182612a55565b949350505050565b634e487b7160e01b600052603260045260246000fd5b600060ff821660ff84168160ff0481118215151615612bf457612bf46129f7565b029392505050565b60006001600160801b03808316818103612c1857612c186129f7565b6001019392505050565b6000816000190483118215151615612c3c57612c3c6129f7565b500290565b600082612c5e57634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115612c7657612c766129f7565b500190565b600082821015612c8d57612c8d6129f7565b500390565b634e487b7160e01b600052603160045260246000fdfea26469706673582212201429e85fe57eb19ce9d04396cc105a8b113e85ae41221dba674f0b51074c7b8064736f6c634300080d0033", "linkReferences": {}, "deployedLinkReferences": {} -} +} \ No newline at end of file diff --git a/abi/abi.socket.merged.json b/abi/abi.socket.merged.json index b2c19114..747c704b 100644 --- a/abi/abi.socket.merged.json +++ b/abi/abi.socket.merged.json @@ -143,8 +143,8 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { @@ -170,13 +170,13 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { - "internalType": "RBCmethod", - "name": "method", + "internalType": "bytes16", + "name": "RBCmethod", "type": "bytes16" } ], @@ -187,12 +187,12 @@ { "components": [ { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX0", "type": "bytes32" }, { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX1", "type": "bytes32" }, @@ -272,8 +272,8 @@ "name": "BFC_MAIN", "outputs": [ { - "internalType": "ChainIndex", - "name": "", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" } ], @@ -496,8 +496,8 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { @@ -537,8 +537,8 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { @@ -626,8 +626,8 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { @@ -818,8 +818,8 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { @@ -845,13 +845,13 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { - "internalType": "RBCmethod", - "name": "method", + "internalType": "bytes16", + "name": "RBCmethod", "type": "bytes16" } ], @@ -862,12 +862,12 @@ { "components": [ { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX0", "type": "bytes32" }, { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX1", "type": "bytes32" }, @@ -1354,8 +1354,8 @@ { "inputs": [ { - "internalType": "ChainIndex", - "name": "_this", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" } ], @@ -1447,8 +1447,8 @@ "name": "this_chain", "outputs": [ { - "internalType": "ChainIndex", - "name": "", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" } ], @@ -1460,8 +1460,8 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { @@ -1484,13 +1484,13 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { - "internalType": "RBCmethod", - "name": "method", + "internalType": "bytes16", + "name": "RBCmethod", "type": "bytes16" } ], @@ -1501,12 +1501,12 @@ { "components": [ { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX0", "type": "bytes32" }, { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX1", "type": "bytes32" }, @@ -1578,13 +1578,13 @@ { "components": [ { - "internalType": "ChainIndex", - "name": "chain", + "internalType": "bytes4", + "name": "ChainIndex", "type": "bytes4" }, { - "internalType": "RBCmethod", - "name": "method", + "internalType": "bytes16", + "name": "RBCmethod", "type": "bytes16" } ], @@ -1595,12 +1595,12 @@ { "components": [ { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX0", "type": "bytes32" }, { - "internalType": "Asset_Index", + "internalType": "bytes32", "name": "tokenIDX1", "type": "bytes32" }, From 9605726b4a83610f43272bd3728d186d36442b1b Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 3 Dec 2024 17:58:16 +0900 Subject: [PATCH 02/60] CCCP-295, chore: fix typo --- relayer/src/verification.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/src/verification.rs b/relayer/src/verification.rs index c90b8949..360ee4fb 100644 --- a/relayer/src/verification.rs +++ b/relayer/src/verification.rs @@ -12,7 +12,7 @@ use br_primitives::{ }; /// Verifies whether the certain numeric parameters specified in the configuration YAML file are valid. -/// If any single paramater has been provided, the system will panic on-start. +/// If any single parameter has been provided, the system will panic on-start. pub(super) fn assert_configuration_validity(config: &Configuration) { let bootstrap_config = &config.relayer_config.bootstrap_config; let evm_providers = &config.relayer_config.evm_providers; From 18a60da339c701ace7be8107bc43c5a2a78f38ff Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 3 Dec 2024 17:58:50 +0900 Subject: [PATCH 03/60] CCCP-295, feature: migrate into alloy-rs (WIP) --- Cargo.toml | 12 +- client/Cargo.toml | 5 +- client/src/btc/block.rs | 96 ++- client/src/btc/handlers/inbound.rs | 214 +++--- client/src/btc/handlers/mod.rs | 39 +- client/src/btc/handlers/outbound.rs | 177 +++-- client/src/btc/storage/pending_outbound.rs | 68 +- client/src/eth/events.rs | 134 ++-- .../src/eth/handlers/roundup_relay_handler.rs | 343 ++++----- .../src/eth/handlers/socket_relay_handler.rs | 693 +++++++++--------- client/src/eth/mod.rs | 499 +++---------- client/src/eth/traits.rs | 336 +++++---- client/src/eth/tx/eip1559_manager.rs | 328 --------- client/src/eth/tx/legacy_manager.rs | 380 ---------- client/src/eth/tx/mod.rs | 22 - client/src/eth/wallet.rs | 79 -- client/src/substrate/traits.rs | 13 +- client/src/substrate/tx/unsigned_manager.rs | 53 +- metrics/Cargo.toml | 2 +- metrics/src/prometheus.rs | 21 +- periodic/Cargo.toml | 4 +- periodic/src/bitcoin_rollback_verifier.rs | 169 +++-- periodic/src/heartbeat_sender.rs | 137 ++-- periodic/src/keypair_migrator.rs | 33 +- periodic/src/price_feeder.rs | 170 +++-- periodic/src/price_source/binance.rs | 72 +- periodic/src/price_source/chainlink.rs | 69 +- periodic/src/price_source/coingecko.rs | 62 +- periodic/src/price_source/gateio.rs | 57 +- periodic/src/price_source/kucoin.rs | 32 +- periodic/src/price_source/mod.rs | 67 +- periodic/src/price_source/upbit.rs | 94 +-- periodic/src/psbt_signer.rs | 150 ++-- periodic/src/pub_key_presubmitter.rs | 87 ++- periodic/src/pub_key_submitter.rs | 128 ++-- periodic/src/roundup_emitter.rs | 282 +++---- periodic/src/socket_rollback_emitter.rs | 165 +++-- periodic/src/traits.rs | 9 +- primitives/Cargo.toml | 5 +- primitives/src/cli.rs | 11 +- primitives/src/constants/cli.rs | 4 +- primitives/src/constants/config.rs | 6 +- primitives/src/constants/schedule.rs | 2 +- primitives/src/constants/tx.rs | 4 +- primitives/src/contracts/authority.rs | 10 +- primitives/src/contracts/bitcoin_socket.rs | 19 +- .../src/contracts/chainlink_aggregator.rs | 10 +- primitives/src/contracts/registration_pool.rs | 10 +- primitives/src/contracts/relay_executive.rs | 10 +- primitives/src/contracts/relayer_manager.rs | 10 +- primitives/src/contracts/socket.rs | 200 +---- primitives/src/contracts/socket_queue.rs | 10 +- primitives/src/eth.rs | 323 ++++++-- primitives/src/periodic.rs | 18 +- primitives/src/tx.rs | 223 +----- primitives/src/utils.rs | 26 +- relayer/Cargo.toml | 3 +- relayer/src/main.rs | 1 + relayer/src/service.rs | 681 +++++------------ 59 files changed, 2839 insertions(+), 4048 deletions(-) delete mode 100644 client/src/eth/tx/eip1559_manager.rs delete mode 100644 client/src/eth/tx/legacy_manager.rs delete mode 100644 client/src/eth/tx/mod.rs delete mode 100644 client/src/eth/wallet.rs diff --git a/Cargo.toml b/Cargo.toml index f8310650..978d6ebc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,13 +47,23 @@ rand = "0.8.5" lazy_static = "1.4.0" url = "2.5.0" array-bytes = "6.1" +eyre = "0.6" +tower = "0.5.0" +byteorder = "1.5.0" serde_yaml = "0.9.25" serde = "1.0.188" serde_json = "1.0.107" hex = "0.4.3" -ethers = { git = "https://github.com/bifrost-platform/ethers-rs", tag = "ethers-v2.0.10-arbitrum-support" } +alloy = { version = "0.6", features = [ + "full", + "reqwest-rustls-tls", + "provider-txpool-api", + "json-rpc", + "transports", + "k256", +] } k256 = "0.13.1" sha3 = "0.10.8" diff --git a/client/Cargo.toml b/client/Cargo.toml index b3337ca1..7a16876d 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -15,7 +15,6 @@ miniscript = { workspace = true } log = { workspace = true } rand = { workspace = true } sentry = { workspace = true } -ethers = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } tokio-stream = { workspace = true } @@ -32,6 +31,10 @@ sha3 = { workspace = true } subxt = { workspace = true } url = { workspace = true } array-bytes = { workspace = true } +alloy = { workspace = true } +tower = { workspace = true } +eyre = { workspace = true } +byteorder = { workspace = true } # subs sc-keystore = { workspace = true } diff --git a/client/src/btc/block.rs b/client/src/btc/block.rs index 377d19ee..b40ef332 100644 --- a/client/src/btc/block.rs +++ b/client/src/btc/block.rs @@ -3,6 +3,10 @@ use crate::{ eth::EthClient, }; +use alloy::{ + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; use br_primitives::{ bootstrap::BootstrapSharedData, constants::{ @@ -13,11 +17,11 @@ use br_primitives::{ eth::BootstrapState, utils::sub_display_format, }; +use eyre::Result; use bitcoincore_rpc::{ bitcoincore_rpc_json::GetRawTransactionResultVout, Client as BtcClient, RpcApi, }; -use ethers::providers::JsonRpcClient; use miniscript::bitcoin::{address::NetworkUnchecked, Address, Amount, Txid}; use serde::Deserialize; use serde_json::Value; @@ -86,11 +90,16 @@ impl EventMessage { } /// A module that reads every new Bitcoin block and filters `Inbound`, `Outbound` events. -pub struct BlockManager { +pub struct BlockManager +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The Bitcoin client. btc_client: BtcClient, /// The Bifrost client. - bfc_client: Arc>, + bfc_client: Arc>, /// The event message sender. sender: Sender, /// The configured minimum block confirmations required to process a block. @@ -108,7 +117,12 @@ pub struct BlockManager { } #[async_trait::async_trait] -impl RpcApi for BlockManager { +impl RpcApi for BlockManager +where + F: TxFiller + WalletProvider, + P: Provider, + TR: Transport + Clone, +{ async fn call Deserialize<'a> + Send>( &self, cmd: &str, @@ -135,11 +149,16 @@ impl RpcApi for BlockManager { } } -impl BlockManager { +impl BlockManager +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `BlockManager` instance. pub fn new( btc_client: BtcClient, - bfc_client: Arc>, + bfc_client: Arc>, _pending_outbounds: PendingOutboundPool, bootstrap_shared_data: Arc, call_interval: u64, @@ -175,7 +194,7 @@ impl BlockManager { } /// Starts the block manager. - pub async fn run(&mut self) { + pub async fn run(&mut self) -> Result<()> { let latest_block = self.get_block_count().await.unwrap(); self.waiting_block = latest_block.saturating_add(1); @@ -188,11 +207,11 @@ impl BlockManager { loop { if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapSocketRelay).await { - self.bootstrap().await; + self.bootstrap().await?; } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { let latest_block_num = self.get_block_count().await.unwrap(); if self.is_block_confirmed(latest_block_num) { - let (vault_set, refund_set) = self.fetch_registration_sets().await; + let (vault_set, refund_set) = self.fetch_registration_sets().await?; self.process_confirmed_block( latest_block_num.saturating_sub(self.block_confirmations), &vault_set, @@ -207,60 +226,56 @@ impl BlockManager { } /// Returns the generated user vault addresses. - async fn get_vault_addresses(&self) -> Vec { + async fn get_vault_addresses(&self) -> Result> { let registration_pool = self.bfc_client.protocol_contracts.registration_pool.as_ref().unwrap(); - self.bfc_client - .contract_call( - registration_pool.vault_addresses(self.get_current_round().await), - "registration_pool.vault_addresses", - ) - .await + Ok(registration_pool + .vault_addresses(self.get_current_round().await?) + .call() + .await? + ._0) } /// Returns the registered user refund addresses. - async fn get_refund_addresses(&self) -> Vec { + async fn get_refund_addresses(&self) -> Result> { let registration_pool = self.bfc_client.protocol_contracts.registration_pool.as_ref().unwrap(); - self.bfc_client - .contract_call( - registration_pool.refund_addresses(self.get_current_round().await), - "registration_pool.refund_addresses", - ) - .await + Ok(registration_pool + .refund_addresses(self.get_current_round().await?) + .call() + .await? + ._0) } /// Returns current pool round. - async fn get_current_round(&self) -> u32 { + async fn get_current_round(&self) -> Result { let registration_pool = self.bfc_client.protocol_contracts.registration_pool.as_ref().unwrap(); - self.bfc_client - .contract_call(registration_pool.current_round(), "registration_pool.current_round") - .await + Ok(registration_pool.current_round().call().await?._0) } /// Returns the vault and refund addresses. #[inline] async fn fetch_registration_sets( &self, - ) -> (BTreeSet>, BTreeSet>) { + ) -> Result<(BTreeSet>, BTreeSet>)> { let vault_set: BTreeSet> = self .get_vault_addresses() - .await + .await? .iter() .map(|s| Address::from_str(s).unwrap()) .collect(); let refund_set: BTreeSet> = self .get_refund_addresses() - .await + .await? .iter() .map(|s| Address::from_str(s).unwrap()) .collect(); - (vault_set, refund_set) + Ok((vault_set, refund_set)) } /// Verifies if the stored waiting block has waited enough. @@ -357,19 +372,24 @@ impl BlockManager { } #[async_trait::async_trait] -impl BootstrapHandler for BlockManager { +impl BootstrapHandler for BlockManager +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn bootstrap_shared_data(&self) -> Arc { self.bootstrap_shared_data.clone() } - async fn bootstrap(&self) { + async fn bootstrap(&self) -> Result<()> { log::info!( target: LOG_TARGET, "-[{}] ⚙️ [Bootstrap mode] Bootstrapping Bitcoin events", sub_display_format(SUB_LOG_TARGET), ); - let (inbound, outbound) = self.get_bootstrap_events().await; + let (inbound, outbound) = self.get_bootstrap_events().await?; self.sender.send(inbound).unwrap(); self.sender.send(outbound).unwrap(); @@ -390,10 +410,12 @@ impl BootstrapHandler for BlockManager { sub_display_format(SUB_LOG_TARGET), ); } + + Ok(()) } - async fn get_bootstrap_events(&self) -> (EventMessage, EventMessage) { - let (vault_set, refund_set) = self.fetch_registration_sets().await; + async fn get_bootstrap_events(&self) -> Result<(EventMessage, EventMessage)> { + let (vault_set, refund_set) = self.fetch_registration_sets().await?; let to_block = self.waiting_block.saturating_sub(1); let from_block = to_block.saturating_sub(self.bootstrap_offset.into()); @@ -418,6 +440,6 @@ impl BootstrapHandler for BlockManager { .await; } } - (inbound, outbound) + Ok((inbound, outbound)) } } diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index e7738736..67f662ca 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -6,22 +6,28 @@ use crate::{ eth::EthClient, }; +use alloy::{ + network::Ethereum, + primitives::{Address as EthAddress, ChainId, B256, U256}, + providers::{ + fillers::{FillProvider, TxFiller}, + Provider, WalletProvider, + }, + rpc::types::{TransactionInput, TransactionRequest}, + transports::Transport, +}; use bitcoincore_rpc::bitcoin::Txid; use br_primitives::{ bootstrap::BootstrapSharedData, - contracts::bitcoin_socket::BitcoinSocketContract, + contracts::bitcoin_socket::BitcoinSocketContract::BitcoinSocketContractInstance, eth::BootstrapState, tx::{BitcoinRelayMetadata, TxRequestMetadata, TxRequestSender}, utils::sub_display_format, }; +use eyre::Result; -use ethers::{ - providers::{JsonRpcClient, Provider}, - types::{Address as EthAddress, Address, TransactionRequest}, -}; use miniscript::bitcoin::{address::NetworkUnchecked, hashes::Hash, Address as BtcAddress}; -use sp_core::H256; -use std::sync::Arc; +use std::{collections::BTreeMap, sync::Arc}; use tokio::sync::broadcast::Receiver; use tokio_stream::StreamExt; @@ -29,11 +35,16 @@ use super::{BootstrapHandler, EventMessage, TxRequester}; const SUB_LOG_TARGET: &str = "inbound-handler"; -pub struct InboundHandler { +pub struct InboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// `EthClient` for interact with Bifrost network. - bfc_client: Arc>, - /// Sender that sends messages to tx request channel (Bifrost network) - tx_request_sender: Arc, + bfc_client: Arc>, + /// All clients. + clients: Arc>>>, /// The receiver that consumes new events from the block channel. event_receiver: Receiver, /// Event type which this handler should handle. @@ -42,16 +53,21 @@ pub struct InboundHandler { bootstrap_shared_data: Arc, } -impl InboundHandler { +impl InboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ pub fn new( - bfc_client: Arc>, - tx_request_sender: Arc, + bfc_client: Arc>, + clients: Arc>>>, event_receiver: Receiver, bootstrap_shared_data: Arc, ) -> Self { Self { bfc_client, - tx_request_sender, + clients, event_receiver, target_event: EventType::Inbound, bootstrap_shared_data, @@ -61,114 +77,113 @@ impl InboundHandler { async fn get_user_bfc_address( &self, vault_address: &BtcAddress, - ) -> Option { + ) -> Result> { let registration_pool = self.bfc_client.protocol_contracts.registration_pool.as_ref().unwrap(); - let user_address: EthAddress = self - .bfc_client - .contract_call( - registration_pool.user_address( - vault_address.clone().assume_checked().to_string(), - self.get_current_round().await, - ), - "registration_pool.user_address", - ) - .await; - if user_address == EthAddress::zero() { - None + let vault_address = vault_address.clone().assume_checked().to_string(); + let round = self.get_current_round().await?; + let user_address = registration_pool.user_address(vault_address, round).call().await?._0; + + if user_address == EthAddress::ZERO { + Ok(None) } else { - user_address.into() + Ok(Some(user_address)) } } - async fn is_rollback_output(&self, txid: Txid, index: u32) -> bool { + async fn is_rollback_output(&self, txid: Txid, index: u32) -> Result { let socket_queue = self.bfc_client.protocol_contracts.socket_queue.as_ref().unwrap(); let slice: [u8; 32] = txid.to_byte_array(); + let psbt_txid = + socket_queue.rollback_output(slice.into(), U256::from(index)).call().await?._0; - let psbt_txid = self - .bfc_client - .contract_call( - socket_queue.rollback_output(slice, index.into()), - "socket_queue.rollback_output", - ) - .await; - - !H256::from(psbt_txid).is_zero() + Ok(!B256::from(psbt_txid).is_zero()) } - fn build_transaction(&self, event: &Event, user_bfc_address: Address) -> TransactionRequest { + fn build_transaction(&self, event: &Event, user_bfc_address: EthAddress) -> TransactionRequest { let calldata = self .bitcoin_socket() .poll( - event.txid.to_byte_array(), - event.index.into(), + event.txid.to_byte_array().into(), + U256::from(event.index), user_bfc_address, - event.amount.to_sat().into(), + U256::from(event.amount.to_sat()), ) .calldata() - .unwrap(); + .clone(); - TransactionRequest::default().data(calldata).to(self.bitcoin_socket().address()) + TransactionRequest::default() + .input(TransactionInput::new(calldata)) + .to(self.bitcoin_socket().address().clone()) } /// Checks if the relayer has already voted on the event. - async fn is_relayer_voted(&self, event: &Event, user_bfc_address: Address) -> bool { + async fn is_relayer_voted(&self, event: &Event, user_bfc_address: EthAddress) -> Result { let hash_key = self - .bfc_client - .contract_call( - self.bitcoin_socket().get_hash_key( - event.txid.to_byte_array(), - event.index.into(), - user_bfc_address, - event.amount.to_sat().into(), - ), - "bitcoin_socket.get_hash_key", + .bitcoin_socket() + .getHashKey( + event.txid.to_byte_array().into(), + U256::from(event.index), + user_bfc_address, + U256::from(event.amount.to_sat()), ) - .await; + .call() + .await? + ._0; - self.bfc_client - .contract_call( - self.bitcoin_socket().is_relayer_voted(hash_key, self.bfc_client.address()), - "bitcoin_socket.is_relayer_voted", - ) - .await + Ok(self + .bitcoin_socket() + .isRelayerVoted(hash_key, self.bfc_client.address()) + .call() + .await? + ._0) } - async fn get_current_round(&self) -> u32 { + async fn get_current_round(&self) -> Result { let registration_pool = self.bfc_client.protocol_contracts.registration_pool.as_ref().unwrap(); - self.bfc_client - .contract_call(registration_pool.current_round(), "registration_pool.current_round") - .await + Ok(registration_pool.current_round().call().await?._0) } #[inline] - fn bitcoin_socket(&self) -> &BitcoinSocketContract> { + fn bitcoin_socket( + &self, + ) -> &BitcoinSocketContractInstance>> { self.bfc_client.protocol_contracts.bitcoin_socket.as_ref().unwrap() } } -#[async_trait::async_trait] -impl TxRequester for InboundHandler { - fn tx_request_sender(&self) -> Arc { - self.tx_request_sender.clone() - } +// #[async_trait::async_trait] +// impl TxRequester for InboundHandler +// where +// F: TxFiller + WalletProvider, +// P: Provider, +// T: Transport + Clone, +// { +// fn tx_request_sender(&self) -> Arc { +// self.tx_request_sender.clone() +// } - fn bfc_client(&self) -> Arc> { - self.bfc_client.clone() - } -} +// fn bfc_client(&self) -> Arc> { +// self.bfc_client.clone() +// } +// } #[async_trait::async_trait] -impl Handler for InboundHandler { - async fn run(&mut self) { +impl Handler for InboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + async fn run(&mut self) -> Result<()> { loop { if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { let msg = self.event_receiver.recv().await.unwrap(); - if !self.bfc_client.is_selected_relayer().await + if !self.bfc_client.is_selected_relayer().await? || !self.is_target_event(msg.event_type) { continue; @@ -184,14 +199,14 @@ impl Handler for InboundHandler { let mut stream = tokio_stream::iter(msg.events); while let Some(event) = stream.next().await { - self.process_event(event).await; + self.process_event(event).await?; } } } } - async fn process_event(&self, mut event: Event) { - if let Some(user_bfc_address) = self.get_user_bfc_address(&event.address).await { + async fn process_event(&self, mut event: Event) -> Result<()> { + if let Some(user_bfc_address) = self.get_user_bfc_address(&event.address).await? { // txid from event is in little endian, convert it to big endian event.txid = { let mut slice: [u8; 32] = event.txid.to_byte_array(); @@ -200,23 +215,27 @@ impl Handler for InboundHandler { }; // check if transaction has been submitted to be rollbacked - if self.is_rollback_output(event.txid, event.index).await { - return; + if self.is_rollback_output(event.txid, event.index).await? { + return Ok(()); } - if self.is_relayer_voted(&event, user_bfc_address).await { - return; + if self.is_relayer_voted(&event, user_bfc_address).await? { + return Ok(()); } let tx_request = self.build_transaction(&event, user_bfc_address); let metadata = BitcoinRelayMetadata::new(event.address, user_bfc_address, event.txid, event.index); - self.request_send_transaction( - tx_request, - TxRequestMetadata::BitcoinSocketRelay(metadata), - SUB_LOG_TARGET, - ) - .await; + + // self.request_send_transaction( + // tx_request, + // TxRequestMetadata::BitcoinSocketRelay(metadata), + // SUB_LOG_TARGET, + // ) + // .await; + todo!() } + + Ok(()) } #[inline] @@ -226,16 +245,21 @@ impl Handler for InboundHandler { } #[async_trait::async_trait] -impl BootstrapHandler for InboundHandler { +impl BootstrapHandler for InboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn bootstrap_shared_data(&self) -> Arc { self.bootstrap_shared_data.clone() } - async fn bootstrap(&self) { + async fn bootstrap(&self) -> Result<()> { unreachable!("unimplemented") } - async fn get_bootstrap_events(&self) -> (EventMessage, EventMessage) { + async fn get_bootstrap_events(&self) -> Result<(EventMessage, EventMessage)> { unreachable!("unimplemented") } } diff --git a/client/src/btc/handlers/mod.rs b/client/src/btc/handlers/mod.rs index 977d43c7..3a284c01 100644 --- a/client/src/btc/handlers/mod.rs +++ b/client/src/btc/handlers/mod.rs @@ -12,25 +12,35 @@ use crate::{ eth::EthClient, }; +use alloy::{ + providers::{fillers::TxFiller, Provider, WalletProvider}, + rpc::types::TransactionRequest, + transports::Transport, +}; use br_primitives::{ bootstrap::BootstrapSharedData, eth::{BootstrapState, GasCoefficient}, tx::{ - TxRequest, TxRequestMessage, TxRequestMetadata, TxRequestSender, XtRequest, - XtRequestMessage, XtRequestMetadata, XtRequestSender, + TxRequestMessage, TxRequestMetadata, TxRequestSender, XtRequest, XtRequestMessage, + XtRequestMetadata, XtRequestSender, }, utils::sub_display_format, }; -use ethers::{prelude::TransactionRequest, providers::JsonRpcClient}; +use eyre::Result; use std::sync::Arc; use super::block::EventMessage; #[async_trait::async_trait] -pub trait XtRequester { +pub trait XtRequester +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn xt_request_sender(&self) -> Arc; - fn bfc_client(&self) -> Arc>; + fn bfc_client(&self) -> Arc>; fn request_send_transaction( &self, @@ -88,10 +98,15 @@ pub trait XtRequester { } #[async_trait::async_trait] -pub trait TxRequester { +pub trait TxRequester +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn tx_request_sender(&self) -> Arc; - fn bfc_client(&self) -> Arc>; + fn bfc_client(&self) -> Arc>; async fn request_send_transaction( &self, @@ -100,7 +115,7 @@ pub trait TxRequester { sub_log_target: &str, ) { match self.tx_request_sender().send(TxRequestMessage::new( - TxRequest::Legacy(tx_request), + tx_request, metadata.clone(), true, false, @@ -133,9 +148,9 @@ pub trait TxRequester { #[async_trait::async_trait] pub trait Handler { - async fn run(&mut self); + async fn run(&mut self) -> Result<()>; - async fn process_event(&self, event_tx: Event); + async fn process_event(&self, event_tx: Event) -> Result<()>; fn is_target_event(&self, event_type: EventType) -> bool; } @@ -146,10 +161,10 @@ pub trait BootstrapHandler { fn bootstrap_shared_data(&self) -> Arc; /// Starts the bootstrap process. - async fn bootstrap(&self); + async fn bootstrap(&self) -> Result<()>; /// Fetch the historical events to bootstrap. - async fn get_bootstrap_events(&self) -> (EventMessage, EventMessage); + async fn get_bootstrap_events(&self) -> Result<(EventMessage, EventMessage)>; /// Verifies whether the bootstrap state has been synced to the given state. async fn is_bootstrap_state_synced_as(&self, state: BootstrapState) -> bool { diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index 4d0a846c..a9e0b08c 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -6,50 +6,70 @@ use crate::{ eth::{traits::SocketRelayBuilder, EthClient}, }; +use alloy::{ + network::Ethereum, + primitives::ChainId, + providers::{ + fillers::{FillProvider, TxFiller}, + Provider, WalletProvider, + }, + rpc::types::TransactionRequest, + sol_types::SolEvent, + transports::Transport, +}; use br_primitives::{ bootstrap::BootstrapSharedData, contracts::{ - socket::{Socket, SocketMessage}, - socket_queue::SocketQueueContract, + socket::{SocketContract::Socket, Socket_Struct::Socket_Message}, + socket_queue::SocketQueueContract::SocketQueueContractInstance, }, - eth::{BootstrapState, BuiltRelayTransaction, ChainID, SocketEventStatus}, + eth::{BootstrapState, BuiltRelayTransaction, SocketEventStatus}, tx::{SocketRelayMetadata, TxRequestMetadata, TxRequestSender}, utils::sub_display_format, }; -use ethers::{ - abi::AbiDecode, - prelude::TransactionRequest, - providers::{JsonRpcClient, Provider}, - types::Bytes, -}; +use byteorder::{BigEndian, ByteOrder}; +use eyre::Result; use miniscript::bitcoin::hashes::Hash; use miniscript::bitcoin::Txid; -use std::{collections::BTreeSet, sync::Arc}; +use std::{ + collections::{BTreeMap, BTreeSet}, + sync::Arc, +}; use tokio::sync::broadcast::Receiver; use super::{BootstrapHandler, EventMessage, TxRequester}; const SUB_LOG_TARGET: &str = "outbound-handler"; -pub struct OutboundHandler { - bfc_client: Arc>, - tx_request_sender: Arc, +pub struct OutboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + bfc_client: Arc>, + clients: Arc>>>, event_receiver: Receiver, target_event: EventType, /// The bootstrap shared data. bootstrap_shared_data: Arc, } -impl OutboundHandler { +impl OutboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ pub fn new( - bfc_client: Arc>, - tx_request_sender: Arc, + bfc_client: Arc>, + clients: Arc>>>, event_receiver: Receiver, bootstrap_shared_data: Arc, ) -> Self { Self { bfc_client, - tx_request_sender, + clients, event_receiver, target_event: EventType::Outbound, bootstrap_shared_data, @@ -57,46 +77,56 @@ impl OutboundHandler { } #[inline] - fn socket_queue(&self) -> &SocketQueueContract> { + fn socket_queue( + &self, + ) -> &SocketQueueContractInstance>> { self.bfc_client.protocol_contracts.socket_queue.as_ref().unwrap() } /// Check if the transaction was originated by CCCP. If true, returns the composed socket messages. - async fn check_socket_queue(&self, txid: Txid) -> Vec { + async fn check_socket_queue(&self, txid: Txid) -> Result> { let mut slice: [u8; 32] = txid.to_byte_array(); slice.reverse(); - let socket_messages: Vec = self - .bfc_client - .contract_call(self.socket_queue().outbound_tx(slice), "socket_queue.outbound_tx") - .await; + let socket_messages = self.socket_queue().outbound_tx(slice.into()).call().await?._0; socket_messages .iter() - .map(|bytes| Socket::decode(&bytes).unwrap().msg) - .collect() + .map(|bytes| Socket::abi_decode_data(bytes, true).map(|decoded| decoded.0)) + .collect::, _>>() + .map_err(|e| e.into()) } } -#[async_trait::async_trait] -impl TxRequester for OutboundHandler { - fn tx_request_sender(&self) -> Arc { - self.tx_request_sender.clone() - } - - fn bfc_client(&self) -> Arc> { - self.bfc_client.clone() - } -} +// #[async_trait::async_trait] +// impl TxRequester for OutboundHandler +// where +// F: TxFiller + WalletProvider, +// P: Provider, +// T: Transport + Clone, +// { +// fn tx_request_sender(&self) -> Arc { +// self.tx_request_sender.clone() +// } + +// fn bfc_client(&self) -> Arc> { +// self.bfc_client.clone() +// } +// } #[async_trait::async_trait] -impl Handler for OutboundHandler { - async fn run(&mut self) { +impl Handler for OutboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + async fn run(&mut self) -> Result<()> { loop { if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { let msg = self.event_receiver.recv().await.unwrap(); - if !self.bfc_client.is_selected_relayer().await + if !self.bfc_client.is_selected_relayer().await? || !self.is_target_event(msg.event_type) { continue; @@ -112,27 +142,28 @@ impl Handler for OutboundHandler { let txids: BTreeSet = msg.events.iter().map(|event| event.txid).collect(); for txid in txids { - let socket_messages = self.check_socket_queue(txid).await; + let socket_messages = self.check_socket_queue(txid).await?; for mut msg in socket_messages { msg.status = SocketEventStatus::Executed.into(); if let Some(built_transaction) = - self.build_transaction(msg.clone(), false, Default::default()).await + self.build_transaction(msg.clone(), false, Default::default()).await? { - self.request_send_transaction( - built_transaction.tx_request, - TxRequestMetadata::SocketRelay(SocketRelayMetadata::new( - false, - SocketEventStatus::from(msg.status), - msg.req_id.sequence, - ChainID::from_be_bytes(msg.req_id.chain), - ChainID::from_be_bytes(msg.ins_code.chain), - msg.params.to, - false, - )), - SUB_LOG_TARGET, - ) - .await; + // self.request_send_transaction( + // built_transaction.tx_request, + // TxRequestMetadata::SocketRelay(SocketRelayMetadata::new( + // false, + // SocketEventStatus::from(msg.status), + // msg.req_id.sequence, + // BigEndian::read_u32(&msg.req_id.ChainIndex.0) as ChainId, + // BigEndian::read_u32(&msg.ins_code.ChainIndex.0) as ChainId, + // msg.params.to, + // false, + // )), + // SUB_LOG_TARGET, + // ) + // .await; + todo!() } } } @@ -140,7 +171,7 @@ impl Handler for OutboundHandler { } } - async fn process_event(&self, _event_tx: Event) { + async fn process_event(&self, _event_tx: Event) -> Result<()> { unreachable!("unimplemented") } @@ -151,39 +182,49 @@ impl Handler for OutboundHandler { } #[async_trait::async_trait] -impl SocketRelayBuilder for OutboundHandler { - fn get_client(&self) -> Arc> { +impl SocketRelayBuilder for OutboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + fn get_client(&self) -> Arc> { self.bfc_client.clone() } async fn build_transaction( &self, - msg: SocketMessage, + msg: Socket_Message, _: bool, - _: ChainID, - ) -> Option { + _: ChainId, + ) -> Result> { // the original msg must be used for building calldata - let (signatures, is_external) = self.build_outbound_signatures(msg.clone()).await; - return Some(BuiltRelayTransaction::new( + let (signatures, is_external) = self.build_outbound_signatures(msg.clone()).await?; + return Ok(Some(BuiltRelayTransaction::new( TransactionRequest::default() - .data(self.build_poll_call_data(msg, signatures)) - .to(self.bfc_client.protocol_contracts.socket.address()), + .input(self.build_poll_call_data(msg, signatures)) + .to(self.bfc_client.protocol_contracts.socket.address().clone()), is_external, - )); + ))); } } #[async_trait::async_trait] -impl BootstrapHandler for OutboundHandler { +impl BootstrapHandler for OutboundHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn bootstrap_shared_data(&self) -> Arc { self.bootstrap_shared_data.clone() } - async fn bootstrap(&self) { + async fn bootstrap(&self) -> Result<()> { unreachable!("unimplemented") } - async fn get_bootstrap_events(&self) -> (EventMessage, EventMessage) { + async fn get_bootstrap_events(&self) -> Result<(EventMessage, EventMessage)> { unreachable!("unimplemented") } } diff --git a/client/src/btc/storage/pending_outbound.rs b/client/src/btc/storage/pending_outbound.rs index 12c78d95..407bc3e5 100644 --- a/client/src/btc/storage/pending_outbound.rs +++ b/client/src/btc/storage/pending_outbound.rs @@ -1,6 +1,4 @@ -use br_primitives::contracts::socket::SocketMessage; -use br_primitives::substrate::BoundedVec; -use ethers::abi::Token; +use br_primitives::{contracts::socket::Socket_Struct::Socket_Message, substrate::BoundedVec}; use miniscript::bitcoin::{address::NetworkUnchecked, Address, Amount}; use std::{ collections::{BTreeMap, HashMap}, @@ -10,7 +8,7 @@ use tokio::sync::RwLock; #[derive(Debug, Clone)] pub struct PendingOutboundValue { - pub socket_messages: Vec, + pub socket_messages: Vec, pub amount: Amount, } @@ -77,36 +75,38 @@ impl PendingOutboundPool { (outputs, socket_messages) } - fn encode_socket_messages(&self, messages: Vec) -> Vec> { - messages - .into_iter() - .map(|msg| { - let req_id_token = Token::Tuple(vec![ - Token::FixedBytes(msg.req_id.chain.into()), - Token::Uint(msg.req_id.round_id.into()), - Token::Uint(msg.req_id.sequence.into()), - ]); - let status_token = Token::Uint(msg.status.into()); - let ins_code_token = Token::Tuple(vec![ - Token::FixedBytes(msg.ins_code.chain.into()), - Token::FixedBytes(msg.ins_code.method.into()), - ]); - let params_token = Token::Tuple(vec![ - Token::FixedBytes(msg.params.token_idx0.into()), - Token::FixedBytes(msg.params.token_idx1.into()), - Token::Address(msg.params.refund), - Token::Address(msg.params.to), - Token::Uint(msg.params.amount), - Token::Bytes(msg.params.variants.to_vec()), - ]); + fn encode_socket_messages(&self, _messages: Vec) -> Vec> { + // messages + // .into_iter() + // .map(|msg| { + // let req_id_token = Token::Tuple(vec![ + // Token::FixedBytes(msg.req_id.chain.into()), + // Token::Uint(msg.req_id.round_id.into()), + // Token::Uint(msg.req_id.sequence.into()), + // ]); + // let status_token = Token::Uint(msg.status.into()); + // let ins_code_token = Token::Tuple(vec![ + // Token::FixedBytes(msg.ins_code.chain.into()), + // Token::FixedBytes(msg.ins_code.method.into()), + // ]); + // let params_token = Token::Tuple(vec![ + // Token::FixedBytes(msg.params.token_idx0.into()), + // Token::FixedBytes(msg.params.token_idx1.into()), + // Token::Address(msg.params.refund), + // Token::Address(msg.params.to), + // Token::Uint(msg.params.amount), + // Token::Bytes(msg.params.variants.to_vec()), + // ]); - ethers::abi::encode(&[Token::Tuple(vec![ - req_id_token, - status_token, - ins_code_token, - params_token, - ])]) - }) - .collect() + // ethers::abi::encode(&[Token::Tuple(vec![ + // req_id_token, + // status_token, + // ins_code_token, + // params_token, + // ])]) + // }) + // .collect() + + todo!("Implement this") } } diff --git a/client/src/eth/events.rs b/client/src/eth/events.rs index 09a5ea44..68f08790 100644 --- a/client/src/eth/events.rs +++ b/client/src/eth/events.rs @@ -1,18 +1,18 @@ -use std::sync::Arc; - -use ethers::{ - providers::JsonRpcClient, - types::{BlockNumber, Filter, Log, SyncingStatus, U64}, +use alloy::{ + primitives::{BlockNumber, ChainId}, + providers::{fillers::TxFiller, Provider, WalletProvider}, + rpc::types::{Filter, Log, SyncStatus}, + transports::Transport, }; +use eyre::Result; +use std::sync::Arc; use tokio::{ sync::broadcast::{self, Receiver, Sender}, time::{sleep, Duration}, }; use br_primitives::{ - bootstrap::BootstrapSharedData, - eth::{BootstrapState, ChainID}, - utils::sub_display_format, + bootstrap::BootstrapSharedData, eth::BootstrapState, utils::sub_display_format, }; use super::{traits::BootstrapHandler, EthClient}; @@ -21,13 +21,13 @@ use super::{traits::BootstrapHandler, EthClient}; /// The message format passed through the block channel. pub struct EventMessage { /// The processed block number. - pub block_number: U64, + pub block_number: u64, /// The detected transaction logs from the target contracts. pub event_logs: Vec, } impl EventMessage { - pub fn new(block_number: U64, event_logs: Vec) -> Self { + pub fn new(block_number: u64, event_logs: Vec) -> Self { Self { block_number, event_logs } } } @@ -35,13 +35,13 @@ impl EventMessage { /// The message receiver connected to the block channel. pub struct EventReceiver { /// The chain ID of the block channel. - pub id: ChainID, + pub id: ChainId, /// The message receiver. pub receiver: Receiver, } impl EventReceiver { - pub fn new(id: ChainID, receiver: Receiver) -> Self { + pub fn new(id: ChainId, receiver: Receiver) -> Self { Self { id, receiver } } } @@ -49,13 +49,18 @@ impl EventReceiver { const SUB_LOG_TARGET: &str = "event-manager"; /// The essential task that listens and handle new events. -pub struct EventManager { +pub struct EventManager +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The ethereum client for the connected chain. - pub client: Arc>, + pub client: Arc>, /// The channel sending event messages. pub sender: Sender, /// The block waiting for enough confirmations. - waiting_block: U64, + waiting_block: u64, /// The bootstrap shared data. bootstrap_shared_data: Arc, /// The flag whether the relayer has enabled self balance synchronization. This field will be @@ -63,32 +68,30 @@ pub struct EventManager { is_balance_sync_enabled: bool, } -impl EventManager { +impl EventManager +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `EventManager` instance. pub fn new( - client: Arc>, + client: Arc>, bootstrap_shared_data: Arc, is_balance_sync_enabled: bool, ) -> Self { let (sender, _receiver) = broadcast::channel(512); - - Self { - client, - sender, - waiting_block: U64::default(), - bootstrap_shared_data, - is_balance_sync_enabled, - } + Self { client, sender, waiting_block: 0u64, bootstrap_shared_data, is_balance_sync_enabled } } /// Initialize event manager. - async fn initialize(&mut self) { - self.client.verify_chain_id().await; - self.client.verify_minimum_balance().await; + async fn initialize(&mut self) -> Result<()> { + self.client.verify_chain_id().await?; + self.client.verify_minimum_balance().await?; // initialize waiting block to the latest block + 1 - let latest_block = self.client.get_latest_block_number().await; - self.waiting_block = latest_block.saturating_add(U64::from(1u64)); + let latest_block = self.client.get_block_number().await?; + self.waiting_block = latest_block.saturating_add(1u64); log::info!( target: &self.client.get_chain_name(), "-[{}] 💤 Idle, best: #{:?}", @@ -97,23 +100,24 @@ impl EventManager { ); if self.is_balance_sync_enabled { - self.client.sync_balance().await; + self.client.sync_balance().await?; } + Ok(()) } /// Starts the event manager. Reads every new mined block of the connected chain and starts to /// publish to the event channel. - pub async fn run(&mut self) { - self.initialize().await; + pub async fn run(&mut self) -> Result<()> { + self.initialize().await?; loop { if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { - let latest_block = self.client.get_latest_block_number().await; + let latest_block = self.client.get_block_number().await?; while self.is_block_confirmed(latest_block) { - self.process_confirmed_block().await; + self.process_confirmed_block().await?; if self.is_balance_sync_enabled { - self.client.sync_balance().await; + self.client.sync_balance().await?; } } } @@ -124,28 +128,26 @@ impl EventManager { /// Process the confirmed block and verifies if any events emitted from the target /// contracts. - async fn process_confirmed_block(&mut self) { + async fn process_confirmed_block(&mut self) -> Result<()> { let from = self.waiting_block; - let to = from.saturating_add( - self.client.metadata.get_logs_batch_size.saturating_sub(U64::from(1u64)), - ); + let to = from.saturating_add(self.client.metadata.get_logs_batch_size.saturating_sub(1u64)); let filter = if let Some(bitcoin_socket) = &self.client.protocol_contracts.bitcoin_socket { Filter::new() .from_block(BlockNumber::from(from)) .to_block(BlockNumber::from(to)) .address(vec![ - self.client.protocol_contracts.socket.address(), - bitcoin_socket.address(), + self.client.protocol_contracts.socket.address().clone(), + bitcoin_socket.address().clone(), ]) } else { Filter::new() .from_block(BlockNumber::from(from)) .to_block(BlockNumber::from(to)) - .address(self.client.protocol_contracts.socket.address()) + .address(self.client.protocol_contracts.socket.address().clone()) }; - let target_logs = self.client.get_logs(&filter).await; + let target_logs = self.client.get_logs(&filter).await?; if !target_logs.is_empty() { self.sender.send(EventMessage::new(self.waiting_block, target_logs)).unwrap(); } @@ -168,17 +170,19 @@ impl EventManager { } self.increment_waiting_block(to); + + Ok(()) } /// Increment the waiting block. - fn increment_waiting_block(&mut self, to: U64) { - self.waiting_block = to.saturating_add(U64::from(1u64)); - br_metrics::set_block_height(&self.client.get_chain_name(), self.waiting_block.as_u64()); + fn increment_waiting_block(&mut self, to: u64) { + self.waiting_block = to.saturating_add(1u64); + br_metrics::set_block_height(&self.client.get_chain_name(), self.waiting_block); } /// Verifies if the stored waiting block has waited enough. #[inline] - fn is_block_confirmed(&self, latest_block: U64) -> bool { + fn is_block_confirmed(&self, latest_block: u64) -> bool { if self.waiting_block > latest_block { return false; } @@ -186,10 +190,10 @@ impl EventManager { } /// Verifies if the connected provider is in block sync mode. - pub async fn wait_provider_sync(&self) { + pub async fn wait_provider_sync(&self) -> Result<()> { loop { - match self.client.is_syncing().await { - SyncingStatus::IsFalse => { + match self.client.syncing().await? { + SyncStatus::None => { for state in self.bootstrap_shared_data.bootstrap_states.write().await.iter_mut() { @@ -197,9 +201,9 @@ impl EventManager { *state = BootstrapState::BootstrapRoundUpPhase1; } } - return; + return Ok(()); }, - SyncingStatus::IsSyncing(status) => { + SyncStatus::Info(status) => { log::info!( target: &self.client.get_chain_name(), "-[{}] ⚙️ Syncing: #{:?}, Highest: #{:?}", @@ -208,15 +212,6 @@ impl EventManager { status.highest_block, ); }, - SyncingStatus::IsArbitrumSyncing(status) => { - log::info!( - target: &self.client.get_chain_name(), - "-[{}] ⚙️ Processed batch: #{:?}, Last batch: #{:?}", - sub_display_format(SUB_LOG_TARGET), - status.batch_processed, - status.batch_seen - ) - }, } sleep(Duration::from_millis(self.client.metadata.call_interval)).await; @@ -225,14 +220,21 @@ impl EventManager { } #[async_trait::async_trait] -impl BootstrapHandler for EventManager { +impl BootstrapHandler for EventManager +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn bootstrap_shared_data(&self) -> Arc { self.bootstrap_shared_data.clone() } - async fn bootstrap(&self) {} + async fn bootstrap(&self) -> Result<()> { + Ok(()) + } - async fn get_bootstrap_events(&self) -> Vec { - vec![] + async fn get_bootstrap_events(&self) -> Result> { + Ok(vec![]) } } diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 65f57a75..1040ac3e 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -1,31 +1,35 @@ use std::{collections::BTreeMap, str::FromStr, sync::Arc, time::Duration}; -use async_trait::async_trait; -use ethers::{ - abi::{encode, Detokenize, Token, Tokenize}, - contract::EthLogDecode, - providers::{JsonRpcClient, Provider}, - types::{Address, Bytes, Filter, Log, Signature, TransactionRequest, H256, U256}, +use alloy::{ + dyn_abi::DynSolValue, + network::Ethereum, + primitives::{Address, Bytes, ChainId, Signature, B256, U256}, + providers::{ + fillers::{FillProvider, TxFiller}, + Provider, WalletProvider, + }, + rpc::types::{Filter, Log, TransactionInput, TransactionRequest}, + sol_types::SolEvent as _, + transports::Transport, }; +use async_trait::async_trait; +use eyre::Result; use tokio::{sync::broadcast::Receiver, time::sleep}; use tokio_stream::StreamExt; use br_primitives::{ bootstrap::BootstrapSharedData, constants::{ - cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, - config::BOOTSTRAP_BLOCK_CHUNK_SIZE, - errors::{INVALID_BIFROST_NATIVENESS, INVALID_CONTRACT_ABI}, + cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE, + errors::INVALID_BIFROST_NATIVENESS, }, - contracts::{ - authority::RoundMetaData, - socket::{ - RoundUpSubmit, SerializedRoundUp, Signatures, SocketContract, SocketContractEvents, - }, + contracts::socket::{ + SocketContract::{RoundUp, SocketContractInstance}, + Socket_Struct::{Round_Up_Submit, Signatures}, }, - eth::{BootstrapState, ChainID, GasCoefficient, RecoveredSignature, RoundUpEventStatus}, - tx::{TxRequest, TxRequestMessage, TxRequestMetadata, TxRequestSender, VSPPhase2Metadata}, - utils::sub_display_format, + eth::{BootstrapState, GasCoefficient, RecoveredSignature, RoundUpEventStatus}, + tx::{TxRequestMessage, TxRequestMetadata, TxRequestSender, VSPPhase2Metadata}, + utils::{recover_message, sub_display_format}, }; use crate::eth::{ @@ -37,27 +41,35 @@ use crate::eth::{ const SUB_LOG_TARGET: &str = "roundup-handler"; /// The essential task that handles `roundup relay` related events. -pub struct RoundupRelayHandler { +pub struct RoundupRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The `EthClient` to interact with the bifrost network. - pub client: Arc>, - /// The senders that sends messages to each tx request channel. - tx_request_senders: BTreeMap>, + pub client: Arc>, /// The receiver that consumes new events from the block channel. event_receiver: Receiver, /// `EthClient`s to interact with provided networks except bifrost network. - external_clients: Vec>>, + external_clients: Arc>>>, /// Signature of RoundUp Event. - roundup_signature: H256, + roundup_signature: B256, /// The bootstrap shared data. bootstrap_shared_data: Arc, } #[async_trait] -impl Handler for RoundupRelayHandler { - async fn run(&mut self) { +impl Handler for RoundupRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + async fn run(&mut self) -> Result<()> { loop { if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapRoundUpPhase2).await { - self.bootstrap().await; + self.bootstrap().await?; sleep(Duration::from_millis(self.client.metadata.call_interval)).await; } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { @@ -73,20 +85,20 @@ impl Handler for RoundupRelayHandler { let mut stream = tokio_stream::iter(msg.event_logs); while let Some(log) = stream.next().await { - if self.is_target_contract(&log) && self.is_target_event(log.topics[0]) { - self.process_confirmed_log(&log, false).await; + if self.is_target_contract(&log) && self.is_target_event(log.topic0()) { + self.process_confirmed_log(&log, false).await?; } } } } } - async fn process_confirmed_log(&self, log: &Log, is_bootstrap: bool) { + async fn process_confirmed_log(&self, log: &Log, is_bootstrap: bool) -> Result<()> { if let Some(receipt) = - self.client.get_transaction_receipt(log.transaction_hash.unwrap()).await + self.client.get_transaction_receipt(log.transaction_hash.unwrap()).await? { - if receipt.status.unwrap().is_zero() { - return; + if !receipt.status() { + return Ok(()); } match self.decode_log(log.clone()).await { Ok(serialized_log) => { @@ -101,9 +113,12 @@ impl Handler for RoundupRelayHandler { } match RoundUpEventStatus::from_u8(serialized_log.status) { RoundUpEventStatus::NextAuthorityCommitted => { - if !self.is_selected_relayer(serialized_log.roundup.round - 1).await { + if !self + .is_selected_relayer(serialized_log.roundup.round - U256::from(1)) + .await? + { // do nothing if not selected - return; + return Ok(()); } self.broadcast_roundup( &self @@ -111,12 +126,12 @@ impl Handler for RoundupRelayHandler { serialized_log.roundup.round, serialized_log.roundup.new_relayers, ) - .await, + .await?, is_bootstrap, ) - .await; + .await?; }, - RoundUpEventStatus::NextAuthorityRelayed => return, + RoundUpEventStatus::NextAuthorityRelayed => return Ok(()), } }, Err(e) => { @@ -134,100 +149,91 @@ impl Handler for RoundupRelayHandler { }, } } + Ok(()) } fn is_target_contract(&self, log: &Log) -> bool { - log.address == self.client.protocol_contracts.socket.address() + &log.address() == self.client.protocol_contracts.socket.address() } - fn is_target_event(&self, topic: H256) -> bool { - topic == self.roundup_signature + fn is_target_event(&self, topic: Option<&B256>) -> bool { + match topic { + Some(topic) => topic == &self.roundup_signature, + None => false, + } } } -impl RoundupRelayHandler { +impl RoundupRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `RoundupRelayHandler` instance. pub fn new( - mut tx_request_senders_vec: Vec>, + bifrost_chain_id: &ChainId, event_receiver: Receiver, - clients: Vec>>, + clients: Arc>>>, bootstrap_shared_data: Arc, ) -> Self { - // Only broadcast to external chains - tx_request_senders_vec.retain(|channel| !channel.is_native); - - let tx_request_senders: BTreeMap> = tx_request_senders_vec - .iter() - .map(|sender| (sender.id, sender.clone())) - .collect(); - - let client = clients - .iter() - .find(|client| client.metadata.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(); - - let external_clients = - clients.into_iter().filter(|client| !client.metadata.is_native).collect(); - - let roundup_signature = client - .protocol_contracts - .socket - .abi() - .event("RoundUp") - .expect(INVALID_CONTRACT_ABI) - .signature(); + let client = clients.get(&bifrost_chain_id).expect(INVALID_BIFROST_NATIVENESS).clone(); Self { - tx_request_senders, event_receiver, client, - external_clients, - roundup_signature, + external_clients: clients, + roundup_signature: RoundUp::SIGNATURE_HASH, bootstrap_shared_data, } } - /// Decode & Serialize log to `SerializedRoundUp` struct. - async fn decode_log(&self, log: Log) -> Result { - match SocketContractEvents::decode_log(&log.into()) { - Ok(roundup) => Ok(SerializedRoundUp::from_tokens(roundup.into_tokens()).unwrap()), - Err(error) => Err(error), - } + /// Decode & Serialize log to `RoundUp` struct. + async fn decode_log(&self, log: Log) -> Result { + Ok(log.log_decode::()?.inner.data) } /// Encodes the given round and new relayers to bytes. fn encode_relayer_array(&self, round: U256, new_relayers: &[Address]) -> Vec { - encode(&[ - Token::Uint(round), - Token::Array(new_relayers.iter().map(|address| Token::Address(*address)).collect()), + DynSolValue::Tuple(vec![ + DynSolValue::Uint(round, 256), + DynSolValue::Array( + new_relayers.iter().map(|address| DynSolValue::Address(*address)).collect(), + ), ]) + .abi_encode() } /// Get the submitted signatures of the updated round. - async fn get_sorted_signatures(&self, round: U256, new_relayers: &[Address]) -> Signatures { + async fn get_sorted_signatures( + &self, + round: U256, + new_relayers: &[Address], + ) -> Result { let raw_sigs = self .client - .contract_call( - self.client.protocol_contracts.socket.get_round_signatures(round), - "socket.get_round_signatures", - ) - .await; + .protocol_contracts + .socket + .get_round_signatures(round) + .call() + .await? + ._0; let raw_concated_v = &raw_sigs.v.to_string()[2..]; let mut recovered_sigs = vec![]; let encoded_msg = self.encode_relayer_array(round, new_relayers); for idx in 0..raw_sigs.r.len() { - let sig = Signature { - r: raw_sigs.r[idx].into(), - s: raw_sigs.s[idx].into(), - v: u64::from_str_radix(&raw_concated_v[idx * 2..idx * 2 + 2], 16).unwrap(), - }; + let sig = Signature::from_rs_and_parity( + raw_sigs.r[idx].into(), + raw_sigs.s[idx].into(), + u64::from_str_radix(&raw_concated_v[idx * 2..idx * 2 + 2], 16).unwrap(), + )?; + recovered_sigs.push(RecoveredSignature::new( idx, sig, - self.client.wallet.recover_message(sig, &encoded_msg), + recover_message(sig, &encoded_msg), )); } recovered_sigs.sort_by_key(|k| k.signer); @@ -238,23 +244,21 @@ impl RoundupRelayHandler { let idx = sig.idx; sorted_sigs.r.push(raw_sigs.r[idx]); sorted_sigs.s.push(raw_sigs.s[idx]); - let v = Bytes::from([sig.signature.v as u8]); - sorted_concated_v.push_str(&v.to_string()[2..]); + sorted_concated_v.push_str(&format!("{:x}", sig.signature.v().recid().to_byte())); }); - sorted_sigs.v = Bytes::from_str(&sorted_concated_v).unwrap(); + sorted_sigs.v = Bytes::from_str(&sorted_concated_v)?; - sorted_sigs + Ok(sorted_sigs) } /// Verifies whether the current relayer was selected at the given round. - async fn is_selected_relayer(&self, round: U256) -> bool { + async fn is_selected_relayer(&self, round: U256) -> Result { let relayer_manager = self.client.protocol_contracts.relayer_manager.as_ref().unwrap(); - self.client - .contract_call( - relayer_manager.is_previous_selected_relayer(round, self.client.address(), true), - "relayer_manager.is_previous_selected_relayer", - ) - .await + Ok(relayer_manager + .is_previous_selected_relayer(round, self.client.address(), true) + .call() + .await? + ._0) } /// Build `round_control_relay` method call param. @@ -262,84 +266,81 @@ impl RoundupRelayHandler { &self, round: U256, mut new_relayers: Vec
, - ) -> RoundUpSubmit { + ) -> Result { new_relayers.sort(); - let sigs = self.get_sorted_signatures(round, &new_relayers).await; - - RoundUpSubmit { round, new_relayers, sigs } + let sigs = self.get_sorted_signatures(round, &new_relayers).await?; + Ok(Round_Up_Submit { round, new_relayers, sigs }) } /// Build `round_control_relay` method call transaction. fn build_transaction_request( &self, - target_socket: &SocketContract>, - roundup_submit: &RoundUpSubmit, + target_socket: &SocketContractInstance>>, + roundup_submit: &Round_Up_Submit, ) -> TransactionRequest { TransactionRequest::default() - .to(target_socket.address()) - .data(target_socket.round_control_relay(roundup_submit.clone()).calldata().unwrap()) + .to(*target_socket.address()) + .input(TransactionInput::new( + target_socket.round_control_relay(roundup_submit.clone()).calldata().clone(), + )) } /// Check roundup submitted before. If not, call `round_control_relay`. - async fn broadcast_roundup(&self, roundup_submit: &RoundUpSubmit, is_bootstrap: bool) { + async fn broadcast_roundup( + &self, + roundup_submit: &Round_Up_Submit, + is_bootstrap: bool, + ) -> Result<()> { if self.external_clients.is_empty() { - return; + return Ok(()); } let mut stream = tokio_stream::iter(self.external_clients.iter()); - while let Some(target_client) = stream.next().await { + while let Some((_, target_client)) = stream.next().await { // Check roundup submitted to target chain before. - let latest_round = target_client - .contract_call( - target_client.protocol_contracts.authority.latest_round(), - "authority.latest_round", - ) - .await; + let latest_round = + target_client.protocol_contracts.authority.latest_round().call().await?._0; if roundup_submit.round > latest_round { let transaction_request = self.build_transaction_request( &target_client.protocol_contracts.socket, roundup_submit, ); - - if let Some(sender) = self.tx_request_senders.get(&target_client.get_chain_id()) { - sender - .send(TxRequestMessage::new( - TxRequest::Legacy(transaction_request), - TxRequestMetadata::VSPPhase2(VSPPhase2Metadata::new( - roundup_submit.round, - target_client.get_chain_id(), - )), - true, - true, - GasCoefficient::Low, - is_bootstrap, - )) - .unwrap() - } + let target_chain_id = target_client.get_chain_id().await?; + + // if let Some(sender) = self.tx_request_senders.get(&target_chain_id) { + // sender + // .send(TxRequestMessage::new( + // transaction_request, + // TxRequestMetadata::VSPPhase2(VSPPhase2Metadata::new( + // roundup_submit.round, + // target_chain_id, + // )), + // true, + // true, + // GasCoefficient::Low, + // is_bootstrap, + // )) + // .unwrap() + // } + todo!() } } + + Ok(()) } /// Check if external clients are in the latest round. - async fn wait_if_latest_round(&self) { + async fn wait_if_latest_round(&self) -> Result<()> { let barrier_clone = self.bootstrap_shared_data.roundup_barrier.clone(); let external_clients = &self.external_clients; - for target_client in external_clients { + for (_, target_client) in external_clients.iter() { let barrier_clone_inner = barrier_clone.clone(); - let current_round = self - .client - .contract_call( - self.client.protocol_contracts.authority.latest_round(), - "authority.latest_round", - ) - .await; - let target_chain_round = target_client - .contract_call( - target_client.protocol_contracts.authority.latest_round(), - "authority.latest_round", - ) - .await; + let current_round = + self.client.protocol_contracts.authority.latest_round().call().await?._0; + let target_chain_round = + target_client.protocol_contracts.authority.latest_round().call().await?._0; + let bootstrap_guard = self.bootstrap_shared_data.roundup_bootstrap_count.clone(); tokio::spawn(async move { @@ -349,16 +350,23 @@ impl RoundupRelayHandler { barrier_clone_inner.wait().await; }); } + + Ok(()) } } #[async_trait] -impl BootstrapHandler for RoundupRelayHandler { +impl BootstrapHandler for RoundupRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn bootstrap_shared_data(&self) -> Arc { self.bootstrap_shared_data.clone() } - async fn bootstrap(&self) { + async fn bootstrap(&self) -> Result<()> { log::info!( target: &self.client.get_chain_name(), "-[{}] ⚙️ [Bootstrap mode] Bootstrapping RoundUp events.", @@ -367,7 +375,7 @@ impl BootstrapHandler for RoundupRelayHandler { let mut bootstrap_guard = self.bootstrap_shared_data.bootstrap_states.write().await; // Checking if the current round is the latest round - self.wait_if_latest_round().await; + self.wait_if_latest_round().await?; // Wait to lock after checking if it is latest round self.bootstrap_shared_data.roundup_barrier.clone().wait().await; @@ -384,11 +392,11 @@ impl BootstrapHandler for RoundupRelayHandler { if bootstrap_guard.iter().all(|s| *s == BootstrapState::BootstrapRoundUpPhase2) { drop(bootstrap_guard); - let logs = self.get_bootstrap_events().await; + let logs = self.get_bootstrap_events().await?; let mut stream = tokio_stream::iter(logs); while let Some(log) = stream.next().await { - self.process_confirmed_log(&log, true).await; + self.process_confirmed_log(&log, true).await?; } } @@ -398,28 +406,23 @@ impl BootstrapHandler for RoundupRelayHandler { tokio::spawn(async move { socket_barrier_clone.clone().wait().await; }); + + Ok(()) } - async fn get_bootstrap_events(&self) -> Vec { + async fn get_bootstrap_events(&self) -> Result> { let mut logs = vec![]; if let Some(bootstrap_config) = &self.bootstrap_shared_data.bootstrap_config { - let round_info: RoundMetaData = self - .client - .contract_call( - self.client.protocol_contracts.authority.round_info(), - "authority.round_info", - ) - .await; let bootstrap_offset_height = self .client .get_bootstrap_offset_height_based_on_block_time( bootstrap_config.round_offset.unwrap_or(DEFAULT_BOOTSTRAP_ROUND_OFFSET), - round_info, + self.client.protocol_contracts.authority.round_info().call().await?._0, ) - .await; + .await?; - let latest_block_number = self.client.get_latest_block_number().await; + let latest_block_number = self.client.get_block_number().await?; let mut from_block = latest_block_number.saturating_sub(bootstrap_offset_height); let to_block = latest_block_number; @@ -429,18 +432,18 @@ impl BootstrapHandler for RoundupRelayHandler { std::cmp::min(from_block + BOOTSTRAP_BLOCK_CHUNK_SIZE - 1, to_block); let filter = Filter::new() - .address(self.client.protocol_contracts.socket.address()) - .topic0(self.roundup_signature) + .address(*self.client.protocol_contracts.socket.address()) + .event_signature(self.roundup_signature) .from_block(from_block) .to_block(chunk_to_block); - let chunk_logs = self.client.get_logs(&filter).await; + let chunk_logs = self.client.get_logs(&filter).await?; logs.extend(chunk_logs); from_block = chunk_to_block + 1; } } - logs + Ok(logs) } } diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index b4e67430..2c82d19b 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -1,11 +1,14 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration}; -use ethers::{ - abi::RawLog, - prelude::decode_logs, - providers::JsonRpcClient, - types::{Filter, Log, TransactionRequest, H256, U256}, +use alloy::{ + primitives::{ChainId, B256, U256}, + providers::{fillers::TxFiller, Provider, WalletProvider}, + rpc::types::{Filter, Log, TransactionRequest}, + sol_types::SolEvent, + transports::Transport, }; +use byteorder::{BigEndian, ByteOrder}; +use eyre::Result; use tokio::{sync::broadcast::Receiver, time::sleep}; use tokio_stream::StreamExt; @@ -14,18 +17,17 @@ use br_primitives::{ constants::{ cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE, - errors::{INVALID_BIFROST_NATIVENESS, INVALID_CHAIN_ID, INVALID_CONTRACT_ABI}, + errors::{INVALID_BIFROST_NATIVENESS, INVALID_CHAIN_ID}, }, - contracts::{ - authority::RoundMetaData, - socket::{Instruction, RequestID, Signatures, SocketEvents, SocketMessage}, + contracts::socket::{ + SocketContract::Socket, + Socket_Struct::{Instruction, RequestID, Signatures, Socket_Message}, }, eth::{ - BootstrapState, BuiltRelayTransaction, ChainID, GasCoefficient, RelayDirection, - SocketEventStatus, + BootstrapState, BuiltRelayTransaction, GasCoefficient, RelayDirection, SocketEventStatus, }, periodic::RollbackSender, - tx::{SocketRelayMetadata, TxRequest, TxRequestMessage, TxRequestMetadata, TxRequestSender}, + tx::{SocketRelayMetadata, TxRequestMessage, TxRequestMetadata}, utils::sub_display_format, }; @@ -38,29 +40,35 @@ use crate::eth::{ const SUB_LOG_TARGET: &str = "socket-handler"; /// The essential task that handles `socket relay` related events. -pub struct SocketRelayHandler { +pub struct SocketRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The `EthClient` to interact with the connected blockchain. - pub client: Arc>, - /// The senders that sends messages to the tx request channel. - tx_request_senders: BTreeMap>, - /// The rollback senders that sends rollbackable socket messages. - rollback_senders: BTreeMap>, + pub client: Arc>, /// The receiver that consumes new events from the block channel. event_receiver: Receiver, /// The entire clients instantiated in the system. > - system_clients: BTreeMap>>, + system_clients: Arc>>>, /// Signature of the `Socket` Event. - socket_signature: H256, + socket_signature: B256, /// The bootstrap shared data. bootstrap_shared_data: Arc, } #[async_trait::async_trait] -impl Handler for SocketRelayHandler { - async fn run(&mut self) { +impl Handler for SocketRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + async fn run(&mut self) -> Result<()> { loop { if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapSocketRelay).await { - self.bootstrap().await; + self.bootstrap().await?; sleep(Duration::from_millis(self.client.metadata.call_interval)).await; } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { @@ -76,121 +84,125 @@ impl Handler for SocketRelayHandler { let mut stream = tokio_stream::iter(msg.event_logs); while let Some(log) = stream.next().await { - if self.is_target_contract(&log) && self.is_target_event(log.topics[0]) { - self.process_confirmed_log(&log, false).await; + if self.is_target_contract(&log) && self.is_target_event(log.topic0()) { + self.process_confirmed_log(&log, false).await?; } } } } } - async fn process_confirmed_log(&self, log: &Log, is_bootstrap: bool) { - match decode_logs::(&[RawLog::from(log.clone())]) { - Ok(decoded) => match &decoded[0] { - SocketEvents::Socket(socket) => { - let msg = socket.clone().msg; - let metadata = SocketRelayMetadata::new( - self.is_inbound_sequence(ChainID::from_be_bytes(msg.ins_code.chain)), - SocketEventStatus::from(msg.status), - msg.req_id.sequence, - ChainID::from_be_bytes(msg.req_id.chain), - ChainID::from_be_bytes(msg.ins_code.chain), - msg.params.to, - is_bootstrap, - ); - - if !metadata.is_bootstrap { - log::info!( - target: &self.client.get_chain_name(), - "-[{}] 🔖 Detected socket event: {}, {:?}-{:?}", - sub_display_format(SUB_LOG_TARGET), - metadata, - log.block_number.unwrap(), - log.transaction_hash.unwrap(), - ); - } + async fn process_confirmed_log(&self, log: &Log, is_bootstrap: bool) -> Result<()> { + match log.log_decode::() { + Ok(decoded_log) => { + let decoded_socket = decoded_log.inner.data; - if !self.is_selected_relayer(&msg.req_id.round_id.into()).await { - // do nothing if not selected - return; - } - if self.is_sequence_ended(&msg.req_id, &msg.ins_code, metadata.status).await { - // do nothing if protocol sequence ended - return; - } + let msg = decoded_socket.msg.clone(); + let metadata = SocketRelayMetadata::new( + self.is_inbound_sequence( + BigEndian::read_u32(&msg.ins_code.ChainIndex.0) as ChainId + ), + SocketEventStatus::from(msg.status), + msg.req_id.sequence, + BigEndian::read_u32(&msg.req_id.ChainIndex.0) as ChainId, + BigEndian::read_u32(&msg.ins_code.ChainIndex.0) as ChainId, + msg.params.to, + is_bootstrap, + ); - self.send_socket_message(msg.clone(), metadata.clone(), metadata.is_inbound) - .await; - }, + if !self.is_selected_relayer(&U256::from(msg.req_id.round_id)).await? { + // do nothing if not selected + return Ok(()); + } + if self.is_sequence_ended(&msg.req_id, &msg.ins_code, metadata.status).await? { + // do nothing if protocol sequence ended + return Ok(()); + } + + self.send_socket_message(msg.clone(), metadata.clone(), metadata.is_inbound) + .await; }, Err(error) => panic!( "[{}]-[{}]-[{}] Unknown error while decoding socket event: {:?}", - self.client.get_chain_name(), + &self.client.get_chain_name(), SUB_LOG_TARGET, self.client.address(), error, ), } + + Ok(()) } #[inline] fn is_target_contract(&self, log: &Log) -> bool { if let Some(bitcoin_socket) = self.client.protocol_contracts.bitcoin_socket.as_ref() { - log.address == self.client.protocol_contracts.socket.address() - || log.address == bitcoin_socket.address() + log.address() == *self.client.protocol_contracts.socket.address() + || log.address() == *bitcoin_socket.address() } else { - log.address == self.client.protocol_contracts.socket.address() + log.address() == *self.client.protocol_contracts.socket.address() } } #[inline] - fn is_target_event(&self, topic: H256) -> bool { - topic == self.socket_signature + fn is_target_event(&self, topic: Option<&B256>) -> bool { + match topic { + Some(topic) => topic == &self.socket_signature, + None => false, + } } } #[async_trait::async_trait] -impl SocketRelayBuilder for SocketRelayHandler { - fn get_client(&self) -> Arc> { +impl SocketRelayBuilder for SocketRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + fn get_client(&self) -> Arc> { self.client.clone() } async fn build_transaction( &self, - msg: SocketMessage, + msg: Socket_Message, is_inbound: bool, - relay_tx_chain_id: ChainID, - ) -> Option { + relay_tx_chain_id: ChainId, + ) -> Result> { if let Some(system_client) = self.system_clients.get(&relay_tx_chain_id) { let to_socket = system_client.protocol_contracts.socket.address(); // the original msg must be used for building calldata let (signatures, is_external) = if is_inbound { - self.build_inbound_signatures(msg.clone()).await + self.build_inbound_signatures(msg.clone()).await? } else { - self.build_outbound_signatures(msg.clone()).await + self.build_outbound_signatures(msg.clone()).await? }; - return Some(BuiltRelayTransaction::new( + return Ok(Some(BuiltRelayTransaction::new( TransactionRequest::default() - .data(self.build_poll_call_data(msg, signatures)) - .to(to_socket), + .input(self.build_poll_call_data(msg, signatures)) + .to(*to_socket), is_external, - )); + ))); } - None + Ok(None) } - async fn build_inbound_signatures(&self, mut msg: SocketMessage) -> (Signatures, bool) { + async fn build_inbound_signatures( + &self, + mut msg: Socket_Message, + ) -> Result<(Signatures, bool)> { let status = SocketEventStatus::from(msg.status); let mut is_external = false; let signatures = match status { SocketEventStatus::Requested | SocketEventStatus::Failed => Signatures::default(), SocketEventStatus::Executed => { msg.status = SocketEventStatus::Accepted.into(); - Signatures::from(self.sign_socket_message(msg)) + Signatures::from(self.sign_socket_message(msg).await?) }, SocketEventStatus::Reverted => { msg.status = SocketEventStatus::Rejected.into(); - Signatures::from(self.sign_socket_message(msg)) + Signatures::from(self.sign_socket_message(msg).await?) }, SocketEventStatus::Accepted | SocketEventStatus::Rejected => { is_external = true; @@ -204,16 +216,19 @@ impl SocketRelayBuilder for SocketRelayHandler { status ), }; - (signatures, is_external) + Ok((signatures, is_external)) } - async fn build_outbound_signatures(&self, mut msg: SocketMessage) -> (Signatures, bool) { + async fn build_outbound_signatures( + &self, + mut msg: Socket_Message, + ) -> Result<(Signatures, bool)> { let status = SocketEventStatus::from(msg.status); let mut is_external = false; let signatures = match status { SocketEventStatus::Requested => { msg.status = SocketEventStatus::Accepted.into(); - Signatures::from(self.sign_socket_message(msg)) + Signatures::from(self.sign_socket_message(msg).await?) }, SocketEventStatus::Accepted | SocketEventStatus::Rejected => { is_external = true; @@ -228,43 +243,28 @@ impl SocketRelayBuilder for SocketRelayHandler { status ), }; - (signatures, is_external) + Ok((signatures, is_external)) } } -impl SocketRelayHandler { +impl SocketRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `SocketRelayHandler` instance. pub fn new( - id: ChainID, - tx_request_senders_vec: Vec>, - rollback_senders: BTreeMap>, + id: ChainId, event_receiver: Receiver, - system_clients_vec: Vec>>, + system_clients: Arc>>>, bootstrap_shared_data: Arc, ) -> Self { - let system_clients: BTreeMap>> = system_clients_vec - .iter() - .map(|client| (client.get_chain_id(), client.clone())) - .collect(); - let client = system_clients.get(&id).expect(INVALID_CHAIN_ID).clone(); - let tx_request_senders: BTreeMap> = tx_request_senders_vec - .iter() - .map(|sender| (sender.id, sender.clone())) - .collect(); - Self { - tx_request_senders, - rollback_senders, event_receiver, - socket_signature: client - .protocol_contracts - .socket - .abi() - .event("Socket") - .expect(INVALID_CONTRACT_ABI) - .signature(), + socket_signature: Socket::SIGNATURE_HASH, client, system_clients, bootstrap_shared_data, @@ -274,10 +274,10 @@ impl SocketRelayHandler { /// Sends the `SocketMessage` to the target chain channel. async fn send_socket_message( &self, - socket_msg: SocketMessage, + socket_msg: Socket_Message, metadata: SocketRelayMetadata, is_inbound: bool, - ) { + ) -> Result<()> { let status = SocketEventStatus::from(socket_msg.status); let relay_tx_chain_id = if is_inbound { @@ -291,8 +291,9 @@ impl SocketRelayHandler { }; // build and send transaction request - if let Some(built_transaction) = - self.build_transaction(socket_msg.clone(), is_inbound, relay_tx_chain_id).await + if let Some(built_transaction) = self + .build_transaction(socket_msg.clone(), is_inbound, relay_tx_chain_id) + .await? { self.request_send_transaction( relay_tx_chain_id, @@ -308,15 +309,17 @@ impl SocketRelayHandler { ) .await; } + + Ok(()) } /// Get the chain ID of the inbound sequence relay transaction. fn get_inbound_relay_tx_chain_id( &self, status: SocketEventStatus, - src_chain_id: ChainID, - dst_chain_id: ChainID, - ) -> ChainID { + src_chain_id: ChainId, + dst_chain_id: ChainId, + ) -> ChainId { match status { SocketEventStatus::Requested | SocketEventStatus::Failed @@ -336,9 +339,9 @@ impl SocketRelayHandler { fn get_outbound_relay_tx_chain_id( &self, status: SocketEventStatus, - src_chain_id: ChainID, - dst_chain_id: ChainID, - ) -> ChainID { + src_chain_id: ChainId, + dst_chain_id: ChainId, + ) -> ChainId { match status { SocketEventStatus::Requested | SocketEventStatus::Executed @@ -360,37 +363,38 @@ impl SocketRelayHandler { req_id: &RequestID, ins_code: &Instruction, status: SocketEventStatus, - ) -> bool { - let src = ChainID::from_be_bytes(req_id.chain); - let dst = ChainID::from_be_bytes(ins_code.chain); + ) -> Result { + let src = BigEndian::read_u32(&req_id.ChainIndex.0) as ChainId; + let dst = BigEndian::read_u32(&ins_code.ChainIndex.0) as ChainId; // if inbound::accepted and relaying to bitcoin we consider as ended if let Some(bitcoin_chain_id) = self.client.get_bitcoin_chain_id() { if self.is_inbound_sequence(dst) && bitcoin_chain_id == src { - return matches!(status, SocketEventStatus::Accepted); + return Ok(matches!(status, SocketEventStatus::Accepted)); } } if let Some(src_client) = &self.system_clients.get(&src) { let request = src_client - .contract_call( - src_client.protocol_contracts.socket.get_request(req_id.clone()), - "socket.get_request", - ) - .await; + .protocol_contracts + .socket + .get_request(req_id.clone()) + .call() + .await? + ._0; - return matches!( + return Ok(matches!( SocketEventStatus::from(&request.field[0]), SocketEventStatus::Committed | SocketEventStatus::Rollbacked - ); + )); } - false + Ok(false) } /// Verifies whether the socket event is an inbound sequence. - fn is_inbound_sequence(&self, dst_chain_id: ChainID) -> bool { + fn is_inbound_sequence(&self, dst_chain_id: ChainId) -> bool { matches!( - (self.client.get_chain_id() == dst_chain_id, self.client.metadata.if_destination_chain), + (self.client.chain_id() == dst_chain_id, self.client.metadata.if_destination_chain), (true, RelayDirection::Inbound) | (false, RelayDirection::Outbound) ) } @@ -404,142 +408,141 @@ impl SocketRelayHandler { } /// Verifies whether the current relayer was selected at the given round. - async fn is_selected_relayer(&self, round: &U256) -> bool { + async fn is_selected_relayer(&self, round: &U256) -> Result { if self.client.metadata.is_native { let relayer_manager = self.client.protocol_contracts.relayer_manager.as_ref().unwrap(); - return self - .client - .contract_call( - relayer_manager.is_previous_selected_relayer( - *round, - self.client.address(), - false, - ), - "relayer_manager.is_previous_selected_relayer", - ) - .await; + let res = relayer_manager + .is_previous_selected_relayer(*round, self.client.address(), false) + .call() + .await? + ._0; + return Ok(res); } else if let Some((_id, native_client)) = self.system_clients.iter().find(|(_id, client)| client.metadata.is_native) { // always use the native client's contract. due to handle missed VSP's. let relayer_manager = native_client.protocol_contracts.relayer_manager.as_ref().unwrap(); - return native_client - .contract_call( - relayer_manager.is_previous_selected_relayer( - *round, - self.client.address(), - false, - ), - "relayer_manager.is_previous_selected_relayer", - ) - .await; + let res = relayer_manager + .is_previous_selected_relayer(*round, self.client.address(), false) + .call() + .await? + ._0; + return Ok(res); } - false + + Ok(false) } /// Request send socket relay transaction to the target event channel. async fn request_send_transaction( &self, - chain_id: ChainID, + chain_id: ChainId, tx_request: TransactionRequest, metadata: SocketRelayMetadata, - socket_msg: SocketMessage, + socket_msg: Socket_Message, give_random_delay: bool, gas_coefficient: GasCoefficient, ) { - if let Some(sender) = self.tx_request_senders.get(&chain_id) { - if self.is_executable(metadata.is_inbound, metadata.status) { - self.send_rollbackable_request(chain_id, metadata.clone(), socket_msg); - } - - match sender.send(TxRequestMessage::new( - TxRequest::Legacy(tx_request), - TxRequestMetadata::SocketRelay(metadata.clone()), - true, - give_random_delay, - gas_coefficient, - metadata.is_bootstrap, - )) { - Ok(()) => log::info!( - target: &self.client.get_chain_name(), - "-[{}] 🔖 Request relay transaction to chain({:?}): {}", - sub_display_format(SUB_LOG_TARGET), - chain_id, - metadata - ), - Err(error) => { - let log_msg = format!( - "-[{}]-[{}] ❗️ Failed to send relay transaction to chain({:?}): {}, Error: {}", - sub_display_format(SUB_LOG_TARGET), - self.client.address(), - chain_id, - metadata, - error - ); - log::error!(target: &self.client.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &self.client.get_chain_name()), - sentry::Level::Error, - ); - }, - } - } + // if let Some(sender) = self.tx_request_senders.get(&chain_id) { + // if self.is_executable(metadata.is_inbound, metadata.status) { + // self.send_rollbackable_request(chain_id, metadata.clone(), socket_msg); + // } + + // match sender.send(TxRequestMessage::new( + // tx_request, + // TxRequestMetadata::SocketRelay(metadata.clone()), + // true, + // give_random_delay, + // gas_coefficient, + // metadata.is_bootstrap, + // )) { + // Ok(()) => log::info!( + // target: &self.client.get_chain_name(), + // "-[{}] 🔖 Request relay transaction to chain({:?}): {}", + // sub_display_format(SUB_LOG_TARGET), + // chain_id, + // metadata + // ), + // Err(error) => { + // let log_msg = format!( + // "-[{}]-[{}] ❗️ Failed to send relay transaction to chain({:?}): {}, Error: {}", + // sub_display_format(SUB_LOG_TARGET), + // self.client.address(), + // chain_id, + // metadata, + // error + // ); + // log::error!(target: &self.client.get_chain_name(), "{log_msg}"); + // sentry::capture_message( + // &format!("[{}]{log_msg}", &self.client.get_chain_name()), + // sentry::Level::Error, + // ); + // }, + // } + // } + todo!() } /// Sends a rollbackable socket message to the rollback channel. /// The received message will be handled when the interval has been reached. fn send_rollbackable_request( &self, - chain_id: ChainID, + chain_id: ChainId, metadata: SocketRelayMetadata, - socket_msg: SocketMessage, + socket_msg: Socket_Message, ) { - if let Some(rollback_sender) = self.rollback_senders.get(&chain_id) { - match rollback_sender.send(socket_msg) { - Ok(()) => log::info!( - target: &self.client.get_chain_name(), - "-[{}] 🔃 Store rollbackable socket message: {}", - sub_display_format(SUB_LOG_TARGET), - metadata - ), - Err(error) => { - let msg = format!( - "-[{}]-[{}] ❗️ Failed to store rollbackable socket message: {}, Error: {}", - sub_display_format(SUB_LOG_TARGET), - self.client.address(), - metadata, - error.to_string() - ); - log::error!(target: &self.client.get_chain_name(), "{msg}"); - sentry::capture_message( - &format!("[{}]{msg}", &self.client.get_chain_name()), - sentry::Level::Error, - ); - }, - } - } + // if let Some(rollback_sender) = self.rollback_senders.get(&chain_id) { + // match rollback_sender.send(socket_msg) { + // Ok(()) => log::info!( + // target: &self.client.get_chain_name(), + // "-[{}] 🔃 Store rollbackable socket message: {}", + // sub_display_format(SUB_LOG_TARGET), + // metadata + // ), + // Err(error) => { + // let msg = format!( + // "-[{}]-[{}] ❗️ Failed to store rollbackable socket message: {}, Error: {}", + // sub_display_format(SUB_LOG_TARGET), + // self.client.address(), + // metadata, + // error.to_string() + // ); + // log::error!(target: &self.client.get_chain_name(), "{msg}"); + // sentry::capture_message( + // &format!("[{}]{msg}", &self.client.get_chain_name()), + // sentry::Level::Error, + // ); + // }, + // } + // } + todo!() } } #[async_trait::async_trait] -impl BootstrapHandler for SocketRelayHandler { +impl BootstrapHandler for SocketRelayHandler +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn bootstrap_shared_data(&self) -> Arc { self.bootstrap_shared_data.clone() } - async fn bootstrap(&self) { + async fn bootstrap(&self) -> Result<()> { log::info!( target: &self.client.get_chain_name(), "-[{}] ⚙️ [Bootstrap mode] Bootstrapping Socket events.", sub_display_format(SUB_LOG_TARGET), ); - let logs = self.get_bootstrap_events().await; + let logs = self.get_bootstrap_events().await?; let mut stream = tokio_stream::iter(logs); while let Some(log) = stream.next().await { - self.process_confirmed_log(&log, true).await; + self.process_confirmed_log(&log, true).await?; } let mut bootstrap_count = self.bootstrap_shared_data.socket_bootstrap_count.lock().await; @@ -559,28 +562,20 @@ impl BootstrapHandler for SocketRelayHandler { sub_display_format(SUB_LOG_TARGET), ); } + + Ok(()) } - async fn get_bootstrap_events(&self) -> Vec { + async fn get_bootstrap_events(&self) -> Result> { let mut logs = vec![]; if let Some(bootstrap_config) = &self.bootstrap_shared_data.bootstrap_config { - let round_info: RoundMetaData = if self.client.metadata.is_native { - self.client - .contract_call( - self.client.protocol_contracts.authority.round_info(), - "authority.round_info", - ) - .await + let round_info = if self.client.metadata.is_native { + self.client.protocol_contracts.authority.round_info().call().await?._0 } else if let Some((_id, native_client)) = self.system_clients.iter().find(|(_id, client)| client.metadata.is_native) { - native_client - .contract_call( - native_client.protocol_contracts.authority.round_info(), - "authority.round_info", - ) - .await + native_client.protocol_contracts.authority.round_info().call().await?._0 } else { panic!( "[{}]-[{}] {}", @@ -596,9 +591,9 @@ impl BootstrapHandler for SocketRelayHandler { bootstrap_config.round_offset.unwrap_or(DEFAULT_BOOTSTRAP_ROUND_OFFSET), round_info, ) - .await; + .await?; - let latest_block_number = self.client.get_latest_block_number().await; + let latest_block_number = self.client.get_block_number().await?; let mut from_block = latest_block_number.saturating_sub(bootstrap_offset_height); let to_block = latest_block_number; @@ -611,128 +606,126 @@ impl BootstrapHandler for SocketRelayHandler { if let Some(bitcoin_socket) = &self.client.protocol_contracts.bitcoin_socket { Filter::new() .address(vec![ - self.client.protocol_contracts.socket.address(), - bitcoin_socket.address(), + *self.client.protocol_contracts.socket.address(), + *bitcoin_socket.address(), ]) - .topic0(self.socket_signature) + .event_signature(self.socket_signature) .from_block(from_block) .to_block(chunk_to_block) } else { Filter::new() - .address(self.client.protocol_contracts.socket.address()) - .topic0(self.socket_signature) + .address(*self.client.protocol_contracts.socket.address()) + .event_signature(self.socket_signature) .from_block(from_block) .to_block(chunk_to_block) }; - let target_logs_chunk = self.client.get_logs(&filter).await; + let target_logs_chunk = self.client.get_logs(&filter).await?; logs.extend(target_logs_chunk); from_block = chunk_to_block + 1; } } - logs + Ok(logs) } } -#[cfg(test)] -mod tests { - use std::{str::FromStr, sync::Arc}; - - use ethers::{ - abi::AbiDecode, - providers::{Http, Provider}, - types::{Bytes, H160}, - utils::hex::ToHexExt, - }; - - use br_primitives::contracts::socket::{Socket, SocketContract}; - - use super::*; - - #[tokio::test] - async fn test_is_already_done() { - let src_provider = Arc::new(Provider::::try_from("").unwrap()); - let dst_provider = Arc::new(Provider::::try_from("").unwrap()); - - let src_socket = SocketContract::new( - H160::from_str("0xd551F33Ca8eCb0Be83d8799D9C68a368BA36Dd52").unwrap(), - src_provider.clone(), - ); - let dst_socket = SocketContract::new( - H160::from_str("0xb5Fa48E8B9b89760a9f9176388D1B64A8D4968dF").unwrap(), - dst_provider.clone(), - ); - - let request_id: RequestID = - RequestID { chain: [0, 0, 11, 252], round_id: 677, sequence: 3446 }; - println!("request_id : {:?}", request_id); - - let src_request = src_socket.get_request(request_id.clone()).call().await.unwrap(); - println!("src_request : {:?}", src_request); - let dst_request = dst_socket.get_request(request_id).call().await.unwrap(); - println!("dst_request : {:?}", dst_request); - } - - #[test] - fn test_socket_event_decode() { - let data = Bytes::from_str("0x00000000000000000000000000000000000000000000000000000000000000200000bfc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000271200000000000000000000000000000000000000000000000000000000030203010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000003000000030000bfc0148a26ea2376f006c09b7d3163f1fc70ad4134a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e661745856b03130d03932f683cda020d7ee9ea0000000000000000000000006e661745856b03130d03932f683cda020d7ee9ea00000000000000000000000000000000000000000000000000000000000ee5e800000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000").unwrap(); - - match Socket::decode(&data) { - Ok(socket) => { - let req_id = socket.msg.req_id; - let status = socket.msg.status; - let ins_code = socket.msg.ins_code; - let params = socket.msg.params; - - println!("req_id.chain -> {:?}", req_id.chain.encode_hex_with_prefix()); - println!("req_id.round_id -> {:?}", req_id.round_id); - println!("req_id.sequence -> {:?}", req_id.sequence); - - println!("status -> {:?}", status); - - println!("ins_code.chain -> {:?}", ins_code.chain.encode_hex_with_prefix()); - println!("ins_code.method -> {:?}", ins_code.method.encode_hex_with_prefix()); - - println!("params.tokenIDX0 -> {:?}", params.token_idx0.encode_hex_with_prefix()); - println!("params.tokenIDX1 -> {:?}", params.token_idx1.encode_hex_with_prefix()); - println!("params.refund -> {:?}", params.refund); - println!("params.to -> {:?}", params.to); - println!("params.amount -> {:?}", params.amount); - println!("params.variants -> {:?}", params.variants.to_string()); - }, - Err(error) => { - panic!("decode failed -> {}", error); - }, - } - } - - #[test] - fn test_socket_msg_decode() { - let req_id_chain = array_bytes::bytes2hex("0x", [0, 0, 39, 18]); - let ins_code_chain = array_bytes::bytes2hex("0x", [0, 0, 191, 192]); - let ins_code_method = - array_bytes::bytes2hex("0x", [3, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - - let params_tokenidx0 = array_bytes::bytes2hex( - "0x", - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - ); - let params_tokenidx1 = array_bytes::bytes2hex( - "0x", - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - ], - ); - - println!("req_id_chain -> {:?}", req_id_chain); - println!("ins_code_chain -> {:?}", ins_code_chain); - println!("ins_code_method -> {:?}", ins_code_method); - println!("params_tokenidx0 -> {:?}", params_tokenidx0); - println!("params_tokenidx1 -> {:?}", params_tokenidx1); - } -} +// #[cfg(test)] +// mod tests { +// use alloy::{ +// primitives::{address, bytes}, +// providers::ProviderBuilder, +// sol_types::SolEvent, +// }; +// use br_primitives::contracts::socket::SocketContract::{self, Socket}; +// use std::sync::Arc; + +// use super::*; + +// #[tokio::test] +// async fn test_is_already_done() { +// let src_provider = Arc::new(ProviderBuilder::new().on_http("".parse().unwrap())); +// let dst_provider = Arc::new(ProviderBuilder::new().on_http("".parse().unwrap())); + +// let src_socket = SocketContract::new( +// address!("d551F33Ca8eCb0Be83d8799D9C68a368BA36Dd52"), +// src_provider.clone(), +// ); +// let dst_socket = SocketContract::new( +// address!("b5Fa48E8B9b89760a9f9176388D1B64A8D4968dF"), +// dst_provider.clone(), +// ); + +// let request_id = +// RequestID { ChainIndex: [0, 0, 11, 252].into(), round_id: 677, sequence: 3446 }; +// println!("request_id : {:?}", request_id); + +// let src_request = src_socket.get_request(request_id.clone()).call().await.unwrap(); +// println!("src_request : {:?}", src_request); +// let dst_request = dst_socket.get_request(request_id).call().await.unwrap(); +// println!("dst_request : {:?}", dst_request); +// } + +// #[test] +// fn test_socket_event_decode() { +// let data = bytes!("00000000000000000000000000000000000000000000000000000000000000200000bfc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000271200000000000000000000000000000000000000000000000000000000030203010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000003000000030000bfc0148a26ea2376f006c09b7d3163f1fc70ad4134a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e661745856b03130d03932f683cda020d7ee9ea0000000000000000000000006e661745856b03130d03932f683cda020d7ee9ea00000000000000000000000000000000000000000000000000000000000ee5e800000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000"); + +// match Socket::abi_decode_data(&data, true) { +// Ok(socket) => { +// let socket_msg = socket.0; +// let req_id = socket_msg.req_id; +// let status = socket_msg.status; +// let ins_code = socket_msg.ins_code; +// let params = socket_msg.params; + +// println!("req_id.chain -> {:?}", req_id.ChainIndex.encode_hex_with_prefix()); +// println!("req_id.round_id -> {:?}", req_id.round_id); +// println!("req_id.sequence -> {:?}", req_id.sequence); + +// println!("status -> {:?}", status); + +// println!("ins_code.chain -> {:?}", ins_code.ChainIndex.encode_hex_with_prefix()); +// println!("ins_code.method -> {:?}", ins_code.method.encode_hex_with_prefix()); + +// println!("params.tokenIDX0 -> {:?}", params.token_idx0.encode_hex_with_prefix()); +// println!("params.tokenIDX1 -> {:?}", params.token_idx1.encode_hex_with_prefix()); +// println!("params.refund -> {:?}", params.refund); +// println!("params.to -> {:?}", params.to); +// println!("params.amount -> {:?}", params.amount); +// println!("params.variants -> {:?}", params.variants.to_string()); +// }, +// Err(error) => { +// panic!("decode failed -> {}", error); +// }, +// } +// } + +// #[test] +// fn test_socket_msg_decode() { +// let req_id_chain = array_bytes::bytes2hex("0x", [0, 0, 39, 18]); +// let ins_code_chain = array_bytes::bytes2hex("0x", [0, 0, 191, 192]); +// let ins_code_method = +// array_bytes::bytes2hex("0x", [3, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + +// let params_tokenidx0 = array_bytes::bytes2hex( +// "0x", +// [ +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, +// ], +// ); +// let params_tokenidx1 = array_bytes::bytes2hex( +// "0x", +// [ +// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// 0, 0, 0, 0, +// ], +// ); + +// println!("req_id_chain -> {:?}", req_id_chain); +// println!("ins_code_chain -> {:?}", ins_code_chain); +// println!("ins_code_method -> {:?}", ins_code_method); +// println!("params_tokenidx0 -> {:?}", params_tokenidx0); +// println!("params_tokenidx1 -> {:?}", params_tokenidx1); +// } +// } diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index a31096aa..e24c4ffa 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -1,447 +1,182 @@ -use std::{cmp::max, fmt::Debug, sync::Arc}; - use br_primitives::{ constants::{ config::{BOOTSTRAP_BLOCK_OFFSET, NATIVE_BLOCK_TIME}, errors::{INSUFFICIENT_FUNDS, INVALID_CHAIN_ID, PROVIDER_INTERNAL_ERROR}, - tx::{DEFAULT_CALL_RETRIES, DEFAULT_CALL_RETRY_INTERVAL_MS}, }, - contracts::authority::RoundMetaData, - eth::{AggregatorContracts, ChainID, ProtocolContracts, ProviderMetadata}, - utils::sub_display_format, + contracts::authority::BfcStaking::round_meta_data, + eth::{AggregatorContracts, ProtocolContracts, ProviderMetadata}, }; -use ethers::{ - abi::Detokenize, - prelude::ContractCall, - providers::{JsonRpcClient, Middleware, Provider}, - types::{ - Address, Block, BlockId, Filter, Log, SyncingStatus, Transaction, TransactionReceipt, - TxpoolContent, H256, U256, U64, +use alloy::{ + network::Ethereum, + primitives::{ + utils::{format_units, parse_ether}, + Address, ChainId, + }, + providers::{ + fillers::{FillProvider, TxFiller}, + PendingTransactionBuilder, Provider, RootProvider, SendableTx, WalletProvider, }, - utils::{format_units, WEI_IN_ETHER}, + signers::{local::LocalSigner, Signature, Signer as _}, + transports::{Transport, TransportResult}, }; -use serde::{de::DeserializeOwned, Serialize}; -use tokio::time::{sleep, Duration}; +use eyre::{eyre, Result}; +use k256::ecdsa::SigningKey; +use std::sync::Arc; use url::Url; -use self::{ - traits::{Eip1559GasMiddleware, LegacyGasMiddleware}, - wallet::WalletManager, -}; - pub mod events; pub mod handlers; pub mod traits; -pub mod tx; -pub mod wallet; - -const SUB_LOG_TARGET: &str = "eth-client"; -/// The core client for EVM-based chain interactions. -pub struct EthClient { - /// The wallet manager for the connected relayer. - pub wallet: WalletManager, - /// The metadata of the provider. +#[derive(Clone)] +pub struct EthClient +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + /// The inner provider. + inner: Arc>, + /// The signer. + pub signer: LocalSigner, + /// The provider metadata. pub metadata: ProviderMetadata, - /// the protocol contract instances of the provider. - pub protocol_contracts: ProtocolContracts, - /// the aggregator contract instances of the provider. - pub aggregator_contracts: AggregatorContracts, - /// The ethers.rs wrapper for the connected chain. - provider: Arc>, - /// The flag whether debug mode is enabled. If enabled, certain errors will be logged such as - /// gas estimation failures. - debug_mode: bool, + /// The protocol contracts. + pub protocol_contracts: ProtocolContracts, + /// The aggregator contracts. + pub aggregator_contracts: AggregatorContracts, } -impl EthClient { - /// Instantiates a new `EthClient` instance for the given chain. +impl EthClient +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + /// Create a new EthClient pub fn new( - wallet: WalletManager, - provider: Arc>, + inner: Arc>, + signer: LocalSigner, metadata: ProviderMetadata, - protocol_contracts: ProtocolContracts, - aggregator_contracts: AggregatorContracts, - debug_mode: bool, + protocol_contracts: ProtocolContracts, + aggregator_contracts: AggregatorContracts, ) -> Self { - Self { wallet, provider, metadata, protocol_contracts, aggregator_contracts, debug_mode } - } - - /// Returns the relayer address. - pub fn address(&self) -> Address { - self.wallet.address() - } - - /// Returns name which chain this client interacts with. - pub fn get_chain_name(&self) -> String { - self.metadata.name.clone() - } - - /// Returns id which chain this client interacts with. - pub fn get_chain_id(&self) -> ChainID { - self.metadata.id - } - - pub fn get_bitcoin_chain_id(&self) -> Option { - self.metadata.bitcoin_chain_id - } - - /// Returns the provider URL. - pub fn get_url(&self) -> Url { - self.metadata.url.clone() - } - - /// Returns `Arc`. - pub fn get_provider(&self) -> Arc> { - self.provider.clone() + Self { inner, signer, metadata, protocol_contracts, aggregator_contracts } } - /// Make a JSON RPC request to the chain provider via the internal connection, and return the - /// result. This method wraps the original JSON RPC call and retries whenever the request fails - /// until it exceeds the maximum retries. - async fn rpc_call(&self, method: &str, params: P) -> R - where - P: Debug + Serialize + Send + Sync + Clone, - R: Serialize + DeserializeOwned + Debug + Send, - { - let mut retries_remaining: u8 = DEFAULT_CALL_RETRIES; - let mut error_msg = String::default(); - - while retries_remaining > 0 { - br_metrics::increase_rpc_calls(&self.get_chain_name()); - match self.provider.request(method, params.clone()).await { - Ok(result) => return result, - Err(error) => { - // retry on error - retries_remaining = retries_remaining.saturating_sub(1); - error_msg = error.to_string(); - }, - } - sleep(Duration::from_millis(DEFAULT_CALL_RETRY_INTERVAL_MS)).await; - } - panic!( - "[{}]-[{}]-[{}] {} [method: {}]: {}", - &self.get_chain_name(), - SUB_LOG_TARGET, - self.address(), - PROVIDER_INTERNAL_ERROR, - method, - error_msg - ); - } - - /// Make a contract call to the chain provider via the internal connection, and return the - /// result. This method wraps the original contract call and retries whenever the request fails - /// until it exceeds the maximum retries. - pub async fn contract_call(&self, raw_call: ContractCall, method: &str) -> D - where - M: Middleware, - D: Serialize + DeserializeOwned + Debug + Send + Detokenize, - { - let mut retries_remaining: u8 = DEFAULT_CALL_RETRIES; - let mut error_msg = String::default(); - - while retries_remaining > 0 { - br_metrics::increase_rpc_calls(&self.get_chain_name()); - match raw_call.call().await { - Ok(result) => return result, - Err(error) => { - // retry on error - retries_remaining = retries_remaining.saturating_sub(1); - error_msg = error.to_string(); - }, - } - sleep(Duration::from_millis(DEFAULT_CALL_RETRY_INTERVAL_MS)).await; - } - panic!( - "[{}]-[{}]-[{}] {} [method: {}]: {}", - &self.get_chain_name(), - SUB_LOG_TARGET, - self.address(), - PROVIDER_INTERNAL_ERROR, - method, - error_msg - ); - } - - /// Verifies whether the configured chain ID and the provider's actual chain ID matches. - pub async fn verify_chain_id(&self) { - let chain_id: U256 = self.rpc_call("eth_chainId", ()).await; - if self.get_chain_id() != chain_id.as_u32() { - panic!( - "[{}]-[{}]-[{}] {}", - &self.get_chain_name(), - SUB_LOG_TARGET, - self.address(), - INVALID_CHAIN_ID - ); + /// Verifies whether the configured chain id and the provider's chain id match. + pub async fn verify_chain_id(&self) -> Result<()> { + let chain_id = self.get_chain_id().await?; + if chain_id != self.metadata.id { + Err(eyre!(INVALID_CHAIN_ID)) + } else { + Ok(()) } } - /// Verifies whether the relayer has at least the minimum balance required. - pub async fn verify_minimum_balance(&self) { + /// Verifies whether the relayer has enough balance to pay for the transaction fees. + pub async fn verify_minimum_balance(&self) -> Result<()> { if self.metadata.is_native { - let balance = self.get_balance(self.address()).await; - if balance < WEI_IN_ETHER { - panic!( - "[{}]-[{}]-[{}] {}", - &self.get_chain_name(), - SUB_LOG_TARGET, - self.address(), - INSUFFICIENT_FUNDS - ); + let balance = self.get_balance(self.address()).await?; + if balance < parse_ether("1")? { + eyre::bail!(INSUFFICIENT_FUNDS) } } + Ok(()) } - /// Retrieves the balance of the given address. - pub async fn get_balance(&self, who: Address) -> U256 { - self.rpc_call("eth_getBalance", (who, "latest")).await - } - - /// Retrieves the latest mined block number of the connected chain. - pub async fn get_latest_block_number(&self) -> U64 { - self.rpc_call("eth_blockNumber", ()).await - } - - /// Retrieves the block information of the given block hash. - pub async fn get_block_with_txs(&self, id: BlockId) -> Option> { - self.rpc_call("eth_getBlockByNumber", (id, true)).await + /// Get the signer address. + pub fn address(&self) -> Address { + self.inner.default_signer_address() } - /// Retrieves the block information of the given block hash. - pub async fn get_block(&self, id: BlockId) -> Option> { - self.rpc_call("eth_getBlockByNumber", (id, false)).await + /// Get the chain name. + pub fn get_chain_name(&self) -> String { + self.metadata.name.clone() } - /// Retrieves the transaction of the given transaction hash. - pub async fn get_transaction(&self, hash: H256) -> Option { - self.rpc_call("eth_getTransactionByHash", vec![hash]).await + /// Returns the URL of the provider. + pub fn get_url(&self) -> Url { + self.metadata.url.clone() } - - /// Retrieves the transaction receipt of the given transaction hash. - pub async fn get_transaction_receipt(&self, hash: H256) -> Option { - self.rpc_call("eth_getTransactionReceipt", vec![hash]).await + /// Sync the native token balance to the metrics. + pub async fn sync_balance(&self) -> Result<()> { + br_metrics::set_native_balance( + &self.get_chain_name(), + format_units(self.get_balance(self.address()).await?, "ether")?.parse::()?, + ); + Ok(()) } - /// Returns the details of all transactions currently pending for inclusion in the next - /// block(s). - pub async fn get_txpool_content(&self) -> TxpoolContent { - self.rpc_call("txpool_content", ()).await + /// Get the chain id. + pub fn chain_id(&self) -> ChainId { + self.metadata.id } - /// Returns an array of all logs matching the given filter. - pub async fn get_logs(&self, filter: &Filter) -> Vec { - self.rpc_call("eth_getLogs", vec![filter]).await + /// Get the bitcoin chain id. + pub fn get_bitcoin_chain_id(&self) -> Option { + self.metadata.bitcoin_chain_id } - /// Returns an object with data about the sync status or false. - pub async fn is_syncing(&self) -> SyncingStatus { - self.rpc_call("eth_syncing", ()).await + /// Signs the given message. + pub async fn sign_message(&self, message: &[u8]) -> Result { + let a: Vec = self.signer.sign_message(message).await?.into(); + Ok(Signature::try_from(a.as_slice())?) } - /// Get factor between the block time of native-chain and block time of this chain + /// Get the bootstrap offset height based on the block time. /// Approximately Bifrost: 3s, Polygon: 2s, BSC: 3s, Ethereum: 12s pub async fn get_bootstrap_offset_height_based_on_block_time( &self, - round_offset: u32, - round_info: RoundMetaData, - ) -> U64 { - let block_number = self.get_latest_block_number().await; - let prev_block_number = block_number.saturating_sub(BOOTSTRAP_BLOCK_OFFSET.into()); - let block_diff = block_number.checked_sub(prev_block_number).unwrap().as_u64(); - - if let (Some(current_block), Some(prev_block)) = ( - self.get_block(block_number.into()).await, - self.get_block(prev_block_number.into()).await, - ) { - let timestamp_diff = - current_block.timestamp.checked_sub(prev_block.timestamp).unwrap().as_u64() as f64; + round_offset: u64, + round_info: round_meta_data, + ) -> Result { + let block_number = self.get_block_number().await?; + let prev_block_number = block_number.saturating_sub(BOOTSTRAP_BLOCK_OFFSET); + let block_diff = block_number.checked_sub(prev_block_number).unwrap(); + + let current_block = self.get_block(block_number.into(), true.into()).await?; + let prev_block = self.get_block(prev_block_number.into(), true.into()).await?; + if let (Some(current_block), Some(prev_block)) = (current_block, prev_block) { + let current_timestamp = current_block.header.timestamp; + let prev_timestamp = prev_block.header.timestamp; + let timestamp_diff = current_timestamp.checked_sub(prev_timestamp).unwrap() as f64; let avg_block_time = timestamp_diff / block_diff as f64; - let blocks = round_offset.checked_mul(round_info.round_length.as_u32()).unwrap(); + let blocks = round_offset + .checked_mul(round_info.round_length.saturating_to::()) + .unwrap(); let blocks_to_native_chain_time = blocks.checked_mul(NATIVE_BLOCK_TIME).unwrap(); let bootstrap_offset_height = blocks_to_native_chain_time as f64 / avg_block_time; - (bootstrap_offset_height.ceil() as u32).into() + Ok(bootstrap_offset_height.ceil() as u64) } else { - panic!( - "[{}]-[{}]-[{}] {} [method: bootstrap]", - &self.get_chain_name(), - SUB_LOG_TARGET, - PROVIDER_INTERNAL_ERROR, - self.address() - ); + Err(eyre!(PROVIDER_INTERNAL_ERROR)) } } - /// Send prometheus metric of the current balance. - pub async fn sync_balance(&self) { - br_metrics::set_native_balance( - &self.get_chain_name(), - format_units(self.get_balance(self.address()).await, "ether") - .unwrap() - .parse::() - .unwrap(), - ); - } - - /// Verifies whether the current relayer was selected at the current round. - pub async fn is_selected_relayer(&self) -> bool { + /// Verifies whether the current relayer was selected at the current round + pub async fn is_selected_relayer(&self) -> Result { let relayer_manager = self.protocol_contracts.relayer_manager.as_ref().unwrap(); - self.contract_call( - relayer_manager.is_selected_relayer(self.address(), false), - "relayer_manager.is_selected_relayer", - ) - .await + Ok(relayer_manager.is_selected_relayer(self.address(), false).call().await?._0) } } #[async_trait::async_trait] -impl LegacyGasMiddleware for EthClient { - async fn get_gas_price(&self) -> U256 { - match self.provider.get_gas_price().await { - Ok(gas_price) => { - br_metrics::increase_rpc_calls(&self.get_chain_name()); - gas_price - }, - Err(error) => { - self.handle_failed_get_gas_price(DEFAULT_CALL_RETRIES, error.to_string()).await - }, - } - } - - async fn get_gas_price_for_retry( - &self, - previous_gas_price: U256, - gas_price_coefficient: f64, - min_gas_price: U256, - ) -> U256 { - let previous_gas_price = previous_gas_price.as_u64() as f64; - - let current_gas_price = self.get_gas_price().await; - let escalated_gas_price = - U256::from((previous_gas_price * gas_price_coefficient).ceil() as u64); - - max(max(current_gas_price, escalated_gas_price), min_gas_price) +impl Provider for EthClient +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + fn root(&self) -> &RootProvider { + self.inner.root() } - async fn get_gas_price_for_escalation( + async fn send_transaction_internal( &self, - gas_price: U256, - gas_price_coefficient: f64, - min_gas_price: U256, - ) -> U256 { - max( - U256::from((gas_price.as_u64() as f64 * gas_price_coefficient).ceil() as u64), - min_gas_price, - ) - } - - async fn handle_failed_get_gas_price(&self, retries_remaining: u8, error: String) -> U256 { - let mut retries = retries_remaining; - let mut last_error = error; - - while retries > 0 { - br_metrics::increase_rpc_calls(&self.get_chain_name()); - - if self.debug_mode { - let log_msg = format!( - "-[{}]-[{}] ⚠️ Warning! Error encountered during get gas price, Retries left: {:?}, Error: {}", - sub_display_format(SUB_LOG_TARGET), - self.address(), - retries - 1, - last_error - ); - log::warn!(target: &self.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &self.get_chain_name()), - sentry::Level::Warning, - ); - } - - match self.provider.get_gas_price().await { - Ok(gas_price) => return gas_price, - Err(error) => { - sleep(Duration::from_millis(DEFAULT_CALL_RETRY_INTERVAL_MS)).await; - retries -= 1; - last_error = error.to_string(); - }, - } - } - - panic!( - "[{}]-[{}]-[{}] {} [method: get_gas_price]: {}", - &self.get_chain_name(), - SUB_LOG_TARGET, - self.address(), - PROVIDER_INTERNAL_ERROR, - last_error - ); - } -} - -#[async_trait::async_trait] -impl Eip1559GasMiddleware for EthClient { - async fn get_estimated_eip1559_fees(&self) -> (U256, U256) { - match self.provider.estimate_eip1559_fees(None).await { - Ok(fees) => { - br_metrics::increase_rpc_calls(&self.get_chain_name()); - fees - }, - Err(error) => { - self.handle_failed_get_estimated_eip1559_fees( - DEFAULT_CALL_RETRIES, - error.to_string(), - ) - .await - }, - } - } - - async fn handle_failed_get_estimated_eip1559_fees( - &self, - retries_remaining: u8, - error: String, - ) -> (U256, U256) { - let mut retries = retries_remaining; - let mut last_error = error; - - while retries > 0 { - br_metrics::increase_rpc_calls(&self.get_chain_name()); - - if self.debug_mode { - let log_msg = format!( - "-[{}]-[{}] ⚠️ Warning! Error encountered during get estimated eip1559 fees, Retries left: {:?}, Error: {}", - sub_display_format(SUB_LOG_TARGET), - self.address(), - retries - 1, - last_error - ); - log::warn!(target: &self.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &self.get_chain_name()), - sentry::Level::Warning, - ); - } - - match self.provider.estimate_eip1559_fees(None).await { - Ok(fees) => return fees, - Err(error) => { - sleep(Duration::from_millis(DEFAULT_CALL_RETRY_INTERVAL_MS)).await; - retries -= 1; - last_error = error.to_string(); - }, - } - } - - panic!( - "[{}]-[{}]-[{}] {} [method: get_estimated_eip1559_fees]: {}", - &self.get_chain_name(), - SUB_LOG_TARGET, - self.address(), - PROVIDER_INTERNAL_ERROR, - last_error - ); + tx: SendableTx, + ) -> TransportResult> { + self.inner.send_transaction_internal(tx).await } } diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index c1387d5b..7e2c6ee9 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -1,17 +1,22 @@ -use std::{error::Error, str::FromStr, sync::Arc, time::Duration}; - +use std::{error::Error, sync::Arc, time::Duration}; + +use alloy::{ + consensus::Transaction as _, + primitives::{ChainId, TxHash, B256, U256}, + providers::{ext::TxPoolApi as _, fillers::TxFiller, Provider, WalletProvider}, + rpc::types::{Log, Transaction, TransactionInput, TransactionReceipt, TransactionRequest}, + signers::Signature, + sol_types::SolValue, + transports::Transport, +}; use br_primitives::{ bootstrap::BootstrapSharedData, - contracts::socket::{PollSubmit, Signatures, SocketMessage}, - eth::{BootstrapState, BuiltRelayTransaction, ChainID, GasCoefficient, RecoveredSignature}, - tx::{FlushMetadata, TxRequest, TxRequestMessage, TxRequestMetadata}, + contracts::socket::Socket_Struct::{Poll_Submit, Signatures, Socket_Message}, + eth::{BootstrapState, BuiltRelayTransaction, GasCoefficient}, + tx::{FlushMetadata, TxRequestMessage, TxRequestMetadata}, utils::sub_display_format, }; -use ethers::{ - abi::Token, - providers::JsonRpcClient, - types::{Bytes, Log, Signature, Transaction, TransactionReceipt, TxHash, H256, U256}, -}; +use eyre::Result; use sc_service::SpawnTaskHandle; use tokio::time::sleep; @@ -20,36 +25,35 @@ use super::EthClient; #[async_trait::async_trait] pub trait Handler { /// Starts the event handler and listens to every new consumed block. - async fn run(&mut self); + async fn run(&mut self) -> Result<()>; /// Decode and parse the event if the given log triggered an relay target event. - async fn process_confirmed_log(&self, log: &Log, is_bootstrap: bool); + async fn process_confirmed_log(&self, log: &Log, is_bootstrap: bool) -> Result<()>; /// Verifies whether the given transaction interacted with the target contract. fn is_target_contract(&self, log: &Log) -> bool; /// Verifies whether the given event topic matches the target event signature. - fn is_target_event(&self, topic: H256) -> bool; + fn is_target_event(&self, topic: Option<&B256>) -> bool; } #[async_trait::async_trait] /// The client to interact with the `Socket` contract instance. -pub trait SocketRelayBuilder { +pub trait SocketRelayBuilder +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Get the `EthClient` of the implemented handler. - fn get_client(&self) -> Arc>; + fn get_client(&self) -> Arc>; /// Builds the `poll()` function call data. - fn build_poll_call_data(&self, msg: SocketMessage, sigs: Signatures) -> Bytes - where - T: JsonRpcClient, - { - let poll_submit = PollSubmit { msg, sigs, option: U256::default() }; - self.get_client() - .protocol_contracts - .socket - .poll(poll_submit) - .calldata() - .unwrap() + fn build_poll_call_data(&self, msg: Socket_Message, sigs: Signatures) -> TransactionInput { + let poll_submit = Poll_Submit { msg, sigs, option: U256::default() }; + TransactionInput::new( + self.get_client().protocol_contracts.socket.poll(poll_submit).calldata().clone(), + ) } /// Builds the `poll()` transaction request. @@ -58,106 +62,81 @@ pub trait SocketRelayBuilder { /// Possibly can happen when a new chain definition hasn't been added to the operator's config. async fn build_transaction( &self, - _msg: SocketMessage, + _msg: Socket_Message, _is_inbound: bool, - _relay_tx_chain_id: ChainID, - ) -> Option { - None + _relay_tx_chain_id: ChainId, + ) -> Result> { + Ok(None) } /// Build the signatures required to request an inbound `poll()` and returns a flag which represents /// whether the relay transaction should be processed to an external chain. - async fn build_inbound_signatures(&self, _msg: SocketMessage) -> (Signatures, bool) { - (Signatures::default(), false) + async fn build_inbound_signatures(&self, _msg: Socket_Message) -> Result<(Signatures, bool)> { + Ok((Signatures::default(), false)) } /// Build the signatures required to request an outbound `poll()` and returns a flag which represents /// whether the relay transaction should be processed to an external chain. - async fn build_outbound_signatures(&self, _msg: SocketMessage) -> (Signatures, bool) { - (Signatures::default(), false) + async fn build_outbound_signatures(&self, _msg: Socket_Message) -> Result<(Signatures, bool)> { + Ok((Signatures::default(), false)) } /// Encodes the given socket message to bytes. - fn encode_socket_message(&self, msg: SocketMessage) -> Vec { - let req_id_token = Token::Tuple(vec![ - Token::FixedBytes(msg.req_id.chain.into()), - Token::Uint(msg.req_id.round_id.into()), - Token::Uint(msg.req_id.sequence.into()), - ]); - let status_token = Token::Uint(msg.status.into()); - let ins_code_token = Token::Tuple(vec![ - Token::FixedBytes(msg.ins_code.chain.into()), - Token::FixedBytes(msg.ins_code.method.into()), - ]); - let params_token = Token::Tuple(vec![ - Token::FixedBytes(msg.params.token_idx0.into()), - Token::FixedBytes(msg.params.token_idx1.into()), - Token::Address(msg.params.refund), - Token::Address(msg.params.to), - Token::Uint(msg.params.amount), - Token::Bytes(msg.params.variants.to_vec()), - ]); - - ethers::abi::encode(&[Token::Tuple(vec![ - req_id_token, - status_token, - ins_code_token, - params_token, - ])]) + fn encode_socket_message(&self, msg: Socket_Message) -> Vec { + msg.abi_encode() } /// Signs the given socket message. - fn sign_socket_message(&self, msg: SocketMessage) -> Signature { + async fn sign_socket_message(&self, msg: Socket_Message) -> Result { let encoded_msg = self.encode_socket_message(msg); - self.get_client().wallet.sign_message(&encoded_msg) + Ok(self.get_client().sign_message(&encoded_msg).await?) } /// Get the signatures of the given message. - async fn get_sorted_signatures(&self, msg: SocketMessage) -> Signatures - where - T: JsonRpcClient, - { - let raw_sigs = self - .get_client() - .contract_call( - self.get_client() - .protocol_contracts - .socket - .get_signatures(msg.clone().req_id, msg.clone().status), - "socket.get_signatures", - ) - .await; - - let raw_concated_v = &raw_sigs.v.to_string()[2..]; - - let mut recovered_sigs = vec![]; - let encoded_msg = self.encode_socket_message(msg); - for idx in 0..raw_sigs.r.len() { - let sig = Signature { - r: raw_sigs.r[idx].into(), - s: raw_sigs.s[idx].into(), - v: u64::from_str_radix(&raw_concated_v[idx * 2..idx * 2 + 2], 16).unwrap(), - }; - recovered_sigs.push(RecoveredSignature::new( - idx, - sig, - self.get_client().wallet.recover_message(sig, &encoded_msg), - )); - } - recovered_sigs.sort_by_key(|k| k.signer); - - let mut sorted_sigs = Signatures::default(); - let mut sorted_concated_v = String::from("0x"); - recovered_sigs.into_iter().for_each(|sig| { - let idx = sig.idx; - sorted_sigs.r.push(raw_sigs.r[idx]); - sorted_sigs.s.push(raw_sigs.s[idx]); - let v = Bytes::from([sig.signature.v as u8]); - sorted_concated_v.push_str(&v.to_string()[2..]); - }); - sorted_sigs.v = Bytes::from_str(&sorted_concated_v).unwrap(); - - sorted_sigs + async fn get_sorted_signatures(&self, _msg: Socket_Message) -> Signatures { + // let raw_sigs = self + // .get_client() + // .contract_call( + // self.get_client() + // .protocol_contracts + // .socket + // .get_signatures(msg.clone().req_id, msg.clone().status), + // "socket.get_signatures", + // ) + // .await; + + // let raw_concated_v = &raw_sigs.v.to_string()[2..]; + + // let mut recovered_sigs = vec![]; + // let encoded_msg = self.encode_socket_message(msg); + // for idx in 0..raw_sigs.r.len() { + // let sig = Signature { + // r: raw_sigs.r[idx].into(), + // s: raw_sigs.s[idx].into(), + // v: u64::from_str_radix(&raw_concated_v[idx * 2..idx * 2 + 2], 16).unwrap(), + // }; + // recovered_sigs.push(RecoveredSignature::new( + // idx, + // sig, + // self.get_client().wallet.recover_message(sig, &encoded_msg), + // )); + // } + // recovered_sigs.sort_by_key(|k| k.signer); + + // let mut sorted_sigs = Signatures::default(); + // let mut sorted_concated_v = String::from("0x"); + // recovered_sigs.into_iter().for_each(|sig| { + // let idx = sig.idx; + // sorted_sigs.r.push(raw_sigs.r[idx]); + // sorted_sigs.s.push(raw_sigs.s[idx]); + // let v = Bytes::from([sig.signature.v as u8]); + // sorted_concated_v.push_str(&v.to_string()[2..]); + // }); + // sorted_sigs.v = Bytes::from_str(&sorted_concated_v).unwrap(); + + // sorted_sigs + + todo!("Implement this") } } @@ -205,9 +184,11 @@ pub trait Eip1559GasMiddleware { #[async_trait::async_trait] /// The manager trait for Legacy and Eip1559 transactions. -pub trait TransactionManager +pub trait TransactionManager where - T: JsonRpcClient, + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, { /// Starts the transaction manager. Listens to every new consumed tx request message. async fn run(&mut self); @@ -216,7 +197,7 @@ where async fn initialize(&mut self); /// Get the `EthClient`. - fn get_client(&self) -> Arc>; + fn get_client(&self) -> Arc>; /// Get the transaction spawn handle. fn get_spawn_handle(&self) -> SpawnTaskHandle; @@ -228,9 +209,10 @@ where fn is_txpool_enabled(&self) -> bool; /// Flush all transaction from mempool. - async fn flush_stuck_transaction(&self) { + async fn flush_stuck_transaction(&self) -> Result<()> { if self.is_txpool_enabled() && !self.get_client().metadata.is_native { - let mempool = self.get_client().get_txpool_content().await; + // let mempool = self.get_client().get_txpool_content().await; + let mempool = self.get_client().txpool_content().await?; br_metrics::increase_rpc_calls(&self.get_client().get_chain_name()); let mut transactions = Vec::new(); @@ -253,25 +235,28 @@ where .await; } } + Ok(()) } /// Converts stuck transaction to `TxRequest(TransactionRequest | Eip1559TransactionRequest)` async fn stuck_transaction_to_transaction_request( &self, transaction: &Transaction, - ) -> TxRequest; + ) -> TransactionRequest; } #[async_trait::async_trait] -pub trait TransactionTask +pub trait TransactionTask where - T: JsonRpcClient, + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, { /// The flag whether the client has enabled txpool namespace. fn is_txpool_enabled(&self) -> bool; /// Get the `EthClient`. - fn get_client(&self) -> Arc>; + fn get_client(&self) -> Arc>; /// If first relay transaction is stuck in mempool after waiting for this amount of time(ms), /// ignore duplicate prevent logic. (default: 12s) @@ -282,20 +267,24 @@ where fn debug_mode(&self) -> bool; /// Verifies whether the relayer has sufficient funds to pay for the transaction. - async fn is_sufficient_funds(&self, gas_price: U256, gas: U256) -> bool { + async fn is_sufficient_funds(&self, gas_price: U256, gas: U256) -> Result { let fee = gas_price.saturating_mul(gas); let client = self.get_client(); - let balance = client.get_balance(client.address()).await; + let balance = client.get_balance(client.address()).await?; br_metrics::increase_rpc_calls(&client.get_chain_name()); if balance < fee { - return false; + return Ok(false); } - true + Ok(true) } /// Function that query mempool for check if the relay event that this relayer is about to send /// has already been processed by another relayer. - async fn is_duplicate_relay(&self, tx_request: &TxRequest, check_mempool: bool) -> bool { + async fn is_duplicate_relay( + &self, + tx_request: &TransactionRequest, + check_mempool: bool, + ) -> Result { let client = self.get_client(); // does not check the txpool if the following condition satisfies @@ -303,40 +292,45 @@ where // 2. the txpool check flag is false // 3. the client is Bifrost (native) if !self.is_txpool_enabled() || !check_mempool || client.metadata.is_native { - return false; + return Ok(false); } let (data, to, from) = ( - tx_request.get_data(), - tx_request.get_to().as_address().unwrap(), - tx_request.get_from(), + tx_request.input.input().clone().unwrap_or_default(), + tx_request.to.unwrap().to().unwrap().clone(), + tx_request.from.unwrap(), ); - let mempool = client.get_txpool_content().await; + let mempool = client.txpool_content().await?; br_metrics::increase_rpc_calls(&client.get_chain_name()); for (_address, tx_map) in mempool.pending.iter().chain(mempool.queued.iter()) { for (_nonce, mempool_tx) in tx_map.iter() { - if mempool_tx.to.unwrap_or_default() == *to && mempool_tx.input == *data { + if mempool_tx.to().unwrap_or_default() == to && mempool_tx.input() == data { // Trying gas escalating is not duplicate action - if mempool_tx.from == *from { - return false; + if mempool_tx.from == from { + return Ok(false); } sleep(self.duplicate_confirm_delay()).await; - return if client.get_transaction_receipt(mempool_tx.hash).await.is_some() { + return if client + .get_transaction_receipt(*mempool_tx.inner.tx_hash()) + .await? + .is_some() + { // if others relay processed br_metrics::increase_rpc_calls(&client.get_chain_name()); - true + Ok(true) } else { // if others relay stalled in mempool br_metrics::increase_rpc_calls(&client.get_chain_name()); - false + Ok(false) }; } } } - false + + Ok(false) } /// Retry send_transaction() for failed transaction execution. @@ -361,7 +355,7 @@ where ) { let client = self.get_client(); - let status = receipt.status.unwrap(); + let status = receipt.status(); log::info!( target: &client.get_chain_name(), "-[{}] 🎁 The requested transaction has been successfully mined in block: {}, {:?}-{:?}-{:?}", @@ -371,16 +365,16 @@ where status, receipt.transaction_hash ); - if status.is_zero() && self.debug_mode() { + if !status && self.debug_mode() { let log_msg = format!( - "-[{}]-[{}] ⚠️ Warning! Error encountered during contract execution [execution reverted]. A prior transaction might have been already submitted: {}, {:?}-{:?}-{:?}", - sub_display_format(sub_target), - client.address(), - metadata, - receipt.block_number.unwrap(), - status, - receipt.transaction_hash - ); + "-[{}]-[{}] ⚠️ Warning! Error encountered during contract execution [execution reverted]. A prior transaction might have been already submitted: {}, {:?}-{:?}-{:?}", + sub_display_format(sub_target), + client.address(), + metadata, + receipt.block_number.unwrap(), + status, + receipt.transaction_hash + ); log::warn!(target: &client.get_chain_name(), "{log_msg}"); sentry::capture_message( &format!("[{}]{log_msg}", &client.get_chain_name()), @@ -401,12 +395,12 @@ where let client = self.get_client(); let log_msg = format!( - "-[{}]-[{}] ♻️ The pending transaction has been stalled over 3 blocks. Try gas-escalation: {}-{}", - sub_display_format(sub_target), - client.address(), - msg.metadata, - pending - ); + "-[{}]-[{}] ♻️ The pending transaction has been stalled over 3 blocks. Try gas-escalation: {}-{}", + sub_display_format(sub_target), + client.address(), + msg.metadata, + pending + ); log::warn!(target: &client.get_chain_name(), "{log_msg}"); sentry::capture_message( &format!("[{}]{log_msg}", &client.get_chain_name()), @@ -421,12 +415,12 @@ where let client = self.get_client(); let log_msg = format!( - "-[{}]-[{}] ♻️ The requested transaction failed to generate a receipt: {}, Retries left: {:?}", - sub_display_format(sub_target), - client.address(), - msg.metadata, - msg.retries_remaining - 1 - ); + "-[{}]-[{}] ♻️ The requested transaction failed to generate a receipt: {}, Retries left: {:?}", + sub_display_format(sub_target), + client.address(), + msg.metadata, + msg.retries_remaining - 1 + ); log::error!(target: &client.get_chain_name(), "{log_msg}"); sentry::capture_message( &format!("[{}]{log_msg}", &client.get_chain_name()), @@ -446,13 +440,13 @@ where let client = self.get_client(); let log_msg = format!( - "-[{}]-[{}] ♻️ Unknown error while requesting a transaction request: {}, Retries left: {:?}, Error: {}", - sub_display_format(sub_target), - client.address(), - msg.metadata, - msg.retries_remaining - 1, - error.to_string(), - ); + "-[{}]-[{}] ♻️ Unknown error while requesting a transaction request: {}, Retries left: {:?}, Error: {}", + sub_display_format(sub_target), + client.address(), + msg.metadata, + msg.retries_remaining - 1, + error.to_string(), + ); log::error!(target: &client.get_chain_name(), "{log_msg}"); sentry::capture_message( &format!("[{}]{log_msg}", &client.get_chain_name()), @@ -473,13 +467,13 @@ where if self.debug_mode() { let log_msg = format!( - "-[{}]-[{}] ⚠️ Warning! Error encountered during gas estimation: {}, Retries left: {:?}, Error: {}", - sub_display_format(sub_target), - client.address(), - msg.metadata, - msg.retries_remaining - 1, - error.to_string() - ); + "-[{}]-[{}] ⚠️ Warning! Error encountered during gas estimation: {}, Retries left: {:?}, Error: {}", + sub_display_format(sub_target), + client.address(), + msg.metadata, + msg.retries_remaining - 1, + error.to_string() + ); log::warn!(target: &client.get_chain_name(), "{log_msg}"); sentry::capture_message( &format!("[{}]{log_msg}", &client.get_chain_name()), @@ -496,10 +490,10 @@ pub trait BootstrapHandler { fn bootstrap_shared_data(&self) -> Arc; /// Starts the bootstrap process. - async fn bootstrap(&self); + async fn bootstrap(&self) -> Result<()>; /// Fetch the historical events to bootstrap. - async fn get_bootstrap_events(&self) -> Vec; + async fn get_bootstrap_events(&self) -> Result>; /// Verifies whether the bootstrap state has been synced to the given state. async fn is_bootstrap_state_synced_as(&self, state: BootstrapState) -> bool { diff --git a/client/src/eth/tx/eip1559_manager.rs b/client/src/eth/tx/eip1559_manager.rs deleted file mode 100644 index 5a169079..00000000 --- a/client/src/eth/tx/eip1559_manager.rs +++ /dev/null @@ -1,328 +0,0 @@ -use crate::eth::{ - traits::{TransactionManager, TransactionTask}, - Eip1559GasMiddleware, EthClient, -}; - -use async_trait::async_trait; -use br_primitives::{ - constants::{ - config::ETHEREUM_BLOCK_TIME, - errors::{INSUFFICIENT_FUNDS, NETWORK_DOES_NOT_SUPPORT_EIP1559, PROVIDER_INTERNAL_ERROR}, - tx::{DEFAULT_TX_RETRIES, MAX_FEE_COEFFICIENT, MAX_PRIORITY_FEE_COEFFICIENT}, - }, - tx::{TxRequest, TxRequestMessage}, - utils::sub_display_format, -}; -use ethers::{ - middleware::{MiddlewareBuilder, NonceManagerMiddleware, SignerMiddleware}, - prelude::Transaction, - providers::{JsonRpcClient, Middleware}, - types::{ - transaction::eip2718::TypedTransaction, BlockId, BlockNumber, Bytes, - Eip1559TransactionRequest, U256, - }, -}; -use sc_service::SpawnTaskHandle; -use std::{cmp::max, sync::Arc, time::Duration}; -use tokio::{ - sync::{ - mpsc, - mpsc::{UnboundedReceiver, UnboundedSender}, - }, - time::sleep, -}; - -use super::{generate_delay, TransactionMiddleware}; - -const SUB_LOG_TARGET: &str = "eip1559-tx-manager"; - -/// The essential task that sends eip1559 transactions asynchronously. -pub struct Eip1559TransactionManager { - /// The ethereum client for the connected chain. - pub client: Arc>, - /// The client signs transaction for the connected chain with local nonce manager. - middleware: Arc>, - /// The receiver connected to the tx request channel. - receiver: UnboundedReceiver, - /// The flag whether the client has enabled txpool namespace. - is_txpool_enabled: bool, - /// The minimum priority fee required. - min_priority_fee: U256, - /// If first relay transaction is stuck in mempool after waiting for this amount of time(ms), - /// ignore duplicate prevent logic. (default: 12s) - duplicate_confirm_delay: Option, - /// A handle for spawning transaction tasks in the service. - tx_spawn_handle: SpawnTaskHandle, -} - -impl Eip1559TransactionManager { - /// Instantiates a new `Eip1559TransactionManager` instance. - pub fn new( - client: Arc>, - min_priority_fee: U256, - duplicate_confirm_delay: Option, - tx_spawn_handle: SpawnTaskHandle, - ) -> (Self, UnboundedSender) { - let (sender, receiver) = mpsc::unbounded_channel::(); - - let middleware = Arc::new( - client - .get_provider() - .wrap_into(|p| SignerMiddleware::new(p, client.wallet.signer.clone())) - .wrap_into(|p| NonceManagerMiddleware::new(p, client.address())), - ); - - ( - Self { - client, - middleware, - receiver, - is_txpool_enabled: false, - min_priority_fee, - duplicate_confirm_delay, - tx_spawn_handle, - }, - sender, - ) - } -} - -#[async_trait] -impl TransactionManager for Eip1559TransactionManager { - async fn run(&mut self) { - self.initialize().await; - - while let Some(msg) = self.receiver.recv().await { - log::info!( - target: &self.client.get_chain_name(), - "-[{}] 🔖 Received transaction request: {}", - sub_display_format(SUB_LOG_TARGET), - msg.metadata, - ); - - self.spawn_send_transaction(msg).await; - } - } - - async fn initialize(&mut self) { - self.is_txpool_enabled = self.client.provider.txpool_content().await.is_ok(); - - self.flush_stuck_transaction().await; - } - - fn get_client(&self) -> Arc> { - self.client.clone() - } - - fn get_spawn_handle(&self) -> SpawnTaskHandle { - self.tx_spawn_handle.clone() - } - - async fn spawn_send_transaction(&self, msg: TxRequestMessage) { - let task = Eip1559TransactionTask::new( - self.get_client(), - self.middleware.clone(), - self.is_txpool_enabled(), - self.min_priority_fee, - self.duplicate_confirm_delay, - ); - self.get_spawn_handle() - .spawn("send_transaction", None, async move { task.try_send_transaction(msg).await }); - } - - fn is_txpool_enabled(&self) -> bool { - self.is_txpool_enabled - } - - async fn stuck_transaction_to_transaction_request( - &self, - transaction: &Transaction, - ) -> TxRequest { - let current_fees = self.client.get_estimated_eip1559_fees().await; - - let mut request: Eip1559TransactionRequest = transaction.into(); - if let Some(prev_max_fee_per_gas) = transaction.max_fee_per_gas { - let prev_max_priority_fee_per_gas = transaction.max_priority_fee_per_gas.unwrap(); - - let new_max_fee_per_gas = max(prev_max_fee_per_gas, current_fees.0); - let new_priority_fee = max( - prev_max_priority_fee_per_gas * MAX_PRIORITY_FEE_COEFFICIENT, - max(current_fees.1, self.min_priority_fee), - ); - - request = request - .max_fee_per_gas(new_max_fee_per_gas.saturating_add(new_priority_fee)) - .max_priority_fee_per_gas(new_priority_fee); - } else { - let prev_gas_price_escalated = - U256::from((transaction.gas_price.unwrap().as_u64() as f64 * 1.125).ceil() as u64); - - if let Some(pending_block) = - self.client.get_block(BlockId::Number(BlockNumber::Pending)).await - { - let pending_base_fee = - pending_block.base_fee_per_gas.expect(NETWORK_DOES_NOT_SUPPORT_EIP1559); - if prev_gas_price_escalated > pending_base_fee + current_fees.1 { - request = request - .max_fee_per_gas(prev_gas_price_escalated) - .max_priority_fee_per_gas(prev_gas_price_escalated - pending_base_fee); - } else { - request = request - .max_fee_per_gas(current_fees.0) - .max_priority_fee_per_gas(current_fees.1); - } - } else { - panic!( - "[{}]-[{}]-[{}] {} [method: get_block]", - &self.client.get_chain_name(), - SUB_LOG_TARGET, - self.client.address(), - PROVIDER_INTERNAL_ERROR - ); - } - }; - - if self - .middleware - .estimate_gas(&TypedTransaction::Eip1559(request.clone()), None) - .await - .is_err() - { - request = request.to(self.client.address()).value(0).data(Bytes::default()); - } - - TxRequest::Eip1559(request) - } -} - -/// The transaction task for Eip1559 transactions. -pub struct Eip1559TransactionTask { - /// The ethereum client for the connected chain. - client: Arc>, - /// The client signs transaction for the connected chain with local nonce manager. - middleware: Arc>, - /// The flag whether the client has enabled txpool namespace. - is_txpool_enabled: bool, - /// The minimum priority fee required. - min_priority_fee: U256, - /// If first relay transaction is stuck in mempool after waiting for this amount of time(ms), - /// ignore duplicate prevent logic. (default: 12s) - duplicate_confirm_delay: Option, -} - -impl Eip1559TransactionTask { - /// Build an Eip1559 transaction task. - pub fn new( - client: Arc>, - middleware: Arc>, - is_txpool_enabled: bool, - min_priority_fee: U256, - duplicate_confirm_delay: Option, - ) -> Self { - Self { client, middleware, is_txpool_enabled, min_priority_fee, duplicate_confirm_delay } - } -} - -#[async_trait::async_trait] -impl TransactionTask for Eip1559TransactionTask { - fn is_txpool_enabled(&self) -> bool { - self.is_txpool_enabled - } - - fn get_client(&self) -> Arc> { - self.client.clone() - } - - fn duplicate_confirm_delay(&self) -> Duration { - Duration::from_millis(self.duplicate_confirm_delay.unwrap_or(ETHEREUM_BLOCK_TIME * 1000)) - } - - fn debug_mode(&self) -> bool { - self.client.debug_mode - } - - async fn try_send_transaction(&self, mut msg: TxRequestMessage) { - msg.tx_request = TxRequest::Eip1559(msg.tx_request.to_eip1559()); - - if msg.retries_remaining == 0 { - return; - } - - // sets a random delay on external chain transactions on first try - if msg.give_random_delay && msg.retries_remaining == DEFAULT_TX_RETRIES { - sleep(Duration::from_millis(generate_delay())).await; - } - - // set transaction `from` field - msg.tx_request.from(self.client.address()); - - // set transaction gas fee parameters - let fees = self.client.get_estimated_eip1559_fees().await; - let priority_fee = max(fees.1, self.min_priority_fee); - let max_fee_per_gas = - fees.0.saturating_add(priority_fee).saturating_mul(MAX_FEE_COEFFICIENT.into()); - msg.tx_request.max_fee_per_gas(max_fee_per_gas); - msg.tx_request.max_priority_fee_per_gas(priority_fee); - - match msg.tx_request.get_gas() { - None => { - // estimate the gas amount to be used - let estimated_gas = - match self.middleware.estimate_gas(&msg.tx_request.to_typed(), None).await { - Ok(estimated_gas) => { - br_metrics::increase_rpc_calls(&self.client.get_chain_name()); - U256::from( - (estimated_gas.as_u64() as f64 * msg.gas_coefficient.into_f64()) - .ceil() as u64, - ) - }, - Err(error) => { - return self - .handle_failed_gas_estimation(SUB_LOG_TARGET, msg, &error) - .await - }, - }; - msg.tx_request.gas(estimated_gas); - }, - Some(_) => {}, - } - - // check the txpool for transaction duplication prevention - if !self.is_duplicate_relay(&msg.tx_request, msg.check_mempool).await { - // no duplication found - if !self - .is_sufficient_funds(max_fee_per_gas, msg.tx_request.get_gas().unwrap()) - .await - { - panic!( - "[{}]-[{}]-[{}] {}", - &self.client.get_chain_name(), - SUB_LOG_TARGET, - self.client.address(), - INSUFFICIENT_FUNDS, - ); - } - - let result = self.middleware.send_transaction(msg.tx_request.to_eip1559(), None).await; - br_metrics::increase_rpc_calls(&self.client.get_chain_name()); - - match result { - Ok(pending_tx) => match pending_tx.await { - Ok(receipt) => { - if let Some(receipt) = receipt { - self.handle_success_tx_receipt(SUB_LOG_TARGET, receipt, msg.metadata); - } else { - self.handle_failed_tx_receipt(SUB_LOG_TARGET, msg).await; - } - }, - Err(error) => { - self.handle_failed_tx_request(SUB_LOG_TARGET, msg, &error).await; - }, - }, - Err(error) => { - self.handle_failed_tx_request(SUB_LOG_TARGET, msg, &error).await; - }, - } - } - } -} diff --git a/client/src/eth/tx/legacy_manager.rs b/client/src/eth/tx/legacy_manager.rs deleted file mode 100644 index 90df0b7c..00000000 --- a/client/src/eth/tx/legacy_manager.rs +++ /dev/null @@ -1,380 +0,0 @@ -use crate::eth::{ - traits::{TransactionManager, TransactionTask}, - EthClient, LegacyGasMiddleware, -}; - -use async_trait::async_trait; -use br_primitives::{ - constants::{ - cli::{DEFAULT_ESCALATE_PERCENTAGE, DEFAULT_MIN_GAS_PRICE}, - config::ETHEREUM_BLOCK_TIME, - errors::{INSUFFICIENT_FUNDS, NETWORK_DOES_NOT_SUPPORT_EIP1559, PROVIDER_INTERNAL_ERROR}, - tx::DEFAULT_TX_RETRIES, - }, - tx::{TxRequest, TxRequestMessage}, - utils::sub_display_format, -}; -use ethers::{ - middleware::{MiddlewareBuilder, NonceManagerMiddleware, SignerMiddleware}, - providers::{JsonRpcClient, Middleware}, - types::{BlockId, BlockNumber, Transaction, TransactionRequest, U256}, -}; -use sc_service::SpawnTaskHandle; -use std::{sync::Arc, time::Duration}; -use tokio::{ - sync::{ - mpsc, - mpsc::{UnboundedReceiver, UnboundedSender}, - }, - time::sleep, -}; - -use super::{generate_delay, TransactionMiddleware}; - -const SUB_LOG_TARGET: &str = "legacy-tx-manager"; - -/// The essential task that sends legacy transactions asynchronously. -pub struct LegacyTransactionManager { - /// The ethereum client for the connected chain. - pub client: Arc>, - /// The client signs transaction for the connected chain with local nonce manager. - middleware: Arc>, - /// The receiver connected to the tx request channel. - receiver: UnboundedReceiver, - /// The flag whether the client has enabled txpool namespace. - is_txpool_enabled: bool, - /// The flag whether if the gas price will be initially escalated. The `escalate_percentage` - /// will be used on escalation. This will only have effect on legacy transactions. (default: - /// false) - is_initially_escalated: bool, - /// The coefficient used on transaction gas price escalation (default: 1.15) - gas_price_coefficient: f64, - /// The minimum value use for gas_price. (default: 0) - min_gas_price: U256, - /// If first relay transaction is stuck in mempool after waiting for this amount of time(ms), - /// ignore duplicate prevent logic. (default: 12s) - duplicate_confirm_delay: Option, - /// A handle for spawning transaction tasks in the service. - tx_spawn_handle: SpawnTaskHandle, -} - -impl LegacyTransactionManager { - /// Instantiates a new `LegacyTransactionManager` instance. - pub fn new( - client: Arc>, - escalate_percentage: Option, - min_gas_price: Option, - is_initially_escalated: bool, - duplicate_confirm_delay: Option, - tx_spawn_handle: SpawnTaskHandle, - ) -> (Self, UnboundedSender) { - let (sender, receiver) = mpsc::unbounded_channel::(); - - let gas_price_coefficient = { - if let Some(escalate_percentage) = escalate_percentage { - 1.0 + (escalate_percentage / 100.0) - } else { - 1.0 + (DEFAULT_ESCALATE_PERCENTAGE / 100.0) - } - }; - - let middleware = client - .get_provider() - .wrap_into(|p| SignerMiddleware::new(p, client.wallet.signer.clone())) - .wrap_into(|p| NonceManagerMiddleware::new(p, client.address())); - - ( - Self { - client, - middleware: Arc::new(middleware), - receiver, - is_txpool_enabled: false, - is_initially_escalated, - gas_price_coefficient, - min_gas_price: U256::from(min_gas_price.unwrap_or(DEFAULT_MIN_GAS_PRICE)), - duplicate_confirm_delay, - tx_spawn_handle, - }, - sender, - ) - } -} - -#[async_trait] -impl TransactionManager for LegacyTransactionManager { - async fn run(&mut self) { - self.initialize().await; - - while let Some(msg) = self.receiver.recv().await { - log::info!( - target: &self.client.get_chain_name(), - "-[{}] 🔖 Received transaction request: {}", - sub_display_format(SUB_LOG_TARGET), - msg.metadata, - ); - - self.spawn_send_transaction(msg).await; - } - } - - async fn initialize(&mut self) { - self.is_txpool_enabled = self.client.provider.txpool_content().await.is_ok(); - - self.flush_stuck_transaction().await; - } - - fn get_client(&self) -> Arc> { - self.client.clone() - } - - fn get_spawn_handle(&self) -> SpawnTaskHandle { - self.tx_spawn_handle.clone() - } - - async fn spawn_send_transaction(&self, msg: TxRequestMessage) { - let task = LegacyTransactionTask::new( - self.get_client(), - self.middleware.clone(), - self.is_txpool_enabled(), - self.is_initially_escalated, - self.gas_price_coefficient, - self.min_gas_price, - self.duplicate_confirm_delay, - ); - self.get_spawn_handle() - .spawn("send_transaction", None, async move { task.try_send_transaction(msg).await }); - } - - fn is_txpool_enabled(&self) -> bool { - self.is_txpool_enabled - } - - async fn stuck_transaction_to_transaction_request( - &self, - transaction: &Transaction, - ) -> TxRequest { - let request: TransactionRequest = transaction.into(); - - return if let Some(gas_price) = transaction.gas_price { - TxRequest::Legacy( - request.gas_price( - self.client - .get_gas_price_for_retry( - gas_price, - self.gas_price_coefficient, - self.min_gas_price, - ) - .await, - ), - ) - } else { - let prev_priority_fee_per_gas = transaction.max_priority_fee_per_gas.unwrap(); - - if let Some(pending_block) = - self.client.get_block(BlockId::Number(BlockNumber::Pending)).await - { - let pending_base_fee = - pending_block.base_fee_per_gas.expect(NETWORK_DOES_NOT_SUPPORT_EIP1559); - - TxRequest::Legacy( - request.gas_price( - self.client - .get_gas_price_for_retry( - prev_priority_fee_per_gas + pending_base_fee, - self.gas_price_coefficient, - self.min_gas_price, - ) - .await, - ), - ) - } else { - panic!( - "[{}]-[{}]-[{}] {} [method: get_block]", - &self.client.get_chain_name(), - SUB_LOG_TARGET, - self.client.address(), - PROVIDER_INTERNAL_ERROR, - ); - } - }; - } -} - -/// The transaction task for Legacy transactions. -pub struct LegacyTransactionTask { - /// The ethereum client for the connected chain. - client: Arc>, - /// The client signs transaction for the connected chain with local nonce manager. - middleware: Arc>, - /// The flag whether the client has enabled txpool namespace. - is_txpool_enabled: bool, - /// The flag whether if the gas price will be initially escalated. The `escalate_percentage` - /// will be used on escalation. This will only have effect on legacy transactions. (default: - /// false) - is_initially_escalated: bool, - /// The coefficient used on transaction gas price escalation (default: 1.15) - gas_price_coefficient: f64, - /// The minimum value used for gas_price. (default: 0) - min_gas_price: U256, - /// If first relay transaction is stuck in mempool after waiting for this amount of time(ms), - /// ignore duplicate prevent logic. (default: 12s) - duplicate_confirm_delay: Option, -} - -impl LegacyTransactionTask { - /// Build an Legacy transaction task. - pub fn new( - client: Arc>, - middleware: Arc>, - is_txpool_enabled: bool, - is_initially_escalated: bool, - gas_price_coefficient: f64, - min_gas_price: U256, - duplicate_confirm_delay: Option, - ) -> Self { - Self { - client, - middleware, - is_txpool_enabled, - is_initially_escalated, - gas_price_coefficient, - min_gas_price, - duplicate_confirm_delay, - } - } -} - -#[async_trait::async_trait] -impl TransactionTask for LegacyTransactionTask { - fn is_txpool_enabled(&self) -> bool { - self.is_txpool_enabled - } - - fn get_client(&self) -> Arc> { - self.client.clone() - } - - fn duplicate_confirm_delay(&self) -> Duration { - Duration::from_millis(self.duplicate_confirm_delay.unwrap_or(ETHEREUM_BLOCK_TIME * 1000)) - } - - fn debug_mode(&self) -> bool { - self.client.debug_mode - } - - async fn try_send_transaction(&self, mut msg: TxRequestMessage) { - if msg.retries_remaining == 0 { - return; - } - - // sets a random delay on external chain transactions on first try - if msg.give_random_delay && msg.retries_remaining == DEFAULT_TX_RETRIES { - sleep(Duration::from_millis(generate_delay())).await; - } - - // set transaction `from` field - msg.tx_request.from(self.client.address()); - - // set transaction `gas_price` field - let mut gas_price = self.client.get_gas_price().await; - if msg.tx_request.get_gas_price().unwrap_or(U256::zero()) == U256::zero() - && self.is_initially_escalated - { - gas_price = self - .client - .get_gas_price_for_escalation( - gas_price, - self.gas_price_coefficient, - self.min_gas_price, - ) - .await; - msg.tx_request.gas_price(gas_price); - } - - match msg.tx_request.get_gas() { - None => { - // estimate the gas amount to be used - let estimated_gas = - match self.middleware.estimate_gas(&msg.tx_request.to_typed(), None).await { - Ok(estimated_gas) => { - br_metrics::increase_rpc_calls(&self.client.get_chain_name()); - U256::from( - (estimated_gas.as_u64() as f64 * msg.gas_coefficient.into_f64()) - .ceil() as u64, - ) - }, - Err(error) => { - return self - .handle_failed_gas_estimation(SUB_LOG_TARGET, msg, &error) - .await - }, - }; - msg.tx_request.gas(estimated_gas); - }, - Some(_) => {}, - } - - // check the txpool for transaction duplication prevention - if !self.is_duplicate_relay(&msg.tx_request, msg.check_mempool).await { - if !self.is_sufficient_funds(gas_price, msg.tx_request.get_gas().unwrap()).await { - panic!( - "[{}]-[{}]-[{}] {}", - &self.client.get_chain_name(), - SUB_LOG_TARGET, - self.client.address(), - INSUFFICIENT_FUNDS, - ); - } - - let result = self.middleware.send_transaction(msg.tx_request.to_legacy(), None).await; - br_metrics::increase_rpc_calls(&self.client.get_chain_name()); - - match result { - Ok(pending_tx) => { - let pending_hash = pending_tx.tx_hash(); - match pending_tx.await { - Ok(receipt) => { - if let Some(receipt) = receipt { - self.handle_success_tx_receipt( - SUB_LOG_TARGET, - receipt, - msg.metadata, - ) - } else { - // if 3 blocks passed since send_transaction, but the receipt has not come out, - if let Some(pending_tx) = - self.get_client().get_transaction(pending_hash).await - { - msg.tx_request.nonce(Some(pending_tx.nonce)); - msg.tx_request.gas_price( - self.client - .get_gas_price_for_escalation( - pending_tx.gas_price.unwrap(), - self.gas_price_coefficient, - self.min_gas_price, - ) - .await, - ); - self.handle_stalled_tx(SUB_LOG_TARGET, msg, pending_hash, true) - .await; - } else { - self.handle_stalled_tx( - SUB_LOG_TARGET, - msg, - pending_hash, - false, - ) - .await; - } - } - }, - Err(error) => { - self.handle_failed_tx_request(SUB_LOG_TARGET, msg, &error).await - }, - } - }, - Err(error) => self.handle_failed_tx_request(SUB_LOG_TARGET, msg, &error).await, - } - } - } -} diff --git a/client/src/eth/tx/mod.rs b/client/src/eth/tx/mod.rs deleted file mode 100644 index de400071..00000000 --- a/client/src/eth/tx/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -use ethers::{ - prelude::{NonceManagerMiddleware, SignerMiddleware}, - providers::Provider, - signers::LocalWallet, -}; -use rand::Rng; -use std::sync::Arc; - -mod eip1559_manager; -mod legacy_manager; - -pub use eip1559_manager::*; -pub use legacy_manager::*; - -/// The tranaction middleware type used for `TransactionManager`'s. -pub type TransactionMiddleware = - NonceManagerMiddleware>, LocalWallet>>; - -/// Generates a random delay that is ranged as 0 to 12000 milliseconds (in milliseconds). -pub fn generate_delay() -> u64 { - rand::thread_rng().gen_range(0..=12000) -} diff --git a/client/src/eth/wallet.rs b/client/src/eth/wallet.rs deleted file mode 100644 index c70f1e69..00000000 --- a/client/src/eth/wallet.rs +++ /dev/null @@ -1,79 +0,0 @@ -use br_primitives::{constants::errors::INVALID_PRIVATE_KEY, eth::ChainID}; - -use ethers::{ - prelude::k256::ecdsa::SigningKey, - signers::{LocalWallet, Signer}, - types::{Address, Signature, U256}, - utils::{hex::FromHex, keccak256}, -}; -use k256::{ - ecdsa::{SigningKey as K256SigningKey, VerifyingKey}, - elliptic_curve::sec1::ToEncodedPoint, - PublicKey as K256PublicKey, -}; -use sha3::{Digest, Keccak256}; - -type WalletResult = Result>; - -#[derive(Debug)] -/// The component that contains anEthereum private-public key pair -/// which can be used for signing messages. -pub struct WalletManager { - /// The wallet instantiated with a locally stored private key. - pub signer: ethers::signers::Wallet, - /// The ECDSA/secp256k1 signing key. - secret_key: Option, -} - -impl WalletManager { - /// Initialize `WalletManager` by the given private key. - pub fn from_private_key(private_key: &str, chain_id: ChainID) -> WalletResult { - assert_eq!(private_key.len(), 66, "{}", INVALID_PRIVATE_KEY); - assert!(private_key.starts_with("0x"), "{}", INVALID_PRIVATE_KEY); - - let wallet = private_key.parse::().expect(INVALID_PRIVATE_KEY); - - let pk_bytes = <[u8; 32]>::from_hex(private_key.to_string().trim_start_matches("0x")) - .expect(INVALID_PRIVATE_KEY); - let signing_key = K256SigningKey::from_bytes(&pk_bytes.into()).expect(INVALID_PRIVATE_KEY); - - Ok(Self { signer: wallet.with_chain_id(chain_id), secret_key: Some(signing_key) }) - } - - /// Signs the given message and returns the generated signature. - pub fn sign_message(&self, msg: &[u8]) -> Signature { - let digest = Keccak256::new_with_prefix(msg); - let (sig, recovery_id) = - self.secret_key.clone().unwrap().sign_digest_recoverable(digest).unwrap(); - - let (r, s) = sig.split_bytes(); - - Signature { - r: U256::from_big_endian(r.as_slice()), - s: U256::from_big_endian(s.as_slice()), - v: (recovery_id.to_byte() + 27).into(), - } - } - - /// Recovers the given signature and returns the signer address. - pub fn recover_message(&self, sig: Signature, msg: &[u8]) -> Address { - let r: [u8; 32] = sig.r.into(); - let s: [u8; 32] = sig.s.into(); - let v = sig.recovery_id().unwrap(); - - let rs = k256::ecdsa::Signature::from_slice([r, s].concat().as_slice()).unwrap(); - - let verify_key = - VerifyingKey::recover_from_digest(Keccak256::new_with_prefix(msg), &rs, v).unwrap(); - - let public_key = K256PublicKey::from(&verify_key).to_encoded_point(false); - let hash = keccak256(&public_key.as_bytes()[1..]); - - Address::from_slice(&hash[12..]) - } - - /// Returns the relayer address. - pub fn address(&self) -> Address { - self.signer.address() - } -} diff --git a/client/src/substrate/traits.rs b/client/src/substrate/traits.rs index 09e065be..fd7bfdaa 100644 --- a/client/src/substrate/traits.rs +++ b/client/src/substrate/traits.rs @@ -1,10 +1,13 @@ +use alloy::{ + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; use br_primitives::{ substrate::CustomConfig, tx::{XtRequestMessage, XtRequestMetadata}, utils::sub_display_format, }; -use ethers::providers::JsonRpcClient; use std::{error::Error, sync::Arc, time::Duration}; use subxt::{blocks::ExtrinsicEvents, Config, OnlineClient}; use tokio::time::sleep; @@ -12,15 +15,17 @@ use tokio::time::sleep; use crate::eth::EthClient; #[async_trait::async_trait] -pub trait ExtrinsicTask +pub trait ExtrinsicTask where - T: JsonRpcClient, + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, { /// Get the substrate client. fn get_sub_client(&self) -> Arc>; /// Get the Bifrost client. - fn get_bfc_client(&self) -> Arc>; + fn get_bfc_client(&self) -> Arc>; /// Sends the consumed unsigned transaction request to the Bifrost network. async fn try_send_unsigned_transaction(&self, msg: XtRequestMessage); diff --git a/client/src/substrate/tx/unsigned_manager.rs b/client/src/substrate/tx/unsigned_manager.rs index f8716d7e..4967f7d7 100644 --- a/client/src/substrate/tx/unsigned_manager.rs +++ b/client/src/substrate/tx/unsigned_manager.rs @@ -1,3 +1,7 @@ +use alloy::{ + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; use br_primitives::{ constants::errors::INVALID_PROVIDER_URL, substrate::CustomConfig, @@ -5,7 +9,6 @@ use br_primitives::{ utils::sub_display_format, }; -use ethers::providers::JsonRpcClient; use sc_service::SpawnTaskHandle; use std::sync::Arc; use subxt::OnlineClient; @@ -16,21 +19,31 @@ use crate::{eth::EthClient, substrate::traits::ExtrinsicTask}; const SUB_LOG_TARGET: &str = "unsigned-tx-manager"; /// The essential task that sends unsigned transactions asynchronously. -pub struct UnsignedTransactionManager { +pub struct UnsignedTransactionManager +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The substrate client. sub_client: Option>, /// The Bifrost client. - bfc_client: Arc>, + bfc_client: Arc>, /// The receiver connected to the tx request channel. receiver: UnboundedReceiver, /// A handle for spawning transaction tasks in the service. xt_spawn_handle: SpawnTaskHandle, } -impl UnsignedTransactionManager { +impl UnsignedTransactionManager +where + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, + T: Transport + Clone, +{ /// Instantiates a new `UnsignedTransactionManager`. pub fn new( - bfc_client: Arc>, + bfc_client: Arc>, xt_spawn_handle: SpawnTaskHandle, ) -> (Self, UnboundedSender) { let (sender, receiver) = mpsc::unbounded_channel::(); @@ -82,27 +95,45 @@ impl UnsignedTransactionManager { } /// The transaction task for unsigned transactions. -pub struct UnsignedTransactionTask { +pub struct UnsignedTransactionTask +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The substrate client. sub_client: Arc>, /// The Bifrost client. - bfc_client: Arc>, + bfc_client: Arc>, } -impl UnsignedTransactionTask { +impl UnsignedTransactionTask +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Build an unsigned transaction task instance. - pub fn new(sub_client: Arc>, bfc_client: Arc>) -> Self { + pub fn new( + sub_client: Arc>, + bfc_client: Arc>, + ) -> Self { Self { sub_client, bfc_client } } } #[async_trait::async_trait] -impl ExtrinsicTask for UnsignedTransactionTask { +impl ExtrinsicTask for UnsignedTransactionTask +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn get_sub_client(&self) -> Arc> { self.sub_client.clone() } - fn get_bfc_client(&self) -> Arc> { + fn get_bfc_client(&self) -> Arc> { self.bfc_client.clone() } diff --git a/metrics/Cargo.toml b/metrics/Cargo.toml index ac5971bf..855057c9 100644 --- a/metrics/Cargo.toml +++ b/metrics/Cargo.toml @@ -12,7 +12,7 @@ repository = { workspace = true } # General lazy_static = { workspace = true } sentry = { workspace = true } -ethers = { workspace = true } +alloy = { workspace = true } # Substrate prometheus-endpoint = { workspace = true } diff --git a/metrics/src/prometheus.rs b/metrics/src/prometheus.rs index 70784a68..11625ae8 100644 --- a/metrics/src/prometheus.rs +++ b/metrics/src/prometheus.rs @@ -1,6 +1,6 @@ use std::time::SystemTime; -use ethers::{types::TransactionReceipt, utils::format_units}; +use alloy::{primitives::utils::format_units, rpc::types::TransactionReceipt}; use prometheus_endpoint::{Gauge, GaugeVec, Opts, Registry, F64, U64}; lazy_static! { @@ -68,15 +68,16 @@ pub fn set_native_balance(label: &str, balance: f64) { /// Increase the payed transaction fees. pub fn set_payed_fees(label: &str, receipt: &TransactionReceipt) { - if let (Some(gas_price), Some(gas_used)) = (receipt.effective_gas_price, receipt.gas_used) { - let payed_fee = format_units(gas_price.saturating_mul(gas_used), "ether") - .unwrap() - .parse::() - .unwrap(); - PAYED_FEES - .with_label_values(&[label]) - .set(PAYED_FEES.with_label_values(&[label]).get() + payed_fee) - } + let gas_price = receipt.effective_gas_price; + let gas_used = receipt.gas_used; + + let payed_fee = format_units(gas_price.saturating_mul(gas_used), "eth") + .unwrap() + .parse::() + .unwrap(); + PAYED_FEES + .with_label_values(&[label]) + .set(PAYED_FEES.with_label_values(&[label]).get() + payed_fee) } /// Set the relayer uptime. diff --git a/periodic/Cargo.toml b/periodic/Cargo.toml index c79d7dfe..c6284048 100644 --- a/periodic/Cargo.toml +++ b/periodic/Cargo.toml @@ -15,7 +15,7 @@ sentry = { workspace = true } async-trait = { workspace = true } chrono = { workspace = true } cron = { workspace = true } -ethers = { workspace = true } +alloy = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } rand = { workspace = true } @@ -27,6 +27,8 @@ bitcoincore-rpc = { workspace = true } subxt = { workspace = true } array-bytes = { workspace = true } miniscript = { workspace = true } +eyre = { workspace = true } +byteorder = { workspace = true } # Bifrost Relayer br-cli = { path = "../client/cli", default-features = false } diff --git a/periodic/src/bitcoin_rollback_verifier.rs b/periodic/src/bitcoin_rollback_verifier.rs index 216aa7c2..83dff149 100644 --- a/periodic/src/bitcoin_rollback_verifier.rs +++ b/periodic/src/bitcoin_rollback_verifier.rs @@ -1,5 +1,14 @@ use std::{collections::BTreeMap, str::FromStr, sync::Arc, time::Duration}; +use alloy::{ + network::Ethereum, + primitives::{keccak256, Address as EvmAddress, Bytes, B256}, + providers::{ + fillers::{FillProvider, TxFiller}, + Provider, WalletProvider, + }, + transports::Transport, +}; use bitcoincore_rpc::{ bitcoin::{hashes::sha256d::Hash, Address, Amount, Psbt, Txid}, bitcoincore_rpc_json::GetRawTransactionResult, @@ -12,19 +21,18 @@ use br_primitives::{ schedule::BITCOIN_ROLLBACK_CHECK_SCHEDULE, tx::{DEFAULT_CALL_RETRIES, DEFAULT_CALL_RETRY_INTERVAL_MS}, }, - contracts::socket_queue::SocketQueueContract, + contracts::socket_queue::SocketQueueContract::{ + rollback_requestReturn, SocketQueueContractInstance, + }, substrate::{bifrost_runtime, AccountId20, EthereumSignature, RollbackPollMessage}, tx::{SubmitRollbackPollMetadata, XtRequest, XtRequestMetadata, XtRequestSender}, utils::{convert_ethers_to_ecdsa_signature, sub_display_format}, }; use cron::Schedule; -use ethers::{ - providers::{JsonRpcClient, Provider}, - types::{Bytes, H160, H256, U256}, - utils::keccak256, -}; +use eyre::Result; use serde::Deserialize; use serde_json::Value; +use subxt::utils::H256; use tokio::time::sleep; use tokio_stream::StreamExt; @@ -32,25 +40,13 @@ use crate::traits::PeriodicWorker; const SUB_LOG_TARGET: &str = "rollback-verifier"; -type EvmRollbackRequestOf = ( - Bytes, // unsigned_psbt - H160, // who - [u8; 32], // txid - U256, // vout - String, // to (=vault) - U256, // amount - Vec, // authorities - Vec, // votes - bool, // is_approved -); - #[derive(Debug)] /// (Bitcoin) Rollback request information. pub struct RollbackRequest { /// The unsigned PSBT of the rollback request (in bytes). pub unsigned_psbt: Bytes, - /// The registered user who attemps to request. - pub who: H160, + /// The registered user who attempts to request. + pub who: EvmAddress, /// The Bitcoin transaction hash that contains the output. pub txid: Txid, /// The output index. @@ -60,37 +56,43 @@ pub struct RollbackRequest { /// The output amount. pub amount: Amount, /// The current voting state of the request. - pub votes: BTreeMap, + pub votes: BTreeMap, /// The current voting result of the request. pub is_approved: bool, } -impl From for RollbackRequest { - fn from(value: EvmRollbackRequestOf) -> Self { +impl From for RollbackRequest { + fn from(value: rollback_requestReturn) -> Self { let mut votes = BTreeMap::default(); - for idx in 0..value.6.len() { - votes.insert(value.6[idx], value.7[idx]); + for idx in 0..value._6.len() { + votes.insert(value._6[idx], value._7[idx]); } - let mut txid = value.2; + let mut txid = value._2; txid.reverse(); + Self { - unsigned_psbt: value.0, - who: value.1, + unsigned_psbt: value._0, + who: value._1, txid: Txid::from_raw_hash(*Hash::from_bytes_ref(&txid)), - vout: value.3.as_usize(), - to: Address::from_str(&value.4).unwrap().assume_checked(), - amount: Amount::from_sat(value.5.as_u64()), + vout: usize::from_str(&value._3.to_string()).unwrap(), + to: Address::from_str(&value._4).unwrap().assume_checked(), + amount: Amount::from_sat(u64::from_str(&value._5.to_string()).unwrap()), votes, - is_approved: value.8, + is_approved: value._8, } } } -pub struct BitcoinRollbackVerifier { +pub struct BitcoinRollbackVerifier +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The Bitcoin client. btc_client: BtcClient, /// The Bifrost client. - bfc_client: Arc>, + bfc_client: Arc>, /// The unsigned transaction message sender. xt_request_sender: Arc, /// The periodic schedule. @@ -98,7 +100,12 @@ pub struct BitcoinRollbackVerifier { } #[async_trait::async_trait] -impl RpcApi for BitcoinRollbackVerifier { +impl RpcApi for BitcoinRollbackVerifier +where + F: TxFiller + WalletProvider, + P: Provider, + TR: Transport + Clone, +{ async fn call Deserialize<'a> + Send>( &self, cmd: &str, @@ -119,28 +126,38 @@ impl RpcApi for BitcoinRollbackVerifier { } #[async_trait::async_trait] -impl XtRequester for BitcoinRollbackVerifier { +impl XtRequester for BitcoinRollbackVerifier +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn xt_request_sender(&self) -> Arc { self.xt_request_sender.clone() } - fn bfc_client(&self) -> Arc> { + fn bfc_client(&self) -> Arc> { self.bfc_client.clone() } } #[async_trait::async_trait] -impl PeriodicWorker for BitcoinRollbackVerifier { +impl PeriodicWorker for BitcoinRollbackVerifier +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { + async fn run(&mut self) -> Result<()> { loop { self.wait_until_next_time().await; - if self.bfc_client.is_selected_relayer().await { - let pending_rollback_psbts = self.get_rollback_psbts().await; + if self.bfc_client.is_selected_relayer().await? { + let pending_rollback_psbts = self.get_rollback_psbts().await?; log::info!( target: &self.bfc_client.get_chain_name(), @@ -152,8 +169,8 @@ impl PeriodicWorker for BitcoinRollbackVerifier { let mut stream = tokio_stream::iter(pending_rollback_psbts); while let Some(raw_psbt) = stream.next().await { let psbt = Psbt::deserialize(&raw_psbt).unwrap(); - let txid = H256::from_str(&psbt.unsigned_tx.txid().to_string()).unwrap(); - let request = self.get_rollback_request(txid).await; + let txid = B256::from_str(&psbt.unsigned_tx.txid().to_string()).unwrap(); + let request = self.get_rollback_request(txid).await?; // the request must exist if request.who.is_zero() { @@ -191,7 +208,7 @@ impl PeriodicWorker for BitcoinRollbackVerifier { } // build payload - let (call, metadata) = self.build_unsigned_tx(txid, is_approved); + let (call, metadata) = self.build_unsigned_tx(txid, is_approved).await?; self.request_send_transaction( call, XtRequestMetadata::SubmitRollbackPoll(metadata), @@ -203,11 +220,16 @@ impl PeriodicWorker for BitcoinRollbackVerifier { } } -impl BitcoinRollbackVerifier { +impl BitcoinRollbackVerifier +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `BitcoinRollbackVerifier` instance. pub fn new( btc_client: BtcClient, - bfc_client: Arc>, + bfc_client: Arc>, xt_request_sender: Arc, ) -> Self { Self { @@ -248,60 +270,59 @@ impl BitcoinRollbackVerifier { } /// Build the payload for the unsigned transaction. (`submit_rollback_poll()`) - fn build_payload( + async fn build_payload( &self, - txid: H256, + txid: B256, is_approved: bool, - ) -> (RollbackPollMessage, EthereumSignature) { + ) -> Result<(RollbackPollMessage, EthereumSignature)> { let msg = RollbackPollMessage { - authority_id: AccountId20(self.bfc_client.address().0), - txid, + authority_id: AccountId20(self.bfc_client.address().0 .0), + txid: H256::from(txid.0), is_approved, }; - let signature = convert_ethers_to_ecdsa_signature(self.bfc_client.wallet.sign_message( - &[keccak256("RollbackPoll").as_slice(), txid.as_ref(), &[is_approved as u8]].concat(), - )); + let signature = convert_ethers_to_ecdsa_signature( + self.bfc_client + .sign_message( + &[keccak256("RollbackPoll").as_slice(), txid.as_ref(), &[is_approved as u8]] + .concat(), + ) + .await?, + ); - (msg, signature) + Ok((msg, signature)) } /// Build the calldata for the unsigned transaction. (`submit_rollback_poll()`) - fn build_unsigned_tx( + async fn build_unsigned_tx( &self, - txid: H256, + txid: B256, is_approved: bool, - ) -> (XtRequest, SubmitRollbackPollMetadata) { - let (msg, signature) = self.build_payload(txid, is_approved); + ) -> Result<(XtRequest, SubmitRollbackPollMetadata)> { + let (msg, signature) = self.build_payload(txid, is_approved).await?; let metadata = SubmitRollbackPollMetadata::new(txid, is_approved); - ( + Ok(( XtRequest::from( bifrost_runtime::tx().btc_socket_queue().submit_rollback_poll(msg, signature), ), metadata, - ) + )) } /// Get the pending rollback PSBT's. - async fn get_rollback_psbts(&self) -> Vec { - self.bfc_client - .contract_call(self.socket_queue().rollback_psbts(), "socket_queue.rollback_psbts") - .await + async fn get_rollback_psbts(&self) -> Result> { + Ok(self.socket_queue().rollback_psbts().call().await?._0) } /// Get the rollback information. - async fn get_rollback_request(&self, txid: H256) -> RollbackRequest { - self.bfc_client - .contract_call( - self.socket_queue().rollback_request(txid.into()), - "socket_queue.rollback_request", - ) - .await - .into() + async fn get_rollback_request(&self, txid: B256) -> Result { + Ok(self.socket_queue().rollback_request(txid).call().await?.into()) } /// Get the `BtcSocketQueue` precompile contract instance. - fn socket_queue(&self) -> &SocketQueueContract> { + fn socket_queue( + &self, + ) -> &SocketQueueContractInstance>> { self.bfc_client.protocol_contracts.socket_queue.as_ref().unwrap() } } diff --git a/periodic/src/heartbeat_sender.rs b/periodic/src/heartbeat_sender.rs index ae3ced8a..a0c98cdf 100644 --- a/periodic/src/heartbeat_sender.rs +++ b/periodic/src/heartbeat_sender.rs @@ -1,3 +1,9 @@ +use alloy::{ + primitives::ChainId, + providers::{fillers::TxFiller, Provider, WalletProvider}, + rpc::types::TransactionRequest, + transports::Transport, +}; use br_client::eth::EthClient; use br_primitives::{ constants::{ @@ -5,61 +11,54 @@ use br_primitives::{ schedule::HEARTBEAT_SCHEDULE, }, eth::GasCoefficient, - tx::{HeartbeatMetadata, TxRequest, TxRequestMessage, TxRequestMetadata, TxRequestSender}, + tx::{HeartbeatMetadata, TxRequestMessage, TxRequestMetadata}, utils::sub_display_format, }; use cron::Schedule; -use ethers::{providers::JsonRpcClient, types::TransactionRequest}; -use std::{str::FromStr, sync::Arc}; +use eyre::Result; +use std::{collections::BTreeMap, str::FromStr, sync::Arc}; use crate::traits::PeriodicWorker; const SUB_LOG_TARGET: &str = "heartbeat-sender"; /// The essential task that sending heartbeat transaction. -pub struct HeartbeatSender { +pub struct HeartbeatSender +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The time schedule that represents when to check heartbeat pulsed. schedule: Schedule, - /// The sender that sends messages to the tx request channel. - tx_request_sender: Arc, /// The `EthClient` to interact with the bifrost network. - client: Arc>, + client: Arc>, + /// The clients to interact with external chains. + clients: Arc>>>, } #[async_trait::async_trait] -impl PeriodicWorker for HeartbeatSender { +impl PeriodicWorker for HeartbeatSender +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { + async fn run(&mut self) -> Result<()> { loop { let address = self.client.address(); let relayer_manager = self.client.protocol_contracts.relayer_manager.as_ref().unwrap(); - let is_selected = self - .client - .contract_call( - relayer_manager.is_selected_relayer(address, false), - "relayer_manager.is_selected_relayer", - ) - .await; - let is_heartbeat_pulsed = self - .client - .contract_call( - relayer_manager.is_heartbeat_pulsed(address), - "relayer_manager.is_heartbeat_pulsed", - ) - .await; + let is_selected = relayer_manager.is_selected_relayer(address, false).call().await?._0; + let is_heartbeat_pulsed = relayer_manager.is_heartbeat_pulsed(address).call().await?._0; if is_selected && !is_heartbeat_pulsed { - let round_info = self - .client - .contract_call( - self.client.protocol_contracts.authority.round_info(), - "authority.round_info", - ) - .await; + let round_info = + self.client.protocol_contracts.authority.round_info().call().await?._0; self.request_send_transaction( self.build_transaction(), HeartbeatMetadata::new( @@ -74,24 +73,23 @@ impl PeriodicWorker for HeartbeatSender { } } -impl HeartbeatSender { +impl HeartbeatSender +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `HeartbeatSender` instance. - pub fn new( - tx_request_senders: Vec>, - system_clients: Vec>>, - ) -> Self { + pub fn new(clients: Arc>>>) -> Self { + let (_, client) = clients + .iter() + .find(|(_, client)| client.metadata.is_native) + .expect(INVALID_BIFROST_NATIVENESS); + Self { schedule: Schedule::from_str(HEARTBEAT_SCHEDULE).expect(INVALID_PERIODIC_SCHEDULE), - tx_request_sender: tx_request_senders - .iter() - .find(|sender| sender.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(), - client: system_clients - .iter() - .find(|client| client.metadata.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(), + client: client.clone(), + clients, } } @@ -99,8 +97,8 @@ impl HeartbeatSender { fn build_transaction(&self) -> TransactionRequest { let relayer_manager = self.client.protocol_contracts.relayer_manager.as_ref().unwrap(); TransactionRequest::default() - .to(relayer_manager.address()) - .data(relayer_manager.heartbeat().calldata().unwrap()) + .to(*relayer_manager.address()) + .input(relayer_manager.heartbeat().calldata().clone().into()) } /// Request send transaction to the target tx request channel. @@ -109,27 +107,28 @@ impl HeartbeatSender { tx_request: TransactionRequest, metadata: HeartbeatMetadata, ) { - match self.tx_request_sender.send(TxRequestMessage::new( - TxRequest::Legacy(tx_request), - TxRequestMetadata::Heartbeat(metadata.clone()), - false, - false, - GasCoefficient::Low, - false, - )) { - Ok(()) => log::info!( - target: &self.client.get_chain_name(), - "-[{}] 💓 Request Heartbeat transaction: {}", - sub_display_format(SUB_LOG_TARGET), - metadata - ), - Err(error) => log::error!( - target: &self.client.get_chain_name(), - "-[{}] ❗️ Failed to request Heartbeat transaction: {}, Error: {}", - sub_display_format(SUB_LOG_TARGET), - metadata, - error.to_string() - ), - } + // match self.tx_request_sender.send(TxRequestMessage::new( + // tx_request, + // TxRequestMetadata::Heartbeat(metadata.clone()), + // false, + // false, + // GasCoefficient::Low, + // false, + // )) { + // Ok(()) => log::info!( + // target: &self.client.get_chain_name(), + // "-[{}] 💓 Request Heartbeat transaction: {}", + // sub_display_format(SUB_LOG_TARGET), + // metadata + // ), + // Err(error) => log::error!( + // target: &self.client.get_chain_name(), + // "-[{}] ❗️ Failed to request Heartbeat transaction: {}, Error: {}", + // sub_display_format(SUB_LOG_TARGET), + // metadata, + // error.to_string() + // ), + // } + todo!() } } diff --git a/periodic/src/keypair_migrator.rs b/periodic/src/keypair_migrator.rs index e3ed322c..9e02f61e 100644 --- a/periodic/src/keypair_migrator.rs +++ b/periodic/src/keypair_migrator.rs @@ -1,5 +1,9 @@ use crate::traits::PeriodicWorker; +use alloy::{ + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; use br_client::{btc::storage::keypair::KeypairStorage, eth::EthClient}; use br_primitives::{ constants::{schedule::MIGRATION_DETECTOR_SCHEDULE, tx::DEFAULT_CALL_RETRY_INTERVAL_MS}, @@ -11,23 +15,33 @@ use br_primitives::{ }, }; use cron::Schedule; -use ethers::prelude::JsonRpcClient; +use eyre::Result; use std::{str::FromStr, sync::Arc, time::Duration}; use subxt::OnlineClient; use tokio::sync::RwLock; -pub struct KeypairMigrator { +pub struct KeypairMigrator +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ sub_client: Option>, - bfc_client: Arc>, + bfc_client: Arc>, migration_sequence: Arc>, keypair_storage: Arc>, schedule: Schedule, } -impl KeypairMigrator { +impl KeypairMigrator +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `KeypairMigrator` instance. pub fn new( - bfc_client: Arc>, + bfc_client: Arc>, migration_sequence: Arc>, keypair_storage: Arc>, ) -> Self { @@ -128,12 +142,17 @@ impl KeypairMigrator { } #[async_trait::async_trait] -impl PeriodicWorker for KeypairMigrator { +impl PeriodicWorker for KeypairMigrator +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { + async fn run(&mut self) -> Result<()> { self.initialize().await; loop { diff --git a/periodic/src/price_feeder.rs b/periodic/src/price_feeder.rs index bf2cf704..45edc9d5 100644 --- a/periodic/src/price_feeder.rs +++ b/periodic/src/price_feeder.rs @@ -1,13 +1,15 @@ use std::{collections::BTreeMap, fmt::Error, str::FromStr, sync::Arc}; +use alloy::{ + primitives::{utils::parse_ether, ChainId, FixedBytes, B256, U256}, + providers::{fillers::TxFiller, Provider, WalletProvider}, + rpc::types::{TransactionInput, TransactionRequest}, + transports::Transport, +}; use async_trait::async_trait; use chrono::{DateTime, Utc}; use cron::Schedule; -use ethers::{ - providers::JsonRpcClient, - types::{TransactionRequest, H256, U256}, - utils::parse_ether, -}; +use eyre::Result; use rand::Rng; use tokio::time::sleep; @@ -20,7 +22,7 @@ use br_primitives::{ contracts::socket::get_asset_oids, eth::GasCoefficient, periodic::{PriceResponse, PriceSource}, - tx::{PriceFeedMetadata, TxRequest, TxRequestMessage, TxRequestMetadata, TxRequestSender}, + tx::{PriceFeedMetadata, TxRequestMessage, TxRequestMetadata, TxRequestSender}, utils::sub_display_format, }; @@ -32,37 +34,45 @@ use crate::{ const SUB_LOG_TARGET: &str = "price-feeder"; /// The essential task that handles oracle price feedings. -pub struct OraclePriceFeeder { +pub struct OraclePriceFeeder +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The `EthClient` to interact with the bifrost network. - pub client: Arc>, + pub client: Arc>, /// The time schedule that represents when to send price feeds. schedule: Schedule, /// The primary source for fetching prices. (Coingecko) - primary_source: Vec>, + primary_source: Vec>, /// The secondary source for fetching prices. (aggregated from external sources) - secondary_sources: Vec>, - /// The sender that sends messages to the tx request channel. - tx_request_sender: Arc, + secondary_sources: Vec>, /// The pre-defined oracle ID's for each asset. - asset_oid: BTreeMap<&'static str, H256>, + asset_oid: BTreeMap<&'static str, B256>, /// The vector that contains each `EthClient`. - system_clients: Vec>>, + clients: Arc>>>, } #[async_trait] -impl PeriodicWorker for OraclePriceFeeder { +impl PeriodicWorker for OraclePriceFeeder +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { + async fn run(&mut self) -> Result<()> { self.initialize_fetchers().await; loop { let upcoming = self.schedule.upcoming(Utc).next().unwrap(); self.feed_period_spreader(upcoming, true).await; - if self.client.is_selected_relayer().await { + if self.client.is_selected_relayer().await? { if self.primary_source.is_empty() { log::warn!( target: &self.client.get_chain_name(), @@ -80,29 +90,26 @@ impl PeriodicWorker for OraclePriceFeeder { } } -impl OraclePriceFeeder { +impl OraclePriceFeeder +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ pub fn new( - tx_request_senders: Vec>, - system_clients: Vec>>, + bifrost_chain_id: &ChainId, + clients: Arc>>>, ) -> Self { let asset_oid = get_asset_oids(); + let client = clients.get(bifrost_chain_id).expect(INVALID_BIFROST_NATIVENESS).clone(); Self { schedule: Schedule::from_str(PRICE_FEEDER_SCHEDULE).expect(INVALID_PERIODIC_SCHEDULE), primary_source: vec![], secondary_sources: vec![], - tx_request_sender: tx_request_senders - .iter() - .find(|sender| sender.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(), asset_oid, - client: system_clients - .iter() - .find(|client| client.metadata.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(), - system_clients, + client, + clients, } } @@ -194,13 +201,13 @@ impl OraclePriceFeeder { } if !volume_weighted.contains_key("USDC") { - volume_weighted.insert("USDC".into(), (parse_ether(1).unwrap(), U256::from(1))); + volume_weighted.insert("USDC".into(), (parse_ether("1").unwrap(), U256::from(1))); } if !volume_weighted.contains_key("USDT") { - volume_weighted.insert("USDT".into(), (parse_ether(1).unwrap(), U256::from(1))); + volume_weighted.insert("USDT".into(), (parse_ether("1").unwrap(), U256::from(1))); } if !volume_weighted.contains_key("DAI") { - volume_weighted.insert("DAI".into(), (parse_ether(1).unwrap(), U256::from(1))); + volume_weighted.insert("DAI".into(), (parse_ether("1").unwrap(), U256::from(1))); } Ok(volume_weighted @@ -234,7 +241,8 @@ impl OraclePriceFeeder { self.secondary_sources.push(fetcher); } } - for client in &self.system_clients { + + for (_, client) in self.clients.iter() { if client.aggregator_contracts.chainlink_usdc_usd.is_some() || client.aggregator_contracts.chainlink_usdt_usd.is_some() { @@ -249,11 +257,11 @@ impl OraclePriceFeeder { /// Build and send transaction. async fn build_and_send_transaction(&self, price_responses: BTreeMap) { - let mut oid_bytes_list: Vec<[u8; 32]> = vec![]; - let mut price_bytes_list: Vec<[u8; 32]> = vec![]; + let mut oid_bytes_list: Vec> = vec![]; + let mut price_bytes_list: Vec> = vec![]; price_responses.iter().for_each(|(symbol, price_response)| { - oid_bytes_list.push(self.asset_oid.get(symbol.as_str()).unwrap().to_fixed_bytes()); + oid_bytes_list.push(self.asset_oid.get(symbol.as_str()).unwrap().clone()); price_bytes_list.push(price_response.price.into()); }); @@ -267,19 +275,20 @@ impl OraclePriceFeeder { /// Build price feed transaction. async fn build_transaction( &self, - oid_bytes_list: Vec<[u8; 32]>, - price_bytes_list: Vec<[u8; 32]>, + oid_bytes_list: Vec>, + price_bytes_list: Vec>, ) -> TransactionRequest { + let input = self + .client + .protocol_contracts + .socket + .oracle_aggregate_feeding(oid_bytes_list, price_bytes_list) + .calldata() + .clone(); + TransactionRequest::default() - .to(self.client.protocol_contracts.socket.address()) - .data( - self.client - .protocol_contracts - .socket - .oracle_aggregate_feeding(oid_bytes_list, price_bytes_list) - .calldata() - .unwrap(), - ) + .to(*self.client.protocol_contracts.socket.address()) + .input(TransactionInput::new(input)) } /// Request send transaction to the target tx request channel. @@ -288,36 +297,37 @@ impl OraclePriceFeeder { tx_request: TransactionRequest, metadata: PriceFeedMetadata, ) { - match self.tx_request_sender.send(TxRequestMessage::new( - TxRequest::Legacy(tx_request), - TxRequestMetadata::PriceFeed(metadata.clone()), - false, - false, - GasCoefficient::Mid, - false, - )) { - Ok(()) => log::info!( - target: &self.client.get_chain_name(), - "-[{}] 💵 Request price feed transaction to chain({:?}): {}", - sub_display_format(SUB_LOG_TARGET), - self.client.get_chain_id(), - metadata - ), - Err(error) => { - let log_msg = format!( - "-[{}]-[{}] ❗️ Failed to request price feed transaction to chain({:?}): {}, Error: {}", - sub_display_format(SUB_LOG_TARGET), - self.client.address(), - self.client.get_chain_id(), - metadata, - error.to_string() - ); - log::error!(target: &self.client.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &self.client.get_chain_name()), - sentry::Level::Error, - ); - }, - } + // match self.tx_request_sender.send(TxRequestMessage::new( + // tx_request, + // TxRequestMetadata::PriceFeed(metadata.clone()), + // false, + // false, + // GasCoefficient::Mid, + // false, + // )) { + // Ok(()) => log::info!( + // target: &self.client.get_chain_name(), + // "-[{}] 💵 Request price feed transaction to chain({:?}): {}", + // sub_display_format(SUB_LOG_TARGET), + // self.client.chain_id(), + // metadata + // ), + // Err(error) => { + // let log_msg = format!( + // "-[{}]-[{}] ❗️ Failed to request price feed transaction to chain({:?}): {}, Error: {}", + // sub_display_format(SUB_LOG_TARGET), + // self.client.address(), + // self.client.chain_id(), + // metadata, + // error.to_string() + // ); + // log::error!(target: &self.client.get_chain_name(), "{log_msg}"); + // sentry::capture_message( + // &format!("[{}]{log_msg}", &self.client.get_chain_name()), + // sentry::Level::Error, + // ); + // }, + // } + todo!() } } diff --git a/periodic/src/price_source/binance.rs b/periodic/src/price_source/binance.rs index 7f278df1..a59dcdfa 100644 --- a/periodic/src/price_source/binance.rs +++ b/periodic/src/price_source/binance.rs @@ -1,6 +1,7 @@ -use std::{collections::BTreeMap, fmt::Error, marker::PhantomData}; +use std::{collections::BTreeMap, fmt::Error}; -use ethers::{providers::JsonRpcClient, utils::parse_ether}; +use alloy::primitives::utils::parse_ether; +use eyre::Result; use reqwest::Url; use serde::Deserialize; @@ -20,15 +21,14 @@ pub struct BinanceResponse { } #[derive(Clone)] -pub struct BinancePriceFetcher { +pub struct BinancePriceFetcher { base_url: Url, symbols: String, - _phantom: PhantomData, } #[async_trait::async_trait] -impl PriceFetcher for BinancePriceFetcher { - async fn get_ticker_with_symbol(&self, symbol: String) -> Result { +impl PriceFetcher for BinancePriceFetcher { + async fn get_ticker_with_symbol(&self, symbol: String) -> Result { let mut url = self.base_url.join("ticker/24hr").unwrap(); url.query_pairs_mut().append_pair("symbol", (symbol + "USDT").as_str()); @@ -40,44 +40,40 @@ impl PriceFetcher for BinancePriceFetcher { }) } - async fn get_tickers(&self) -> Result, Error> { + async fn get_tickers(&self) -> Result> { let mut url = self.base_url.join("ticker/24hr").unwrap(); url.query_pairs_mut().append_pair("symbols", self.symbols.as_str()); + let response = reqwest::get(url).await?.json::>().await?; + let mut ret = BTreeMap::new(); - match reqwest::get(url).await { - Ok(response) => match response.json::>().await { - Ok(binance_response) => binance_response.iter().for_each(|ticker| { - if ticker.symbol == "BTC" { - // BTC ticker from binance is BTCB ticker - ret.insert( - "BTCB".into(), - PriceResponse { - price: parse_ether(&ticker.lastPrice).unwrap(), - volume: parse_ether(&ticker.volume).unwrap().into(), - }, - ); - } else { - ret.insert( - ticker.symbol.clone().replace("USDT", ""), - PriceResponse { - price: parse_ether(&ticker.lastPrice).unwrap(), - volume: parse_ether(&ticker.volume).unwrap().into(), - }, - ); - } - }), - Err(_) => return Err(Error), - }, - Err(_) => return Err(Error), - }; + response.iter().for_each(|ticker| { + if ticker.symbol == "BTC" { + // BTC ticker from binance is BTCB ticker + ret.insert( + "BTCB".into(), + PriceResponse { + price: parse_ether(&ticker.lastPrice).unwrap(), + volume: parse_ether(&ticker.volume).unwrap().into(), + }, + ); + } else { + ret.insert( + ticker.symbol.clone().replace("USDT", ""), + PriceResponse { + price: parse_ether(&ticker.lastPrice).unwrap(), + volume: parse_ether(&ticker.volume).unwrap().into(), + }, + ); + } + }); Ok(ret) } } -impl BinancePriceFetcher { - pub async fn new() -> Result, Error> { +impl BinancePriceFetcher { + pub async fn new() -> Result { let mut symbols: Vec = vec!["ETH".into(), "BNB".into(), "MATIC".into(), "BTC".into()]; symbols.iter_mut().for_each(|symbol| symbol.push_str("USDT")); @@ -85,7 +81,6 @@ impl BinancePriceFetcher { Ok(Self { base_url: Url::parse("https://api.binance.com/api/v3/").unwrap(), symbols: serde_json::to_string(&symbols).unwrap(), - _phantom: PhantomData, }) } @@ -96,13 +91,12 @@ impl BinancePriceFetcher { #[cfg(test)] mod tests { - use ethers::providers::Http; use super::*; #[tokio::test] async fn fetch_price() { - let binance_fetcher: BinancePriceFetcher = BinancePriceFetcher::new().await.unwrap(); + let binance_fetcher: BinancePriceFetcher = BinancePriceFetcher::new().await.unwrap(); let res = binance_fetcher.get_ticker_with_symbol("BTC".to_string()).await; println!("{:?}", res); @@ -110,7 +104,7 @@ mod tests { #[tokio::test] async fn fetch_prices() { - let binance_fetcher: BinancePriceFetcher = BinancePriceFetcher::new().await.unwrap(); + let binance_fetcher: BinancePriceFetcher = BinancePriceFetcher::new().await.unwrap(); let res = binance_fetcher.get_tickers().await; println!("{:#?}", res); diff --git a/periodic/src/price_source/chainlink.rs b/periodic/src/price_source/chainlink.rs index 1b2a3f7b..da83caf6 100644 --- a/periodic/src/price_source/chainlink.rs +++ b/periodic/src/price_source/chainlink.rs @@ -1,22 +1,35 @@ -use std::{collections::BTreeMap, fmt::Error, sync::Arc}; - -use br_primitives::periodic::PriceResponse; -use ethers::{providers::JsonRpcClient, types::U256}; - -use br_client::eth::EthClient; +use std::{collections::BTreeMap, sync::Arc}; use crate::traits::PriceFetcher; +use alloy::{ + primitives::U256, + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; +use br_client::eth::EthClient; +use br_primitives::periodic::PriceResponse; +use eyre::{eyre, Result}; #[derive(Clone)] -pub struct ChainlinkPriceFetcher { - client: Option>>, +pub struct ChainlinkPriceFetcher +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + client: Option>>, } #[async_trait::async_trait] -impl PriceFetcher for ChainlinkPriceFetcher { +impl PriceFetcher for ChainlinkPriceFetcher +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Get the price of a symbol from Chainlink aggregator. /// Available symbols: USDC, USDT, DAI, BTC, WBTC, CBBTC - async fn get_ticker_with_symbol(&self, symbol: String) -> Result { + async fn get_ticker_with_symbol(&self, symbol: String) -> Result { if let Some(client) = &self.client { let symbol_str = symbol.as_str(); @@ -29,31 +42,30 @@ impl PriceFetcher for ChainlinkPriceFetcher { "BTC" => &client.aggregator_contracts.chainlink_btc_usd, "WBTC" => &client.aggregator_contracts.chainlink_wbtc_usd, "CBBTC" => &client.aggregator_contracts.chainlink_cbbtc_usd, - _ => todo!(), + _ => return Err(eyre!("Invalid symbol")), } { - let (_, price, _, _, _) = contract.latest_round_data().await.unwrap(); - let decimals = contract.decimals().await.unwrap(); + let (_, price, _, _, _) = contract.latestRoundData().call().await?.into(); + let price = price.into_raw(); + let decimals = contract.decimals().call().await?._0; + + let price = price * U256::from(10u128.pow((18 - decimals).into())); + let volume = Some(U256::from(1)); - Ok(PriceResponse { - price: U256::from( - (price * 10u128.pow((18 - decimals).into())).as_u128(), - ), - volume: U256::from(1).into(), - }) + Ok(PriceResponse { price, volume }) } else { - Err(Error) + Err(eyre!("Invalid symbol")) } }, _ => { - todo!() + return Err(eyre!("Invalid symbol")); }, } } else { - return Err(Error); + return Err(eyre!("Client not found")); } } - async fn get_tickers(&self) -> Result, Error> { + async fn get_tickers(&self) -> Result> { let mut ret = BTreeMap::new(); for symbol in ["USDC".to_string(), "USDT".to_string(), "DAI".to_string(), "BTC".to_string()] @@ -64,12 +76,17 @@ impl PriceFetcher for ChainlinkPriceFetcher { }; } - return if ret.is_empty() { Err(Error) } else { Ok(ret) }; + return if ret.is_empty() { Err(eyre!("No tickers found")) } else { Ok(ret) }; } } -impl ChainlinkPriceFetcher { - pub async fn new(client: Option>>) -> Self { +impl ChainlinkPriceFetcher +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + pub async fn new(client: Option>>) -> Self { ChainlinkPriceFetcher { client } } } diff --git a/periodic/src/price_source/coingecko.rs b/periodic/src/price_source/coingecko.rs index 091f0a7b..06484955 100644 --- a/periodic/src/price_source/coingecko.rs +++ b/periodic/src/price_source/coingecko.rs @@ -1,6 +1,7 @@ -use std::{collections::BTreeMap, fmt::Error, marker::PhantomData}; +use std::{collections::BTreeMap, fmt::Error}; -use ethers::{providers::JsonRpcClient, utils::parse_ether}; +use alloy::primitives::utils::parse_ether; +use eyre::Result; use reqwest::{Response, Url}; use serde::Deserialize; use tokio::time::{sleep, Duration}; @@ -18,16 +19,15 @@ pub struct SupportedCoin { } #[derive(Clone)] -pub struct CoingeckoPriceFetcher { +pub struct CoingeckoPriceFetcher { pub base_url: Url, pub ids: Vec, pub supported_coins: Vec, - _phantom: PhantomData, } #[async_trait::async_trait] -impl PriceFetcher for CoingeckoPriceFetcher { - async fn get_ticker_with_symbol(&self, symbol: String) -> Result { +impl PriceFetcher for CoingeckoPriceFetcher { + async fn get_ticker_with_symbol(&self, symbol: String) -> Result { let id = self.get_id_from_symbol(&symbol); let url = self .base_url @@ -36,47 +36,44 @@ impl PriceFetcher for CoingeckoPriceFetcher { let price = *self ._send_request(url) - .await - .unwrap() + .await? .get(id) .expect("Cannot find symbol in response") .get("usd") .expect("Cannot find usd price in response"); - Ok(PriceResponse { price: parse_ether(price).unwrap(), volume: None }) + Ok(PriceResponse { price: parse_ether(&price.to_string())?, volume: None }) } - async fn get_tickers(&self) -> Result, Error> { + async fn get_tickers(&self) -> Result> { let url = self .base_url .join(&format!("simple/price?ids={}&vs_currencies=usd", self.ids.join(","))) .unwrap(); - return match self._send_request(url).await { - Ok(response) => { - let mut ret = BTreeMap::new(); - self.ids.iter().for_each(|id| { - let price = response.get(id).unwrap().get("usd").unwrap(); - let symbol = self - .supported_coins - .iter() - .find(|coin| coin.id == *id) - .unwrap() - .symbol - .to_uppercase(); - ret.insert( - symbol, - PriceResponse { price: parse_ether(price).unwrap(), volume: None }, - ); - }); - Ok(ret) - }, - Err(e) => Err(e), - }; + let response = self._send_request(url).await?; + + let mut ret = BTreeMap::new(); + self.ids.iter().for_each(|id| { + let price = response.get(id).unwrap().get("usd").unwrap(); + let symbol = self + .supported_coins + .iter() + .find(|coin| coin.id == *id) + .unwrap() + .symbol + .to_uppercase(); + ret.insert( + symbol, + PriceResponse { price: parse_ether(&price.to_string()).unwrap(), volume: None }, + ); + }); + + Ok(ret) } } -impl CoingeckoPriceFetcher { +impl CoingeckoPriceFetcher { pub async fn new() -> Result { let ids: Vec = vec![ "ethereum".into(), @@ -103,7 +100,6 @@ impl CoingeckoPriceFetcher { base_url: Url::parse("https://api.coingecko.com/api/v3/").unwrap(), ids, supported_coins: support_coin_list, - _phantom: PhantomData, }) } diff --git a/periodic/src/price_source/gateio.rs b/periodic/src/price_source/gateio.rs index 004d68d9..da22fb36 100644 --- a/periodic/src/price_source/gateio.rs +++ b/periodic/src/price_source/gateio.rs @@ -1,6 +1,7 @@ -use std::{collections::BTreeMap, fmt::Error, marker::PhantomData}; +use std::{collections::BTreeMap, fmt::Error}; -use ethers::{providers::JsonRpcClient, utils::parse_ether}; +use alloy::primitives::utils::parse_ether; +use eyre::Result; use reqwest::Url; use serde::Deserialize; @@ -19,15 +20,14 @@ pub struct GateioResponse { } #[derive(Clone)] -pub struct GateioPriceFetcher { +pub struct GateioPriceFetcher { base_url: Url, symbols: Vec, - _phantom: PhantomData, } #[async_trait::async_trait] -impl PriceFetcher for GateioPriceFetcher { - async fn get_ticker_with_symbol(&self, symbol: String) -> Result { +impl PriceFetcher for GateioPriceFetcher { + async fn get_ticker_with_symbol(&self, symbol: String) -> Result { let mut url = self.base_url.join("spot/tickers").unwrap(); url.query_pairs_mut() .append_pair("currency_pair", (symbol.clone() + "_USDT").as_str()); @@ -40,29 +40,27 @@ impl PriceFetcher for GateioPriceFetcher { }) } - async fn get_tickers(&self) -> Result, Error> { - return match self._send_request(self.base_url.join("spot/tickers").unwrap()).await { - Ok(response) => { - let mut ret = BTreeMap::new(); - response.iter().for_each(|ticker| { - if self.symbols.contains(&ticker.currency_pair) { - ret.insert( - ticker.currency_pair.replace("BIFIF_USDT", "BIFI").replace("_USDT", ""), - PriceResponse { - price: parse_ether(&ticker.last).unwrap(), - volume: parse_ether(&ticker.base_volume).unwrap().into(), - }, - ); - } - }); - Ok(ret) - }, - Err(_) => Err(Error), - }; + async fn get_tickers(&self) -> Result> { + let response = self._send_request(self.base_url.join("spot/tickers")?).await?; + + let mut ret = BTreeMap::new(); + response.iter().for_each(|ticker| { + if self.symbols.contains(&ticker.currency_pair) { + ret.insert( + ticker.currency_pair.replace("BIFIF_USDT", "BIFI").replace("_USDT", ""), + PriceResponse { + price: parse_ether(&ticker.last).unwrap(), + volume: parse_ether(&ticker.base_volume).unwrap().into(), + }, + ); + } + }); + + Ok(ret) } } -impl GateioPriceFetcher { +impl GateioPriceFetcher { pub async fn new() -> Result { let mut symbols: Vec = vec!["ETH".into(), "BFC".into(), "BNB".into(), "MATIC".into(), "BIFI".into()]; @@ -79,7 +77,6 @@ impl GateioPriceFetcher { base_url: Url::parse("https://api.gateio.ws/api/v4/") .expect("Failed to parse GateIo URL"), symbols, - _phantom: PhantomData, }) } @@ -96,13 +93,11 @@ impl GateioPriceFetcher { #[cfg(test)] mod tests { - use ethers::providers::Http; - use super::*; #[tokio::test] async fn fetch_price() { - let gateio_fetcher: GateioPriceFetcher = GateioPriceFetcher::new().await.unwrap(); + let gateio_fetcher: GateioPriceFetcher = GateioPriceFetcher::new().await.unwrap(); let res = gateio_fetcher.get_ticker_with_symbol("BTC".to_string()).await; println!("{:?}", res); @@ -110,7 +105,7 @@ mod tests { #[tokio::test] async fn fetch_prices() { - let gateio_fetcher: GateioPriceFetcher = GateioPriceFetcher::new().await.unwrap(); + let gateio_fetcher: GateioPriceFetcher = GateioPriceFetcher::new().await.unwrap(); let res = gateio_fetcher.get_tickers().await; println!("{:#?}", res); diff --git a/periodic/src/price_source/kucoin.rs b/periodic/src/price_source/kucoin.rs index 3c759c9f..c39628a7 100644 --- a/periodic/src/price_source/kucoin.rs +++ b/periodic/src/price_source/kucoin.rs @@ -1,7 +1,8 @@ -use std::{collections::BTreeMap, fmt::Error, marker::PhantomData}; +use std::{collections::BTreeMap, fmt::Error}; +use alloy::primitives::utils::parse_ether; use br_primitives::periodic::PriceResponse; -use ethers::{providers::JsonRpcClient, utils::parse_ether}; +use eyre::Result; use reqwest::Url; use serde::Deserialize; @@ -21,27 +22,23 @@ struct KucoinResponse { } #[derive(Clone)] -pub struct KucoinPriceFetcher { +pub struct KucoinPriceFetcher { base_url: Url, symbols: Vec, - _phantom: PhantomData, } #[async_trait::async_trait] -impl PriceFetcher for KucoinPriceFetcher { - async fn get_ticker_with_symbol(&self, symbol: String) -> Result { - let mut url = self.base_url.join("market/stats").unwrap(); +impl PriceFetcher for KucoinPriceFetcher { + async fn get_ticker_with_symbol(&self, symbol: String) -> Result { + let mut url = self.base_url.join("market/stats")?; url.query_pairs_mut().append_pair("symbol", (symbol.clone() + "-USDT").as_str()); - let res = &self._send_request(url).await.unwrap().data; + let res = &self._send_request(url).await?.data; - Ok(PriceResponse { - price: parse_ether(&res.last).unwrap(), - volume: parse_ether(&res.vol).unwrap().into(), - }) + Ok(PriceResponse { price: parse_ether(&res.last)?, volume: parse_ether(&res.vol)?.into() }) } - async fn get_tickers(&self) -> Result, Error> { + async fn get_tickers(&self) -> Result> { let mut ret = BTreeMap::new(); for symbol in &self.symbols { ret.insert(symbol.clone(), self.get_ticker_with_symbol(symbol.clone()).await?); @@ -51,7 +48,7 @@ impl PriceFetcher for KucoinPriceFetcher { } } -impl KucoinPriceFetcher { +impl KucoinPriceFetcher { pub async fn new() -> Result { let symbols: Vec = vec!["ETH".into(), "BFC".into(), "BNB".into(), "MATIC".into(), "BIFI".into()]; @@ -60,7 +57,6 @@ impl KucoinPriceFetcher { base_url: Url::parse("https://api.kucoin.com/api/v1/") .expect("Failed to parse KuCoin URL"), symbols, - _phantom: PhantomData, }) } @@ -77,13 +73,11 @@ impl KucoinPriceFetcher { #[cfg(test)] mod tests { - use ethers::providers::Http; - use super::*; #[tokio::test] async fn fetch_price() { - let kucoin_fetcher: KucoinPriceFetcher = KucoinPriceFetcher::new().await.unwrap(); + let kucoin_fetcher: KucoinPriceFetcher = KucoinPriceFetcher::new().await.unwrap(); let res = kucoin_fetcher.get_ticker_with_symbol("BFC".to_string()).await; println!("{:#?}", res); @@ -91,7 +85,7 @@ mod tests { #[tokio::test] async fn fetch_prices() { - let kucoin_fetcher: KucoinPriceFetcher = KucoinPriceFetcher::new().await.unwrap(); + let kucoin_fetcher: KucoinPriceFetcher = KucoinPriceFetcher::new().await.unwrap(); let res = kucoin_fetcher.get_tickers().await; println!("{:#?}", res); diff --git a/periodic/src/price_source/mod.rs b/periodic/src/price_source/mod.rs index bb34ca3f..3e8a7ff8 100644 --- a/periodic/src/price_source/mod.rs +++ b/periodic/src/price_source/mod.rs @@ -1,7 +1,12 @@ use std::{collections::BTreeMap, fmt::Error, ops::Mul, sync::Arc}; +use alloy::{ + primitives::U256, + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; use async_trait::async_trait; -use ethers::{providers::JsonRpcClient, types::U256}; +use eyre::Result; use serde::Deserialize; use br_client::eth::EthClient; @@ -26,13 +31,18 @@ mod upbit; const LOG_TARGET: &str = "price-fetcher"; #[derive(Clone)] -pub enum PriceFetchers { - Binance(BinancePriceFetcher), - Chainlink(ChainlinkPriceFetcher), - CoinGecko(CoingeckoPriceFetcher), - Gateio(GateioPriceFetcher), - Kucoin(KucoinPriceFetcher), - Upbit(UpbitPriceFetcher), +pub enum PriceFetchers +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + Binance(BinancePriceFetcher), + Chainlink(ChainlinkPriceFetcher), + CoinGecko(CoingeckoPriceFetcher), + Gateio(GateioPriceFetcher), + Kucoin(KucoinPriceFetcher), + Upbit(UpbitPriceFetcher), } #[derive(Deserialize)] @@ -73,11 +83,16 @@ pub async fn krw_to_usd(krw_amount: U256) -> Result { } } -impl PriceFetchers { +impl PriceFetchers +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ pub async fn new( exchange: PriceSource, - client: Option>>, - ) -> Result { + client: Option>>, + ) -> Result { match exchange { PriceSource::Binance => Ok(PriceFetchers::Binance(BinancePriceFetcher::new().await?)), PriceSource::Chainlink => { @@ -94,8 +109,13 @@ impl PriceFetchers { } #[async_trait] -impl PriceFetcher for PriceFetchers { - async fn get_ticker_with_symbol(&self, symbol: String) -> Result { +impl PriceFetcher for PriceFetchers +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + async fn get_ticker_with_symbol(&self, symbol: String) -> Result { match self { PriceFetchers::Binance(fetcher) => fetcher.get_ticker_with_symbol(symbol).await, PriceFetchers::Chainlink(fetcher) => fetcher.get_ticker_with_symbol(symbol).await, @@ -106,7 +126,7 @@ impl PriceFetcher for PriceFetchers { } } - async fn get_tickers(&self) -> Result, Error> { + async fn get_tickers(&self) -> Result> { match self { PriceFetchers::Binance(fetcher) => fetcher.get_tickers().await, PriceFetchers::Chainlink(fetcher) => fetcher.get_tickers().await, @@ -120,28 +140,13 @@ impl PriceFetcher for PriceFetchers { #[cfg(test)] mod tests { - use ethers::{providers::Http, utils::parse_ether}; + use alloy::primitives::utils::parse_ether; use super::*; - #[tokio::test] - async fn fetcher_enum_matching() { - let fetchers: Vec> = vec![ - PriceFetchers::new(PriceSource::Binance, None).await.unwrap(), - PriceFetchers::new(PriceSource::Coingecko, None).await.unwrap(), - PriceFetchers::new(PriceSource::Gateio, None).await.unwrap(), - PriceFetchers::new(PriceSource::Kucoin, None).await.unwrap(), - PriceFetchers::new(PriceSource::Upbit, None).await.unwrap(), - ]; - - for fetcher in fetchers { - println!("{:?}", fetcher.get_tickers().await); - } - } - #[tokio::test] async fn krw_to_usd_exchange() { - let res = krw_to_usd(parse_ether(1).unwrap()).await; + let res = krw_to_usd(parse_ether("1").unwrap()).await; println!("{:?}", res); } } diff --git a/periodic/src/price_source/upbit.rs b/periodic/src/price_source/upbit.rs index 2e7ded97..66f730a1 100644 --- a/periodic/src/price_source/upbit.rs +++ b/periodic/src/price_source/upbit.rs @@ -1,6 +1,7 @@ -use std::{collections::BTreeMap, fmt::Error, marker::PhantomData}; +use std::{collections::BTreeMap, fmt::Error}; -use ethers::{providers::JsonRpcClient, types::U256, utils::parse_ether}; +use alloy::primitives::{utils::parse_ether, U256}; +use eyre::Result; use reqwest::Url; use serde::Deserialize; @@ -19,52 +20,43 @@ pub struct UpbitResponse { } #[derive(Clone)] -pub struct UpbitPriceFetcher { +pub struct UpbitPriceFetcher { base_url: Url, symbols: String, - _phantom: PhantomData, } #[async_trait::async_trait] -impl PriceFetcher for UpbitPriceFetcher { - async fn get_ticker_with_symbol(&self, symbol: String) -> Result { - let mut url = self.base_url.join("ticker").unwrap(); +impl PriceFetcher for UpbitPriceFetcher { + async fn get_ticker_with_symbol(&self, symbol: String) -> Result { + let mut url = self.base_url.join("ticker")?; if symbol.contains("BFC") { url.query_pairs_mut().append_pair("markets", format!("BTC-{}", symbol).as_str()); } else { url.query_pairs_mut().append_pair("markets", format!("KRW-{}", symbol).as_str()); } - Ok(self - .format_response(self._send_request(url).await.unwrap()[0].clone()) - .await - .unwrap() - .1) + Ok(self.format_response(self._send_request(url).await?[0].clone()).await?.1) } - async fn get_tickers(&self) -> Result, Error> { - let mut url = self.base_url.join("ticker").unwrap(); + async fn get_tickers(&self) -> Result> { + let mut url = self.base_url.join("ticker")?; url.query_pairs_mut().append_pair("markets", self.symbols.as_str()); - return match self._send_request(url).await { - Ok(responses) => { - let mut ret = BTreeMap::new(); - for response in responses { - match self.format_response(response).await { - Ok(formatted_response) => { - ret.insert(formatted_response.0, formatted_response.1); - }, - Err(_) => continue, - } - } - Ok(ret) - }, - Err(_) => Err(Error), - }; + let responses = self._send_request(url).await?; + let mut ret = BTreeMap::new(); + for response in responses { + match self.format_response(response).await { + Ok(formatted_response) => { + ret.insert(formatted_response.0, formatted_response.1); + }, + Err(_) => continue, + } + } + Ok(ret) } } -impl UpbitPriceFetcher { +impl UpbitPriceFetcher { pub async fn new() -> Result { let symbols: Vec = vec!["ETH".into(), "BFC".into(), "MATIC".into()]; @@ -82,7 +74,6 @@ impl UpbitPriceFetcher { Ok(Self { base_url: Url::parse("https://api.upbit.com/v1/").unwrap(), symbols: formatted_symbols.join(","), - _phantom: PhantomData, }) } @@ -91,12 +82,15 @@ impl UpbitPriceFetcher { response: UpbitResponse, ) -> Result<(String, PriceResponse), Error> { if response.market.contains("KRW-") { - match krw_to_usd(parse_ether(response.trade_price).unwrap()).await { + let amount = parse_ether(&response.trade_price.to_string()).unwrap(); + match krw_to_usd(amount).await { Ok(usd_price) => Ok(( response.market.replace("KRW-", ""), PriceResponse { price: usd_price, - volume: parse_ether(response.acc_trade_volume_24h).unwrap().into(), + volume: parse_ether(&response.acc_trade_volume_24h.to_string()) + .unwrap() + .into(), }, )), Err(_) => Err(Error), @@ -108,7 +102,9 @@ impl UpbitPriceFetcher { response.market.replace("BTC-", ""), PriceResponse { price: usd_price, - volume: parse_ether(response.acc_trade_volume_24h).unwrap().into(), + volume: parse_ether(&response.acc_trade_volume_24h.to_string()) + .unwrap() + .into(), }, )), Err(_) => Err(Error), @@ -120,36 +116,24 @@ impl UpbitPriceFetcher { } } - async fn btc_to_krw(&self, btc_amount: f64) -> Result { - match self._send_request(self.base_url.join("ticker?markets=KRW-BTC").unwrap()).await { - Ok(response) => { - let btc_price = response[0].trade_price; - Ok(parse_ether(btc_price * btc_amount).unwrap()) - }, - Err(_) => Err(Error), - } + async fn btc_to_krw(&self, btc_amount: f64) -> Result { + let response = self._send_request(self.base_url.join("ticker?markets=KRW-BTC")?).await?; + let btc_price = response[0].trade_price; + Ok(parse_ether(&(btc_price * btc_amount).to_string())?) } - async fn _send_request(&self, url: Url) -> Result, Error> { - match reqwest::get(url).await { - Ok(response) => match response.json::>().await { - Ok(response) => Ok(response), - Err(_) => Err(Error), - }, - Err(_) => Err(Error), - } + async fn _send_request(&self, url: Url) -> Result> { + Ok(reqwest::get(url).await?.json::>().await?) } } #[cfg(test)] mod tests { - use ethers::providers::Http; - use super::*; #[tokio::test] async fn fetch_price() { - let upbit_fetcher: UpbitPriceFetcher = UpbitPriceFetcher::new().await.unwrap(); + let upbit_fetcher: UpbitPriceFetcher = UpbitPriceFetcher::new().await.unwrap(); let res = upbit_fetcher.get_ticker_with_symbol("BFC".to_string()).await; println!("{:?}", res); @@ -157,7 +141,7 @@ mod tests { #[tokio::test] async fn fetch_prices() { - let upbit_fetcher: UpbitPriceFetcher = UpbitPriceFetcher::new().await.unwrap(); + let upbit_fetcher: UpbitPriceFetcher = UpbitPriceFetcher::new().await.unwrap(); let res = upbit_fetcher.get_tickers().await; println!("{:#?}", res); @@ -165,7 +149,7 @@ mod tests { #[tokio::test] async fn btc_krw_conversion() { - let upbit_fetcher: UpbitPriceFetcher = UpbitPriceFetcher::new().await.unwrap(); + let upbit_fetcher: UpbitPriceFetcher = UpbitPriceFetcher::new().await.unwrap(); let res = upbit_fetcher.btc_to_krw(0.00000175f64).await; println!("{:?}", res); diff --git a/periodic/src/psbt_signer.rs b/periodic/src/psbt_signer.rs index 450abcbc..ccff7ca2 100644 --- a/periodic/src/psbt_signer.rs +++ b/periodic/src/psbt_signer.rs @@ -1,4 +1,9 @@ use crate::traits::PeriodicWorker; +use alloy::{ + primitives::{keccak256, Bytes, B256}, + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; use br_client::{ btc::{handlers::XtRequester, storage::keypair::KeypairStorage}, eth::EthClient, @@ -12,10 +17,7 @@ use br_primitives::{ utils::{convert_ethers_to_ecdsa_signature, hash_bytes, sub_display_format}, }; use cron::Schedule; -use ethers::{ - prelude::{Bytes, JsonRpcClient, H256}, - utils::keccak256, -}; +use eyre::Result; use miniscript::bitcoin::{Address as BtcAddress, Network, Psbt}; use std::{str::FromStr, sync::Arc}; use tokio::sync::RwLock; @@ -24,9 +26,14 @@ use tokio_stream::StreamExt; const SUB_LOG_TARGET: &str = "psbt-signer"; /// The essential task that submits signed PSBT's. -pub struct PsbtSigner { +pub struct PsbtSigner +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The Bifrost client. - client: Arc>, + client: Arc>, /// The unsigned transaction message sender. xt_request_sender: Arc, /// The public and private keypair local storage. @@ -39,10 +46,15 @@ pub struct PsbtSigner { schedule: Schedule, } -impl PsbtSigner { +impl PsbtSigner +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `PsbtSigner` instance. pub fn new( - client: Arc>, + client: Arc>, xt_request_sender: Arc, keypair_storage: Arc>, migration_sequence: Arc>, @@ -59,36 +71,32 @@ impl PsbtSigner { } /// Get the pending unsigned PSBT's (in bytes) - async fn get_unsigned_psbts(&self) -> Vec { + async fn get_unsigned_psbts(&self) -> Result> { let socket_queue = self.client.protocol_contracts.socket_queue.as_ref().unwrap(); - - self.client - .contract_call(socket_queue.unsigned_psbts(), "socket_queue.unsigned_psbts") - .await + let res = socket_queue.unsigned_psbts().call().await?._0; + Ok(res) } /// Verify whether the current relayer is an executive. - async fn is_relay_executive(&self) -> bool { + async fn is_relay_executive(&self) -> Result { let relay_exec = self.client.protocol_contracts.relay_executive.as_ref().unwrap(); - - self.client - .contract_call(relay_exec.is_member(self.client.address()), "relay_executive.is_member") - .await + Ok(relay_exec.is_member(self.client.address()).call().await?._0) } /// Build the payload for the unsigned transaction. (`submit_signed_psbt()`) async fn build_payload( &self, unsigned_psbt: &mut Psbt, - ) -> Option<(SignedPsbtMessage, EthereumSignature)> { + ) -> Result, EthereumSignature)>> { match *self.migration_sequence.read().await { MigrationSequence::Normal => {}, MigrationSequence::SetExecutiveMembers | MigrationSequence::PrepareNextSystemVault => { - return None; + return Ok(None); }, MigrationSequence::UTXOTransfer => { // Ensure that the unsigned transaction is for the system vault. - let system_vault = self.get_system_vault(self.get_current_round().await + 1).await; + let system_vault = + self.get_system_vault(self.get_current_round().await? + 1).await?; if !unsigned_psbt.unsigned_tx.output.iter().all(|x| { BtcAddress::from_script(x.script_pubkey.as_script(), self.btc_network).unwrap() == system_vault @@ -104,7 +112,7 @@ impl PsbtSigner { sentry::Level::Warning, ); - return None; + return Ok(None); } }, }; @@ -115,23 +123,27 @@ impl PsbtSigner { if self .is_signed_psbt_submitted( - H256::from_str(&psbt.unsigned_tx.txid().to_string()).unwrap(), + B256::from_str(&psbt.unsigned_tx.txid().to_string()).unwrap(), signed_psbt.clone(), ) - .await + .await? { - return None; + return Ok(None); } let msg = SignedPsbtMessage { - authority_id: AccountId20(self.client.address().0), + authority_id: AccountId20(self.client.address().0 .0), unsigned_psbt: unsigned_psbt.serialize(), signed_psbt: signed_psbt.clone(), }; - let signature = convert_ethers_to_ecdsa_signature(self.client.wallet.sign_message( - &[keccak256("SignedPsbt").as_slice(), signed_psbt.as_ref()].concat(), - )); - return Some((msg, signature)); + let signature = convert_ethers_to_ecdsa_signature( + self.client + .sign_message( + &[keccak256("SignedPsbt").as_slice(), signed_psbt.as_ref()].concat(), + ) + .await?, + ); + return Ok(Some((msg, signature))); } log::warn!( target: &self.client.get_chain_name(), @@ -139,86 +151,88 @@ impl PsbtSigner { sub_display_format(SUB_LOG_TARGET), hash_bytes(&psbt.serialize()) ); - None + Ok(None) } /// Build the calldata for the unsigned transaction. (`submit_signed_psbt()`) async fn build_unsigned_tx( &self, unsigned_psbt: &mut Psbt, - ) -> Option<(XtRequest, SubmitSignedPsbtMetadata)> { - if let Some((msg, signature)) = self.build_payload(unsigned_psbt).await { + ) -> Result> { + if let Some((msg, signature)) = self.build_payload(unsigned_psbt).await? { let metadata = SubmitSignedPsbtMetadata::new(hash_bytes(&msg.unsigned_psbt)); - return Some(( + return Ok(Some(( XtRequest::from( bifrost_runtime::tx().btc_socket_queue().submit_signed_psbt(msg, signature), ), metadata, - )); + ))); } - None + Ok(None) } /// Get the system vault address. - async fn get_system_vault(&self, round: u32) -> BtcAddress { + async fn get_system_vault(&self, round: u32) -> Result { let registration_pool = self.client.protocol_contracts.registration_pool.as_ref().unwrap(); - let system_vault = self - .client - .contract_call( - registration_pool.vault_address(registration_pool.address(), round), - "registration_pool.vault_address", - ) - .await; - - BtcAddress::from_str(&system_vault).unwrap().assume_checked() + let system_vault = registration_pool + .vault_address(*registration_pool.address(), round) + .call() + .await? + ._0; + + Ok(BtcAddress::from_str(&system_vault).unwrap().assume_checked()) } /// Get the current round number. - async fn get_current_round(&self) -> u32 { + async fn get_current_round(&self) -> Result { let registration_pool = self.client.protocol_contracts.registration_pool.as_ref().unwrap(); - self.client - .contract_call(registration_pool.current_round(), "registration_pool.current_round") - .await + Ok(registration_pool.current_round().call().await?._0) } - async fn is_signed_psbt_submitted(&self, txid: H256, psbt: Vec) -> bool { + async fn is_signed_psbt_submitted(&self, txid: B256, psbt: Vec) -> Result { let socket_queue = self.client.protocol_contracts.socket_queue.as_ref().unwrap(); - self.client - .contract_call( - socket_queue.is_signed_psbt_submitted( - txid.into(), - psbt.into(), - self.client.address(), - ), - "socket_queue.is_signed_psbt_submitted", - ) - .await + let res = socket_queue + .is_signed_psbt_submitted(txid, Bytes::from(psbt), self.client.address()) + .call() + .await? + ._0; + Ok(res) } } #[async_trait::async_trait] -impl XtRequester for PsbtSigner { +impl XtRequester for PsbtSigner +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn xt_request_sender(&self) -> Arc { self.xt_request_sender.clone() } - fn bfc_client(&self) -> Arc> { + fn bfc_client(&self) -> Arc> { self.client.clone() } } #[async_trait::async_trait] -impl PeriodicWorker for PsbtSigner { +impl PeriodicWorker for PsbtSigner +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { + async fn run(&mut self) -> Result<()> { loop { self.wait_until_next_time().await; - if self.is_relay_executive().await { - let unsigned_psbts = self.get_unsigned_psbts().await; + if self.is_relay_executive().await? { + let unsigned_psbts = self.get_unsigned_psbts().await?; log::info!( target: &self.client.get_chain_name(), "-[{}] 🔐 {} unsigned PSBT exists.", @@ -231,7 +245,7 @@ impl PeriodicWorker for PsbtSigner { // Build the unsigned transaction. if let Some((call, metadata)) = self .build_unsigned_tx(&mut Psbt::deserialize(&unsigned_psbt).unwrap()) - .await + .await? { // Send the unsigned transaction. self.request_send_transaction( diff --git a/periodic/src/pub_key_presubmitter.rs b/periodic/src/pub_key_presubmitter.rs index 4bf9418c..85f295c4 100644 --- a/periodic/src/pub_key_presubmitter.rs +++ b/periodic/src/pub_key_presubmitter.rs @@ -1,4 +1,8 @@ use crate::traits::PeriodicWorker; +use alloy::{ + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; use bitcoincore_rpc::bitcoin::PublicKey; use br_client::{btc::storage::keypair::KeypairStorage, eth::EthClient}; use br_primitives::{ @@ -19,15 +23,20 @@ use br_primitives::{ utils::{convert_ethers_to_ecdsa_signature, sub_display_format}, }; use cron::Schedule; -use ethers::prelude::JsonRpcClient; +use eyre::Result; use std::{str::FromStr, sync::Arc, time::Duration}; use subxt::{storage::Storage, OnlineClient}; use tokio::sync::RwLock; const SUB_LOG_TARGET: &str = "pubkey-presubmitter"; -pub struct PubKeyPreSubmitter { - bfc_client: Arc>, +pub struct PubKeyPreSubmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + bfc_client: Arc>, /// The Bifrost client. sub_client: Option>, /// The unsigned transaction message sender. @@ -41,18 +50,23 @@ pub struct PubKeyPreSubmitter { } #[async_trait::async_trait] -impl PeriodicWorker for PubKeyPreSubmitter { +impl PeriodicWorker for PubKeyPreSubmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { + async fn run(&mut self) -> Result<()> { self.initialize().await; loop { self.wait_until_next_time().await; - if self.is_relay_executive().await { + if self.is_relay_executive().await? { if *self.migration_sequence.read().await != ServiceState::Normal { continue; } @@ -67,7 +81,7 @@ impl PeriodicWorker for PubKeyPreSubmitter { ); let (call, metadata) = - self.build_unsigned_tx(self.create_pub_keys(n).await).await; + self.build_unsigned_tx(self.create_pub_keys(n).await).await?; self.request_send_transaction(call, metadata); } } @@ -75,10 +89,15 @@ impl PeriodicWorker for PubKeyPreSubmitter { } } -impl PubKeyPreSubmitter { +impl PubKeyPreSubmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `PubKeyPreSubmitter` instance. pub fn new( - bfc_client: Arc>, + bfc_client: Arc>, xt_request_sender: Arc, keypair_storage: Arc>, migration_sequence: Arc>, @@ -111,24 +130,24 @@ impl PubKeyPreSubmitter { async fn build_unsigned_tx( &self, pub_keys: Vec, - ) -> (XtRequest, VaultKeyPresubmissionMetadata) { - let (msg, signature) = self.build_payload(&pub_keys).await; + ) -> Result<(XtRequest, VaultKeyPresubmissionMetadata)> { + let (msg, signature) = self.build_payload(&pub_keys).await?; let metadata = VaultKeyPresubmissionMetadata { keys: pub_keys.len() }; - ( + Ok(( XtRequest::from( bifrost_runtime::tx() .btc_registration_pool() .vault_key_presubmission(msg, signature), ), metadata, - ) + )) } async fn build_payload( &self, pub_keys: &Vec, - ) -> (VaultKeyPreSubmission, EthereumSignature) { + ) -> Result<(VaultKeyPreSubmission, EthereumSignature)> { let converted_pub_keys = pub_keys .iter() .map(|k| { @@ -140,26 +159,28 @@ impl PubKeyPreSubmitter { let pool_round = self.get_current_round().await; let msg = VaultKeyPreSubmission { - authority_id: AccountId20(self.bfc_client.address().0), + authority_id: AccountId20(self.bfc_client.address().0 .0), pub_keys: converted_pub_keys.iter().map(|x| Public(*x)).collect(), pool_round, }; let signature = convert_ethers_to_ecdsa_signature( - self.bfc_client.wallet.sign_message( - &format!( - "{}:{}", - pool_round, - converted_pub_keys - .iter() - .map(|x| array_bytes::bytes2hex("0x", *x)) - .collect::>() - .concat() + self.bfc_client + .sign_message( + &format!( + "{}:{}", + pool_round, + converted_pub_keys + .iter() + .map(|x| array_bytes::bytes2hex("0x", *x)) + .collect::>() + .concat() + ) + .as_bytes(), ) - .as_bytes(), - ), + .await?, ); - (msg, signature) + Ok((msg, signature)) } /// Send the transaction request message to the channel. @@ -203,15 +224,9 @@ impl PubKeyPreSubmitter { } /// Verify whether the current relayer is an executive. - async fn is_relay_executive(&self) -> bool { + async fn is_relay_executive(&self) -> Result { let relay_exec = self.bfc_client.protocol_contracts.relay_executive.as_ref().unwrap(); - - self.bfc_client - .contract_call( - relay_exec.is_member(self.bfc_client.address()), - "relay_executive.is_member", - ) - .await + Ok(relay_exec.is_member(self.bfc_client.address()).call().await?._0) } async fn get_latest_storage(&self) -> Storage> { @@ -237,7 +252,7 @@ impl PubKeyPreSubmitter { match storage .fetch(&bifrost_runtime::storage().btc_registration_pool().pre_submitted_pub_keys( self.get_current_round().await, - AccountId20(self.bfc_client.address().0), + AccountId20(self.bfc_client.address().0 .0), )) .await { diff --git a/periodic/src/pub_key_submitter.rs b/periodic/src/pub_key_submitter.rs index 022630f8..9af50cee 100644 --- a/periodic/src/pub_key_submitter.rs +++ b/periodic/src/pub_key_submitter.rs @@ -1,10 +1,19 @@ use std::{str::FromStr, sync::Arc, time::Duration}; +use alloy::{ + network::Ethereum, + primitives::{Address, Bytes}, + providers::{ + fillers::{FillProvider, TxFiller}, + Provider, WalletProvider, + }, + transports::Transport, +}; use bitcoincore_rpc::bitcoin::PublicKey; use br_client::{btc::storage::keypair::KeypairStorage, eth::EthClient}; use br_primitives::{ constants::{errors::INVALID_PERIODIC_SCHEDULE, schedule::PUB_KEY_SUBMITTER_SCHEDULE}, - contracts::registration_pool::RegistrationPoolContract, + contracts::registration_pool::RegistrationPoolContract::RegistrationPoolContractInstance, substrate::{ bifrost_runtime::{ self, btc_registration_pool::storage::types::service_state::ServiceState, @@ -15,10 +24,7 @@ use br_primitives::{ utils::{convert_ethers_to_ecdsa_signature, sub_display_format}, }; use cron::Schedule; -use ethers::{ - providers::{JsonRpcClient, Provider}, - types::{Address, Bytes}, -}; +use eyre::Result; use tokio::{sync::RwLock, time::sleep}; use tokio_stream::StreamExt; @@ -26,9 +32,14 @@ use crate::traits::PeriodicWorker; const SUB_LOG_TARGET: &str = "pubkey-submitter"; -pub struct PubKeySubmitter { +pub struct PubKeySubmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The Bifrost client. - client: Arc>, + client: Arc>, /// The unsigned transaction message sender. xt_request_sender: Arc, /// The public and private keypair local storage. @@ -40,26 +51,31 @@ pub struct PubKeySubmitter { } #[async_trait::async_trait] -impl PeriodicWorker for PubKeySubmitter { +impl PeriodicWorker for PubKeySubmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { + async fn run(&mut self) -> Result<()> { loop { self.wait_until_next_time().await; - if self.is_relay_executive().await { + if self.is_relay_executive().await? { let target_round = if *self.migration_sequence.read().await == ServiceState::Normal { - self.get_current_round().await + self.get_current_round().await? } else { // wait for 9 seconds to ensure the migration sequence is updated. (at least finalization time) sleep(Duration::from_secs(9)).await; - self.get_current_round().await.saturating_add(1) + self.get_current_round().await?.saturating_add(1) }; - let pending_registrations = self.get_pending_registrations(target_round).await; + let pending_registrations = self.get_pending_registrations(target_round).await?; log::info!( target: &self.client.get_chain_name(), @@ -77,7 +93,7 @@ impl PeriodicWorker for PubKeySubmitter { continue; } - let registration_info = self.get_registration_info(who, target_round).await; + let registration_info = self.get_registration_info(who, target_round).await?; // user doesn't exist in the pool. if registration_info.0 != who { @@ -93,7 +109,7 @@ impl PeriodicWorker for PubKeySubmitter { } let pub_key = self.keypair_storage.write().await.create_new_keypair().await; - let (call, metadata) = self.build_unsigned_tx(who, pub_key).await; + let (call, metadata) = self.build_unsigned_tx(who, pub_key).await?; self.request_send_transaction(call, metadata); } } @@ -101,10 +117,15 @@ impl PeriodicWorker for PubKeySubmitter { } } -impl PubKeySubmitter { +impl PubKeySubmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `PubKeySubmitter` instance. pub fn new( - client: Arc>, + client: Arc>, xt_request_sender: Arc, keypair_storage: Arc>, migration_sequence: Arc>, @@ -125,30 +146,32 @@ impl PubKeySubmitter { &self, who: Address, pub_key: PublicKey, - ) -> (VaultKeySubmission, EthereumSignature) { + ) -> Result<(VaultKeySubmission, EthereumSignature)> { let mut converted_pub_key = [0u8; 33]; converted_pub_key.copy_from_slice(&pub_key.to_bytes()); let pool_round = if *self.migration_sequence.read().await == ServiceState::Normal { - self.get_current_round().await + self.get_current_round().await? } else { - self.get_current_round().await.saturating_add(1) + self.get_current_round().await?.saturating_add(1) }; let msg = VaultKeySubmission { - authority_id: AccountId20(self.client.address().0), - who: AccountId20(who.0), + authority_id: AccountId20(self.client.address().0 .0), + who: AccountId20(who.0 .0), pub_key: Public(converted_pub_key), pool_round, }; let signature = convert_ethers_to_ecdsa_signature( - self.client.wallet.sign_message( - &format!("{}:{}", pool_round, array_bytes::bytes2hex("0x", converted_pub_key)) - .as_bytes(), - ), + self.client + .sign_message( + &format!("{}:{}", pool_round, array_bytes::bytes2hex("0x", converted_pub_key)) + .as_bytes(), + ) + .await?, ); - (msg, signature) + Ok((msg, signature)) } /// Build the calldata for the unsigned transaction. @@ -157,25 +180,25 @@ impl PubKeySubmitter { &self, who: Address, pub_key: PublicKey, - ) -> (XtRequest, SubmitVaultKeyMetadata) { - let (msg, signature) = self.build_payload(who, pub_key).await; + ) -> Result<(XtRequest, SubmitVaultKeyMetadata)> { + let (msg, signature) = self.build_payload(who, pub_key).await?; let metadata = SubmitVaultKeyMetadata::new(who, pub_key); if self.is_system_vault(who) { - ( + Ok(( XtRequest::from( bifrost_runtime::tx() .btc_registration_pool() .submit_system_vault_key(msg, signature), ), metadata, - ) + )) } else { - ( + Ok(( XtRequest::from( bifrost_runtime::tx().btc_registration_pool().submit_vault_key(msg, signature), ), metadata, - ) + )) } } @@ -209,14 +232,8 @@ impl PubKeySubmitter { } /// Get the pending registrations. - async fn get_pending_registrations(&self, round: u32) -> Vec
{ - self.client - .contract_call( - self.registration_pool().pending_registrations(round), - "registration_pool.pending_registrations", - ) - .await - .0 + async fn get_pending_registrations(&self, round: u32) -> Result> { + Ok(self.registration_pool().pending_registrations(round).call().await?._0) } /// Get the user's registration information. @@ -224,38 +241,29 @@ impl PubKeySubmitter { &self, who: Address, round: u32, - ) -> (Address, String, String, Vec
, Vec) { - self.client - .contract_call( - self.registration_pool().registration_info(who, round), - "registration_pool.registration_info", - ) - .await + ) -> Result<(Address, String, String, Vec
, Vec)> { + Ok(self.registration_pool().registration_info(who, round).call().await?.into()) } - fn registration_pool(&self) -> &RegistrationPoolContract> { + fn registration_pool( + &self, + ) -> &RegistrationPoolContractInstance>> { self.client.protocol_contracts.registration_pool.as_ref().unwrap() } /// Verify whether the current relayer is an executive. - async fn is_relay_executive(&self) -> bool { + async fn is_relay_executive(&self) -> Result { let relay_exec = self.client.protocol_contracts.relay_executive.as_ref().unwrap(); - - self.client - .contract_call(relay_exec.is_member(self.client.address()), "relay_executive.is_member") - .await + Ok(relay_exec.is_member(self.client.address()).call().await?._0) } /// Verify whether the given address is a system vault. #[inline] fn is_system_vault(&self, who: Address) -> bool { - who == self.registration_pool().address() + who == *self.registration_pool().address() } - async fn get_current_round(&self) -> u32 { - let registration_pool = self.registration_pool(); - self.client - .contract_call(registration_pool.current_round(), "registration_pool.current_round") - .await + async fn get_current_round(&self) -> Result { + Ok(self.registration_pool().current_round().call().await?._0) } } diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index 516739b4..dd131e31 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -1,11 +1,13 @@ -use cron::Schedule; -use ethers::{ - abi::{encode, Detokenize, Token, Tokenize}, - contract::EthLogDecode, - providers::JsonRpcClient, - types::{Address, Filter, Log, TransactionRequest, U256}, +use alloy::{ + dyn_abi::DynSolValue, + primitives::{Address, ChainId, U256}, + providers::{fillers::TxFiller, Provider, WalletProvider}, + rpc::types::{Filter, Log, TransactionInput, TransactionRequest}, + sol_types::SolEvent as _, + transports::Transport, }; -use std::{str::FromStr, sync::Arc, time::Duration}; +use cron::Schedule; +use std::{collections::BTreeMap, str::FromStr, sync::Arc, time::Duration}; use tokio::time::sleep; use br_client::eth::{traits::BootstrapHandler, EthClient}; @@ -14,29 +16,35 @@ use br_primitives::{ constants::{ cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE, - errors::{INVALID_BIFROST_NATIVENESS, INVALID_CONTRACT_ABI, INVALID_PERIODIC_SCHEDULE}, + errors::{INVALID_BIFROST_NATIVENESS, INVALID_PERIODIC_SCHEDULE}, schedule::ROUNDUP_EMITTER_SCHEDULE, }, - contracts::{ - authority::RoundMetaData, - socket::{RoundUpSubmit, SerializedRoundUp, Signatures, SocketContractEvents}, + contracts::socket::{ + SocketContract::RoundUp, + Socket_Struct::{Round_Up_Submit, Signatures}, }, eth::{BootstrapState, GasCoefficient, RoundUpEventStatus}, - tx::{TxRequest, TxRequestMessage, TxRequestMetadata, TxRequestSender, VSPPhase1Metadata}, + tx::{TxRequestMessage, TxRequestMetadata, TxRequestSender, VSPPhase1Metadata}, utils::sub_display_format, }; +use eyre::Result; use crate::traits::PeriodicWorker; const SUB_LOG_TARGET: &str = "roundup-emitter"; -pub struct RoundupEmitter { +pub struct RoundupEmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Current round number current_round: U256, /// The ethereum client for the Bifrost network. - client: Arc>, - /// The sender that sends messages to the tx request channel. - tx_request_sender: Arc, + client: Arc>, + /// The clients for the external chains. + clients: Arc>>>, /// The time schedule that represents when to check round info. schedule: Schedule, /// The bootstrap shared data. @@ -44,17 +52,22 @@ pub struct RoundupEmitter { } #[async_trait::async_trait] -impl PeriodicWorker for RoundupEmitter { +impl PeriodicWorker for RoundupEmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { - self.current_round = self.get_latest_round().await; + async fn run(&mut self) -> Result<()> { + self.current_round = self.get_latest_round().await?; loop { if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapRoundUpPhase1).await { - self.bootstrap().await; + self.bootstrap().await?; break; } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { break; @@ -66,7 +79,7 @@ impl PeriodicWorker for RoundupEmitter { loop { self.wait_until_next_time().await; - let latest_round = self.get_latest_round().await; + let latest_round = self.get_latest_round().await?; if self.current_round < latest_round { log::info!( @@ -77,13 +90,13 @@ impl PeriodicWorker for RoundupEmitter { latest_round, ); - if !self.is_selected_relayer(latest_round).await { + if !self.is_selected_relayer(latest_round).await? { continue; } - let new_relayers = self.fetch_validator_list(latest_round).await; + let new_relayers = self.fetch_validator_list(latest_round).await?; self.request_send_transaction( - self.build_transaction(latest_round, new_relayers.clone()), + self.build_transaction(latest_round, new_relayers.clone()).await?, VSPPhase1Metadata::new(latest_round, new_relayers), ); @@ -93,89 +106,85 @@ impl PeriodicWorker for RoundupEmitter { } } -impl RoundupEmitter { +impl RoundupEmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `RoundupEmitter` instance. pub fn new( - tx_request_senders: Vec>, - clients: Vec>>, + clients: Arc>>>, bootstrap_shared_data: Arc, ) -> Self { - let client = clients + let (_, client) = clients .iter() - .find(|client| client.metadata.is_native) + .find(|(_, client)| client.metadata.is_native) .expect(INVALID_BIFROST_NATIVENESS) .clone(); Self { current_round: U256::default(), - client, - tx_request_sender: tx_request_senders - .iter() - .find(|sender| sender.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(), + client: client.clone(), + clients, schedule: Schedule::from_str(ROUNDUP_EMITTER_SCHEDULE) .expect(INVALID_PERIODIC_SCHEDULE), bootstrap_shared_data, } } - /// Decode & Serialize log to `SerializedRoundUp` struct. - fn decode_log(&self, log: Log) -> Result { - match SocketContractEvents::decode_log(&log.into()) { - Ok(roundup) => Ok(SerializedRoundUp::from_tokens(roundup.into_tokens()).unwrap()), - Err(error) => Err(error), - } + /// Decode & Serialize log to `RoundUp` struct. + fn decode_log(&self, log: Log) -> Result { + Ok(log.log_decode::()?.inner.data) } /// Check relayer has selected in previous round - async fn is_selected_relayer(&self, round: U256) -> bool { + async fn is_selected_relayer(&self, round: U256) -> Result { let relayer_manager = self.client.protocol_contracts.relayer_manager.as_ref().unwrap(); - self.client - .contract_call( - relayer_manager.is_previous_selected_relayer( - round - 1, - self.client.address(), - true, - ), - "relayer_manager.is_previous_selected_relayer", - ) - .await + Ok(relayer_manager + .is_previous_selected_relayer(round - U256::from(1), self.client.address(), true) + .call() + .await? + ._0) } /// Fetch new validator list - async fn fetch_validator_list(&self, round: U256) -> Vec
{ + async fn fetch_validator_list(&self, round: U256) -> Result> { let relayer_manager = self.client.protocol_contracts.relayer_manager.as_ref().unwrap(); - let mut addresses = self - .client - .contract_call( - relayer_manager.previous_selected_relayers(round, true), - "relayer_manager.selected_relayers", - ) - .await; + let mut addresses = + relayer_manager.previous_selected_relayers(round, true).call().await?._0; addresses.sort(); - addresses + Ok(addresses) } /// Build `VSP phase 1` transaction. - fn build_transaction(&self, round: U256, new_relayers: Vec
) -> TransactionRequest { - let encoded_msg = encode(&[ - Token::Uint(round), - Token::Array(new_relayers.iter().map(|address| Token::Address(*address)).collect()), - ]); - let sigs = Signatures::from(self.client.wallet.sign_message(&encoded_msg)); - let round_up_submit = RoundUpSubmit { round, new_relayers, sigs }; - - TransactionRequest::default() - .to(self.client.protocol_contracts.socket.address()) - .data( - self.client - .protocol_contracts - .socket - .round_control_poll(round_up_submit) - .calldata() - .unwrap(), - ) + async fn build_transaction( + &self, + round: U256, + new_relayers: Vec
, + ) -> Result { + let encoded_msg = DynSolValue::Tuple(vec![ + DynSolValue::Uint(round, 256), + DynSolValue::Array( + new_relayers.iter().map(|address| DynSolValue::Address(*address)).collect(), + ), + ]) + .abi_encode(); + + let sigs = Signatures::from(self.client.sign_message(&encoded_msg).await?); + let round_up_submit = Round_Up_Submit { round, new_relayers, sigs }; + + let input = self + .client + .protocol_contracts + .socket + .round_control_poll(round_up_submit) + .calldata() + .clone(); + + Ok(TransactionRequest::default() + .to(*self.client.protocol_contracts.socket.address()) + .input(TransactionInput::new(input))) } /// Request send transaction to the target tx request channel. @@ -184,51 +193,56 @@ impl RoundupEmitter { tx_request: TransactionRequest, metadata: VSPPhase1Metadata, ) { - match self.tx_request_sender.send(TxRequestMessage::new( - TxRequest::Legacy(tx_request), - TxRequestMetadata::VSPPhase1(metadata.clone()), - false, - false, - GasCoefficient::Mid, - false, - )) { - Ok(()) => log::info!( - target: &self.client.get_chain_name(), - "-[{}] 👤 Request VSP phase1 transaction: {}", - sub_display_format(SUB_LOG_TARGET), - metadata - ), - Err(error) => log::error!( - target: &self.client.get_chain_name(), - "-[{}] ❗️ Failed to request VSP phase1 transaction: {}, Error: {}", - sub_display_format(SUB_LOG_TARGET), - metadata, - error.to_string() - ), - } + // match self.tx_request_sender.send(TxRequestMessage::new( + // tx_request, + // TxRequestMetadata::VSPPhase1(metadata.clone()), + // false, + // false, + // GasCoefficient::Mid, + // false, + // )) { + // Ok(()) => log::info!( + // target: &self.client.get_chain_name(), + // "-[{}] 👤 Request VSP phase1 transaction: {}", + // sub_display_format(SUB_LOG_TARGET), + // metadata + // ), + // Err(error) => log::error!( + // target: &self.client.get_chain_name(), + // "-[{}] ❗️ Failed to request VSP phase1 transaction: {}, Error: {}", + // sub_display_format(SUB_LOG_TARGET), + // metadata, + // error.to_string() + // ), + // } + + todo!() } /// Get the latest round index. - async fn get_latest_round(&self) -> U256 { - self.client - .contract_call( - self.client.protocol_contracts.authority.latest_round(), - "authority.latest_round", - ) - .await + async fn get_latest_round(&self) -> Result { + Ok(self.client.protocol_contracts.authority.latest_round().call().await?._0) } } #[async_trait::async_trait] -impl BootstrapHandler for RoundupEmitter { +impl BootstrapHandler for RoundupEmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn bootstrap_shared_data(&self) -> Arc { self.bootstrap_shared_data.clone() } - async fn bootstrap(&self) { + async fn bootstrap(&self) -> Result<()> { let get_next_poll_round = || async move { - let logs = self.get_bootstrap_events().await; - let round_up_events: Vec = + let logs = self.get_bootstrap_events().await.unwrap(); + // let round_up_events = + // logs.iter().map(|log| self.decode_log(log.clone()).unwrap()).collect(); + + let round_up_events: Vec = logs.iter().map(|log| self.decode_log(log.clone()).unwrap()).collect(); let max_round = round_up_events @@ -236,7 +250,7 @@ impl BootstrapHandler for RoundupEmitter { .map(|round_up_event| round_up_event.roundup.round) .max() .unwrap(); - let max_events: Vec<&SerializedRoundUp> = round_up_events + let max_events: Vec<&RoundUp> = round_up_events .iter() .filter(|round_up_event| round_up_event.roundup.round == max_round) .collect(); @@ -247,22 +261,22 @@ impl BootstrapHandler for RoundupEmitter { match status { RoundUpEventStatus::NextAuthorityRelayed => max_round, - RoundUpEventStatus::NextAuthorityCommitted => max_round + 1, + RoundUpEventStatus::NextAuthorityCommitted => max_round + U256::from(1), } }; let mut next_poll_round = get_next_poll_round().await; loop { - if next_poll_round == self.current_round + 1 { + if next_poll_round == self.current_round + U256::from(1) { // If RoundUp reached to latest round, escape loop break; } else if next_poll_round <= self.current_round { // If RoundUp not reached to latest round, process round_control_poll - if self.is_selected_relayer(next_poll_round).await { - let new_relayers = self.fetch_validator_list(next_poll_round).await; + if self.is_selected_relayer(next_poll_round).await? { + let new_relayers = self.fetch_validator_list(next_poll_round).await?; self.request_send_transaction( - self.build_transaction(next_poll_round, new_relayers.clone()), + self.build_transaction(next_poll_round, new_relayers.clone()).await?, VSPPhase1Metadata::new(next_poll_round, new_relayers), ); } @@ -291,28 +305,24 @@ impl BootstrapHandler for RoundupEmitter { *state = BootstrapState::BootstrapRoundUpPhase2; } } + + Ok(()) } - async fn get_bootstrap_events(&self) -> Vec { + async fn get_bootstrap_events(&self) -> Result> { let mut round_up_events = vec![]; if let Some(bootstrap_config) = &self.bootstrap_shared_data.bootstrap_config { - let round_info: RoundMetaData = self - .client - .contract_call( - self.client.protocol_contracts.authority.round_info(), - "authority.round_info", - ) - .await; + let round_info = self.client.protocol_contracts.authority.round_info().call().await?._0; let bootstrap_offset_height = self .client .get_bootstrap_offset_height_based_on_block_time( bootstrap_config.round_offset.unwrap_or(DEFAULT_BOOTSTRAP_ROUND_OFFSET), round_info, ) - .await; + .await?; - let latest_block_number = self.client.get_latest_block_number().await; + let latest_block_number = self.client.get_block_number().await?; let mut from_block = latest_block_number.saturating_sub(bootstrap_offset_height); let to_block = latest_block_number; @@ -322,20 +332,12 @@ impl BootstrapHandler for RoundupEmitter { std::cmp::min(from_block + BOOTSTRAP_BLOCK_CHUNK_SIZE - 1, to_block); let filter = Filter::new() - .address(self.client.protocol_contracts.socket.address()) - .topic0( - self.client - .protocol_contracts - .socket - .abi() - .event("RoundUp") - .expect(INVALID_CONTRACT_ABI) - .signature(), - ) + .address(*self.client.protocol_contracts.socket.address()) + .event_signature(RoundUp::SIGNATURE_HASH) .from_block(from_block) .to_block(chunk_to_block); - let chunk_logs = self.client.get_logs(&filter).await; + let chunk_logs = self.client.get_logs(&filter).await?; round_up_events.extend(chunk_logs); from_block = chunk_to_block + 1; @@ -352,6 +354,6 @@ impl BootstrapHandler for RoundupEmitter { } } - round_up_events + Ok(round_up_events) } } diff --git a/periodic/src/socket_rollback_emitter.rs b/periodic/src/socket_rollback_emitter.rs index a4dfb5ed..6d7dc006 100644 --- a/periodic/src/socket_rollback_emitter.rs +++ b/periodic/src/socket_rollback_emitter.rs @@ -1,8 +1,13 @@ -use cron::Schedule; -use ethers::{ - providers::JsonRpcClient, - types::{TransactionRequest, U256}, +use alloy::{ + consensus::BlockHeader as _, + primitives::ChainId, + providers::{fillers::TxFiller, Provider, WalletProvider}, + rpc::types::TransactionRequest, + transports::Transport, }; +use byteorder::{BigEndian, ByteOrder as _}; +use cron::Schedule; +use eyre::Result; use std::{collections::BTreeMap, str::FromStr, sync::Arc}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; @@ -12,10 +17,10 @@ use br_primitives::{ errors::{INVALID_BIFROST_NATIVENESS, INVALID_CHAIN_ID, INVALID_PERIODIC_SCHEDULE}, schedule::{ROLLBACK_CHECK_MINIMUM_INTERVAL, ROLLBACK_CHECK_SCHEDULE}, }, - contracts::socket::{RequestID, RequestInfo, Signatures, SocketMessage}, - eth::{ChainID, GasCoefficient, RelayDirection, SocketEventStatus}, + contracts::socket::Socket_Struct::{RequestID, RequestInfo, Signatures, Socket_Message}, + eth::{GasCoefficient, RelayDirection, SocketEventStatus}, periodic::{RawRequestID, RollbackableMessage}, - tx::{RollbackMetadata, TxRequest, TxRequestMessage, TxRequestMetadata, TxRequestSender}, + tx::{RollbackMetadata, TxRequestMessage, TxRequestMetadata, TxRequestSender}, utils::sub_display_format, }; @@ -26,13 +31,18 @@ const SUB_LOG_TARGET: &str = "rollback-emitter"; /// The essential task that handles `Socket` event rollbacks. /// This only handles requests that are relayed to the target client. /// (`client` and `tx_request_sender` are connected to the same chain) -pub struct SocketRollbackEmitter { +pub struct SocketRollbackEmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// The `EthClient` to interact with the connected blockchain. - pub client: Arc>, + pub client: Arc>, /// The entire clients instantiated in the system. > - system_clients: BTreeMap>>, + system_clients: Arc>>>, /// The receiver connected to the socket rollback channel. - rollback_receiver: UnboundedReceiver, + rollback_receiver: UnboundedReceiver, /// The local storage saving emitted `Socket` event messages. rollback_msgs: BTreeMap, /// The sender that sends messages to the tx request channel. @@ -41,18 +51,18 @@ pub struct SocketRollbackEmitter { schedule: Schedule, } -impl SocketRollbackEmitter { +impl SocketRollbackEmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Instantiates a new `SocketRollbackEmitter`. pub fn new( tx_request_sender: Arc, - system_clients_vec: Vec>>, - ) -> (Self, UnboundedSender) { - let (sender, rollback_receiver) = mpsc::unbounded_channel::(); - - let system_clients: BTreeMap>> = system_clients_vec - .iter() - .map(|client| (client.get_chain_id(), client.clone())) - .collect(); + system_clients: Arc>>>, + ) -> (Self, UnboundedSender) { + let (sender, rollback_receiver) = mpsc::unbounded_channel::(); ( Self { @@ -69,45 +79,50 @@ impl SocketRollbackEmitter { } /// Verifies whether the given socket message has been executed. - async fn is_request_executed(&self, socket_msg: &SocketMessage) -> bool { + async fn is_request_executed(&self, socket_msg: &Socket_Message) -> Result { let src_request = self - .get_socket_request(&socket_msg.req_id, ChainID::from_be_bytes(socket_msg.req_id.chain)) - .await; + .get_socket_request( + &socket_msg.req_id, + BigEndian::read_u32(&socket_msg.req_id.ChainIndex.0) as ChainId, + ) + .await?; let dst_request = self .get_socket_request( &socket_msg.req_id, - ChainID::from_be_bytes(socket_msg.ins_code.chain), + BigEndian::read_u32(&socket_msg.ins_code.ChainIndex.0) as ChainId, ) - .await; + .await?; if let (Some(src_request), Some(dst_request)) = (src_request, dst_request) { let src_status = SocketEventStatus::from(&src_request.field[0]); let dst_status = SocketEventStatus::from(&dst_request.field[0]); match src_status { - SocketEventStatus::Committed | SocketEventStatus::Rollbacked => return true, + SocketEventStatus::Committed | SocketEventStatus::Rollbacked => return Ok(true), _ => (), } - if self.is_inbound_sequence(ChainID::from_be_bytes(socket_msg.ins_code.chain)) { + if self.is_inbound_sequence( + BigEndian::read_u32(&socket_msg.ins_code.ChainIndex.0) as ChainId + ) { match dst_status { SocketEventStatus::Executed | SocketEventStatus::Reverted | SocketEventStatus::Accepted - | SocketEventStatus::Rejected => return true, + | SocketEventStatus::Rejected => return Ok(true), _ => (), } } else { match dst_status { - SocketEventStatus::Executed | SocketEventStatus::Reverted => return true, + SocketEventStatus::Executed | SocketEventStatus::Reverted => return Ok(true), _ => (), } } } - false + Ok(false) } /// Verifies whether the socket event is an inbound sequence. - fn is_inbound_sequence(&self, dst_chain_id: ChainID) -> bool { + fn is_inbound_sequence(&self, dst_chain_id: ChainId) -> bool { if let Some(client) = self.system_clients.get(&dst_chain_id) { return matches!(client.metadata.if_destination_chain, RelayDirection::Inbound); } @@ -115,10 +130,8 @@ impl SocketRollbackEmitter { } /// Verifies whether a certain socket message has been waited for at least the required minimum time. - fn is_request_timeout(&self, timeout_started_at: U256, current_timestamp: U256) -> bool { - if current_timestamp.saturating_sub(timeout_started_at) - >= ROLLBACK_CHECK_MINIMUM_INTERVAL.into() - { + fn is_request_timeout(&self, timeout_started_at: u64, current_timestamp: u64) -> bool { + if current_timestamp.saturating_sub(timeout_started_at) >= ROLLBACK_CHECK_MINIMUM_INTERVAL { return true; } false @@ -128,23 +141,18 @@ impl SocketRollbackEmitter { async fn get_socket_request( &self, req_id: &RequestID, - chain_id: ChainID, - ) -> Option { + chain_id: ChainId, + ) -> Result> { if let Some(client) = self.system_clients.get(&chain_id) { - return Some( - client - .contract_call( - client.protocol_contracts.socket.get_request(req_id.clone()), - "socket.get_request", - ) - .await, - ); + return Ok(Some( + client.protocol_contracts.socket.get_request(req_id.clone()).call().await?._0, + )); } - None + Ok(None) } /// Tries to rollback the given socket message. - async fn try_rollback(&self, socket_msg: &SocketMessage) { + async fn try_rollback(&self, socket_msg: &Socket_Message) { let status = SocketEventStatus::from(socket_msg.status); match status { SocketEventStatus::Requested => self.try_rollback_inbound(socket_msg).await, @@ -154,19 +162,19 @@ impl SocketRollbackEmitter { } /// Tries to rollback the given inbound socket message. - async fn try_rollback_inbound(&self, socket_msg: &SocketMessage) { + async fn try_rollback_inbound(&self, socket_msg: &Socket_Message) { let mut submit_sig = socket_msg.clone(); submit_sig.status = SocketEventStatus::Failed.into(); let tx_request = TransactionRequest::default() - .data(self.build_poll_call_data(submit_sig.clone(), Signatures::default())) - .to(self.client.protocol_contracts.socket.address()); + .input(self.build_poll_call_data(submit_sig.clone(), Signatures::default())) + .to(*self.client.protocol_contracts.socket.address()); let metadata = RollbackMetadata::new( true, SocketEventStatus::Failed, socket_msg.req_id.sequence, - ChainID::from_be_bytes(socket_msg.req_id.chain), - ChainID::from_be_bytes(socket_msg.ins_code.chain), + BigEndian::read_u32(&socket_msg.req_id.ChainIndex.0) as ChainId, + BigEndian::read_u32(&socket_msg.ins_code.ChainIndex.0) as ChainId, ); // transaction executed on Bifrost so no random delay required. @@ -175,24 +183,24 @@ impl SocketRollbackEmitter { } /// Tries to rollback the given outbound socket message. - async fn try_rollback_outbound(&self, socket_msg: &SocketMessage) { + async fn try_rollback_outbound(&self, socket_msg: &Socket_Message) { // `submit_sig` is the state changed socket message that will be passed. // `socket_msg` is the origin message that will be used for signature builds. let mut submit_sig = socket_msg.clone(); submit_sig.status = SocketEventStatus::Rejected.into(); let tx_request = TransactionRequest::default() - .data(self.build_poll_call_data( + .input(self.build_poll_call_data( submit_sig.clone(), self.get_sorted_signatures(socket_msg.clone()).await, )) - .to(self.client.protocol_contracts.socket.address()); + .to(*self.client.protocol_contracts.socket.address()); let metadata = RollbackMetadata::new( false, SocketEventStatus::Rejected, socket_msg.req_id.sequence, - ChainID::from_be_bytes(socket_msg.req_id.chain), - ChainID::from_be_bytes(socket_msg.ins_code.chain), + BigEndian::read_u32(&socket_msg.req_id.ChainIndex.0) as ChainId, + BigEndian::read_u32(&socket_msg.ins_code.ChainIndex.0) as ChainId, ); // transaction executed on External chain's so random delay required. @@ -202,12 +210,12 @@ impl SocketRollbackEmitter { /// Tries to receive any new rollbackable messages and store's it locally. /// The timestamp will be set to the current highest block's timestamp. - fn receive(&mut self, current_timestamp: U256) { + fn receive(&mut self, current_timestamp: u64) { while let Ok(msg) = self.rollback_receiver.try_recv() { // prevent rollback for bitcoin bridges if let Some(bitcoin_chain_id) = self.client.get_bitcoin_chain_id() { - if msg.req_id.chain.as_slice() == bitcoin_chain_id.to_be_bytes() - || msg.ins_code.chain.as_slice() == bitcoin_chain_id.to_be_bytes() + if BigEndian::read_u32(&msg.req_id.ChainIndex.0) as u64 == bitcoin_chain_id + || BigEndian::read_u32(&msg.ins_code.ChainIndex.0) as u64 == bitcoin_chain_id { continue; } @@ -241,7 +249,7 @@ impl SocketRollbackEmitter { // asynchronous transaction tasks will work fine for rollback transactions, // so `is_bootstrap` parameter is set to `false`. match self.tx_request_sender.send(TxRequestMessage::new( - TxRequest::Legacy(tx_request), + tx_request, TxRequestMetadata::Rollback(metadata.clone()), true, give_random_delay, @@ -275,8 +283,13 @@ impl SocketRollbackEmitter { } #[async_trait::async_trait] -impl SocketRelayBuilder for SocketRollbackEmitter { - fn get_client(&self) -> Arc> { +impl SocketRelayBuilder for SocketRollbackEmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + fn get_client(&self) -> Arc> { // This will always return the Bifrost client. // Used only for `get_sorted_signatures()` on `Outbound::Accepted` rollbacks. self.system_clients @@ -289,34 +302,42 @@ impl SocketRelayBuilder for SocketRollbackEmitter { } #[async_trait::async_trait] -impl PeriodicWorker for SocketRollbackEmitter { +impl PeriodicWorker for SocketRollbackEmitter +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn schedule(&self) -> Schedule { self.schedule.clone() } - async fn run(&mut self) { + async fn run(&mut self) -> Result<()> { loop { self.wait_until_next_time().await; // executed or rollback handled request ID's. let mut handled_req_ids = vec![]; - if let Some(latest_block) = - self.client.get_block(self.client.get_latest_block_number().await.into()).await + if let Some(latest_block) = self + .client + .get_block(self.client.get_block_number().await?.into(), true.into()) + .await? { - self.receive(latest_block.timestamp); + self.receive(latest_block.header.timestamp()); for (req_id, rollback_msg) in self.rollback_msgs.clone() { // ignore if the request has already been processed. // it should be removed from the local storage. - if self.is_request_executed(&rollback_msg.socket_msg).await { + if self.is_request_executed(&rollback_msg.socket_msg).await? { handled_req_ids.push(req_id); continue; } // ignore if the required interval didn't pass. - if !self - .is_request_timeout(rollback_msg.timeout_started_at, latest_block.timestamp) - { + if !self.is_request_timeout( + rollback_msg.timeout_started_at, + latest_block.header.timestamp(), + ) { continue; } // the pending request has not been processed in the waiting period. rollback should be handled. diff --git a/periodic/src/traits.rs b/periodic/src/traits.rs index 11e711ad..669488b8 100644 --- a/periodic/src/traits.rs +++ b/periodic/src/traits.rs @@ -1,5 +1,6 @@ use cron::Schedule; -use std::{collections::BTreeMap, fmt::Error}; +use eyre::Result; +use std::collections::BTreeMap; use tokio::time::sleep; use br_primitives::periodic::PriceResponse; @@ -10,7 +11,7 @@ pub trait PeriodicWorker { fn schedule(&self) -> Schedule; /// Starts the periodic worker. - async fn run(&mut self); + async fn run(&mut self) -> Result<()>; /// Wait until it reaches the next schedule. async fn wait_until_next_time(&self) { @@ -27,8 +28,8 @@ pub trait PeriodicWorker { #[async_trait::async_trait] pub trait PriceFetcher { /// Get price with ticker symbol. - async fn get_ticker_with_symbol(&self, symbol: String) -> Result; + async fn get_ticker_with_symbol(&self, symbol: String) -> Result; /// Get all prices of support coin/token. - async fn get_tickers(&self) -> Result, Error>; + async fn get_tickers(&self) -> Result>; } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 2634ac37..6a99dea8 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -13,7 +13,8 @@ repository = { workspace = true } hex = { workspace = true } chrono = { workspace = true } cron = { workspace = true } -ethers = { workspace = true } +alloy = { workspace = true } +tower = { workspace = true } thiserror = { workspace = true } async-trait = { workspace = true } reqwest = { workspace = true } @@ -29,3 +30,5 @@ miniscript = { workspace = true } subxt = { workspace = true } bitcoincore-rpc = { workspace = true } url = { workspace = true } +sha3 = { workspace = true } +k256 = { workspace = true } diff --git a/primitives/src/cli.rs b/primitives/src/cli.rs index d6f92c28..bec14156 100644 --- a/primitives/src/cli.rs +++ b/primitives/src/cli.rs @@ -1,8 +1,7 @@ +use alloy::primitives::ChainId; use serde::Deserialize; use std::{borrow::Cow, fmt::Display}; -use crate::eth::ChainID; - pub type Result = std::result::Result; /// Error type for the CLI. @@ -71,7 +70,7 @@ pub struct EVMProvider { /// Network name pub name: String, /// Chain ID - pub id: ChainID, + pub id: ChainId, /// Endpoint provider pub provider: String, /// The time interval(ms) used when to request a new block @@ -134,7 +133,7 @@ pub struct EVMProvider { #[derive(Debug, Clone, Deserialize)] pub struct BTCProvider { /// The bitcoin chain ID used for CCCP. - pub id: u32, + pub id: u64, /// The Bitcoin provider URL. pub provider: String, /// The time interval(ms) used when to request a new block @@ -174,7 +173,7 @@ pub struct HandlerConfig { /// Handle type pub handler_type: HandlerType, /// Watch target list - pub watch_list: Vec, + pub watch_list: Vec, } #[derive(Debug, Clone, Deserialize)] @@ -182,7 +181,7 @@ pub struct BootstrapConfig { /// Bootstrapping flag pub is_enabled: bool, /// Optional. The round offset used for EVM bootstrap. - pub round_offset: Option, + pub round_offset: Option, /// Optional. The block offset used for Bitcoin bootstrap. pub btc_block_offset: Option, } diff --git a/primitives/src/constants/cli.rs b/primitives/src/constants/cli.rs index 1128ba10..0d4dd959 100644 --- a/primitives/src/constants/cli.rs +++ b/primitives/src/constants/cli.rs @@ -2,7 +2,7 @@ pub const DEFAULT_KEYSTORE_PATH: &str = "./keys"; /// The default round offset used on bootstrap. (=3 rounds) -pub const DEFAULT_BOOTSTRAP_ROUND_OFFSET: u32 = 3; +pub const DEFAULT_BOOTSTRAP_ROUND_OFFSET: u64 = 3; /// The default bootstrap offset for Bitcoin (in blocks) pub const DEFAULT_BITCOIN_BOOTSTRAP_BLOCK_OFFSET: u32 = 3; @@ -50,4 +50,4 @@ pub const MAX_DUPLICATE_CONFIRM_DELAY_MS: u64 = 60_000; pub const MIN_GET_LOGS_BATCH_SIZE: u64 = 1; /// The maximum round offset allowed for bootstrap. (=14 rounds) -pub const MAX_BOOTSTRAP_ROUND_OFFSET: u32 = 14; +pub const MAX_BOOTSTRAP_ROUND_OFFSET: u64 = 14; diff --git a/primitives/src/constants/config.rs b/primitives/src/constants/config.rs index 3084f89c..1fb551e4 100644 --- a/primitives/src/constants/config.rs +++ b/primitives/src/constants/config.rs @@ -1,11 +1,11 @@ /// The native chain's average block time in seconds. -pub const NATIVE_BLOCK_TIME: u32 = 3u32; +pub const NATIVE_BLOCK_TIME: u64 = 3; /// Ethereum network's average block time in seconds. -pub const ETHEREUM_BLOCK_TIME: u64 = 12u64; +pub const ETHEREUM_BLOCK_TIME: u64 = 12; /// The block range chunk size for getLogs requests. pub const BOOTSTRAP_BLOCK_CHUNK_SIZE: u64 = 2000; /// The block offset used to measure the average block time at bootstrap. -pub const BOOTSTRAP_BLOCK_OFFSET: u32 = 100; +pub const BOOTSTRAP_BLOCK_OFFSET: u64 = 100; diff --git a/primitives/src/constants/schedule.rs b/primitives/src/constants/schedule.rs index 7879acb3..9d208dec 100644 --- a/primitives/src/constants/schedule.rs +++ b/primitives/src/constants/schedule.rs @@ -17,7 +17,7 @@ pub const BITCOIN_ROLLBACK_CHECK_SCHEDULE: &str = "*/15 * * * * * *"; pub const ROLLBACK_CHECK_SCHEDULE: &str = "0 * * * * * *"; /// The minimum interval that should be passed in order to handle rollback checks. (=3 minutes) -pub const ROLLBACK_CHECK_MINIMUM_INTERVAL: u32 = 3 * 60; +pub const ROLLBACK_CHECK_MINIMUM_INTERVAL: u64 = 3 * 60; /// The schedule definition for migration detector. This will trigger on every second. pub const MIGRATION_DETECTOR_SCHEDULE: &str = "*/1 * * * * * *"; diff --git a/primitives/src/constants/tx.rs b/primitives/src/constants/tx.rs index 3f67bfef..77afd59c 100644 --- a/primitives/src/constants/tx.rs +++ b/primitives/src/constants/tx.rs @@ -1,5 +1,5 @@ /// The default retries of a single json rpc request. -pub const DEFAULT_CALL_RETRIES: u8 = 3; +pub const DEFAULT_CALL_RETRIES: u32 = 3; /// The default call retry interval in milliseconds. pub const DEFAULT_CALL_RETRY_INTERVAL_MS: u64 = 3000; @@ -13,5 +13,5 @@ pub const DEFAULT_TX_RETRY_INTERVAL_MS: u64 = 3000; /// The coefficient that will be multiplied on the max fee. pub const MAX_FEE_COEFFICIENT: u64 = 2; -/// The coefficient that will be multipled on the max priority fee. +/// The coefficient that will be multiplied on the max priority fee. pub const MAX_PRIORITY_FEE_COEFFICIENT: u64 = 2; diff --git a/primitives/src/contracts/authority.rs b/primitives/src/contracts/authority.rs index dd43a11a..62ff442f 100644 --- a/primitives/src/contracts/authority.rs +++ b/primitives/src/contracts/authority.rs @@ -1,7 +1,9 @@ -use ethers::prelude::abigen; +use alloy::sol; -abigen!( +sol!( + #[allow(missing_docs)] + #[derive(Debug)] + #[sol(rpc)] AuthorityContract, - "../abi/abi.authority.merged.json", - event_derives(serde::Deserialize, serde::Serialize) + "../abi/abi.authority.merged.json" ); diff --git a/primitives/src/contracts/bitcoin_socket.rs b/primitives/src/contracts/bitcoin_socket.rs index e0365286..8f0c3f87 100644 --- a/primitives/src/contracts/bitcoin_socket.rs +++ b/primitives/src/contracts/bitcoin_socket.rs @@ -1,16 +1,9 @@ -use ethers::prelude::abigen; +use alloy::sol; -abigen!( +sol!( + #[allow(missing_docs)] + #[derive(Debug)] + #[sol(rpc)] BitcoinSocketContract, - "../abi/abi.socket.bitcoin.json", - event_derives(serde::Deserialize, serde::Serialize) + "../abi/abi.socket.bitcoin.json" ); - -#[derive(Debug, Clone, PartialEq, ethers::contract::EthEvent, ethers::contract::EthDisplay)] -#[ethevent( - name = "Socket", - abi = "Socket(((bytes4,uint64,uint128),uint8,(bytes4,bytes16),(bytes32,bytes32,address,address,uint256,bytes)))" -)] -pub struct Socket { - pub msg: SocketMessage, -} diff --git a/primitives/src/contracts/chainlink_aggregator.rs b/primitives/src/contracts/chainlink_aggregator.rs index 2ec96f02..ef9ac14e 100644 --- a/primitives/src/contracts/chainlink_aggregator.rs +++ b/primitives/src/contracts/chainlink_aggregator.rs @@ -1,7 +1,9 @@ -use ethers::prelude::abigen; +use alloy::sol; -abigen!( +sol!( + #[allow(missing_docs)] + #[derive(Debug)] + #[sol(rpc)] ChainlinkContract, - "../abi/abi.aggregatorv3.chainlink.json", - event_derives(serde::Deserialize, serde::Serialize) + "../abi/abi.aggregatorv3.chainlink.json" ); diff --git a/primitives/src/contracts/registration_pool.rs b/primitives/src/contracts/registration_pool.rs index e246810d..3c0b9e4d 100644 --- a/primitives/src/contracts/registration_pool.rs +++ b/primitives/src/contracts/registration_pool.rs @@ -1,7 +1,9 @@ -use ethers::prelude::abigen; +use alloy::sol; -abigen!( +sol!( + #[allow(missing_docs)] + #[derive(Debug)] + #[sol(rpc)] RegistrationPoolContract, - "../abi/abi.registration_pool.bifrost.json", - event_derives(serde::Deserialize, serde::Serialize) + "../abi/abi.registration_pool.bifrost.json" ); diff --git a/primitives/src/contracts/relay_executive.rs b/primitives/src/contracts/relay_executive.rs index 6e62591c..4d8797c2 100644 --- a/primitives/src/contracts/relay_executive.rs +++ b/primitives/src/contracts/relay_executive.rs @@ -1,7 +1,9 @@ -use ethers::prelude::abigen; +use alloy::sol; -abigen!( +sol!( + #[allow(missing_docs)] + #[derive(Debug)] + #[sol(rpc)] RelayExecutiveContract, - "../abi/abi.relay_executive.bifrost.json", - event_derives(serde::Deserialize, serde::Serialize) + "../abi/abi.relay_executive.bifrost.json" ); diff --git a/primitives/src/contracts/relayer_manager.rs b/primitives/src/contracts/relayer_manager.rs index b7a5a3dc..4da028e2 100644 --- a/primitives/src/contracts/relayer_manager.rs +++ b/primitives/src/contracts/relayer_manager.rs @@ -1,7 +1,9 @@ -use ethers::prelude::abigen; +use alloy::sol; -abigen!( +sol!( + #[allow(missing_docs)] + #[derive(Debug)] + #[sol(rpc)] RelayerManagerContract, - "../abi/abi.relayer.bifrost.json", - event_derives(serde::Deserialize, serde::Serialize) + "../abi/abi.relayer.bifrost.json" ); diff --git a/primitives/src/contracts/socket.rs b/primitives/src/contracts/socket.rs index b170e427..975048ec 100644 --- a/primitives/src/contracts/socket.rs +++ b/primitives/src/contracts/socket.rs @@ -1,174 +1,44 @@ -use std::{collections::BTreeMap, str::FromStr}; - -use ethers::{ - abi::RawLog, - prelude::{abigen, H256}, - types::{Bytes, Signature, U256}, +use alloy::{ + primitives::{b256, Bytes, B256}, + signers::Signature, + sol, }; +use std::collections::BTreeMap; +use Socket_Struct::*; + +pub fn get_asset_oids() -> BTreeMap<&'static str, B256> { + >::from([ + ("BFC", b256!("0100010000000000000000000000000000000000000000000000000000000001")), + ("BIFI", b256!("0100010000000000000000000000000000000000000000000000000000000002")), + ("BTC", b256!("0100010000000000000000000000000000000000000000000000000000000003")), + ("ETH", b256!("0100010000000000000000000000000000000000000000000000000000000004")), + ("BNB", b256!("0100010000000000000000000000000000000000000000000000000000000005")), + ("MATIC", b256!("0100010000000000000000000000000000000000000000000000000000000006")), + ("AVAX", b256!("0100010000000000000000000000000000000000000000000000000000000007")), + ("USDC", b256!("0100010000000000000000000000000000000000000000000000000000000008")), + ("BUSD", b256!("0100010000000000000000000000000000000000000000000000000000000009")), + ("USDT", b256!("010001000000000000000000000000000000000000000000000000000000000a")), + ("DAI", b256!("010001000000000000000000000000000000000000000000000000000000000b")), + ("BTCB", b256!("010001000000000000000000000000000000000000000000000000000000000c")), + ("WBTC", b256!("010001000000000000000000000000000000000000000000000000000000000d")), + ("CBBTC", b256!("010001000000000000000000000000000000000000000000000000000000000e")), + ]) +} -abigen!( +sol!( + #[allow(missing_docs)] + #[derive(Debug, PartialEq, Eq, Default)] + #[sol(rpc)] SocketContract, - "../abi/abi.socket.merged.json", - event_derives(serde::Deserialize, serde::Serialize) + "../abi/abi.socket.merged.json" ); -#[derive( - Clone, - ethers::contract::EthEvent, - ethers::contract::EthDisplay, - ethers::contract::EthAbiCodec, - Default, - Debug, - PartialEq, - Eq, - Hash, -)] -#[ethevent( - name = "Socket", - abi = "Socket(((bytes4,uint64,uint128),uint8,(bytes4,bytes16),(bytes32,bytes32,address,address,uint256,bytes)))" -)] -/// The `Socket` event from the `SocketExternal` contract. -pub struct Socket { - pub msg: SocketMessage, -} - -#[derive(Clone, ethers::contract::EthAbiType, Debug, PartialEq, Eq, Hash)] -/// The event enums originated from the `SocketExternal` contract. -pub enum SocketEvents { - Socket(Socket), -} - -impl ethers::contract::EthLogDecode for SocketEvents { - fn decode_log(log: &RawLog) -> Result - where - Self: Sized, - { - if let Ok(decoded) = Socket::decode_log(log) { - return Ok(SocketEvents::Socket(decoded)); - } - Err(ethers::abi::Error::InvalidData) - } -} - -#[derive( - Clone, - ethers::contract::EthCall, - ethers::contract::EthDisplay, - Default, - Debug, - PartialEq, - Eq, - Hash, -)] -#[ethcall( - name = "poll", - abi = "poll(((bytes4,uint64,uint128),uint8,(bytes4,bytes16),(bytes32,bytes32,address,address,uint256,bytes)),(bytes32[],bytes32[],bytes),uint256)" -)] -pub struct SerializedPoll { - pub msg: SocketMessage, - pub sigs: Signatures, - pub option: U256, -} - -#[derive( - Clone, - ethers::contract::EthEvent, - ethers::contract::EthDisplay, - Default, - Debug, - PartialEq, - Eq, - Hash, -)] -#[ethevent( - name = "RoundUp", - abi = "RoundUp(uint8,(uint256,address[],(bytes32[],bytes32[],bytes)))" -)] -pub struct SerializedRoundUp { - pub status: u8, - pub roundup: RoundUpSubmit, -} - -pub fn get_asset_oids() -> BTreeMap<&'static str, H256> { - >::from([ - ( - "BFC", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000001") - .unwrap(), - ), - ( - "BIFI", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000002") - .unwrap(), - ), - ( - "BTC", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000003") - .unwrap(), - ), - ( - "ETH", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000004") - .unwrap(), - ), - ( - "BNB", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000005") - .unwrap(), - ), - ( - "MATIC", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000006") - .unwrap(), - ), - ( - "AVAX", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000007") - .unwrap(), - ), - ( - "USDC", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000008") - .unwrap(), - ), - ( - "BUSD", - H256::from_str("0100010000000000000000000000000000000000000000000000000000000009") - .unwrap(), - ), - ( - "USDT", - H256::from_str("010001000000000000000000000000000000000000000000000000000000000a") - .unwrap(), - ), - ( - "DAI", - H256::from_str("010001000000000000000000000000000000000000000000000000000000000b") - .unwrap(), - ), - ( - "BTCB", - H256::from_str("010001000000000000000000000000000000000000000000000000000000000c") - .unwrap(), - ), - ( - "WBTC", - H256::from_str("010001000000000000000000000000000000000000000000000000000000000d") - .unwrap(), - ), - ( - "CBBTC", - H256::from_str("010001000000000000000000000000000000000000000000000000000000000e") - .unwrap(), - ), - ]) -} - impl From for Signatures { fn from(signature: Signature) -> Self { - let r: [u8; 32] = signature.r.into(); - let s: [u8; 32] = signature.s.into(); - let v: [u8; 1] = [signature.v as u8]; - Signatures { r: vec![r], s: vec![s], v: Bytes::from(v) } + let r = signature.r().into(); + let s = signature.s().into(); + let v = signature.v().to_u64() as u8; + + Signatures { r: vec![r], s: vec![s], v: Bytes::from(vec![v]) } } } diff --git a/primitives/src/contracts/socket_queue.rs b/primitives/src/contracts/socket_queue.rs index a2bad6b8..d55e60f7 100644 --- a/primitives/src/contracts/socket_queue.rs +++ b/primitives/src/contracts/socket_queue.rs @@ -1,7 +1,9 @@ -use ethers::prelude::abigen; +use alloy::sol; -abigen!( +sol!( + #[allow(missing_docs)] + #[derive(Debug)] + #[sol(rpc)] SocketQueueContract, - "../abi/abi.socket_queue.bifrost.json", - event_derives(serde::Deserialize, serde::Serialize) + "../abi/abi.socket_queue.bifrost.json" ); diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index 94ab83bf..14b75f46 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -1,24 +1,33 @@ use std::{str::FromStr, sync::Arc}; -use ethers::{ - providers::{JsonRpcClient, Provider}, - types::{Address, Signature, TransactionRequest, Uint8, H160, U64}, +use alloy::{ + network::Ethereum, + primitives::{Address, ChainId}, + providers::{ + fillers::{FillProvider, TxFiller}, + Provider, WalletProvider, + }, + rpc::types::TransactionRequest, + signers::Signature, + transports::Transport, }; use url::Url; use crate::{ - constants::errors::{INVALID_CONTRACT_ADDRESS, INVALID_PROVIDER_URL, MISSING_CONTRACT_ADDRESS}, + constants::errors::{INVALID_CONTRACT_ADDRESS, MISSING_CONTRACT_ADDRESS}, contracts::{ - authority::AuthorityContract, bitcoin_socket::BitcoinSocketContract, - chainlink_aggregator::ChainlinkContract, registration_pool::RegistrationPoolContract, - relay_executive::RelayExecutiveContract, relayer_manager::RelayerManagerContract, - socket::SocketContract, socket_queue::SocketQueueContract, + authority::AuthorityContract::{self, AuthorityContractInstance}, + bitcoin_socket::BitcoinSocketContract::{self, BitcoinSocketContractInstance}, + chainlink_aggregator::ChainlinkContract::{self, ChainlinkContractInstance}, + registration_pool::RegistrationPoolContract::{self, RegistrationPoolContractInstance}, + relay_executive::RelayExecutiveContract::{self, RelayExecutiveContractInstance}, + relayer_manager::RelayerManagerContract::{self, RelayerManagerContractInstance}, + socket::SocketContract::{self, SocketContractInstance}, + socket_queue::SocketQueueContract::{self, SocketQueueContractInstance}, }, }; -/// The type of EVM chain ID's. -pub type ChainID = u32; - +#[derive(Clone)] /// The metadata of the EVM provider. pub struct ProviderMetadata { /// The name of this provider. @@ -26,14 +35,14 @@ pub struct ProviderMetadata { /// The provider URL. (Allowed values: `http`, `https`) pub url: Url, /// Id of chain which this client interact with. - pub id: ChainID, + pub id: ChainId, /// The bitcoin chain ID used for CCCP. - pub bitcoin_chain_id: Option, + pub bitcoin_chain_id: Option, /// The total number of confirmations required for a block to be processed. (block /// confirmations + eth_getLogs batch size) - pub block_confirmations: U64, + pub block_confirmations: u64, /// The batch size used on `eth_getLogs()` requests. - pub get_logs_batch_size: U64, + pub get_logs_batch_size: u64, /// The `get_block` request interval in milliseconds. pub call_interval: u64, /// Relay direction when CCCP event points this chain as destination. @@ -45,9 +54,9 @@ pub struct ProviderMetadata { impl ProviderMetadata { pub fn new( name: String, - url: String, - id: ChainID, - bitcoin_chain_id: Option, + url: Url, + id: ChainId, + bitcoin_chain_id: Option, block_confirmations: u64, call_interval: u64, get_logs_batch_size: u64, @@ -55,11 +64,11 @@ impl ProviderMetadata { ) -> Self { Self { name, - url: Url::parse(&url).expect(INVALID_PROVIDER_URL), + url, id, bitcoin_chain_id, - block_confirmations: U64::from(block_confirmations.saturating_add(get_logs_batch_size)), - get_logs_batch_size: U64::from(get_logs_batch_size), + block_confirmations: block_confirmations.saturating_add(get_logs_batch_size), + get_logs_batch_size, call_interval, is_native, if_destination_chain: match is_native { @@ -70,24 +79,41 @@ impl ProviderMetadata { } } -pub struct AggregatorContracts { +#[derive(Clone)] +pub struct AggregatorContracts +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// Chainlink usdc/usd aggregator - pub chainlink_usdc_usd: Option>>, + pub chainlink_usdc_usd: + Option>>>, /// Chainlink usdt/usd aggregator - pub chainlink_usdt_usd: Option>>, + pub chainlink_usdt_usd: + Option>>>, /// Chainlink dai/usd aggregator - pub chainlink_dai_usd: Option>>, + pub chainlink_dai_usd: + Option>>>, /// Chainlink btc/usd aggregator - pub chainlink_btc_usd: Option>>, + pub chainlink_btc_usd: + Option>>>, /// Chainlink wbtc/usd aggregator - pub chainlink_wbtc_usd: Option>>, + pub chainlink_wbtc_usd: + Option>>>, /// Chainlink cbbtc/usd aggregator - pub chainlink_cbbtc_usd: Option>>, + pub chainlink_cbbtc_usd: + Option>>>, } -impl AggregatorContracts { +impl AggregatorContracts +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ pub fn new( - provider: Arc>, + provider: Arc>, chainlink_usdc_usd_address: Option, chainlink_usdt_usd_address: Option, chainlink_dai_usd_address: Option, @@ -97,7 +123,7 @@ impl AggregatorContracts { ) -> Self { let create_contract_instance = |address: String| { ChainlinkContract::new( - H160::from_str(&address).expect(INVALID_CONTRACT_ADDRESS), + Address::from_str(&address).expect(INVALID_CONTRACT_ADDRESS), provider.clone(), ) }; @@ -113,7 +139,12 @@ impl AggregatorContracts { } } -impl Default for AggregatorContracts { +impl Default for AggregatorContracts +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ fn default() -> Self { Self { chainlink_usdc_usd: None, @@ -126,28 +157,43 @@ impl Default for AggregatorContracts { } } +#[derive(Clone)] /// The protocol contract instances of the EVM provider. -pub struct ProtocolContracts { +pub struct ProtocolContracts +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ /// SocketContract - pub socket: SocketContract>, + pub socket: SocketContractInstance>>, /// AuthorityContract - pub authority: AuthorityContract>, + pub authority: AuthorityContractInstance>>, /// RelayerManagerContract (Bifrost only) - pub relayer_manager: Option>>, + pub relayer_manager: + Option>>>, /// BitcoinSocketContract (Bifrost only) - pub bitcoin_socket: Option>>, + pub bitcoin_socket: + Option>>>, /// SocketQueueContract (Bifrost only) - pub socket_queue: Option>>, + pub socket_queue: Option>>>, /// RegistrationPoolContract (Bifrost only) - pub registration_pool: Option>>, + pub registration_pool: + Option>>>, /// RelayExecutiveContract (Bifrost only) - pub relay_executive: Option>>, + pub relay_executive: + Option>>>, } -impl ProtocolContracts { +impl ProtocolContracts +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ pub fn new( is_native: bool, - provider: Arc>, + provider: Arc>, socket_address: String, authority_address: String, relayer_manager_address: Option, @@ -158,11 +204,11 @@ impl ProtocolContracts { ) -> Self { let mut contracts = Self { socket: SocketContract::new( - H160::from_str(&socket_address).expect(INVALID_CONTRACT_ADDRESS), + Address::from_str(&socket_address).expect(INVALID_CONTRACT_ADDRESS), provider.clone(), ), authority: AuthorityContract::new( - H160::from_str(&authority_address).expect(INVALID_CONTRACT_ADDRESS), + Address::from_str(&authority_address).expect(INVALID_CONTRACT_ADDRESS), provider.clone(), ), relayer_manager: None, @@ -173,27 +219,27 @@ impl ProtocolContracts { }; if is_native { contracts.relayer_manager = Some(RelayerManagerContract::new( - H160::from_str(&relayer_manager_address.expect(MISSING_CONTRACT_ADDRESS)) + Address::from_str(&relayer_manager_address.expect(MISSING_CONTRACT_ADDRESS)) .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); contracts.bitcoin_socket = Some(BitcoinSocketContract::new( - H160::from_str(&bitcoin_socket_address.expect(MISSING_CONTRACT_ADDRESS)) + Address::from_str(&bitcoin_socket_address.expect(MISSING_CONTRACT_ADDRESS)) .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); contracts.socket_queue = Some(SocketQueueContract::new( - H160::from_str(&socket_queue_address.expect(MISSING_CONTRACT_ADDRESS)) + Address::from_str(&socket_queue_address.expect(MISSING_CONTRACT_ADDRESS)) .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); contracts.registration_pool = Some(RegistrationPoolContract::new( - H160::from_str(®istration_pool_address.expect(MISSING_CONTRACT_ADDRESS)) + Address::from_str(®istration_pool_address.expect(MISSING_CONTRACT_ADDRESS)) .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); contracts.relay_executive = Some(RelayExecutiveContract::new( - H160::from_str(&relay_executive_address.expect(MISSING_CONTRACT_ADDRESS)) + Address::from_str(&relay_executive_address.expect(MISSING_CONTRACT_ADDRESS)) .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); @@ -290,9 +336,9 @@ impl From for SocketEventStatus { } } -impl From<&Uint8> for SocketEventStatus { - fn from(value: &Uint8) -> Self { - Self::from(u8::from(value.clone())) +impl From<&u8> for SocketEventStatus { + fn from(value: &u8) -> Self { + Self::from(*value) } } @@ -367,3 +413,176 @@ impl BuiltRelayTransaction { Self { tx_request, is_external } } } + +pub mod retry { + use alloy::{ + rpc::json_rpc::{RequestPacket, ResponsePacket}, + transports::{ + layers::RetryPolicy as RetryPolicyT, RpcError, TransportError, TransportErrorKind, + TransportFut, + }, + }; + use std::{ + sync::{ + atomic::{AtomicU32, Ordering}, + Arc, + }, + task::{Context, Poll}, + time::Duration, + }; + use tokio::time::sleep; + use tower::{Layer, Service}; + + /// A Transport Layer that is responsible for retrying requests based on the + /// error type. See [`TransportError`]. + #[derive(Debug, Clone)] + pub struct RetryBackoffLayer { + /// The maximum number of retries for errors + max_retries: u32, + /// The initial backoff in milliseconds + initial_backoff: u64, + } + + impl RetryBackoffLayer { + /// Creates a new retry layer with the given parameters. + pub const fn new(max_retries: u32, initial_backoff: u64) -> Self { + Self { max_retries, initial_backoff } + } + } + + /// [RetryPolicy] implements [RetryPolicyT] to determine whether to retry depending on the + /// err. + #[derive(Debug, Copy, Clone, Default)] + #[non_exhaustive] + pub struct RetryPolicy; + + impl RetryPolicyT for RetryPolicy { + fn should_retry(&self, _error: &TransportError) -> bool { + // TODO: Filter out errors that are not retryable. now we retry all errors. + true + } + + /// Provides a backoff hint if the error response contains it + fn backoff_hint(&self, error: &TransportError) -> Option { + if let RpcError::ErrorResp(resp) = error { + let data = resp.try_data_as::(); + if let Some(Ok(data)) = data { + // if daily rate limit exceeded, infura returns the requested backoff in the error + // response + let backoff_seconds = &data["rate"]["backoff_seconds"]; + // infura rate limit error + if let Some(seconds) = backoff_seconds.as_u64() { + return Some(std::time::Duration::from_secs(seconds)); + } + if let Some(seconds) = backoff_seconds.as_f64() { + return Some(std::time::Duration::from_secs(seconds as u64 + 1)); + } + } + } + + None + } + } + + impl Layer for RetryBackoffLayer { + type Service = RetryBackoffService; + + fn layer(&self, inner: S) -> Self::Service { + RetryBackoffService { + inner, + policy: RetryPolicy, + max_retries: self.max_retries, + initial_backoff: self.initial_backoff, + requests_enqueued: Arc::new(AtomicU32::new(0)), + } + } + } + + /// A Tower Service used by the [RetryBackoffLayer] that is responsible for retrying all requests on error. + /// See [TransportError] and [RetryPolicy]. + #[derive(Debug, Clone)] + pub struct RetryBackoffService { + /// The inner service + inner: S, + /// The retry policy + policy: RetryPolicy, + /// The maximum number of retries for errors + max_retries: u32, + /// The initial backoff in milliseconds + initial_backoff: u64, + /// The number of requests currently enqueued + requests_enqueued: Arc, + } + + impl RetryBackoffService { + const fn initial_backoff(&self) -> Duration { + Duration::from_millis(self.initial_backoff) + } + } + + impl Service for RetryBackoffService + where + S: Service + + Send + + 'static + + Clone, + S::Future: Send + 'static, + { + type Response = ResponsePacket; + type Error = TransportError; + type Future = TransportFut<'static>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, request: RequestPacket) -> Self::Future { + let inner = self.inner.clone(); + let this = self.clone(); + let mut inner = std::mem::replace(&mut self.inner, inner); + Box::pin(async move { + let _ = this.requests_enqueued.fetch_add(1, Ordering::SeqCst) as u64; + let mut retry_count: u32 = 0; + loop { + let err; + let res = inner.call(request.clone()).await; + + match res { + Ok(res) => { + if let Some(e) = res.as_error() { + err = TransportError::ErrorResp(e.clone()) + } else { + this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); + return Ok(res); + } + }, + Err(e) => err = e, + } + + let should_retry = this.policy.should_retry(&err); + if should_retry { + retry_count += 1; + if retry_count > this.max_retries { + return Err(TransportErrorKind::custom_str(&format!( + "Max retries exceeded {}", + err + ))); + } + + let _ = this.requests_enqueued.load(Ordering::SeqCst) as u64; + + // try to extract the requested backoff from the error or compute the next + // backoff based on retry count + let backoff_hint = this.policy.backoff_hint(&err); + let next_backoff = backoff_hint.unwrap_or_else(|| this.initial_backoff()); + + sleep(next_backoff).await; + } else { + this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); + return Err(err); + } + } + }) + } + } +} diff --git a/primitives/src/periodic.rs b/primitives/src/periodic.rs index f4e42af5..f7d91cce 100644 --- a/primitives/src/periodic.rs +++ b/primitives/src/periodic.rs @@ -1,8 +1,8 @@ -use ethers::types::U256; +use alloy::primitives::{ChainId, U256}; use serde::Deserialize; use tokio::sync::mpsc::{error::SendError, UnboundedSender}; -use crate::{contracts::socket::SocketMessage, eth::ChainID}; +use crate::contracts::socket::Socket_Struct::Socket_Message; #[derive(Clone, Debug, Deserialize)] pub struct PriceResponse { @@ -27,13 +27,13 @@ pub enum PriceSource { pub struct RollbackableMessage { /// The timestamp that this relayer has tried to process the socket event. /// If time passed as long as `ROLLBACK_CHECK_MINIMUM_INTERVAL`, this request will be rollbacked. - pub timeout_started_at: U256, + pub timeout_started_at: u64, /// The rollbackable socket message. This will be either `Inbound::Requested` or `Outbound::Accepted`. - pub socket_msg: SocketMessage, + pub socket_msg: Socket_Message, } impl RollbackableMessage { - pub fn new(timestamp: U256, socket_msg: SocketMessage) -> Self { + pub fn new(timestamp: u64, socket_msg: Socket_Message) -> Self { Self { timeout_started_at: timestamp, socket_msg } } } @@ -44,17 +44,17 @@ pub type RawRequestID = u128; /// The channel message sender for rollbackable socket messages. pub struct RollbackSender { /// The unique chain ID for this sender. - pub id: ChainID, + pub id: ChainId, /// The channel message sender. - pub sender: UnboundedSender, + pub sender: UnboundedSender, } impl RollbackSender { - pub fn new(id: ChainID, sender: UnboundedSender) -> Self { + pub fn new(id: ChainId, sender: UnboundedSender) -> Self { Self { id, sender } } - pub fn send(&self, message: SocketMessage) -> Result<(), SendError> { + pub fn send(&self, message: Socket_Message) -> Result<(), SendError> { self.sender.send(message) } } diff --git a/primitives/src/tx.rs b/primitives/src/tx.rs index 134829d5..08a263e8 100644 --- a/primitives/src/tx.rs +++ b/primitives/src/tx.rs @@ -3,13 +3,12 @@ use std::{ fmt::{Display, Formatter}, }; -use bitcoincore_rpc::bitcoin::PublicKey; -use ethers::types::{ - transaction::eip2718::TypedTransaction, Address, Bytes, Eip1559TransactionRequest, - NameOrAddress, TransactionRequest, H256, U256, +use alloy::{ + primitives::{Address, ChainId, B256, U256}, + rpc::types::TransactionRequest, }; -use miniscript::bitcoin::address::NetworkUnchecked; -use miniscript::bitcoin::{Address as BtcAddress, Txid}; +use bitcoincore_rpc::bitcoin::PublicKey; +use miniscript::bitcoin::{address::NetworkUnchecked, Address as BtcAddress, Txid}; use subxt::{ ext::subxt_core::Error, tx::{DefaultPayload, Payload}, @@ -19,7 +18,7 @@ use tokio::sync::mpsc::{error::SendError, UnboundedSender}; use crate::{ constants::tx::{DEFAULT_TX_RETRIES, DEFAULT_TX_RETRY_INTERVAL_MS}, - eth::{ChainID, GasCoefficient, SocketEventStatus}, + eth::{GasCoefficient, SocketEventStatus}, periodic::PriceResponse, substrate::{ ApproveSetRefunds, SubmitExecutedRequest, SubmitRollbackPoll, SubmitSignedPsbt, @@ -36,9 +35,9 @@ pub struct SocketRelayMetadata { /// The socket request sequence ID. pub sequence: u128, /// The source chain ID. - pub src_chain_id: ChainID, + pub src_chain_id: ChainId, /// The destination chain ID. - pub dst_chain_id: ChainID, + pub dst_chain_id: ChainId, /// The receiver address for this request. pub receiver: Address, /// The flag whether this relay is processed on bootstrap. @@ -50,8 +49,8 @@ impl SocketRelayMetadata { is_inbound: bool, status: SocketEventStatus, sequence: u128, - src_chain_id: ChainID, - dst_chain_id: ChainID, + src_chain_id: ChainId, + dst_chain_id: ChainId, receiver: Address, is_bootstrap: bool, ) -> Self { @@ -121,11 +120,11 @@ pub struct VSPPhase2Metadata { /// The round index to update. pub round: U256, /// The destination chain ID to update. - pub dst_chain_id: ChainID, + pub dst_chain_id: ChainId, } impl VSPPhase2Metadata { - pub fn new(round: U256, dst_chain_id: ChainID) -> Self { + pub fn new(round: U256, dst_chain_id: ChainId) -> Self { Self { round, dst_chain_id } } } @@ -190,9 +189,9 @@ pub struct RollbackMetadata { /// The socket request sequence ID. pub sequence: u128, /// The source chain ID. - pub src_chain_id: ChainID, + pub src_chain_id: ChainId, /// The destination chain ID. - pub dst_chain_id: ChainID, + pub dst_chain_id: ChainId, } impl RollbackMetadata { @@ -200,8 +199,8 @@ impl RollbackMetadata { is_inbound: bool, status: SocketEventStatus, sequence: u128, - src_chain_id: ChainID, - dst_chain_id: ChainID, + src_chain_id: ChainId, + dst_chain_id: ChainId, ) -> Self { Self { is_inbound, status, sequence, src_chain_id, dst_chain_id } } @@ -305,11 +304,11 @@ impl Display for SubmitVaultKeyMetadata { #[derive(Clone, Debug)] /// The metadata used for signed psbt submission. pub struct SubmitSignedPsbtMetadata { - pub unsigned_psbt: H256, + pub unsigned_psbt: B256, } impl SubmitSignedPsbtMetadata { - pub fn new(unsigned_psbt: H256) -> Self { + pub fn new(unsigned_psbt: B256) -> Self { Self { unsigned_psbt } } } @@ -323,11 +322,11 @@ impl Display for SubmitSignedPsbtMetadata { #[derive(Clone, Debug)] /// The metadata used for unsigned psbt submission. pub struct SubmitUnsignedPsbtMetadata { - pub unsigned_psbt: H256, + pub unsigned_psbt: B256, } impl SubmitUnsignedPsbtMetadata { - pub fn new(unsigned_psbt: H256) -> Self { + pub fn new(unsigned_psbt: B256) -> Self { Self { unsigned_psbt } } } @@ -340,11 +339,11 @@ impl Display for SubmitUnsignedPsbtMetadata { #[derive(Clone, Debug)] pub struct SubmitExecutedRequestMetadata { - pub txid: H256, + pub txid: B256, } impl SubmitExecutedRequestMetadata { - pub fn new(txid: H256) -> Self { + pub fn new(txid: B256) -> Self { Self { txid } } } @@ -358,12 +357,12 @@ impl Display for SubmitExecutedRequestMetadata { #[derive(Clone, Debug)] /// The metadata used for rollback poll submission. pub struct SubmitRollbackPollMetadata { - pub txid: H256, + pub txid: B256, pub is_approved: bool, } impl SubmitRollbackPollMetadata { - pub fn new(txid: H256, is_approved: bool) -> Self { + pub fn new(txid: B256, is_approved: bool) -> Self { Self { txid, is_approved } } } @@ -640,170 +639,6 @@ impl From> for XtRequest { } } -/// Wrapper for TransactionRequest|Eip1559TransactionRequest to support both fee payment in one -/// relayer -#[derive(Clone, Debug)] -pub enum TxRequest { - Legacy(TransactionRequest), - Eip1559(Eip1559TransactionRequest), -} - -impl TxRequest { - /// Get the `data` field of the transaction request. - pub fn get_data(&self) -> &Bytes { - match self { - TxRequest::Legacy(tx_request) => tx_request.data.as_ref().unwrap(), - TxRequest::Eip1559(tx_request) => tx_request.data.as_ref().unwrap(), - } - } - - /// Get the `to` field of the transaction request. - pub fn get_to(&self) -> &NameOrAddress { - match self { - TxRequest::Legacy(tx_request) => tx_request.to.as_ref().unwrap(), - TxRequest::Eip1559(tx_request) => tx_request.to.as_ref().unwrap(), - } - } - - /// Get the `from` field of the transaction request. - pub fn get_from(&self) -> &Address { - match self { - TxRequest::Legacy(tx_request) => tx_request.from.as_ref().unwrap(), - TxRequest::Eip1559(tx_request) => tx_request.from.as_ref().unwrap(), - } - } - - /// Get the `gas_price` field of the transaction request. - pub fn get_gas_price(&self) -> Option { - match self { - TxRequest::Legacy(tx_request) => tx_request.gas_price, - TxRequest::Eip1559(_) => None, - } - } - - /// Sets the `from` field in the transaction to the provided value. - pub fn from(&mut self, address: Address) { - match self { - TxRequest::Legacy(tx_request) => { - tx_request.from = Some(address); - }, - TxRequest::Eip1559(tx_request) => { - tx_request.from = Some(address); - }, - } - } - - /// Get the `gas` field of the transaction request. - pub fn get_gas(&self) -> Option { - match self { - TxRequest::Legacy(tx_request) => tx_request.gas, - TxRequest::Eip1559(tx_request) => tx_request.gas, - } - } - - /// Sets the `gas` field in the transaction to the provided. - pub fn gas(&mut self, estimated_gas: U256) { - match self { - TxRequest::Legacy(tx_request) => { - tx_request.gas = Some(estimated_gas); - }, - TxRequest::Eip1559(tx_request) => { - tx_request.gas = Some(estimated_gas); - }, - } - } - - /// Sets the `max_fee_per_gas` field in the transaction request. - /// This method will only have effect when the type is EIP-1559. - /// It will be ignored if the type is legacy. - pub fn max_fee_per_gas(&mut self, max_fee_per_gas: U256) { - match self { - TxRequest::Legacy(_) => {}, - TxRequest::Eip1559(tx_request) => { - tx_request.max_fee_per_gas = Some(max_fee_per_gas); - }, - } - } - - /// Sets the `max_priority_fee_per_gas` field in the transaction request. - /// This method will only have effect when the type is EIP-1559. - /// It will be ignored if the type is legacy. - pub fn max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: U256) { - match self { - TxRequest::Legacy(_) => {}, - TxRequest::Eip1559(tx_request) => { - tx_request.max_priority_fee_per_gas = Some(max_priority_fee_per_gas); - }, - } - } - - /// Sets the `gas_price` field in the transaction request. - /// This method will only have effect when the type is legacy. - /// It will be ignored if the type is EIP-1559. - pub fn gas_price(&mut self, gas_price: U256) { - match self { - TxRequest::Legacy(tx_request) => { - tx_request.gas_price = Some(gas_price); - }, - TxRequest::Eip1559(_) => {}, - } - } - - /// Sets the `nonce` field in the transaction request. - pub fn nonce(&mut self, nonce: Option) { - match self { - TxRequest::Legacy(tx_request) => { - tx_request.nonce = nonce; - }, - TxRequest::Eip1559(tx_request) => { - tx_request.nonce = nonce; - }, - } - } - - /// If self is Eip1559, returns it self. - /// If self is Legacy, converts it self to Eip1559 and return it. - pub fn to_eip1559(&self) -> Eip1559TransactionRequest { - match self { - TxRequest::Legacy(tx_request) => Eip1559TransactionRequest { - from: tx_request.from, - to: tx_request.to.clone(), - value: tx_request.value, - nonce: tx_request.nonce, - data: tx_request.data.clone(), - gas: tx_request.gas, - ..Default::default() - }, - TxRequest::Eip1559(tx_request) => tx_request.clone(), - } - } - - /// If self is Eip1559, converts it self to Legacy and return it. - /// If self is Legacy, returns it self. - pub fn to_legacy(&self) -> TransactionRequest { - match self { - TxRequest::Legacy(tx_request) => tx_request.clone(), - TxRequest::Eip1559(tx_request) => TransactionRequest { - from: tx_request.from, - to: tx_request.to.clone(), - value: tx_request.value, - nonce: tx_request.nonce, - data: tx_request.data.clone(), - gas: tx_request.gas, - ..Default::default() - }, - } - } - - /// Converts to `TypedTransaction`. - pub fn to_typed(&self) -> TypedTransaction { - match self { - TxRequest::Legacy(tx_request) => TypedTransaction::Legacy(tx_request.clone()), - TxRequest::Eip1559(tx_request) => TypedTransaction::Eip1559(tx_request.clone()), - } - } -} - #[derive(Clone, Debug)] /// The message format passed through the event channel. pub struct TxRequestMessage { @@ -812,7 +647,7 @@ pub struct TxRequestMessage { /// The retry interval in milliseconds. pub retry_interval: u64, /// The raw transaction request. - pub tx_request: TxRequest, + pub tx_request: TransactionRequest, /// Additional data of the transaction request. pub metadata: TxRequestMetadata, /// Check mempool to prevent duplicate relay. @@ -829,7 +664,7 @@ pub struct TxRequestMessage { impl TxRequestMessage { /// Instantiates a new `TxRequestMessage` instance. pub fn new( - tx_request: TxRequest, + tx_request: TransactionRequest, metadata: TxRequestMetadata, check_mempool: bool, give_random_delay: bool, @@ -851,7 +686,7 @@ impl TxRequestMessage { /// Builds a new `TxRequestMessage` to use on transaction retry. This will reduce the remaining /// retry counter and increase the retry interval. pub fn build_retry_event(&mut self) { - self.tx_request.nonce(None); + self.tx_request.nonce = None; self.retries_remaining = self.retries_remaining.saturating_sub(1); } } @@ -859,7 +694,7 @@ impl TxRequestMessage { /// The message sender connected to the event channel. pub struct TxRequestSender { /// The chain ID of the event channel. - pub id: ChainID, + pub id: ChainId, /// The message sender. pub sender: UnboundedSender, /// Is Bifrost network? @@ -868,7 +703,7 @@ pub struct TxRequestSender { impl TxRequestSender { /// Instantiates a new `TxRequestSender` instance. - pub fn new(id: ChainID, sender: UnboundedSender, is_native: bool) -> Self { + pub fn new(id: ChainId, sender: UnboundedSender, is_native: bool) -> Self { Self { id, sender, is_native } } diff --git a/primitives/src/utils.rs b/primitives/src/utils.rs index 712ea8fb..f9c911f6 100644 --- a/primitives/src/utils.rs +++ b/primitives/src/utils.rs @@ -1,7 +1,6 @@ -use ethers::{ - types::{Signature as EthersSignature, H256}, - utils::keccak256, -}; +use alloy::primitives::{keccak256, Address, Signature as EthersSignature, B256}; +use k256::{ecdsa::VerifyingKey, elliptic_curve::sec1::ToEncodedPoint}; +use sha3::{Digest, Keccak256}; use crate::substrate::{EthereumSignature, Signature}; @@ -16,6 +15,21 @@ pub fn convert_ethers_to_ecdsa_signature(ethers_signature: EthersSignature) -> E } /// Hash the given bytes. -pub fn hash_bytes(bytes: &Vec) -> H256 { - H256::from(keccak256(bytes)) +pub fn hash_bytes(bytes: &Vec) -> B256 { + B256::from(keccak256(bytes)) +} + +/// Recovers the address from the given signature and message. +pub fn recover_message(sig: EthersSignature, msg: &[u8]) -> Address { + let r = sig.r().to_be_bytes::<32>(); + let s = sig.s().to_be_bytes::<32>(); + let v = sig.v().recid(); + let rs = k256::ecdsa::Signature::from_slice([r, s].concat().as_slice()).unwrap(); + + let verify_key = + VerifyingKey::recover_from_digest(Keccak256::new_with_prefix(msg), &rs, v).unwrap(); + let public_key = k256::PublicKey::from(&verify_key).to_encoded_point(false); + let hash = keccak256(&public_key.as_bytes()[1..]); + + Address::from_slice(&hash[12..]) } diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index 427ffb5c..15b8984a 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -15,7 +15,7 @@ log = { workspace = true } env_logger = { workspace = true } chrono = { workspace = true } serde_yaml = { workspace = true } -ethers = { workspace = true } +alloy = { workspace = true } futures = { workspace = true } clap = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = [ @@ -26,6 +26,7 @@ tokio = { workspace = true, features = [ miniscript = { workspace = true } bitcoincore-rpc = { workspace = true } subxt = { workspace = true } +eyre = { workspace = true } # Bifrost Relayer br-cli = { path = "../client/cli", default-features = false } diff --git a/relayer/src/main.rs b/relayer/src/main.rs index abacde3a..0f007ecb 100644 --- a/relayer/src/main.rs +++ b/relayer/src/main.rs @@ -1,5 +1,6 @@ mod cli; mod service; +mod service_deps; mod verification; use std::io::Write; diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 9571f725..d0b94b42 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -1,12 +1,19 @@ use std::{ collections::BTreeMap, net::{Ipv4Addr, SocketAddr}, + str::FromStr, sync::Arc, time::Duration, }; -use bitcoincore_rpc::{Auth, Client as BitcoinClient}; -use ethers::providers::{Http, Provider}; +use alloy::{ + network::EthereumWallet, + primitives::ChainId, + providers::{fillers::TxFiller, Provider, ProviderBuilder, WalletProvider}, + rpc::client::RpcClient, + signers::{local::PrivateKeySigner, Signer}, + transports::{http::reqwest::Url, Transport}, +}; use futures::FutureExt; use miniscript::bitcoin::Network; use sc_service::{config::PrometheusConfig, Error as ServiceError, TaskManager}; @@ -14,385 +21,118 @@ use tokio::sync::RwLock; use br_client::{ btc::{ - block::BlockManager, - handlers::{Handler as BitcoinHandler, InboundHandler, OutboundHandler}, - storage::keypair::KeypairStorage, - storage::pending_outbound::PendingOutboundPool, - }, - eth::{ - events::EventManager, - handlers::{RoundupRelayHandler, SocketRelayHandler}, - traits::{Handler, TransactionManager}, - tx::{Eip1559TransactionManager, LegacyTransactionManager}, - wallet::WalletManager, - EthClient, + handlers::Handler as _, + storage::{keypair::KeypairStorage, pending_outbound::PendingOutboundPool}, }, - substrate::tx::UnsignedTransactionManager, -}; -use br_periodic::{ - traits::PeriodicWorker, BitcoinRollbackVerifier, HeartbeatSender, KeypairMigrator, - OraclePriceFeeder, PsbtSigner, PubKeyPreSubmitter, PubKeySubmitter, RoundupEmitter, - SocketRollbackEmitter, + eth::{traits::Handler as _, EthClient}, }; +use br_periodic::traits::PeriodicWorker; use br_primitives::{ bootstrap::BootstrapSharedData, cli::{Configuration, HandlerType}, constants::{ - cli::{ - DEFAULT_BITCOIN_BLOCK_CONFIRMATIONS, DEFAULT_GET_LOGS_BATCH_SIZE, - DEFAULT_KEYSTORE_PATH, DEFAULT_MIN_PRIORITY_FEE, DEFAULT_PROMETHEUS_PORT, - }, + cli::{DEFAULT_GET_LOGS_BATCH_SIZE, DEFAULT_KEYSTORE_PATH, DEFAULT_PROMETHEUS_PORT}, errors::{ - INVALID_BIFROST_NATIVENESS, INVALID_BITCOIN_NETWORK, INVALID_CHAIN_ID, - INVALID_PRIVATE_KEY, INVALID_PROVIDER_URL, + INVALID_BIFROST_NATIVENESS, INVALID_BITCOIN_NETWORK, INVALID_PRIVATE_KEY, + INVALID_PROVIDER_URL, }, + tx::DEFAULT_CALL_RETRIES, + }, + eth::{ + retry::RetryBackoffLayer, AggregatorContracts, BootstrapState, ProtocolContracts, + ProviderMetadata, }, - eth::{AggregatorContracts, BootstrapState, ChainID, ProtocolContracts, ProviderMetadata}, - periodic::RollbackSender, substrate::MigrationSequence, - tx::{TxRequestSender, XtRequestSender}, utils::sub_display_format, }; use crate::{ cli::{LOG_TARGET, SUB_LOG_TARGET}, + service_deps::{BtcDeps, FullDeps, HandlerDeps, ManagerDeps, PeriodicDeps, SubstrateDeps}, verification::assert_configuration_validity, }; /// Starts the relayer service. pub fn relay(config: Configuration) -> Result { - new_relay_base(config).map(|RelayBase { task_manager, .. }| task_manager) -} - -/// Initializes periodic components. -fn construct_periodics( - bootstrap_shared_data: BootstrapSharedData, - migration_sequence: Arc>, - keypair_storage: Arc>, - relayer_deps: &ManagerDeps, - substrate_deps: &SubstrateDeps, -) -> PeriodicDeps { - let clients = &relayer_deps.clients; - let tx_request_senders = &relayer_deps.tx_request_senders; - - let mut rollback_emitters = vec![]; - let mut rollback_senders = BTreeMap::new(); - - // initialize the heartbeat sender - let heartbeat_sender = HeartbeatSender::new(tx_request_senders.clone(), clients.clone()); - - // initialize the oracle price feeder - let oracle_price_feeder = OraclePriceFeeder::new(tx_request_senders.clone(), clients.clone()); - - // initialize the roundup emitter - let roundup_emitter = RoundupEmitter::new( - tx_request_senders.clone(), - clients.clone(), - Arc::new(bootstrap_shared_data.clone()), - ); - - // initialize socket rollback handlers - tx_request_senders.iter().for_each(|tx_request_sender| { - let (rollback_emitter, rollback_sender) = - SocketRollbackEmitter::new(tx_request_sender.clone(), clients.clone()); - rollback_emitters.push(rollback_emitter); - rollback_senders.insert( - tx_request_sender.id, - Arc::new(RollbackSender::new(tx_request_sender.id, rollback_sender)), - ); - }); - - // initialize migration detector - let bfc_client = clients - .iter() - .find(|client| client.metadata.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(); - let keypair_migrator = KeypairMigrator::new( - bfc_client.clone(), - migration_sequence.clone(), - keypair_storage.clone(), - ); - let presubmitter = PubKeyPreSubmitter::new( - bfc_client.clone(), - substrate_deps.xt_request_sender.clone(), - keypair_storage.clone(), - migration_sequence.clone(), - ); - - PeriodicDeps { - heartbeat_sender, - oracle_price_feeder, - roundup_emitter, - rollback_emitters, - rollback_senders, - keypair_migrator, - presubmitter, - } -} - -/// Initializes `Socket` & `RoundUp` handlers. -fn construct_handlers( - config: &Configuration, - periodic_deps: &PeriodicDeps, - manager_deps: &ManagerDeps, - bootstrap_shared_data: BootstrapSharedData, -) -> HandlerDeps { - let mut handlers = (vec![], vec![]); - let PeriodicDeps { rollback_senders, .. } = periodic_deps; - let ManagerDeps { clients, event_managers, tx_request_senders, .. } = manager_deps; - - config.relayer_config.handler_configs.iter().for_each(|handler_config| { - match handler_config.handler_type { - HandlerType::Socket => handler_config.watch_list.iter().for_each(|target| { - handlers.0.push(SocketRelayHandler::new( - *target, - tx_request_senders.clone(), - rollback_senders.clone(), - event_managers.get(target).expect(INVALID_CHAIN_ID).sender.subscribe(), - clients.clone(), - Arc::new(bootstrap_shared_data.clone()), - )); - }), - HandlerType::Roundup => { - handlers.1.push(RoundupRelayHandler::new( - tx_request_senders.clone(), - event_managers - .get(&handler_config.watch_list[0]) - .expect(INVALID_CHAIN_ID) - .sender - .subscribe(), - clients.clone(), - Arc::new(bootstrap_shared_data.clone()), - )); - }, - } - }); - HandlerDeps { socket_relay_handlers: handlers.0, roundup_relay_handlers: handlers.1 } -} + assert_configuration_validity(&config); -/// Initializes the `EthClient`, `TransactionManager`, `EventManager`, `TxRequestSender` for each chain. -fn construct_managers( - config: &Configuration, - bootstrap_shared_data: BootstrapSharedData, - task_manager: &TaskManager, -) -> ManagerDeps { - let prometheus_config = &config.relayer_config.prometheus_config; let evm_providers = &config.relayer_config.evm_providers; let btc_provider = &config.relayer_config.btc_provider; let system = &config.relayer_config.system; - let mut clients = vec![]; - let mut tx_managers = (vec![], vec![]); - let mut event_managers = BTreeMap::new(); - let mut tx_request_senders = vec![]; - - // iterate each evm provider and construct inner components. - evm_providers.iter().for_each(|evm_provider| { - let is_native = evm_provider.is_native.unwrap_or(false); - let provider = Provider::::try_from(evm_provider.provider.clone()) - .expect(INVALID_PROVIDER_URL) - .interval(Duration::from_millis(evm_provider.call_interval)); - - let client = Arc::new(EthClient::new( - WalletManager::from_private_key(system.private_key.as_str(), evm_provider.id) - .expect(INVALID_PRIVATE_KEY), - Arc::new(provider.clone()), - ProviderMetadata::new( - evm_provider.name.clone(), - provider.url().to_string(), - evm_provider.id, - if is_native { Some(btc_provider.id) } else { None }, - evm_provider.block_confirmations, - evm_provider.call_interval, - evm_provider.get_logs_batch_size.unwrap_or(DEFAULT_GET_LOGS_BATCH_SIZE), - is_native, - ), - ProtocolContracts::new( - is_native, - Arc::new(provider.clone()), - evm_provider.socket_address.clone(), - evm_provider.authority_address.clone(), - evm_provider.relayer_manager_address.clone(), - evm_provider.bitcoin_socket_address.clone(), - evm_provider.socket_queue_address.clone(), - evm_provider.registration_pool_address.clone(), - evm_provider.relay_executive_address.clone(), - ), - AggregatorContracts::new( - Arc::new(provider), - evm_provider.chainlink_usdc_usd_address.clone(), - evm_provider.chainlink_usdt_usd_address.clone(), - evm_provider.chainlink_dai_usd_address.clone(), - evm_provider.chainlink_btc_usd_address.clone(), - evm_provider.chainlink_wbtc_usd_address.clone(), - evm_provider.chainlink_cbbtc_usd_address.clone(), - ), - system.debug_mode.unwrap_or(false), - )); - - if evm_provider.is_relay_target { - if evm_provider.eip1559.unwrap_or(false) { - let (tx_manager, sender) = Eip1559TransactionManager::new( - client.clone(), - evm_provider.min_priority_fee.unwrap_or(DEFAULT_MIN_PRIORITY_FEE).into(), - evm_provider.duplicate_confirm_delay, - task_manager.spawn_handle(), - ); - tx_managers.1.push(tx_manager); - tx_request_senders.push(Arc::new(TxRequestSender::new( + let clients = evm_providers + .iter() + .map(|evm_provider| { + let url: Url = evm_provider.provider.clone().parse().expect(INVALID_PROVIDER_URL); + let is_native = evm_provider.is_native.unwrap_or(false); + + let mut signer = + PrivateKeySigner::from_str(&system.private_key).expect(INVALID_PRIVATE_KEY); + signer.set_chain_id(Some(evm_provider.id)); + let wallet = EthereumWallet::from(signer.clone()); + + let client = RpcClient::builder() + .layer(RetryBackoffLayer::new(DEFAULT_CALL_RETRIES, evm_provider.call_interval)) + .http(url.clone()) + .with_poll_interval(Duration::from_millis(evm_provider.call_interval)); + let provider = Arc::new( + ProviderBuilder::new() + .with_recommended_fillers() + .wallet(wallet) + .on_client(client), + ); + let client = Arc::new(EthClient::new( + provider.clone(), + signer.clone(), + ProviderMetadata::new( + evm_provider.name.clone(), + url.clone(), evm_provider.id, - sender, + if is_native { Some(btc_provider.id) } else { None }, + evm_provider.block_confirmations, + evm_provider.call_interval, + evm_provider.get_logs_batch_size.unwrap_or(DEFAULT_GET_LOGS_BATCH_SIZE), is_native, - ))); - } else { - let (tx_manager, sender) = LegacyTransactionManager::new( - client.clone(), - evm_provider.escalate_percentage, - evm_provider.min_gas_price, - evm_provider.is_initially_escalated.unwrap_or(false), - evm_provider.duplicate_confirm_delay, - task_manager.spawn_handle(), - ); - tx_managers.0.push(tx_manager); - tx_request_senders.push(Arc::new(TxRequestSender::new( - evm_provider.id, - sender, + ), + ProtocolContracts::new( is_native, - ))); - } - } - let event_manager = EventManager::new( - client.clone(), - Arc::new(bootstrap_shared_data.clone()), - match &prometheus_config { - Some(config) => config.is_enabled, - None => false, - }, - ); - - clients.push(client); - event_managers.insert(event_manager.client.get_chain_id(), event_manager); - }); - - ManagerDeps { clients, tx_managers, event_managers, tx_request_senders } -} - -/// Initializes Bitcoin related instances. -fn construct_btc_deps( - config: &Configuration, - pending_outbounds: PendingOutboundPool, - keypair_storage: Arc>, - bootstrap_shared_data: BootstrapSharedData, - manager_deps: &ManagerDeps, - substrate_deps: &SubstrateDeps, - migration_sequence: Arc>, -) -> BtcDeps { - let bootstrap_shared_data = Arc::new(bootstrap_shared_data.clone()); - let network = Network::from_core_arg(&config.relayer_config.btc_provider.chain) - .expect(INVALID_BITCOIN_NETWORK); - - let auth = match ( - config.relayer_config.btc_provider.username.clone(), - config.relayer_config.btc_provider.password.clone(), - ) { - (Some(username), Some(password)) => Auth::UserPass(username, password), - _ => Auth::None, - }; - let btc_client = BitcoinClient::new( - &config.relayer_config.btc_provider.provider, - auth, - config.relayer_config.btc_provider.wallet.clone(), - Some(60), - ) - .expect(INVALID_PROVIDER_URL); - - let bfc_client = manager_deps - .clients - .iter() - .find(|client| client.metadata.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(); - let tx_request_sender = manager_deps - .tx_request_senders - .iter() - .find(|sender| sender.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(); - - let block_manager = BlockManager::new( - btc_client.clone(), - bfc_client.clone(), - pending_outbounds.clone(), - bootstrap_shared_data.clone(), - config.relayer_config.btc_provider.call_interval.clone(), - config - .relayer_config - .btc_provider - .block_confirmations - .unwrap_or(DEFAULT_BITCOIN_BLOCK_CONFIRMATIONS), - ); - let inbound = InboundHandler::new( - bfc_client.clone(), - tx_request_sender.clone(), - block_manager.subscribe(), - bootstrap_shared_data.clone(), - ); - let outbound = OutboundHandler::new( - bfc_client.clone(), - tx_request_sender.clone(), - block_manager.subscribe(), - bootstrap_shared_data.clone(), - ); - - let psbt_signer = PsbtSigner::new( - bfc_client.clone(), - substrate_deps.xt_request_sender.clone(), - keypair_storage.clone(), - migration_sequence.clone(), - network, - ); - let pub_key_submitter = PubKeySubmitter::new( - bfc_client.clone(), - substrate_deps.xt_request_sender.clone(), - keypair_storage.clone(), - migration_sequence.clone(), - ); - let rollback_verifier = BitcoinRollbackVerifier::new( - btc_client.clone(), - bfc_client.clone(), - substrate_deps.xt_request_sender.clone(), - ); - - BtcDeps { outbound, inbound, block_manager, psbt_signer, pub_key_submitter, rollback_verifier } -} - -/// Initializes Substrate related instances. -fn construct_substrate_deps( - manager_deps: &ManagerDeps, - task_manager: &TaskManager, -) -> SubstrateDeps { - let bfc_client = manager_deps - .clients - .iter() - .find(|client| client.metadata.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(); - - let (unsigned_tx_manager, sender) = - UnsignedTransactionManager::new(bfc_client.clone(), task_manager.spawn_handle()); - - let xt_request_sender = Arc::new(XtRequestSender::new(sender)); + provider.clone(), + evm_provider.socket_address.clone(), + evm_provider.authority_address.clone(), + evm_provider.relayer_manager_address.clone(), + evm_provider.bitcoin_socket_address.clone(), + evm_provider.socket_queue_address.clone(), + evm_provider.registration_pool_address.clone(), + evm_provider.relay_executive_address.clone(), + ), + AggregatorContracts::new( + provider.clone(), + evm_provider.chainlink_usdc_usd_address.clone(), + evm_provider.chainlink_usdt_usd_address.clone(), + evm_provider.chainlink_dai_usd_address.clone(), + evm_provider.chainlink_btc_usd_address.clone(), + evm_provider.chainlink_wbtc_usd_address.clone(), + evm_provider.chainlink_cbbtc_usd_address.clone(), + ), + )); + (evm_provider.id, client) + }) + .collect::>(); - SubstrateDeps { unsigned_tx_manager, xt_request_sender } + new_relay_base(config, clients).map(|RelayBase { task_manager, .. }| task_manager) } /// Spawn relayer service tasks by the `TaskManager`. -fn spawn_relayer_tasks( +fn spawn_relayer_tasks( task_manager: TaskManager, - deps: FullDeps, + deps: FullDeps, config: &Configuration, -) -> TaskManager { +) -> TaskManager +where + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, + T: Transport + Clone, +{ let prometheus_config = &config.relayer_config.prometheus_config; let FullDeps { @@ -405,12 +145,11 @@ fn spawn_relayer_tasks( } = deps; let BootstrapSharedData { socket_barrier, bootstrap_states, .. } = bootstrap_shared_data; - let ManagerDeps { tx_managers, event_managers, .. } = manager_deps; + let ManagerDeps { event_managers, .. } = manager_deps; let PeriodicDeps { mut heartbeat_sender, mut oracle_price_feeder, mut roundup_emitter, - rollback_emitters, mut keypair_migrator, mut presubmitter, .. @@ -430,38 +169,22 @@ fn spawn_relayer_tasks( task_manager.spawn_essential_handle().spawn( "migration-detector", Some("migration-detector"), - async move { keypair_migrator.run().await }, + async move { + keypair_migrator.run().await; + () + }, ); // spawn public key presubmitter task_manager.spawn_essential_handle().spawn( "pub-key-presubmitter", Some("pub-key-presubmitter"), - async move { presubmitter.run().await }, + async move { + presubmitter.run().await; + () + }, ); - // spawn legacy transaction managers - tx_managers.0.into_iter().for_each(|mut tx_manager| { - task_manager.spawn_essential_handle().spawn( - Box::leak( - format!("{}-transaction-manager", tx_manager.client.get_chain_name()) - .into_boxed_str(), - ), - Some("transaction-managers"), - async move { tx_manager.run().await }, - ) - }); - // spawn eip1559 transaction managers - tx_managers.1.into_iter().for_each(|mut tx_manager| { - task_manager.spawn_essential_handle().spawn( - Box::leak( - format!("{}-transaction-manager", tx_manager.client.get_chain_name()) - .into_boxed_str(), - ), - Some("transaction-managers"), - async move { tx_manager.run().await }, - ) - }); // spawn unsigned transaction manager task_manager.spawn_essential_handle().spawn( "unsigned-transaction-manager", @@ -472,7 +195,10 @@ fn spawn_relayer_tasks( // spawn heartbeat sender task_manager .spawn_essential_handle() - .spawn("heartbeat", Some("heartbeat"), async move { heartbeat_sender.run().await }); + .spawn("heartbeat", Some("heartbeat"), async move { + heartbeat_sender.run().await; + () + }); // spawn oracle price feeder task_manager.spawn_essential_handle().spawn( @@ -481,19 +207,11 @@ fn spawn_relayer_tasks( .into_boxed_str(), ), Some("oracle"), - async move { oracle_price_feeder.run().await }, + async move { + oracle_price_feeder.run().await; + () + }, ); - // spawn socket rollback emitters - rollback_emitters.into_iter().for_each(|mut emitter| { - task_manager.spawn_essential_handle().spawn( - Box::leak( - format!("{}-socket-rollback-emitter", emitter.client.get_chain_name()) - .into_boxed_str(), - ), - Some("rollback"), - async move { emitter.run().await }, - ) - }); // spawn socket relay handlers socket_relay_handlers.into_iter().for_each(|mut handler| { @@ -518,7 +236,9 @@ fn spawn_relayer_tasks( } drop(guard); - handler.run().await + handler.run().await; + + () }, ); }); @@ -531,7 +251,10 @@ fn spawn_relayer_tasks( .into_boxed_str(), ), Some("handlers"), - async move { handler.run().await }, + async move { + handler.run().await; + () + }, ); }); @@ -539,7 +262,10 @@ fn spawn_relayer_tasks( task_manager.spawn_essential_handle().spawn( "roundup-emitter", Some("roundup-emitter"), - async move { roundup_emitter.run().await }, + async move { + roundup_emitter.run().await; + () + }, ); // spawn event managers @@ -551,8 +277,8 @@ fn spawn_relayer_tasks( Some("event-managers"), async move { event_manager.wait_provider_sync().await; - - event_manager.run().await + event_manager.run().await; + () }, ) }); @@ -561,27 +287,42 @@ fn spawn_relayer_tasks( task_manager.spawn_essential_handle().spawn( "bitcoin-inbound-handler", Some("handlers"), - async move { inbound.run().await }, + async move { + inbound.run().await; + () + }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-outbound-handler", Some("handlers"), - async move { outbound.run().await }, + async move { + outbound.run().await; + () + }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-psbt-signer", Some("handlers"), - async move { psbt_signer.run().await }, + async move { + psbt_signer.run().await; + () + }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-public-key-submitter", Some("pub-key-submitter"), - async move { pub_key_submitter.run().await }, + async move { + pub_key_submitter.run().await; + () + }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-rollback-verifier", Some("rollback-verifier"), - async move { rollback_verifier.run().await }, + async move { + rollback_verifier.run().await; + () + }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-block-manager", @@ -598,7 +339,9 @@ fn spawn_relayer_tasks( } drop(guard); - block_manager.run().await + block_manager.run().await; + + () }, ); @@ -633,48 +376,50 @@ fn spawn_relayer_tasks( } /// Log the configured relay targets. -fn print_relay_targets(manager_deps: &ManagerDeps) { - let tx_managers = &manager_deps.tx_managers; - +fn print_relay_targets(manager_deps: &ManagerDeps) +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + let bfc_client = manager_deps + .clients + .get(&manager_deps.bifrost_chain_id) + .expect(INVALID_BIFROST_NATIVENESS) + .clone(); log::info!( target: LOG_TARGET, "-[{}] 👤 Relayer: {:?}", sub_display_format(SUB_LOG_TARGET), - &manager_deps.clients[0].address() + bfc_client.address() + ); + log::info!( + target: LOG_TARGET, + "-[{}] 🔨 Relay Targets: {}", + sub_display_format(SUB_LOG_TARGET), + manager_deps + .clients + .iter() + .map(|(chain_id, client)| format!("{} ({})", client.get_chain_name(), chain_id)) + .collect::>() + .join(", ") ); - - if !tx_managers.0.is_empty() { - log::info!( - target: LOG_TARGET, - "-[{}] 🔨 Relay Targets (Legacy): {}", - sub_display_format(SUB_LOG_TARGET), - tx_managers.0 - .iter() - .map(|tx_manager| tx_manager.client.get_chain_name()) - .collect::>() - .join(", ") - ); - } - if !tx_managers.1.is_empty() { - log::info!( - target: LOG_TARGET, - "-[{}] 🔨 Relay Targets (EIP1559): {}", - sub_display_format(SUB_LOG_TARGET), - tx_managers.1 - .iter() - .map(|tx_manager| tx_manager.client.get_chain_name()) - .collect::>() - .join(", ") - ); - } } /// Builds the internal components for the relayer service and spawns asynchronous tasks. -fn new_relay_base(config: Configuration) -> Result { - assert_configuration_validity(&config); - +fn new_relay_base( + config: Configuration, + clients: BTreeMap>>, +) -> Result +where + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, + T: Transport + Clone, +{ let task_manager = TaskManager::new(config.clone().tokio_handle, None)?; + let clients = Arc::new(clients); + let bootstrap_shared_data = BootstrapSharedData::new(&config); let pending_outbounds = PendingOutboundPool::new(); @@ -692,18 +437,17 @@ fn new_relay_base(config: Configuration) -> Result { let migration_sequence = Arc::new(RwLock::new(MigrationSequence::Normal)); - let manager_deps = construct_managers(&config, bootstrap_shared_data.clone(), &task_manager); - let substrate_deps = construct_substrate_deps(&manager_deps, &task_manager); - let periodic_deps = construct_periodics( + let manager_deps = ManagerDeps::new(&config, clients, bootstrap_shared_data.clone()); + let substrate_deps = SubstrateDeps::new(&manager_deps, &task_manager); + let periodic_deps = PeriodicDeps::new( bootstrap_shared_data.clone(), migration_sequence.clone(), keypair_storage.clone(), &manager_deps, &substrate_deps, ); - let handler_deps = - construct_handlers(&config, &periodic_deps, &manager_deps, bootstrap_shared_data.clone()); - let btc_deps = construct_btc_deps( + let handler_deps = HandlerDeps::new(&config, &manager_deps, bootstrap_shared_data.clone()); + let btc_deps = BtcDeps::new( &config, pending_outbounds.clone(), keypair_storage.clone(), @@ -735,70 +479,3 @@ struct RelayBase { /// The task manager of the relayer. task_manager: TaskManager, } - -struct ManagerDeps { - /// The `EthClient`'s for each specified chain. - clients: Vec>>, - /// The `TransactionManager`'s for each specified chain. - tx_managers: (Vec>, Vec>), - /// The `EventManager`'s for each specified chain. - event_managers: BTreeMap>, - /// The `TxRequestSender`'s for each specified chain. - tx_request_senders: Vec>, -} - -struct BtcDeps { - /// The Bitcoin outbound handler. - outbound: OutboundHandler, - /// The Bitcoin inbound handler. - inbound: InboundHandler, - /// The Bitcoin block manager. - block_manager: BlockManager, - /// The Bitcoin PSBT signer. - psbt_signer: PsbtSigner, - /// The Bitcoin vault public key submitter. - pub_key_submitter: PubKeySubmitter, - /// The Bitcoin rollback verifier. - rollback_verifier: BitcoinRollbackVerifier, -} - -struct SubstrateDeps { - /// The `UnsignedTransactionManager` for Bifrost. - unsigned_tx_manager: UnsignedTransactionManager, - /// The `XtRequestSender` for Bifrost. - xt_request_sender: Arc, -} - -struct PeriodicDeps { - /// The `HeartbeatSender` used for system health checks. - heartbeat_sender: HeartbeatSender, - /// The `OraclePriceFeeder` used for price feeding. - oracle_price_feeder: OraclePriceFeeder, - /// The `RoundupEmitter` used for detecting and emitting new round updates. - roundup_emitter: RoundupEmitter, - /// The `SocketRollbackEmitter`'s for each specified chain. - rollback_emitters: Vec>, - /// The `RollbackSender`'s for each specified chain. - rollback_senders: BTreeMap>, - /// The `KeypairMigrator` used for detecting migration sequences. - keypair_migrator: KeypairMigrator, - /// The `PubKeyPreSubmitter` used for presubmitting public keys. - presubmitter: PubKeyPreSubmitter, -} - -struct HandlerDeps { - /// The `SocketRelayHandler`'s for each specified chain. - socket_relay_handlers: Vec>, - /// The `RoundupRelayHandler`'s for each specified chain. - roundup_relay_handlers: Vec>, -} - -/// The relayer client dependencies. -struct FullDeps { - bootstrap_shared_data: BootstrapSharedData, - manager_deps: ManagerDeps, - periodic_deps: PeriodicDeps, - handler_deps: HandlerDeps, - substrate_deps: SubstrateDeps, - btc_deps: BtcDeps, -} From 69010665c8376fd4a071c41607a1a6d20b9aeb38 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 3 Dec 2024 19:28:07 +0900 Subject: [PATCH 04/60] CCCP-295, fix: remove redundant find --- .../src/eth/handlers/roundup_relay_handler.rs | 4 +--- periodic/src/heartbeat_sender.rs | 12 +++++----- periodic/src/price_feeder.rs | 3 +-- periodic/src/roundup_emitter.rs | 8 ++----- relayer/src/service.rs | 22 +++++++++---------- 5 files changed, 19 insertions(+), 30 deletions(-) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 1040ac3e..2ca3c8d7 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -172,13 +172,11 @@ where { /// Instantiates a new `RoundupRelayHandler` instance. pub fn new( - bifrost_chain_id: &ChainId, + client: Arc>, event_receiver: Receiver, clients: Arc>>>, bootstrap_shared_data: Arc, ) -> Self { - let client = clients.get(&bifrost_chain_id).expect(INVALID_BIFROST_NATIVENESS).clone(); - Self { event_receiver, client, diff --git a/periodic/src/heartbeat_sender.rs b/periodic/src/heartbeat_sender.rs index a0c98cdf..7ba802b8 100644 --- a/periodic/src/heartbeat_sender.rs +++ b/periodic/src/heartbeat_sender.rs @@ -80,15 +80,13 @@ where T: Transport + Clone, { /// Instantiates a new `HeartbeatSender` instance. - pub fn new(clients: Arc>>>) -> Self { - let (_, client) = clients - .iter() - .find(|(_, client)| client.metadata.is_native) - .expect(INVALID_BIFROST_NATIVENESS); - + pub fn new( + client: Arc>, + clients: Arc>>>, + ) -> Self { Self { schedule: Schedule::from_str(HEARTBEAT_SCHEDULE).expect(INVALID_PERIODIC_SCHEDULE), - client: client.clone(), + client, clients, } } diff --git a/periodic/src/price_feeder.rs b/periodic/src/price_feeder.rs index 45edc9d5..55830a86 100644 --- a/periodic/src/price_feeder.rs +++ b/periodic/src/price_feeder.rs @@ -97,11 +97,10 @@ where T: Transport + Clone, { pub fn new( - bifrost_chain_id: &ChainId, + client: Arc>, clients: Arc>>>, ) -> Self { let asset_oid = get_asset_oids(); - let client = clients.get(bifrost_chain_id).expect(INVALID_BIFROST_NATIVENESS).clone(); Self { schedule: Schedule::from_str(PRICE_FEEDER_SCHEDULE).expect(INVALID_PERIODIC_SCHEDULE), diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index dd131e31..8b66779c 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -114,18 +114,14 @@ where { /// Instantiates a new `RoundupEmitter` instance. pub fn new( + client: Arc>, clients: Arc>>>, bootstrap_shared_data: Arc, ) -> Self { - let (_, client) = clients - .iter() - .find(|(_, client)| client.metadata.is_native) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(); Self { current_round: U256::default(), - client: client.clone(), + client, clients, schedule: Schedule::from_str(ROUNDUP_EMITTER_SCHEDULE) .expect(INVALID_PERIODIC_SCHEDULE), diff --git a/relayer/src/service.rs b/relayer/src/service.rs index d0b94b42..a2eac646 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -382,16 +382,11 @@ where P: Provider, T: Transport + Clone, { - let bfc_client = manager_deps - .clients - .get(&manager_deps.bifrost_chain_id) - .expect(INVALID_BIFROST_NATIVENESS) - .clone(); log::info!( target: LOG_TARGET, "-[{}] 👤 Relayer: {:?}", sub_display_format(SUB_LOG_TARGET), - bfc_client.address() + manager_deps.bifrost_client.address() ); log::info!( target: LOG_TARGET, @@ -418,8 +413,6 @@ where { let task_manager = TaskManager::new(config.clone().tokio_handle, None)?; - let clients = Arc::new(clients); - let bootstrap_shared_data = BootstrapSharedData::new(&config); let pending_outbounds = PendingOutboundPool::new(); @@ -437,16 +430,20 @@ where let migration_sequence = Arc::new(RwLock::new(MigrationSequence::Normal)); - let manager_deps = ManagerDeps::new(&config, clients, bootstrap_shared_data.clone()); - let substrate_deps = SubstrateDeps::new(&manager_deps, &task_manager); + let manager_deps = ManagerDeps::new(&config, Arc::new(clients), bootstrap_shared_data.clone()); + let bfc_client = manager_deps.bifrost_client.clone(); + + let substrate_deps = SubstrateDeps::new(bfc_client.clone(), &task_manager); let periodic_deps = PeriodicDeps::new( bootstrap_shared_data.clone(), migration_sequence.clone(), keypair_storage.clone(), - &manager_deps, &substrate_deps, + manager_deps.clients.clone(), + bfc_client.clone(), ); - let handler_deps = HandlerDeps::new(&config, &manager_deps, bootstrap_shared_data.clone()); + let handler_deps = + HandlerDeps::new(&config, &manager_deps, bootstrap_shared_data.clone(), bfc_client.clone()); let btc_deps = BtcDeps::new( &config, pending_outbounds.clone(), @@ -455,6 +452,7 @@ where &manager_deps, &substrate_deps, migration_sequence.clone(), + bfc_client.clone(), ); print_relay_targets(&manager_deps); From b5027d6e328cef17a1686232469c255cc21c7e79 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 3 Dec 2024 19:29:46 +0900 Subject: [PATCH 05/60] CCCP-295, chore: move deps to mod --- relayer/src/service_deps/btc_deps.rs | 104 +++++++++++++++++++++ relayer/src/service_deps/full_deps.rs | 16 ++++ relayer/src/service_deps/handler_deps.rs | 56 +++++++++++ relayer/src/service_deps/manager_deps.rs | 61 ++++++++++++ relayer/src/service_deps/mod.rs | 53 +++++++++++ relayer/src/service_deps/periodic_deps.rs | 69 ++++++++++++++ relayer/src/service_deps/substrate_deps.rs | 29 ++++++ 7 files changed, 388 insertions(+) create mode 100644 relayer/src/service_deps/btc_deps.rs create mode 100644 relayer/src/service_deps/full_deps.rs create mode 100644 relayer/src/service_deps/handler_deps.rs create mode 100644 relayer/src/service_deps/manager_deps.rs create mode 100644 relayer/src/service_deps/mod.rs create mode 100644 relayer/src/service_deps/periodic_deps.rs create mode 100644 relayer/src/service_deps/substrate_deps.rs diff --git a/relayer/src/service_deps/btc_deps.rs b/relayer/src/service_deps/btc_deps.rs new file mode 100644 index 00000000..efa3db7e --- /dev/null +++ b/relayer/src/service_deps/btc_deps.rs @@ -0,0 +1,104 @@ +use super::*; + +pub struct BtcDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + /// The Bitcoin outbound handler. + pub outbound: OutboundHandler, + /// The Bitcoin inbound handler. + pub inbound: InboundHandler, + /// The Bitcoin block manager. + pub block_manager: BlockManager, + /// The Bitcoin PSBT signer. + pub psbt_signer: PsbtSigner, + /// The Bitcoin vault public key submitter. + pub pub_key_submitter: PubKeySubmitter, + /// The Bitcoin rollback verifier. + pub rollback_verifier: BitcoinRollbackVerifier, +} + +impl BtcDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + pub fn new( + config: &Configuration, + pending_outbounds: PendingOutboundPool, + keypair_storage: Arc>, + bootstrap_shared_data: BootstrapSharedData, + manager_deps: &ManagerDeps, + substrate_deps: &SubstrateDeps, + migration_sequence: Arc>, + bfc_client: Arc>, + ) -> Self { + let bootstrap_shared_data = Arc::new(bootstrap_shared_data.clone()); + let network = Network::from_core_arg(&config.relayer_config.btc_provider.chain) + .expect(INVALID_BITCOIN_NETWORK); + + let auth = match ( + config.relayer_config.btc_provider.username.clone(), + config.relayer_config.btc_provider.password.clone(), + ) { + (Some(username), Some(password)) => Auth::UserPass(username, password), + _ => Auth::None, + }; + let btc_client = BitcoinClient::new( + &config.relayer_config.btc_provider.provider, + auth, + config.relayer_config.btc_provider.wallet.clone(), + Some(60), + ) + .expect(INVALID_PROVIDER_URL); + + let block_manager = BlockManager::new( + btc_client.clone(), + bfc_client.clone(), + pending_outbounds.clone(), + bootstrap_shared_data.clone(), + config.relayer_config.btc_provider.call_interval.clone(), + config + .relayer_config + .btc_provider + .block_confirmations + .unwrap_or(DEFAULT_BITCOIN_BLOCK_CONFIRMATIONS), + ); + let inbound = InboundHandler::new( + bfc_client.clone(), + manager_deps.clients.clone(), + block_manager.subscribe(), + bootstrap_shared_data.clone(), + ); + let outbound = OutboundHandler::new( + bfc_client.clone(), + manager_deps.clients.clone(), + block_manager.subscribe(), + bootstrap_shared_data.clone(), + ); + + let psbt_signer = PsbtSigner::new( + bfc_client.clone(), + substrate_deps.xt_request_sender.clone(), + keypair_storage.clone(), + migration_sequence.clone(), + network, + ); + let pub_key_submitter = PubKeySubmitter::new( + bfc_client.clone(), + substrate_deps.xt_request_sender.clone(), + keypair_storage.clone(), + migration_sequence.clone(), + ); + let rollback_verifier = BitcoinRollbackVerifier::new( + btc_client.clone(), + bfc_client.clone(), + substrate_deps.xt_request_sender.clone(), + ); + + Self { outbound, inbound, block_manager, psbt_signer, pub_key_submitter, rollback_verifier } + } +} diff --git a/relayer/src/service_deps/full_deps.rs b/relayer/src/service_deps/full_deps.rs new file mode 100644 index 00000000..570125d9 --- /dev/null +++ b/relayer/src/service_deps/full_deps.rs @@ -0,0 +1,16 @@ +use super::*; + +/// The relayer client dependencies. +pub struct FullDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + pub bootstrap_shared_data: BootstrapSharedData, + pub manager_deps: ManagerDeps, + pub periodic_deps: PeriodicDeps, + pub handler_deps: HandlerDeps, + pub substrate_deps: SubstrateDeps, + pub btc_deps: BtcDeps, +} diff --git a/relayer/src/service_deps/handler_deps.rs b/relayer/src/service_deps/handler_deps.rs new file mode 100644 index 00000000..bf9517d5 --- /dev/null +++ b/relayer/src/service_deps/handler_deps.rs @@ -0,0 +1,56 @@ +use super::*; + +pub struct HandlerDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + /// The `SocketRelayHandler`'s for each specified chain. + pub socket_relay_handlers: Vec>, + /// The `RoundupRelayHandler`'s for each specified chain. + pub roundup_relay_handlers: Vec>, +} + +impl HandlerDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + pub fn new( + config: &Configuration, + manager_deps: &ManagerDeps, + bootstrap_shared_data: BootstrapSharedData, + bfc_client: Arc>, + ) -> Self { + let mut handlers = (vec![], vec![]); + let ManagerDeps { clients, event_managers, .. } = manager_deps; + + config.relayer_config.handler_configs.iter().for_each( + |handler_config| match handler_config.handler_type { + HandlerType::Socket => handler_config.watch_list.iter().for_each(|target| { + handlers.0.push(SocketRelayHandler::new( + *target, + event_managers.get(target).expect(INVALID_CHAIN_ID).sender.subscribe(), + clients.clone(), + Arc::new(bootstrap_shared_data.clone()), + )); + }), + HandlerType::Roundup => { + handlers.1.push(RoundupRelayHandler::new( + bfc_client.clone(), + event_managers + .get(&handler_config.watch_list[0]) + .expect(INVALID_CHAIN_ID) + .sender + .subscribe(), + clients.clone(), + Arc::new(bootstrap_shared_data.clone()), + )); + }, + }, + ); + Self { socket_relay_handlers: handlers.0, roundup_relay_handlers: handlers.1 } + } +} diff --git a/relayer/src/service_deps/manager_deps.rs b/relayer/src/service_deps/manager_deps.rs new file mode 100644 index 00000000..8eaca76c --- /dev/null +++ b/relayer/src/service_deps/manager_deps.rs @@ -0,0 +1,61 @@ +use super::*; +use br_client::eth::{events::EventManager, EthClient}; +use br_primitives::cli::Configuration; + +pub struct ManagerDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + /// Bifrost chain id. + pub bifrost_client: Arc>, + /// The `EthClient`'s for each specified chain. + pub clients: Arc>>>, + /// The `EventManager`'s for each specified chain. + pub event_managers: BTreeMap>, +} + +impl ManagerDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + /// Initializes the `EthClient`, `TransactionManager`, `EventManager`, `TxRequestSender` for each chain. + pub fn new( + config: &Configuration, + clients: Arc>>>, + bootstrap_shared_data: BootstrapSharedData, + ) -> Self { + let prometheus_config = &config.relayer_config.prometheus_config; + let evm_providers = &config.relayer_config.evm_providers; + let mut bifrost_client = None; + + let mut event_managers = BTreeMap::new(); + + // iterate each evm provider and construct inner components. + evm_providers.iter().for_each(|evm_provider| { + let client = clients.get(&evm_provider.id).unwrap().clone(); + if evm_provider.is_native.unwrap_or(false) { + bifrost_client = Some(client.clone()); + } + let event_manager = EventManager::new( + client, + Arc::new(bootstrap_shared_data.clone()), + match &prometheus_config { + Some(config) => config.is_enabled, + None => false, + }, + ); + + event_managers.insert(event_manager.client.chain_id(), event_manager); + }); + + Self { + bifrost_client: bifrost_client.expect(INVALID_BIFROST_NATIVENESS), + clients, + event_managers, + } + } +} diff --git a/relayer/src/service_deps/mod.rs b/relayer/src/service_deps/mod.rs new file mode 100644 index 00000000..09b23cc5 --- /dev/null +++ b/relayer/src/service_deps/mod.rs @@ -0,0 +1,53 @@ +mod btc_deps; +mod full_deps; +mod handler_deps; +mod manager_deps; +mod periodic_deps; +mod substrate_deps; + +pub use btc_deps::BtcDeps; +pub use full_deps::FullDeps; +pub use handler_deps::HandlerDeps; +pub use manager_deps::ManagerDeps; +pub use periodic_deps::PeriodicDeps; +pub use substrate_deps::SubstrateDeps; + +use alloy::{ + primitives::ChainId, + providers::{fillers::TxFiller, Provider, WalletProvider}, + transports::Transport, +}; +use bitcoincore_rpc::{Auth, Client as BitcoinClient}; +use br_client::{ + btc::{ + block::BlockManager, + handlers::{InboundHandler, OutboundHandler}, + storage::{keypair::KeypairStorage, pending_outbound::PendingOutboundPool}, + }, + eth::{ + handlers::{RoundupRelayHandler, SocketRelayHandler}, + EthClient, + }, + substrate::tx::UnsignedTransactionManager, +}; +use br_periodic::{ + BitcoinRollbackVerifier, HeartbeatSender, KeypairMigrator, OraclePriceFeeder, PsbtSigner, + PubKeyPreSubmitter, PubKeySubmitter, RoundupEmitter, +}; +use br_primitives::{ + bootstrap::BootstrapSharedData, + cli::{Configuration, HandlerType}, + constants::{ + cli::DEFAULT_BITCOIN_BLOCK_CONFIRMATIONS, + errors::{ + INVALID_BIFROST_NATIVENESS, INVALID_BITCOIN_NETWORK, INVALID_CHAIN_ID, + INVALID_PROVIDER_URL, + }, + }, + substrate::MigrationSequence, + tx::XtRequestSender, +}; +use miniscript::bitcoin::Network; +use sc_service::TaskManager; +use std::{collections::BTreeMap, sync::Arc}; +use tokio::sync::RwLock; diff --git a/relayer/src/service_deps/periodic_deps.rs b/relayer/src/service_deps/periodic_deps.rs new file mode 100644 index 00000000..f8f35c23 --- /dev/null +++ b/relayer/src/service_deps/periodic_deps.rs @@ -0,0 +1,69 @@ +use super::*; + +pub struct PeriodicDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + /// The `HeartbeatSender` used for system health checks. + pub heartbeat_sender: HeartbeatSender, + /// The `OraclePriceFeeder` used for price feeding. + pub oracle_price_feeder: OraclePriceFeeder, + /// The `RoundupEmitter` used for detecting and emitting new round updates. + pub roundup_emitter: RoundupEmitter, + /// The `KeypairMigrator` used for detecting migration sequences. + pub keypair_migrator: KeypairMigrator, + /// The `PubKeyPreSubmitter` used for presubmitting public keys. + pub presubmitter: PubKeyPreSubmitter, +} + +impl PeriodicDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + pub fn new( + bootstrap_shared_data: BootstrapSharedData, + migration_sequence: Arc>, + keypair_storage: Arc>, + substrate_deps: &SubstrateDeps, + clients: Arc>>>, + bfc_client: Arc>, + ) -> Self { + // initialize the heartbeat sender + let heartbeat_sender = HeartbeatSender::new(bfc_client.clone(), clients.clone()); + + // initialize the oracle price feeder + let oracle_price_feeder = OraclePriceFeeder::new(bfc_client.clone(), clients.clone()); + + // initialize the roundup emitter + let roundup_emitter = RoundupEmitter::new( + bfc_client.clone(), + clients.clone(), + Arc::new(bootstrap_shared_data.clone()), + ); + + // initialize migration detector + let keypair_migrator = KeypairMigrator::new( + bfc_client.clone(), + migration_sequence.clone(), + keypair_storage.clone(), + ); + let presubmitter = PubKeyPreSubmitter::new( + bfc_client.clone(), + substrate_deps.xt_request_sender.clone(), + keypair_storage.clone(), + migration_sequence.clone(), + ); + + Self { + heartbeat_sender, + oracle_price_feeder, + roundup_emitter, + keypair_migrator, + presubmitter, + } + } +} diff --git a/relayer/src/service_deps/substrate_deps.rs b/relayer/src/service_deps/substrate_deps.rs new file mode 100644 index 00000000..7edf39d7 --- /dev/null +++ b/relayer/src/service_deps/substrate_deps.rs @@ -0,0 +1,29 @@ +use super::*; + +pub struct SubstrateDeps +where + F: TxFiller + WalletProvider, + P: Provider, + T: Transport + Clone, +{ + /// The `UnsignedTransactionManager` for Bifrost. + pub unsigned_tx_manager: UnsignedTransactionManager, + /// The `XtRequestSender` for Bifrost. + pub xt_request_sender: Arc, +} + +impl SubstrateDeps +where + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, + T: Transport + Clone, +{ + pub fn new(bfc_client: Arc>, task_manager: &TaskManager) -> Self { + let (unsigned_tx_manager, sender) = + UnsignedTransactionManager::new(bfc_client.clone(), task_manager.spawn_handle()); + + let xt_request_sender = Arc::new(XtRequestSender::new(sender)); + + Self { unsigned_tx_manager, xt_request_sender } + } +} From 53cd8bcf180276d6fb740678815f166cd6d90a98 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 5 Dec 2024 17:25:56 +0900 Subject: [PATCH 06/60] CCCP-295, refactor: `fn get_sorted_signatures()` --- .../src/eth/handlers/roundup_relay_handler.rs | 37 ++-------- .../src/eth/handlers/socket_relay_handler.rs | 4 +- client/src/eth/traits.rs | 68 +++++-------------- periodic/src/socket_rollback_emitter.rs | 12 ++-- primitives/src/contracts/socket.rs | 34 +++++++++- primitives/src/eth.rs | 18 ----- 6 files changed, 64 insertions(+), 109 deletions(-) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 2ca3c8d7..52e34032 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -27,7 +27,7 @@ use br_primitives::{ SocketContract::{RoundUp, SocketContractInstance}, Socket_Struct::{Round_Up_Submit, Signatures}, }, - eth::{BootstrapState, GasCoefficient, RecoveredSignature, RoundUpEventStatus}, + eth::{BootstrapState, GasCoefficient, RoundUpEventStatus}, tx::{TxRequestMessage, TxRequestMetadata, TxRequestSender, VSPPhase2Metadata}, utils::{recover_message, sub_display_format}, }; @@ -208,7 +208,7 @@ where round: U256, new_relayers: &[Address], ) -> Result { - let raw_sigs = self + let signatures = self .client .protocol_contracts .socket @@ -217,36 +217,11 @@ where .await? ._0; - let raw_concated_v = &raw_sigs.v.to_string()[2..]; - - let mut recovered_sigs = vec![]; - let encoded_msg = self.encode_relayer_array(round, new_relayers); - for idx in 0..raw_sigs.r.len() { - let sig = Signature::from_rs_and_parity( - raw_sigs.r[idx].into(), - raw_sigs.s[idx].into(), - u64::from_str_radix(&raw_concated_v[idx * 2..idx * 2 + 2], 16).unwrap(), - )?; - - recovered_sigs.push(RecoveredSignature::new( - idx, - sig, - recover_message(sig, &encoded_msg), - )); - } - recovered_sigs.sort_by_key(|k| k.signer); - - let mut sorted_sigs = Signatures::default(); - let mut sorted_concated_v = String::from("0x"); - recovered_sigs.into_iter().for_each(|sig| { - let idx = sig.idx; - sorted_sigs.r.push(raw_sigs.r[idx]); - sorted_sigs.s.push(raw_sigs.s[idx]); - sorted_concated_v.push_str(&format!("{:x}", sig.signature.v().recid().to_byte())); - }); - sorted_sigs.v = Bytes::from_str(&sorted_concated_v)?; + let mut signature_vec = Vec::::from(signatures); + signature_vec + .sort_by_key(|k| recover_message(*k, &self.encode_relayer_array(round, new_relayers))); - Ok(sorted_sigs) + Ok(Signatures::from(signature_vec)) } /// Verifies whether the current relayer was selected at the given round. diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index 2c82d19b..8de9df35 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -206,7 +206,7 @@ where }, SocketEventStatus::Accepted | SocketEventStatus::Rejected => { is_external = true; - self.get_sorted_signatures(msg).await + self.get_sorted_signatures(msg).await? }, _ => panic!( "[{}]-[{}]-[{}] Unknown socket event status received: {:?}", @@ -232,7 +232,7 @@ where }, SocketEventStatus::Accepted | SocketEventStatus::Rejected => { is_external = true; - self.get_sorted_signatures(msg).await + self.get_sorted_signatures(msg).await? }, SocketEventStatus::Executed | SocketEventStatus::Reverted => Signatures::default(), _ => panic!( diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index 7e2c6ee9..2ab7b4c7 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -14,7 +14,7 @@ use br_primitives::{ contracts::socket::Socket_Struct::{Poll_Submit, Signatures, Socket_Message}, eth::{BootstrapState, BuiltRelayTransaction, GasCoefficient}, tx::{FlushMetadata, TxRequestMessage, TxRequestMetadata}, - utils::sub_display_format, + utils::{recover_message, sub_display_format}, }; use eyre::Result; use sc_service::SpawnTaskHandle; @@ -81,62 +81,26 @@ where Ok((Signatures::default(), false)) } - /// Encodes the given socket message to bytes. - fn encode_socket_message(&self, msg: Socket_Message) -> Vec { - msg.abi_encode() - } - /// Signs the given socket message. async fn sign_socket_message(&self, msg: Socket_Message) -> Result { - let encoded_msg = self.encode_socket_message(msg); - Ok(self.get_client().sign_message(&encoded_msg).await?) + Ok(self.get_client().sign_message(&msg.abi_encode()).await?) } /// Get the signatures of the given message. - async fn get_sorted_signatures(&self, _msg: Socket_Message) -> Signatures { - // let raw_sigs = self - // .get_client() - // .contract_call( - // self.get_client() - // .protocol_contracts - // .socket - // .get_signatures(msg.clone().req_id, msg.clone().status), - // "socket.get_signatures", - // ) - // .await; - - // let raw_concated_v = &raw_sigs.v.to_string()[2..]; - - // let mut recovered_sigs = vec![]; - // let encoded_msg = self.encode_socket_message(msg); - // for idx in 0..raw_sigs.r.len() { - // let sig = Signature { - // r: raw_sigs.r[idx].into(), - // s: raw_sigs.s[idx].into(), - // v: u64::from_str_radix(&raw_concated_v[idx * 2..idx * 2 + 2], 16).unwrap(), - // }; - // recovered_sigs.push(RecoveredSignature::new( - // idx, - // sig, - // self.get_client().wallet.recover_message(sig, &encoded_msg), - // )); - // } - // recovered_sigs.sort_by_key(|k| k.signer); - - // let mut sorted_sigs = Signatures::default(); - // let mut sorted_concated_v = String::from("0x"); - // recovered_sigs.into_iter().for_each(|sig| { - // let idx = sig.idx; - // sorted_sigs.r.push(raw_sigs.r[idx]); - // sorted_sigs.s.push(raw_sigs.s[idx]); - // let v = Bytes::from([sig.signature.v as u8]); - // sorted_concated_v.push_str(&v.to_string()[2..]); - // }); - // sorted_sigs.v = Bytes::from_str(&sorted_concated_v).unwrap(); - - // sorted_sigs - - todo!("Implement this") + async fn get_sorted_signatures(&self, msg: Socket_Message) -> Result { + let client = self.get_client(); + let signatures = client + .protocol_contracts + .socket + .get_signatures(msg.req_id.clone(), msg.status.clone()) + .call() + .await? + ._0; + + let mut signature_vec = Vec::::from(signatures); + signature_vec.sort_by_key(|k| recover_message(*k, &msg.abi_encode())); + + Ok(Signatures::from(signature_vec)) } } diff --git a/periodic/src/socket_rollback_emitter.rs b/periodic/src/socket_rollback_emitter.rs index 6d7dc006..d6a39f1f 100644 --- a/periodic/src/socket_rollback_emitter.rs +++ b/periodic/src/socket_rollback_emitter.rs @@ -152,13 +152,15 @@ where } /// Tries to rollback the given socket message. - async fn try_rollback(&self, socket_msg: &Socket_Message) { + async fn try_rollback(&self, socket_msg: &Socket_Message) -> Result<()> { let status = SocketEventStatus::from(socket_msg.status); match status { SocketEventStatus::Requested => self.try_rollback_inbound(socket_msg).await, - SocketEventStatus::Accepted => self.try_rollback_outbound(socket_msg).await, + SocketEventStatus::Accepted => self.try_rollback_outbound(socket_msg).await?, _ => panic!("Trying rollback on an invalid socket event status"), } + + Ok(()) } /// Tries to rollback the given inbound socket message. @@ -183,7 +185,7 @@ where } /// Tries to rollback the given outbound socket message. - async fn try_rollback_outbound(&self, socket_msg: &Socket_Message) { + async fn try_rollback_outbound(&self, socket_msg: &Socket_Message) -> Result<()> { // `submit_sig` is the state changed socket message that will be passed. // `socket_msg` is the origin message that will be used for signature builds. let mut submit_sig = socket_msg.clone(); @@ -191,7 +193,7 @@ where let tx_request = TransactionRequest::default() .input(self.build_poll_call_data( submit_sig.clone(), - self.get_sorted_signatures(socket_msg.clone()).await, + self.get_sorted_signatures(socket_msg.clone()).await?, )) .to(*self.client.protocol_contracts.socket.address()); @@ -206,6 +208,8 @@ where // transaction executed on External chain's so random delay required. // aggregated relay typed transactions are good with low gas coefficient. self.request_send_transaction(tx_request, metadata, true, GasCoefficient::Low); + + Ok(()) } /// Tries to receive any new rollbackable messages and store's it locally. diff --git a/primitives/src/contracts/socket.rs b/primitives/src/contracts/socket.rs index 975048ec..47fe876f 100644 --- a/primitives/src/contracts/socket.rs +++ b/primitives/src/contracts/socket.rs @@ -1,5 +1,5 @@ use alloy::{ - primitives::{b256, Bytes, B256}, + primitives::{b256, Bytes, Parity, B256}, signers::Signature, sol, }; @@ -39,6 +39,36 @@ impl From for Signatures { let s = signature.s().into(); let v = signature.v().to_u64() as u8; - Signatures { r: vec![r], s: vec![s], v: Bytes::from(vec![v]) } + Signatures { r: vec![r], s: vec![s], v: Bytes::from([v]) } + } +} + +impl From> for Signatures { + fn from(signatures: Vec) -> Self { + let mut r = Vec::with_capacity(signatures.len()); + let mut s = Vec::with_capacity(signatures.len()); + let mut v = Vec::with_capacity(signatures.len()); + + for sig in signatures.iter() { + r.push(sig.r().into()); + s.push(sig.s().into()); + v.push(sig.v().to_u64() as u8); + } + + Signatures { r, s, v: Bytes::from(v) } + } +} + +impl From for Vec { + fn from(signatures: Signatures) -> Self { + let mut res = Vec::with_capacity(signatures.r.len()); + for idx in 0..signatures.r.len() { + let r = signatures.r[idx].into(); + let s = signatures.s[idx].into(); + let v = Parity::try_from(signatures.v[idx] as u64).unwrap(); + res.push(Signature::from_rs_and_parity(r, s, v).unwrap()); + } + + res } } diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index 14b75f46..fc7b6ddc 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -8,7 +8,6 @@ use alloy::{ Provider, WalletProvider, }, rpc::types::TransactionRequest, - signers::Signature, transports::Transport, }; use url::Url; @@ -382,23 +381,6 @@ pub enum BootstrapState { NormalStart, } -#[derive(Clone, Debug)] -/// The information of a recovered signature. -pub struct RecoveredSignature { - /// The original index that represents the order from the result of `get_signatures()`. - pub idx: usize, - /// The signature of the message. - pub signature: Signature, - /// The account who signed the message. - pub signer: Address, -} - -impl RecoveredSignature { - pub fn new(idx: usize, signature: Signature, signer: Address) -> Self { - Self { idx, signature, signer } - } -} - #[derive(Clone, Debug)] /// The built relay transaction request. pub struct BuiltRelayTransaction { From 1b0d54b9d6698b62c77e92be03d0b6f1d1a066e1 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 10 Dec 2024 17:00:38 +0900 Subject: [PATCH 07/60] CCCP-295, refactor: remove unused dependencies and simplify transaction handling - Removed `byteorder` dependency from `Cargo.toml` files in both root and client directories. - Updated transaction handling in various handlers to utilize `send_transaction` function for better task management and error handling. - Introduced `SpawnTaskHandle` in several handlers to facilitate asynchronous task spawning. - Cleaned up unused code and commented-out sections across multiple files, enhancing overall code readability and maintainability. - Adjusted imports and struct definitions to reflect the removal of redundant components. This commit streamlines the codebase by eliminating unnecessary dependencies and improving the transaction processing flow. --- Cargo.toml | 1 - client/Cargo.toml | 2 - client/src/btc/handlers/inbound.rs | 53 +-- client/src/btc/handlers/mod.rs | 57 +-- client/src/btc/handlers/outbound.rs | 102 ++--- client/src/btc/storage/pending_outbound.rs | 36 +- .../src/eth/handlers/roundup_relay_handler.rs | 85 ++-- .../src/eth/handlers/socket_relay_handler.rs | 387 ++++++++---------- client/src/eth/mod.rs | 59 ++- client/src/eth/traits.rs | 45 -- periodic/Cargo.toml | 2 +- periodic/src/heartbeat_sender.rs | 64 +-- periodic/src/price_feeder.rs | 62 +-- periodic/src/price_source/upbit.rs | 2 +- periodic/src/roundup_emitter.rs | 78 ++-- periodic/src/socket_rollback_emitter.rs | 107 ++--- primitives/Cargo.toml | 1 + primitives/src/eth.rs | 4 + primitives/src/periodic.rs | 21 +- primitives/src/tx.rs | 22 - primitives/src/utils.rs | 5 + relayer/src/service.rs | 165 ++++---- relayer/src/service_deps/btc_deps.rs | 6 +- relayer/src/service_deps/full_deps.rs | 4 +- relayer/src/service_deps/handler_deps.rs | 12 +- relayer/src/service_deps/manager_deps.rs | 4 +- relayer/src/service_deps/mod.rs | 6 +- relayer/src/service_deps/periodic_deps.rs | 34 +- 28 files changed, 585 insertions(+), 841 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 978d6ebc..24952b09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,6 @@ url = "2.5.0" array-bytes = "6.1" eyre = "0.6" tower = "0.5.0" -byteorder = "1.5.0" serde_yaml = "0.9.25" serde = "1.0.188" diff --git a/client/Cargo.toml b/client/Cargo.toml index 7a16876d..34d332e1 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -13,7 +13,6 @@ repository = { workspace = true } bitcoincore-rpc = { workspace = true } miniscript = { workspace = true } log = { workspace = true } -rand = { workspace = true } sentry = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } @@ -34,7 +33,6 @@ array-bytes = { workspace = true } alloy = { workspace = true } tower = { workspace = true } eyre = { workspace = true } -byteorder = { workspace = true } # subs sc-keystore = { workspace = true } diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index 67f662ca..2f2bf881 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -3,12 +3,12 @@ use crate::{ block::{Event, EventMessage as BTCEventMessage, EventType}, handlers::{Handler, LOG_TARGET}, }, - eth::EthClient, + eth::{send_transaction, EthClient}, }; use alloy::{ network::Ethereum, - primitives::{Address as EthAddress, ChainId, B256, U256}, + primitives::{Address as EthAddress, B256, U256}, providers::{ fillers::{FillProvider, TxFiller}, Provider, WalletProvider, @@ -21,17 +21,18 @@ use br_primitives::{ bootstrap::BootstrapSharedData, contracts::bitcoin_socket::BitcoinSocketContract::BitcoinSocketContractInstance, eth::BootstrapState, - tx::{BitcoinRelayMetadata, TxRequestMetadata, TxRequestSender}, + tx::{BitcoinRelayMetadata, TxRequestMetadata}, utils::sub_display_format, }; use eyre::Result; use miniscript::bitcoin::{address::NetworkUnchecked, hashes::Hash, Address as BtcAddress}; -use std::{collections::BTreeMap, sync::Arc}; +use sc_service::SpawnTaskHandle; +use std::sync::Arc; use tokio::sync::broadcast::Receiver; use tokio_stream::StreamExt; -use super::{BootstrapHandler, EventMessage, TxRequester}; +use super::{BootstrapHandler, EventMessage}; const SUB_LOG_TARGET: &str = "inbound-handler"; @@ -43,14 +44,14 @@ where { /// `EthClient` for interact with Bifrost network. bfc_client: Arc>, - /// All clients. - clients: Arc>>>, /// The receiver that consumes new events from the block channel. event_receiver: Receiver, /// Event type which this handler should handle. target_event: EventType, /// The bootstrap shared data. bootstrap_shared_data: Arc, + /// The handle to spawn tasks. + handle: SpawnTaskHandle, } impl InboundHandler @@ -61,16 +62,16 @@ where { pub fn new( bfc_client: Arc>, - clients: Arc>>>, event_receiver: Receiver, bootstrap_shared_data: Arc, + handle: SpawnTaskHandle, ) -> Self { Self { bfc_client, - clients, event_receiver, target_event: EventType::Inbound, bootstrap_shared_data, + handle, } } @@ -155,27 +156,11 @@ where } } -// #[async_trait::async_trait] -// impl TxRequester for InboundHandler -// where -// F: TxFiller + WalletProvider, -// P: Provider, -// T: Transport + Clone, -// { -// fn tx_request_sender(&self) -> Arc { -// self.tx_request_sender.clone() -// } - -// fn bfc_client(&self) -> Arc> { -// self.bfc_client.clone() -// } -// } - #[async_trait::async_trait] impl Handler for InboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { async fn run(&mut self) -> Result<()> { @@ -226,13 +211,13 @@ where let metadata = BitcoinRelayMetadata::new(event.address, user_bfc_address, event.txid, event.index); - // self.request_send_transaction( - // tx_request, - // TxRequestMetadata::BitcoinSocketRelay(metadata), - // SUB_LOG_TARGET, - // ) - // .await; - todo!() + send_transaction( + self.bfc_client.clone(), + tx_request, + format!("{} ({})", SUB_LOG_TARGET, self.bfc_client.get_chain_name()), + TxRequestMetadata::BitcoinSocketRelay(metadata), + self.handle.clone(), + ); } Ok(()) diff --git a/client/src/btc/handlers/mod.rs b/client/src/btc/handlers/mod.rs index 3a284c01..718a91b2 100644 --- a/client/src/btc/handlers/mod.rs +++ b/client/src/btc/handlers/mod.rs @@ -14,16 +14,12 @@ use crate::{ use alloy::{ providers::{fillers::TxFiller, Provider, WalletProvider}, - rpc::types::TransactionRequest, transports::Transport, }; use br_primitives::{ bootstrap::BootstrapSharedData, - eth::{BootstrapState, GasCoefficient}, - tx::{ - TxRequestMessage, TxRequestMetadata, TxRequestSender, XtRequest, XtRequestMessage, - XtRequestMetadata, XtRequestSender, - }, + eth::BootstrapState, + tx::{XtRequest, XtRequestMessage, XtRequestMetadata, XtRequestSender}, utils::sub_display_format, }; use eyre::Result; @@ -97,55 +93,6 @@ where } } -#[async_trait::async_trait] -pub trait TxRequester -where - F: TxFiller + WalletProvider, - P: Provider, - T: Transport + Clone, -{ - fn tx_request_sender(&self) -> Arc; - - fn bfc_client(&self) -> Arc>; - - async fn request_send_transaction( - &self, - tx_request: TransactionRequest, - metadata: TxRequestMetadata, - sub_log_target: &str, - ) { - match self.tx_request_sender().send(TxRequestMessage::new( - tx_request, - metadata.clone(), - true, - false, - GasCoefficient::Mid, - false, - )) { - Ok(_) => log::info!( - target: LOG_TARGET, - "-[{}] 🔖 Request relay transaction: {}", - sub_display_format(sub_log_target), - metadata - ), - Err(error) => { - let log_msg = format!( - "-[{}]-[{}] ❗️ Failed to send relay transaction: {}, Error: {}", - sub_display_format(sub_log_target), - self.bfc_client().address(), - metadata, - error - ); - log::error!(target: LOG_TARGET, "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", LOG_TARGET), - sentry::Level::Error, - ); - }, - } - } -} - #[async_trait::async_trait] pub trait Handler { async fn run(&mut self) -> Result<()>; diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index a9e0b08c..422be996 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -3,7 +3,7 @@ use crate::{ block::{Event, EventMessage as BTCEventMessage, EventType}, handlers::{Handler, LOG_TARGET}, }, - eth::{traits::SocketRelayBuilder, EthClient}, + eth::{send_transaction, traits::SocketRelayBuilder, EthClient}, }; use alloy::{ @@ -24,20 +24,17 @@ use br_primitives::{ socket_queue::SocketQueueContract::SocketQueueContractInstance, }, eth::{BootstrapState, BuiltRelayTransaction, SocketEventStatus}, - tx::{SocketRelayMetadata, TxRequestMetadata, TxRequestSender}, + tx::{SocketRelayMetadata, TxRequestMetadata}, utils::sub_display_format, }; -use byteorder::{BigEndian, ByteOrder}; use eyre::Result; -use miniscript::bitcoin::hashes::Hash; -use miniscript::bitcoin::Txid; -use std::{ - collections::{BTreeMap, BTreeSet}, - sync::Arc, -}; +use miniscript::bitcoin::{hashes::Hash, Txid}; +use sc_service::SpawnTaskHandle; +use std::sync::Arc; use tokio::sync::broadcast::Receiver; +use tokio_stream::StreamExt; -use super::{BootstrapHandler, EventMessage, TxRequester}; +use super::{BootstrapHandler, EventMessage}; const SUB_LOG_TARGET: &str = "outbound-handler"; @@ -48,11 +45,12 @@ where T: Transport + Clone, { bfc_client: Arc>, - clients: Arc>>>, event_receiver: Receiver, target_event: EventType, /// The bootstrap shared data. bootstrap_shared_data: Arc, + /// The handle to spawn tasks. + handle: SpawnTaskHandle, } impl OutboundHandler @@ -63,16 +61,16 @@ where { pub fn new( bfc_client: Arc>, - clients: Arc>>>, event_receiver: Receiver, bootstrap_shared_data: Arc, + handle: SpawnTaskHandle, ) -> Self { Self { bfc_client, - clients, event_receiver, target_event: EventType::Outbound, bootstrap_shared_data, + handle, } } @@ -98,27 +96,11 @@ where } } -// #[async_trait::async_trait] -// impl TxRequester for OutboundHandler -// where -// F: TxFiller + WalletProvider, -// P: Provider, -// T: Transport + Clone, -// { -// fn tx_request_sender(&self) -> Arc { -// self.tx_request_sender.clone() -// } - -// fn bfc_client(&self) -> Arc> { -// self.bfc_client.clone() -// } -// } - #[async_trait::async_trait] impl Handler for OutboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { async fn run(&mut self) -> Result<()> { @@ -140,39 +122,41 @@ where msg.events.len() ); - let txids: BTreeSet = msg.events.iter().map(|event| event.txid).collect(); - for txid in txids { - let socket_messages = self.check_socket_queue(txid).await?; - for mut msg in socket_messages { - msg.status = SocketEventStatus::Executed.into(); - - if let Some(built_transaction) = - self.build_transaction(msg.clone(), false, Default::default()).await? - { - // self.request_send_transaction( - // built_transaction.tx_request, - // TxRequestMetadata::SocketRelay(SocketRelayMetadata::new( - // false, - // SocketEventStatus::from(msg.status), - // msg.req_id.sequence, - // BigEndian::read_u32(&msg.req_id.ChainIndex.0) as ChainId, - // BigEndian::read_u32(&msg.ins_code.ChainIndex.0) as ChainId, - // msg.params.to, - // false, - // )), - // SUB_LOG_TARGET, - // ) - // .await; - todo!() - } - } + let mut stream = tokio_stream::iter(msg.events.into_iter()); + while let Some(event) = stream.next().await { + self.process_event(event).await?; } } } } - async fn process_event(&self, _event_tx: Event) -> Result<()> { - unreachable!("unimplemented") + async fn process_event(&self, event: Event) -> Result<()> { + let mut stream = tokio_stream::iter(self.check_socket_queue(event.txid).await?.into_iter()); + while let Some(mut msg) = stream.next().await { + msg.status = SocketEventStatus::Executed.into(); + + if let Some(built_transaction) = + self.build_transaction(msg.clone(), false, Default::default()).await? + { + send_transaction( + self.bfc_client.clone(), + built_transaction.tx_request, + format!("{} ({})", SUB_LOG_TARGET, self.bfc_client.get_chain_name()), + TxRequestMetadata::SocketRelay(SocketRelayMetadata::new( + false, + SocketEventStatus::from(msg.status), + msg.req_id.sequence, + Into::::into(msg.req_id.ChainIndex) as ChainId, + Into::::into(msg.ins_code.ChainIndex) as ChainId, + msg.params.to, + false, + )), + self.handle.clone(), + ); + } + } + + Ok(()) } #[inline] diff --git a/client/src/btc/storage/pending_outbound.rs b/client/src/btc/storage/pending_outbound.rs index 407bc3e5..3fcf78e8 100644 --- a/client/src/btc/storage/pending_outbound.rs +++ b/client/src/btc/storage/pending_outbound.rs @@ -1,3 +1,4 @@ +use alloy::sol_types::SolValue; use br_primitives::{contracts::socket::Socket_Struct::Socket_Message, substrate::BoundedVec}; use miniscript::bitcoin::{address::NetworkUnchecked, Address, Amount}; use std::{ @@ -75,38 +76,7 @@ impl PendingOutboundPool { (outputs, socket_messages) } - fn encode_socket_messages(&self, _messages: Vec) -> Vec> { - // messages - // .into_iter() - // .map(|msg| { - // let req_id_token = Token::Tuple(vec![ - // Token::FixedBytes(msg.req_id.chain.into()), - // Token::Uint(msg.req_id.round_id.into()), - // Token::Uint(msg.req_id.sequence.into()), - // ]); - // let status_token = Token::Uint(msg.status.into()); - // let ins_code_token = Token::Tuple(vec![ - // Token::FixedBytes(msg.ins_code.chain.into()), - // Token::FixedBytes(msg.ins_code.method.into()), - // ]); - // let params_token = Token::Tuple(vec![ - // Token::FixedBytes(msg.params.token_idx0.into()), - // Token::FixedBytes(msg.params.token_idx1.into()), - // Token::Address(msg.params.refund), - // Token::Address(msg.params.to), - // Token::Uint(msg.params.amount), - // Token::Bytes(msg.params.variants.to_vec()), - // ]); - - // ethers::abi::encode(&[Token::Tuple(vec![ - // req_id_token, - // status_token, - // ins_code_token, - // params_token, - // ])]) - // }) - // .collect() - - todo!("Implement this") + fn encode_socket_messages(&self, messages: Vec) -> Vec> { + messages.iter().map(|msg| msg.abi_encode()).collect() } } diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 52e34032..add3fad6 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -1,9 +1,9 @@ -use std::{collections::BTreeMap, str::FromStr, sync::Arc, time::Duration}; +use std::{collections::BTreeMap, sync::Arc, time::Duration}; use alloy::{ dyn_abi::DynSolValue, network::Ethereum, - primitives::{Address, Bytes, ChainId, Signature, B256, U256}, + primitives::{Address, ChainId, Signature, B256, U256}, providers::{ fillers::{FillProvider, TxFiller}, Provider, WalletProvider, @@ -14,26 +14,25 @@ use alloy::{ }; use async_trait::async_trait; use eyre::Result; +use sc_service::SpawnTaskHandle; use tokio::{sync::broadcast::Receiver, time::sleep}; use tokio_stream::StreamExt; use br_primitives::{ bootstrap::BootstrapSharedData, - constants::{ - cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE, - errors::INVALID_BIFROST_NATIVENESS, - }, + constants::{cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE}, contracts::socket::{ SocketContract::{RoundUp, SocketContractInstance}, Socket_Struct::{Round_Up_Submit, Signatures}, }, - eth::{BootstrapState, GasCoefficient, RoundUpEventStatus}, - tx::{TxRequestMessage, TxRequestMetadata, TxRequestSender, VSPPhase2Metadata}, + eth::{BootstrapState, RoundUpEventStatus}, + tx::{TxRequestMetadata, VSPPhase2Metadata}, utils::{recover_message, sub_display_format}, }; use crate::eth::{ events::EventMessage, + send_transaction, traits::{BootstrapHandler, Handler}, EthClient, }; @@ -57,13 +56,15 @@ where roundup_signature: B256, /// The bootstrap shared data. bootstrap_shared_data: Arc, + /// The handle to spawn tasks. + handle: SpawnTaskHandle, } #[async_trait] impl Handler for RoundupRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { async fn run(&mut self) -> Result<()> { @@ -102,6 +103,14 @@ where } match self.decode_log(log.clone()).await { Ok(serialized_log) => { + if !self + .is_selected_relayer(serialized_log.roundup.round - U256::from(1)) + .await? + { + // do nothing if not selected + return Ok(()); + } + if !is_bootstrap { log::info!( target: &self.client.get_chain_name(), @@ -111,15 +120,9 @@ where log.transaction_hash, ); } + match RoundUpEventStatus::from_u8(serialized_log.status) { RoundUpEventStatus::NextAuthorityCommitted => { - if !self - .is_selected_relayer(serialized_log.roundup.round - U256::from(1)) - .await? - { - // do nothing if not selected - return Ok(()); - } self.broadcast_roundup( &self .build_roundup_submit( @@ -127,7 +130,6 @@ where serialized_log.roundup.new_relayers, ) .await?, - is_bootstrap, ) .await?; }, @@ -166,8 +168,8 @@ where impl RoundupRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `RoundupRelayHandler` instance. @@ -176,6 +178,7 @@ where event_receiver: Receiver, clients: Arc>>>, bootstrap_shared_data: Arc, + handle: SpawnTaskHandle, ) -> Self { Self { event_receiver, @@ -183,6 +186,7 @@ where external_clients: clients, roundup_signature: RoundUp::SIGNATURE_HASH, bootstrap_shared_data, + handle, } } @@ -259,17 +263,13 @@ where } /// Check roundup submitted before. If not, call `round_control_relay`. - async fn broadcast_roundup( - &self, - roundup_submit: &Round_Up_Submit, - is_bootstrap: bool, - ) -> Result<()> { + async fn broadcast_roundup(&self, roundup_submit: &Round_Up_Submit) -> Result<()> { if self.external_clients.is_empty() { return Ok(()); } let mut stream = tokio_stream::iter(self.external_clients.iter()); - while let Some((_, target_client)) = stream.next().await { + while let Some((dst_chain_id, target_client)) = stream.next().await { // Check roundup submitted to target chain before. let latest_round = target_client.protocol_contracts.authority.latest_round().call().await?._0; @@ -278,24 +278,17 @@ where &target_client.protocol_contracts.socket, roundup_submit, ); - let target_chain_id = target_client.get_chain_id().await?; - - // if let Some(sender) = self.tx_request_senders.get(&target_chain_id) { - // sender - // .send(TxRequestMessage::new( - // transaction_request, - // TxRequestMetadata::VSPPhase2(VSPPhase2Metadata::new( - // roundup_submit.round, - // target_chain_id, - // )), - // true, - // true, - // GasCoefficient::Low, - // is_bootstrap, - // )) - // .unwrap() - // } - todo!() + + send_transaction( + target_client.clone(), + transaction_request, + SUB_LOG_TARGET.to_string(), + TxRequestMetadata::VSPPhase2(VSPPhase2Metadata::new( + roundup_submit.round, + *dst_chain_id, + )), + self.handle.clone(), + ); } } @@ -331,8 +324,8 @@ where #[async_trait] impl BootstrapHandler for RoundupRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index 8de9df35..62795985 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -7,9 +7,12 @@ use alloy::{ sol_types::SolEvent, transports::Transport, }; -use byteorder::{BigEndian, ByteOrder}; use eyre::Result; -use tokio::{sync::broadcast::Receiver, time::sleep}; +use sc_service::SpawnTaskHandle; +use tokio::{ + sync::{broadcast::Receiver, mpsc::UnboundedSender}, + time::sleep, +}; use tokio_stream::StreamExt; use br_primitives::{ @@ -23,16 +26,14 @@ use br_primitives::{ SocketContract::Socket, Socket_Struct::{Instruction, RequestID, Signatures, Socket_Message}, }, - eth::{ - BootstrapState, BuiltRelayTransaction, GasCoefficient, RelayDirection, SocketEventStatus, - }, - periodic::RollbackSender, - tx::{SocketRelayMetadata, TxRequestMessage, TxRequestMetadata}, + eth::{BootstrapState, BuiltRelayTransaction, RelayDirection, SocketEventStatus}, + tx::{SocketRelayMetadata, TxRequestMetadata}, utils::sub_display_format, }; use crate::eth::{ events::EventMessage, + send_transaction, traits::{BootstrapHandler, Handler, SocketRelayBuilder}, EthClient, }; @@ -42,8 +43,8 @@ const SUB_LOG_TARGET: &str = "socket-handler"; /// The essential task that handles `socket relay` related events. pub struct SocketRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// The `EthClient` to interact with the connected blockchain. @@ -52,6 +53,12 @@ where event_receiver: Receiver, /// The entire clients instantiated in the system. > system_clients: Arc>>>, + /// The bifrost client. + bifrost_client: Arc>, + /// The rollback sender for each chain. + rollback_senders: Arc>>>, + /// The handle to spawn tasks. + handle: SpawnTaskHandle, /// Signature of the `Socket` Event. socket_signature: B256, /// The bootstrap shared data. @@ -99,13 +106,11 @@ where let msg = decoded_socket.msg.clone(); let metadata = SocketRelayMetadata::new( - self.is_inbound_sequence( - BigEndian::read_u32(&msg.ins_code.ChainIndex.0) as ChainId - ), + self.is_inbound_sequence(Into::::into(msg.ins_code.ChainIndex) as ChainId), SocketEventStatus::from(msg.status), msg.req_id.sequence, - BigEndian::read_u32(&msg.req_id.ChainIndex.0) as ChainId, - BigEndian::read_u32(&msg.ins_code.ChainIndex.0) as ChainId, + Into::::into(msg.req_id.ChainIndex) as ChainId, + Into::::into(msg.ins_code.ChainIndex) as ChainId, msg.params.to, is_bootstrap, ); @@ -120,7 +125,7 @@ where } self.send_socket_message(msg.clone(), metadata.clone(), metadata.is_inbound) - .await; + .await?; }, Err(error) => panic!( "[{}]-[{}]-[{}] Unknown error while decoding socket event: {:?}", @@ -249,8 +254,8 @@ where impl SocketRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `SocketRelayHandler` instance. @@ -258,6 +263,9 @@ where id: ChainId, event_receiver: Receiver, system_clients: Arc>>>, + bifrost_client: Arc>, + rollback_senders: Arc>>>, + handle: SpawnTaskHandle, bootstrap_shared_data: Arc, ) -> Self { let client = system_clients.get(&id).expect(INVALID_CHAIN_ID).clone(); @@ -267,6 +275,9 @@ where socket_signature: Socket::SIGNATURE_HASH, client, system_clients, + bifrost_client, + rollback_senders, + handle, bootstrap_shared_data, } } @@ -300,12 +311,6 @@ where built_transaction.tx_request, metadata, socket_msg, - built_transaction.is_external, - if built_transaction.is_external { - GasCoefficient::Low - } else { - GasCoefficient::Mid - }, ) .await; } @@ -364,8 +369,8 @@ where ins_code: &Instruction, status: SocketEventStatus, ) -> Result { - let src = BigEndian::read_u32(&req_id.ChainIndex.0) as ChainId; - let dst = BigEndian::read_u32(&ins_code.ChainIndex.0) as ChainId; + let src = Into::::into(req_id.ChainIndex) as ChainId; + let dst = Into::::into(ins_code.ChainIndex) as ChainId; // if inbound::accepted and relaying to bitcoin we consider as ended if let Some(bitcoin_chain_id) = self.client.get_bitcoin_chain_id() { @@ -409,29 +414,14 @@ where /// Verifies whether the current relayer was selected at the given round. async fn is_selected_relayer(&self, round: &U256) -> Result { - if self.client.metadata.is_native { - let relayer_manager = self.client.protocol_contracts.relayer_manager.as_ref().unwrap(); - let res = relayer_manager - .is_previous_selected_relayer(*round, self.client.address(), false) - .call() - .await? - ._0; - return Ok(res); - } else if let Some((_id, native_client)) = - self.system_clients.iter().find(|(_id, client)| client.metadata.is_native) - { - // always use the native client's contract. due to handle missed VSP's. - let relayer_manager = - native_client.protocol_contracts.relayer_manager.as_ref().unwrap(); - let res = relayer_manager - .is_previous_selected_relayer(*round, self.client.address(), false) - .call() - .await? - ._0; - return Ok(res); - } - - Ok(false) + let relayer_manager = + self.bifrost_client.protocol_contracts.relayer_manager.as_ref().unwrap(); + let res = relayer_manager + .is_previous_selected_relayer(*round, self.client.address(), false) + .call() + .await? + ._0; + Ok(res) } /// Request send socket relay transaction to the target event channel. @@ -441,47 +431,21 @@ where tx_request: TransactionRequest, metadata: SocketRelayMetadata, socket_msg: Socket_Message, - give_random_delay: bool, - gas_coefficient: GasCoefficient, ) { - // if let Some(sender) = self.tx_request_senders.get(&chain_id) { - // if self.is_executable(metadata.is_inbound, metadata.status) { - // self.send_rollbackable_request(chain_id, metadata.clone(), socket_msg); - // } - - // match sender.send(TxRequestMessage::new( - // tx_request, - // TxRequestMetadata::SocketRelay(metadata.clone()), - // true, - // give_random_delay, - // gas_coefficient, - // metadata.is_bootstrap, - // )) { - // Ok(()) => log::info!( - // target: &self.client.get_chain_name(), - // "-[{}] 🔖 Request relay transaction to chain({:?}): {}", - // sub_display_format(SUB_LOG_TARGET), - // chain_id, - // metadata - // ), - // Err(error) => { - // let log_msg = format!( - // "-[{}]-[{}] ❗️ Failed to send relay transaction to chain({:?}): {}, Error: {}", - // sub_display_format(SUB_LOG_TARGET), - // self.client.address(), - // chain_id, - // metadata, - // error - // ); - // log::error!(target: &self.client.get_chain_name(), "{log_msg}"); - // sentry::capture_message( - // &format!("[{}]{log_msg}", &self.client.get_chain_name()), - // sentry::Level::Error, - // ); - // }, - // } - // } - todo!() + if let Some(client) = self.system_clients.get(&chain_id) { + if self.is_executable(metadata.is_inbound, metadata.status) { + self.send_rollbackable_request(chain_id, metadata.clone(), socket_msg); + } + + let metadata = TxRequestMetadata::SocketRelay(metadata); + send_transaction( + client.clone(), + tx_request, + format!("{} ({})", SUB_LOG_TARGET, self.client.get_chain_name()), + metadata, + self.handle.clone(), + ); + } } /// Sends a rollbackable socket message to the rollback channel. @@ -492,31 +456,30 @@ where metadata: SocketRelayMetadata, socket_msg: Socket_Message, ) { - // if let Some(rollback_sender) = self.rollback_senders.get(&chain_id) { - // match rollback_sender.send(socket_msg) { - // Ok(()) => log::info!( - // target: &self.client.get_chain_name(), - // "-[{}] 🔃 Store rollbackable socket message: {}", - // sub_display_format(SUB_LOG_TARGET), - // metadata - // ), - // Err(error) => { - // let msg = format!( - // "-[{}]-[{}] ❗️ Failed to store rollbackable socket message: {}, Error: {}", - // sub_display_format(SUB_LOG_TARGET), - // self.client.address(), - // metadata, - // error.to_string() - // ); - // log::error!(target: &self.client.get_chain_name(), "{msg}"); - // sentry::capture_message( - // &format!("[{}]{msg}", &self.client.get_chain_name()), - // sentry::Level::Error, - // ); - // }, - // } - // } - todo!() + if let Some(rollback_sender) = self.rollback_senders.get(&chain_id) { + match rollback_sender.send(socket_msg) { + Ok(()) => log::info!( + target: &self.client.get_chain_name(), + "-[{}] 🔃 Store rollbackable socket message: {}", + sub_display_format(SUB_LOG_TARGET), + metadata + ), + Err(error) => { + let msg = format!( + "-[{}]-[{}] ❗️ Failed to store rollbackable socket message: {}, Error: {}", + sub_display_format(SUB_LOG_TARGET), + self.client.address(), + metadata, + error.to_string() + ); + log::error!(target: &self.client.get_chain_name(), "{msg}"); + sentry::capture_message( + &format!("[{}]{msg}", &self.client.get_chain_name()), + sentry::Level::Error, + ); + }, + } + } } } @@ -630,102 +593,102 @@ where } } -// #[cfg(test)] -// mod tests { -// use alloy::{ -// primitives::{address, bytes}, -// providers::ProviderBuilder, -// sol_types::SolEvent, -// }; -// use br_primitives::contracts::socket::SocketContract::{self, Socket}; -// use std::sync::Arc; - -// use super::*; - -// #[tokio::test] -// async fn test_is_already_done() { -// let src_provider = Arc::new(ProviderBuilder::new().on_http("".parse().unwrap())); -// let dst_provider = Arc::new(ProviderBuilder::new().on_http("".parse().unwrap())); - -// let src_socket = SocketContract::new( -// address!("d551F33Ca8eCb0Be83d8799D9C68a368BA36Dd52"), -// src_provider.clone(), -// ); -// let dst_socket = SocketContract::new( -// address!("b5Fa48E8B9b89760a9f9176388D1B64A8D4968dF"), -// dst_provider.clone(), -// ); - -// let request_id = -// RequestID { ChainIndex: [0, 0, 11, 252].into(), round_id: 677, sequence: 3446 }; -// println!("request_id : {:?}", request_id); - -// let src_request = src_socket.get_request(request_id.clone()).call().await.unwrap(); -// println!("src_request : {:?}", src_request); -// let dst_request = dst_socket.get_request(request_id).call().await.unwrap(); -// println!("dst_request : {:?}", dst_request); -// } - -// #[test] -// fn test_socket_event_decode() { -// let data = bytes!("00000000000000000000000000000000000000000000000000000000000000200000bfc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000271200000000000000000000000000000000000000000000000000000000030203010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000003000000030000bfc0148a26ea2376f006c09b7d3163f1fc70ad4134a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e661745856b03130d03932f683cda020d7ee9ea0000000000000000000000006e661745856b03130d03932f683cda020d7ee9ea00000000000000000000000000000000000000000000000000000000000ee5e800000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000"); - -// match Socket::abi_decode_data(&data, true) { -// Ok(socket) => { -// let socket_msg = socket.0; -// let req_id = socket_msg.req_id; -// let status = socket_msg.status; -// let ins_code = socket_msg.ins_code; -// let params = socket_msg.params; - -// println!("req_id.chain -> {:?}", req_id.ChainIndex.encode_hex_with_prefix()); -// println!("req_id.round_id -> {:?}", req_id.round_id); -// println!("req_id.sequence -> {:?}", req_id.sequence); - -// println!("status -> {:?}", status); - -// println!("ins_code.chain -> {:?}", ins_code.ChainIndex.encode_hex_with_prefix()); -// println!("ins_code.method -> {:?}", ins_code.method.encode_hex_with_prefix()); - -// println!("params.tokenIDX0 -> {:?}", params.token_idx0.encode_hex_with_prefix()); -// println!("params.tokenIDX1 -> {:?}", params.token_idx1.encode_hex_with_prefix()); -// println!("params.refund -> {:?}", params.refund); -// println!("params.to -> {:?}", params.to); -// println!("params.amount -> {:?}", params.amount); -// println!("params.variants -> {:?}", params.variants.to_string()); -// }, -// Err(error) => { -// panic!("decode failed -> {}", error); -// }, -// } -// } - -// #[test] -// fn test_socket_msg_decode() { -// let req_id_chain = array_bytes::bytes2hex("0x", [0, 0, 39, 18]); -// let ins_code_chain = array_bytes::bytes2hex("0x", [0, 0, 191, 192]); -// let ins_code_method = -// array_bytes::bytes2hex("0x", [3, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - -// let params_tokenidx0 = array_bytes::bytes2hex( -// "0x", -// [ -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, -// ], -// ); -// let params_tokenidx1 = array_bytes::bytes2hex( -// "0x", -// [ -// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// 0, 0, 0, 0, -// ], -// ); - -// println!("req_id_chain -> {:?}", req_id_chain); -// println!("ins_code_chain -> {:?}", ins_code_chain); -// println!("ins_code_method -> {:?}", ins_code_method); -// println!("params_tokenidx0 -> {:?}", params_tokenidx0); -// println!("params_tokenidx1 -> {:?}", params_tokenidx1); -// } -// } +#[cfg(test)] +mod tests { + use alloy::{ + primitives::{address, bytes}, + providers::ProviderBuilder, + sol_types::SolEvent, + }; + use br_primitives::contracts::socket::SocketContract::{self, Socket}; + use std::sync::Arc; + + use super::*; + + #[tokio::test] + async fn test_is_already_done() { + let src_provider = Arc::new(ProviderBuilder::new().on_http("".parse().unwrap())); + let dst_provider = Arc::new(ProviderBuilder::new().on_http("".parse().unwrap())); + + let src_socket = SocketContract::new( + address!("d551F33Ca8eCb0Be83d8799D9C68a368BA36Dd52"), + src_provider.clone(), + ); + let dst_socket = SocketContract::new( + address!("b5Fa48E8B9b89760a9f9176388D1B64A8D4968dF"), + dst_provider.clone(), + ); + + let request_id = + RequestID { ChainIndex: [0, 0, 11, 252].into(), round_id: 677, sequence: 3446 }; + println!("request_id : {:?}", request_id); + + let src_request = src_socket.get_request(request_id.clone()).call().await.unwrap(); + println!("src_request : {:?}", src_request); + let dst_request = dst_socket.get_request(request_id).call().await.unwrap(); + println!("dst_request : {:?}", dst_request); + } + + #[test] + fn test_socket_event_decode() { + let data = bytes!("00000000000000000000000000000000000000000000000000000000000000200000bfc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000271200000000000000000000000000000000000000000000000000000000030203010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000003000000030000bfc0148a26ea2376f006c09b7d3163f1fc70ad4134a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e661745856b03130d03932f683cda020d7ee9ea0000000000000000000000006e661745856b03130d03932f683cda020d7ee9ea00000000000000000000000000000000000000000000000000000000000ee5e800000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000"); + + match Socket::abi_decode_data(&data, true) { + Ok(socket) => { + let socket_msg = socket.0; + let req_id = socket_msg.req_id; + let status = socket_msg.status; + let ins_code = socket_msg.ins_code; + let params = socket_msg.params; + + println!("req_id.chain -> {:?}", req_id.ChainIndex); + println!("req_id.round_id -> {:?}", req_id.round_id); + println!("req_id.sequence -> {:?}", req_id.sequence); + + println!("status -> {:?}", status); + + println!("ins_code.chain -> {:?}", ins_code.ChainIndex); + println!("ins_code.method -> {:?}", ins_code.RBCmethod); + + println!("params.tokenIDX0 -> {:?}", params.tokenIDX0); + println!("params.tokenIDX1 -> {:?}", params.tokenIDX1); + println!("params.refund -> {:?}", params.refund); + println!("params.to -> {:?}", params.to); + println!("params.amount -> {:?}", params.amount); + println!("params.variants -> {:?}", params.variants); + }, + Err(error) => { + panic!("decode failed -> {}", error); + }, + } + } + + #[test] + fn test_socket_msg_decode() { + let req_id_chain = array_bytes::bytes2hex("0x", [0, 0, 39, 18]); + let ins_code_chain = array_bytes::bytes2hex("0x", [0, 0, 191, 192]); + let ins_code_method = + array_bytes::bytes2hex("0x", [3, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + let params_tokenidx0 = array_bytes::bytes2hex( + "0x", + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + ); + let params_tokenidx1 = array_bytes::bytes2hex( + "0x", + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + ], + ); + + println!("req_id_chain -> {:?}", req_id_chain); + println!("ins_code_chain -> {:?}", ins_code_chain); + println!("ins_code_method -> {:?}", ins_code_method); + println!("params_tokenidx0 -> {:?}", params_tokenidx0); + println!("params_tokenidx1 -> {:?}", params_tokenidx1); + } +} diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index e24c4ffa..77a6a084 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -5,24 +5,28 @@ use br_primitives::{ }, contracts::authority::BfcStaking::round_meta_data, eth::{AggregatorContracts, ProtocolContracts, ProviderMetadata}, + tx::TxRequestMetadata, + utils::generate_delay, }; use alloy::{ network::Ethereum, primitives::{ - utils::{format_units, parse_ether}, + utils::{format_units, parse_ether, Unit}, Address, ChainId, }, providers::{ fillers::{FillProvider, TxFiller}, PendingTransactionBuilder, Provider, RootProvider, SendableTx, WalletProvider, }, + rpc::types::TransactionRequest, signers::{local::LocalSigner, Signature, Signer as _}, transports::{Transport, TransportResult}, }; use eyre::{eyre, Result}; use k256::ecdsa::SigningKey; -use std::sync::Arc; +use sc_service::SpawnTaskHandle; +use std::{sync::Arc, time::Duration}; use url::Url; pub mod events; @@ -180,3 +184,54 @@ where self.inner.send_transaction_internal(tx).await } } + +pub fn send_transaction( + client: Arc>, + mut request: TransactionRequest, + requester: String, + metadata: TxRequestMetadata, + handle: SpawnTaskHandle, +) where + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, + T: Transport + Clone, +{ + handle.spawn("send_transaction", None, async move { + if client.metadata.is_native { + // gas price is fixed to 1000 Gwei on bifrost network + request.max_fee_per_gas = Some(0); + request.max_priority_fee_per_gas = Some(1000 * Unit::GWEI.wei().to::()); + } else { + // to avoid duplicate(will revert) external networks transactions + tokio::time::sleep(Duration::from_millis(generate_delay())).await; + + if !client.metadata.eip1559 { + // TODO: if transaction failed by gas price issue, should bring back `GasCoefficient` here. + request.gas_price = Some(client.get_gas_price().await.unwrap()); + } + } + + match client.send_transaction(request).await { + Ok(pending) => { + log::info!( + target: &requester, + " 🔖 Send transaction ({} tx:{}): {}", + client.get_chain_name(), + pending.tx_hash(), + metadata + ); + }, + Err(err) => { + let msg = format!( + " ❗️ Failed to send transaction ({} address:{}): {}, Error: {}", + client.get_chain_name(), + client.address(), + metadata, + err + ); + log::error!(target: &requester, "{msg}"); + sentry::capture_message(&msg, sentry::Level::Error); + }, + } + }); +} diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index 2ab7b4c7..d7d50a03 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -104,48 +104,6 @@ where } } -#[async_trait::async_trait] -pub trait LegacyGasMiddleware { - /// Get the current gas price of the network. - async fn get_gas_price(&self) -> U256; - - /// Get gas_price for legacy retry transaction request. - /// - /// Returns `max(current_network_gas_price,escalated_gas_price)` - async fn get_gas_price_for_retry( - &self, - previous_gas_price: U256, - gas_price_coefficient: f64, - min_gas_price: U256, - ) -> U256; - - /// Get gas_price for escalated legacy transaction request. This will be only used when - /// `is_initially_escalated` is enabled. - async fn get_gas_price_for_escalation( - &self, - gas_price: U256, - gas_price_coefficient: f64, - min_gas_price: U256, - ) -> U256; - - /// Handles the failed gas price rpc request. - async fn handle_failed_get_gas_price(&self, retries_remaining: u8, error: String) -> U256; -} - -#[async_trait::async_trait] -pub trait Eip1559GasMiddleware { - /// Gets a heuristic recommendation of max fee per gas and max priority fee per gas for EIP-1559 - /// compatible transactions. - async fn get_estimated_eip1559_fees(&self) -> (U256, U256); - - /// Handles the failed eip1559 fees rpc request. - async fn handle_failed_get_estimated_eip1559_fees( - &self, - retries_remaining: u8, - error: String, - ) -> (U256, U256); -} - #[async_trait::async_trait] /// The manager trait for Legacy and Eip1559 transactions. pub trait TransactionManager @@ -154,9 +112,6 @@ where P: Provider, T: Transport + Clone, { - /// Starts the transaction manager. Listens to every new consumed tx request message. - async fn run(&mut self); - /// Initialize transaction manager. async fn initialize(&mut self); diff --git a/periodic/Cargo.toml b/periodic/Cargo.toml index c6284048..4bf9f7e4 100644 --- a/periodic/Cargo.toml +++ b/periodic/Cargo.toml @@ -28,7 +28,7 @@ subxt = { workspace = true } array-bytes = { workspace = true } miniscript = { workspace = true } eyre = { workspace = true } -byteorder = { workspace = true } +sc-service = { workspace = true } # Bifrost Relayer br-cli = { path = "../client/cli", default-features = false } diff --git a/periodic/src/heartbeat_sender.rs b/periodic/src/heartbeat_sender.rs index 7ba802b8..cd0834e0 100644 --- a/periodic/src/heartbeat_sender.rs +++ b/periodic/src/heartbeat_sender.rs @@ -1,22 +1,17 @@ use alloy::{ - primitives::ChainId, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::TransactionRequest, transports::Transport, }; -use br_client::eth::EthClient; +use br_client::eth::{send_transaction, EthClient}; use br_primitives::{ - constants::{ - errors::{INVALID_BIFROST_NATIVENESS, INVALID_PERIODIC_SCHEDULE}, - schedule::HEARTBEAT_SCHEDULE, - }, - eth::GasCoefficient, - tx::{HeartbeatMetadata, TxRequestMessage, TxRequestMetadata}, - utils::sub_display_format, + constants::{errors::INVALID_PERIODIC_SCHEDULE, schedule::HEARTBEAT_SCHEDULE}, + tx::{HeartbeatMetadata, TxRequestMetadata}, }; use cron::Schedule; use eyre::Result; -use std::{collections::BTreeMap, str::FromStr, sync::Arc}; +use sc_service::SpawnTaskHandle; +use std::{str::FromStr, sync::Arc}; use crate::traits::PeriodicWorker; @@ -33,15 +28,15 @@ where schedule: Schedule, /// The `EthClient` to interact with the bifrost network. client: Arc>, - /// The clients to interact with external chains. - clients: Arc>>>, + /// The handle to spawn tasks. + handle: SpawnTaskHandle, } #[async_trait::async_trait] impl PeriodicWorker for HeartbeatSender where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -75,19 +70,16 @@ where impl HeartbeatSender where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `HeartbeatSender` instance. - pub fn new( - client: Arc>, - clients: Arc>>>, - ) -> Self { + pub fn new(client: Arc>, handle: SpawnTaskHandle) -> Self { Self { schedule: Schedule::from_str(HEARTBEAT_SCHEDULE).expect(INVALID_PERIODIC_SCHEDULE), client, - clients, + handle, } } @@ -105,28 +97,12 @@ where tx_request: TransactionRequest, metadata: HeartbeatMetadata, ) { - // match self.tx_request_sender.send(TxRequestMessage::new( - // tx_request, - // TxRequestMetadata::Heartbeat(metadata.clone()), - // false, - // false, - // GasCoefficient::Low, - // false, - // )) { - // Ok(()) => log::info!( - // target: &self.client.get_chain_name(), - // "-[{}] 💓 Request Heartbeat transaction: {}", - // sub_display_format(SUB_LOG_TARGET), - // metadata - // ), - // Err(error) => log::error!( - // target: &self.client.get_chain_name(), - // "-[{}] ❗️ Failed to request Heartbeat transaction: {}, Error: {}", - // sub_display_format(SUB_LOG_TARGET), - // metadata, - // error.to_string() - // ), - // } - todo!() + send_transaction( + self.client.clone(), + tx_request, + SUB_LOG_TARGET.to_string(), + TxRequestMetadata::Heartbeat(metadata), + self.handle.clone(), + ); } } diff --git a/periodic/src/price_feeder.rs b/periodic/src/price_feeder.rs index 55830a86..05ff0586 100644 --- a/periodic/src/price_feeder.rs +++ b/periodic/src/price_feeder.rs @@ -11,18 +11,15 @@ use chrono::{DateTime, Utc}; use cron::Schedule; use eyre::Result; use rand::Rng; +use sc_service::SpawnTaskHandle; use tokio::time::sleep; -use br_client::eth::EthClient; +use br_client::eth::{send_transaction, EthClient}; use br_primitives::{ - constants::{ - errors::{INVALID_BIFROST_NATIVENESS, INVALID_PERIODIC_SCHEDULE}, - schedule::PRICE_FEEDER_SCHEDULE, - }, + constants::{errors::INVALID_PERIODIC_SCHEDULE, schedule::PRICE_FEEDER_SCHEDULE}, contracts::socket::get_asset_oids, - eth::GasCoefficient, periodic::{PriceResponse, PriceSource}, - tx::{PriceFeedMetadata, TxRequestMessage, TxRequestMetadata, TxRequestSender}, + tx::{PriceFeedMetadata, TxRequestMetadata}, utils::sub_display_format, }; @@ -52,13 +49,15 @@ where asset_oid: BTreeMap<&'static str, B256>, /// The vector that contains each `EthClient`. clients: Arc>>>, + /// The handle to spawn tasks. + handle: SpawnTaskHandle, } #[async_trait] impl PeriodicWorker for OraclePriceFeeder where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -92,13 +91,14 @@ where impl OraclePriceFeeder where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { pub fn new( client: Arc>, clients: Arc>>>, + handle: SpawnTaskHandle, ) -> Self { let asset_oid = get_asset_oids(); @@ -109,6 +109,7 @@ where asset_oid, client, clients, + handle, } } @@ -296,37 +297,12 @@ where tx_request: TransactionRequest, metadata: PriceFeedMetadata, ) { - // match self.tx_request_sender.send(TxRequestMessage::new( - // tx_request, - // TxRequestMetadata::PriceFeed(metadata.clone()), - // false, - // false, - // GasCoefficient::Mid, - // false, - // )) { - // Ok(()) => log::info!( - // target: &self.client.get_chain_name(), - // "-[{}] 💵 Request price feed transaction to chain({:?}): {}", - // sub_display_format(SUB_LOG_TARGET), - // self.client.chain_id(), - // metadata - // ), - // Err(error) => { - // let log_msg = format!( - // "-[{}]-[{}] ❗️ Failed to request price feed transaction to chain({:?}): {}, Error: {}", - // sub_display_format(SUB_LOG_TARGET), - // self.client.address(), - // self.client.chain_id(), - // metadata, - // error.to_string() - // ); - // log::error!(target: &self.client.get_chain_name(), "{log_msg}"); - // sentry::capture_message( - // &format!("[{}]{log_msg}", &self.client.get_chain_name()), - // sentry::Level::Error, - // ); - // }, - // } - todo!() + send_transaction( + self.client.clone(), + tx_request, + SUB_LOG_TARGET.to_string(), + TxRequestMetadata::PriceFeed(metadata), + self.handle.clone(), + ); } } diff --git a/periodic/src/price_source/upbit.rs b/periodic/src/price_source/upbit.rs index 66f730a1..59d87805 100644 --- a/periodic/src/price_source/upbit.rs +++ b/periodic/src/price_source/upbit.rs @@ -112,7 +112,7 @@ impl UpbitPriceFetcher { Err(_) => Err(Error), } } else { - todo!() + unimplemented!() } } diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index 8b66779c..714dfe95 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -1,30 +1,29 @@ use alloy::{ dyn_abi::DynSolValue, - primitives::{Address, ChainId, U256}, + primitives::{Address, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{Filter, Log, TransactionInput, TransactionRequest}, sol_types::SolEvent as _, transports::Transport, }; use cron::Schedule; -use std::{collections::BTreeMap, str::FromStr, sync::Arc, time::Duration}; +use sc_service::SpawnTaskHandle; +use std::{str::FromStr, sync::Arc, time::Duration}; use tokio::time::sleep; -use br_client::eth::{traits::BootstrapHandler, EthClient}; +use br_client::eth::{send_transaction, traits::BootstrapHandler, EthClient}; use br_primitives::{ bootstrap::BootstrapSharedData, constants::{ - cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, - config::BOOTSTRAP_BLOCK_CHUNK_SIZE, - errors::{INVALID_BIFROST_NATIVENESS, INVALID_PERIODIC_SCHEDULE}, - schedule::ROUNDUP_EMITTER_SCHEDULE, + cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE, + errors::INVALID_PERIODIC_SCHEDULE, schedule::ROUNDUP_EMITTER_SCHEDULE, }, contracts::socket::{ SocketContract::RoundUp, Socket_Struct::{Round_Up_Submit, Signatures}, }, - eth::{BootstrapState, GasCoefficient, RoundUpEventStatus}, - tx::{TxRequestMessage, TxRequestMetadata, TxRequestSender, VSPPhase1Metadata}, + eth::{BootstrapState, RoundUpEventStatus}, + tx::{TxRequestMetadata, VSPPhase1Metadata}, utils::sub_display_format, }; use eyre::Result; @@ -43,19 +42,19 @@ where current_round: U256, /// The ethereum client for the Bifrost network. client: Arc>, - /// The clients for the external chains. - clients: Arc>>>, /// The time schedule that represents when to check round info. schedule: Schedule, /// The bootstrap shared data. bootstrap_shared_data: Arc, + /// The handle to spawn tasks. + handle: SpawnTaskHandle, } #[async_trait::async_trait] impl PeriodicWorker for RoundupEmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -108,32 +107,26 @@ where impl RoundupEmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `RoundupEmitter` instance. pub fn new( client: Arc>, - clients: Arc>>>, bootstrap_shared_data: Arc, + handle: SpawnTaskHandle, ) -> Self { - Self { current_round: U256::default(), client, - clients, schedule: Schedule::from_str(ROUNDUP_EMITTER_SCHEDULE) .expect(INVALID_PERIODIC_SCHEDULE), bootstrap_shared_data, + handle, } } - /// Decode & Serialize log to `RoundUp` struct. - fn decode_log(&self, log: Log) -> Result { - Ok(log.log_decode::()?.inner.data) - } - /// Check relayer has selected in previous round async fn is_selected_relayer(&self, round: U256) -> Result { let relayer_manager = self.client.protocol_contracts.relayer_manager.as_ref().unwrap(); @@ -189,30 +182,13 @@ where tx_request: TransactionRequest, metadata: VSPPhase1Metadata, ) { - // match self.tx_request_sender.send(TxRequestMessage::new( - // tx_request, - // TxRequestMetadata::VSPPhase1(metadata.clone()), - // false, - // false, - // GasCoefficient::Mid, - // false, - // )) { - // Ok(()) => log::info!( - // target: &self.client.get_chain_name(), - // "-[{}] 👤 Request VSP phase1 transaction: {}", - // sub_display_format(SUB_LOG_TARGET), - // metadata - // ), - // Err(error) => log::error!( - // target: &self.client.get_chain_name(), - // "-[{}] ❗️ Failed to request VSP phase1 transaction: {}, Error: {}", - // sub_display_format(SUB_LOG_TARGET), - // metadata, - // error.to_string() - // ), - // } - - todo!() + send_transaction( + self.client.clone(), + tx_request, + SUB_LOG_TARGET.to_string(), + TxRequestMetadata::VSPPhase1(metadata), + self.handle.clone(), + ); } /// Get the latest round index. @@ -224,8 +200,8 @@ where #[async_trait::async_trait] impl BootstrapHandler for RoundupEmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { @@ -235,11 +211,9 @@ where async fn bootstrap(&self) -> Result<()> { let get_next_poll_round = || async move { let logs = self.get_bootstrap_events().await.unwrap(); - // let round_up_events = - // logs.iter().map(|log| self.decode_log(log.clone()).unwrap()).collect(); let round_up_events: Vec = - logs.iter().map(|log| self.decode_log(log.clone()).unwrap()).collect(); + logs.iter().map(|log| log.log_decode::().unwrap().inner.data).collect(); let max_round = round_up_events .iter() diff --git a/periodic/src/socket_rollback_emitter.rs b/periodic/src/socket_rollback_emitter.rs index d6a39f1f..8d097542 100644 --- a/periodic/src/socket_rollback_emitter.rs +++ b/periodic/src/socket_rollback_emitter.rs @@ -5,22 +5,22 @@ use alloy::{ rpc::types::TransactionRequest, transports::Transport, }; -use byteorder::{BigEndian, ByteOrder as _}; use cron::Schedule; use eyre::Result; +use sc_service::SpawnTaskHandle; use std::{collections::BTreeMap, str::FromStr, sync::Arc}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use br_client::eth::{traits::SocketRelayBuilder, EthClient}; +use br_client::eth::{send_transaction, traits::SocketRelayBuilder, EthClient}; use br_primitives::{ constants::{ - errors::{INVALID_BIFROST_NATIVENESS, INVALID_CHAIN_ID, INVALID_PERIODIC_SCHEDULE}, + errors::{INVALID_BIFROST_NATIVENESS, INVALID_PERIODIC_SCHEDULE}, schedule::{ROLLBACK_CHECK_MINIMUM_INTERVAL, ROLLBACK_CHECK_SCHEDULE}, }, contracts::socket::Socket_Struct::{RequestID, RequestInfo, Signatures, Socket_Message}, - eth::{GasCoefficient, RelayDirection, SocketEventStatus}, + eth::{RelayDirection, SocketEventStatus}, periodic::{RawRequestID, RollbackableMessage}, - tx::{RollbackMetadata, TxRequestMessage, TxRequestMetadata, TxRequestSender}, + tx::{RollbackMetadata, TxRequestMetadata}, utils::sub_display_format, }; @@ -45,36 +45,37 @@ where rollback_receiver: UnboundedReceiver, /// The local storage saving emitted `Socket` event messages. rollback_msgs: BTreeMap, - /// The sender that sends messages to the tx request channel. - tx_request_sender: Arc, /// The time schedule that represents when to check heartbeat pulsed. schedule: Schedule, + /// The handle to spawn tasks. + handle: SpawnTaskHandle, } impl SocketRollbackEmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `SocketRollbackEmitter`. pub fn new( - tx_request_sender: Arc, + client: Arc>, system_clients: Arc>>>, - ) -> (Self, UnboundedSender) { + handle: SpawnTaskHandle, + ) -> (Self, Arc>) { let (sender, rollback_receiver) = mpsc::unbounded_channel::(); ( Self { - client: system_clients.get(&tx_request_sender.id).expect(INVALID_CHAIN_ID).clone(), + client, system_clients, rollback_receiver, rollback_msgs: BTreeMap::new(), - tx_request_sender, schedule: Schedule::from_str(ROLLBACK_CHECK_SCHEDULE) .expect(INVALID_PERIODIC_SCHEDULE), + handle, }, - sender, + Arc::new(sender), ) } @@ -83,13 +84,13 @@ where let src_request = self .get_socket_request( &socket_msg.req_id, - BigEndian::read_u32(&socket_msg.req_id.ChainIndex.0) as ChainId, + Into::::into(socket_msg.req_id.ChainIndex) as ChainId, ) .await?; let dst_request = self .get_socket_request( &socket_msg.req_id, - BigEndian::read_u32(&socket_msg.ins_code.ChainIndex.0) as ChainId, + Into::::into(socket_msg.ins_code.ChainIndex) as ChainId, ) .await?; @@ -101,9 +102,9 @@ where SocketEventStatus::Committed | SocketEventStatus::Rollbacked => return Ok(true), _ => (), } - if self.is_inbound_sequence( - BigEndian::read_u32(&socket_msg.ins_code.ChainIndex.0) as ChainId - ) { + if self + .is_inbound_sequence(Into::::into(socket_msg.ins_code.ChainIndex) as ChainId) + { match dst_status { SocketEventStatus::Executed | SocketEventStatus::Reverted @@ -175,13 +176,13 @@ where true, SocketEventStatus::Failed, socket_msg.req_id.sequence, - BigEndian::read_u32(&socket_msg.req_id.ChainIndex.0) as ChainId, - BigEndian::read_u32(&socket_msg.ins_code.ChainIndex.0) as ChainId, + Into::::into(socket_msg.req_id.ChainIndex) as ChainId, + Into::::into(socket_msg.ins_code.ChainIndex) as ChainId, ); // transaction executed on Bifrost so no random delay required. // due to majority checks, higher gas coefficient required. - self.request_send_transaction(tx_request, metadata, false, GasCoefficient::Mid); + self.request_send_transaction(tx_request, metadata); } /// Tries to rollback the given outbound socket message. @@ -201,13 +202,13 @@ where false, SocketEventStatus::Rejected, socket_msg.req_id.sequence, - BigEndian::read_u32(&socket_msg.req_id.ChainIndex.0) as ChainId, - BigEndian::read_u32(&socket_msg.ins_code.ChainIndex.0) as ChainId, + Into::::into(socket_msg.req_id.ChainIndex) as ChainId, + Into::::into(socket_msg.ins_code.ChainIndex) as ChainId, ); // transaction executed on External chain's so random delay required. // aggregated relay typed transactions are good with low gas coefficient. - self.request_send_transaction(tx_request, metadata, true, GasCoefficient::Low); + self.request_send_transaction(tx_request, metadata); Ok(()) } @@ -218,8 +219,8 @@ where while let Ok(msg) = self.rollback_receiver.try_recv() { // prevent rollback for bitcoin bridges if let Some(bitcoin_chain_id) = self.client.get_bitcoin_chain_id() { - if BigEndian::read_u32(&msg.req_id.ChainIndex.0) as u64 == bitcoin_chain_id - || BigEndian::read_u32(&msg.ins_code.ChainIndex.0) as u64 == bitcoin_chain_id + if Into::::into(msg.req_id.ChainIndex) as ChainId == bitcoin_chain_id + || Into::::into(msg.ins_code.ChainIndex) as ChainId == bitcoin_chain_id { continue; } @@ -243,46 +244,14 @@ where } /// Request a socket rollback transaction to the target tx request channel. - fn request_send_transaction( - &self, - tx_request: TransactionRequest, - metadata: RollbackMetadata, - give_random_delay: bool, - gas_coefficient: GasCoefficient, - ) { - // asynchronous transaction tasks will work fine for rollback transactions, - // so `is_bootstrap` parameter is set to `false`. - match self.tx_request_sender.send(TxRequestMessage::new( + fn request_send_transaction(&self, tx_request: TransactionRequest, metadata: RollbackMetadata) { + send_transaction( + self.client.clone(), tx_request, - TxRequestMetadata::Rollback(metadata.clone()), - true, - give_random_delay, - gas_coefficient, - false, - )) { - Ok(()) => { - log::info!( - target: &self.client.get_chain_name(), - "-[{}] 🔃 Try Rollback::Socket: {}", - sub_display_format(SUB_LOG_TARGET), - metadata - ); - }, - Err(error) => { - let log_msg = format!( - "-[{}]-[{}] ❗️ Failed to try Rollback::Socket: {}, Error: {}", - sub_display_format(SUB_LOG_TARGET), - self.client.address(), - metadata, - error - ); - log::error!(target: &self.client.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &self.client.get_chain_name()), - sentry::Level::Error, - ); - }, - } + SUB_LOG_TARGET.to_string(), + TxRequestMetadata::Rollback(metadata), + self.handle.clone(), + ); } } @@ -308,8 +277,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for SocketRollbackEmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -345,7 +314,7 @@ where continue; } // the pending request has not been processed in the waiting period. rollback should be handled. - self.try_rollback(&rollback_msg.socket_msg).await; + self.try_rollback(&rollback_msg.socket_msg).await?; handled_req_ids.push(req_id); } } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 6a99dea8..1aaa4078 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -32,3 +32,4 @@ bitcoincore-rpc = { workspace = true } url = { workspace = true } sha3 = { workspace = true } k256 = { workspace = true } +rand = { workspace = true } diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index fc7b6ddc..45456b53 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -44,6 +44,8 @@ pub struct ProviderMetadata { pub get_logs_batch_size: u64, /// The `get_block` request interval in milliseconds. pub call_interval: u64, + /// The flag whether EIP-1559 is enabled. + pub eip1559: bool, /// Relay direction when CCCP event points this chain as destination. pub if_destination_chain: RelayDirection, /// The flag whether the chain is Bifrost(native) or an external chain. @@ -58,6 +60,7 @@ impl ProviderMetadata { bitcoin_chain_id: Option, block_confirmations: u64, call_interval: u64, + eip1559: bool, get_logs_batch_size: u64, is_native: bool, ) -> Self { @@ -69,6 +72,7 @@ impl ProviderMetadata { block_confirmations: block_confirmations.saturating_add(get_logs_batch_size), get_logs_batch_size, call_interval, + eip1559, is_native, if_destination_chain: match is_native { true => RelayDirection::Inbound, diff --git a/primitives/src/periodic.rs b/primitives/src/periodic.rs index f7d91cce..ba47120c 100644 --- a/primitives/src/periodic.rs +++ b/primitives/src/periodic.rs @@ -1,6 +1,5 @@ -use alloy::primitives::{ChainId, U256}; +use alloy::primitives::U256; use serde::Deserialize; -use tokio::sync::mpsc::{error::SendError, UnboundedSender}; use crate::contracts::socket::Socket_Struct::Socket_Message; @@ -40,21 +39,3 @@ impl RollbackableMessage { /// The primitive sequence ID of a single socket request. pub type RawRequestID = u128; - -/// The channel message sender for rollbackable socket messages. -pub struct RollbackSender { - /// The unique chain ID for this sender. - pub id: ChainId, - /// The channel message sender. - pub sender: UnboundedSender, -} - -impl RollbackSender { - pub fn new(id: ChainId, sender: UnboundedSender) -> Self { - Self { id, sender } - } - - pub fn send(&self, message: Socket_Message) -> Result<(), SendError> { - self.sender.send(message) - } -} diff --git a/primitives/src/tx.rs b/primitives/src/tx.rs index 08a263e8..96caad53 100644 --- a/primitives/src/tx.rs +++ b/primitives/src/tx.rs @@ -691,28 +691,6 @@ impl TxRequestMessage { } } -/// The message sender connected to the event channel. -pub struct TxRequestSender { - /// The chain ID of the event channel. - pub id: ChainId, - /// The message sender. - pub sender: UnboundedSender, - /// Is Bifrost network? - pub is_native: bool, -} - -impl TxRequestSender { - /// Instantiates a new `TxRequestSender` instance. - pub fn new(id: ChainId, sender: UnboundedSender, is_native: bool) -> Self { - Self { id, sender, is_native } - } - - /// Sends a new event message. - pub fn send(&self, message: TxRequestMessage) -> Result<(), SendError> { - self.sender.send(message) - } -} - pub struct XtRequestMessage { /// The remaining retries of the transaction request. pub retries_remaining: u8, diff --git a/primitives/src/utils.rs b/primitives/src/utils.rs index f9c911f6..a5e7d4c8 100644 --- a/primitives/src/utils.rs +++ b/primitives/src/utils.rs @@ -1,5 +1,6 @@ use alloy::primitives::{keccak256, Address, Signature as EthersSignature, B256}; use k256::{ecdsa::VerifyingKey, elliptic_curve::sec1::ToEncodedPoint}; +use rand::Rng as _; use sha3::{Digest, Keccak256}; use crate::substrate::{EthereumSignature, Signature}; @@ -8,6 +9,10 @@ pub fn sub_display_format(log_target: &str) -> String { format!("{:<019}", log_target) } +pub fn generate_delay() -> u64 { + rand::thread_rng().gen_range(0..=12000) +} + /// Converts the ethers::Signature to a bifrost_runtime::Signature. pub fn convert_ethers_to_ecdsa_signature(ethers_signature: EthersSignature) -> EthereumSignature { let sig: [u8; 65] = ethers_signature.into(); diff --git a/relayer/src/service.rs b/relayer/src/service.rs index a2eac646..8f62739b 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -32,10 +32,7 @@ use br_primitives::{ cli::{Configuration, HandlerType}, constants::{ cli::{DEFAULT_GET_LOGS_BATCH_SIZE, DEFAULT_KEYSTORE_PATH, DEFAULT_PROMETHEUS_PORT}, - errors::{ - INVALID_BIFROST_NATIVENESS, INVALID_BITCOIN_NETWORK, INVALID_PRIVATE_KEY, - INVALID_PROVIDER_URL, - }, + errors::{INVALID_BITCOIN_NETWORK, INVALID_PRIVATE_KEY, INVALID_PROVIDER_URL}, tx::DEFAULT_CALL_RETRIES, }, eth::{ @@ -56,6 +53,8 @@ use crate::{ pub fn relay(config: Configuration) -> Result { assert_configuration_validity(&config); + let task_manager = TaskManager::new(config.clone().tokio_handle, None)?; + let evm_providers = &config.relayer_config.evm_providers; let btc_provider = &config.relayer_config.btc_provider; let system = &config.relayer_config.system; @@ -91,6 +90,7 @@ pub fn relay(config: Configuration) -> Result { if is_native { Some(btc_provider.id) } else { None }, evm_provider.block_confirmations, evm_provider.call_interval, + evm_provider.eip1559.unwrap_or(false), evm_provider.get_logs_batch_size.unwrap_or(DEFAULT_GET_LOGS_BATCH_SIZE), is_native, ), @@ -119,7 +119,69 @@ pub fn relay(config: Configuration) -> Result { }) .collect::>(); - new_relay_base(config, clients).map(|RelayBase { task_manager, .. }| task_manager) + let bootstrap_shared_data = BootstrapSharedData::new(&config); + + let pending_outbounds = PendingOutboundPool::new(); + let keypair_storage = Arc::new(RwLock::new(KeypairStorage::new( + config + .clone() + .relayer_config + .system + .keystore_path + .unwrap_or(DEFAULT_KEYSTORE_PATH.to_string()), + config.relayer_config.system.keystore_password.clone(), + Network::from_core_arg(&config.relayer_config.btc_provider.chain) + .expect(INVALID_BITCOIN_NETWORK), + ))); + + let migration_sequence = Arc::new(RwLock::new(MigrationSequence::Normal)); + + let manager_deps = ManagerDeps::new(&config, Arc::new(clients), bootstrap_shared_data.clone()); + let bfc_client = manager_deps.bifrost_client.clone(); + + let substrate_deps = SubstrateDeps::new(bfc_client.clone(), &task_manager); + let periodic_deps = PeriodicDeps::new( + bootstrap_shared_data.clone(), + migration_sequence.clone(), + keypair_storage.clone(), + &substrate_deps, + manager_deps.clients.clone(), + bfc_client.clone(), + &task_manager, + ); + let handler_deps = HandlerDeps::new( + &config, + &manager_deps, + bootstrap_shared_data.clone(), + bfc_client.clone(), + periodic_deps.rollback_senders.clone(), + &task_manager, + ); + let btc_deps = BtcDeps::new( + &config, + pending_outbounds.clone(), + keypair_storage.clone(), + bootstrap_shared_data.clone(), + &substrate_deps, + migration_sequence.clone(), + bfc_client.clone(), + &task_manager, + ); + + print_relay_targets(&manager_deps); + + Ok(spawn_relayer_tasks( + task_manager, + FullDeps { + bootstrap_shared_data, + manager_deps, + periodic_deps, + handler_deps, + substrate_deps, + btc_deps, + }, + &config, + )) } /// Spawn relayer service tasks by the `TaskManager`. @@ -150,6 +212,7 @@ where mut heartbeat_sender, mut oracle_price_feeder, mut roundup_emitter, + rollback_emitters, mut keypair_migrator, mut presubmitter, .. @@ -213,6 +276,21 @@ where }, ); + // spawn socket rollback emitters + rollback_emitters.into_iter().for_each(|mut emitter| { + task_manager.spawn_essential_handle().spawn( + Box::leak( + format!("{}-socket-rollback-emitter", emitter.client.get_chain_name()) + .into_boxed_str(), + ), + Some("rollback"), + async move { + emitter.run().await; + () + }, + ) + }); + // spawn socket relay handlers socket_relay_handlers.into_iter().for_each(|mut handler| { let socket_barrier_clone = socket_barrier.clone(); @@ -400,80 +478,3 @@ where .join(", ") ); } - -/// Builds the internal components for the relayer service and spawns asynchronous tasks. -fn new_relay_base( - config: Configuration, - clients: BTreeMap>>, -) -> Result -where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, - T: Transport + Clone, -{ - let task_manager = TaskManager::new(config.clone().tokio_handle, None)?; - - let bootstrap_shared_data = BootstrapSharedData::new(&config); - - let pending_outbounds = PendingOutboundPool::new(); - let keypair_storage = Arc::new(RwLock::new(KeypairStorage::new( - config - .clone() - .relayer_config - .system - .keystore_path - .unwrap_or(DEFAULT_KEYSTORE_PATH.to_string()), - config.relayer_config.system.keystore_password.clone(), - Network::from_core_arg(&config.relayer_config.btc_provider.chain) - .expect(INVALID_BITCOIN_NETWORK), - ))); - - let migration_sequence = Arc::new(RwLock::new(MigrationSequence::Normal)); - - let manager_deps = ManagerDeps::new(&config, Arc::new(clients), bootstrap_shared_data.clone()); - let bfc_client = manager_deps.bifrost_client.clone(); - - let substrate_deps = SubstrateDeps::new(bfc_client.clone(), &task_manager); - let periodic_deps = PeriodicDeps::new( - bootstrap_shared_data.clone(), - migration_sequence.clone(), - keypair_storage.clone(), - &substrate_deps, - manager_deps.clients.clone(), - bfc_client.clone(), - ); - let handler_deps = - HandlerDeps::new(&config, &manager_deps, bootstrap_shared_data.clone(), bfc_client.clone()); - let btc_deps = BtcDeps::new( - &config, - pending_outbounds.clone(), - keypair_storage.clone(), - bootstrap_shared_data.clone(), - &manager_deps, - &substrate_deps, - migration_sequence.clone(), - bfc_client.clone(), - ); - - print_relay_targets(&manager_deps); - - Ok(RelayBase { - task_manager: spawn_relayer_tasks( - task_manager, - FullDeps { - bootstrap_shared_data, - manager_deps, - periodic_deps, - handler_deps, - substrate_deps, - btc_deps, - }, - &config, - ), - }) -} - -struct RelayBase { - /// The task manager of the relayer. - task_manager: TaskManager, -} diff --git a/relayer/src/service_deps/btc_deps.rs b/relayer/src/service_deps/btc_deps.rs index efa3db7e..196a2bfc 100644 --- a/relayer/src/service_deps/btc_deps.rs +++ b/relayer/src/service_deps/btc_deps.rs @@ -31,10 +31,10 @@ where pending_outbounds: PendingOutboundPool, keypair_storage: Arc>, bootstrap_shared_data: BootstrapSharedData, - manager_deps: &ManagerDeps, substrate_deps: &SubstrateDeps, migration_sequence: Arc>, bfc_client: Arc>, + task_manager: &TaskManager, ) -> Self { let bootstrap_shared_data = Arc::new(bootstrap_shared_data.clone()); let network = Network::from_core_arg(&config.relayer_config.btc_provider.chain) @@ -69,15 +69,15 @@ where ); let inbound = InboundHandler::new( bfc_client.clone(), - manager_deps.clients.clone(), block_manager.subscribe(), bootstrap_shared_data.clone(), + task_manager.spawn_handle(), ); let outbound = OutboundHandler::new( bfc_client.clone(), - manager_deps.clients.clone(), block_manager.subscribe(), bootstrap_shared_data.clone(), + task_manager.spawn_handle(), ); let psbt_signer = PsbtSigner::new( diff --git a/relayer/src/service_deps/full_deps.rs b/relayer/src/service_deps/full_deps.rs index 570125d9..ab34f096 100644 --- a/relayer/src/service_deps/full_deps.rs +++ b/relayer/src/service_deps/full_deps.rs @@ -3,8 +3,8 @@ use super::*; /// The relayer client dependencies. pub struct FullDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { pub bootstrap_shared_data: BootstrapSharedData, diff --git a/relayer/src/service_deps/handler_deps.rs b/relayer/src/service_deps/handler_deps.rs index bf9517d5..b59e0aa1 100644 --- a/relayer/src/service_deps/handler_deps.rs +++ b/relayer/src/service_deps/handler_deps.rs @@ -2,8 +2,8 @@ use super::*; pub struct HandlerDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// The `SocketRelayHandler`'s for each specified chain. @@ -23,9 +23,11 @@ where manager_deps: &ManagerDeps, bootstrap_shared_data: BootstrapSharedData, bfc_client: Arc>, + rollback_senders: Arc>>>, + task_manager: &TaskManager, ) -> Self { let mut handlers = (vec![], vec![]); - let ManagerDeps { clients, event_managers, .. } = manager_deps; + let ManagerDeps { bifrost_client, clients, event_managers } = manager_deps; config.relayer_config.handler_configs.iter().for_each( |handler_config| match handler_config.handler_type { @@ -34,6 +36,9 @@ where *target, event_managers.get(target).expect(INVALID_CHAIN_ID).sender.subscribe(), clients.clone(), + bifrost_client.clone(), + rollback_senders.clone(), + task_manager.spawn_handle(), Arc::new(bootstrap_shared_data.clone()), )); }), @@ -47,6 +52,7 @@ where .subscribe(), clients.clone(), Arc::new(bootstrap_shared_data.clone()), + task_manager.spawn_handle(), )); }, }, diff --git a/relayer/src/service_deps/manager_deps.rs b/relayer/src/service_deps/manager_deps.rs index 8eaca76c..6c587fc7 100644 --- a/relayer/src/service_deps/manager_deps.rs +++ b/relayer/src/service_deps/manager_deps.rs @@ -1,6 +1,4 @@ use super::*; -use br_client::eth::{events::EventManager, EthClient}; -use br_primitives::cli::Configuration; pub struct ManagerDeps where @@ -8,7 +6,7 @@ where P: Provider, T: Transport + Clone, { - /// Bifrost chain id. + /// Bifrost client pub bifrost_client: Arc>, /// The `EthClient`'s for each specified chain. pub clients: Arc>>>, diff --git a/relayer/src/service_deps/mod.rs b/relayer/src/service_deps/mod.rs index 09b23cc5..22c16678 100644 --- a/relayer/src/service_deps/mod.rs +++ b/relayer/src/service_deps/mod.rs @@ -25,6 +25,7 @@ use br_client::{ storage::{keypair::KeypairStorage, pending_outbound::PendingOutboundPool}, }, eth::{ + events::EventManager, handlers::{RoundupRelayHandler, SocketRelayHandler}, EthClient, }, @@ -32,7 +33,7 @@ use br_client::{ }; use br_periodic::{ BitcoinRollbackVerifier, HeartbeatSender, KeypairMigrator, OraclePriceFeeder, PsbtSigner, - PubKeyPreSubmitter, PubKeySubmitter, RoundupEmitter, + PubKeyPreSubmitter, PubKeySubmitter, RoundupEmitter, SocketRollbackEmitter, }; use br_primitives::{ bootstrap::BootstrapSharedData, @@ -44,10 +45,11 @@ use br_primitives::{ INVALID_PROVIDER_URL, }, }, + contracts::socket::Socket_Struct::Socket_Message, substrate::MigrationSequence, tx::XtRequestSender, }; use miniscript::bitcoin::Network; use sc_service::TaskManager; use std::{collections::BTreeMap, sync::Arc}; -use tokio::sync::RwLock; +use tokio::sync::{mpsc::UnboundedSender, RwLock}; diff --git a/relayer/src/service_deps/periodic_deps.rs b/relayer/src/service_deps/periodic_deps.rs index f8f35c23..7be6c6e7 100644 --- a/relayer/src/service_deps/periodic_deps.rs +++ b/relayer/src/service_deps/periodic_deps.rs @@ -12,6 +12,10 @@ where pub oracle_price_feeder: OraclePriceFeeder, /// The `RoundupEmitter` used for detecting and emitting new round updates. pub roundup_emitter: RoundupEmitter, + /// The `SocketRollbackEmitter`'s for each specified chain. + pub rollback_emitters: Vec>, + /// The `RollbackSender`'s for each specified chain. + pub rollback_senders: Arc>>>, /// The `KeypairMigrator` used for detecting migration sequences. pub keypair_migrator: KeypairMigrator, /// The `PubKeyPreSubmitter` used for presubmitting public keys. @@ -20,8 +24,8 @@ where impl PeriodicDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { pub fn new( @@ -31,20 +35,38 @@ where substrate_deps: &SubstrateDeps, clients: Arc>>>, bfc_client: Arc>, + task_manager: &TaskManager, ) -> Self { // initialize the heartbeat sender - let heartbeat_sender = HeartbeatSender::new(bfc_client.clone(), clients.clone()); + let heartbeat_sender = + HeartbeatSender::new(bfc_client.clone(), task_manager.spawn_handle()); // initialize the oracle price feeder - let oracle_price_feeder = OraclePriceFeeder::new(bfc_client.clone(), clients.clone()); + let oracle_price_feeder = OraclePriceFeeder::new( + bfc_client.clone(), + clients.clone(), + task_manager.spawn_handle(), + ); // initialize the roundup emitter let roundup_emitter = RoundupEmitter::new( bfc_client.clone(), - clients.clone(), Arc::new(bootstrap_shared_data.clone()), + task_manager.spawn_handle(), ); + let mut rollback_emitters = vec![]; + let mut rollback_senders = BTreeMap::new(); + clients.iter().for_each(|(chain_id, client)| { + let (rollback_emitter, rollback_sender) = SocketRollbackEmitter::new( + client.clone(), + clients.clone(), + task_manager.spawn_handle(), + ); + rollback_emitters.push(rollback_emitter); + rollback_senders.insert(*chain_id, rollback_sender); + }); + // initialize migration detector let keypair_migrator = KeypairMigrator::new( bfc_client.clone(), @@ -62,6 +84,8 @@ where heartbeat_sender, oracle_price_feeder, roundup_emitter, + rollback_emitters, + rollback_senders: Arc::new(rollback_senders), keypair_migrator, presubmitter, } From be91ccf45e33e4c725c47175da9192dd57cec7c0 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 10 Dec 2024 17:08:42 +0900 Subject: [PATCH 08/60] CCCP-295, refactor: streamline transaction management and clean up code - Removed unused traits and functions related to transaction management, enhancing code clarity. - Simplified imports by eliminating unnecessary components, focusing on essential dependencies. --- client/src/eth/traits.rs | 315 +-------------------------------------- 1 file changed, 6 insertions(+), 309 deletions(-) diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index d7d50a03..4efd58f6 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -1,10 +1,9 @@ -use std::{error::Error, sync::Arc, time::Duration}; +use std::sync::Arc; use alloy::{ - consensus::Transaction as _, - primitives::{ChainId, TxHash, B256, U256}, - providers::{ext::TxPoolApi as _, fillers::TxFiller, Provider, WalletProvider}, - rpc::types::{Log, Transaction, TransactionInput, TransactionReceipt, TransactionRequest}, + primitives::{ChainId, B256, U256}, + providers::{fillers::TxFiller, Provider, WalletProvider}, + rpc::types::{Log, TransactionInput}, signers::Signature, sol_types::SolValue, transports::Transport, @@ -12,13 +11,10 @@ use alloy::{ use br_primitives::{ bootstrap::BootstrapSharedData, contracts::socket::Socket_Struct::{Poll_Submit, Signatures, Socket_Message}, - eth::{BootstrapState, BuiltRelayTransaction, GasCoefficient}, - tx::{FlushMetadata, TxRequestMessage, TxRequestMetadata}, - utils::{recover_message, sub_display_format}, + eth::{BootstrapState, BuiltRelayTransaction}, + utils::recover_message, }; use eyre::Result; -use sc_service::SpawnTaskHandle; -use tokio::time::sleep; use super::EthClient; @@ -104,305 +100,6 @@ where } } -#[async_trait::async_trait] -/// The manager trait for Legacy and Eip1559 transactions. -pub trait TransactionManager -where - F: TxFiller + WalletProvider, - P: Provider, - T: Transport + Clone, -{ - /// Initialize transaction manager. - async fn initialize(&mut self); - - /// Get the `EthClient`. - fn get_client(&self) -> Arc>; - - /// Get the transaction spawn handle. - fn get_spawn_handle(&self) -> SpawnTaskHandle; - - /// Spawn a transaction task and try sending the transaction. - async fn spawn_send_transaction(&self, msg: TxRequestMessage); - - /// The flag whether the client has enabled txpool namespace. - fn is_txpool_enabled(&self) -> bool; - - /// Flush all transaction from mempool. - async fn flush_stuck_transaction(&self) -> Result<()> { - if self.is_txpool_enabled() && !self.get_client().metadata.is_native { - // let mempool = self.get_client().get_txpool_content().await; - let mempool = self.get_client().txpool_content().await?; - br_metrics::increase_rpc_calls(&self.get_client().get_chain_name()); - - let mut transactions = Vec::new(); - transactions.extend( - mempool.queued.get(&self.get_client().address()).cloned().unwrap_or_default(), - ); - transactions.extend( - mempool.pending.get(&self.get_client().address()).cloned().unwrap_or_default(), - ); - - for (_nonce, transaction) in transactions { - self.spawn_send_transaction(TxRequestMessage::new( - self.stuck_transaction_to_transaction_request(&transaction).await, - TxRequestMetadata::Flush(FlushMetadata::default()), - false, - false, - GasCoefficient::Low, - true, - )) - .await; - } - } - Ok(()) - } - - /// Converts stuck transaction to `TxRequest(TransactionRequest | Eip1559TransactionRequest)` - async fn stuck_transaction_to_transaction_request( - &self, - transaction: &Transaction, - ) -> TransactionRequest; -} - -#[async_trait::async_trait] -pub trait TransactionTask -where - F: TxFiller + WalletProvider, - P: Provider, - T: Transport + Clone, -{ - /// The flag whether the client has enabled txpool namespace. - fn is_txpool_enabled(&self) -> bool; - - /// Get the `EthClient`. - fn get_client(&self) -> Arc>; - - /// If first relay transaction is stuck in mempool after waiting for this amount of time(ms), - /// ignore duplicate prevent logic. (default: 12s) - fn duplicate_confirm_delay(&self) -> Duration; - - /// The flag whether debug mode is enabled. If enabled, certain errors will be logged such as - /// gas estimation failures. - fn debug_mode(&self) -> bool; - - /// Verifies whether the relayer has sufficient funds to pay for the transaction. - async fn is_sufficient_funds(&self, gas_price: U256, gas: U256) -> Result { - let fee = gas_price.saturating_mul(gas); - let client = self.get_client(); - let balance = client.get_balance(client.address()).await?; - br_metrics::increase_rpc_calls(&client.get_chain_name()); - if balance < fee { - return Ok(false); - } - Ok(true) - } - - /// Function that query mempool for check if the relay event that this relayer is about to send - /// has already been processed by another relayer. - async fn is_duplicate_relay( - &self, - tx_request: &TransactionRequest, - check_mempool: bool, - ) -> Result { - let client = self.get_client(); - - // does not check the txpool if the following condition satisfies - // 1. the txpool namespace is disabled for the client - // 2. the txpool check flag is false - // 3. the client is Bifrost (native) - if !self.is_txpool_enabled() || !check_mempool || client.metadata.is_native { - return Ok(false); - } - - let (data, to, from) = ( - tx_request.input.input().clone().unwrap_or_default(), - tx_request.to.unwrap().to().unwrap().clone(), - tx_request.from.unwrap(), - ); - - let mempool = client.txpool_content().await?; - br_metrics::increase_rpc_calls(&client.get_chain_name()); - - for (_address, tx_map) in mempool.pending.iter().chain(mempool.queued.iter()) { - for (_nonce, mempool_tx) in tx_map.iter() { - if mempool_tx.to().unwrap_or_default() == to && mempool_tx.input() == data { - // Trying gas escalating is not duplicate action - if mempool_tx.from == from { - return Ok(false); - } - - sleep(self.duplicate_confirm_delay()).await; - return if client - .get_transaction_receipt(*mempool_tx.inner.tx_hash()) - .await? - .is_some() - { - // if others relay processed - br_metrics::increase_rpc_calls(&client.get_chain_name()); - Ok(true) - } else { - // if others relay stalled in mempool - br_metrics::increase_rpc_calls(&client.get_chain_name()); - Ok(false) - }; - } - } - } - - Ok(false) - } - - /// Retry send_transaction() for failed transaction execution. - async fn retry_transaction(&self, mut msg: TxRequestMessage, escalation: bool) { - if !escalation { - msg.build_retry_event(); - sleep(Duration::from_millis(msg.retry_interval)).await; - } - self.try_send_transaction(msg).await; - } - - /// Sends the consumed transaction request to the connected chain. The transaction send will - /// be retry if the transaction fails to be mined in a block. - async fn try_send_transaction(&self, msg: TxRequestMessage); - - /// Handles the successful transaction receipt. - fn handle_success_tx_receipt( - &self, - sub_target: &str, - receipt: TransactionReceipt, - metadata: TxRequestMetadata, - ) { - let client = self.get_client(); - - let status = receipt.status(); - log::info!( - target: &client.get_chain_name(), - "-[{}] 🎁 The requested transaction has been successfully mined in block: {}, {:?}-{:?}-{:?}", - sub_display_format(sub_target), - metadata.to_string(), - receipt.block_number.unwrap(), - status, - receipt.transaction_hash - ); - if !status && self.debug_mode() { - let log_msg = format!( - "-[{}]-[{}] ⚠️ Warning! Error encountered during contract execution [execution reverted]. A prior transaction might have been already submitted: {}, {:?}-{:?}-{:?}", - sub_display_format(sub_target), - client.address(), - metadata, - receipt.block_number.unwrap(), - status, - receipt.transaction_hash - ); - log::warn!(target: &client.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &client.get_chain_name()), - sentry::Level::Warning, - ); - } - br_metrics::set_payed_fees(&client.get_chain_name(), &receipt); - } - - /// Handles the stalled transaction. - async fn handle_stalled_tx( - &self, - sub_target: &str, - msg: TxRequestMessage, - pending: TxHash, - escalation: bool, - ) { - let client = self.get_client(); - - let log_msg = format!( - "-[{}]-[{}] ♻️ The pending transaction has been stalled over 3 blocks. Try gas-escalation: {}-{}", - sub_display_format(sub_target), - client.address(), - msg.metadata, - pending - ); - log::warn!(target: &client.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &client.get_chain_name()), - sentry::Level::Warning, - ); - - self.retry_transaction(msg, escalation).await; - } - - /// Handles the failed transaction receipt generation. - async fn handle_failed_tx_receipt(&self, sub_target: &str, msg: TxRequestMessage) { - let client = self.get_client(); - - let log_msg = format!( - "-[{}]-[{}] ♻️ The requested transaction failed to generate a receipt: {}, Retries left: {:?}", - sub_display_format(sub_target), - client.address(), - msg.metadata, - msg.retries_remaining - 1 - ); - log::error!(target: &client.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &client.get_chain_name()), - sentry::Level::Error, - ); - - self.retry_transaction(msg, false).await; - } - - /// Handles the failed transaction request. - async fn handle_failed_tx_request( - &self, - sub_target: &str, - msg: TxRequestMessage, - error: &E, - ) { - let client = self.get_client(); - - let log_msg = format!( - "-[{}]-[{}] ♻️ Unknown error while requesting a transaction request: {}, Retries left: {:?}, Error: {}", - sub_display_format(sub_target), - client.address(), - msg.metadata, - msg.retries_remaining - 1, - error.to_string(), - ); - log::error!(target: &client.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &client.get_chain_name()), - sentry::Level::Error, - ); - - self.retry_transaction(msg, false).await; - } - - /// Handles the failed gas estimation. - async fn handle_failed_gas_estimation( - &self, - sub_target: &str, - msg: TxRequestMessage, - error: &E, - ) { - let client = self.get_client(); - - if self.debug_mode() { - let log_msg = format!( - "-[{}]-[{}] ⚠️ Warning! Error encountered during gas estimation: {}, Retries left: {:?}, Error: {}", - sub_display_format(sub_target), - client.address(), - msg.metadata, - msg.retries_remaining - 1, - error.to_string() - ); - log::warn!(target: &client.get_chain_name(), "{log_msg}"); - sentry::capture_message( - &format!("[{}]{log_msg}", &client.get_chain_name()), - sentry::Level::Warning, - ); - } - self.retry_transaction(msg, false).await; - } -} - #[async_trait::async_trait] pub trait BootstrapHandler { /// Fetch the shared bootstrap data. From ff437e9285c2bc577b93d473fca79e3b54a5a857 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 10 Dec 2024 17:40:22 +0900 Subject: [PATCH 09/60] CCCP-295, refactor: restart tasks separately on error (WIP) --- client/src/btc/block.rs | 2 +- client/src/btc/handlers/inbound.rs | 2 +- client/src/btc/handlers/outbound.rs | 2 +- periodic/src/bitcoin_rollback_verifier.rs | 2 +- periodic/src/heartbeat_sender.rs | 2 +- periodic/src/psbt_signer.rs | 2 +- periodic/src/pub_key_presubmitter.rs | 2 +- periodic/src/pub_key_submitter.rs | 2 +- periodic/src/roundup_emitter.rs | 2 +- relayer/Cargo.toml | 1 + relayer/src/service.rs | 187 ++++++++++++++++++---- 11 files changed, 164 insertions(+), 42 deletions(-) diff --git a/client/src/btc/block.rs b/client/src/btc/block.rs index b40ef332..0be04c74 100644 --- a/client/src/btc/block.rs +++ b/client/src/btc/block.rs @@ -99,7 +99,7 @@ where /// The Bitcoin client. btc_client: BtcClient, /// The Bifrost client. - bfc_client: Arc>, + pub bfc_client: Arc>, /// The event message sender. sender: Sender, /// The configured minimum block confirmations required to process a block. diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index 2f2bf881..cc8966d2 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -43,7 +43,7 @@ where T: Transport + Clone, { /// `EthClient` for interact with Bifrost network. - bfc_client: Arc>, + pub bfc_client: Arc>, /// The receiver that consumes new events from the block channel. event_receiver: Receiver, /// Event type which this handler should handle. diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index 422be996..9dd61392 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -44,7 +44,7 @@ where P: Provider, T: Transport + Clone, { - bfc_client: Arc>, + pub bfc_client: Arc>, event_receiver: Receiver, target_event: EventType, /// The bootstrap shared data. diff --git a/periodic/src/bitcoin_rollback_verifier.rs b/periodic/src/bitcoin_rollback_verifier.rs index 83dff149..b0017949 100644 --- a/periodic/src/bitcoin_rollback_verifier.rs +++ b/periodic/src/bitcoin_rollback_verifier.rs @@ -92,7 +92,7 @@ where /// The Bitcoin client. btc_client: BtcClient, /// The Bifrost client. - bfc_client: Arc>, + pub bfc_client: Arc>, /// The unsigned transaction message sender. xt_request_sender: Arc, /// The periodic schedule. diff --git a/periodic/src/heartbeat_sender.rs b/periodic/src/heartbeat_sender.rs index cd0834e0..328c917a 100644 --- a/periodic/src/heartbeat_sender.rs +++ b/periodic/src/heartbeat_sender.rs @@ -27,7 +27,7 @@ where /// The time schedule that represents when to check heartbeat pulsed. schedule: Schedule, /// The `EthClient` to interact with the bifrost network. - client: Arc>, + pub client: Arc>, /// The handle to spawn tasks. handle: SpawnTaskHandle, } diff --git a/periodic/src/psbt_signer.rs b/periodic/src/psbt_signer.rs index ccff7ca2..871e99b0 100644 --- a/periodic/src/psbt_signer.rs +++ b/periodic/src/psbt_signer.rs @@ -33,7 +33,7 @@ where T: Transport + Clone, { /// The Bifrost client. - client: Arc>, + pub client: Arc>, /// The unsigned transaction message sender. xt_request_sender: Arc, /// The public and private keypair local storage. diff --git a/periodic/src/pub_key_presubmitter.rs b/periodic/src/pub_key_presubmitter.rs index 85f295c4..a2673f93 100644 --- a/periodic/src/pub_key_presubmitter.rs +++ b/periodic/src/pub_key_presubmitter.rs @@ -36,7 +36,7 @@ where P: Provider, T: Transport + Clone, { - bfc_client: Arc>, + pub bfc_client: Arc>, /// The Bifrost client. sub_client: Option>, /// The unsigned transaction message sender. diff --git a/periodic/src/pub_key_submitter.rs b/periodic/src/pub_key_submitter.rs index 9af50cee..6ce4ab3c 100644 --- a/periodic/src/pub_key_submitter.rs +++ b/periodic/src/pub_key_submitter.rs @@ -39,7 +39,7 @@ where T: Transport + Clone, { /// The Bifrost client. - client: Arc>, + pub client: Arc>, /// The unsigned transaction message sender. xt_request_sender: Arc, /// The public and private keypair local storage. diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index 714dfe95..6a613a18 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -41,7 +41,7 @@ where /// Current round number current_round: U256, /// The ethereum client for the Bifrost network. - client: Arc>, + pub client: Arc>, /// The time schedule that represents when to check round info. schedule: Schedule, /// The bootstrap shared data. diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index 15b8984a..2f4e0e91 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -27,6 +27,7 @@ miniscript = { workspace = true } bitcoincore-rpc = { workspace = true } subxt = { workspace = true } eyre = { workspace = true } +sentry = { workspace = true } # Bifrost Relayer br-cli = { path = "../client/cli", default-features = false } diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 8f62739b..28d18f60 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -233,8 +233,10 @@ where "migration-detector", Some("migration-detector"), async move { - keypair_migrator.run().await; - () + let reason = keypair_migrator.run().await; + let log_msg = format!("migration detector stopped: {:?}", reason); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); }, ); @@ -243,8 +245,16 @@ where "pub-key-presubmitter", Some("pub-key-presubmitter"), async move { - presubmitter.run().await; - () + loop { + let report = presubmitter.run().await; + let log_msg = format!( + "public key presubmitter({}) stopped: {:?}\nRestarting in 12 seconds...", + presubmitter.bfc_client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); @@ -259,8 +269,19 @@ where task_manager .spawn_essential_handle() .spawn("heartbeat", Some("heartbeat"), async move { - heartbeat_sender.run().await; - () + loop { + let report = heartbeat_sender.run().await; + let log_msg = format!( + "heartbeat sender({}:{}) stopped: {:?}\nRestarting in 12 seconds...", + heartbeat_sender.client.get_chain_name(), + heartbeat_sender.client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + + tokio::time::sleep(Duration::from_secs(12)).await; + } }); // spawn oracle price feeder @@ -271,8 +292,19 @@ where ), Some("oracle"), async move { - oracle_price_feeder.run().await; - () + loop { + let report = oracle_price_feeder.run().await; + let log_msg = format!( + "oracle price feeder({}:{}) stopped: {:?}\nRestarting in 12 seconds...", + oracle_price_feeder.client.get_chain_name(), + oracle_price_feeder.client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + + tokio::time::sleep(Duration::from_secs(12)).await; + } }, ); @@ -285,8 +317,19 @@ where ), Some("rollback"), async move { - emitter.run().await; - () + loop { + let report = emitter.run().await; + let log_msg = format!( + "rollback emitter({}:{}) stopped: {:?}\nRestarting in 12 seconds...", + emitter.client.get_chain_name(), + emitter.client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + + tokio::time::sleep(Duration::from_secs(12)).await; + } }, ) }); @@ -314,9 +357,16 @@ where } drop(guard); - handler.run().await; - - () + loop { + let report = handler.run().await; + let log_msg = format!( + "socket relay handler({}) stopped: {:?}\nRestarting immediately...", + handler.client.get_chain_name(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); }); @@ -330,8 +380,16 @@ where ), Some("handlers"), async move { - handler.run().await; - () + loop { + let report = handler.run().await; + let log_msg = format!( + "roundup relay handler({}) stopped: {:?}\nRestarting immediately...", + handler.client.get_chain_name(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); }); @@ -341,8 +399,16 @@ where "roundup-emitter", Some("roundup-emitter"), async move { - roundup_emitter.run().await; - () + loop { + let report = roundup_emitter.run().await; + let log_msg = format!( + "roundup emitter({}) stopped: {:?}\nRestarting immediately...", + roundup_emitter.client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); @@ -354,9 +420,17 @@ where ), Some("event-managers"), async move { - event_manager.wait_provider_sync().await; - event_manager.run().await; - () + let _ = event_manager.wait_provider_sync().await; + loop { + let report = event_manager.run().await; + let log_msg = format!( + "event manager({}) stopped: {:?}\nRestarting immediately...", + event_manager.client.get_chain_name(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ) }); @@ -366,40 +440,80 @@ where "bitcoin-inbound-handler", Some("handlers"), async move { - inbound.run().await; - () + loop { + let report = inbound.run().await; + let log_msg = format!( + "bitcoin inbound handler({}) stopped: {:?}\nRestarting immediately...", + inbound.bfc_client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-outbound-handler", Some("handlers"), async move { - outbound.run().await; - () + loop { + let report = outbound.run().await; + let log_msg = format!( + "bitcoin outbound handler({}) stopped: {:?}\nRestarting immediately...", + outbound.bfc_client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-psbt-signer", Some("handlers"), async move { - psbt_signer.run().await; - () + loop { + let report = psbt_signer.run().await; + let log_msg = format!( + "bitcoin psbt signer({}) stopped: {:?}\nRestarting immediately...", + psbt_signer.client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-public-key-submitter", Some("pub-key-submitter"), async move { - pub_key_submitter.run().await; - () + loop { + let report = pub_key_submitter.run().await; + let log_msg = format!( + "bitcoin public key submitter({}) stopped: {:?}\nRestarting immediately...", + pub_key_submitter.client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); task_manager.spawn_essential_handle().spawn( "bitcoin-rollback-verifier", Some("rollback-verifier"), async move { - rollback_verifier.run().await; - () + loop { + let report = rollback_verifier.run().await; + let log_msg = format!( + "bitcoin rollback verifier({}) stopped: {:?}\nRestarting immediately...", + rollback_verifier.bfc_client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); task_manager.spawn_essential_handle().spawn( @@ -417,9 +531,16 @@ where } drop(guard); - block_manager.run().await; - - () + loop { + let report = block_manager.run().await; + let log_msg = format!( + "bitcoin block manager({}) stopped: {:?}\nRestarting immediately...", + block_manager.bfc_client.address(), + report + ); + log::error!("{log_msg}"); + sentry::capture_message(&log_msg, sentry::Level::Error); + } }, ); From 45f0ce2baa59fcec108515668eefc97ead07b929 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 10 Dec 2024 18:49:13 +0900 Subject: [PATCH 10/60] CCCP-295, feat: move retry transport layer & add `br_metrics::increase_rpc_calls` --- client/src/eth/mod.rs | 179 +++++++++++++++++++++++++++++++++++++++++ primitives/src/eth.rs | 173 --------------------------------------- relayer/src/service.rs | 13 +-- 3 files changed, 186 insertions(+), 179 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 77a6a084..bd4110b9 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -235,3 +235,182 @@ pub fn send_transaction( } }); } + +pub mod retry { + use alloy::{ + rpc::json_rpc::{RequestPacket, ResponsePacket}, + transports::{ + layers::RetryPolicy as RetryPolicyT, RpcError, TransportError, TransportErrorKind, + TransportFut, + }, + }; + use std::{ + sync::{ + atomic::{AtomicU32, Ordering}, + Arc, + }, + task::{Context, Poll}, + time::Duration, + }; + use tokio::time::sleep; + use tower::{Layer, Service}; + + /// A Transport Layer that is responsible for retrying requests based on the + /// error type. See [`TransportError`]. + #[derive(Debug, Clone)] + pub struct RetryBackoffLayer { + /// The maximum number of retries for errors + max_retries: u32, + /// The initial backoff in milliseconds + initial_backoff: u64, + /// Chain name + chain_name: String, + } + + impl RetryBackoffLayer { + /// Creates a new retry layer with the given parameters. + pub const fn new(max_retries: u32, initial_backoff: u64, chain_name: String) -> Self { + Self { max_retries, initial_backoff, chain_name } + } + } + + /// [RetryPolicy] implements [RetryPolicyT] to determine whether to retry depending on the + /// err. + #[derive(Debug, Copy, Clone, Default)] + #[non_exhaustive] + pub struct RetryPolicy; + + impl RetryPolicyT for RetryPolicy { + fn should_retry(&self, _error: &TransportError) -> bool { + // TODO: Filter out errors that are not retryable. now we retry all errors. + true + } + + /// Provides a backoff hint if the error response contains it + fn backoff_hint(&self, error: &TransportError) -> Option { + if let RpcError::ErrorResp(resp) = error { + let data = resp.try_data_as::(); + if let Some(Ok(data)) = data { + // if daily rate limit exceeded, infura returns the requested backoff in the error + // response + let backoff_seconds = &data["rate"]["backoff_seconds"]; + // infura rate limit error + if let Some(seconds) = backoff_seconds.as_u64() { + return Some(std::time::Duration::from_secs(seconds)); + } + if let Some(seconds) = backoff_seconds.as_f64() { + return Some(std::time::Duration::from_secs(seconds as u64 + 1)); + } + } + } + + None + } + } + + impl Layer for RetryBackoffLayer { + type Service = RetryBackoffService; + + fn layer(&self, inner: S) -> Self::Service { + RetryBackoffService { + inner, + policy: RetryPolicy, + max_retries: self.max_retries, + initial_backoff: self.initial_backoff, + requests_enqueued: Arc::new(AtomicU32::new(0)), + chain_name: self.chain_name.clone(), + } + } + } + + /// A Tower Service used by the [RetryBackoffLayer] that is responsible for retrying all requests on error. + /// See [TransportError] and [RetryPolicy]. + #[derive(Debug, Clone)] + pub struct RetryBackoffService { + /// The inner service + inner: S, + /// The retry policy + policy: RetryPolicy, + /// The maximum number of retries for errors + max_retries: u32, + /// The initial backoff in milliseconds + initial_backoff: u64, + /// The number of requests currently enqueued + requests_enqueued: Arc, + /// Chain name + chain_name: String, + } + + impl RetryBackoffService { + const fn initial_backoff(&self) -> Duration { + Duration::from_millis(self.initial_backoff) + } + } + + impl Service for RetryBackoffService + where + S: Service + + Send + + 'static + + Clone, + S::Future: Send + 'static, + { + type Response = ResponsePacket; + type Error = TransportError; + type Future = TransportFut<'static>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, request: RequestPacket) -> Self::Future { + let inner = self.inner.clone(); + let this = self.clone(); + let mut inner = std::mem::replace(&mut self.inner, inner); + Box::pin(async move { + let _ = this.requests_enqueued.fetch_add(1, Ordering::SeqCst) as u64; + let mut retry_count: u32 = 0; + loop { + let err; + let res = inner.call(request.clone()).await; + br_metrics::increase_rpc_calls(&this.chain_name); + + match res { + Ok(res) => { + if let Some(e) = res.as_error() { + err = TransportError::ErrorResp(e.clone()) + } else { + this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); + return Ok(res); + } + }, + Err(e) => err = e, + } + + let should_retry = this.policy.should_retry(&err); + if should_retry { + retry_count += 1; + if retry_count > this.max_retries { + return Err(TransportErrorKind::custom_str(&format!( + "Max retries exceeded {}", + err + ))); + } + + let _ = this.requests_enqueued.load(Ordering::SeqCst) as u64; + + // try to extract the requested backoff from the error or compute the next + // backoff based on retry count + let backoff_hint = this.policy.backoff_hint(&err); + let next_backoff = backoff_hint.unwrap_or_else(|| this.initial_backoff()); + + sleep(next_backoff).await; + } else { + this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); + return Err(err); + } + } + }) + } + } +} diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index 45456b53..d5ba2393 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -399,176 +399,3 @@ impl BuiltRelayTransaction { Self { tx_request, is_external } } } - -pub mod retry { - use alloy::{ - rpc::json_rpc::{RequestPacket, ResponsePacket}, - transports::{ - layers::RetryPolicy as RetryPolicyT, RpcError, TransportError, TransportErrorKind, - TransportFut, - }, - }; - use std::{ - sync::{ - atomic::{AtomicU32, Ordering}, - Arc, - }, - task::{Context, Poll}, - time::Duration, - }; - use tokio::time::sleep; - use tower::{Layer, Service}; - - /// A Transport Layer that is responsible for retrying requests based on the - /// error type. See [`TransportError`]. - #[derive(Debug, Clone)] - pub struct RetryBackoffLayer { - /// The maximum number of retries for errors - max_retries: u32, - /// The initial backoff in milliseconds - initial_backoff: u64, - } - - impl RetryBackoffLayer { - /// Creates a new retry layer with the given parameters. - pub const fn new(max_retries: u32, initial_backoff: u64) -> Self { - Self { max_retries, initial_backoff } - } - } - - /// [RetryPolicy] implements [RetryPolicyT] to determine whether to retry depending on the - /// err. - #[derive(Debug, Copy, Clone, Default)] - #[non_exhaustive] - pub struct RetryPolicy; - - impl RetryPolicyT for RetryPolicy { - fn should_retry(&self, _error: &TransportError) -> bool { - // TODO: Filter out errors that are not retryable. now we retry all errors. - true - } - - /// Provides a backoff hint if the error response contains it - fn backoff_hint(&self, error: &TransportError) -> Option { - if let RpcError::ErrorResp(resp) = error { - let data = resp.try_data_as::(); - if let Some(Ok(data)) = data { - // if daily rate limit exceeded, infura returns the requested backoff in the error - // response - let backoff_seconds = &data["rate"]["backoff_seconds"]; - // infura rate limit error - if let Some(seconds) = backoff_seconds.as_u64() { - return Some(std::time::Duration::from_secs(seconds)); - } - if let Some(seconds) = backoff_seconds.as_f64() { - return Some(std::time::Duration::from_secs(seconds as u64 + 1)); - } - } - } - - None - } - } - - impl Layer for RetryBackoffLayer { - type Service = RetryBackoffService; - - fn layer(&self, inner: S) -> Self::Service { - RetryBackoffService { - inner, - policy: RetryPolicy, - max_retries: self.max_retries, - initial_backoff: self.initial_backoff, - requests_enqueued: Arc::new(AtomicU32::new(0)), - } - } - } - - /// A Tower Service used by the [RetryBackoffLayer] that is responsible for retrying all requests on error. - /// See [TransportError] and [RetryPolicy]. - #[derive(Debug, Clone)] - pub struct RetryBackoffService { - /// The inner service - inner: S, - /// The retry policy - policy: RetryPolicy, - /// The maximum number of retries for errors - max_retries: u32, - /// The initial backoff in milliseconds - initial_backoff: u64, - /// The number of requests currently enqueued - requests_enqueued: Arc, - } - - impl RetryBackoffService { - const fn initial_backoff(&self) -> Duration { - Duration::from_millis(self.initial_backoff) - } - } - - impl Service for RetryBackoffService - where - S: Service - + Send - + 'static - + Clone, - S::Future: Send + 'static, - { - type Response = ResponsePacket; - type Error = TransportError; - type Future = TransportFut<'static>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, request: RequestPacket) -> Self::Future { - let inner = self.inner.clone(); - let this = self.clone(); - let mut inner = std::mem::replace(&mut self.inner, inner); - Box::pin(async move { - let _ = this.requests_enqueued.fetch_add(1, Ordering::SeqCst) as u64; - let mut retry_count: u32 = 0; - loop { - let err; - let res = inner.call(request.clone()).await; - - match res { - Ok(res) => { - if let Some(e) = res.as_error() { - err = TransportError::ErrorResp(e.clone()) - } else { - this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); - return Ok(res); - } - }, - Err(e) => err = e, - } - - let should_retry = this.policy.should_retry(&err); - if should_retry { - retry_count += 1; - if retry_count > this.max_retries { - return Err(TransportErrorKind::custom_str(&format!( - "Max retries exceeded {}", - err - ))); - } - - let _ = this.requests_enqueued.load(Ordering::SeqCst) as u64; - - // try to extract the requested backoff from the error or compute the next - // backoff based on retry count - let backoff_hint = this.policy.backoff_hint(&err); - let next_backoff = backoff_hint.unwrap_or_else(|| this.initial_backoff()); - - sleep(next_backoff).await; - } else { - this.requests_enqueued.fetch_sub(1, Ordering::SeqCst); - return Err(err); - } - } - }) - } - } -} diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 28d18f60..91a9c757 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -24,7 +24,7 @@ use br_client::{ handlers::Handler as _, storage::{keypair::KeypairStorage, pending_outbound::PendingOutboundPool}, }, - eth::{traits::Handler as _, EthClient}, + eth::{retry::RetryBackoffLayer, traits::Handler as _, EthClient}, }; use br_periodic::traits::PeriodicWorker; use br_primitives::{ @@ -35,10 +35,7 @@ use br_primitives::{ errors::{INVALID_BITCOIN_NETWORK, INVALID_PRIVATE_KEY, INVALID_PROVIDER_URL}, tx::DEFAULT_CALL_RETRIES, }, - eth::{ - retry::RetryBackoffLayer, AggregatorContracts, BootstrapState, ProtocolContracts, - ProviderMetadata, - }, + eth::{AggregatorContracts, BootstrapState, ProtocolContracts, ProviderMetadata}, substrate::MigrationSequence, utils::sub_display_format, }; @@ -71,7 +68,11 @@ pub fn relay(config: Configuration) -> Result { let wallet = EthereumWallet::from(signer.clone()); let client = RpcClient::builder() - .layer(RetryBackoffLayer::new(DEFAULT_CALL_RETRIES, evm_provider.call_interval)) + .layer(RetryBackoffLayer::new( + DEFAULT_CALL_RETRIES, + evm_provider.call_interval, + evm_provider.name.clone(), + )) .http(url.clone()) .with_poll_interval(Duration::from_millis(evm_provider.call_interval)); let provider = Arc::new( From 4be988107884c94c25ce7ea495ed23f32fb7333b Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Wed, 11 Dec 2024 15:36:02 +0900 Subject: [PATCH 11/60] CCCP-295, feature: implement txpool flushing mechanism - Added a new method `flush_stalled_transactions` to handle stalled transactions in the transaction pool. - Enhanced transaction management by adjusting gas prices for legacy and EIP-1559 transactions based on current market conditions. - Updated the `send_transaction` function to call `flush_stalled_transactions` when encountering a "nonce too low" error, improving error handling and transaction reliability. --- client/src/eth/mod.rs | 97 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index bd4110b9..3b1166d9 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -10,12 +10,14 @@ use br_primitives::{ }; use alloy::{ - network::Ethereum, + consensus::Transaction, + network::{Ethereum, TransactionResponse as _}, primitives::{ utils::{format_units, parse_ether, Unit}, Address, ChainId, }, providers::{ + ext::TxPoolApi as _, fillers::{FillProvider, TxFiller}, PendingTransactionBuilder, Provider, RootProvider, SendableTx, WalletProvider, }, @@ -26,7 +28,8 @@ use alloy::{ use eyre::{eyre, Result}; use k256::ecdsa::SigningKey; use sc_service::SpawnTaskHandle; -use std::{sync::Arc, time::Duration}; +use std::{collections::VecDeque, sync::Arc, time::Duration}; +use tokio::sync::Mutex; use url::Url; pub mod events; @@ -50,6 +53,8 @@ where pub protocol_contracts: ProtocolContracts, /// The aggregator contracts. pub aggregator_contracts: AggregatorContracts, + /// send_transaction not allowed while flushing. + pub martial_law: Arc>, } impl EthClient @@ -66,7 +71,14 @@ where protocol_contracts: ProtocolContracts, aggregator_contracts: AggregatorContracts, ) -> Self { - Self { inner, signer, metadata, protocol_contracts, aggregator_contracts } + Self { + inner, + signer, + metadata, + protocol_contracts, + aggregator_contracts, + martial_law: Arc::new(Mutex::new(())), + } } /// Verifies whether the configured chain id and the provider's chain id match. @@ -164,6 +176,72 @@ where let relayer_manager = self.protocol_contracts.relayer_manager.as_ref().unwrap(); Ok(relayer_manager.is_selected_relayer(self.address(), false).call().await?._0) } + + /// Flush stalled transactions from the txpool. + pub async fn flush_stalled_transactions(&self) -> Result<()> { + let _lock = self.martial_law.lock().await; + + // if the chain is native or txpool is not enabled, do nothing + if self.metadata.is_native || self.txpool_status().await.is_err() { + return Ok(()); + } + + // possibility of txpool being flushed automatically. wait for 2 blocks. + tokio::time::sleep(Duration::from_millis(self.metadata.call_interval * 2)).await; + + let pending = self.txpool_content().await?.remove_from(&self.address()).pending; + let mut transactions = pending.into_iter().map(|(_, tx)| tx).collect::>(); + transactions.make_contiguous().sort_by(|a, b| a.nonce().cmp(&b.nonce())); + + while let Some(tx) = transactions.pop_front() { + if self.get_transaction_receipt(tx.tx_hash()).await.unwrap().is_some() { + continue; + } + + let mut tx_request = tx.clone().into_request(); + + // RBF + if tx.is_legacy_gas() { + let new_gas_price = ((tx_request.gas_price.unwrap() as f64) * 1.1).ceil() as u128; + let current_gas_price = self.get_gas_price().await.unwrap(); + + if new_gas_price >= current_gas_price { + tx_request.gas_price = Some(new_gas_price); + } else { + tx_request.gas_price = Some(current_gas_price); + } + } else { + let current_gas_price = self.estimate_eip1559_fees(None).await.unwrap(); + + let new_max_fee_per_gas = + (tx_request.max_fee_per_gas.unwrap() as f64 * 1.1).ceil() as u128; + let new_max_priority_fee_per_gas = + (tx_request.max_priority_fee_per_gas.unwrap() as f64 * 1.1).ceil() as u128; + + if new_max_fee_per_gas >= current_gas_price.max_fee_per_gas { + tx_request.max_fee_per_gas = Some(new_max_fee_per_gas); + } else { + tx_request.max_fee_per_gas = Some(current_gas_price.max_fee_per_gas); + } + if new_max_priority_fee_per_gas >= current_gas_price.max_priority_fee_per_gas { + tx_request.max_priority_fee_per_gas = Some(new_max_priority_fee_per_gas); + } else { + tx_request.max_priority_fee_per_gas = + Some(current_gas_price.max_priority_fee_per_gas); + } + } + + let pending = self + .send_transaction(tx_request) + .await? + .with_timeout(Some(Duration::from_millis(self.metadata.call_interval))); + if pending.watch().await.is_err() { + transactions.push_front(tx); + } + } + + Ok(()) + } } #[async_trait::async_trait] @@ -196,7 +274,11 @@ pub fn send_transaction( P: Provider + 'static, T: Transport + Clone, { - handle.spawn("send_transaction", None, async move { + let this_handle = handle.clone(); + this_handle.spawn("send_transaction", None, async move { + let lock = client.martial_law.lock().await; + drop(lock); + if client.metadata.is_native { // gas price is fixed to 1000 Gwei on bifrost network request.max_fee_per_gas = Some(0); @@ -211,7 +293,7 @@ pub fn send_transaction( } } - match client.send_transaction(request).await { + match client.send_transaction(request.clone()).await { Ok(pending) => { log::info!( target: &requester, @@ -231,6 +313,11 @@ pub fn send_transaction( ); log::error!(target: &requester, "{msg}"); sentry::capture_message(&msg, sentry::Level::Error); + + if err.to_string().to_lowercase().contains("nonce too low") { + client.flush_stalled_transactions().await.unwrap(); + send_transaction(client, request, requester, metadata, handle); + } }, } }); From f7ee4fc1079880a17658c1132fe6fb01235e7d88 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Wed, 11 Dec 2024 16:06:26 +0900 Subject: [PATCH 12/60] CCCP-295, chore: remove unnecessary locking --- client/src/eth/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 3b1166d9..2cb6b378 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -53,7 +53,7 @@ where pub protocol_contracts: ProtocolContracts, /// The aggregator contracts. pub aggregator_contracts: AggregatorContracts, - /// send_transaction not allowed while flushing. + /// flushing not allowed to work concurrently. pub martial_law: Arc>, } @@ -276,9 +276,6 @@ pub fn send_transaction( { let this_handle = handle.clone(); this_handle.spawn("send_transaction", None, async move { - let lock = client.martial_law.lock().await; - drop(lock); - if client.metadata.is_native { // gas price is fixed to 1000 Gwei on bifrost network request.max_fee_per_gas = Some(0); From 5225d8a290c6e8191f1c74f27eca9b468ba99bb3 Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Thu, 5 Dec 2024 11:54:47 +0900 Subject: [PATCH 13/60] CCCP-476, feat: support core --- configs/config.mainnet.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/configs/config.mainnet.yaml b/configs/config.mainnet.yaml index 4dd1c626..827e5b56 100644 --- a/configs/config.mainnet.yaml +++ b/configs/config.mainnet.yaml @@ -79,10 +79,20 @@ evm_providers: eip1559: true socket_address: "0xac1552e30857A814a225BAa81145bcB071B46DDd" authority_address: "0xA069a57426Cd4c53925c1847Bec01aAB832A5118" + - name: "core" + id: 1116 + provider: "" + call_interval: 3000 + block_confirmations: 3 + is_relay_target: false + eip1559: false + min_gas_price: 30000000000 + socket_address: "0x4C7a44F3FB37A53F33D3fe3cCdE97A444F105239" + authority_address: "0xA069a57426Cd4c53925c1847Bec01aAB832A5118" handler_configs: - handler_type: Socket - watch_list: [3068, 1, 56, 137, 8453, 42161] + watch_list: [3068, 1, 56, 137, 8453, 42161, 1116] - handler_type: Roundup watch_list: [3068] From 774aaabaaff2ceb9dc150050aa17487e6295c859 Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Wed, 11 Dec 2024 18:26:59 +0900 Subject: [PATCH 14/60] CCCP-295, deps: update polkadot-sdk --- Cargo.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 24952b09..8845fb16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,19 +75,19 @@ tokio-stream = "0.1.14" subxt = "0.37.0" # Substrate Clients -sc-cli = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-v1.12.0" } -sc-utils = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-v1.12.0" } -sc-service = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-v1.12.0" } -sc-sysinfo = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-v1.12.0" } -sc-keystore = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-v1.12.0" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", default-features = false, git = "https://github.com/bifrost-platform/polkadot-sdk", branch = "bifrost-polkadot-v1.12.0" } +sc-cli = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-stable2407" } +sc-utils = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-stable2407" } +sc-service = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-stable2407" } +sc-sysinfo = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-stable2407" } +sc-keystore = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-stable2407" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", default-features = false, git = "https://github.com/bifrost-platform/polkadot-sdk", branch = "bifrost-polkadot-stable2407" } # Substrate Primitives -sp-core = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-v1.12.0" } -sp-application-crypto = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-v1.12.0" } +sp-core = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-stable2407" } +sp-application-crypto = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-stable2407" } # Substrate Builds -substrate-build-script-utils = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-v1.12.0" } +substrate-build-script-utils = { git = "https://github.com/bifrost-platform/polkadot-sdk", default-features = false, branch = "bifrost-polkadot-stable2407" } [profile.production] inherits = "release" From 63df5237cd358c2e1eb6dc1c9a3b300abcbec0ef Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Wed, 11 Dec 2024 19:27:30 +0900 Subject: [PATCH 15/60] CCCP-295, chore: update alloy to 0.8 --- Cargo.toml | 2 +- .../src/eth/handlers/roundup_relay_handler.rs | 4 ++-- primitives/src/contracts/socket.rs | 21 +++++++++---------- primitives/src/utils.rs | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8845fb16..75914cc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ serde = "1.0.188" serde_json = "1.0.107" hex = "0.4.3" -alloy = { version = "0.6", features = [ +alloy = { version = "0.8", features = [ "full", "reqwest-rustls-tls", "provider-txpool-api", diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index add3fad6..dcafdd13 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -3,7 +3,7 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration}; use alloy::{ dyn_abi::DynSolValue, network::Ethereum, - primitives::{Address, ChainId, Signature, B256, U256}, + primitives::{Address, ChainId, PrimitiveSignature, B256, U256}, providers::{ fillers::{FillProvider, TxFiller}, Provider, WalletProvider, @@ -221,7 +221,7 @@ where .await? ._0; - let mut signature_vec = Vec::::from(signatures); + let mut signature_vec = Vec::::from(signatures); signature_vec .sort_by_key(|k| recover_message(*k, &self.encode_relayer_array(round, new_relayers))); diff --git a/primitives/src/contracts/socket.rs b/primitives/src/contracts/socket.rs index 47fe876f..c125c10a 100644 --- a/primitives/src/contracts/socket.rs +++ b/primitives/src/contracts/socket.rs @@ -1,6 +1,5 @@ use alloy::{ - primitives::{b256, Bytes, Parity, B256}, - signers::Signature, + primitives::{b256, Bytes, PrimitiveSignature, B256}, sol, }; use std::collections::BTreeMap; @@ -33,18 +32,18 @@ sol!( "../abi/abi.socket.merged.json" ); -impl From for Signatures { - fn from(signature: Signature) -> Self { +impl From for Signatures { + fn from(signature: PrimitiveSignature) -> Self { let r = signature.r().into(); let s = signature.s().into(); - let v = signature.v().to_u64() as u8; + let v = signature.v() as u8 + 27; Signatures { r: vec![r], s: vec![s], v: Bytes::from([v]) } } } -impl From> for Signatures { - fn from(signatures: Vec) -> Self { +impl From> for Signatures { + fn from(signatures: Vec) -> Self { let mut r = Vec::with_capacity(signatures.len()); let mut s = Vec::with_capacity(signatures.len()); let mut v = Vec::with_capacity(signatures.len()); @@ -52,21 +51,21 @@ impl From> for Signatures { for sig in signatures.iter() { r.push(sig.r().into()); s.push(sig.s().into()); - v.push(sig.v().to_u64() as u8); + v.push(sig.v() as u8 + 27); } Signatures { r, s, v: Bytes::from(v) } } } -impl From for Vec { +impl From for Vec { fn from(signatures: Signatures) -> Self { let mut res = Vec::with_capacity(signatures.r.len()); for idx in 0..signatures.r.len() { let r = signatures.r[idx].into(); let s = signatures.s[idx].into(); - let v = Parity::try_from(signatures.v[idx] as u64).unwrap(); - res.push(Signature::from_rs_and_parity(r, s, v).unwrap()); + let v = (signatures.v[idx] - 27) != 0; + res.push(PrimitiveSignature::new(r, s, v)); } res diff --git a/primitives/src/utils.rs b/primitives/src/utils.rs index a5e7d4c8..2ca55ea6 100644 --- a/primitives/src/utils.rs +++ b/primitives/src/utils.rs @@ -1,4 +1,4 @@ -use alloy::primitives::{keccak256, Address, Signature as EthersSignature, B256}; +use alloy::primitives::{keccak256, Address, PrimitiveSignature as EthersSignature, B256}; use k256::{ecdsa::VerifyingKey, elliptic_curve::sec1::ToEncodedPoint}; use rand::Rng as _; use sha3::{Digest, Keccak256}; @@ -28,7 +28,7 @@ pub fn hash_bytes(bytes: &Vec) -> B256 { pub fn recover_message(sig: EthersSignature, msg: &[u8]) -> Address { let r = sig.r().to_be_bytes::<32>(); let s = sig.s().to_be_bytes::<32>(); - let v = sig.v().recid(); + let v = sig.recid(); let rs = k256::ecdsa::Signature::from_slice([r, s].concat().as_slice()).unwrap(); let verify_key = From 419599ccb5936d25a7109050f412b489f37b1552 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 12 Dec 2024 12:03:23 +0900 Subject: [PATCH 16/60] CCCP-295, refactor: simplify signature recovery in `recover_message` function --- primitives/src/utils.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/primitives/src/utils.rs b/primitives/src/utils.rs index 2ca55ea6..39a300ee 100644 --- a/primitives/src/utils.rs +++ b/primitives/src/utils.rs @@ -26,10 +26,8 @@ pub fn hash_bytes(bytes: &Vec) -> B256 { /// Recovers the address from the given signature and message. pub fn recover_message(sig: EthersSignature, msg: &[u8]) -> Address { - let r = sig.r().to_be_bytes::<32>(); - let s = sig.s().to_be_bytes::<32>(); let v = sig.recid(); - let rs = k256::ecdsa::Signature::from_slice([r, s].concat().as_slice()).unwrap(); + let rs = sig.to_k256().unwrap(); let verify_key = VerifyingKey::recover_from_digest(Keccak256::new_with_prefix(msg), &rs, v).unwrap(); From d09577de439d11c3fb4293e4bb6e4409a770c99e Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 12 Dec 2024 14:09:31 +0900 Subject: [PATCH 17/60] CCCP-295, refactor: migrate to AnyNetwork for improved flexibility in network handling - Updated various components to use `AnyNetwork` instead of specific network types, enhancing the adaptability of the codebase. - Adjusted type constraints in multiple structs and traits to accommodate the new network abstraction. --- client/src/btc/block.rs | 17 ++--- client/src/btc/handlers/inbound.rs | 20 +++--- client/src/btc/handlers/mod.rs | 5 +- client/src/btc/handlers/outbound.rs | 24 +++---- client/src/eth/events.rs | 13 ++-- .../src/eth/handlers/roundup_relay_handler.rs | 26 ++++---- .../src/eth/handlers/socket_relay_handler.rs | 21 +++--- client/src/eth/mod.rs | 41 ++++++------ client/src/eth/traits.rs | 5 +- client/src/substrate/traits.rs | 5 +- client/src/substrate/tx/unsigned_manager.rs | 21 +++--- periodic/src/bitcoin_rollback_verifier.rs | 24 +++---- periodic/src/heartbeat_sender.rs | 13 ++-- periodic/src/keypair_migrator.rs | 13 ++-- periodic/src/price_feeder.rs | 13 ++-- periodic/src/price_source/chainlink.rs | 13 ++-- periodic/src/price_source/mod.rs | 13 ++-- periodic/src/psbt_signer.rs | 17 ++--- periodic/src/pub_key_presubmitter.rs | 13 ++-- periodic/src/pub_key_submitter.rs | 16 ++--- periodic/src/roundup_emitter.rs | 17 ++--- periodic/src/socket_rollback_emitter.rs | 17 ++--- primitives/src/eth.rs | 65 ++++++++++--------- relayer/src/service.rs | 11 ++-- relayer/src/service_deps/btc_deps.rs | 8 +-- relayer/src/service_deps/full_deps.rs | 4 +- relayer/src/service_deps/handler_deps.rs | 8 +-- relayer/src/service_deps/manager_deps.rs | 8 +-- relayer/src/service_deps/mod.rs | 1 + relayer/src/service_deps/periodic_deps.rs | 8 +-- relayer/src/service_deps/substrate_deps.rs | 8 +-- 31 files changed, 259 insertions(+), 229 deletions(-) diff --git a/client/src/btc/block.rs b/client/src/btc/block.rs index 0be04c74..6854c90a 100644 --- a/client/src/btc/block.rs +++ b/client/src/btc/block.rs @@ -4,6 +4,7 @@ use crate::{ }; use alloy::{ + network::AnyNetwork, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, }; @@ -92,8 +93,8 @@ impl EventMessage { /// A module that reads every new Bitcoin block and filters `Inbound`, `Outbound` events. pub struct BlockManager where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The Bitcoin client. @@ -119,8 +120,8 @@ where #[async_trait::async_trait] impl RpcApi for BlockManager where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, TR: Transport + Clone, { async fn call Deserialize<'a> + Send>( @@ -151,8 +152,8 @@ where impl BlockManager where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Instantiates a new `BlockManager` instance. @@ -374,8 +375,8 @@ where #[async_trait::async_trait] impl BootstrapHandler for BlockManager where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index cc8966d2..ce7327ee 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -7,7 +7,7 @@ use crate::{ }; use alloy::{ - network::Ethereum, + network::AnyNetwork, primitives::{Address as EthAddress, B256, U256}, providers::{ fillers::{FillProvider, TxFiller}, @@ -38,8 +38,8 @@ const SUB_LOG_TARGET: &str = "inbound-handler"; pub struct InboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// `EthClient` for interact with Bifrost network. @@ -56,8 +56,8 @@ where impl InboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub fn new( @@ -151,7 +151,7 @@ where #[inline] fn bitcoin_socket( &self, - ) -> &BitcoinSocketContractInstance>> { + ) -> &BitcoinSocketContractInstance>, AnyNetwork> { self.bfc_client.protocol_contracts.bitcoin_socket.as_ref().unwrap() } } @@ -159,8 +159,8 @@ where #[async_trait::async_trait] impl Handler for InboundHandler where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { async fn run(&mut self) -> Result<()> { @@ -232,8 +232,8 @@ where #[async_trait::async_trait] impl BootstrapHandler for InboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { diff --git a/client/src/btc/handlers/mod.rs b/client/src/btc/handlers/mod.rs index 718a91b2..de07fe25 100644 --- a/client/src/btc/handlers/mod.rs +++ b/client/src/btc/handlers/mod.rs @@ -13,6 +13,7 @@ use crate::{ }; use alloy::{ + network::AnyNetwork, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, }; @@ -30,8 +31,8 @@ use super::block::EventMessage; #[async_trait::async_trait] pub trait XtRequester where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn xt_request_sender(&self) -> Arc; diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index 9dd61392..0bfa49d9 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -7,7 +7,7 @@ use crate::{ }; use alloy::{ - network::Ethereum, + network::AnyNetwork, primitives::ChainId, providers::{ fillers::{FillProvider, TxFiller}, @@ -40,8 +40,8 @@ const SUB_LOG_TARGET: &str = "outbound-handler"; pub struct OutboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub bfc_client: Arc>, @@ -55,8 +55,8 @@ where impl OutboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub fn new( @@ -77,7 +77,7 @@ where #[inline] fn socket_queue( &self, - ) -> &SocketQueueContractInstance>> { + ) -> &SocketQueueContractInstance>, AnyNetwork> { self.bfc_client.protocol_contracts.socket_queue.as_ref().unwrap() } @@ -99,8 +99,8 @@ where #[async_trait::async_trait] impl Handler for OutboundHandler where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { async fn run(&mut self) -> Result<()> { @@ -168,8 +168,8 @@ where #[async_trait::async_trait] impl SocketRelayBuilder for OutboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn get_client(&self) -> Arc> { @@ -196,8 +196,8 @@ where #[async_trait::async_trait] impl BootstrapHandler for OutboundHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { diff --git a/client/src/eth/events.rs b/client/src/eth/events.rs index 68f08790..b0052156 100644 --- a/client/src/eth/events.rs +++ b/client/src/eth/events.rs @@ -1,4 +1,5 @@ use alloy::{ + network::AnyNetwork, primitives::{BlockNumber, ChainId}, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{Filter, Log, SyncStatus}, @@ -51,8 +52,8 @@ const SUB_LOG_TARGET: &str = "event-manager"; /// The essential task that listens and handle new events. pub struct EventManager where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The ethereum client for the connected chain. @@ -70,8 +71,8 @@ where impl EventManager where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Instantiates a new `EventManager` instance. @@ -222,8 +223,8 @@ where #[async_trait::async_trait] impl BootstrapHandler for EventManager where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index dcafdd13..40073aa6 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -2,7 +2,7 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration}; use alloy::{ dyn_abi::DynSolValue, - network::Ethereum, + network::{primitives::ReceiptResponse as _, AnyNetwork}, primitives::{Address, ChainId, PrimitiveSignature, B256, U256}, providers::{ fillers::{FillProvider, TxFiller}, @@ -42,8 +42,8 @@ const SUB_LOG_TARGET: &str = "roundup-handler"; /// The essential task that handles `roundup relay` related events. pub struct RoundupRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The `EthClient` to interact with the bifrost network. @@ -63,8 +63,8 @@ where #[async_trait] impl Handler for RoundupRelayHandler where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { async fn run(&mut self) -> Result<()> { @@ -98,7 +98,7 @@ where if let Some(receipt) = self.client.get_transaction_receipt(log.transaction_hash.unwrap()).await? { - if !receipt.status() { + if !receipt.inner.status() { return Ok(()); } match self.decode_log(log.clone()).await { @@ -168,8 +168,8 @@ where impl RoundupRelayHandler where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `RoundupRelayHandler` instance. @@ -252,7 +252,11 @@ where /// Build `round_control_relay` method call transaction. fn build_transaction_request( &self, - target_socket: &SocketContractInstance>>, + target_socket: &SocketContractInstance< + T, + Arc>, + AnyNetwork, + >, roundup_submit: &Round_Up_Submit, ) -> TransactionRequest { TransactionRequest::default() @@ -324,8 +328,8 @@ where #[async_trait] impl BootstrapHandler for RoundupRelayHandler where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index 62795985..f0676a7f 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -1,6 +1,7 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration}; use alloy::{ + network::AnyNetwork, primitives::{ChainId, B256, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{Filter, Log, TransactionRequest}, @@ -43,8 +44,8 @@ const SUB_LOG_TARGET: &str = "socket-handler"; /// The essential task that handles `socket relay` related events. pub struct SocketRelayHandler where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// The `EthClient` to interact with the connected blockchain. @@ -68,8 +69,8 @@ where #[async_trait::async_trait] impl Handler for SocketRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { async fn run(&mut self) -> Result<()> { @@ -161,8 +162,8 @@ where #[async_trait::async_trait] impl SocketRelayBuilder for SocketRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn get_client(&self) -> Arc> { @@ -254,8 +255,8 @@ where impl SocketRelayHandler where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `SocketRelayHandler` instance. @@ -486,8 +487,8 @@ where #[async_trait::async_trait] impl BootstrapHandler for SocketRelayHandler where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 2cb6b378..126bd00a 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -11,7 +11,7 @@ use br_primitives::{ use alloy::{ consensus::Transaction, - network::{Ethereum, TransactionResponse as _}, + network::{AnyNetwork, AnyRpcTransaction, AnyTypedTransaction, TransactionResponse as _}, primitives::{ utils::{format_units, parse_ether, Unit}, Address, ChainId, @@ -21,7 +21,7 @@ use alloy::{ fillers::{FillProvider, TxFiller}, PendingTransactionBuilder, Provider, RootProvider, SendableTx, WalletProvider, }, - rpc::types::TransactionRequest, + rpc::types::{serde_helpers::WithOtherFields, txpool::TxpoolContent, TransactionRequest}, signers::{local::LocalSigner, Signature, Signer as _}, transports::{Transport, TransportResult}, }; @@ -39,12 +39,12 @@ pub mod traits; #[derive(Clone)] pub struct EthClient where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The inner provider. - inner: Arc>, + inner: Arc>, /// The signer. pub signer: LocalSigner, /// The provider metadata. @@ -59,13 +59,13 @@ where impl EthClient where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Create a new EthClient pub fn new( - inner: Arc>, + inner: Arc>, signer: LocalSigner, metadata: ProviderMetadata, protocol_contracts: ProtocolContracts, @@ -189,7 +189,8 @@ where // possibility of txpool being flushed automatically. wait for 2 blocks. tokio::time::sleep(Duration::from_millis(self.metadata.call_interval * 2)).await; - let pending = self.txpool_content().await?.remove_from(&self.address()).pending; + let mut content: TxpoolContent = self.txpool_content().await?; + let pending = content.remove_from(&self.address()).pending; let mut transactions = pending.into_iter().map(|(_, tx)| tx).collect::>(); transactions.make_contiguous().sort_by(|a, b| a.nonce().cmp(&b.nonce())); @@ -198,7 +199,9 @@ where continue; } - let mut tx_request = tx.clone().into_request(); + let mut tx_request = WithOtherFields::::from( + AnyTypedTransaction::from(tx.inner.inner.clone()), + ); // RBF if tx.is_legacy_gas() { @@ -245,20 +248,20 @@ where } #[async_trait::async_trait] -impl Provider for EthClient +impl Provider for EthClient where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { - fn root(&self) -> &RootProvider { + fn root(&self) -> &RootProvider { self.inner.root() } async fn send_transaction_internal( &self, - tx: SendableTx, - ) -> TransportResult> { + tx: SendableTx, + ) -> TransportResult> { self.inner.send_transaction_internal(tx).await } } @@ -270,8 +273,8 @@ pub fn send_transaction( metadata: TxRequestMetadata, handle: SpawnTaskHandle, ) where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { let this_handle = handle.clone(); @@ -290,7 +293,7 @@ pub fn send_transaction( } } - match client.send_transaction(request.clone()).await { + match client.send_transaction(WithOtherFields::new(request.clone())).await { Ok(pending) => { log::info!( target: &requester, diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index 4efd58f6..372ef47b 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use alloy::{ + network::AnyNetwork, primitives::{ChainId, B256, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{Log, TransactionInput}, @@ -37,8 +38,8 @@ pub trait Handler { /// The client to interact with the `Socket` contract instance. pub trait SocketRelayBuilder where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Get the `EthClient` of the implemented handler. diff --git a/client/src/substrate/traits.rs b/client/src/substrate/traits.rs index fd7bfdaa..3ed17fc3 100644 --- a/client/src/substrate/traits.rs +++ b/client/src/substrate/traits.rs @@ -1,4 +1,5 @@ use alloy::{ + network::AnyNetwork, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, }; @@ -17,8 +18,8 @@ use crate::eth::EthClient; #[async_trait::async_trait] pub trait ExtrinsicTask where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Get the substrate client. diff --git a/client/src/substrate/tx/unsigned_manager.rs b/client/src/substrate/tx/unsigned_manager.rs index 1bc5a357..daf7e499 100644 --- a/client/src/substrate/tx/unsigned_manager.rs +++ b/client/src/substrate/tx/unsigned_manager.rs @@ -1,4 +1,5 @@ use alloy::{ + network::AnyNetwork, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, }; @@ -21,8 +22,8 @@ const SUB_LOG_TARGET: &str = "unsigned-tx-manager"; /// The essential task that sends unsigned transactions asynchronously. pub struct UnsignedTransactionManager where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The substrate client. @@ -37,8 +38,8 @@ where impl UnsignedTransactionManager where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `UnsignedTransactionManager`. @@ -97,8 +98,8 @@ where /// The transaction task for unsigned transactions. pub struct UnsignedTransactionTask where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The substrate client. @@ -109,8 +110,8 @@ where impl UnsignedTransactionTask where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Build an unsigned transaction task instance. @@ -125,8 +126,8 @@ where #[async_trait::async_trait] impl ExtrinsicTask for UnsignedTransactionTask where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn get_sub_client(&self) -> Arc> { diff --git a/periodic/src/bitcoin_rollback_verifier.rs b/periodic/src/bitcoin_rollback_verifier.rs index b0017949..65a2e7df 100644 --- a/periodic/src/bitcoin_rollback_verifier.rs +++ b/periodic/src/bitcoin_rollback_verifier.rs @@ -1,7 +1,7 @@ use std::{collections::BTreeMap, str::FromStr, sync::Arc, time::Duration}; use alloy::{ - network::Ethereum, + network::AnyNetwork, primitives::{keccak256, Address as EvmAddress, Bytes, B256}, providers::{ fillers::{FillProvider, TxFiller}, @@ -85,8 +85,8 @@ impl From for RollbackRequest { pub struct BitcoinRollbackVerifier where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The Bitcoin client. @@ -102,8 +102,8 @@ where #[async_trait::async_trait] impl RpcApi for BitcoinRollbackVerifier where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, TR: Transport + Clone, { async fn call Deserialize<'a> + Send>( @@ -128,8 +128,8 @@ where #[async_trait::async_trait] impl XtRequester for BitcoinRollbackVerifier where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn xt_request_sender(&self) -> Arc { @@ -144,8 +144,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for BitcoinRollbackVerifier where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -222,8 +222,8 @@ where impl BitcoinRollbackVerifier where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Instantiates a new `BitcoinRollbackVerifier` instance. @@ -322,7 +322,7 @@ where /// Get the `BtcSocketQueue` precompile contract instance. fn socket_queue( &self, - ) -> &SocketQueueContractInstance>> { + ) -> &SocketQueueContractInstance>, AnyNetwork> { self.bfc_client.protocol_contracts.socket_queue.as_ref().unwrap() } } diff --git a/periodic/src/heartbeat_sender.rs b/periodic/src/heartbeat_sender.rs index 328c917a..b237de6e 100644 --- a/periodic/src/heartbeat_sender.rs +++ b/periodic/src/heartbeat_sender.rs @@ -1,4 +1,5 @@ use alloy::{ + network::AnyNetwork, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::TransactionRequest, transports::Transport, @@ -20,8 +21,8 @@ const SUB_LOG_TARGET: &str = "heartbeat-sender"; /// The essential task that sending heartbeat transaction. pub struct HeartbeatSender where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The time schedule that represents when to check heartbeat pulsed. @@ -35,8 +36,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for HeartbeatSender where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -70,8 +71,8 @@ where impl HeartbeatSender where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `HeartbeatSender` instance. diff --git a/periodic/src/keypair_migrator.rs b/periodic/src/keypair_migrator.rs index c70e9701..f2a3f3c1 100644 --- a/periodic/src/keypair_migrator.rs +++ b/periodic/src/keypair_migrator.rs @@ -1,6 +1,7 @@ use crate::traits::PeriodicWorker; use alloy::{ + network::AnyNetwork, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, }; @@ -25,8 +26,8 @@ use tokio::sync::RwLock; pub struct KeypairMigrator where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { sub_client: Option>, @@ -38,8 +39,8 @@ where impl KeypairMigrator where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Instantiates a new `KeypairMigrator` instance. @@ -151,8 +152,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for KeypairMigrator where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn schedule(&self) -> Schedule { diff --git a/periodic/src/price_feeder.rs b/periodic/src/price_feeder.rs index 05ff0586..92d415db 100644 --- a/periodic/src/price_feeder.rs +++ b/periodic/src/price_feeder.rs @@ -1,6 +1,7 @@ use std::{collections::BTreeMap, fmt::Error, str::FromStr, sync::Arc}; use alloy::{ + network::AnyNetwork, primitives::{utils::parse_ether, ChainId, FixedBytes, B256, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{TransactionInput, TransactionRequest}, @@ -33,8 +34,8 @@ const SUB_LOG_TARGET: &str = "price-feeder"; /// The essential task that handles oracle price feedings. pub struct OraclePriceFeeder where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The `EthClient` to interact with the bifrost network. @@ -56,8 +57,8 @@ where #[async_trait] impl PeriodicWorker for OraclePriceFeeder where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -91,8 +92,8 @@ where impl OraclePriceFeeder where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { pub fn new( diff --git a/periodic/src/price_source/chainlink.rs b/periodic/src/price_source/chainlink.rs index da83caf6..5abdb36c 100644 --- a/periodic/src/price_source/chainlink.rs +++ b/periodic/src/price_source/chainlink.rs @@ -2,6 +2,7 @@ use std::{collections::BTreeMap, sync::Arc}; use crate::traits::PriceFetcher; use alloy::{ + network::AnyNetwork, primitives::U256, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, @@ -13,8 +14,8 @@ use eyre::{eyre, Result}; #[derive(Clone)] pub struct ChainlinkPriceFetcher where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { client: Option>>, @@ -23,8 +24,8 @@ where #[async_trait::async_trait] impl PriceFetcher for ChainlinkPriceFetcher where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Get the price of a symbol from Chainlink aggregator. @@ -82,8 +83,8 @@ where impl ChainlinkPriceFetcher where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub async fn new(client: Option>>) -> Self { diff --git a/periodic/src/price_source/mod.rs b/periodic/src/price_source/mod.rs index 3e8a7ff8..f3ba3e94 100644 --- a/periodic/src/price_source/mod.rs +++ b/periodic/src/price_source/mod.rs @@ -1,6 +1,7 @@ use std::{collections::BTreeMap, fmt::Error, ops::Mul, sync::Arc}; use alloy::{ + network::AnyNetwork, primitives::U256, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, @@ -33,8 +34,8 @@ const LOG_TARGET: &str = "price-fetcher"; #[derive(Clone)] pub enum PriceFetchers where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { Binance(BinancePriceFetcher), @@ -85,8 +86,8 @@ pub async fn krw_to_usd(krw_amount: U256) -> Result { impl PriceFetchers where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub async fn new( @@ -111,8 +112,8 @@ where #[async_trait] impl PriceFetcher for PriceFetchers where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { async fn get_ticker_with_symbol(&self, symbol: String) -> Result { diff --git a/periodic/src/psbt_signer.rs b/periodic/src/psbt_signer.rs index 871e99b0..c2e81f11 100644 --- a/periodic/src/psbt_signer.rs +++ b/periodic/src/psbt_signer.rs @@ -1,5 +1,6 @@ use crate::traits::PeriodicWorker; use alloy::{ + network::AnyNetwork, primitives::{keccak256, Bytes, B256}, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, @@ -28,8 +29,8 @@ const SUB_LOG_TARGET: &str = "psbt-signer"; /// The essential task that submits signed PSBT's. pub struct PsbtSigner where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The Bifrost client. @@ -48,8 +49,8 @@ where impl PsbtSigner where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Instantiates a new `PsbtSigner` instance. @@ -203,8 +204,8 @@ where #[async_trait::async_trait] impl XtRequester for PsbtSigner where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn xt_request_sender(&self) -> Arc { @@ -219,8 +220,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for PsbtSigner where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn schedule(&self) -> Schedule { diff --git a/periodic/src/pub_key_presubmitter.rs b/periodic/src/pub_key_presubmitter.rs index d2d69e45..b735b1b4 100644 --- a/periodic/src/pub_key_presubmitter.rs +++ b/periodic/src/pub_key_presubmitter.rs @@ -1,5 +1,6 @@ use crate::traits::PeriodicWorker; use alloy::{ + network::AnyNetwork, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, }; @@ -32,8 +33,8 @@ const SUB_LOG_TARGET: &str = "pubkey-presubmitter"; pub struct PubKeyPreSubmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub bfc_client: Arc>, @@ -52,8 +53,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for PubKeyPreSubmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -91,8 +92,8 @@ where impl PubKeyPreSubmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Instantiates a new `PubKeyPreSubmitter` instance. diff --git a/periodic/src/pub_key_submitter.rs b/periodic/src/pub_key_submitter.rs index 6ce4ab3c..3f5295b4 100644 --- a/periodic/src/pub_key_submitter.rs +++ b/periodic/src/pub_key_submitter.rs @@ -1,7 +1,7 @@ use std::{str::FromStr, sync::Arc, time::Duration}; use alloy::{ - network::Ethereum, + network::AnyNetwork, primitives::{Address, Bytes}, providers::{ fillers::{FillProvider, TxFiller}, @@ -34,8 +34,8 @@ const SUB_LOG_TARGET: &str = "pubkey-submitter"; pub struct PubKeySubmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The Bifrost client. @@ -53,8 +53,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for PubKeySubmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -119,8 +119,8 @@ where impl PubKeySubmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Instantiates a new `PubKeySubmitter` instance. @@ -247,7 +247,7 @@ where fn registration_pool( &self, - ) -> &RegistrationPoolContractInstance>> { + ) -> &RegistrationPoolContractInstance>, AnyNetwork> { self.client.protocol_contracts.registration_pool.as_ref().unwrap() } diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index 6a613a18..f0300a2f 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -1,5 +1,6 @@ use alloy::{ dyn_abi::DynSolValue, + network::AnyNetwork, primitives::{Address, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{Filter, Log, TransactionInput, TransactionRequest}, @@ -34,8 +35,8 @@ const SUB_LOG_TARGET: &str = "roundup-emitter"; pub struct RoundupEmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Current round number @@ -53,8 +54,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for RoundupEmitter where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn schedule(&self) -> Schedule { @@ -107,8 +108,8 @@ where impl RoundupEmitter where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `RoundupEmitter` instance. @@ -200,8 +201,8 @@ where #[async_trait::async_trait] impl BootstrapHandler for RoundupEmitter where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn bootstrap_shared_data(&self) -> Arc { diff --git a/periodic/src/socket_rollback_emitter.rs b/periodic/src/socket_rollback_emitter.rs index 8d097542..8ad14e95 100644 --- a/periodic/src/socket_rollback_emitter.rs +++ b/periodic/src/socket_rollback_emitter.rs @@ -1,5 +1,6 @@ use alloy::{ consensus::BlockHeader as _, + network::AnyNetwork, primitives::ChainId, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::TransactionRequest, @@ -33,8 +34,8 @@ const SUB_LOG_TARGET: &str = "rollback-emitter"; /// (`client` and `tx_request_sender` are connected to the same chain) pub struct SocketRollbackEmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The `EthClient` to interact with the connected blockchain. @@ -53,8 +54,8 @@ where impl SocketRollbackEmitter where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// Instantiates a new `SocketRollbackEmitter`. @@ -258,8 +259,8 @@ where #[async_trait::async_trait] impl SocketRelayBuilder for SocketRollbackEmitter where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn get_client(&self) -> Arc> { @@ -277,8 +278,8 @@ where #[async_trait::async_trait] impl PeriodicWorker for SocketRollbackEmitter where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { fn schedule(&self) -> Schedule { diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index d5ba2393..2e341541 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -1,7 +1,7 @@ use std::{str::FromStr, sync::Arc}; use alloy::{ - network::Ethereum, + network::AnyNetwork, primitives::{Address, ChainId}, providers::{ fillers::{FillProvider, TxFiller}, @@ -85,38 +85,38 @@ impl ProviderMetadata { #[derive(Clone)] pub struct AggregatorContracts where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Chainlink usdc/usd aggregator pub chainlink_usdc_usd: - Option>>>, + Option>, AnyNetwork>>, /// Chainlink usdt/usd aggregator pub chainlink_usdt_usd: - Option>>>, + Option>, AnyNetwork>>, /// Chainlink dai/usd aggregator pub chainlink_dai_usd: - Option>>>, + Option>, AnyNetwork>>, /// Chainlink btc/usd aggregator pub chainlink_btc_usd: - Option>>>, + Option>, AnyNetwork>>, /// Chainlink wbtc/usd aggregator pub chainlink_wbtc_usd: - Option>>>, + Option>, AnyNetwork>>, /// Chainlink cbbtc/usd aggregator pub chainlink_cbbtc_usd: - Option>>>, + Option>, AnyNetwork>>, } impl AggregatorContracts where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub fn new( - provider: Arc>, + provider: Arc>, chainlink_usdc_usd_address: Option, chainlink_usdt_usd_address: Option, chainlink_dai_usd_address: Option, @@ -144,8 +144,8 @@ where impl Default for AggregatorContracts where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { fn default() -> Self { @@ -164,39 +164,44 @@ where /// The protocol contract instances of the EVM provider. pub struct ProtocolContracts where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// SocketContract - pub socket: SocketContractInstance>>, + pub socket: SocketContractInstance>, AnyNetwork>, /// AuthorityContract - pub authority: AuthorityContractInstance>>, + pub authority: AuthorityContractInstance>, AnyNetwork>, /// RelayerManagerContract (Bifrost only) - pub relayer_manager: - Option>>>, + pub relayer_manager: Option< + RelayerManagerContractInstance>, AnyNetwork>, + >, /// BitcoinSocketContract (Bifrost only) - pub bitcoin_socket: - Option>>>, + pub bitcoin_socket: Option< + BitcoinSocketContractInstance>, AnyNetwork>, + >, /// SocketQueueContract (Bifrost only) - pub socket_queue: Option>>>, + pub socket_queue: + Option>, AnyNetwork>>, /// RegistrationPoolContract (Bifrost only) - pub registration_pool: - Option>>>, + pub registration_pool: Option< + RegistrationPoolContractInstance>, AnyNetwork>, + >, /// RelayExecutiveContract (Bifrost only) - pub relay_executive: - Option>>>, + pub relay_executive: Option< + RelayExecutiveContractInstance>, AnyNetwork>, + >, } impl ProtocolContracts where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub fn new( is_native: bool, - provider: Arc>, + provider: Arc>, socket_address: String, authority_address: String, relayer_manager_address: Option, diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 91a9c757..81080fc1 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -7,7 +7,7 @@ use std::{ }; use alloy::{ - network::EthereumWallet, + network::{AnyNetwork, EthereumWallet}, primitives::ChainId, providers::{fillers::TxFiller, Provider, ProviderBuilder, WalletProvider}, rpc::client::RpcClient, @@ -78,6 +78,7 @@ pub fn relay(config: Configuration) -> Result { let provider = Arc::new( ProviderBuilder::new() .with_recommended_fillers() + .network::() .wallet(wallet) .on_client(client), ); @@ -192,8 +193,8 @@ fn spawn_relayer_tasks( config: &Configuration, ) -> TaskManager where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { let prometheus_config = &config.relayer_config.prometheus_config; @@ -578,8 +579,8 @@ where /// Log the configured relay targets. fn print_relay_targets(manager_deps: &ManagerDeps) where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { log::info!( diff --git a/relayer/src/service_deps/btc_deps.rs b/relayer/src/service_deps/btc_deps.rs index 196a2bfc..5bf657aa 100644 --- a/relayer/src/service_deps/btc_deps.rs +++ b/relayer/src/service_deps/btc_deps.rs @@ -2,8 +2,8 @@ use super::*; pub struct BtcDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The Bitcoin outbound handler. @@ -22,8 +22,8 @@ where impl BtcDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub fn new( diff --git a/relayer/src/service_deps/full_deps.rs b/relayer/src/service_deps/full_deps.rs index ab34f096..11674ce4 100644 --- a/relayer/src/service_deps/full_deps.rs +++ b/relayer/src/service_deps/full_deps.rs @@ -3,8 +3,8 @@ use super::*; /// The relayer client dependencies. pub struct FullDeps where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { pub bootstrap_shared_data: BootstrapSharedData, diff --git a/relayer/src/service_deps/handler_deps.rs b/relayer/src/service_deps/handler_deps.rs index b59e0aa1..70b7fcb3 100644 --- a/relayer/src/service_deps/handler_deps.rs +++ b/relayer/src/service_deps/handler_deps.rs @@ -2,8 +2,8 @@ use super::*; pub struct HandlerDeps where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { /// The `SocketRelayHandler`'s for each specified chain. @@ -14,8 +14,8 @@ where impl HandlerDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { pub fn new( diff --git a/relayer/src/service_deps/manager_deps.rs b/relayer/src/service_deps/manager_deps.rs index 6c587fc7..99f299ac 100644 --- a/relayer/src/service_deps/manager_deps.rs +++ b/relayer/src/service_deps/manager_deps.rs @@ -2,8 +2,8 @@ use super::*; pub struct ManagerDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Bifrost client @@ -16,8 +16,8 @@ where impl ManagerDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// Initializes the `EthClient`, `TransactionManager`, `EventManager`, `TxRequestSender` for each chain. diff --git a/relayer/src/service_deps/mod.rs b/relayer/src/service_deps/mod.rs index 22c16678..93e4eb23 100644 --- a/relayer/src/service_deps/mod.rs +++ b/relayer/src/service_deps/mod.rs @@ -13,6 +13,7 @@ pub use periodic_deps::PeriodicDeps; pub use substrate_deps::SubstrateDeps; use alloy::{ + network::AnyNetwork, primitives::ChainId, providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, diff --git a/relayer/src/service_deps/periodic_deps.rs b/relayer/src/service_deps/periodic_deps.rs index 7be6c6e7..d8f83da3 100644 --- a/relayer/src/service_deps/periodic_deps.rs +++ b/relayer/src/service_deps/periodic_deps.rs @@ -2,8 +2,8 @@ use super::*; pub struct PeriodicDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The `HeartbeatSender` used for system health checks. @@ -24,8 +24,8 @@ where impl PeriodicDeps where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { pub fn new( diff --git a/relayer/src/service_deps/substrate_deps.rs b/relayer/src/service_deps/substrate_deps.rs index 7edf39d7..3c5e0036 100644 --- a/relayer/src/service_deps/substrate_deps.rs +++ b/relayer/src/service_deps/substrate_deps.rs @@ -2,8 +2,8 @@ use super::*; pub struct SubstrateDeps where - F: TxFiller + WalletProvider, - P: Provider, + F: TxFiller + WalletProvider, + P: Provider, T: Transport + Clone, { /// The `UnsignedTransactionManager` for Bifrost. @@ -14,8 +14,8 @@ where impl SubstrateDeps where - F: TxFiller + WalletProvider + 'static, - P: Provider + 'static, + F: TxFiller + WalletProvider + 'static, + P: Provider + 'static, T: Transport + Clone, { pub fn new(bfc_client: Arc>, task_manager: &TaskManager) -> Self { From a544c1013ffd0343cece28ce64313fe4064e9aec Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 12 Dec 2024 14:14:49 +0900 Subject: [PATCH 18/60] CCCP-295, chore: keypair_migrator never return --- relayer/src/service.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 81080fc1..a6e22224 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -235,10 +235,7 @@ where "migration-detector", Some("migration-detector"), async move { - let reason = keypair_migrator.run().await; - let log_msg = format!("migration detector stopped: {:?}", reason); - log::error!("{log_msg}"); - sentry::capture_message(&log_msg, sentry::Level::Error); + let _ = keypair_migrator.run().await; }, ); From d4ab5f237492fdab8816824816bc06de1b8eb67f Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 12 Dec 2024 14:15:59 +0900 Subject: [PATCH 19/60] CCCP-295, chore: update doc comment --- relayer/src/service_deps/manager_deps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/src/service_deps/manager_deps.rs b/relayer/src/service_deps/manager_deps.rs index 99f299ac..e85d0342 100644 --- a/relayer/src/service_deps/manager_deps.rs +++ b/relayer/src/service_deps/manager_deps.rs @@ -20,7 +20,7 @@ where P: Provider, T: Transport + Clone, { - /// Initializes the `EthClient`, `TransactionManager`, `EventManager`, `TxRequestSender` for each chain. + /// Initializes the `EthClient`'s and `EventManager`'s for each chain. pub fn new( config: &Configuration, clients: Arc>>>, From efa5d7f08db8567d7dcd35450efe58c5da3eafdf Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 12 Dec 2024 14:19:09 +0900 Subject: [PATCH 20/60] CCCP-295, chore: add missing sleep --- relayer/src/service.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/relayer/src/service.rs b/relayer/src/service.rs index a6e22224..284fe98d 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -253,6 +253,8 @@ where ); log::error!("{log_msg}"); sentry::capture_message(&log_msg, sentry::Level::Error); + + tokio::time::sleep(Duration::from_secs(12)).await; } }, ); From 6e62b38864e0b7258627ecab7f3dbacc23493c9c Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Fri, 13 Dec 2024 10:31:59 +0900 Subject: [PATCH 21/60] CCCP-295, fix: check every chainlink feed existence --- periodic/src/price_feeder.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/periodic/src/price_feeder.rs b/periodic/src/price_feeder.rs index 92d415db..98713bdc 100644 --- a/periodic/src/price_feeder.rs +++ b/periodic/src/price_feeder.rs @@ -19,6 +19,7 @@ use br_client::eth::{send_transaction, EthClient}; use br_primitives::{ constants::{errors::INVALID_PERIODIC_SCHEDULE, schedule::PRICE_FEEDER_SCHEDULE}, contracts::socket::get_asset_oids, + eth::AggregatorContracts, periodic::{PriceResponse, PriceSource}, tx::{PriceFeedMetadata, TxRequestMetadata}, utils::sub_display_format, @@ -244,9 +245,7 @@ where } for (_, client) in self.clients.iter() { - if client.aggregator_contracts.chainlink_usdc_usd.is_some() - || client.aggregator_contracts.chainlink_usdt_usd.is_some() - { + if Self::has_any_chainlink_feeds(&client.aggregator_contracts) { if let Ok(fetcher) = PriceFetchers::new(PriceSource::Chainlink, client.clone().into()).await { @@ -256,6 +255,19 @@ where } } + fn has_any_chainlink_feeds(contracts: &AggregatorContracts) -> bool { + [ + &contracts.chainlink_usdc_usd, + &contracts.chainlink_usdt_usd, + &contracts.chainlink_dai_usd, + &contracts.chainlink_btc_usd, + &contracts.chainlink_wbtc_usd, + &contracts.chainlink_cbbtc_usd, + ] + .iter() + .any(|contract| contract.is_some()) + } + /// Build and send transaction. async fn build_and_send_transaction(&self, price_responses: BTreeMap) { let mut oid_bytes_list: Vec> = vec![]; From 831904ecabb7e812e2a6145a660b868c823df631 Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Fri, 13 Dec 2024 10:43:28 +0900 Subject: [PATCH 22/60] CCCP-295, fix: add missing tickers --- periodic/src/price_source/chainlink.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/periodic/src/price_source/chainlink.rs b/periodic/src/price_source/chainlink.rs index 5abdb36c..4334384b 100644 --- a/periodic/src/price_source/chainlink.rs +++ b/periodic/src/price_source/chainlink.rs @@ -69,8 +69,14 @@ where async fn get_tickers(&self) -> Result> { let mut ret = BTreeMap::new(); - for symbol in ["USDC".to_string(), "USDT".to_string(), "DAI".to_string(), "BTC".to_string()] - { + for symbol in [ + "USDC".to_string(), + "USDT".to_string(), + "DAI".to_string(), + "BTC".to_string(), + "WBTC".to_string(), + "CBBTC".to_string(), + ] { match self.get_ticker_with_symbol(symbol.clone()).await { Ok(ticker) => ret.insert(symbol, ticker), Err(_) => continue, From 271cd633b45bff5374687fc5c3d0f72d85d3a3ce Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Fri, 13 Dec 2024 12:54:12 +0900 Subject: [PATCH 23/60] CCCP-295, refactor: impl From trait for EthereumSignature --- periodic/src/bitcoin_rollback_verifier.rs | 18 ++++++------- periodic/src/psbt_signer.rs | 14 +++++----- periodic/src/pub_key_presubmitter.rs | 32 +++++++++++------------ periodic/src/pub_key_submitter.rs | 18 ++++++------- primitives/src/utils.rs | 13 ++++----- 5 files changed, 47 insertions(+), 48 deletions(-) diff --git a/periodic/src/bitcoin_rollback_verifier.rs b/periodic/src/bitcoin_rollback_verifier.rs index 65a2e7df..72c38e90 100644 --- a/periodic/src/bitcoin_rollback_verifier.rs +++ b/periodic/src/bitcoin_rollback_verifier.rs @@ -26,7 +26,7 @@ use br_primitives::{ }, substrate::{bifrost_runtime, AccountId20, EthereumSignature, RollbackPollMessage}, tx::{SubmitRollbackPollMetadata, XtRequest, XtRequestMetadata, XtRequestSender}, - utils::{convert_ethers_to_ecdsa_signature, sub_display_format}, + utils::sub_display_format, }; use cron::Schedule; use eyre::Result; @@ -280,14 +280,14 @@ where txid: H256::from(txid.0), is_approved, }; - let signature = convert_ethers_to_ecdsa_signature( - self.bfc_client - .sign_message( - &[keccak256("RollbackPoll").as_slice(), txid.as_ref(), &[is_approved as u8]] - .concat(), - ) - .await?, - ); + let signature = self + .bfc_client + .sign_message( + &[keccak256("RollbackPoll").as_slice(), txid.as_ref(), &[is_approved as u8]] + .concat(), + ) + .await? + .into(); Ok((msg, signature)) } diff --git a/periodic/src/psbt_signer.rs b/periodic/src/psbt_signer.rs index c2e81f11..75da5f41 100644 --- a/periodic/src/psbt_signer.rs +++ b/periodic/src/psbt_signer.rs @@ -15,7 +15,7 @@ use br_primitives::{ bifrost_runtime, AccountId20, EthereumSignature, MigrationSequence, SignedPsbtMessage, }, tx::{SubmitSignedPsbtMetadata, XtRequest, XtRequestMetadata, XtRequestSender}, - utils::{convert_ethers_to_ecdsa_signature, hash_bytes, sub_display_format}, + utils::{hash_bytes, sub_display_format}, }; use cron::Schedule; use eyre::Result; @@ -137,13 +137,11 @@ where unsigned_psbt: unsigned_psbt.serialize(), signed_psbt: signed_psbt.clone(), }; - let signature = convert_ethers_to_ecdsa_signature( - self.client - .sign_message( - &[keccak256("SignedPsbt").as_slice(), signed_psbt.as_ref()].concat(), - ) - .await?, - ); + let signature = self + .client + .sign_message(&[keccak256("SignedPsbt").as_slice(), signed_psbt.as_ref()].concat()) + .await? + .into(); return Ok(Some((msg, signature))); } log::warn!( diff --git a/periodic/src/pub_key_presubmitter.rs b/periodic/src/pub_key_presubmitter.rs index b735b1b4..8d3e8153 100644 --- a/periodic/src/pub_key_presubmitter.rs +++ b/periodic/src/pub_key_presubmitter.rs @@ -21,7 +21,7 @@ use br_primitives::{ VaultKeyPresubmissionMetadata, XtRequest, XtRequestMessage, XtRequestMetadata, XtRequestSender, }, - utils::{convert_ethers_to_ecdsa_signature, sub_display_format}, + utils::sub_display_format, }; use cron::Schedule; use eyre::Result; @@ -164,22 +164,22 @@ where pub_keys: converted_pub_keys.iter().map(|x| Public(*x)).collect(), pool_round, }; - let signature = convert_ethers_to_ecdsa_signature( - self.bfc_client - .sign_message( - &format!( - "{}:{}", - pool_round, - converted_pub_keys - .iter() - .map(|x| array_bytes::bytes2hex("0x", *x)) - .collect::>() - .concat() - ) - .as_bytes(), + let signature = self + .bfc_client + .sign_message( + &format!( + "{}:{}", + pool_round, + converted_pub_keys + .iter() + .map(|x| array_bytes::bytes2hex("0x", *x)) + .collect::>() + .concat() ) - .await?, - ); + .as_bytes(), + ) + .await? + .into(); Ok((msg, signature)) } diff --git a/periodic/src/pub_key_submitter.rs b/periodic/src/pub_key_submitter.rs index 3f5295b4..e7e47375 100644 --- a/periodic/src/pub_key_submitter.rs +++ b/periodic/src/pub_key_submitter.rs @@ -21,7 +21,7 @@ use br_primitives::{ AccountId20, EthereumSignature, MigrationSequence, Public, VaultKeySubmission, }, tx::{SubmitVaultKeyMetadata, XtRequest, XtRequestMessage, XtRequestMetadata, XtRequestSender}, - utils::{convert_ethers_to_ecdsa_signature, sub_display_format}, + utils::sub_display_format, }; use cron::Schedule; use eyre::Result; @@ -162,14 +162,14 @@ where pub_key: Public(converted_pub_key), pool_round, }; - let signature = convert_ethers_to_ecdsa_signature( - self.client - .sign_message( - &format!("{}:{}", pool_round, array_bytes::bytes2hex("0x", converted_pub_key)) - .as_bytes(), - ) - .await?, - ); + let signature = self + .client + .sign_message( + &format!("{}:{}", pool_round, array_bytes::bytes2hex("0x", converted_pub_key)) + .as_bytes(), + ) + .await? + .into(); Ok((msg, signature)) } diff --git a/primitives/src/utils.rs b/primitives/src/utils.rs index 39a300ee..e130198a 100644 --- a/primitives/src/utils.rs +++ b/primitives/src/utils.rs @@ -1,4 +1,4 @@ -use alloy::primitives::{keccak256, Address, PrimitiveSignature as EthersSignature, B256}; +use alloy::primitives::{keccak256, Address, PrimitiveSignature, B256}; use k256::{ecdsa::VerifyingKey, elliptic_curve::sec1::ToEncodedPoint}; use rand::Rng as _; use sha3::{Digest, Keccak256}; @@ -13,10 +13,11 @@ pub fn generate_delay() -> u64 { rand::thread_rng().gen_range(0..=12000) } -/// Converts the ethers::Signature to a bifrost_runtime::Signature. -pub fn convert_ethers_to_ecdsa_signature(ethers_signature: EthersSignature) -> EthereumSignature { - let sig: [u8; 65] = ethers_signature.into(); - EthereumSignature(Signature(sig)) +impl From for EthereumSignature { + fn from(signature: PrimitiveSignature) -> Self { + let sig: [u8; 65] = signature.into(); + EthereumSignature(Signature(sig)) + } } /// Hash the given bytes. @@ -25,7 +26,7 @@ pub fn hash_bytes(bytes: &Vec) -> B256 { } /// Recovers the address from the given signature and message. -pub fn recover_message(sig: EthersSignature, msg: &[u8]) -> Address { +pub fn recover_message(sig: PrimitiveSignature, msg: &[u8]) -> Address { let v = sig.recid(); let rs = sig.to_k256().unwrap(); From 42c9d9b6359406419b4571e196080f5de703321a Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Fri, 13 Dec 2024 13:13:55 +0900 Subject: [PATCH 24/60] CCCP-295, fix: correct gas fee assignment in send_transaction function for bifrost network --- client/src/eth/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 126bd00a..3630fa3d 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -281,8 +281,8 @@ pub fn send_transaction( this_handle.spawn("send_transaction", None, async move { if client.metadata.is_native { // gas price is fixed to 1000 Gwei on bifrost network - request.max_fee_per_gas = Some(0); - request.max_priority_fee_per_gas = Some(1000 * Unit::GWEI.wei().to::()); + request.max_fee_per_gas = Some(1000 * Unit::GWEI.wei().to::()); + request.max_priority_fee_per_gas = Some(0); } else { // to avoid duplicate(will revert) external networks transactions tokio::time::sleep(Duration::from_millis(generate_delay())).await; From 3370c40d5ac8f465f1efc8235b520c71583ae267 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Fri, 13 Dec 2024 14:23:35 +0900 Subject: [PATCH 25/60] CCCP-295, fix: use DynSolValue to encode poll input --- .../src/eth/handlers/socket_relay_handler.rs | 8 ++-- client/src/eth/mod.rs | 7 ++-- client/src/eth/traits.rs | 37 ++++++++++++++++++- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index f0676a7f..787e00b6 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -184,10 +184,12 @@ where } else { self.build_outbound_signatures(msg.clone()).await? }; + + let input = self.build_poll_call_data(msg, signatures); + println!("input: {:?}", input); + return Ok(Some(BuiltRelayTransaction::new( - TransactionRequest::default() - .input(self.build_poll_call_data(msg, signatures)) - .to(*to_socket), + TransactionRequest::default().input(input).to(*to_socket), is_external, ))); } diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 3630fa3d..53e57e10 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -22,12 +22,13 @@ use alloy::{ PendingTransactionBuilder, Provider, RootProvider, SendableTx, WalletProvider, }, rpc::types::{serde_helpers::WithOtherFields, txpool::TxpoolContent, TransactionRequest}, - signers::{local::LocalSigner, Signature, Signer as _}, + signers::{local::LocalSigner, Signature}, transports::{Transport, TransportResult}, }; use eyre::{eyre, Result}; use k256::ecdsa::SigningKey; use sc_service::SpawnTaskHandle; +use sha3::{Digest, Keccak256}; use std::{collections::VecDeque, sync::Arc, time::Duration}; use tokio::sync::Mutex; use url::Url; @@ -137,8 +138,8 @@ where /// Signs the given message. pub async fn sign_message(&self, message: &[u8]) -> Result { - let a: Vec = self.signer.sign_message(message).await?.into(); - Ok(Signature::try_from(a.as_slice())?) + let cred = self.signer.credential(); + Ok(Signature::from(cred.sign_digest_recoverable(Keccak256::new_with_prefix(message))?)) } /// Get the bootstrap offset height based on the block time. diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index 372ef47b..6ba9b9f3 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -1,8 +1,9 @@ use std::sync::Arc; use alloy::{ + dyn_abi::DynSolValue, network::AnyNetwork, - primitives::{ChainId, B256, U256}, + primitives::{ChainId, FixedBytes, B256, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{Log, TransactionInput}, signers::Signature, @@ -66,6 +67,38 @@ where Ok(None) } + fn encode_socket_message(&self, msg: Socket_Message) -> Vec { + let req_id = DynSolValue::Tuple(vec![ + DynSolValue::FixedBytes( + FixedBytes::<32>::from_slice(msg.req_id.ChainIndex.as_slice()), + 4, + ), + DynSolValue::Uint(U256::from(msg.req_id.round_id), 64), + DynSolValue::Uint(U256::from(msg.req_id.sequence), 128), + ]); + let status = DynSolValue::Uint(U256::from(msg.status), 8); + let ins_code = DynSolValue::Tuple(vec![ + DynSolValue::FixedBytes( + FixedBytes::<32>::from_slice(msg.ins_code.ChainIndex.as_slice()), + 4, + ), + DynSolValue::FixedBytes( + FixedBytes::<32>::from_slice(msg.ins_code.RBCmethod.as_slice()), + 16, + ), + ]); + let params = DynSolValue::Tuple(vec![ + DynSolValue::FixedBytes(msg.params.tokenIDX0, 32), + DynSolValue::FixedBytes(msg.params.tokenIDX1, 32), + DynSolValue::Address(msg.params.refund), + DynSolValue::Address(msg.params.to), + DynSolValue::Uint(msg.params.amount, 256), + DynSolValue::Bytes(msg.params.variants.to_vec()), + ]); + + DynSolValue::Tuple(vec![req_id, status, ins_code, params]).abi_encode() + } + /// Build the signatures required to request an inbound `poll()` and returns a flag which represents /// whether the relay transaction should be processed to an external chain. async fn build_inbound_signatures(&self, _msg: Socket_Message) -> Result<(Signatures, bool)> { @@ -80,7 +113,7 @@ where /// Signs the given socket message. async fn sign_socket_message(&self, msg: Socket_Message) -> Result { - Ok(self.get_client().sign_message(&msg.abi_encode()).await?) + Ok(self.get_client().sign_message(&self.encode_socket_message(msg)).await?) } /// Get the signatures of the given message. From 1081613b223f8ff654429646f1d5108a695b6d95 Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Fri, 13 Dec 2024 16:08:56 +0900 Subject: [PATCH 26/60] CCCP-295, refactor: use max() --- client/src/eth/mod.rs | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 53e57e10..3a39cf3a 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -29,7 +29,7 @@ use eyre::{eyre, Result}; use k256::ecdsa::SigningKey; use sc_service::SpawnTaskHandle; use sha3::{Digest, Keccak256}; -use std::{collections::VecDeque, sync::Arc, time::Duration}; +use std::{cmp::max, collections::VecDeque, sync::Arc, time::Duration}; use tokio::sync::Mutex; use url::Url; @@ -209,11 +209,7 @@ where let new_gas_price = ((tx_request.gas_price.unwrap() as f64) * 1.1).ceil() as u128; let current_gas_price = self.get_gas_price().await.unwrap(); - if new_gas_price >= current_gas_price { - tx_request.gas_price = Some(new_gas_price); - } else { - tx_request.gas_price = Some(current_gas_price); - } + tx_request.gas_price = Some(max(new_gas_price, current_gas_price)); } else { let current_gas_price = self.estimate_eip1559_fees(None).await.unwrap(); @@ -222,17 +218,12 @@ where let new_max_priority_fee_per_gas = (tx_request.max_priority_fee_per_gas.unwrap() as f64 * 1.1).ceil() as u128; - if new_max_fee_per_gas >= current_gas_price.max_fee_per_gas { - tx_request.max_fee_per_gas = Some(new_max_fee_per_gas); - } else { - tx_request.max_fee_per_gas = Some(current_gas_price.max_fee_per_gas); - } - if new_max_priority_fee_per_gas >= current_gas_price.max_priority_fee_per_gas { - tx_request.max_priority_fee_per_gas = Some(new_max_priority_fee_per_gas); - } else { - tx_request.max_priority_fee_per_gas = - Some(current_gas_price.max_priority_fee_per_gas); - } + tx_request.max_fee_per_gas = + Some(max(new_max_fee_per_gas, current_gas_price.max_fee_per_gas)); + tx_request.max_priority_fee_per_gas = Some(max( + new_max_priority_fee_per_gas, + current_gas_price.max_priority_fee_per_gas, + )); } let pending = self From e1f7ecbe94d0c3161e5d5e250c60ed0f2856a07e Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Fri, 13 Dec 2024 16:14:50 +0900 Subject: [PATCH 27/60] CCCP-295, fix: padding issue --- client/src/eth/handlers/socket_relay_handler.rs | 7 +++---- client/src/eth/traits.rs | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index 787e00b6..f43f0a96 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -185,11 +185,10 @@ where self.build_outbound_signatures(msg.clone()).await? }; - let input = self.build_poll_call_data(msg, signatures); - println!("input: {:?}", input); - return Ok(Some(BuiltRelayTransaction::new( - TransactionRequest::default().input(input).to(*to_socket), + TransactionRequest::default() + .input(self.build_poll_call_data(msg, signatures)) + .to(*to_socket), is_external, ))); } diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index 6ba9b9f3..81157d4c 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -70,7 +70,7 @@ where fn encode_socket_message(&self, msg: Socket_Message) -> Vec { let req_id = DynSolValue::Tuple(vec![ DynSolValue::FixedBytes( - FixedBytes::<32>::from_slice(msg.req_id.ChainIndex.as_slice()), + FixedBytes::<32>::right_padding_from(msg.req_id.ChainIndex.as_slice()), 4, ), DynSolValue::Uint(U256::from(msg.req_id.round_id), 64), @@ -79,11 +79,11 @@ where let status = DynSolValue::Uint(U256::from(msg.status), 8); let ins_code = DynSolValue::Tuple(vec![ DynSolValue::FixedBytes( - FixedBytes::<32>::from_slice(msg.ins_code.ChainIndex.as_slice()), + FixedBytes::<32>::right_padding_from(msg.ins_code.ChainIndex.as_slice()), 4, ), DynSolValue::FixedBytes( - FixedBytes::<32>::from_slice(msg.ins_code.RBCmethod.as_slice()), + FixedBytes::<32>::right_padding_from(msg.ins_code.RBCmethod.as_slice()), 16, ), ]); From c8150038a7a436c430ef63e14452b7ab777f3b01 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Fri, 13 Dec 2024 17:14:47 +0900 Subject: [PATCH 28/60] CCCP-295, chore: gas coefficient --- client/src/eth/mod.rs | 25 +++++++++++++++++++++++-- primitives/src/eth.rs | 14 +++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 3a39cf3a..c06f6929 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -4,7 +4,7 @@ use br_primitives::{ errors::{INSUFFICIENT_FUNDS, INVALID_CHAIN_ID, PROVIDER_INTERNAL_ERROR}, }, contracts::authority::BfcStaking::round_meta_data, - eth::{AggregatorContracts, ProtocolContracts, ProviderMetadata}, + eth::{AggregatorContracts, GasCoefficient, ProtocolContracts, ProviderMetadata}, tx::TxRequestMetadata, utils::generate_delay, }; @@ -271,6 +271,28 @@ pub fn send_transaction( { let this_handle = handle.clone(); this_handle.spawn("send_transaction", None, async move { + request.from = Some(client.address()); + + match client.estimate_gas(&WithOtherFields::new(request.clone())).await { + Ok(gas) => { + let coefficient: f64 = GasCoefficient::from(client.metadata.is_native).into(); + let estimated_gas = gas as f64 * coefficient; + request.gas = Some(estimated_gas.ceil() as u64); + }, + Err(err) => { + let msg = format!( + " ❗️ Failed to estimate gas ({} address:{}): {}, Error: {}", + client.get_chain_name(), + client.address(), + metadata, + err + ); + log::error!(target: &requester, "{msg}"); + sentry::capture_message(&msg, sentry::Level::Error); + return; + }, + }; + if client.metadata.is_native { // gas price is fixed to 1000 Gwei on bifrost network request.max_fee_per_gas = Some(1000 * Unit::GWEI.wei().to::()); @@ -280,7 +302,6 @@ pub fn send_transaction( tokio::time::sleep(Duration::from_millis(generate_delay())).await; if !client.metadata.eip1559 { - // TODO: if transaction failed by gas price issue, should bring back `GasCoefficient` here. request.gas_price = Some(client.get_gas_price().await.unwrap()); } } diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index 2e341541..62a977d3 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -267,14 +267,18 @@ pub enum GasCoefficient { High, } -impl GasCoefficient { - pub fn into_f64(&self) -> f64 { - f64::from(self) +impl From for GasCoefficient { + fn from(is_native: bool) -> Self { + if is_native { + GasCoefficient::Mid + } else { + GasCoefficient::Low + } } } -impl From<&GasCoefficient> for f64 { - fn from(value: &GasCoefficient) -> Self { +impl From for f64 { + fn from(value: GasCoefficient) -> Self { match value { GasCoefficient::Low => 1.2, GasCoefficient::Mid => 7.0, From 109cd1e4f66df94b53f6a653e01fab631b415144 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Fri, 13 Dec 2024 18:44:41 +0900 Subject: [PATCH 29/60] CCCP-295, chore: resolve clippy --- client/src/btc/block.rs | 9 +- client/src/btc/handlers/inbound.rs | 13 +- client/src/btc/handlers/outbound.rs | 13 +- client/src/btc/storage/keypair.rs | 2 +- client/src/btc/storage/pending_outbound.rs | 6 +- client/src/eth/events.rs | 6 +- .../src/eth/handlers/roundup_relay_handler.rs | 26 ++-- .../src/eth/handlers/socket_relay_handler.rs | 8 +- client/src/eth/mod.rs | 28 ++-- client/src/eth/traits.rs | 2 +- client/src/substrate/traits.rs | 2 +- periodic/src/bitcoin_rollback_verifier.rs | 13 +- periodic/src/keypair_migrator.rs | 29 ++-- periodic/src/price_feeder.rs | 10 +- periodic/src/pub_key_presubmitter.rs | 6 +- periodic/src/pub_key_submitter.rs | 15 +-- periodic/src/socket_rollback_emitter.rs | 6 +- primitives/src/contracts/authority.rs | 7 +- primitives/src/contracts/bitcoin_socket.rs | 7 +- .../src/contracts/chainlink_aggregator.rs | 7 +- primitives/src/contracts/mod.rs | 8 ++ primitives/src/contracts/registration_pool.rs | 7 +- primitives/src/contracts/relay_executive.rs | 7 +- primitives/src/contracts/relayer_manager.rs | 7 +- primitives/src/contracts/socket.rs | 11 +- primitives/src/contracts/socket_queue.rs | 7 +- primitives/src/eth.rs | 126 ++++++++---------- primitives/src/tx.rs | 2 +- relayer/src/cli.rs | 4 +- relayer/src/service.rs | 28 +--- relayer/src/service_deps/btc_deps.rs | 4 +- relayer/src/service_deps/manager_deps.rs | 6 +- relayer/src/service_deps/mod.rs | 2 +- relayer/src/service_deps/periodic_deps.rs | 4 +- 34 files changed, 209 insertions(+), 229 deletions(-) diff --git a/client/src/btc/block.rs b/client/src/btc/block.rs index 6854c90a..2b26be60 100644 --- a/client/src/btc/block.rs +++ b/client/src/btc/block.rs @@ -1,7 +1,4 @@ -use crate::{ - btc::{storage::pending_outbound::PendingOutboundPool, LOG_TARGET}, - eth::EthClient, -}; +use crate::{btc::LOG_TARGET, eth::EthClient}; use alloy::{ network::AnyNetwork, @@ -113,8 +110,6 @@ where bootstrap_shared_data: Arc, /// The bootstrap offset in blocks. bootstrap_offset: u32, - /// NOTE: currently not used. - _pending_outbounds: PendingOutboundPool, } #[async_trait::async_trait] @@ -160,7 +155,6 @@ where pub fn new( btc_client: BtcClient, bfc_client: Arc>, - _pending_outbounds: PendingOutboundPool, bootstrap_shared_data: Arc, call_interval: u64, block_confirmations: u64, @@ -185,7 +179,6 @@ where call_interval, bootstrap_shared_data, bootstrap_offset, - _pending_outbounds, } } diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index ce7327ee..e74d81f6 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -9,17 +9,14 @@ use crate::{ use alloy::{ network::AnyNetwork, primitives::{Address as EthAddress, B256, U256}, - providers::{ - fillers::{FillProvider, TxFiller}, - Provider, WalletProvider, - }, + providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{TransactionInput, TransactionRequest}, transports::Transport, }; use bitcoincore_rpc::bitcoin::Txid; use br_primitives::{ bootstrap::BootstrapSharedData, - contracts::bitcoin_socket::BitcoinSocketContract::BitcoinSocketContractInstance, + contracts::bitcoin_socket::BitcoinSocketInstance, eth::BootstrapState, tx::{BitcoinRelayMetadata, TxRequestMetadata}, utils::sub_display_format, @@ -117,7 +114,7 @@ where TransactionRequest::default() .input(TransactionInput::new(calldata)) - .to(self.bitcoin_socket().address().clone()) + .to(*self.bitcoin_socket().address()) } /// Checks if the relayer has already voted on the event. @@ -149,9 +146,7 @@ where } #[inline] - fn bitcoin_socket( - &self, - ) -> &BitcoinSocketContractInstance>, AnyNetwork> { + fn bitcoin_socket(&self) -> &BitcoinSocketInstance { self.bfc_client.protocol_contracts.bitcoin_socket.as_ref().unwrap() } } diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index 0bfa49d9..ad0d5740 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -9,10 +9,7 @@ use crate::{ use alloy::{ network::AnyNetwork, primitives::ChainId, - providers::{ - fillers::{FillProvider, TxFiller}, - Provider, WalletProvider, - }, + providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::TransactionRequest, sol_types::SolEvent, transports::Transport, @@ -21,7 +18,7 @@ use br_primitives::{ bootstrap::BootstrapSharedData, contracts::{ socket::{SocketContract::Socket, Socket_Struct::Socket_Message}, - socket_queue::SocketQueueContract::SocketQueueContractInstance, + socket_queue::SocketQueueInstance, }, eth::{BootstrapState, BuiltRelayTransaction, SocketEventStatus}, tx::{SocketRelayMetadata, TxRequestMetadata}, @@ -75,9 +72,7 @@ where } #[inline] - fn socket_queue( - &self, - ) -> &SocketQueueContractInstance>, AnyNetwork> { + fn socket_queue(&self) -> &SocketQueueInstance { self.bfc_client.protocol_contracts.socket_queue.as_ref().unwrap() } @@ -187,7 +182,7 @@ where return Ok(Some(BuiltRelayTransaction::new( TransactionRequest::default() .input(self.build_poll_call_data(msg, signatures)) - .to(self.bfc_client.protocol_contracts.socket.address().clone()), + .to(*self.bfc_client.protocol_contracts.socket.address()), is_external, ))); } diff --git a/client/src/btc/storage/keypair.rs b/client/src/btc/storage/keypair.rs index 88235a9b..5589775d 100644 --- a/client/src/btc/storage/keypair.rs +++ b/client/src/btc/storage/keypair.rs @@ -120,7 +120,7 @@ impl GetKey for KeypairStorage { .expect(KEYSTORE_INTERNAL_ERROR), ) { Ok(pair) => { - return if let Some(pair) = pair { + if let Some(pair) = pair { Ok(Some( PrivateKey::from_slice(&pair.into_inner().seed(), self.network) .expect(KEYSTORE_INTERNAL_ERROR), diff --git a/client/src/btc/storage/pending_outbound.rs b/client/src/btc/storage/pending_outbound.rs index 3fcf78e8..2c04f4cf 100644 --- a/client/src/btc/storage/pending_outbound.rs +++ b/client/src/btc/storage/pending_outbound.rs @@ -13,16 +13,12 @@ pub struct PendingOutboundValue { pub amount: Amount, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct PendingOutboundPool( Arc, PendingOutboundValue>>>, ); impl PendingOutboundPool { - pub fn new() -> Self { - Self(Default::default()) - } - pub async fn insert( &self, key: Address, diff --git a/client/src/eth/events.rs b/client/src/eth/events.rs index b0052156..69b47071 100644 --- a/client/src/eth/events.rs +++ b/client/src/eth/events.rs @@ -138,14 +138,14 @@ where .from_block(BlockNumber::from(from)) .to_block(BlockNumber::from(to)) .address(vec![ - self.client.protocol_contracts.socket.address().clone(), - bitcoin_socket.address().clone(), + *self.client.protocol_contracts.socket.address(), + *bitcoin_socket.address(), ]) } else { Filter::new() .from_block(BlockNumber::from(from)) .to_block(BlockNumber::from(to)) - .address(self.client.protocol_contracts.socket.address().clone()) + .address(*self.client.protocol_contracts.socket.address()) }; let target_logs = self.client.get_logs(&filter).await?; diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 40073aa6..385e8109 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -1,13 +1,10 @@ -use std::{collections::BTreeMap, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; use alloy::{ dyn_abi::DynSolValue, network::{primitives::ReceiptResponse as _, AnyNetwork}, - primitives::{Address, ChainId, PrimitiveSignature, B256, U256}, - providers::{ - fillers::{FillProvider, TxFiller}, - Provider, WalletProvider, - }, + primitives::{Address, PrimitiveSignature, B256, U256}, + providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{Filter, Log, TransactionInput, TransactionRequest}, sol_types::SolEvent as _, transports::Transport, @@ -22,7 +19,8 @@ use br_primitives::{ bootstrap::BootstrapSharedData, constants::{cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE}, contracts::socket::{ - SocketContract::{RoundUp, SocketContractInstance}, + SocketContract::RoundUp, + SocketInstance, Socket_Struct::{Round_Up_Submit, Signatures}, }, eth::{BootstrapState, RoundUpEventStatus}, @@ -34,7 +32,7 @@ use crate::eth::{ events::EventMessage, send_transaction, traits::{BootstrapHandler, Handler}, - EthClient, + ClientMap, EthClient, }; const SUB_LOG_TARGET: &str = "roundup-handler"; @@ -51,7 +49,7 @@ where /// The receiver that consumes new events from the block channel. event_receiver: Receiver, /// `EthClient`s to interact with provided networks except bifrost network. - external_clients: Arc>>>, + external_clients: Arc>, /// Signature of RoundUp Event. roundup_signature: B256, /// The bootstrap shared data. @@ -141,7 +139,7 @@ where "-[{}] Error on decoding RoundUp event ({:?}):{}", sub_display_format(SUB_LOG_TARGET), log.transaction_hash, - e.to_string(), + e, ); log::error!(target: &self.client.get_chain_name(), "{log_msg}"); sentry::capture_message( @@ -176,7 +174,7 @@ where pub fn new( client: Arc>, event_receiver: Receiver, - clients: Arc>>>, + clients: Arc>, bootstrap_shared_data: Arc, handle: SpawnTaskHandle, ) -> Self { @@ -252,11 +250,7 @@ where /// Build `round_control_relay` method call transaction. fn build_transaction_request( &self, - target_socket: &SocketContractInstance< - T, - Arc>, - AnyNetwork, - >, + target_socket: &SocketInstance, roundup_submit: &Round_Up_Submit, ) -> TransactionRequest { TransactionRequest::default() diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index f43f0a96..665cc18f 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -36,7 +36,7 @@ use crate::eth::{ events::EventMessage, send_transaction, traits::{BootstrapHandler, Handler, SocketRelayBuilder}, - EthClient, + ClientMap, EthClient, }; const SUB_LOG_TARGET: &str = "socket-handler"; @@ -53,7 +53,7 @@ where /// The receiver that consumes new events from the block channel. event_receiver: Receiver, /// The entire clients instantiated in the system. > - system_clients: Arc>>>, + system_clients: Arc>, /// The bifrost client. bifrost_client: Arc>, /// The rollback sender for each chain. @@ -264,7 +264,7 @@ where pub fn new( id: ChainId, event_receiver: Receiver, - system_clients: Arc>>>, + system_clients: Arc>, bifrost_client: Arc>, rollback_senders: Arc>>>, handle: SpawnTaskHandle, @@ -472,7 +472,7 @@ where sub_display_format(SUB_LOG_TARGET), self.client.address(), metadata, - error.to_string() + error ); log::error!(target: &self.client.get_chain_name(), "{msg}"); sentry::capture_message( diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index c06f6929..6357582c 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -29,7 +29,12 @@ use eyre::{eyre, Result}; use k256::ecdsa::SigningKey; use sc_service::SpawnTaskHandle; use sha3::{Digest, Keccak256}; -use std::{cmp::max, collections::VecDeque, sync::Arc, time::Duration}; +use std::{ + cmp::max, + collections::{BTreeMap, VecDeque}, + sync::Arc, + time::Duration, +}; use tokio::sync::Mutex; use url::Url; @@ -37,6 +42,8 @@ pub mod events; pub mod handlers; pub mod traits; +pub type ClientMap = BTreeMap>>; + #[derive(Clone)] pub struct EthClient where @@ -192,11 +199,11 @@ where let mut content: TxpoolContent = self.txpool_content().await?; let pending = content.remove_from(&self.address()).pending; - let mut transactions = pending.into_iter().map(|(_, tx)| tx).collect::>(); - transactions.make_contiguous().sort_by(|a, b| a.nonce().cmp(&b.nonce())); + let mut transactions = pending.into_values().collect::>(); + transactions.make_contiguous().sort_by_key(|a| a.nonce()); while let Some(tx) = transactions.pop_front() { - if self.get_transaction_receipt(tx.tx_hash()).await.unwrap().is_some() { + if self.get_transaction_receipt(tx.tx_hash()).await?.is_some() { continue; } @@ -207,11 +214,11 @@ where // RBF if tx.is_legacy_gas() { let new_gas_price = ((tx_request.gas_price.unwrap() as f64) * 1.1).ceil() as u128; - let current_gas_price = self.get_gas_price().await.unwrap(); + let current_gas_price = self.get_gas_price().await?; tx_request.gas_price = Some(max(new_gas_price, current_gas_price)); } else { - let current_gas_price = self.estimate_eip1559_fees(None).await.unwrap(); + let current_gas_price = self.estimate_eip1559_fees(None).await?; let new_max_fee_per_gas = (tx_request.max_fee_per_gas.unwrap() as f64 * 1.1).ceil() as u128; @@ -374,8 +381,7 @@ pub mod retry { } } - /// [RetryPolicy] implements [RetryPolicyT] to determine whether to retry depending on the - /// err. + /// [RetryPolicy] implements [RetryPolicyT] to determine whether to retry depending on err. #[derive(Debug, Copy, Clone, Default)] #[non_exhaustive] pub struct RetryPolicy; @@ -387,7 +393,7 @@ pub mod retry { } /// Provides a backoff hint if the error response contains it - fn backoff_hint(&self, error: &TransportError) -> Option { + fn backoff_hint(&self, error: &TransportError) -> Option { if let RpcError::ErrorResp(resp) = error { let data = resp.try_data_as::(); if let Some(Ok(data)) = data { @@ -396,10 +402,10 @@ pub mod retry { let backoff_seconds = &data["rate"]["backoff_seconds"]; // infura rate limit error if let Some(seconds) = backoff_seconds.as_u64() { - return Some(std::time::Duration::from_secs(seconds)); + return Some(Duration::from_secs(seconds)); } if let Some(seconds) = backoff_seconds.as_f64() { - return Some(std::time::Duration::from_secs(seconds as u64 + 1)); + return Some(Duration::from_secs(seconds as u64 + 1)); } } } diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index 81157d4c..dd48f58e 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -122,7 +122,7 @@ where let signatures = client .protocol_contracts .socket - .get_signatures(msg.req_id.clone(), msg.status.clone()) + .get_signatures(msg.req_id.clone(), msg.status) .call() .await? ._0; diff --git a/client/src/substrate/traits.rs b/client/src/substrate/traits.rs index 3ed17fc3..c71029d3 100644 --- a/client/src/substrate/traits.rs +++ b/client/src/substrate/traits.rs @@ -84,7 +84,7 @@ where self.get_bfc_client().address(), msg.metadata, msg.retries_remaining - 1, - error.to_string(), + error, ); log::error!(target: &self.get_bfc_client().get_chain_name(), "{log_msg}"); sentry::capture_message( diff --git a/periodic/src/bitcoin_rollback_verifier.rs b/periodic/src/bitcoin_rollback_verifier.rs index 72c38e90..f071d1b1 100644 --- a/periodic/src/bitcoin_rollback_verifier.rs +++ b/periodic/src/bitcoin_rollback_verifier.rs @@ -3,10 +3,7 @@ use std::{collections::BTreeMap, str::FromStr, sync::Arc, time::Duration}; use alloy::{ network::AnyNetwork, primitives::{keccak256, Address as EvmAddress, Bytes, B256}, - providers::{ - fillers::{FillProvider, TxFiller}, - Provider, WalletProvider, - }, + providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, }; use bitcoincore_rpc::{ @@ -21,9 +18,7 @@ use br_primitives::{ schedule::BITCOIN_ROLLBACK_CHECK_SCHEDULE, tx::{DEFAULT_CALL_RETRIES, DEFAULT_CALL_RETRY_INTERVAL_MS}, }, - contracts::socket_queue::SocketQueueContract::{ - rollback_requestReturn, SocketQueueContractInstance, - }, + contracts::socket_queue::{SocketQueueContract::rollback_requestReturn, SocketQueueInstance}, substrate::{bifrost_runtime, AccountId20, EthereumSignature, RollbackPollMessage}, tx::{SubmitRollbackPollMetadata, XtRequest, XtRequestMetadata, XtRequestSender}, utils::sub_display_format, @@ -320,9 +315,7 @@ where } /// Get the `BtcSocketQueue` precompile contract instance. - fn socket_queue( - &self, - ) -> &SocketQueueContractInstance>, AnyNetwork> { + fn socket_queue(&self) -> &SocketQueueInstance { self.bfc_client.protocol_contracts.socket_queue.as_ref().unwrap() } } diff --git a/periodic/src/keypair_migrator.rs b/periodic/src/keypair_migrator.rs index f2a3f3c1..351aa231 100644 --- a/periodic/src/keypair_migrator.rs +++ b/periodic/src/keypair_migrator.rs @@ -171,36 +171,31 @@ where let mut write_lock = self.migration_sequence.write().await; match *write_lock { MigrationSequence::Normal | MigrationSequence::SetExecutiveMembers => { - match service_state { - ServiceState::PrepareNextSystemVault => { - self.keypair_storage - .write() - .await - .load(self.get_current_round().await + 1) - .await; - }, - _ => {}, + if service_state == ServiceState::PrepareNextSystemVault { + self.keypair_storage + .write() + .await + .load(self.get_current_round().await + 1) + .await; } }, - MigrationSequence::PrepareNextSystemVault => match service_state { - ServiceState::UTXOTransfer => { + MigrationSequence::PrepareNextSystemVault => { + if service_state == ServiceState::UTXOTransfer { self.keypair_storage .write() .await .load(self.get_current_round().await) .await; - }, - _ => {}, + } }, - MigrationSequence::UTXOTransfer => match service_state { - ServiceState::Normal => { + MigrationSequence::UTXOTransfer => { + if service_state == ServiceState::Normal { self.keypair_storage .write() .await .load(self.get_current_round().await) .await; - }, - _ => {}, + } }, } *write_lock = service_state; diff --git a/periodic/src/price_feeder.rs b/periodic/src/price_feeder.rs index 98713bdc..e7526413 100644 --- a/periodic/src/price_feeder.rs +++ b/periodic/src/price_feeder.rs @@ -2,7 +2,7 @@ use std::{collections::BTreeMap, fmt::Error, str::FromStr, sync::Arc}; use alloy::{ network::AnyNetwork, - primitives::{utils::parse_ether, ChainId, FixedBytes, B256, U256}, + primitives::{utils::parse_ether, FixedBytes, B256, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, rpc::types::{TransactionInput, TransactionRequest}, transports::Transport, @@ -15,7 +15,7 @@ use rand::Rng; use sc_service::SpawnTaskHandle; use tokio::time::sleep; -use br_client::eth::{send_transaction, EthClient}; +use br_client::eth::{send_transaction, ClientMap, EthClient}; use br_primitives::{ constants::{errors::INVALID_PERIODIC_SCHEDULE, schedule::PRICE_FEEDER_SCHEDULE}, contracts::socket::get_asset_oids, @@ -50,7 +50,7 @@ where /// The pre-defined oracle ID's for each asset. asset_oid: BTreeMap<&'static str, B256>, /// The vector that contains each `EthClient`. - clients: Arc>>>, + clients: Arc>, /// The handle to spawn tasks. handle: SpawnTaskHandle, } @@ -99,7 +99,7 @@ where { pub fn new( client: Arc>, - clients: Arc>>>, + clients: Arc>, handle: SpawnTaskHandle, ) -> Self { let asset_oid = get_asset_oids(); @@ -274,7 +274,7 @@ where let mut price_bytes_list: Vec> = vec![]; price_responses.iter().for_each(|(symbol, price_response)| { - oid_bytes_list.push(self.asset_oid.get(symbol.as_str()).unwrap().clone()); + oid_bytes_list.push(*self.asset_oid.get(symbol.as_str()).unwrap()); price_bytes_list.push(price_response.price.into()); }); diff --git a/periodic/src/pub_key_presubmitter.rs b/periodic/src/pub_key_presubmitter.rs index 8d3e8153..8dabc839 100644 --- a/periodic/src/pub_key_presubmitter.rs +++ b/periodic/src/pub_key_presubmitter.rs @@ -147,7 +147,7 @@ where async fn build_payload( &self, - pub_keys: &Vec, + pub_keys: &[PublicKey], ) -> Result<(VaultKeyPreSubmission, EthereumSignature)> { let converted_pub_keys = pub_keys .iter() @@ -167,7 +167,7 @@ where let signature = self .bfc_client .sign_message( - &format!( + format!( "{}:{}", pool_round, converted_pub_keys @@ -202,7 +202,7 @@ where sub_display_format(SUB_LOG_TARGET), self.bfc_client.address(), metadata, - error.to_string() + error ); log::error!(target: &self.bfc_client.get_chain_name(), "{log_msg}"); sentry::capture_message( diff --git a/periodic/src/pub_key_submitter.rs b/periodic/src/pub_key_submitter.rs index e7e47375..e3c9a6f8 100644 --- a/periodic/src/pub_key_submitter.rs +++ b/periodic/src/pub_key_submitter.rs @@ -3,17 +3,14 @@ use std::{str::FromStr, sync::Arc, time::Duration}; use alloy::{ network::AnyNetwork, primitives::{Address, Bytes}, - providers::{ - fillers::{FillProvider, TxFiller}, - Provider, WalletProvider, - }, + providers::{fillers::TxFiller, Provider, WalletProvider}, transports::Transport, }; use bitcoincore_rpc::bitcoin::PublicKey; use br_client::{btc::storage::keypair::KeypairStorage, eth::EthClient}; use br_primitives::{ constants::{errors::INVALID_PERIODIC_SCHEDULE, schedule::PUB_KEY_SUBMITTER_SCHEDULE}, - contracts::registration_pool::RegistrationPoolContract::RegistrationPoolContractInstance, + contracts::registration_pool::RegistrationPoolInstance, substrate::{ bifrost_runtime::{ self, btc_registration_pool::storage::types::service_state::ServiceState, @@ -165,7 +162,7 @@ where let signature = self .client .sign_message( - &format!("{}:{}", pool_round, array_bytes::bytes2hex("0x", converted_pub_key)) + format!("{}:{}", pool_round, array_bytes::bytes2hex("0x", converted_pub_key)) .as_bytes(), ) .await? @@ -220,7 +217,7 @@ where sub_display_format(SUB_LOG_TARGET), self.client.address(), metadata, - error.to_string() + error ); log::error!(target: &self.client.get_chain_name(), "{log_msg}"); sentry::capture_message( @@ -245,9 +242,7 @@ where Ok(self.registration_pool().registration_info(who, round).call().await?.into()) } - fn registration_pool( - &self, - ) -> &RegistrationPoolContractInstance>, AnyNetwork> { + fn registration_pool(&self) -> &RegistrationPoolInstance { self.client.protocol_contracts.registration_pool.as_ref().unwrap() } diff --git a/periodic/src/socket_rollback_emitter.rs b/periodic/src/socket_rollback_emitter.rs index 8ad14e95..4b00e7c7 100644 --- a/periodic/src/socket_rollback_emitter.rs +++ b/periodic/src/socket_rollback_emitter.rs @@ -12,7 +12,7 @@ use sc_service::SpawnTaskHandle; use std::{collections::BTreeMap, str::FromStr, sync::Arc}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use br_client::eth::{send_transaction, traits::SocketRelayBuilder, EthClient}; +use br_client::eth::{send_transaction, traits::SocketRelayBuilder, ClientMap, EthClient}; use br_primitives::{ constants::{ errors::{INVALID_BIFROST_NATIVENESS, INVALID_PERIODIC_SCHEDULE}, @@ -41,7 +41,7 @@ where /// The `EthClient` to interact with the connected blockchain. pub client: Arc>, /// The entire clients instantiated in the system. > - system_clients: Arc>>>, + system_clients: Arc>, /// The receiver connected to the socket rollback channel. rollback_receiver: UnboundedReceiver, /// The local storage saving emitted `Socket` event messages. @@ -61,7 +61,7 @@ where /// Instantiates a new `SocketRollbackEmitter`. pub fn new( client: Arc>, - system_clients: Arc>>>, + system_clients: Arc>, handle: SpawnTaskHandle, ) -> (Self, Arc>) { let (sender, rollback_receiver) = mpsc::unbounded_channel::(); diff --git a/primitives/src/contracts/authority.rs b/primitives/src/contracts/authority.rs index 62ff442f..68b1b1e7 100644 --- a/primitives/src/contracts/authority.rs +++ b/primitives/src/contracts/authority.rs @@ -1,4 +1,4 @@ -use alloy::sol; +use super::*; sol!( #[allow(missing_docs)] @@ -7,3 +7,8 @@ sol!( AuthorityContract, "../abi/abi.authority.merged.json" ); + +use AuthorityContract::AuthorityContractInstance; + +pub type AuthorityInstance = + AuthorityContractInstance>, AnyNetwork>; diff --git a/primitives/src/contracts/bitcoin_socket.rs b/primitives/src/contracts/bitcoin_socket.rs index 8f0c3f87..415f45a1 100644 --- a/primitives/src/contracts/bitcoin_socket.rs +++ b/primitives/src/contracts/bitcoin_socket.rs @@ -1,4 +1,4 @@ -use alloy::sol; +use super::*; sol!( #[allow(missing_docs)] @@ -7,3 +7,8 @@ sol!( BitcoinSocketContract, "../abi/abi.socket.bitcoin.json" ); + +use BitcoinSocketContract::BitcoinSocketContractInstance; + +pub type BitcoinSocketInstance = + BitcoinSocketContractInstance>, AnyNetwork>; diff --git a/primitives/src/contracts/chainlink_aggregator.rs b/primitives/src/contracts/chainlink_aggregator.rs index ef9ac14e..4304c94b 100644 --- a/primitives/src/contracts/chainlink_aggregator.rs +++ b/primitives/src/contracts/chainlink_aggregator.rs @@ -1,4 +1,4 @@ -use alloy::sol; +use super::*; sol!( #[allow(missing_docs)] @@ -7,3 +7,8 @@ sol!( ChainlinkContract, "../abi/abi.aggregatorv3.chainlink.json" ); + +use ChainlinkContract::ChainlinkContractInstance; + +pub type ChainlinkInstance = + ChainlinkContractInstance>, AnyNetwork>; diff --git a/primitives/src/contracts/mod.rs b/primitives/src/contracts/mod.rs index 5d0d0f4f..3556f06a 100644 --- a/primitives/src/contracts/mod.rs +++ b/primitives/src/contracts/mod.rs @@ -6,3 +6,11 @@ pub mod relay_executive; pub mod relayer_manager; pub mod socket; pub mod socket_queue; + +use alloy::{ + network::AnyNetwork, + primitives::{b256, Bytes, PrimitiveSignature, B256}, + providers::fillers::FillProvider, + sol, +}; +use std::{collections::BTreeMap, sync::Arc}; diff --git a/primitives/src/contracts/registration_pool.rs b/primitives/src/contracts/registration_pool.rs index 3c0b9e4d..4f5104da 100644 --- a/primitives/src/contracts/registration_pool.rs +++ b/primitives/src/contracts/registration_pool.rs @@ -1,4 +1,4 @@ -use alloy::sol; +use super::*; sol!( #[allow(missing_docs)] @@ -7,3 +7,8 @@ sol!( RegistrationPoolContract, "../abi/abi.registration_pool.bifrost.json" ); + +use RegistrationPoolContract::RegistrationPoolContractInstance; + +pub type RegistrationPoolInstance = + RegistrationPoolContractInstance>, AnyNetwork>; diff --git a/primitives/src/contracts/relay_executive.rs b/primitives/src/contracts/relay_executive.rs index 4d8797c2..1257b966 100644 --- a/primitives/src/contracts/relay_executive.rs +++ b/primitives/src/contracts/relay_executive.rs @@ -1,4 +1,4 @@ -use alloy::sol; +use super::*; sol!( #[allow(missing_docs)] @@ -7,3 +7,8 @@ sol!( RelayExecutiveContract, "../abi/abi.relay_executive.bifrost.json" ); + +use RelayExecutiveContract::RelayExecutiveContractInstance; + +pub type RelayExecutiveInstance = + RelayExecutiveContractInstance>, AnyNetwork>; diff --git a/primitives/src/contracts/relayer_manager.rs b/primitives/src/contracts/relayer_manager.rs index 4da028e2..24bd7a6e 100644 --- a/primitives/src/contracts/relayer_manager.rs +++ b/primitives/src/contracts/relayer_manager.rs @@ -1,4 +1,4 @@ -use alloy::sol; +use super::*; sol!( #[allow(missing_docs)] @@ -7,3 +7,8 @@ sol!( RelayerManagerContract, "../abi/abi.relayer.bifrost.json" ); + +use RelayerManagerContract::RelayerManagerContractInstance; + +pub type RelayerManagerInstance = + RelayerManagerContractInstance>, AnyNetwork>; diff --git a/primitives/src/contracts/socket.rs b/primitives/src/contracts/socket.rs index c125c10a..35262cb6 100644 --- a/primitives/src/contracts/socket.rs +++ b/primitives/src/contracts/socket.rs @@ -1,8 +1,4 @@ -use alloy::{ - primitives::{b256, Bytes, PrimitiveSignature, B256}, - sol, -}; -use std::collections::BTreeMap; +use super::*; use Socket_Struct::*; pub fn get_asset_oids() -> BTreeMap<&'static str, B256> { @@ -71,3 +67,8 @@ impl From for Vec { res } } + +use SocketContract::SocketContractInstance; + +pub type SocketInstance = + SocketContractInstance>, AnyNetwork>; diff --git a/primitives/src/contracts/socket_queue.rs b/primitives/src/contracts/socket_queue.rs index d55e60f7..db8bcf61 100644 --- a/primitives/src/contracts/socket_queue.rs +++ b/primitives/src/contracts/socket_queue.rs @@ -1,4 +1,4 @@ -use alloy::sol; +use super::*; sol!( #[allow(missing_docs)] @@ -7,3 +7,8 @@ sol!( SocketQueueContract, "../abi/abi.socket_queue.bifrost.json" ); + +use SocketQueueContract::SocketQueueContractInstance; + +pub type SocketQueueInstance = + SocketQueueContractInstance>, AnyNetwork>; diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index 62a977d3..5a56808e 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -13,16 +13,20 @@ use alloy::{ use url::Url; use crate::{ - constants::errors::{INVALID_CONTRACT_ADDRESS, MISSING_CONTRACT_ADDRESS}, + cli::EVMProvider, + constants::{ + cli::DEFAULT_GET_LOGS_BATCH_SIZE, + errors::{INVALID_CONTRACT_ADDRESS, MISSING_CONTRACT_ADDRESS}, + }, contracts::{ - authority::AuthorityContract::{self, AuthorityContractInstance}, - bitcoin_socket::BitcoinSocketContract::{self, BitcoinSocketContractInstance}, - chainlink_aggregator::ChainlinkContract::{self, ChainlinkContractInstance}, - registration_pool::RegistrationPoolContract::{self, RegistrationPoolContractInstance}, - relay_executive::RelayExecutiveContract::{self, RelayExecutiveContractInstance}, - relayer_manager::RelayerManagerContract::{self, RelayerManagerContractInstance}, - socket::SocketContract::{self, SocketContractInstance}, - socket_queue::SocketQueueContract::{self, SocketQueueContractInstance}, + authority::{AuthorityContract, AuthorityInstance}, + bitcoin_socket::{BitcoinSocketContract, BitcoinSocketInstance}, + chainlink_aggregator::{ChainlinkContract, ChainlinkInstance}, + registration_pool::{RegistrationPoolContract, RegistrationPoolInstance}, + relay_executive::{RelayExecutiveContract, RelayExecutiveInstance}, + relayer_manager::{RelayerManagerContract, RelayerManagerInstance}, + socket::{SocketContract, SocketInstance}, + socket_queue::{SocketQueueContract, SocketQueueInstance}, }, }; @@ -54,25 +58,24 @@ pub struct ProviderMetadata { impl ProviderMetadata { pub fn new( - name: String, + evm_provider: EVMProvider, url: Url, - id: ChainId, bitcoin_chain_id: Option, - block_confirmations: u64, - call_interval: u64, - eip1559: bool, - get_logs_batch_size: u64, is_native: bool, ) -> Self { + let get_logs_batch_size = + evm_provider.get_logs_batch_size.unwrap_or(DEFAULT_GET_LOGS_BATCH_SIZE); Self { - name, + name: evm_provider.name, url, - id, + id: evm_provider.id, bitcoin_chain_id, - block_confirmations: block_confirmations.saturating_add(get_logs_batch_size), + block_confirmations: evm_provider + .block_confirmations + .saturating_add(get_logs_batch_size), get_logs_batch_size, - call_interval, - eip1559, + call_interval: evm_provider.call_interval, + eip1559: evm_provider.eip1559.unwrap_or(false), is_native, if_destination_chain: match is_native { true => RelayDirection::Inbound, @@ -90,23 +93,17 @@ where T: Transport + Clone, { /// Chainlink usdc/usd aggregator - pub chainlink_usdc_usd: - Option>, AnyNetwork>>, + pub chainlink_usdc_usd: Option>, /// Chainlink usdt/usd aggregator - pub chainlink_usdt_usd: - Option>, AnyNetwork>>, + pub chainlink_usdt_usd: Option>, /// Chainlink dai/usd aggregator - pub chainlink_dai_usd: - Option>, AnyNetwork>>, + pub chainlink_dai_usd: Option>, /// Chainlink btc/usd aggregator - pub chainlink_btc_usd: - Option>, AnyNetwork>>, + pub chainlink_btc_usd: Option>, /// Chainlink wbtc/usd aggregator - pub chainlink_wbtc_usd: - Option>, AnyNetwork>>, + pub chainlink_wbtc_usd: Option>, /// Chainlink cbbtc/usd aggregator - pub chainlink_cbbtc_usd: - Option>, AnyNetwork>>, + pub chainlink_cbbtc_usd: Option>, } impl AggregatorContracts @@ -169,28 +166,19 @@ where T: Transport + Clone, { /// SocketContract - pub socket: SocketContractInstance>, AnyNetwork>, + pub socket: SocketInstance, /// AuthorityContract - pub authority: AuthorityContractInstance>, AnyNetwork>, + pub authority: AuthorityInstance, /// RelayerManagerContract (Bifrost only) - pub relayer_manager: Option< - RelayerManagerContractInstance>, AnyNetwork>, - >, + pub relayer_manager: Option>, /// BitcoinSocketContract (Bifrost only) - pub bitcoin_socket: Option< - BitcoinSocketContractInstance>, AnyNetwork>, - >, + pub bitcoin_socket: Option>, /// SocketQueueContract (Bifrost only) - pub socket_queue: - Option>, AnyNetwork>>, + pub socket_queue: Option>, /// RegistrationPoolContract (Bifrost only) - pub registration_pool: Option< - RegistrationPoolContractInstance>, AnyNetwork>, - >, + pub registration_pool: Option>, /// RelayExecutiveContract (Bifrost only) - pub relay_executive: Option< - RelayExecutiveContractInstance>, AnyNetwork>, - >, + pub relay_executive: Option>, } impl ProtocolContracts @@ -202,21 +190,15 @@ where pub fn new( is_native: bool, provider: Arc>, - socket_address: String, - authority_address: String, - relayer_manager_address: Option, - bitcoin_socket_address: Option, - socket_queue_address: Option, - registration_pool_address: Option, - relay_executive_address: Option, + evm_provider: EVMProvider, ) -> Self { let mut contracts = Self { socket: SocketContract::new( - Address::from_str(&socket_address).expect(INVALID_CONTRACT_ADDRESS), + Address::from_str(&evm_provider.socket_address).expect(INVALID_CONTRACT_ADDRESS), provider.clone(), ), authority: AuthorityContract::new( - Address::from_str(&authority_address).expect(INVALID_CONTRACT_ADDRESS), + Address::from_str(&evm_provider.authority_address).expect(INVALID_CONTRACT_ADDRESS), provider.clone(), ), relayer_manager: None, @@ -227,28 +209,38 @@ where }; if is_native { contracts.relayer_manager = Some(RelayerManagerContract::new( - Address::from_str(&relayer_manager_address.expect(MISSING_CONTRACT_ADDRESS)) - .expect(INVALID_CONTRACT_ADDRESS), + Address::from_str( + &evm_provider.relayer_manager_address.expect(MISSING_CONTRACT_ADDRESS), + ) + .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); contracts.bitcoin_socket = Some(BitcoinSocketContract::new( - Address::from_str(&bitcoin_socket_address.expect(MISSING_CONTRACT_ADDRESS)) - .expect(INVALID_CONTRACT_ADDRESS), + Address::from_str( + &evm_provider.bitcoin_socket_address.expect(MISSING_CONTRACT_ADDRESS), + ) + .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); contracts.socket_queue = Some(SocketQueueContract::new( - Address::from_str(&socket_queue_address.expect(MISSING_CONTRACT_ADDRESS)) - .expect(INVALID_CONTRACT_ADDRESS), + Address::from_str( + &evm_provider.socket_queue_address.expect(MISSING_CONTRACT_ADDRESS), + ) + .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); contracts.registration_pool = Some(RegistrationPoolContract::new( - Address::from_str(®istration_pool_address.expect(MISSING_CONTRACT_ADDRESS)) - .expect(INVALID_CONTRACT_ADDRESS), + Address::from_str( + &evm_provider.registration_pool_address.expect(MISSING_CONTRACT_ADDRESS), + ) + .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); contracts.relay_executive = Some(RelayExecutiveContract::new( - Address::from_str(&relay_executive_address.expect(MISSING_CONTRACT_ADDRESS)) - .expect(INVALID_CONTRACT_ADDRESS), + Address::from_str( + &evm_provider.relay_executive_address.expect(MISSING_CONTRACT_ADDRESS), + ) + .expect(INVALID_CONTRACT_ADDRESS), provider.clone(), )); } diff --git a/primitives/src/tx.rs b/primitives/src/tx.rs index 96caad53..0260e44d 100644 --- a/primitives/src/tx.rs +++ b/primitives/src/tx.rs @@ -297,7 +297,7 @@ impl SubmitVaultKeyMetadata { impl Display for SubmitVaultKeyMetadata { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "SubmitVaultKey({:?}:{})", self.who, self.key.to_string()) + write!(f, "SubmitVaultKey({:?}:{})", self.who, self.key) } } diff --git a/relayer/src/cli.rs b/relayer/src/cli.rs index a3876a64..ce6f37e0 100644 --- a/relayer/src/cli.rs +++ b/relayer/src/cli.rs @@ -111,11 +111,11 @@ impl Cli { /// Chain spec factory pub fn load_spec(&self) -> &str { - return match self.chain.as_str() { + match self.chain.as_str() { "testnet" => TESTNET_CONFIG_FILE_PATH, "mainnet" => MAINNET_CONFIG_FILE_PATH, path => path, - }; + } } /// Log information about the relayer itself. diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 284fe98d..dd5080b5 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -20,10 +20,7 @@ use sc_service::{config::PrometheusConfig, Error as ServiceError, TaskManager}; use tokio::sync::RwLock; use br_client::{ - btc::{ - handlers::Handler as _, - storage::{keypair::KeypairStorage, pending_outbound::PendingOutboundPool}, - }, + btc::{handlers::Handler as _, storage::keypair::KeypairStorage}, eth::{retry::RetryBackoffLayer, traits::Handler as _, EthClient}, }; use br_periodic::traits::PeriodicWorker; @@ -31,7 +28,7 @@ use br_primitives::{ bootstrap::BootstrapSharedData, cli::{Configuration, HandlerType}, constants::{ - cli::{DEFAULT_GET_LOGS_BATCH_SIZE, DEFAULT_KEYSTORE_PATH, DEFAULT_PROMETHEUS_PORT}, + cli::{DEFAULT_KEYSTORE_PATH, DEFAULT_PROMETHEUS_PORT}, errors::{INVALID_BITCOIN_NETWORK, INVALID_PRIVATE_KEY, INVALID_PROVIDER_URL}, tx::DEFAULT_CALL_RETRIES, }, @@ -86,27 +83,12 @@ pub fn relay(config: Configuration) -> Result { provider.clone(), signer.clone(), ProviderMetadata::new( - evm_provider.name.clone(), + evm_provider.clone(), url.clone(), - evm_provider.id, if is_native { Some(btc_provider.id) } else { None }, - evm_provider.block_confirmations, - evm_provider.call_interval, - evm_provider.eip1559.unwrap_or(false), - evm_provider.get_logs_batch_size.unwrap_or(DEFAULT_GET_LOGS_BATCH_SIZE), is_native, ), - ProtocolContracts::new( - is_native, - provider.clone(), - evm_provider.socket_address.clone(), - evm_provider.authority_address.clone(), - evm_provider.relayer_manager_address.clone(), - evm_provider.bitcoin_socket_address.clone(), - evm_provider.socket_queue_address.clone(), - evm_provider.registration_pool_address.clone(), - evm_provider.relay_executive_address.clone(), - ), + ProtocolContracts::new(is_native, provider.clone(), evm_provider.clone()), AggregatorContracts::new( provider.clone(), evm_provider.chainlink_usdc_usd_address.clone(), @@ -123,7 +105,6 @@ pub fn relay(config: Configuration) -> Result { let bootstrap_shared_data = BootstrapSharedData::new(&config); - let pending_outbounds = PendingOutboundPool::new(); let keypair_storage = Arc::new(RwLock::new(KeypairStorage::new( config .clone() @@ -161,7 +142,6 @@ pub fn relay(config: Configuration) -> Result { ); let btc_deps = BtcDeps::new( &config, - pending_outbounds.clone(), keypair_storage.clone(), bootstrap_shared_data.clone(), &substrate_deps, diff --git a/relayer/src/service_deps/btc_deps.rs b/relayer/src/service_deps/btc_deps.rs index 5bf657aa..b33057c9 100644 --- a/relayer/src/service_deps/btc_deps.rs +++ b/relayer/src/service_deps/btc_deps.rs @@ -28,7 +28,6 @@ where { pub fn new( config: &Configuration, - pending_outbounds: PendingOutboundPool, keypair_storage: Arc>, bootstrap_shared_data: BootstrapSharedData, substrate_deps: &SubstrateDeps, @@ -58,9 +57,8 @@ where let block_manager = BlockManager::new( btc_client.clone(), bfc_client.clone(), - pending_outbounds.clone(), bootstrap_shared_data.clone(), - config.relayer_config.btc_provider.call_interval.clone(), + config.relayer_config.btc_provider.call_interval, config .relayer_config .btc_provider diff --git a/relayer/src/service_deps/manager_deps.rs b/relayer/src/service_deps/manager_deps.rs index e85d0342..965a8298 100644 --- a/relayer/src/service_deps/manager_deps.rs +++ b/relayer/src/service_deps/manager_deps.rs @@ -1,3 +1,5 @@ +use br_client::eth::ClientMap; + use super::*; pub struct ManagerDeps @@ -9,7 +11,7 @@ where /// Bifrost client pub bifrost_client: Arc>, /// The `EthClient`'s for each specified chain. - pub clients: Arc>>>, + pub clients: Arc>, /// The `EventManager`'s for each specified chain. pub event_managers: BTreeMap>, } @@ -23,7 +25,7 @@ where /// Initializes the `EthClient`'s and `EventManager`'s for each chain. pub fn new( config: &Configuration, - clients: Arc>>>, + clients: Arc>, bootstrap_shared_data: BootstrapSharedData, ) -> Self { let prometheus_config = &config.relayer_config.prometheus_config; diff --git a/relayer/src/service_deps/mod.rs b/relayer/src/service_deps/mod.rs index 93e4eb23..a4bc5a6d 100644 --- a/relayer/src/service_deps/mod.rs +++ b/relayer/src/service_deps/mod.rs @@ -23,7 +23,7 @@ use br_client::{ btc::{ block::BlockManager, handlers::{InboundHandler, OutboundHandler}, - storage::{keypair::KeypairStorage, pending_outbound::PendingOutboundPool}, + storage::keypair::KeypairStorage, }, eth::{ events::EventManager, diff --git a/relayer/src/service_deps/periodic_deps.rs b/relayer/src/service_deps/periodic_deps.rs index d8f83da3..2aa52bae 100644 --- a/relayer/src/service_deps/periodic_deps.rs +++ b/relayer/src/service_deps/periodic_deps.rs @@ -1,3 +1,5 @@ +use br_client::eth::ClientMap; + use super::*; pub struct PeriodicDeps @@ -33,7 +35,7 @@ where migration_sequence: Arc>, keypair_storage: Arc>, substrate_deps: &SubstrateDeps, - clients: Arc>>>, + clients: Arc>, bfc_client: Arc>, task_manager: &TaskManager, ) -> Self { From 7e8551ffbc0b9d1fbfb11aa306ca8ed9a6cb22d9 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Mon, 16 Dec 2024 13:41:29 +0900 Subject: [PATCH 30/60] CCCP-295, chore: debug_mode --- client/src/btc/handlers/inbound.rs | 5 ++++ client/src/btc/handlers/outbound.rs | 5 ++++ .../src/eth/handlers/roundup_relay_handler.rs | 5 ++++ .../src/eth/handlers/socket_relay_handler.rs | 5 ++++ client/src/eth/mod.rs | 23 +++++++++++-------- periodic/src/heartbeat_sender.rs | 6 ++++- periodic/src/price_feeder.rs | 5 ++++ periodic/src/roundup_emitter.rs | 5 ++++ periodic/src/socket_rollback_emitter.rs | 5 ++++ relayer/src/service.rs | 3 +++ relayer/src/service_deps/btc_deps.rs | 3 +++ relayer/src/service_deps/handler_deps.rs | 3 +++ relayer/src/service_deps/periodic_deps.rs | 6 ++++- 13 files changed, 67 insertions(+), 12 deletions(-) diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index e74d81f6..40665911 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -49,6 +49,8 @@ where bootstrap_shared_data: Arc, /// The handle to spawn tasks. handle: SpawnTaskHandle, + /// Whether to enable debug mode. + debug_mode: bool, } impl InboundHandler @@ -62,6 +64,7 @@ where event_receiver: Receiver, bootstrap_shared_data: Arc, handle: SpawnTaskHandle, + debug_mode: bool, ) -> Self { Self { bfc_client, @@ -69,6 +72,7 @@ where target_event: EventType::Inbound, bootstrap_shared_data, handle, + debug_mode, } } @@ -211,6 +215,7 @@ where tx_request, format!("{} ({})", SUB_LOG_TARGET, self.bfc_client.get_chain_name()), TxRequestMetadata::BitcoinSocketRelay(metadata), + self.debug_mode, self.handle.clone(), ); } diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index ad0d5740..8f4d8a54 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -48,6 +48,8 @@ where bootstrap_shared_data: Arc, /// The handle to spawn tasks. handle: SpawnTaskHandle, + /// Whether to enable debug mode. + debug_mode: bool, } impl OutboundHandler @@ -61,6 +63,7 @@ where event_receiver: Receiver, bootstrap_shared_data: Arc, handle: SpawnTaskHandle, + debug_mode: bool, ) -> Self { Self { bfc_client, @@ -68,6 +71,7 @@ where target_event: EventType::Outbound, bootstrap_shared_data, handle, + debug_mode, } } @@ -146,6 +150,7 @@ where msg.params.to, false, )), + self.debug_mode, self.handle.clone(), ); } diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 385e8109..8902a028 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -56,6 +56,8 @@ where bootstrap_shared_data: Arc, /// The handle to spawn tasks. handle: SpawnTaskHandle, + /// Whether to enable debug mode. + debug_mode: bool, } #[async_trait] @@ -177,6 +179,7 @@ where clients: Arc>, bootstrap_shared_data: Arc, handle: SpawnTaskHandle, + debug_mode: bool, ) -> Self { Self { event_receiver, @@ -185,6 +188,7 @@ where roundup_signature: RoundUp::SIGNATURE_HASH, bootstrap_shared_data, handle, + debug_mode, } } @@ -285,6 +289,7 @@ where roundup_submit.round, *dst_chain_id, )), + self.debug_mode, self.handle.clone(), ); } diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index 665cc18f..d94674b7 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -64,6 +64,8 @@ where socket_signature: B256, /// The bootstrap shared data. bootstrap_shared_data: Arc, + /// Whether to enable debug mode. + debug_mode: bool, } #[async_trait::async_trait] @@ -269,6 +271,7 @@ where rollback_senders: Arc>>>, handle: SpawnTaskHandle, bootstrap_shared_data: Arc, + debug_mode: bool, ) -> Self { let client = system_clients.get(&id).expect(INVALID_CHAIN_ID).clone(); @@ -281,6 +284,7 @@ where rollback_senders, handle, bootstrap_shared_data, + debug_mode, } } @@ -445,6 +449,7 @@ where tx_request, format!("{} ({})", SUB_LOG_TARGET, self.client.get_chain_name()), metadata, + self.debug_mode, self.handle.clone(), ); } diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 6357582c..47e6ab3c 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -270,6 +270,7 @@ pub fn send_transaction( mut request: TransactionRequest, requester: String, metadata: TxRequestMetadata, + debug_mode: bool, handle: SpawnTaskHandle, ) where F: TxFiller + WalletProvider + 'static, @@ -287,15 +288,17 @@ pub fn send_transaction( request.gas = Some(estimated_gas.ceil() as u64); }, Err(err) => { - let msg = format!( - " ❗️ Failed to estimate gas ({} address:{}): {}, Error: {}", - client.get_chain_name(), - client.address(), - metadata, - err - ); - log::error!(target: &requester, "{msg}"); - sentry::capture_message(&msg, sentry::Level::Error); + if debug_mode { + let msg = format!( + " ❗️ Failed to estimate gas ({} address:{}): {}, Error: {}", + client.get_chain_name(), + client.address(), + metadata, + err + ); + log::error!(target: &requester, "{msg}"); + sentry::capture_message(&msg, sentry::Level::Error); + } return; }, }; @@ -336,7 +339,7 @@ pub fn send_transaction( if err.to_string().to_lowercase().contains("nonce too low") { client.flush_stalled_transactions().await.unwrap(); - send_transaction(client, request, requester, metadata, handle); + send_transaction(client, request, requester, metadata, debug_mode, handle); } }, } diff --git a/periodic/src/heartbeat_sender.rs b/periodic/src/heartbeat_sender.rs index b237de6e..9712f8fc 100644 --- a/periodic/src/heartbeat_sender.rs +++ b/periodic/src/heartbeat_sender.rs @@ -31,6 +31,8 @@ where pub client: Arc>, /// The handle to spawn tasks. handle: SpawnTaskHandle, + /// Whether to enable debug mode. + debug_mode: bool, } #[async_trait::async_trait] @@ -76,11 +78,12 @@ where T: Transport + Clone, { /// Instantiates a new `HeartbeatSender` instance. - pub fn new(client: Arc>, handle: SpawnTaskHandle) -> Self { + pub fn new(client: Arc>, handle: SpawnTaskHandle, debug_mode: bool) -> Self { Self { schedule: Schedule::from_str(HEARTBEAT_SCHEDULE).expect(INVALID_PERIODIC_SCHEDULE), client, handle, + debug_mode, } } @@ -103,6 +106,7 @@ where tx_request, SUB_LOG_TARGET.to_string(), TxRequestMetadata::Heartbeat(metadata), + self.debug_mode, self.handle.clone(), ); } diff --git a/periodic/src/price_feeder.rs b/periodic/src/price_feeder.rs index e7526413..24bec969 100644 --- a/periodic/src/price_feeder.rs +++ b/periodic/src/price_feeder.rs @@ -53,6 +53,8 @@ where clients: Arc>, /// The handle to spawn tasks. handle: SpawnTaskHandle, + /// Whether to enable debug mode. + debug_mode: bool, } #[async_trait] @@ -101,6 +103,7 @@ where client: Arc>, clients: Arc>, handle: SpawnTaskHandle, + debug_mode: bool, ) -> Self { let asset_oid = get_asset_oids(); @@ -112,6 +115,7 @@ where client, clients, handle, + debug_mode, } } @@ -315,6 +319,7 @@ where tx_request, SUB_LOG_TARGET.to_string(), TxRequestMetadata::PriceFeed(metadata), + self.debug_mode, self.handle.clone(), ); } diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index f0300a2f..d54c2a29 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -49,6 +49,8 @@ where bootstrap_shared_data: Arc, /// The handle to spawn tasks. handle: SpawnTaskHandle, + /// Whether to enable debug mode. + debug_mode: bool, } #[async_trait::async_trait] @@ -117,6 +119,7 @@ where client: Arc>, bootstrap_shared_data: Arc, handle: SpawnTaskHandle, + debug_mode: bool, ) -> Self { Self { current_round: U256::default(), @@ -125,6 +128,7 @@ where .expect(INVALID_PERIODIC_SCHEDULE), bootstrap_shared_data, handle, + debug_mode, } } @@ -188,6 +192,7 @@ where tx_request, SUB_LOG_TARGET.to_string(), TxRequestMetadata::VSPPhase1(metadata), + self.debug_mode, self.handle.clone(), ); } diff --git a/periodic/src/socket_rollback_emitter.rs b/periodic/src/socket_rollback_emitter.rs index 4b00e7c7..a1ae2221 100644 --- a/periodic/src/socket_rollback_emitter.rs +++ b/periodic/src/socket_rollback_emitter.rs @@ -50,6 +50,8 @@ where schedule: Schedule, /// The handle to spawn tasks. handle: SpawnTaskHandle, + /// Whether to enable debug mode. + debug_mode: bool, } impl SocketRollbackEmitter @@ -63,6 +65,7 @@ where client: Arc>, system_clients: Arc>, handle: SpawnTaskHandle, + debug_mode: bool, ) -> (Self, Arc>) { let (sender, rollback_receiver) = mpsc::unbounded_channel::(); @@ -75,6 +78,7 @@ where schedule: Schedule::from_str(ROLLBACK_CHECK_SCHEDULE) .expect(INVALID_PERIODIC_SCHEDULE), handle, + debug_mode, }, Arc::new(sender), ) @@ -251,6 +255,7 @@ where tx_request, SUB_LOG_TARGET.to_string(), TxRequestMetadata::Rollback(metadata), + self.debug_mode, self.handle.clone(), ); } diff --git a/relayer/src/service.rs b/relayer/src/service.rs index dd5080b5..5f6ee6b6 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -131,6 +131,7 @@ pub fn relay(config: Configuration) -> Result { manager_deps.clients.clone(), bfc_client.clone(), &task_manager, + system.debug_mode.unwrap_or(false), ); let handler_deps = HandlerDeps::new( &config, @@ -139,6 +140,7 @@ pub fn relay(config: Configuration) -> Result { bfc_client.clone(), periodic_deps.rollback_senders.clone(), &task_manager, + system.debug_mode.unwrap_or(false), ); let btc_deps = BtcDeps::new( &config, @@ -148,6 +150,7 @@ pub fn relay(config: Configuration) -> Result { migration_sequence.clone(), bfc_client.clone(), &task_manager, + system.debug_mode.unwrap_or(false), ); print_relay_targets(&manager_deps); diff --git a/relayer/src/service_deps/btc_deps.rs b/relayer/src/service_deps/btc_deps.rs index b33057c9..adb917a2 100644 --- a/relayer/src/service_deps/btc_deps.rs +++ b/relayer/src/service_deps/btc_deps.rs @@ -34,6 +34,7 @@ where migration_sequence: Arc>, bfc_client: Arc>, task_manager: &TaskManager, + debug_mode: bool, ) -> Self { let bootstrap_shared_data = Arc::new(bootstrap_shared_data.clone()); let network = Network::from_core_arg(&config.relayer_config.btc_provider.chain) @@ -70,12 +71,14 @@ where block_manager.subscribe(), bootstrap_shared_data.clone(), task_manager.spawn_handle(), + debug_mode, ); let outbound = OutboundHandler::new( bfc_client.clone(), block_manager.subscribe(), bootstrap_shared_data.clone(), task_manager.spawn_handle(), + debug_mode, ); let psbt_signer = PsbtSigner::new( diff --git a/relayer/src/service_deps/handler_deps.rs b/relayer/src/service_deps/handler_deps.rs index 70b7fcb3..19ba9ccf 100644 --- a/relayer/src/service_deps/handler_deps.rs +++ b/relayer/src/service_deps/handler_deps.rs @@ -25,6 +25,7 @@ where bfc_client: Arc>, rollback_senders: Arc>>>, task_manager: &TaskManager, + debug_mode: bool, ) -> Self { let mut handlers = (vec![], vec![]); let ManagerDeps { bifrost_client, clients, event_managers } = manager_deps; @@ -40,6 +41,7 @@ where rollback_senders.clone(), task_manager.spawn_handle(), Arc::new(bootstrap_shared_data.clone()), + debug_mode, )); }), HandlerType::Roundup => { @@ -53,6 +55,7 @@ where clients.clone(), Arc::new(bootstrap_shared_data.clone()), task_manager.spawn_handle(), + debug_mode, )); }, }, diff --git a/relayer/src/service_deps/periodic_deps.rs b/relayer/src/service_deps/periodic_deps.rs index 2aa52bae..2674314a 100644 --- a/relayer/src/service_deps/periodic_deps.rs +++ b/relayer/src/service_deps/periodic_deps.rs @@ -38,16 +38,18 @@ where clients: Arc>, bfc_client: Arc>, task_manager: &TaskManager, + debug_mode: bool, ) -> Self { // initialize the heartbeat sender let heartbeat_sender = - HeartbeatSender::new(bfc_client.clone(), task_manager.spawn_handle()); + HeartbeatSender::new(bfc_client.clone(), task_manager.spawn_handle(), debug_mode); // initialize the oracle price feeder let oracle_price_feeder = OraclePriceFeeder::new( bfc_client.clone(), clients.clone(), task_manager.spawn_handle(), + debug_mode, ); // initialize the roundup emitter @@ -55,6 +57,7 @@ where bfc_client.clone(), Arc::new(bootstrap_shared_data.clone()), task_manager.spawn_handle(), + debug_mode, ); let mut rollback_emitters = vec![]; @@ -64,6 +67,7 @@ where client.clone(), clients.clone(), task_manager.spawn_handle(), + debug_mode, ); rollback_emitters.push(rollback_emitter); rollback_senders.insert(*chain_id, rollback_sender); From f31e397e460f31c7d647c4baa4bc70c790da63ac Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 17 Dec 2024 11:20:47 +0900 Subject: [PATCH 31/60] feat: rollback barrier count --- primitives/src/bootstrap.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/primitives/src/bootstrap.rs b/primitives/src/bootstrap.rs index ea6201e0..826610c1 100644 --- a/primitives/src/bootstrap.rs +++ b/primitives/src/bootstrap.rs @@ -50,11 +50,7 @@ impl BootstrapSharedData { // 1. One for the roundup handler for each external chain // 2. One for the roundup emitter let roundup_barrier = Arc::new(Barrier::new( - evm_providers - .iter() - .filter(|p| !p.is_native.unwrap_or(false)) // Count all external chains - .count() - .saturating_add(1), // +1 for emitter + evm_providers.iter().filter(|evm_provider| evm_provider.is_relay_target).count(), )); let socket_bootstrap_count = Arc::new(Mutex::new(u8::default())); let roundup_bootstrap_count = Arc::new(Mutex::new(u8::default())); From eebfc734df3ed77b80963247e52f6a9ba22f1c96 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 17 Dec 2024 12:04:58 +0900 Subject: [PATCH 32/60] fix: is sequentially increased --- periodic/src/roundup_emitter.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index d54c2a29..01dd4004 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -221,6 +221,17 @@ where let round_up_events: Vec = logs.iter().map(|log| log.log_decode::().unwrap().inner.data).collect(); + let mut prev_status = round_up_events[0].status; + let mut prev_round = round_up_events[0].roundup.round; + for event in round_up_events.iter().skip(1) { + if event.roundup.round > prev_round && prev_status == 9 { + return prev_round; + } + + prev_status = event.status; + prev_round = event.roundup.round; + } + let max_round = round_up_events .iter() .map(|round_up_event| round_up_event.roundup.round) From f556126eb7540bd32482fe38de555d5218d8e7a1 Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Tue, 17 Dec 2024 12:47:41 +0900 Subject: [PATCH 33/60] CCCP-295, rollback sequential check and roundup barrier --- periodic/src/roundup_emitter.rs | 11 ----------- primitives/src/bootstrap.rs | 6 +++++- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index 01dd4004..d54c2a29 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -221,17 +221,6 @@ where let round_up_events: Vec = logs.iter().map(|log| log.log_decode::().unwrap().inner.data).collect(); - let mut prev_status = round_up_events[0].status; - let mut prev_round = round_up_events[0].roundup.round; - for event in round_up_events.iter().skip(1) { - if event.roundup.round > prev_round && prev_status == 9 { - return prev_round; - } - - prev_status = event.status; - prev_round = event.roundup.round; - } - let max_round = round_up_events .iter() .map(|round_up_event| round_up_event.roundup.round) diff --git a/primitives/src/bootstrap.rs b/primitives/src/bootstrap.rs index 826610c1..ea6201e0 100644 --- a/primitives/src/bootstrap.rs +++ b/primitives/src/bootstrap.rs @@ -50,7 +50,11 @@ impl BootstrapSharedData { // 1. One for the roundup handler for each external chain // 2. One for the roundup emitter let roundup_barrier = Arc::new(Barrier::new( - evm_providers.iter().filter(|evm_provider| evm_provider.is_relay_target).count(), + evm_providers + .iter() + .filter(|p| !p.is_native.unwrap_or(false)) // Count all external chains + .count() + .saturating_add(1), // +1 for emitter )); let socket_bootstrap_count = Arc::new(Mutex::new(u8::default())); let roundup_bootstrap_count = Arc::new(Mutex::new(u8::default())); From c34a01ab920a79c830c537a7e026442361a31818 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 17 Dec 2024 16:08:19 +0900 Subject: [PATCH 34/60] CCCP-295, fix: use right encode --- client/src/eth/handlers/roundup_relay_handler.rs | 16 ++-------------- periodic/src/roundup_emitter.rs | 11 ++--------- primitives/src/utils.rs | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 8902a028..8ffbaac6 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -1,7 +1,6 @@ use std::{sync::Arc, time::Duration}; use alloy::{ - dyn_abi::DynSolValue, network::{primitives::ReceiptResponse as _, AnyNetwork}, primitives::{Address, PrimitiveSignature, B256, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, @@ -25,7 +24,7 @@ use br_primitives::{ }, eth::{BootstrapState, RoundUpEventStatus}, tx::{TxRequestMetadata, VSPPhase2Metadata}, - utils::{recover_message, sub_display_format}, + utils::{encode_roundup_param, recover_message, sub_display_format}, }; use crate::eth::{ @@ -197,17 +196,6 @@ where Ok(log.log_decode::()?.inner.data) } - /// Encodes the given round and new relayers to bytes. - fn encode_relayer_array(&self, round: U256, new_relayers: &[Address]) -> Vec { - DynSolValue::Tuple(vec![ - DynSolValue::Uint(round, 256), - DynSolValue::Array( - new_relayers.iter().map(|address| DynSolValue::Address(*address)).collect(), - ), - ]) - .abi_encode() - } - /// Get the submitted signatures of the updated round. async fn get_sorted_signatures( &self, @@ -225,7 +213,7 @@ where let mut signature_vec = Vec::::from(signatures); signature_vec - .sort_by_key(|k| recover_message(*k, &self.encode_relayer_array(round, new_relayers))); + .sort_by_key(|k| recover_message(*k, &encode_roundup_param(round, &new_relayers))); Ok(Signatures::from(signature_vec)) } diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index d54c2a29..751cca26 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -1,5 +1,4 @@ use alloy::{ - dyn_abi::DynSolValue, network::AnyNetwork, primitives::{Address, U256}, providers::{fillers::TxFiller, Provider, WalletProvider}, @@ -25,7 +24,7 @@ use br_primitives::{ }, eth::{BootstrapState, RoundUpEventStatus}, tx::{TxRequestMetadata, VSPPhase1Metadata}, - utils::sub_display_format, + utils::{encode_roundup_param, sub_display_format}, }; use eyre::Result; @@ -157,13 +156,7 @@ where round: U256, new_relayers: Vec
, ) -> Result { - let encoded_msg = DynSolValue::Tuple(vec![ - DynSolValue::Uint(round, 256), - DynSolValue::Array( - new_relayers.iter().map(|address| DynSolValue::Address(*address)).collect(), - ), - ]) - .abi_encode(); + let encoded_msg = encode_roundup_param(round, &new_relayers); let sigs = Signatures::from(self.client.sign_message(&encoded_msg).await?); let round_up_submit = Round_Up_Submit { round, new_relayers, sigs }; diff --git a/primitives/src/utils.rs b/primitives/src/utils.rs index e130198a..b6c1e205 100644 --- a/primitives/src/utils.rs +++ b/primitives/src/utils.rs @@ -1,4 +1,7 @@ -use alloy::primitives::{keccak256, Address, PrimitiveSignature, B256}; +use alloy::{ + dyn_abi::DynSolValue, + primitives::{keccak256, Address, PrimitiveSignature, B256, U256}, +}; use k256::{ecdsa::VerifyingKey, elliptic_curve::sec1::ToEncodedPoint}; use rand::Rng as _; use sha3::{Digest, Keccak256}; @@ -13,6 +16,17 @@ pub fn generate_delay() -> u64 { rand::thread_rng().gen_range(0..=12000) } +/// Encodes the given round and new relayers to bytes. +pub fn encode_roundup_param(round: U256, new_relayers: &[Address]) -> Vec { + DynSolValue::Tuple(vec![ + DynSolValue::Uint(round, 256), + DynSolValue::Array( + new_relayers.iter().map(|address| DynSolValue::Address(*address)).collect(), + ), + ]) + .abi_encode_params() +} + impl From for EthereumSignature { fn from(signature: PrimitiveSignature) -> Self { let sig: [u8; 65] = signature.into(); From c5018d794c50b97029a8148299404d9ca02b11bf Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Tue, 17 Dec 2024 16:43:55 +0900 Subject: [PATCH 35/60] CCCP-295, fix: no relay to non relay target --- client/src/eth/mod.rs | 4 ++++ primitives/src/eth.rs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 47e6ab3c..2345c7d4 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -277,6 +277,10 @@ pub fn send_transaction( P: Provider + 'static, T: Transport + Clone, { + if !client.metadata.is_relay_target { + return; + } + let this_handle = handle.clone(); this_handle.spawn("send_transaction", None, async move { request.from = Some(client.address()); diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index 5a56808e..205d28eb 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -54,6 +54,8 @@ pub struct ProviderMetadata { pub if_destination_chain: RelayDirection, /// The flag whether the chain is Bifrost(native) or an external chain. pub is_native: bool, + /// The flag whether the chain is relay target. + pub is_relay_target: bool, } impl ProviderMetadata { @@ -81,6 +83,7 @@ impl ProviderMetadata { true => RelayDirection::Inbound, false => RelayDirection::Outbound, }, + is_relay_target: evm_provider.is_relay_target, } } } From c201ac24a604a0ddde64e7d182b09ecb9b1eabf4 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 12:13:51 +0900 Subject: [PATCH 36/60] CCCP-295, fix: bsc `estimateGas` issue --- client/src/eth/mod.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 2345c7d4..02f1a7cc 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -11,15 +11,16 @@ use br_primitives::{ use alloy::{ consensus::Transaction, + eips::BlockNumberOrTag, network::{AnyNetwork, AnyRpcTransaction, AnyTypedTransaction, TransactionResponse as _}, primitives::{ utils::{format_units, parse_ether, Unit}, - Address, ChainId, + Address, ChainId, U64, }, providers::{ ext::TxPoolApi as _, fillers::{FillProvider, TxFiller}, - PendingTransactionBuilder, Provider, RootProvider, SendableTx, WalletProvider, + EthCall, PendingTransactionBuilder, Provider, RootProvider, SendableTx, WalletProvider, }, rpc::types::{serde_helpers::WithOtherFields, txpool::TxpoolContent, TransactionRequest}, signers::{local::LocalSigner, Signature}, @@ -263,6 +264,19 @@ where ) -> TransportResult> { self.inner.send_transaction_internal(tx).await } + + fn estimate_gas<'req>( + &self, + tx: &'req ::TransactionRequest, + ) -> EthCall<'req, T, AnyNetwork, U64, u64> { + let call = EthCall::gas_estimate(self.inner.weak_client(), tx); + + if self.chain_id() == 56 || self.chain_id() == 97 { + call.map_resp(|r| r.to::()) + } else { + call.block(BlockNumberOrTag::Pending.into()).map_resp(|r| r.to::()) + } + } } pub fn send_transaction( From 7afde288a85520b7c43e7f3bf1f2ebb6d5f8464e Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 14:05:03 +0900 Subject: [PATCH 37/60] CCCP-295, fix: round relay sequential issue --- client/src/eth/mod.rs | 57 +++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 02f1a7cc..aa730964 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -2,6 +2,7 @@ use br_primitives::{ constants::{ config::{BOOTSTRAP_BLOCK_OFFSET, NATIVE_BLOCK_TIME}, errors::{INSUFFICIENT_FUNDS, INVALID_CHAIN_ID, PROVIDER_INTERNAL_ERROR}, + tx::DEFAULT_CALL_RETRIES, }, contracts::authority::BfcStaking::round_meta_data, eth::{AggregatorContracts, GasCoefficient, ProtocolContracts, ProviderMetadata}, @@ -36,7 +37,7 @@ use std::{ sync::Arc, time::Duration, }; -use tokio::sync::Mutex; +use tokio::{sync::Mutex, time::sleep}; use url::Url; pub mod events; @@ -196,7 +197,7 @@ where } // possibility of txpool being flushed automatically. wait for 2 blocks. - tokio::time::sleep(Duration::from_millis(self.metadata.call_interval * 2)).await; + sleep(Duration::from_millis(self.metadata.call_interval * 2)).await; let mut content: TxpoolContent = self.txpool_content().await?; let pending = content.remove_from(&self.address()).pending; @@ -299,27 +300,35 @@ pub fn send_transaction( this_handle.spawn("send_transaction", None, async move { request.from = Some(client.address()); - match client.estimate_gas(&WithOtherFields::new(request.clone())).await { - Ok(gas) => { - let coefficient: f64 = GasCoefficient::from(client.metadata.is_native).into(); - let estimated_gas = gas as f64 * coefficient; - request.gas = Some(estimated_gas.ceil() as u64); - }, - Err(err) => { - if debug_mode { - let msg = format!( - " ❗️ Failed to estimate gas ({} address:{}): {}, Error: {}", - client.get_chain_name(), - client.address(), - metadata, - err - ); - log::error!(target: &requester, "{msg}"); - sentry::capture_message(&msg, sentry::Level::Error); - } - return; - }, - }; + loop { + match client.estimate_gas(&WithOtherFields::new(request.clone())).await { + Ok(gas) => { + let coefficient: f64 = GasCoefficient::from(client.metadata.is_native).into(); + let estimated_gas = gas as f64 * coefficient; + request.gas = Some(estimated_gas.ceil() as u64); + break; + }, + Err(err) => { + // only retry infinitely if the error is related to the round sync issue + if err.to_string().contains("latest round") { + sleep(Duration::from_millis(client.metadata.call_interval * 2)).await; + } else { + if debug_mode { + let msg = format!( + " ❗️ Failed to estimate gas ({} address:{}): {}, Error: {}", + client.get_chain_name(), + client.address(), + metadata, + err + ); + log::error!(target: &requester, "{msg}"); + sentry::capture_message(&msg, sentry::Level::Error); + } + return; + } + }, + }; + } if client.metadata.is_native { // gas price is fixed to 1000 Gwei on bifrost network @@ -327,7 +336,7 @@ pub fn send_transaction( request.max_priority_fee_per_gas = Some(0); } else { // to avoid duplicate(will revert) external networks transactions - tokio::time::sleep(Duration::from_millis(generate_delay())).await; + sleep(Duration::from_millis(generate_delay())).await; if !client.metadata.eip1559 { request.gas_price = Some(client.get_gas_price().await.unwrap()); From e0532ddbfff7530ad71eaee9d66d0ab9c82cb260 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 14:14:26 +0900 Subject: [PATCH 38/60] CCCP-295, fix: add missing condition --- client/src/eth/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index aa730964..e816fc10 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -2,7 +2,6 @@ use br_primitives::{ constants::{ config::{BOOTSTRAP_BLOCK_OFFSET, NATIVE_BLOCK_TIME}, errors::{INSUFFICIENT_FUNDS, INVALID_CHAIN_ID, PROVIDER_INTERNAL_ERROR}, - tx::DEFAULT_CALL_RETRIES, }, contracts::authority::BfcStaking::round_meta_data, eth::{AggregatorContracts, GasCoefficient, ProtocolContracts, ProviderMetadata}, @@ -310,7 +309,7 @@ pub fn send_transaction( }, Err(err) => { // only retry infinitely if the error is related to the round sync issue - if err.to_string().contains("latest round") { + if err.to_string().contains("latest round") && !client.metadata.is_native { sleep(Duration::from_millis(client.metadata.call_interval * 2)).await; } else { if debug_mode { From 8be9f6a37e06949122d8fb7392f8b07770d1adb6 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 14:37:58 +0900 Subject: [PATCH 39/60] CCCP-295, feature: flush txpool when tx fails to confirmed while 3 block pass --- client/src/eth/mod.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index e816fc10..5ca75d28 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -351,6 +351,36 @@ pub fn send_transaction( pending.tx_hash(), metadata ); + + // wait for the transaction to be confirmed in 3 blocks + match pending + .with_timeout(Some(Duration::from_millis(client.metadata.call_interval * 3))) + .watch() + .await + { + Ok(tx_hash) => { + log::info!( + target: &requester, + " 🔖 Transaction confirmed ({} tx:{}): {}", + client.get_chain_name(), + tx_hash, + metadata + ); + }, + Err(err) => { + let msg = format!( + " ❗️ Transaction failed to register ({} address:{}): {}, Error: {}", + client.get_chain_name(), + client.address(), + metadata, + err + ); + log::error!(target: &requester, "{msg}"); + sentry::capture_message(&msg, sentry::Level::Error); + + client.flush_stalled_transactions().await.unwrap(); + }, + } }, Err(err) => { let msg = format!( From 26130944edc1667deb54980639d99913bb8abb8e Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 15:06:31 +0900 Subject: [PATCH 40/60] CCCP-295, feature: synchronous send transaction on bootstrap roundup relay --- .../src/eth/handlers/roundup_relay_handler.rs | 42 ++++--- client/src/eth/mod.rs | 108 +++++++++++------- 2 files changed, 95 insertions(+), 55 deletions(-) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 8ffbaac6..2d2550c3 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -129,6 +129,7 @@ where serialized_log.roundup.new_relayers, ) .await?, + is_bootstrap, ) .await?; }, @@ -253,7 +254,11 @@ where } /// Check roundup submitted before. If not, call `round_control_relay`. - async fn broadcast_roundup(&self, roundup_submit: &Round_Up_Submit) -> Result<()> { + async fn broadcast_roundup( + &self, + roundup_submit: &Round_Up_Submit, + is_bootstrap: bool, + ) -> Result<()> { if self.external_clients.is_empty() { return Ok(()); } @@ -268,18 +273,29 @@ where &target_client.protocol_contracts.socket, roundup_submit, ); - - send_transaction( - target_client.clone(), - transaction_request, - SUB_LOG_TARGET.to_string(), - TxRequestMetadata::VSPPhase2(VSPPhase2Metadata::new( - roundup_submit.round, - *dst_chain_id, - )), - self.debug_mode, - self.handle.clone(), - ); + let metadata = TxRequestMetadata::VSPPhase2(VSPPhase2Metadata::new( + roundup_submit.round, + *dst_chain_id, + )); + + if is_bootstrap { + target_client + .sync_send_transaction( + transaction_request, + SUB_LOG_TARGET.to_string(), + metadata, + ) + .await? + } else { + send_transaction( + target_client.clone(), + transaction_request, + SUB_LOG_TARGET.to_string(), + metadata, + self.debug_mode, + self.handle.clone(), + ); + } } } diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 5ca75d28..7e3e64ab 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -245,6 +245,60 @@ where Ok(()) } + + async fn fill_gas(&self, request: &mut TransactionRequest) -> Result<()> { + request.from = Some(self.address()); + + let gas = self.estimate_gas(&WithOtherFields::new(request.clone())).await?; + let coefficient: f64 = GasCoefficient::from(self.metadata.is_native).into(); + let estimated_gas = gas as f64 * coefficient; + request.gas = Some(estimated_gas.ceil() as u64); + + if self.metadata.is_native { + // gas price is fixed to 1000 Gwei on bifrost network + request.max_fee_per_gas = Some(1000 * Unit::GWEI.wei().to::()); + request.max_priority_fee_per_gas = Some(0); + } else { + // to avoid duplicate(will revert) external networks transactions + sleep(Duration::from_millis(generate_delay())).await; + + if !self.metadata.eip1559 { + request.gas_price = Some(self.get_gas_price().await.unwrap()); + } + } + + Ok(()) + } + + async fn sync_send_transaction( + &self, + mut request: TransactionRequest, + requester: String, + metadata: TxRequestMetadata, + ) -> Result<()> { + self.fill_gas(&mut request).await?; + let pending = self.send_transaction(WithOtherFields::new(request)).await?; + match pending + .with_timeout(Some(Duration::from_millis(self.metadata.call_interval * 3))) + .watch() + .await + { + Ok(tx_hash) => { + log::info!( + target: &requester, + " 🔖 Transaction confirmed ({} tx:{}): {}", + self.get_chain_name(), + tx_hash, + metadata + ); + Ok(()) + }, + Err(_) => { + self.flush_stalled_transactions().await?; + Ok(()) + }, + } + } } #[async_trait::async_trait] @@ -297,49 +351,19 @@ pub fn send_transaction( let this_handle = handle.clone(); this_handle.spawn("send_transaction", None, async move { - request.from = Some(client.address()); - - loop { - match client.estimate_gas(&WithOtherFields::new(request.clone())).await { - Ok(gas) => { - let coefficient: f64 = GasCoefficient::from(client.metadata.is_native).into(); - let estimated_gas = gas as f64 * coefficient; - request.gas = Some(estimated_gas.ceil() as u64); - break; - }, - Err(err) => { - // only retry infinitely if the error is related to the round sync issue - if err.to_string().contains("latest round") && !client.metadata.is_native { - sleep(Duration::from_millis(client.metadata.call_interval * 2)).await; - } else { - if debug_mode { - let msg = format!( - " ❗️ Failed to estimate gas ({} address:{}): {}, Error: {}", - client.get_chain_name(), - client.address(), - metadata, - err - ); - log::error!(target: &requester, "{msg}"); - sentry::capture_message(&msg, sentry::Level::Error); - } - return; - } - }, - }; - } - - if client.metadata.is_native { - // gas price is fixed to 1000 Gwei on bifrost network - request.max_fee_per_gas = Some(1000 * Unit::GWEI.wei().to::()); - request.max_priority_fee_per_gas = Some(0); - } else { - // to avoid duplicate(will revert) external networks transactions - sleep(Duration::from_millis(generate_delay())).await; - - if !client.metadata.eip1559 { - request.gas_price = Some(client.get_gas_price().await.unwrap()); + if let Err(err) = client.fill_gas(&mut request).await { + if debug_mode { + let msg = format!( + " ❗️ Failed to estimate gas ({} address:{}): {}, Error: {}", + client.get_chain_name(), + client.address(), + metadata, + err + ); + log::error!(target: &requester, "{msg}"); + sentry::capture_message(&msg, sentry::Level::Error); } + return; } match client.send_transaction(WithOtherFields::new(request.clone())).await { From 3af60397ef3471e13643e64615082aec826c71ab Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 15:15:21 +0900 Subject: [PATCH 41/60] CCCP-295, fix: add flush condition --- client/src/eth/mod.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 7e3e64ab..7c1f9834 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -272,12 +272,34 @@ where async fn sync_send_transaction( &self, - mut request: TransactionRequest, + request: TransactionRequest, requester: String, metadata: TxRequestMetadata, ) -> Result<()> { - self.fill_gas(&mut request).await?; - let pending = self.send_transaction(WithOtherFields::new(request)).await?; + let mut this_request = request.clone(); + self.fill_gas(&mut this_request).await?; + + let pending = match self.send_transaction(WithOtherFields::new(this_request)).await { + Ok(pending) => pending, + Err(err) => { + let msg = format!( + " ❗️ Failed to send transaction ({} address:{}): {}, Error: {}", + self.get_chain_name(), + self.address(), + metadata, + err + ); + log::error!(target: &requester, "{msg}"); + sentry::capture_message(&msg, sentry::Level::Error); + + if err.to_string().to_lowercase().contains("nonce too low") { + self.flush_stalled_transactions().await?; + return self.sync_send_transaction(request, requester, metadata).await; + } else { + eyre::bail!(err) + } + }, + }; match pending .with_timeout(Some(Duration::from_millis(self.metadata.call_interval * 3))) .watch() From cc99b597adef9ea571f6b35aa6cc16265a432c29 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 15:51:38 +0900 Subject: [PATCH 42/60] CCCP-295, refactor: simplify log processing in roundup relay handler --- client/src/eth/handlers/roundup_relay_handler.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 2d2550c3..ddf769d2 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -366,9 +366,7 @@ where if bootstrap_guard.iter().all(|s| *s == BootstrapState::BootstrapRoundUpPhase2) { drop(bootstrap_guard); let logs = self.get_bootstrap_events().await?; - - let mut stream = tokio_stream::iter(logs); - while let Some(log) = stream.next().await { + for log in logs { self.process_confirmed_log(&log, true).await?; } } From 80a00135130b997bf550caed180ea2fb0e2b8fbf Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 17:13:53 +0900 Subject: [PATCH 43/60] CCCP-295, refactor: streamline event handling and bootstrap state synchronization across BTC and ETH handlers --- client/src/btc/block.rs | 15 +++--- client/src/btc/handlers/inbound.rs | 51 +++++++++---------- client/src/btc/handlers/mod.rs | 11 +++- client/src/btc/handlers/outbound.rs | 50 +++++++++--------- client/src/eth/events.rs | 13 +++-- .../src/eth/handlers/roundup_relay_handler.rs | 50 +++++++++--------- .../src/eth/handlers/socket_relay_handler.rs | 49 ++++++++---------- client/src/eth/traits.rs | 11 +++- client/src/substrate/tx/unsigned_manager.rs | 17 +++++-- primitives/src/eth.rs | 2 +- 10 files changed, 145 insertions(+), 124 deletions(-) diff --git a/client/src/btc/block.rs b/client/src/btc/block.rs index 2b26be60..2b181261 100644 --- a/client/src/btc/block.rs +++ b/client/src/btc/block.rs @@ -25,13 +25,10 @@ use serde::Deserialize; use serde_json::Value; use std::{collections::BTreeSet, str::FromStr, sync::Arc}; use tokio::{ - sync::{ - broadcast, - broadcast::{Receiver, Sender}, - }, - time::{sleep, Duration}, + sync::broadcast::{self, Receiver, Sender}, + time::{interval, sleep, Duration}, }; -use tokio_stream::StreamExt; +use tokio_stream::{wrappers::IntervalStream, StreamExt}; use super::handlers::BootstrapHandler; @@ -199,7 +196,8 @@ where latest_block ); - loop { + let mut stream = IntervalStream::new(interval(Duration::from_millis(self.call_interval))); + while let Some(_) = stream.next().await { if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapSocketRelay).await { self.bootstrap().await?; } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { @@ -214,9 +212,8 @@ where .await; } } - - sleep(Duration::from_millis(self.call_interval)).await; } + Ok(()) } /// Returns the generated user vault addresses. diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index 40665911..49954103 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -17,7 +17,6 @@ use bitcoincore_rpc::bitcoin::Txid; use br_primitives::{ bootstrap::BootstrapSharedData, contracts::bitcoin_socket::BitcoinSocketInstance, - eth::BootstrapState, tx::{BitcoinRelayMetadata, TxRequestMetadata}, utils::sub_display_format, }; @@ -27,7 +26,7 @@ use miniscript::bitcoin::{address::NetworkUnchecked, hashes::Hash, Address as Bt use sc_service::SpawnTaskHandle; use std::sync::Arc; use tokio::sync::broadcast::Receiver; -use tokio_stream::StreamExt; +use tokio_stream::{wrappers::BroadcastStream, StreamExt}; use super::{BootstrapHandler, EventMessage}; @@ -42,7 +41,7 @@ where /// `EthClient` for interact with Bifrost network. pub bfc_client: Arc>, /// The receiver that consumes new events from the block channel. - event_receiver: Receiver, + event_stream: BroadcastStream, /// Event type which this handler should handle. target_event: EventType, /// The bootstrap shared data. @@ -68,7 +67,7 @@ where ) -> Self { Self { bfc_client, - event_receiver, + event_stream: BroadcastStream::new(event_receiver), target_event: EventType::Inbound, bootstrap_shared_data, handle, @@ -163,30 +162,30 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - loop { - if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { - let msg = self.event_receiver.recv().await.unwrap(); - - if !self.bfc_client.is_selected_relayer().await? - || !self.is_target_event(msg.event_type) - { - continue; - } - - log::info!( - target: LOG_TARGET, - "-[{}] 📦 Imported #{:?} with target logs({:?})", - sub_display_format(SUB_LOG_TARGET), - msg.block_number, - msg.events.len() - ); - - let mut stream = tokio_stream::iter(msg.events); - while let Some(event) = stream.next().await { - self.process_event(event).await?; - } + while let Some(Ok(msg)) = self.event_stream.next().await { + self.wait_for_normal_state().await?; + + if !self.bfc_client.is_selected_relayer().await? + || !self.is_target_event(msg.event_type) + { + continue; + } + + log::info!( + target: LOG_TARGET, + "-[{}] 📦 Imported #{:?} with target logs({:?})", + sub_display_format(SUB_LOG_TARGET), + msg.block_number, + msg.events.len() + ); + + let mut stream = tokio_stream::iter(msg.events); + while let Some(event) = stream.next().await { + self.process_event(event).await?; } } + + Ok(()) } async fn process_event(&self, mut event: Event) -> Result<()> { diff --git a/client/src/btc/handlers/mod.rs b/client/src/btc/handlers/mod.rs index de07fe25..76f38270 100644 --- a/client/src/btc/handlers/mod.rs +++ b/client/src/btc/handlers/mod.rs @@ -3,6 +3,7 @@ mod outbound; pub use inbound::*; pub use outbound::*; +use tokio::time::sleep; use crate::{ btc::{ @@ -24,7 +25,7 @@ use br_primitives::{ utils::sub_display_format, }; use eyre::Result; -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use super::block::EventMessage; @@ -123,4 +124,12 @@ pub trait BootstrapHandler { .iter() .all(|s| *s == state) } + + /// Waits for the bootstrap state to be synced to the normal start state. + async fn wait_for_normal_state(&self) -> Result<()> { + while !self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { + sleep(Duration::from_millis(100)).await; + } + Ok(()) + } } diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index 8f4d8a54..8eb134ff 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -20,7 +20,7 @@ use br_primitives::{ socket::{SocketContract::Socket, Socket_Struct::Socket_Message}, socket_queue::SocketQueueInstance, }, - eth::{BootstrapState, BuiltRelayTransaction, SocketEventStatus}, + eth::{BuiltRelayTransaction, SocketEventStatus}, tx::{SocketRelayMetadata, TxRequestMetadata}, utils::sub_display_format, }; @@ -29,7 +29,7 @@ use miniscript::bitcoin::{hashes::Hash, Txid}; use sc_service::SpawnTaskHandle; use std::sync::Arc; use tokio::sync::broadcast::Receiver; -use tokio_stream::StreamExt; +use tokio_stream::{wrappers::BroadcastStream, StreamExt}; use super::{BootstrapHandler, EventMessage}; @@ -42,7 +42,7 @@ where T: Transport + Clone, { pub bfc_client: Arc>, - event_receiver: Receiver, + event_stream: BroadcastStream, target_event: EventType, /// The bootstrap shared data. bootstrap_shared_data: Arc, @@ -67,7 +67,7 @@ where ) -> Self { Self { bfc_client, - event_receiver, + event_stream: BroadcastStream::new(event_receiver), target_event: EventType::Outbound, bootstrap_shared_data, handle, @@ -103,30 +103,30 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - loop { - if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { - let msg = self.event_receiver.recv().await.unwrap(); - - if !self.bfc_client.is_selected_relayer().await? - || !self.is_target_event(msg.event_type) - { - continue; - } - - log::info!( - target: LOG_TARGET, - "-[{}] 📦 Imported #{:?} with target logs({:?})", - sub_display_format(SUB_LOG_TARGET), - msg.block_number, - msg.events.len() - ); + while let Some(Ok(msg)) = self.event_stream.next().await { + self.wait_for_normal_state().await?; + + if !self.bfc_client.is_selected_relayer().await? + || !self.is_target_event(msg.event_type) + { + continue; + } - let mut stream = tokio_stream::iter(msg.events.into_iter()); - while let Some(event) = stream.next().await { - self.process_event(event).await?; - } + log::info!( + target: LOG_TARGET, + "-[{}] 📦 Imported #{:?} with target logs({:?})", + sub_display_format(SUB_LOG_TARGET), + msg.block_number, + msg.events.len() + ); + + let mut stream = tokio_stream::iter(msg.events); + while let Some(event) = stream.next().await { + self.process_event(event).await?; } } + + Ok(()) } async fn process_event(&self, event: Event) -> Result<()> { diff --git a/client/src/eth/events.rs b/client/src/eth/events.rs index 69b47071..87490548 100644 --- a/client/src/eth/events.rs +++ b/client/src/eth/events.rs @@ -9,8 +9,9 @@ use eyre::Result; use std::sync::Arc; use tokio::{ sync::broadcast::{self, Receiver, Sender}, - time::{sleep, Duration}, + time::{interval, sleep, Duration}, }; +use tokio_stream::{wrappers::IntervalStream, StreamExt}; use br_primitives::{ bootstrap::BootstrapSharedData, eth::BootstrapState, utils::sub_display_format, @@ -111,7 +112,11 @@ where pub async fn run(&mut self) -> Result<()> { self.initialize().await?; - loop { + let mut stream = IntervalStream::new(interval(Duration::from_millis( + self.client.metadata.call_interval, + ))); + + while let Some(_) = stream.next().await { if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { let latest_block = self.client.get_block_number().await?; while self.is_block_confirmed(latest_block) { @@ -122,9 +127,9 @@ where } } } - - sleep(Duration::from_millis(self.client.metadata.call_interval)).await; } + + Ok(()) } /// Process the confirmed block and verifies if any events emitted from the target diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index ddf769d2..af0eb8bb 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, time::Duration}; +use std::sync::Arc; use alloy::{ network::{primitives::ReceiptResponse as _, AnyNetwork}, @@ -11,8 +11,8 @@ use alloy::{ use async_trait::async_trait; use eyre::Result; use sc_service::SpawnTaskHandle; -use tokio::{sync::broadcast::Receiver, time::sleep}; -use tokio_stream::StreamExt; +use tokio::sync::broadcast::Receiver; +use tokio_stream::{wrappers::BroadcastStream, StreamExt}; use br_primitives::{ bootstrap::BootstrapSharedData, @@ -46,7 +46,7 @@ where /// The `EthClient` to interact with the bifrost network. pub client: Arc>, /// The receiver that consumes new events from the block channel. - event_receiver: Receiver, + event_stream: BroadcastStream, /// `EthClient`s to interact with provided networks except bifrost network. external_clients: Arc>, /// Signature of RoundUp Event. @@ -67,30 +67,28 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - loop { - if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapRoundUpPhase2).await { - self.bootstrap().await?; - - sleep(Duration::from_millis(self.client.metadata.call_interval)).await; - } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { - let msg = self.event_receiver.recv().await.unwrap(); - - log::info!( - target: &self.client.get_chain_name(), - "-[{}] 📦 Imported #{:?} with target logs({:?})", - sub_display_format(SUB_LOG_TARGET), - msg.block_number, - msg.event_logs.len(), - ); - - let mut stream = tokio_stream::iter(msg.event_logs); - while let Some(log) = stream.next().await { - if self.is_target_contract(&log) && self.is_target_event(log.topic0()) { - self.process_confirmed_log(&log, false).await?; - } + self.wait_for_bootstrap_state(BootstrapState::BootstrapRoundUpPhase2).await?; + self.bootstrap().await?; + + self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; + while let Some(Ok(msg)) = self.event_stream.next().await { + log::info!( + target: &self.client.get_chain_name(), + "-[{}] 📦 Imported #{:?} with target logs({:?})", + sub_display_format(SUB_LOG_TARGET), + msg.block_number, + msg.event_logs.len(), + ); + + let mut stream = tokio_stream::iter(msg.event_logs); + while let Some(log) = stream.next().await { + if self.is_target_contract(&log) && self.is_target_event(log.topic0()) { + self.process_confirmed_log(&log, false).await?; } } } + + Ok(()) } async fn process_confirmed_log(&self, log: &Log, is_bootstrap: bool) -> Result<()> { @@ -182,7 +180,7 @@ where debug_mode: bool, ) -> Self { Self { - event_receiver, + event_stream: BroadcastStream::new(event_receiver), client, external_clients: clients, roundup_signature: RoundUp::SIGNATURE_HASH, diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index d94674b7..ff44928b 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, sync::Arc, time::Duration}; +use std::{collections::BTreeMap, sync::Arc}; use alloy::{ network::AnyNetwork, @@ -10,11 +10,8 @@ use alloy::{ }; use eyre::Result; use sc_service::SpawnTaskHandle; -use tokio::{ - sync::{broadcast::Receiver, mpsc::UnboundedSender}, - time::sleep, -}; -use tokio_stream::StreamExt; +use tokio::sync::{broadcast::Receiver, mpsc::UnboundedSender}; +use tokio_stream::{wrappers::BroadcastStream, StreamExt}; use br_primitives::{ bootstrap::BootstrapSharedData, @@ -51,7 +48,7 @@ where /// The `EthClient` to interact with the connected blockchain. pub client: Arc>, /// The receiver that consumes new events from the block channel. - event_receiver: Receiver, + event_stream: BroadcastStream, /// The entire clients instantiated in the system. > system_clients: Arc>, /// The bifrost client. @@ -76,30 +73,28 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - loop { - if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapSocketRelay).await { - self.bootstrap().await?; - - sleep(Duration::from_millis(self.client.metadata.call_interval)).await; - } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { - let msg = self.event_receiver.recv().await.unwrap(); + self.wait_for_bootstrap_state(BootstrapState::BootstrapSocketRelay).await?; + self.bootstrap().await?; - log::info!( - target: &self.client.get_chain_name(), - "-[{}] 📦 Imported #{:?} with target logs({:?})", - sub_display_format(SUB_LOG_TARGET), - msg.block_number, - msg.event_logs.len(), - ); + self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; + while let Some(Ok(msg)) = self.event_stream.next().await { + log::info!( + target: &self.client.get_chain_name(), + "-[{}] 📦 Imported #{:?} with target logs({:?})", + sub_display_format(SUB_LOG_TARGET), + msg.block_number, + msg.event_logs.len(), + ); - let mut stream = tokio_stream::iter(msg.event_logs); - while let Some(log) = stream.next().await { - if self.is_target_contract(&log) && self.is_target_event(log.topic0()) { - self.process_confirmed_log(&log, false).await?; - } + let mut stream = tokio_stream::iter(msg.event_logs); + while let Some(log) = stream.next().await { + if self.is_target_contract(&log) && self.is_target_event(log.topic0()) { + self.process_confirmed_log(&log, false).await?; } } } + + Ok(()) } async fn process_confirmed_log(&self, log: &Log, is_bootstrap: bool) -> Result<()> { @@ -276,7 +271,7 @@ where let client = system_clients.get(&id).expect(INVALID_CHAIN_ID).clone(); Self { - event_receiver, + event_stream: BroadcastStream::new(event_receiver), socket_signature: Socket::SIGNATURE_HASH, client, system_clients, diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index dd48f58e..18530747 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use alloy::{ dyn_abi::DynSolValue, @@ -17,6 +17,7 @@ use br_primitives::{ utils::recover_message, }; use eyre::Result; +use tokio::time::sleep; use super::EthClient; @@ -154,4 +155,12 @@ pub trait BootstrapHandler { .iter() .all(|s| *s == state) } + + /// Waits for the bootstrap state to be synced to the normal start state. + async fn wait_for_bootstrap_state(&self, state: BootstrapState) -> Result<()> { + while !self.is_bootstrap_state_synced_as(state).await { + sleep(Duration::from_millis(100)).await; + } + Ok(()) + } } diff --git a/client/src/substrate/tx/unsigned_manager.rs b/client/src/substrate/tx/unsigned_manager.rs index daf7e499..f82efb4a 100644 --- a/client/src/substrate/tx/unsigned_manager.rs +++ b/client/src/substrate/tx/unsigned_manager.rs @@ -13,7 +13,8 @@ use br_primitives::{ use sc_service::SpawnTaskHandle; use std::sync::Arc; use subxt::OnlineClient; -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use tokio::sync::mpsc::{self, UnboundedSender}; +use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use crate::{eth::EthClient, substrate::traits::ExtrinsicTask}; @@ -31,7 +32,7 @@ where /// The Bifrost client. bfc_client: Arc>, /// The receiver connected to the tx request channel. - receiver: UnboundedReceiver, + message_stream: UnboundedReceiverStream, /// A handle for spawning transaction tasks in the service. xt_spawn_handle: SpawnTaskHandle, } @@ -48,7 +49,15 @@ where xt_spawn_handle: SpawnTaskHandle, ) -> (Self, UnboundedSender) { let (sender, receiver) = mpsc::unbounded_channel::(); - (Self { sub_client: None, bfc_client, receiver, xt_spawn_handle }, sender) + ( + Self { + sub_client: None, + bfc_client, + message_stream: UnboundedReceiverStream::new(receiver), + xt_spawn_handle, + }, + sender, + ) } /// Initialize the substrate client. @@ -71,7 +80,7 @@ where pub async fn run(&mut self) { self.initialize().await; - while let Some(msg) = self.receiver.recv().await { + while let Some(msg) = self.message_stream.next().await { log::info!( target: &self.bfc_client.get_chain_name(), "-[{}] 🔖 Received unsigned transaction request: {}", diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index 205d28eb..cbe7c50f 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -374,7 +374,7 @@ pub enum RelayDirection { Outbound, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] /// The state for bootstrapping pub enum BootstrapState { /// phase 0. check if the node is in syncing From 7250a891f3027a8714e3b205286aa6f2b10022e2 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 17:18:18 +0900 Subject: [PATCH 44/60] CCCP-295, fix: state check location --- client/src/btc/handlers/inbound.rs | 3 +-- client/src/btc/handlers/outbound.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index 49954103..abb56fea 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -162,9 +162,8 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { + self.wait_for_normal_state().await?; while let Some(Ok(msg)) = self.event_stream.next().await { - self.wait_for_normal_state().await?; - if !self.bfc_client.is_selected_relayer().await? || !self.is_target_event(msg.event_type) { diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index 8eb134ff..3c9e941a 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -103,9 +103,8 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { + self.wait_for_normal_state().await?; while let Some(Ok(msg)) = self.event_stream.next().await { - self.wait_for_normal_state().await?; - if !self.bfc_client.is_selected_relayer().await? || !self.is_target_event(msg.event_type) { From 340528bacb3fe4a5c270f00b4cf2a94be7b588da Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 17:36:42 +0900 Subject: [PATCH 45/60] CCCP-295, fix: recursive issue --- .../src/eth/handlers/roundup_relay_handler.rs | 25 +++++++++++++------ client/src/eth/mod.rs | 7 +----- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index af0eb8bb..7571c9eb 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -277,13 +277,24 @@ where )); if is_bootstrap { - target_client - .sync_send_transaction( - transaction_request, - SUB_LOG_TARGET.to_string(), - metadata, - ) - .await? + loop { + if let Err(e) = target_client + .sync_send_transaction( + transaction_request.clone(), + SUB_LOG_TARGET.to_string(), + metadata.clone(), + ) + .await + { + if e.to_string().to_lowercase().contains("nonce too low") { + continue; + } else { + eyre::bail!(e); + } + } else { + break; + } + } } else { send_transaction( target_client.clone(), diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 7c1f9834..e53dc232 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -292,12 +292,7 @@ where log::error!(target: &requester, "{msg}"); sentry::capture_message(&msg, sentry::Level::Error); - if err.to_string().to_lowercase().contains("nonce too low") { - self.flush_stalled_transactions().await?; - return self.sync_send_transaction(request, requester, metadata).await; - } else { - eyre::bail!(err) - } + eyre::bail!(err) }, }; match pending From 05552707e4ab56f89a343d573d0033a084a90900 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 19 Dec 2024 17:37:53 +0900 Subject: [PATCH 46/60] CCCP-295, fix: add transaction flushing on nonce too low error in roundup relay handler --- client/src/eth/handlers/roundup_relay_handler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 7571c9eb..963b74f2 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -287,6 +287,7 @@ where .await { if e.to_string().to_lowercase().contains("nonce too low") { + target_client.flush_stalled_transactions().await?; continue; } else { eyre::bail!(e); From d0052ba779f1f5119a51fe6b35e61b9ff7403f05 Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Fri, 20 Dec 2024 17:35:09 +0900 Subject: [PATCH 47/60] CCCP-295, feat: retry on already known --- client/src/eth/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index e53dc232..03306635 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -437,6 +437,8 @@ pub fn send_transaction( if err.to_string().to_lowercase().contains("nonce too low") { client.flush_stalled_transactions().await.unwrap(); send_transaction(client, request, requester, metadata, debug_mode, handle); + } else if err.to_string().to_lowercase().contains("already known") { + send_transaction(client, request, requester, metadata, debug_mode, handle); } }, } From fdfde675d76b06b0a2e1bc09ae4fda1f29444674 Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Mon, 23 Dec 2024 11:56:32 +0900 Subject: [PATCH 48/60] CCCP-295, deps: bump sentry --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 75914cc7..f660d165 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ repository = "https://github.com/bifrost-platform/bifrost-relayer.rs" # General log = "0.4.20" env_logger = "0.10.0" -sentry = { version = "0.32.1", default-features = false, features = [ +sentry = { version = "0.35.0", default-features = false, features = [ "reqwest", "rustls", "backtrace", From 70d880ed8a52a6b783bd21f7f1f4289918e92ad9 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Mon, 23 Dec 2024 12:35:13 +0900 Subject: [PATCH 49/60] CCCP-295, fix: use `CachedNonceManager` --- client/src/eth/mod.rs | 2 -- relayer/src/service.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index 03306635..e53dc232 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -437,8 +437,6 @@ pub fn send_transaction( if err.to_string().to_lowercase().contains("nonce too low") { client.flush_stalled_transactions().await.unwrap(); send_transaction(client, request, requester, metadata, debug_mode, handle); - } else if err.to_string().to_lowercase().contains("already known") { - send_transaction(client, request, requester, metadata, debug_mode, handle); } }, } diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 5f6ee6b6..59a96244 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -74,7 +74,7 @@ pub fn relay(config: Configuration) -> Result { .with_poll_interval(Duration::from_millis(evm_provider.call_interval)); let provider = Arc::new( ProviderBuilder::new() - .with_recommended_fillers() + .with_cached_nonce_management() .network::() .wallet(wallet) .on_client(client), From ba5412b2f8c8acc74b33c8e8dfcad5a8f7711a8b Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Mon, 23 Dec 2024 13:06:40 +0900 Subject: [PATCH 50/60] CCCP-295, fix: fill chain_id --- client/src/eth/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index e53dc232..af510eaa 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -247,6 +247,7 @@ where } async fn fill_gas(&self, request: &mut TransactionRequest) -> Result<()> { + request.chain_id = Some(self.chain_id()); request.from = Some(self.address()); let gas = self.estimate_gas(&WithOtherFields::new(request.clone())).await?; From fb55d6b4ccf46a6964282358c9d362b49b21fc1f Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Mon, 23 Dec 2024 13:08:10 +0900 Subject: [PATCH 51/60] CCCP-295, chore: print sentry client init log --- metrics/Cargo.toml | 3 +++ metrics/src/sentry.rs | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/metrics/Cargo.toml b/metrics/Cargo.toml index 855057c9..a6a9206b 100644 --- a/metrics/Cargo.toml +++ b/metrics/Cargo.toml @@ -10,9 +10,12 @@ repository = { workspace = true } [dependencies] # General +log = { workspace = true } lazy_static = { workspace = true } sentry = { workspace = true } alloy = { workspace = true } # Substrate prometheus-endpoint = { workspace = true } + +br-primitives = { path = "../primitives", default-features = false } diff --git a/metrics/src/sentry.rs b/metrics/src/sentry.rs index ce98c3f5..14b862d2 100644 --- a/metrics/src/sentry.rs +++ b/metrics/src/sentry.rs @@ -1,6 +1,11 @@ use sentry::ClientInitGuard; use std::borrow::Cow; +use br_primitives::utils::sub_display_format; + +pub const LOG_TARGET: &str = "bifrost-relayer"; +const SUB_LOG_TARGET: &str = "sentry-client"; + /// Builds a sentry client only when the sentry config exists. pub fn build_sentry_client( is_enabled: bool, @@ -12,10 +17,16 @@ pub fn build_sentry_client( dsn, sentry::ClientOptions { release: sentry::release_name!(), - environment, + environment: environment.clone(), ..Default::default() }, )); + log::info!( + target: LOG_TARGET, + "-[{}] 🔨 Initializing sentry client with environment: {}", + sub_display_format(SUB_LOG_TARGET), + environment.unwrap_or_default() + ); return Some(sentry_client); } None From 7235e0ebe81f46b0b47880f8fe6bc322da15d7f1 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Mon, 23 Dec 2024 13:41:16 +0900 Subject: [PATCH 52/60] CCCP-295, fix: fillers --- client/src/eth/mod.rs | 1 - relayer/src/service.rs | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index af510eaa..e53dc232 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -247,7 +247,6 @@ where } async fn fill_gas(&self, request: &mut TransactionRequest) -> Result<()> { - request.chain_id = Some(self.chain_id()); request.from = Some(self.address()); let gas = self.estimate_gas(&WithOtherFields::new(request.clone())).await?; diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 59a96244..e1d5a931 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -9,7 +9,10 @@ use std::{ use alloy::{ network::{AnyNetwork, EthereumWallet}, primitives::ChainId, - providers::{fillers::TxFiller, Provider, ProviderBuilder, WalletProvider}, + providers::{ + fillers::{ChainIdFiller, GasFiller, TxFiller}, + Provider, ProviderBuilder, WalletProvider, + }, rpc::client::RpcClient, signers::{local::PrivateKeySigner, Signer}, transports::{http::reqwest::Url, Transport}, @@ -75,6 +78,8 @@ pub fn relay(config: Configuration) -> Result { let provider = Arc::new( ProviderBuilder::new() .with_cached_nonce_management() + .filler(GasFiller::default()) + .filler(ChainIdFiller::new(evm_provider.id.into())) .network::() .wallet(wallet) .on_client(client), From 53d508d4dd2d9372df45920e818b06a076272da4 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Mon, 23 Dec 2024 17:12:14 +0900 Subject: [PATCH 53/60] CCCP-295, refactor: roundup phase2 bootstrap --- .../src/eth/handlers/roundup_relay_handler.rs | 77 +++++++++++-------- primitives/src/bootstrap.rs | 12 +-- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 963b74f2..b297dda3 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use alloy::{ network::{primitives::ReceiptResponse as _, AnyNetwork}, @@ -16,7 +16,10 @@ use tokio_stream::{wrappers::BroadcastStream, StreamExt}; use br_primitives::{ bootstrap::BootstrapSharedData, - constants::{cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE}, + constants::{ + cli::DEFAULT_BOOTSTRAP_ROUND_OFFSET, config::BOOTSTRAP_BLOCK_CHUNK_SIZE, + tx::DEFAULT_CALL_RETRY_INTERVAL_MS, + }, contracts::socket::{ SocketContract::RoundUp, SocketInstance, @@ -67,7 +70,6 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - self.wait_for_bootstrap_state(BootstrapState::BootstrapRoundUpPhase2).await?; self.bootstrap().await?; self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; @@ -179,10 +181,23 @@ where handle: SpawnTaskHandle, debug_mode: bool, ) -> Self { + let external_clients = Arc::new( + clients + .iter() + .filter_map(|(id, client)| { + if !client.metadata.is_native { + Some((*id, client.clone())) + } else { + None + } + }) + .collect::>(), + ); + Self { event_stream: BroadcastStream::new(event_receiver), client, - external_clients: clients, + external_clients, roundup_signature: RoundUp::SIGNATURE_HASH, bootstrap_shared_data, handle, @@ -314,23 +329,21 @@ where /// Check if external clients are in the latest round. async fn wait_if_latest_round(&self) -> Result<()> { - let barrier_clone = self.bootstrap_shared_data.roundup_barrier.clone(); let external_clients = &self.external_clients; for (_, target_client) in external_clients.iter() { - let barrier_clone_inner = barrier_clone.clone(); - let current_round = - self.client.protocol_contracts.authority.latest_round().call().await?._0; - let target_chain_round = - target_client.protocol_contracts.authority.latest_round().call().await?._0; - - let bootstrap_guard = self.bootstrap_shared_data.roundup_bootstrap_count.clone(); + let this_roundup_barrier = self.bootstrap_shared_data.roundup_barrier.clone(); + let bifrost_authority = self.client.protocol_contracts.authority.clone(); + let target_authority = target_client.protocol_contracts.authority.clone(); tokio::spawn(async move { - if current_round == target_chain_round { - *bootstrap_guard.lock().await += 1; + while target_authority.latest_round().call().await.unwrap()._0 + < bifrost_authority.latest_round().call().await.unwrap()._0 + { + tokio::time::sleep(Duration::from_millis(DEFAULT_CALL_RETRY_INTERVAL_MS)).await; } - barrier_clone_inner.wait().await; + + this_roundup_barrier.wait().await; }); } @@ -350,36 +363,34 @@ where } async fn bootstrap(&self) -> Result<()> { + self.wait_for_bootstrap_state(BootstrapState::BootstrapRoundUpPhase2).await?; + log::info!( target: &self.client.get_chain_name(), "-[{}] ⚙️ [Bootstrap mode] Bootstrapping RoundUp events.", sub_display_format(SUB_LOG_TARGET), ); - let mut bootstrap_guard = self.bootstrap_shared_data.bootstrap_states.write().await; + // Fetch roundup events + let logs = self.get_bootstrap_events().await?; + for log in logs { + // Process roundup events + self.process_confirmed_log(&log, true).await?; + } + // Checking if the current round is the latest round self.wait_if_latest_round().await?; // Wait to lock after checking if it is latest round self.bootstrap_shared_data.roundup_barrier.clone().wait().await; - // if all of chain is the latest round already - if *self.bootstrap_shared_data.roundup_bootstrap_count.lock().await - == self.external_clients.len() as u8 - { - // set all of state to BootstrapSocket - for state in bootstrap_guard.iter_mut() { - *state = BootstrapState::BootstrapSocketRelay; - } - } - - if bootstrap_guard.iter().all(|s| *s == BootstrapState::BootstrapRoundUpPhase2) { - drop(bootstrap_guard); - let logs = self.get_bootstrap_events().await?; - for log in logs { - self.process_confirmed_log(&log, true).await?; - } - } + // set all of state to BootstrapSocketRelay + self.bootstrap_shared_data + .bootstrap_states + .write() + .await + .iter_mut() + .for_each(|state| *state = BootstrapState::BootstrapSocketRelay); // Poll socket barrier to call wait() let socket_barrier_clone = self.bootstrap_shared_data.socket_barrier.clone(); diff --git a/primitives/src/bootstrap.rs b/primitives/src/bootstrap.rs index ea6201e0..04017c0d 100644 --- a/primitives/src/bootstrap.rs +++ b/primitives/src/bootstrap.rs @@ -16,8 +16,6 @@ pub struct BootstrapSharedData { pub roundup_barrier: Arc, /// The current number of finished socket bootstrap processes. pub socket_bootstrap_count: Arc>, - /// The current number of finished roundup bootstrap processes. - pub roundup_bootstrap_count: Arc>, /// The current shared state of the bootstrap process. pub bootstrap_states: Arc>>, /// The bootstrap configurations. @@ -46,18 +44,14 @@ impl BootstrapSharedData { }; let socket_barrier = Arc::new(Barrier::new(socket_barrier_len)); - // For roundup barrier, we need: - // 1. One for the roundup handler for each external chain - // 2. One for the roundup emitter + // For roundup barrier, we need to count all external chains let roundup_barrier = Arc::new(Barrier::new( evm_providers .iter() .filter(|p| !p.is_native.unwrap_or(false)) // Count all external chains - .count() - .saturating_add(1), // +1 for emitter + .count(), )); let socket_bootstrap_count = Arc::new(Mutex::new(u8::default())); - let roundup_bootstrap_count = Arc::new(Mutex::new(u8::default())); let bootstrap_states = Arc::new(RwLock::new(vec![bootstrap_states; system_providers_len])); Self { @@ -65,7 +59,6 @@ impl BootstrapSharedData { socket_barrier, roundup_barrier, socket_bootstrap_count, - roundup_bootstrap_count, bootstrap_states, bootstrap_config: bootstrap_config.clone(), } @@ -77,7 +70,6 @@ impl BootstrapSharedData { socket_barrier: Arc::new(Barrier::new(0)), roundup_barrier: Arc::new(Barrier::new(0)), socket_bootstrap_count: Arc::new(Default::default()), - roundup_bootstrap_count: Arc::new(Default::default()), bootstrap_states: Arc::new(RwLock::new(vec![BootstrapState::NormalStart])), bootstrap_config: None, } From 6383157400523faa6a03e0d518ca3aeff3e23930 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Mon, 23 Dec 2024 18:18:35 +0900 Subject: [PATCH 54/60] CCCP-295, refactor: bootstrap --- client/src/btc/block.rs | 33 ++++++------- client/src/btc/handlers/inbound.rs | 3 +- client/src/btc/handlers/mod.rs | 14 +----- client/src/btc/handlers/outbound.rs | 4 +- client/src/eth/events.rs | 25 +++++----- .../src/eth/handlers/roundup_relay_handler.rs | 15 +----- .../src/eth/handlers/socket_relay_handler.rs | 9 ++-- client/src/eth/traits.rs | 12 +---- periodic/src/roundup_emitter.rs | 21 +++------ primitives/src/bootstrap.rs | 32 ++++++------- relayer/src/service.rs | 46 ++----------------- relayer/src/service_deps/full_deps.rs | 1 - 12 files changed, 62 insertions(+), 153 deletions(-) diff --git a/client/src/btc/block.rs b/client/src/btc/block.rs index 2b181261..73307e92 100644 --- a/client/src/btc/block.rs +++ b/client/src/btc/block.rs @@ -196,21 +196,22 @@ where latest_block ); + self.wait_for_bootstrap_state(BootstrapState::BootstrapSocketRelay).await?; + self.bootstrap().await?; + + self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; + let mut stream = IntervalStream::new(interval(Duration::from_millis(self.call_interval))); while let Some(_) = stream.next().await { - if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapSocketRelay).await { - self.bootstrap().await?; - } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { - let latest_block_num = self.get_block_count().await.unwrap(); - if self.is_block_confirmed(latest_block_num) { - let (vault_set, refund_set) = self.fetch_registration_sets().await?; - self.process_confirmed_block( - latest_block_num.saturating_sub(self.block_confirmations), - &vault_set, - &refund_set, - ) - .await; - } + let latest_block_num = self.get_block_count().await.unwrap(); + if self.is_block_confirmed(latest_block_num) { + let (vault_set, refund_set) = self.fetch_registration_sets().await?; + self.process_confirmed_block( + latest_block_num.saturating_sub(self.block_confirmations), + &vault_set, + &refund_set, + ) + .await; } } Ok(()) @@ -389,11 +390,7 @@ where *bootstrap_count += 1; if *bootstrap_count == self.bootstrap_shared_data.system_providers_len as u8 { - let mut bootstrap_guard = self.bootstrap_shared_data.bootstrap_states.write().await; - - for state in bootstrap_guard.iter_mut() { - *state = BootstrapState::NormalStart; - } + *self.bootstrap_shared_data.bootstrap_state.write().await = BootstrapState::NormalStart; log::info!( target: "bifrost-relayer", diff --git a/client/src/btc/handlers/inbound.rs b/client/src/btc/handlers/inbound.rs index abb56fea..12261125 100644 --- a/client/src/btc/handlers/inbound.rs +++ b/client/src/btc/handlers/inbound.rs @@ -17,6 +17,7 @@ use bitcoincore_rpc::bitcoin::Txid; use br_primitives::{ bootstrap::BootstrapSharedData, contracts::bitcoin_socket::BitcoinSocketInstance, + eth::BootstrapState, tx::{BitcoinRelayMetadata, TxRequestMetadata}, utils::sub_display_format, }; @@ -162,7 +163,7 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - self.wait_for_normal_state().await?; + self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; while let Some(Ok(msg)) = self.event_stream.next().await { if !self.bfc_client.is_selected_relayer().await? || !self.is_target_event(msg.event_type) diff --git a/client/src/btc/handlers/mod.rs b/client/src/btc/handlers/mod.rs index 76f38270..b16729d1 100644 --- a/client/src/btc/handlers/mod.rs +++ b/client/src/btc/handlers/mod.rs @@ -115,19 +115,9 @@ pub trait BootstrapHandler { /// Fetch the historical events to bootstrap. async fn get_bootstrap_events(&self) -> Result<(EventMessage, EventMessage)>; - /// Verifies whether the bootstrap state has been synced to the given state. - async fn is_bootstrap_state_synced_as(&self, state: BootstrapState) -> bool { - self.bootstrap_shared_data() - .bootstrap_states - .read() - .await - .iter() - .all(|s| *s == state) - } - /// Waits for the bootstrap state to be synced to the normal start state. - async fn wait_for_normal_state(&self) -> Result<()> { - while !self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { + async fn wait_for_bootstrap_state(&self, state: BootstrapState) -> Result<()> { + while *self.bootstrap_shared_data().bootstrap_state.read().await != state { sleep(Duration::from_millis(100)).await; } Ok(()) diff --git a/client/src/btc/handlers/outbound.rs b/client/src/btc/handlers/outbound.rs index 3c9e941a..6ed89ffe 100644 --- a/client/src/btc/handlers/outbound.rs +++ b/client/src/btc/handlers/outbound.rs @@ -20,7 +20,7 @@ use br_primitives::{ socket::{SocketContract::Socket, Socket_Struct::Socket_Message}, socket_queue::SocketQueueInstance, }, - eth::{BuiltRelayTransaction, SocketEventStatus}, + eth::{BootstrapState, BuiltRelayTransaction, SocketEventStatus}, tx::{SocketRelayMetadata, TxRequestMetadata}, utils::sub_display_format, }; @@ -103,7 +103,7 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - self.wait_for_normal_state().await?; + self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; while let Some(Ok(msg)) = self.event_stream.next().await { if !self.bfc_client.is_selected_relayer().await? || !self.is_target_event(msg.event_type) diff --git a/client/src/eth/events.rs b/client/src/eth/events.rs index 87490548..e35232a7 100644 --- a/client/src/eth/events.rs +++ b/client/src/eth/events.rs @@ -112,19 +112,19 @@ where pub async fn run(&mut self) -> Result<()> { self.initialize().await?; + self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; + let mut stream = IntervalStream::new(interval(Duration::from_millis( self.client.metadata.call_interval, ))); while let Some(_) = stream.next().await { - if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { - let latest_block = self.client.get_block_number().await?; - while self.is_block_confirmed(latest_block) { - self.process_confirmed_block().await?; + let latest_block = self.client.get_block_number().await?; + while self.is_block_confirmed(latest_block) { + self.process_confirmed_block().await?; - if self.is_balance_sync_enabled { - self.client.sync_balance().await?; - } + if self.is_balance_sync_enabled { + self.client.sync_balance().await?; } } } @@ -200,13 +200,12 @@ where loop { match self.client.syncing().await? { SyncStatus::None => { - for state in - self.bootstrap_shared_data.bootstrap_states.write().await.iter_mut() - { - if *state == BootstrapState::NodeSyncing { - *state = BootstrapState::BootstrapRoundUpPhase1; - } + let mut bootstrap_state = + self.bootstrap_shared_data.bootstrap_state.write().await; + if *bootstrap_state == BootstrapState::NodeSyncing { + *bootstrap_state = BootstrapState::BootstrapRoundUpPhase1; } + return Ok(()); }, SyncStatus::Info(status) => { diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index b297dda3..aaafd6d3 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -385,19 +385,8 @@ where self.bootstrap_shared_data.roundup_barrier.clone().wait().await; // set all of state to BootstrapSocketRelay - self.bootstrap_shared_data - .bootstrap_states - .write() - .await - .iter_mut() - .for_each(|state| *state = BootstrapState::BootstrapSocketRelay); - - // Poll socket barrier to call wait() - let socket_barrier_clone = self.bootstrap_shared_data.socket_barrier.clone(); - - tokio::spawn(async move { - socket_barrier_clone.clone().wait().await; - }); + *self.bootstrap_shared_data.bootstrap_state.write().await = + BootstrapState::BootstrapSocketRelay; Ok(()) } diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index ff44928b..ffdc94e6 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -73,7 +73,6 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - self.wait_for_bootstrap_state(BootstrapState::BootstrapSocketRelay).await?; self.bootstrap().await?; self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; @@ -497,6 +496,8 @@ where } async fn bootstrap(&self) -> Result<()> { + self.wait_for_bootstrap_state(BootstrapState::BootstrapSocketRelay).await?; + log::info!( target: &self.client.get_chain_name(), "-[{}] ⚙️ [Bootstrap mode] Bootstrapping Socket events.", @@ -515,11 +516,7 @@ where // If All thread complete the task, starts the blockManager if *bootstrap_count == self.bootstrap_shared_data.system_providers_len as u8 { - let mut bootstrap_guard = self.bootstrap_shared_data.bootstrap_states.write().await; - - for state in bootstrap_guard.iter_mut() { - *state = BootstrapState::NormalStart; - } + *self.bootstrap_shared_data.bootstrap_state.write().await = BootstrapState::NormalStart; log::info!( target: "bifrost-relayer", diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index 18530747..db853b53 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -146,19 +146,9 @@ pub trait BootstrapHandler { /// Fetch the historical events to bootstrap. async fn get_bootstrap_events(&self) -> Result>; - /// Verifies whether the bootstrap state has been synced to the given state. - async fn is_bootstrap_state_synced_as(&self, state: BootstrapState) -> bool { - self.bootstrap_shared_data() - .bootstrap_states - .read() - .await - .iter() - .all(|s| *s == state) - } - /// Waits for the bootstrap state to be synced to the normal start state. async fn wait_for_bootstrap_state(&self, state: BootstrapState) -> Result<()> { - while !self.is_bootstrap_state_synced_as(state).await { + while *self.bootstrap_shared_data().bootstrap_state.read().await != state { sleep(Duration::from_millis(100)).await; } Ok(()) diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index 751cca26..b8c515e6 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -66,16 +66,7 @@ where async fn run(&mut self) -> Result<()> { self.current_round = self.get_latest_round().await?; - loop { - if self.is_bootstrap_state_synced_as(BootstrapState::BootstrapRoundUpPhase1).await { - self.bootstrap().await?; - break; - } else if self.is_bootstrap_state_synced_as(BootstrapState::NormalStart).await { - break; - } - - sleep(Duration::from_millis(self.client.metadata.call_interval)).await; - } + self.bootstrap().await?; loop { self.wait_until_next_time().await; @@ -208,6 +199,8 @@ where } async fn bootstrap(&self) -> Result<()> { + self.wait_for_bootstrap_state(BootstrapState::BootstrapRoundUpPhase1).await?; + let get_next_poll_round = || async move { let logs = self.get_bootstrap_events().await.unwrap(); @@ -269,11 +262,11 @@ where } } - for state in self.bootstrap_shared_data.bootstrap_states.write().await.iter_mut() { - if *state == BootstrapState::BootstrapRoundUpPhase1 { - *state = BootstrapState::BootstrapRoundUpPhase2; - } + let mut lock = self.bootstrap_shared_data.bootstrap_state.write().await; + if *lock == BootstrapState::BootstrapRoundUpPhase1 { + *lock = BootstrapState::BootstrapRoundUpPhase2; } + drop(lock); Ok(()) } diff --git a/primitives/src/bootstrap.rs b/primitives/src/bootstrap.rs index 04017c0d..c1f621cf 100644 --- a/primitives/src/bootstrap.rs +++ b/primitives/src/bootstrap.rs @@ -10,14 +10,12 @@ use crate::{ pub struct BootstrapSharedData { /// The evm + non-evm providers length. pub system_providers_len: usize, - /// The barrier used to lock the system until the socket bootstrap process is done. - pub socket_barrier: Arc, /// The barrier used to lock the system until the roundup bootstrap process is done. pub roundup_barrier: Arc, /// The current number of finished socket bootstrap processes. pub socket_bootstrap_count: Arc>, /// The current shared state of the bootstrap process. - pub bootstrap_states: Arc>>, + pub bootstrap_state: Arc>, /// The bootstrap configurations. pub bootstrap_config: Option, } @@ -31,35 +29,32 @@ impl BootstrapSharedData { let system_providers_len = evm_providers.len().saturating_add(1); // add 1 for Bitcoin - let (bootstrap_states, socket_barrier_len): (BootstrapState, usize) = { - let mut ret: (BootstrapState, usize) = - (BootstrapState::NormalStart, system_providers_len); - if let Some(bootstrap_config) = bootstrap_config.clone() { - if bootstrap_config.is_enabled { - ret.0 = BootstrapState::NodeSyncing; - ret.1 = ret.1.saturating_add(1); // add 1 for roundup handler - } + let bootstrap_state = if let Some(bootstrap_config) = bootstrap_config.clone() { + if bootstrap_config.is_enabled { + BootstrapState::NodeSyncing + } else { + BootstrapState::NormalStart } - ret + } else { + BootstrapState::NormalStart }; - let socket_barrier = Arc::new(Barrier::new(socket_barrier_len)); // For roundup barrier, we need to count all external chains let roundup_barrier = Arc::new(Barrier::new( evm_providers .iter() .filter(|p| !p.is_native.unwrap_or(false)) // Count all external chains - .count(), + .count() + .saturating_add(1), )); let socket_bootstrap_count = Arc::new(Mutex::new(u8::default())); - let bootstrap_states = Arc::new(RwLock::new(vec![bootstrap_states; system_providers_len])); + let bootstrap_state = Arc::new(RwLock::new(bootstrap_state)); Self { system_providers_len, - socket_barrier, roundup_barrier, socket_bootstrap_count, - bootstrap_states, + bootstrap_state, bootstrap_config: bootstrap_config.clone(), } } @@ -67,10 +62,9 @@ impl BootstrapSharedData { pub fn dummy() -> Self { Self { system_providers_len: 0, - socket_barrier: Arc::new(Barrier::new(0)), roundup_barrier: Arc::new(Barrier::new(0)), socket_bootstrap_count: Arc::new(Default::default()), - bootstrap_states: Arc::new(RwLock::new(vec![BootstrapState::NormalStart])), + bootstrap_state: Arc::new(RwLock::new(BootstrapState::NormalStart)), bootstrap_config: None, } } diff --git a/relayer/src/service.rs b/relayer/src/service.rs index e1d5a931..7ea70de5 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -35,7 +35,7 @@ use br_primitives::{ errors::{INVALID_BITCOIN_NETWORK, INVALID_PRIVATE_KEY, INVALID_PROVIDER_URL}, tx::DEFAULT_CALL_RETRIES, }, - eth::{AggregatorContracts, BootstrapState, ProtocolContracts, ProviderMetadata}, + eth::{AggregatorContracts, ProtocolContracts, ProviderMetadata}, substrate::MigrationSequence, utils::sub_display_format, }; @@ -162,14 +162,7 @@ pub fn relay(config: Configuration) -> Result { Ok(spawn_relayer_tasks( task_manager, - FullDeps { - bootstrap_shared_data, - manager_deps, - periodic_deps, - handler_deps, - substrate_deps, - btc_deps, - }, + FullDeps { manager_deps, periodic_deps, handler_deps, substrate_deps, btc_deps }, &config, )) } @@ -187,16 +180,8 @@ where { let prometheus_config = &config.relayer_config.prometheus_config; - let FullDeps { - bootstrap_shared_data, - manager_deps, - periodic_deps, - handler_deps, - substrate_deps, - btc_deps, - } = deps; + let FullDeps { manager_deps, periodic_deps, handler_deps, substrate_deps, btc_deps } = deps; - let BootstrapSharedData { socket_barrier, bootstrap_states, .. } = bootstrap_shared_data; let ManagerDeps { event_managers, .. } = manager_deps; let PeriodicDeps { mut heartbeat_sender, @@ -325,9 +310,6 @@ where // spawn socket relay handlers socket_relay_handlers.into_iter().for_each(|mut handler| { - let socket_barrier_clone = socket_barrier.clone(); - let is_bootstrapped = bootstrap_states.clone(); - task_manager.spawn_essential_handle().spawn( Box::leak( format!("{}-{}-handler", handler.client.get_chain_name(), HandlerType::Socket,) @@ -335,17 +317,6 @@ where ), Some("handlers"), async move { - socket_barrier_clone.wait().await; - - // After All of barrier complete the waiting - let mut guard = is_bootstrapped.write().await; - if guard.iter().all(|s| *s == BootstrapState::BootstrapRoundUpPhase2) { - for state in guard.iter_mut() { - *state = BootstrapState::BootstrapSocketRelay; - } - } - drop(guard); - loop { let report = handler.run().await; let log_msg = format!( @@ -509,17 +480,6 @@ where "bitcoin-block-manager", Some("block-manager"), async move { - socket_barrier.wait().await; - - // After All of barrier complete the waiting - let mut guard = bootstrap_states.write().await; - if guard.iter().all(|s| *s == BootstrapState::BootstrapRoundUpPhase2) { - for state in guard.iter_mut() { - *state = BootstrapState::BootstrapSocketRelay; - } - } - drop(guard); - loop { let report = block_manager.run().await; let log_msg = format!( diff --git a/relayer/src/service_deps/full_deps.rs b/relayer/src/service_deps/full_deps.rs index 11674ce4..d2d160ac 100644 --- a/relayer/src/service_deps/full_deps.rs +++ b/relayer/src/service_deps/full_deps.rs @@ -7,7 +7,6 @@ where P: Provider + 'static, T: Transport + Clone, { - pub bootstrap_shared_data: BootstrapSharedData, pub manager_deps: ManagerDeps, pub periodic_deps: PeriodicDeps, pub handler_deps: HandlerDeps, From 6547ffd8edb66808c360a91dbba5406d9935a700 Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Fri, 27 Dec 2024 13:20:07 +0900 Subject: [PATCH 55/60] CCCP-295, refactor: --- client/src/btc/block.rs | 2 +- client/src/eth/events.rs | 2 +- .../src/eth/handlers/roundup_relay_handler.rs | 30 ++++++++----------- relayer/src/service.rs | 2 +- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/client/src/btc/block.rs b/client/src/btc/block.rs index 73307e92..1a635579 100644 --- a/client/src/btc/block.rs +++ b/client/src/btc/block.rs @@ -202,7 +202,7 @@ where self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; let mut stream = IntervalStream::new(interval(Duration::from_millis(self.call_interval))); - while let Some(_) = stream.next().await { + while (stream.next().await).is_some() { let latest_block_num = self.get_block_count().await.unwrap(); if self.is_block_confirmed(latest_block_num) { let (vault_set, refund_set) = self.fetch_registration_sets().await?; diff --git a/client/src/eth/events.rs b/client/src/eth/events.rs index e35232a7..1b342cf4 100644 --- a/client/src/eth/events.rs +++ b/client/src/eth/events.rs @@ -118,7 +118,7 @@ where self.client.metadata.call_interval, ))); - while let Some(_) = stream.next().await { + while (stream.next().await).is_some() { let latest_block = self.client.get_block_number().await?; while self.is_block_confirmed(latest_block) { self.process_confirmed_block().await?; diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index aaafd6d3..32f729da 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -227,7 +227,7 @@ where let mut signature_vec = Vec::::from(signatures); signature_vec - .sort_by_key(|k| recover_message(*k, &encode_roundup_param(round, &new_relayers))); + .sort_by_key(|k| recover_message(*k, &encode_roundup_param(round, new_relayers))); Ok(Signatures::from(signature_vec)) } @@ -292,23 +292,19 @@ where )); if is_bootstrap { - loop { - if let Err(e) = target_client - .sync_send_transaction( - transaction_request.clone(), - SUB_LOG_TARGET.to_string(), - metadata.clone(), - ) - .await - { - if e.to_string().to_lowercase().contains("nonce too low") { - target_client.flush_stalled_transactions().await?; - continue; - } else { - eyre::bail!(e); - } + while let Err(e) = target_client + .sync_send_transaction( + transaction_request.clone(), + SUB_LOG_TARGET.to_string(), + metadata.clone(), + ) + .await + { + if e.to_string().to_lowercase().contains("nonce too low") { + target_client.flush_stalled_transactions().await?; + continue; } else { - break; + eyre::bail!(e); } } } else { diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 7ea70de5..7d4c086a 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -78,7 +78,7 @@ pub fn relay(config: Configuration) -> Result { let provider = Arc::new( ProviderBuilder::new() .with_cached_nonce_management() - .filler(GasFiller::default()) + .filler(GasFiller) .filler(ChainIdFiller::new(evm_provider.id.into())) .network::() .wallet(wallet) From 84f6bcef2393bd62afeaec531960a884f0c2bf5a Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 2 Jan 2025 15:07:46 +0900 Subject: [PATCH 56/60] CCCP-295, fix: comparison by ord for bootstrapstate --- client/src/btc/handlers/mod.rs | 2 +- client/src/eth/traits.rs | 2 +- primitives/src/eth.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/btc/handlers/mod.rs b/client/src/btc/handlers/mod.rs index b16729d1..544cd84f 100644 --- a/client/src/btc/handlers/mod.rs +++ b/client/src/btc/handlers/mod.rs @@ -117,7 +117,7 @@ pub trait BootstrapHandler { /// Waits for the bootstrap state to be synced to the normal start state. async fn wait_for_bootstrap_state(&self, state: BootstrapState) -> Result<()> { - while *self.bootstrap_shared_data().bootstrap_state.read().await != state { + while *self.bootstrap_shared_data().bootstrap_state.read().await < state { sleep(Duration::from_millis(100)).await; } Ok(()) diff --git a/client/src/eth/traits.rs b/client/src/eth/traits.rs index db853b53..cf9d6679 100644 --- a/client/src/eth/traits.rs +++ b/client/src/eth/traits.rs @@ -148,7 +148,7 @@ pub trait BootstrapHandler { /// Waits for the bootstrap state to be synced to the normal start state. async fn wait_for_bootstrap_state(&self, state: BootstrapState) -> Result<()> { - while *self.bootstrap_shared_data().bootstrap_state.read().await != state { + while *self.bootstrap_shared_data().bootstrap_state.read().await < state { sleep(Duration::from_millis(100)).await; } Ok(()) diff --git a/primitives/src/eth.rs b/primitives/src/eth.rs index cbe7c50f..5112a467 100644 --- a/primitives/src/eth.rs +++ b/primitives/src/eth.rs @@ -374,7 +374,7 @@ pub enum RelayDirection { Outbound, } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] /// The state for bootstrapping pub enum BootstrapState { /// phase 0. check if the node is in syncing From 889855d19f1013b09aa9228d14e2b49010c0004f Mon Sep 17 00:00:00 2001 From: alstjd0921 Date: Thu, 2 Jan 2025 15:22:47 +0900 Subject: [PATCH 57/60] CCCP-295, fix: skip bootstrap when start from NormalStart --- client/src/btc/block.rs | 9 +++++++-- client/src/eth/handlers/roundup_relay_handler.rs | 6 +++++- client/src/eth/handlers/socket_relay_handler.rs | 6 +++++- periodic/src/roundup_emitter.rs | 6 +++++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/client/src/btc/block.rs b/client/src/btc/block.rs index 1a635579..eb2ff1b3 100644 --- a/client/src/btc/block.rs +++ b/client/src/btc/block.rs @@ -196,8 +196,11 @@ where latest_block ); - self.wait_for_bootstrap_state(BootstrapState::BootstrapSocketRelay).await?; - self.bootstrap().await?; + if *self.bootstrap_shared_data.bootstrap_state.read().await + <= BootstrapState::BootstrapSocketRelay + { + self.bootstrap().await?; + } self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; @@ -375,6 +378,8 @@ where } async fn bootstrap(&self) -> Result<()> { + self.wait_for_bootstrap_state(BootstrapState::BootstrapSocketRelay).await?; + log::info!( target: LOG_TARGET, "-[{}] ⚙️ [Bootstrap mode] Bootstrapping Bitcoin events", diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index 32f729da..dcc5ea96 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -70,7 +70,11 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - self.bootstrap().await?; + if *self.bootstrap_shared_data.bootstrap_state.read().await + <= BootstrapState::BootstrapRoundUpPhase2 + { + self.bootstrap().await?; + } self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; while let Some(Ok(msg)) = self.event_stream.next().await { diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index ffdc94e6..87d59154 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -73,7 +73,11 @@ where T: Transport + Clone, { async fn run(&mut self) -> Result<()> { - self.bootstrap().await?; + if *self.bootstrap_shared_data.bootstrap_state.read().await + <= BootstrapState::BootstrapSocketRelay + { + self.bootstrap().await?; + } self.wait_for_bootstrap_state(BootstrapState::NormalStart).await?; while let Some(Ok(msg)) = self.event_stream.next().await { diff --git a/periodic/src/roundup_emitter.rs b/periodic/src/roundup_emitter.rs index b8c515e6..1d4e2e2f 100644 --- a/periodic/src/roundup_emitter.rs +++ b/periodic/src/roundup_emitter.rs @@ -66,7 +66,11 @@ where async fn run(&mut self) -> Result<()> { self.current_round = self.get_latest_round().await?; - self.bootstrap().await?; + if *self.bootstrap_shared_data.bootstrap_state.read().await + <= BootstrapState::BootstrapRoundUpPhase1 + { + self.bootstrap().await?; + } loop { self.wait_until_next_time().await; From 2b0a3523859dfd960fc1e3754df7395fe24bfd69 Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Thu, 2 Jan 2025 18:16:09 +0900 Subject: [PATCH 58/60] CCCP-295, fix: use signature constant --- client/src/eth/handlers/roundup_relay_handler.rs | 7 ++----- client/src/eth/handlers/socket_relay_handler.rs | 9 +++------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/client/src/eth/handlers/roundup_relay_handler.rs b/client/src/eth/handlers/roundup_relay_handler.rs index dcc5ea96..25eedd3e 100644 --- a/client/src/eth/handlers/roundup_relay_handler.rs +++ b/client/src/eth/handlers/roundup_relay_handler.rs @@ -52,8 +52,6 @@ where event_stream: BroadcastStream, /// `EthClient`s to interact with provided networks except bifrost network. external_clients: Arc>, - /// Signature of RoundUp Event. - roundup_signature: B256, /// The bootstrap shared data. bootstrap_shared_data: Arc, /// The handle to spawn tasks. @@ -164,7 +162,7 @@ where fn is_target_event(&self, topic: Option<&B256>) -> bool { match topic { - Some(topic) => topic == &self.roundup_signature, + Some(topic) => topic == &RoundUp::SIGNATURE_HASH, None => false, } } @@ -202,7 +200,6 @@ where event_stream: BroadcastStream::new(event_receiver), client, external_clients, - roundup_signature: RoundUp::SIGNATURE_HASH, bootstrap_shared_data, handle, debug_mode, @@ -414,7 +411,7 @@ where let filter = Filter::new() .address(*self.client.protocol_contracts.socket.address()) - .event_signature(self.roundup_signature) + .event_signature(RoundUp::SIGNATURE_HASH) .from_block(from_block) .to_block(chunk_to_block); diff --git a/client/src/eth/handlers/socket_relay_handler.rs b/client/src/eth/handlers/socket_relay_handler.rs index 87d59154..c0f25a46 100644 --- a/client/src/eth/handlers/socket_relay_handler.rs +++ b/client/src/eth/handlers/socket_relay_handler.rs @@ -57,8 +57,6 @@ where rollback_senders: Arc>>>, /// The handle to spawn tasks. handle: SpawnTaskHandle, - /// Signature of the `Socket` Event. - socket_signature: B256, /// The bootstrap shared data. bootstrap_shared_data: Arc, /// Whether to enable debug mode. @@ -153,7 +151,7 @@ where #[inline] fn is_target_event(&self, topic: Option<&B256>) -> bool { match topic { - Some(topic) => topic == &self.socket_signature, + Some(topic) => topic == &Socket::SIGNATURE_HASH, None => false, } } @@ -275,7 +273,6 @@ where Self { event_stream: BroadcastStream::new(event_receiver), - socket_signature: Socket::SIGNATURE_HASH, client, system_clients, bifrost_client, @@ -575,13 +572,13 @@ where *self.client.protocol_contracts.socket.address(), *bitcoin_socket.address(), ]) - .event_signature(self.socket_signature) + .event_signature(Socket::SIGNATURE_HASH) .from_block(from_block) .to_block(chunk_to_block) } else { Filter::new() .address(*self.client.protocol_contracts.socket.address()) - .event_signature(self.socket_signature) + .event_signature(Socket::SIGNATURE_HASH) .from_block(from_block) .to_block(chunk_to_block) }; From c98b3e5ad7951a4588a4774dc694c76154e59c6a Mon Sep 17 00:00:00 2001 From: dnjscksdn98 Date: Mon, 6 Jan 2025 14:27:02 +0900 Subject: [PATCH 59/60] CCCP-295, deps: release v2.1.1 --- relayer/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index bd440ff7..1e28114c 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bifrost-relayer" -version = "2.0.1" +version = "2.1.1" description = "The core rust implementation for Bifrost relayer" authors = { workspace = true } homepage = { workspace = true } From 25856d5cb0a10cda5d116d61de4cccaac2448739 Mon Sep 17 00:00:00 2001 From: Min-seong Kwon Date: Thu, 9 Jan 2025 17:58:01 +0900 Subject: [PATCH 60/60] CCCP-475 integrate aws-kms (#170) * CCCP-475, feature: dynamic signer (Local | AWS KMS) * CCCP-475, fix: make `private_key` field optional --- Cargo.toml | 4 + client/src/eth/mod.rs | 12 ++- configs/config.mainnet.yaml | 7 +- configs/config.testnet.yaml | 7 +- primitives/src/cli.rs | 12 ++- primitives/src/constants/errors.rs | 3 + relayer/Cargo.toml | 2 + relayer/src/main.rs | 2 +- relayer/src/service.rs | 114 ++++++++++++++++++++--------- 9 files changed, 115 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f660d165..0b2c801c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,10 +62,14 @@ alloy = { version = "0.8", features = [ "json-rpc", "transports", "k256", + "signer-aws", ] } k256 = "0.13.1" sha3 = "0.10.8" +aws-sdk-kms = "1.54.0" +aws-config = { version = "1.5.13", features = ["behavior-version-latest"] } + miniscript = "11.0.0" bitcoincore-rpc = { git = "https://github.com/bifrost-platform/rust-bitcoincore-rpc", branch = "reqwest-migration" } diff --git a/client/src/eth/mod.rs b/client/src/eth/mod.rs index e53dc232..e88dcdff 100644 --- a/client/src/eth/mod.rs +++ b/client/src/eth/mod.rs @@ -14,6 +14,7 @@ use alloy::{ eips::BlockNumberOrTag, network::{AnyNetwork, AnyRpcTransaction, AnyTypedTransaction, TransactionResponse as _}, primitives::{ + keccak256, utils::{format_units, parse_ether, Unit}, Address, ChainId, U64, }, @@ -23,13 +24,11 @@ use alloy::{ EthCall, PendingTransactionBuilder, Provider, RootProvider, SendableTx, WalletProvider, }, rpc::types::{serde_helpers::WithOtherFields, txpool::TxpoolContent, TransactionRequest}, - signers::{local::LocalSigner, Signature}, + signers::{Signature, Signer}, transports::{Transport, TransportResult}, }; use eyre::{eyre, Result}; -use k256::ecdsa::SigningKey; use sc_service::SpawnTaskHandle; -use sha3::{Digest, Keccak256}; use std::{ cmp::max, collections::{BTreeMap, VecDeque}, @@ -55,7 +54,7 @@ where /// The inner provider. inner: Arc>, /// The signer. - pub signer: LocalSigner, + pub signer: Arc, /// The provider metadata. pub metadata: ProviderMetadata, /// The protocol contracts. @@ -75,7 +74,7 @@ where /// Create a new EthClient pub fn new( inner: Arc>, - signer: LocalSigner, + signer: Arc, metadata: ProviderMetadata, protocol_contracts: ProtocolContracts, aggregator_contracts: AggregatorContracts, @@ -146,8 +145,7 @@ where /// Signs the given message. pub async fn sign_message(&self, message: &[u8]) -> Result { - let cred = self.signer.credential(); - Ok(Signature::from(cred.sign_digest_recoverable(Keccak256::new_with_prefix(message))?)) + Ok(self.signer.sign_hash(&keccak256(message)).await?) } /// Get the bootstrap offset height based on the block time. diff --git a/configs/config.mainnet.yaml b/configs/config.mainnet.yaml index 827e5b56..3685c354 100644 --- a/configs/config.mainnet.yaml +++ b/configs/config.mainnet.yaml @@ -1,9 +1,14 @@ system: - private_key: "" keystore_path: "" keystore_password: "" debug_mode: false +signer_config: + # Two parameters below are optional, but one of them must be provided. + # `kms_key_id` has higher priority than `private_key`. + kms_key_id: "" + private_key: "" + btc_provider: id: 10000 chain: "main" diff --git a/configs/config.testnet.yaml b/configs/config.testnet.yaml index 8bb156e2..bf0762ae 100644 --- a/configs/config.testnet.yaml +++ b/configs/config.testnet.yaml @@ -1,9 +1,14 @@ system: - private_key: "" keystore_path: "" keystore_password: "" debug_mode: false +signer_config: + # Two parameters below are optional, but one of them must be provided. + # `kms_key_id` has higher priority than `private_key`. + kms_key_id: "" + private_key: "" + btc_provider: id: 10001 chain: "test" diff --git a/primitives/src/cli.rs b/primitives/src/cli.rs index bec14156..d0a73c16 100644 --- a/primitives/src/cli.rs +++ b/primitives/src/cli.rs @@ -39,6 +39,8 @@ pub struct Configuration { pub struct RelayerConfig { /// System config pub system: SystemConfig, + /// Signer config + pub signer_config: SignerConfig, /// EVM configs pub evm_providers: Vec, /// BTC configs @@ -55,8 +57,6 @@ pub struct RelayerConfig { #[derive(Debug, Clone, Deserialize)] pub struct SystemConfig { - /// The private key of the relayer. - pub private_key: String, /// Path of the keystore. (default: `./keys`) pub keystore_path: Option, /// Password of the keystore. (default: `None`) @@ -209,3 +209,11 @@ pub struct PrometheusConfig { /// Prometheus exporter TCP Port. pub port: Option, } + +#[derive(Debug, Clone, Deserialize)] +pub struct SignerConfig { + /// AWS KMS key ID. (to use AwsSigner) + pub kms_key_id: Option, + /// The private key of the relayer. (to use LocalSigner) + pub private_key: Option, +} diff --git a/primitives/src/constants/errors.rs b/primitives/src/constants/errors.rs index 99dbb9b3..775887d7 100644 --- a/primitives/src/constants/errors.rs +++ b/primitives/src/constants/errors.rs @@ -56,3 +56,6 @@ pub const KEYSTORE_INTERNAL_ERROR: &str = pub const PARAMETER_OUT_OF_RANGE: &str = "An invalid parameter is out of range. Please check your configuration file."; + +pub const KMS_INITIALIZATION_ERROR: &str = + "An internal error thrown when initializing the KMS. Please check your KMS's status."; diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index 1e28114c..59f04ed7 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -28,6 +28,8 @@ bitcoincore-rpc = { workspace = true } subxt = { workspace = true } eyre = { workspace = true } sentry = { workspace = true } +aws-sdk-kms = { workspace = true } +aws-config = { workspace = true } # Bifrost Relayer br-cli = { path = "../client/cli", default-features = false } diff --git a/relayer/src/main.rs b/relayer/src/main.rs index 0f007ecb..5bbd392f 100644 --- a/relayer/src/main.rs +++ b/relayer/src/main.rs @@ -50,7 +50,7 @@ fn main() { let runner = Runner::new(configuration, tokio_runtime).unwrap(); runner .run_relayer_until_exit(|config| async move { - service::relay(config).map_err(sc_cli::Error::Service) + service::relay(config).await.map_err(sc_cli::Error::Service) }) .unwrap_or_default(); } diff --git a/relayer/src/service.rs b/relayer/src/service.rs index 7d4c086a..d4009843 100644 --- a/relayer/src/service.rs +++ b/relayer/src/service.rs @@ -8,13 +8,12 @@ use std::{ use alloy::{ network::{AnyNetwork, EthereumWallet}, - primitives::ChainId, providers::{ fillers::{ChainIdFiller, GasFiller, TxFiller}, Provider, ProviderBuilder, WalletProvider, }, rpc::client::RpcClient, - signers::{local::PrivateKeySigner, Signer}, + signers::{aws::AwsSigner, local::PrivateKeySigner, Signer}, transports::{http::reqwest::Url, Transport}, }; use futures::FutureExt; @@ -32,7 +31,10 @@ use br_primitives::{ cli::{Configuration, HandlerType}, constants::{ cli::{DEFAULT_KEYSTORE_PATH, DEFAULT_PROMETHEUS_PORT}, - errors::{INVALID_BITCOIN_NETWORK, INVALID_PRIVATE_KEY, INVALID_PROVIDER_URL}, + errors::{ + INVALID_BITCOIN_NETWORK, INVALID_PRIVATE_KEY, INVALID_PROVIDER_URL, + KMS_INITIALIZATION_ERROR, + }, tx::DEFAULT_CALL_RETRIES, }, eth::{AggregatorContracts, ProtocolContracts, ProviderMetadata}, @@ -47,7 +49,7 @@ use crate::{ }; /// Starts the relayer service. -pub fn relay(config: Configuration) -> Result { +pub async fn relay(config: Configuration) -> Result { assert_configuration_validity(&config); let task_manager = TaskManager::new(config.clone().tokio_handle, None)?; @@ -56,43 +58,82 @@ pub fn relay(config: Configuration) -> Result { let btc_provider = &config.relayer_config.btc_provider; let system = &config.relayer_config.system; - let clients = evm_providers - .iter() - .map(|evm_provider| { - let url: Url = evm_provider.provider.clone().parse().expect(INVALID_PROVIDER_URL); - let is_native = evm_provider.is_native.unwrap_or(false); + let mut clients = BTreeMap::new(); + for evm_provider in evm_providers { + let url: Url = evm_provider.provider.clone().parse().expect(INVALID_PROVIDER_URL); + let is_native = evm_provider.is_native.unwrap_or(false); - let mut signer = - PrivateKeySigner::from_str(&system.private_key).expect(INVALID_PRIVATE_KEY); - signer.set_chain_id(Some(evm_provider.id)); - let wallet = EthereumWallet::from(signer.clone()); - - let client = RpcClient::builder() - .layer(RetryBackoffLayer::new( - DEFAULT_CALL_RETRIES, - evm_provider.call_interval, - evm_provider.name.clone(), - )) - .http(url.clone()) - .with_poll_interval(Duration::from_millis(evm_provider.call_interval)); + let metadata = ProviderMetadata::new( + evm_provider.clone(), + url.clone(), + if is_native { Some(btc_provider.id) } else { None }, + is_native, + ); + + let retry_client = RpcClient::builder() + .layer(RetryBackoffLayer::new( + DEFAULT_CALL_RETRIES, + evm_provider.call_interval, + evm_provider.name.clone(), + )) + .http(url.clone()) + .with_poll_interval(Duration::from_millis(evm_provider.call_interval)); + let provider_builder = ProviderBuilder::new() + .with_cached_nonce_management() + .filler(GasFiller) + .filler(ChainIdFiller::new(evm_provider.id.into())) + .network::(); + + let (id, client) = if let Some(key_id) = &config.relayer_config.signer_config.kms_key_id { + let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; + let client = aws_sdk_kms::Client::new(&config); + let signer = Arc::new( + AwsSigner::new(client, key_id.clone(), evm_provider.id.into()) + .await + .expect(KMS_INITIALIZATION_ERROR), + ); let provider = Arc::new( - ProviderBuilder::new() - .with_cached_nonce_management() - .filler(GasFiller) - .filler(ChainIdFiller::new(evm_provider.id.into())) - .network::() - .wallet(wallet) - .on_client(client), + provider_builder + .wallet(EthereumWallet::from(signer.clone())) + .on_client(retry_client), ); let client = Arc::new(EthClient::new( provider.clone(), signer.clone(), - ProviderMetadata::new( - evm_provider.clone(), - url.clone(), - if is_native { Some(btc_provider.id) } else { None }, - is_native, + metadata, + ProtocolContracts::new(is_native, provider.clone(), evm_provider.clone()), + AggregatorContracts::new( + provider.clone(), + evm_provider.chainlink_usdc_usd_address.clone(), + evm_provider.chainlink_usdt_usd_address.clone(), + evm_provider.chainlink_dai_usd_address.clone(), + evm_provider.chainlink_btc_usd_address.clone(), + evm_provider.chainlink_wbtc_usd_address.clone(), + evm_provider.chainlink_cbbtc_usd_address.clone(), ), + )); + (evm_provider.id, client) + } else { + let mut signer = PrivateKeySigner::from_str( + &config + .relayer_config + .signer_config + .private_key + .clone() + .expect(INVALID_PRIVATE_KEY), + ) + .expect(INVALID_PRIVATE_KEY); + signer.set_chain_id(Some(evm_provider.id)); + let signer = Arc::new(signer); + let provider = Arc::new( + provider_builder + .wallet(EthereumWallet::from(signer.clone())) + .on_client(retry_client), + ); + let client = Arc::new(EthClient::new( + provider.clone(), + signer.clone(), + metadata, ProtocolContracts::new(is_native, provider.clone(), evm_provider.clone()), AggregatorContracts::new( provider.clone(), @@ -105,8 +146,9 @@ pub fn relay(config: Configuration) -> Result { ), )); (evm_provider.id, client) - }) - .collect::>(); + }; + clients.insert(id, client); + } let bootstrap_shared_data = BootstrapSharedData::new(&config);