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

R4R: metatx bugfix #83

Merged
merged 14 commits into from
Jun 17, 2024
3 changes: 2 additions & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
if mantleUpgradeChainConfig != nil {
config.BaseFeeTime = mantleUpgradeChainConfig.BaseFeeTime
config.BVMETHMintUpgradeTime = mantleUpgradeChainConfig.BVMETHMintUpgradeTime
config.MetaTxUpgradeTime = mantleUpgradeChainConfig.MetaTxUpgradeTime
config.MetaTxV2UpgradeTime = mantleUpgradeChainConfig.MetaTxV2UpgradeTime
config.MetaTxV3UpgradeTime = mantleUpgradeChainConfig.MetaTxV3UpgradeTime
}

if overrides != nil && overrides.OverrideShanghai != nil {
Expand Down
37 changes: 30 additions & 7 deletions core/mantle_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,53 @@ var (
ChainID: params.MantleMainnetChainId,
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
MetaTxUpgradeTime: u64Ptr(0),
MetaTxV2UpgradeTime: u64Ptr(0),
MetaTxV3UpgradeTime: u64Ptr(0), //TODO set upgrade timestamp
}

MantleSepoliaUpgradeConfig = MantleUpgradeChainConfig{
ChainID: params.MantleSepoliaChainId,
BaseFeeTime: u64Ptr(1_704_891_600),
BVMETHMintUpgradeTime: nil, //TODO set upgrade timestamp
MetaTxUpgradeTime: nil, //TODO set upgrade timestamp
MetaTxV2UpgradeTime: nil, //TODO set upgrade timestamp
MetaTxV3UpgradeTime: nil, //TODO set upgrade timestamp
}
MantleSepoliaQA3UpgradeConfig = MantleUpgradeChainConfig{
ChainID: params.MantleSepoliaQA3ChainId,
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
MetaTxV2UpgradeTime: u64Ptr(0),
MetaTxV3UpgradeTime: u64Ptr(1717689600),

Choose a reason for hiding this comment

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

Is this timestamp suitable for QA3 network?

Copy link
Author

Choose a reason for hiding this comment

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

yes, the upgrade of QA3 has been completed

}
MantleSepoliaQA9UpgradeConfig = MantleUpgradeChainConfig{
ChainID: params.MantleSepoliaQA9ChainId,
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
MetaTxV2UpgradeTime: u64Ptr(0),
MetaTxV3UpgradeTime: u64Ptr(1716962400),

Choose a reason for hiding this comment

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

ditto

Copy link
Author

Choose a reason for hiding this comment

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

this is QA9, already upgrade

}
MantleLocalUpgradeConfig = MantleUpgradeChainConfig{
ChainID: params.MantleLocalChainId,
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
MetaTxUpgradeTime: u64Ptr(0),
MetaTxV2UpgradeTime: u64Ptr(0),
MetaTxV3UpgradeTime: u64Ptr(0),
}
MantleDefaultUpgradeConfig = MantleUpgradeChainConfig{
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
MetaTxUpgradeTime: u64Ptr(0),
MetaTxV2UpgradeTime: u64Ptr(0),
MetaTxV3UpgradeTime: nil,
}
)

type MantleUpgradeChainConfig struct {
ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection

BaseFeeTime *uint64 `json:"BaseFeeTime"` // Mantle BaseFee switch time (nil = no fork, 0 = already on mantle baseFee)
BVMETHMintUpgradeTime *uint64 `json:"BVMETHMintUpgradeTime"` // BVM_ETH mint upgrade switch time (nil = no fork, 0 = already on)
MetaTxUpgradeTime *uint64 `json:"metaTxUpgradeTime"` // MetaTxUpgradeBlock identifies the current block height is using metaTx with MetaTxSignDataV2
BaseFeeTime *uint64 `json:"baseFeeTime"` // Mantle BaseFee switch time (nil = no fork, 0 = already on mantle baseFee)
BVMETHMintUpgradeTime *uint64 `json:"bvmETHMintUpgradeTime"` // BVM_ETH mint upgrade switch time (nil = no fork, 0 = already on)
MetaTxV2UpgradeTime *uint64 `json:"metaTxV2UpgradeTime"` // MetaTxV1UpgradeBlock identifies the current block height is using metaTx with MetaTxSignDataV2
MetaTxV3UpgradeTime *uint64 `json:"metaTxV3UpgradeTime"` // MetaTxV3UpgradeBlock identifies the current block height is ensuring sponsor and sender are not the same
}

func GetUpgradeConfigForMantle(chainID *big.Int) *MantleUpgradeChainConfig {
Expand All @@ -50,6 +69,10 @@ func GetUpgradeConfigForMantle(chainID *big.Int) *MantleUpgradeChainConfig {
return &MantleMainnetUpgradeConfig
case params.MantleSepoliaChainId.Int64():
return &MantleSepoliaUpgradeConfig
case params.MantleSepoliaQA3ChainId.Int64():
return &MantleSepoliaQA3UpgradeConfig
case params.MantleSepoliaQA9ChainId.Int64():
return &MantleSepoliaQA9UpgradeConfig
case params.MantleLocalChainId.Int64():
return &MantleLocalUpgradeConfig
default:
Expand Down
87 changes: 65 additions & 22 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import (
)

var (
BVM_ETH_ADDR = common.HexToAddress("0xdEAddEaDdeadDEadDEADDEAddEADDEAddead1111")
BVM_ETH_ADDR = common.HexToAddress("0xdEAddEaDdeadDEadDEADDEAddEADDEAddead1111")
LEGACY_ERC20_MNT = common.HexToAddress("0xdEAddEaDdeadDEadDEADDEAddEADDEAddead0000")
)

// ExecutionResult includes all output after executing given evm
Expand Down Expand Up @@ -175,7 +176,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
if rules == nil {
return nil, errors.New("param rules is nil pointer")
}
metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, rules.IsMetaTxV2)
metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, rules.IsMetaTxV2, rules.IsMetaTxV3)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -223,6 +224,11 @@ func (st *StateTransition) CalculateRollupGasDataFromMessage() {
// add a constant to cover sigs(V,R,S) and other data to make sure that the gasLimit from eth_estimateGas can cover L1 cost
// just used for estimateGas and the actual L1 cost depends on users' tx when executing
st.msg.RollupDataGas.Ones += 80

// add a constant to cover meta tx sigs(V,R,S)
if st.msg.MetaTxParams != nil {
st.msg.RollupDataGas.Ones += 80
}
}

// ApplyMessage computes the new state by applying the given message
Expand Down Expand Up @@ -265,15 +271,18 @@ type StateTransition struct {
initialGas uint64
state vm.StateDB
evm *vm.EVM

initialSponsorValue *big.Int
}

// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
evm: evm,
msg: msg,
state: evm.StateDB,
gp: gp,
evm: evm,
msg: msg,
state: evm.StateDB,
initialSponsorValue: big.NewInt(0),
}
}

Expand All @@ -285,10 +294,13 @@ func (st *StateTransition) to() common.Address {
return *st.msg.To
}

func (st *StateTransition) buyGas() (*big.Int, error) {
if err := st.applyMetaTransaction(); err != nil {
return nil, err
func (st *StateTransition) buyGas(metaTxV3 bool) (*big.Int, error) {
if !metaTxV3 {
if err := st.applyMetaTransaction(); err != nil {
return nil, err
}
}

mgval := new(big.Int).SetUint64(st.msg.GasLimit)
mgval = mgval.Mul(mgval, st.msg.GasPrice)
var l1Cost *big.Int
Expand All @@ -298,17 +310,12 @@ func (st *StateTransition) buyGas() (*big.Int, error) {
if st.evm.Context.L1CostFunc != nil && st.msg.RunMode != EthcallMode {
l1Cost = st.evm.Context.L1CostFunc(st.evm.Context.BlockNumber.Uint64(), st.evm.Context.Time, st.msg.RollupDataGas, st.msg.IsDepositTx, st.msg.To)
}
if l1Cost != nil && (st.msg.RunMode == GasEstimationMode || st.msg.RunMode == GasEstimationWithSkipCheckBalanceMode) {
mgval = mgval.Add(mgval, l1Cost)
}

balanceCheck := new(big.Int).Set(mgval)
if st.msg.GasFeeCap != nil {
balanceCheck.SetUint64(st.msg.GasLimit)
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
balanceCheck.Add(balanceCheck, st.msg.Value)
if l1Cost != nil && st.msg.RunMode == GasEstimationMode {
balanceCheck.Add(balanceCheck, l1Cost)
}
}
if st.msg.RunMode != GasEstimationWithSkipCheckBalanceMode && st.msg.RunMode != EthcallMode {
if st.msg.MetaTxParams != nil {
Expand All @@ -321,6 +328,11 @@ func (st *StateTransition) buyGas() (*big.Int, error) {
if have, want := st.state.GetBalance(st.msg.From), selfPayAmount; have.Cmp(want) < 0 {
return nil, fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
}
if st.msg.MetaTxParams.GasFeeSponsor == st.msg.From {
if have, want := st.state.GetBalance(st.msg.From), pureGasFeeValue; have.Cmp(want) < 0 {
return nil, fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
}
}
} else {
if have, want := st.state.GetBalance(st.msg.From), balanceCheck; have.Cmp(want) < 0 {
return nil, fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
Expand All @@ -337,9 +349,10 @@ func (st *StateTransition) buyGas() (*big.Int, error) {
if st.msg.RunMode != GasEstimationWithSkipCheckBalanceMode && st.msg.RunMode != EthcallMode {
if st.msg.MetaTxParams != nil {
sponsorAmount, selfPayAmount := types.CalculateSponsorPercentAmount(st.msg.MetaTxParams, mgval)
st.initialSponsorValue = sponsorAmount
st.state.SubBalance(st.msg.MetaTxParams.GasFeeSponsor, sponsorAmount)
st.state.SubBalance(st.msg.From, selfPayAmount)
log.Debug("BuyGas for metaTx",
log.Debug("BuyGas for metaTx", "v3", metaTxV3,
"sponsor", st.msg.MetaTxParams.GasFeeSponsor.String(), "amount", sponsorAmount.String(),
"user", st.msg.From.String(), "amount", selfPayAmount.String())
} else {
Expand All @@ -361,7 +374,7 @@ func (st *StateTransition) applyMetaTransaction() error {
return nil
}

func (st *StateTransition) preCheck() (*big.Int, error) {
func (st *StateTransition) preCheck(metaTxV3 bool) (*big.Int, error) {
if st.msg.IsDepositTx {
// No fee fields to check, no nonce to check, and no need to check if EOA (L1 already verified it for us)
// Gas is free, but no refunds!
Expand Down Expand Up @@ -427,7 +440,7 @@ func (st *StateTransition) preCheck() (*big.Int, error) {
}
}
}
return st.buyGas()
return st.buyGas(metaTxV3)
}

// TransitionDb will transition the state by applying the current message and
Expand Down Expand Up @@ -500,7 +513,7 @@ func (st *StateTransition) innerTransitionDb() (*ExecutionResult, error) {

// Check clauses 1-3, buy gas if everything is correct
tokenRatio := st.state.GetState(types.GasOracleAddr, types.TokenRatioSlot).Big().Uint64()
l1Cost, err := st.preCheck()
l1Cost, err := st.preCheck(rules.IsMetaTxV3)
if err != nil {
return nil, err
}
Expand All @@ -524,6 +537,13 @@ func (st *StateTransition) innerTransitionDb() (*ExecutionResult, error) {
return nil, err
}

// after calculate intrinsic gas, apply meta tx data
if rules.IsMetaTxV3 {
if err := st.applyMetaTransaction(); err != nil {
return nil, err
}
}

if !st.msg.IsDepositTx && !st.msg.IsSystemTx {
gas = gas * tokenRatio
}
Expand Down Expand Up @@ -594,10 +614,10 @@ func (st *StateTransition) innerTransitionDb() (*ExecutionResult, error) {
if !st.msg.IsDepositTx && !st.msg.IsSystemTx {
if !rules.IsLondon {
// Before EIP-3529: refunds were capped to gasUsed / 2
st.refundGas(params.RefundQuotient, tokenRatio)
st.refundGas(params.RefundQuotient, tokenRatio, rules)
} else {
// After EIP-3529: refunds are capped to gasUsed / 5
st.refundGas(params.RefundQuotientEIP3529, tokenRatio)
st.refundGas(params.RefundQuotientEIP3529, tokenRatio, rules)
}
}

Expand Down Expand Up @@ -641,7 +661,7 @@ func (st *StateTransition) innerTransitionDb() (*ExecutionResult, error) {
}, nil
}

func (st *StateTransition) refundGas(refundQuotient, tokenRatio uint64) {
func (st *StateTransition) refundGas(refundQuotient, tokenRatio uint64, rules params.Rules) {
if st.msg.RunMode == GasEstimationWithSkipCheckBalanceMode || st.msg.RunMode == EthcallMode {
st.gasRemaining = st.gasRemaining * tokenRatio
st.gp.AddGas(st.gasRemaining)
Expand All @@ -661,6 +681,10 @@ func (st *StateTransition) refundGas(refundQuotient, tokenRatio uint64) {
sponsorRefundAmount, selfRefundAmount := types.CalculateSponsorPercentAmount(st.msg.MetaTxParams, remaining)
st.state.AddBalance(st.msg.MetaTxParams.GasFeeSponsor, sponsorRefundAmount)
st.state.AddBalance(st.msg.From, selfRefundAmount)
if rules.IsMetaTxV3 {
actualSponsorValue := new(big.Int).Sub(st.initialSponsorValue, sponsorRefundAmount)
st.generateMetaTxSponsorEvent(st.msg.MetaTxParams.GasFeeSponsor, st.msg.From, actualSponsorValue)
}
log.Debug("RefundGas for metaTx",
"sponsor", st.msg.MetaTxParams.GasFeeSponsor.String(), "refundAmount", sponsorRefundAmount.String(),
"user", st.msg.From.String(), "refundAmount", selfRefundAmount.String())
Expand Down Expand Up @@ -801,3 +825,22 @@ func (st *StateTransition) generateBVMETHTransferEvent(from, to common.Address,
BlockNumber: st.evm.Context.BlockNumber.Uint64(),
})
}

func (st *StateTransition) generateMetaTxSponsorEvent(sponsor, txSender common.Address, actualSponsorAmount *big.Int) {
// keccak("MetaTxSponsor(address,address,uint256)") = "0xe57e5af44e0b977ac446043abcb6b8958c04a1004c91d7342e2870761b21af95"
methodHash := common.HexToHash("0xe57e5af44e0b977ac446043abcb6b8958c04a1004c91d7342e2870761b21af95")
topics := make([]common.Hash, 3)
topics[0] = methodHash
topics[1] = sponsor.Hash()
topics[2] = txSender.Hash()
//data means the sponsor amount in MetaTxSponsor EVENT.
d := common.HexToHash(common.Bytes2Hex(actualSponsorAmount.Bytes())).Bytes()
st.evm.StateDB.AddLog(&types.Log{
Address: LEGACY_ERC20_MNT,
Topics: topics,
Data: d,
// This is a non-consensus field, but assigned here because
// core/state doesn't know the current block number.
BlockNumber: st.evm.Context.BlockNumber.Uint64(),
})
}
14 changes: 10 additions & 4 deletions core/txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,9 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
cost = cost.Add(cost, l1Cost)
}

metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time))
metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx,
pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time),
pool.chainconfig.IsMetaTxV3(pool.chain.CurrentBlock().Time))
if err != nil {
return err
}
Expand Down Expand Up @@ -735,9 +737,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// Deduct the cost of a transaction replaced by this
replCost := repl.Cost()
if replL1Cost := pool.l1CostFn(repl.RollupDataGas(), repl.IsDepositTx(), repl.To()); replL1Cost != nil { // add rollup cost
replCost = replCost.Add(cost, replL1Cost)
replCost = replCost.Add(replCost, replL1Cost)
}
replMetaTxParams, err := types.DecodeAndVerifyMetaTxParams(repl, pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time))
replMetaTxParams, err := types.DecodeAndVerifyMetaTxParams(repl,
pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time),
pool.chainconfig.IsMetaTxV3(pool.chain.CurrentBlock().Time))
if err != nil {
return err
}
Expand Down Expand Up @@ -1525,7 +1529,9 @@ func (pool *TxPool) validateMetaTxList(list *list) ([]*types.Transaction, *big.I
sponsorCostSum := big.NewInt(0)
sponsorCostSumPerSponsor := make(map[common.Address]*big.Int)
for _, tx := range list.txs.Flatten() {
metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time))
metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx,
pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time),
pool.chainconfig.IsMetaTxV3(pool.chain.CurrentBlock().Time))
if err != nil {
invalidMetaTxs = append(invalidMetaTxs, tx)
continue
Expand Down
29 changes: 20 additions & 9 deletions core/types/meta_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ var (
// ASCII code of "MantleMetaTxPrefix"
MetaTxPrefix, _ = hexutil.Decode("0x00000000000000000000000000004D616E746C654D6574615478507265666978")

ErrExpiredMetaTx = errors.New("expired meta transaction")
ErrInvalidGasFeeSponsorSig = errors.New("invalid gas fee sponsor signature")
ErrGasFeeSponsorMismatch = errors.New("gas fee sponsor address is mismatch with signature")
ErrInvalidSponsorPercent = errors.New("invalid sponsor percent, expected range (0, 100]")
ErrSponsorBalanceNotEnough = errors.New("sponsor doesn't have enough balance")
ErrExpiredMetaTx = errors.New("expired meta transaction")
ErrInvalidGasFeeSponsorSig = errors.New("invalid gas fee sponsor signature")
ErrGasFeeSponsorMismatch = errors.New("gas fee sponsor address is mismatch with signature")
ErrInvalidSponsorPercent = errors.New("invalid sponsor percent, expected range (0, 100]")
ErrSponsorBalanceNotEnough = errors.New("sponsor doesn't have enough balance")
ErrSponsorMustNotEqualToSender = errors.New("sponsor must not equal to tx sender")
)

type MetaTxParams struct {
Expand Down Expand Up @@ -104,7 +105,7 @@ func DecodeMetaTxParams(txData []byte) (*MetaTxParams, error) {
return &metaTxParams, nil
}

func DecodeAndVerifyMetaTxParams(tx *Transaction, isMetaTxUpgraded bool) (*MetaTxParams, error) {
func DecodeAndVerifyMetaTxParams(tx *Transaction, isMetaTxV2, isMetaTxV3 bool) (*MetaTxParams, error) {
if tx.Type() != DynamicFeeTxType {
return nil, nil
}
Expand All @@ -128,18 +129,28 @@ func DecodeAndVerifyMetaTxParams(tx *Transaction, isMetaTxUpgraded bool) (*MetaT
return nil, nil
}

if err = checkSponsorSignature(tx, metaTxParams, isMetaTxUpgraded); err != nil {
if err = checkSponsorSignature(tx, metaTxParams, isMetaTxV2); err != nil {
return nil, err
}

if isMetaTxV3 {
txSender, err := Sender(LatestSignerForChainID(tx.ChainId()), tx)
if err != nil {
return nil, err
}
if txSender == metaTxParams.GasFeeSponsor {
return nil, ErrSponsorMustNotEqualToSender
}
}

tx.metaTxParams.Store(&MetaTxParamsCache{
metaTxParams: metaTxParams,
})

return metaTxParams, nil
}

func checkSponsorSignature(tx *Transaction, metaTxParams *MetaTxParams, isMetaTxUpgraded bool) error {
func checkSponsorSignature(tx *Transaction, metaTxParams *MetaTxParams, isMetaTxV2 bool) error {
var (
txSender, gasFeeSponsorSigner common.Address
err error
Expand All @@ -150,7 +161,7 @@ func checkSponsorSignature(tx *Transaction, metaTxParams *MetaTxParams, isMetaTx
return err
}

if isMetaTxUpgraded {
if isMetaTxV2 {
metaTxSignData := &MetaTxSignDataV2{
From: txSender,
ChainID: tx.ChainId(),
Expand Down
Loading