diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85e7c1d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea/ diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 1eb8806..811f793 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -5,3 +5,4 @@ libs = ["lib"] solc_version = "0.8.24" fs_permissions = [{ access = "read-write", path = "./"}] evm_version = "shanghai" +extra_output_files = ['abi'] diff --git a/services/go-filler/.gitignore b/services/go-filler/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/services/go-filler/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/services/go-filler/Makefile b/services/go-filler/Makefile new file mode 100644 index 0000000..1c17428 --- /dev/null +++ b/services/go-filler/Makefile @@ -0,0 +1,15 @@ +-include .env +export + +.PHONY: bindings + +test-log-fetcher: + go test ./log-fetcher/internal/... + +run-log-fetcher: + go run ./log-fetcher/cmd + +bindings: + go install github.com/ethereum/go-ethereum/cmd/abigen@v1.14.11 + cd ../../contracts && forge build + abigen --abi ../../contracts/out/RIP7755Outbox.sol/RIP7755Outbox.abi.json --pkg bindings --type RIP7755Outbox --out bindings/rip_7755_outbox.go diff --git a/services/go-filler/README.md b/services/go-filler/README.md index 350c2ad..47a49db 100644 --- a/services/go-filler/README.md +++ b/services/go-filler/README.md @@ -1 +1,45 @@ -# An Example Filler App implemented in Go \ No newline at end of file +# An Example Fulfiller App implemented in Go + +## Getting Started + +Navigate to the `go-filler` directory: + +```bash +cd services/go-filler +``` + +Install dependencies: + +```bash +go mod tidy +``` + +Spin up the docker containers: + +```bash +docker-compose up -d +``` + +Create a `.env` file (the rpc urls must be websocket): + +```txt +ARBITRUM_SEPOLIA_RPC= +BASE_SEPOLIA_RPC= +OPTIMISM_SEPOLIA_RPC= +SEPOLIA_RPC= +MONGO_URI= +``` + +### Log Fetcher + +Run the log fetcher: + +```bash +make run-log-fetcher +``` + +Run log fetcher unit tests: + +```bash +make test-log-fetcher +``` diff --git a/services/go-filler/bindings/rip_7755_outbox.go b/services/go-filler/bindings/rip_7755_outbox.go new file mode 100644 index 0000000..45e1897 --- /dev/null +++ b/services/go-filler/bindings/rip_7755_outbox.go @@ -0,0 +1,688 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindings + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// Call is an auto generated low-level Go binding around an user-defined struct. +type Call struct { + To common.Address + Data []byte + Value *big.Int +} + +// CrossChainRequest is an auto generated low-level Go binding around an user-defined struct. +type CrossChainRequest struct { + Requester common.Address + Calls []Call + ProverContract common.Address + DestinationChainId *big.Int + InboxContract common.Address + L2Oracle common.Address + L2OracleStorageKey [32]byte + RewardAsset common.Address + RewardAmount *big.Int + FinalityDelaySeconds *big.Int + Nonce *big.Int + Expiry *big.Int + PrecheckContract common.Address + PrecheckData []byte +} + +// RIP7755InboxFulfillmentInfo is an auto generated low-level Go binding around an user-defined struct. +type RIP7755InboxFulfillmentInfo struct { + Timestamp *big.Int + Filler common.Address +} + +// RIP7755OutboxMetaData contains all meta data concerning the RIP7755Outbox contract. +var RIP7755OutboxMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"CANCEL_DELAY_SECONDS\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"cancelRequest\",\"inputs\":[{\"name\":\"request\",\"type\":\"tuple\",\"internalType\":\"structCrossChainRequest\",\"components\":[{\"name\":\"requester\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"calls\",\"type\":\"tuple[]\",\"internalType\":\"structCall[]\",\"components\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"proverContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"inboxContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2Oracle\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2OracleStorageKey\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardAsset\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"rewardAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"finalityDelaySeconds\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expiry\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"precheckContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"precheckData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"claimReward\",\"inputs\":[{\"name\":\"request\",\"type\":\"tuple\",\"internalType\":\"structCrossChainRequest\",\"components\":[{\"name\":\"requester\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"calls\",\"type\":\"tuple[]\",\"internalType\":\"structCall[]\",\"components\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"proverContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"inboxContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2Oracle\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2OracleStorageKey\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardAsset\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"rewardAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"finalityDelaySeconds\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expiry\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"precheckContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"precheckData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"fulfillmentInfo\",\"type\":\"tuple\",\"internalType\":\"structRIP7755Inbox.FulfillmentInfo\",\"components\":[{\"name\":\"timestamp\",\"type\":\"uint96\",\"internalType\":\"uint96\"},{\"name\":\"filler\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"name\":\"proof\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"payTo\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getRequestStatus\",\"inputs\":[{\"name\":\"requestHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"enumRIP7755Outbox.CrossChainCallStatus\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"hashRequest\",\"inputs\":[{\"name\":\"request\",\"type\":\"tuple\",\"internalType\":\"structCrossChainRequest\",\"components\":[{\"name\":\"requester\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"calls\",\"type\":\"tuple[]\",\"internalType\":\"structCall[]\",\"components\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"proverContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"inboxContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2Oracle\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2OracleStorageKey\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardAsset\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"rewardAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"finalityDelaySeconds\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expiry\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"precheckContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"precheckData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"hashRequestMemory\",\"inputs\":[{\"name\":\"request\",\"type\":\"tuple\",\"internalType\":\"structCrossChainRequest\",\"components\":[{\"name\":\"requester\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"calls\",\"type\":\"tuple[]\",\"internalType\":\"structCall[]\",\"components\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"proverContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"inboxContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2Oracle\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2OracleStorageKey\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardAsset\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"rewardAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"finalityDelaySeconds\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expiry\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"precheckContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"precheckData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"requestCrossChainCall\",\"inputs\":[{\"name\":\"request\",\"type\":\"tuple\",\"internalType\":\"structCrossChainRequest\",\"components\":[{\"name\":\"requester\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"calls\",\"type\":\"tuple[]\",\"internalType\":\"structCall[]\",\"components\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"proverContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"inboxContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2Oracle\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2OracleStorageKey\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardAsset\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"rewardAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"finalityDelaySeconds\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expiry\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"precheckContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"precheckData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"event\",\"name\":\"CrossChainCallCanceled\",\"inputs\":[{\"name\":\"requestHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"CrossChainCallRequested\",\"inputs\":[{\"name\":\"requestHash\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"request\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structCrossChainRequest\",\"components\":[{\"name\":\"requester\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"calls\",\"type\":\"tuple[]\",\"internalType\":\"structCall[]\",\"components\":[{\"name\":\"to\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"name\":\"proverContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"inboxContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2Oracle\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"l2OracleStorageKey\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"rewardAsset\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"rewardAmount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"finalityDelaySeconds\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expiry\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"precheckContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"precheckData\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"AddressEmptyCode\",\"inputs\":[{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"AddressInsufficientBalance\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"CannotCancelRequestBeforeExpiry\",\"inputs\":[{\"name\":\"currentTimestamp\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"expiry\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ExpiryTooSoon\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"FailedInnerCall\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidCaller\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"expectedCaller\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"InvalidStatus\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint8\",\"internalType\":\"enumRIP7755Outbox.CrossChainCallStatus\"},{\"name\":\"actual\",\"type\":\"uint8\",\"internalType\":\"enumRIP7755Outbox.CrossChainCallStatus\"}]},{\"type\":\"error\",\"name\":\"InvalidValue\",\"inputs\":[{\"name\":\"expected\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"received\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ProofValidationFailed\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"SafeERC20FailedOperation\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}]}]", +} + +// RIP7755OutboxABI is the input ABI used to generate the binding from. +// Deprecated: Use RIP7755OutboxMetaData.ABI instead. +var RIP7755OutboxABI = RIP7755OutboxMetaData.ABI + +// RIP7755Outbox is an auto generated Go binding around an Ethereum contract. +type RIP7755Outbox struct { + RIP7755OutboxCaller // Read-only binding to the contract + RIP7755OutboxTransactor // Write-only binding to the contract + RIP7755OutboxFilterer // Log filterer for contract events +} + +// RIP7755OutboxCaller is an auto generated read-only Go binding around an Ethereum contract. +type RIP7755OutboxCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RIP7755OutboxTransactor is an auto generated write-only Go binding around an Ethereum contract. +type RIP7755OutboxTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RIP7755OutboxFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type RIP7755OutboxFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RIP7755OutboxSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type RIP7755OutboxSession struct { + Contract *RIP7755Outbox // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// RIP7755OutboxCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type RIP7755OutboxCallerSession struct { + Contract *RIP7755OutboxCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// RIP7755OutboxTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type RIP7755OutboxTransactorSession struct { + Contract *RIP7755OutboxTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// RIP7755OutboxRaw is an auto generated low-level Go binding around an Ethereum contract. +type RIP7755OutboxRaw struct { + Contract *RIP7755Outbox // Generic contract binding to access the raw methods on +} + +// RIP7755OutboxCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type RIP7755OutboxCallerRaw struct { + Contract *RIP7755OutboxCaller // Generic read-only contract binding to access the raw methods on +} + +// RIP7755OutboxTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type RIP7755OutboxTransactorRaw struct { + Contract *RIP7755OutboxTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewRIP7755Outbox creates a new instance of RIP7755Outbox, bound to a specific deployed contract. +func NewRIP7755Outbox(address common.Address, backend bind.ContractBackend) (*RIP7755Outbox, error) { + contract, err := bindRIP7755Outbox(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &RIP7755Outbox{RIP7755OutboxCaller: RIP7755OutboxCaller{contract: contract}, RIP7755OutboxTransactor: RIP7755OutboxTransactor{contract: contract}, RIP7755OutboxFilterer: RIP7755OutboxFilterer{contract: contract}}, nil +} + +// NewRIP7755OutboxCaller creates a new read-only instance of RIP7755Outbox, bound to a specific deployed contract. +func NewRIP7755OutboxCaller(address common.Address, caller bind.ContractCaller) (*RIP7755OutboxCaller, error) { + contract, err := bindRIP7755Outbox(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &RIP7755OutboxCaller{contract: contract}, nil +} + +// NewRIP7755OutboxTransactor creates a new write-only instance of RIP7755Outbox, bound to a specific deployed contract. +func NewRIP7755OutboxTransactor(address common.Address, transactor bind.ContractTransactor) (*RIP7755OutboxTransactor, error) { + contract, err := bindRIP7755Outbox(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &RIP7755OutboxTransactor{contract: contract}, nil +} + +// NewRIP7755OutboxFilterer creates a new log filterer instance of RIP7755Outbox, bound to a specific deployed contract. +func NewRIP7755OutboxFilterer(address common.Address, filterer bind.ContractFilterer) (*RIP7755OutboxFilterer, error) { + contract, err := bindRIP7755Outbox(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &RIP7755OutboxFilterer{contract: contract}, nil +} + +// bindRIP7755Outbox binds a generic wrapper to an already deployed contract. +func bindRIP7755Outbox(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := RIP7755OutboxMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_RIP7755Outbox *RIP7755OutboxRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _RIP7755Outbox.Contract.RIP7755OutboxCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_RIP7755Outbox *RIP7755OutboxRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.RIP7755OutboxTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_RIP7755Outbox *RIP7755OutboxRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.RIP7755OutboxTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_RIP7755Outbox *RIP7755OutboxCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _RIP7755Outbox.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_RIP7755Outbox *RIP7755OutboxTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_RIP7755Outbox *RIP7755OutboxTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.contract.Transact(opts, method, params...) +} + +// CANCELDELAYSECONDS is a free data retrieval call binding the contract method 0xdf130c43. +// +// Solidity: function CANCEL_DELAY_SECONDS() view returns(uint256) +func (_RIP7755Outbox *RIP7755OutboxCaller) CANCELDELAYSECONDS(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _RIP7755Outbox.contract.Call(opts, &out, "CANCEL_DELAY_SECONDS") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// CANCELDELAYSECONDS is a free data retrieval call binding the contract method 0xdf130c43. +// +// Solidity: function CANCEL_DELAY_SECONDS() view returns(uint256) +func (_RIP7755Outbox *RIP7755OutboxSession) CANCELDELAYSECONDS() (*big.Int, error) { + return _RIP7755Outbox.Contract.CANCELDELAYSECONDS(&_RIP7755Outbox.CallOpts) +} + +// CANCELDELAYSECONDS is a free data retrieval call binding the contract method 0xdf130c43. +// +// Solidity: function CANCEL_DELAY_SECONDS() view returns(uint256) +func (_RIP7755Outbox *RIP7755OutboxCallerSession) CANCELDELAYSECONDS() (*big.Int, error) { + return _RIP7755Outbox.Contract.CANCELDELAYSECONDS(&_RIP7755Outbox.CallOpts) +} + +// GetRequestStatus is a free data retrieval call binding the contract method 0x45d07664. +// +// Solidity: function getRequestStatus(bytes32 requestHash) view returns(uint8) +func (_RIP7755Outbox *RIP7755OutboxCaller) GetRequestStatus(opts *bind.CallOpts, requestHash [32]byte) (uint8, error) { + var out []interface{} + err := _RIP7755Outbox.contract.Call(opts, &out, "getRequestStatus", requestHash) + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// GetRequestStatus is a free data retrieval call binding the contract method 0x45d07664. +// +// Solidity: function getRequestStatus(bytes32 requestHash) view returns(uint8) +func (_RIP7755Outbox *RIP7755OutboxSession) GetRequestStatus(requestHash [32]byte) (uint8, error) { + return _RIP7755Outbox.Contract.GetRequestStatus(&_RIP7755Outbox.CallOpts, requestHash) +} + +// GetRequestStatus is a free data retrieval call binding the contract method 0x45d07664. +// +// Solidity: function getRequestStatus(bytes32 requestHash) view returns(uint8) +func (_RIP7755Outbox *RIP7755OutboxCallerSession) GetRequestStatus(requestHash [32]byte) (uint8, error) { + return _RIP7755Outbox.Contract.GetRequestStatus(&_RIP7755Outbox.CallOpts, requestHash) +} + +// HashRequest is a free data retrieval call binding the contract method 0xdd7d3b6a. +// +// Solidity: function hashRequest((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) pure returns(bytes32) +func (_RIP7755Outbox *RIP7755OutboxCaller) HashRequest(opts *bind.CallOpts, request CrossChainRequest) ([32]byte, error) { + var out []interface{} + err := _RIP7755Outbox.contract.Call(opts, &out, "hashRequest", request) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// HashRequest is a free data retrieval call binding the contract method 0xdd7d3b6a. +// +// Solidity: function hashRequest((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) pure returns(bytes32) +func (_RIP7755Outbox *RIP7755OutboxSession) HashRequest(request CrossChainRequest) ([32]byte, error) { + return _RIP7755Outbox.Contract.HashRequest(&_RIP7755Outbox.CallOpts, request) +} + +// HashRequest is a free data retrieval call binding the contract method 0xdd7d3b6a. +// +// Solidity: function hashRequest((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) pure returns(bytes32) +func (_RIP7755Outbox *RIP7755OutboxCallerSession) HashRequest(request CrossChainRequest) ([32]byte, error) { + return _RIP7755Outbox.Contract.HashRequest(&_RIP7755Outbox.CallOpts, request) +} + +// HashRequestMemory is a free data retrieval call binding the contract method 0x9110312d. +// +// Solidity: function hashRequestMemory((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) pure returns(bytes32) +func (_RIP7755Outbox *RIP7755OutboxCaller) HashRequestMemory(opts *bind.CallOpts, request CrossChainRequest) ([32]byte, error) { + var out []interface{} + err := _RIP7755Outbox.contract.Call(opts, &out, "hashRequestMemory", request) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// HashRequestMemory is a free data retrieval call binding the contract method 0x9110312d. +// +// Solidity: function hashRequestMemory((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) pure returns(bytes32) +func (_RIP7755Outbox *RIP7755OutboxSession) HashRequestMemory(request CrossChainRequest) ([32]byte, error) { + return _RIP7755Outbox.Contract.HashRequestMemory(&_RIP7755Outbox.CallOpts, request) +} + +// HashRequestMemory is a free data retrieval call binding the contract method 0x9110312d. +// +// Solidity: function hashRequestMemory((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) pure returns(bytes32) +func (_RIP7755Outbox *RIP7755OutboxCallerSession) HashRequestMemory(request CrossChainRequest) ([32]byte, error) { + return _RIP7755Outbox.Contract.HashRequestMemory(&_RIP7755Outbox.CallOpts, request) +} + +// CancelRequest is a paid mutator transaction binding the contract method 0xa309a4e2. +// +// Solidity: function cancelRequest((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) returns() +func (_RIP7755Outbox *RIP7755OutboxTransactor) CancelRequest(opts *bind.TransactOpts, request CrossChainRequest) (*types.Transaction, error) { + return _RIP7755Outbox.contract.Transact(opts, "cancelRequest", request) +} + +// CancelRequest is a paid mutator transaction binding the contract method 0xa309a4e2. +// +// Solidity: function cancelRequest((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) returns() +func (_RIP7755Outbox *RIP7755OutboxSession) CancelRequest(request CrossChainRequest) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.CancelRequest(&_RIP7755Outbox.TransactOpts, request) +} + +// CancelRequest is a paid mutator transaction binding the contract method 0xa309a4e2. +// +// Solidity: function cancelRequest((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) returns() +func (_RIP7755Outbox *RIP7755OutboxTransactorSession) CancelRequest(request CrossChainRequest) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.CancelRequest(&_RIP7755Outbox.TransactOpts, request) +} + +// ClaimReward is a paid mutator transaction binding the contract method 0x2dc6629c. +// +// Solidity: function claimReward((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request, (uint96,address) fulfillmentInfo, bytes proof, address payTo) returns() +func (_RIP7755Outbox *RIP7755OutboxTransactor) ClaimReward(opts *bind.TransactOpts, request CrossChainRequest, fulfillmentInfo RIP7755InboxFulfillmentInfo, proof []byte, payTo common.Address) (*types.Transaction, error) { + return _RIP7755Outbox.contract.Transact(opts, "claimReward", request, fulfillmentInfo, proof, payTo) +} + +// ClaimReward is a paid mutator transaction binding the contract method 0x2dc6629c. +// +// Solidity: function claimReward((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request, (uint96,address) fulfillmentInfo, bytes proof, address payTo) returns() +func (_RIP7755Outbox *RIP7755OutboxSession) ClaimReward(request CrossChainRequest, fulfillmentInfo RIP7755InboxFulfillmentInfo, proof []byte, payTo common.Address) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.ClaimReward(&_RIP7755Outbox.TransactOpts, request, fulfillmentInfo, proof, payTo) +} + +// ClaimReward is a paid mutator transaction binding the contract method 0x2dc6629c. +// +// Solidity: function claimReward((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request, (uint96,address) fulfillmentInfo, bytes proof, address payTo) returns() +func (_RIP7755Outbox *RIP7755OutboxTransactorSession) ClaimReward(request CrossChainRequest, fulfillmentInfo RIP7755InboxFulfillmentInfo, proof []byte, payTo common.Address) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.ClaimReward(&_RIP7755Outbox.TransactOpts, request, fulfillmentInfo, proof, payTo) +} + +// RequestCrossChainCall is a paid mutator transaction binding the contract method 0xe786188e. +// +// Solidity: function requestCrossChainCall((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) payable returns() +func (_RIP7755Outbox *RIP7755OutboxTransactor) RequestCrossChainCall(opts *bind.TransactOpts, request CrossChainRequest) (*types.Transaction, error) { + return _RIP7755Outbox.contract.Transact(opts, "requestCrossChainCall", request) +} + +// RequestCrossChainCall is a paid mutator transaction binding the contract method 0xe786188e. +// +// Solidity: function requestCrossChainCall((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) payable returns() +func (_RIP7755Outbox *RIP7755OutboxSession) RequestCrossChainCall(request CrossChainRequest) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.RequestCrossChainCall(&_RIP7755Outbox.TransactOpts, request) +} + +// RequestCrossChainCall is a paid mutator transaction binding the contract method 0xe786188e. +// +// Solidity: function requestCrossChainCall((address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) payable returns() +func (_RIP7755Outbox *RIP7755OutboxTransactorSession) RequestCrossChainCall(request CrossChainRequest) (*types.Transaction, error) { + return _RIP7755Outbox.Contract.RequestCrossChainCall(&_RIP7755Outbox.TransactOpts, request) +} + +// RIP7755OutboxCrossChainCallCanceledIterator is returned from FilterCrossChainCallCanceled and is used to iterate over the raw logs and unpacked data for CrossChainCallCanceled events raised by the RIP7755Outbox contract. +type RIP7755OutboxCrossChainCallCanceledIterator struct { + Event *RIP7755OutboxCrossChainCallCanceled // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *RIP7755OutboxCrossChainCallCanceledIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(RIP7755OutboxCrossChainCallCanceled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(RIP7755OutboxCrossChainCallCanceled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *RIP7755OutboxCrossChainCallCanceledIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *RIP7755OutboxCrossChainCallCanceledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// RIP7755OutboxCrossChainCallCanceled represents a CrossChainCallCanceled event raised by the RIP7755Outbox contract. +type RIP7755OutboxCrossChainCallCanceled struct { + RequestHash [32]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCrossChainCallCanceled is a free log retrieval operation binding the contract event 0x1be39b5d9d7a848f6e4636bfaf521d9a6b7a351a73c7d0e945b79ffc7e169346. +// +// Solidity: event CrossChainCallCanceled(bytes32 indexed requestHash) +func (_RIP7755Outbox *RIP7755OutboxFilterer) FilterCrossChainCallCanceled(opts *bind.FilterOpts, requestHash [][32]byte) (*RIP7755OutboxCrossChainCallCanceledIterator, error) { + + var requestHashRule []interface{} + for _, requestHashItem := range requestHash { + requestHashRule = append(requestHashRule, requestHashItem) + } + + logs, sub, err := _RIP7755Outbox.contract.FilterLogs(opts, "CrossChainCallCanceled", requestHashRule) + if err != nil { + return nil, err + } + return &RIP7755OutboxCrossChainCallCanceledIterator{contract: _RIP7755Outbox.contract, event: "CrossChainCallCanceled", logs: logs, sub: sub}, nil +} + +// WatchCrossChainCallCanceled is a free log subscription operation binding the contract event 0x1be39b5d9d7a848f6e4636bfaf521d9a6b7a351a73c7d0e945b79ffc7e169346. +// +// Solidity: event CrossChainCallCanceled(bytes32 indexed requestHash) +func (_RIP7755Outbox *RIP7755OutboxFilterer) WatchCrossChainCallCanceled(opts *bind.WatchOpts, sink chan<- *RIP7755OutboxCrossChainCallCanceled, requestHash [][32]byte) (event.Subscription, error) { + + var requestHashRule []interface{} + for _, requestHashItem := range requestHash { + requestHashRule = append(requestHashRule, requestHashItem) + } + + logs, sub, err := _RIP7755Outbox.contract.WatchLogs(opts, "CrossChainCallCanceled", requestHashRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(RIP7755OutboxCrossChainCallCanceled) + if err := _RIP7755Outbox.contract.UnpackLog(event, "CrossChainCallCanceled", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCrossChainCallCanceled is a log parse operation binding the contract event 0x1be39b5d9d7a848f6e4636bfaf521d9a6b7a351a73c7d0e945b79ffc7e169346. +// +// Solidity: event CrossChainCallCanceled(bytes32 indexed requestHash) +func (_RIP7755Outbox *RIP7755OutboxFilterer) ParseCrossChainCallCanceled(log types.Log) (*RIP7755OutboxCrossChainCallCanceled, error) { + event := new(RIP7755OutboxCrossChainCallCanceled) + if err := _RIP7755Outbox.contract.UnpackLog(event, "CrossChainCallCanceled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// RIP7755OutboxCrossChainCallRequestedIterator is returned from FilterCrossChainCallRequested and is used to iterate over the raw logs and unpacked data for CrossChainCallRequested events raised by the RIP7755Outbox contract. +type RIP7755OutboxCrossChainCallRequestedIterator struct { + Event *RIP7755OutboxCrossChainCallRequested // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *RIP7755OutboxCrossChainCallRequestedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(RIP7755OutboxCrossChainCallRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(RIP7755OutboxCrossChainCallRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *RIP7755OutboxCrossChainCallRequestedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *RIP7755OutboxCrossChainCallRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// RIP7755OutboxCrossChainCallRequested represents a CrossChainCallRequested event raised by the RIP7755Outbox contract. +type RIP7755OutboxCrossChainCallRequested struct { + RequestHash [32]byte + Request CrossChainRequest + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCrossChainCallRequested is a free log retrieval operation binding the contract event 0x91466a77985019372d6bde6728a808e42b6db50de58526264b5b3716bf7d11de. +// +// Solidity: event CrossChainCallRequested(bytes32 indexed requestHash, (address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) +func (_RIP7755Outbox *RIP7755OutboxFilterer) FilterCrossChainCallRequested(opts *bind.FilterOpts, requestHash [][32]byte) (*RIP7755OutboxCrossChainCallRequestedIterator, error) { + + var requestHashRule []interface{} + for _, requestHashItem := range requestHash { + requestHashRule = append(requestHashRule, requestHashItem) + } + + logs, sub, err := _RIP7755Outbox.contract.FilterLogs(opts, "CrossChainCallRequested", requestHashRule) + if err != nil { + return nil, err + } + return &RIP7755OutboxCrossChainCallRequestedIterator{contract: _RIP7755Outbox.contract, event: "CrossChainCallRequested", logs: logs, sub: sub}, nil +} + +// WatchCrossChainCallRequested is a free log subscription operation binding the contract event 0x91466a77985019372d6bde6728a808e42b6db50de58526264b5b3716bf7d11de. +// +// Solidity: event CrossChainCallRequested(bytes32 indexed requestHash, (address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) +func (_RIP7755Outbox *RIP7755OutboxFilterer) WatchCrossChainCallRequested(opts *bind.WatchOpts, sink chan<- *RIP7755OutboxCrossChainCallRequested, requestHash [][32]byte) (event.Subscription, error) { + + var requestHashRule []interface{} + for _, requestHashItem := range requestHash { + requestHashRule = append(requestHashRule, requestHashItem) + } + + logs, sub, err := _RIP7755Outbox.contract.WatchLogs(opts, "CrossChainCallRequested", requestHashRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(RIP7755OutboxCrossChainCallRequested) + if err := _RIP7755Outbox.contract.UnpackLog(event, "CrossChainCallRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCrossChainCallRequested is a log parse operation binding the contract event 0x91466a77985019372d6bde6728a808e42b6db50de58526264b5b3716bf7d11de. +// +// Solidity: event CrossChainCallRequested(bytes32 indexed requestHash, (address,(address,bytes,uint256)[],address,uint256,address,address,bytes32,address,uint256,uint256,uint256,uint256,address,bytes) request) +func (_RIP7755Outbox *RIP7755OutboxFilterer) ParseCrossChainCallRequested(log types.Log) (*RIP7755OutboxCrossChainCallRequested, error) { + event := new(RIP7755OutboxCrossChainCallRequested) + if err := _RIP7755Outbox.contract.UnpackLog(event, "CrossChainCallRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/services/go-filler/docker-compose.yml b/services/go-filler/docker-compose.yml new file mode 100644 index 0000000..5298cf6 --- /dev/null +++ b/services/go-filler/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" +services: + database: + image: mongo + container_name: mongodb + restart: always + ports: + - 27017:27017 + environment: + - MONGODB_DATABASE="calls" diff --git a/services/go-filler/go.mod b/services/go-filler/go.mod new file mode 100644 index 0000000..cf88ba5 --- /dev/null +++ b/services/go-filler/go.mod @@ -0,0 +1,63 @@ +module github.com/base-org/RIP-7755-poc/services/go-filler + +go 1.23.2 + +require ( + github.com/ethereum/go-ethereum v1.14.11 + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli/v2 v2.27.5 + go.mongodb.org/mongo-driver v1.17.1 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/bits-and-blooms/bitset v1.13.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/hashicorp/go-bexpr v0.1.11 // indirect + github.com/holiman/uint256 v1.3.1 // indirect + github.com/klauspost/compress v1.16.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.13.0 // indirect + github.com/rivo/uniseg v0.3.4 // indirect + github.com/rs/cors v1.8.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/supranational/blst v0.3.13 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) diff --git a/services/go-filler/go.sum b/services/go-filler/go.sum new file mode 100644 index 0000000..cbec529 --- /dev/null +++ b/services/go-filler/go.sum @@ -0,0 +1,317 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= +github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= +github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.14.11 h1:8nFDCUUE67rPc6AKxFj7JKaOa2W/W1Rse3oS6LvvxEY= +github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= +github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= +github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= +github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= +github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= +github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= +github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= +go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/services/go-filler/log-fetcher/README.md b/services/go-filler/log-fetcher/README.md new file mode 100644 index 0000000..8d482c1 --- /dev/null +++ b/services/go-filler/log-fetcher/README.md @@ -0,0 +1,13 @@ +# Log Fetcher + +## Overview + +The Log Fetcher is the initial component in the RIP-7755 Fulfiller architecture. Its primary function is to monitor events emitted by `RIP7755Outbox` contracts across supported blockchain networks. Upon detecting an event that signifies a cross-chain call request, the Log Fetcher parses the log into a format suitable for further processing. + +Next, it performs a validation of the request by checking that all routing information aligns with the pre-defined configurations for both the source and destination chains. Additionally, it ensures that the specified reward asset and amount are sufficient to guarantee a profit if the request is processed by the system. + +If the request successfully passes all validation checks, the Log Fetcher forwards it to a MongoDB queue for subsequent processing. + +## Getting Started + +To run the log fetcher, see the [README](../README.md) in the `go-filler` directory. diff --git a/services/go-filler/log-fetcher/cmd/main.go b/services/go-filler/log-fetcher/cmd/main.go new file mode 100644 index 0000000..0e4637c --- /dev/null +++ b/services/go-filler/log-fetcher/cmd/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "os" + + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/fetcher" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/flags" + "github.com/ethereum/go-ethereum/log" + "github.com/urfave/cli/v2" +) + +func main() { + app := &cli.App{ + Name: "log-fetcher", + Version: "0.0.1", + Usage: "Fetches logs from a given set of chains and stores them in MongoDB", + Flags: flags.Flags, + Action: fetcher.Main, + } + + if err := app.Run(os.Args); err != nil { + log.Crit("Failed to run app", "error", err) + } +} diff --git a/services/go-filler/log-fetcher/config/networks.yaml b/services/go-filler/log-fetcher/config/networks.yaml new file mode 100644 index 0000000..93f0b92 --- /dev/null +++ b/services/go-filler/log-fetcher/config/networks.yaml @@ -0,0 +1,42 @@ +networks: + 421614: # Arbitrum Sepolia + chain-id: 421614 + prover-contracts: + OPStack: 0x062fBdCfd17A0346D2A9d89FE233bbAdBd1DC14C + rpc-url: ${ARBITRUM_SEPOLIA_RPC} + l2-oracle: 0xd80810638dbDF9081b72C1B33c65375e807281C8 + l2-oracle-storage-key: 0x0000000000000000000000000000000000000000000000000000000000000076 + contracts: + inbox: 0xeE962eD1671F655a806cB22623eEA8A7cCc233bC + outbox: 0xBCd5762cF9B07EF5597014c350CE2efB2b0DB2D2 + target-prover: Arbitrum + 84532: # Base Sepolia + chain-id: 84532 + prover-contracts: + Arbitrum: 0x49E2cDC9e81825B6C718ae8244fe0D5b062F4874 + OPStack: 0x562879614C9Db8Da9379be1D5B52BAEcDD456d78 + rpc-url: ${BASE_SEPOLIA_RPC} + l2-oracle: 0x4C8BA32A5DAC2A720bb35CeDB51D6B067D104205 + l2-oracle-storage-key: 0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49 + contracts: + inbox: 0xB482b292878FDe64691d028A2237B34e91c7c7ea + outbox: 0xD7a5A114A07cC4B5ebd9C5e1cD1136a99fFA3d68 + target-prover: OPStack + 11155420: # Optimism Sepolia + chain-id: 11155420 + prover-contracts: + rpc-url: ${OPTIMISM_SEPOLIA_RPC} + l2-oracle: 0x218CD9489199F321E1177b56385d333c5B598629 + l2-oracle-storage-key: 0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49 + contracts: + inbox: 0x49E2cDC9e81825B6C718ae8244fe0D5b062F4874 + l2-message-passer: 0x4200000000000000000000000000000000000016 + target-prover: OPStack + 11155111: # Sepolia + chain-id: 11155111 + prover-contracts: + rpc-url: ${SEPOLIA_RPC} + contracts: + anchor-state-registry: 0x218CD9489199F321E1177b56385d333c5B598629 + arb-rollup: 0xd80810638dbDF9081b72C1B33c65375e807281C8 + target-prover: None diff --git a/services/go-filler/log-fetcher/internal/chains/config.go b/services/go-filler/log-fetcher/internal/chains/config.go new file mode 100644 index 0000000..ab79abc --- /dev/null +++ b/services/go-filler/log-fetcher/internal/chains/config.go @@ -0,0 +1,41 @@ +package chains + +import ( + "fmt" + "math/big" + + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/provers" + "github.com/ethereum/go-ethereum/common" +) + +type NetworksConfig struct { + Networks Networks `yaml:"networks"` +} +type Networks map[string]ChainConfig + +type Contracts struct { + AnchorStateRegistry common.Address `yaml:"anchor-state-registry"` + ArbRollup common.Address `yaml:"arb-rollup"` + L2MessagePasser common.Address `yaml:"l2-message-passer"` + Inbox common.Address `yaml:"inbox"` + Outbox common.Address `yaml:"outbox"` +} + +type ChainConfig struct { + ChainId *big.Int `yaml:"chain-id"` + ProverContracts map[string]common.Address `yaml:"prover-contracts"` + RpcUrl string `yaml:"rpc-url"` + L2Oracle common.Address `yaml:"l2-oracle"` + L2OracleStorageKey string `yaml:"l2-oracle-storage-key"` + Contracts *Contracts `yaml:"contracts"` + TargetProver provers.Prover `yaml:"target-prover"` +} + +func (n *Networks) GetChainConfig(chainId *big.Int) (*ChainConfig, error) { + chainConfig, ok := (*n)[chainId.String()] + if !ok { + return nil, fmt.Errorf("unknown chainId: %d", chainId.Int64()) + } + + return &chainConfig, nil +} diff --git a/services/go-filler/log-fetcher/internal/chains/config_test.go b/services/go-filler/log-fetcher/internal/chains/config_test.go new file mode 100644 index 0000000..9030c51 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/chains/config_test.go @@ -0,0 +1,75 @@ +package chains + +import ( + "math/big" + "reflect" + "testing" + + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/provers" + "github.com/ethereum/go-ethereum/common" +) + +func TestGetChainConfig(t *testing.T) { + testCases := []struct { + name string + chainID *big.Int + expected *ChainConfig + networks Networks + }{ + { + name: "Arbitrum Sepolia", + chainID: big.NewInt(421614), + expected: &ChainConfig{ + ChainId: big.NewInt(421614), + ProverContracts: map[string]common.Address{ + string(provers.OPStackProver): common.HexToAddress("0x062fBdCfd17A0346D2A9d89FE233bbAdBd1DC14C"), + }, + RpcUrl: "https://arb-sepolia.example.com", + L2Oracle: common.HexToAddress("0xd80810638dbDF9081b72C1B33c65375e807281C8"), + L2OracleStorageKey: "0x0000000000000000000000000000000000000000000000000000000000000076", + Contracts: &Contracts{ + Inbox: common.HexToAddress("0xeE962eD1671F655a806cB22623eEA8A7cCc233bC"), + Outbox: common.HexToAddress("0xBCd5762cF9B07EF5597014c350CE2efB2b0DB2D2"), + }, + TargetProver: provers.ArbitrumProver, + }, + networks: Networks{ + "421614": { + ChainId: big.NewInt(421614), + ProverContracts: map[string]common.Address{ + string(provers.OPStackProver): common.HexToAddress("0x062fBdCfd17A0346D2A9d89FE233bbAdBd1DC14C"), + }, + RpcUrl: "https://arb-sepolia.example.com", + L2Oracle: common.HexToAddress("0xd80810638dbDF9081b72C1B33c65375e807281C8"), + L2OracleStorageKey: "0x0000000000000000000000000000000000000000000000000000000000000076", + Contracts: &Contracts{ + Inbox: common.HexToAddress("0xeE962eD1671F655a806cB22623eEA8A7cCc233bC"), + Outbox: common.HexToAddress("0xBCd5762cF9B07EF5597014c350CE2efB2b0DB2D2"), + }, + TargetProver: provers.ArbitrumProver, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result, err := tc.networks.GetChainConfig(tc.chainID) + if err != nil { + t.Errorf("GetChainConfig(%d) returned error: %v", tc.chainID, err) + } + + if !reflect.DeepEqual(result, tc.expected) { + t.Errorf("GetChainConfig(%d) = %+v, want %+v", tc.chainID, result, tc.expected) + } + }) + } +} + +func TestGetChainConfig_UnknownChain(t *testing.T) { + unknownChainID := big.NewInt(999999) + result, err := (&Networks{}).GetChainConfig(unknownChainID) + if err == nil { + t.Errorf("GetChainConfig(%d) = %+v, want error", unknownChainID, result) + } +} diff --git a/services/go-filler/log-fetcher/internal/clients/eth_client.go b/services/go-filler/log-fetcher/internal/clients/eth_client.go new file mode 100644 index 0000000..83ff4a5 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/clients/eth_client.go @@ -0,0 +1,18 @@ +package clients + +import ( + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/chains" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" +) + +func GetEthClient(cfg *chains.ChainConfig) (*ethclient.Client, error) { + client, err := ethclient.Dial(cfg.RpcUrl) + if err != nil { + return nil, err + } + + log.Info("Connected to client", "url", cfg.RpcUrl) + + return client, nil +} diff --git a/services/go-filler/log-fetcher/internal/clients/eth_client_test.go b/services/go-filler/log-fetcher/internal/clients/eth_client_test.go new file mode 100644 index 0000000..b556270 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/clients/eth_client_test.go @@ -0,0 +1,46 @@ +package clients + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/chains" + "github.com/stretchr/testify/assert" +) + +func TestGetClient(t *testing.T) { + t.Run("successful connection", func(t *testing.T) { + // Create a mock HTTP server + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer server.Close() + + // Create a chain config with the mock server URL + cfg := &chains.ChainConfig{ + RpcUrl: server.URL, + } + + // Call GetClient + client, err := GetEthClient(cfg) + + // Assert + assert.NoError(t, err) + assert.NotNil(t, client) + }) + + t.Run("failed connection", func(t *testing.T) { + // Create a chain config with an invalid URL + cfg := &chains.ChainConfig{ + RpcUrl: "invalid-url", + } + + // Call GetClient + client, err := GetEthClient(cfg) + + // Assert + assert.Error(t, err) + assert.Nil(t, client) + }) +} diff --git a/services/go-filler/log-fetcher/internal/fetcher/fetcher.go b/services/go-filler/log-fetcher/internal/fetcher/fetcher.go new file mode 100644 index 0000000..e727084 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/fetcher/fetcher.go @@ -0,0 +1,84 @@ +package fetcher + +import ( + "context" + "math/big" + "os" + "os/signal" + "sync" + "syscall" + + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/chains" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/listener" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/store" + "github.com/ethereum/go-ethereum/log" + "github.com/urfave/cli/v2" + "gopkg.in/yaml.v2" +) + +func Main(ctx *cli.Context) error { + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) + + networksFile, err := os.ReadFile("log-fetcher/config/networks.yaml") + if err != nil { + log.Crit("Failed to read networks file", "error", err) + } + + // expand environment variables + networksFile = []byte(os.ExpandEnv(string(networksFile))) + + var cfg chains.NetworksConfig + err = yaml.Unmarshal(networksFile, &cfg) + if err != nil { + log.Crit("Failed to unmarshal networks file", "error", err) + } + + queue, err := store.NewQueue(ctx) + if err != nil { + return err + } + defer queue.Close() + + var wg sync.WaitGroup + stopped, stop := context.WithCancel(context.Background()) + + for _, chainId := range ctx.StringSlice("supported-chains") { + chainIdBigInt, ok := new(big.Int).SetString(chainId, 10) + if !ok { + log.Crit("Failed to convert chainId to big.Int", "chainId", chainId) + } + + checkpoint, err := queue.ReadCheckpoint(chainId) + if err != nil { + log.Crit("Failed to read checkpoint", "error", err) + } + + l, err := listener.NewListener(chainIdBigInt, cfg.Networks, queue, checkpoint+1) + if err != nil { + log.Crit("Failed to create listener", "error", err) + } + + wg.Add(1) + err = l.Start() + if err != nil { + log.Crit("Failed to start listener", "error", err) + } + + go func() { + defer wg.Done() + <-stopped.Done() + l.Stop() + }() + } + + // Handle signals to initiate shutdown + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + <-c + + log.Info("Shutting down...") + stop() + wg.Wait() + + return nil +} diff --git a/services/go-filler/log-fetcher/internal/flags/flags.go b/services/go-filler/log-fetcher/internal/flags/flags.go new file mode 100644 index 0000000..a3a4ed4 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/flags/flags.go @@ -0,0 +1,48 @@ +package flags + +import ( + "github.com/urfave/cli/v2" +) + +var ( + MongoUriFlag = &cli.StringFlag{ + Name: "mongo-uri", + Usage: "Connection string to MongoDB", + EnvVars: []string{"MONGO_URI"}, + Required: true, + } + ArbitrumSepoliaRpcFlag = &cli.StringFlag{ + Name: "arbitrum-sepolia-rpc", + Usage: "Arbitrum Sepolia RPC", + EnvVars: []string{"ARBITRUM_SEPOLIA_RPC"}, + Required: true, + } + BaseSepoliaRpcFlag = &cli.StringFlag{ + Name: "base-sepolia-rpc", + Usage: "Base Sepolia RPC", + EnvVars: []string{"BASE_SEPOLIA_RPC"}, + Required: true, + } + OptimismSepoliaRpcFlag = &cli.StringFlag{ + Name: "optimism-sepolia-rpc", + Usage: "Optimism Sepolia RPC", + EnvVars: []string{"OPTIMISM_SEPOLIA_RPC"}, + Required: true, + } + SepoliaRpcFlag = &cli.StringFlag{ + Name: "sepolia-rpc", + Usage: "Sepolia RPC", + EnvVars: []string{"SEPOLIA_RPC"}, + Required: true, + } + SupportedChainsFlag = &cli.StringSliceFlag{ + Name: "supported-chains", + Usage: "Comma separated list of supported chains", + Value: cli.NewStringSlice("421614"), + EnvVars: []string{"SUPPORTED_CHAINS"}, + Required: false, + } +) + +// Flags contains the list of configuration options available to the binary. +var Flags = []cli.Flag{MongoUriFlag, ArbitrumSepoliaRpcFlag, BaseSepoliaRpcFlag, OptimismSepoliaRpcFlag, SepoliaRpcFlag, SupportedChainsFlag} diff --git a/services/go-filler/log-fetcher/internal/handler/handler.go b/services/go-filler/log-fetcher/internal/handler/handler.go new file mode 100644 index 0000000..1e93c5d --- /dev/null +++ b/services/go-filler/log-fetcher/internal/handler/handler.go @@ -0,0 +1,40 @@ +package handler + +import ( + "github.com/base-org/RIP-7755-poc/services/go-filler/bindings" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/chains" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/store" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/validator" +) + +type Handler interface { + HandleLog(chainId string, log *bindings.RIP7755OutboxCrossChainCallRequested) error +} + +type handler struct { + validator validator.Validator + queue store.Queue +} + +func NewHandler(srcChain *chains.ChainConfig, networks chains.Networks, queue store.Queue) (Handler, error) { + return &handler{validator: validator.NewValidator(srcChain, networks), queue: queue}, nil +} + +func (h *handler) HandleLog(chainId string, log *bindings.RIP7755OutboxCrossChainCallRequested) error { + err := h.validator.ValidateLog(log) + if err != nil { + return err + } + + err = h.queue.Enqueue(log) + if err != nil { + return err + } + + err = h.queue.WriteCheckpoint(chainId, log.Raw.BlockNumber) + if err != nil { + return err + } + + return nil +} diff --git a/services/go-filler/log-fetcher/internal/handler/handler_test.go b/services/go-filler/log-fetcher/internal/handler/handler_test.go new file mode 100644 index 0000000..1a8258b --- /dev/null +++ b/services/go-filler/log-fetcher/internal/handler/handler_test.go @@ -0,0 +1,110 @@ +package handler + +import ( + "errors" + "testing" + + "github.com/base-org/RIP-7755-poc/services/go-filler/bindings" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type ValidatorMock struct { + mock.Mock +} + +type QueueMock struct { + mock.Mock +} + +func (v *ValidatorMock) ValidateLog(log *bindings.RIP7755OutboxCrossChainCallRequested) error { + args := v.Called(log) + return args.Error(0) +} + +func (q *QueueMock) Enqueue(log *bindings.RIP7755OutboxCrossChainCallRequested) error { + args := q.Called(log) + return args.Error(0) +} + +func (q *QueueMock) ReadCheckpoint(checkpointId string) (uint64, error) { + args := q.Called(checkpointId) + return args.Get(0).(uint64), args.Error(1) +} + +func (q *QueueMock) WriteCheckpoint(checkpointId string, blockNumber uint64) error { + args := q.Called(checkpointId, blockNumber) + return args.Error(0) +} + +func (q *QueueMock) Close() error { + args := q.Called() + return args.Error(0) +} + +func TestHandler(t *testing.T) { + validatorMock := new(ValidatorMock) + queueMock := new(QueueMock) + + log := &bindings.RIP7755OutboxCrossChainCallRequested{} + + validatorMock.On("ValidateLog", log).Return(nil) + queueMock.On("Enqueue", log).Return(nil) + queueMock.On("WriteCheckpoint", "test", log.Raw.BlockNumber).Return(nil) + handler := &handler{validator: validatorMock, queue: queueMock} + + err := handler.HandleLog("test", log) + + assert.NoError(t, err) + + validatorMock.AssertExpectations(t) + queueMock.AssertExpectations(t) +} + +func TestHandlerReturnsErrorFromValidator(t *testing.T) { + validatorMock := new(ValidatorMock) + queueMock := new(QueueMock) + + log := &bindings.RIP7755OutboxCrossChainCallRequested{} + + validatorMock.On("ValidateLog", log).Return(errors.New("test error")) + + handler := &handler{validator: validatorMock, queue: queueMock} + + err := handler.HandleLog("test", log) + + assert.Error(t, err) +} + +func TestHandlerReturnsErrorFromQueue(t *testing.T) { + validatorMock := new(ValidatorMock) + queueMock := new(QueueMock) + + log := &bindings.RIP7755OutboxCrossChainCallRequested{} + + validatorMock.On("ValidateLog", log).Return(nil) + queueMock.On("Enqueue", log).Return(errors.New("test error")) + + handler := &handler{validator: validatorMock, queue: queueMock} + + err := handler.HandleLog("test", log) + + assert.Error(t, err) +} + +func TestHandlerReturnsErrorFromWriteCheckpoint(t *testing.T) { + validatorMock := new(ValidatorMock) + queueMock := new(QueueMock) + + log := &bindings.RIP7755OutboxCrossChainCallRequested{} + + validatorMock.On("ValidateLog", log).Return(nil) + queueMock.On("Enqueue", log).Return(nil) + queueMock.On("WriteCheckpoint", "test", log.Raw.BlockNumber).Return(errors.New("test error")) + + handler := &handler{validator: validatorMock, queue: queueMock} + + err := handler.HandleLog("test", log) + + assert.Error(t, err) +} diff --git a/services/go-filler/log-fetcher/internal/listener/listener.go b/services/go-filler/log-fetcher/internal/listener/listener.go new file mode 100644 index 0000000..128f686 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/listener/listener.go @@ -0,0 +1,179 @@ +package listener + +import ( + "context" + "fmt" + "math/big" + "regexp" + "sync" + "time" + + "github.com/base-org/RIP-7755-poc/services/go-filler/bindings" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/chains" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/clients" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/handler" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/store" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + logger "github.com/ethereum/go-ethereum/log" +) + +type Listener interface { + Start() error + Stop() +} + +type listener struct { + outbox *bindings.RIP7755Outbox + handler handler.Handler + logs chan *bindings.RIP7755OutboxCrossChainCallRequested + stop chan struct{} + wg sync.WaitGroup + pollRate time.Duration + pollReqCh chan struct{} + polling bool + startingBlock uint64 + srcChainId string +} + +var httpRegex = regexp.MustCompile("^http(s)?://") + +func NewListener(srcChainId *big.Int, networks chains.Networks, queue store.Queue, startingBlock uint64) (Listener, error) { + srcChain, err := networks.GetChainConfig(srcChainId) + if err != nil { + return nil, err + } + + h, err := handler.NewHandler(srcChain, networks, queue) + if err != nil { + return nil, err + } + + contractAddress := srcChain.Contracts.Outbox + if contractAddress == common.HexToAddress("") { + return nil, fmt.Errorf("source chain %s missing Outbox contract address", srcChain.ChainId) + } + + client, err := clients.GetEthClient(srcChain) + if err != nil { + return nil, fmt.Errorf("failed to get eth client: %v", err) + } + outbox, err := bindings.NewRIP7755Outbox(contractAddress, client) + if err != nil { + return nil, fmt.Errorf("failed to create Outbox contract binding: %v", err) + } + + return &listener{ + outbox: outbox, + handler: h, + logs: make(chan *bindings.RIP7755OutboxCrossChainCallRequested), + stop: make(chan struct{}), + pollReqCh: make(chan struct{}, 1), + pollRate: 3 * time.Second, + polling: httpRegex.MatchString(srcChain.RpcUrl), + startingBlock: startingBlock, + srcChainId: srcChainId.String(), + }, nil +} + +func (l *listener) Start() error { + if l.polling { + return pollListener(l) + } + + return webSocketListener(l) +} + +func webSocketListener(l *listener) error { + sub, err := l.outbox.WatchCrossChainCallRequested(&bind.WatchOpts{Start: &l.startingBlock}, l.logs, [][32]byte{}) + if err != nil { + return fmt.Errorf("failed to subscribe to logs: %v", err) + } + + logger.Info("Subscribed to logs") + + l.wg.Add(1) + go l.loop(sub) + + return nil +} + +func pollListener(l *listener) error { + logger.Info("Polling for logs") + reqPollAfter := func() { + if l.pollRate == 0 { + return + } + time.AfterFunc(l.pollRate, l.reqPoll) + } + + reqPollAfter() + + for { + select { + case <-l.pollReqCh: + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + logIterator, err := l.outbox.FilterCrossChainCallRequested(&bind.FilterOpts{Context: ctx, Start: l.startingBlock}, [][32]byte{}) + if err != nil { + logger.Error("failed to filter logs", "error", err) + cancel() + logIterator.Close() + reqPollAfter() + continue + } + + for logIterator.Next() { + err := logIterator.Error() + if err != nil { + logger.Error("error iterating over logs", "error", err) + continue + } + + log := logIterator.Event + err = l.handler.HandleLog(l.srcChainId, log) + if err != nil { + logger.Error("failed to handle log", "error", err) + continue + } + } + + cancel() + logIterator.Close() + reqPollAfter() + case <-l.stop: + return nil + } + } +} + +func (l *listener) loop(sub ethereum.Subscription) { + defer l.wg.Done() + for { + select { + case err := <-sub.Err(): + logger.Info("Subscription error", "error", err) + case log := <-l.logs: + logger.Info("Log received!") + logger.Info("Log Block Number", "blockNumber", log.Raw.BlockNumber) + logger.Info("Log Index", "index", log.Raw.Index) + + err := l.handler.HandleLog(l.srcChainId, log) + if err != nil { + logger.Error("Error handling log", "error", err) + } + case <-l.stop: + sub.Unsubscribe() + return + } + } +} + +func (l *listener) Stop() { + close(l.stop) + l.wg.Wait() +} + +func (l *listener) reqPoll() { + l.pollReqCh <- struct{}{} +} diff --git a/services/go-filler/log-fetcher/internal/listener/listener_test.go b/services/go-filler/log-fetcher/internal/listener/listener_test.go new file mode 100644 index 0000000..d3d31bc --- /dev/null +++ b/services/go-filler/log-fetcher/internal/listener/listener_test.go @@ -0,0 +1,33 @@ +package listener + +import ( + "math/big" + "testing" + + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/chains" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/store" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" +) + +var networksCfg chains.NetworksConfig = chains.NetworksConfig{ + Networks: chains.Networks{ + "421614": chains.ChainConfig{ + RpcUrl: "https://arb-sepolia.example.com", + Contracts: &chains.Contracts{ + Outbox: common.HexToAddress("0xBCd5762cF9B07EF5597014c350CE2efB2b0DB2D2"), + }, + }, + }, +} + +var queue store.Queue + +func TestNewListener(t *testing.T) { + l, err := NewListener(big.NewInt(421614), networksCfg.Networks, queue, 0) + if err != nil { + t.Fatalf("Failed to create listener: %v", err) + } + + assert.NotNil(t, l) +} diff --git a/services/go-filler/log-fetcher/internal/provers/provers.go b/services/go-filler/log-fetcher/internal/provers/provers.go new file mode 100644 index 0000000..895e010 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/provers/provers.go @@ -0,0 +1,9 @@ +package provers + +type Prover string + +const ( + NilProver Prover = "None" + ArbitrumProver Prover = "Arbitrum" + OPStackProver Prover = "OPStack" +) diff --git a/services/go-filler/log-fetcher/internal/store/mongo_client.go b/services/go-filler/log-fetcher/internal/store/mongo_client.go new file mode 100644 index 0000000..21d4bb5 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/store/mongo_client.go @@ -0,0 +1,118 @@ +package store + +import ( + "context" + + "github.com/base-org/RIP-7755-poc/services/go-filler/bindings" + logger "github.com/ethereum/go-ethereum/log" + "github.com/urfave/cli/v2" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type Queue interface { + Enqueue(*bindings.RIP7755OutboxCrossChainCallRequested) error + ReadCheckpoint(checkpointId string) (uint64, error) + WriteCheckpoint(checkpointId string, blockNumber uint64) error + Close() error +} + +type MongoCollection interface { + InsertOne(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) + UpdateOne(ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) + FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult +} + +type MongoDriverClient interface { + Database(name string, opts ...*options.DatabaseOptions) *mongo.Database + Disconnect(context.Context) error +} + +type queue struct { + client MongoDriverClient + collection MongoCollection + checkpoint MongoCollection +} + +type record struct { + RequestHash [32]byte + Request bindings.CrossChainRequest +} + +type checkpoint struct { + BlockNumber uint64 +} + +func NewQueue(ctx *cli.Context) (Queue, error) { + client, err := connect(ctx) + if err != nil { + return nil, err + } + + return &queue{client: client, collection: client.Database("calls").Collection("requests"), checkpoint: client.Database("calls").Collection("checkpoint")}, nil +} + +func (q *queue) Enqueue(log *bindings.RIP7755OutboxCrossChainCallRequested) error { + logger.Info("Sending job to queue") + + r := record{ + RequestHash: log.RequestHash, + Request: log.Request, + } + _, err := q.collection.InsertOne(context.TODO(), r) + if err != nil { + return err + } + + logger.Info("Job sent to queue") + + return nil +} + +func (q *queue) ReadCheckpoint(checkpointId string) (uint64, error) { + res := q.checkpoint.FindOne(context.TODO(), bson.M{"id": checkpointId}) + if res.Err() != nil { + // If the checkpoint doesn't exist, return 0 as starting block + if res.Err() == mongo.ErrNoDocuments { + return 0, nil + } + return 0, res.Err() + } + + var c checkpoint + if err := res.Decode(&c); err != nil { + return 0, err + } + + return c.BlockNumber, nil +} + +func (q *queue) WriteCheckpoint(checkpointId string, blockNumber uint64) error { + c := checkpoint{ + BlockNumber: blockNumber, + } + opts := options.Update().SetUpsert(true) + _, err := q.checkpoint.UpdateOne(context.TODO(), bson.M{"id": checkpointId}, bson.M{"$set": c}, opts) + if err != nil { + return err + } + + return nil +} + +func (q *queue) Close() error { + return q.client.Disconnect(context.TODO()) +} + +func connect(ctx *cli.Context) (MongoDriverClient, error) { + logger.Info("Connecting to MongoDB") + client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(ctx.String("mongo-uri"))) + if err != nil { + return nil, err + } + + logger.Info("Connected to MongoDB") + + return client, nil +} diff --git a/services/go-filler/log-fetcher/internal/store/mongo_client_test.go b/services/go-filler/log-fetcher/internal/store/mongo_client_test.go new file mode 100644 index 0000000..909c776 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/store/mongo_client_test.go @@ -0,0 +1,132 @@ +package store + +import ( + "context" + "errors" + "testing" + + "github.com/base-org/RIP-7755-poc/services/go-filler/bindings" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type MongoClientMock struct { + mock.Mock +} + +type MongoConnectionMock struct { + mock.Mock +} + +func (c *MongoConnectionMock) InsertOne(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) { + args := c.Called(ctx, document, opts) + return args.Get(0).(*mongo.InsertOneResult), args.Error(1) +} + +func (c *MongoConnectionMock) UpdateOne(ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { + args := c.Called(ctx, filter, update, opts) + return args.Get(0).(*mongo.UpdateResult), args.Error(1) +} + +func (c *MongoConnectionMock) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult { + args := c.Called(ctx, filter, opts) + return args.Get(0).(*mongo.SingleResult) +} + +func (m *MongoClientMock) Database(name string, opts ...*options.DatabaseOptions) *mongo.Database { + args := m.Called(name, opts) + return args.Get(0).(*mongo.Database) +} + +func (m *MongoClientMock) Disconnect(ctx context.Context) error { + args := m.Called(ctx) + return args.Error(0) +} + +func TestEnqueue(t *testing.T) { + mockConnection := new(MongoConnectionMock) + queue := &queue{collection: mockConnection} + + mockConnection.On("InsertOne", mock.Anything, mock.Anything, mock.Anything).Return(&mongo.InsertOneResult{}, nil) + + err := queue.Enqueue(&bindings.RIP7755OutboxCrossChainCallRequested{}) + + assert.NoError(t, err) +} + +func TestEnqueuePassesParsedLogToInsertOne(t *testing.T) { + mockConnection := new(MongoConnectionMock) + queue := &queue{collection: mockConnection} + log := &bindings.RIP7755OutboxCrossChainCallRequested{} + r := record{ + RequestHash: log.RequestHash, + Request: log.Request, + } + + mockConnection.On("InsertOne", context.TODO(), r, mock.Anything).Return(&mongo.InsertOneResult{}, nil) + + err := queue.Enqueue(log) + + assert.NoError(t, err) + mockConnection.AssertExpectations(t) +} + +func TestEnqueueError(t *testing.T) { + mockConnection := new(MongoConnectionMock) + queue := &queue{collection: mockConnection} + + mockConnection.On("InsertOne", mock.Anything, mock.Anything, mock.Anything).Return(&mongo.InsertOneResult{}, errors.New("error")) + + err := queue.Enqueue(&bindings.RIP7755OutboxCrossChainCallRequested{}) + + assert.Error(t, err) +} + +func TestReadCheckpoint(t *testing.T) { + mockConnection := new(MongoConnectionMock) + queue := &queue{checkpoint: mockConnection} + + mockConnection.On("FindOne", mock.Anything, mock.Anything, mock.Anything).Return(&mongo.SingleResult{}) + + checkpoint, err := queue.ReadCheckpoint("test") + + assert.NoError(t, err) + assert.Equal(t, uint64(0), checkpoint) + mockConnection.AssertExpectations(t) +} + +func TestWriteCheckpoint(t *testing.T) { + mockConnection := new(MongoConnectionMock) + queue := &queue{checkpoint: mockConnection} + + mockConnection.On("UpdateOne", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&mongo.UpdateResult{}, nil) + + err := queue.WriteCheckpoint("test", 1) + + assert.NoError(t, err) + mockConnection.AssertExpectations(t) +} + +func TestClose(t *testing.T) { + mockClient := new(MongoClientMock) + queue := &queue{client: mockClient} + + mockClient.On("Disconnect", context.TODO()).Return(nil) + + err := queue.Close() + + assert.NoError(t, err) +} + +func TestCloseError(t *testing.T) { + mockClient := new(MongoClientMock) + queue := &queue{client: mockClient} + + mockClient.On("Disconnect", context.TODO()).Return(errors.New("error")) + + err := queue.Close() + + assert.Error(t, err) +} diff --git a/services/go-filler/log-fetcher/internal/validator/validator.go b/services/go-filler/log-fetcher/internal/validator/validator.go new file mode 100644 index 0000000..801f954 --- /dev/null +++ b/services/go-filler/log-fetcher/internal/validator/validator.go @@ -0,0 +1,82 @@ +package validator + +import ( + "errors" + "math/big" + + "github.com/base-org/RIP-7755-poc/services/go-filler/bindings" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/chains" + "github.com/ethereum/go-ethereum/common" + logger "github.com/ethereum/go-ethereum/log" +) + +type Validator interface { + ValidateLog(*bindings.RIP7755OutboxCrossChainCallRequested) error +} + +type validator struct { + srcChain *chains.ChainConfig + networks chains.Networks +} + +func NewValidator(srcChain *chains.ChainConfig, networks chains.Networks) Validator { + return &validator{srcChain: srcChain, networks: networks} +} + +func (v *validator) ValidateLog(log *bindings.RIP7755OutboxCrossChainCallRequested) error { + logger.Info("Validating log") + + // - Confirm valid proverContract address on source chain + dstChain, err := v.networks.GetChainConfig(log.Request.DestinationChainId) + if err != nil { + return err + } + + proverName := string(dstChain.TargetProver) + if proverName == "" { + return errors.New("destination chain missing Prover name") + } + + expectedProverAddr := v.srcChain.ProverContracts[proverName] + if expectedProverAddr == common.HexToAddress("") { + return errors.New("expected prover address not found for source chain") + } + + if log.Request.ProverContract != expectedProverAddr { + return errors.New("unknown Prover contract") + } + + // - Make sure inboxContract matches the trusted inbox for dst chain Id + if log.Request.InboxContract != dstChain.Contracts.Inbox { + return errors.New("unknown Inbox contract on destination chain") + } + + // - Confirm l2Oracle and l2OracleStorageKey are valid for dst chain + if log.Request.L2Oracle != dstChain.L2Oracle { + return errors.New("unknown Oracle contract for destination chain") + } + if log.Request.L2OracleStorageKey != common.HexToHash(dstChain.L2OracleStorageKey) { + return errors.New("unknown storage key for dst L2Oracle") + } + + // - Add up total value needed + valueNeeded := big.NewInt(0) + + for i := 0; i < len(log.Request.Calls); i++ { + valueNeeded.Add(valueNeeded, log.Request.Calls[i].Value) + } + + // - rewardAsset + rewardAmount should make sense given requested calls + if !isValidReward(&log.Request, valueNeeded) { + return errors.New("undesirable reward") + } + + return nil +} + +func isValidReward(request *bindings.CrossChainRequest, valueNeeded *big.Int) bool { + nativeAssetAddr := common.HexToAddress("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") + isGreaterThan := request.RewardAmount.Cmp(valueNeeded) == 1 + + return request.RewardAsset == nativeAssetAddr && isGreaterThan +} diff --git a/services/go-filler/log-fetcher/internal/validator/validator_test.go b/services/go-filler/log-fetcher/internal/validator/validator_test.go new file mode 100644 index 0000000..eaa0ecf --- /dev/null +++ b/services/go-filler/log-fetcher/internal/validator/validator_test.go @@ -0,0 +1,159 @@ +package validator + +import ( + "math/big" + "testing" + + "github.com/base-org/RIP-7755-poc/services/go-filler/bindings" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/chains" + "github.com/base-org/RIP-7755-poc/services/go-filler/log-fetcher/internal/provers" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" +) + +var networksCfg chains.NetworksConfig = chains.NetworksConfig{ + Networks: chains.Networks{ + "421614": chains.ChainConfig{ + ProverContracts: map[string]common.Address{ + "OPStack": common.HexToAddress("0x1234567890123456789012345678901234567890"), + }, + }, + "84532": chains.ChainConfig{ + Contracts: &chains.Contracts{ + Inbox: common.HexToAddress("0xB482b292878FDe64691d028A2237B34e91c7c7ea"), + }, + L2Oracle: common.HexToAddress("0x4C8BA32A5DAC2A720bb35CeDB51D6B067D104205"), + L2OracleStorageKey: "0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49", + TargetProver: provers.OPStackProver, + }, + }, +} + +var srcChain = &chains.ChainConfig{ + ChainId: big.NewInt(421614), + ProverContracts: map[string]common.Address{ + "OPStack": common.HexToAddress("0x1234567890123456789012345678901234567890"), + }, +} + +var parsedLog = &bindings.RIP7755OutboxCrossChainCallRequested{ + Request: bindings.CrossChainRequest{ + Calls: []bindings.Call{ + { + To: common.HexToAddress("0x1234567890123456789012345678901234567890"), + Value: big.NewInt(1000000000000000000), + }, + }, + DestinationChainId: big.NewInt(84532), + InboxContract: common.HexToAddress("0xB482b292878FDe64691d028A2237B34e91c7c7ea"), + L2Oracle: common.HexToAddress("0x4C8BA32A5DAC2A720bb35CeDB51D6B067D104205"), + L2OracleStorageKey: common.HexToHash("0xa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49"), + ProverContract: common.HexToAddress("0x1234567890123456789012345678901234567890"), + RewardAsset: common.HexToAddress("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"), + RewardAmount: big.NewInt(2000000000000000000), + }, +} + +func TestValidateLog(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + err := validator.ValidateLog(parsedLog) + + assert.NoError(t, err) +} + +func TestValidateLog_UnknownDestinationChain(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + prevDstChainId := parsedLog.Request.DestinationChainId + parsedLog.Request.DestinationChainId = big.NewInt(11155112) + defer func() { parsedLog.Request.DestinationChainId = prevDstChainId }() + + err := validator.ValidateLog(parsedLog) + + assert.Error(t, err) +} + +func TestValidateLog_UnknownProverName(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + prevDstChainId := parsedLog.Request.DestinationChainId + parsedLog.Request.DestinationChainId = big.NewInt(11155111) + defer func() { parsedLog.Request.DestinationChainId = prevDstChainId }() + + err := validator.ValidateLog(parsedLog) + + assert.Error(t, err) +} + +func TestValidateLog_UnknownProverContract(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + prevProverContract := parsedLog.Request.ProverContract + parsedLog.Request.ProverContract = common.HexToAddress("0x1234567890123456789012345678901234567891") + defer func() { parsedLog.Request.ProverContract = prevProverContract }() + + err := validator.ValidateLog(parsedLog) + + assert.Error(t, err) +} + +func TestValidateLog_UnknownInboxContract(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + prevInboxContract := parsedLog.Request.InboxContract + parsedLog.Request.InboxContract = common.HexToAddress("0x1234567890123456789012345678901234567891") + defer func() { parsedLog.Request.InboxContract = prevInboxContract }() + + err := validator.ValidateLog(parsedLog) + + assert.Error(t, err) +} + +func TestValidateLog_UnknownL2Oracle(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + prevL2Oracle := parsedLog.Request.L2Oracle + parsedLog.Request.L2Oracle = common.HexToAddress("0x1234567890123456789012345678901234567891") + defer func() { parsedLog.Request.L2Oracle = prevL2Oracle }() + + err := validator.ValidateLog(parsedLog) + + assert.Error(t, err) +} + +func TestValidateLog_UnknownL2OracleStorageKey(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + prevL2OracleStorageKey := parsedLog.Request.L2OracleStorageKey + parsedLog.Request.L2OracleStorageKey = common.HexToHash("0x1234567890123456789012345678901234567891") + defer func() { parsedLog.Request.L2OracleStorageKey = prevL2OracleStorageKey }() + + err := validator.ValidateLog(parsedLog) + + assert.Error(t, err) +} + +func TestValidateLog_InvalidReward_NotNativeAsset(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + prevRewardAsset := parsedLog.Request.RewardAsset + parsedLog.Request.RewardAsset = common.HexToAddress("0x1234567890123456789012345678901234567891") + defer func() { parsedLog.Request.RewardAsset = prevRewardAsset }() + + err := validator.ValidateLog(parsedLog) + + assert.Error(t, err) +} + +func TestValidateLog_InvalidReward_NotGreaterThanValueNeeded(t *testing.T) { + validator := NewValidator(srcChain, networksCfg.Networks) + + prevRewardAmount := parsedLog.Request.RewardAmount + parsedLog.Request.RewardAmount = big.NewInt(1000000000000000000) + defer func() { parsedLog.Request.RewardAmount = prevRewardAmount }() + + err := validator.ValidateLog(parsedLog) + + assert.Error(t, err) +}