Skip to content

Commit

Permalink
Internal txs and vmErr logic
Browse files Browse the repository at this point in the history
  • Loading branch information
quentinlesceller committed Feb 17, 2022
1 parent fdae1a8 commit 85b9d34
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 17 deletions.
2 changes: 1 addition & 1 deletion core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1591,7 +1591,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)

// Process block using the parent state as reference point
substart := time.Now()
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
receipts, logs, _, _, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
if err != nil {
bc.reportBlock(block, receipts, err)
atomic.StoreUint32(&followupInterrupt, 1)
Expand Down
2 changes: 1 addition & 1 deletion core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
if err != nil {
return err
}
receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
receipts, _, _, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{})
if err != nil {
blockchain.reportBlock(block, receipts, err)
return err
Expand Down
2 changes: 1 addition & 1 deletion core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
b.SetCoinbase(common.Address{})
}
b.statedb.Prepare(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
receipt, _, _, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
Expand Down
84 changes: 84 additions & 0 deletions core/internals_processor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package core

import (
"math/big"

"github.com/blockcypher/go-ethereum/common"
"github.com/blockcypher/go-ethereum/core/types"
"github.com/blockcypher/go-ethereum/core/vm"
)

// Implementation of evm.InternalTxListener

type InternalTxWatcher struct {
internals types.InternalTransactions
}

func NewInternalTxWatcher() *InternalTxWatcher {
return &InternalTxWatcher{
internals: make(types.InternalTransactions, 0),
}
}

// Public API: for users
func (self *InternalTxWatcher) SetParentHash(ph common.Hash) {
for i := range self.internals {
self.internals[i].ParentHash = ph
}
}

func (self *InternalTxWatcher) InternalTransactions() types.InternalTransactions {
return self.internals
}

// Public API: For interfacing with EVM
func (self *InternalTxWatcher) RegisterCall(nonce uint64, gasPrice *big.Int, gas uint64, srcAddr, dstAddr common.Address, value *big.Int, data []byte, depth uint64) {
self.internals = append(self.internals,
types.NewInternalTransaction(nonce, gasPrice, gas,
srcAddr, dstAddr, value, data, depth, self.index(), "call"))
}

func (self *InternalTxWatcher) RegisterStaticCall(nonce uint64, gasPrice *big.Int, gas uint64, srcAddr, dstAddr common.Address, data []byte, depth uint64) {

self.internals = append(self.internals,
types.NewInternalTransaction(nonce, gasPrice, gas,
srcAddr, dstAddr, big.NewInt(0), data, depth, self.index(),
"staticcall"))
}

func (self *InternalTxWatcher) RegisterCallCode(nonce uint64, gasPrice *big.Int, gas uint64, contractAddr common.Address, value *big.Int, data []byte, depth uint64) {
self.internals = append(self.internals,
types.NewInternalTransaction(nonce, gasPrice, gas,
contractAddr, contractAddr, value, data, depth, self.index(),
"call"))
}

func (self *InternalTxWatcher) RegisterCreate(nonce uint64, gasPrice *big.Int, gas uint64, srcAddr, newContractAddr common.Address, value *big.Int, data []byte, depth uint64) {
self.internals = append(self.internals,
types.NewInternalTransaction(nonce, gasPrice, gas,
srcAddr, newContractAddr, value, data, depth, self.index(),
"create"))
}

func (self *InternalTxWatcher) RegisterDelegateCall(nonce uint64, gasPrice *big.Int, gas uint64, callerAddr common.Address, value *big.Int, data []byte, depth uint64) {
self.internals = append(self.internals,
types.NewInternalTransaction(nonce, gasPrice, gas,
callerAddr, callerAddr, value, data, depth, self.index(), "call"))
}

func (self *InternalTxWatcher) RegisterSuicide(nonce uint64, gasPrice *big.Int, gas uint64, contractAddr, creatorAddr common.Address, remainingValue *big.Int, depth uint64) {
self.internals = append(self.internals,
types.NewInternalTransaction(nonce, gasPrice, gas,
contractAddr, creatorAddr, remainingValue,
append([]byte{byte(vm.SELFDESTRUCT)}, creatorAddr[:]...),
depth, self.index(), "suicide"))
}

// Utilities
func (self *InternalTxWatcher) index() uint64 {
return uint64(len(self.internals))
}

func toBigInt(g uint64) *big.Int {
return big.NewInt(0).SetUint64(g)
}
33 changes: 23 additions & 10 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func NewStateProcessor(config *params.ChainConfig, bc blockchain, engine consens
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, []types.InternalTransactions, []string, uint64, error) {
var (
receipts types.Receipts
usedGas = new(uint64)
Expand All @@ -71,6 +71,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
blockNumber = block.Number()
allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit())
intTxs []types.InternalTransactions
vmerrs []string
)
// Mutate the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
Expand All @@ -82,31 +84,36 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
for i, tx := range block.Transactions() {
msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee)
if err != nil {
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
return nil, nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
statedb.Prepare(tx.Hash(), i)
receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
receipt, internals, vmerr, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
if err != nil {
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
return nil, nil, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, receipt.Logs...)
intTxs = append(intTxs, internals)
vmerrs = append(vmerrs, vmerr)
}
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles())

return receipts, allLogs, *usedGas, nil
return receipts, allLogs, intTxs, vmerrs, *usedGas, nil
}

func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, types.InternalTransactions, string, error) {
// Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg)
evm.Reset(txContext, statedb)

itx := NewInternalTxWatcher()
evm.AddListener(itx)

// Apply the transaction to the current state (included in the env).
result, err := ApplyMessage(evm, msg, gp)
if err != nil {
return nil, err
return nil, nil, "", err
}

// Update the state with pending changes.
Expand Down Expand Up @@ -140,17 +147,23 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
receipt.BlockHash = blockHash
receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
return receipt, err

itx.SetParentHash(tx.Hash())
if result.Err != nil {
return receipt, itx.InternalTransactions(), result.Err.Error(), err
}

return receipt, itx.InternalTransactions(), "", err
}

// ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, types.InternalTransactions, string, error) {
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee)
if err != nil {
return nil, err
return nil, nil, "", err
}
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author)
Expand Down
2 changes: 1 addition & 1 deletion core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,5 @@ type Processor interface {
// Process processes the state changes according to the Ethereum rules by running
// the transaction messages using the statedb and applying any rewards to both
// the processor (coinbase) and any included uncles.
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error)
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, []types.InternalTransactions, []string, uint64, error)
}
57 changes: 57 additions & 0 deletions core/types/internal_tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package types

import (
"math/big"

"github.com/blockcypher/go-ethereum/common"
)

type InternalTransaction struct {
Transaction Transaction

Sender common.Address
ParentHash common.Hash
Depth uint64
Index uint64
Note string
Rejected bool
}

type InternalTransactions []*InternalTransaction

func NewInternalTransaction(accountNonce uint64, price *big.Int,
gasLimit uint64, sender common.Address,
recipient common.Address, amount *big.Int, payload []byte,
depth, index uint64, note string) *InternalTransaction {

tx := NewTransaction(accountNonce, recipient, amount, gasLimit, price, payload)
var h common.Hash
return &InternalTransaction{*tx, sender, h, depth, index, note, false}
}

func (tx *InternalTransaction) Reject() {
tx.Rejected = true
}

func (tx *InternalTransaction) Hash() common.Hash {
rej := byte(0)
if tx.Rejected {
rej = byte(1)
}

data := []interface{}{
tx.Transaction.Nonce(),
tx.ParentHash,
tx.Sender,
*tx.Transaction.To(),
tx.Transaction.Value(),
tx.Transaction.GasPrice(),
tx.Transaction.Gas(),
tx.Transaction.Data(),
tx.Note,
tx.Depth,
tx.Index,
rej,
}
return rlpHash(data)
}
2 changes: 1 addition & 1 deletion eth/state_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state
if current = eth.blockchain.GetBlockByNumber(next); current == nil {
return nil, fmt.Errorf("block #%d not found", next)
}
_, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{})
_, _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{})
if err != nil {
return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err)
}
Expand Down
4 changes: 2 additions & 2 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"sync/atomic"
"time"

mapset "github.com/deckarep/golang-set"
"github.com/blockcypher/go-ethereum/common"
"github.com/blockcypher/go-ethereum/consensus"
"github.com/blockcypher/go-ethereum/consensus/misc"
Expand All @@ -35,6 +34,7 @@ import (
"github.com/blockcypher/go-ethereum/log"
"github.com/blockcypher/go-ethereum/params"
"github.com/blockcypher/go-ethereum/trie"
mapset "github.com/deckarep/golang-set"
)

const (
Expand Down Expand Up @@ -830,7 +830,7 @@ func (w *worker) updateSnapshot(env *environment) {
func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) {
snap := env.state.Snapshot()

receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
receipt, _, _, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
if err != nil {
env.state.RevertToSnapshot(snap)
return nil, err
Expand Down

0 comments on commit 85b9d34

Please sign in to comment.