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

evm t8n tool to use ExecuteBlockEphemerally api #4512

Merged
merged 24 commits into from
Jul 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
768423f
fix to set V, R, S in legacy transaction
sudeepdino008 May 12, 2022
a326df8
fix to dump post-execution alloc for evm t8n
sudeepdino008 May 18, 2022
75d3819
close tx in evm t8n
sudeepdino008 May 18, 2022
8ecb49e
populate current difficulty and gas used in output result
sudeepdino008 May 26, 2022
6beaf80
initial attempt at migrating 'evm t8n' to use ExecuteBlockEphemerally
sudeepdino008 May 30, 2022
6011292
using ExecutionResult in ExecuteBlockEphemerally
sudeepdino008 Jun 20, 2022
916ac3e
bypass validations and integrate with EphemeralExecResult
sudeepdino008 May 31, 2022
759ee99
fixing output of 'evm t8n'
sudeepdino008 May 31, 2022
b8ea5d1
get ExecuteBlockEphemerally to accept getTracer lambda
sudeepdino008 Jun 13, 2022
26c51e2
fix build failure
sudeepdino008 Jun 21, 2022
77d3df5
test cases for evm t8n
sudeepdino008 Jun 14, 2022
829adae
more test cases for evm t8n
sudeepdino008 Jun 15, 2022
13b4425
fix stateRoot computation in evm t8n
sudeepdino008 Jun 16, 2022
ba1df9f
remove reward argument, as EBE itself takes care of it
sudeepdino008 Jun 21, 2022
801dde2
final cleanups for migration to using ExecuteBlockEphemerally
sudeepdino008 Jun 21, 2022
ca7bef0
change EBEforBSC to match EBE
sudeepdino008 Jun 22, 2022
2af5630
fix linter issues
sudeepdino008 Jun 23, 2022
e276478
manually revert an unwanted diff
sudeepdino008 Jun 23, 2022
c1de585
avoid calculating ReceiptHash twice
sudeepdino008 Jun 27, 2022
17868df
Merge remote-tracking branch 'origin/devel' into evm_t8n_2
sudeepdino008 Jun 27, 2022
3ec0f94
linter check
sudeepdino008 Jun 27, 2022
60c2d0a
minor correction
sudeepdino008 Jun 27, 2022
2725d23
Merge branch 'devel' into evm_t8n_2
sudeepdino008 Jun 28, 2022
78583c7
remove unnecessary logic in EBEforBsc
sudeepdino008 Jun 30, 2022
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
5 changes: 3 additions & 2 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,8 @@ func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg
msg := callMsg{call}

txContext := core.NewEVMTxContext(msg)
evmContext := core.NewEVMBlockContext(block.Header(), b.getHeader, b.m.Engine, nil, b.contractHasTEVM)
header := block.Header()
evmContext := core.NewEVMBlockContext(header, core.GetHashFn(header, b.getHeader), b.m.Engine, nil, b.contractHasTEVM)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmEnv := vm.NewEVM(evmContext, txContext, statedb, b.m.ChainConfig, vm.Config{})
Expand Down Expand Up @@ -696,7 +697,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx types.Transac
b.pendingState.Prepare(tx.Hash(), common.Hash{}, len(b.pendingBlock.Transactions()))
//fmt.Printf("==== Start producing block %d, header: %d\n", b.pendingBlock.NumberU64(), b.pendingHeader.Number.Uint64())
if _, _, err := core.ApplyTransaction(
b.m.ChainConfig, b.getHeader, b.m.Engine,
b.m.ChainConfig, core.GetHashFn(b.pendingHeader, b.getHeader), b.m.Engine,
&b.pendingHeader.Coinbase, b.gasPool,
b.pendingState, state.NewNoopWriter(),
b.pendingHeader, tx,
Expand Down
288 changes: 44 additions & 244 deletions cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,275 +17,63 @@
package t8ntool

import (
"context"
"encoding/binary"
"fmt"
"math/big"

"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon-lib/kv/memdb"
"github.com/ledgerwatch/log/v3"
"golang.org/x/crypto/sha3"

"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/consensus/ethash"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/systemcontracts"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/turbo/trie"
)

type Prestate struct {
Env stEnv `json:"env"`
Pre core.GenesisAlloc `json:"pre"`
}

// ExecutionResult contains the execution status after running a state test, any
// error that might have occurred and a dump of the final state if requested.
type ExecutionResult struct {
StateRoot common.Hash `json:"stateRoot"`
TxRoot common.Hash `json:"txRoot"`
ReceiptRoot common.Hash `json:"receiptRoot"`
LogsHash common.Hash `json:"logsHash"`
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
Receipts types.Receipts `json:"receipts"`
Rejected []*rejectedTx `json:"rejected,omitempty"`
}

type ommer struct {
Delta uint64 `json:"delta"`
Address common.Address `json:"address"`
}

//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
type stEnv struct {
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
Number uint64 `json:"currentNumber" gencodec:"required"`
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"`
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
Random *common.Hash `json:"currentRandom,omitempty"`
}

type rejectedTx struct {
Index int `json:"index"`
Err string `json:"error"`
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
Difficulty *big.Int `json:"currentDifficulty"`
Random *big.Int `json:"currentRandom"`
ParentDifficulty *big.Int `json:"parentDifficulty"`
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
Number uint64 `json:"currentNumber" gencodec:"required"`
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"`
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
ParentUncleHash common.Hash `json:"parentUncleHash"`
}

type stEnvMarshaling struct {
Coinbase common.UnprefixedAddress
Difficulty *math.HexOrDecimal256
GasLimit math.HexOrDecimal64
Number math.HexOrDecimal64
Timestamp math.HexOrDecimal64
BaseFee *math.HexOrDecimal256
}

// Apply applies a set of transactions to a pre-state
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
txs types.Transactions, miningReward int64,
getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error)) (kv.RwDB, *ExecutionResult, error) {

// Capture errors for BLOCKHASH operation, if we haven't been supplied the
// required blockhashes
var hashError error
getHash := func(num uint64) common.Hash {
if pre.Env.BlockHashes == nil {
hashError = fmt.Errorf("getHash(%d) invoked, no blockhashes provided", num)
return common.Hash{}
}
h, ok := pre.Env.BlockHashes[math.HexOrDecimal64(num)]
if !ok {
hashError = fmt.Errorf("getHash(%d) invoked, blockhash for that block not provided", num)
}
return h
}
db := memdb.New()

tx, err := db.BeginRw(context.Background())
if err != nil {
return nil, nil, err
}
defer tx.Rollback()

var (
rules0 = chainConfig.Rules(0)
rules1 = chainConfig.Rules(1)
rules = chainConfig.Rules(pre.Env.Number)
ibs = MakePreState(rules0, tx, pre.Pre)
signer = types.MakeSigner(chainConfig, pre.Env.Number)
gaspool = new(core.GasPool)
blockHash = common.Hash{0x13, 0x37}
rejectedTxs []*rejectedTx
includedTxs types.Transactions
gasUsed = uint64(0)
receipts = make(types.Receipts, 0)
txIndex = 0
)
gaspool.AddGas(pre.Env.GasLimit)

difficulty := new(big.Int)
if pre.Env.Random == nil {
difficulty = pre.Env.Difficulty
} else {
// We are on POS hence difficulty opcode is now supplant with RANDOM
random := pre.Env.Random.Bytes()
difficulty.SetBytes(random)
}
vmContext := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Coinbase: pre.Env.Coinbase,
BlockNumber: pre.Env.Number,
ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
Time: pre.Env.Timestamp,
Difficulty: difficulty,
GasLimit: pre.Env.GasLimit,
GetHash: getHash,
}
// If currentBaseFee is defined, add it to the vmContext.
if pre.Env.BaseFee != nil {
vmContext.BaseFee = new(uint256.Int)
overflow := vmContext.BaseFee.SetFromBig(pre.Env.BaseFee)
if overflow {
return nil, nil, fmt.Errorf("pre.Env.BaseFee higher than 2^256-1")
}
}
// If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
// done in StateProcessor.Process(block, ...), right before transactions are applied.
if chainConfig.DAOForkSupport &&
chainConfig.DAOForkBlock != nil &&
chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
misc.ApplyDAOHardFork(ibs)
}
systemcontracts.UpgradeBuildInSystemContract(chainConfig, new(big.Int).SetUint64(pre.Env.Number), ibs)

for i, txn := range txs {
msg, err := txn.AsMessage(*signer, pre.Env.BaseFee, rules)
if err != nil {
log.Warn("rejected txn", "index", i, "hash", txn.Hash(), "err", err)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
continue
}
tracer, err := getTracerFn(txIndex, txn.Hash())
if err != nil {
return nil, nil, err
}
vmConfig.Tracer = tracer
vmConfig.Debug = (tracer != nil)
ibs.Prepare(txn.Hash(), blockHash, txIndex)
txContext := core.NewEVMTxContext(msg)
snapshot := ibs.Snapshot()
evm := vm.NewEVM(vmContext, txContext, ibs, chainConfig, vmConfig)

// (ret []byte, usedGas uint64, failed bool, err error)
msgResult, err := core.ApplyMessage(evm, msg, gaspool, true /* refunds */, false /* gasBailout */)
if err != nil {
ibs.RevertToSnapshot(snapshot)
log.Info("rejected txn", "index", i, "hash", txn.Hash(), "from", msg.From(), "err", err)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
continue
}
includedTxs = append(includedTxs, txn)
if hashError != nil {
return nil, nil, NewError(ErrorMissingBlockhash, hashError)
}
gasUsed += msgResult.UsedGas

// Receipt:
{
// Create a new receipt for the transaction, storing the intermediate root and
// gas used by the txn.
receipt := &types.Receipt{Type: txn.Type(), CumulativeGasUsed: gasUsed}
if msgResult.Failed() {
receipt.Status = types.ReceiptStatusFailed
} else {
receipt.Status = types.ReceiptStatusSuccessful
}
receipt.TxHash = txn.Hash()
receipt.GasUsed = msgResult.UsedGas

// If the transaction created a contract, store the creation address in the receipt.
if msg.To() == nil {
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext().Origin, txn.GetNonce())
}

// Set the receipt logs and create a bloom for filtering
receipt.Logs = ibs.GetLogs(txn.Hash())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
// These three are non-consensus fields:
//receipt.BlockHash
//receipt.BlockNumber
receipt.TransactionIndex = uint(txIndex)
receipts = append(receipts, receipt)
}

txIndex++
}
// Add mining reward?
if miningReward > 0 {
// Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
// where
// - the coinbase suicided, or
// - there are only 'bad' transactions, which aren't executed. In those cases,
// the coinbase gets no txfee, so isn't created, and thus needs to be touched
var (
blockReward = uint256.NewInt(uint64(miningReward))
minerReward = uint256.NewInt(0).Set(blockReward)
perOmmer = uint256.NewInt(0).Div(blockReward, uint256.NewInt(32))
)
for _, ommer := range pre.Env.Ommers {
// Add 1/32th for each ommer included
minerReward.Add(minerReward, perOmmer)
// Add (8-delta)/8
reward := uint256.NewInt(8)
reward.Sub(reward, uint256.NewInt(ommer.Delta))
reward.Mul(reward, blockReward)
reward.Div(reward, uint256.NewInt(8))
ibs.AddBalance(ommer.Address, reward)
}
ibs.AddBalance(pre.Env.Coinbase, minerReward)
}

// Commit block
var root common.Hash
if err = ibs.FinalizeTx(rules1, state.NewPlainStateWriter(tx, tx, 1)); err != nil {
return nil, nil, err
}
root, err = trie.CalcRoot("", tx)
if err != nil {
return nil, nil, err
}
if err = tx.Commit(); err != nil {
return nil, nil, err
}

execRs := &ExecutionResult{
StateRoot: root,
TxRoot: types.DeriveSha(includedTxs),
ReceiptRoot: types.DeriveSha(receipts),
Bloom: types.CreateBloom(receipts),
LogsHash: rlpHash(ibs.Logs()),
Receipts: receipts,
Rejected: rejectedTxs,
}
return db, execRs, nil
Coinbase common.UnprefixedAddress
Difficulty *math.HexOrDecimal256
Random *math.HexOrDecimal256
ParentDifficulty *math.HexOrDecimal256
GasLimit math.HexOrDecimal64
Number math.HexOrDecimal64
Timestamp math.HexOrDecimal64
ParentTimestamp math.HexOrDecimal64
BaseFee *math.HexOrDecimal256
}

func MakePreState(chainRules *params.Rules, tx kv.RwTx, accounts core.GenesisAlloc) *state.IntraBlockState {
func MakePreState(chainRules *params.Rules, tx kv.RwTx, accounts core.GenesisAlloc) (*state.PlainStateReader, *state.PlainStateWriter) {
var blockNr uint64 = 0
r, _ := state.NewPlainStateReader(tx), state.NewPlainStateWriter(tx, tx, blockNr)
statedb := state.New(r)
stateReader, stateWriter := state.NewPlainStateReader(tx), state.NewPlainStateWriter(tx, tx, blockNr)
statedb := state.New(stateReader) //ibs
for addr, a := range accounts {
statedb.SetCode(addr, a.Code)
statedb.SetNonce(addr, a.Nonce)
Expand All @@ -299,7 +87,6 @@ func MakePreState(chainRules *params.Rules, tx kv.RwTx, accounts core.GenesisAll

if len(a.Code) > 0 || len(a.Storage) > 0 {
statedb.SetIncarnation(addr, state.FirstContractIncarnation)

var b [8]byte
binary.BigEndian.PutUint64(b[:], state.FirstContractIncarnation)
tx.Put(kv.IncarnationMap, addr[:], b[:])
Expand All @@ -312,12 +99,25 @@ func MakePreState(chainRules *params.Rules, tx kv.RwTx, accounts core.GenesisAll
if err := statedb.CommitBlock(chainRules, state.NewPlainStateWriter(tx, tx, blockNr+1)); err != nil {
panic(err)
}
return statedb
return stateReader, stateWriter
}

func rlpHash(x interface{}) (h common.Hash) {
hw := sha3.NewLegacyKeccak256()
rlp.Encode(hw, x) //nolint:errcheck
hw.Sum(h[:0])
return h
// calcDifficulty is based on ethash.CalcDifficulty. This method is used in case
// the caller does not provide an explicit difficulty, but instead provides only
// parent timestamp + difficulty.
// Note: this method only works for ethash engine.
func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64,
parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int {
uncleHash := parentUncleHash
if uncleHash == (common.Hash{}) {
uncleHash = types.EmptyUncleHash
}
parent := &types.Header{
ParentHash: common.Hash{},
UncleHash: uncleHash,
Difficulty: parentDifficulty,
Number: new(big.Int).SetUint64(number - 1),
Time: parentTime,
}
return ethash.CalcDifficulty(config, currentTime, parent.Time, parent.Difficulty, number-1, parent.UncleHash)
}
7 changes: 1 addition & 6 deletions cmd/evm/internal/t8ntool/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,6 @@ var (
Usage: "`stdin` or file name of where to find the transactions to apply.",
Value: "txs.json",
}
RewardFlag = cli.Int64Flag{
Name: "state.reward",
Usage: "Mining reward. Set to -1 to disable",
Value: 0,
}
ChainIDFlag = cli.Int64Flag{
Name: "state.chainid",
Usage: "ChainID to use",
Expand All @@ -103,7 +98,7 @@ var (
"\n\tSyntax <forkname>(+ExtraEip)",
strings.Join(tests.AvailableForks(), "\n\t "),
strings.Join(vm.ActivateableEips(), ", ")),
Value: "Istanbul",
Value: "ArrowGlacier",
}
VerbosityFlag = cli.IntFlag{
Name: "verbosity",
Expand Down
Loading