From a7ea021b23e017f515ba3a716240dcd189248237 Mon Sep 17 00:00:00 2001 From: Matt Curtis Date: Thu, 23 May 2024 13:01:20 +0100 Subject: [PATCH] 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/components/batch_executor.go | 2 +- go/enclave/crosschain/common.go | 2 +- go/enclave/enclave.go | 2 +- go/enclave/genesis/testnet_genesis.go | 6 +- go/enclave/nodetype/common.go | 2 +- go/enclave/nodetype/sequencer.go | 1 + 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 ++++++++++++------- 19 files changed, 135 insertions(+), 72 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 aed1b8a4a7..0428cd80c8 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" @@ -397,27 +398,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/components/batch_executor.go b/go/enclave/components/batch_executor.go index 77aae4e75f..cfead4334b 100644 --- a/go/enclave/components/batch_executor.go +++ b/go/enclave/components/batch_executor.go @@ -379,7 +379,7 @@ func (executor *batchExecutor) populateOutboundCrossChainData(ctx context.Contex encodedTree, err := json.Marshal(xchainTree) if err != nil { - panic(err) //todo: figure out what to do + panic(err) // todo: figure out what to do } batch.Header.CrossChainTree = encodedTree diff --git a/go/enclave/crosschain/common.go b/go/enclave/crosschain/common.go index b6ee881c89..1d64083dbb 100644 --- a/go/enclave/crosschain/common.go +++ b/go/enclave/crosschain/common.go @@ -234,7 +234,7 @@ func (ms MessageStructs) HashPacked(index int) gethcommon.Hash { }, } - //todo @siliev: err + // todo @siliev: err packed, _ := args.Pack(messageStruct.Sender, messageStruct.Sequence, messageStruct.Nonce, messageStruct.Topic, messageStruct.Payload, messageStruct.ConsistencyLevel) hash := crypto.Keccak256Hash(packed) return hash diff --git a/go/enclave/enclave.go b/go/enclave/enclave.go index 780693a10a..715829e4c6 100644 --- a/go/enclave/enclave.go +++ b/go/enclave/enclave.go @@ -858,7 +858,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/genesis/testnet_genesis.go b/go/enclave/genesis/testnet_genesis.go index 49b48c16ca..fed93c6f0d 100644 --- a/go/enclave/genesis/testnet_genesis.go +++ b/go/enclave/genesis/testnet_genesis.go @@ -9,8 +9,10 @@ import ( ) const TestnetPrefundedPK = "8dfb8083da6275ae3e4f41e3e8a8c19d028d32c9247e24530933782f2a05035b" // The genesis main account private key. -var GasBridgingKeys, _ = crypto.GenerateKey() // todo - make static -var GasWithdrawalKeys, _ = crypto.GenerateKey() // todo - make static +var ( + GasBridgingKeys, _ = crypto.GenerateKey() // todo - make static + GasWithdrawalKeys, _ = crypto.GenerateKey() // todo - make static +) var TestnetGenesis = Genesis{ Accounts: []Account{ diff --git a/go/enclave/nodetype/common.go b/go/enclave/nodetype/common.go index c9a28978b4..c54ef5e3ca 100644 --- a/go/enclave/nodetype/common.go +++ b/go/enclave/nodetype/common.go @@ -40,6 +40,6 @@ func ExportCrossChainData(ctx context.Context, storage storage.Storage, fromSeqN L1BlockHash: block.Hash(), L1BlockNum: big.NewInt(0).Set(block.Header().Number), CrossChainRootHashes: crossChainHashes, - } //todo: check fromSeqNo + } // todo: check fromSeqNo return bundle, nil } diff --git a/go/enclave/nodetype/sequencer.go b/go/enclave/nodetype/sequencer.go index a0283c2312..16e5117590 100644 --- a/go/enclave/nodetype/sequencer.go +++ b/go/enclave/nodetype/sequencer.go @@ -468,6 +468,7 @@ func (s *sequencer) signCrossChainBundle(bundle *common.ExtCrossChainBundle) err } return nil } + func (s *sequencer) OnL1Block(ctx context.Context, block *types.Block, result *components.BlockIngestionType) error { // nothing to do return nil 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 cd42716030..2dc280f348 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"]