From aa2e42b7fe296e3efdfbed4ed8164537fe311f1e Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Mon, 20 May 2024 11:15:37 +0100 Subject: [PATCH 01/10] Fix personal transactions RPC request --- go/common/custom_query_types.go | 27 ++++++ go/common/gethencoding/geth_encoding.go | 19 +++- go/common/query_types.go | 5 - go/enclave/rpc/GetCustomQuery.go | 6 +- go/obsclient/authclient.go | 12 ++- .../tests/tenscan/tenscan_rpc_test.go | 61 ++++++++++++ .../networktest/userwallet/authclient.go | 6 ++ integration/networktest/userwallet/gateway.go | 15 +++ integration/networktest/userwallet/user.go | 2 + tools/walletextension/common/constants.go | 43 ++++----- .../walletextension/rpcapi/blockchain_api.go | 96 +++++++++++++------ 11 files changed, 226 insertions(+), 66 deletions(-) create mode 100644 go/common/custom_query_types.go diff --git a/go/common/custom_query_types.go b/go/common/custom_query_types.go new file mode 100644 index 0000000000..bf811e2023 --- /dev/null +++ b/go/common/custom_query_types.go @@ -0,0 +1,27 @@ +package common + +import "github.com/ethereum/go-ethereum/common" + +// CustomQueries are Ten-specific queries that are not supported by the Ethereum RPC API but that we wish to support +// through the same interface. +// +// We currently use the eth_getStorageAt method to route these queries through the Ethereum RPC API, since it will not +// be supported by the Ten network. +// +// A custom query has a name (string), an address (if private request) and a params field (generic json object). +// +// NOTE: Private custom queries must include "address" as a top-level field in the params json object. + +// CustomQuery methods +const ( + UserIDRequestMethodName = "getUserID" + ListPrivateTransactionsMethodName = "listPersonalTransactions" + + // DeprecatedUserIDRequestMethodName is still supported for backwards compatibility for User ID requests + DeprecatedUserIDRequestMethodName = "0x0000000000000000000000000000000000000000" +) + +type ListPrivateTransactionsQueryParams struct { + Address common.Address `json:"address"` + Pagination QueryPagination `json:"pagination"` +} diff --git a/go/common/gethencoding/geth_encoding.go b/go/common/gethencoding/geth_encoding.go index bac9685bfb..cd830eaebd 100644 --- a/go/common/gethencoding/geth_encoding.go +++ b/go/common/gethencoding/geth_encoding.go @@ -13,7 +13,6 @@ import ( "github.com/dgraph-io/ristretto" "github.com/eko/gocache/lib/v4/cache" ristretto_store "github.com/eko/gocache/store/ristretto/v4" - gethlog "github.com/ethereum/go-ethereum/log" "github.com/ten-protocol/go-ten/go/common/log" "github.com/ten-protocol/go-ten/go/enclave/storage" @@ -361,14 +360,26 @@ func (enc *gethEncodingServiceImpl) CreateEthBlockFromBatch(ctx context.Context, return (*types.Block)(unsafe.Pointer(&lb)), nil } -func ExtractPrivateCustomQuery(_ interface{}, query interface{}) (*common.PrivateCustomQueryListTransactions, error) { +// ExtractPrivateCustomQuery is designed to support a wide range of custom Ten queries. +// The first parameter here is the method name, which is used to determine the query type. +// The second parameter is the query parameters. +func ExtractPrivateCustomQuery(methodName interface{}, queryParams interface{}) (*common.ListPrivateTransactionsQueryParams, error) { + methodNameStr, ok := methodName.(string) + if !ok { + return nil, fmt.Errorf("unsupported method type %T", methodName) + } + // currently we only have to support this custom query method in the enclave + if methodNameStr != common.ListPrivateTransactionsMethodName { + return nil, fmt.Errorf("unsupported method %s", methodName) + } + // Convert the map to a JSON string - jsonData, err := json.Marshal(query) + jsonData, err := json.Marshal(queryParams) if err != nil { return nil, err } - var result common.PrivateCustomQueryListTransactions + var result common.ListPrivateTransactionsQueryParams err = json.Unmarshal(jsonData, &result) if err != nil { return nil, err diff --git a/go/common/query_types.go b/go/common/query_types.go index eb6ed1f89c..7098bdb96e 100644 --- a/go/common/query_types.go +++ b/go/common/query_types.go @@ -111,11 +111,6 @@ func (p *QueryPagination) UnmarshalJSON(data []byte) error { return nil } -type PrivateCustomQueryListTransactions struct { - Address common.Address `json:"address"` - Pagination QueryPagination `json:"pagination"` -} - type ObscuroNetworkInfo struct { ManagementContractAddress common.Address L1StartHash common.Hash diff --git a/go/enclave/rpc/GetCustomQuery.go b/go/enclave/rpc/GetCustomQuery.go index 148a50526e..bfce9dbebe 100644 --- a/go/enclave/rpc/GetCustomQuery.go +++ b/go/enclave/rpc/GetCustomQuery.go @@ -7,10 +7,10 @@ import ( "github.com/ten-protocol/go-ten/go/common/gethencoding" ) -func GetCustomQueryValidate(reqParams []any, builder *CallBuilder[common.PrivateCustomQueryListTransactions, common.PrivateQueryResponse], _ *EncryptionManager) error { +func GetCustomQueryValidate(reqParams []any, builder *CallBuilder[common.ListPrivateTransactionsQueryParams, common.PrivateQueryResponse], _ *EncryptionManager) error { // Parameters are [PrivateCustomQueryHeader, PrivateCustomQueryArgs, null] if len(reqParams) != 3 { - builder.Err = fmt.Errorf("unexpected number of parameters") + builder.Err = fmt.Errorf("unexpected number of parameters (expected %d, got %d)", 3, len(reqParams)) return nil } @@ -24,7 +24,7 @@ func GetCustomQueryValidate(reqParams []any, builder *CallBuilder[common.Private return nil } -func GetCustomQueryExecute(builder *CallBuilder[common.PrivateCustomQueryListTransactions, common.PrivateQueryResponse], rpc *EncryptionManager) error { +func GetCustomQueryExecute(builder *CallBuilder[common.ListPrivateTransactionsQueryParams, common.PrivateQueryResponse], rpc *EncryptionManager) error { err := authenticateFrom(builder.VK, builder.From) if err != nil { builder.Err = err diff --git a/go/obsclient/authclient.go b/go/obsclient/authclient.go index 6d66d63751..f56d7994d5 100644 --- a/go/obsclient/authclient.go +++ b/go/obsclient/authclient.go @@ -251,12 +251,16 @@ func (ac *AuthObsClient) EstimateGasAndGasPrice(txData types.TxData) types.TxDat // GetReceiptsByAddress retrieves the receipts for the account registered on this client (due to obscuro privacy restrictions, // balance cannot be requested for other accounts) -func (ac *AuthObsClient) GetReceiptsByAddress(ctx context.Context, address *gethcommon.Address) (types.Receipts, error) { - var result types.Receipts - err := ac.rpcClient.CallContext(ctx, &result, rpc.GetStorageAt, address, nil, nil) +func (ac *AuthObsClient) GetReceiptsByAddress(ctx context.Context, address *gethcommon.Address, pagination common.QueryPagination) (types.Receipts, error) { + queryParam := &common.ListPrivateTransactionsQueryParams{ + Address: *address, + Pagination: pagination, + } + var result common.PrivateQueryResponse + err := ac.rpcClient.CallContext(ctx, &result, rpc.GetStorageAt, "listPersonalTransactions", queryParam, nil) if err != nil { return nil, err } - return result, nil + return result.Receipts, nil } diff --git a/integration/networktest/tests/tenscan/tenscan_rpc_test.go b/integration/networktest/tests/tenscan/tenscan_rpc_test.go index fb4b4835c4..3885f6d7ff 100644 --- a/integration/networktest/tests/tenscan/tenscan_rpc_test.go +++ b/integration/networktest/tests/tenscan/tenscan_rpc_test.go @@ -1,14 +1,21 @@ package tenscan import ( + "context" + "fmt" + "math/big" "testing" + "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/integration/networktest" "github.com/ten-protocol/go-ten/integration/networktest/actions" "github.com/ten-protocol/go-ten/integration/networktest/actions/publicdata" "github.com/ten-protocol/go-ten/integration/networktest/env" + "github.com/ten-protocol/go-ten/integration/simulation/devnetwork" ) +var _transferAmount = big.NewInt(100_000_000) + // Verify and debug the RPC endpoints that Tenscan relies on for data in various environments func TestRPC(t *testing.T) { @@ -22,3 +29,57 @@ func TestRPC(t *testing.T) { ), ) } + +// Test the personal transactions endpoint in various environments (it uses getStorageAt so it can run through MM etc.) +// 1. create user +// 2. send some transactions +// 3. verify transactions are returned by the personal transactions endpoint that tenscan uses +func TestPersonalTransactions(t *testing.T) { + networktest.TestOnlyRunsInIDE(t) + networktest.Run( + "tenscan-personal-transactions", + t, + env.LocalDevNetwork(devnetwork.WithGateway()), + actions.Series( + // create 3 users + &actions.CreateTestUser{UserID: 0, UseGateway: true}, // <-- this user makes the PersonalTransactions request, choose gateway or not here + &actions.CreateTestUser{UserID: 1}, + &actions.CreateTestUser{UserID: 2}, + actions.SetContextValue(actions.KeyNumberOfTestUsers, 3), + + &actions.AllocateFaucetFunds{UserID: 0}, + actions.SnapshotUserBalances(actions.SnapAfterAllocation), // record user balances (we have no guarantee on how much the network faucet allocates) + + // user 0 sends funds to users 1 and 2 + &actions.SendNativeFunds{FromUser: 0, ToUser: 1, Amount: _transferAmount}, + &actions.SendNativeFunds{FromUser: 0, ToUser: 2, Amount: _transferAmount}, + + // after the test we will verify the other users received them + &actions.VerifyBalanceAfterTest{UserID: 1, ExpectedBalance: _transferAmount}, + &actions.VerifyBalanceAfterTest{UserID: 2, ExpectedBalance: _transferAmount}, + + // verify the personal transactions endpoint returns the two txs + actions.VerifyOnlyAction(func(ctx context.Context, network networktest.NetworkConnector) error { + user, err := actions.FetchTestUser(ctx, 0) + if err != nil { + return err + } + + pagination := common.QueryPagination{ + Offset: 0, + Size: 20, + } + personalTxs, err := user.GetPersonalTransactions(ctx, pagination) + if err != nil { + return fmt.Errorf("unable to get personal transactions - %w", err) + } + + // verify the transactions + if len(personalTxs) != 2 { + return fmt.Errorf("expected 2 transactions, got %d", len(personalTxs)) + } + return nil + }), + ), + ) +} diff --git a/integration/networktest/userwallet/authclient.go b/integration/networktest/userwallet/authclient.go index fba2bd7e88..ca4bc58feb 100644 --- a/integration/networktest/userwallet/authclient.go +++ b/integration/networktest/userwallet/authclient.go @@ -8,6 +8,7 @@ import ( "time" "github.com/ethereum/go-ethereum" + "github.com/ten-protocol/go-ten/go/common" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -130,3 +131,8 @@ func (s *AuthClientUser) Init(ctx context.Context) (*AuthClientUser, error) { func (s *AuthClientUser) Wallet() wallet.Wallet { return s.wal } + +func (s *AuthClientUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, error) { + address := s.wal.Address() + return s.client.GetReceiptsByAddress(ctx, &address, pagination) +} diff --git a/integration/networktest/userwallet/gateway.go b/integration/networktest/userwallet/gateway.go index 9408c4c06c..bb81543852 100644 --- a/integration/networktest/userwallet/gateway.go +++ b/integration/networktest/userwallet/gateway.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" gethlog "github.com/ethereum/go-ethereum/log" + "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/retry" "github.com/ten-protocol/go-ten/go/wallet" "github.com/ten-protocol/go-ten/tools/walletextension/lib" @@ -110,6 +111,20 @@ func (g *GatewayUser) NativeBalance(ctx context.Context) (*big.Int, error) { return g.client.BalanceAt(ctx, g.wal.Address(), nil) } +func (g *GatewayUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, error) { + rpcClient := g.client.Client() + queryParams := &common.ListPrivateTransactionsQueryParams{ + Address: g.wal.Address(), + Pagination: pagination, + } + var result common.PrivateQueryResponse + err := rpcClient.CallContext(ctx, &result, "eth_getStorageAt", "listPersonalTransactions", queryParams, nil) + if err != nil { + return nil, fmt.Errorf("rpc call failed - %w", err) + } + return result.Receipts, nil +} + func (g *GatewayUser) Wallet() wallet.Wallet { return g.wal } diff --git a/integration/networktest/userwallet/user.go b/integration/networktest/userwallet/user.go index 105e2cd8e6..33b9048e6c 100644 --- a/integration/networktest/userwallet/user.go +++ b/integration/networktest/userwallet/user.go @@ -6,6 +6,7 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/wallet" ) @@ -19,4 +20,5 @@ type User interface { SendFunds(ctx context.Context, addr gethcommon.Address, value *big.Int) (*gethcommon.Hash, error) AwaitReceipt(ctx context.Context, txHash *gethcommon.Hash) (*types.Receipt, error) NativeBalance(ctx context.Context) (*big.Int, error) + GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, error) } diff --git a/tools/walletextension/common/constants.go b/tools/walletextension/common/constants.go index bf14a59f5f..3fdb4d63a2 100644 --- a/tools/walletextension/common/constants.go +++ b/tools/walletextension/common/constants.go @@ -15,26 +15,25 @@ const ( ) const ( - PathStatic = "/static/" - PathReady = "/ready/" - PathJoin = "/join/" - PathGetMessage = "/getmessage/" - PathAuthenticate = "/authenticate/" - PathQuery = "/query/" - PathRevoke = "/revoke/" - PathHealth = "/health/" - PathNetworkHealth = "/network-health/" - WSProtocol = "ws://" - HTTPProtocol = "http://" - EncryptedTokenQueryParameter = "token" - AddressQueryParameter = "a" - MessageUserIDLen = 40 - MessageUserIDLenWithPrefix = 42 - EthereumAddressLen = 42 - GetStorageAtUserIDRequestMethodName = "0x0000000000000000000000000000000000000000" - SuccessMsg = "success" - APIVersion1 = "/v1" - PathVersion = "/version/" - DeduplicationBufferSize = 20 - DefaultGatewayAuthMessageType = "EIP712" + PathStatic = "/static/" + PathReady = "/ready/" + PathJoin = "/join/" + PathGetMessage = "/getmessage/" + PathAuthenticate = "/authenticate/" + PathQuery = "/query/" + PathRevoke = "/revoke/" + PathHealth = "/health/" + PathNetworkHealth = "/network-health/" + WSProtocol = "ws://" + HTTPProtocol = "http://" + EncryptedTokenQueryParameter = "token" + AddressQueryParameter = "a" + MessageUserIDLen = 40 + MessageUserIDLenWithPrefix = 42 + EthereumAddressLen = 42 + SuccessMsg = "success" + APIVersion1 = "/v1" + PathVersion = "/version/" + DeduplicationBufferSize = 20 + DefaultGatewayAuthMessageType = "EIP712" ) diff --git a/tools/walletextension/rpcapi/blockchain_api.go b/tools/walletextension/rpcapi/blockchain_api.go index 7993231108..d5afcc0dac 100644 --- a/tools/walletextension/rpcapi/blockchain_api.go +++ b/tools/walletextension/rpcapi/blockchain_api.go @@ -3,14 +3,15 @@ package rpcapi import ( "context" "encoding/json" + "fmt" "github.com/ethereum/go-ethereum/core/types" + "github.com/ten-protocol/go-ten/go/common" - "github.com/ethereum/go-ethereum/common" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ten-protocol/go-ten/go/common/gethapi" "github.com/ten-protocol/go-ten/lib/gethfork/rpc" - wecommon "github.com/ten-protocol/go-ten/tools/walletextension/common" ) type BlockChainAPI struct { @@ -34,7 +35,7 @@ func (api *BlockChainAPI) BlockNumber() hexutil.Uint64 { return *nr } -func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { +func (api *BlockChainAPI) GetBalance(ctx context.Context, address gethcommon.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { return ExecAuthRPC[hexutil.Big]( ctx, api.we, @@ -55,13 +56,13 @@ func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address // Result structs for GetProof type AccountResult struct { - Address common.Address `json:"address"` - AccountProof []string `json:"accountProof"` - Balance *hexutil.Big `json:"balance"` - CodeHash common.Hash `json:"codeHash"` - Nonce hexutil.Uint64 `json:"nonce"` - StorageHash common.Hash `json:"storageHash"` - StorageProof []StorageResult `json:"storageProof"` + Address gethcommon.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *hexutil.Big `json:"balance"` + CodeHash gethcommon.Hash `json:"codeHash"` + Nonce hexutil.Uint64 `json:"nonce"` + StorageHash gethcommon.Hash `json:"storageHash"` + StorageProof []StorageResult `json:"storageProof"` } type StorageResult struct { @@ -70,7 +71,7 @@ type StorageResult struct { Proof []string `json:"proof"` } -func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { +func (s *BlockChainAPI) GetProof(ctx context.Context, address gethcommon.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { return nil, rpcNotImplemented } @@ -84,7 +85,7 @@ func (api *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.Bloc return *resp, err } -func (api *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) map[string]interface{} { +func (api *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash gethcommon.Hash) map[string]interface{} { resp, _ := UnauthenticatedTenRPCCall[map[string]interface{}](ctx, api.we, &CacheCfg{CacheType: LongLiving}, "eth_getHeaderByHash", hash) if resp == nil { return nil @@ -107,7 +108,7 @@ func (api *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.Block return *resp, err } -func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) { +func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash gethcommon.Hash, fullTx bool) (map[string]interface{}, error) { resp, err := UnauthenticatedTenRPCCall[map[string]interface{}](ctx, api.we, &CacheCfg{CacheType: LongLiving}, "eth_getBlockByHash", hash, fullTx) if resp == nil { return nil, err @@ -115,7 +116,7 @@ func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, return *resp, err } -func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { +func (api *BlockChainAPI) GetCode(ctx context.Context, address gethcommon.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { // todo - must be authenticated resp, err := UnauthenticatedTenRPCCall[hexutil.Bytes]( ctx, @@ -135,9 +136,18 @@ func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, b return *resp, err } -func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { +// GetStorageAt is not compatible with ETH RPC tooling. Ten network will never support getStorageAt because it would +// violate the privacy guarantees of the network. +// +// However, we can repurpose this method to be able to route Ten-specific requests through from an ETH RPC client. +// We call these requests Custom Queries. +// +// If this method is called using the standard ETH API parameters it will error, the correct params for this method are: +// [ customMethodName string, customMethodParams any, nil ] +// the final nil is to support the same number of params that getStorageAt sends, it is unused. +func (api *BlockChainAPI) GetStorageAt(ctx context.Context, customMethod string, customParams any, _ any) (hexutil.Bytes, error) { // GetStorageAt is repurposed to return the userID - if address.Hex() == wecommon.GetStorageAtUserIDRequestMethodName { + if customMethod == common.UserIDRequestMethodName { userID, err := extractUserID(ctx, api.we) if err != nil { return nil, err @@ -150,7 +160,9 @@ func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Addre return userID, nil } - resp, err := ExecAuthRPC[hexutil.Bytes](ctx, api.we, &ExecCfg{account: &address}, "eth_getStorageAt", address, hexKey, blockNrOrHash) + // sensitive CustomQuery methods use the convention of having "address" at the top level of the params json + address, err := extractCustomQueryAddress(customParams) + resp, err := ExecAuthRPC[hexutil.Bytes](ctx, api.we, &ExecCfg{account: address}, "eth_getStorageAt", customMethod, customParams, nil) if resp == nil { return nil, err } @@ -162,21 +174,21 @@ func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc. } type OverrideAccount struct { - Nonce *hexutil.Uint64 `json:"nonce"` - Code *hexutil.Bytes `json:"code"` - Balance **hexutil.Big `json:"balance"` - State *map[common.Hash]common.Hash `json:"state"` - StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance **hexutil.Big `json:"balance"` + State *map[gethcommon.Hash]gethcommon.Hash `json:"state"` + StateDiff *map[gethcommon.Hash]gethcommon.Hash `json:"stateDiff"` } type ( - StateOverride map[common.Address]OverrideAccount + StateOverride map[gethcommon.Address]OverrideAccount BlockOverrides struct { Number *hexutil.Big Difficulty *hexutil.Big Time *hexutil.Uint64 GasLimit *hexutil.Uint64 - Coinbase *common.Address - Random *common.Hash + Coinbase *gethcommon.Address + Random *gethcommon.Hash BaseFee *hexutil.Big } ) @@ -188,7 +200,7 @@ func (api *BlockChainAPI) Call(ctx context.Context, args gethapi.TransactionArgs return cacheBlockNumberOrHash(blockNrOrHash) }, }, - computeFromCallback: func(user *GWUser) *common.Address { + computeFromCallback: func(user *GWUser) *gethcommon.Address { return searchFromAndData(user.GetAllAddresses(), args) }, adjustArgs: func(acct *GWAccount) []any { @@ -213,7 +225,7 @@ func (api *BlockChainAPI) EstimateGas(ctx context.Context, args gethapi.Transact return LatestBatch }, }, - computeFromCallback: func(user *GWUser) *common.Address { + computeFromCallback: func(user *GWUser) *gethcommon.Address { return searchFromAndData(user.GetAllAddresses(), args) }, adjustArgs: func(acct *GWAccount) []any { @@ -233,7 +245,7 @@ func populateFrom(acct *GWAccount, args gethapi.TransactionArgs) gethapi.Transac // clone the args argsClone := cloneArgs(args) // set the from - if args.From == nil || args.From.Hex() == (common.Address{}).Hex() { + if args.From == nil || args.From.Hex() == (gethcommon.Address{}).Hex() { argsClone.From = acct.address } return argsClone @@ -258,3 +270,31 @@ type accessListResult struct { func (s *BlockChainAPI) CreateAccessList(ctx context.Context, args gethapi.TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) { return nil, rpcNotImplemented } + +func extractCustomQueryAddress(params any) (*gethcommon.Address, error) { + // sensitive CustomQuery methods use the convention of having "address" at the top level of the params json + // we don't care about the params struct overall, just want to extract the address string field + paramsStr, ok := params.(string) + if !ok { + return nil, fmt.Errorf("params must be a json string") + } + var paramsJSON map[string]json.RawMessage + err := json.Unmarshal([]byte(paramsStr), ¶msJSON) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } + // Extract the RawMessage for the key "address" + addressRaw, ok := paramsJSON["address"] + if !ok { + return nil, fmt.Errorf("params must contain an 'address' field") + } + + // Unmarshal the RawMessage to a string + var addressStr string + err = json.Unmarshal(addressRaw, &addressStr) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal address field to string: %w", err) + } + address := gethcommon.HexToAddress(addressStr) + return &address, nil +} From 29e5ec28185326cc1ef6d778121228a5c10a75b0 Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Mon, 20 May 2024 11:29:54 +0100 Subject: [PATCH 02/10] fix test --- go/common/custom_query_types.go | 8 ++++---- go/common/gethencoding/geth_encoding.go | 2 +- integration/obscurogateway/tengateway_test.go | 4 ++-- tools/walletextension/rpcapi/blockchain_api.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go/common/custom_query_types.go b/go/common/custom_query_types.go index bf811e2023..46735b739b 100644 --- a/go/common/custom_query_types.go +++ b/go/common/custom_query_types.go @@ -14,11 +14,11 @@ import "github.com/ethereum/go-ethereum/common" // CustomQuery methods const ( - UserIDRequestMethodName = "getUserID" - ListPrivateTransactionsMethodName = "listPersonalTransactions" + UserIDRequestCQMethod = "getUserID" + ListPrivateTransactionsCQMethod = "listPersonalTransactions" - // DeprecatedUserIDRequestMethodName is still supported for backwards compatibility for User ID requests - DeprecatedUserIDRequestMethodName = "0x0000000000000000000000000000000000000000" + // DeprecatedUserIDRequestCQMethod is still supported for backwards compatibility for User ID requests + DeprecatedUserIDRequestCQMethod = "0x0000000000000000000000000000000000000000" ) type ListPrivateTransactionsQueryParams struct { diff --git a/go/common/gethencoding/geth_encoding.go b/go/common/gethencoding/geth_encoding.go index cd830eaebd..e60404130a 100644 --- a/go/common/gethencoding/geth_encoding.go +++ b/go/common/gethencoding/geth_encoding.go @@ -369,7 +369,7 @@ func ExtractPrivateCustomQuery(methodName interface{}, queryParams interface{}) return nil, fmt.Errorf("unsupported method type %T", methodName) } // currently we only have to support this custom query method in the enclave - if methodNameStr != common.ListPrivateTransactionsMethodName { + if methodNameStr != common.ListPrivateTransactionsCQMethod { return nil, fmt.Errorf("unsupported method %s", methodName) } diff --git a/integration/obscurogateway/tengateway_test.go b/integration/obscurogateway/tengateway_test.go index 56f35b8a53..a8652e34e7 100644 --- a/integration/obscurogateway/tengateway_test.go +++ b/integration/obscurogateway/tengateway_test.go @@ -723,7 +723,7 @@ func testGetStorageAtForReturningUserID(t *testing.T, httpURL, wsURL string, w w var response JSONResponse // make a request to GetStorageAt with correct parameters to get userID that exists in the database - respBody := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, user.tgClient.UserID(), []interface{}{wecommon.GetStorageAtUserIDRequestMethodName, "0", nil}) + respBody := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, user.tgClient.UserID(), []interface{}{common.UserIDRequestCQMethod, "0", nil}) if err = json.Unmarshal(respBody, &response); err != nil { t.Error("Unable to unmarshal response") } @@ -732,7 +732,7 @@ func testGetStorageAtForReturningUserID(t *testing.T, httpURL, wsURL string, w w } // make a request to GetStorageAt with correct parameters to get userID, but with wrong userID - respBody2 := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, "0x0000000000000000000000000000000000000001", []interface{}{wecommon.GetStorageAtUserIDRequestMethodName, "0", nil}) + respBody2 := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, "0x0000000000000000000000000000000000000001", []interface{}{common.UserIDRequestCQMethod, "0", nil}) if !strings.Contains(string(respBody2), "not found") { t.Error("eth_getStorageAt did not respond with not found error") } diff --git a/tools/walletextension/rpcapi/blockchain_api.go b/tools/walletextension/rpcapi/blockchain_api.go index d5afcc0dac..a38e491ac4 100644 --- a/tools/walletextension/rpcapi/blockchain_api.go +++ b/tools/walletextension/rpcapi/blockchain_api.go @@ -147,7 +147,7 @@ func (api *BlockChainAPI) GetCode(ctx context.Context, address gethcommon.Addres // the final nil is to support the same number of params that getStorageAt sends, it is unused. func (api *BlockChainAPI) GetStorageAt(ctx context.Context, customMethod string, customParams any, _ any) (hexutil.Bytes, error) { // GetStorageAt is repurposed to return the userID - if customMethod == common.UserIDRequestMethodName { + if customMethod == common.UserIDRequestCQMethod { userID, err := extractUserID(ctx, api.we) if err != nil { return nil, err From 09306f7917db8d8de2df6ebba2ab20ed3b4c5b1d Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Mon, 20 May 2024 11:36:00 +0100 Subject: [PATCH 03/10] lint fix --- tools/walletextension/rpcapi/blockchain_api.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/walletextension/rpcapi/blockchain_api.go b/tools/walletextension/rpcapi/blockchain_api.go index a38e491ac4..efd0de9e40 100644 --- a/tools/walletextension/rpcapi/blockchain_api.go +++ b/tools/walletextension/rpcapi/blockchain_api.go @@ -162,6 +162,9 @@ func (api *BlockChainAPI) GetStorageAt(ctx context.Context, customMethod string, // sensitive CustomQuery methods use the convention of having "address" at the top level of the params json address, err := extractCustomQueryAddress(customParams) + if err != nil { + return nil, fmt.Errorf("unable to extract address from custom query params: %w", err) + } resp, err := ExecAuthRPC[hexutil.Bytes](ctx, api.we, &ExecCfg{account: address}, "eth_getStorageAt", customMethod, customParams, nil) if resp == nil { return nil, err From b34d03225ae4e134d825722908a56d5372f98870 Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Thu, 23 May 2024 13:01:20 +0100 Subject: [PATCH 04/10] Rework to comply strictly with getStorageAt signature --- go/common/custom_query_types.go | 20 +++--- go/common/gethencoding/geth_encoding.go | 34 ++++++---- go/common/query_types.go | 2 +- go/common/rpc/generated/enclave.pb.go | 4 +- go/common/rpc/generated/enclave_grpc.pb.go | 6 +- go/enclave/enclave.go | 2 +- go/enclave/rpc/GetCustomQuery.go | 16 ++--- go/obsclient/authclient.go | 14 +++-- go/rpc/encrypted_client.go | 6 ++ .../networktest/userwallet/authclient.go | 2 +- integration/networktest/userwallet/gateway.go | 15 ++++- tools/tenscan/frontend/src/routes/index.ts | 5 ++ .../src/services/useTransactionsService.ts | 6 +- .../walletextension/rpcapi/blockchain_api.go | 62 ++++++++++++------- 14 files changed, 127 insertions(+), 67 deletions(-) diff --git a/go/common/custom_query_types.go b/go/common/custom_query_types.go index 46735b739b..e3e483e63b 100644 --- a/go/common/custom_query_types.go +++ b/go/common/custom_query_types.go @@ -5,20 +5,22 @@ import "github.com/ethereum/go-ethereum/common" // CustomQueries are Ten-specific queries that are not supported by the Ethereum RPC API but that we wish to support // through the same interface. // -// We currently use the eth_getStorageAt method to route these queries through the Ethereum RPC API, since it will not -// be supported by the Ten network. +// We currently use the eth_getStorageAt method to route these queries through the Ethereum RPC API. // -// A custom query has a name (string), an address (if private request) and a params field (generic json object). +// In order to match the eth_getStorageAt method signature, we require that all custom queries use an incrementing "address" +// to specify the method we are calling (e.g. 0x000...001 is getUserID, 0x000...002 is listPrivateTransactions). // -// NOTE: Private custom queries must include "address" as a top-level field in the params json object. +// The signature is: eth_getStorageAt(method, params, nil) where: +// - method is the address of the custom query as an address (e.g. 0x000...001) +// - params is a JSON string with the parameters for the query (this complies with the eth_getStorageAt method signature since position gets encoded as a hex string) +// +// NOTE: Private custom queries must also include "address" as a top-level field in the params json object to indicate +// the account the query is being made for. // CustomQuery methods const ( - UserIDRequestCQMethod = "getUserID" - ListPrivateTransactionsCQMethod = "listPersonalTransactions" - - // DeprecatedUserIDRequestCQMethod is still supported for backwards compatibility for User ID requests - DeprecatedUserIDRequestCQMethod = "0x0000000000000000000000000000000000000000" + UserIDRequestCQMethod = "0x0000000000000000000000000000000000000001" + ListPrivateTransactionsCQMethod = "0x0000000000000000000000000000000000000002" ) type ListPrivateTransactionsQueryParams struct { diff --git a/go/common/gethencoding/geth_encoding.go b/go/common/gethencoding/geth_encoding.go index e60404130a..6b4a3312c6 100644 --- a/go/common/gethencoding/geth_encoding.go +++ b/go/common/gethencoding/geth_encoding.go @@ -2,6 +2,7 @@ package gethencoding import ( "context" + "encoding/base64" "encoding/json" "fmt" "math/big" @@ -363,27 +364,38 @@ func (enc *gethEncodingServiceImpl) CreateEthBlockFromBatch(ctx context.Context, // ExtractPrivateCustomQuery is designed to support a wide range of custom Ten queries. // The first parameter here is the method name, which is used to determine the query type. // The second parameter is the query parameters. -func ExtractPrivateCustomQuery(methodName interface{}, queryParams interface{}) (*common.ListPrivateTransactionsQueryParams, error) { +func ExtractPrivateCustomQuery(methodName any, queryParams any) (*common.ListPrivateTransactionsQueryParams, error) { + // we expect the first parameter to be a string methodNameStr, ok := methodName.(string) if !ok { - return nil, fmt.Errorf("unsupported method type %T", methodName) + return nil, fmt.Errorf("expected methodName as string but was type %T", methodName) } // currently we only have to support this custom query method in the enclave if methodNameStr != common.ListPrivateTransactionsCQMethod { - return nil, fmt.Errorf("unsupported method %s", methodName) + return nil, fmt.Errorf("unsupported method %s", methodNameStr) } - // Convert the map to a JSON string - jsonData, err := json.Marshal(queryParams) - if err != nil { - return nil, err + // we expect second param to be a json string + queryParamsStr, ok := queryParams.(string) + if !ok { + return nil, fmt.Errorf("expected queryParams as string but was type %T", queryParams) } - var result common.ListPrivateTransactionsQueryParams - err = json.Unmarshal(jsonData, &result) + var privateQueryParams common.ListPrivateTransactionsQueryParams + err := json.Unmarshal([]byte(queryParamsStr), &privateQueryParams) if err != nil { - return nil, err + // if it fails, check if the string was base64 encoded + bytesStr, err64 := base64.StdEncoding.DecodeString(queryParamsStr) + if err64 != nil { + // was not base64 encoded, give up + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } + // was base64 encoded, try to unmarshal + err = json.Unmarshal(bytesStr, &privateQueryParams) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } } - return &result, nil + return &privateQueryParams, nil } diff --git a/go/common/query_types.go b/go/common/query_types.go index 7098bdb96e..6fe6b07b03 100644 --- a/go/common/query_types.go +++ b/go/common/query_types.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" ) -type PrivateQueryResponse struct { +type PrivateTransactionsQueryResponse struct { Receipts types.Receipts Total uint64 } diff --git a/go/common/rpc/generated/enclave.pb.go b/go/common/rpc/generated/enclave.pb.go index 6fa2bb7841..721c3db445 100644 --- a/go/common/rpc/generated/enclave.pb.go +++ b/go/common/rpc/generated/enclave.pb.go @@ -5087,7 +5087,7 @@ var file_enclave_proto_depIdxs = []int32{ 10, // 69: generated.EnclaveProto.StreamL2Updates:input_type -> generated.StreamL2UpdatesRequest 16, // 70: generated.EnclaveProto.DebugEventLogRelevancy:input_type -> generated.DebugEventLogRelevancyRequest 14, // 71: generated.EnclaveProto.GetTotalContractCount:input_type -> generated.GetTotalContractCountRequest - 2, // 72: generated.EnclaveProto.GetReceiptsByAddress:input_type -> generated.GetReceiptsByAddressRequest + 2, // 72: generated.EnclaveProto.GetPrivateTransactions:input_type -> generated.GetReceiptsByAddressRequest 0, // 73: generated.EnclaveProto.EnclavePublicConfig:input_type -> generated.EnclavePublicConfigRequest 27, // 74: generated.EnclaveProto.Status:output_type -> generated.StatusResponse 29, // 75: generated.EnclaveProto.Attestation:output_type -> generated.AttestationResponse @@ -5119,7 +5119,7 @@ var file_enclave_proto_depIdxs = []int32{ 11, // 101: generated.EnclaveProto.StreamL2Updates:output_type -> generated.EncodedUpdateResponse 17, // 102: generated.EnclaveProto.DebugEventLogRelevancy:output_type -> generated.DebugEventLogRelevancyResponse 15, // 103: generated.EnclaveProto.GetTotalContractCount:output_type -> generated.GetTotalContractCountResponse - 3, // 104: generated.EnclaveProto.GetReceiptsByAddress:output_type -> generated.GetReceiptsByAddressResponse + 3, // 104: generated.EnclaveProto.GetPrivateTransactions:output_type -> generated.GetReceiptsByAddressResponse 1, // 105: generated.EnclaveProto.EnclavePublicConfig:output_type -> generated.EnclavePublicConfigResponse 74, // [74:106] is the sub-list for method output_type 42, // [42:74] is the sub-list for method input_type diff --git a/go/common/rpc/generated/enclave_grpc.pb.go b/go/common/rpc/generated/enclave_grpc.pb.go index d1fd02ebc0..5dba3f31ee 100644 --- a/go/common/rpc/generated/enclave_grpc.pb.go +++ b/go/common/rpc/generated/enclave_grpc.pb.go @@ -49,7 +49,7 @@ const ( EnclaveProto_StreamL2Updates_FullMethodName = "/generated.EnclaveProto/StreamL2Updates" EnclaveProto_DebugEventLogRelevancy_FullMethodName = "/generated.EnclaveProto/DebugEventLogRelevancy" EnclaveProto_GetTotalContractCount_FullMethodName = "/generated.EnclaveProto/GetTotalContractCount" - EnclaveProto_GetReceiptsByAddress_FullMethodName = "/generated.EnclaveProto/GetReceiptsByAddress" + EnclaveProto_GetReceiptsByAddress_FullMethodName = "/generated.EnclaveProto/GetPrivateTransactions" EnclaveProto_EnclavePublicConfig_FullMethodName = "/generated.EnclaveProto/EnclavePublicConfig" ) @@ -606,7 +606,7 @@ func (UnimplementedEnclaveProtoServer) GetTotalContractCount(context.Context, *G return nil, status.Errorf(codes.Unimplemented, "method GetTotalContractCount not implemented") } func (UnimplementedEnclaveProtoServer) GetReceiptsByAddress(context.Context, *GetReceiptsByAddressRequest) (*GetReceiptsByAddressResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetReceiptsByAddress not implemented") + return nil, status.Errorf(codes.Unimplemented, "method GetPrivateTransactions not implemented") } func (UnimplementedEnclaveProtoServer) EnclavePublicConfig(context.Context, *EnclavePublicConfigRequest) (*EnclavePublicConfigResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method EnclavePublicConfig not implemented") @@ -1327,7 +1327,7 @@ var EnclaveProto_ServiceDesc = grpc.ServiceDesc{ Handler: _EnclaveProto_GetTotalContractCount_Handler, }, { - MethodName: "GetReceiptsByAddress", + MethodName: "GetPrivateTransactions", Handler: _EnclaveProto_GetReceiptsByAddress_Handler, }, { diff --git a/go/enclave/enclave.go b/go/enclave/enclave.go index a12e7a7028..0ca5372bdd 100644 --- a/go/enclave/enclave.go +++ b/go/enclave/enclave.go @@ -857,7 +857,7 @@ func (e *enclaveImpl) GetTotalContractCount(ctx context.Context) (*big.Int, comm func (e *enclaveImpl) GetCustomQuery(ctx context.Context, encryptedParams common.EncryptedParamsGetStorageAt) (*responses.PrivateQueryResponse, common.SystemError) { // ensure the enclave is running if e.stopControl.IsStopping() { - return nil, responses.ToInternalError(fmt.Errorf("requested GetReceiptsByAddress with the enclave stopping")) + return nil, responses.ToInternalError(fmt.Errorf("requested GetPrivateTransactions with the enclave stopping")) } return rpc.WithVKEncryption(ctx, e.rpcEncryptionManager, encryptedParams, rpc.GetCustomQueryValidate, rpc.GetCustomQueryExecute) diff --git a/go/enclave/rpc/GetCustomQuery.go b/go/enclave/rpc/GetCustomQuery.go index bfce9dbebe..6dd9d10100 100644 --- a/go/enclave/rpc/GetCustomQuery.go +++ b/go/enclave/rpc/GetCustomQuery.go @@ -3,11 +3,12 @@ package rpc import ( "fmt" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/gethencoding" ) -func GetCustomQueryValidate(reqParams []any, builder *CallBuilder[common.ListPrivateTransactionsQueryParams, common.PrivateQueryResponse], _ *EncryptionManager) error { +func GetCustomQueryValidate(reqParams []any, builder *CallBuilder[common.ListPrivateTransactionsQueryParams, common.PrivateTransactionsQueryResponse], _ *EncryptionManager) error { // Parameters are [PrivateCustomQueryHeader, PrivateCustomQueryArgs, null] if len(reqParams) != 3 { builder.Err = fmt.Errorf("unexpected number of parameters (expected %d, got %d)", 3, len(reqParams)) @@ -19,29 +20,30 @@ func GetCustomQueryValidate(reqParams []any, builder *CallBuilder[common.ListPri builder.Err = fmt.Errorf("unable to extract query - %w", err) return nil } - builder.From = &privateCustomQuery.Address + addr := privateCustomQuery.Address + builder.From = &addr builder.Param = privateCustomQuery return nil } -func GetCustomQueryExecute(builder *CallBuilder[common.ListPrivateTransactionsQueryParams, common.PrivateQueryResponse], rpc *EncryptionManager) error { +func GetCustomQueryExecute(builder *CallBuilder[common.ListPrivateTransactionsQueryParams, common.PrivateTransactionsQueryResponse], rpc *EncryptionManager) error { err := authenticateFrom(builder.VK, builder.From) if err != nil { builder.Err = err return nil //nolint:nilerr } - - encryptReceipts, err := rpc.storage.GetTransactionsPerAddress(builder.ctx, &builder.Param.Address, &builder.Param.Pagination) + addr := gethcommon.Address(builder.Param.Address) + encryptReceipts, err := rpc.storage.GetTransactionsPerAddress(builder.ctx, &addr, &builder.Param.Pagination) if err != nil { return fmt.Errorf("GetTransactionsPerAddress - %w", err) } - receiptsCount, err := rpc.storage.CountTransactionsPerAddress(builder.ctx, &builder.Param.Address) + receiptsCount, err := rpc.storage.CountTransactionsPerAddress(builder.ctx, &addr) if err != nil { return fmt.Errorf("CountTransactionsPerAddress - %w", err) } - builder.ReturnValue = &common.PrivateQueryResponse{ + builder.ReturnValue = &common.PrivateTransactionsQueryResponse{ Receipts: encryptReceipts, Total: receiptsCount, } diff --git a/go/obsclient/authclient.go b/go/obsclient/authclient.go index f56d7994d5..eeba52980a 100644 --- a/go/obsclient/authclient.go +++ b/go/obsclient/authclient.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum" @@ -249,15 +250,18 @@ func (ac *AuthObsClient) EstimateGasAndGasPrice(txData types.TxData) types.TxDat } } -// GetReceiptsByAddress retrieves the receipts for the account registered on this client (due to obscuro privacy restrictions, -// balance cannot be requested for other accounts) -func (ac *AuthObsClient) GetReceiptsByAddress(ctx context.Context, address *gethcommon.Address, pagination common.QueryPagination) (types.Receipts, error) { +// GetPrivateTransactions retrieves the receipts for the specified account (must be registered on this client) +func (ac *AuthObsClient) GetPrivateTransactions(ctx context.Context, address *gethcommon.Address, pagination common.QueryPagination) (types.Receipts, error) { queryParam := &common.ListPrivateTransactionsQueryParams{ Address: *address, Pagination: pagination, } - var result common.PrivateQueryResponse - err := ac.rpcClient.CallContext(ctx, &result, rpc.GetStorageAt, "listPersonalTransactions", queryParam, nil) + queryParamStr, err := json.Marshal(queryParam) + if err != nil { + return nil, fmt.Errorf("unable to marshal query params - %w", err) + } + var result common.PrivateTransactionsQueryResponse + err = ac.rpcClient.CallContext(ctx, &result, rpc.GetStorageAt, common.ListPrivateTransactionsCQMethod, string(queryParamStr), nil) if err != nil { return nil, err } diff --git a/go/rpc/encrypted_client.go b/go/rpc/encrypted_client.go index 764890a857..b4ef8655b6 100644 --- a/go/rpc/encrypted_client.go +++ b/go/rpc/encrypted_client.go @@ -166,6 +166,12 @@ func (c *EncRPCClient) executeSensitiveCall(ctx context.Context, result interfac // and never error. resultBytes, _ := decodedResult.MarshalJSON() + // if expected result type is bytes, we return the bytes + if _, ok := result.(*[]byte); ok { + *result.(*[]byte) = resultBytes + return nil + } + // We put the raw json in the passed result object. // This works for structs, strings, integers and interface types. err = json.Unmarshal(resultBytes, result) diff --git a/integration/networktest/userwallet/authclient.go b/integration/networktest/userwallet/authclient.go index ca4bc58feb..76ccadaed5 100644 --- a/integration/networktest/userwallet/authclient.go +++ b/integration/networktest/userwallet/authclient.go @@ -134,5 +134,5 @@ func (s *AuthClientUser) Wallet() wallet.Wallet { func (s *AuthClientUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, error) { address := s.wal.Address() - return s.client.GetReceiptsByAddress(ctx, &address, pagination) + return s.client.GetPrivateTransactions(ctx, &address, pagination) } diff --git a/integration/networktest/userwallet/gateway.go b/integration/networktest/userwallet/gateway.go index bb81543852..44232c5fbb 100644 --- a/integration/networktest/userwallet/gateway.go +++ b/integration/networktest/userwallet/gateway.go @@ -2,6 +2,7 @@ package userwallet import ( "context" + "encoding/json" "errors" "fmt" "math/big" @@ -9,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum" gethcommon "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" gethlog "github.com/ethereum/go-ethereum/log" @@ -117,11 +119,20 @@ func (g *GatewayUser) GetPersonalTransactions(ctx context.Context, pagination co Address: g.wal.Address(), Pagination: pagination, } - var result common.PrivateQueryResponse - err := rpcClient.CallContext(ctx, &result, "eth_getStorageAt", "listPersonalTransactions", queryParams, nil) + queryParamsStr, err := json.Marshal(queryParams) + if err != nil { + return nil, fmt.Errorf("unable to marshal query params - %w", err) + } + var resultBytes hexutil.Bytes + err = rpcClient.CallContext(ctx, &resultBytes, "eth_getStorageAt", common.ListPrivateTransactionsCQMethod, queryParamsStr, nil) if err != nil { return nil, fmt.Errorf("rpc call failed - %w", err) } + var result common.PrivateTransactionsQueryResponse + err = json.Unmarshal(resultBytes, &result) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal result - %w", err) + } return result.Receipts, nil } diff --git a/tools/tenscan/frontend/src/routes/index.ts b/tools/tenscan/frontend/src/routes/index.ts index b0516e895a..f41e673e56 100644 --- a/tools/tenscan/frontend/src/routes/index.ts +++ b/tools/tenscan/frontend/src/routes/index.ts @@ -31,6 +31,11 @@ export const apiRoutes = { export const ethMethods = { getStorageAt: "eth_getStorageAt", }; +// to send TEN Custom Queries (CQ) through the provider we call eth_getStorageAt and use these addresses to identify the TEN CQ method +export const tenCustomQueryMethods = { + getUserID: "0x0000000000000000000000000000000000000001", + listPersonalTransactions: "0x0000000000000000000000000000000000000002", +}; export const NavLinks: NavLink[] = [ { diff --git a/tools/tenscan/frontend/src/services/useTransactionsService.ts b/tools/tenscan/frontend/src/services/useTransactionsService.ts index f51f7d7f79..9418dd06de 100644 --- a/tools/tenscan/frontend/src/services/useTransactionsService.ts +++ b/tools/tenscan/frontend/src/services/useTransactionsService.ts @@ -15,7 +15,7 @@ import { PersonalTransactionsResponse } from "../types/interfaces/TransactionInt import { useRouter } from "next/router"; import { showToast } from "../components/ui/use-toast"; import { ToastType } from "../types/interfaces"; -import { ethMethods } from "../routes"; +import {ethMethods, tenCustomQueryMethods} from "../routes"; export const useTransactionsService = () => { const { query } = useRouter(); @@ -62,8 +62,8 @@ export const useTransactionsService = () => { }, }; const personalTxData = await provider.send(ethMethods.getStorageAt, [ - "listPersonalTransactions", - requestPayload, + tenCustomQueryMethods.listPersonalTransactions, + JSON.stringify(requestPayload), null, ]); setPersonalTxns(personalTxData); diff --git a/tools/walletextension/rpcapi/blockchain_api.go b/tools/walletextension/rpcapi/blockchain_api.go index efd0de9e40..89aef5044f 100644 --- a/tools/walletextension/rpcapi/blockchain_api.go +++ b/tools/walletextension/rpcapi/blockchain_api.go @@ -2,14 +2,14 @@ package rpcapi import ( "context" + "encoding/base64" "encoding/json" "fmt" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ten-protocol/go-ten/go/common" - gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/gethapi" "github.com/ten-protocol/go-ten/lib/gethfork/rpc" ) @@ -136,18 +136,19 @@ func (api *BlockChainAPI) GetCode(ctx context.Context, address gethcommon.Addres return *resp, err } -// GetStorageAt is not compatible with ETH RPC tooling. Ten network will never support getStorageAt because it would +// GetStorageAt is not compatible with ETH RPC tooling. Ten network does not getStorageAt because it would // violate the privacy guarantees of the network. // // However, we can repurpose this method to be able to route Ten-specific requests through from an ETH RPC client. // We call these requests Custom Queries. // -// If this method is called using the standard ETH API parameters it will error, the correct params for this method are: -// [ customMethodName string, customMethodParams any, nil ] -// the final nil is to support the same number of params that getStorageAt sends, it is unused. -func (api *BlockChainAPI) GetStorageAt(ctx context.Context, customMethod string, customParams any, _ any) (hexutil.Bytes, error) { - // GetStorageAt is repurposed to return the userID - if customMethod == common.UserIDRequestCQMethod { +// This method signature matches eth_getStorageAt, but we use the address field to specify the custom query method, +// the hex-encoded position field to specify the parameters json, and nil for the block number. +// +// In future, we can support both CustomQueries and some debug version of eth_getStorageAt if needed. +func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address gethcommon.Address, params string, _ rpc.BlockNumberOrHash) (hexutil.Bytes, error) { + switch address.Hex() { + case common.UserIDRequestCQMethod: userID, err := extractUserID(ctx, api.we) if err != nil { return nil, err @@ -158,18 +159,25 @@ func (api *BlockChainAPI) GetStorageAt(ctx context.Context, customMethod string, return nil, err } return userID, nil + case common.ListPrivateTransactionsCQMethod: + // sensitive CustomQuery methods use the convention of having "address" at the top level of the params json + userAddr, err := extractCustomQueryAddress(params) + if err != nil { + return nil, fmt.Errorf("unable to extract address from custom query params: %w", err) + } + resp, err := ExecAuthRPC[any](ctx, api.we, &ExecCfg{account: userAddr}, "eth_getStorageAt", address.Hex(), params, nil) + if err != nil { + return nil, fmt.Errorf("unable to execute custom query: %w", err) + } + // turn resp object into hexutil.Bytes + serialised, err := json.Marshal(resp) + if err != nil { + return nil, fmt.Errorf("unable to marshal response object: %w", err) + } + return serialised, nil + default: // address was not a recognised custom query method address + return nil, fmt.Errorf("eth_getStorageAt is not supported on TEN") } - - // sensitive CustomQuery methods use the convention of having "address" at the top level of the params json - address, err := extractCustomQueryAddress(customParams) - if err != nil { - return nil, fmt.Errorf("unable to extract address from custom query params: %w", err) - } - resp, err := ExecAuthRPC[hexutil.Bytes](ctx, api.we, &ExecCfg{account: address}, "eth_getStorageAt", customMethod, customParams, nil) - if resp == nil { - return nil, err - } - return *resp, err } func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { @@ -284,7 +292,17 @@ func extractCustomQueryAddress(params any) (*gethcommon.Address, error) { var paramsJSON map[string]json.RawMessage err := json.Unmarshal([]byte(paramsStr), ¶msJSON) if err != nil { - return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + // try to base64 decode the params string and then unmarshal before giving up + bytesStr, err64 := base64.StdEncoding.DecodeString(paramsStr) + if err64 != nil { + // was not base64 encoded, give up + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } + // was base64 encoded, try to unmarshal + err = json.Unmarshal(bytesStr, ¶msJSON) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal params string: %w", err) + } } // Extract the RawMessage for the key "address" addressRaw, ok := paramsJSON["address"] From 8e04401c9b913833760b849001c4a950e73a6c00 Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Fri, 24 May 2024 07:46:48 +0100 Subject: [PATCH 05/10] lint --- go/enclave/rpc/GetCustomQuery.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/go/enclave/rpc/GetCustomQuery.go b/go/enclave/rpc/GetCustomQuery.go index 6dd9d10100..e804a24120 100644 --- a/go/enclave/rpc/GetCustomQuery.go +++ b/go/enclave/rpc/GetCustomQuery.go @@ -3,7 +3,6 @@ package rpc import ( "fmt" - gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ten-protocol/go-ten/go/common" "github.com/ten-protocol/go-ten/go/common/gethencoding" ) @@ -32,7 +31,7 @@ func GetCustomQueryExecute(builder *CallBuilder[common.ListPrivateTransactionsQu builder.Err = err return nil //nolint:nilerr } - addr := gethcommon.Address(builder.Param.Address) + addr := builder.Param.Address encryptReceipts, err := rpc.storage.GetTransactionsPerAddress(builder.ctx, &addr, &builder.Param.Pagination) if err != nil { return fmt.Errorf("GetTransactionsPerAddress - %w", err) From 48db22e554f3ba7f97db634f277a3007e8df8ebd Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Fri, 24 May 2024 08:25:04 +0100 Subject: [PATCH 06/10] fix test --- integration/obscurogateway/tengateway_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/integration/obscurogateway/tengateway_test.go b/integration/obscurogateway/tengateway_test.go index a8652e34e7..72f25c9b56 100644 --- a/integration/obscurogateway/tengateway_test.go +++ b/integration/obscurogateway/tengateway_test.go @@ -738,9 +738,10 @@ func testGetStorageAtForReturningUserID(t *testing.T, httpURL, wsURL string, w w } // make a request to GetStorageAt with wrong parameters to get userID, but correct userID - respBody3 := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, user.tgClient.UserID(), []interface{}{"0x0000000000000000000000000000000000000001", "0", nil}) - if !strings.Contains(string(respBody3), "illegal access") { - t.Error("eth_getStorageAt did not respond with error: illegal access") + respBody3 := makeHTTPEthJSONReq(httpURL, tenrpc.GetStorageAt, user.tgClient.UserID(), []interface{}{"0x0000000000000000000000000000000000000007", "0", nil}) + expectedErr := "not supported" + if !strings.Contains(string(respBody3), expectedErr) { + t.Errorf("eth_getStorageAt did not respond with error: %s, it was: %s", expectedErr, string(respBody3)) } } From 136054739b7c22a130f84d1088b8d18b49ef0dcc Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Fri, 21 Jun 2024 15:23:03 +0200 Subject: [PATCH 07/10] Fix gateway user id req --- tools/walletextension/frontend/src/lib/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/walletextension/frontend/src/lib/constants.ts b/tools/walletextension/frontend/src/lib/constants.ts index bd8814dba9..de81e9447d 100644 --- a/tools/walletextension/frontend/src/lib/constants.ts +++ b/tools/walletextension/frontend/src/lib/constants.ts @@ -43,7 +43,7 @@ export const tenChainIDDecimal = 443; export const tenChainIDHex = "0x" + tenChainIDDecimal.toString(16); // Convert to hexadecimal and prefix with '0x' export const METAMASK_CONNECTION_TIMEOUT = 3000; -export const userStorageAddress = "0x0000000000000000000000000000000000000000"; +export const userStorageAddress = "0x0000000000000000000000000000000000000001"; export const nativeCurrency = { name: "Sepolia Ether", From e92de2092cae48e93a33308b7198c78a550c66bc Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Fri, 28 Jun 2024 10:48:52 +0100 Subject: [PATCH 08/10] fix personal tx sql queries --- go/enclave/storage/enclavedb/batch.go | 7 +++++-- go/obsclient/authclient.go | 10 +++++----- .../networktest/tests/tenscan/tenscan_rpc_test.go | 7 ++++++- integration/networktest/userwallet/authclient.go | 2 +- integration/networktest/userwallet/gateway.go | 10 +++++----- integration/networktest/userwallet/user.go | 2 +- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/go/enclave/storage/enclavedb/batch.go b/go/enclave/storage/enclavedb/batch.go index 005cb69f73..a3ed53c042 100644 --- a/go/enclave/storage/enclavedb/batch.go +++ b/go/enclave/storage/enclavedb/batch.go @@ -428,11 +428,14 @@ func BatchWasExecuted(ctx context.Context, db *sql.DB, hash common.L2BatchHash) } func GetTransactionsPerAddress(ctx context.Context, db *sql.DB, config *params.ChainConfig, address *gethcommon.Address, pagination *common.QueryPagination) (types.Receipts, error) { - return selectReceipts(ctx, db, config, "where tx.sender_address = ? ORDER BY height DESC LIMIT ? OFFSET ? ", address.Bytes(), pagination.Size, pagination.Offset) + return selectReceipts(ctx, db, config, "join externally_owned_account eoa on tx.sender_address = eoa.id where eoa.address = ? ORDER BY height DESC LIMIT ? OFFSET ? ", address.Bytes(), pagination.Size, pagination.Offset) } func CountTransactionsPerAddress(ctx context.Context, db *sql.DB, address *gethcommon.Address) (uint64, error) { - row := db.QueryRowContext(ctx, "select count(1) from receipt join tx on tx.id=receipt.tx join batch on batch.sequence=receipt.batch "+" where tx.sender_address = ?", address.Bytes()) + row := db.QueryRowContext(ctx, "select count(1) from receipt "+ + "join tx on tx.id=receipt.tx "+ + "join externally_owned_account eoa on eoa.id = tx.sender_address "+ + "where eoa.address = ?", address.Bytes()) var count uint64 err := row.Scan(&count) diff --git a/go/obsclient/authclient.go b/go/obsclient/authclient.go index eeba52980a..7c379002ce 100644 --- a/go/obsclient/authclient.go +++ b/go/obsclient/authclient.go @@ -250,21 +250,21 @@ func (ac *AuthObsClient) EstimateGasAndGasPrice(txData types.TxData) types.TxDat } } -// GetPrivateTransactions retrieves the receipts for the specified account (must be registered on this client) -func (ac *AuthObsClient) GetPrivateTransactions(ctx context.Context, address *gethcommon.Address, pagination common.QueryPagination) (types.Receipts, error) { +// GetPrivateTransactions retrieves the receipts for the specified account (must be registered on this client), returns requested range of receipts and the total number of receipts for that acc +func (ac *AuthObsClient) GetPrivateTransactions(ctx context.Context, address *gethcommon.Address, pagination common.QueryPagination) (types.Receipts, uint64, error) { queryParam := &common.ListPrivateTransactionsQueryParams{ Address: *address, Pagination: pagination, } queryParamStr, err := json.Marshal(queryParam) if err != nil { - return nil, fmt.Errorf("unable to marshal query params - %w", err) + return nil, 0, fmt.Errorf("unable to marshal query params - %w", err) } var result common.PrivateTransactionsQueryResponse err = ac.rpcClient.CallContext(ctx, &result, rpc.GetStorageAt, common.ListPrivateTransactionsCQMethod, string(queryParamStr), nil) if err != nil { - return nil, err + return nil, 0, err } - return result.Receipts, nil + return result.Receipts, result.Total, nil } diff --git a/integration/networktest/tests/tenscan/tenscan_rpc_test.go b/integration/networktest/tests/tenscan/tenscan_rpc_test.go index 3885f6d7ff..3ccb517b45 100644 --- a/integration/networktest/tests/tenscan/tenscan_rpc_test.go +++ b/integration/networktest/tests/tenscan/tenscan_rpc_test.go @@ -69,7 +69,7 @@ func TestPersonalTransactions(t *testing.T) { Offset: 0, Size: 20, } - personalTxs, err := user.GetPersonalTransactions(ctx, pagination) + personalTxs, total, err := user.GetPersonalTransactions(ctx, pagination) if err != nil { return fmt.Errorf("unable to get personal transactions - %w", err) } @@ -78,6 +78,11 @@ func TestPersonalTransactions(t *testing.T) { if len(personalTxs) != 2 { return fmt.Errorf("expected 2 transactions, got %d", len(personalTxs)) } + + // verify total set + if total != 2 { + return fmt.Errorf("expected total receipts to be at least 2, got %d", total) + } return nil }), ), diff --git a/integration/networktest/userwallet/authclient.go b/integration/networktest/userwallet/authclient.go index 76ccadaed5..143e7dc0ab 100644 --- a/integration/networktest/userwallet/authclient.go +++ b/integration/networktest/userwallet/authclient.go @@ -132,7 +132,7 @@ func (s *AuthClientUser) Wallet() wallet.Wallet { return s.wal } -func (s *AuthClientUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, error) { +func (s *AuthClientUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, uint64, error) { address := s.wal.Address() return s.client.GetPrivateTransactions(ctx, &address, pagination) } diff --git a/integration/networktest/userwallet/gateway.go b/integration/networktest/userwallet/gateway.go index 44232c5fbb..7ca839b496 100644 --- a/integration/networktest/userwallet/gateway.go +++ b/integration/networktest/userwallet/gateway.go @@ -113,7 +113,7 @@ func (g *GatewayUser) NativeBalance(ctx context.Context) (*big.Int, error) { return g.client.BalanceAt(ctx, g.wal.Address(), nil) } -func (g *GatewayUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, error) { +func (g *GatewayUser) GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, uint64, error) { rpcClient := g.client.Client() queryParams := &common.ListPrivateTransactionsQueryParams{ Address: g.wal.Address(), @@ -121,19 +121,19 @@ func (g *GatewayUser) GetPersonalTransactions(ctx context.Context, pagination co } queryParamsStr, err := json.Marshal(queryParams) if err != nil { - return nil, fmt.Errorf("unable to marshal query params - %w", err) + return nil, 0, fmt.Errorf("unable to marshal query params - %w", err) } var resultBytes hexutil.Bytes err = rpcClient.CallContext(ctx, &resultBytes, "eth_getStorageAt", common.ListPrivateTransactionsCQMethod, queryParamsStr, nil) if err != nil { - return nil, fmt.Errorf("rpc call failed - %w", err) + return nil, 0, fmt.Errorf("rpc call failed - %w", err) } var result common.PrivateTransactionsQueryResponse err = json.Unmarshal(resultBytes, &result) if err != nil { - return nil, fmt.Errorf("failed to unmarshal result - %w", err) + return nil, 0, fmt.Errorf("failed to unmarshal result - %w", err) } - return result.Receipts, nil + return result.Receipts, result.Total, nil } func (g *GatewayUser) Wallet() wallet.Wallet { diff --git a/integration/networktest/userwallet/user.go b/integration/networktest/userwallet/user.go index 33b9048e6c..c4e1388720 100644 --- a/integration/networktest/userwallet/user.go +++ b/integration/networktest/userwallet/user.go @@ -20,5 +20,5 @@ type User interface { SendFunds(ctx context.Context, addr gethcommon.Address, value *big.Int) (*gethcommon.Hash, error) AwaitReceipt(ctx context.Context, txHash *gethcommon.Hash) (*types.Receipt, error) NativeBalance(ctx context.Context) (*big.Int, error) - GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, error) + GetPersonalTransactions(ctx context.Context, pagination common.QueryPagination) (types.Receipts, uint64, error) } From 1050bd78c9c7c92a8f015182ddaaad32f257fb61 Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Thu, 4 Jul 2024 00:04:21 +0100 Subject: [PATCH 09/10] Fix personal txs display --- integration/networktest/env/network_setup.go | 4 ++-- .../tests/helpful/spin_up_local_network_test.go | 2 ++ .../frontend/src/services/useTransactionsService.ts | 7 ++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/integration/networktest/env/network_setup.go b/integration/networktest/env/network_setup.go index bd107052da..a1a0a89f5b 100644 --- a/integration/networktest/env/network_setup.go +++ b/integration/networktest/env/network_setup.go @@ -57,8 +57,8 @@ func DevTestnet(opts ...TestnetEnvOption) networktest.Environment { // LongRunningLocalNetwork is a local network, the l1WSURL is optional (can be empty string), only required if testing L1 interactions func LongRunningLocalNetwork(l1WSURL string) networktest.Environment { connector := newTestnetConnectorWithFaucetAccount( - "ws://127.0.0.1:26900", - []string{"ws://127.0.0.1:26901"}, + "ws://127.0.0.1:17900", + []string{"ws://127.0.0.1:17901"}, genesis.TestnetPrefundedPK, l1WSURL, "", diff --git a/integration/networktest/tests/helpful/spin_up_local_network_test.go b/integration/networktest/tests/helpful/spin_up_local_network_test.go index 5f671fb8b4..9e96230a50 100644 --- a/integration/networktest/tests/helpful/spin_up_local_network_test.go +++ b/integration/networktest/tests/helpful/spin_up_local_network_test.go @@ -29,6 +29,8 @@ const ( _sepoliaValidator1PK = "" // account 0x ) +// Spins up a local network with a gateway, with all processes debuggable. The network will run until the test is stopped. +// Note: If you want to access the gateway frontend you need to `npm run build` its frontend with NEXT_PUBLIC_API_GATEWAY_URL=http://localhost:11180 func TestRunLocalNetwork(t *testing.T) { networktest.TestOnlyRunsInIDE(t) networktest.EnsureTestLogsSetUp("local-geth-network") diff --git a/tools/tenscan/frontend/src/services/useTransactionsService.ts b/tools/tenscan/frontend/src/services/useTransactionsService.ts index 9418dd06de..5edbc3f345 100644 --- a/tools/tenscan/frontend/src/services/useTransactionsService.ts +++ b/tools/tenscan/frontend/src/services/useTransactionsService.ts @@ -61,11 +61,12 @@ export const useTransactionsService = () => { ...options, }, }; - const personalTxData = await provider.send(ethMethods.getStorageAt, [ + const personalTxResp = await provider.send(ethMethods.getStorageAt, [ tenCustomQueryMethods.listPersonalTransactions, JSON.stringify(requestPayload), null, ]); + const personalTxData = jsonHexToObj(personalTxResp); setPersonalTxns(personalTxData); } } catch (error) { @@ -96,3 +97,7 @@ export const useTransactionsService = () => { price, }; }; + +function jsonHexToObj(hex: string) { + return JSON.parse(Buffer.from(hex.slice(2), "hex").toString()); +} \ No newline at end of file From c05cafcb7767c0045068c7fc6ad14bc76bc660d6 Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Thu, 4 Jul 2024 15:35:35 +0100 Subject: [PATCH 10/10] Review: add todo to avoid custom query in enclave layer --- go/enclave/enclave.go | 2 ++ tools/walletextension/rpcapi/blockchain_api.go | 1 + 2 files changed, 3 insertions(+) diff --git a/go/enclave/enclave.go b/go/enclave/enclave.go index 848de7d55c..552f2a0ea9 100644 --- a/go/enclave/enclave.go +++ b/go/enclave/enclave.go @@ -855,6 +855,8 @@ func (e *enclaveImpl) GetTotalContractCount(ctx context.Context) (*big.Int, comm return e.storage.GetContractCount(ctx) } +// GetCustomQuery is a generic query method for queries that don't match the eth API. +// todo: get rid of this method and use specific methods for each query (e.g. GetPersonalTransactions) func (e *enclaveImpl) GetCustomQuery(ctx context.Context, encryptedParams common.EncryptedParamsGetStorageAt) (*responses.PrivateQueryResponse, common.SystemError) { // ensure the enclave is running if e.stopControl.IsStopping() { diff --git a/tools/walletextension/rpcapi/blockchain_api.go b/tools/walletextension/rpcapi/blockchain_api.go index 89aef5044f..775a3bf842 100644 --- a/tools/walletextension/rpcapi/blockchain_api.go +++ b/tools/walletextension/rpcapi/blockchain_api.go @@ -165,6 +165,7 @@ func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address gethcommon.A if err != nil { return nil, fmt.Errorf("unable to extract address from custom query params: %w", err) } + // todo: we should be calling something like `ten_getPrivateTransactions` here, this custom query stuff only needs to be in the gateway layer resp, err := ExecAuthRPC[any](ctx, api.we, &ExecCfg{account: userAddr}, "eth_getStorageAt", address.Hex(), params, nil) if err != nil { return nil, fmt.Errorf("unable to execute custom query: %w", err)