Skip to content

Commit

Permalink
96: Support EIP-1898: Add blockHash to defaultBlock methods
Browse files Browse the repository at this point in the history
  • Loading branch information
tolga-coplu committed Jun 26, 2023
1 parent ae8b1d8 commit 3408c75
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 43 deletions.
98 changes: 71 additions & 27 deletions endpoint/engineEth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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
Expand All @@ -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}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
27 changes: 11 additions & 16 deletions endpoint/engineEth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ const engineEthTestFailYaml2 = `
endpoint:
engine:
nearNetworkID: testnet
nearNodeURL: https://rpc.testnet.near.org
nearNodeURL: https://archival-rpc.testnet.near.org
signer: asd.testnet
`

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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand All @@ -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}},
Expand Down Expand Up @@ -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()
Expand All @@ -318,19 +314,18 @@ 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"},
{"test eth_call transfer to EOA", "eth_call", "Call", []interface{}{ctx, newTransactionForCall(&fromAddr, &toAddr, nil, nil, &transferVal, nil), &LatestBlockNumber}, false, "0x"},
// 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 {
Expand All @@ -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())
}
}
Expand Down

0 comments on commit 3408c75

Please sign in to comment.