Skip to content

Commit

Permalink
Merge pull request #48 from OffchainLabs/schedule-txes
Browse files Browse the repository at this point in the history
Schedule Transactions
  • Loading branch information
rachel-bousfield committed Feb 9, 2022
2 parents 3829645 + cdcb95b commit bf2301d
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 26 deletions.
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
26 changes: 26 additions & 0 deletions core/state/statedb_arbitrum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// Package state provides a caching layer atop the Ethereum state trie.
package state

import (
"github.com/ethereum/go-ethereum/core/types"
)

func (s *StateDB) GetCurrentTxLogs() []*types.Log {
return s.logs[s.thash]
}
12 changes: 6 additions & 6 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return 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, _, 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)
}
Expand All @@ -92,15 +92,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return receipts, allLogs, *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, *ExecutionResult, error) {
// Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg)
evm.Reset(txContext, statedb)

// 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 @@ -134,17 +134,17 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
receipt.BlockHash = blockHash
receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
return receipt, err
return receipt, result, 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, *ExecutionResult, 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
11 changes: 8 additions & 3 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ type ExecutionResult struct {
UsedGas uint64 // Total used gas but include the refunded gas
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)

// Arbitrum: a tx may yield others that need to run afterward (see retryables)
ScheduledTxes types.Transactions
}

// Unwrap returns the internal evm error which allows us for further
Expand Down Expand Up @@ -366,15 +369,17 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
endTxNow, usedGas, err, returnData := st.evm.ProcessingHook.StartTxHook()
if endTxNow {
return &ExecutionResult{
UsedGas: usedGas,
Err: err,
ReturnData: returnData,
UsedGas: usedGas,
Err: err,
ReturnData: returnData,
ScheduledTxes: st.evm.ProcessingHook.ScheduledTxes(),
}, nil
}

res, err := st.transitionDbImpl()
if err == nil {
st.evm.ProcessingHook.EndTxHook(st.gas, res.Err == nil)
res.ScheduledTxes = st.evm.ProcessingHook.ScheduledTxes()
}
return res, err
}
Expand Down
24 changes: 15 additions & 9 deletions core/vm/evm_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package vm

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

// Depth returns the current depth
Expand All @@ -28,11 +29,12 @@ func (evm *EVM) Depth() int {
type TxProcessingHook interface {
StartTxHook() (bool, uint64, error, []byte) // return 4-tuple rather than *struct to avoid an import cycle
GasChargingHook(gasRemaining *uint64) error
EndTxHook(totalGasUsed uint64, success bool)
NonrefundableGas() uint64
ForceRefundGas() uint64
PushCaller(addr common.Address)
PopCaller()
ForceRefundGas() uint64
NonrefundableGas() uint64
EndTxHook(totalGasUsed uint64, evmSuccess bool)
ScheduledTxes() types.Transactions
L1BlockNumber(blockCtx BlockContext) (uint64, error)
L1BlockHash(blockCtx BlockContext, l1BlocKNumber uint64) (common.Hash, error)
}
Expand All @@ -47,26 +49,30 @@ func (p DefaultTxProcessor) GasChargingHook(gasRemaining *uint64) error {
return nil
}

func (p DefaultTxProcessor) EndTxHook(totalGasUsed uint64, success bool) {
func (p DefaultTxProcessor) PushCaller(addr common.Address) {
return
}

func (p DefaultTxProcessor) NonrefundableGas() uint64 {
return 0
func (p DefaultTxProcessor) PopCaller() {
return
}

func (p DefaultTxProcessor) ForceRefundGas() uint64 {
return 0
}

func (p DefaultTxProcessor) PushCaller(addr common.Address) {
return
func (p DefaultTxProcessor) NonrefundableGas() uint64 {
return 0
}

func (p DefaultTxProcessor) PopCaller() {
func (p DefaultTxProcessor) EndTxHook(totalGasUsed uint64, evmSuccess bool) {
return
}

func (p DefaultTxProcessor) ScheduledTxes() types.Transactions {
return types.Transactions{}
}

func (p DefaultTxProcessor) L1BlockNumber(blockCtx BlockContext) (uint64, error) {
return blockCtx.BlockNumber.Uint64(), nil
}
Expand Down
1 change: 1 addition & 0 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type StateDB interface {

AddLog(*types.Log)
AddPreimage(common.Hash, []byte)
GetCurrentTxLogs() []*types.Log

ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
}
Expand Down
2 changes: 1 addition & 1 deletion eth/catalyst/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type blockExecutionEnv struct {
func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error {
vmconfig := *env.chain.GetVMConfig()
snap := env.state.Snapshot()
receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig)
receipt, _, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig)
if err != nil {
env.state.RevertToSnapshot(snap)
return err
Expand Down
4 changes: 2 additions & 2 deletions graphql/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ func (b *Block) Call(ctx context.Context, args struct {
return nil, err
}
}
result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, b.backend.RPCEVMTimeout(), b.backend.RPCGasCap())
result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, b.backend.RPCEVMTimeout(), b.backend.RPCGasCap(), false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1023,7 +1023,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
Data ethapi.TransactionArgs
}) (*CallResult, error) {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, p.backend.RPCEVMTimeout(), p.backend.RPCGasCap())
result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, p.backend.RPCEVMTimeout(), p.backend.RPCGasCap(), false)
if err != nil {
return nil, err
}
Expand Down
38 changes: 35 additions & 3 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
return nil
}

func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64, runScheduledTxes bool) (*core.ExecutionResult, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())

state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
Expand Down Expand Up @@ -932,6 +932,38 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
if err != nil {
return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas())
}

// Arbitrum: a tx can schedule another (see retryables)
scheduled := result.ScheduledTxes
for runScheduledTxes && len(scheduled) > 0 {
// make a new EVM for the scheduled Tx (an EVM must never be reused)
evm, vmError, err := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true})
if err != nil {
return nil, err
}
go func() {
<-ctx.Done()
evm.Cancel()
}()

// This will panic if the scheduled tx is signed, but we only schedule unsigned ones
msg, err := scheduled[0].AsMessage(types.NewArbitrumSigner(nil), header.BaseFee)
if err != nil {
return nil, err
}
scheduledTxResult, err := core.ApplyMessage(evm, msg, gp)
if err != nil {
return nil, err // Bail out
}
if err := vmError(); err != nil {
return nil, err
}
if scheduledTxResult.Failed() {
return scheduledTxResult, nil
}
scheduled = append(scheduled[1:], scheduledTxResult.ScheduledTxes...)
}

return result, nil
}

Expand Down Expand Up @@ -972,7 +1004,7 @@ func (e *revertError) ErrorData() interface{} {
// Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values.
func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap(), false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1057,7 +1089,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
args.Gas = (*hexutil.Uint64)(&gas)

result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap, true)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
Expand Down
2 changes: 1 addition & 1 deletion miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ func (w *worker) updateSnapshot() {
func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {
snap := w.current.state.Snapshot()

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

0 comments on commit bf2301d

Please sign in to comment.