From a662019db8aab5f9ee44004d4854c0fd75a12662 Mon Sep 17 00:00:00 2001 From: blockchaindevsh Date: Wed, 9 Oct 2024 22:14:25 +0800 Subject: [PATCH 1/3] avoid evm.call for SGT --- core/state_transition.go | 147 ++++++++++----------------------------- 1 file changed, 36 insertions(+), 111 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 22b577bb9b..9b7c6bee4a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -22,11 +22,11 @@ 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/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" @@ -245,127 +245,55 @@ func (st *StateTransition) to() common.Address { return *st.msg.To } -var SoulGasTokenABI abi.ABI - -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) - } -} - -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 parseSoulBalanceResp(ret []byte) (*uint256.Int, error) { - method, ok := SoulGasTokenABI.Methods["balanceOf"] - if !ok { - panic("balanceOf method not found") - } +const ( + // should keep it in sync with the balances field of SoulGasToken contract + balancesSlot = uint64(51) +) - returnValueInterface, err := method.Outputs.Unpack(ret) - if err != nil { - return nil, fmt.Errorf("parseSoulBalanceResp Unpack failed:%w", err) - } +var ( + slotArgs abi.Arguments +) - 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 init() { + 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 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 targetSlot(account common.Address) (slot common.Hash) { + data, _ := slotArgs.Pack(account, balancesSlot) + slot = crypto.Keccak256Hash(data) + return } -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") - } - - 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 (st *StateTransition) GetSoulBalance(account common.Address) *uint256.Int { + slot := targetSlot(account) + value := st.state.GetState(types.SoulGasTokenAddr, slot) + balance := new(uint256.Int) + balance.SetBytes(value[:]) + return balance } -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 +func (st *StateTransition) SubSoulBalance(account common.Address, amount *big.Int) (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) } - return parseSoulBalanceResp(ret) -} + var value common.Hash + current.Sub(current, amount).FillBytes(value[:]) + st.state.SetState(types.SoulGasTokenAddr, targetSlot(account), value) -func (st *StateTransition) SubSoulBalance(account common.Address, amount *big.Int) (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)) - } + if st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.IsSoulBackedByNative { + st.state.SubBalance(types.SoulGasTokenAddr, uint256.MustFromBig(amount)) } return } func (st *StateTransition) AddSoulBalance(account common.Address, amount *big.Int) { - - _, _, 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() + var value common.Hash + current.Add(current, amount).FillBytes(value[:]) + st.state.SetState(types.SoulGasTokenAddr, targetSlot(account), value) if st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.IsSoulBackedByNative { st.state.AddBalance(types.SoulGasTokenAddr, uint256.MustFromBig(amount)) @@ -413,10 +341,7 @@ func (st *StateTransition) buyGas() error { // 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) - } + 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) From 0a47c98590cf8006159cb185e9409d50c7340748 Mon Sep 17 00:00:00 2001 From: blockchaindevsh Date: Fri, 11 Oct 2024 13:18:12 +0800 Subject: [PATCH 2/3] address comments --- core/state_transition.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 9b7c6bee4a..3837e33d79 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -279,8 +279,8 @@ func (st *StateTransition) SubSoulBalance(account common.Address, amount *big.In if current.Cmp(amount) < 0 { return fmt.Errorf("soul balance not enough, current:%v, expect:%v", current, amount) } - var value common.Hash - current.Sub(current, amount).FillBytes(value[:]) + + value := uint256.MustFromBig(current.Sub(current, amount)).Bytes32() st.state.SetState(types.SoulGasTokenAddr, targetSlot(account), value) if st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.IsSoulBackedByNative { @@ -291,8 +291,7 @@ func (st *StateTransition) SubSoulBalance(account common.Address, amount *big.In func (st *StateTransition) AddSoulBalance(account common.Address, amount *big.Int) { current := st.GetSoulBalance(account).ToBig() - var value common.Hash - current.Add(current, amount).FillBytes(value[:]) + value := uint256.MustFromBig(current.Add(current, amount)).Bytes32() st.state.SetState(types.SoulGasTokenAddr, targetSlot(account), value) if st.evm.ChainConfig().IsOptimism() && st.evm.ChainConfig().Optimism.IsSoulBackedByNative { From 850f1c9e5b8c0829c5bbee53be3249d9ed730f37 Mon Sep 17 00:00:00 2001 From: blockchaindevsh Date: Fri, 11 Oct 2024 15:15:02 +0800 Subject: [PATCH 3/3] export BalancesSlot for external testability --- core/state_transition.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 3837e33d79..b1df9115bd 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -247,7 +247,7 @@ func (st *StateTransition) to() common.Address { const ( // should keep it in sync with the balances field of SoulGasToken contract - balancesSlot = uint64(51) + BalancesSlot = uint64(51) ) var ( @@ -261,7 +261,7 @@ func init() { } func targetSlot(account common.Address) (slot common.Hash) { - data, _ := slotArgs.Pack(account, balancesSlot) + data, _ := slotArgs.Pack(account, BalancesSlot) slot = crypto.Keccak256Hash(data) return }