Skip to content

Commit

Permalink
Merge remote-tracking branch 'es/op-es' into merge
Browse files Browse the repository at this point in the history
 Conflicts:
	core/state_transition.go
  • Loading branch information
blockchaindevsh committed Oct 14, 2024
2 parents fb6c573 + 1ef7e80 commit 9dba4cd
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 121 deletions.
147 changes: 35 additions & 112 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ import (
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/util"
"github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
Expand Down Expand Up @@ -246,127 +246,54 @@ func (st *StateTransition) to() common.Address {
return *st.msg.To
}

var SoulGasTokenABI abi.ABI
const (
// should keep it in sync with the balances field of SoulGasToken contract
BalancesSlot = uint64(51)
)

var (
slotArgs abi.Arguments
)

func init() {
var err error
SoulGasTokenABI, err = util.ParseFunctionsAsABI([]string{
"function deposit()", // used by op_geth_test.go
"function balanceOf(address account) returns (uint256 balance)",
"function burnFrom(address account, uint256 value)",
"function batchMint(address[] accounts, uint256[] values)"})
if err != nil {
panic(err)
}
uint64Ty, _ := abi.NewType("uint64", "", nil)
addressTy, _ := abi.NewType("address", "", nil)
slotArgs = abi.Arguments{{Name: "addr", Type: addressTy, Indexed: false}, {Name: "slot", Type: uint64Ty, Indexed: false}}
}

func getSoulBalanceData(account common.Address) []byte {
method, ok := SoulGasTokenABI.Methods["balanceOf"]
if !ok {
panic("balanceOf method not found")
}

argument, err := method.Inputs.Pack(account)
if err != nil {
panic("failed to pack argument")
}

data := make([]byte, len(method.ID)+len(argument))
copy(data[0:len(method.ID)], method.ID)
copy(data[len(method.ID):], argument)
return data
func TargetSGTBalanceSlot(account common.Address) (slot common.Hash) {
data, _ := slotArgs.Pack(account, BalancesSlot)
slot = crypto.Keccak256Hash(data)
return
}

func parseSoulBalanceResp(ret []byte) (*uint256.Int, error) {
method, ok := SoulGasTokenABI.Methods["balanceOf"]
if !ok {
panic("balanceOf method not found")
}

returnValueInterface, err := method.Outputs.Unpack(ret)
if err != nil {
return nil, fmt.Errorf("parseSoulBalanceResp Unpack failed:%w", err)
}

returnValue := new(big.Int)
err = method.Outputs.Copy(&returnValue, returnValueInterface)
if err != nil {
return nil, fmt.Errorf("parseSoulBalanceResp Copy failed:%w", err)
}
returnValueU256, overflow := uint256.FromBig(returnValue)
if overflow {
return nil, fmt.Errorf("parsed soul balance overflow:%v", returnValue)
}
return returnValueU256, nil
func (st *StateTransition) GetSoulBalance(account common.Address) *uint256.Int {
slot := TargetSGTBalanceSlot(account)
value := st.state.GetState(types.SoulGasTokenAddr, slot)
balance := new(uint256.Int)
balance.SetBytes(value[:])
return balance
}

func burnSoulBalanceData(account common.Address, amount *big.Int) []byte {

method, ok := SoulGasTokenABI.Methods["burnFrom"]
if !ok {
panic("burnFrom method not found")
}

argument, err := method.Inputs.Pack(account, amount)
if err != nil {
panic("failed to pack argument")
}

data := make([]byte, len(method.ID)+len(argument))
copy(data[0:len(method.ID)], method.ID)
copy(data[len(method.ID):], argument)
return data
}

func mintSoulBalanceData(account common.Address, amount *big.Int) []byte {
method, ok := SoulGasTokenABI.Methods["batchMint"]
if !ok {
panic("batchMint method not found")
}

argument, err := method.Inputs.Pack([]common.Address{account}, []*big.Int{amount})
if err != nil {
panic("failed to pack argument")
func (st *StateTransition) SubSoulBalance(account common.Address, amount *big.Int, reason tracing.BalanceChangeReason) (err error) {
current := st.GetSoulBalance(account).ToBig()
if current.Cmp(amount) < 0 {
return fmt.Errorf("soul balance not enough, current:%v, expect:%v", current, amount)
}

data := make([]byte, len(method.ID)+len(argument))
copy(data[0:len(method.ID)], method.ID)
copy(data[len(method.ID):], argument)
return data
}

var (
callSoulGasLimit = uint64(8_000_000)
// use hardcoded DEPOSITOR_ACCOUNT both as minter and burner
depositorAddress = common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001")
)

func (st *StateTransition) GetSoulBalance(account common.Address) (*uint256.Int, error) {
// this evm call is free of gas charging
ret, _, vmerr := st.evm.StaticCall(vm.AccountRef(account), types.SoulGasTokenAddr, getSoulBalanceData(account), callSoulGasLimit)
if vmerr != nil {
return nil, vmerr
}
return parseSoulBalanceResp(ret)
}
value := uint256.MustFromBig(current.Sub(current, amount)).Bytes32()
st.state.SetState(types.SoulGasTokenAddr, TargetSGTBalanceSlot(account), value)

func (st *StateTransition) SubSoulBalance(account common.Address, amount *big.Int, reason tracing.BalanceChangeReason) (err error) {
_, _, err = st.evm.Call(vm.AccountRef(depositorAddress), types.SoulGasTokenAddr, burnSoulBalanceData(account, amount), callSoulGasLimit, common.U2560)
if err == nil {
if st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.IsSoulBackedByNative {
st.state.SubBalance(types.SoulGasTokenAddr, uint256.MustFromBig(amount), reason)
}
if st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.IsSoulBackedByNative {
st.state.SubBalance(types.SoulGasTokenAddr, uint256.MustFromBig(amount), reason)
}
return
}

func (st *StateTransition) AddSoulBalance(account common.Address, amount *big.Int, reason tracing.BalanceChangeReason) {

_, _, err := st.evm.Call(vm.AccountRef(depositorAddress), types.SoulGasTokenAddr, mintSoulBalanceData(account, amount), callSoulGasLimit, common.U2560)

if err != nil {
panic(fmt.Sprintf("mint should never fail:%v", err))
}
current := st.GetSoulBalance(account).ToBig()
value := uint256.MustFromBig(current.Add(current, amount)).Bytes32()
st.state.SetState(types.SoulGasTokenAddr, TargetSGTBalanceSlot(account), value)

if st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.IsSoulBackedByNative {
st.state.AddBalance(types.SoulGasTokenAddr, uint256.MustFromBig(amount), reason)
Expand Down Expand Up @@ -413,12 +340,8 @@ func (st *StateTransition) buyGas() error {

st.gasFromSoul = false

// SoulGasToken doesn't support burning balance of zero account, so here we ensure that `gasFromSoul` is false for zero account.
if st.msg.From != (common.Address{}) && st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.UseSoulGasToken {
have, err := st.GetSoulBalance(st.msg.From)
if err != nil {
return fmt.Errorf("GetSoulBalance error:%v", err)
}
if st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.UseSoulGasToken {
have := st.GetSoulBalance(st.msg.From)
if have, want := have.ToBig(), new(big.Int).Sub(balanceCheck, st.msg.Value); have.Cmp(want) >= 0 {
if have, want := st.state.GetBalance(st.msg.From).ToBig(), st.msg.Value; have.Cmp(want) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
Expand Down
3 changes: 2 additions & 1 deletion core/txpool/blobpool/blobpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,8 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
}
// Ensure the transaction adheres to the stateful pool filters (nonce, balance)
stateOpts := &txpool.ValidationOptionsWithState{
State: p.state,
State: p.state,
Chainconfig: p.chain.Config(),

FirstNonceGap: func(addr common.Address) uint64 {
// Nonce gaps are not permitted in the blob pool, the first gap will
Expand Down
3 changes: 2 additions & 1 deletion core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,8 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
// rules and adheres to some heuristic limits of the local node (price and size).
func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
opts := &txpool.ValidationOptionsWithState{
State: pool.currentState,
State: pool.currentState,
Chainconfig: pool.chainconfig,

FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
UsedAndLeftSlots: func(addr common.Address) (int, int) {
Expand Down
24 changes: 17 additions & 7 deletions core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)

// L1 Info Gas Overhead is the amount of gas the the L1 info deposit consumes.
Expand Down Expand Up @@ -202,7 +203,8 @@ func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) err
// ValidationOptionsWithState define certain differences between stateful transaction
// validation across the different pools without having to duplicate those checks.
type ValidationOptionsWithState struct {
State *state.StateDB // State database to check nonces and balances against
State *state.StateDB // State database to check nonces and balances against
Chainconfig *params.ChainConfig

// FirstNonceGap is an optional callback to retrieve the first nonce gap in
// the list of pooled transactions of a specific account. If this method is
Expand Down Expand Up @@ -255,27 +257,35 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
balance = opts.State.GetBalance(from).ToBig()
cost = tx.Cost()
)

sgtBalance := new(big.Int)
if opts.Chainconfig != nil && opts.Chainconfig.IsOptimism() && opts.Chainconfig.Optimism.UseSoulGasToken {
sgtBalanceSlot := core.TargetSGTBalanceSlot(from)
sgtBalanceValue := opts.State.GetState(types.SoulGasTokenAddr, sgtBalanceSlot)
sgtBalance = new(uint256.Int).SetBytes(sgtBalanceValue[:]).ToBig()
}

if opts.L1CostFn != nil {
if l1Cost := opts.L1CostFn(tx.RollupCostData()); l1Cost != nil { // add rollup cost
cost = cost.Add(cost, l1Cost)
}
}
if balance.Cmp(cost) < 0 {
return fmt.Errorf("%w: balance %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, cost, new(big.Int).Sub(cost, balance))
if balance.Cmp(cost) < 0 && sgtBalance.Cmp(cost) < 0 {
return fmt.Errorf("%w: balance %v, sgt balance:%v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, sgtBalance, cost, new(big.Int).Sub(cost, balance))
}
// Ensure the transactor has enough funds to cover for replacements or nonce
// expansions without overdrafts
spent := opts.ExistingExpenditure(from)
if prev := opts.ExistingCost(from, tx.Nonce()); prev != nil {
bump := new(big.Int).Sub(cost, prev)
need := new(big.Int).Add(spent, bump)
if balance.Cmp(need) < 0 {
return fmt.Errorf("%w: balance %v, queued cost %v, tx bumped %v, overshot %v", core.ErrInsufficientFunds, balance, spent, bump, new(big.Int).Sub(need, balance))
if balance.Cmp(need) < 0 && sgtBalance.Cmp(need) < 0 {
return fmt.Errorf("%w: balance %v, sgt balance:%v, queued cost %v, tx bumped %v, overshot %v", core.ErrInsufficientFunds, balance, sgtBalance, spent, bump, new(big.Int).Sub(need, balance))
}
} else {
need := new(big.Int).Add(spent, cost)
if balance.Cmp(need) < 0 {
return fmt.Errorf("%w: balance %v, queued cost %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, spent, cost, new(big.Int).Sub(need, balance))
if balance.Cmp(need) < 0 && sgtBalance.Cmp(need) < 0 {
return fmt.Errorf("%w: balance %v, sgt balance:%v, queued cost %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, sgtBalance, spent, cost, new(big.Int).Sub(need, balance))
}
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
// overflow the number of permitted transactions from a single account
Expand Down

0 comments on commit 9dba4cd

Please sign in to comment.