Add erigon_getBalanceChangesInBlock RPC endpoint (#4609)

* Add eth_getBalanceChangesInBlock RPC endpoint

* Fix lints

* added assertion for one test

* moved balance change api from eth to erigon

Co-authored-by: fatemebagherii <fa.bagheri.a@gmail.com>
This commit is contained in:
Levi Aul 2022-07-15 07:04:23 -07:00 committed by GitHub
parent e8f83db208
commit b6440eea1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/hexutil"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/p2p"
"github.com/ledgerwatch/erigon/rpc"
@ -20,6 +21,7 @@ type ErigonAPI interface {
GetHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
GetHeaderByHash(_ context.Context, hash common.Hash) (*types.Header, error)
GetBlockByTimestamp(ctx context.Context, timeStamp rpc.Timestamp, fullTx bool) (map[string]interface{}, error)
GetBalanceChangesInBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (map[common.Address]*hexutil.Big, error)
// Receipt related (see ./erigon_receipts.go)
GetLogsByHash(ctx context.Context, hash common.Hash) ([][]*types.Log, error)

View File

@ -1,15 +1,21 @@
package commands
import (
"bytes"
"context"
"errors"
"fmt"
"sort"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/changeset"
"github.com/ledgerwatch/erigon/common/dbutils"
"github.com/ledgerwatch/erigon/common/hexutil"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/internal/ethapi"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/turbo/rpchelper"
@ -168,3 +174,65 @@ func buildBlockResponse(db kv.Tx, blockNum uint64, fullTx bool) (map[string]inte
}
return response, err
}
func (api *ErigonImpl) GetBalanceChangesInBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (map[common.Address]*hexutil.Big, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()
blockNumber, _, _, err := rpchelper.GetBlockNumber(blockNrOrHash, tx, api.filters)
if err != nil {
return nil, err
}
c, err := tx.Cursor(kv.AccountChangeSet)
if err != nil {
return nil, err
}
defer c.Close()
startkey := dbutils.EncodeBlockNumber(blockNumber)
decodeFn := changeset.Mapper[kv.AccountChangeSet].Decode
balancesMapping := make(map[common.Address]*hexutil.Big)
newReader, err := rpchelper.CreateStateReader(ctx, tx, blockNrOrHash, api.filters, api.stateCache)
if err != nil {
return nil, err
}
for dbKey, dbValue, _ := c.Seek(startkey); bytes.Equal(dbKey, startkey) && dbKey != nil; dbKey, dbValue, _ = c.Next() {
_, addressBytes, v, err := decodeFn(dbKey, dbValue)
if err != nil {
return nil, err
}
var oldAcc accounts.Account
if err = oldAcc.DecodeForStorage(v); err != nil {
return nil, err
}
oldBalance := oldAcc.Balance
address := common.BytesToAddress(addressBytes)
newAcc, err := newReader.ReadAccountData(address)
if err != nil {
return nil, err
}
newBalance := uint256.NewInt(0)
if newAcc != nil {
newBalance = &newAcc.Balance
}
if !oldBalance.Eq(newBalance) {
newBalanceDesc := (*hexutil.Big)(newBalance.ToBig())
balancesMapping[address] = newBalanceDesc
}
}
return balancesMapping, nil
}

View File

@ -5,6 +5,8 @@ import (
"fmt"
"testing"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon/common/hexutil"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/internal/ethapi"
"github.com/ledgerwatch/erigon/rpc"
@ -16,6 +18,29 @@ import (
"github.com/ledgerwatch/erigon/common"
)
func TestGetBalanceChangesInBlock(t *testing.T) {
assert := assert.New(t)
myBlockNum := rpc.BlockNumberOrHashWithNumber(0)
db := rpcdaemontest.CreateTestKV(t)
stateCache := kvcache.New(kvcache.DefaultCoherentConfig)
api := NewErigonAPI(NewBaseApi(nil, stateCache, snapshotsync.NewBlockReader(), false), db, nil)
balances, err := api.GetBalanceChangesInBlock(context.Background(), myBlockNum)
if err != nil {
t.Errorf("calling GetBalanceChangesInBlock resulted in an error: %v", err)
}
expected := map[common.Address]*hexutil.Big{
common.HexToAddress("0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e"): (*hexutil.Big)(uint256.NewInt(200000000000000000).ToBig()),
common.HexToAddress("0x703c4b2bD70c169f5717101CaeE543299Fc946C7"): (*hexutil.Big)(uint256.NewInt(300000000000000000).ToBig()),
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): (*hexutil.Big)(uint256.NewInt(9000000000000000000).ToBig()),
}
assert.Equal(len(expected), len(balances))
for i := range balances {
assert.Contains(expected, i, "%s is not expected to be present in the output.", i)
assert.Equal(balances[i], expected[i], "the value for %s is expected to be %v, but got %v.", i, expected[i], balances[i])
}
}
func TestGetTransactionReceipt(t *testing.T) {
db := rpcdaemontest.CreateTestKV(t)
stateCache := kvcache.New(kvcache.DefaultCoherentConfig)