Skip to content
This repository has been archived by the owner on Oct 18, 2023. It is now read-only.

Commit

Permalink
Add erigon_getBalanceChangesInBlock RPC endpoint (ledgerwatch#4609)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
tsutsu and fatemebagherii committed Jul 15, 2022
1 parent e8f83db commit b6440ee
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
2 changes: 2 additions & 0 deletions cmd/rpcdaemon/commands/erigon_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand Down
68 changes: 68 additions & 0 deletions cmd/rpcdaemon/commands/erigon_block.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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
}
25 changes: 25 additions & 0 deletions cmd/rpcdaemon/commands/eth_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand Down

0 comments on commit b6440ee

Please sign in to comment.