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

Schedule Transactions #48

Merged
merged 9 commits into from
Feb 9, 2022
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
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, types.Transactions, 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.ScheduledTxes, 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.Transactions, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add redeem fields to Reciept instead of adding a return parameter? With "omitempty" they won't affect any transaction that's not a redeem. For those that do have a redeem it shouldn't be noticed by tools that don't understand the field, and might help those that are aware of the field.. (need to ask Fred)

If not - I'd say it's better to return ExecutionResult than Transaction. Seems less tailored and more flexible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea, that reduced the diff :)
I spoke to Fred and he said we've already added fields before, so it'll work with tooling.

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
15 changes: 9 additions & 6 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@ type Message interface {
// ExecutionResult includes all output after executing given evm
// message no matter the execution itself is successful or not.
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)
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)
ScheduledTxes types.Transactions // Arbitrum: a tx may yield others that need to run afterward (see retryables)
rachel-bousfield marked this conversation as resolved.
Show resolved Hide resolved
}

// Unwrap returns the internal evm error which allows us for further
Expand Down Expand Up @@ -367,9 +368,10 @@ 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
}

Expand All @@ -387,6 +389,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {

if err == nil {
st.evm.ProcessingHook.EndTxHook(st.gas, transitionSuccess, res.Err == nil)
res.ScheduledTxes = st.evm.ProcessingHook.ScheduledTxes()
}
return res, err
}
Expand Down
18 changes: 12 additions & 6 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,10 +29,11 @@ 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, transitionSuccess bool, evmSuccess bool)
NonrefundableGas() uint64
PushCaller(addr common.Address)
PopCaller()
NonrefundableGas() uint64
EndTxHook(totalGasUsed uint64, transitionSuccess bool, evmSuccess bool)
ScheduledTxes() types.Transactions
L1BlockNumber(blockCtx BlockContext) (uint64, error)
L1BlockHash(blockCtx BlockContext, l1BlocKNumber uint64) (common.Hash, error)
}
Expand All @@ -46,20 +48,24 @@ func (p DefaultTxProcessor) GasChargingHook(gasRemaining *uint64) error {
return nil
}

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

func (p DefaultTxProcessor) PopCaller() {
return
}

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

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

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

func (p DefaultTxProcessor) L1BlockNumber(blockCtx BlockContext) (uint64, error) {
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)
Logs() []*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
27 changes: 25 additions & 2 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1057,14 +1057,37 @@ 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)
primary, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
}
return true, nil, err // Bail out
}
return result.Failed(), result, nil
if primary.Failed() {
return primary.Failed(), primary, nil
}

// Arbitrum: a tx can schedule another (see retryables)
scheduled := primary.ScheduledTxes
for len(scheduled) > 0 {
args, err := TransactArgsFromArbitrumTx(scheduled[0])
if err != nil {
return true, nil, err // Bail out
}
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
}
return true, nil, err // Bail out
}
if result.Failed() {
return result.Failed(), result, nil
}
scheduled = append(scheduled[1:], result.ScheduledTxes...)
}
return false, primary, nil
}
// Execute the binary search and hone in on an executable gas limit
for lo+1 < hi {
Expand Down
46 changes: 46 additions & 0 deletions internal/ethapi/transaction_args_arbitrum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2021 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 ethapi

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

func TransactArgsFromArbitrumTx(tx *types.Transaction) (TransactionArgs, error) {
msg, err := tx.AsMessage(types.NewArbitrumSigner(nil), tx.GasPrice())
rachel-bousfield marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return TransactionArgs{}, err
}
from := msg.From()
gas := hexutil.Uint64(msg.Gas())
nonce := hexutil.Uint64(msg.Nonce())
input := hexutil.Bytes(tx.Data())
accessList := tx.AccessList()
return TransactionArgs{
From: &from,
To: msg.To(),
Gas: &gas,
MaxFeePerGas: (*hexutil.Big)(msg.GasFeeCap()),
MaxPriorityFeePerGas: (*hexutil.Big)(msg.GasTipCap()),
Value: (*hexutil.Big)(msg.Value()),
Nonce: &nonce,
Input: &input,
AccessList: &accessList,
ChainID: (*hexutil.Big)(tx.ChainId()),
}, nil
}
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