Skip to content
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

Add erigon_getBalanceChangesInBlock RPC endpoint #4609

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
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