From dca7e8a0b0d1d1484dd9529626e783d9e6626f91 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 17 Nov 2023 11:54:34 +0200 Subject: [PATCH 01/87] exclude IDEs --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 2186f743..60688ffd 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,9 @@ .DS_Store tmp .env + +# Goland ide +.idea + +# v code +launch.json From 59fc6db9764296fede67187e2dcf70710fbc02a5 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:40:57 +0200 Subject: [PATCH 02/87] Enhance controller to handle Std Eth requests --- internal/start/private.go | 2 +- internal/start/searcher.go | 2 +- pkg/jsonrpc/handler.go | 101 ++++++++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/internal/start/private.go b/internal/start/private.go index 37978996..dfee6737 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -193,7 +193,7 @@ func PrivateMode() { g.Status(http.StatusOK) }) handlers := []gin.HandlerFunc{ - jsonrpc.Controller(client.NewRpcAdapter(c, d)), + jsonrpc.Controller(client.NewRpcAdapter(c, d), eth), jsonrpc.WithOTELTracerAttributes(), } r.POST("/", handlers...) diff --git a/internal/start/searcher.go b/internal/start/searcher.go index b296407b..662f7d63 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -185,7 +185,7 @@ func SearcherMode() { g.Status(http.StatusOK) }) handlers := []gin.HandlerFunc{ - jsonrpc.Controller(client.NewRpcAdapter(c, d)), + jsonrpc.Controller(client.NewRpcAdapter(c, d), eth), jsonrpc.WithOTELTracerAttributes(), } r.POST("/", handlers...) diff --git a/pkg/jsonrpc/handler.go b/pkg/jsonrpc/handler.go index cdf47782..69bfc084 100644 --- a/pkg/jsonrpc/handler.go +++ b/pkg/jsonrpc/handler.go @@ -5,13 +5,20 @@ import ( "encoding/json" "fmt" "io" + "math/big" "net/http" "reflect" + "strconv" + "strings" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" "github.com/gin-gonic/gin" - "github.com/stackup-wallet/stackup-bundler/pkg/errors" "golang.org/x/text/cases" "golang.org/x/text/language" + + "github.com/stackup-wallet/stackup-bundler/pkg/errors" ) func jsonrpcError(c *gin.Context, code int, message string, data any, id *float64) { @@ -33,7 +40,7 @@ func jsonrpcError(c *gin.Context, code int, message string, data any, id *float6 // params spread as arguments. // // If request is valid it will also set the data on the Gin context with the key "json-rpc-request". -func Controller(api interface{}) gin.HandlerFunc { +func Controller(api interface{}, ethRPCClient *ethclient.Client) gin.HandlerFunc { return func(c *gin.Context) { if c.Request.Method != "POST" { jsonrpcError(c, -32700, "Parse error", "POST method excepted", nil) @@ -75,6 +82,12 @@ func Controller(api interface{}) gin.HandlerFunc { return } + if isStdEthereumRPCMethod(method) { + // Proxy the request to the Ethereum node + handleStdEthereumRPCRequest(c, ethRPCClient, data) + return + } + params, ok := data["params"].([]interface{}) if !ok { jsonrpcError(c, -32602, "Invalid params", "No or invalid 'params' in request", &id) @@ -412,3 +425,87 @@ func Controller(api interface{}) gin.HandlerFunc { } } } + +func isStdEthereumRPCMethod(method string) bool { + bundlerMethods := map[string]bool{ + "eth_senduseroperation": true, + "eth_estimateuseroperationgas": true, + "eth_getuseroperationreceipt": true, + "eth_getuseroperationbyhash": true, + "eth_supportedentrypoints": true, + "eth_chainid": true, + "debug_bundler_clearstate": true, + "debug_bundler_dumpmempool": true, + "debug_bundler_sendbundlenow": true, + "debug_bundler_setbundlingmode": true, + // Add any other bundler-specific methods here + } + + // Check if the method is NOT a bundler-specific method + _, isBundlerMethod := bundlerMethods[strings.ToLower(method)] + + return !isBundlerMethod +} + +func handleStdEthereumRPCRequest(c *gin.Context, ethClient *ethclient.Client, requestData map[string]any) { + params := requestData["params"].([]interface{}) + + var ( + callParams map[string]interface{} + to string + data string + callMsg ethereum.CallMsg + ) + if len(params) > 0 { + // Assuming the first param is the address and the second is the data + // This needs to be adjusted according to the specific RPC method and parameters + ok := false + callParams, ok = params[0].(map[string]interface{}) + if !ok { + jsonrpcError(c, -32602, "Invalid params", "First parameter should be a map", nil) + return + } + + to, ok = callParams["to"].(string) + if !ok { + jsonrpcError(c, -32602, "Invalid params", "Contract address (to) not provided or invalid", nil) + return + } + + data, ok = callParams["data"].(string) + if !ok { + jsonrpcError(c, -32602, "Invalid params", "Data not provided or invalid", nil) + return + } + + address := common.HexToAddress(to) + callMsg = ethereum.CallMsg{ + To: &address, + Data: common.FromHex(data), + } + } + + var blockNumber *big.Int + blockParam := params[1].(string) + if blockParam != "latest" { + var intBlockNumber int64 + intBlockNumber, err := strconv.ParseInt(blockParam, 10, 64) + if err != nil { + jsonrpcError(c, -32602, "Invalid params", "Third parameter should be a block number or 'latest'", nil) + return + } + blockNumber = big.NewInt(intBlockNumber) + } + + result, err := ethClient.CallContract(c, callMsg, blockNumber) + if err != nil { + jsonrpcError(c, -32603, "Internal error", err.Error(), nil) + return + } + + c.JSON(http.StatusOK, gin.H{ + "result": common.Bytes2Hex(result), + "jsonrpc": "2.0", + "id": requestData["id"], + }) +} From f8229b537db5e5f9022e7627300cf1cf85bc1063 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Sun, 19 Nov 2023 16:41:22 +0200 Subject: [PATCH 03/87] Handle special revert case --- pkg/jsonrpc/handler.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/pkg/jsonrpc/handler.go b/pkg/jsonrpc/handler.go index 69bfc084..3e712f3e 100644 --- a/pkg/jsonrpc/handler.go +++ b/pkg/jsonrpc/handler.go @@ -498,6 +498,21 @@ func handleStdEthereumRPCRequest(c *gin.Context, ethClient *ethclient.Client, re } result, err := ethClient.CallContract(c, callMsg, blockNumber) + // The erc-4337 spec has a special case for revert errors, where the revert data is returned as the result + const revertErrorKey = "execution reverted" + if err != nil && err.Error() == revertErrorKey { + strResult := extractDataFromUnexportedError(err) + if strResult != "" { + c.JSON(http.StatusOK, gin.H{ + "result": strResult, + "jsonrpc": "2.0", + "id": requestData["id"], + }) + + return + } + } + if err != nil { jsonrpcError(c, -32603, "Internal error", err.Error(), nil) return @@ -509,3 +524,26 @@ func handleStdEthereumRPCRequest(c *gin.Context, ethClient *ethclient.Client, re "id": requestData["id"], }) } + +// extractDataFromUnexportedError extracts the "Data" field from *rpc.jsonError that is not exported +// using reflection. +func extractDataFromUnexportedError(err error) string { + if err == nil { + return "" + } + + val := reflect.ValueOf(err) + if val.Kind() == reflect.Ptr && !val.IsNil() { + // Assuming jsonError is a struct + errVal := val.Elem() + + // Check if the struct has a field named "Data". + dataField := errVal.FieldByName("Data") + if dataField.IsValid() && dataField.CanInterface() { + // Assuming the data field is a string + return dataField.Interface().(string) + } + } + + return "" +} From 3c141a384f5fc7cbc8bbba77d244fc4e37cf6d2a Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Mon, 20 Nov 2023 19:44:38 +0200 Subject: [PATCH 04/87] Add RPC methods getBlock, getMaxPriorityFeeGasPrice --- internal/start/private.go | 2 +- internal/start/searcher.go | 9 +- pkg/jsonrpc/blockhancler.go | 160 ++++++++++++++++++++++++++++++++++++ pkg/jsonrpc/handler.go | 84 ++++++++++++++++--- 4 files changed, 238 insertions(+), 17 deletions(-) create mode 100644 pkg/jsonrpc/blockhancler.go diff --git a/internal/start/private.go b/internal/start/private.go index dfee6737..a0b4f426 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -193,7 +193,7 @@ func PrivateMode() { g.Status(http.StatusOK) }) handlers := []gin.HandlerFunc{ - jsonrpc.Controller(client.NewRpcAdapter(c, d), eth), + jsonrpc.Controller(client.NewRpcAdapter(c, d), rpc, eth), jsonrpc.WithOTELTracerAttributes(), } r.POST("/", handlers...) diff --git a/internal/start/searcher.go b/internal/start/searcher.go index 662f7d63..f3d6247f 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -6,13 +6,16 @@ import ( "log" "net/http" - badger "github.com/dgraph-io/badger/v3" + "github.com/dgraph-io/badger/v3" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/metachris/flashbotsrpc" + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" + "go.opentelemetry.io/otel" + "github.com/stackup-wallet/stackup-bundler/internal/config" "github.com/stackup-wallet/stackup-bundler/internal/logger" "github.com/stackup-wallet/stackup-bundler/internal/o11y" @@ -29,8 +32,6 @@ import ( "github.com/stackup-wallet/stackup-bundler/pkg/modules/gasprice" "github.com/stackup-wallet/stackup-bundler/pkg/modules/paymaster" "github.com/stackup-wallet/stackup-bundler/pkg/signer" - "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" - "go.opentelemetry.io/otel" ) func SearcherMode() { @@ -185,7 +186,7 @@ func SearcherMode() { g.Status(http.StatusOK) }) handlers := []gin.HandlerFunc{ - jsonrpc.Controller(client.NewRpcAdapter(c, d), eth), + jsonrpc.Controller(client.NewRpcAdapter(c, d), rpc, eth), jsonrpc.WithOTELTracerAttributes(), } r.POST("/", handlers...) diff --git a/pkg/jsonrpc/blockhancler.go b/pkg/jsonrpc/blockhancler.go new file mode 100644 index 00000000..18862f20 --- /dev/null +++ b/pkg/jsonrpc/blockhancler.go @@ -0,0 +1,160 @@ +package jsonrpc + +import ( + "encoding/json" + "fmt" + "math/big" + "net/http" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" + "github.com/gin-gonic/gin" +) + +type rpcBlock struct { + Hash common.Hash `json:"hash"` + Transactions []rpcTransaction `json:"transactions"` + UncleHashes []common.Hash `json:"uncles"` + Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` +} + +type rpcTransaction struct { + tx *types.Transaction + txExtraInfo +} + +type txExtraInfo struct { + BlockNumber *string `json:"blockNumber,omitempty"` + BlockHash *common.Hash `json:"blockHash,omitempty"` + From *common.Address `json:"from,omitempty"` +} + +func (tx *rpcTransaction) UnmarshalJSON(msg []byte) error { + if err := json.Unmarshal(msg, &tx.tx); err != nil { + return err + } + return json.Unmarshal(msg, &tx.txExtraInfo) +} + +func getBlock(c *gin.Context, method string, rpcClient *rpc.Client, requestData map[string]any) { + var raw json.RawMessage + + params, ok := requestData["params"].([]interface{}) + if !ok { + jsonrpcError(c, -32602, "Invalid params format", "Expected a slice of parameters", nil) + return + } + + err := rpcClient.CallContext(c, &raw, method, params...) + if err != nil { + jsonrpcError(c, -32603, "Internal error", err.Error(), nil) + return + } + + // Decode header and transactions. + var head *types.Header + if err := json.Unmarshal(raw, &head); err != nil { + return + } + // When the block is not found, the API returns JSON null. + if head == nil { + return + } + + var body rpcBlock + if err := json.Unmarshal(raw, &body); err != nil { + return + } + // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. + if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { + return + } + if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { + return + } + if head.TxHash == types.EmptyTxsHash && len(body.Transactions) > 0 { + return + } + if head.TxHash != types.EmptyTxsHash && len(body.Transactions) == 0 { + return + } + // Load uncles because they are not included in the block response. + // var uncles []*types.Header + // if len(body.UncleHashes) > 0 { + // uncles = make([]*types.Header, len(body.UncleHashes)) + // reqs := make([]rpc.BatchElem, len(body.UncleHashes)) + // for i := range reqs { + // reqs[i] = rpc.BatchElem{ + // Method: "eth_getUncleByBlockHashAndIndex", + // Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))}, + // Result: &uncles[i], + // } + // } + // if err := rpcClient.BatchCallContext(c, reqs); err != nil { + // return + // } + // for i := range reqs { + // if reqs[i].Error != nil { + // return + // } + // if uncles[i] == nil { + // return + // } + // } + // } + + // Fill the sender cache of transactions in the block. + txs := make([]*types.Transaction, len(body.Transactions)) + for i, tx := range body.Transactions { + if tx.From != nil { + setSenderFromServer(tx.tx, *tx.From, body.Hash) + } + txs[i] = tx.tx + } + + emptyUncles := make([]*types.Header, 0) + retBlock := types.NewBlockWithHeader(head).WithBody(txs, emptyUncles).WithWithdrawals(body.Withdrawals) + + c.JSON(http.StatusOK, gin.H{ + "result": retBlock, + "jsonrpc": "2.0", + "id": requestData["id"], + }) + return +} + +// senderFromServer is a types.Signer that remembers the sender address returned by the RPC +// server. It is stored in the transaction's sender address cache to avoid an additional +// request in TransactionSender. +type senderFromServer struct { + addr common.Address + blockhash common.Hash +} + +func (s *senderFromServer) Equal(other types.Signer) bool { + os, ok := other.(*senderFromServer) + return ok && os.blockhash == s.blockhash +} + +func setSenderFromServer(tx *types.Transaction, addr common.Address, block common.Hash) { + // Use types.Sender for side-effect to store our signer into the cache. + types.Sender(&senderFromServer{addr, block}, tx) +} + +func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) { + if s.addr == (common.Address{}) { + return common.Address{}, fmt.Errorf("sender not cached") + } + return s.addr, nil +} + +func (s *senderFromServer) ChainID() *big.Int { + panic("can't sign with senderFromServer") +} +func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash { + panic("can't sign with senderFromServer") +} +func (s *senderFromServer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) { + panic("can't sign with senderFromServer") +} diff --git a/pkg/jsonrpc/handler.go b/pkg/jsonrpc/handler.go index 3e712f3e..87fce900 100644 --- a/pkg/jsonrpc/handler.go +++ b/pkg/jsonrpc/handler.go @@ -13,7 +13,9 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" "github.com/gin-gonic/gin" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -40,7 +42,7 @@ func jsonrpcError(c *gin.Context, code int, message string, data any, id *float6 // params spread as arguments. // // If request is valid it will also set the data on the Gin context with the key "json-rpc-request". -func Controller(api interface{}, ethRPCClient *ethclient.Client) gin.HandlerFunc { +func Controller(api interface{}, rpcClient *rpc.Client, ethRPCClient *ethclient.Client) gin.HandlerFunc { return func(c *gin.Context) { if c.Request.Method != "POST" { jsonrpcError(c, -32700, "Parse error", "POST method excepted", nil) @@ -83,8 +85,9 @@ func Controller(api interface{}, ethRPCClient *ethclient.Client) gin.HandlerFunc } if isStdEthereumRPCMethod(method) { + fmt.Println("Method:", method) // Proxy the request to the Ethereum node - handleStdEthereumRPCRequest(c, ethRPCClient, data) + routeStdEthereumRPCRequest(c, method, rpcClient, ethRPCClient, data) return } @@ -447,7 +450,60 @@ func isStdEthereumRPCMethod(method string) bool { return !isBundlerMethod } -func handleStdEthereumRPCRequest(c *gin.Context, ethClient *ethclient.Client, requestData map[string]any) { +func routeStdEthereumRPCRequest(c *gin.Context, method string, rpcClient *rpc.Client, ethClient *ethclient.Client, requestData map[string]any) { + const ethCall = "eth_call" + if strings.ToLower(method) == ethCall { + handleEthCallRequest(c, ethClient, requestData) + return + } + + handleEthRequest(c, method, rpcClient, requestData) +} + +func handleEthRequest(c *gin.Context, method string, rpcClient *rpc.Client, requestData map[string]any) { + // Extract params and keep them in their original type + params, ok := requestData["params"].([]interface{}) + if !ok { + jsonrpcError(c, -32602, "Invalid params format", "Expected a slice of parameters", nil) + return + } + + // Prepare a slice to hold the result references based on the method requirements + var result interface{} + switch method { + case "eth_getBlockByNumber": + case "eth_maxPriorityFeePerGas": + result = new(hexutil.Big) + default: + jsonrpcError(c, -32601, "Method not found", method, nil) + return + } + + // Call the method with the parameters + err := rpcClient.Call(result, method, params...) + if err != nil { + jsonrpcError(c, -32603, "Internal error", err.Error(), nil) + return + } + + // Convert result to a string representation or handle based on type + var resultStr string + switch res := result.(type) { + case *hexutil.Big: + resultStr = res.String() + default: + jsonrpcError(c, -32603, "Unexpected result type", fmt.Sprintf("%T", result), nil) + return + } + + c.JSON(http.StatusOK, gin.H{ + "result": resultStr, + "jsonrpc": "2.0", + "id": requestData["id"], + }) +} + +func handleEthCallRequest(c *gin.Context, ethClient *ethclient.Client, requestData map[string]any) { params := requestData["params"].([]interface{}) var ( @@ -486,15 +542,17 @@ func handleStdEthereumRPCRequest(c *gin.Context, ethClient *ethclient.Client, re } var blockNumber *big.Int - blockParam := params[1].(string) - if blockParam != "latest" { - var intBlockNumber int64 - intBlockNumber, err := strconv.ParseInt(blockParam, 10, 64) - if err != nil { - jsonrpcError(c, -32602, "Invalid params", "Third parameter should be a block number or 'latest'", nil) - return + if len(params) > 1 { + blockParam := params[1].(string) + if blockParam != "latest" { + var intBlockNumber int64 + intBlockNumber, err := strconv.ParseInt(blockParam, 10, 64) + if err != nil { + jsonrpcError(c, -32602, "Invalid params", "Third parameter should be a block number or 'latest'", nil) + return + } + blockNumber = big.NewInt(intBlockNumber) } - blockNumber = big.NewInt(intBlockNumber) } result, err := ethClient.CallContract(c, callMsg, blockNumber) @@ -518,8 +576,10 @@ func handleStdEthereumRPCRequest(c *gin.Context, ethClient *ethclient.Client, re return } + resultStr := "0x" + common.Bytes2Hex(result) + c.JSON(http.StatusOK, gin.H{ - "result": common.Bytes2Hex(result), + "result": resultStr, "jsonrpc": "2.0", "id": requestData["id"], }) From 76c6fd45354af509c53607d16a5100b63fcdbeb1 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Tue, 21 Nov 2023 20:39:35 +0200 Subject: [PATCH 05/87] Remove getBlockByNumber --- pkg/jsonrpc/blockhancler.go | 160 ------------------------------------ pkg/jsonrpc/handler.go | 1 - 2 files changed, 161 deletions(-) delete mode 100644 pkg/jsonrpc/blockhancler.go diff --git a/pkg/jsonrpc/blockhancler.go b/pkg/jsonrpc/blockhancler.go deleted file mode 100644 index 18862f20..00000000 --- a/pkg/jsonrpc/blockhancler.go +++ /dev/null @@ -1,160 +0,0 @@ -package jsonrpc - -import ( - "encoding/json" - "fmt" - "math/big" - "net/http" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rpc" - "github.com/gin-gonic/gin" -) - -type rpcBlock struct { - Hash common.Hash `json:"hash"` - Transactions []rpcTransaction `json:"transactions"` - UncleHashes []common.Hash `json:"uncles"` - Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` -} - -type rpcTransaction struct { - tx *types.Transaction - txExtraInfo -} - -type txExtraInfo struct { - BlockNumber *string `json:"blockNumber,omitempty"` - BlockHash *common.Hash `json:"blockHash,omitempty"` - From *common.Address `json:"from,omitempty"` -} - -func (tx *rpcTransaction) UnmarshalJSON(msg []byte) error { - if err := json.Unmarshal(msg, &tx.tx); err != nil { - return err - } - return json.Unmarshal(msg, &tx.txExtraInfo) -} - -func getBlock(c *gin.Context, method string, rpcClient *rpc.Client, requestData map[string]any) { - var raw json.RawMessage - - params, ok := requestData["params"].([]interface{}) - if !ok { - jsonrpcError(c, -32602, "Invalid params format", "Expected a slice of parameters", nil) - return - } - - err := rpcClient.CallContext(c, &raw, method, params...) - if err != nil { - jsonrpcError(c, -32603, "Internal error", err.Error(), nil) - return - } - - // Decode header and transactions. - var head *types.Header - if err := json.Unmarshal(raw, &head); err != nil { - return - } - // When the block is not found, the API returns JSON null. - if head == nil { - return - } - - var body rpcBlock - if err := json.Unmarshal(raw, &body); err != nil { - return - } - // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. - if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { - return - } - if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { - return - } - if head.TxHash == types.EmptyTxsHash && len(body.Transactions) > 0 { - return - } - if head.TxHash != types.EmptyTxsHash && len(body.Transactions) == 0 { - return - } - // Load uncles because they are not included in the block response. - // var uncles []*types.Header - // if len(body.UncleHashes) > 0 { - // uncles = make([]*types.Header, len(body.UncleHashes)) - // reqs := make([]rpc.BatchElem, len(body.UncleHashes)) - // for i := range reqs { - // reqs[i] = rpc.BatchElem{ - // Method: "eth_getUncleByBlockHashAndIndex", - // Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))}, - // Result: &uncles[i], - // } - // } - // if err := rpcClient.BatchCallContext(c, reqs); err != nil { - // return - // } - // for i := range reqs { - // if reqs[i].Error != nil { - // return - // } - // if uncles[i] == nil { - // return - // } - // } - // } - - // Fill the sender cache of transactions in the block. - txs := make([]*types.Transaction, len(body.Transactions)) - for i, tx := range body.Transactions { - if tx.From != nil { - setSenderFromServer(tx.tx, *tx.From, body.Hash) - } - txs[i] = tx.tx - } - - emptyUncles := make([]*types.Header, 0) - retBlock := types.NewBlockWithHeader(head).WithBody(txs, emptyUncles).WithWithdrawals(body.Withdrawals) - - c.JSON(http.StatusOK, gin.H{ - "result": retBlock, - "jsonrpc": "2.0", - "id": requestData["id"], - }) - return -} - -// senderFromServer is a types.Signer that remembers the sender address returned by the RPC -// server. It is stored in the transaction's sender address cache to avoid an additional -// request in TransactionSender. -type senderFromServer struct { - addr common.Address - blockhash common.Hash -} - -func (s *senderFromServer) Equal(other types.Signer) bool { - os, ok := other.(*senderFromServer) - return ok && os.blockhash == s.blockhash -} - -func setSenderFromServer(tx *types.Transaction, addr common.Address, block common.Hash) { - // Use types.Sender for side-effect to store our signer into the cache. - types.Sender(&senderFromServer{addr, block}, tx) -} - -func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) { - if s.addr == (common.Address{}) { - return common.Address{}, fmt.Errorf("sender not cached") - } - return s.addr, nil -} - -func (s *senderFromServer) ChainID() *big.Int { - panic("can't sign with senderFromServer") -} -func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash { - panic("can't sign with senderFromServer") -} -func (s *senderFromServer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) { - panic("can't sign with senderFromServer") -} diff --git a/pkg/jsonrpc/handler.go b/pkg/jsonrpc/handler.go index 87fce900..e4715bfe 100644 --- a/pkg/jsonrpc/handler.go +++ b/pkg/jsonrpc/handler.go @@ -471,7 +471,6 @@ func handleEthRequest(c *gin.Context, method string, rpcClient *rpc.Client, requ // Prepare a slice to hold the result references based on the method requirements var result interface{} switch method { - case "eth_getBlockByNumber": case "eth_maxPriorityFeePerGas": result = new(hexutil.Big) default: From ad83e1ae2d5a539ef0257d230f1d781757e5459f Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Tue, 21 Nov 2023 20:40:10 +0200 Subject: [PATCH 06/87] Optimize map initialization --- pkg/jsonrpc/handler.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/jsonrpc/handler.go b/pkg/jsonrpc/handler.go index e4715bfe..f298adda 100644 --- a/pkg/jsonrpc/handler.go +++ b/pkg/jsonrpc/handler.go @@ -429,21 +429,21 @@ func Controller(api interface{}, rpcClient *rpc.Client, ethRPCClient *ethclient. } } -func isStdEthereumRPCMethod(method string) bool { - bundlerMethods := map[string]bool{ - "eth_senduseroperation": true, - "eth_estimateuseroperationgas": true, - "eth_getuseroperationreceipt": true, - "eth_getuseroperationbyhash": true, - "eth_supportedentrypoints": true, - "eth_chainid": true, - "debug_bundler_clearstate": true, - "debug_bundler_dumpmempool": true, - "debug_bundler_sendbundlenow": true, - "debug_bundler_setbundlingmode": true, - // Add any other bundler-specific methods here - } +var bundlerMethods = map[string]bool{ + "eth_senduseroperation": true, + "eth_estimateuseroperationgas": true, + "eth_getuseroperationreceipt": true, + "eth_getuseroperationbyhash": true, + "eth_supportedentrypoints": true, + "eth_chainid": true, + "debug_bundler_clearstate": true, + "debug_bundler_dumpmempool": true, + "debug_bundler_sendbundlenow": true, + "debug_bundler_setbundlingmode": true, + // Add any other bundler-specific methods here +} +func isStdEthereumRPCMethod(method string) bool { // Check if the method is NOT a bundler-specific method _, isBundlerMethod := bundlerMethods[strings.ToLower(method)] From b011f97e3a746b30433b2400b95678250f70f316 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Tue, 21 Nov 2023 20:47:59 +0200 Subject: [PATCH 07/87] Refactor with raw json --- pkg/jsonrpc/handler.go | 63 ++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/pkg/jsonrpc/handler.go b/pkg/jsonrpc/handler.go index f298adda..ec814fda 100644 --- a/pkg/jsonrpc/handler.go +++ b/pkg/jsonrpc/handler.go @@ -13,7 +13,6 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" "github.com/gin-gonic/gin" @@ -23,6 +22,10 @@ import ( "github.com/stackup-wallet/stackup-bundler/pkg/errors" ) +const ( + ethCall = "eth_call" +) + func jsonrpcError(c *gin.Context, code int, message string, data any, id *float64) { c.JSON(http.StatusOK, gin.H{ "jsonrpc": "2.0", @@ -451,13 +454,12 @@ func isStdEthereumRPCMethod(method string) bool { } func routeStdEthereumRPCRequest(c *gin.Context, method string, rpcClient *rpc.Client, ethClient *ethclient.Client, requestData map[string]any) { - const ethCall = "eth_call" - if strings.ToLower(method) == ethCall { + switch strings.ToLower(method) { + case ethCall: handleEthCallRequest(c, ethClient, requestData) - return + default: + handleEthRequest(c, method, rpcClient, requestData) } - - handleEthRequest(c, method, rpcClient, requestData) } func handleEthRequest(c *gin.Context, method string, rpcClient *rpc.Client, requestData map[string]any) { @@ -468,38 +470,39 @@ func handleEthRequest(c *gin.Context, method string, rpcClient *rpc.Client, requ return } - // Prepare a slice to hold the result references based on the method requirements - var result interface{} - switch method { - case "eth_maxPriorityFeePerGas": - result = new(hexutil.Big) - default: - jsonrpcError(c, -32601, "Method not found", method, nil) + // Call the method with the parameters + raw, err := rpcCall(c, method, rpcClient, params) + if err != nil { return } - // Call the method with the parameters - err := rpcClient.Call(result, method, params...) + sendRawJson(c, raw, requestData["id"]) + +} + +func rpcCall(c *gin.Context, method string, rpcClient *rpc.Client, params []interface{}) (json.RawMessage, error) { + var raw json.RawMessage + err := rpcClient.CallContext(c, &raw, method, params...) if err != nil { jsonrpcError(c, -32603, "Internal error", err.Error(), nil) - return + return nil, err } + return raw, nil +} - // Convert result to a string representation or handle based on type - var resultStr string - switch res := result.(type) { - case *hexutil.Big: - resultStr = res.String() - default: - jsonrpcError(c, -32603, "Unexpected result type", fmt.Sprintf("%T", result), nil) - return - } +func sendRawJson(c *gin.Context, raw json.RawMessage, id any) { + c.Writer.Header().Set("Content-Type", "application/json") + c.Writer.WriteHeader(http.StatusOK) - c.JSON(http.StatusOK, gin.H{ - "result": resultStr, - "jsonrpc": "2.0", - "id": requestData["id"], - }) + // Construct the JSON response manually + response := fmt.Sprintf(`{"result": %s, "jsonrpc": "2.0", "id": %v}`, raw, id) + + // Write the response + _, writeErr := c.Writer.Write([]byte(response)) + if writeErr != nil { + // Handle error in writing response + jsonrpcError(c, -32603, "Internal error", writeErr.Error(), nil) + } } func handleEthCallRequest(c *gin.Context, ethClient *ethclient.Client, requestData map[string]any) { From 34cf02c5915345b34a22fd24f64c87e3e19615d3 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 23 Nov 2023 18:16:01 +0200 Subject: [PATCH 08/87] bootstrap geth in unit tests --- go.mod | 3 + go.sum | 3 +- int_tests/userop_gas_test.go | 113 +++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 int_tests/userop_gas_test.go diff --git a/go.mod b/go.mod index 30a2eae5..6d41f05d 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.15.0 + github.com/stretchr/testify v1.8.4 github.com/wangjia184/sortedset v0.0.0-20220209072355-af6d6d227aa7 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 go.opentelemetry.io/otel v1.16.0 @@ -43,6 +44,7 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect @@ -77,6 +79,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect diff --git a/go.sum b/go.sum index 21bdf2f1..42efa614 100644 --- a/go.sum +++ b/go.sum @@ -383,7 +383,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= diff --git a/int_tests/userop_gas_test.go b/int_tests/userop_gas_test.go new file mode 100644 index 00000000..d0abc3e6 --- /dev/null +++ b/int_tests/userop_gas_test.go @@ -0,0 +1,113 @@ +package int_tests + +import ( + "bufio" + "context" + "io" + "log" + "net/http" + "os" + "os/exec" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/assert" +) + +const LocalGeth = "http://localhost:8545" + +var client *ethclient.Client + +func TestMain(m *testing.M) { + // Check if Geth is in the user's path + _, err := exec.LookPath("geth") + if err != nil { + log.Fatal("Geth is not installed or not in the system path. Please install Geth and try again.") + } + + // Start Geth + gethCmd := exec.Command("geth", "--verbosity", "5", + "--http.vhosts", "'*,localhost,host.docker.internal'", + "--http", "--http.api", "eth,net,web3,debug", + "--http.corsdomain", "'*'", + "--http.addr", "0.0.0.0", + "--nodiscover", "--maxpeers", "0", "--mine", + "--networkid", "1337", + "--dev", + "--allow-insecure-unlock", + "--rpc.allow-unprotected-txs", + "--miner.gaslimit", "12000000") + + gethStdout, _ := gethCmd.StdoutPipe() + gethStderr, _ := gethCmd.StderrPipe() + if err := gethCmd.Start(); err != nil { + log.Fatal("Failed to start Geth:", err) + } + + // Asynchronously log Geth output + go logOutput(gethStdout) + go logOutput(gethStderr) + + // Wait for Geth to be ready + waitForGeth() + + client = connectToGeth() + defer client.Close() + + // Run tests + code := m.Run() + + // Terminate Geth + if err := gethCmd.Process.Kill(); err != nil { + println("Failed to kill Geth process:", err.Error()) + os.Exit(1) + } + + os.Exit(code) +} + +func logOutput(pipe io.ReadCloser) { + scanner := bufio.NewScanner(pipe) + for scanner.Scan() { + log.Println(scanner.Text()) + } +} + +func waitForGeth() { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + timeout := time.After(30 * time.Second) + + for { + select { + case <-ticker.C: + resp, err := http.Get(LocalGeth) + if err == nil { + resp.Body.Close() + return + } + case <-timeout: + log.Fatal("Geth did not start in the expected time") + } + } +} + +func connectToGeth() *ethclient.Client { + client, err := ethclient.Dial(LocalGeth) + if err != nil { + panic("Failed to connect to Geth: " + err.Error()) + } + + return client +} + +func TestBalance(t *testing.T) { + addr := common.HexToAddress("0x0A7199a96fdf0252E09F76545c1eF2be3692F46b") + balance, err := client.BalanceAt(context.Background(), addr, nil) + assert.NoError(t, err, "Failed to retrieve balance") + assert.NotNil(t, balance, "Balance should not be nil") + // more tests... +} From d7d49aea915f7f68216c2e72d8e8a23cfa3834bb Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:15:51 +0200 Subject: [PATCH 09/87] set initial account balances --- int_tests/sendtx.sh | 7 +++++++ int_tests/userop_gas_test.go | 20 +++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100755 int_tests/sendtx.sh diff --git a/int_tests/sendtx.sh b/int_tests/sendtx.sh new file mode 100755 index 00000000..183b9eab --- /dev/null +++ b/int_tests/sendtx.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -x + +COINBASE=$(curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_coinbase","params":[],"id":1}' http://localhost:8545 | jq -r '.result') +curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from": "'$COINBASE'", "to":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b", "value": "0xde0b6b3a7640000"}],"id":1}' http://localhost:8545 +curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from": "'$COINBASE'", "to":"0x3068c2408c01bECde4BcCB9f246b56651BE1d12D", "value": "0xde0b6b3a7640000"}],"id":1}' http://localhost:8545 \ No newline at end of file diff --git a/int_tests/userop_gas_test.go b/int_tests/userop_gas_test.go index d0abc3e6..04524e3a 100644 --- a/int_tests/userop_gas_test.go +++ b/int_tests/userop_gas_test.go @@ -53,6 +53,12 @@ func TestMain(m *testing.M) { // Wait for Geth to be ready waitForGeth() + // Execute the sendtx.sh script + err = executeSendTxScript() + if err != nil { + log.Fatalf("Failed to execute sendtx.sh script: %v", err) + } + client = connectToGeth() defer client.Close() @@ -68,6 +74,13 @@ func TestMain(m *testing.M) { os.Exit(code) } +func executeSendTxScript() error { + cmd := exec.Command("./sendtx.sh") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + func logOutput(pipe io.ReadCloser) { scanner := bufio.NewScanner(pipe) for scanner.Scan() { @@ -76,7 +89,7 @@ func logOutput(pipe io.ReadCloser) { } func waitForGeth() { - ticker := time.NewTicker(time.Second) + ticker := time.NewTicker(time.Millisecond * 300) defer ticker.Stop() timeout := time.After(30 * time.Second) @@ -96,7 +109,8 @@ func waitForGeth() { } func connectToGeth() *ethclient.Client { - client, err := ethclient.Dial(LocalGeth) + var err error + client, err = ethclient.Dial(LocalGeth) if err != nil { panic("Failed to connect to Geth: " + err.Error()) } @@ -109,5 +123,5 @@ func TestBalance(t *testing.T) { balance, err := client.BalanceAt(context.Background(), addr, nil) assert.NoError(t, err, "Failed to retrieve balance") assert.NotNil(t, balance, "Balance should not be nil") - // more tests... + assert.Equal(t, balance.String(), "1000000000000000000", "Balance should be greater than 0") } From abca43f3392f76e59b0393d1a4094d7fa701da8e Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 23 Nov 2023 21:25:44 +0200 Subject: [PATCH 10/87] Add dotenv mod --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 6d41f05d..57b11f43 100644 --- a/go.mod +++ b/go.mod @@ -68,6 +68,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/uint256 v1.2.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect diff --git a/go.sum b/go.sum index 42efa614..542a8737 100644 --- a/go.sum +++ b/go.sum @@ -264,6 +264,8 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= From 79210709c12cd447b9970153dbf4f0d3e9d94af6 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:40:22 +0200 Subject: [PATCH 11/87] Add signer key from .env --- int_tests/userop_gas_test.go | 37 +++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/int_tests/userop_gas_test.go b/int_tests/userop_gas_test.go index 04524e3a..7d93925b 100644 --- a/int_tests/userop_gas_test.go +++ b/int_tests/userop_gas_test.go @@ -3,6 +3,7 @@ package int_tests import ( "bufio" "context" + "crypto/ecdsa" "io" "log" "net/http" @@ -12,13 +13,18 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" + "github.com/joho/godotenv" "github.com/stretchr/testify/assert" ) const LocalGeth = "http://localhost:8545" -var client *ethclient.Client +var ( + client *ethclient.Client + signerKey *ecdsa.PrivateKey +) func TestMain(m *testing.M) { // Check if Geth is in the user's path @@ -45,6 +51,9 @@ func TestMain(m *testing.M) { if err := gethCmd.Start(); err != nil { log.Fatal("Failed to start Geth:", err) } + defer terminateGeth(gethCmd) + + setSignerKey() // Asynchronously log Geth output go logOutput(gethStdout) @@ -65,13 +74,31 @@ func TestMain(m *testing.M) { // Run tests code := m.Run() - // Terminate Geth + os.Exit(code) +} + +func terminateGeth(gethCmd *exec.Cmd) { if err := gethCmd.Process.Kill(); err != nil { - println("Failed to kill Geth process:", err.Error()) - os.Exit(1) + log.Println("Failed to kill Geth process:", err.Error()) } +} - os.Exit(code) +func setSignerKey() { + if err := godotenv.Load(); err != nil { + log.Fatalf("Error loading .env file: %v", err) + } + + // Retrieve the prv value + var privateKeyStr string + if privateKeyStr = os.Getenv("PRIVATE_KEY"); privateKeyStr == "" { + log.Fatal("PRIVATE_KEY environment variable is not set") + } + // Convert PRIVATE_KEY to ECDSA private key + var err error + signerKey, err = crypto.HexToECDSA(privateKeyStr) + if err != nil { + log.Fatalf("Invalid PRIVATE_KEY: %v", err) + } } func executeSendTxScript() error { From dba36f9059fe37a2d9d60c52fdf1af207279165d Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:41:45 +0200 Subject: [PATCH 12/87] Document e2e README.md finds --- e2e/README.md | 57 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/e2e/README.md b/e2e/README.md index bb37cff7..7a5e7b16 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -10,14 +10,14 @@ Below are instructions on how to run a series of E2E tests to check that everyth The steps in the following section assumes that all these tools have been installed and ready to go. -- Node.JS >= 18 -- [Geth](https://geth.ethereum.org/docs/getting-started/installing-geth) +- Node.JS = 18 +- [Geth](tested with 1.13.5 https://geth.ethereum.org/docs/getting-started/installing-geth) ## Setting the environment To reduce the impact of external factors, we'll run the E2E test using an isolated local instance of both geth and the bundler. -First, we'll need to run a local instance of geth with the following command: +First, in a new tab/pane run a local instance of geth with the following command: ```bash geth \ @@ -34,19 +34,26 @@ geth \ --miner.gaslimit 12000000 ``` -In a separate process, navigate to the [eth-infinitism/account-abstraction](https://github.com/eth-infinitism/account-abstraction/) directory and run the following command to deploy the required contracts: +In a separate process, +- Clone [eth-infinitism/account-abstraction](https://github.com/eth-infinitism/account-abstraction/) +- Checkout the latest tag that is live on mainnet, currently v0.6.0 +- ```yarn install``` +- Run the following command to deploy the required contracts: ```bash yarn deploy --network localhost ``` -Next, navigate to the [stackup-wallet/contracts](https://github.com/stackup-wallet/contracts) directory and run the following command to deploy the supporting test contracts: +- Navigate to the [stackup-wallet/contracts](https://github.com/stackup-wallet/contracts) directory. +- ```yarn install``` +- Run the following command to deploy the supporting test contracts: ```bash yarn deploy:AllTest --network localhost ``` -Lastly, run the bundler with the following config: +- In a new pane/tab `cd ../` to [github.com/blndgs/stackup-bundler](https://github.com/blndgs/stackup-bundler) +- Set the following environment variables: ``` ERC4337_BUNDLER_ETH_CLIENT_URL=http://localhost:8545 @@ -55,10 +62,44 @@ ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT=12000000 ERC4337_BUNDLER_DEBUG_MODE=true ``` +Run the bundler with the following config: +`make dev-private-mode` + ## Running the test suite -Assuming you have your environment properly setup, you can use the following commands to run the QA test suite. +- Navigate to stackup-wallet/e2e directory (`cd ./e2e`) +- ```yarn install``` +- check the contents of the `config.json` file to ensure the `private key` matches the `ERC4337_BUNDLER_PRIVATE_KEY` environment variable set above. +- Use the following command to run the eth_infinitism test suite. ```bash -yarn run test +yarn run test # see note below if you get a sender_address error +``` + +** _Note: try the following step_ +edit the `setup.ts` file and hardcode the `config.ts` contents into a config object. This is a temporary workaround until a solution for reading `config.ts`. + +```typescript +import { fundIfRequired } from "./src/helpers"; +// import config from "./config"; + +const config = { + // This is for testing only. DO NOT use in production. + // signingKey: '19b8ac9d574d2dcddc3b9f3ae29aec0bb1f13519c8eed4ff8ddcee642076d689', + signingKey: + "c6cbc5ffad570fdad0544d1b5358a36edeb98d163b6567912ac4754e144d4edb", + nodeUrl: "http://localhost:8545", + bundlerUrl: "http://localhost:4337", + + // https://github.com/stackup-wallet/contracts/blob/main/contracts/test + testERC20Token: "0x3870419Ba2BBf0127060bCB37f69A1b1C090992B", + testGas: "0xc2e76Ee793a194Dd930C18c4cDeC93E7C75d567C", + testAccount: "0x3dFD39F2c17625b301ae0EF72B411D1de5211325", +}; + +// no changes below this line +export default async function () { + // ... ``` + +try again with `yarn run test` \ No newline at end of file From 8e173a91c8790cfad3403d602bbf4ec7e58c9320 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:57:01 +0200 Subject: [PATCH 13/87] Rename eth client for clarity. --- int_tests/userop_gas_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/int_tests/userop_gas_test.go b/int_tests/userop_gas_test.go index 7d93925b..3d923aef 100644 --- a/int_tests/userop_gas_test.go +++ b/int_tests/userop_gas_test.go @@ -22,7 +22,7 @@ import ( const LocalGeth = "http://localhost:8545" var ( - client *ethclient.Client + ethclnt *ethclient.Client signerKey *ecdsa.PrivateKey ) @@ -68,8 +68,8 @@ func TestMain(m *testing.M) { log.Fatalf("Failed to execute sendtx.sh script: %v", err) } - client = connectToGeth() - defer client.Close() + ethclnt = connectToGeth() + defer ethclnt.Close() // Run tests code := m.Run() @@ -137,17 +137,17 @@ func waitForGeth() { func connectToGeth() *ethclient.Client { var err error - client, err = ethclient.Dial(LocalGeth) + ethclnt, err = ethclient.Dial(LocalGeth) if err != nil { panic("Failed to connect to Geth: " + err.Error()) } - return client + return ethclnt } func TestBalance(t *testing.T) { addr := common.HexToAddress("0x0A7199a96fdf0252E09F76545c1eF2be3692F46b") - balance, err := client.BalanceAt(context.Background(), addr, nil) + balance, err := ethclnt.BalanceAt(context.Background(), addr, nil) assert.NoError(t, err, "Failed to retrieve balance") assert.NotNil(t, balance, "Balance should not be nil") assert.Equal(t, balance.String(), "1000000000000000000", "Balance should be greater than 0") From b8e611581a79af7220579438eed9ffc1b8c183c8 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:58:45 +0200 Subject: [PATCH 14/87] Add print utilities for userOp, ctx --- pkg/bundler/utils.go | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/pkg/bundler/utils.go b/pkg/bundler/utils.go index ce15ee76..fc9ee8fd 100644 --- a/pkg/bundler/utils.go +++ b/pkg/bundler/utils.go @@ -1,6 +1,11 @@ package bundler -import "github.com/stackup-wallet/stackup-bundler/pkg/userop" +import ( + "encoding/json" + + "github.com/stackup-wallet/stackup-bundler/pkg/modules" + "github.com/stackup-wallet/stackup-bundler/pkg/userop" +) func adjustBatchSize(max int, batch []*userop.UserOperation) []*userop.UserOperation { if len(batch) > max && max > 0 { @@ -8,3 +13,31 @@ func adjustBatchSize(max int, batch []*userop.UserOperation) []*userop.UserOpera } return batch } + +func PrintCtx(ctx *modules.UserOpHandlerCtx) { + println("UserOpHandlerCtx") + PrintUserOp(ctx.UserOp) + penOps := ctx.GetPendingOps() + println("penOps:", penOps) + for _, op := range penOps { + PrintUserOp(op) + } + println("ChainID:", ctx.ChainID) + println("EntryPoint:", ctx.EntryPoint.String()) + println("Deposits:") + deps := ctx.GetDeposits() + for _, dep := range deps { + println("dep:", dep.Deposit.String()) + println("staked:", dep.Staked) + println("stake:", dep.Stake.String()) + } +} + +func PrintUserOp(op *userop.UserOperation) { + opJSON, err := json.Marshal(op) + if err != nil { + println("userOp JSON marshalling err:", err) + } + + println("opJSON:", string(opJSON)) +} From 962212acb4ea64841764a3c44edcbb3661ec4232 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:59:47 +0200 Subject: [PATCH 15/87] Add debug RPC method for Bundler state --- pkg/client/debug.go | 37 +++++++++++++++++++++++++++++++++++++ pkg/client/rpc.go | 9 +++++++++ 2 files changed, 46 insertions(+) diff --git a/pkg/client/debug.go b/pkg/client/debug.go index bad8a19f..c53f9634 100644 --- a/pkg/client/debug.go +++ b/pkg/client/debug.go @@ -103,3 +103,40 @@ func (d *Debug) SetBundlingMode(mode string) (string, error) { return "ok", nil } + +func (d *Debug) DumpDebugInfo() (map[string]any, error) { + info := make(map[string]any) + + // Dump mempool + mempool, err := d.DumpMempool(d.entrypoint.String()) + if err != nil { + return nil, err + } + info["mempool"] = mempool + + // Dump EOA + eoaPrvKey := hex.EncodeToString(d.eoa.PrivateKey.D.Bytes()) + eoaAddress := d.eoa.Address.String() + info["eoaPrivateKey"] = eoaPrvKey + info["eoaAddress"] = eoaAddress + + // Dump chainID + info["chainID"] = d.chainID.String() + + // Dump entrypoint + info["entrypoint"] = d.entrypoint.String() + + // Dump beneficiary + info["beneficiary"] = d.beneficiary.String() + + return info, nil +} + +func (d *Debug) DumpDebugInfoJSON() ([]byte, error) { + info, err := d.DumpDebugInfo() + if err != nil { + return nil, err + } + + return json.Marshal(info) +} diff --git a/pkg/client/rpc.go b/pkg/client/rpc.go index 1c146619..bd61c06a 100644 --- a/pkg/client/rpc.go +++ b/pkg/client/rpc.go @@ -70,6 +70,15 @@ func (r *RpcAdapter) Debug_bundler_dumpMempool(ep string) ([]map[string]any, err return r.debug.DumpMempool(ep) } +// DebugDumpBundlerState routes method call to *Debug.DumpMempool. +func (r *RpcAdapter) DebugDumpBundlerState() (map[string]any, error) { + if r.debug == nil { + return map[string]any{}, errors.New("rpc: debug mode is not enabled") + } + + return r.debug.DumpDebugInfo() +} + // Debug_bundler_sendBundleNow routes method calls to *Debug.SendBundleNow. func (r *RpcAdapter) Debug_bundler_sendBundleNow() (string, error) { if r.debug == nil { From 6b828314b19c5feccfb7e3d65c9fa425f5f47799 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 27 Nov 2023 14:58:05 -0300 Subject: [PATCH 16/87] Testing deploy to ECR. --- .github/workflows/deploy.yml | 39 +++++++++++++++++++ .../compliance.yml | 0 .github/{workflows => workflows_bkp}/core.yml | 0 .../{workflows => workflows_bkp}/docker.yml | 0 .github/{workflows => workflows_bkp}/e2e.yml | 0 5 files changed, 39 insertions(+) create mode 100644 .github/workflows/deploy.yml rename .github/{workflows => workflows_bkp}/compliance.yml (100%) rename .github/{workflows => workflows_bkp}/core.yml (100%) rename .github/{workflows => workflows_bkp}/docker.yml (100%) rename .github/{workflows => workflows_bkp}/e2e.yml (100%) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..47f6dc90 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,39 @@ +name: Deploy to ECR + +on: + + push: + branches: [ proxy-ethnode ] + +jobs: + + build: + + name: Build Image + runs-on: ubuntu-latest + + + steps: + + - name: Check out code + uses: actions/checkout@v2 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-north-1 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Build, tag, and push image to Amazon ECR + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: stackup-bundler + IMAGE_TAG: ${{ github.sha }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG diff --git a/.github/workflows/compliance.yml b/.github/workflows_bkp/compliance.yml similarity index 100% rename from .github/workflows/compliance.yml rename to .github/workflows_bkp/compliance.yml diff --git a/.github/workflows/core.yml b/.github/workflows_bkp/core.yml similarity index 100% rename from .github/workflows/core.yml rename to .github/workflows_bkp/core.yml diff --git a/.github/workflows/docker.yml b/.github/workflows_bkp/docker.yml similarity index 100% rename from .github/workflows/docker.yml rename to .github/workflows_bkp/docker.yml diff --git a/.github/workflows/e2e.yml b/.github/workflows_bkp/e2e.yml similarity index 100% rename from .github/workflows/e2e.yml rename to .github/workflows_bkp/e2e.yml From 124f495f37243db3f9e9113431d40692028b5258 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 29 Nov 2023 20:14:45 +0200 Subject: [PATCH 17/87] feat: add conf solver URL, add solver http client --- internal/config/values.go | 12 ++++++++++++ internal/start/searcher.go | 2 +- pkg/bundler/bundler.go | 12 +++++++++--- pkg/client/debug.go | 2 ++ pkg/entrypoint/simulation/simulatevalidation.go | 2 ++ pkg/modules/checks/standalone.go | 4 +++- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/internal/config/values.go b/internal/config/values.go index cf22afff..1c0c5b88 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/gin-gonic/gin" "github.com/spf13/viper" + "github.com/stackup-wallet/stackup-bundler/pkg/signer" ) @@ -25,6 +26,7 @@ type Values struct { MaxOpTTL time.Duration MaxOpsForUnstakedSender int Beneficiary string + SolverUrl string // Searcher mode variables. EthBuilderUrl string @@ -81,8 +83,11 @@ func variableNotSetOrIsNil(env string) bool { // GetValues returns config for the bundler that has been read in from env vars. See // https://docs.stackup.sh/docs/packages/bundler/configure for details. func GetValues() *Values { + const solverURL = "solver_url" + // Default variables viper.SetDefault("erc4337_bundler_port", 4337) + viper.SetDefault(solverURL, "http://localhost:7322") viper.SetDefault("erc4337_bundler_data_directory", "/tmp/stackup_bundler") viper.SetDefault("erc4337_bundler_supported_entry_points", "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") viper.SetDefault("erc4337_bundler_max_verification_gas", 3000000) @@ -130,6 +135,7 @@ func GetValues() *Values { _ = viper.BindEnv("erc4337_bundler_alt_mempool_ids") _ = viper.BindEnv("erc4337_bundler_debug_mode") _ = viper.BindEnv("erc4337_bundler_gin_mode") + _ = viper.BindEnv(solverURL) // Validate required variables if variableNotSetOrIsNil("erc4337_bundler_eth_client_url") { @@ -167,10 +173,15 @@ func GetValues() *Values { panic("Fatal config error: erc4337_bundler_alt_mempool_ids is set without specifying an IPFS gateway") } + if variableNotSetOrIsNil(solverURL) { + panic("Fatal config error: solver_url not set") + } + // Return Values privateKey := viper.GetString("erc4337_bundler_private_key") ethClientUrl := viper.GetString("erc4337_bundler_eth_client_url") port := viper.GetInt("erc4337_bundler_port") + solverUrl := viper.GetString(solverURL) dataDirectory := viper.GetString("erc4337_bundler_data_directory") supportedEntryPoints := envArrayToAddressSlice(viper.GetString("erc4337_bundler_supported_entry_points")) beneficiary := viper.GetString("erc4337_bundler_beneficiary") @@ -193,6 +204,7 @@ func GetValues() *Values { PrivateKey: privateKey, EthClientUrl: ethClientUrl, Port: port, + SolverUrl: solverUrl, DataDirectory: dataDirectory, SupportedEntryPoints: supportedEntryPoints, Beneficiary: beneficiary, diff --git a/internal/start/searcher.go b/internal/start/searcher.go index f3d6247f..b3e30a67 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -137,7 +137,7 @@ func SearcherMode() { ) // Init Bundler - b := bundler.New(mem, chain, conf.SupportedEntryPoints) + b := bundler.New(mem, chain, conf.SupportedEntryPoints, conf.SolverUrl) b.SetGetBaseFeeFunc(gasprice.GetBaseFeeWithEthClient(eth)) b.SetGetGasTipFunc(gasprice.GetGasTipWithEthClient(eth)) b.SetGetLegacyGasPriceFunc(gasprice.GetLegacyGasPriceWithEthClient(eth)) diff --git a/pkg/bundler/bundler.go b/pkg/bundler/bundler.go index 13e04958..1839ce50 100644 --- a/pkg/bundler/bundler.go +++ b/pkg/bundler/bundler.go @@ -4,23 +4,27 @@ package bundler import ( "context" "math/big" + "net/http" "time" "github.com/ethereum/go-ethereum/common" "github.com/go-logr/logr" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" + "github.com/stackup-wallet/stackup-bundler/internal/logger" "github.com/stackup-wallet/stackup-bundler/pkg/mempool" "github.com/stackup-wallet/stackup-bundler/pkg/modules" "github.com/stackup-wallet/stackup-bundler/pkg/modules/gasprice" "github.com/stackup-wallet/stackup-bundler/pkg/modules/noop" "github.com/stackup-wallet/stackup-bundler/pkg/userop" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/metric" ) // Bundler controls the end to end process of creating a batch of UserOperations from the mempool and sending // it to the EntryPoint. type Bundler struct { + solverURL string + solverClient *http.Client mempool *mempool.Mempool chainID *big.Int supportedEntryPoints []common.Address @@ -38,8 +42,10 @@ type Bundler struct { // New initializes a new EIP-4337 bundler which can be extended with modules for validating batches and // excluding UserOperations that should not be sent to the EntryPoint and/or dropped from the mempool. -func New(mempool *mempool.Mempool, chainID *big.Int, supportedEntryPoints []common.Address) *Bundler { +func New(mempool *mempool.Mempool, chainID *big.Int, supportedEntryPoints []common.Address, solverURL string) *Bundler { return &Bundler{ + solverURL: solverURL, + solverClient: &http.Client{Timeout: 100 * time.Second}, mempool: mempool, chainID: chainID, supportedEntryPoints: supportedEntryPoints, diff --git a/pkg/client/debug.go b/pkg/client/debug.go index c53f9634..93aaa922 100644 --- a/pkg/client/debug.go +++ b/pkg/client/debug.go @@ -1,6 +1,7 @@ package client import ( + "encoding/hex" "encoding/json" "errors" "fmt" @@ -8,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" + "github.com/stackup-wallet/stackup-bundler/pkg/bundler" "github.com/stackup-wallet/stackup-bundler/pkg/mempool" "github.com/stackup-wallet/stackup-bundler/pkg/signer" diff --git a/pkg/entrypoint/simulation/simulatevalidation.go b/pkg/entrypoint/simulation/simulatevalidation.go index 0ec0bb9b..d62138c1 100644 --- a/pkg/entrypoint/simulation/simulatevalidation.go +++ b/pkg/entrypoint/simulation/simulatevalidation.go @@ -7,6 +7,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" + + "github.com/stackup-wallet/stackup-bundler/pkg/bundler" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/reverts" "github.com/stackup-wallet/stackup-bundler/pkg/errors" diff --git a/pkg/modules/checks/standalone.go b/pkg/modules/checks/standalone.go index 3b916ca0..5e13a124 100644 --- a/pkg/modules/checks/standalone.go +++ b/pkg/modules/checks/standalone.go @@ -3,6 +3,7 @@ package checks import ( + "fmt" "math/big" "time" @@ -10,6 +11,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/sync/errgroup" + "github.com/stackup-wallet/stackup-bundler/pkg/altmempools" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/simulation" @@ -18,7 +21,6 @@ import ( "github.com/stackup-wallet/stackup-bundler/pkg/modules" "github.com/stackup-wallet/stackup-bundler/pkg/modules/gasprice" "github.com/stackup-wallet/stackup-bundler/pkg/userop" - "golang.org/x/sync/errgroup" ) // Standalone exposes modules to perform basic Client and Bundler checks as specified in EIP-4337. It is From d357f2499561ad741aebec5185d9caeb25b73895 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 29 Nov 2023 20:37:59 +0200 Subject: [PATCH 18/87] feat: add conf solver url to bundler --- internal/start/private.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/start/private.go b/internal/start/private.go index a0b4f426..c28f2055 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -143,7 +143,7 @@ func PrivateMode() { ) // Init Bundler - b := bundler.New(mem, chain, conf.SupportedEntryPoints) + b := bundler.New(mem, chain, conf.SupportedEntryPoints, conf.SolverUrl) b.SetGetBaseFeeFunc(gasprice.GetBaseFeeWithEthClient(eth)) b.SetGetGasTipFunc(gasprice.GetGasTipWithEthClient(eth)) b.SetGetLegacyGasPriceFunc(gasprice.GetLegacyGasPriceWithEthClient(eth)) From c962c6742c4fd56551700f647ef98c0c4e456cd6 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 29 Nov 2023 20:39:57 +0200 Subject: [PATCH 19/87] feat: Partial intents solving --- pkg/bundler/bundler.go | 19 +++++++++ pkg/bundler/solveintents.go | 81 +++++++++++++++++++++++++++++++++++++ pkg/userop/object.go | 14 +++++++ 3 files changed, 114 insertions(+) create mode 100644 pkg/bundler/solveintents.go diff --git a/pkg/bundler/bundler.go b/pkg/bundler/bundler.go index 1839ce50..6bc823c6 100644 --- a/pkg/bundler/bundler.go +++ b/pkg/bundler/bundler.go @@ -134,6 +134,25 @@ func (i *Bundler) Process(ep common.Address) (*modules.BatchHandlerCtx, error) { if len(batch) == 0 { return nil, nil } + + intentsOps := make([]*userop.UserOperation, len(batch)) + for _, userOp := range batch { + if userOp.IsIntent() { + l = l. + WithName("intents"). + WithValues( + "userop_hash", userOp.GetUserOpHash(ep, i.chainID).String(), + "userop_nonce", userOp.Nonce, + "userop_sender", userOp.Sender.String(), + "is_intent", userOp.IsIntent, + "call_data", userOp.CallData) + intentsOps = append(intentsOps, userOp) + } + } + if len(intentsOps) > 0 { + + } + batch = adjustBatchSize(i.maxBatch, batch) // Get current block basefee diff --git a/pkg/bundler/solveintents.go b/pkg/bundler/solveintents.go new file mode 100644 index 00000000..d9d9a13d --- /dev/null +++ b/pkg/bundler/solveintents.go @@ -0,0 +1,81 @@ +package bundler + +import ( + "bytes" + "encoding/json" + "log" + "net/http" + "time" + + intentsdt "github.com/blndgs/intents" + + "github.com/stackup-wallet/stackup-bundler/pkg/userop" +) + +func sendToSolver(solverClient *http.Client, intents []*intentsdt.Intent) ([]*intentsdt.Intent, error) { + jsonBody, err := json.Marshal(intents) + if err != nil { + return nil, err + } + + // Create a new HTTP POST request + req, err := http.NewRequest(http.MethodPost, "http://localhost:7322", bytes.NewBuffer(jsonBody)) + if err != nil { + // TODO log error + return nil, err + } + + // Set the content type to application/json + req.Header.Set("Content-Type", "application/json") + + // Send the request + resp, err := solverClient.Do(req) + if err != nil { + log.Fatalf("Error occurred sending request. Error: %s", err.Error()) + } + defer resp.Body.Close() + + // Decode the response body into intents + err = json.NewDecoder(resp.Body).Decode(&intents) + + return intents, nil +} + +func solveIntents(solverClient *http.Client, intentOps []*userop.UserOperation) ([]*userop.UserOperation, error) { + intents := make([]*intentsdt.Intent, len(intentOps)) + var ( + errorsCnt int + lastErr error + err error + ) + mapIntentOps := make(map[string]*userop.UserOperation) + for i, op := range intentOps { + op.RemoveIntentPrefix() + err := json.Unmarshal(op.CallData, &intents[i]) + if err != nil { + // TODO: log and continue + errorsCnt++ + lastErr = err + continue + } + mapIntentOps[intents[i].Hash] = op + intents[i].Status = intentsdt.SentToSolver + if intents[i].CreatedAt == 0 { + intents[i].CreatedAt = uint64(time.Now().Unix()) + } + } + + intents, err = sendToSolver(solverClient, intents) + if err != nil { + return nil, err + } + + for _, intent := range intents { + if intent.Status == intentsdt.Solved { + mapIntentOps[intent.Hash].CallData = []byte(intent.CallData) + } + // TODO: handle unsolved, invalid intents, log etc. + } + + return intentOps, lastErr +} diff --git a/pkg/userop/object.go b/pkg/userop/object.go index 35fdf0b8..53fddcfe 100644 --- a/pkg/userop/object.go +++ b/pkg/userop/object.go @@ -38,6 +38,8 @@ var ( UserOpArr, _ = abi.NewType("tuple[]", "ops", UserOpPrimitives) ) +const IntentCallDataPrefix = "" + // UserOperation represents an EIP-4337 style transaction for a smart contract account. type UserOperation struct { Sender common.Address `json:"sender" mapstructure:"sender" validate:"required"` @@ -87,6 +89,18 @@ func (op *UserOperation) GetMaxGasAvailable() *big.Int { ) } +// IsIntent returns true if the UserOperation is an intent. +func (op *UserOperation) IsIntent() bool { + return string(op.CallData[:len(IntentCallDataPrefix)]) == IntentCallDataPrefix +} + +// RemoveIntentPrefix returns the calldata without the intent prefix. +func (op *UserOperation) RemoveIntentPrefix() { + if op.IsIntent() { + op.CallData = op.CallData[len(IntentCallDataPrefix):] + } +} + // GetMaxPrefund returns the max amount of wei required to pay for gas fees by either the sender or // paymaster. func (op *UserOperation) GetMaxPrefund() *big.Int { From 11fb431c304527144785e010b6d77901ad45e5c2 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 29 Nov 2023 20:42:31 +0200 Subject: [PATCH 20/87] feat: include intent, upg go, mods --- go.mod | 63 ++++++++++------- go.sum | 214 ++++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 188 insertions(+), 89 deletions(-) diff --git a/go.mod b/go.mod index 57b11f43..0df642c8 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,19 @@ module github.com/stackup-wallet/stackup-bundler -go 1.19 +go 1.21.3 require ( + github.com/blndgs/intents v0.1.0 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 - github.com/ethereum/go-ethereum v1.11.5 + github.com/ethereum/go-ethereum v1.13.5 github.com/gin-contrib/cors v1.4.0 - github.com/gin-gonic/gin v1.9.0 + github.com/gin-gonic/gin v1.9.1 github.com/go-logr/logr v1.2.4 github.com/go-logr/zerologr v1.2.3 - github.com/go-playground/validator/v10 v10.12.0 + github.com/go-playground/validator/v10 v10.14.0 github.com/google/go-cmp v0.5.9 + github.com/joho/godotenv v1.5.1 github.com/metachris/flashbotsrpc v0.5.0 github.com/mitchellh/mapstructure v1.5.0 github.com/puzpuzpuz/xsync/v3 v3.0.1 @@ -30,55 +32,62 @@ require ( go.opentelemetry.io/otel/sdk v1.16.0 go.opentelemetry.io/otel/sdk/metric v0.39.0 go.opentelemetry.io/otel/trace v1.16.0 - golang.org/x/sync v0.1.0 - golang.org/x/text v0.9.0 + golang.org/x/sync v0.3.0 + golang.org/x/text v0.13.0 google.golang.org/grpc v1.55.0 ) require ( - github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect - github.com/bytedance/sonic v1.8.0 // indirect + github.com/bytedance/sonic v1.9.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/goccy/go-json v0.10.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/holiman/uint256 v1.2.0 // indirect + github.com/holiman/uint256 v1.2.3 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.15 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect - github.com/leodido/go-urn v1.2.2 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect @@ -87,21 +96,25 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect - github.com/tklauser/go-sysconf v0.3.5 // indirect - github.com/tklauser/numcpus v0.2.2 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.9 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect - golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/tools v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 542a8737..03ac0a7b 100644 --- a/go.sum +++ b/go.sum @@ -38,26 +38,36 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +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.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 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.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/blndgs/intents v0.1.0 h1:0cVB49PWqiWz22xHaf/ZRn32wIYbLSmUzoVw/s3qKW4= +github.com/blndgs/intents v0.1.0/go.mod h1:wNIlLUBBRLy2ajOnNH1tRjfhnK8fTPHryrdiib93yn0= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= -github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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 v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -78,10 +88,22 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= -github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= -github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +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/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -90,6 +112,8 @@ github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -108,7 +132,6 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -117,23 +140,29 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.11.5 h1:3M1uan+LAUvdn+7wCEFrcMM4LJTeuxDrPTg/f31a5QQ= -github.com/ethereum/go-ethereum v1.11.5/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= +github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= -github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -144,10 +173,11 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs= github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho= -github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -155,18 +185,20 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= -github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 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.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= +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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= @@ -202,8 +234,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= @@ -237,6 +269,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -249,21 +282,28 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= -github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= +github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +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-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= +github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -275,21 +315,26 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4= -github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -298,10 +343,12 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/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/metachris/flashbotsrpc v0.5.0 h1:5OLpm6+6n4kXxeh3TZBeSj0PQWDxqUsOFwy7xertXQQ= github.com/metachris/flashbotsrpc v0.5.0/go.mod h1:UrS249kKA1PK27sf12M6tUxo/M4ayfFrBk7IMFY1TNw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -309,35 +356,48 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh 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.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +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.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/puzpuzpuz/xsync/v3 v3.0.1 h1:yhTYnDJlgIYp/3Bb14b43VfUPrk/QNJ1HrLYEZ8r2AE= github.com/puzpuzpuz/xsync/v3 v3.0.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 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.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= @@ -345,7 +405,6 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 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/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= @@ -372,6 +431,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -385,31 +445,41 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= +github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= -github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 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/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU= -github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/wangjia184/sortedset v0.0.0-20220209072355-af6d6d227aa7 h1:/9VctXVXpt04S1G44mCHPJh7RuIH3YGP8bAI0dC4t1o= github.com/wangjia184/sortedset v0.0.0-20220209072355-af6d6d227aa7/go.mod h1:yHUVPw1qUPZmDuKhFMHPOI4WjziTH2Wp/GeNjBAycpM= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -425,6 +495,7 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= +go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0= @@ -449,8 +520,10 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -460,8 +533,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -472,7 +545,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -496,6 +570,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -529,8 +605,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -551,8 +627,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -563,6 +639,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -586,7 +663,6 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -594,11 +670,15 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.6.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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -608,12 +688,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -663,6 +744,8 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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= @@ -779,10 +862,11 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/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.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -799,3 +883,5 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From 8039b662f27cf2617977301f6c7213f75468362a Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 29 Nov 2023 20:43:26 +0200 Subject: [PATCH 21/87] feat: add get userOp deposits --- pkg/modules/context.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/modules/context.go b/pkg/modules/context.go index 9865c033..8cf82e92 100644 --- a/pkg/modules/context.go +++ b/pkg/modules/context.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" + "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint" "github.com/stackup-wallet/stackup-bundler/pkg/userop" ) @@ -109,6 +110,16 @@ func (c *UserOpHandlerCtx) GetDepositInfo(entity common.Address) *entrypoint.ISt return dep.(*entrypoint.IStakeManagerDepositInfo) } +// GetDeposits retrieves deposits if any +func (c *UserOpHandlerCtx) GetDeposits() (deps []*entrypoint.IStakeManagerDepositInfo) { + c.deposits.Range(func(key, value any) bool { + deps = append(deps, value.(*entrypoint.IStakeManagerDepositInfo)) + return true + }) + + return deps +} + // GetPendingOps returns all pending UserOperations in the mempool by the same UserOp.Sender. func (c *UserOpHandlerCtx) GetPendingOps() []*userop.UserOperation { return c.pendingOps From 6013086c8a47b1187df93b7e65969f964fdb1da1 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 29 Nov 2023 20:46:54 +0200 Subject: [PATCH 22/87] build: remove unused imports --- pkg/entrypoint/simulation/simulatevalidation.go | 1 - pkg/modules/checks/standalone.go | 1 - 2 files changed, 2 deletions(-) diff --git a/pkg/entrypoint/simulation/simulatevalidation.go b/pkg/entrypoint/simulation/simulatevalidation.go index d62138c1..20dc7b9a 100644 --- a/pkg/entrypoint/simulation/simulatevalidation.go +++ b/pkg/entrypoint/simulation/simulatevalidation.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" - "github.com/stackup-wallet/stackup-bundler/pkg/bundler" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/reverts" "github.com/stackup-wallet/stackup-bundler/pkg/errors" diff --git a/pkg/modules/checks/standalone.go b/pkg/modules/checks/standalone.go index 5e13a124..e6765602 100644 --- a/pkg/modules/checks/standalone.go +++ b/pkg/modules/checks/standalone.go @@ -3,7 +3,6 @@ package checks import ( - "fmt" "math/big" "time" From 682cd717fcafc9a554c65ee0d5bb7ca88c63dfbe Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:48:41 +0200 Subject: [PATCH 23/87] feat: add flow to solve intent userOps --- go.mod | 2 +- go.sum | 4 +- pkg/bundler/bundler.go | 18 ---- pkg/bundler/solveintents.go | 158 ++++++++++++++++++++++++++++-------- 4 files changed, 127 insertions(+), 55 deletions(-) diff --git a/go.mod b/go.mod index 0df642c8..ef509b7f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.21.3 require ( - github.com/blndgs/intents v0.1.0 + github.com/blndgs/intents v0.2.3 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.13.5 diff --git a/go.sum b/go.sum index 03ac0a7b..f93ff603 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ 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.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/blndgs/intents v0.1.0 h1:0cVB49PWqiWz22xHaf/ZRn32wIYbLSmUzoVw/s3qKW4= -github.com/blndgs/intents v0.1.0/go.mod h1:wNIlLUBBRLy2ajOnNH1tRjfhnK8fTPHryrdiib93yn0= +github.com/blndgs/intents v0.2.3 h1:i7b0dIqs/qhllzq2WDYZ8Lpc0IAq9LCbxLxPr+vOVbo= +github.com/blndgs/intents v0.2.3/go.mod h1:wNIlLUBBRLy2ajOnNH1tRjfhnK8fTPHryrdiib93yn0= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= diff --git a/pkg/bundler/bundler.go b/pkg/bundler/bundler.go index 6bc823c6..2d97f609 100644 --- a/pkg/bundler/bundler.go +++ b/pkg/bundler/bundler.go @@ -135,24 +135,6 @@ func (i *Bundler) Process(ep common.Address) (*modules.BatchHandlerCtx, error) { return nil, nil } - intentsOps := make([]*userop.UserOperation, len(batch)) - for _, userOp := range batch { - if userOp.IsIntent() { - l = l. - WithName("intents"). - WithValues( - "userop_hash", userOp.GetUserOpHash(ep, i.chainID).String(), - "userop_nonce", userOp.Nonce, - "userop_sender", userOp.Sender.String(), - "is_intent", userOp.IsIntent, - "call_data", userOp.CallData) - intentsOps = append(intentsOps, userOp) - } - } - if len(intentsOps) > 0 { - - } - batch = adjustBatchSize(i.maxBatch, batch) // Get current block basefee diff --git a/pkg/bundler/solveintents.go b/pkg/bundler/solveintents.go index d9d9a13d..41fc6e6f 100644 --- a/pkg/bundler/solveintents.go +++ b/pkg/bundler/solveintents.go @@ -3,25 +3,53 @@ package bundler import ( "bytes" "encoding/json" + "fmt" "log" "net/http" "time" intentsdt "github.com/blndgs/intents" + "github.com/ethereum/go-ethereum/common" "github.com/stackup-wallet/stackup-bundler/pkg/userop" ) -func sendToSolver(solverClient *http.Client, intents []*intentsdt.Intent) ([]*intentsdt.Intent, error) { - jsonBody, err := json.Marshal(intents) +type IntentOpsBatch map[string]*userop.UserOperation +type IntentsBatch map[string]*intentsdt.Intent + +type EntryPointIntents struct { + EntryPoint common.Address + Intents IntentsBatch + IntentsOps IntentOpsBatch + InvalidIntents uint +} + +func NewEntryPointIntents(entryPoint common.Address) *EntryPointIntents { + return &EntryPointIntents{ + EntryPoint: entryPoint, + Intents: make(IntentsBatch), + IntentsOps: make(IntentOpsBatch), + } +} + +func sendToSolver(solverClient *http.Client, solverURL string, senderAddress string, intents []*intentsdt.Intent) ([]*intentsdt.Intent, error) { + // Create a Body instance and populate it + body := intentsdt.Body{ + Sender: senderAddress, // Check if the sender address is needed + Intents: intents, + } + + // Marshal the Body instance into JSON + jsonBody, err := json.Marshal(body) if err != nil { return nil, err } // Create a new HTTP POST request - req, err := http.NewRequest(http.MethodPost, "http://localhost:7322", bytes.NewBuffer(jsonBody)) + req, err := http.NewRequest(http.MethodPost, solverURL, bytes.NewBuffer(jsonBody)) if err != nil { - // TODO log error + // Log error and return + log.Printf("Error creating request: %s", err.Error()) return nil, err } @@ -31,51 +59,113 @@ func sendToSolver(solverClient *http.Client, intents []*intentsdt.Intent) ([]*in // Send the request resp, err := solverClient.Do(req) if err != nil { - log.Fatalf("Error occurred sending request. Error: %s", err.Error()) + log.Printf("Error occurred sending request. Error: %s", err.Error()) + return nil, err } defer resp.Body.Close() - // Decode the response body into intents - err = json.NewDecoder(resp.Body).Decode(&intents) + // Decode the returned intents back into the same slice of intents + if err := json.NewDecoder(resp.Body).Decode(&intents); err != nil { + log.Printf("Error decoding response: %s", err.Error()) + return nil, err + } return intents, nil } -func solveIntents(solverClient *http.Client, intentOps []*userop.UserOperation) ([]*userop.UserOperation, error) { - intents := make([]*intentsdt.Intent, len(intentOps)) - var ( - errorsCnt int - lastErr error - err error - ) - mapIntentOps := make(map[string]*userop.UserOperation) - for i, op := range intentOps { - op.RemoveIntentPrefix() - err := json.Unmarshal(op.CallData, &intents[i]) - if err != nil { - // TODO: log and continue - errorsCnt++ - lastErr = err - continue - } - mapIntentOps[intents[i].Hash] = op - intents[i].Status = intentsdt.SentToSolver - if intents[i].CreatedAt == 0 { - intents[i].CreatedAt = uint64(time.Now().Unix()) - } +func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { + if len(intentsBatch.Intents) == 0 { + return + } + + l := i.logger.WithName("solveIntents").V(1) + intents := make([]*intentsdt.Intent, len(intentsBatch.Intents)) + j := 0 + for _, itt := range intentsBatch.Intents { + itt.Status = intentsdt.SentToSolver + intents[j] = itt + j++ } - intents, err = sendToSolver(solverClient, intents) + // TODO: Add Backoff logic + var err error + intents, err = sendToSolver(i.solverClient, i.solverURL, intents[0].Sender, intents) if err != nil { - return nil, err + l.WithValues("number_intents", len(intents)). + Error(err, "failed to send intents to solver") + return } for _, intent := range intents { - if intent.Status == intentsdt.Solved { - mapIntentOps[intent.Hash].CallData = []byte(intent.CallData) + switch intent.Status { + case intentsdt.Solved: + intentsBatch.Intents[intent.Hash].CallData = intent.CallData + intentsBatch.IntentsOps[intent.Hash].CallData = []byte(intent.CallData) + case intentsdt.Unsolved: + intentsBatch.Intents[intent.Hash].Status = intentsdt.Unsolved + case intentsdt.Expired, intentsdt.Invalid: + delete(intentsBatch.Intents, intent.Hash) + delete(intentsBatch.IntentsOps, intent.Hash) + l.WithValues("intent_hash", intent.Hash, + "intent_status", intent.Status). + Info("cannot process further") + default: + l.WithValues("intent_hash", intent.Hash, + "intent_status", intent.Status). + Error(fmt.Errorf("unknown intent status"), "unknown returned solver status") } + // TODO: handle unsolved, invalid intents, log etc. } +} + +func (i *Bundler) identifyIntents(entryPoint common.Address, batch []*userop.UserOperation) *EntryPointIntents { + l := i.logger.WithName("identifyIntents").V(1) + intentsBatch := NewEntryPointIntents(entryPoint) + + for _, userOp := range batch { + opHash := userOp.GetUserOpHash(entryPoint, i.chainID).String() + var intent intentsdt.Intent + if userOp.IsIntent() { + userOp.RemoveIntentPrefix() + if err := json.Unmarshal(userOp.CallData, &intent); err != nil { + l.WithValues( + "userop_hash", opHash, + "userop_nonce", userOp.Nonce, + "userop_sender", userOp.Sender.String(), + "is_intent", userOp.IsIntent, + "call_data", userOp.CallData). + Error(err, "failed to unmarshal intent") + intentsBatch.InvalidIntents++ + continue + } + + // Save the identified intent + opHash := userOp.GetUserOpHash(entryPoint, i.chainID).String() + intentsBatch.IntentsOps[opHash] = userOp + + // Set the intent hash to userOp's + intent.Hash = opHash + if intent.CreatedAt == 0 { + intent.CreatedAt = time.Now().Unix() + } + intentsBatch.Intents[opHash] = &intent + } + } + if len(intentsBatch.Intents) > 0 { + i.solveIntents(intentsBatch) + } + + return intentsBatch +} + +func (i *Bundler) PreProcessIntents(entryPoint common.Address, userOpsBatch []*userop.UserOperation) *EntryPointIntents { + intentsBatch := i.identifyIntents(entryPoint, userOpsBatch) + if len(intentsBatch.Intents) == 0 { + return intentsBatch + } + + i.solveIntents(intentsBatch) - return intentOps, lastErr + return intentsBatch } From 394b23d1b4ce436a772c6dfc66d16776ec39209d Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:33:04 +0200 Subject: [PATCH 24/87] feat: Link Solver side effects to original batch --- pkg/bundler/bundler.go | 4 ++++ pkg/bundler/solveintents.go | 33 ++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/pkg/bundler/bundler.go b/pkg/bundler/bundler.go index 2d97f609..21c488cd 100644 --- a/pkg/bundler/bundler.go +++ b/pkg/bundler/bundler.go @@ -135,6 +135,10 @@ func (i *Bundler) Process(ep common.Address) (*modules.BatchHandlerCtx, error) { return nil, nil } + // Side effect of Solved Intent userOps: + // Calldata will be altered with the solution calldata from the solver. + i.PreProcessIntents(ep, batch) + batch = adjustBatchSize(i.maxBatch, batch) // Get current block basefee diff --git a/pkg/bundler/solveintents.go b/pkg/bundler/solveintents.go index 41fc6e6f..6f135767 100644 --- a/pkg/bundler/solveintents.go +++ b/pkg/bundler/solveintents.go @@ -16,19 +16,24 @@ import ( type IntentOpsBatch map[string]*userop.UserOperation type IntentsBatch map[string]*intentsdt.Intent +type OrigBatchIdx map[string]int type EntryPointIntents struct { EntryPoint common.Address - Intents IntentsBatch - IntentsOps IntentOpsBatch + Intents IntentsBatch // set of intents in the batch + IntentsOps IntentOpsBatch // subset of userOp intents out of Batch + UserOpsOrigIdx OrigBatchIdx // map of userOp hash to its index in the original batch + OrigBatch []*userop.UserOperation InvalidIntents uint } -func NewEntryPointIntents(entryPoint common.Address) *EntryPointIntents { +func NewEntryPointIntents(entryPoint common.Address, origBatch []*userop.UserOperation) *EntryPointIntents { return &EntryPointIntents{ - EntryPoint: entryPoint, - Intents: make(IntentsBatch), - IntentsOps: make(IntentOpsBatch), + EntryPoint: entryPoint, + Intents: make(IntentsBatch), + IntentsOps: make(IntentOpsBatch), + UserOpsOrigIdx: make(OrigBatchIdx), + OrigBatch: origBatch, } } @@ -78,7 +83,7 @@ func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { return } - l := i.logger.WithName("solveIntents").V(1) + l := i.logger.WithName("solveIntents") intents := make([]*intentsdt.Intent, len(intentsBatch.Intents)) j := 0 for _, itt := range intentsBatch.Intents { @@ -99,9 +104,11 @@ func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { for _, intent := range intents { switch intent.Status { case intentsdt.Solved: - intentsBatch.Intents[intent.Hash].CallData = intent.CallData + // Set the solution back to the original userOp + intentsBatch.OrigBatch[intentsBatch.UserOpsOrigIdx[intent.Hash]].CallData = []byte(intent.CallData) intentsBatch.IntentsOps[intent.Hash].CallData = []byte(intent.CallData) case intentsdt.Unsolved: + // will be retried till expired intentsBatch.Intents[intent.Hash].Status = intentsdt.Unsolved case intentsdt.Expired, intentsdt.Invalid: delete(intentsBatch.Intents, intent.Hash) @@ -114,16 +121,14 @@ func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { "intent_status", intent.Status). Error(fmt.Errorf("unknown intent status"), "unknown returned solver status") } - - // TODO: handle unsolved, invalid intents, log etc. } } func (i *Bundler) identifyIntents(entryPoint common.Address, batch []*userop.UserOperation) *EntryPointIntents { l := i.logger.WithName("identifyIntents").V(1) - intentsBatch := NewEntryPointIntents(entryPoint) + intentsBatch := NewEntryPointIntents(entryPoint, batch) - for _, userOp := range batch { + for idx, userOp := range batch { opHash := userOp.GetUserOpHash(entryPoint, i.chainID).String() var intent intentsdt.Intent if userOp.IsIntent() { @@ -141,7 +146,6 @@ func (i *Bundler) identifyIntents(entryPoint common.Address, batch []*userop.Use } // Save the identified intent - opHash := userOp.GetUserOpHash(entryPoint, i.chainID).String() intentsBatch.IntentsOps[opHash] = userOp // Set the intent hash to userOp's @@ -150,6 +154,9 @@ func (i *Bundler) identifyIntents(entryPoint common.Address, batch []*userop.Use intent.CreatedAt = time.Now().Unix() } intentsBatch.Intents[opHash] = &intent + + // Save the index of the userOp in the original batch + intentsBatch.UserOpsOrigIdx[opHash] = idx } } if len(intentsBatch.Intents) > 0 { From 1662a7e6a6e26424c9449eeb17f0745d9a930a05 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:35:24 +0200 Subject: [PATCH 25/87] fix: resp, url... --- go.mod | 2 +- go.sum | 4 ++-- internal/config/values.go | 2 +- pkg/bundler/bundler.go | 2 +- pkg/bundler/solveintents.go | 5 ++--- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index ef509b7f..c3a32bc0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.21.3 require ( - github.com/blndgs/intents v0.2.3 + github.com/blndgs/intents v0.2.4 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.13.5 diff --git a/go.sum b/go.sum index f93ff603..864a77fe 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ 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.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/blndgs/intents v0.2.3 h1:i7b0dIqs/qhllzq2WDYZ8Lpc0IAq9LCbxLxPr+vOVbo= -github.com/blndgs/intents v0.2.3/go.mod h1:wNIlLUBBRLy2ajOnNH1tRjfhnK8fTPHryrdiib93yn0= +github.com/blndgs/intents v0.2.4 h1:DyK76ssP7QpXg4BF2RNonBOx7aX26woHEMcilH5QxCc= +github.com/blndgs/intents v0.2.4/go.mod h1:wNIlLUBBRLy2ajOnNH1tRjfhnK8fTPHryrdiib93yn0= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= diff --git a/internal/config/values.go b/internal/config/values.go index 1c0c5b88..5ccc55e5 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -87,7 +87,7 @@ func GetValues() *Values { // Default variables viper.SetDefault("erc4337_bundler_port", 4337) - viper.SetDefault(solverURL, "http://localhost:7322") + viper.SetDefault(solverURL, "http://localhost:7322/solve") viper.SetDefault("erc4337_bundler_data_directory", "/tmp/stackup_bundler") viper.SetDefault("erc4337_bundler_supported_entry_points", "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") viper.SetDefault("erc4337_bundler_max_verification_gas", 3000000) diff --git a/pkg/bundler/bundler.go b/pkg/bundler/bundler.go index 21c488cd..997a7911 100644 --- a/pkg/bundler/bundler.go +++ b/pkg/bundler/bundler.go @@ -136,7 +136,7 @@ func (i *Bundler) Process(ep common.Address) (*modules.BatchHandlerCtx, error) { } // Side effect of Solved Intent userOps: - // Calldata will be altered with the solution calldata from the solver. + // Calldata will be altered with the solution calldata received by the solver. i.PreProcessIntents(ep, batch) batch = adjustBatchSize(i.maxBatch, batch) diff --git a/pkg/bundler/solveintents.go b/pkg/bundler/solveintents.go index 6f135767..c7037014 100644 --- a/pkg/bundler/solveintents.go +++ b/pkg/bundler/solveintents.go @@ -70,12 +70,12 @@ func sendToSolver(solverClient *http.Client, solverURL string, senderAddress str defer resp.Body.Close() // Decode the returned intents back into the same slice of intents - if err := json.NewDecoder(resp.Body).Decode(&intents); err != nil { + if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { log.Printf("Error decoding response: %s", err.Error()) return nil, err } - return intents, nil + return body.Intents, nil } func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { @@ -106,7 +106,6 @@ func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { case intentsdt.Solved: // Set the solution back to the original userOp intentsBatch.OrigBatch[intentsBatch.UserOpsOrigIdx[intent.Hash]].CallData = []byte(intent.CallData) - intentsBatch.IntentsOps[intent.Hash].CallData = []byte(intent.CallData) case intentsdt.Unsolved: // will be retried till expired intentsBatch.Intents[intent.Hash].Status = intentsdt.Unsolved From 7414a561953c132b627a765f3caf557494f50818 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 30 Nov 2023 23:36:38 +0200 Subject: [PATCH 26/87] feat: Hardcode intent prefix till full integration --- pkg/userop/object.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/userop/object.go b/pkg/userop/object.go index 53fddcfe..b69173e7 100644 --- a/pkg/userop/object.go +++ b/pkg/userop/object.go @@ -4,6 +4,7 @@ package userop import ( "encoding/json" "math/big" + "strings" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -91,6 +92,15 @@ func (op *UserOperation) GetMaxGasAvailable() *big.Int { // IsIntent returns true if the UserOperation is an intent. func (op *UserOperation) IsIntent() bool { + // TODO remove next line when integration is complete + if !strings.HasPrefix(string(op.CallData), IntentCallDataPrefix) { + op.CallData = []byte("" + `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}`) + } + + if len(op.CallData) < len(IntentCallDataPrefix) { + return false + } + return string(op.CallData[:len(IntentCallDataPrefix)]) == IntentCallDataPrefix } From 8910e2f71f178e19f5b91d109fd907838e816f2d Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:37:07 +0200 Subject: [PATCH 27/87] chore!: Rename to model the intents deps --- go.mod | 2 +- go.sum | 4 ++-- pkg/bundler/solveintents.go | 23 +++++++++++------------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index c3a32bc0..5b55689e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.21.3 require ( - github.com/blndgs/intents v0.2.4 + github.com/blndgs/model v0.3.2 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.13.5 diff --git a/go.sum b/go.sum index 864a77fe..45ea62ef 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ 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.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/blndgs/intents v0.2.4 h1:DyK76ssP7QpXg4BF2RNonBOx7aX26woHEMcilH5QxCc= -github.com/blndgs/intents v0.2.4/go.mod h1:wNIlLUBBRLy2ajOnNH1tRjfhnK8fTPHryrdiib93yn0= +github.com/blndgs/model v0.3.2 h1:SDagpW1LexaE3s8asZuTBjR1Yz69WICrDHHZ+PPKvMw= +github.com/blndgs/model v0.3.2/go.mod h1:W1Z2jUuU4vtrPZ0H6NlgZeX7h+cjNxfhjLUSQZCCrCs= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= diff --git a/pkg/bundler/solveintents.go b/pkg/bundler/solveintents.go index c7037014..fdf8eaf9 100644 --- a/pkg/bundler/solveintents.go +++ b/pkg/bundler/solveintents.go @@ -8,14 +8,14 @@ import ( "net/http" "time" - intentsdt "github.com/blndgs/intents" + "github.com/blndgs/model" "github.com/ethereum/go-ethereum/common" "github.com/stackup-wallet/stackup-bundler/pkg/userop" ) type IntentOpsBatch map[string]*userop.UserOperation -type IntentsBatch map[string]*intentsdt.Intent +type IntentsBatch map[string]*model.Intent type OrigBatchIdx map[string]int type EntryPointIntents struct { @@ -37,10 +37,9 @@ func NewEntryPointIntents(entryPoint common.Address, origBatch []*userop.UserOpe } } -func sendToSolver(solverClient *http.Client, solverURL string, senderAddress string, intents []*intentsdt.Intent) ([]*intentsdt.Intent, error) { +func sendToSolver(solverClient *http.Client, solverURL string, senderAddress string, intents []*model.Intent) ([]*model.Intent, error) { // Create a Body instance and populate it - body := intentsdt.Body{ - Sender: senderAddress, // Check if the sender address is needed + body := model.Body{ Intents: intents, } @@ -84,10 +83,10 @@ func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { } l := i.logger.WithName("solveIntents") - intents := make([]*intentsdt.Intent, len(intentsBatch.Intents)) + intents := make([]*model.Intent, len(intentsBatch.Intents)) j := 0 for _, itt := range intentsBatch.Intents { - itt.Status = intentsdt.SentToSolver + itt.Status = model.SentToSolver intents[j] = itt j++ } @@ -103,13 +102,13 @@ func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { for _, intent := range intents { switch intent.Status { - case intentsdt.Solved: + case model.Solved: // Set the solution back to the original userOp intentsBatch.OrigBatch[intentsBatch.UserOpsOrigIdx[intent.Hash]].CallData = []byte(intent.CallData) - case intentsdt.Unsolved: + case model.Unsolved: // will be retried till expired - intentsBatch.Intents[intent.Hash].Status = intentsdt.Unsolved - case intentsdt.Expired, intentsdt.Invalid: + intentsBatch.Intents[intent.Hash].Status = model.Unsolved + case model.Expired, model.Invalid: delete(intentsBatch.Intents, intent.Hash) delete(intentsBatch.IntentsOps, intent.Hash) l.WithValues("intent_hash", intent.Hash, @@ -129,7 +128,7 @@ func (i *Bundler) identifyIntents(entryPoint common.Address, batch []*userop.Use for idx, userOp := range batch { opHash := userOp.GetUserOpHash(entryPoint, i.chainID).String() - var intent intentsdt.Intent + var intent model.Intent if userOp.IsIntent() { userOp.RemoveIntentPrefix() if err := json.Unmarshal(userOp.CallData, &intent); err != nil { From 43c3c85cbaf7d3e0a6e910b917ef9d78536a22cb Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:49:22 +0200 Subject: [PATCH 28/87] feat: Add debugging getDeposits dump method --- pkg/client/debug.go | 2 ++ pkg/modules/context.go | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/client/debug.go b/pkg/client/debug.go index c53f9634..93aaa922 100644 --- a/pkg/client/debug.go +++ b/pkg/client/debug.go @@ -1,6 +1,7 @@ package client import ( + "encoding/hex" "encoding/json" "errors" "fmt" @@ -8,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" + "github.com/stackup-wallet/stackup-bundler/pkg/bundler" "github.com/stackup-wallet/stackup-bundler/pkg/mempool" "github.com/stackup-wallet/stackup-bundler/pkg/signer" diff --git a/pkg/modules/context.go b/pkg/modules/context.go index 9865c033..501a517b 100644 --- a/pkg/modules/context.go +++ b/pkg/modules/context.go @@ -109,6 +109,16 @@ func (c *UserOpHandlerCtx) GetDepositInfo(entity common.Address) *entrypoint.ISt return dep.(*entrypoint.IStakeManagerDepositInfo) } +// GetDeposits retrieves deposits if any +func (c *UserOpHandlerCtx) GetDeposits() (deps []*entrypoint.IStakeManagerDepositInfo) { + c.deposits.Range(func(key, value any) bool { + deps = append(deps, value.(*entrypoint.IStakeManagerDepositInfo)) + return true + }) + + return deps +} + // GetPendingOps returns all pending UserOperations in the mempool by the same UserOp.Sender. func (c *UserOpHandlerCtx) GetPendingOps() []*userop.UserOperation { return c.pendingOps From 5ad6ed4e294960fd5d21eca9defe052937d111cb Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:27:13 +0200 Subject: [PATCH 29/87] fix: Downgrade deps to heal Bundler --- go.mod | 46 ++++++--------- go.sum | 182 ++++++++++++++++----------------------------------------- 2 files changed, 68 insertions(+), 160 deletions(-) diff --git a/go.mod b/go.mod index 5b55689e..87ef0f96 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,17 @@ module github.com/stackup-wallet/stackup-bundler -go 1.21.3 +go 1.19 require ( - github.com/blndgs/model v0.3.2 + github.com/blndgs/model v0.3.5 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 - github.com/ethereum/go-ethereum v1.13.5 + github.com/ethereum/go-ethereum v1.11.5 github.com/gin-contrib/cors v1.4.0 - github.com/gin-gonic/gin v1.9.1 + github.com/gin-gonic/gin v1.9.0 github.com/go-logr/logr v1.2.4 github.com/go-logr/zerologr v1.2.3 - github.com/go-playground/validator/v10 v10.14.0 + github.com/go-playground/validator/v10 v10.12.0 github.com/google/go-cmp v0.5.9 github.com/joho/godotenv v1.5.1 github.com/metachris/flashbotsrpc v0.5.0 @@ -32,15 +32,13 @@ require ( go.opentelemetry.io/otel/sdk v1.16.0 go.opentelemetry.io/otel/sdk/metric v0.39.0 go.opentelemetry.io/otel/trace v1.16.0 - golang.org/x/sync v0.3.0 - golang.org/x/text v0.13.0 + golang.org/x/sync v0.1.0 + golang.org/x/text v0.9.0 google.golang.org/grpc v1.55.0 ) require ( - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/StackExchange/wmi v1.2.1 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect github.com/bytedance/sonic v1.9.1 // indirect @@ -48,19 +46,14 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.12.1 // indirect - github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect - github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.5 // indirect + github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-stack/stack v1.8.1 // indirect @@ -69,13 +62,13 @@ require ( github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/holiman/uint256 v1.2.3 // indirect + github.com/holiman/uint256 v1.2.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.15 // indirect @@ -84,7 +77,6 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect @@ -96,9 +88,8 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect - github.com/supranational/blst v0.3.11 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect + github.com/tklauser/go-sysconf v0.3.5 // indirect + github.com/tklauser/numcpus v0.2.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect go.opencensus.io v0.24.0 // indirect @@ -106,15 +97,12 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 45ea62ef..e10b4a8b 100644 --- a/go.sum +++ b/go.sum @@ -38,24 +38,17 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -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.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= -github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= -github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 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.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/blndgs/model v0.3.2 h1:SDagpW1LexaE3s8asZuTBjR1Yz69WICrDHHZ+PPKvMw= -github.com/blndgs/model v0.3.2/go.mod h1:W1Z2jUuU4vtrPZ0H6NlgZeX7h+cjNxfhjLUSQZCCrCs= +github.com/blndgs/model v0.3.5 h1:PVpot3MFtdhu0yuLw65vVDRUr5AorI19nQh4CuwRq3Q= +github.com/blndgs/model v0.3.5/go.mod h1:MfOzkW3ZqL4v5HqUugDj61RU8f85wS0SCq/benI+xYA= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= @@ -67,7 +60,6 @@ github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqy github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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 v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -88,22 +80,10 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= -github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= -github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= -github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= -github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= -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/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= +github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -112,8 +92,6 @@ github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= -github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -132,6 +110,7 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -140,29 +119,23 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= -github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= -github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= +github.com/ethereum/go-ethereum v1.11.5 h1:3M1uan+LAUvdn+7wCEFrcMM4LJTeuxDrPTg/f31a5QQ= +github.com/ethereum/go-ethereum v1.11.5/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= +github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -173,11 +146,10 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs= github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho= -github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= -github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -185,8 +157,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= +github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -194,11 +166,9 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 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-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= @@ -234,8 +204,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/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/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= @@ -269,7 +239,6 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -282,28 +251,21 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= -github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/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.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= -github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= -github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= +github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= -github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -323,15 +285,11 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= @@ -345,10 +303,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/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/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/metachris/flashbotsrpc v0.5.0 h1:5OLpm6+6n4kXxeh3TZBeSj0PQWDxqUsOFwy7xertXQQ= github.com/metachris/flashbotsrpc v0.5.0/go.mod h1:UrS249kKA1PK27sf12M6tUxo/M4ayfFrBk7IMFY1TNw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -356,17 +312,12 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh 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.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= -github.com/mitchellh/pointerstructure v1.2.0/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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= @@ -377,27 +328,19 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= -github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -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.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/puzpuzpuz/xsync/v3 v3.0.1 h1:yhTYnDJlgIYp/3Bb14b43VfUPrk/QNJ1HrLYEZ8r2AE= github.com/puzpuzpuz/xsync/v3 v3.0.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 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.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= @@ -431,7 +374,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -450,36 +392,27 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= -github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= -github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= -github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -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/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= +github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 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/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= github.com/wangjia184/sortedset v0.0.0-20220209072355-af6d6d227aa7 h1:/9VctXVXpt04S1G44mCHPJh7RuIH3YGP8bAI0dC4t1o= github.com/wangjia184/sortedset v0.0.0-20220209072355-af6d6d227aa7/go.mod h1:yHUVPw1qUPZmDuKhFMHPOI4WjziTH2Wp/GeNjBAycpM= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -495,7 +428,6 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0/go.mod h1:Ep4uoO2ijR0f49Pr7jAqyTjSCyS1SRL18wwttKfwqXA= go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= -go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0= @@ -520,7 +452,6 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -533,8 +464,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -545,8 +476,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -570,8 +500,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -605,8 +533,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -627,8 +555,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -639,7 +567,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -663,6 +590,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -675,10 +603,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -688,13 +614,12 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -744,8 +669,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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= @@ -862,11 +785,10 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/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.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -883,5 +805,3 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From fcc859f0a8f4429ad111dd4a79adfdb9017f1f5c Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:28:51 +0200 Subject: [PATCH 30/87] fix: Remove redundant call --- pkg/bundler/solveintents.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/bundler/solveintents.go b/pkg/bundler/solveintents.go index fdf8eaf9..00161466 100644 --- a/pkg/bundler/solveintents.go +++ b/pkg/bundler/solveintents.go @@ -157,9 +157,6 @@ func (i *Bundler) identifyIntents(entryPoint common.Address, batch []*userop.Use intentsBatch.UserOpsOrigIdx[opHash] = idx } } - if len(intentsBatch.Intents) > 0 { - i.solveIntents(intentsBatch) - } return intentsBatch } From b45cf60281671bd399a9db09af9d6c4188c34926 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 4 Dec 2023 09:31:37 -0300 Subject: [PATCH 31/87] Adding deploy script. --- .github/workflows/deploy.yml | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 47f6dc90..642d0fd0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,7 +3,7 @@ name: Deploy to ECR on: push: - branches: [ proxy-ethnode ] + branches: [ proxy-ethnode, test ] jobs: @@ -11,7 +11,6 @@ jobs: name: Build Image runs-on: ubuntu-latest - steps: @@ -23,7 +22,7 @@ jobs: with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-north-1 + aws-region: ${{ secrets.AWS_REGION }} - name: Login to Amazon ECR id: login-ecr @@ -32,8 +31,35 @@ jobs: - name: Build, tag, and push image to Amazon ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: stackup-bundler + ECR_REPOSITORY: ${{ secrets.AWS_REPOSITORY }} IMAGE_TAG: ${{ github.sha }} run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + deploy: + name: Deploy Image and Env Variables + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Deploy + uses: peterkimzz/aws-ssm-send-command@master + id: deploy + with: + aws-region: ${{ secrets.AWS_REGION }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + instance-ids: ${{ secrets.INSTANCE_ID }} + working-directory: /root/stackup-bundler + command: | + aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.ECR_URL }} + yq w -i docker-compose.yaml 'services.stackup-bundler.image' ${{ secrets.ECR_URL }}/${{ secrets.AWS_REPOSITORY }}:${{ github.sha }} + yq w -i docker-compose.yaml 'services.stackup-bundler.environment[0]' "ERC4337_BUNDLER_ETH_CLIENT_URL=${{ secrets.ERC4337_BUNDLER_ETH_CLIENT_URL }}" + yq w -i docker-compose.yaml 'services.stackup-bundler.environment[1]' "ERC4337_BUNDLER_PRIVATE_KEY=${{ secrets.ERC4337_BUNDLER_PRIVATE_KEY }}" + yq w -i docker-compose.yaml 'services.stackup-bundler.environment[2]' "ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT=${{ secrets.ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT }}" + yq w -i docker-compose.yaml 'services.stackup-bundler.environment[3]' "ERC4337_BUNDLER_DEBUG_MODE=${{ secrets.ERC4337_BUNDLER_DEBUG_MODE }}" + docker-compose up -d + + # Catch SSM outputs + - name: Get the outputs from Deploy image + run: echo "The Command id is ${{ steps.deploy.outputs.command-id }}" From 5875d260e1f4472516e47e826b790b9a5e748918 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Mon, 4 Dec 2023 20:35:33 +0200 Subject: [PATCH 32/87] feat: Add solving intents when receiving userOps --- internal/start/private.go | 9 +- internal/start/searcher.go | 2 +- pkg/bundler/bundler.go | 4 - pkg/client/client.go | 20 ++++ pkg/client/solveintents.go | 189 +++++++++++++++++++++++++++++++++++++ pkg/userop/intent.go | 10 ++ 6 files changed, 225 insertions(+), 9 deletions(-) create mode 100644 pkg/client/solveintents.go create mode 100644 pkg/userop/intent.go diff --git a/internal/start/private.go b/internal/start/private.go index 112fd125..bd291cdd 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -6,12 +6,15 @@ import ( "log" "net/http" - badger "github.com/dgraph-io/badger/v3" + "github.com/dgraph-io/badger/v3" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" + "go.opentelemetry.io/otel" + "github.com/stackup-wallet/stackup-bundler/internal/config" "github.com/stackup-wallet/stackup-bundler/internal/logger" "github.com/stackup-wallet/stackup-bundler/internal/o11y" @@ -28,8 +31,6 @@ import ( "github.com/stackup-wallet/stackup-bundler/pkg/modules/paymaster" "github.com/stackup-wallet/stackup-bundler/pkg/modules/relay" "github.com/stackup-wallet/stackup-bundler/pkg/signer" - "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" - "go.opentelemetry.io/otel" ) func PrivateMode() { @@ -128,7 +129,7 @@ func PrivateMode() { paymaster := paymaster.New(db) // Init Client - c := client.New(mem, ov, chain, conf.SupportedEntryPoints) + c := client.New(mem, ov, chain, conf.SupportedEntryPoints, conf.SolverUrl) c.SetGetUserOpReceiptFunc(client.GetUserOpReceiptWithEthClient(eth)) c.SetGetGasEstimateFunc( client.GetGasEstimateWithEthClient(rpc, ov, chain, conf.MaxBatchGasLimit), diff --git a/internal/start/searcher.go b/internal/start/searcher.go index 03f3a78b..c63f5b17 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -121,7 +121,7 @@ func SearcherMode() { paymaster := paymaster.New(db) // Init Client - c := client.New(mem, ov, chain, conf.SupportedEntryPoints) + c := client.New(mem, ov, chain, conf.SupportedEntryPoints, conf.SolverUrl) c.SetGetUserOpReceiptFunc(client.GetUserOpReceiptWithEthClient(eth)) c.SetGetGasEstimateFunc( client.GetGasEstimateWithEthClient(rpc, ov, chain, conf.MaxBatchGasLimit), diff --git a/pkg/bundler/bundler.go b/pkg/bundler/bundler.go index 997a7911..2d97f609 100644 --- a/pkg/bundler/bundler.go +++ b/pkg/bundler/bundler.go @@ -135,10 +135,6 @@ func (i *Bundler) Process(ep common.Address) (*modules.BatchHandlerCtx, error) { return nil, nil } - // Side effect of Solved Intent userOps: - // Calldata will be altered with the solution calldata received by the solver. - i.PreProcessIntents(ep, batch) - batch = adjustBatchSize(i.maxBatch, batch) // Get current block basefee diff --git a/pkg/client/client.go b/pkg/client/client.go index a5eda6a3..059128e8 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -4,10 +4,13 @@ package client import ( "errors" "math/big" + "net/http" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/go-logr/logr" + "github.com/stackup-wallet/stackup-bundler/internal/logger" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/filter" "github.com/stackup-wallet/stackup-bundler/pkg/gas" @@ -29,6 +32,9 @@ type Client struct { getUserOpReceipt GetUserOpReceiptFunc getGasEstimate GetGasEstimateFunc getUserOpByHash GetUserOpByHashFunc + solverURL string + solverClient *http.Client + entryPointsIntents EntryPointsIntents } // New initializes a new ERC-4337 client which can be extended with modules for validating UserOperations @@ -38,7 +44,15 @@ func New( ov *gas.Overhead, chainID *big.Int, supportedEntryPoints []common.Address, + solverURL string, ) *Client { + entryPointsIntents := make(map[common.Address]*EntryPointIntents) + for _, ep := range supportedEntryPoints { + if ep == common.HexToAddress("0x00") { + continue + } + entryPointsIntents[ep] = NewEntryPointIntent(ep, nil) + } return &Client{ mempool: mempool, ov: ov, @@ -49,6 +63,10 @@ func New( getUserOpReceipt: getUserOpReceiptNoop(), getGasEstimate: getGasEstimateNoop(), getUserOpByHash: getUserOpByHashNoop(), + solverURL: solverURL, + // TODO: Make timeout value configurable + solverClient: &http.Client{Timeout: 100 * time.Second}, + entryPointsIntents: entryPointsIntents, } } @@ -122,6 +140,8 @@ func (i *Client) SendUserOperation(op map[string]any, ep string) (string, error) return "", err } + i.processIntent(epAddr, userOp) + // Run through client module stack. ctx := modules.NewUserOpHandlerContext(userOp, penOps, epAddr, i.chainID) if err := i.userOpHandler(ctx); err != nil { diff --git a/pkg/client/solveintents.go b/pkg/client/solveintents.go new file mode 100644 index 00000000..f51d46c1 --- /dev/null +++ b/pkg/client/solveintents.go @@ -0,0 +1,189 @@ +package client + +import ( + "bytes" + "fmt" + "log" + "net/http" + "time" + + "github.com/blndgs/model" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + + "github.com/stackup-wallet/stackup-bundler/pkg/userop" +) + +type EntryPointsIntents map[common.Address]*EntryPointIntents + +type EntryPointIntents struct { + EntryPoint common.Address + NewIntent *model.Intent + NewIntentUserOp *userop.UserOperation // the userOp intent or nil + Unsolved []*model.Intent + InvalidIntents uint +} + +func NewEntryPointIntent(entryPoint common.Address, origOp *userop.UserOperation) *EntryPointIntents { + const unsolvedCap = 5 + return &EntryPointIntents{ + EntryPoint: entryPoint, + NewIntentUserOp: origOp, + Unsolved: make([]*model.Intent, 0, unsolvedCap), + } +} + +func sendToSolver(solverClient *http.Client, solverURL string, intents []*model.Intent) ([]*model.Intent, error) { + // Create a Body instance and populate it + body := model.Body{ + Intents: intents, + } + + // Marshal the Body instance into JSON + jsonBody, err := json.Marshal(body) + if err != nil { + return nil, err + } + + // Create a new HTTP POST request + req, err := http.NewRequest(http.MethodPost, solverURL, bytes.NewBuffer(jsonBody)) + if err != nil { + // Log error and return + log.Printf("Error creating request: %s", err.Error()) + return nil, err + } + + // Set the content type to application/json + req.Header.Set("Content-Type", "application/json") + + // Send the request + resp, err := solverClient.Do(req) + if err != nil { + log.Printf("Error occurred sending request. Error: %s", err.Error()) + return nil, err + } + defer resp.Body.Close() + + // Decode the returned intents back into the same slice of intents + if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { + log.Printf("Error decoding response: %s", err.Error()) + return nil, err + } + + return body.Intents, nil +} + +func (i *Client) solveIntent(entrypointIntent *EntryPointIntents) { + l := i.logger.WithName("solveIntents") + if entrypointIntent.NewIntentUserOp == nil { + l.Error( + fmt.Errorf("entryPointEntries or NewIntentUserOp is nil"), + "") + } + + hash := entrypointIntent.NewIntentUserOp.GetUserOpHash(entrypointIntent.EntryPoint, i.chainID).String() + + intent := model.Intent{ + Hash: hash, + Status: model.SentToSolver, + } + + intentsToSend := make([]*model.Intent, len(entrypointIntent.Unsolved)+1) + + // Add the new NewIntent first to solve + intentsToSend[0] = &intent + for u, unsolved := range entrypointIntent.Unsolved { + intentsToSend[u+1] = unsolved + intentsToSend[u+1].Status = model.SentToSolver + } + + // TODO: Add Backoff logic + var err error + intents, err := sendToSolver(i.solverClient, i.solverURL, intentsToSend) + if err != nil { + l.WithValues("number_intents", len(intents)). + Error(err, "failed to send intents to solver") + } + + for _, intent := range intents { + switch intent.Status { + case model.Solved: + // Set the solution back to the original userOp + entrypointIntent.NewIntentUserOp.CallData = []byte(intent.CallData) + case model.Unsolved: + // will be retried till expired + entrypointIntent.NewIntent.Status = model.Unsolved + entrypointIntent.Unsolved = append(entrypointIntent.Unsolved, intent) + default: + l.WithValues("intent_hash", intent.Hash, + "intent_status", intent.Status). + Error(fmt.Errorf("unknown intent status"), "unknown returned solver status") + } + } +} + +func (i *Client) identifyIntent(entrypointIntent *EntryPointIntents, userOp *userop.UserOperation) bool { + l := i.logger.WithName("identifyIntents") + opHash := userOp.GetUserOpHash(entrypointIntent.EntryPoint, i.chainID).String() + if !userOp.HasIntent() { + i.logger.WithValues("userop_hash", opHash, + "userop_nonce", userOp.Nonce, + "userop_sender", userOp.Sender.String(), + "userop_call_data", string(userOp.CallData)). + Info("userOp is not an intent") + + return false + } + + var intent model.Intent + if err := json.Unmarshal(userOp.CallData, &intent); err != nil { + l.WithValues( + "userop_hash", opHash, + "userop_nonce", userOp.Nonce, + "userop_sender", userOp.Sender.String(), + "is_intent", userOp.HasIntent(), + "call_data", userOp.CallData). + Error(err, "failed to unmarshal intent") + entrypointIntent.InvalidIntents++ + + return false + } + + // Save the identified intent + entrypointIntent.NewIntentUserOp = userOp + + // Set the intent hash to userOp's + intent.Hash = opHash + if intent.CreatedAt == 0 { + intent.CreatedAt = time.Now().Unix() + } + + return true +} + +func (i *Client) processIntent(entrypoint common.Address, userOp *userop.UserOperation) { + if userOp == nil { + i.logger.Error(fmt.Errorf("userOp is nil"), "userOp is nil") + } + if !userOp.HasIntent() { + i.logger.WithValues("userop_hash", userOp.GetUserOpHash(entrypoint, i.chainID).String(), + "userop_nonce", userOp.Nonce, + "userop_sender", userOp.Sender.String(), + "userop_call_data", string(userOp.CallData)). + Info("userOp is not an intent") + + return + } + + if i.entryPointsIntents[entrypoint] == nil { + i.entryPointsIntents[entrypoint] = NewEntryPointIntent(entrypoint, userOp) + + // TODO: Add scheduling logic for unsolved intents + } + + entrypointIntent := i.entryPointsIntents[entrypoint] + + if i.identifyIntent(entrypointIntent, userOp) { + i.solveIntent(entrypointIntent) + } +} diff --git a/pkg/userop/intent.go b/pkg/userop/intent.go new file mode 100644 index 00000000..a2825493 --- /dev/null +++ b/pkg/userop/intent.go @@ -0,0 +1,10 @@ +package userop + +import "github.com/goccy/go-json" + +// HasIntent checks if the userOp's `Calldata` is an intent userOp by checking +// whether it contains valid JSON. +func (op *UserOperation) HasIntent() bool { + var js json.RawMessage + return json.Unmarshal(op.CallData, &js) == nil +} From 541ba6cb9ed82bc074db0f7d6a59b9c9b0f18532 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:52:57 +0200 Subject: [PATCH 33/87] feat: Add queue, tests --- pkg/client/queue.go | 144 +++++++++++++++++++++++++++ pkg/client/queue_test.go | 208 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 352 insertions(+) create mode 100644 pkg/client/queue.go create mode 100644 pkg/client/queue_test.go diff --git a/pkg/client/queue.go b/pkg/client/queue.go new file mode 100644 index 00000000..f22d2437 --- /dev/null +++ b/pkg/client/queue.go @@ -0,0 +1,144 @@ +package client + +import ( + "sync" + + "github.com/pkg/errors" +) + +type Queue[T any] struct { + items []T + keys map[string]int + mu sync.Mutex +} + +func NewQueue[T any](capacity uint) *Queue[T] { + return &Queue[T]{ + items: make([]T, 0, capacity), + keys: make(map[string]int), + } +} + +func (q *Queue[T]) Delete(index int) error { + q.mu.Lock() + defer q.mu.Unlock() + + if index < 0 || index >= len(q.items) { + return errors.New("index out of range") + } + + q.items = append(q.items[:index], q.items[index+1:]...) + // Update keys map + for key, idx := range q.keys { + if idx > index { + q.keys[key] = idx - 1 + } else if idx == index { + delete(q.keys, key) + } + } + + return nil +} + +func (q *Queue[T]) FindIndexByKey(key string) (int, bool) { + q.mu.Lock() + defer q.mu.Unlock() + + index, found := q.keys[key] + return index, found +} + +func (q *Queue[T]) EnqueueWithKey(key string, item T) { + q.mu.Lock() + defer q.mu.Unlock() + + q.items = append(q.items, item) + q.keys[key] = len(q.items) - 1 +} + +func (q *Queue[T]) Reset(capacity uint) { + q.mu.Lock() + defer q.mu.Unlock() + + q.items = make([]T, 0, capacity) +} + +func (q *Queue[T]) Peek(index int) (T, error) { + q.mu.Lock() + defer q.mu.Unlock() + + if index < 0 || index >= len(q.items) { + var zeroValue T + return zeroValue, errors.New("index out of range") + } + + return q.items[index], nil +} + +func (q *Queue[T]) Range(f func(index int, value T)) { + q.mu.Lock() + defer q.mu.Unlock() + + for i, item := range q.items { + f(i, item) + } +} + +func (q *Queue[T]) Size() int { + q.mu.Lock() + defer q.mu.Unlock() + + return len(q.items) +} + +func (q *Queue[T]) EnqueueHead(key string, item T) { + q.mu.Lock() + defer q.mu.Unlock() + + q.items = append([]T{item}, q.items...) + q.updateKeysAfterEnqueue(0) + q.keys[key] = 0 +} + +func (q *Queue[T]) EnqueueTail(key string, item T) { + q.mu.Lock() + defer q.mu.Unlock() + + q.items = append(q.items, item) + q.keys[key] = len(q.items) - 1 +} + +func (q *Queue[T]) updateKeysAfterEnqueue(index int) { + for k, v := range q.keys { + if v >= index { + q.keys[k] = v + 1 + } + } +} + +func (q *Queue[T]) Dequeue() (T, bool) { + q.mu.Lock() + defer q.mu.Unlock() + + if len(q.items) == 0 { + var zeroValue T + return zeroValue, false + } + + item := q.items[0] + q.items = q.items[1:] + return item, true +} + +func (q *Queue[T]) ToSlice() []T { + q.mu.Lock() + defer q.mu.Unlock() + return append([]T(nil), q.items...) +} + +func (q *Queue[T]) Capacity() int { + q.mu.Lock() + defer q.mu.Unlock() + + return cap(q.items) +} diff --git a/pkg/client/queue_test.go b/pkg/client/queue_test.go new file mode 100644 index 00000000..d0d39ba4 --- /dev/null +++ b/pkg/client/queue_test.go @@ -0,0 +1,208 @@ +package client + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestQueueInitializationWithCapacity(t *testing.T) { + capacity := uint(10) + queue := NewQueue[int](capacity) + + // The initial length of the queue should be 0 + assert.Equal(t, 0, len(queue.ToSlice()), "Initial length of queue should be 0") +} + +func TestQueueFindIndexByKey(t *testing.T) { + queue := NewQueue[int](10) + queue.EnqueueWithKey("first", 1) + queue.EnqueueWithKey("second", 2) + + // Find index by key + index, found := queue.FindIndexByKey("second") + assert.True(t, found, "Item with key 'second' should be found") + assert.Equal(t, 1, index, "Index of item with key 'second' should be 1") + + // Try to find index by a non-existing key + _, found = queue.FindIndexByKey("third") + assert.False(t, found, "Item with key 'third' should not be found") +} + +func TestDequeueEmptyQueue(t *testing.T) { + queue := NewQueue[int](0) + + // Dequeue from an empty queue + item, ok := queue.Dequeue() + assert.False(t, ok, "Expected false from Dequeue on empty queue but got true") + assert.Equal(t, 0, item, "Expected zero value from Dequeue on empty queue") +} + +func TestQueueReset(t *testing.T) { + queue := NewQueue[int](10) + + // Add items to the queue + queue.EnqueueTail("key1", 1) + queue.EnqueueTail("key2", 2) + + // Reset the queue with a different capacity + newCapacity := uint(5) + queue.Reset(newCapacity) + + // After reset, the size should be 0 + assert.Equal(t, 0, queue.Size(), "Size of queue after reset should be 0") + + // The capacity of the queue should match the new capacity + assert.Equal(t, int(newCapacity), queue.Capacity(), "Capacity of queue after reset should match new capacity") +} + +func TestQueueSize(t *testing.T) { + queue := NewQueue[int](10) + + // Initially, the size should be 0 + assert.Equal(t, 0, queue.Size(), "Initial size of queue should be 0") + + // Add items to the queue + queue.EnqueueTail("key1", 1) + queue.EnqueueTail("key2", 2) + + // Now, the size should be 2 + assert.Equal(t, 2, queue.Size(), "Size of queue after enqueueing items should be 2") +} + +func TestEnqueueAndDequeue(t *testing.T) { + queue := NewQueue[int](0) + + // Test EnqueueTail + queue.EnqueueTail("key1", 1) + queue.EnqueueTail("key2", 2) + + // Test EnqueueHead + queue.EnqueueHead("key3", 3) + + // Check the state of the queue + expectedSliceAfterEnqueue := []int{3, 1, 2} + assert.Equal(t, expectedSliceAfterEnqueue, queue.ToSlice(), "Queue contents after enqueue operations are not as expected") + + // Test Dequeue + item, ok := queue.Dequeue() + assert.True(t, ok, "Expected true from Dequeue but got false") + assert.Equal(t, 3, item, "Dequeued item is not as expected") + + // Check the state of the queue after dequeue + expectedSliceAfterDequeue := []int{1, 2} + assert.Equal(t, expectedSliceAfterDequeue, queue.ToSlice(), "Queue contents after dequeue operation are not as expected") +} + +func TestQueueDelete(t *testing.T) { + queue := NewQueue[int](10) + queue.EnqueueTail("key1", 1) + queue.EnqueueTail("key2", 2) + queue.EnqueueTail("key3", 3) + + // Delete the item at index 1 (value 2) + err := queue.Delete(1) + assert.NoError(t, err, "Error should not occur when deleting valid index") + + expectedItems := []int{1, 3} + assert.Equal(t, expectedItems, queue.ToSlice(), "Items after deletion do not match expected items") +} + +func TestQueueToSlice(t *testing.T) { + queue := NewQueue[string](0) + + queue.EnqueueTail("key1", "a") + queue.EnqueueTail("key2", "b") + queue.EnqueueHead("key3", "c") + + expectedSlice := []string{"c", "a", "b"} + assert.Equal(t, expectedSlice, queue.ToSlice(), "Queue to slice conversion did not match expected slice") +} + +func TestQueueRange(t *testing.T) { + queue := NewQueue[int](10) + queue.EnqueueTail("key1", 10) + queue.EnqueueTail("key2", 20) + queue.EnqueueTail("key3", 30) + + var items []int + queue.Range(func(index int, value int) { + items = append(items, value) + }) + + expectedItems := []int{10, 20, 30} + assert.Equal(t, expectedItems, items, "Items iterated by Range do not match expected items") +} + +func TestConcurrentAccess(t *testing.T) { + queue := NewQueue[int](1) + + // Perform concurrent Enqueue and Dequeue operations + go func() { + for i := 0; i < 1000; i++ { + queue.EnqueueTail(fmt.Sprintf("key%d", i), i) + } + }() + + go func() { + for i := 0; i < 1000; i++ { + queue.EnqueueHead(fmt.Sprintf("headKey%d", i), i) + } + }() + + time.Sleep(1 * time.Second) // Wait for goroutines to finish + + // The exact contents of the queue are unpredictable due to concurrent access, + // but we can check the size of the queue + assert.Equal(t, 2000, len(queue.ToSlice()), "Queue size after concurrent access is not as expected") +} + +func TestPeek(t *testing.T) { + queue := NewQueue[int](50000) + + // Add items to the queue + queue.EnqueueTail("key1", 10) + queue.EnqueueTail("key2", 20) + queue.EnqueueTail("key3", 30) + + // Test valid Peek + item, err := queue.Peek(1) + assert.NoError(t, err, "Expected no error from Peek on valid index") + assert.Equal(t, 20, item, "Peeked item at index 1 is not as expected") + + // Test Peek with invalid index + _, err = queue.Peek(5) + assert.Error(t, err, "Expected error from Peek on invalid index") +} + +func TestQueueEnqueueHead(t *testing.T) { + queue := NewQueue[int](10) + queue.EnqueueHead("first", 1) + queue.EnqueueHead("second", 2) + + // Check if the items are enqueued correctly + expectedItems := []int{2, 1} + assert.Equal(t, expectedItems, queue.ToSlice(), "Items enqueued at head do not match expected items") + + // Check if the keys are stored correctly + index, found := queue.FindIndexByKey("second") + assert.True(t, found, "Item with key 'second' should be found") + assert.Equal(t, 0, index, "Index of item with key 'second' should be 0") +} + +func TestQueueEnqueueTail(t *testing.T) { + queue := NewQueue[int](10) + queue.EnqueueTail("first", 1) + queue.EnqueueTail("second", 2) + + // Check if the items are enqueued correctly + expectedItems := []int{1, 2} + assert.Equal(t, expectedItems, queue.ToSlice(), "Items enqueued at tail do not match expected items") + + // Check if the keys are stored correctly + index, found := queue.FindIndexByKey("second") + assert.True(t, found, "Item with key 'second' should be found") + assert.Equal(t, 1, index, "Index of item with key 'second' should be 1") +} From 258235bbb28183e9081581ae862c9ba2c73ce7cd Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 6 Dec 2023 18:57:05 +0200 Subject: [PATCH 34/87] feat: Plug q for intents buffer --- pkg/client/solveintents.go | 44 ++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/pkg/client/solveintents.go b/pkg/client/solveintents.go index f51d46c1..708c15f7 100644 --- a/pkg/client/solveintents.go +++ b/pkg/client/solveintents.go @@ -20,7 +20,8 @@ type EntryPointIntents struct { EntryPoint common.Address NewIntent *model.Intent NewIntentUserOp *userop.UserOperation // the userOp intent or nil - Unsolved []*model.Intent + Unsolved *Queue[*model.Intent] + Buffer map[string]*userop.UserOperation // buffer for intent userOps to be sent to Solver InvalidIntents uint } @@ -29,7 +30,8 @@ func NewEntryPointIntent(entryPoint common.Address, origOp *userop.UserOperation return &EntryPointIntents{ EntryPoint: entryPoint, NewIntentUserOp: origOp, - Unsolved: make([]*model.Intent, 0, unsolvedCap), + Unsolved: NewQueue[*model.Intent](unsolvedCap), + Buffer: make(map[string]*userop.UserOperation), } } @@ -88,36 +90,42 @@ func (i *Client) solveIntent(entrypointIntent *EntryPointIntents) { Status: model.SentToSolver, } - intentsToSend := make([]*model.Intent, len(entrypointIntent.Unsolved)+1) - // Add the new NewIntent first to solve - intentsToSend[0] = &intent - for u, unsolved := range entrypointIntent.Unsolved { - intentsToSend[u+1] = unsolved - intentsToSend[u+1].Status = model.SentToSolver - } + entrypointIntent.Unsolved.EnqueueHead(hash, &intent) + entrypointIntent.Unsolved.Range(func(index int, value *model.Intent) { + value.Status = model.SentToSolver + }) // TODO: Add Backoff logic var err error - intents, err := sendToSolver(i.solverClient, i.solverURL, intentsToSend) + intents, err := sendToSolver(i.solverClient, i.solverURL, entrypointIntent.Unsolved.ToSlice()) if err != nil { l.WithValues("number_intents", len(intents)). Error(err, "failed to send intents to solver") } + entrypointIntent.Unsolved.Reset(uint(len(intents))) + for _, intent := range intents { + if intent.ExpirationAt < time.Now().Unix() { + // expired, log & drop + l.WithValues("intent_hash", intent.Hash, + "intent_status", intent.Status). + Info("dropping expired intent") + continue + } switch intent.Status { case model.Solved: // Set the solution back to the original userOp - entrypointIntent.NewIntentUserOp.CallData = []byte(intent.CallData) + entrypointIntent.Unsolved.EnqueueTail(intent.Hash, intent) case model.Unsolved: // will be retried till expired - entrypointIntent.NewIntent.Status = model.Unsolved - entrypointIntent.Unsolved = append(entrypointIntent.Unsolved, intent) + entrypointIntent.Unsolved.EnqueueHead(intent.Hash, intent) default: + // invalid or expired l.WithValues("intent_hash", intent.Hash, "intent_status", intent.Status). - Error(fmt.Errorf("unknown intent status"), "unknown returned solver status") + Info("dropping intent") } } } @@ -151,12 +159,20 @@ func (i *Client) identifyIntent(entrypointIntent *EntryPointIntents, userOp *use // Save the identified intent entrypointIntent.NewIntentUserOp = userOp + entrypointIntent.Buffer[opHash] = userOp + entrypointIntent.NewIntent = &intent + entrypointIntent.NewIntent.Hash = opHash + intent.Status = model.Received + entrypointIntent.Unsolved.EnqueueHead(opHash, &intent) // Set the intent hash to userOp's intent.Hash = opHash if intent.CreatedAt == 0 { intent.CreatedAt = time.Now().Unix() } + if intent.ExpirationAt == 0 { + intent.ExpirationAt = time.Unix(intent.CreatedAt, 0).Add(time.Duration(100 * time.Second)).Unix() + } return true } From c5f4b7b14de8aeb6b62774348ac236110ced4104 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 7 Dec 2023 01:58:34 +0200 Subject: [PATCH 35/87] feat: Add userOps send script, parse hex callData, json intent --- pkg/userop/parse.go | 22 ++++++--- scripts/senduserop/main.go | 97 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 scripts/senduserop/main.go diff --git a/pkg/userop/parse.go b/pkg/userop/parse.go index d0486c4e..1a38fe7f 100644 --- a/pkg/userop/parse.go +++ b/pkg/userop/parse.go @@ -1,6 +1,7 @@ package userop import ( + "encoding/base64" "encoding/hex" "errors" "fmt" @@ -55,17 +56,24 @@ func decodeOpTypes( // String to []byte conversion if f == reflect.String && t == reflect.Slice { byteStr := data.(string) - if len(byteStr) < 2 || byteStr[:2] != "0x" { - return nil, errors.New("not byte string") + + // Check for hex encoding + if len(byteStr) >= 2 && byteStr[:2] == "0x" { + b, err := hex.DecodeString(byteStr[2:]) + if err != nil { + return nil, err + } + return b, nil } - b, err := hex.DecodeString(byteStr[2:]) - if err != nil { - return nil, err + // Handle base64 encoding + b, err := base64.StdEncoding.DecodeString(byteStr) + if err == nil { + return b, nil } - return b, nil - } + return nil, errors.New("string is not valid hex or base64 encoded") + } return data, nil } diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go new file mode 100644 index 00000000..9d79d950 --- /dev/null +++ b/scripts/senduserop/main.go @@ -0,0 +1,97 @@ +package main + +import ( + "bytes" + "fmt" + "math/big" + "net/http" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/goccy/go-json" + "github.com/spf13/viper" + + "github.com/stackup-wallet/stackup-bundler/pkg/signer" + "github.com/stackup-wallet/stackup-bundler/pkg/userop" +) + +type JsonRpcRequest struct { + Jsonrpc string `json:"jsonrpc"` + Id int `json:"id"` + Method string `json:"method"` + Params []interface{} `json:"params"` +} + +func main() { + + viper.SetConfigName(".env") + viper.SetConfigType("env") + viper.AddConfigPath(".") + if err := viper.ReadInConfig(); err != nil { + panic(fmt.Errorf("fatal error config file: %w", err)) + } + + s, err := signer.New(viper.GetString("erc4337_bundler_private_key")) + if err != nil { + panic(fmt.Errorf("fatal signer error: %w", err)) + } + fmt.Printf("Public key: %s\n", hexutil.Encode(crypto.FromECDSAPub(s.PublicKey))[4:]) + fmt.Printf("Address: %s\n", s.Address) + + sender := common.HexToAddress("0x3068c2408c01bECde4BcCB9f246b56651BE1d12D") + nonce := big.NewInt(11) + // initCode := hex.EncodeToString([]byte{}) + callData := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` + callGasLimit := big.NewInt(12052) + verificationGasLimit := big.NewInt(58592) + preVerificationGas := big.NewInt(48000) + maxFeePerGas := big.NewInt(0xac97bb286) + maxPriorityFeePerGas := big.NewInt(0xac97bb264) + // paymasterAndData := hex.EncodeToString([]byte{}) + + // Placeholder for signature + // signature := hex.EncodeToString([]byte{}) + + userOp := userop.UserOperation{ + Sender: sender, + Nonce: nonce, + InitCode: []byte{}, + CallData: []byte(callData), + CallGasLimit: callGasLimit, + VerificationGasLimit: verificationGasLimit, + PreVerificationGas: preVerificationGas, + MaxFeePerGas: maxFeePerGas, + MaxPriorityFeePerGas: maxPriorityFeePerGas, + PaymasterAndData: []byte{}, + Signature: []byte{}, + } + + const entrypointAddr = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + request := JsonRpcRequest{ + Jsonrpc: "2.0", + Id: 3, + Method: "eth_sendUserOperation", + Params: []interface{}{userOp, entrypointAddr}, + } + + requestBytes, err := json.Marshal(request) + if err != nil { + panic(err) + } + + resp, err := http.Post("http://localhost:4337", "application/json", bytes.NewBuffer(requestBytes)) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + // Decode the response + var result map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + panic(err) + } + + // Print the response + fmt.Println("Response from server:", result) +} From 93c4fec1ffee8f4ee9a188e78a479051a7c78ea4 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 7 Dec 2023 16:02:57 +0200 Subject: [PATCH 36/87] chore: Adjust limits, value for testing --- scripts/senduserop/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 9d79d950..14e86180 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -43,9 +43,9 @@ func main() { nonce := big.NewInt(11) // initCode := hex.EncodeToString([]byte{}) callData := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` - callGasLimit := big.NewInt(12052) + callGasLimit := big.NewInt(15000) // error if below 12100 verificationGasLimit := big.NewInt(58592) - preVerificationGas := big.NewInt(48000) + preVerificationGas := big.NewInt(56000) // error if below 54560 maxFeePerGas := big.NewInt(0xac97bb286) maxPriorityFeePerGas := big.NewInt(0xac97bb264) // paymasterAndData := hex.EncodeToString([]byte{}) From c56a8b086b6ed53edd8e6d04f48fb08054c40822 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 7 Dec 2023 22:09:39 +0200 Subject: [PATCH 37/87] feat: Add client Intents buffer implementation --- pkg/client/client.go | 59 +++++++--- pkg/client/queue.go | 40 ++++++- pkg/client/queue_test.go | 24 +++- pkg/client/solveintents.go | 222 +++++++++++++++++++------------------ 4 files changed, 215 insertions(+), 130 deletions(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index aabea533..42a26c5f 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -37,6 +37,7 @@ type Client struct { solverURL string solverClient *http.Client entryPointsIntents EntryPointsIntents + solvedOps chan *userop.UserOperation } // New initializes a new ERC-4337 client which can be extended with modules for validating UserOperations @@ -48,14 +49,9 @@ func New( supportedEntryPoints []common.Address, solverURL string, ) *Client { - entryPointsIntents := make(map[common.Address]*EntryPointIntents) - for _, ep := range supportedEntryPoints { - if ep == common.HexToAddress("0x00") { - continue - } - entryPointsIntents[ep] = NewEntryPointIntent(ep, nil) - } - return &Client{ + const userOpsBatchSize = 200 + + client := &Client{ mempool: mempool, ov: ov, chainID: chainID, @@ -69,8 +65,20 @@ func New( solverURL: solverURL, // TODO: Make timeout value configurable solverClient: &http.Client{Timeout: 100 * time.Second}, - entryPointsIntents: entryPointsIntents, + entryPointsIntents: make(map[common.Address]*EntryPointIntents), + solvedOps: make(chan *userop.UserOperation, userOpsBatchSize), } + + for _, ep := range supportedEntryPoints { + if ep == common.HexToAddress("0x00") { + continue + } + + // Start Solved Intent userOps consumer + go client.processIntentUserOps(ep) + } + + return client } func (i *Client) parseEntryPointAddress(ep string) (common.Address, error) { @@ -140,33 +148,48 @@ func (i *Client) SendUserOperation(op map[string]any, ep string) (string, error) l.Error(err, "eth_sendUserOperation error") return "", err } - hash := userOp.GetUserOpHash(epAddr, i.chainID) - l = l.WithValues("userop_hash", hash) + + i.processIntent(epAddr, userOp) + + if !userOp.HasIntent() { + return i.addToMemPool(epAddr, userOp) + } + + return userOp.GetUserOpHash(epAddr, i.chainID).String(), nil +} + +func (i *Client) addToMemPool(epAddr common.Address, userOp *userop.UserOperation) (string, error) { + l := i.logger.WithName("addToMemPool") + + opHash := userOp.GetUserOpHash(epAddr, i.chainID) + l = l.WithValues("userop_hash", opHash, + "userop_nonce", userOp.Nonce, + "userop_sender", userOp.Sender.String(), + "userop_call_data", string(userOp.CallData)) // Fetch any pending UserOperations in the mempool by the same sender penOps, err := i.mempool.GetOps(epAddr, userOp.Sender) if err != nil { - l.Error(err, "eth_sendUserOperation error") + l.Error(err, "addToMemPool error") return "", err } - i.processIntent(epAddr, userOp) - // Run through client module stack. ctx := modules.NewUserOpHandlerContext(userOp, penOps, epAddr, i.chainID) if err := i.userOpHandler(ctx); err != nil { - l.Error(err, "eth_sendUserOperation error") + l.Error(err, "addToMemPool error") return "", err } // Add userOp to mempool. if err := i.mempool.AddOp(epAddr, ctx.UserOp); err != nil { - l.Error(err, "eth_sendUserOperation error") + l.Error(err, "addToMemPool error") return "", err } - l.Info("eth_sendUserOperation ok") - return hash.String(), nil + l.Info("addToMemPool ok") + + return opHash.String(), nil } // EstimateUserOperationGas returns estimates for PreVerificationGas, VerificationGasLimit, and CallGasLimit diff --git a/pkg/client/queue.go b/pkg/client/queue.go index f22d2437..89232415 100644 --- a/pkg/client/queue.go +++ b/pkg/client/queue.go @@ -2,14 +2,16 @@ package client import ( "sync" + "time" "github.com/pkg/errors" ) type Queue[T any] struct { - items []T - keys map[string]int - mu sync.Mutex + items []T + keys map[string]int + ticker *time.Ticker + mu sync.Mutex } func NewQueue[T any](capacity uint) *Queue[T] { @@ -19,6 +21,36 @@ func NewQueue[T any](capacity uint) *Queue[T] { } } +func (q *Queue[T]) SetTickerFunc(duration time.Duration, tickFunc func()) { + q.mu.Lock() + defer q.mu.Unlock() + + // Prevent multiple tickers from running simultaneously + // by stopping the previous ticker + if q.ticker != nil { + q.ticker.Stop() + } + + if duration > 0 && tickFunc != nil { + q.ticker = time.NewTicker(duration) + go func() { + for range q.ticker.C { + tickFunc() + } + }() + } +} + +func (q *Queue[T]) StopTicker() { + q.mu.Lock() + defer q.mu.Unlock() + + if q.ticker != nil { + q.ticker.Stop() + q.ticker = nil + } +} + func (q *Queue[T]) Delete(index int) error { q.mu.Lock() defer q.mu.Unlock() @@ -56,7 +88,7 @@ func (q *Queue[T]) EnqueueWithKey(key string, item T) { q.keys[key] = len(q.items) - 1 } -func (q *Queue[T]) Reset(capacity uint) { +func (q *Queue[T]) Reset(capacity int) { q.mu.Lock() defer q.mu.Unlock() diff --git a/pkg/client/queue_test.go b/pkg/client/queue_test.go index d0d39ba4..5bcd7bc9 100644 --- a/pkg/client/queue_test.go +++ b/pkg/client/queue_test.go @@ -16,6 +16,28 @@ func TestQueueInitializationWithCapacity(t *testing.T) { assert.Equal(t, 0, len(queue.ToSlice()), "Initial length of queue should be 0") } +func TestQueueTickerFunc(t *testing.T) { + queue := NewQueue[int](10) + + tickChannel := make(chan bool) + tickFunc := func() { + tickChannel <- true + } + + // Set a short duration for the ticker to trigger quickly + queue.SetTickerFunc(100*time.Millisecond, tickFunc) + + // Wait for the ticker function to be executed + select { + case <-tickChannel: + // Ticker function executed + case <-time.After(1 * time.Second): + t.Fatal("Ticker function was not executed within expected time") + } + + queue.StopTicker() +} + func TestQueueFindIndexByKey(t *testing.T) { queue := NewQueue[int](10) queue.EnqueueWithKey("first", 1) @@ -48,7 +70,7 @@ func TestQueueReset(t *testing.T) { queue.EnqueueTail("key2", 2) // Reset the queue with a different capacity - newCapacity := uint(5) + newCapacity := 5 queue.Reset(newCapacity) // After reset, the size should be 0 diff --git a/pkg/client/solveintents.go b/pkg/client/solveintents.go index 708c15f7..39079ef4 100644 --- a/pkg/client/solveintents.go +++ b/pkg/client/solveintents.go @@ -3,12 +3,12 @@ package client import ( "bytes" "fmt" - "log" "net/http" "time" "github.com/blndgs/model" "github.com/ethereum/go-ethereum/common" + "github.com/go-logr/logr" "github.com/goccy/go-json" "github.com/stackup-wallet/stackup-bundler/pkg/userop" @@ -17,115 +17,93 @@ import ( type EntryPointsIntents map[common.Address]*EntryPointIntents type EntryPointIntents struct { - EntryPoint common.Address - NewIntent *model.Intent - NewIntentUserOp *userop.UserOperation // the userOp intent or nil - Unsolved *Queue[*model.Intent] - Buffer map[string]*userop.UserOperation // buffer for intent userOps to be sent to Solver - InvalidIntents uint + EntryPoint common.Address + Unsolved *Queue[*model.Intent] + Buffer map[string]*userop.UserOperation // buffer for intent userOps to be sent to Solver + InvalidIntents uint } -func NewEntryPointIntent(entryPoint common.Address, origOp *userop.UserOperation) *EntryPointIntents { +func NewEntryPointIntent(entryPoint common.Address) *EntryPointIntents { const unsolvedCap = 5 return &EntryPointIntents{ - EntryPoint: entryPoint, - NewIntentUserOp: origOp, - Unsolved: NewQueue[*model.Intent](unsolvedCap), - Buffer: make(map[string]*userop.UserOperation), + EntryPoint: entryPoint, + Unsolved: NewQueue[*model.Intent](unsolvedCap), + Buffer: make(map[string]*userop.UserOperation), } } -func sendToSolver(solverClient *http.Client, solverURL string, intents []*model.Intent) ([]*model.Intent, error) { - // Create a Body instance and populate it - body := model.Body{ - Intents: intents, - } - - // Marshal the Body instance into JSON - jsonBody, err := json.Marshal(body) - if err != nil { - return nil, err - } - - // Create a new HTTP POST request - req, err := http.NewRequest(http.MethodPost, solverURL, bytes.NewBuffer(jsonBody)) - if err != nil { - // Log error and return - log.Printf("Error creating request: %s", err.Error()) - return nil, err - } - - // Set the content type to application/json - req.Header.Set("Content-Type", "application/json") - - // Send the request - resp, err := solverClient.Do(req) - if err != nil { - log.Printf("Error occurred sending request. Error: %s", err.Error()) - return nil, err - } - defer resp.Body.Close() +func sendToSolver(log logr.Logger, unsolvedQ *Queue[*model.Intent], solvedOps chan *userop.UserOperation, + epIntents *EntryPointIntents, solverClient *http.Client, solverURL string) func() { + return func() { + l := log.WithName("sendToSolver") + // Get the unsolved intents from the queue + intents := unsolvedQ.ToSlice() - // Decode the returned intents back into the same slice of intents - if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { - log.Printf("Error decoding response: %s", err.Error()) - return nil, err - } - - return body.Intents, nil -} + // If there are no intents, return + if len(intents) == 0 { + return + } -func (i *Client) solveIntent(entrypointIntent *EntryPointIntents) { - l := i.logger.WithName("solveIntents") - if entrypointIntent.NewIntentUserOp == nil { - l.Error( - fmt.Errorf("entryPointEntries or NewIntentUserOp is nil"), - "") - } + epIntents.Unsolved.Reset(len(intents)) - hash := entrypointIntent.NewIntentUserOp.GetUserOpHash(entrypointIntent.EntryPoint, i.chainID).String() + // Rest of the sendToSolver logic + body := model.Body{ + Intents: intents, + } + jsonBody, err := json.Marshal(body) + if err != nil { + l.WithValues("number_intents", len(intents)). + Error(err, "failed to marshal intents") + return + } - intent := model.Intent{ - Hash: hash, - Status: model.SentToSolver, - } + req, err := http.NewRequest(http.MethodPost, solverURL, bytes.NewBuffer(jsonBody)) + if err != nil { + l.WithValues("number_intents", len(intents)). + Error(err, "failed to create request") + return + } - // Add the new NewIntent first to solve - entrypointIntent.Unsolved.EnqueueHead(hash, &intent) - entrypointIntent.Unsolved.Range(func(index int, value *model.Intent) { - value.Status = model.SentToSolver - }) - - // TODO: Add Backoff logic - var err error - intents, err := sendToSolver(i.solverClient, i.solverURL, entrypointIntent.Unsolved.ToSlice()) - if err != nil { - l.WithValues("number_intents", len(intents)). - Error(err, "failed to send intents to solver") - } + req.Header.Set("Content-Type", "application/json") - entrypointIntent.Unsolved.Reset(uint(len(intents))) + resp, err := solverClient.Do(req) + if err != nil { + l.WithValues("number_intents", len(intents)). + Error(err, "failed to send request") + return + } + defer resp.Body.Close() - for _, intent := range intents { - if intent.ExpirationAt < time.Now().Unix() { - // expired, log & drop - l.WithValues("intent_hash", intent.Hash, - "intent_status", intent.Status). - Info("dropping expired intent") - continue + if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { + l.WithValues("number_intents", len(intents)). + Error(err, "failed to decode response") + return } - switch intent.Status { - case model.Solved: - // Set the solution back to the original userOp - entrypointIntent.Unsolved.EnqueueTail(intent.Hash, intent) - case model.Unsolved: - // will be retried till expired - entrypointIntent.Unsolved.EnqueueHead(intent.Hash, intent) - default: - // invalid or expired - l.WithValues("intent_hash", intent.Hash, - "intent_status", intent.Status). - Info("dropping intent") + + for _, intent := range intents { + if intent.ExpirationAt < time.Now().Unix() { + // expired, log & drop + l.WithValues("intent_hash", intent.Hash, + "intent_status", intent.Status). + Info("dropping expired intent") + continue + } + switch intent.Status { + case model.Solved: + // Set the solution to be processed by the bundler client + solvedUserOp := epIntents.Buffer[intent.Hash] + solvedUserOp.CallData = []byte(intent.CallData) + solvedOps <- solvedUserOp + delete(epIntents.Buffer, intent.Hash) + case model.Unsolved: + // will be retried till expired + epIntents.Unsolved.EnqueueHead(intent.Hash, intent) + default: + // invalid or expired + l.WithValues("intent_hash", intent.Hash, + "intent_status", intent.Status). + Info("dropping intent") + } } } } @@ -158,12 +136,9 @@ func (i *Client) identifyIntent(entrypointIntent *EntryPointIntents, userOp *use } // Save the identified intent - entrypointIntent.NewIntentUserOp = userOp entrypointIntent.Buffer[opHash] = userOp - entrypointIntent.NewIntent = &intent - entrypointIntent.NewIntent.Hash = opHash + intent.Hash = opHash intent.Status = model.Received - entrypointIntent.Unsolved.EnqueueHead(opHash, &intent) // Set the intent hash to userOp's intent.Hash = opHash @@ -171,18 +146,25 @@ func (i *Client) identifyIntent(entrypointIntent *EntryPointIntents, userOp *use intent.CreatedAt = time.Now().Unix() } if intent.ExpirationAt == 0 { - intent.ExpirationAt = time.Unix(intent.CreatedAt, 0).Add(time.Duration(100 * time.Second)).Unix() + // TODO: set intents expiration configurable + const ttl = time.Duration(100 * time.Second) + intent.ExpirationAt = time.Unix(intent.CreatedAt, 0).Add(ttl).Unix() } + entrypointIntent.Unsolved.EnqueueHead(opHash, &intent) + return true } +// processIntent solves intents from new received Intent userOps func (i *Client) processIntent(entrypoint common.Address, userOp *userop.UserOperation) { + l := i.logger.WithName("processIntent") + if userOp == nil { - i.logger.Error(fmt.Errorf("userOp is nil"), "userOp is nil") + l.Error(fmt.Errorf("userOp is nil"), "userOp is nil") } if !userOp.HasIntent() { - i.logger.WithValues("userop_hash", userOp.GetUserOpHash(entrypoint, i.chainID).String(), + l.WithValues("userop_hash", userOp.GetUserOpHash(entrypoint, i.chainID).String(), "userop_nonce", userOp.Nonce, "userop_sender", userOp.Sender.String(), "userop_call_data", string(userOp.CallData)). @@ -192,14 +174,40 @@ func (i *Client) processIntent(entrypoint common.Address, userOp *userop.UserOpe } if i.entryPointsIntents[entrypoint] == nil { - i.entryPointsIntents[entrypoint] = NewEntryPointIntent(entrypoint, userOp) + ep := NewEntryPointIntent(entrypoint) + i.entryPointsIntents[entrypoint] = ep + scheduledFunc := sendToSolver(i.logger, ep.Unsolved, i.solvedOps, ep, i.solverClient, i.solverURL) - // TODO: Add scheduling logic for unsolved intents + // Start scheduling the sendToSolver function + ep.Unsolved.SetTickerFunc(time.Second*1, scheduledFunc) } - entrypointIntent := i.entryPointsIntents[entrypoint] + entrypointIntents := i.entryPointsIntents[entrypoint] + + i.identifyIntent(entrypointIntents, userOp) +} + +// processIntentUserOps consumes solved Intent userOps +func (i *Client) processIntentUserOps(entrypoint common.Address) { + l := i.logger.WithName("client.processIntentUserOps") + + for userOp := range i.solvedOps { + + println("A solved userOp: ", userOp, " popped") + + go func(entrypoint common.Address, userOp *userop.UserOperation) { + + println("Adding to mempool the solved userOp: ", string(userOp.CallData)) - if i.identifyIntent(entrypointIntent, userOp) { - i.solveIntent(entrypointIntent) + hashOp, err := i.addToMemPool(entrypoint, userOp) + if err != nil { + l.WithValues("userop_hash", hashOp, + "userop_nonce", userOp.Nonce, + "userop_sender", userOp.Sender.String(), + "userop_call_data", string(userOp.CallData), + "entrypoint", entrypoint.String()). + Error(err, "failed to add userOp to mempool") + } + }(entrypoint, userOp) } } From 9ae3ff004a9c445f3eb886e9a467945d50aa859a Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 7 Dec 2023 11:05:48 +0200 Subject: [PATCH 38/87] chore: Clarify flow --- pkg/client/client.go | 16 +++++++++------- pkg/client/solveintents.go | 25 ++++++++++++++----------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/pkg/client/client.go b/pkg/client/client.go index 42a26c5f..dc98d35d 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -149,19 +149,21 @@ func (i *Client) SendUserOperation(op map[string]any, ep string) (string, error) return "", err } - i.processIntent(epAddr, userOp) + opHash := userOp.GetUserOpHash(epAddr, i.chainID).String() - if !userOp.HasIntent() { - return i.addToMemPool(epAddr, userOp) + if userOp.HasIntent() { + // route userOps with intent to the solver + go i.processUserOpIntent(epAddr, userOp, opHash) + + return opHash, nil } - return userOp.GetUserOpHash(epAddr, i.chainID).String(), nil + return i.addToMemPool(epAddr, userOp, opHash) } -func (i *Client) addToMemPool(epAddr common.Address, userOp *userop.UserOperation) (string, error) { +func (i *Client) addToMemPool(epAddr common.Address, userOp *userop.UserOperation, opHash string) (string, error) { l := i.logger.WithName("addToMemPool") - opHash := userOp.GetUserOpHash(epAddr, i.chainID) l = l.WithValues("userop_hash", opHash, "userop_nonce", userOp.Nonce, "userop_sender", userOp.Sender.String(), @@ -189,7 +191,7 @@ func (i *Client) addToMemPool(epAddr common.Address, userOp *userop.UserOperatio l.Info("addToMemPool ok") - return opHash.String(), nil + return opHash, nil } // EstimateUserOperationGas returns estimates for PreVerificationGas, VerificationGasLimit, and CallGasLimit diff --git a/pkg/client/solveintents.go b/pkg/client/solveintents.go index 39079ef4..5181d77f 100644 --- a/pkg/client/solveintents.go +++ b/pkg/client/solveintents.go @@ -156,16 +156,17 @@ func (i *Client) identifyIntent(entrypointIntent *EntryPointIntents, userOp *use return true } -// processIntent solves intents from new received Intent userOps -func (i *Client) processIntent(entrypoint common.Address, userOp *userop.UserOperation) { - l := i.logger.WithName("processIntent") +// processUserOpIntent solves intents from new received Intent userOps +func (i *Client) processUserOpIntent(entrypoint common.Address, userOp *userop.UserOperation, opHash string) { + l := i.logger.WithName("processUserOpIntent") if userOp == nil { - l.Error(fmt.Errorf("userOp is nil"), "userOp is nil") + l.Error(fmt.Errorf("userOp is nil"), "userOp is nil in processUserOpIntent") + return } + if !userOp.HasIntent() { - l.WithValues("userop_hash", userOp.GetUserOpHash(entrypoint, i.chainID).String(), - "userop_nonce", userOp.Nonce, + l.WithValues("userop_hash", opHash, "userop_sender", userOp.Sender.String(), "userop_call_data", string(userOp.CallData)). Info("userOp is not an intent") @@ -173,18 +174,20 @@ func (i *Client) processIntent(entrypoint common.Address, userOp *userop.UserOpe return } + i.identifyIntent(i.getEPIntentsBuffer(entrypoint), userOp) +} + +func (i *Client) getEPIntentsBuffer(entrypoint common.Address) *EntryPointIntents { if i.entryPointsIntents[entrypoint] == nil { ep := NewEntryPointIntent(entrypoint) i.entryPointsIntents[entrypoint] = ep - scheduledFunc := sendToSolver(i.logger, ep.Unsolved, i.solvedOps, ep, i.solverClient, i.solverURL) // Start scheduling the sendToSolver function + scheduledFunc := sendToSolver(i.logger, ep.Unsolved, i.solvedOps, ep, i.solverClient, i.solverURL) ep.Unsolved.SetTickerFunc(time.Second*1, scheduledFunc) } - entrypointIntents := i.entryPointsIntents[entrypoint] - - i.identifyIntent(entrypointIntents, userOp) + return i.entryPointsIntents[entrypoint] } // processIntentUserOps consumes solved Intent userOps @@ -199,7 +202,7 @@ func (i *Client) processIntentUserOps(entrypoint common.Address) { println("Adding to mempool the solved userOp: ", string(userOp.CallData)) - hashOp, err := i.addToMemPool(entrypoint, userOp) + hashOp, err := i.addToMemPool(entrypoint, userOp, userOp.GetUserOpHash(entrypoint, i.chainID).String()) if err != nil { l.WithValues("userop_hash", hashOp, "userop_nonce", userOp.Nonce, From 3869085fd9d0cfba503aa00de11faa196880bc17 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:11:49 +0200 Subject: [PATCH 39/87] chore: Rename for clarity --- pkg/client/solveintents.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/client/solveintents.go b/pkg/client/solveintents.go index 5181d77f..05b450b4 100644 --- a/pkg/client/solveintents.go +++ b/pkg/client/solveintents.go @@ -108,7 +108,7 @@ func sendToSolver(log logr.Logger, unsolvedQ *Queue[*model.Intent], solvedOps ch } } -func (i *Client) identifyIntent(entrypointIntent *EntryPointIntents, userOp *userop.UserOperation) bool { +func (i *Client) bufferUserOpIntent(entrypointIntent *EntryPointIntents, userOp *userop.UserOperation) bool { l := i.logger.WithName("identifyIntents") opHash := userOp.GetUserOpHash(entrypointIntent.EntryPoint, i.chainID).String() if !userOp.HasIntent() { @@ -174,7 +174,7 @@ func (i *Client) processUserOpIntent(entrypoint common.Address, userOp *userop.U return } - i.identifyIntent(i.getEPIntentsBuffer(entrypoint), userOp) + i.bufferUserOpIntent(i.getEPIntentsBuffer(entrypoint), userOp) } func (i *Client) getEPIntentsBuffer(entrypoint common.Address) *EntryPointIntents { From c0f4adbb424a1fefb810a36a8838a9f60c4394d8 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Sat, 9 Dec 2023 12:17:08 +0200 Subject: [PATCH 40/87] feat: Sign --- scripts/senduserop/main.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 14e86180..fb9c8de2 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -32,10 +32,12 @@ func main() { panic(fmt.Errorf("fatal error config file: %w", err)) } - s, err := signer.New(viper.GetString("erc4337_bundler_private_key")) + prvKeyHex := viper.GetString("erc4337_bundler_private_key") + s, err := signer.New(prvKeyHex) if err != nil { panic(fmt.Errorf("fatal signer error: %w", err)) } + fmt.Printf("Private key: %s\n", hexutil.Encode(crypto.FromECDSA(s.PrivateKey))) fmt.Printf("Public key: %s\n", hexutil.Encode(crypto.FromECDSAPub(s.PublicKey))[4:]) fmt.Printf("Address: %s\n", s.Address) @@ -45,13 +47,13 @@ func main() { callData := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` callGasLimit := big.NewInt(15000) // error if below 12100 verificationGasLimit := big.NewInt(58592) - preVerificationGas := big.NewInt(56000) // error if below 54560 + preVerificationGas := big.NewInt(60000) maxFeePerGas := big.NewInt(0xac97bb286) maxPriorityFeePerGas := big.NewInt(0xac97bb264) // paymasterAndData := hex.EncodeToString([]byte{}) + const entrypointAddr = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" // Placeholder for signature - // signature := hex.EncodeToString([]byte{}) userOp := userop.UserOperation{ Sender: sender, @@ -64,10 +66,19 @@ func main() { MaxFeePerGas: maxFeePerGas, MaxPriorityFeePerGas: maxPriorityFeePerGas, PaymasterAndData: []byte{}, - Signature: []byte{}, } - const entrypointAddr = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + privateKey, err := crypto.HexToECDSA(prvKeyHex) + if err != nil { + panic(err) + } + + // Sign the userOp + userOp.Signature, err = crypto.Sign(userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), big.NewInt(80001)).Bytes(), privateKey) + if err != nil { + panic(err) + } + request := JsonRpcRequest{ Jsonrpc: "2.0", Id: 3, From 9e5b64e29e1c737c76046e2da6011bf7d1d2e3d1 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:40:17 +0200 Subject: [PATCH 41/87] chore: Remove previous Solver integration --- pkg/bundler/solveintents.go | 173 ----------------------------- pkg/client/client.go | 29 ----- pkg/client/solveintents.go | 216 ------------------------------------ 3 files changed, 418 deletions(-) delete mode 100644 pkg/bundler/solveintents.go delete mode 100644 pkg/client/solveintents.go diff --git a/pkg/bundler/solveintents.go b/pkg/bundler/solveintents.go deleted file mode 100644 index 00161466..00000000 --- a/pkg/bundler/solveintents.go +++ /dev/null @@ -1,173 +0,0 @@ -package bundler - -import ( - "bytes" - "encoding/json" - "fmt" - "log" - "net/http" - "time" - - "github.com/blndgs/model" - "github.com/ethereum/go-ethereum/common" - - "github.com/stackup-wallet/stackup-bundler/pkg/userop" -) - -type IntentOpsBatch map[string]*userop.UserOperation -type IntentsBatch map[string]*model.Intent -type OrigBatchIdx map[string]int - -type EntryPointIntents struct { - EntryPoint common.Address - Intents IntentsBatch // set of intents in the batch - IntentsOps IntentOpsBatch // subset of userOp intents out of Batch - UserOpsOrigIdx OrigBatchIdx // map of userOp hash to its index in the original batch - OrigBatch []*userop.UserOperation - InvalidIntents uint -} - -func NewEntryPointIntents(entryPoint common.Address, origBatch []*userop.UserOperation) *EntryPointIntents { - return &EntryPointIntents{ - EntryPoint: entryPoint, - Intents: make(IntentsBatch), - IntentsOps: make(IntentOpsBatch), - UserOpsOrigIdx: make(OrigBatchIdx), - OrigBatch: origBatch, - } -} - -func sendToSolver(solverClient *http.Client, solverURL string, senderAddress string, intents []*model.Intent) ([]*model.Intent, error) { - // Create a Body instance and populate it - body := model.Body{ - Intents: intents, - } - - // Marshal the Body instance into JSON - jsonBody, err := json.Marshal(body) - if err != nil { - return nil, err - } - - // Create a new HTTP POST request - req, err := http.NewRequest(http.MethodPost, solverURL, bytes.NewBuffer(jsonBody)) - if err != nil { - // Log error and return - log.Printf("Error creating request: %s", err.Error()) - return nil, err - } - - // Set the content type to application/json - req.Header.Set("Content-Type", "application/json") - - // Send the request - resp, err := solverClient.Do(req) - if err != nil { - log.Printf("Error occurred sending request. Error: %s", err.Error()) - return nil, err - } - defer resp.Body.Close() - - // Decode the returned intents back into the same slice of intents - if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { - log.Printf("Error decoding response: %s", err.Error()) - return nil, err - } - - return body.Intents, nil -} - -func (i *Bundler) solveIntents(intentsBatch *EntryPointIntents) { - if len(intentsBatch.Intents) == 0 { - return - } - - l := i.logger.WithName("solveIntents") - intents := make([]*model.Intent, len(intentsBatch.Intents)) - j := 0 - for _, itt := range intentsBatch.Intents { - itt.Status = model.SentToSolver - intents[j] = itt - j++ - } - - // TODO: Add Backoff logic - var err error - intents, err = sendToSolver(i.solverClient, i.solverURL, intents[0].Sender, intents) - if err != nil { - l.WithValues("number_intents", len(intents)). - Error(err, "failed to send intents to solver") - return - } - - for _, intent := range intents { - switch intent.Status { - case model.Solved: - // Set the solution back to the original userOp - intentsBatch.OrigBatch[intentsBatch.UserOpsOrigIdx[intent.Hash]].CallData = []byte(intent.CallData) - case model.Unsolved: - // will be retried till expired - intentsBatch.Intents[intent.Hash].Status = model.Unsolved - case model.Expired, model.Invalid: - delete(intentsBatch.Intents, intent.Hash) - delete(intentsBatch.IntentsOps, intent.Hash) - l.WithValues("intent_hash", intent.Hash, - "intent_status", intent.Status). - Info("cannot process further") - default: - l.WithValues("intent_hash", intent.Hash, - "intent_status", intent.Status). - Error(fmt.Errorf("unknown intent status"), "unknown returned solver status") - } - } -} - -func (i *Bundler) identifyIntents(entryPoint common.Address, batch []*userop.UserOperation) *EntryPointIntents { - l := i.logger.WithName("identifyIntents").V(1) - intentsBatch := NewEntryPointIntents(entryPoint, batch) - - for idx, userOp := range batch { - opHash := userOp.GetUserOpHash(entryPoint, i.chainID).String() - var intent model.Intent - if userOp.IsIntent() { - userOp.RemoveIntentPrefix() - if err := json.Unmarshal(userOp.CallData, &intent); err != nil { - l.WithValues( - "userop_hash", opHash, - "userop_nonce", userOp.Nonce, - "userop_sender", userOp.Sender.String(), - "is_intent", userOp.IsIntent, - "call_data", userOp.CallData). - Error(err, "failed to unmarshal intent") - intentsBatch.InvalidIntents++ - continue - } - - // Save the identified intent - intentsBatch.IntentsOps[opHash] = userOp - - // Set the intent hash to userOp's - intent.Hash = opHash - if intent.CreatedAt == 0 { - intent.CreatedAt = time.Now().Unix() - } - intentsBatch.Intents[opHash] = &intent - - // Save the index of the userOp in the original batch - intentsBatch.UserOpsOrigIdx[opHash] = idx - } - } - - return intentsBatch -} - -func (i *Bundler) PreProcessIntents(entryPoint common.Address, userOpsBatch []*userop.UserOperation) *EntryPointIntents { - intentsBatch := i.identifyIntents(entryPoint, userOpsBatch) - if len(intentsBatch.Intents) == 0 { - return intentsBatch - } - - i.solveIntents(intentsBatch) - - return intentsBatch -} diff --git a/pkg/client/client.go b/pkg/client/client.go index dc98d35d..5729b591 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -4,8 +4,6 @@ package client import ( "errors" "math/big" - "net/http" - "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -34,10 +32,6 @@ type Client struct { getGasPrices GetGasPricesFunc getGasEstimate GetGasEstimateFunc getUserOpByHash GetUserOpByHashFunc - solverURL string - solverClient *http.Client - entryPointsIntents EntryPointsIntents - solvedOps chan *userop.UserOperation } // New initializes a new ERC-4337 client which can be extended with modules for validating UserOperations @@ -49,8 +43,6 @@ func New( supportedEntryPoints []common.Address, solverURL string, ) *Client { - const userOpsBatchSize = 200 - client := &Client{ mempool: mempool, ov: ov, @@ -62,20 +54,6 @@ func New( getGasPrices: getGasPricesNoop(), getGasEstimate: getGasEstimateNoop(), getUserOpByHash: getUserOpByHashNoop(), - solverURL: solverURL, - // TODO: Make timeout value configurable - solverClient: &http.Client{Timeout: 100 * time.Second}, - entryPointsIntents: make(map[common.Address]*EntryPointIntents), - solvedOps: make(chan *userop.UserOperation, userOpsBatchSize), - } - - for _, ep := range supportedEntryPoints { - if ep == common.HexToAddress("0x00") { - continue - } - - // Start Solved Intent userOps consumer - go client.processIntentUserOps(ep) } return client @@ -151,13 +129,6 @@ func (i *Client) SendUserOperation(op map[string]any, ep string) (string, error) opHash := userOp.GetUserOpHash(epAddr, i.chainID).String() - if userOp.HasIntent() { - // route userOps with intent to the solver - go i.processUserOpIntent(epAddr, userOp, opHash) - - return opHash, nil - } - return i.addToMemPool(epAddr, userOp, opHash) } diff --git a/pkg/client/solveintents.go b/pkg/client/solveintents.go deleted file mode 100644 index 05b450b4..00000000 --- a/pkg/client/solveintents.go +++ /dev/null @@ -1,216 +0,0 @@ -package client - -import ( - "bytes" - "fmt" - "net/http" - "time" - - "github.com/blndgs/model" - "github.com/ethereum/go-ethereum/common" - "github.com/go-logr/logr" - "github.com/goccy/go-json" - - "github.com/stackup-wallet/stackup-bundler/pkg/userop" -) - -type EntryPointsIntents map[common.Address]*EntryPointIntents - -type EntryPointIntents struct { - EntryPoint common.Address - Unsolved *Queue[*model.Intent] - Buffer map[string]*userop.UserOperation // buffer for intent userOps to be sent to Solver - InvalidIntents uint -} - -func NewEntryPointIntent(entryPoint common.Address) *EntryPointIntents { - const unsolvedCap = 5 - return &EntryPointIntents{ - EntryPoint: entryPoint, - Unsolved: NewQueue[*model.Intent](unsolvedCap), - Buffer: make(map[string]*userop.UserOperation), - } -} - -func sendToSolver(log logr.Logger, unsolvedQ *Queue[*model.Intent], solvedOps chan *userop.UserOperation, - epIntents *EntryPointIntents, solverClient *http.Client, solverURL string) func() { - return func() { - l := log.WithName("sendToSolver") - // Get the unsolved intents from the queue - intents := unsolvedQ.ToSlice() - - // If there are no intents, return - if len(intents) == 0 { - return - } - - epIntents.Unsolved.Reset(len(intents)) - - // Rest of the sendToSolver logic - body := model.Body{ - Intents: intents, - } - jsonBody, err := json.Marshal(body) - if err != nil { - l.WithValues("number_intents", len(intents)). - Error(err, "failed to marshal intents") - return - } - - req, err := http.NewRequest(http.MethodPost, solverURL, bytes.NewBuffer(jsonBody)) - if err != nil { - l.WithValues("number_intents", len(intents)). - Error(err, "failed to create request") - return - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := solverClient.Do(req) - if err != nil { - l.WithValues("number_intents", len(intents)). - Error(err, "failed to send request") - return - } - defer resp.Body.Close() - - if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { - l.WithValues("number_intents", len(intents)). - Error(err, "failed to decode response") - return - } - - for _, intent := range intents { - if intent.ExpirationAt < time.Now().Unix() { - // expired, log & drop - l.WithValues("intent_hash", intent.Hash, - "intent_status", intent.Status). - Info("dropping expired intent") - continue - } - switch intent.Status { - case model.Solved: - // Set the solution to be processed by the bundler client - solvedUserOp := epIntents.Buffer[intent.Hash] - solvedUserOp.CallData = []byte(intent.CallData) - solvedOps <- solvedUserOp - delete(epIntents.Buffer, intent.Hash) - case model.Unsolved: - // will be retried till expired - epIntents.Unsolved.EnqueueHead(intent.Hash, intent) - default: - // invalid or expired - l.WithValues("intent_hash", intent.Hash, - "intent_status", intent.Status). - Info("dropping intent") - } - } - } -} - -func (i *Client) bufferUserOpIntent(entrypointIntent *EntryPointIntents, userOp *userop.UserOperation) bool { - l := i.logger.WithName("identifyIntents") - opHash := userOp.GetUserOpHash(entrypointIntent.EntryPoint, i.chainID).String() - if !userOp.HasIntent() { - i.logger.WithValues("userop_hash", opHash, - "userop_nonce", userOp.Nonce, - "userop_sender", userOp.Sender.String(), - "userop_call_data", string(userOp.CallData)). - Info("userOp is not an intent") - - return false - } - - var intent model.Intent - if err := json.Unmarshal(userOp.CallData, &intent); err != nil { - l.WithValues( - "userop_hash", opHash, - "userop_nonce", userOp.Nonce, - "userop_sender", userOp.Sender.String(), - "is_intent", userOp.HasIntent(), - "call_data", userOp.CallData). - Error(err, "failed to unmarshal intent") - entrypointIntent.InvalidIntents++ - - return false - } - - // Save the identified intent - entrypointIntent.Buffer[opHash] = userOp - intent.Hash = opHash - intent.Status = model.Received - - // Set the intent hash to userOp's - intent.Hash = opHash - if intent.CreatedAt == 0 { - intent.CreatedAt = time.Now().Unix() - } - if intent.ExpirationAt == 0 { - // TODO: set intents expiration configurable - const ttl = time.Duration(100 * time.Second) - intent.ExpirationAt = time.Unix(intent.CreatedAt, 0).Add(ttl).Unix() - } - - entrypointIntent.Unsolved.EnqueueHead(opHash, &intent) - - return true -} - -// processUserOpIntent solves intents from new received Intent userOps -func (i *Client) processUserOpIntent(entrypoint common.Address, userOp *userop.UserOperation, opHash string) { - l := i.logger.WithName("processUserOpIntent") - - if userOp == nil { - l.Error(fmt.Errorf("userOp is nil"), "userOp is nil in processUserOpIntent") - return - } - - if !userOp.HasIntent() { - l.WithValues("userop_hash", opHash, - "userop_sender", userOp.Sender.String(), - "userop_call_data", string(userOp.CallData)). - Info("userOp is not an intent") - - return - } - - i.bufferUserOpIntent(i.getEPIntentsBuffer(entrypoint), userOp) -} - -func (i *Client) getEPIntentsBuffer(entrypoint common.Address) *EntryPointIntents { - if i.entryPointsIntents[entrypoint] == nil { - ep := NewEntryPointIntent(entrypoint) - i.entryPointsIntents[entrypoint] = ep - - // Start scheduling the sendToSolver function - scheduledFunc := sendToSolver(i.logger, ep.Unsolved, i.solvedOps, ep, i.solverClient, i.solverURL) - ep.Unsolved.SetTickerFunc(time.Second*1, scheduledFunc) - } - - return i.entryPointsIntents[entrypoint] -} - -// processIntentUserOps consumes solved Intent userOps -func (i *Client) processIntentUserOps(entrypoint common.Address) { - l := i.logger.WithName("client.processIntentUserOps") - - for userOp := range i.solvedOps { - - println("A solved userOp: ", userOp, " popped") - - go func(entrypoint common.Address, userOp *userop.UserOperation) { - - println("Adding to mempool the solved userOp: ", string(userOp.CallData)) - - hashOp, err := i.addToMemPool(entrypoint, userOp, userOp.GetUserOpHash(entrypoint, i.chainID).String()) - if err != nil { - l.WithValues("userop_hash", hashOp, - "userop_nonce", userOp.Nonce, - "userop_sender", userOp.Sender.String(), - "userop_call_data", string(userOp.CallData), - "entrypoint", entrypoint.String()). - Error(err, "failed to add userOp to mempool") - } - }(entrypoint, userOp) - } -} From a00f858d71ec3ce88f50e674122c15750ef6d991 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:41:01 +0200 Subject: [PATCH 42/87] chore: Remove intent detection at userOp object --- pkg/userop/object.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/pkg/userop/object.go b/pkg/userop/object.go index b69173e7..35fdf0b8 100644 --- a/pkg/userop/object.go +++ b/pkg/userop/object.go @@ -4,7 +4,6 @@ package userop import ( "encoding/json" "math/big" - "strings" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -39,8 +38,6 @@ var ( UserOpArr, _ = abi.NewType("tuple[]", "ops", UserOpPrimitives) ) -const IntentCallDataPrefix = "" - // UserOperation represents an EIP-4337 style transaction for a smart contract account. type UserOperation struct { Sender common.Address `json:"sender" mapstructure:"sender" validate:"required"` @@ -90,27 +87,6 @@ func (op *UserOperation) GetMaxGasAvailable() *big.Int { ) } -// IsIntent returns true if the UserOperation is an intent. -func (op *UserOperation) IsIntent() bool { - // TODO remove next line when integration is complete - if !strings.HasPrefix(string(op.CallData), IntentCallDataPrefix) { - op.CallData = []byte("" + `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}`) - } - - if len(op.CallData) < len(IntentCallDataPrefix) { - return false - } - - return string(op.CallData[:len(IntentCallDataPrefix)]) == IntentCallDataPrefix -} - -// RemoveIntentPrefix returns the calldata without the intent prefix. -func (op *UserOperation) RemoveIntentPrefix() { - if op.IsIntent() { - op.CallData = op.CallData[len(IntentCallDataPrefix):] - } -} - // GetMaxPrefund returns the max amount of wei required to pay for gas fees by either the sender or // paymaster. func (op *UserOperation) GetMaxPrefund() *big.Int { From fb0e6665e0dbc846f3e16075b7cc5da7bce92f5f Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:48:16 +0200 Subject: [PATCH 43/87] feat: Add intents solver module --- internal/start/private.go | 4 + internal/start/searcher.go | 6 +- pkg/modules/solution/solveintents.go | 108 +++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 pkg/modules/solution/solveintents.go diff --git a/internal/start/private.go b/internal/start/private.go index 23d89d16..7af322d8 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -30,6 +30,7 @@ import ( "github.com/stackup-wallet/stackup-bundler/pkg/modules/gasprice" "github.com/stackup-wallet/stackup-bundler/pkg/modules/paymaster" "github.com/stackup-wallet/stackup-bundler/pkg/modules/relay" + "github.com/stackup-wallet/stackup-bundler/pkg/modules/solution" "github.com/stackup-wallet/stackup-bundler/pkg/signer" ) @@ -124,6 +125,8 @@ func PrivateMode() { exp := expire.New(conf.MaxOpTTL) + solver := solution.New(conf.SolverUrl) + relayer := relay.New(eoa, eth, chain, beneficiary, logr) paymaster := paymaster.New(db) @@ -161,6 +164,7 @@ func PrivateMode() { batch.MaintainGasLimit(conf.MaxBatchGasLimit), check.CodeHashes(), check.PaymasterDeposit(), + solver.SolveIntents(), relayer.SendUserOperation(), paymaster.IncOpsIncluded(), check.Clean(), diff --git a/internal/start/searcher.go b/internal/start/searcher.go index 773ccb35..eefd91f0 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -31,6 +31,7 @@ import ( "github.com/stackup-wallet/stackup-bundler/pkg/modules/expire" "github.com/stackup-wallet/stackup-bundler/pkg/modules/gasprice" "github.com/stackup-wallet/stackup-bundler/pkg/modules/paymaster" + "github.com/stackup-wallet/stackup-bundler/pkg/modules/solution" "github.com/stackup-wallet/stackup-bundler/pkg/signer" ) @@ -116,7 +117,9 @@ func SearcherMode() { exp := expire.New(conf.MaxOpTTL) - // TODO: Create separate go-routine for tracking transactions sent to the block builder. + // Init Intents Solver + solver := solution.New(conf.SolverUrl) + builder := builder.New(eoa, eth, fb, beneficiary, conf.BlocksInTheFuture) paymaster := paymaster.New(db) @@ -154,6 +157,7 @@ func SearcherMode() { batch.MaintainGasLimit(conf.MaxBatchGasLimit), check.CodeHashes(), check.PaymasterDeposit(), + solver.SolveIntents(), builder.SendUserOperation(), paymaster.IncOpsIncluded(), check.Clean(), diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go new file mode 100644 index 00000000..876f65e0 --- /dev/null +++ b/pkg/modules/solution/solveintents.go @@ -0,0 +1,108 @@ +package solution + +import ( + "bytes" + "math/big" + "net/http" + "time" + "unsafe" + + "github.com/blndgs/model" + "github.com/ethereum/go-ethereum/common" + "github.com/goccy/go-json" + + "github.com/stackup-wallet/stackup-bundler/pkg/modules" + "github.com/stackup-wallet/stackup-bundler/pkg/userop" +) + +type EntryPointsIntents map[common.Address]*EntryPointIntents + +type EntryPointIntents struct { + SolverURL string + SolverClient *http.Client + Buffer map[string]int // buffer for Hash to index of userOp in batch + Ops []*userop.UserOperation +} + +func New(solverURL string) *EntryPointIntents { + const httpClientTimeout = 100 * time.Second + + return &EntryPointIntents{ + SolverURL: solverURL, + SolverClient: &http.Client{Timeout: httpClientTimeout}, + } +} + +// bufferSentUserOp caches the index of the userOp in the batch +func (ei *EntryPointIntents) bufferSentUserOp(bodyOfUserOps model.BodyOfUserOps) { + // Cache the index of the userOp in the batch + for idx, opExt := range bodyOfUserOps.UserOpsExt { + ei.Buffer[opExt.OriginalHashValue] = idx + } +} + +// getExtSlice returns a slice of UserOperationExt with cached Hashes and ProcessingStatus set to Received. +func getExtSlice(entrypoint common.Address, chainID *big.Int, userOps []*model.UserOperation) []model.UserOperationExt { + userOpsExt := make([]model.UserOperationExt, len(userOps)) + for idx, op := range userOps { + userOpsExt[idx].ProcessingStatus = model.Received + + // Cache hash before it changes + userOpsExt[idx].OriginalHashValue = op.GetUserOpHash(entrypoint, chainID).String() + } + + return userOpsExt +} + +// SolveIntents returns a BatchHandlerFunc that will send the batch of UserOperations to the Solver and +// mark the UserOperations that have been expired been solved to be sent on chain. +func (ei *EntryPointIntents) SolveIntents() modules.BatchHandlerFunc { + return func(ctx *modules.BatchHandlerCtx) error { + // cast the received userOp batch to a slice of model.UserOperation + modelUserOps := *(*[]*model.UserOperation)(unsafe.Pointer(&ctx.Batch)) + + // Rest of the sendToSolver logic + body := model.BodyOfUserOps{ + UserOps: modelUserOps, + UserOpsExt: getExtSlice(ctx.EntryPoint, ctx.ChainID, modelUserOps), + } + + // batch Ops hashes have been cached before calling this function + ei.bufferSentUserOp(body) + + jsonBody, err := json.Marshal(body) + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodPost, ei.SolverURL, bytes.NewBuffer(jsonBody)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := ei.SolverClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { + return err + } + + // Mark the userOps that have been expired or invalid to be removed from the batch + for idx, opExt := range body.UserOpsExt { + if opExt.ProcessingStatus == model.Expired || opExt.ProcessingStatus == model.Invalid { + batchIndex := ei.Buffer[body.UserOpsExt[idx].OriginalHashValue] + ctx.MarkOpIndexForRemoval(batchIndex) + + // Remove the userOp from the buffer + delete(ei.Buffer, body.UserOpsExt[idx].OriginalHashValue) + } + } + + return nil + } +} From a2a963606bb033d52d6d07ea258d192034ed9ae7 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 13 Dec 2023 02:33:56 +0200 Subject: [PATCH 44/87] feat: add self-verifying signing functions --- scripts/senduserop/main.go | 115 ++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 7 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index fb9c8de2..bb784721 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -2,7 +2,9 @@ package main import ( "bytes" + "crypto/ecdsa" "fmt" + "log" "math/big" "net/http" @@ -23,6 +25,8 @@ type JsonRpcRequest struct { Params []interface{} `json:"params"` } +const entrypointAddr = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + func main() { viper.SetConfigName(".env") @@ -41,10 +45,16 @@ func main() { fmt.Printf("Public key: %s\n", hexutil.Encode(crypto.FromECDSAPub(s.PublicKey))[4:]) fmt.Printf("Address: %s\n", s.Address) + verifySignedMessage(s.PrivateKey) + sender := common.HexToAddress("0x3068c2408c01bECde4BcCB9f246b56651BE1d12D") nonce := big.NewInt(11) // initCode := hex.EncodeToString([]byte{}) callData := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` + cdHex := hexutil.Encode([]byte(callData)) + + println(callData, cdHex) + callGasLimit := big.NewInt(15000) // error if below 12100 verificationGasLimit := big.NewInt(58592) preVerificationGas := big.NewInt(60000) @@ -52,14 +62,13 @@ func main() { maxPriorityFeePerGas := big.NewInt(0xac97bb264) // paymasterAndData := hex.EncodeToString([]byte{}) - const entrypointAddr = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" // Placeholder for signature userOp := userop.UserOperation{ Sender: sender, Nonce: nonce, InitCode: []byte{}, - CallData: []byte(callData), + CallData: []byte(hexutil.Encode([]byte{})), // []byte(callData), CallGasLimit: callGasLimit, VerificationGasLimit: verificationGasLimit, PreVerificationGas: preVerificationGas, @@ -73,10 +82,14 @@ func main() { panic(err) } - // Sign the userOp - userOp.Signature, err = crypto.Sign(userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), big.NewInt(80001)).Bytes(), privateKey) - if err != nil { - panic(err) + // signature := getVerifiedSignature(&userOp, privateKey) + userOp.Signature = getVerifiedSignature(&userOp, privateKey) + + // Verify the signature + if verifySignature(&userOp, &privateKey.PublicKey) { + println("Signature is valid") + } else { + println("Signature is invalid") } request := JsonRpcRequest{ @@ -104,5 +117,93 @@ func main() { } // Print the response - fmt.Println("Response from server:", result) + println("Response from server:", result) +} + +func getVerifiedSignature(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey) []byte { + userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), big.NewInt(80001)).Bytes() + + prefixedHash := crypto.Keccak256Hash( + []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(userOpHash), userOpHash)), + ) + + signature, err := crypto.Sign(prefixedHash.Bytes(), privateKey) + if err != nil { + panic(err) + } + + // Normalize S value for Ethereum + sValue := big.NewInt(0).SetBytes(signature[32:64]) + secp256k1N := crypto.S256().Params().N + if sValue.Cmp(new(big.Int).Rsh(secp256k1N, 1)) > 0 { + sValue.Sub(secp256k1N, sValue) + copy(signature[32:64], sValue.Bytes()) + } + + return signature +} + +func verifySignature(userOp *userop.UserOperation, publicKey *ecdsa.PublicKey) bool { + userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), big.NewInt(80001)).Bytes() + + prefixedHash := crypto.Keccak256Hash( + []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(userOpHash), userOpHash)), + ) + + signature := userOp.Signature // Already in RSV format + + recoveredPubKey, err := crypto.SigToPub(prefixedHash.Bytes(), signature) + if err != nil { + fmt.Printf("Failed to recover public key: %v\n", err) + return false + } + + recoveredAddress := crypto.PubkeyToAddress(*recoveredPubKey) + expectedAddress := crypto.PubkeyToAddress(*publicKey) + + return recoveredAddress == expectedAddress +} + +func verifySignedMessage(privateKey *ecdsa.PrivateKey /*publicKey *ecdsa.PublicKey, address common.Address*/) { + publicKeyECDSA, ok := privateKey.Public().(*ecdsa.PublicKey) + if !ok { + log.Fatal("error casting public key to ECDSA") + } + address := crypto.PubkeyToAddress(*publicKeyECDSA) + + // Sample message + message := "Hello, Ethereum!" + prefixedHash := crypto.Keccak256Hash([]byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(message), message))) + + // Sign the message + signature, err := crypto.Sign(prefixedHash.Bytes(), privateKey) + if err != nil { + log.Fatal(err) + } + + // Normalize S value + sValue := big.NewInt(0).SetBytes(signature[32:64]) + // Curve order for secp256k1 + secp256k1N := crypto.S256().Params().N + if sValue.Cmp(new(big.Int).Rsh(secp256k1N, 1)) > 0 { + sValue.Sub(secp256k1N, sValue) + copy(signature[32:64], sValue.Bytes()) + } + + // Recover the public key without adjusting V + recoveredPubKey, err := crypto.SigToPub(prefixedHash.Bytes(), signature) + if err != nil { + log.Fatal(err) + } + recoveredAddress := crypto.PubkeyToAddress(*recoveredPubKey) + + fmt.Printf("Original Address: %s\n", address.Hex()) + fmt.Printf("Recovered Address: %s\n", recoveredAddress.Hex()) + + // Check if the recovered address matches the original address + if address.Hex() == recoveredAddress.Hex() { + fmt.Println("Signature valid, recovered address matches the original address") + } else { + fmt.Println("Invalid signature, recovered address does not match") + } } From f4c2e35a1039e7e756ba6718de33d316bb1895b8 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:58:08 +0200 Subject: [PATCH 45/87] fix: Use reference userOp to use custom JSON marshaller Updated values to bring userOp up to date --- scripts/senduserop/main.go | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index bb784721..1c4663c7 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -48,27 +48,27 @@ func main() { verifySignedMessage(s.PrivateKey) sender := common.HexToAddress("0x3068c2408c01bECde4BcCB9f246b56651BE1d12D") - nonce := big.NewInt(11) + nonce := big.NewInt(0x10) // initCode := hex.EncodeToString([]byte{}) callData := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` cdHex := hexutil.Encode([]byte(callData)) println(callData, cdHex) - callGasLimit := big.NewInt(15000) // error if below 12100 - verificationGasLimit := big.NewInt(58592) - preVerificationGas := big.NewInt(60000) - maxFeePerGas := big.NewInt(0xac97bb286) - maxPriorityFeePerGas := big.NewInt(0xac97bb264) + callGasLimit := big.NewInt(0x2f44) // error if below 12100 + verificationGasLimit := big.NewInt(0xe4e0) + preVerificationGas := big.NewInt(0xbb7c) + maxFeePerGas := big.NewInt(0x12183576da) + maxPriorityFeePerGas := big.NewInt(0x12183576ba) // paymasterAndData := hex.EncodeToString([]byte{}) // Placeholder for signature - userOp := userop.UserOperation{ + userOp := &userop.UserOperation{ Sender: sender, Nonce: nonce, InitCode: []byte{}, - CallData: []byte(hexutil.Encode([]byte{})), // []byte(callData), + CallData: []byte{}, // []byte(callData), CallGasLimit: callGasLimit, VerificationGasLimit: verificationGasLimit, PreVerificationGas: preVerificationGas, @@ -83,18 +83,24 @@ func main() { } // signature := getVerifiedSignature(&userOp, privateKey) - userOp.Signature = getVerifiedSignature(&userOp, privateKey) + userOp.Signature = getVerifiedSignature(userOp, privateKey) // Verify the signature - if verifySignature(&userOp, &privateKey.PublicKey) { + if verifySignature(userOp, &privateKey.PublicKey) { println("Signature is valid") } else { println("Signature is invalid") } + jsonStr, err := userOp.MarshalJSON() + if err != nil { + panic(err) + } + println(string(jsonStr)) + request := JsonRpcRequest{ Jsonrpc: "2.0", - Id: 3, + Id: 45, Method: "eth_sendUserOperation", Params: []interface{}{userOp, entrypointAddr}, } @@ -103,6 +109,7 @@ func main() { if err != nil { panic(err) } + println(string(requestBytes)) resp, err := http.Post("http://localhost:4337", "application/json", bytes.NewBuffer(requestBytes)) if err != nil { From 29e72a9c95b314bea6d2dcc68a6289c1bc87c1d9 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:38:10 +0200 Subject: [PATCH 46/87] chore: Remove obsolete changes --- internal/start/private.go | 2 +- internal/start/searcher.go | 2 +- pkg/client/client.go | 32 ++++++++------------------------ 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/internal/start/private.go b/internal/start/private.go index 7af322d8..1948df3b 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -132,7 +132,7 @@ func PrivateMode() { paymaster := paymaster.New(db) // Init Client - c := client.New(mem, ov, chain, conf.SupportedEntryPoints, conf.SolverUrl) + c := client.New(mem, ov, chain, conf.SupportedEntryPoints) c.SetGetUserOpReceiptFunc(client.GetUserOpReceiptWithEthClient(eth)) c.SetGetGasPricesFunc(client.GetGasPricesWithEthClient(eth)) c.SetGetGasEstimateFunc( diff --git a/internal/start/searcher.go b/internal/start/searcher.go index eefd91f0..3b0f1806 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -124,7 +124,7 @@ func SearcherMode() { paymaster := paymaster.New(db) // Init Client - c := client.New(mem, ov, chain, conf.SupportedEntryPoints, conf.SolverUrl) + c := client.New(mem, ov, chain, conf.SupportedEntryPoints) c.SetGetUserOpReceiptFunc(client.GetUserOpReceiptWithEthClient(eth)) c.SetGetGasPricesFunc(client.GetGasPricesWithEthClient(eth)) c.SetGetGasEstimateFunc( diff --git a/pkg/client/client.go b/pkg/client/client.go index 5729b591..7c2929f1 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/go-logr/logr" - "github.com/stackup-wallet/stackup-bundler/internal/logger" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/filter" "github.com/stackup-wallet/stackup-bundler/pkg/gas" @@ -41,9 +40,8 @@ func New( ov *gas.Overhead, chainID *big.Int, supportedEntryPoints []common.Address, - solverURL string, ) *Client { - client := &Client{ + return &Client{ mempool: mempool, ov: ov, chainID: chainID, @@ -55,8 +53,6 @@ func New( getGasEstimate: getGasEstimateNoop(), getUserOpByHash: getUserOpByHashNoop(), } - - return client } func (i *Client) parseEntryPointAddress(ep string) (common.Address, error) { @@ -126,43 +122,31 @@ func (i *Client) SendUserOperation(op map[string]any, ep string) (string, error) l.Error(err, "eth_sendUserOperation error") return "", err } - - opHash := userOp.GetUserOpHash(epAddr, i.chainID).String() - - return i.addToMemPool(epAddr, userOp, opHash) -} - -func (i *Client) addToMemPool(epAddr common.Address, userOp *userop.UserOperation, opHash string) (string, error) { - l := i.logger.WithName("addToMemPool") - - l = l.WithValues("userop_hash", opHash, - "userop_nonce", userOp.Nonce, - "userop_sender", userOp.Sender.String(), - "userop_call_data", string(userOp.CallData)) + hash := userOp.GetUserOpHash(epAddr, i.chainID) + l = l.WithValues("userop_hash", hash) // Fetch any pending UserOperations in the mempool by the same sender penOps, err := i.mempool.GetOps(epAddr, userOp.Sender) if err != nil { - l.Error(err, "addToMemPool error") + l.Error(err, "eth_sendUserOperation error") return "", err } // Run through client module stack. ctx := modules.NewUserOpHandlerContext(userOp, penOps, epAddr, i.chainID) if err := i.userOpHandler(ctx); err != nil { - l.Error(err, "addToMemPool error") + l.Error(err, "eth_sendUserOperation error") return "", err } // Add userOp to mempool. if err := i.mempool.AddOp(epAddr, ctx.UserOp); err != nil { - l.Error(err, "addToMemPool error") + l.Error(err, "eth_sendUserOperation error") return "", err } - l.Info("addToMemPool ok") - - return opHash, nil + l.Info("eth_sendUserOperation ok") + return hash.String(), nil } // EstimateUserOperationGas returns estimates for PreVerificationGas, VerificationGasLimit, and CallGasLimit From 84b49864d6f3f4220b13029d02909d6651014e8a Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:39:01 +0200 Subject: [PATCH 47/87] feat: Retrieve nonce and chainID from env node url --- scripts/senduserop/main.go | 53 ++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 1c4663c7..42f4b892 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "crypto/ecdsa" "fmt" "log" @@ -11,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/goccy/go-json" "github.com/spf13/viper" @@ -36,6 +38,7 @@ func main() { panic(fmt.Errorf("fatal error config file: %w", err)) } + nodeURL := viper.GetString("ERC4337_BUNDLER_ETH_CLIENT_URL") prvKeyHex := viper.GetString("erc4337_bundler_private_key") s, err := signer.New(prvKeyHex) if err != nil { @@ -48,8 +51,12 @@ func main() { verifySignedMessage(s.PrivateKey) sender := common.HexToAddress("0x3068c2408c01bECde4BcCB9f246b56651BE1d12D") - nonce := big.NewInt(0x10) - // initCode := hex.EncodeToString([]byte{}) + + nonce, chainID, err := getNext(nodeURL, s.Address) + if err != nil { + panic(err) + } + callData := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` cdHex := hexutil.Encode([]byte(callData)) @@ -60,9 +67,6 @@ func main() { preVerificationGas := big.NewInt(0xbb7c) maxFeePerGas := big.NewInt(0x12183576da) maxPriorityFeePerGas := big.NewInt(0x12183576ba) - // paymasterAndData := hex.EncodeToString([]byte{}) - - // Placeholder for signature userOp := &userop.UserOperation{ Sender: sender, @@ -83,10 +87,10 @@ func main() { } // signature := getVerifiedSignature(&userOp, privateKey) - userOp.Signature = getVerifiedSignature(userOp, privateKey) + userOp.Signature = getVerifiedSignature(userOp, privateKey, chainID) // Verify the signature - if verifySignature(userOp, &privateKey.PublicKey) { + if verifySignature(userOp, &privateKey.PublicKey, chainID) { println("Signature is valid") } else { println("Signature is invalid") @@ -127,8 +131,8 @@ func main() { println("Response from server:", result) } -func getVerifiedSignature(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey) []byte { - userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), big.NewInt(80001)).Bytes() +func getVerifiedSignature(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey, chainID *big.Int) []byte { + userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), chainID).Bytes() prefixedHash := crypto.Keccak256Hash( []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(userOpHash), userOpHash)), @@ -150,8 +154,8 @@ func getVerifiedSignature(userOp *userop.UserOperation, privateKey *ecdsa.Privat return signature } -func verifySignature(userOp *userop.UserOperation, publicKey *ecdsa.PublicKey) bool { - userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), big.NewInt(80001)).Bytes() +func verifySignature(userOp *userop.UserOperation, publicKey *ecdsa.PublicKey, chainID *big.Int) bool { + userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), chainID).Bytes() prefixedHash := crypto.Keccak256Hash( []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(userOpHash), userOpHash)), @@ -214,3 +218,30 @@ func verifySignedMessage(privateKey *ecdsa.PrivateKey /*publicKey *ecdsa.PublicK fmt.Println("Invalid signature, recovered address does not match") } } + +func getNext(nodeURL string, address common.Address) (nonce *big.Int, chainID *big.Int, err error) { + // Initialize a client instance to interact with the Ethereum network + client, err := ethclient.Dial(nodeURL) + if err != nil { + log.Fatalf("Failed to connect to the Ethereum client: %v", err) + } + defer client.Close() + + // Retrieve the next nonce to be used + nonceInt, err := client.PendingNonceAt(context.Background(), address) + if err != nil { + log.Fatalf("Failed to retrieve the nonce: %v", err) + } + fmt.Printf("Next nonce for address %s: %d\n", address.Hex(), nonce) + + nonce = big.NewInt(int64(nonceInt)) + + // Retrieve the chain ID + chainID, err = client.NetworkID(context.Background()) + if err != nil { + log.Fatalf("Failed to retrieve the chain ID: %v", err) + } + fmt.Println("Chain ID:", chainID) + + return nonce, chainID, nil +} From cf33ce8fb8bd1bb0d671f8a465e7a608b9a53dbf Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 13 Dec 2023 23:28:00 +0200 Subject: [PATCH 48/87] fix: Solver url default, value --- internal/config/values.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/internal/config/values.go b/internal/config/values.go index 3a562505..90e1e4be 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -82,11 +82,8 @@ func variableNotSetOrIsNil(env string) bool { // GetValues returns config for the bundler that has been read in from env vars. See // https://docs.stackup.sh/docs/packages/bundler/configure for details. func GetValues() *Values { - const solverURL = "solver_url" - // Default variables viper.SetDefault("erc4337_bundler_port", 4337) - viper.SetDefault(solverURL, "http://localhost:7322/solve") viper.SetDefault("erc4337_bundler_data_directory", "/tmp/stackup_bundler") viper.SetDefault("erc4337_bundler_supported_entry_points", "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789") viper.SetDefault("erc4337_bundler_max_verification_gas", 3000000) @@ -97,6 +94,7 @@ func GetValues() *Values { viper.SetDefault("erc4337_bundler_otel_insecure_mode", false) viper.SetDefault("erc4337_bundler_debug_mode", false) viper.SetDefault("erc4337_bundler_gin_mode", gin.ReleaseMode) + viper.SetDefault("solver_url", "http://localhost:7322/solvegas") // Read in from .env file if available viper.SetConfigName(".env") @@ -132,7 +130,6 @@ func GetValues() *Values { _ = viper.BindEnv("erc4337_bundler_alt_mempool_ids") _ = viper.BindEnv("erc4337_bundler_debug_mode") _ = viper.BindEnv("erc4337_bundler_gin_mode") - _ = viper.BindEnv(solverURL) // Validate required variables if variableNotSetOrIsNil("erc4337_bundler_eth_client_url") { @@ -170,15 +167,10 @@ func GetValues() *Values { panic("Fatal config error: erc4337_bundler_alt_mempool_ids is set without specifying an IPFS gateway") } - if variableNotSetOrIsNil(solverURL) { - panic("Fatal config error: solver_url not set") - } - // Return Values privateKey := viper.GetString("erc4337_bundler_private_key") ethClientUrl := viper.GetString("erc4337_bundler_eth_client_url") port := viper.GetInt("erc4337_bundler_port") - solverUrl := viper.GetString(solverURL) dataDirectory := viper.GetString("erc4337_bundler_data_directory") supportedEntryPoints := envArrayToAddressSlice(viper.GetString("erc4337_bundler_supported_entry_points")) beneficiary := viper.GetString("erc4337_bundler_beneficiary") @@ -196,11 +188,11 @@ func GetValues() *Values { altMempoolIds := envArrayToStringSlice(viper.GetString("erc4337_bundler_alt_mempool_ids")) debugMode := viper.GetBool("erc4337_bundler_debug_mode") ginMode := viper.GetString("erc4337_bundler_gin_mode") + solverUrl := viper.GetString("solver_url") return &Values{ PrivateKey: privateKey, EthClientUrl: ethClientUrl, Port: port, - SolverUrl: solverUrl, DataDirectory: dataDirectory, SupportedEntryPoints: supportedEntryPoints, Beneficiary: beneficiary, @@ -218,5 +210,6 @@ func GetValues() *Values { AltMempoolIds: altMempoolIds, DebugMode: debugMode, GinMode: ginMode, + SolverUrl: solverUrl, } } From 7bc08325208fc314ba1dd569a67d0d71eb155d37 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 13 Dec 2023 23:28:45 +0200 Subject: [PATCH 49/87] feat: Process Intent UserOps changes --- go.mod | 6 +- go.sum | 4 +- pkg/modules/checks/standalone.go | 16 +++++ pkg/modules/checks/verificationgas.go | 6 ++ pkg/modules/gasprice/filter.go | 8 +++ pkg/modules/relay/relayer.go | 97 +++++++++++++++++++-------- pkg/modules/solution/solveintents.go | 12 ++++ pkg/userop/intent.go | 11 +-- scripts/senduserop/main.go | 2 +- 9 files changed, 123 insertions(+), 39 deletions(-) diff --git a/go.mod b/go.mod index 04fc8d91..4cbe9c8a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.20 require ( - github.com/blndgs/model v0.3.5 + github.com/blndgs/model v0.4.1 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.11.5 @@ -12,10 +12,12 @@ require ( github.com/go-logr/logr v1.2.4 github.com/go-logr/zerologr v1.2.3 github.com/go-playground/validator/v10 v10.12.0 + github.com/goccy/go-json v0.10.2 github.com/google/go-cmp v0.5.9 github.com/joho/godotenv v1.5.1 github.com/metachris/flashbotsrpc v0.5.0 github.com/mitchellh/mapstructure v1.5.0 + github.com/pkg/errors v0.9.1 github.com/puzpuzpuz/xsync/v3 v3.0.1 github.com/rs/zerolog v1.29.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 @@ -57,7 +59,6 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -80,7 +81,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spf13/afero v1.9.3 // indirect diff --git a/go.sum b/go.sum index e10b4a8b..6314e0ef 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/blndgs/model v0.3.5 h1:PVpot3MFtdhu0yuLw65vVDRUr5AorI19nQh4CuwRq3Q= -github.com/blndgs/model v0.3.5/go.mod h1:MfOzkW3ZqL4v5HqUugDj61RU8f85wS0SCq/benI+xYA= +github.com/blndgs/model v0.4.1 h1:FuGcdwe8a94QPVufkL5biOxMzjyzn4xdBroCWqpGcfU= +github.com/blndgs/model v0.4.1/go.mod h1:l7SqtUKcm7OkOBSfBWlSptqPXxNm/OnvizdWwBVtZlA= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= diff --git a/pkg/modules/checks/standalone.go b/pkg/modules/checks/standalone.go index e6765602..1c0a4f3a 100644 --- a/pkg/modules/checks/standalone.go +++ b/pkg/modules/checks/standalone.go @@ -83,6 +83,10 @@ func (s *Standalone) ValidateOpValues() modules.UserOpHandlerFunc { // SimulateOp returns a UserOpHandler that runs through simulation of new UserOps with the EntryPoint. func (s *Standalone) SimulateOp() modules.UserOpHandlerFunc { return func(ctx *modules.UserOpHandlerCtx) error { + if ctx.UserOp.HasIntent() { + // skip simulation for intents + return nil + } gc := getCodeWithEthClient(s.eth) g := new(errgroup.Group) g.Go(func() error { @@ -145,6 +149,12 @@ func (s *Standalone) CodeHashes() modules.BatchHandlerFunc { end := len(ctx.Batch) - 1 for i := end; i >= 0; i-- { op := ctx.Batch[i] + + if op.HasIntent() { + // skip codehash check for intents + continue + } + chs, err := getSavedCodeHashes(s.db, op.GetUserOpHash(ctx.EntryPoint, ctx.ChainID)) if err != nil { return err @@ -173,6 +183,12 @@ func (s *Standalone) PaymasterDeposit() modules.BatchHandlerFunc { deps := make(map[common.Address]*big.Int) for i, op := range ctx.Batch { + + if op.HasIntent() { + // paymaster deposit is not used for intents + continue + } + pm := op.GetPaymaster() if pm == common.HexToAddress("0x") { continue diff --git a/pkg/modules/checks/verificationgas.go b/pkg/modules/checks/verificationgas.go index 6198ba34..91f9d442 100644 --- a/pkg/modules/checks/verificationgas.go +++ b/pkg/modules/checks/verificationgas.go @@ -19,6 +19,12 @@ func ValidateVerificationGas(op *userop.UserOperation, ov *gas.Overhead, maxVeri ) } + if op.HasIntent() { + // If the UserOperation has intent, we can't calculate the preVerificationGas until we know the + // calldata size. We can't know the calldata size until we know the intent. + return nil + } + pvg, err := ov.CalcPreVerificationGas(op) if err != nil { return err diff --git a/pkg/modules/gasprice/filter.go b/pkg/modules/gasprice/filter.go index 056313c3..2671e08e 100644 --- a/pkg/modules/gasprice/filter.go +++ b/pkg/modules/gasprice/filter.go @@ -4,6 +4,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/stackup-wallet/stackup-bundler/pkg/modules" "github.com/stackup-wallet/stackup-bundler/pkg/userop" ) @@ -14,6 +15,13 @@ func FilterUnderpriced() modules.BatchHandlerFunc { return func(ctx *modules.BatchHandlerCtx) error { b := []*userop.UserOperation{} for _, op := range ctx.Batch { + + if op.HasIntent() { + // Include all intents before solution regardless of gas price + b = append(b, op) + continue + } + if ctx.BaseFee != nil && ctx.BaseFee.Cmp(common.Big0) != 0 && ctx.Tip != nil { gp := big.NewInt(0).Add(ctx.BaseFee, ctx.Tip) if op.GetDynamicGasPrice(ctx.BaseFee).Cmp(gp) >= 0 { diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index bb7e6031..696b3149 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -9,9 +9,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/go-logr/logr" + "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/transaction" "github.com/stackup-wallet/stackup-bundler/pkg/modules" "github.com/stackup-wallet/stackup-bundler/pkg/signer" + "github.com/stackup-wallet/stackup-bundler/pkg/userop" ) // Relayer provides a module that can relay batches with a regular EOA. Relaying batches to the EntryPoint @@ -63,39 +65,78 @@ func (r *Relayer) SetWaitTimeout(timeout time.Duration) { // transaction. func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { return func(ctx *modules.BatchHandlerCtx) error { - opts := transaction.Opts{ - EOA: r.eoa, - Eth: r.eth, - ChainID: ctx.ChainID, - EntryPoint: ctx.EntryPoint, - Batch: ctx.Batch, - Beneficiary: r.beneficiary, - BaseFee: ctx.BaseFee, - Tip: ctx.Tip, - GasPrice: ctx.GasPrice, - GasLimit: 0, - WaitTimeout: r.waitTimeout, + // Filter out UserOperations on HasIntent() result + nonIntentsBatch := make([]*userop.UserOperation, 0, len(ctx.Batch)) + intentsBatch := make([]*userop.UserOperation, 0, len(ctx.Batch)) + for _, userOp := range ctx.Batch { + if userOp.HasIntent() { + intentsBatch = append(intentsBatch, userOp) + } else { + nonIntentsBatch = append(nonIntentsBatch, userOp) + } } - // Estimate gas for handleOps() and drop all userOps that cause unexpected reverts. - estRev := []string{} - for len(ctx.Batch) > 0 { - est, revert, err := transaction.EstimateHandleOpsGas(&opts) - if err != nil { - return err - } else if revert != nil { - ctx.MarkOpIndexForRemoval(revert.OpIndex) - estRev = append(estRev, revert.Reason) - } else { - opts.GasLimit = est - break + // Only proceed if there are conventional UserOperations to process + if len(nonIntentsBatch) > 0 { + opts := transaction.Opts{ + EOA: r.eoa, + Eth: r.eth, + ChainID: ctx.ChainID, + EntryPoint: ctx.EntryPoint, + Batch: nonIntentsBatch, + Beneficiary: r.beneficiary, + BaseFee: ctx.BaseFee, + Tip: ctx.Tip, + GasPrice: ctx.GasPrice, + GasLimit: 0, + WaitTimeout: r.waitTimeout, + } + // Estimate gas for handleOps() and drop all userOps that cause unexpected reverts. + estRev := []string{} + for len(nonIntentsBatch) > 0 { + est, revert, err := transaction.EstimateHandleOpsGas(&opts) + + if err != nil { + return err + } else if revert != nil { + ctx.MarkOpIndexForRemoval(revert.OpIndex) + estRev = append(estRev, revert.Reason) + } else { + opts.GasLimit = est + break + } + } + ctx.Data["relayer_est_revert_reasons"] = estRev + + // Call handleOps() with gas estimate. Any userOps that cause a revert at this stage will be + // caught and dropped in the next iteration. + if len(nonIntentsBatch) > 0 { + if txn, err := transaction.HandleOps(&opts); err != nil { + return err + } else { + ctx.Data["txn_hash"] = txn.Hash().String() + } } + + return nil } - ctx.Data["relayer_est_revert_reasons"] = estRev + // end of sending conventional userOps + + if len(intentsBatch) > 0 { + opts := transaction.Opts{ + EOA: r.eoa, + Eth: r.eth, + ChainID: ctx.ChainID, + EntryPoint: ctx.EntryPoint, + Batch: intentsBatch, + Beneficiary: r.beneficiary, + BaseFee: ctx.BaseFee, + Tip: ctx.Tip, + GasPrice: ctx.GasPrice, + GasLimit: 0, + WaitTimeout: r.waitTimeout, + } - // Call handleOps() with gas estimate. Any userOps that cause a revert at this stage will be - // caught and dropped in the next iteration. - if len(ctx.Batch) > 0 { if txn, err := transaction.HandleOps(&opts); err != nil { return err } else { diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index 876f65e0..5cf8d950 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -30,6 +30,7 @@ func New(solverURL string) *EntryPointIntents { return &EntryPointIntents{ SolverURL: solverURL, SolverClient: &http.Client{Timeout: httpClientTimeout}, + Buffer: make(map[string]int), } } @@ -101,6 +102,17 @@ func (ei *EntryPointIntents) SolveIntents() modules.BatchHandlerFunc { // Remove the userOp from the buffer delete(ei.Buffer, body.UserOpsExt[idx].OriginalHashValue) } + switch opExt.ProcessingStatus { + case model.Expired, model.Invalid: + // dropping further processing of the userOp + batchIndex := ei.Buffer[body.UserOpsExt[idx].OriginalHashValue] + ctx.MarkOpIndexForRemoval(batchIndex) + delete(ei.Buffer, body.UserOpsExt[idx].OriginalHashValue) + case model.Solved: + // Remove the Solved userOp from the Solver buffer + // Remain in the mempool until the userOp is sent on chain + delete(ei.Buffer, body.UserOpsExt[idx].OriginalHashValue) + } } return nil diff --git a/pkg/userop/intent.go b/pkg/userop/intent.go index a2825493..53d46313 100644 --- a/pkg/userop/intent.go +++ b/pkg/userop/intent.go @@ -1,10 +1,11 @@ package userop -import "github.com/goccy/go-json" +import ( + "github.com/blndgs/model" +) -// HasIntent checks if the userOp's `Calldata` is an intent userOp by checking -// whether it contains valid JSON. func (op *UserOperation) HasIntent() bool { - var js json.RawMessage - return json.Unmarshal(op.CallData, &js) == nil + modelUserOp := model.UserOperation(*op) + + return modelUserOp.HasIntent() } diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 42f4b892..90c8cb43 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -72,7 +72,7 @@ func main() { Sender: sender, Nonce: nonce, InitCode: []byte{}, - CallData: []byte{}, // []byte(callData), + CallData: []byte(callData), CallGasLimit: callGasLimit, VerificationGasLimit: verificationGasLimit, PreVerificationGas: preVerificationGas, From d83b63ec9c1cac7f75f058cb0e6ad2a2729ad86f Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 13 Dec 2023 23:38:13 +0200 Subject: [PATCH 50/87] fix: comment --- pkg/modules/checks/verificationgas.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/modules/checks/verificationgas.go b/pkg/modules/checks/verificationgas.go index 91f9d442..b396c3d2 100644 --- a/pkg/modules/checks/verificationgas.go +++ b/pkg/modules/checks/verificationgas.go @@ -21,7 +21,7 @@ func ValidateVerificationGas(op *userop.UserOperation, ov *gas.Overhead, maxVeri if op.HasIntent() { // If the UserOperation has intent, we can't calculate the preVerificationGas until we know the - // calldata size. We can't know the calldata size until we know the intent. + // calldata size. We can't know the calldata size until we know the intent solution. return nil } From 4e9b965c0bede0e42b4db86f55dc94f69d7b3fc9 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Dec 2023 01:49:35 +0200 Subject: [PATCH 51/87] feat: Refactor and add zero gas userOps --- scripts/senduserop/main.go | 259 ++++++++++++++++++++----------------- 1 file changed, 140 insertions(+), 119 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 90c8cb43..68d52a25 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -5,10 +5,11 @@ import ( "context" "crypto/ecdsa" "fmt" - "log" "math/big" "net/http" + "os" + "github.com/blndgs/model" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -27,94 +28,44 @@ type JsonRpcRequest struct { Params []interface{} `json:"params"` } -const entrypointAddr = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" +const entrypointAddrV060 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" func main() { - - viper.SetConfigName(".env") - viper.SetConfigType("env") - viper.AddConfigPath(".") - if err := viper.ReadInConfig(); err != nil { - panic(fmt.Errorf("fatal error config file: %w", err)) - } - - nodeURL := viper.GetString("ERC4337_BUNDLER_ETH_CLIENT_URL") - prvKeyHex := viper.GetString("erc4337_bundler_private_key") - s, err := signer.New(prvKeyHex) - if err != nil { - panic(fmt.Errorf("fatal signer error: %w", err)) - } - fmt.Printf("Private key: %s\n", hexutil.Encode(crypto.FromECDSA(s.PrivateKey))) - fmt.Printf("Public key: %s\n", hexutil.Encode(crypto.FromECDSAPub(s.PublicKey))[4:]) - fmt.Printf("Address: %s\n", s.Address) - - verifySignedMessage(s.PrivateKey) + nodeURL, eoaSigner := readConf() sender := common.HexToAddress("0x3068c2408c01bECde4BcCB9f246b56651BE1d12D") - nonce, chainID, err := getNext(nodeURL, s.Address) + nonce, chainID, err := getNodeIDs(nodeURL, eoaSigner.Address) if err != nil { panic(err) } - callData := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` - cdHex := hexutil.Encode([]byte(callData)) + zeroGas := len(os.Args) > 1 && (os.Args[1] == "zero" || os.Args[1] == "0") + unsignedUserOp := getMockUserOp(sender, nonce, zeroGas) - println(callData, cdHex) + userOp := getVerifiedSignedUserOp(unsignedUserOp, eoaSigner.PrivateKey, eoaSigner.PublicKey, chainID) - callGasLimit := big.NewInt(0x2f44) // error if below 12100 - verificationGasLimit := big.NewInt(0xe4e0) - preVerificationGas := big.NewInt(0xbb7c) - maxFeePerGas := big.NewInt(0x12183576da) - maxPriorityFeePerGas := big.NewInt(0x12183576ba) - - userOp := &userop.UserOperation{ - Sender: sender, - Nonce: nonce, - InitCode: []byte{}, - CallData: []byte(callData), - CallGasLimit: callGasLimit, - VerificationGasLimit: verificationGasLimit, - PreVerificationGas: preVerificationGas, - MaxFeePerGas: maxFeePerGas, - MaxPriorityFeePerGas: maxPriorityFeePerGas, - PaymasterAndData: []byte{}, - } - - privateKey, err := crypto.HexToECDSA(prvKeyHex) - if err != nil { - panic(err) - } - - // signature := getVerifiedSignature(&userOp, privateKey) - userOp.Signature = getVerifiedSignature(userOp, privateKey, chainID) - - // Verify the signature - if verifySignature(userOp, &privateKey.PublicKey, chainID) { - println("Signature is valid") - } else { - println("Signature is invalid") - } + sendUserOp(userOp) +} - jsonStr, err := userOp.MarshalJSON() - if err != nil { - panic(err) - } - println(string(jsonStr)) +// sendUserOp makes a UserOperation RPC request to the bundler. +func sendUserOp(userOp *userop.UserOperation) { + println("userOp ------------> bundler") + op := model.UserOperation(*userOp) + println(op.String()) + println() request := JsonRpcRequest{ Jsonrpc: "2.0", Id: 45, Method: "eth_sendUserOperation", - Params: []interface{}{userOp, entrypointAddr}, + Params: []interface{}{userOp, entrypointAddrV060}, } requestBytes, err := json.Marshal(request) if err != nil { panic(err) } - println(string(requestBytes)) - resp, err := http.Post("http://localhost:4337", "application/json", bytes.NewBuffer(requestBytes)) if err != nil { panic(err) @@ -131,8 +82,76 @@ func main() { println("Response from server:", result) } -func getVerifiedSignature(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey, chainID *big.Int) []byte { - userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), chainID).Bytes() +func getMockUserOp(sender common.Address, nonce *big.Int, zeroGas bool) *userop.UserOperation { + intentJSON := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` + + // Conditional gas values based on zeroGas flag + var callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas, maxPriorityFeePerGas *big.Int + if zeroGas { + callGasLimit = big.NewInt(0) + verificationGasLimit = big.NewInt(0) + preVerificationGas = big.NewInt(0) + maxFeePerGas = big.NewInt(0) + maxPriorityFeePerGas = big.NewInt(0) + } else { + callGasLimit = big.NewInt(0x2f44) // error if below 12100 + verificationGasLimit = big.NewInt(0xe4e0) + preVerificationGas = big.NewInt(0xbb7c) + maxFeePerGas = big.NewInt(0x12183576da) + maxPriorityFeePerGas = big.NewInt(0x12183576ba) + } + + return &userop.UserOperation{ + Sender: sender, + Nonce: nonce, + InitCode: []byte{}, + CallData: []byte(intentJSON), + CallGasLimit: callGasLimit, + VerificationGasLimit: verificationGasLimit, + PreVerificationGas: preVerificationGas, + MaxFeePerGas: maxFeePerGas, + MaxPriorityFeePerGas: maxPriorityFeePerGas, + PaymasterAndData: []byte{}, + } +} + +func readConf() (string, *signer.EOA) { + viper.SetConfigName(".env") + viper.SetConfigType("env") + viper.AddConfigPath(".") + if err := viper.ReadInConfig(); err != nil { + panic(fmt.Errorf("fatal error config file: %w", err)) + } + + nodeURL := viper.GetString("ERC4337_BUNDLER_ETH_CLIENT_URL") + prvKeyHex := viper.GetString("erc4337_bundler_private_key") + s, err := signer.New(prvKeyHex) + if err != nil { + panic(fmt.Errorf("fatal signer error: %w", err)) + } + + fmt.Printf("Private key: %s\n", hexutil.Encode(crypto.FromECDSA(s.PrivateKey))) + fmt.Printf("Public key: %s\n", hexutil.Encode(crypto.FromECDSAPub(s.PublicKey))[4:]) + fmt.Printf("Address: %s\n", s.Address) + return nodeURL, s +} + +// getVerifiedSignedUserOp returns a signed UserOperation with a signature that has been verified by the private key. +func getVerifiedSignedUserOp(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, chainID *big.Int) *userop.UserOperation { + userOp.Signature = getSignature(userOp, privateKey, chainID) + + // Verify the signature + if verifySignature(userOp, publicKey, chainID) { + println("Signature verified") + } else { + panic("Signature is invalid") + } + + return userOp +} + +func getSignature(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey, chainID *big.Int) []byte { + userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddrV060), chainID).Bytes() prefixedHash := crypto.Keccak256Hash( []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(userOpHash), userOpHash)), @@ -155,7 +174,7 @@ func getVerifiedSignature(userOp *userop.UserOperation, privateKey *ecdsa.Privat } func verifySignature(userOp *userop.UserOperation, publicKey *ecdsa.PublicKey, chainID *big.Int) bool { - userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddr), chainID).Bytes() + userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddrV060), chainID).Bytes() prefixedHash := crypto.Keccak256Hash( []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(userOpHash), userOpHash)), @@ -175,73 +194,75 @@ func verifySignature(userOp *userop.UserOperation, publicKey *ecdsa.PublicKey, c return recoveredAddress == expectedAddress } -func verifySignedMessage(privateKey *ecdsa.PrivateKey /*publicKey *ecdsa.PublicKey, address common.Address*/) { - publicKeyECDSA, ok := privateKey.Public().(*ecdsa.PublicKey) - if !ok { - log.Fatal("error casting public key to ECDSA") - } - address := crypto.PubkeyToAddress(*publicKeyECDSA) - - // Sample message - message := "Hello, Ethereum!" - prefixedHash := crypto.Keccak256Hash([]byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(message), message))) - - // Sign the message - signature, err := crypto.Sign(prefixedHash.Bytes(), privateKey) - if err != nil { - log.Fatal(err) - } - - // Normalize S value - sValue := big.NewInt(0).SetBytes(signature[32:64]) - // Curve order for secp256k1 - secp256k1N := crypto.S256().Params().N - if sValue.Cmp(new(big.Int).Rsh(secp256k1N, 1)) > 0 { - sValue.Sub(secp256k1N, sValue) - copy(signature[32:64], sValue.Bytes()) - } - - // Recover the public key without adjusting V - recoveredPubKey, err := crypto.SigToPub(prefixedHash.Bytes(), signature) - if err != nil { - log.Fatal(err) - } - recoveredAddress := crypto.PubkeyToAddress(*recoveredPubKey) - - fmt.Printf("Original Address: %s\n", address.Hex()) - fmt.Printf("Recovered Address: %s\n", recoveredAddress.Hex()) - - // Check if the recovered address matches the original address - if address.Hex() == recoveredAddress.Hex() { - fmt.Println("Signature valid, recovered address matches the original address") - } else { - fmt.Println("Invalid signature, recovered address does not match") - } -} - -func getNext(nodeURL string, address common.Address) (nonce *big.Int, chainID *big.Int, err error) { +func getNodeIDs(nodeURL string, address common.Address) (nonce *big.Int, chainID *big.Int, err error) { // Initialize a client instance to interact with the Ethereum network client, err := ethclient.Dial(nodeURL) if err != nil { - log.Fatalf("Failed to connect to the Ethereum client: %v", err) + panic(fmt.Errorf("failed to connect to the Ethereum client: %w", err)) } defer client.Close() // Retrieve the next nonce to be used nonceInt, err := client.PendingNonceAt(context.Background(), address) if err != nil { - log.Fatalf("Failed to retrieve the nonce: %v", err) + panic(fmt.Errorf("failed to retrieve the nonce: %w", err)) } - fmt.Printf("Next nonce for address %s: %d\n", address.Hex(), nonce) + fmt.Printf("Next nonce for address %s: %d\n", address.Hex(), nonceInt) nonce = big.NewInt(int64(nonceInt)) // Retrieve the chain ID chainID, err = client.NetworkID(context.Background()) if err != nil { - log.Fatalf("Failed to retrieve the chain ID: %v", err) + panic(fmt.Errorf("failed to retrieve the chain ID: %w", err)) } - fmt.Println("Chain ID:", chainID) + println("Chain ID:", chainID.String()) return nonce, chainID, nil } + +// Uncomment when testing signature verifications +// testVerifyingSignature verifies that the private key generates a signature that can be verified by the public key. +// func testVerifyingSignature(privateKey *ecdsa.PrivateKey) { +// publicKeyECDSA, ok := privateKey.Public().(*ecdsa.PublicKey) +// if !ok { +// panic("error casting public key to ECDSA") +// } +// address := crypto.PubkeyToAddress(*publicKeyECDSA) +// +// // Sample message +// message := "Hello, Ethereum!" +// prefixedHash := crypto.Keccak256Hash([]byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(message), message))) +// +// // Sign the message +// signature, err := crypto.Sign(prefixedHash.Bytes(), privateKey) +// if err != nil { +// panic(err) +// } +// +// // Normalize S value +// sValue := big.NewInt(0).SetBytes(signature[32:64]) +// // Curve order for secp256k1 +// secp256k1N := crypto.S256().Params().N +// if sValue.Cmp(new(big.Int).Rsh(secp256k1N, 1)) > 0 { +// sValue.Sub(secp256k1N, sValue) +// copy(signature[32:64], sValue.Bytes()) +// } +// +// // Recover the public key without adjusting V +// recoveredPubKey, err := crypto.SigToPub(prefixedHash.Bytes(), signature) +// if err != nil { +// panic(err) +// } +// recoveredAddress := crypto.PubkeyToAddress(*recoveredPubKey) +// +// fmt.Printf("Original Address: %s\n", address.Hex()) +// fmt.Printf("Recovered Address: %s\n", recoveredAddress.Hex()) +// +// // Check if the recovered address matches the original address +// if address.Hex() == recoveredAddress.Hex() { +// println("Signature valid, recovered address matches the original address") +// } else { +// panic("Invalid signature, recovered address does not match") +// } +// } From c65e2da973345e951a9906edc3de0c22f897a560 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Dec 2023 01:52:39 +0200 Subject: [PATCH 52/87] feat: Support 0 gas userOps, integrate Solver solution --- go.mod | 2 +- go.sum | 4 +- internal/config/values.go | 2 +- pkg/modules/checks/standalone.go | 12 ++- pkg/modules/relay/relayer.go | 81 +++++++------- pkg/modules/solution/solveintents.go | 152 +++++++++++++++++---------- 6 files changed, 153 insertions(+), 100 deletions(-) diff --git a/go.mod b/go.mod index 4cbe9c8a..e05e21e6 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.20 require ( - github.com/blndgs/model v0.4.1 + github.com/blndgs/model v0.7.0 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.11.5 diff --git a/go.sum b/go.sum index 6314e0ef..92a736f2 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/blndgs/model v0.4.1 h1:FuGcdwe8a94QPVufkL5biOxMzjyzn4xdBroCWqpGcfU= -github.com/blndgs/model v0.4.1/go.mod h1:l7SqtUKcm7OkOBSfBWlSptqPXxNm/OnvizdWwBVtZlA= +github.com/blndgs/model v0.7.0 h1:t2C1AbNSBIsTmgcdOtTL2RX6K71vkZBoccR78KFrWJc= +github.com/blndgs/model v0.7.0/go.mod h1:l7SqtUKcm7OkOBSfBWlSptqPXxNm/OnvizdWwBVtZlA= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= diff --git a/internal/config/values.go b/internal/config/values.go index 90e1e4be..e0e1e9ce 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -94,7 +94,7 @@ func GetValues() *Values { viper.SetDefault("erc4337_bundler_otel_insecure_mode", false) viper.SetDefault("erc4337_bundler_debug_mode", false) viper.SetDefault("erc4337_bundler_gin_mode", gin.ReleaseMode) - viper.SetDefault("solver_url", "http://localhost:7322/solvegas") + viper.SetDefault("solver_url", "http://localhost:7322/solve") // Read in from .env file if available viper.SetConfigName(".env") diff --git a/pkg/modules/checks/standalone.go b/pkg/modules/checks/standalone.go index 1c0a4f3a..ab6a08ef 100644 --- a/pkg/modules/checks/standalone.go +++ b/pkg/modules/checks/standalone.go @@ -68,8 +68,16 @@ func (s *Standalone) ValidateOpValues() modules.UserOpHandlerFunc { g.Go(func() error { return ValidateInitCode(ctx.UserOp, gs) }) g.Go(func() error { return ValidateVerificationGas(ctx.UserOp, s.ov, s.maxVerificationGas) }) g.Go(func() error { return ValidatePaymasterAndData(ctx.UserOp, gc, gs) }) - g.Go(func() error { return ValidateCallGasLimit(ctx.UserOp, s.ov) }) - g.Go(func() error { return ValidateFeePerGas(ctx.UserOp, gbf) }) + + if !ctx.UserOp.HasIntent() { + // skip gas limit validation for intents + g.Go(func() error { return ValidateCallGasLimit(ctx.UserOp, s.ov) }) + } + + if !ctx.UserOp.HasIntent() { + g.Go(func() error { return ValidateFeePerGas(ctx.UserOp, gbf) }) + } + g.Go(func() error { return ValidatePendingOps(ctx.UserOp, penOps, s.maxOpsForUnstakedSender, gs) }) g.Go(func() error { return ValidateGasAvailable(ctx.UserOp, s.maxBatchGasLimit) }) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index 696b3149..2dbc3ee9 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -6,6 +6,7 @@ import ( "math/big" "time" + "github.com/blndgs/model" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/go-logr/logr" @@ -78,19 +79,8 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { // Only proceed if there are conventional UserOperations to process if len(nonIntentsBatch) > 0 { - opts := transaction.Opts{ - EOA: r.eoa, - Eth: r.eth, - ChainID: ctx.ChainID, - EntryPoint: ctx.EntryPoint, - Batch: nonIntentsBatch, - Beneficiary: r.beneficiary, - BaseFee: ctx.BaseFee, - Tip: ctx.Tip, - GasPrice: ctx.GasPrice, - GasLimit: 0, - WaitTimeout: r.waitTimeout, - } + opts := r.getCallOptions(ctx, nonIntentsBatch) + // Estimate gas for handleOps() and drop all userOps that cause unexpected reverts. estRev := []string{} for len(nonIntentsBatch) > 0 { @@ -110,40 +100,57 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { // Call handleOps() with gas estimate. Any userOps that cause a revert at this stage will be // caught and dropped in the next iteration. - if len(nonIntentsBatch) > 0 { - if txn, err := transaction.HandleOps(&opts); err != nil { - return err - } else { - ctx.Data["txn_hash"] = txn.Hash().String() - } + if err := handleOps(ctx, opts); err != nil { + return err } return nil - } - // end of sending conventional userOps + } // end of sending conventional userOps if len(intentsBatch) > 0 { - opts := transaction.Opts{ - EOA: r.eoa, - Eth: r.eth, - ChainID: ctx.ChainID, - EntryPoint: ctx.EntryPoint, - Batch: intentsBatch, - Beneficiary: r.beneficiary, - BaseFee: ctx.BaseFee, - Tip: ctx.Tip, - GasPrice: ctx.GasPrice, - GasLimit: 0, - WaitTimeout: r.waitTimeout, + opts := r.getCallOptions(ctx, intentsBatch) + println() + for _, op := range intentsBatch { + // cast to print it + operation := model.UserOperation(*op) + println(operation.String()) } + println() + println("--> handleOps") - if txn, err := transaction.HandleOps(&opts); err != nil { - return err - } else { - ctx.Data["txn_hash"] = txn.Hash().String() + if err := handleOps(ctx, opts); err != nil { + // swallow error + println(err.Error()) } } return nil } } + +func handleOps(ctx *modules.BatchHandlerCtx, opts transaction.Opts) error { + if txn, err := transaction.HandleOps(&opts); err != nil { + return err + } else { + ctx.Data["txn_hash"] = txn.Hash().String() + } + + return nil +} + +func (r *Relayer) getCallOptions(ctx *modules.BatchHandlerCtx, intentsBatch []*userop.UserOperation) transaction.Opts { + opts := transaction.Opts{ + EOA: r.eoa, + Eth: r.eth, + ChainID: ctx.ChainID, + EntryPoint: ctx.EntryPoint, + Batch: intentsBatch, + Beneficiary: r.beneficiary, + BaseFee: ctx.BaseFee, + Tip: ctx.Tip, + GasPrice: ctx.GasPrice, + GasLimit: 0, + WaitTimeout: r.waitTimeout, + } + return opts +} diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index 5cf8d950..1dc7b8b0 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -1,3 +1,17 @@ +// Package solution sends the received bundler batch of Intent UserOperations +// to the Solver to solve the Intent and fill-in the EVM instructions. +// +// This implementation makes 1 attempt for each Intent userOp to be solved. +// +// Solved userOps update the received bundle +// All other returned statuses result in dropping those userOps +// from the batch. +// Received are treated as expired because they may have been compressed to +// Solved Intents. +// +// The Solver may return a subset and in different sequence the UserOperations +// and a matching occurs by the hash value of each UserOperation to the bundle +// UserOperation. package solution import ( @@ -10,111 +24,135 @@ import ( "github.com/blndgs/model" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" + "github.com/pkg/errors" "github.com/stackup-wallet/stackup-bundler/pkg/modules" "github.com/stackup-wallet/stackup-bundler/pkg/userop" ) -type EntryPointsIntents map[common.Address]*EntryPointIntents +// userOpHashID is the hash value of the UserOperation +type opHashID string + +// batchOpIndex is the index of the UserOperation in the Bundler batch +type batchOpIndex int + +// batchIntentIndices buffers the mapping of the UserOperation hash value -> the index of the UserOperation in the batch +type batchIntentIndices map[opHashID]batchOpIndex type EntryPointIntents struct { SolverURL string SolverClient *http.Client - Buffer map[string]int // buffer for Hash to index of userOp in batch - Ops []*userop.UserOperation } +// Verify structural congruence +var _ = model.UserOperation(userop.UserOperation{}) + func New(solverURL string) *EntryPointIntents { const httpClientTimeout = 100 * time.Second return &EntryPointIntents{ SolverURL: solverURL, SolverClient: &http.Client{Timeout: httpClientTimeout}, - Buffer: make(map[string]int), } } -// bufferSentUserOp caches the index of the userOp in the batch -func (ei *EntryPointIntents) bufferSentUserOp(bodyOfUserOps model.BodyOfUserOps) { - // Cache the index of the userOp in the batch - for idx, opExt := range bodyOfUserOps.UserOpsExt { - ei.Buffer[opExt.OriginalHashValue] = idx - } -} - -// getExtSlice returns a slice of UserOperationExt with cached Hashes and ProcessingStatus set to Received. -func getExtSlice(entrypoint common.Address, chainID *big.Int, userOps []*model.UserOperation) []model.UserOperationExt { +// bufferIntentOps caches the index of the userOp in the received batch and creates the UserOperationExt slice for the +// Solver with cached Hashes and ProcessingStatus set to `Received`. +func (ei *EntryPointIntents) bufferIntentOps(entrypoint common.Address, chainID *big.Int, batchIndices batchIntentIndices, userOps []*model.UserOperation) []model.UserOperationExt { userOpsExt := make([]model.UserOperationExt, len(userOps)) for idx, op := range userOps { - userOpsExt[idx].ProcessingStatus = model.Received + if op.HasIntent() { + userOpsExt[idx].ProcessingStatus = model.Received + hashID := op.GetUserOpHash(entrypoint, chainID).String() + + // Cache hash before it changes + userOpsExt[idx].OriginalHashValue = hashID - // Cache hash before it changes - userOpsExt[idx].OriginalHashValue = op.GetUserOpHash(entrypoint, chainID).String() + // Reverse caching + batchIndices[opHashID(hashID)] = batchOpIndex(idx) + } } return userOpsExt } -// SolveIntents returns a BatchHandlerFunc that will send the batch of UserOperations to the Solver and -// mark the UserOperations that have been expired been solved to be sent on chain. +// SolveIntents returns a BatchHandlerFunc that will send the batch of UserOperations to the Solver +// and those solved to be sent on chain. func (ei *EntryPointIntents) SolveIntents() modules.BatchHandlerFunc { return func(ctx *modules.BatchHandlerCtx) error { + batchIntentIndices := make(batchIntentIndices) + // cast the received userOp batch to a slice of model.UserOperation modelUserOps := *(*[]*model.UserOperation)(unsafe.Pointer(&ctx.Batch)) // Rest of the sendToSolver logic body := model.BodyOfUserOps{ UserOps: modelUserOps, - UserOpsExt: getExtSlice(ctx.EntryPoint, ctx.ChainID, modelUserOps), + UserOpsExt: ei.bufferIntentOps(ctx.EntryPoint, ctx.ChainID, batchIntentIndices, modelUserOps), } - // batch Ops hashes have been cached before calling this function - ei.bufferSentUserOp(body) - - jsonBody, err := json.Marshal(body) - if err != nil { - return err - } - - req, err := http.NewRequest(http.MethodPost, ei.SolverURL, bytes.NewBuffer(jsonBody)) - if err != nil { - return err - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := ei.SolverClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { + if err := ei.sendToSolver(body); err != nil { return err } // Mark the userOps that have been expired or invalid to be removed from the batch for idx, opExt := range body.UserOpsExt { - if opExt.ProcessingStatus == model.Expired || opExt.ProcessingStatus == model.Invalid { - batchIndex := ei.Buffer[body.UserOpsExt[idx].OriginalHashValue] - ctx.MarkOpIndexForRemoval(batchIndex) - - // Remove the userOp from the buffer - delete(ei.Buffer, body.UserOpsExt[idx].OriginalHashValue) - } + batchIndex := batchIntentIndices[opHashID(body.UserOpsExt[idx].OriginalHashValue)] switch opExt.ProcessingStatus { - case model.Expired, model.Invalid: - // dropping further processing of the userOp - batchIndex := ei.Buffer[body.UserOpsExt[idx].OriginalHashValue] - ctx.MarkOpIndexForRemoval(batchIndex) - delete(ei.Buffer, body.UserOpsExt[idx].OriginalHashValue) + case model.Unsolved, model.Expired, model.Invalid, model.Received: + // dropping further processing + ctx.MarkOpIndexForRemoval(int(batchIndex)) case model.Solved: - // Remove the Solved userOp from the Solver buffer - // Remain in the mempool until the userOp is sent on chain - delete(ei.Buffer, body.UserOpsExt[idx].OriginalHashValue) + intentSolution, err := body.UserOps[idx].GetEVMInstructions() + if err != nil { + // failed to retrieve the EVM solution for the solved Intent + // allow residing in the mempool for another solving attempt or till expired + unsolvedOpJson, _ := json.Marshal(body.UserOps[idx]) + return errors.Errorf("failed to get EVM instructions: %s for solved Intent at index %d, userOp: %s", err, batchIndex, unsolvedOpJson) + } + + // set the solved userOp values to the received batch's userOp values + + modelUserOps[batchIndex].SetEVMInstructions(intentSolution) + ctx.Batch[batchIndex].CallGasLimit = body.UserOps[idx].CallGasLimit + ctx.Batch[batchIndex].VerificationGasLimit = body.UserOps[idx].VerificationGasLimit + ctx.Batch[batchIndex].PreVerificationGas = body.UserOps[idx].PreVerificationGas + ctx.Batch[batchIndex].MaxFeePerGas = body.UserOps[idx].MaxFeePerGas + ctx.Batch[batchIndex].MaxPriorityFeePerGas = body.UserOps[idx].MaxPriorityFeePerGas + + default: + return errors.Errorf("unknown processing status: %s", opExt.ProcessingStatus) } } return nil } } + +// sendToSolver sends the batch of UserOperations to the Solver. +// TODO - implement retry logic +func (ei *EntryPointIntents) sendToSolver(body model.BodyOfUserOps) error { + jsonBody, err := json.Marshal(body) + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodPost, ei.SolverURL, bytes.NewBuffer(jsonBody)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := ei.SolverClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { + return err + } + + return nil +} From f0a698ac85773cf385c2bbdd4944883e5e0b1416 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Dec 2023 02:06:36 +0200 Subject: [PATCH 53/87] chore: remove obsolete files --- pkg/client/queue.go | 176 ------------------------------ pkg/client/queue_test.go | 230 --------------------------------------- 2 files changed, 406 deletions(-) delete mode 100644 pkg/client/queue.go delete mode 100644 pkg/client/queue_test.go diff --git a/pkg/client/queue.go b/pkg/client/queue.go deleted file mode 100644 index 89232415..00000000 --- a/pkg/client/queue.go +++ /dev/null @@ -1,176 +0,0 @@ -package client - -import ( - "sync" - "time" - - "github.com/pkg/errors" -) - -type Queue[T any] struct { - items []T - keys map[string]int - ticker *time.Ticker - mu sync.Mutex -} - -func NewQueue[T any](capacity uint) *Queue[T] { - return &Queue[T]{ - items: make([]T, 0, capacity), - keys: make(map[string]int), - } -} - -func (q *Queue[T]) SetTickerFunc(duration time.Duration, tickFunc func()) { - q.mu.Lock() - defer q.mu.Unlock() - - // Prevent multiple tickers from running simultaneously - // by stopping the previous ticker - if q.ticker != nil { - q.ticker.Stop() - } - - if duration > 0 && tickFunc != nil { - q.ticker = time.NewTicker(duration) - go func() { - for range q.ticker.C { - tickFunc() - } - }() - } -} - -func (q *Queue[T]) StopTicker() { - q.mu.Lock() - defer q.mu.Unlock() - - if q.ticker != nil { - q.ticker.Stop() - q.ticker = nil - } -} - -func (q *Queue[T]) Delete(index int) error { - q.mu.Lock() - defer q.mu.Unlock() - - if index < 0 || index >= len(q.items) { - return errors.New("index out of range") - } - - q.items = append(q.items[:index], q.items[index+1:]...) - // Update keys map - for key, idx := range q.keys { - if idx > index { - q.keys[key] = idx - 1 - } else if idx == index { - delete(q.keys, key) - } - } - - return nil -} - -func (q *Queue[T]) FindIndexByKey(key string) (int, bool) { - q.mu.Lock() - defer q.mu.Unlock() - - index, found := q.keys[key] - return index, found -} - -func (q *Queue[T]) EnqueueWithKey(key string, item T) { - q.mu.Lock() - defer q.mu.Unlock() - - q.items = append(q.items, item) - q.keys[key] = len(q.items) - 1 -} - -func (q *Queue[T]) Reset(capacity int) { - q.mu.Lock() - defer q.mu.Unlock() - - q.items = make([]T, 0, capacity) -} - -func (q *Queue[T]) Peek(index int) (T, error) { - q.mu.Lock() - defer q.mu.Unlock() - - if index < 0 || index >= len(q.items) { - var zeroValue T - return zeroValue, errors.New("index out of range") - } - - return q.items[index], nil -} - -func (q *Queue[T]) Range(f func(index int, value T)) { - q.mu.Lock() - defer q.mu.Unlock() - - for i, item := range q.items { - f(i, item) - } -} - -func (q *Queue[T]) Size() int { - q.mu.Lock() - defer q.mu.Unlock() - - return len(q.items) -} - -func (q *Queue[T]) EnqueueHead(key string, item T) { - q.mu.Lock() - defer q.mu.Unlock() - - q.items = append([]T{item}, q.items...) - q.updateKeysAfterEnqueue(0) - q.keys[key] = 0 -} - -func (q *Queue[T]) EnqueueTail(key string, item T) { - q.mu.Lock() - defer q.mu.Unlock() - - q.items = append(q.items, item) - q.keys[key] = len(q.items) - 1 -} - -func (q *Queue[T]) updateKeysAfterEnqueue(index int) { - for k, v := range q.keys { - if v >= index { - q.keys[k] = v + 1 - } - } -} - -func (q *Queue[T]) Dequeue() (T, bool) { - q.mu.Lock() - defer q.mu.Unlock() - - if len(q.items) == 0 { - var zeroValue T - return zeroValue, false - } - - item := q.items[0] - q.items = q.items[1:] - return item, true -} - -func (q *Queue[T]) ToSlice() []T { - q.mu.Lock() - defer q.mu.Unlock() - return append([]T(nil), q.items...) -} - -func (q *Queue[T]) Capacity() int { - q.mu.Lock() - defer q.mu.Unlock() - - return cap(q.items) -} diff --git a/pkg/client/queue_test.go b/pkg/client/queue_test.go deleted file mode 100644 index 5bcd7bc9..00000000 --- a/pkg/client/queue_test.go +++ /dev/null @@ -1,230 +0,0 @@ -package client - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestQueueInitializationWithCapacity(t *testing.T) { - capacity := uint(10) - queue := NewQueue[int](capacity) - - // The initial length of the queue should be 0 - assert.Equal(t, 0, len(queue.ToSlice()), "Initial length of queue should be 0") -} - -func TestQueueTickerFunc(t *testing.T) { - queue := NewQueue[int](10) - - tickChannel := make(chan bool) - tickFunc := func() { - tickChannel <- true - } - - // Set a short duration for the ticker to trigger quickly - queue.SetTickerFunc(100*time.Millisecond, tickFunc) - - // Wait for the ticker function to be executed - select { - case <-tickChannel: - // Ticker function executed - case <-time.After(1 * time.Second): - t.Fatal("Ticker function was not executed within expected time") - } - - queue.StopTicker() -} - -func TestQueueFindIndexByKey(t *testing.T) { - queue := NewQueue[int](10) - queue.EnqueueWithKey("first", 1) - queue.EnqueueWithKey("second", 2) - - // Find index by key - index, found := queue.FindIndexByKey("second") - assert.True(t, found, "Item with key 'second' should be found") - assert.Equal(t, 1, index, "Index of item with key 'second' should be 1") - - // Try to find index by a non-existing key - _, found = queue.FindIndexByKey("third") - assert.False(t, found, "Item with key 'third' should not be found") -} - -func TestDequeueEmptyQueue(t *testing.T) { - queue := NewQueue[int](0) - - // Dequeue from an empty queue - item, ok := queue.Dequeue() - assert.False(t, ok, "Expected false from Dequeue on empty queue but got true") - assert.Equal(t, 0, item, "Expected zero value from Dequeue on empty queue") -} - -func TestQueueReset(t *testing.T) { - queue := NewQueue[int](10) - - // Add items to the queue - queue.EnqueueTail("key1", 1) - queue.EnqueueTail("key2", 2) - - // Reset the queue with a different capacity - newCapacity := 5 - queue.Reset(newCapacity) - - // After reset, the size should be 0 - assert.Equal(t, 0, queue.Size(), "Size of queue after reset should be 0") - - // The capacity of the queue should match the new capacity - assert.Equal(t, int(newCapacity), queue.Capacity(), "Capacity of queue after reset should match new capacity") -} - -func TestQueueSize(t *testing.T) { - queue := NewQueue[int](10) - - // Initially, the size should be 0 - assert.Equal(t, 0, queue.Size(), "Initial size of queue should be 0") - - // Add items to the queue - queue.EnqueueTail("key1", 1) - queue.EnqueueTail("key2", 2) - - // Now, the size should be 2 - assert.Equal(t, 2, queue.Size(), "Size of queue after enqueueing items should be 2") -} - -func TestEnqueueAndDequeue(t *testing.T) { - queue := NewQueue[int](0) - - // Test EnqueueTail - queue.EnqueueTail("key1", 1) - queue.EnqueueTail("key2", 2) - - // Test EnqueueHead - queue.EnqueueHead("key3", 3) - - // Check the state of the queue - expectedSliceAfterEnqueue := []int{3, 1, 2} - assert.Equal(t, expectedSliceAfterEnqueue, queue.ToSlice(), "Queue contents after enqueue operations are not as expected") - - // Test Dequeue - item, ok := queue.Dequeue() - assert.True(t, ok, "Expected true from Dequeue but got false") - assert.Equal(t, 3, item, "Dequeued item is not as expected") - - // Check the state of the queue after dequeue - expectedSliceAfterDequeue := []int{1, 2} - assert.Equal(t, expectedSliceAfterDequeue, queue.ToSlice(), "Queue contents after dequeue operation are not as expected") -} - -func TestQueueDelete(t *testing.T) { - queue := NewQueue[int](10) - queue.EnqueueTail("key1", 1) - queue.EnqueueTail("key2", 2) - queue.EnqueueTail("key3", 3) - - // Delete the item at index 1 (value 2) - err := queue.Delete(1) - assert.NoError(t, err, "Error should not occur when deleting valid index") - - expectedItems := []int{1, 3} - assert.Equal(t, expectedItems, queue.ToSlice(), "Items after deletion do not match expected items") -} - -func TestQueueToSlice(t *testing.T) { - queue := NewQueue[string](0) - - queue.EnqueueTail("key1", "a") - queue.EnqueueTail("key2", "b") - queue.EnqueueHead("key3", "c") - - expectedSlice := []string{"c", "a", "b"} - assert.Equal(t, expectedSlice, queue.ToSlice(), "Queue to slice conversion did not match expected slice") -} - -func TestQueueRange(t *testing.T) { - queue := NewQueue[int](10) - queue.EnqueueTail("key1", 10) - queue.EnqueueTail("key2", 20) - queue.EnqueueTail("key3", 30) - - var items []int - queue.Range(func(index int, value int) { - items = append(items, value) - }) - - expectedItems := []int{10, 20, 30} - assert.Equal(t, expectedItems, items, "Items iterated by Range do not match expected items") -} - -func TestConcurrentAccess(t *testing.T) { - queue := NewQueue[int](1) - - // Perform concurrent Enqueue and Dequeue operations - go func() { - for i := 0; i < 1000; i++ { - queue.EnqueueTail(fmt.Sprintf("key%d", i), i) - } - }() - - go func() { - for i := 0; i < 1000; i++ { - queue.EnqueueHead(fmt.Sprintf("headKey%d", i), i) - } - }() - - time.Sleep(1 * time.Second) // Wait for goroutines to finish - - // The exact contents of the queue are unpredictable due to concurrent access, - // but we can check the size of the queue - assert.Equal(t, 2000, len(queue.ToSlice()), "Queue size after concurrent access is not as expected") -} - -func TestPeek(t *testing.T) { - queue := NewQueue[int](50000) - - // Add items to the queue - queue.EnqueueTail("key1", 10) - queue.EnqueueTail("key2", 20) - queue.EnqueueTail("key3", 30) - - // Test valid Peek - item, err := queue.Peek(1) - assert.NoError(t, err, "Expected no error from Peek on valid index") - assert.Equal(t, 20, item, "Peeked item at index 1 is not as expected") - - // Test Peek with invalid index - _, err = queue.Peek(5) - assert.Error(t, err, "Expected error from Peek on invalid index") -} - -func TestQueueEnqueueHead(t *testing.T) { - queue := NewQueue[int](10) - queue.EnqueueHead("first", 1) - queue.EnqueueHead("second", 2) - - // Check if the items are enqueued correctly - expectedItems := []int{2, 1} - assert.Equal(t, expectedItems, queue.ToSlice(), "Items enqueued at head do not match expected items") - - // Check if the keys are stored correctly - index, found := queue.FindIndexByKey("second") - assert.True(t, found, "Item with key 'second' should be found") - assert.Equal(t, 0, index, "Index of item with key 'second' should be 0") -} - -func TestQueueEnqueueTail(t *testing.T) { - queue := NewQueue[int](10) - queue.EnqueueTail("first", 1) - queue.EnqueueTail("second", 2) - - // Check if the items are enqueued correctly - expectedItems := []int{1, 2} - assert.Equal(t, expectedItems, queue.ToSlice(), "Items enqueued at tail do not match expected items") - - // Check if the keys are stored correctly - index, found := queue.FindIndexByKey("second") - assert.True(t, found, "Item with key 'second' should be found") - assert.Equal(t, 1, index, "Index of item with key 'second' should be 1") -} From e0afed5c208257dd94b76ab346c14d02f0d8a56a Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Dec 2023 02:07:19 +0200 Subject: [PATCH 54/87] feat: set zero gas userOps option default --- scripts/senduserop/main.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 68d52a25..c27beca1 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -1,3 +1,8 @@ +// Scripted test wallet functionality for submitting Intent userOps to the +// bundler. +// Sends semi-mocked userOps to the bundler with live nonce, +// chainID values. Support submitting userOps 0 gas for testing. +// UserOp signature verification is included. package main import ( @@ -40,7 +45,7 @@ func main() { panic(err) } - zeroGas := len(os.Args) > 1 && (os.Args[1] == "zero" || os.Args[1] == "0") + zeroGas := (len(os.Args) > 1 && (os.Args[1] == "zero" || os.Args[1] == "0")) || len(os.Args) == 1 // default choice unsignedUserOp := getMockUserOp(sender, nonce, zeroGas) userOp := getVerifiedSignedUserOp(unsignedUserOp, eoaSigner.PrivateKey, eoaSigner.PublicKey, chainID) From 9423222492a740465748dff43c0ecf7d684f8bfa Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Dec 2023 02:11:27 +0200 Subject: [PATCH 55/87] chore: Remove obsolete change --- pkg/userop/parse.go | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/pkg/userop/parse.go b/pkg/userop/parse.go index 1a38fe7f..d0486c4e 100644 --- a/pkg/userop/parse.go +++ b/pkg/userop/parse.go @@ -1,7 +1,6 @@ package userop import ( - "encoding/base64" "encoding/hex" "errors" "fmt" @@ -56,24 +55,17 @@ func decodeOpTypes( // String to []byte conversion if f == reflect.String && t == reflect.Slice { byteStr := data.(string) - - // Check for hex encoding - if len(byteStr) >= 2 && byteStr[:2] == "0x" { - b, err := hex.DecodeString(byteStr[2:]) - if err != nil { - return nil, err - } - return b, nil + if len(byteStr) < 2 || byteStr[:2] != "0x" { + return nil, errors.New("not byte string") } - // Handle base64 encoding - b, err := base64.StdEncoding.DecodeString(byteStr) - if err == nil { - return b, nil + b, err := hex.DecodeString(byteStr[2:]) + if err != nil { + return nil, err } - - return nil, errors.New("string is not valid hex or base64 encoded") + return b, nil } + return data, nil } From f23ec22443c4d382fb2bd0779bcd7da04fe23276 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Dec 2023 10:24:17 +0200 Subject: [PATCH 56/87] fix: Clarify comments --- pkg/modules/solution/solveintents.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index 1dc7b8b0..ee16882d 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -83,9 +83,10 @@ func (ei *EntryPointIntents) SolveIntents() modules.BatchHandlerFunc { batchIntentIndices := make(batchIntentIndices) // cast the received userOp batch to a slice of model.UserOperation + // to be sent to the Solver modelUserOps := *(*[]*model.UserOperation)(unsafe.Pointer(&ctx.Batch)) - // Rest of the sendToSolver logic + // Prepare the body to send to the Solver body := model.BodyOfUserOps{ UserOps: modelUserOps, UserOpsExt: ei.bufferIntentOps(ctx.EntryPoint, ctx.ChainID, batchIntentIndices, modelUserOps), @@ -95,7 +96,6 @@ func (ei *EntryPointIntents) SolveIntents() modules.BatchHandlerFunc { return err } - // Mark the userOps that have been expired or invalid to be removed from the batch for idx, opExt := range body.UserOpsExt { batchIndex := batchIntentIndices[opHashID(body.UserOpsExt[idx].OriginalHashValue)] switch opExt.ProcessingStatus { From 7dd6bbd4fdad199590295ac8fa93875fee9ab22a Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Dec 2023 18:08:05 +0200 Subject: [PATCH 57/87] fix: Restore compatibility with conventional userOps --- pkg/modules/solution/solveintents.go | 31 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index ee16882d..30df34f8 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -58,22 +58,31 @@ func New(solverURL string) *EntryPointIntents { // bufferIntentOps caches the index of the userOp in the received batch and creates the UserOperationExt slice for the // Solver with cached Hashes and ProcessingStatus set to `Received`. -func (ei *EntryPointIntents) bufferIntentOps(entrypoint common.Address, chainID *big.Int, batchIndices batchIntentIndices, userOps []*model.UserOperation) []model.UserOperationExt { - userOpsExt := make([]model.UserOperationExt, len(userOps)) - for idx, op := range userOps { +func (ei *EntryPointIntents) bufferIntentOps(entrypoint common.Address, chainID *big.Int, batchIndices batchIntentIndices, userOpBatch []*model.UserOperation) model.BodyOfUserOps { + body := model.BodyOfUserOps{ + UserOps: make([]*model.UserOperation, 0, len(userOpBatch)), + UserOpsExt: make([]model.UserOperationExt, 0, len(userOpBatch)), + } + for idx, op := range userOpBatch { if op.HasIntent() { - userOpsExt[idx].ProcessingStatus = model.Received hashID := op.GetUserOpHash(entrypoint, chainID).String() - // Cache hash before it changes - userOpsExt[idx].OriginalHashValue = hashID + // Don't mutate the original op + clonedOp := *op + body.UserOps = append(body.UserOps, &clonedOp) + + body.UserOpsExt = append(body.UserOpsExt, model.UserOperationExt{ + OriginalHashValue: hashID, + // Cache hash before it changes + ProcessingStatus: model.Received, + }) // Reverse caching batchIndices[opHashID(hashID)] = batchOpIndex(idx) } } - return userOpsExt + return body } // SolveIntents returns a BatchHandlerFunc that will send the batch of UserOperations to the Solver @@ -87,9 +96,11 @@ func (ei *EntryPointIntents) SolveIntents() modules.BatchHandlerFunc { modelUserOps := *(*[]*model.UserOperation)(unsafe.Pointer(&ctx.Batch)) // Prepare the body to send to the Solver - body := model.BodyOfUserOps{ - UserOps: modelUserOps, - UserOpsExt: ei.bufferIntentOps(ctx.EntryPoint, ctx.ChainID, batchIntentIndices, modelUserOps), + body := ei.bufferIntentOps(ctx.EntryPoint, ctx.ChainID, batchIntentIndices, modelUserOps) + + // Intents to process + if len(body.UserOps) == 0 { + return nil } if err := ei.sendToSolver(body); err != nil { From 81504e0a0a2dabd1027c80507bc7af04f7b7e180 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Dec 2023 18:14:38 +0200 Subject: [PATCH 58/87] fix: Address comment --- pkg/modules/solution/solveintents.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index 30df34f8..37f7bce6 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -39,7 +39,7 @@ type batchOpIndex int // batchIntentIndices buffers the mapping of the UserOperation hash value -> the index of the UserOperation in the batch type batchIntentIndices map[opHashID]batchOpIndex -type EntryPointIntents struct { +type IntentsHandler struct { SolverURL string SolverClient *http.Client } @@ -47,10 +47,10 @@ type EntryPointIntents struct { // Verify structural congruence var _ = model.UserOperation(userop.UserOperation{}) -func New(solverURL string) *EntryPointIntents { +func New(solverURL string) *IntentsHandler { const httpClientTimeout = 100 * time.Second - return &EntryPointIntents{ + return &IntentsHandler{ SolverURL: solverURL, SolverClient: &http.Client{Timeout: httpClientTimeout}, } @@ -58,7 +58,7 @@ func New(solverURL string) *EntryPointIntents { // bufferIntentOps caches the index of the userOp in the received batch and creates the UserOperationExt slice for the // Solver with cached Hashes and ProcessingStatus set to `Received`. -func (ei *EntryPointIntents) bufferIntentOps(entrypoint common.Address, chainID *big.Int, batchIndices batchIntentIndices, userOpBatch []*model.UserOperation) model.BodyOfUserOps { +func (ei *IntentsHandler) bufferIntentOps(entrypoint common.Address, chainID *big.Int, batchIndices batchIntentIndices, userOpBatch []*model.UserOperation) model.BodyOfUserOps { body := model.BodyOfUserOps{ UserOps: make([]*model.UserOperation, 0, len(userOpBatch)), UserOpsExt: make([]model.UserOperationExt, 0, len(userOpBatch)), @@ -87,7 +87,7 @@ func (ei *EntryPointIntents) bufferIntentOps(entrypoint common.Address, chainID // SolveIntents returns a BatchHandlerFunc that will send the batch of UserOperations to the Solver // and those solved to be sent on chain. -func (ei *EntryPointIntents) SolveIntents() modules.BatchHandlerFunc { +func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { return func(ctx *modules.BatchHandlerCtx) error { batchIntentIndices := make(batchIntentIndices) @@ -142,7 +142,7 @@ func (ei *EntryPointIntents) SolveIntents() modules.BatchHandlerFunc { // sendToSolver sends the batch of UserOperations to the Solver. // TODO - implement retry logic -func (ei *EntryPointIntents) sendToSolver(body model.BodyOfUserOps) error { +func (ei *IntentsHandler) sendToSolver(body model.BodyOfUserOps) error { jsonBody, err := json.Marshal(body) if err != nil { return err From a304ad05467aa778d18b276e925c297acfc13ab4 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Mon, 18 Dec 2023 13:59:11 +0200 Subject: [PATCH 59/87] latest up to Dec 10th (#5) * Broadcast UserOperation batch to list of builders in searcher mode (#352) * Add estimate revert reason and txn hash to searcher mode logs (#353) * Fix: include tip in OP PVG calculation (#354) * Increase the default relayer timeout to 72s (#357) --------- Co-authored-by: hazim Co-authored-by: NickSolante <41457938+NickSolante@users.noreply.github.com> --- go.mod | 4 +- go.sum | 4 +- internal/config/constants.go | 1 + internal/config/values.go | 14 +-- internal/start/searcher.go | 4 +- internal/testutils/buildermock.go | 21 +++++ internal/testutils/constants.go | 7 ++ internal/testutils/ethmock.go | 79 +++++++--------- internal/testutils/rpcmock.go | 49 ++++++++++ pkg/entrypoint/transaction/handleops.go | 52 +--------- pkg/entrypoint/transaction/utils.go | 34 +++++++ pkg/gas/pvg.go | 10 +- pkg/modules/builder/client.go | 89 +++++++++++------- pkg/modules/builder/client_test.go | 120 ++++++++++++++++++++++++ pkg/modules/builder/utils.go | 13 ++- pkg/modules/relay/relayer.go | 2 +- pkg/modules/relay/utils.go | 2 +- 17 files changed, 364 insertions(+), 141 deletions(-) create mode 100644 internal/testutils/buildermock.go create mode 100644 internal/testutils/rpcmock.go create mode 100644 pkg/entrypoint/transaction/utils.go create mode 100644 pkg/modules/builder/client_test.go diff --git a/go.mod b/go.mod index f872ed33..adfd567f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/go-logr/zerologr v1.2.3 github.com/go-playground/validator/v10 v10.12.0 github.com/google/go-cmp v0.5.9 - github.com/metachris/flashbotsrpc v0.5.0 + github.com/metachris/flashbotsrpc v0.6.0 github.com/mitchellh/mapstructure v1.5.0 github.com/puzpuzpuz/xsync/v3 v3.0.1 github.com/rs/zerolog v1.29.0 @@ -105,3 +105,5 @@ require ( gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/metachris/flashbotsrpc => github.com/stackup-wallet/flashbotsrpc v0.6.1-rc1 diff --git a/go.sum b/go.sum index 542a8737..b70d57b1 100644 --- a/go.sum +++ b/go.sum @@ -302,8 +302,6 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/metachris/flashbotsrpc v0.5.0 h1:5OLpm6+6n4kXxeh3TZBeSj0PQWDxqUsOFwy7xertXQQ= -github.com/metachris/flashbotsrpc v0.5.0/go.mod h1:UrS249kKA1PK27sf12M6tUxo/M4ayfFrBk7IMFY1TNw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -371,6 +369,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/stackup-wallet/flashbotsrpc v0.6.1-rc1 h1:kCB1WQZgD2edUiPZh+mghl1Ir/cuH5/4bI+a03Ki6Tc= +github.com/stackup-wallet/flashbotsrpc v0.6.1-rc1/go.mod h1:UrS249kKA1PK27sf12M6tUxo/M4ayfFrBk7IMFY1TNw= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= diff --git a/internal/config/constants.go b/internal/config/constants.go index a30ec4d1..fefd0503 100644 --- a/internal/config/constants.go +++ b/internal/config/constants.go @@ -5,6 +5,7 @@ import "math/big" var ( EthereumChainID = big.NewInt(1) GoerliChainID = big.NewInt(5) + SepoliaChainID = big.NewInt(11155111) ArbitrumOneChainID = big.NewInt(42161) ArbitrumGoerliChainID = big.NewInt(421613) ArbitrumSepoliaChainID = big.NewInt(421614) diff --git a/internal/config/values.go b/internal/config/values.go index 22b33933..f22363ff 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -26,7 +26,7 @@ type Values struct { Beneficiary string // Searcher mode variables. - EthBuilderUrl string + EthBuilderUrls []string BlocksInTheFuture int // Observability variables. @@ -88,7 +88,7 @@ func GetValues() *Values { viper.SetDefault("erc4337_bundler_max_batch_gas_limit", 25000000) viper.SetDefault("erc4337_bundler_max_op_ttl_seconds", 180) viper.SetDefault("erc4337_bundler_max_ops_for_unstaked_sender", 4) - viper.SetDefault("erc4337_bundler_blocks_in_the_future", 25) + viper.SetDefault("erc4337_bundler_blocks_in_the_future", 6) viper.SetDefault("erc4337_bundler_otel_insecure_mode", false) viper.SetDefault("erc4337_bundler_debug_mode", false) viper.SetDefault("erc4337_bundler_gin_mode", gin.ReleaseMode) @@ -117,7 +117,7 @@ func GetValues() *Values { _ = viper.BindEnv("erc4337_bundler_max_batch_gas_limit") _ = viper.BindEnv("erc4337_bundler_max_op_ttl_seconds") _ = viper.BindEnv("erc4337_bundler_max_ops_for_unstaked_sender") - _ = viper.BindEnv("erc4337_bundler_eth_builder_url") + _ = viper.BindEnv("erc4337_bundler_eth_builder_urls") _ = viper.BindEnv("erc4337_bundler_blocks_in_the_future") _ = viper.BindEnv("erc4337_bundler_otel_service_name") _ = viper.BindEnv("erc4337_bundler_otel_collector_headers") @@ -147,8 +147,8 @@ func GetValues() *Values { switch viper.GetString("mode") { case "searcher": - if variableNotSetOrIsNil("erc4337_bundler_eth_builder_url") { - panic("Fatal config error: erc4337_bundler_eth_builder_url not set") + if variableNotSetOrIsNil("erc4337_bundler_eth_builder_urls") { + panic("Fatal config error: erc4337_bundler_eth_builder_urls not set") } } @@ -175,7 +175,7 @@ func GetValues() *Values { maxBatchGasLimit := big.NewInt(int64(viper.GetInt("erc4337_bundler_max_batch_gas_limit"))) maxOpTTL := time.Second * viper.GetDuration("erc4337_bundler_max_op_ttl_seconds") maxOpsForUnstakedSender := viper.GetInt("erc4337_bundler_max_ops_for_unstaked_sender") - ethBuilderUrl := viper.GetString("erc4337_bundler_eth_builder_url") + ethBuilderUrls := envArrayToStringSlice(viper.GetString("erc4337_bundler_eth_builder_urls")) blocksInTheFuture := viper.GetInt("erc4337_bundler_blocks_in_the_future") otelServiceName := viper.GetString("erc4337_bundler_otel_service_name") otelCollectorHeader := envKeyValStringToMap(viper.GetString("erc4337_bundler_otel_collector_headers")) @@ -196,7 +196,7 @@ func GetValues() *Values { MaxBatchGasLimit: maxBatchGasLimit, MaxOpTTL: maxOpTTL, MaxOpsForUnstakedSender: maxOpsForUnstakedSender, - EthBuilderUrl: ethBuilderUrl, + EthBuilderUrls: ethBuilderUrls, BlocksInTheFuture: blocksInTheFuture, OTELServiceName: otelServiceName, OTELCollectorHeaders: otelCollectorHeader, diff --git a/internal/start/searcher.go b/internal/start/searcher.go index b8deb79a..f0e39be9 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -61,7 +61,7 @@ func SearcherMode() { eth := ethclient.NewClient(rpc) - fb := flashbotsrpc.NewFlashbotsRPC(conf.EthBuilderUrl) + fb := flashbotsrpc.NewBuilderBroadcastRPC(conf.EthBuilderUrls) chain, err := eth.ChainID(context.Background()) if err != nil { @@ -118,6 +118,7 @@ func SearcherMode() { // TODO: Create separate go-routine for tracking transactions sent to the block builder. builder := builder.New(eoa, eth, fb, beneficiary, conf.BlocksInTheFuture) + paymaster := paymaster.New(db) // Init Client @@ -192,6 +193,7 @@ func SearcherMode() { } r.POST("/", handlers...) r.POST("/rpc", handlers...) + if err := r.Run(fmt.Sprintf(":%d", conf.Port)); err != nil { log.Fatal(err) } diff --git a/internal/testutils/buildermock.go b/internal/testutils/buildermock.go new file mode 100644 index 00000000..9b2e7668 --- /dev/null +++ b/internal/testutils/buildermock.go @@ -0,0 +1,21 @@ +package testutils + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + + "github.com/metachris/flashbotsrpc" +) + +func BadBuilderRpcMock() *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + res := &flashbotsrpc.RelayErrorResponse{ + Error: "Mock upstream builder error", + } + w.WriteHeader(http.StatusOK) + if err := json.NewEncoder(w).Encode(res); err != nil { + panic(err) + } + })) +} diff --git a/internal/testutils/constants.go b/internal/testutils/constants.go index 18c3f8c4..cdc5b099 100644 --- a/internal/testutils/constants.go +++ b/internal/testutils/constants.go @@ -5,7 +5,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint" + "github.com/stackup-wallet/stackup-bundler/pkg/signer" ) var ( @@ -44,4 +47,8 @@ var ( UnstakeDelaySec: uint32(0), WithdrawTime: big.NewInt(0), } + + pk, _ = crypto.GenerateKey() + DummyEOA, _ = signer.New(hexutil.Encode(crypto.FromECDSA(pk))[2:]) + MockHash = "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead" ) diff --git a/internal/testutils/ethmock.go b/internal/testutils/ethmock.go index 15b86947..77ce9586 100644 --- a/internal/testutils/ethmock.go +++ b/internal/testutils/ethmock.go @@ -1,51 +1,44 @@ package testutils import ( - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" -) + "math/big" + "time" -type mockReq struct { - JsonRpc string `json:"jsonrpc"` - ID float64 `json:"id"` - Method string `json:"method"` -} + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) -type mockRes struct { - JsonRpc string `json:"jsonrpc"` - ID float64 `json:"id"` - Result any `json:"result"` +func NewBlockMock() map[string]any { + return map[string]any{ + "parentHash": MockHash, + "sha3Uncles": MockHash, + "stateRoot": MockHash, + "transactionsRoot": MockHash, + "receiptsRoot": MockHash, + "logsBloom": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "difficulty": "0x0", + "number": "0x1", + "gasLimit": hexutil.EncodeBig(big.NewInt(30000000)), + "gasUsed": hexutil.EncodeBig(big.NewInt(5000000)), + "timestamp": hexutil.EncodeUint64(uint64(time.Now().Unix())), + "extraData": "0x", + } } -type MethodMocks map[string]any - -// EthMock returns a httptest.Server for mocking the return value of a JSON-RPC method call to an Ethereum node. -func EthMock(mocks MethodMocks) *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var req mockReq - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - panic(err) - } - - mock, ok := mocks[req.Method] - if !ok { - w.WriteHeader(http.StatusBadRequest) - if _, err := w.Write([]byte(fmt.Sprintf("method not in mocks: %s", req.Method))); err != nil { - panic(err) - } - return - } - - res := &mockRes{ - JsonRpc: req.JsonRpc, - ID: req.ID, - Result: mock, - } - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(res); err != nil { - panic(err) - } - })) +func NewTransactionReceiptMock() map[string]any { + return map[string]any{ + "blockHash": MockHash, + "blockNumber": "0x1", + "cumulativeGasUsed": "0x1", + "effectiveGasPrice": "0x1", + "from": common.HexToAddress("0x").Hex(), + "gasUsed": "0x1", + "logs": []any{}, + "logsBloom": "0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead", + "status": "0x1", + "to": common.HexToAddress("0x").Hex(), + "transactionHash": MockHash, + "transactionIndex": "0x1", + "type": "0x2", + } } diff --git a/internal/testutils/rpcmock.go b/internal/testutils/rpcmock.go new file mode 100644 index 00000000..37a0e670 --- /dev/null +++ b/internal/testutils/rpcmock.go @@ -0,0 +1,49 @@ +package testutils + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" +) + +type mockReq struct { + JsonRpc string `json:"jsonrpc"` + ID float64 `json:"id"` + Method string `json:"method"` +} + +type mockRes struct { + JsonRpc string `json:"jsonrpc"` + ID float64 `json:"id"` + Result any `json:"result"` +} + +type MethodMocks map[string]any + +func RpcMock(mocks MethodMocks) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var req mockReq + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + panic(err) + } + mock, ok := mocks[req.Method] + if !ok { + w.WriteHeader(http.StatusBadRequest) + if _, err := w.Write([]byte(fmt.Sprintf("method not in mocks: %s", req.Method))); err != nil { + panic(err) + } + return + } + + res := &mockRes{ + JsonRpc: req.JsonRpc, + ID: req.ID, + Result: mock, + } + w.WriteHeader(http.StatusOK) + if err := json.NewEncoder(w).Encode(res); err != nil { + panic(err) + } + })) +} diff --git a/pkg/entrypoint/transaction/handleops.go b/pkg/entrypoint/transaction/handleops.go index 5342d374..01a25b6c 100644 --- a/pkg/entrypoint/transaction/handleops.go +++ b/pkg/entrypoint/transaction/handleops.go @@ -1,7 +1,6 @@ package transaction import ( - bytesPkg "bytes" "context" "errors" "math" @@ -11,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint" @@ -38,6 +36,7 @@ type Opts struct { Tip *big.Int GasPrice *big.Int GasLimit uint64 + NoSend bool WaitTimeout time.Duration } @@ -104,6 +103,7 @@ func HandleOps(opts *Opts) (txn *types.Transaction, err error) { return nil, err } auth.GasLimit = opts.GasLimit + auth.NoSend = opts.NoSend nonce, err := opts.Eth.NonceAt(context.Background(), opts.EOA.Address, nil) if err != nil { @@ -123,55 +123,11 @@ func HandleOps(opts *Opts) (txn *types.Transaction, err error) { txn, err = ep.HandleOps(auth, toAbiType(opts.Batch), opts.Beneficiary) if err != nil { return nil, err - } else if opts.WaitTimeout == 0 { + } else if opts.WaitTimeout == 0 || opts.NoSend { // Don't wait for transaction to be included. All userOps in the current batch will be dropped // regardless of the transaction status. return txn, nil } - ctx, cancel := context.WithTimeout(context.Background(), opts.WaitTimeout) - defer cancel() - if receipt, err := bind.WaitMined(ctx, opts.Eth, txn); err != nil { - return nil, err - } else if receipt.Status == types.ReceiptStatusFailed { - // Return an error here so that the current batch stays in the mempool. In the next bundler iteration, - // the offending userOps will be dropped during gas estimation. - return nil, errors.New("transaction: failed status") - } - return txn, nil -} - -// CreateRawHandleOps returns a raw transaction string that calls handleOps() on the EntryPoint with a given -// batch, gas limit, and tip. -func CreateRawHandleOps(opts *Opts) (string, error) { - ep, err := entrypoint.NewEntrypoint(opts.EntryPoint, opts.Eth) - if err != nil { - return "", err - } - - auth, err := bind.NewKeyedTransactorWithChainID(opts.EOA.PrivateKey, opts.ChainID) - if err != nil { - return "", err - } - auth.GasLimit = opts.GasLimit - auth.NoSend = true - if opts.BaseFee != nil { - tip, err := opts.Eth.SuggestGasTipCap(context.Background()) - if err != nil { - return "", err - } - - auth.GasTipCap = tip - auth.GasFeeCap = big.NewInt(0).Add(opts.BaseFee, tip) - } - - tx, err := ep.HandleOps(auth, toAbiType(opts.Batch), opts.Beneficiary) - if err != nil { - return "", err - } - - ts := types.Transactions{tx} - rawTxBytes := new(bytesPkg.Buffer) - ts.EncodeIndex(0, rawTxBytes) - return hexutil.Encode(rawTxBytes.Bytes()), nil + return Wait(txn, opts.Eth, opts.WaitTimeout) } diff --git a/pkg/entrypoint/transaction/utils.go b/pkg/entrypoint/transaction/utils.go new file mode 100644 index 00000000..1bed87e4 --- /dev/null +++ b/pkg/entrypoint/transaction/utils.go @@ -0,0 +1,34 @@ +package transaction + +import ( + "bytes" + "context" + "errors" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" +) + +// ToRawTxHex Takes a Geth types.Transaction and returns the encoded raw hex string. +func ToRawTxHex(txn *types.Transaction) string { + rawTxn := new(bytes.Buffer) + types.Transactions{txn}.EncodeIndex(0, rawTxn) + return hexutil.Encode(rawTxn.Bytes()) +} + +// Wait blocks the process until a given transaction has been included on-chain or timeout has been reached. +func Wait(txn *types.Transaction, eth *ethclient.Client, timeout time.Duration) (*types.Transaction, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + if receipt, err := bind.WaitMined(ctx, eth, txn); err != nil { + return nil, err + } else if receipt.Status == types.ReceiptStatusFailed { + // Return an error here so that the current batch stays in the mempool. In the next bundler iteration, + // the offending userOps will be dropped during gas estimation. + return nil, errors.New("transaction: failed status") + } + return txn, nil +} diff --git a/pkg/gas/pvg.go b/pkg/gas/pvg.go index 20b1ea65..4b6d64db 100644 --- a/pkg/gas/pvg.go +++ b/pkg/gas/pvg.go @@ -112,7 +112,11 @@ func CalcOptimismPVGWithEthClient( if err != nil { return nil, err } - tx, err := transaction.CreateRawHandleOps(&transaction.Opts{ + tip, err := eth.SuggestGasTipCap(context.Background()) + if err != nil { + return nil, err + } + tx, err := transaction.HandleOps(&transaction.Opts{ EOA: dummy, Eth: eth, ChainID: chainID, @@ -120,14 +124,16 @@ func CalcOptimismPVGWithEthClient( Batch: []*userop.UserOperation{op}, Beneficiary: dummy.Address, BaseFee: head.BaseFee, + Tip: tip, GasLimit: math.MaxUint64, + NoSend: true, }) if err != nil { return nil, err } // Encode function data for GetL1Fee - data, err := hexutil.Decode(tx) + data, err := hexutil.Decode(transaction.ToRawTxHex(tx)) if err != nil { return nil, err } diff --git a/pkg/modules/builder/client.go b/pkg/modules/builder/client.go index 49cc67cd..88baf454 100644 --- a/pkg/modules/builder/client.go +++ b/pkg/modules/builder/client.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "math/big" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -22,9 +23,10 @@ import ( type BuilderClient struct { eoa *signer.EOA eth *ethclient.Client - rpc *flashbotsrpc.FlashbotsRPC + rpc *flashbotsrpc.BuilderBroadcastRPC beneficiary common.Address blocksInTheFuture int + waitTimeout time.Duration } // New returns an instance of a BuilderClient with modules to send UserOperation bundles via the mev-boost @@ -32,7 +34,7 @@ type BuilderClient struct { func New( eoa *signer.EOA, eth *ethclient.Client, - fb *flashbotsrpc.FlashbotsRPC, + fb *flashbotsrpc.BuilderBroadcastRPC, beneficiary common.Address, blocksInTheFuture int, ) *BuilderClient { @@ -42,14 +44,23 @@ func New( rpc: fb, beneficiary: beneficiary, blocksInTheFuture: blocksInTheFuture, + waitTimeout: DefaultWaitTimeout, } } +// SetWaitTimeout sets the total time to wait for a transaction to be included. When a timeout is reached, the +// BatchHandler will throw an error if the transaction has not been included or has been included but with a +// failed status. +// +// The default value is 72 seconds. Setting the value to 0 will skip waiting for a transaction to be included. +func (b *BuilderClient) SetWaitTimeout(timeout time.Duration) { + b.waitTimeout = timeout +} + // SendUserOperation returns a BatchHandler that is used by the Bundler to send batches to a block builder -// that supports eth_callBundle and eth_sendBundle. +// that supports eth_sendBundle. func (b *BuilderClient) SendUserOperation() modules.BatchHandlerFunc { return func(ctx *modules.BatchHandlerCtx) error { - // Estimate gas for handleOps() and drop all userOps that cause unexpected reverts. opts := transaction.Opts{ EOA: b.eoa, Eth: b.eth, @@ -58,9 +69,14 @@ func (b *BuilderClient) SendUserOperation() modules.BatchHandlerFunc { Batch: ctx.Batch, Beneficiary: b.beneficiary, BaseFee: ctx.BaseFee, + Tip: ctx.Tip, GasPrice: ctx.GasPrice, GasLimit: 0, + NoSend: true, + WaitTimeout: b.waitTimeout, } + // Estimate gas for handleOps() and drop all userOps that cause unexpected reverts. + estRev := []string{} for len(ctx.Batch) > 0 { est, revert, err := transaction.EstimateHandleOpsGas(&opts) @@ -68,19 +84,25 @@ func (b *BuilderClient) SendUserOperation() modules.BatchHandlerFunc { return err } else if revert != nil { ctx.MarkOpIndexForRemoval(revert.OpIndex) + estRev = append(estRev, revert.Reason) } else { opts.GasLimit = est break } } + ctx.Data["estimate_revert_reasons"] = estRev + + // No need to continue if the batch size is 0. Otherwise we would just be sending empty batches. + if len(ctx.Batch) == 0 { + return nil + } // Calculate the max base fee up to a future block number. bn, err := b.eth.BlockNumber(context.Background()) if err != nil { return err } - blkNum := big.NewInt(0).SetUint64(bn) - NxtBlkNum := big.NewInt(0).Add(blkNum, big.NewInt(1)) + nbn := big.NewInt(0).Add(big.NewInt(0).SetUint64(bn), big.NewInt(1)) mbf := ctx.BaseFee for i := 0; i < b.blocksInTheFuture; i++ { a := big.NewInt(0).Mul(mbf, big.NewInt(1125)) @@ -89,44 +111,43 @@ func (b *BuilderClient) SendUserOperation() modules.BatchHandlerFunc { } opts.BaseFee = mbf - // Call CreateRawHandleOps() with gas estimate and max base fee. - rawTx, err := transaction.CreateRawHandleOps(&opts) + // Create no send transaction to the EntryPoint + txn, err := transaction.HandleOps(&opts) if err != nil { return err } - // Simulate bundle. - callBundleArgs := flashbotsrpc.FlashbotsCallBundleParam{ - Txs: []string{rawTx}, - BlockNumber: hexutil.EncodeBig(NxtBlkNum), - StateBlockNumber: "latest", - } - sim, err := b.rpc.FlashbotsCallBundle(b.eoa.PrivateKey, callBundleArgs) - if err != nil { - return err - } - if len(sim.Results) != 1 { - return fmt.Errorf("unexpected simulation result length, want 1, got %d", len(sim.Results)) - } - if sim.Results[0].Error != "" { - // TODO: Implement better error handling and retry. - return errors.New(sim.Results[0].Error) - } - - // Send bundle for all blocks up to a future block number. - // Note: Do not try to access bundleHash from results. Flashbots builder does not return it. + // Broadcast bundle to a list of ethereum block builders for all blocks up to a future block. + shouldFail := true + var errs error for i := 0; i < b.blocksInTheFuture; i++ { - futureBlkNum := big.NewInt(0).Add(blkNum, big.NewInt(int64(i))) + fbn := big.NewInt(0).Add(nbn, big.NewInt(int64(i))) sendBundleArgs := flashbotsrpc.FlashbotsSendBundleRequest{ - Txs: []string{rawTx}, - BlockNumber: hexutil.EncodeBig(futureBlkNum), + Txs: []string{transaction.ToRawTxHex(txn)}, + BlockNumber: hexutil.EncodeBig(fbn), } - _, err := b.rpc.FlashbotsSendBundle(b.eoa.PrivateKey, sendBundleArgs) - if err != nil { - return err + + results := b.rpc.BroadcastBundle(b.eoa.PrivateKey, sendBundleArgs) + for _, result := range results { + if result.Err != nil { + errs = errors.Join(errs, result.Err) + } else { + shouldFail = false + } } } + // If there are no successful broadcast, return an error. + if shouldFail { + return fmt.Errorf("%w: \n\n%w", ErrFlashbotsBroadcastBundle, errs) + } + + // Wait for transaction to be included on-chain. + if _, err := transaction.Wait(txn, opts.Eth, opts.WaitTimeout); err != nil { + return err + } + ctx.Data["txn_hash"] = txn.Hash().String() + return nil } } diff --git a/pkg/modules/builder/client_test.go b/pkg/modules/builder/client_test.go new file mode 100644 index 00000000..05d81bac --- /dev/null +++ b/pkg/modules/builder/client_test.go @@ -0,0 +1,120 @@ +package builder + +import ( + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + "github.com/metachris/flashbotsrpc" + "github.com/stackup-wallet/stackup-bundler/internal/testutils" + "github.com/stackup-wallet/stackup-bundler/pkg/modules" + "github.com/stackup-wallet/stackup-bundler/pkg/userop" +) + +func TestSendUserOperationWithAllUpstreamErrors(t *testing.T) { + n := testutils.RpcMock(testutils.MethodMocks{ + "eth_blockNumber": "0x1", + "eth_gasPrice": "0x1", + "eth_getTransactionCount": "0x1", + "eth_estimateGas": "0x1", + "eth_getBlockByNumber": testutils.NewBlockMock(), + "eth_getTransactionReceipt": testutils.NewTransactionReceiptMock(), + }) + r, _ := rpc.Dial(n.URL) + eth := ethclient.NewClient(r) + + bb1 := testutils.BadBuilderRpcMock() + bb2 := testutils.BadBuilderRpcMock() + fb := flashbotsrpc.NewBuilderBroadcastRPC([]string{bb1.URL, bb2.URL}) + fn := New(testutils.DummyEOA, eth, fb, testutils.DummyEOA.Address, 1).SendUserOperation() + + if err := fn( + modules.NewBatchHandlerContext( + []*userop.UserOperation{testutils.MockValidInitUserOp()}, + common.HexToAddress("0x"), + testutils.ChainID, + big.NewInt(1), + big.NewInt(1), + big.NewInt(1), + ), + ); !errors.Is(err, ErrFlashbotsBroadcastBundle) { + t.Fatalf("got %v, want ErrFlashbotsBroadcastBundle", err) + } +} + +func TestSendUserOperationWithPartialUpstreamErrors(t *testing.T) { + n := testutils.RpcMock(testutils.MethodMocks{ + "eth_blockNumber": "0x1", + "eth_gasPrice": "0x1", + "eth_getTransactionCount": "0x1", + "eth_estimateGas": "0x1", + "eth_getBlockByNumber": testutils.NewBlockMock(), + "eth_getTransactionReceipt": testutils.NewTransactionReceiptMock(), + }) + r, _ := rpc.Dial(n.URL) + eth := ethclient.NewClient(r) + + bb1 := testutils.RpcMock(testutils.MethodMocks{ + "eth_sendBundle": map[string]string{ + "bundleHash": testutils.MockHash, + }, + }) + bb2 := testutils.BadBuilderRpcMock() + fb := flashbotsrpc.NewBuilderBroadcastRPC([]string{bb1.URL, bb2.URL}) + fn := New(testutils.DummyEOA, eth, fb, testutils.DummyEOA.Address, 1).SendUserOperation() + + if err := fn( + modules.NewBatchHandlerContext( + []*userop.UserOperation{testutils.MockValidInitUserOp()}, + common.HexToAddress("0x"), + testutils.ChainID, + big.NewInt(1), + big.NewInt(1), + big.NewInt(1), + ), + ); err != nil { + t.Fatalf("got %v, want nil", err) + } +} + +func TestSendUserOperationWithNoUpstreamErrors(t *testing.T) { + n := testutils.RpcMock(testutils.MethodMocks{ + "eth_blockNumber": "0x1", + "eth_gasPrice": "0x1", + "eth_getTransactionCount": "0x1", + "eth_estimateGas": "0x1", + "eth_getBlockByNumber": testutils.NewBlockMock(), + "eth_getTransactionReceipt": testutils.NewTransactionReceiptMock(), + }) + r, _ := rpc.Dial(n.URL) + eth := ethclient.NewClient(r) + + bb1 := testutils.RpcMock(testutils.MethodMocks{ + "eth_sendBundle": map[string]string{ + "bundleHash": testutils.MockHash, + }, + }) + bb2 := testutils.RpcMock(testutils.MethodMocks{ + "eth_sendBundle": map[string]string{ + "bundleHash": testutils.MockHash, + }, + }) + fb := flashbotsrpc.NewBuilderBroadcastRPC([]string{bb1.URL, bb2.URL}) + fn := New(testutils.DummyEOA, eth, fb, testutils.DummyEOA.Address, 1).SendUserOperation() + + if err := fn( + modules.NewBatchHandlerContext( + []*userop.UserOperation{testutils.MockValidInitUserOp()}, + common.HexToAddress("0x"), + testutils.ChainID, + big.NewInt(1), + big.NewInt(1), + big.NewInt(1), + ), + ); err != nil { + t.Fatalf("got %v, want nil", err) + } +} diff --git a/pkg/modules/builder/utils.go b/pkg/modules/builder/utils.go index c4b2108e..8c75c7a8 100644 --- a/pkg/modules/builder/utils.go +++ b/pkg/modules/builder/utils.go @@ -1,11 +1,22 @@ package builder import ( + "errors" + "time" + mapset "github.com/deckarep/golang-set/v2" "github.com/stackup-wallet/stackup-bundler/internal/config" ) var ( // CompatibleChainIDs is a set of chainIDs that support the Block Builder API. - CompatibleChainIDs = mapset.NewSet(config.EthereumChainID.Uint64(), config.GoerliChainID.Uint64()) + CompatibleChainIDs = mapset.NewSet( + config.EthereumChainID.Uint64(), + config.GoerliChainID.Uint64(), + config.SepoliaChainID.Uint64(), + ) + + DefaultWaitTimeout = 72 * time.Second + + ErrFlashbotsBroadcastBundle = errors.New("flashbots broadcast bundle error") ) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index bb7e6031..f211d412 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -91,7 +91,7 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { break } } - ctx.Data["relayer_est_revert_reasons"] = estRev + ctx.Data["estimate_revert_reasons"] = estRev // Call handleOps() with gas estimate. Any userOps that cause a revert at this stage will be // caught and dropped in the next iteration. diff --git a/pkg/modules/relay/utils.go b/pkg/modules/relay/utils.go index e167c33e..f93b2a97 100644 --- a/pkg/modules/relay/utils.go +++ b/pkg/modules/relay/utils.go @@ -5,5 +5,5 @@ import ( ) var ( - DefaultWaitTimeout = 30 * time.Second + DefaultWaitTimeout = 72 * time.Second ) From 22544ccc8e7e5d2b903d6b231af05c7378f7276b Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:48:26 +0200 Subject: [PATCH 60/87] Latest on Intents with latest from syncing Main (#7) * feat: display hash value on sent userOp * fix: handle solver being offline * latest up to Dec 10th (#5) (#6) * Broadcast UserOperation batch to list of builders in searcher mode (#352) * Add estimate revert reason and txn hash to searcher mode logs (#353) * Fix: include tip in OP PVG calculation (#354) * Increase the default relayer timeout to 72s (#357) --------- Co-authored-by: hazim Co-authored-by: NickSolante <41457938+NickSolante@users.noreply.github.com> * chore: Go mod tidying * fix: Remove redundant line out of merge * feat: Allow unsolved intents to go again --------- Co-authored-by: hazim Co-authored-by: NickSolante <41457938+NickSolante@users.noreply.github.com> --- go.mod | 1 + pkg/bundler/bundler.go | 11 ++++++++++- pkg/modules/relay/relayer.go | 12 ++++++++++-- pkg/modules/solution/solveintents.go | 6 ++++-- pkg/userop/intent.go | 12 ++++++++++++ scripts/senduserop/main.go | 8 +++++--- 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 53d17dfb..ddab91ec 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/google/go-cmp v0.5.9 github.com/metachris/flashbotsrpc v0.6.0 github.com/joho/godotenv v1.5.1 + github.com/metachris/flashbotsrpc v0.6.0 github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 github.com/puzpuzpuz/xsync/v3 v3.0.1 diff --git a/pkg/bundler/bundler.go b/pkg/bundler/bundler.go index 2d97f609..63cef31b 100644 --- a/pkg/bundler/bundler.go +++ b/pkg/bundler/bundler.go @@ -169,7 +169,16 @@ func (i *Bundler) Process(ep common.Address) (*modules.BatchHandlerCtx, error) { } // Remove userOps that remain in the context from mempool. - rmOps := append([]*userop.UserOperation{}, ctx.Batch...) + // Unsolved intents are not removed from the mempool + // for another Solving go in the next bundler run. + rmOps := make([]*userop.UserOperation, 0, len(ctx.Batch)) + for _, remainingOp := range ctx.Batch { + if remainingOp.IsUnsolvedIntent() { + continue + } + + rmOps = append(rmOps, remainingOp) + } rmOps = append(rmOps, ctx.PendingRemoval...) if err := i.mempool.RemoveOps(ep, rmOps...); err != nil { l.Error(err, "bundler run error") diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index 54ab4522..5cec6358 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -70,10 +70,18 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { nonIntentsBatch := make([]*userop.UserOperation, 0, len(ctx.Batch)) intentsBatch := make([]*userop.UserOperation, 0, len(ctx.Batch)) for _, userOp := range ctx.Batch { - if userOp.HasIntent() { + if userOp.IsIntentExecutable() { + // Solved Intent UserOperations intentsBatch = append(intentsBatch, userOp) - } else { + + } else if !userOp.HasIntent() { + // conventional UserOperation nonIntentsBatch = append(nonIntentsBatch, userOp) + + } else { + // Do not send unsolved Intents to the EntryPoint + r.logger.WithValues("userOp Hash", userOp.GetUserOpHash(ctx.EntryPoint, ctx.ChainID)). + Info("unsolved intent not sent to entrypoint") } } diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index 37f7bce6..fe965d4b 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -104,7 +104,10 @@ func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { } if err := ei.sendToSolver(body); err != nil { - return err + // swallow Solver connectivity error here to avoid + // an infinite loop of retries for the same batch till the + // batch expires or the solver is back online. + return nil } for idx, opExt := range body.UserOpsExt { @@ -141,7 +144,6 @@ func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { } // sendToSolver sends the batch of UserOperations to the Solver. -// TODO - implement retry logic func (ei *IntentsHandler) sendToSolver(body model.BodyOfUserOps) error { jsonBody, err := json.Marshal(body) if err != nil { diff --git a/pkg/userop/intent.go b/pkg/userop/intent.go index 53d46313..28eb08e8 100644 --- a/pkg/userop/intent.go +++ b/pkg/userop/intent.go @@ -9,3 +9,15 @@ func (op *UserOperation) HasIntent() bool { return modelUserOp.HasIntent() } + +func (op *UserOperation) IsUnsolvedIntent() bool { + modelUserOp := model.UserOperation(*op) + + return modelUserOp.HasIntent() && !modelUserOp.HasEVMInstructions() +} + +func (op *UserOperation) IsIntentExecutable() bool { + modelUserOp := model.UserOperation(*op) + + return modelUserOp.HasIntent() && modelUserOp.HasEVMInstructions() +} diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index c27beca1..704a6d41 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -50,12 +50,14 @@ func main() { userOp := getVerifiedSignedUserOp(unsignedUserOp, eoaSigner.PrivateKey, eoaSigner.PublicKey, chainID) - sendUserOp(userOp) + sendUserOp(userOp, chainID) } // sendUserOp makes a UserOperation RPC request to the bundler. -func sendUserOp(userOp *userop.UserOperation) { - println("userOp ------------> bundler") +func sendUserOp(userOp *userop.UserOperation, chainID *big.Int) { + userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddrV060), chainID).String() + + println("userOp (", userOpHash, ") ------------> bundler") op := model.UserOperation(*userOp) println(op.String()) println() From f6bc07539ec9fffb9a365f6561da127bc0400e65 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:58:46 +0200 Subject: [PATCH 61/87] fix: Remove loc side effect from merging --- pkg/modules/relay/relayer.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index 5cec6358..61db673b 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -85,7 +85,6 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { } } - ctx.Data["estimate_revert_reasons"] = estRev // Only proceed if there are conventional UserOperations to process if len(nonIntentsBatch) > 0 { opts := r.getCallOptions(ctx, nonIntentsBatch) From 5e8e6b562121c567cdf684430d92b3e2366ab89a Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:48:48 +0200 Subject: [PATCH 62/87] chore: Change EoA owner --- scripts/senduserop/main.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 704a6d41..3f8bd1d2 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -38,7 +38,7 @@ const entrypointAddrV060 = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" func main() { nodeURL, eoaSigner := readConf() - sender := common.HexToAddress("0x3068c2408c01bECde4BcCB9f246b56651BE1d12D") + sender := common.HexToAddress("0x6B5f6558CB8B3C8Fec2DA0B1edA9b9d5C064ca47") nonce, chainID, err := getNodeIDs(nodeURL, eoaSigner.Address) if err != nil { @@ -50,6 +50,8 @@ func main() { userOp := getVerifiedSignedUserOp(unsignedUserOp, eoaSigner.PrivateKey, eoaSigner.PublicKey, chainID) + fmt.Printf("%s\n", string(userOp.InitCode)) + fmt.Printf("%x\n", userOp.Signature) sendUserOp(userOp, chainID) } @@ -61,6 +63,12 @@ func sendUserOp(userOp *userop.UserOperation, chainID *big.Int) { op := model.UserOperation(*userOp) println(op.String()) println() + // marshal JSON of op + opJSON, err := op.MarshalJSON() + if err != nil { + panic(err) + } + println(string(opJSON)) request := JsonRpcRequest{ Jsonrpc: "2.0", @@ -91,7 +99,7 @@ func sendUserOp(userOp *userop.UserOperation, chainID *big.Int) { func getMockUserOp(sender common.Address, nonce *big.Int, zeroGas bool) *userop.UserOperation { intentJSON := `{"sender":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b","kind":"swap","hash":"","sellToken":"TokenA","buyToken":"TokenB","sellAmount":10,"buyAmount":5,"partiallyFillable":false,"status":"Received","createdAt":0,"expirationAt":0}` - + println("intentJSON:", intentJSON) // Conditional gas values based on zeroGas flag var callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas, maxPriorityFeePerGas *big.Int if zeroGas { From 8792bef180476819f0a81de4fd367e4d4c8c9bae Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Sat, 23 Dec 2023 13:53:13 +0200 Subject: [PATCH 63/87] chore: Add v0.8 model --- go.mod | 3 +-- go.sum | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index ddab91ec..9f30e9d8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.20 require ( - github.com/blndgs/model v0.7.0 + github.com/blndgs/model v0.8.0 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.11.5 @@ -14,7 +14,6 @@ require ( github.com/go-playground/validator/v10 v10.12.0 github.com/goccy/go-json v0.10.2 github.com/google/go-cmp v0.5.9 - github.com/metachris/flashbotsrpc v0.6.0 github.com/joho/godotenv v1.5.1 github.com/metachris/flashbotsrpc v0.6.0 github.com/mitchellh/mapstructure v1.5.0 diff --git a/go.sum b/go.sum index e4cad365..db39d8a8 100644 --- a/go.sum +++ b/go.sum @@ -49,6 +49,8 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/blndgs/model v0.7.0 h1:t2C1AbNSBIsTmgcdOtTL2RX6K71vkZBoccR78KFrWJc= github.com/blndgs/model v0.7.0/go.mod h1:l7SqtUKcm7OkOBSfBWlSptqPXxNm/OnvizdWwBVtZlA= +github.com/blndgs/model v0.8.0 h1:e91B3kF0+f0e+LB63vcnYaJNsarcOhv9PEqETikF94Y= +github.com/blndgs/model v0.8.0/go.mod h1:l7SqtUKcm7OkOBSfBWlSptqPXxNm/OnvizdWwBVtZlA= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= From f5259401aa6baefb52a78d2d12f8180f3ae578b2 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:00:54 +0200 Subject: [PATCH 64/87] chore: Comment in likely redundant signing --- scripts/senduserop/main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 3f8bd1d2..92d61ebf 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -178,12 +178,12 @@ func getSignature(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey, ch } // Normalize S value for Ethereum - sValue := big.NewInt(0).SetBytes(signature[32:64]) - secp256k1N := crypto.S256().Params().N - if sValue.Cmp(new(big.Int).Rsh(secp256k1N, 1)) > 0 { - sValue.Sub(secp256k1N, sValue) - copy(signature[32:64], sValue.Bytes()) - } + // sValue := big.NewInt(0).SetBytes(signature[32:64]) + // secp256k1N := crypto.S256().Params().N + // if sValue.Cmp(new(big.Int).Rsh(secp256k1N, 1)) > 0 { + // sValue.Sub(secp256k1N, sValue) + // copy(signature[32:64], sValue.Bytes()) + // } return signature } From 3f744b4c6319c0cb406a60a88d620823860f66a5 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Sat, 30 Dec 2023 20:07:09 +0200 Subject: [PATCH 65/87] fix: Comply with yellow paper V setting --- scripts/senduserop/main.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/senduserop/main.go b/scripts/senduserop/main.go index 92d61ebf..7f81d777 100644 --- a/scripts/senduserop/main.go +++ b/scripts/senduserop/main.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/goccy/go-json" + "github.com/pkg/errors" "github.com/spf13/viper" "github.com/stackup-wallet/stackup-bundler/pkg/signer" @@ -177,6 +178,8 @@ func getSignature(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey, ch panic(err) } + signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + // Normalize S value for Ethereum // sValue := big.NewInt(0).SetBytes(signature[32:64]) // secp256k1N := crypto.S256().Params().N @@ -189,14 +192,23 @@ func getSignature(userOp *userop.UserOperation, privateKey *ecdsa.PrivateKey, ch } func verifySignature(userOp *userop.UserOperation, publicKey *ecdsa.PublicKey, chainID *big.Int) bool { + if len(userOp.Signature) != 65 { + panic(errors.New("signature must be 65 bytes long")) + } + if userOp.Signature[64] != 27 && userOp.Signature[64] != 28 { + panic(errors.New("invalid Ethereum signature (V is not 27 or 28)")) + } + + signature := bytes.Clone(userOp.Signature) // Not in RSV format + + signature[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 + userOpHash := userOp.GetUserOpHash(common.HexToAddress(entrypointAddrV060), chainID).Bytes() prefixedHash := crypto.Keccak256Hash( []byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(userOpHash), userOpHash)), ) - signature := userOp.Signature // Already in RSV format - recoveredPubKey, err := crypto.SigToPub(prefixedHash.Bytes(), signature) if err != nil { fmt.Printf("Failed to recover public key: %v\n", err) From dd6994f9867411c4c36120a7d1e4fb703beae4dc Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 3 Jan 2024 18:09:46 +0200 Subject: [PATCH 66/87] feat: upgrade model to v0.9 to support intent chain-id --- go.mod | 18 +++++++++--------- go.sum | 37 ++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 9f30e9d8..07400aa8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.20 require ( - github.com/blndgs/model v0.8.0 + github.com/blndgs/model v0.9.0 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.11.5 @@ -35,7 +35,7 @@ require ( go.opentelemetry.io/otel/sdk/metric v0.39.0 go.opentelemetry.io/otel/trace v1.16.0 golang.org/x/sync v0.1.0 - golang.org/x/text v0.9.0 + golang.org/x/text v0.14.0 google.golang.org/grpc v1.55.0 ) @@ -48,7 +48,7 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect @@ -80,8 +80,8 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect @@ -97,11 +97,11 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index db39d8a8..4cad1119 100644 --- a/go.sum +++ b/go.sum @@ -47,10 +47,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/blndgs/model v0.7.0 h1:t2C1AbNSBIsTmgcdOtTL2RX6K71vkZBoccR78KFrWJc= -github.com/blndgs/model v0.7.0/go.mod h1:l7SqtUKcm7OkOBSfBWlSptqPXxNm/OnvizdWwBVtZlA= -github.com/blndgs/model v0.8.0 h1:e91B3kF0+f0e+LB63vcnYaJNsarcOhv9PEqETikF94Y= -github.com/blndgs/model v0.8.0/go.mod h1:l7SqtUKcm7OkOBSfBWlSptqPXxNm/OnvizdWwBVtZlA= +github.com/blndgs/model v0.9.0 h1:M7+aqb+PBXuDG4g6DISPwJMIYoSXjsvtImigN/xIPuY= +github.com/blndgs/model v0.9.0/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= @@ -96,8 +94,9 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= @@ -320,14 +319,15 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= @@ -389,7 +389,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= @@ -466,8 +465,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -535,8 +534,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -605,8 +604,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -616,8 +615,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -777,8 +776,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 7b8cbbddc39788b01ac91e386b7f78c17ce1c8a9 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 3 Jan 2024 18:10:33 +0200 Subject: [PATCH 67/87] feat: Set chain-id in Intent --- pkg/modules/solution/solveintents.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index fe965d4b..b6b8917c 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -65,6 +65,21 @@ func (ei *IntentsHandler) bufferIntentOps(entrypoint common.Address, chainID *bi } for idx, op := range userOpBatch { if op.HasIntent() { + intent, err := op.GetIntent() + if err != nil { + // invalid Intent: drop the UserOperation from the batch + continue + } + + intent.ChainID = chainID + + marshalledIntent, err := json.Marshal(intent) + if err != nil { + // corrupted intent: drop the UserOperation from the batch + continue + } + op.SetIntent(string(marshalledIntent)) + hashID := op.GetUserOpHash(entrypoint, chainID).String() // Don't mutate the original op From 62b475fd0ebee93fba0cad78b64d03d128a01d6d Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Tue, 23 Jan 2024 22:14:08 +0200 Subject: [PATCH 68/87] feat: Upgrade model dependency to Intent userOp spec --- go.mod | 2 +- go.sum | 4 ++-- pkg/userop/intent.go | 10 ++++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 07400aa8..732adbc4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.20 require ( - github.com/blndgs/model v0.9.0 + github.com/blndgs/model v0.14.0 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.11.5 diff --git a/go.sum b/go.sum index 4cad1119..67619817 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/blndgs/model v0.9.0 h1:M7+aqb+PBXuDG4g6DISPwJMIYoSXjsvtImigN/xIPuY= -github.com/blndgs/model v0.9.0/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= +github.com/blndgs/model v0.14.0 h1:t6jcFnN64l00sBKGDfd7H1C/UF4+vXf6DDDd51Pn8NY= +github.com/blndgs/model v0.14.0/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= diff --git a/pkg/userop/intent.go b/pkg/userop/intent.go index 28eb08e8..6c801098 100644 --- a/pkg/userop/intent.go +++ b/pkg/userop/intent.go @@ -12,12 +12,18 @@ func (op *UserOperation) HasIntent() bool { func (op *UserOperation) IsUnsolvedIntent() bool { modelUserOp := model.UserOperation(*op) + _, hasCalldataIntent := model.ExtractJSONFromField(string(modelUserOp.CallData)) - return modelUserOp.HasIntent() && !modelUserOp.HasEVMInstructions() + return hasCalldataIntent } func (op *UserOperation) IsIntentExecutable() bool { modelUserOp := model.UserOperation(*op) - return modelUserOp.HasIntent() && modelUserOp.HasEVMInstructions() + var hasSigIntent bool + if len(modelUserOp.Signature) > model.SignatureLength { + _, hasSigIntent = model.ExtractJSONFromField(string(modelUserOp.Signature[model.SignatureLength:])) + } + + return hasSigIntent } From 35680100284d9b96c658639c0bb6a46d2d8d0848 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Wed, 24 Jan 2024 18:15:26 +0200 Subject: [PATCH 69/87] refactor: Replace set with copy userOps solutions --- pkg/modules/solution/solveintents.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index b6b8917c..b1989d32 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -132,17 +132,11 @@ func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { // dropping further processing ctx.MarkOpIndexForRemoval(int(batchIndex)) case model.Solved: - intentSolution, err := body.UserOps[idx].GetEVMInstructions() - if err != nil { - // failed to retrieve the EVM solution for the solved Intent - // allow residing in the mempool for another solving attempt or till expired - unsolvedOpJson, _ := json.Marshal(body.UserOps[idx]) - return errors.Errorf("failed to get EVM instructions: %s for solved Intent at index %d, userOp: %s", err, batchIndex, unsolvedOpJson) - } - // set the solved userOp values to the received batch's userOp values - - modelUserOps[batchIndex].SetEVMInstructions(intentSolution) + ctx.Batch[batchIndex].CallData = make([]byte, len(body.UserOps[idx].CallData)) + copy(ctx.Batch[batchIndex].CallData, body.UserOps[idx].CallData) + ctx.Batch[batchIndex].Signature = make([]byte, len(body.UserOps[idx].Signature)) + copy(ctx.Batch[batchIndex].Signature, body.UserOps[idx].Signature) ctx.Batch[batchIndex].CallGasLimit = body.UserOps[idx].CallGasLimit ctx.Batch[batchIndex].VerificationGasLimit = body.UserOps[idx].VerificationGasLimit ctx.Batch[batchIndex].PreVerificationGas = body.UserOps[idx].PreVerificationGas From 2fa1d2e3a3b2d728d418d027a1dd14ff71e5fa44 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 25 Jan 2024 14:19:31 +0200 Subject: [PATCH 70/87] fix: Sync intent checks against latest model spec --- pkg/modules/relay/relayer.go | 2 +- pkg/userop/intent.go | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index 61db673b..f80c588c 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -70,7 +70,7 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { nonIntentsBatch := make([]*userop.UserOperation, 0, len(ctx.Batch)) intentsBatch := make([]*userop.UserOperation, 0, len(ctx.Batch)) for _, userOp := range ctx.Batch { - if userOp.IsIntentExecutable() { + if userOp.IsSolvedIntent() { // Solved Intent UserOperations intentsBatch = append(intentsBatch, userOp) diff --git a/pkg/userop/intent.go b/pkg/userop/intent.go index 6c801098..68f89c43 100644 --- a/pkg/userop/intent.go +++ b/pkg/userop/intent.go @@ -12,18 +12,22 @@ func (op *UserOperation) HasIntent() bool { func (op *UserOperation) IsUnsolvedIntent() bool { modelUserOp := model.UserOperation(*op) - _, hasCalldataIntent := model.ExtractJSONFromField(string(modelUserOp.CallData)) - return hasCalldataIntent + status, err := modelUserOp.Validate() + if err != nil || status != model.UnsolvedUserOp { + return false + } + + return true } -func (op *UserOperation) IsIntentExecutable() bool { +func (op *UserOperation) IsSolvedIntent() bool { modelUserOp := model.UserOperation(*op) - var hasSigIntent bool - if len(modelUserOp.Signature) > model.SignatureLength { - _, hasSigIntent = model.ExtractJSONFromField(string(modelUserOp.Signature[model.SignatureLength:])) + status, err := modelUserOp.Validate() + if err != nil || status != model.SolvedUserOp { + return false } - return hasSigIntent + return true } From ae1b2c4f309dac6bf454ecc53bf4b586f093a14c Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:20:36 +0200 Subject: [PATCH 71/87] feat: Process handleOps error --- pkg/modules/relay/relayer.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index f80c588c..fca5ed71 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -3,6 +3,7 @@ package relay import ( + "fmt" "math/big" "time" @@ -11,7 +12,9 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/go-logr/logr" + "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/reverts" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/transaction" + apperrors "github.com/stackup-wallet/stackup-bundler/pkg/errors" "github.com/stackup-wallet/stackup-bundler/pkg/modules" "github.com/stackup-wallet/stackup-bundler/pkg/signer" "github.com/stackup-wallet/stackup-bundler/pkg/userop" @@ -126,9 +129,22 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { println() println("--> handleOps") - if err := handleOps(ctx, opts); err != nil { - // swallow error - println(err.Error()) + err := handleOps(ctx, opts) + if err != nil { + res, rpcErr := reverts.NewExecutionResult(err) + if rpcErr != nil { + println("rpcErr:", rpcErr.Error()) + fo, foErr := reverts.NewFailedOp(err) + if foErr != nil { + println("foErr:", foErr.Error()) + if err != nil { + return err + } + return fmt.Errorf("%s, %s", rpcErr, foErr) + } + return apperrors.NewRPCError(apperrors.REJECTED_BY_EP_OR_ACCOUNT, fo.Reason, fo) + } + fmt.Printf("res: %+v\n", res) } } From 3efb3080328dc9197b6512532967b35164d8b65b Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:48:24 +0200 Subject: [PATCH 72/87] feat: Upgrade model to 16 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 732adbc4..33530c72 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.20 require ( - github.com/blndgs/model v0.14.0 + github.com/blndgs/model v0.16.0 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.11.5 diff --git a/go.sum b/go.sum index 67619817..09753466 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/blndgs/model v0.14.0 h1:t6jcFnN64l00sBKGDfd7H1C/UF4+vXf6DDDd51Pn8NY= -github.com/blndgs/model v0.14.0/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= +github.com/blndgs/model v0.16.0 h1:sI9mscMQE+puZ8xKJJYhWqt634MHmwKl5vn7Y4qMOOs= +github.com/blndgs/model v0.16.0/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= From e116aaa59eeb71aacda96e5bcb9896d767ffbeb6 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 5 Feb 2024 08:34:02 -0300 Subject: [PATCH 73/87] Correcting workflow to use correct variables. --- .github/workflows/deploy.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 642d0fd0..9c9b4c59 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,7 +22,7 @@ jobs: with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ secrets.AWS_REGION }} + aws-region: ${{ vars.AWS_REGION }} - name: Login to Amazon ECR id: login-ecr @@ -31,7 +31,7 @@ jobs: - name: Build, tag, and push image to Amazon ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: ${{ secrets.AWS_REPOSITORY }} + ECR_REPOSITORY: ${{ vars.AWS_REPOSITORY }} IMAGE_TAG: ${{ github.sha }} run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . @@ -46,18 +46,18 @@ jobs: uses: peterkimzz/aws-ssm-send-command@master id: deploy with: - aws-region: ${{ secrets.AWS_REGION }} + aws-region: ${{ vars.AWS_REGION }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} instance-ids: ${{ secrets.INSTANCE_ID }} working-directory: /root/stackup-bundler command: | - aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.ECR_URL }} - yq w -i docker-compose.yaml 'services.stackup-bundler.image' ${{ secrets.ECR_URL }}/${{ secrets.AWS_REPOSITORY }}:${{ github.sha }} - yq w -i docker-compose.yaml 'services.stackup-bundler.environment[0]' "ERC4337_BUNDLER_ETH_CLIENT_URL=${{ secrets.ERC4337_BUNDLER_ETH_CLIENT_URL }}" + aws ecr get-login-password --region ${{ vars.AWS_REGION }} | docker login --username AWS --password-stdin ${{ vars.ECR_URL }} + yq w -i docker-compose.yaml 'services.stackup-bundler.image' ${{ vars.ECR_URL }}/${{ vars.AWS_REPOSITORY }}:${{ github.sha }} + yq w -i docker-compose.yaml 'services.stackup-bundler.environment[0]' "ERC4337_BUNDLER_ETH_CLIENT_URL=${{ vars.ERC4337_BUNDLER_ETH_CLIENT_URL }}" yq w -i docker-compose.yaml 'services.stackup-bundler.environment[1]' "ERC4337_BUNDLER_PRIVATE_KEY=${{ secrets.ERC4337_BUNDLER_PRIVATE_KEY }}" - yq w -i docker-compose.yaml 'services.stackup-bundler.environment[2]' "ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT=${{ secrets.ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT }}" - yq w -i docker-compose.yaml 'services.stackup-bundler.environment[3]' "ERC4337_BUNDLER_DEBUG_MODE=${{ secrets.ERC4337_BUNDLER_DEBUG_MODE }}" + yq w -i docker-compose.yaml 'services.stackup-bundler.environment[2]' "ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT=${{ vars.ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT }}" + yq w -i docker-compose.yaml 'services.stackup-bundler.environment[3]' "ERC4337_BUNDLER_DEBUG_MODE=${{ vars.ERC4337_BUNDLER_DEBUG_MODE }}" docker-compose up -d # Catch SSM outputs From 06db4044d0a3242b614f6007b211f3f14220fa5e Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 15 Feb 2024 17:46:51 +0200 Subject: [PATCH 74/87] [ BDH-177 ] Upgrade to support Solver after the staking model upgrade (#10) * feat: Add Solver health check * fix: Remove unecessary intent validation * feat: Add additional console output * feat: Upgrade -> Model v0.18.0 --- go.mod | 2 +- go.sum | 4 +- internal/start/private.go | 4 ++ internal/start/searcher.go | 5 ++- pkg/modules/relay/relayer.go | 1 + pkg/modules/solution/solveintents.go | 57 ++++++++++++++++++++-------- 6 files changed, 54 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 33530c72..a20ff5ed 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/stackup-wallet/stackup-bundler go 1.20 require ( - github.com/blndgs/model v0.16.0 + github.com/blndgs/model v0.18.0 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.11.5 diff --git a/go.sum b/go.sum index 09753466..30e6215c 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/blndgs/model v0.16.0 h1:sI9mscMQE+puZ8xKJJYhWqt634MHmwKl5vn7Y4qMOOs= -github.com/blndgs/model v0.16.0/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= +github.com/blndgs/model v0.18.0 h1:XAuCs4EvChAzSinCQCFvGCLcg/0QKnR92ujvmH6FcY0= +github.com/blndgs/model v0.18.0/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= diff --git a/internal/start/private.go b/internal/start/private.go index 4fb25216..37d96dd9 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -127,7 +127,11 @@ func PrivateMode() { exp := expire.New(conf.MaxOpTTL) + println("solver URL:", conf.SolverUrl) solver := solution.New(conf.SolverUrl) + if err := solution.ReportSolverHealth(conf.SolverUrl); err != nil { + log.Fatal(err) + } relayer := relay.New(eoa, eth, chain, beneficiary, logr) diff --git a/internal/start/searcher.go b/internal/start/searcher.go index 549ed73c..ef289b5f 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -117,8 +117,11 @@ func SearcherMode() { exp := expire.New(conf.MaxOpTTL) - // Init Intents Solver + println("solver URL:", conf.SolverUrl) solver := solution.New(conf.SolverUrl) + if err := solution.ReportSolverHealth(conf.SolverUrl); err != nil { + log.Fatal(err) + } builder := builder.New(eoa, eth, fb, beneficiary, conf.BlocksInTheFuture) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index fca5ed71..1fb64613 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -142,6 +142,7 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { } return fmt.Errorf("%s, %s", rpcErr, foErr) } + fmt.Printf("RPC Error: %+v\n", fo) return apperrors.NewRPCError(apperrors.REJECTED_BY_EP_OR_ACCOUNT, fo.Reason, fo) } fmt.Printf("res: %+v\n", res) diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index b1989d32..c48b4ffa 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -16,8 +16,11 @@ package solution import ( "bytes" + "io" "math/big" "net/http" + "net/url" + "os" "time" "unsafe" @@ -65,21 +68,6 @@ func (ei *IntentsHandler) bufferIntentOps(entrypoint common.Address, chainID *bi } for idx, op := range userOpBatch { if op.HasIntent() { - intent, err := op.GetIntent() - if err != nil { - // invalid Intent: drop the UserOperation from the batch - continue - } - - intent.ChainID = chainID - - marshalledIntent, err := json.Marshal(intent) - if err != nil { - // corrupted intent: drop the UserOperation from the batch - continue - } - op.SetIntent(string(marshalledIntent)) - hashID := op.GetUserOpHash(entrypoint, chainID).String() // Don't mutate the original op @@ -127,10 +115,13 @@ func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { for idx, opExt := range body.UserOpsExt { batchIndex := batchIntentIndices[opHashID(body.UserOpsExt[idx].OriginalHashValue)] + // print to stdout the userOp and Intent JSON + println("Solver response, status:", opExt.ProcessingStatus, ", batchIndex:", batchIndex, ", hash:", body.UserOpsExt[idx].OriginalHashValue) switch opExt.ProcessingStatus { case model.Unsolved, model.Expired, model.Invalid, model.Received: // dropping further processing ctx.MarkOpIndexForRemoval(int(batchIndex)) + println("Solver dropping userOp: ", body.UserOps[idx].String()) case model.Solved: // set the solved userOp values to the received batch's userOp values ctx.Batch[batchIndex].CallData = make([]byte, len(body.UserOps[idx].CallData)) @@ -152,6 +143,42 @@ func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { } } +func ReportSolverHealth(solverURL string) error { + parsedURL, err := url.Parse(solverURL) + if err != nil { + println("Error parsing Solver URL: ", solverURL, ", ", err) + return err + } + + parsedURL.Path = "/health" + parsedURL.RawQuery = "" + parsedURL.Fragment = "" + + solverURL = parsedURL.String() + println("Requesting solver health at ", solverURL) + + handler := New(solverURL) + + req, err := http.NewRequest(http.MethodGet, solverURL, nil) + if err != nil { + return err + } + + resp, err := handler.SolverClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + println("Solver health response: ", resp.Status) + _, err = io.Copy(os.Stdout, resp.Body) + if err != nil { + return err + } + + return nil +} + // sendToSolver sends the batch of UserOperations to the Solver. func (ei *IntentsHandler) sendToSolver(body model.BodyOfUserOps) error { jsonBody, err := json.Marshal(body) From 998b809e567353dc69eec0de368a8d5ebc27cf39 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 16 Feb 2024 00:04:29 +0200 Subject: [PATCH 75/87] chore: Remove unused files (#11) --- int_tests/sendtx.sh | 7 -- int_tests/userop_gas_test.go | 154 ----------------------------------- 2 files changed, 161 deletions(-) delete mode 100755 int_tests/sendtx.sh delete mode 100644 int_tests/userop_gas_test.go diff --git a/int_tests/sendtx.sh b/int_tests/sendtx.sh deleted file mode 100755 index 183b9eab..00000000 --- a/int_tests/sendtx.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -x - -COINBASE=$(curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_coinbase","params":[],"id":1}' http://localhost:8545 | jq -r '.result') -curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from": "'$COINBASE'", "to":"0x0A7199a96fdf0252E09F76545c1eF2be3692F46b", "value": "0xde0b6b3a7640000"}],"id":1}' http://localhost:8545 -curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from": "'$COINBASE'", "to":"0x3068c2408c01bECde4BcCB9f246b56651BE1d12D", "value": "0xde0b6b3a7640000"}],"id":1}' http://localhost:8545 \ No newline at end of file diff --git a/int_tests/userop_gas_test.go b/int_tests/userop_gas_test.go deleted file mode 100644 index 3d923aef..00000000 --- a/int_tests/userop_gas_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package int_tests - -import ( - "bufio" - "context" - "crypto/ecdsa" - "io" - "log" - "net/http" - "os" - "os/exec" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/joho/godotenv" - "github.com/stretchr/testify/assert" -) - -const LocalGeth = "http://localhost:8545" - -var ( - ethclnt *ethclient.Client - signerKey *ecdsa.PrivateKey -) - -func TestMain(m *testing.M) { - // Check if Geth is in the user's path - _, err := exec.LookPath("geth") - if err != nil { - log.Fatal("Geth is not installed or not in the system path. Please install Geth and try again.") - } - - // Start Geth - gethCmd := exec.Command("geth", "--verbosity", "5", - "--http.vhosts", "'*,localhost,host.docker.internal'", - "--http", "--http.api", "eth,net,web3,debug", - "--http.corsdomain", "'*'", - "--http.addr", "0.0.0.0", - "--nodiscover", "--maxpeers", "0", "--mine", - "--networkid", "1337", - "--dev", - "--allow-insecure-unlock", - "--rpc.allow-unprotected-txs", - "--miner.gaslimit", "12000000") - - gethStdout, _ := gethCmd.StdoutPipe() - gethStderr, _ := gethCmd.StderrPipe() - if err := gethCmd.Start(); err != nil { - log.Fatal("Failed to start Geth:", err) - } - defer terminateGeth(gethCmd) - - setSignerKey() - - // Asynchronously log Geth output - go logOutput(gethStdout) - go logOutput(gethStderr) - - // Wait for Geth to be ready - waitForGeth() - - // Execute the sendtx.sh script - err = executeSendTxScript() - if err != nil { - log.Fatalf("Failed to execute sendtx.sh script: %v", err) - } - - ethclnt = connectToGeth() - defer ethclnt.Close() - - // Run tests - code := m.Run() - - os.Exit(code) -} - -func terminateGeth(gethCmd *exec.Cmd) { - if err := gethCmd.Process.Kill(); err != nil { - log.Println("Failed to kill Geth process:", err.Error()) - } -} - -func setSignerKey() { - if err := godotenv.Load(); err != nil { - log.Fatalf("Error loading .env file: %v", err) - } - - // Retrieve the prv value - var privateKeyStr string - if privateKeyStr = os.Getenv("PRIVATE_KEY"); privateKeyStr == "" { - log.Fatal("PRIVATE_KEY environment variable is not set") - } - // Convert PRIVATE_KEY to ECDSA private key - var err error - signerKey, err = crypto.HexToECDSA(privateKeyStr) - if err != nil { - log.Fatalf("Invalid PRIVATE_KEY: %v", err) - } -} - -func executeSendTxScript() error { - cmd := exec.Command("./sendtx.sh") - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} - -func logOutput(pipe io.ReadCloser) { - scanner := bufio.NewScanner(pipe) - for scanner.Scan() { - log.Println(scanner.Text()) - } -} - -func waitForGeth() { - ticker := time.NewTicker(time.Millisecond * 300) - defer ticker.Stop() - - timeout := time.After(30 * time.Second) - - for { - select { - case <-ticker.C: - resp, err := http.Get(LocalGeth) - if err == nil { - resp.Body.Close() - return - } - case <-timeout: - log.Fatal("Geth did not start in the expected time") - } - } -} - -func connectToGeth() *ethclient.Client { - var err error - ethclnt, err = ethclient.Dial(LocalGeth) - if err != nil { - panic("Failed to connect to Geth: " + err.Error()) - } - - return ethclnt -} - -func TestBalance(t *testing.T) { - addr := common.HexToAddress("0x0A7199a96fdf0252E09F76545c1eF2be3692F46b") - balance, err := ethclnt.BalanceAt(context.Background(), addr, nil) - assert.NoError(t, err, "Failed to retrieve balance") - assert.NotNil(t, balance, "Balance should not be nil") - assert.Equal(t, balance.String(), "1000000000000000000", "Balance should be greater than 0") -} From 96bdd98ab55406baaf24d1ad844239d37e8e6621 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 16 Feb 2024 08:16:29 -0300 Subject: [PATCH 76/87] Add solver URL. --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9c9b4c59..928112af 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -58,6 +58,7 @@ jobs: yq w -i docker-compose.yaml 'services.stackup-bundler.environment[1]' "ERC4337_BUNDLER_PRIVATE_KEY=${{ secrets.ERC4337_BUNDLER_PRIVATE_KEY }}" yq w -i docker-compose.yaml 'services.stackup-bundler.environment[2]' "ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT=${{ vars.ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT }}" yq w -i docker-compose.yaml 'services.stackup-bundler.environment[3]' "ERC4337_BUNDLER_DEBUG_MODE=${{ vars.ERC4337_BUNDLER_DEBUG_MODE }}" + yq w -i docker-compose.yaml 'services.stackup-bundler.environment[4]' "SOLVER_URL=${{ vars.SOLVER_URL }}" docker-compose up -d # Catch SSM outputs From 21c52b9c932fbed6ad909ccbbbb86fd713d9bc6d Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 16 Feb 2024 14:01:52 +0200 Subject: [PATCH 77/87] fix: Add missing env variable (#12) --- internal/config/values.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/config/values.go b/internal/config/values.go index 3f623cbc..d9fe3b83 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -130,6 +130,7 @@ func GetValues() *Values { _ = viper.BindEnv("erc4337_bundler_alt_mempool_ids") _ = viper.BindEnv("erc4337_bundler_debug_mode") _ = viper.BindEnv("erc4337_bundler_gin_mode") + _ = viper.BindEnv("solver_url") // Validate required variables if variableNotSetOrIsNil("erc4337_bundler_eth_client_url") { From 2928c35d70ec3e5faa8111ff06cea48a471c15d2 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 16 Feb 2024 14:43:26 +0200 Subject: [PATCH 78/87] Seeksolverurl (#13) * feat: panic if solver not set correctly * feat: better solver url default --- internal/config/values.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/config/values.go b/internal/config/values.go index d9fe3b83..1bf69563 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -94,7 +94,7 @@ func GetValues() *Values { viper.SetDefault("erc4337_bundler_otel_insecure_mode", false) viper.SetDefault("erc4337_bundler_debug_mode", false) viper.SetDefault("erc4337_bundler_gin_mode", gin.ReleaseMode) - viper.SetDefault("solver_url", "http://localhost:7322/solve") + viper.SetDefault("solver_url", "http://localhost:7322/solve-native") // Read in from .env file if available viper.SetConfigName(".env") @@ -168,6 +168,10 @@ func GetValues() *Values { panic("Fatal config error: erc4337_bundler_alt_mempool_ids is set without specifying an IPFS gateway") } + if variableNotSetOrIsNil("solver_url") && !strings.Contains(viper.GetString("solver_url"), "/solve") { + panic("Fatal config error: solver_url not set") + } + // Return Values privateKey := viper.GetString("erc4337_bundler_private_key") ethClientUrl := viper.GetString("erc4337_bundler_eth_client_url") From 149b464aa2de95183d1cede21dee87ff308b82d0 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 19 Feb 2024 13:46:39 -0300 Subject: [PATCH 79/87] Creating new stack-bundler instance. --- .github/workflows/deploy.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 928112af..51aa6b3e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -60,6 +60,24 @@ jobs: yq w -i docker-compose.yaml 'services.stackup-bundler.environment[3]' "ERC4337_BUNDLER_DEBUG_MODE=${{ vars.ERC4337_BUNDLER_DEBUG_MODE }}" yq w -i docker-compose.yaml 'services.stackup-bundler.environment[4]' "SOLVER_URL=${{ vars.SOLVER_URL }}" docker-compose up -d + - name: Deploy 2 + uses: peterkimzz/aws-ssm-send-command@master + id: deploy2 + with: + aws-region: ${{ vars.AWS_REGION }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + instance-ids: ${{ secrets.INSTANCE_ID }} + working-directory: /root/stackup-bundler + command: | + aws ecr get-login-password --region ${{ vars.AWS_REGION }} | docker login --username AWS --password-stdin ${{ vars.ECR_URL }} + yq w -i docker-compose.yaml 'services.stackup-bundler2.image' ${{ vars.ECR_URL }}/${{ vars.AWS_REPOSITORY }}:${{ github.sha }} + yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[0]' "ERC4337_BUNDLER_MAIN_CLIENT_URL=${{ vars.ERC4337_BUNDLER_MAIN_CLIENT_URL }}" + yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[1]' "ERC4337_BUNDLER_PRIVATE_KEY=${{ secrets.ERC4337_BUNDLER_PRIVATE_KEY }}" + yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[2]' "ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT=${{ vars.ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT }}" + yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[3]' "ERC4337_BUNDLER_DEBUG_MODE=${{ vars.ERC4337_BUNDLER_DEBUG_MODE }}" + yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[4]' "SOLVER_URL=${{ vars.SOLVER_URL }}" + docker-compose up -d # Catch SSM outputs - name: Get the outputs from Deploy image From c3bcfa3c8b03cfe2b892efc9e3123fe38f664b34 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 20 Feb 2024 08:59:24 -0300 Subject: [PATCH 80/87] Removing new stack-bundler instance. --- .github/workflows/deploy.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 51aa6b3e..928112af 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -60,24 +60,6 @@ jobs: yq w -i docker-compose.yaml 'services.stackup-bundler.environment[3]' "ERC4337_BUNDLER_DEBUG_MODE=${{ vars.ERC4337_BUNDLER_DEBUG_MODE }}" yq w -i docker-compose.yaml 'services.stackup-bundler.environment[4]' "SOLVER_URL=${{ vars.SOLVER_URL }}" docker-compose up -d - - name: Deploy 2 - uses: peterkimzz/aws-ssm-send-command@master - id: deploy2 - with: - aws-region: ${{ vars.AWS_REGION }} - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - instance-ids: ${{ secrets.INSTANCE_ID }} - working-directory: /root/stackup-bundler - command: | - aws ecr get-login-password --region ${{ vars.AWS_REGION }} | docker login --username AWS --password-stdin ${{ vars.ECR_URL }} - yq w -i docker-compose.yaml 'services.stackup-bundler2.image' ${{ vars.ECR_URL }}/${{ vars.AWS_REPOSITORY }}:${{ github.sha }} - yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[0]' "ERC4337_BUNDLER_MAIN_CLIENT_URL=${{ vars.ERC4337_BUNDLER_MAIN_CLIENT_URL }}" - yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[1]' "ERC4337_BUNDLER_PRIVATE_KEY=${{ secrets.ERC4337_BUNDLER_PRIVATE_KEY }}" - yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[2]' "ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT=${{ vars.ERC4337_BUNDLER_MAX_BATCH_GAS_LIMIT }}" - yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[3]' "ERC4337_BUNDLER_DEBUG_MODE=${{ vars.ERC4337_BUNDLER_DEBUG_MODE }}" - yq w -i docker-compose.yaml 'services.stackup-bundler2.environment[4]' "SOLVER_URL=${{ vars.SOLVER_URL }}" - docker-compose up -d # Catch SSM outputs - name: Get the outputs from Deploy image From b3c9e47f6095fda20c5e0d199d7b089d6c192afa Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Thu, 22 Feb 2024 00:14:57 +0200 Subject: [PATCH 81/87] feat!: Do not reprocess failed userOps (#14) * feat!: do not retry failed userOps * feat: streamline handleOps error printing --- pkg/modules/relay/relayer.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index 1fb64613..9bc0a261 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -14,7 +14,6 @@ import ( "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/reverts" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/transaction" - apperrors "github.com/stackup-wallet/stackup-bundler/pkg/errors" "github.com/stackup-wallet/stackup-bundler/pkg/modules" "github.com/stackup-wallet/stackup-bundler/pkg/signer" "github.com/stackup-wallet/stackup-bundler/pkg/userop" @@ -131,21 +130,19 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { err := handleOps(ctx, opts) if err != nil { - res, rpcErr := reverts.NewExecutionResult(err) - if rpcErr != nil { - println("rpcErr:", rpcErr.Error()) - fo, foErr := reverts.NewFailedOp(err) - if foErr != nil { - println("foErr:", foErr.Error()) - if err != nil { - return err - } - return fmt.Errorf("%s, %s", rpcErr, foErr) - } - fmt.Printf("RPC Error: %+v\n", fo) - return apperrors.NewRPCError(apperrors.REJECTED_BY_EP_OR_ACCOUNT, fo.Reason, fo) + println("error:", err.Error()) + fo, foErr := reverts.NewFailedOp(err) + if foErr != nil { + fmt.Printf("foErr:%+v\n", foErr) } - fmt.Printf("res: %+v\n", res) + if fo != nil { + println("EVM Reason:", fo.Reason) + } + println() + // Not sure if it's effective to return an error + // And keep recycling attempts to submit a likely + // invalid userOp. + // return apperrors.NewRPCError(apperrors.REJECTED_BY_EP_OR_ACCOUNT, fo.Reason, fo) } } From 1aba8a8eaa71bb9c25a0e74d12f9ff7c058bd891 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:37:43 +0200 Subject: [PATCH 82/87] feat: Print trx hash --- pkg/modules/relay/relayer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index 9bc0a261..dd6f9eec 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -155,6 +155,7 @@ func handleOps(ctx *modules.BatchHandlerCtx, opts transaction.Opts) error { return err } else { ctx.Data["txn_hash"] = txn.Hash().String() + fmt.Println("txn_hash:", txn.Hash().String()) } return nil From 8f67c5222c2b9bd83de638733da597baf1e0f86e Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:38:01 +0200 Subject: [PATCH 83/87] fix: Change solver url --- internal/config/values.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/config/values.go b/internal/config/values.go index 1bf69563..65ceef6b 100644 --- a/internal/config/values.go +++ b/internal/config/values.go @@ -94,7 +94,7 @@ func GetValues() *Values { viper.SetDefault("erc4337_bundler_otel_insecure_mode", false) viper.SetDefault("erc4337_bundler_debug_mode", false) viper.SetDefault("erc4337_bundler_gin_mode", gin.ReleaseMode) - viper.SetDefault("solver_url", "http://localhost:7322/solve-native") + viper.SetDefault("solver_url", "http://localhost:7322/solve") // Read in from .env file if available viper.SetConfigName(".env") From 117b515a48e3f2671313557fe0bc4a6a96bf87ff Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:23:01 +0200 Subject: [PATCH 84/87] Retry when Solver communication fails, printing enhancements (#15) * feat: printing enhancements, fixes * feat: Return an error if the solver communication fails --- pkg/modules/relay/relayer.go | 14 +++++++------- pkg/modules/solution/solveintents.go | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/modules/relay/relayer.go b/pkg/modules/relay/relayer.go index dd6f9eec..644f5b3f 100644 --- a/pkg/modules/relay/relayer.go +++ b/pkg/modules/relay/relayer.go @@ -119,26 +119,26 @@ func (r *Relayer) SendUserOperation() modules.BatchHandlerFunc { if len(intentsBatch) > 0 { opts := r.getCallOptions(ctx, intentsBatch) - println() + fmt.Println() for _, op := range intentsBatch { // cast to print it operation := model.UserOperation(*op) - println(operation.String()) + fmt.Println(operation.String()) } - println() - println("--> handleOps") + fmt.Println() + fmt.Println("--> handleOps") err := handleOps(ctx, opts) if err != nil { - println("error:", err.Error()) + fmt.Println("error:", err.Error()) fo, foErr := reverts.NewFailedOp(err) if foErr != nil { fmt.Printf("foErr:%+v\n", foErr) } if fo != nil { - println("EVM Reason:", fo.Reason) + fmt.Println("EVM Reason:", fo.Reason) } - println() + fmt.Printf("\n") // Not sure if it's effective to return an error // And keep recycling attempts to submit a likely // invalid userOp. diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index c48b4ffa..a7583068 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -16,6 +16,7 @@ package solution import ( "bytes" + "fmt" "io" "math/big" "net/http" @@ -107,10 +108,7 @@ func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { } if err := ei.sendToSolver(body); err != nil { - // swallow Solver connectivity error here to avoid - // an infinite loop of retries for the same batch till the - // batch expires or the solver is back online. - return nil + return err } for idx, opExt := range body.UserOpsExt { @@ -155,7 +153,7 @@ func ReportSolverHealth(solverURL string) error { parsedURL.Fragment = "" solverURL = parsedURL.String() - println("Requesting solver health at ", solverURL) + fmt.Println("Requesting solver health at ", solverURL) handler := New(solverURL) @@ -170,7 +168,7 @@ func ReportSolverHealth(solverURL string) error { } defer resp.Body.Close() - println("Solver health response: ", resp.Status) + fmt.Println("Solver health response: ", resp.Status) _, err = io.Copy(os.Stdout, resp.Body) if err != nil { return err @@ -195,6 +193,8 @@ func (ei *IntentsHandler) sendToSolver(body model.BodyOfUserOps) error { resp, err := ei.SolverClient.Do(req) if err != nil { + println("Solver request failed at URL: ", ei.SolverURL) + println("Solver error: ", err) return err } defer resp.Body.Close() From a8450cc5b1a2222e5c982e52f5f156f39e81c5c4 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:36:13 +0200 Subject: [PATCH 85/87] feat: solver drop print enhancement (#16) --- pkg/modules/solution/solveintents.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index a7583068..eb168245 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -114,12 +114,16 @@ func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { for idx, opExt := range body.UserOpsExt { batchIndex := batchIntentIndices[opHashID(body.UserOpsExt[idx].OriginalHashValue)] // print to stdout the userOp and Intent JSON - println("Solver response, status:", opExt.ProcessingStatus, ", batchIndex:", batchIndex, ", hash:", body.UserOpsExt[idx].OriginalHashValue) + fmt.Println("Solver response, status:", opExt.ProcessingStatus, ", batchIndex:", batchIndex, ", hash:", body.UserOpsExt[idx].OriginalHashValue) switch opExt.ProcessingStatus { case model.Unsolved, model.Expired, model.Invalid, model.Received: // dropping further processing ctx.MarkOpIndexForRemoval(int(batchIndex)) - println("Solver dropping userOp: ", body.UserOps[idx].String()) + println() + println("****************************************************") + println("Solver dropping userOp: ", body.UserOps[idx].String(), " with status: ", opExt.ProcessingStatus) + println("****************************************************") + println() case model.Solved: // set the solved userOp values to the received batch's userOp values ctx.Batch[batchIndex].CallData = make([]byte, len(body.UserOps[idx].CallData)) From 40793eef8d93b67e52b93b9c5b1f3e4674fb2067 Mon Sep 17 00:00:00 2001 From: Mario Karagiorgas <2580304+blewater@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:29:16 +0200 Subject: [PATCH 86/87] feat: eth_getUserOperationReceipt to report whether in mempool (#17) * feat: Report in mempool userOp status * feat: Log received userOps for solving * feat: Increase bundler verbosity --- internal/start/private.go | 3 ++- internal/start/searcher.go | 3 ++- pkg/client/client.go | 15 +++++++++++++++ pkg/mempool/instance.go | 18 ++++++++++++++++++ pkg/modules/solution/solveintents.go | 5 +++++ 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/internal/start/private.go b/internal/start/private.go index 37d96dd9..eab1ce28 100644 --- a/internal/start/private.go +++ b/internal/start/private.go @@ -39,7 +39,8 @@ func PrivateMode() { logr := logger.NewZeroLogr(). WithName("stackup_bundler"). - WithValues("bundler_mode", "private") + WithValues("bundler_mode", "private"). + V(1) eoa, err := signer.New(conf.PrivateKey) if err != nil { diff --git a/internal/start/searcher.go b/internal/start/searcher.go index ef289b5f..b9c8ae89 100644 --- a/internal/start/searcher.go +++ b/internal/start/searcher.go @@ -40,7 +40,8 @@ func SearcherMode() { logr := logger.NewZeroLogr(). WithName("stackup_bundler"). - WithValues("bundler_mode", "searcher") + WithValues("bundler_mode", "searcher"). + V(1) eoa, err := signer.New(conf.PrivateKey) if err != nil { diff --git a/pkg/client/client.go b/pkg/client/client.go index 7c2929f1..e0ac79da 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/go-logr/logr" + "github.com/stackup-wallet/stackup-bundler/internal/logger" "github.com/stackup-wallet/stackup-bundler/pkg/entrypoint/filter" "github.com/stackup-wallet/stackup-bundler/pkg/gas" @@ -237,6 +238,20 @@ func (i *Client) GetUserOperationReceipt( // Init logger l := i.logger.WithName("eth_getUserOperationReceipt").WithValues("userop_hash", hash) + pooled, err := i.mempool.HasUserOpHash(hash) + if err != nil { + l.Error(err, "mempool.HashUserOpHash error") + return nil, err + } + + if pooled { + // UserOperation is in mempool + l.Info("mempool.HasUserOpHash returned true: " + hash) + var r filter.UserOperationReceipt + r.Nonce = "-1" + return &r, nil + } + ev, err := i.getUserOpReceipt(hash, i.supportedEntryPoints[0]) if err != nil { l.Error(err, "eth_getUserOperationReceipt error") diff --git a/pkg/mempool/instance.go b/pkg/mempool/instance.go index 6ab308fe..f1d285ef 100644 --- a/pkg/mempool/instance.go +++ b/pkg/mempool/instance.go @@ -27,6 +27,24 @@ func New(db *badger.DB) (*Mempool, error) { return &Mempool{db, queue}, nil } +// HasUserOpHash returns true if the UserOperation with the given userOpHash is +// in the mempool. +func (m *Mempool) HasUserOpHash(userOpHash string) (bool, error) { + err := m.db.View(func(txn *badger.Txn) error { + _, err := txn.Get([]byte(userOpHash)) + return err + }) + + if err == badger.ErrKeyNotFound { + + return false, nil + } else if err != nil { + return false, err + } + + return true, nil +} + // GetOps returns all the UserOperations associated with an EntryPoint and Sender address. func (m *Mempool) GetOps(entryPoint common.Address, sender common.Address) ([]*userop.UserOperation, error) { ops := m.queue.GetOps(entryPoint, sender) diff --git a/pkg/modules/solution/solveintents.go b/pkg/modules/solution/solveintents.go index eb168245..e65544fd 100644 --- a/pkg/modules/solution/solveintents.go +++ b/pkg/modules/solution/solveintents.go @@ -99,6 +99,11 @@ func (ei *IntentsHandler) SolveIntents() modules.BatchHandlerFunc { // to be sent to the Solver modelUserOps := *(*[]*model.UserOperation)(unsafe.Pointer(&ctx.Batch)) + println("Received batch of UserOperations for solution: ", len(modelUserOps)) + for idx, op := range modelUserOps { + println("Received UserOperation: [", idx, "], isIntent", op.HasIntent(), "op:", op.String()) + } + // Prepare the body to send to the Solver body := ei.bufferIntentOps(ctx.EntryPoint, ctx.ChainID, batchIntentIndices, modelUserOps) From 129e4cf635ef8895daa4336c9f2e938a10344149 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Thu, 4 Apr 2024 17:17:02 +0100 Subject: [PATCH 87/87] make sure to not update geth (#18) --- go.mod | 50 ++++++++++++------------- go.sum | 115 +++++++++++++++++++++++++++++++-------------------------- 2 files changed, 86 insertions(+), 79 deletions(-) diff --git a/go.mod b/go.mod index a20ff5ed..30c4a6c9 100644 --- a/go.mod +++ b/go.mod @@ -3,18 +3,17 @@ module github.com/stackup-wallet/stackup-bundler go 1.20 require ( - github.com/blndgs/model v0.18.0 + github.com/blndgs/model v0.18.1 github.com/deckarep/golang-set/v2 v2.3.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/ethereum/go-ethereum v1.11.5 github.com/gin-contrib/cors v1.4.0 - github.com/gin-gonic/gin v1.9.0 + github.com/gin-gonic/gin v1.9.1 github.com/go-logr/logr v1.2.4 github.com/go-logr/zerologr v1.2.3 - github.com/go-playground/validator/v10 v10.12.0 + github.com/go-playground/validator/v10 v10.19.0 github.com/goccy/go-json v0.10.2 github.com/google/go-cmp v0.5.9 - github.com/joho/godotenv v1.5.1 github.com/metachris/flashbotsrpc v0.6.0 github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 @@ -23,7 +22,6 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.4 github.com/wangjia184/sortedset v0.0.0-20220209072355-af6d6d227aa7 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.42.0 go.opentelemetry.io/otel v1.16.0 @@ -40,22 +38,23 @@ require ( ) require ( - github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect - github.com/bytedance/sonic v1.9.1 // indirect + github.com/bytedance/sonic v1.11.3 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.1 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-stack/stack v1.8.1 // indirect @@ -63,45 +62,44 @@ require ( github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/holiman/uint256 v1.2.0 // indirect + github.com/holiman/uint256 v1.2.4 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.15 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pelletier/go-toml/v2 v2.2.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect - github.com/tklauser/go-sysconf v0.3.5 // indirect - github.com/tklauser/numcpus v0.2.2 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.39.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.16.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/arch v0.7.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 30e6215c..92746ab3 100644 --- a/go.sum +++ b/go.sum @@ -41,21 +41,22 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/blndgs/model v0.18.0 h1:XAuCs4EvChAzSinCQCFvGCLcg/0QKnR92ujvmH6FcY0= -github.com/blndgs/model v0.18.0/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= -github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/blndgs/model v0.18.1 h1:RwzM9/aAvlXrDYoShGatRPFD4fmzvEkdm2mJgVNXvtk= +github.com/blndgs/model v0.18.1/go.mod h1:g5Dvqjo9TY//2kutwPR8d8drdUZK2adZYQMM4PlAtgY= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= +github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -66,8 +67,12 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= +github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 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= @@ -96,13 +101,11 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= github.com/deckarep/golang-set/v2 v2.3.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.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= @@ -127,6 +130,8 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -135,8 +140,8 @@ github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURU github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -147,8 +152,9 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs= github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho= -github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-ole/go-ole v1.2.5/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-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= @@ -158,8 +164,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -205,8 +211,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= @@ -257,8 +263,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= -github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -267,8 +273,6 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -279,8 +283,9 @@ github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -292,8 +297,8 @@ 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/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -319,15 +324,14 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= +github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= @@ -379,6 +383,7 @@ github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobt github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -388,27 +393,27 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= -github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= github.com/wangjia184/sortedset v0.0.0-20220209072355-af6d6d227aa7 h1:/9VctXVXpt04S1G44mCHPJh7RuIH3YGP8bAI0dC4t1o= github.com/wangjia184/sortedset v0.0.0-20220209072355-af6d6d227aa7/go.mod h1:yHUVPw1qUPZmDuKhFMHPOI4WjziTH2Wp/GeNjBAycpM= @@ -454,8 +459,8 @@ go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJP go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= +golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -465,8 +470,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -534,8 +539,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -568,6 +573,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -591,7 +597,6 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -599,13 +604,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -776,8 +784,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -802,6 +810,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=