Re-implemented OnGenesisBlock using the tracer itself

This commit is contained in:
Matthieu Vachon 2023-08-02 11:31:25 -04:00
parent 09df0e0c95
commit d1ad0e35d5

View file

@ -10,7 +10,6 @@ import (
"math/big" "math/big"
"os" "os"
"runtime/debug" "runtime/debug"
"sort"
"strings" "strings"
"time" "time"
@ -38,6 +37,9 @@ var _ core.BlockchainLogger = (*Firehose)(nil)
var emptyAddress = common.Address{}.Bytes() var emptyAddress = common.Address{}.Bytes()
var emptyHash = common.Hash{}.Bytes() var emptyHash = common.Hash{}.Bytes()
var emptyCommonAddress = common.Address{}
var emptyCommonHash = common.Hash{}
type Firehose struct { type Firehose struct {
// Global state // Global state
outputBuffer *bytes.Buffer outputBuffer *bytes.Buffer
@ -146,9 +148,6 @@ func (f *Firehose) CaptureTxStart(evm *vm.EVM, tx *types.Transaction) {
f.ensureInBlockAndNotInTrxAndNotInCall() f.ensureInBlockAndNotInTrxAndNotInCall()
f.inTransaction.Store(true)
f.isPrecompiledAddr = evm.IsPrecompileAddr
signer := types.MakeSigner(evm.ChainConfig(), evm.Context.BlockNumber, evm.Context.Time) signer := types.MakeSigner(evm.ChainConfig(), evm.Context.BlockNumber, evm.Context.Time)
from, err := types.Sender(signer, tx) from, err := types.Sender(signer, tx)
@ -163,11 +162,21 @@ func (f *Firehose) CaptureTxStart(evm *vm.EVM, tx *types.Transaction) {
to = *tx.To() to = *tx.To()
} }
f.captureTxStart(tx, tx.Hash(), from, to, evm.IsPrecompileAddr)
}
// captureTxStart is used internally a two places, in the normal "tracer" and in the "OnGenesisBlock",
// we manually pass some override to the `tx` because genesis block has a different way of creating
// the transaction that wraps the genesis block.
func (f *Firehose) captureTxStart(tx *types.Transaction, hash common.Hash, from, to common.Address, isPrecompiledAddr func(common.Address) bool) {
f.inTransaction.Store(true)
f.isPrecompiledAddr = isPrecompiledAddr
v, r, s := tx.RawSignatureValues() v, r, s := tx.RawSignatureValues()
f.transaction = &pbeth.TransactionTrace{ f.transaction = &pbeth.TransactionTrace{
BeginOrdinal: f.blockOrdinal.Next(), BeginOrdinal: f.blockOrdinal.Next(),
Hash: tx.Hash().Bytes(), Hash: hash.Bytes(),
From: from.Bytes(), From: from.Bytes(),
To: to.Bytes(), To: to.Bytes(),
Nonce: tx.Nonce(), Nonce: tx.Nonce(),
@ -469,104 +478,40 @@ func (f *Firehose) CaptureKeccakPreimage(hash common.Hash, data []byte) {
} }
func (f *Firehose) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) { func (f *Firehose) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) {
// FIXME: Re-implement by actualling callin `OnBlockStart/OnTrxStart/CaptureStart` etc. f.OnBlockStart(b, big.NewInt(0), nil, nil)
block := &pbeth.Block{ f.captureTxStart(types.NewTx(&types.LegacyTx{}), emptyCommonHash, emptyCommonAddress, emptyCommonAddress, func(common.Address) bool { return false })
Hash: b.Hash().Bytes(), f.CaptureStart(emptyCommonAddress, emptyCommonAddress, false, nil, 0, nil)
Number: b.Number().Uint64(),
Header: newBlockHeaderFromChainBlock(b, firehoseBigIntFromNative(b.Difficulty())),
TransactionTraces: []*pbeth.TransactionTrace{
{
BeginOrdinal: f.blockOrdinal.Next(),
Hash: emptyHash,
From: emptyAddress,
To: emptyAddress,
Receipt: &pbeth.TransactionReceipt{
StateRoot: b.Root().Bytes(),
LogsBloom: types.Bloom{}.Bytes(),
},
Status: pbeth.TransactionTraceStatus_SUCCEEDED,
Calls: []*pbeth.Call{
{
// It seems we never properly set the BeginOrdinal/EndOrdinal of the root call of the genesis block
BeginOrdinal: 0,
EndOrdinal: 0,
CallType: pbeth.CallType_CALL,
Caller: emptyAddress,
Address: emptyAddress,
Index: 1,
},
},
},
},
Size: uint64(b.Size()),
Ver: 3,
}
rootTrx := block.TransactionTraces[0]
rootCall := rootTrx.Calls[0]
sortedAddrs := make([]common.Address, len(alloc))
i := 0
for addr := range alloc {
sortedAddrs[i] = addr
i++
}
sort.Slice(sortedAddrs, func(i, j int) bool {
return bytes.Compare(sortedAddrs[i][:], sortedAddrs[j][:]) <= -1
})
for _, addr := range sortedKeys(alloc) { for _, addr := range sortedKeys(alloc) {
account := alloc[addr] account := alloc[addr]
rootCall.AccountCreations = append(rootCall.AccountCreations, &pbeth.AccountCreation{ f.OnNewAccount(addr)
Account: addr.Bytes(),
Ordinal: f.blockOrdinal.Next(),
})
rootCall.BalanceChanges = append(rootCall.BalanceChanges, &pbeth.BalanceChange{ if account.Balance != nil && account.Balance.Sign() != 0 {
Address: addr.Bytes(), activeCall := f.callStack.Peek()
NewValue: firehoseBigIntFromNative(account.Balance), activeCall.BalanceChanges = append(activeCall.BalanceChanges, f.newBalanceChange("genesis", addr, common.Big0, account.Balance, pbeth.BalanceChange_REASON_GENESIS_BALANCE))
Reason: pbeth.BalanceChange_REASON_GENESIS_BALANCE, }
Ordinal: f.blockOrdinal.Next(),
})
if len(account.Code) > 0 { if len(account.Code) > 0 {
rootCall.CodeChanges = append(rootCall.CodeChanges, &pbeth.CodeChange{ f.OnCodeChange(addr, emptyCommonHash, nil, common.BytesToHash(crypto.Keccak256(account.Code)), account.Code)
Address: addr.Bytes(),
NewCode: account.Code,
NewHash: crypto.Keccak256(account.Code),
Ordinal: f.blockOrdinal.Next(),
})
} }
if account.Nonce > 0 { if account.Nonce > 0 {
rootCall.NonceChanges = append(rootCall.NonceChanges, &pbeth.NonceChange{ f.OnNonceChange(addr, 0, account.Nonce)
Address: addr.Bytes(),
OldValue: 0,
NewValue: account.Nonce,
Ordinal: f.blockOrdinal.Next(),
})
} }
// This is bad, we were not sorting the storage changes before! Not a big deal,
// just make it harder for verifiability of previous blocks, they are now sorted.
for _, key := range sortedKeys(account.Storage) { for _, key := range sortedKeys(account.Storage) {
rootCall.StorageChanges = append(rootCall.StorageChanges, &pbeth.StorageChange{ f.OnStorageChange(addr, key, emptyCommonHash, account.Storage[key])
Address: addr.Bytes(),
Key: key.Bytes(),
NewValue: account.Storage[key].Bytes(),
Ordinal: f.blockOrdinal.Next(),
})
} }
} }
// Ordering matters here, we must set the end ordinal of the call before the transaction f.CaptureEnd(nil, 0, nil)
rootTrx.EndOrdinal = f.blockOrdinal.Next() f.CaptureTxEnd(&types.Receipt{
Type: uint8(types.LegacyTxType),
f.printBlockToFirehose(block) PostState: b.Root().Bytes(),
f.resetBlock() Status: types.ReceiptStatusSuccessful,
}, nil)
f.OnBlockEnd(nil)
} }
type bytesGetter interface { type bytesGetter interface {
@ -591,17 +536,7 @@ func (f *Firehose) OnBalanceChange(a common.Address, prev, new *big.Int, reason
f.ensureInBlockOrTrx() f.ensureInBlockOrTrx()
change := &pbeth.BalanceChange{ change := f.newBalanceChange("tracer", a, prev, new, balanceChangeReasonFromChain(reason))
Ordinal: f.blockOrdinal.Next(),
Address: a.Bytes(),
OldValue: firehoseBigIntFromNative(prev),
NewValue: firehoseBigIntFromNative(new),
Reason: balanceChangeReasonFromChain(reason),
}
if change.Reason == pbeth.BalanceChange_REASON_UNKNOWN {
panic(fmt.Errorf("unknown balance change reason %s from code %d not accepted here", change.Reason, reason))
}
if f.inTransaction.Load() { if f.inTransaction.Load() {
activeCall := f.callStack.Peek() activeCall := f.callStack.Peek()
@ -618,6 +553,22 @@ func (f *Firehose) OnBalanceChange(a common.Address, prev, new *big.Int, reason
} }
} }
func (f *Firehose) newBalanceChange(tag string, address common.Address, oldValue, newValue *big.Int, reason pbeth.BalanceChange_Reason) *pbeth.BalanceChange {
firehoseTrace("balance changed tag=%s before=%d after=%d reason=%s", tag, oldValue, newValue, reason)
if reason == pbeth.BalanceChange_REASON_UNKNOWN {
panic(fmt.Errorf("received unknown balance change reason %s", reason))
}
return &pbeth.BalanceChange{
Ordinal: f.blockOrdinal.Next(),
Address: address.Bytes(),
OldValue: firehoseBigIntFromNative(oldValue),
NewValue: firehoseBigIntFromNative(newValue),
Reason: reason,
}
}
func (f *Firehose) OnNonceChange(a common.Address, prev, new uint64) { func (f *Firehose) OnNonceChange(a common.Address, prev, new uint64) {
f.ensureInBlockAndInTrx() f.ensureInBlockAndInTrx()