Skip to content

Commit

Permalink
support get diff accounts
Browse files Browse the repository at this point in the history
Signed-off-by: Keefe-Liu <[email protected]>
  • Loading branch information
keefel committed Oct 8, 2021
1 parent 1ded097 commit 0a05c8f
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 0 deletions.
36 changes: 36 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,42 @@ func (bc *BlockChain) GetDiffLayerRLP(blockHash common.Hash) rlp.RawValue {
return rawData
}

func (bc *BlockChain) GetDiffAccounts(blockHash common.Hash) ([]common.Address, error) {
var (
accounts []common.Address
diffLayer *types.DiffLayer
)

header := bc.GetHeaderByHash(blockHash)
if header == nil {
return nil, fmt.Errorf("no block found")
}

if cached, ok := bc.diffLayerCache.Get(blockHash); ok {
diffLayer = cached.(*types.DiffLayer)
} else if diffStore := bc.db.DiffStore(); diffStore != nil {
diffLayer = rawdb.ReadDiffLayer(diffStore, blockHash)
}

if diffLayer == nil {
if header.TxHash != types.EmptyRootHash {
return nil, fmt.Errorf("no diff layer found")
}

return nil, nil
}

for _, diffAccounts := range diffLayer.Accounts {
accounts = append(accounts, diffAccounts.Account)
}

if header.TxHash != types.EmptyRootHash && len(accounts) == 0 {
return nil, fmt.Errorf("no diff account in block, maybe bad diff layer")
}

return accounts, nil
}

// HasBlock checks if a block is fully present in the database or not.
func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
if bc.blockCache.Contains(hash) {
Expand Down
11 changes: 11 additions & 0 deletions core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,14 @@ type DiffStorage struct {
Keys []string
Vals [][]byte
}

type DiffAccountsInTx struct {
TxHash common.Hash
Accounts map[common.Address]*big.Int
}

type DiffAccountsInBlock struct {
Number uint64
BlockHash common.Hash
Transactions []DiffAccountsInTx
}
4 changes: 4 additions & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
return b.gpo.SuggestPrice(ctx)
}

func (b *EthAPIBackend) Chain() *core.BlockChain {
return b.eth.BlockChain()
}

func (b *EthAPIBackend) ChainDb() ethdb.Database {
return b.eth.ChainDb()
}
Expand Down
14 changes: 14 additions & 0 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,20 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H
return head, err
}

// GetDiffAccounts returns changed accounts in a specific block number.
func (ec *Client) GetDiffAccounts(ctx context.Context, number *big.Int) ([]common.Address, error) {
accounts := make([]common.Address, 0)
err := ec.c.CallContext(ctx, &accounts, "eth_getDiffAccounts", toBlockNumArg(number))
return accounts, err
}

// GetDiffAccountsWithScope returns detailed changes of some interested accounts in a specific block number.
func (ec *Client) GetDiffAccountsWithScope(ctx context.Context, number *big.Int, accounts []common.Address) (*types.DiffAccountsInBlock, error) {
var result types.DiffAccountsInBlock
err := ec.c.CallContext(ctx, &result, "eth_getDiffAccountsWithScope", toBlockNumArg(number), accounts)
return &result, err
}

type rpcTransaction struct {
tx *types.Transaction
txExtraInfo
Expand Down
106 changes: 106 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/common/gopool"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
Expand Down Expand Up @@ -1086,6 +1087,111 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs, bl
return DoEstimateGas(ctx, s.b, args, bNrOrHash, s.b.RPCGasCap())
}

// GetDiffAccounts returns changed accounts in a specific block number.
func (s *PublicBlockChainAPI) GetDiffAccounts(ctx context.Context, blockNr rpc.BlockNumber) ([]common.Address, error) {
if s.b.Chain() == nil {
return nil, fmt.Errorf("blockchain not support get diff accounts")
}

header, err := s.b.HeaderByNumber(ctx, blockNr)
if err != nil {
return nil, fmt.Errorf("block not found for block number (%d): %v", blockNr, err)
}

return s.b.Chain().GetDiffAccounts(header.Hash())
}

// GetDiffAccountsWithScope returns detailed changes of some interested accounts in a specific block number.
func (s *PublicBlockChainAPI) GetDiffAccountsWithScope(ctx context.Context, blockNr rpc.BlockNumber, accounts []common.Address) (*types.DiffAccountsInBlock, error) {
if s.b.Chain() == nil {
return nil, fmt.Errorf("blockchain not support get diff accounts")
}

block, err := s.b.BlockByNumber(ctx, blockNr)
if err != nil {
return nil, fmt.Errorf("block not found for block number (%d): %v", blockNr, err)
}
parent, err := s.b.BlockByHash(ctx, block.ParentHash())
if err != nil {
return nil, fmt.Errorf("block not found for block number (%d): %v", blockNr-1, err)
}
statedb, err := s.b.Chain().StateAt(parent.Root())
if err != nil {
return nil, fmt.Errorf("state not found for block number (%d): %v", blockNr-1, err)
}

result := &types.DiffAccountsInBlock{
Number: uint64(blockNr),
BlockHash: block.Hash(),
Transactions: make([]types.DiffAccountsInTx, 0),
}

accountSet := make(map[common.Address]struct{}, len(accounts))
for _, account := range accounts {
accountSet[account] = struct{}{}
}

// Recompute transactions.
signer := types.MakeSigner(s.b.ChainConfig(), block.Number())
for _, tx := range block.Transactions() {
// Skip data empty tx and to is one of the interested accounts tx.
skip := false
if len(tx.Data()) == 0 {
skip = true
} else if to := tx.To(); to != nil {
if _, exists := accountSet[*to]; exists {
skip = true
}
}

diffTx := types.DiffAccountsInTx{
TxHash: tx.Hash(),
Accounts: make(map[common.Address]*big.Int, len(accounts)),
}

if !skip {
// Record account balance
for _, account := range accounts {
diffTx.Accounts[account] = statedb.GetBalance(account)
}
}

// Apply transaction
msg, _ := tx.AsMessage(signer)
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), s.b.Chain(), nil)
vmenv := vm.NewEVM(context, txContext, statedb, s.b.ChainConfig(), vm.Config{})

if posa, ok := s.b.Engine().(consensus.PoSA); ok {
if isSystem, _ := posa.IsSystemTransaction(tx, block.Header()); isSystem {
balance := statedb.GetBalance(consensus.SystemAddress)
if balance.Cmp(common.Big0) > 0 {
statedb.SetBalance(consensus.SystemAddress, big.NewInt(0))
statedb.AddBalance(block.Header().Coinbase, balance)
}
}
}

if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))

if !skip {
// Compute account balance diff.
for _, account := range accounts {
diffTx.Accounts[account] = new(big.Int).Sub(statedb.GetBalance(account), diffTx.Accounts[account])
if diffTx.Accounts[account].Cmp(big.NewInt(0)) == 0 {
delete(diffTx.Accounts, account)
}
}
result.Transactions = append(result.Transactions, diffTx)
}
}

return result, nil
}

// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value
Expand Down
1 change: 1 addition & 0 deletions internal/ethapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type Backend interface {
// General Ethereum API
Downloader() *downloader.Downloader
SuggestPrice(ctx context.Context) (*big.Int, error)
Chain() *core.BlockChain
ChainDb() ethdb.Database
AccountManager() *accounts.Manager
ExtRPCEnabled() bool
Expand Down
4 changes: 4 additions & 0 deletions les/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
return b.gpo.SuggestPrice(ctx)
}

func (b *LesApiBackend) Chain() *core.BlockChain {
return nil
}

func (b *LesApiBackend) ChainDb() ethdb.Database {
return b.eth.chainDb
}
Expand Down

0 comments on commit 0a05c8f

Please sign in to comment.