8000 Implement getAccount RPC method by cabrador · Pull Request #370 · Fantom-foundation/Sonic · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Implement getAccount RPC method #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,53 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add
return (*hexutil.U256)(state.GetBalance(address)), state.Error()
}

// GetAccountResult is result struct for GetAccount.
// The result contains:
// 1) CodeHash - hash of the code for the given address
// 2) StorageRoot - storage root for the given address
// 3) Balance - the amount of wei for the given address
// 4) Nonce - the number of transactions for given address
type GetAccountResult struct {
CodeHash common.Hash `json:"codeHash"`
StorageRoot common.Hash `json:"storageRoot"`
Balance *hexutil.U256 `json:"balance"`
Nonce hexutil.Uint64 `json:"nonce"`
}

// GetAccount returns the information about account with given address in the state of the given block number.
// The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block numbers are also allowed.
func (s *PublicBlockChainAPI) GetAccount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*GetAccountResult, error) {
state, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if err != nil {
return nil, err
}
defer state.Release()
proof, err := state.GetProof(address, nil)
if err != nil {
return nil, err
}
codeHash, _, err := proof.GetCodeHash(cc.Hash(header.Root), cc.Address(address))
if err != nil {
return nil, err
}
_, storageRoot, _ := proof.GetAccountElements(cc.Hash(header.Root), cc.Address(address))
balance, _, err := proof.GetBalance(cc.Hash(header.Root), cc.Address(address))
if err != nil {
return nil, err
}
nonce, _, err := proof.GetNonce(cc.Hash(header.Root), cc.Address(address))
if err != nil {
return nil, err
}
u256Balance := balance.Uint256()
return &GetAccountResult{
CodeHash: common.Hash(codeHash),
StorageRoot: common.Hash(storageRoot),
Balance: (*hexutil.U256)(&u256Balance),
Nonce: hexutil.Uint64(nonce.ToUint64()),
}, state.Error()
}

// AccountResult is result struct for GetProof
type AccountResult struct {
Address common.Address `json:"address"`
Expand Down
41 changes: 40 additions & 1 deletion ethapi/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/Fantom-foundation/go-opera/inter/state"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"math/big"
"testing"

Expand All @@ -18,7 +19,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
gomock "go.uber.org/mock/gomock"
)

func TestGetBlockReceipts(t *testing.T) {
Expand Down Expand Up @@ -117,6 +117,45 @@ func TestAPI_GetProof(t *testing.T) {
require.Equal(t, []StorageResult{storageProof}, accountProof.StorageProof)
}

func TestAPI_GetAccount(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

addr := cc.Address{1}
codeHash := cc.Hash{2}
storageRoot := cc.Hash{3}
balance := amount.New(4)
nonce := cc.ToNonce(5)
headerRoot := common.Hash{123}

mockBackend := NewMockBackend(ctrl)
mockState := state.NewMockStateDB(ctrl)
mockProof := witness.NewMockProof(ctrl)
mockHeader := &evmcore.EvmHeader{Root: headerRoot}

blkNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)

mockBackend.EXPECT().StateAndHeaderByNumberOrHash(gomock.Any(), blkNr).Return(mockState, mockHeader, nil)
mockState.EXPECT().GetProof(common.Address(addr), nil).Return(mockProof, nil)
mockProof.EXPECT().GetCodeHash(cc.Hash(headerRoot), addr).Return(codeHash, true, nil)
mockProof.EXPECT().GetAccountElements(cc.Hash(headerRoot), addr).Return(nil, storageRoot, true)
mockProof.EXPECT().GetBalance(cc.Hash(headerRoot), addr).Return(balance, true, nil)
mockProof.EXPECT().GetNonce(cc.Hash(headerRoot), addr).Return(nonce, true, nil)
mockState.EXPECT().Error().Return(nil)
mockState.EXPECT().Release()

api := NewPublicBlockChainAPI(mockBackend)

account, err := api.GetAccount(context.Background(), common.Address(addr), blkNr)
require.NoError(t, err, "failed to get account")

u256Balance := balance.Uint256()
require.Equal(t, common.Hash(codeHash), account.CodeHash)
require.Equal(t, common.Hash(storageRoot), account.StorageRoot)
require.Equal(t, (*hexutil.U256)(&u256Balance), account.Balance)
require.Equal(t, hexutil.Uint64(nonce.ToUint64()), account.Nonce)
}

func testGetBlockReceipts(t *testing.T, blockParam rpc.BlockNumberOrHash) ([]map[string]interface{}, error) {

ctrl := gomock.NewController(t)
Expand Down
55 changes: 55 additions & 0 deletions tests/get_account_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package tests

import (
"github.com/Fantom-foundation/go-opera/ethapi"
"github.com/Fantom-foundation/go-opera/tests/contracts/counter"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
"testing"
)

func TestGetAccount(t *testing.T) {
net, err := StartIntegrationTestNet(t.TempDir())
require.NoError(t, err, "failed to start the fake network")
defer net.Stop()

// Deploy the transient storage contract
_, deployReceipt, err := DeployContract(net, counter.DeployCounter)
require.NoError(t, err, "failed to deploy contract")

addr := deployReceipt.ContractAddress

c, err := net.GetClient()
require.NoError(t, err, "failed to get client")
defer c.Close()

rpcClient := c.Client()
defer rpcClient.Close()

var res ethapi.GetAccountResult
err = rpcClient.Call(&res, "eth_getAccount", addr, rpc.LatestBlockNumber)
require.NoError(t, err, "failed to call get account")

// Extract proof to find actual StorageHash(Root), Nonce, Balance and CodeHash
var proofRes struct {
StorageHash common.Hash
Nonce hexutil.Uint64
Balance *hexutil.U256
CodeHash common.Hash
}
err = rpcClient.Call(
&proofRes,
"eth_getProof",
addr,
nil,
rpc.LatestBlockNumber,
)
require.NoError(t, err, "failed call to get proof")

require.Equal(t, proofRes.CodeHash, res.CodeHash)
require.Equal(t, proofRes.StorageHash, res.StorageRoot)
require.Equal(t, proofRes.Balance, res.Balance)
require.Equal(t, proofRes.Nonce, res.Nonce)
}
Loading
0