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

Trace Transfers #664

Merged
merged 12 commits into from
Jun 17, 2022
2 changes: 1 addition & 1 deletion arbos/retryables/retryable.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (rs *RetryableState) DeleteRetryable(id common.Hash, evm *vm.EVM, scenario
escrowAddress := RetryableEscrowAddress(id)
beneficiaryAddress := common.BytesToAddress(beneficiary[:])
amount := evm.StateDB.GetBalance(escrowAddress)
err = util.TransferBalance(&escrowAddress, &beneficiaryAddress, amount, evm, scenario)
err = util.TransferBalance(&escrowAddress, &beneficiaryAddress, amount, evm, scenario, "escrow")
if err != nil {
return false, err
}
Expand Down
41 changes: 23 additions & 18 deletions arbos/tx_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r
if p.msg.From() != arbosAddress {
return false, 0, errors.New("deposit not from arbAddress"), nil
}
util.MintBalance(p.msg.To(), p.msg.Value(), evm, util.TracingDuringEVM)
util.MintBalance(p.msg.To(), p.msg.Value(), evm, util.TracingDuringEVM, "deposit")
return true, 0, nil, nil
case *types.ArbitrumInternalTx:
defer (startTracer())()
Expand All @@ -131,22 +131,26 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r
scenario := util.TracingDuringEVM

// mint funds with the deposit, then charge fees later
util.MintBalance(&from, tx.DepositValue, evm, scenario)
util.MintBalance(&from, tx.DepositValue, evm, scenario, "deposit")

submissionFee := retryables.RetryableSubmissionFee(len(tx.RetryData), tx.L1BaseFee)
excessDeposit := arbmath.BigSub(tx.MaxSubmissionFee, submissionFee)
if excessDeposit.Sign() < 0 {
return true, 0, errors.New("max submission fee is less than the actual submission fee"), nil
}

transfer := func(from, to *common.Address, amount *big.Int) error {
return util.TransferBalance(from, to, amount, evm, scenario, "during evm execution")
}

// move balance to the relevant parties
if err := util.TransferBalance(&from, &networkFeeAccount, submissionFee, evm, scenario); err != nil {
if err := transfer(&from, &networkFeeAccount, submissionFee); err != nil {
return true, 0, err, nil
}
if err := util.TransferBalance(&from, &tx.FeeRefundAddr, excessDeposit, evm, scenario); err != nil {
if err := transfer(&from, &tx.FeeRefundAddr, excessDeposit); err != nil {
return true, 0, err, nil
}
if err := util.TransferBalance(&tx.From, &escrow, tx.Value, evm, scenario); err != nil {
if err := transfer(&tx.From, &escrow, tx.Value); err != nil {
return true, 0, err, nil
}

Expand Down Expand Up @@ -186,8 +190,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r
}

// pay for the retryable's gas and update the pools
err = util.TransferBalance(&tx.From, &networkFeeAccount, gascost, evm, scenario)
if err != nil {
if transfer(&tx.From, &networkFeeAccount, gascost) != nil {
// should be impossible because we just checked the tx.From balance
panic(err)
}
Expand Down Expand Up @@ -232,14 +235,14 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r

// Transfer callvalue from escrow
escrow := retryables.RetryableEscrowAddress(tx.TicketId)
err = util.TransferBalance(&escrow, &tx.From, tx.Value, evm, util.TracingBeforeEVM)
if err != nil {
scenario := util.TracingBeforeEVM
if util.TransferBalance(&escrow, &tx.From, tx.Value, evm, scenario, "escrow") != nil {
return true, 0, err, nil
}

// The redeemer has pre-paid for this tx's gas
basefee := evm.Context.BaseFee
util.MintBalance(&tx.From, arbmath.BigMulByUint(basefee, tx.Gas), evm, util.TracingBeforeEVM)
prepaid := arbmath.BigMulByUint(evm.Context.BaseFee, tx.Gas)
util.MintBalance(&tx.From, prepaid, evm, scenario, "prepaid")
ticketId := tx.TicketId
p.CurrentRetryable = &ticketId
}
Expand Down Expand Up @@ -324,6 +327,7 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) {
underlyingTx := p.msg.UnderlyingTransaction()
gasPrice := p.evm.Context.BaseFee
networkFeeAccount, _ := p.state.NetworkFeeAccount()
scenario := util.TracingAfterEVM

if gasLeft > p.msg.Gas() {
panic("Tx somehow refunds gas after computation")
Expand All @@ -335,13 +339,13 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) {
refund := arbmath.BigMulByUint(gasPrice, gasLeft)

// undo Geth's refund to the From address
err := util.TransferBalance(&inner.From, nil, refund, p.evm, util.TracingAfterEVM)
err := util.TransferBalance(&inner.From, nil, refund, p.evm, scenario, "ignore")
PlasmaPower marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Error("Uh oh, Geth didn't refund the user", inner.From, refund)
}

// refund the RefundTo by taking fees back from the network address
err = util.TransferBalance(&networkFeeAccount, &inner.RefundTo, refund, p.evm, util.TracingAfterEVM)
err = util.TransferBalance(&networkFeeAccount, &inner.RefundTo, refund, p.evm, scenario, "refund")
if err != nil {
// Normally the network fee address should be holding the gas funds.
// However, in theory, they could've been transfered out during the redeem attempt.
Expand All @@ -350,13 +354,13 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) {
}
if success {
// we don't want to charge for this
tracingInfo := util.NewTracingInfo(p.evm, arbosAddress, p.msg.From(), util.TracingAfterEVM)
tracingInfo := util.NewTracingInfo(p.evm, arbosAddress, p.msg.From(), scenario)
state := arbosState.OpenSystemArbosStateOrPanic(p.evm.StateDB, tracingInfo, false)
_, _ = state.RetryableState().DeleteRetryable(inner.TicketId, p.evm, util.TracingAfterEVM)
_, _ = state.RetryableState().DeleteRetryable(inner.TicketId, p.evm, scenario)
} else {
// return the Callvalue to escrow
escrow := retryables.RetryableEscrowAddress(inner.TicketId)
err := util.TransferBalance(&inner.From, &escrow, inner.Value, p.evm, util.TracingAfterEVM)
err := util.TransferBalance(&inner.From, &escrow, inner.Value, p.evm, scenario, "escrow")
if err != nil {
// should be impossible because geth credited the inner.Value to inner.From before the transaction
// and the transaction reverted
Expand All @@ -379,8 +383,9 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) {
computeCost = totalCost
}

util.MintBalance(&networkFeeAccount, computeCost, p.evm, util.TracingAfterEVM)
util.MintBalance(&p.evm.Context.Coinbase, p.PosterFee, p.evm, util.TracingAfterEVM)
purpose := "feeCollection"
util.MintBalance(&networkFeeAccount, computeCost, p.evm, scenario, purpose)
util.MintBalance(&p.evm.Context.Coinbase, p.PosterFee, p.evm, scenario, purpose)

if p.msg.GasPrice().Sign() > 0 { // in tests, gas price coud be 0
// ArbOS's gas pool is meant to enforce the computational speed-limit.
Expand Down
19 changes: 15 additions & 4 deletions arbos/util/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import (

// Represents a balance change occuring aside from a call.
// While most uses will be transfers, setting `from` or `to` to nil will mint or burn funds, respectively.
func TransferBalance(from, to *common.Address, amount *big.Int, evm *vm.EVM, scenario TracingScenario) error {
func TransferBalance(
from, to *common.Address,
amount *big.Int,
evm *vm.EVM,
scenario TracingScenario,
purpose string,
) error {
if from != nil {
balance := evm.StateDB.GetBalance(*from)
if arbmath.BigLessThan(balance, amount) {
Expand All @@ -38,7 +44,7 @@ func TransferBalance(from, to *common.Address, amount *big.Int, evm *vm.EVM, sce
}

if scenario != TracingDuringEVM {
tracer.CaptureArbitrumTransfer(evm, from, to, amount, scenario == TracingBeforeEVM)
tracer.CaptureArbitrumTransfer(evm, from, to, amount, scenario == TracingBeforeEVM, purpose)
return nil
}

Expand All @@ -61,9 +67,14 @@ func TransferBalance(from, to *common.Address, amount *big.Int, evm *vm.EVM, sce
}

// Mints funds for the user and adds them to their balance
func MintBalance(to *common.Address, amount *big.Int, evm *vm.EVM, scenario TracingScenario) {
err := TransferBalance(nil, to, amount, evm, scenario)
func MintBalance(to *common.Address, amount *big.Int, evm *vm.EVM, scenario TracingScenario, purpose string) {
err := TransferBalance(nil, to, amount, evm, scenario, purpose)
if err != nil {
panic(fmt.Sprintf("impossible error: %v", err))
}
}

// Burns funds from a user's account
func BurnBalance(from *common.Address, amount *big.Int, evm *vm.EVM, scenario TracingScenario, purpose string) error {
return TransferBalance(from, nil, amount, evm, scenario, purpose)
}
6 changes: 6 additions & 0 deletions arbstate/geth-hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ func (p ArbosPrecompileWrapper) RunAdvanced(
gasSupplied uint64,
info *vm.AdvancedPrecompileCall,
) (ret []byte, gasLeft uint64, err error) {

// Precompiles don't actually enter evm execution like normal calls do,
// so we need to increment the depth here to simulate the callstack change.
info.Evm.IncrementDepth()
defer info.Evm.DecrementDepth()

return p.inner.Call(
input, info.PrecompileAddress, info.ActingAsAddress,
info.Caller, info.Value, info.ReadOnly, gasSupplied, info.Evm,
Expand Down
7 changes: 5 additions & 2 deletions precompiles/ArbSys.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/offchainlabs/nitro/arbos/util"
"github.com/offchainlabs/nitro/util/arbmath"
"github.com/offchainlabs/nitro/util/merkletree"
)

Expand Down Expand Up @@ -147,7 +148,7 @@ func (con *ArbSys) SendTxToL1(c ctx, evm mech, value huge, destination addr, cal
if err != nil {
return nil, err
}
bigL1BlockNum := new(big.Int).SetUint64(l1BlockNum)
bigL1BlockNum := arbmath.UintToBig(l1BlockNum)

arbosState := c.State
sendHash, err := arbosState.KeccakHash(
Expand All @@ -174,7 +175,9 @@ func (con *ArbSys) SendTxToL1(c ctx, evm mech, value huge, destination addr, cal
}

// burn the callvalue, which was previously deposited to this precompile's account
evm.StateDB.SubBalance(con.Address, value)
if err := util.BurnBalance(&con.Address, value, evm, util.TracingDuringEVM, "l1Send"); err != nil {
PlasmaPower marked this conversation as resolved.
Show resolved Hide resolved
return nil, err
}

for _, merkleUpdateEvent := range merkleUpdateEvents {
position := merkletree.LevelAndLeaf{
Expand Down
1 change: 0 additions & 1 deletion precompiles/precompile.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,6 @@ func (p Precompile) Call(
gasSupplied uint64,
evm *vm.EVM,
) (output []byte, gasLeft uint64, err error) {

arbosVersion := arbosState.ArbOSVersion(evm.StateDB)

if arbosVersion < p.arbosVersion {
Expand Down