From 3408c752dcb2628c166bb52485d56763ea6a0f71 Mon Sep 17 00:00:00 2001 From: Tolga Coplu Date: Fri, 23 Jun 2023 13:21:11 +0300 Subject: [PATCH] 96: Support EIP-1898: Add blockHash to defaultBlock methods --- endpoint/engineEth.go | 98 +++++++++++++++++++++++++++----------- endpoint/engineEth_test.go | 27 +++++------ 2 files changed, 82 insertions(+), 43 deletions(-) diff --git a/endpoint/engineEth.go b/endpoint/engineEth.go index 73a6a29..cd85ded 100644 --- a/endpoint/engineEth.go +++ b/endpoint/engineEth.go @@ -77,14 +77,19 @@ func (e *EngineEth) chainId(_ context.Context) (*common.Uint256, error) { // On failure to access engine or format error on the response, returns error code '-32000' with custom message. // If API is disabled, returns error code '-32601' with message 'the method does not exist/is not available'. // On missing or invalid param returns error code '-32602' with custom message. -func (e *EngineEth) GetCode(ctx context.Context, address common.Address, number *common.BN64) (*string, error) { +func (e *EngineEth) GetCode(ctx context.Context, address common.Address, bNumOrHash *common.BlockNumberOrHash) (*string, error) { return endpoint.Process(ctx, "eth_getCode", e.Endpoint, func(ctx context.Context) (*string, error) { - return e.getCode(ctx, address, number) - }, address, number) + return e.getCode(ctx, address, bNumOrHash) + }, address, bNumOrHash) } -func (e *EngineEth) getCode(_ context.Context, address common.Address, number *common.BN64) (*string, error) { - resp, err := e.signer.ViewFunction(utils.AccountId, "get_code", address.Bytes(), number.Int64()) +func (e *EngineEth) getCode(ctx context.Context, address common.Address, bNumOrHash *common.BlockNumberOrHash) (*string, error) { + bn, err := e.blockNumberByNumberOrHash(ctx, bNumOrHash) + if err != nil { + return nil, err + } + + resp, err := e.signer.ViewFunction(utils.AccountId, "get_code", address.Bytes(), bn.Int64()) if err != nil { // Return "0x" for the blocks before Aurora account or before Genesis if strings.Contains(err.Error(), beforeAuroraError) || strings.Contains(err.Error(), beforeGenesisError) { @@ -100,14 +105,19 @@ func (e *EngineEth) getCode(_ context.Context, address common.Address, number *c // On failure to access engine or format error on the response, returns error code '-32000' with custom message. // If API is disabled, returns error code '-32601' with message 'the method does not exist/is not available'. // On missing or invalid param returns error code '-32602' with custom message. -func (e *EngineEth) GetBalance(ctx context.Context, address common.Address, number *common.BN64) (*common.Uint256, error) { +func (e *EngineEth) GetBalance(ctx context.Context, address common.Address, bNumOrHash *common.BlockNumberOrHash) (*common.Uint256, error) { return endpoint.Process(ctx, "eth_getBalance", e.Endpoint, func(ctx context.Context) (*common.Uint256, error) { - return e.getBalance(ctx, address, number) - }, address, number) + return e.getBalance(ctx, address, bNumOrHash) + }, address, bNumOrHash) } -func (e *EngineEth) getBalance(_ context.Context, address common.Address, number *common.BN64) (*common.Uint256, error) { - resp, err := e.signer.ViewFunction(utils.AccountId, "get_balance", address.Bytes(), number.Int64()) +func (e *EngineEth) getBalance(ctx context.Context, address common.Address, bNumOrHash *common.BlockNumberOrHash) (*common.Uint256, error) { + bn, err := e.blockNumberByNumberOrHash(ctx, bNumOrHash) + if err != nil { + return nil, err + } + + resp, err := e.signer.ViewFunction(utils.AccountId, "get_balance", address.Bytes(), bn.Int64()) if err != nil { // Return "0x0" for the blocks before Aurora account or before Genesis if strings.Contains(err.Error(), beforeAuroraError) || strings.Contains(err.Error(), beforeGenesisError) { @@ -123,14 +133,19 @@ func (e *EngineEth) getBalance(_ context.Context, address common.Address, number // On failure to access engine or format error on the response, returns error code '-32000' with custom message. // If API is disabled, returns error code '-32601' with message 'the method does not exist/is not available'. // On missing or invalid param returns error code '-32602' with custom message. -func (e *EngineEth) GetTransactionCount(ctx context.Context, address common.Address, number *common.BN64) (*common.Uint256, error) { +func (e *EngineEth) GetTransactionCount(ctx context.Context, address common.Address, bNumOrHash *common.BlockNumberOrHash) (*common.Uint256, error) { return endpoint.Process(ctx, "eth_getTransactionCount", e.Endpoint, func(ctx context.Context) (*common.Uint256, error) { - return e.getTransactionCount(ctx, address, number) - }, address, number) + return e.getTransactionCount(ctx, address, bNumOrHash) + }, address, bNumOrHash) } -func (e *EngineEth) getTransactionCount(_ context.Context, address common.Address, number *common.BN64) (*common.Uint256, error) { - resp, err := e.signer.ViewFunction(utils.AccountId, "get_nonce", address.Bytes(), number.Int64()) +func (e *EngineEth) getTransactionCount(ctx context.Context, address common.Address, bNumOrHash *common.BlockNumberOrHash) (*common.Uint256, error) { + bn, err := e.blockNumberByNumberOrHash(ctx, bNumOrHash) + if err != nil { + return nil, err + } + + resp, err := e.signer.ViewFunction(utils.AccountId, "get_nonce", address.Bytes(), bn.Int64()) if err != nil { // Return "0x0" for the blocks before Aurora account or before Genesis if strings.Contains(err.Error(), beforeAuroraError) || strings.Contains(err.Error(), beforeGenesisError) { @@ -148,19 +163,23 @@ func (e *EngineEth) getTransactionCount(_ context.Context, address common.Addres // On failure to access engine or format error on the response, returns error code '-32000' with custom message. // If API is disabled, returns error code '-32601' with message 'the method does not exist/is not available'. // On missing or invalid param returns error code '-32602' with custom message. -func (e *EngineEth) GetStorageAt(ctx context.Context, address common.Address, storageSlot common.Uint256, number *common.BN64) (*string, error) { +func (e *EngineEth) GetStorageAt(ctx context.Context, address common.Address, storageSlot common.Uint256, bNumOrHash *common.BlockNumberOrHash) (*string, error) { return endpoint.Process(ctx, "eth_getStorageAt", e.Endpoint, func(ctx context.Context) (*string, error) { - return e.getStorageAt(ctx, address, storageSlot, number) - }, address, number) + return e.getStorageAt(ctx, address, storageSlot, bNumOrHash) + }, address, bNumOrHash) } -func (e *EngineEth) getStorageAt(_ context.Context, address common.Address, storageSlot common.Uint256, number *common.BN64) (*string, error) { +func (e *EngineEth) getStorageAt(ctx context.Context, address common.Address, storageSlot common.Uint256, bNumOrHash *common.BlockNumberOrHash) (*string, error) { argsBuf, err := formatGetStorageAtArgsForEngine(address, storageSlot) if err != nil { return nil, &errs.GenericError{Err: err} } + bn, err := e.blockNumberByNumberOrHash(ctx, bNumOrHash) + if err != nil { + return nil, err + } - resp, err := e.signer.ViewFunction(utils.AccountId, "get_storage_at", argsBuf, number.Int64()) + resp, err := e.signer.ViewFunction(utils.AccountId, "get_storage_at", argsBuf, bn.Int64()) if err != nil { // Return "0x" for the blocks before Aurora account or before Genesis if strings.Contains(err.Error(), beforeAuroraError) || strings.Contains(err.Error(), beforeGenesisError) { @@ -176,13 +195,13 @@ func (e *EngineEth) getStorageAt(_ context.Context, address common.Address, stor // On failure to access engine or format error on the response, returns error code '-32000' with custom message. // If API is disabled, returns error code '-32601' with message 'the method does not exist/is not available'. // On missing or invalid param returns error code '-32602' with custom message. -func (e *EngineEth) Call(ctx context.Context, txs engine.TransactionForCall, number *common.BN64) (*string, error) { +func (e *EngineEth) Call(ctx context.Context, txs engine.TransactionForCall, bNumOrHash *common.BlockNumberOrHash) (*string, error) { return endpoint.Process(ctx, "eth_call", e.Endpoint, func(ctx context.Context) (*string, error) { - return e.call(ctx, txs, number) - }, txs, number) + return e.call(ctx, txs, bNumOrHash) + }, txs, bNumOrHash) } -func (e *EngineEth) call(_ context.Context, txs engine.TransactionForCall, number *common.BN64) (*string, error) { +func (e *EngineEth) call(ctx context.Context, txs engine.TransactionForCall, bNumOrHash *common.BlockNumberOrHash) (*string, error) { err := txs.Validate() if err != nil { return nil, err @@ -191,7 +210,12 @@ func (e *EngineEth) call(_ context.Context, txs engine.TransactionForCall, numbe if err != nil { return nil, &errs.GenericError{Err: err} } - resp, err := e.signer.ViewFunction(utils.AccountId, "view", argsBuf, number.Int64()) + bn, err := e.blockNumberByNumberOrHash(ctx, bNumOrHash) + if err != nil { + return nil, err + } + + resp, err := e.signer.ViewFunction(utils.AccountId, "view", argsBuf, bn.Int64()) if err != nil { return nil, &errs.GenericError{Err: err} } @@ -231,8 +255,8 @@ func (e *EngineEth) asyncSendRawTransaction(txsBytes []byte) (*string, error) { } txsHash := utils.CalculateKeccak256(txsBytes) e.Logger.Info().Msgf("Near txs hash is: %s, for Eth txs hash: %s", *resp, txsHash) - - return &txsHash, nil + + return &txsHash, nil } // syncSendRawTransaction submits a raw transaction to engine synchronously @@ -272,6 +296,26 @@ func (e *EngineEth) sendRawTransactionWithRetry(txsBytes []byte) (*string, error return nil, errors.New("sendRawTransaction: maximum retries reached") } +func (e *EngineEth) blockNumberByNumberOrHash(ctx context.Context, bNumOrHash *common.BlockNumberOrHash) (*common.BN64, error) { + if bNumOrHash == nil { + bn := common.LatestBlockNumber + return &bn, nil + } + if tbn, ok := bNumOrHash.Number(); ok { + return &tbn, nil + } + if hash, ok := bNumOrHash.Hash(); ok { + tbn, err := e.DbHandler.BlockHashToNumber(ctx, hash) + if err != nil { + return nil, err + } + bn := common.BN64(*tbn) + return &bn, nil + } + + return nil, errors.New("invalid arguments; neither block nor hash specified") +} + // getUint256ResultFromEngineResponse gets the return value from engine and converts it to Uint256 format func getUint256ResultFromEngineResponse(respArg interface{}) (*common.Uint256, error) { engineResult, err := engine.NewQueryResult(respArg) diff --git a/endpoint/engineEth_test.go b/endpoint/engineEth_test.go index 9af8ace..6db76dd 100644 --- a/endpoint/engineEth_test.go +++ b/endpoint/engineEth_test.go @@ -54,7 +54,7 @@ const engineEthTestFailYaml2 = ` endpoint: engine: nearNetworkID: testnet - nearNodeURL: https://rpc.testnet.near.org + nearNodeURL: https://archival-rpc.testnet.near.org signer: asd.testnet ` @@ -62,7 +62,7 @@ const engineEthTestYaml = ` endpoint: engine: nearNetworkID: testnet - nearNodeURL: https://rpc.testnet.near.org + nearNodeURL: https://archival-rpc.testnet.near.org signer: tolgacoplu.testnet SignerKey: /Users/tolgacoplu/.near-credentials/testnet/tolgacoplu.testnet.json minGasPrice: 0 @@ -80,6 +80,7 @@ var engineEth *EngineEth var engineNet *EngineNet var fromAddr common.Address var toAddr common.Address +var gasZero common.Uint256 var transferVal common.Uint256 var transferValOOF common.Uint256 var contractAddr common.Address @@ -131,6 +132,7 @@ func TestMain(m *testing.M) { // Create from, to, and contract addresses to use in the tests fromAddr = common.HexStringToAddress(fromAddress) toAddr = common.HexStringToAddress(toAddress) + gasZero = common.IntToUint256(0) transferVal = common.IntToUint256(transferValue) transferValOOF = common.IntToUint256(transferValueOOF) contractAddr = common.HexStringToAddress(contractAddress) @@ -205,10 +207,7 @@ func TestNetEndpointsCompare(t *testing.T) { } func TestEthEndpointsCompare(t *testing.T) { - SafeBlockNumber := common.SafeBlockNumber - FinalizedBlockNumber := common.FinalizedBlockNumber - PendingBlockNumber := common.PendingBlockNumber - LatestBlockNumber := common.LatestBlockNumber + LatestBlockNumber := common.BlockNumberOrHashWithBN64(common.LatestBlockNumber) ctx, cancel := context.WithTimeout(context.Background(), timeoutSec*time.Second) defer cancel() @@ -220,10 +219,7 @@ func TestEthEndpointsCompare(t *testing.T) { args []interface{} }{ {"test eth_chainId", "eth_chainId", "ChainId", []interface{}{ctx}}, - {"test eth_getCode with EOA and safe block num", "eth_getCode", "GetCode", []interface{}{ctx, toAddr, &SafeBlockNumber}}, - {"test eth_getCode with safe block num", "eth_getCode", "GetCode", []interface{}{ctx, contractAddr, &SafeBlockNumber}}, - {"test eth_getCode with finalized block num", "eth_getCode", "GetCode", []interface{}{ctx, contractAddr, &FinalizedBlockNumber}}, - {"test eth_getCode with pending block num", "eth_getCode", "GetCode", []interface{}{ctx, contractAddr, &PendingBlockNumber}}, + {"test eth_getCode with EOA and safe block num", "eth_getCode", "GetCode", []interface{}{ctx, toAddr, &LatestBlockNumber}}, {"test eth_getCode with latest block num", "eth_getCode", "GetCode", []interface{}{ctx, contractAddr, &LatestBlockNumber}}, {"test eth_getBalance with latest block num", "eth_getBalance", "GetBalance", []interface{}{ctx, fromAddr, &LatestBlockNumber}}, {"test eth_getTransactionCount with latest block num", "eth_getTransactionCount", "GetTransactionCount", []interface{}{ctx, fromAddr, &LatestBlockNumber}}, @@ -304,7 +300,7 @@ func newTransactionForCall(from, to *common.Address, gas, gasPrice, value *commo } func TestEthEndpointsStatic(t *testing.T) { - LatestBlockNumber := common.IntToBN64(-1) + LatestBlockNumber := common.BlockNumberOrHashWithBN64(common.LatestBlockNumber) ctx, cancel := context.WithTimeout(context.Background(), timeoutSec*time.Second) defer cancel() @@ -318,9 +314,7 @@ func TestEthEndpointsStatic(t *testing.T) { expectedResult string }{ {"test aysnc eth_sendRawTransaction incorrect nonce", "eth_sendRawTransaction", "SendRawTransaction", []interface{}{ctx, txsInvalidNonce}, true, "anyHash"}, - {"test sync eth_sendRawTransaction incorrect txs raw data", "eth_sendRawTransaction", "SendRawTransaction", []interface{}{ctx, txsInvalidRawData}, false, "transaction parameter is not correct"}, - // Needs changes on engine side to be able to run this test. Therefore, it is commented out for now - // {"test sync eth_sendRawTransaction low gas price", "eth_sendRawTransaction", "SendRawTransaction", []interface{}{ctx, txsLowGasPrice}, false, "gas price too low"}, + {"test sync eth_sendRawTransaction incorrect txs raw data", "eth_sendRawTransaction", "SendRawTransaction", []interface{}{ctx, txsInvalidRawData}, false, "value size exceeds available input length"}, {"test sync eth_sendRawTransaction low gas limit", "eth_sendRawTransaction", "SendRawTransaction", []interface{}{ctx, txsLowGasLimit}, false, "intrinsic gas too low"}, {"test sync eth_sendRawTransaction incorrect nonce", "eth_sendRawTransaction", "SendRawTransaction", []interface{}{ctx, txsInvalidNonce}, false, "ERR_INCORRECT_NONCE"}, {"test eth_call contract data", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(&fromAddr, &contractAddr, nil, nil, nil, &contractData), &LatestBlockNumber}, false, "0x0000000000000000000000000000000000000000000000000000000000000005"}, @@ -328,9 +322,10 @@ func TestEthEndpointsStatic(t *testing.T) { // Needs changes on engine side to be able to run this test properly. Normally, "execution error: Out Of Gas" should be retrieved. Hovewer, since max gas is staticilly applied the result seems to be success {"test eth_call out of gas", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(&fromAddr, &toAddr, &transferValOOF, nil, &transferVal, nil), &LatestBlockNumber}, false, "0x"}, {"test eth_call out of fund", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(&fromAddr, &toAddr, nil, nil, &transferValOOF, nil), &LatestBlockNumber}, false, "Ok(OutOfFund)"}, + {"test eth_call low gas price", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(&fromAddr, &toAddr, &gasZero, nil, &transferVal, nil), &LatestBlockNumber}, false, "Ok(OutOfGas)"}, {"test eth_call stack overflow", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(nil, &contractAddrStackOverFlow, nil, nil, nil, &contractDataStackOverFlow), &LatestBlockNumber}, false, "EvmError(StackOverflow)"}, // Testnet returns "0x" for Revert status, following the same approach - {"test eth_call call too deep", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(nil, &contractAddrCallTooDeep, nil, nil, nil, &contractDataCallTooDeep), &LatestBlockNumber}, false, "execution reverted without data"}, + {"test eth_call call too deep", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(nil, &contractAddrCallTooDeep, nil, nil, nil, &contractDataCallTooDeep), &LatestBlockNumber}, false, "execution reverted"}, {"test eth_call out of offset", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(&fromAddr, &contractAddrOutOfOffset, nil, nil, nil, &contractDataOutOfOffset), &LatestBlockNumber}, false, "Ok(OutOfOffset)"}, } for _, d := range data { @@ -351,7 +346,7 @@ func TestEthEndpointsStatic(t *testing.T) { t.Errorf("incorrect response: expected %s, got %s", d.expectedResult, *v) } default: - if err.Error() != d.expectedResult { + if !strings.Contains(err.Error(), d.expectedResult) { t.Errorf("incorrect response: expected %s, got %s", d.expectedResult, err.Error()) } }