Skip to content

Commit

Permalink
e3: ots_getContractCreator (erigontech#6705)
Browse files Browse the repository at this point in the history
  • Loading branch information
AskAlexSharov authored Jan 26, 2023
1 parent 584ad34 commit 79b42c9
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 103 deletions.
13 changes: 7 additions & 6 deletions cmd/rpcdaemon/commands/debug_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon-lib/kv/iter"
"github.com/ledgerwatch/erigon-lib/kv/kvcache"
"github.com/ledgerwatch/erigon-lib/kv/order"
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/rpcdaemontest"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state/temporal"
Expand Down Expand Up @@ -317,10 +318,10 @@ func TestMapTxNum2BlockNum(t *testing.T) {
require.NoError(t, err)
defer tx.Rollback()

txNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 1024, 0, false, -1)
txNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 1024, -1, order.Desc, -1)
require.NoError(t, err)
txNumsIter := MapDescendTxNum2BlockNum(tx, txNums)
expectTxNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 1024, 0, false, -1)
expectTxNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 1024, -1, order.Desc, -1)
require.NoError(t, err)
checkIter(t, expectTxNums, txNumsIter)
})
Expand All @@ -329,10 +330,10 @@ func TestMapTxNum2BlockNum(t *testing.T) {
require.NoError(t, err)
defer tx.Rollback()

txNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 0, 1024, true, -1)
txNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 0, 1024, order.Asc, -1)
require.NoError(t, err)
txNumsIter := MapDescendTxNum2BlockNum(tx, txNums)
expectTxNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 0, 1024, true, -1)
expectTxNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 0, 1024, order.Asc, -1)
require.NoError(t, err)
checkIter(t, expectTxNums, txNumsIter)
})
Expand All @@ -341,10 +342,10 @@ func TestMapTxNum2BlockNum(t *testing.T) {
require.NoError(t, err)
defer tx.Rollback()

txNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 0, 1024, true, 2)
txNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 0, 1024, order.Asc, 2)
require.NoError(t, err)
txNumsIter := MapDescendTxNum2BlockNum(tx, txNums)
expectTxNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 0, 1024, true, 2)
expectTxNums, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], 0, 1024, order.Asc, 2)
require.NoError(t, err)
checkIter(t, expectTxNums, txNumsIter)
})
Expand Down
24 changes: 19 additions & 5 deletions cmd/rpcdaemon/commands/eth_receipts.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon-lib/kv/bitmapdb"
"github.com/ledgerwatch/erigon-lib/kv/iter"
"github.com/ledgerwatch/erigon-lib/kv/order"
"github.com/ledgerwatch/erigon-lib/kv/rawdbv3"
"github.com/ledgerwatch/log/v3"

Expand Down Expand Up @@ -391,7 +392,7 @@ func (api *APIImpl) getLogsV3(ctx context.Context, tx kv.TemporalTx, begin, end
if err != nil {
return nil, err
}
exec := newIntraBlockExec(tx, chainConfig, api.engine(), api._blockReader)
exec := txnExecutor(tx, chainConfig, api.engine(), api._blockReader, nil)

var blockHash libcommon.Hash
var header *types.Header
Expand Down Expand Up @@ -464,6 +465,8 @@ type intraBlockExec struct {
chainConfig *chain.Config
evm *vm.EVM

tracer GenericTracer

// calculated by .changeBlock()
blockHash libcommon.Hash
blockNum uint64
Expand All @@ -474,18 +477,24 @@ type intraBlockExec struct {
vmConfig *vm.Config
}

func newIntraBlockExec(tx kv.TemporalTx, chainConfig *chain.Config, engine consensus.EngineReader, br services.FullBlockReader) *intraBlockExec {
func txnExecutor(tx kv.TemporalTx, chainConfig *chain.Config, engine consensus.EngineReader, br services.FullBlockReader, tracer GenericTracer) *intraBlockExec {
stateReader := state.NewHistoryReaderV3()
stateReader.SetTx(tx)
return &intraBlockExec{

ie := &intraBlockExec{
engine: engine,
chainConfig: chainConfig,
br: br,
stateReader: stateReader,
tracer: tracer,
evm: vm.NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, chainConfig, vm.Config{}),
vmConfig: &vm.Config{},
ibs: state.New(stateReader),
}
if tracer != nil {
ie.vmConfig = &vm.Config{Debug: true, Tracer: tracer}
}
return ie
}

func (e *intraBlockExec) changeBlock(header *types.Header) {
Expand Down Expand Up @@ -514,6 +523,11 @@ func (e *intraBlockExec) execTx(txNum uint64, txIndex int, txn types.Transaction
if err != nil {
return nil, nil, fmt.Errorf("%w: blockNum=%d, txNum=%d, %s", err, e.blockNum, txNum, e.ibs.Error())
}
if e.vmConfig.Tracer != nil {
if e.tracer.Found() {
e.tracer.SetTransaction(txn)
}
}
return e.ibs.GetLogs(txHash), res, nil
}

Expand All @@ -534,7 +548,7 @@ func getTopicsBitmapV3(tx kv.TemporalTx, topics [][]libcommon.Hash, from, to uin
var bitmapForORing iter.U64 = iter.Array[uint64]([]uint64{})

for _, topic := range sub {
it, err := tx.IndexRange(temporal.LogTopicIdx, topic.Bytes(), from, to, true, -1)
it, err := tx.IndexRange(temporal.LogTopicIdx, topic.Bytes(), int(from), int(to), order.Asc, -1)
if err != nil {
return nil, err
}
Expand All @@ -556,7 +570,7 @@ func getAddrsBitmapV3(tx kv.TemporalTx, addrs []libcommon.Address, from, to uint
}
var rx iter.U64
for _, addr := range addrs {
it, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], from, to, true, -1)
it, err := tx.IndexRange(temporal.LogAddrIdx, addr[:], int(from), int(to), true, -1)
if err != nil {
return nil, err
}
Expand Down
7 changes: 4 additions & 3 deletions cmd/rpcdaemon/commands/otterscan_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon-lib/kv/iter"
"github.com/ledgerwatch/erigon-lib/kv/order"
"github.com/ledgerwatch/erigon-lib/kv/rawdbv3"
"github.com/ledgerwatch/erigon/core/state/temporal"
"github.com/ledgerwatch/log/v3"
Expand Down Expand Up @@ -266,18 +267,18 @@ func (api *OtterscanAPIImpl) searchTransactionsBeforeV3(tx kv.TemporalTx, ctx co
if err != nil {
return nil, err
}
itTo, err := tx.IndexRange(temporal.TracesToIdx, addr[:], from, 0, false, -1)
itTo, err := tx.IndexRange(temporal.TracesToIdx, addr[:], int(from), -1, order.Desc, -1)
if err != nil {
return nil, err
}
itFrom, err := tx.IndexRange(temporal.TracesFromIdx, addr[:], from, 0, false, -1)
itFrom, err := tx.IndexRange(temporal.TracesFromIdx, addr[:], int(from), -1, order.Desc, -1)
if err != nil {
return nil, err
}
txNums := iter.Union[uint64](itFrom, itTo)
txNumsIter := MapDescendTxNum2BlockNum(tx, txNums)

exec := newIntraBlockExec(tx, chainConfig, api.engine(), api._blockReader)
exec := txnExecutor(tx, chainConfig, api.engine(), api._blockReader, nil)
var blockHash libcommon.Hash
var header *types.Header
txs := make([]*RPCTransaction, 0, pageSize)
Expand Down
130 changes: 123 additions & 7 deletions cmd/rpcdaemon/commands/otterscan_contract_creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ import (
"sort"

libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/cmp"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon-lib/kv/bitmapdb"
"github.com/ledgerwatch/erigon-lib/kv/order"
"github.com/ledgerwatch/erigon-lib/kv/rawdbv3"
"github.com/ledgerwatch/erigon-lib/kv/temporal/historyv2"
"github.com/ledgerwatch/erigon/core/state/temporal"
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
"github.com/ledgerwatch/log/v3"

"github.com/ledgerwatch/erigon/core/state"
Expand Down Expand Up @@ -44,6 +49,123 @@ func (api *OtterscanAPIImpl) GetContractCreator(ctx context.Context, addr libcom
return nil, nil
}

chainConfig, err := api.chainConfig(tx)
if err != nil {
return nil, err
}

var acc accounts.Account
if api.historyV3(tx) {
ttx := tx.(kv.TemporalTx)
headNumber, err := stages.GetStageProgress(tx, stages.Execution)
if err != nil {
return nil, err
}
lastTxNum, _ := rawdbv3.TxNums.Max(tx, headNumber)

// Contract; search for creation tx; navigate forward on AccountsHistory/ChangeSets
//
// We traversing history Index - because it's cheaper than traversing History
// and probe History periodically. In result will have small range of blocks. For binary search or full-scan.
//
// popular contracts may have dozens of states changes due to ETH deposits/withdraw after contract creation,
// so it is optimal to search from the beginning even if the contract has multiple
// incarnations.
var prevTxnID, nextTxnID uint64
it, err := ttx.IndexRange(temporal.AccountsHistoryIdx, addr[:], 0, int(lastTxNum+1), order.Asc, -1)
if err != nil {
return nil, err
}
for i := 0; it.HasNext(); i++ {
txnID, _ := it.Next()

if i%4096 != 0 { // probe history periodically, not on every change
nextTxnID = txnID
continue
}

v, ok, err := ttx.HistoryGet(temporal.AccountsHistory, addr[:], txnID)
if err != nil {
log.Error("Unexpected error, couldn't find changeset", "txNum", i, "addr", addr)
panic(err)
}
if !ok {
err = fmt.Errorf("couldn't find history txnID=%v addr=%v", txnID, addr)
log.Error("[rpc] Unexpected error", "err", err)
return nil, err
}
if len(v) == 0 { // creation, but maybe not our Incarnation
prevTxnID = txnID
continue
}

if err := acc.DecodeForStorage(v); err != nil {
return nil, err
}
// Found the shard where the incarnation change happens; ignore all next index values
if acc.Incarnation >= plainStateAcc.Incarnation {
nextTxnID = txnID
break
}
prevTxnID = txnID
}

// The sort.Search function finds the first block where the incarnation has
// changed to the desired one, so we get the previous block from the bitmap;
// however if the creationTxnID block is already the first one from the bitmap, it means
// the block we want is the max block from the previous shard.
var creationTxnID uint64
var searchErr error

if nextTxnID == 0 {
nextTxnID = prevTxnID + 1
}
// Binary search in [prevTxnID, nextTxnID] range; get first block where desired incarnation appears
// can be replaced by full-scan over ttx.HistoryRange([prevTxnID, nextTxnID])?
_ = sort.Search(int(nextTxnID-prevTxnID), func(i int) bool {
txnID := uint64(i) + prevTxnID
v, ok, err := ttx.HistoryGet(temporal.AccountsHistory, addr[:], txnID)
if err != nil {
log.Error("[rpc] Unexpected error, couldn't find changeset", "txNum", i, "addr", addr)
panic(err)
}
if !ok {
return false
}
if len(v) == 0 {
creationTxnID = cmp.Max(creationTxnID, txnID)
return false
}

if err := acc.DecodeForStorage(v); err != nil {
searchErr = err
return false
}
if acc.Incarnation < plainStateAcc.Incarnation {
creationTxnID = cmp.Max(creationTxnID, txnID)
return false
}
return true
})
if searchErr != nil {
return nil, searchErr
}

_, bn, _ := rawdbv3.TxNums.FindBlockNum(tx, creationTxnID)
minTxNum, _ := rawdbv3.TxNums.Min(tx, bn)
txIndex := creationTxnID - minTxNum - 1 /* system-contract */

// Trace block, find tx and contract creator
tracer := NewCreateTracer(ctx, addr)
if err := api.genericTracer(tx, ctx, bn, creationTxnID, txIndex, chainConfig, tracer); err != nil {
return nil, err
}
return &ContractCreatorData{
Tx: tracer.Tx.Hash(),
Creator: tracer.Creator,
}, nil
}

// Contract; search for creation tx; navigate forward on AccountsHistory/ChangeSets
//
// We search shards in forward order on purpose because popular contracts may have
Expand Down Expand Up @@ -73,7 +195,6 @@ func (api *OtterscanAPIImpl) GetContractCreator(ctx context.Context, addr libcom
return nil, fmt.Errorf("could't find any shard for account history addr=%v", addr)
}

var acc accounts.Account
bm := bitmapdb.NewBitmap64()
defer bitmapdb.ReturnToPool64(bm)
prevShardMaxBl := uint64(0)
Expand Down Expand Up @@ -153,14 +274,9 @@ func (api *OtterscanAPIImpl) GetContractCreator(ctx context.Context, addr libcom
if r > 0 {
blockFound = blocks[r-1]
}

// Trace block, find tx and contract creator
chainConfig, err := api.chainConfig(tx)
if err != nil {
return nil, err
}
tracer := NewCreateTracer(ctx, addr)
if err := api.genericTracer(tx, ctx, blockFound, chainConfig, tracer); err != nil {
if err := api.genericTracer(tx, ctx, blockFound, 0, 0, chainConfig, tracer); err != nil {
return nil, err
}

Expand Down
43 changes: 36 additions & 7 deletions cmd/rpcdaemon/commands/otterscan_generic_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,38 @@ type GenericTracer interface {
Found() bool
}

func (api *OtterscanAPIImpl) genericTracer(dbtx kv.Tx, ctx context.Context, blockNum uint64, chainConfig *chain.Config, tracer GenericTracer) error {
block, err := api.blockByNumberWithSenders(dbtx, blockNum)
if err != nil {
return err
}
if block == nil {
func (api *OtterscanAPIImpl) genericTracer(dbtx kv.Tx, ctx context.Context, blockNum, txnID, txIndex uint64, chainConfig *chain.Config, tracer GenericTracer) error {
if api.historyV3(dbtx) {
ttx := dbtx.(kv.TemporalTx)
executor := txnExecutor(ttx, chainConfig, api.engine(), api._blockReader, tracer)

// if block number changed, calculate all related field
header, err := api._blockReader.HeaderByNumber(ctx, ttx, blockNum)
if err != nil {
return err
}
if header == nil {
log.Warn("[rpc] header is nil", "blockNum", blockNum)
return nil
}
executor.changeBlock(header)

txn, err := api._txnReader.TxnByIdxInBlock(ctx, ttx, blockNum, int(txIndex))
if err != nil {
return err
}
if txn == nil {
log.Warn("[rpc] tx is nil", "blockNum", blockNum, "txIndex", txIndex)
return nil
}
_, _, err = executor.execTx(txnID, int(txIndex), txn)
if err != nil {
return err
}
return nil
}

reader, err := rpchelper.CreateHistoryStateReader(dbtx, blockNum, 0, api.historyV3(dbtx), chainConfig.ChainName)
reader, err := rpchelper.CreateHistoryStateReader(dbtx, blockNum, txIndex, api.historyV3(dbtx), chainConfig.ChainName)
if err != nil {
return err
}
Expand All @@ -51,6 +73,13 @@ func (api *OtterscanAPIImpl) genericTracer(dbtx kv.Tx, ctx context.Context, bloc
return h
}
engine := api.engine()
block, err := api.blockByNumberWithSenders(dbtx, blockNum)
if err != nil {
return err
}
if block == nil {
return nil
}

header := block.Header()
rules := chainConfig.Rules(block.NumberU64(), header.Time)
Expand Down
Loading

0 comments on commit 79b42c9

Please sign in to comment.