Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

Commit

Permalink
Add support for EIP-1898 (#284)
Browse files Browse the repository at this point in the history
* Update query parameters for the getBalance method

* Add backward compatibility for EIP-1898 getBalance method

* Update tests for getting account balance

* Add sanity check for block number value

* Add e2e tests for getBalance method

* Add EIP 1898 support for getTransactionCount method

* Add EIP 1898 support for getCode method

* Add EIP 1898 support for getStorageAt method

* Add EIP 1898 support for getCall method

* Add sanity check in the json-rpc filter value extraction

* Remove interface parameters of EIP-1898 methods

* Remove unused JSON-RPC codec method

* Add tests for EIP-1898 JSON-RPC parameter

* Update unmarshal method for BlockNumberOrHash

* Add unit tests for EIP-1898 implementation of eth_getBalance

* Add unit tests for EIP-1898 implementation of eth_getTransactionCount

* Add unit tests for EIP-1898 implementation of eth_getCode

* Add unit tests for EIP-1898 implementation of eth_getStorageAt

* Remove e2e tests for EIP-1898

* Remove unused mock in unit tests
  • Loading branch information
0xpanoramix authored Jan 10, 2022
1 parent 9e51892 commit 8c076ec
Show file tree
Hide file tree
Showing 4 changed files with 675 additions and 203 deletions.
38 changes: 38 additions & 0 deletions jsonrpc/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,44 @@ const (

type BlockNumber int64

type BlockNumberOrHash struct {
BlockNumber *BlockNumber `json:"blockNumber,omitempty"`
BlockHash *types.Hash `json:"blockHash,omitempty"`
}

// UnmarshalJSON will try to extract the filter's data.
// Here are the possible input formats :
//
// 1 - "latest", "pending" or "earliest" - self-explaining keywords
// 2 - "0x2" - block number #2 (EIP-1898 backward compatible)
// 3 - {blockNumber: "0x2"} - EIP-1898 compliant block number #2
// 4 - {blockHash: "0xe0e..."} - EIP-1898 compliant block hash 0xe0e...
func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
type bnhCopy BlockNumberOrHash
var placeholder bnhCopy

err := json.Unmarshal(data, &placeholder)
if err != nil {
number, err := stringToBlockNumber(string(data))
if err != nil {
return err
}
placeholder.BlockNumber = &number
}

// Try to extract object
bnh.BlockNumber = placeholder.BlockNumber
bnh.BlockHash = placeholder.BlockHash

if bnh.BlockNumber != nil && bnh.BlockHash != nil {
return fmt.Errorf("cannot use both block number and block hash as filters")
} else if bnh.BlockNumber == nil && bnh.BlockHash == nil {
return fmt.Errorf("block number and block hash are empty, please provide one of them")
}

return nil
}

func stringToBlockNumber(str string) (BlockNumber, error) {
if str == "" {
return 0, fmt.Errorf("value is empty")
Expand Down
102 changes: 102 additions & 0 deletions jsonrpc/codec_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package jsonrpc

import (
"github.com/0xPolygon/polygon-sdk/types"
"github.com/stretchr/testify/assert"
"testing"
)

func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) {
var blockHash types.Hash
err := blockHash.UnmarshalText([]byte("0xe0ee62fd4a39a6988e24df0b406b90af71932e1b01d5561400a8eab943a33d68"))
assert.NoError(t, err)

blockNumberZero := BlockNumber(0x0)
blockNumberLatest := LatestBlockNumber

tests := []struct {
name string
rawRequest string
shouldFail bool
expectedBnh BlockNumberOrHash
}{
{
"should return an error for non existing json fields",
`{"blockHash": "", "blockNumber": ""}`,
true,
BlockNumberOrHash{},
},
{
"should return an error for too many json fields",
`{"blockHash": "0xe0ee62fd4a39a6988e24df0b406b90af71932e1b01d5561400a8eab943a33d68", "blockNumber": "0x0"}`,
true,
BlockNumberOrHash{
BlockNumber: &blockNumberZero,
BlockHash: &blockHash,
},
},
{
"should return an error for invalid block number #1",
`{"blockNumber": "abc"}`,
true,
BlockNumberOrHash{},
},
{
"should return an error for invalid block number #2",
`{"blockNumber": ""}`,
true,
BlockNumberOrHash{},
},
{
"should unmarshal latest block number properly",
`"latest"`,
false,
BlockNumberOrHash{
BlockNumber: &blockNumberLatest,
},
},
{
"should unmarshal block number 0 properly #1",
`{"blockNumber": "0x0"}`,
false,
BlockNumberOrHash{
BlockNumber: &blockNumberZero,
},
},
{
"should unmarshal block number 0 properly #2",
`"0x0"`,
false,
BlockNumberOrHash{
BlockNumber: &blockNumberZero,
},
},
{
"should unmarshal block hash properly",
`{"blockHash": "0xe0ee62fd4a39a6988e24df0b406b90af71932e1b01d5561400a8eab943a33d68"}`,
false,
BlockNumberOrHash{
BlockHash: &blockHash,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bnh := BlockNumberOrHash{}
err := bnh.UnmarshalJSON([]byte(tt.rawRequest))

if tt.shouldFail {
assert.Error(t, err)
} else {
assert.NoError(t, err)
if tt.expectedBnh.BlockNumber != nil {
assert.Equal(t, *bnh.BlockNumber, *tt.expectedBnh.BlockNumber)
}
if tt.expectedBnh.BlockHash != nil {
assert.Equal(t, bnh.BlockHash.String(), tt.expectedBnh.BlockHash.String())
}
}
})
}
}
128 changes: 87 additions & 41 deletions jsonrpc/eth_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package jsonrpc
import (
"errors"
"fmt"
"math/big"

"github.com/0xPolygon/polygon-sdk/helper/hex"
"github.com/0xPolygon/polygon-sdk/state"
"github.com/0xPolygon/polygon-sdk/types"
"github.com/umbracle/fastrlp"
"math/big"
)

// Eth is the eth jsonrpc endpoint
Expand All @@ -21,6 +20,26 @@ func (e *Eth) ChainId() (interface{}, error) {
return argUintPtr(e.d.chainID), nil
}

func (e *Eth) getHeaderFromBlockNumberOrHash(bnh *BlockNumberOrHash) (*types.Header, error) {
var header *types.Header
var err error

if bnh.BlockNumber != nil {
header, err = e.d.getBlockHeaderImpl(*bnh.BlockNumber)
if err != nil {
return nil, fmt.Errorf("failed to get the header of block %d: %v", *bnh.BlockNumber, err)
}
} else if bnh.BlockHash != nil {
block, ok := e.d.store.GetBlockByHash(*bnh.BlockHash, false)
if !ok {
return nil, fmt.Errorf("could not find block referenced by the hash %s", bnh.BlockHash.String())
}

header = block.Header
}
return header, nil
}

func (e *Eth) Syncing() (interface{}, error) {
if syncProgression := e.d.store.GetSyncProgression(); syncProgression != nil {
// Node is bulk syncing, return the status
Expand Down Expand Up @@ -273,16 +292,19 @@ func (e *Eth) GetTransactionReceipt(hash types.Hash) (interface{}, error) {
func (e *Eth) GetStorageAt(
address types.Address,
index types.Hash,
number *BlockNumber,
filter BlockNumberOrHash,
) (interface{}, error) {
// Set the block number to latest
if number == nil {
number, _ = createBlockNumberPointer("latest")
var header *types.Header
var err error

// The filter is empty, use the latest block by default
if filter.BlockNumber == nil && filter.BlockHash == nil {
filter.BlockNumber, _ = createBlockNumberPointer("latest")
}
// Fetch the requested header
header, err := e.d.getBlockHeaderImpl(*number)

header, err = e.getHeaderFromBlockNumberOrHash(&filter)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get header from block hash or block number")
}

// Get the storage for the passed in location
Expand Down Expand Up @@ -317,21 +339,25 @@ func (e *Eth) GasPrice() (interface{}, error) {
// Call executes a smart contract call using the transaction object data
func (e *Eth) Call(
arg *txnArgs,
number *BlockNumber,
filter BlockNumberOrHash,
) (interface{}, error) {
if number == nil {
number, _ = createBlockNumberPointer("latest")
var header *types.Header
var err error

// The filter is empty, use the latest block by default
if filter.BlockNumber == nil && filter.BlockHash == nil {
filter.BlockNumber, _ = createBlockNumberPointer("latest")
}
transaction, err := e.d.decodeTxn(arg)

header, err = e.getHeaderFromBlockNumberOrHash(&filter)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get header from block hash or block number")
}
// Fetch the requested header
header, err := e.d.getBlockHeaderImpl(*number)

transaction, err := e.d.decodeTxn(arg)
if err != nil {
return nil, err
}

// If the caller didn't supply the gas limit in the message, then we set it to maximum possible => block gas limit
if transaction.Gas == 0 {
transaction.Gas = header.GasLimit
Expand Down Expand Up @@ -568,41 +594,57 @@ func (e *Eth) GetLogs(filterOptions *LogFilter) (interface{}, error) {
return result, nil
}

// GetBalance returns the account's balance at the referenced block
func (e *Eth) GetBalance(
address types.Address,
number *BlockNumber,
) (interface{}, error) {
if number == nil {
number, _ = createBlockNumberPointer("latest")
// GetBalance returns the account's balance at the referenced block.
func (e *Eth) GetBalance(address types.Address, filter BlockNumberOrHash) (interface{}, error) {
var header *types.Header
var err error

// The filter is empty, use the latest block by default
if filter.BlockNumber == nil && filter.BlockHash == nil {
filter.BlockNumber, _ = createBlockNumberPointer("latest")
}
header, err := e.d.getBlockHeaderImpl(*number)

header, err = e.getHeaderFromBlockNumberOrHash(&filter)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get header from block hash or block number")
}

// Extract the account balance
acc, err := e.d.store.GetAccount(header.StateRoot, address)
if errors.As(err, &ErrStateNotFound) {
// Account not found, return an empty account
return argUintPtr(0), nil
} else if err != nil {
return nil, err
}

return argBigPtr(acc.Balance), nil
}

// GetTransactionCount returns account nonce
func (e *Eth) GetTransactionCount(
address types.Address,
number *BlockNumber,
) (interface{}, error) {
if number == nil {
number, _ = createBlockNumberPointer("latest")
func (e *Eth) GetTransactionCount(address types.Address, filter BlockNumberOrHash) (interface{}, error) {
var blockNumber BlockNumber
var header *types.Header
var err error

// The filter is empty, use the latest block by default
if filter.BlockNumber == nil && filter.BlockHash == nil {
filter.BlockNumber, _ = createBlockNumberPointer("latest")
}
nonce, err := e.d.getNextNonce(address, *number)

if filter.BlockNumber == nil {
header, err = e.getHeaderFromBlockNumberOrHash(&filter)
if err != nil {
return nil, fmt.Errorf("failed to get header from block hash or block number")
}

blockNumber = BlockNumber(header.Number)
} else {
blockNumber = *filter.BlockNumber
}

nonce, err := e.d.getNextNonce(address, blockNumber)
if err != nil {
if errors.As(err, &ErrStateNotFound) {
if errors.Is(err, ErrStateNotFound) {
return argUintPtr(0), nil
}
return nil, err
Expand All @@ -611,14 +653,18 @@ func (e *Eth) GetTransactionCount(
}

// GetCode returns account code at given block number
func (e *Eth) GetCode(address types.Address, number *BlockNumber) (interface{}, error) {
// Set the block number to latest
if number == nil {
number, _ = createBlockNumberPointer("latest")
func (e *Eth) GetCode(address types.Address, filter BlockNumberOrHash) (interface{}, error) {
var header *types.Header
var err error

// The filter is empty, use the latest block by default
if filter.BlockNumber == nil && filter.BlockHash == nil {
filter.BlockNumber, _ = createBlockNumberPointer("latest")
}
header, err := e.d.getBlockHeaderImpl(*number)

header, err = e.getHeaderFromBlockNumberOrHash(&filter)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get header from block hash or block number")
}

emptySlice := []byte{}
Expand Down
Loading

0 comments on commit 8c076ec

Please sign in to comment.