Skip to content

Commit

Permalink
Merge pull request ethereum#30 from OffchainLabs/retryables-update
Browse files Browse the repository at this point in the history
Retryables update
  • Loading branch information
rachel-bousfield authored Jan 7, 2022
2 parents 229bd44 + c69bca6 commit 286d3a2
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 52 deletions.
2 changes: 1 addition & 1 deletion arbitrum/apibackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (a *APIBackend) SyncProgress() ethereum.SyncProgress {
}

func (a *APIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil // TODO: Implement
return big.NewInt(0), nil // there's no tips in L2
}

func (a *APIBackend) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
Expand Down
52 changes: 25 additions & 27 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ import (

var emptyCodeHash = crypto.Keccak256Hash(nil)

type TxProcessingHook interface {
InterceptMessage() *ExecutionResult
GasChargingHook(gasRemaining *uint64) error
EndTxHook(totalGasUsed uint64, success bool) error
}

/*
The State Transitioning Model
Expand All @@ -56,8 +50,6 @@ The state transitioning model does all the necessary work to work out a valid ne
6) Derive new state root
*/
type StateTransition struct {
processingHook TxProcessingHook

gp *GasPool
msg Message
gas uint64
Expand Down Expand Up @@ -165,17 +157,14 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b
return gas, nil
}

var CreateTxProcessingHook func(msg Message, evm *vm.EVM) TxProcessingHook
var ReadyEVMForL2 func(evm *vm.EVM, msg Message)

// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
var processingHook TxProcessingHook
if CreateTxProcessingHook != nil {
processingHook = CreateTxProcessingHook(msg, evm)
if ReadyEVMForL2 != nil {
ReadyEVMForL2(evm, msg)
}
return &StateTransition{
processingHook: processingHook,

gp: gp,
evm: evm,
msg: msg,
Expand Down Expand Up @@ -302,7 +291,7 @@ func (st *StateTransition) transitionDbImpl() (*ExecutionResult, error) {
// 6. caller has enough balance to cover asset transfer for **topmost** call

// There are no tips in L2
if st.evm.ChainConfig().Arbitrum && st.gasPrice.Cmp(st.evm.Context.BaseFee) == -1 {
if st.evm.ChainConfig().IsArbitrum() && st.gasPrice.Cmp(st.evm.Context.BaseFee) == -1 {
st.gasPrice = st.evm.Context.BaseFee
}

Expand All @@ -327,11 +316,9 @@ func (st *StateTransition) transitionDbImpl() (*ExecutionResult, error) {
}
st.gas -= gas

if st.processingHook != nil {
err = st.processingHook.GasChargingHook(&st.gas)
if err != nil {
return nil, err
}
err = st.evm.ProcessingHook.GasChargingHook(&st.gas)
if err != nil {
return nil, err
}

// Check clause 6
Expand Down Expand Up @@ -376,12 +363,17 @@ func (st *StateTransition) transitionDbImpl() (*ExecutionResult, error) {
}

func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if st.processingHook != nil {
res := st.processingHook.InterceptMessage()
if res != nil {
return res, nil

isDeposit := st.evm.ProcessingHook.StartTxHook()
if isDeposit {
res := &ExecutionResult{
UsedGas: 0,
Err: nil,
ReturnData: nil,
}
return res, nil
}

res, err := st.transitionDbImpl()
if err != nil && !errors.Is(err, ErrNonceTooLow) && !errors.Is(err, ErrNonceTooHigh) {
res = &ExecutionResult{
Expand All @@ -392,15 +384,21 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
err = nil
}

if err == nil && st.processingHook != nil {
st.processingHook.EndTxHook(st.gas, res.Err == nil)
if err == nil {
st.evm.ProcessingHook.EndTxHook(st.gas, res.Err == nil)
}
return res, err
}

func (st *StateTransition) refundGas(refundQuotient uint64) {

nonrefundable := st.evm.ProcessingHook.NonrefundableGas()
if nonrefundable >= st.gasUsed() {
return
}

// Apply refund counter, capped to a refund quotient
refund := st.gasUsed() / refundQuotient
refund := (st.gasUsed() - nonrefundable) / refundQuotient
if refund > st.state.GetRefund() {
refund = st.state.GetRefund()
}
Expand Down
107 changes: 106 additions & 1 deletion core/types/arb_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,111 @@ func (tx *ArbitrumContractTx) rawSignatureValues() (v, r, s *big.Int) {
func (tx *ArbitrumContractTx) setSignatureValues(chainID, v, r, s *big.Int) {}
func (tx *ArbitrumContractTx) isFake() bool { return true }

type ArbitrumRetryTx struct {
ArbitrumContractTx
TicketId common.Hash
RefundTo common.Address
}

func (tx *ArbitrumRetryTx) txType() byte { return ArbitrumRetryTxType }

func (tx *ArbitrumRetryTx) copy() TxData {
return &ArbitrumRetryTx{
*tx.ArbitrumContractTx.copy().(*ArbitrumContractTx),
tx.TicketId,
tx.RefundTo,
}
}

func (tx *ArbitrumRetryTx) chainID() *big.Int { return tx.ArbitrumContractTx.chainID() }
func (tx *ArbitrumRetryTx) accessList() AccessList { return tx.ArbitrumContractTx.accessList() }
func (tx *ArbitrumRetryTx) data() []byte { return tx.ArbitrumContractTx.data() }
func (tx *ArbitrumRetryTx) gas() uint64 { return tx.ArbitrumContractTx.gas() }
func (tx *ArbitrumRetryTx) gasPrice() *big.Int { return tx.ArbitrumContractTx.gasPrice() }
func (tx *ArbitrumRetryTx) gasTipCap() *big.Int { return tx.ArbitrumContractTx.gasTipCap() }
func (tx *ArbitrumRetryTx) gasFeeCap() *big.Int { return tx.ArbitrumContractTx.gasFeeCap() }
func (tx *ArbitrumRetryTx) value() *big.Int { return tx.ArbitrumContractTx.value() }
func (tx *ArbitrumRetryTx) nonce() uint64 { return tx.ArbitrumContractTx.nonce() }
func (tx *ArbitrumRetryTx) to() *common.Address { return tx.ArbitrumContractTx.to() }
func (tx *ArbitrumRetryTx) rawSignatureValues() (v, r, s *big.Int) {
return tx.ArbitrumContractTx.rawSignatureValues()
}
func (tx *ArbitrumRetryTx) setSignatureValues(chainID, v, r, s *big.Int) {
tx.ArbitrumContractTx.setSignatureValues(chainID, v, r, s)
}
func (tx *ArbitrumRetryTx) isFake() bool { return true }

type ArbitrumSubmitRetryableTx struct {
ChainId *big.Int
RequestId common.Hash
From common.Address

DepositValue *big.Int
GasPrice *big.Int // wei per gas
Gas uint64 // gas limit
To *common.Address `rlp:"nil"` // nil means contract creation
Value *big.Int // wei amount
Beneficiary common.Address
SubmissionFeePaid *big.Int
FeeRefundAddr common.Address
Data []byte // contract invocation input data
}

func (tx *ArbitrumSubmitRetryableTx) txType() byte { return ArbitrumSubmitRetryableTxType }

func (tx *ArbitrumSubmitRetryableTx) copy() TxData {
cpy := &ArbitrumSubmitRetryableTx{
ChainId: new(big.Int),
RequestId: tx.RequestId,
DepositValue: new(big.Int),
GasPrice: new(big.Int),
Gas: tx.Gas,
From: tx.From,
To: tx.To,
Value: new(big.Int),
Beneficiary: tx.Beneficiary,
SubmissionFeePaid: new(big.Int),
FeeRefundAddr: tx.FeeRefundAddr,
Data: common.CopyBytes(tx.Data),
}
if tx.ChainId != nil {
cpy.ChainId.Set(tx.ChainId)
}
if tx.DepositValue != nil {
cpy.DepositValue.Set(tx.DepositValue)
}
if tx.GasPrice != nil {
cpy.GasPrice.Set(tx.GasPrice)
}
if tx.To != nil {
tmp := *tx.To
cpy.To = &tmp
}
if tx.Value != nil {
cpy.Value.Set(tx.Value)
}
if tx.SubmissionFeePaid != nil {
cpy.SubmissionFeePaid.Set(tx.SubmissionFeePaid)
}
return cpy
}

func (tx *ArbitrumSubmitRetryableTx) chainID() *big.Int { return tx.ChainId }
func (tx *ArbitrumSubmitRetryableTx) accessList() AccessList { return nil }
func (tx *ArbitrumSubmitRetryableTx) data() []byte { return tx.Data }
func (tx *ArbitrumSubmitRetryableTx) gas() uint64 { return tx.Gas }
func (tx *ArbitrumSubmitRetryableTx) gasPrice() *big.Int { return tx.GasPrice }
func (tx *ArbitrumSubmitRetryableTx) gasTipCap() *big.Int { return tx.GasPrice }
func (tx *ArbitrumSubmitRetryableTx) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *ArbitrumSubmitRetryableTx) value() *big.Int { return tx.Value }
func (tx *ArbitrumSubmitRetryableTx) nonce() uint64 { return 0 }
func (tx *ArbitrumSubmitRetryableTx) to() *common.Address { return tx.To }
func (tx *ArbitrumSubmitRetryableTx) rawSignatureValues() (v, r, s *big.Int) {
return bigZero, bigZero, bigZero
}
func (tx *ArbitrumSubmitRetryableTx) setSignatureValues(chainID, v, r, s *big.Int) {}
func (tx *ArbitrumSubmitRetryableTx) isFake() bool { return true }

type ArbitrumDepositTx struct {
ChainId *big.Int
L1RequestId common.Hash
Expand Down Expand Up @@ -159,7 +264,7 @@ func (d *ArbitrumDepositTx) copy() TxData {
func (d *ArbitrumDepositTx) chainID() *big.Int { return d.ChainId }
func (d *ArbitrumDepositTx) accessList() AccessList { return nil }
func (d *ArbitrumDepositTx) data() []byte { return nil }
func (d ArbitrumDepositTx) gas() uint64 { return 0 }
func (d *ArbitrumDepositTx) gas() uint64 { return 0 }
func (d *ArbitrumDepositTx) gasPrice() *big.Int { return bigZero }
func (d *ArbitrumDepositTx) gasTipCap() *big.Int { return bigZero }
func (d *ArbitrumDepositTx) gasFeeCap() *big.Int { return bigZero }
Expand Down
8 changes: 8 additions & 0 deletions core/types/arbitrum_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ func (s arbitrumSigner) Sender(tx *Transaction) (common.Address, error) {
return inner.From, nil
case *ArbitrumDepositTx:
return arbAddress, nil
case *ArbitrumRetryTx:
return inner.From, nil
case *ArbitrumSubmitRetryableTx:
return inner.From, nil
default:
return s.Signer.Sender(tx)
}
Expand All @@ -40,6 +44,10 @@ func (s arbitrumSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *b
return bigZero, bigZero, bigZero, nil
case *ArbitrumDepositTx:
return bigZero, bigZero, bigZero, nil
case *ArbitrumRetryTx:
return bigZero, bigZero, bigZero, nil
case *ArbitrumSubmitRetryableTx:
return bigZero, bigZero, bigZero, nil
default:
return s.Signer.SignatureValues(tx, sig)
}
Expand Down
22 changes: 18 additions & 4 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ const (
LegacyTxType = iota
AccessListTxType
DynamicFeeTxType
ArbitrumDepositTxType = 100
ArbitrumUnsignedTxType = 101
ArbitrumContractTxType = 102
ArbitrumWrappedTxType = 103
ArbitrumDepositTxType = 100
ArbitrumUnsignedTxType = 101
ArbitrumContractTxType = 102
ArbitrumWrappedTxType = 103
ArbitrumRetryTxType = 104
ArbitrumSubmitRetryableTxType = 105
)

// Transaction is an Ethereum transaction.
Expand Down Expand Up @@ -201,6 +203,14 @@ func (tx *Transaction) decodeTyped(b []byte, arbParsing bool) (TxData, error) {
var inner ArbitrumWrappedTx
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
case ArbitrumRetryTxType:
var inner ArbitrumRetryTx
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
case ArbitrumSubmitRetryableTxType:
var inner ArbitrumSubmitRetryableTx
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
}
}
switch b[0] {
Expand Down Expand Up @@ -406,6 +416,10 @@ func (tx *Transaction) Hash() common.Hash {
var h common.Hash
if tx.Type() == LegacyTxType {
h = rlpHash(tx.inner)
} else if tx.Type() == ArbitrumSubmitRetryableTxType {
h = tx.inner.(*ArbitrumSubmitRetryableTx).RequestId // this is required by the retryables API
} else if tx.Type() == ArbitrumRetryTxType {
h = tx.inner.(*ArbitrumRetryTx).RequestId // for this type, RequestId was initialized with the desired tx hash
} else {
h = prefixedRlpHash(tx.Type(), tx.inner)
}
Expand Down
10 changes: 9 additions & 1 deletion core/types/transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
default:
signer = FrontierSigner{}
}
if config.IsArbitrum(blockNumber) {
if config.IsArbitrum() {
signer = NewArbitrumSigner(signer)
}
return signer
Expand Down Expand Up @@ -277,6 +277,14 @@ func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) {
// AL txs are defined to use 0 and 1 as their recovery
// id, add 27 to become equivalent to unprotected Homestead signatures.
V = new(big.Int).Add(V, big.NewInt(27))
case ArbitrumDepositTxType:
return tx.inner.(*ArbitrumDepositTx).To, nil
case ArbitrumUnsignedTxType:
return tx.inner.(*ArbitrumUnsignedTx).From, nil
case ArbitrumContractTxType:
return tx.inner.(*ArbitrumContractTx).From, nil
case ArbitrumWrappedTxType:
return s.Sender(NewTx(tx.inner.(*ArbitrumWrappedTx).TxData))
default:
return common.Address{}, ErrTxTypeNotSupported
}
Expand Down
42 changes: 42 additions & 0 deletions core/vm/arbitrum_evm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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 vm

type TxProcessingHook interface {
StartTxHook() bool
GasChargingHook(gasRemaining *uint64) error
EndTxHook(totalGasUsed uint64, success bool)
NonrefundableGas() uint64
}

type DefaultTxProcessor struct{}

func (p DefaultTxProcessor) StartTxHook() bool {
return false
}

func (p DefaultTxProcessor) GasChargingHook(gasRemaining *uint64) error {
return nil
}

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

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

0 comments on commit 286d3a2

Please sign in to comment.