diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 5f796c1d66..78a067cfb0 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -162,8 +162,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, misc.ApplyDAOHardFork(statedb) } + rules := chainConfig.Rules(new(big.Int).SetUint64(pre.Env.Number), false, pre.Env.Timestamp) for i, tx := range txs { - msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee) + msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee, &rules) if err != nil { log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) diff --git a/core/genesis.go b/core/genesis.go index 2803902814..265cbb130a 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -310,6 +310,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen mantleUpgradeChainConfig := GetUpgradeConfigForMantle(config.ChainID) if mantleUpgradeChainConfig != nil { config.BaseFeeTime = mantleUpgradeChainConfig.BaseFeeTime + config.BVMETHMintUpgradeTime = mantleUpgradeChainConfig.BVMETHMintUpgradeTime + config.MetaTxUpgradeTime = mantleUpgradeChainConfig.MetaTxUpgradeTime } if overrides != nil && overrides.OverrideShanghai != nil { diff --git a/core/mantle_upgrade.go b/core/mantle_upgrade.go index f23fcd816d..ce033805d0 100644 --- a/core/mantle_upgrade.go +++ b/core/mantle_upgrade.go @@ -8,26 +8,37 @@ import ( var ( MantleMainnetUpgradeConfig = MantleUpgradeChainConfig{ - ChainID: params.MantleMainnetChainId, - BaseFeeTime: u64Ptr(0), + ChainID: params.MantleMainnetChainId, + BaseFeeTime: u64Ptr(0), + BVMETHMintUpgradeTime: u64Ptr(0), + MetaTxUpgradeTime: u64Ptr(0), } MantleSepoliaUpgradeConfig = MantleUpgradeChainConfig{ - ChainID: params.MantleSepoliaChainId, - BaseFeeTime: u64Ptr(1_704_891_600), + ChainID: params.MantleSepoliaChainId, + BaseFeeTime: u64Ptr(1_704_891_600), + BVMETHMintUpgradeTime: nil, //TODO set upgrade timestamp + MetaTxUpgradeTime: nil, //TODO set upgrade timestamp } MantleLocalUpgradeConfig = MantleUpgradeChainConfig{ - ChainID: params.MantleLocalChainId, - BaseFeeTime: u64Ptr(0), + ChainID: params.MantleLocalChainId, + BaseFeeTime: u64Ptr(0), + BVMETHMintUpgradeTime: u64Ptr(0), + MetaTxUpgradeTime: u64Ptr(0), } MantleDefaultUpgradeConfig = MantleUpgradeChainConfig{ - BaseFeeTime: u64Ptr(0), + BaseFeeTime: u64Ptr(0), + BVMETHMintUpgradeTime: u64Ptr(0), + MetaTxUpgradeTime: u64Ptr(0), } ) 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) + 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 } func GetUpgradeConfigForMantle(chainID *big.Int) *MantleUpgradeChainConfig { diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 72cb249f22..0141049773 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -57,13 +57,15 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c ) // Iterate over and process the individual transactions byzantium := p.config.IsByzantium(block.Number()) + rules := evm.ChainConfig().Rules(block.Number(), false, header.Time) + for i, tx := range block.Transactions() { // If block precaching was interrupted, abort if interrupt != nil && atomic.LoadUint32(interrupt) == 1 { return } // Convert the transaction into an executable message and pre-cache its sender - msg, err := TransactionToMessage(tx, signer, header.BaseFee) + msg, err := TransactionToMessage(tx, signer, header.BaseFee, &rules) if err != nil { return // Also invalid block, bail out } diff --git a/core/state_processor.go b/core/state_processor.go index 9ab96672b8..a8a0543202 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -72,9 +72,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } blockContext := NewEVMBlockContext(header, p.bc, nil, p.config, statedb) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) + rules := vmenv.ChainConfig().Rules(header.Number, false, header.Time) + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - msg, err := TransactionToMessage(tx, types.MakeSigner(p.config, header.Number), header.BaseFee) + msg, err := TransactionToMessage(tx, types.MakeSigner(p.config, header.Number), header.BaseFee, &rules) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -171,7 +173,8 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { - msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number), header.BaseFee) + rules := config.Rules(header.Number, false, header.Time) + msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number), header.BaseFee, &rules) if err != nil { return nil, err } diff --git a/core/state_transition.go b/core/state_transition.go index 0566e9a4ab..860f3c28b3 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -17,6 +17,7 @@ package core import ( + "errors" "fmt" "math" "math/big" @@ -161,6 +162,7 @@ type Message struct { IsDepositTx bool // IsDepositTx indicates the message is force-included and can persist a mint. Mint *big.Int // Mint is the amount to mint before EVM processing, or nil if there is no minting. ETHValue *big.Int // ETHValue is the amount to mint BVM_ETH before EVM processing, or nil if there is no minting. + ETHTxValue *big.Int // ETHTxValue is the amount to be transferred to msg.To before EVM processing, and the transfer will be reverted if EVM failed MetaTxParams *types.MetaTxParams // MetaTxParams contains necessary parameter to sponsor gas fee for msg.From. RollupDataGas types.RollupGasData // RollupDataGas indicates the rollup cost of the message, 0 if not a rollup or no cost. @@ -169,8 +171,11 @@ type Message struct { } // TransactionToMessage converts a transaction into a Message. -func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) { - metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx) +func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int, rules *params.Rules) (*Message, error) { + if rules == nil { + return nil, errors.New("param rules is nil pointer") + } + metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, rules.IsMetaTxV2) if err != nil { return nil, err } @@ -189,6 +194,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In Mint: tx.Mint(), RollupDataGas: tx.RollupDataGas(), ETHValue: tx.ETHValue(), + ETHTxValue: tx.ETHTxValue(), MetaTxParams: metaTxParams, SkipAccountChecks: false, RunMode: CommitMode, @@ -438,19 +444,18 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if mint := st.msg.Mint; mint != nil { st.state.AddBalance(st.msg.From, mint) } + + //Mint BVM_ETH + rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil, st.evm.Context.Time) //add eth value if ethValue := st.msg.ETHValue; ethValue != nil && ethValue.Cmp(big.NewInt(0)) != 0 { - var ethRecipient common.Address - if st.msg.To != nil { - ethRecipient = *st.msg.To - } else { - ethRecipient = crypto.CreateAddress(st.msg.From, st.evm.StateDB.GetNonce(st.msg.From)) - } - st.addBVMETHBalance(ethRecipient, ethValue) - st.addBVMETHTotalSupply(ethValue) - st.generateBVMETHMintEvent(ethRecipient, ethValue) + st.mintBVMETH(ethValue, rules) } snap := st.state.Snapshot() + // Will be reverted if failed + if ethTxValue := st.msg.ETHTxValue; ethTxValue != nil && ethTxValue.Cmp(big.NewInt(0)) != 0 { + st.transferBVMETH(ethTxValue, rules) + } result, err := st.innerTransitionDb() // Failed deposits must still be included. Unless we cannot produce the block at all due to the gas limit. @@ -671,12 +676,33 @@ func (st *StateTransition) gasUsed() uint64 { return st.initialGas - st.gasRemaining } -func (st *StateTransition) addBVMETHBalance(ethRecipient common.Address, ethValue *big.Int) { - key := getBVMETHBalanceKey(ethRecipient) +func (st *StateTransition) mintBVMETH(ethValue *big.Int, rules params.Rules) { + if !rules.IsMantleBVMETHMintUpgrade { + var key common.Hash + var ethRecipient common.Address + if st.msg.To != nil { + ethRecipient = *st.msg.To + } else { + ethRecipient = crypto.CreateAddress(st.msg.From, st.evm.StateDB.GetNonce(st.msg.From)) + } + key = getBVMETHBalanceKey(ethRecipient) + value := st.state.GetState(BVM_ETH_ADDR, key) + bal := value.Big() + bal = bal.Add(bal, ethValue) + st.state.SetState(BVM_ETH_ADDR, key, common.BigToHash(bal)) + + st.addBVMETHTotalSupply(ethValue) + st.generateBVMETHMintEvent(ethRecipient, ethValue) + return + } + key := getBVMETHBalanceKey(st.msg.From) value := st.state.GetState(BVM_ETH_ADDR, key) bal := value.Big() bal = bal.Add(bal, ethValue) st.state.SetState(BVM_ETH_ADDR, key, common.BigToHash(bal)) + + st.addBVMETHTotalSupply(ethValue) + st.generateBVMETHMintEvent(st.msg.From, ethValue) } func (st *StateTransition) addBVMETHTotalSupply(ethValue *big.Int) { @@ -687,6 +713,38 @@ func (st *StateTransition) addBVMETHTotalSupply(ethValue *big.Int) { st.state.SetState(BVM_ETH_ADDR, key, common.BigToHash(bal)) } +func (st *StateTransition) transferBVMETH(ethValue *big.Int, rules params.Rules) { + if !rules.IsMantleBVMETHMintUpgrade { + return + } + var ethRecipient common.Address + if st.msg.To != nil { + ethRecipient = *st.msg.To + } else { + ethRecipient = crypto.CreateAddress(st.msg.From, st.evm.StateDB.GetNonce(st.msg.From)) + } + if ethRecipient == st.msg.From { + return + } + + fromKey := getBVMETHBalanceKey(st.msg.From) + toKey := getBVMETHBalanceKey(ethRecipient) + + fromBalanceValue := st.state.GetState(BVM_ETH_ADDR, fromKey) + toBalanceValue := st.state.GetState(BVM_ETH_ADDR, toKey) + + fromBalance := fromBalanceValue.Big() + toBalance := toBalanceValue.Big() + + fromBalance = new(big.Int).Sub(fromBalance, ethValue) + toBalance = new(big.Int).Add(toBalance, ethValue) + + st.state.SetState(BVM_ETH_ADDR, fromKey, common.BigToHash(fromBalance)) + st.state.SetState(BVM_ETH_ADDR, toKey, common.BigToHash(toBalance)) + + st.generateBVMETHTransferEvent(st.msg.From, ethRecipient, ethValue) +} + func getBVMETHBalanceKey(addr common.Address) common.Hash { position := common.Big0 hasher := sha3.NewLegacyKeccak256() @@ -717,3 +775,22 @@ func (st *StateTransition) generateBVMETHMintEvent(mintAddress common.Address, m BlockNumber: st.evm.Context.BlockNumber.Uint64(), }) } + +func (st *StateTransition) generateBVMETHTransferEvent(from, to common.Address, amount *big.Int) { + // keccak("Transfer(address,address,uint256)") = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + methodHash := common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") + topics := make([]common.Hash, 3) + topics[0] = methodHash + topics[1] = from.Hash() + topics[2] = to.Hash() + //data means the transfer amount in Transfer EVENT. + data := common.HexToHash(common.Bytes2Hex(amount.Bytes())).Bytes() + st.evm.StateDB.AddLog(&types.Log{ + Address: BVM_ETH_ADDR, + Topics: topics, + Data: data, + // 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(), + }) +} diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 4dda873e44..3ef8d598cd 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -633,6 +633,9 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if tx.Type() == types.DepositTxType { return core.ErrTxTypeNotSupported } + if tx.Type() == types.BlobTxType { + return errors.New("BlobTxType of transaction is currently not supported.") + } // Accept only legacy transactions until EIP-2718/2930 activates. if !pool.eip2718 && tx.Type() != types.LegacyTxType { return core.ErrTxTypeNotSupported @@ -689,7 +692,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { cost = cost.Add(cost, l1Cost) } - metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx) + metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time)) if err != nil { return err } @@ -1480,7 +1483,7 @@ func (pool *TxPool) validateMetaTxList(list *list) ([]*types.Transaction, *big.I var invalidMetaTxs []*types.Transaction sponsorCostSum := big.NewInt(0) for _, tx := range list.txs.Flatten() { - metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx) + metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, pool.chainconfig.IsMetaTxV2(pool.chain.CurrentBlock().Time)) if err != nil { invalidMetaTxs = append(invalidMetaTxs, tx) continue diff --git a/core/types/deposit_tx.go b/core/types/deposit_tx.go index f4917a11f3..7dece4a6a1 100644 --- a/core/types/deposit_tx.go +++ b/core/types/deposit_tx.go @@ -43,6 +43,8 @@ type DepositTx struct { EthValue *big.Int `rlp:"nil"` // Normal Tx data Data []byte + // EthTxValue means L2 BVM_ETH tx tag, nil means that there is no need to transfer BVM_ETH to msg.To. + EthTxValue *big.Int `rlp:"optional"` } // copy creates a deep copy of the transaction data and initializes all fields. @@ -57,6 +59,7 @@ func (tx *DepositTx) copy() TxData { IsSystemTransaction: tx.IsSystemTransaction, Data: common.CopyBytes(tx.Data), EthValue: nil, + EthTxValue: nil, } if tx.Mint != nil { cpy.Mint = new(big.Int).Set(tx.Mint) @@ -67,6 +70,9 @@ func (tx *DepositTx) copy() TxData { if tx.EthValue != nil { cpy.EthValue = new(big.Int).Set(tx.EthValue) } + if tx.EthTxValue != nil { + cpy.EthTxValue = new(big.Int).Set(tx.EthTxValue) + } return cpy } diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index dd7fc41fe1..bf6789976e 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -48,11 +48,19 @@ func (r Receipt) MarshalJSON() ([]byte, error) { enc.TxHash = r.TxHash enc.ContractAddress = r.ContractAddress enc.GasUsed = hexutil.Uint64(r.GasUsed) - effectiveGasPrice := hexutil.Big(*r.EffectiveGasPrice) - enc.EffectiveGasPrice = &effectiveGasPrice + if r.EffectiveGasPrice != nil{ + effectiveGasPrice := hexutil.Big(*r.EffectiveGasPrice) + enc.EffectiveGasPrice = &effectiveGasPrice + } else { + enc.EffectiveGasPrice = nil + } enc.BlobGasUsed = hexutil.Uint64(r.BlobGasUsed) - blobGasPrice := hexutil.Big(*r.BlobGasPrice) - enc.BlobGasPrice = &blobGasPrice + if r.BlobGasPrice != nil{ + blobGasPrice := hexutil.Big(*r.BlobGasPrice) + enc.BlobGasPrice = &blobGasPrice + } else { + enc.BlobGasPrice = nil + } if r.DepositNonce != nil { depositNonce := hexutil.Uint64(*r.DepositNonce) enc.DepositNonce = &depositNonce diff --git a/core/types/meta_transaction.go b/core/types/meta_transaction.go index 945af8cc41..5511256f72 100644 --- a/core/types/meta_transaction.go +++ b/core/types/meta_transaction.go @@ -57,6 +57,21 @@ type MetaTxSignData struct { SponsorPercent uint64 } +type MetaTxSignDataV2 struct { + From common.Address + ChainID *big.Int + Nonce uint64 + GasTipCap *big.Int + GasFeeCap *big.Int + Gas uint64 + To *common.Address `rlp:"nil"` + Value *big.Int + Data []byte + AccessList AccessList + ExpireHeight uint64 + SponsorPercent uint64 +} + func CalculateSponsorPercentAmount(mxParams *MetaTxParams, amount *big.Int) (*big.Int, *big.Int) { if mxParams == nil { return nil, nil @@ -89,7 +104,7 @@ func DecodeMetaTxParams(txData []byte) (*MetaTxParams, error) { return &metaTxParams, nil } -func DecodeAndVerifyMetaTxParams(tx *Transaction) (*MetaTxParams, error) { +func DecodeAndVerifyMetaTxParams(tx *Transaction, isMetaTxUpgraded bool) (*MetaTxParams, error) { if tx.Type() != DynamicFeeTxType { return nil, nil } @@ -117,27 +132,8 @@ func DecodeAndVerifyMetaTxParams(tx *Transaction) (*MetaTxParams, error) { return nil, ErrInvalidSponsorPercent } - metaTxSignData := &MetaTxSignData{ - ChainID: tx.ChainId(), - Nonce: tx.Nonce(), - GasTipCap: tx.GasTipCap(), - GasFeeCap: tx.GasFeeCap(), - Gas: tx.Gas(), - To: tx.To(), - Value: tx.Value(), - Data: metaTxParams.Payload, - AccessList: tx.AccessList(), - ExpireHeight: metaTxParams.ExpireHeight, - SponsorPercent: metaTxParams.SponsorPercent, - } - - gasFeeSponsorSigner, err := recoverPlain(metaTxSignData.Hash(), metaTxParams.R, metaTxParams.S, metaTxParams.V, true) - if err != nil { - return nil, ErrInvalidGasFeeSponsorSig - } - - if gasFeeSponsorSigner != metaTxParams.GasFeeSponsor { - return nil, ErrGasFeeSponsorMismatch + if err = checkSponsorSignature(tx, metaTxParams, isMetaTxUpgraded); err != nil { + return nil, err } tx.metaTxParams.Store(&MetaTxParamsCache{ @@ -147,6 +143,67 @@ func DecodeAndVerifyMetaTxParams(tx *Transaction) (*MetaTxParams, error) { return metaTxParams, nil } +func checkSponsorSignature(tx *Transaction, metaTxParams *MetaTxParams, isMetaTxUpgraded bool) error { + var ( + txSender, gasFeeSponsorSigner common.Address + err error + ) + + txSender, err = Sender(LatestSignerForChainID(tx.ChainId()), tx) + if err != nil { + return err + } + + if isMetaTxUpgraded { + metaTxSignData := &MetaTxSignDataV2{ + From: txSender, + ChainID: tx.ChainId(), + Nonce: tx.Nonce(), + GasTipCap: tx.GasTipCap(), + GasFeeCap: tx.GasFeeCap(), + Gas: tx.Gas(), + To: tx.To(), + Value: tx.Value(), + Data: metaTxParams.Payload, + AccessList: tx.AccessList(), + ExpireHeight: metaTxParams.ExpireHeight, + SponsorPercent: metaTxParams.SponsorPercent, + } + + gasFeeSponsorSigner, err = recoverPlain(metaTxSignData.Hash(), metaTxParams.R, metaTxParams.S, metaTxParams.V, true) + if err != nil { + return ErrInvalidGasFeeSponsorSig + } + } else { + metaTxSignData := &MetaTxSignData{ + ChainID: tx.ChainId(), + Nonce: tx.Nonce(), + GasTipCap: tx.GasTipCap(), + GasFeeCap: tx.GasFeeCap(), + Gas: tx.Gas(), + To: tx.To(), + Value: tx.Value(), + Data: metaTxParams.Payload, + AccessList: tx.AccessList(), + ExpireHeight: metaTxParams.ExpireHeight, + SponsorPercent: metaTxParams.SponsorPercent, + } + + gasFeeSponsorSigner, err = recoverPlain(metaTxSignData.Hash(), metaTxParams.R, metaTxParams.S, metaTxParams.V, true) + if err != nil { + return ErrInvalidGasFeeSponsorSig + } + } + + if gasFeeSponsorSigner != metaTxParams.GasFeeSponsor { + return ErrGasFeeSponsorMismatch + } + return nil +} + func (metaTxSignData *MetaTxSignData) Hash() common.Hash { return rlpHash(metaTxSignData) } +func (metaTxSignData *MetaTxSignDataV2) Hash() common.Hash { + return rlpHash(metaTxSignData) +} diff --git a/core/types/meta_transaction_test.go b/core/types/meta_transaction_test.go index 84cd6b12bf..e6fc819334 100644 --- a/core/types/meta_transaction_test.go +++ b/core/types/meta_transaction_test.go @@ -101,6 +101,48 @@ func generateMetaTxDataWithMockSig(dynamicTx *DynamicFeeTx, expireHeight uint64, return append(MetaTxPrefix, metaTxDataBz...), nil } +func generateMetaTxDataV2(dynamicTx *DynamicFeeTx, expireHeight uint64, sponsorPercent uint64, + gasFeeSponsorAddr common.Address, privateKey *ecdsa.PrivateKey) ([]byte, error) { + from := crypto.PubkeyToAddress(userKey.PublicKey) + metaTxSignData := &MetaTxSignDataV2{ + From: from, + ChainID: dynamicTx.ChainID, + Nonce: dynamicTx.Nonce, + GasTipCap: dynamicTx.GasTipCap, + GasFeeCap: dynamicTx.GasFeeCap, + Gas: dynamicTx.Gas, + To: dynamicTx.To, + Value: dynamicTx.Value, + Data: dynamicTx.Data, + AccessList: dynamicTx.AccessList, + ExpireHeight: expireHeight, + SponsorPercent: sponsorPercent, + } + + sponsorSig, err := crypto.Sign(metaTxSignData.Hash().Bytes(), privateKey) + if err != nil { + return nil, err + } + + r, s, v := decodeSignature(sponsorSig) + + metaTxData := &MetaTxParams{ + ExpireHeight: expireHeight, + Payload: metaTxSignData.Data, + GasFeeSponsor: gasFeeSponsorAddr, + SponsorPercent: sponsorPercent, + R: r, + S: s, + V: v, + } + + metaTxDataBz, err := rlp.EncodeToBytes(metaTxData) + if err != nil { + return nil, err + } + + return append(MetaTxPrefix, metaTxDataBz...), nil +} func TestDecodeMetaTxParams(t *testing.T) { gasFeeSponsorPublicKey := gasFeeSponsorKey1.Public() pubKeyECDSA, _ := gasFeeSponsorPublicKey.(*ecdsa.PublicKey) @@ -190,7 +232,7 @@ func TestDecodeAndVerifyMetaTxParams(t *testing.T) { require.NoError(t, err) // test normal metaTx - metaTxParams, err := DecodeAndVerifyMetaTxParams(signedTx) + metaTxParams, err := DecodeAndVerifyMetaTxParams(signedTx, false) require.NoError(t, err) require.Equal(t, gasFeeSponsorAddr.String(), metaTxParams.GasFeeSponsor.String()) @@ -208,7 +250,7 @@ func TestDecodeAndVerifyMetaTxParams(t *testing.T) { signedTx, err = tx.WithSignature(signer, txSignature) require.NoError(t, err) - _, err = DecodeAndVerifyMetaTxParams(signedTx) + _, err = DecodeAndVerifyMetaTxParams(signedTx, false) require.Equal(t, err, ErrInvalidGasFeeSponsorSig) // Test ErrGasFeeSponsorMismatch @@ -223,7 +265,92 @@ func TestDecodeAndVerifyMetaTxParams(t *testing.T) { signedTx, err = tx.WithSignature(signer, txSignature) require.NoError(t, err) - _, err = DecodeAndVerifyMetaTxParams(signedTx) + _, err = DecodeAndVerifyMetaTxParams(signedTx, true) + require.Equal(t, err, ErrGasFeeSponsorMismatch) + + // Test ErrGasFeeSponsorMismatch + dynamicTx.Data = depositABICalldata + payload, err = generateMetaTxData(dynamicTx, expireHeight, 101, gasFeeSponsorAddr, gasFeeSponsorKey2) + require.NoError(t, err) + + dynamicTx.Data = payload + tx = NewTx(dynamicTx) + txSignature, err = crypto.Sign(signer.Hash(tx).Bytes(), userKey) + require.NoError(t, err) + signedTx, err = tx.WithSignature(signer, txSignature) + require.NoError(t, err) + + _, err = DecodeAndVerifyMetaTxParams(signedTx, false) + require.Equal(t, err, ErrInvalidSponsorPercent) +} + +func TestDecodeAndVerifyMetaTxParamsV2(t *testing.T) { + gasFeeSponsorPublicKey := gasFeeSponsorKey1.Public() + pubKeyECDSA, _ := gasFeeSponsorPublicKey.(*ecdsa.PublicKey) + gasFeeSponsorAddr := crypto.PubkeyToAddress(*pubKeyECDSA) + + chainId := big.NewInt(1) + depositABICalldata, _ := hexutil.Decode("0xd0e30db0") + to := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + expireHeight := uint64(20_000_010) + dynamicTx := &DynamicFeeTx{ + ChainID: chainId, + Nonce: 100, + GasTipCap: big.NewInt(1e9), + GasFeeCap: big.NewInt(1e15), + Gas: 4700000, + To: &to, + Value: big.NewInt(1e18), + Data: depositABICalldata, + AccessList: nil, + } + + payload, err := generateMetaTxDataV2(dynamicTx, expireHeight, 50, gasFeeSponsorAddr, gasFeeSponsorKey1) + require.NoError(t, err) + + dynamicTx.Data = payload + tx := NewTx(dynamicTx) + signer := LatestSignerForChainID(chainId) + txSignature, err := crypto.Sign(signer.Hash(tx).Bytes(), userKey) + require.NoError(t, err) + signedTx, err := tx.WithSignature(signer, txSignature) + require.NoError(t, err) + + // test normal metaTx + metaTxParams, err := DecodeAndVerifyMetaTxParams(signedTx, true) + require.NoError(t, err) + + require.Equal(t, gasFeeSponsorAddr.String(), metaTxParams.GasFeeSponsor.String()) + require.Equal(t, hexutil.Encode(depositABICalldata), hexutil.Encode(metaTxParams.Payload)) + + // Test ErrInvalidGasFeeSponsorSig + dynamicTx.Data = depositABICalldata + payload, err = generateMetaTxDataWithMockSig(dynamicTx, expireHeight, 100, gasFeeSponsorAddr, gasFeeSponsorKey1) + require.NoError(t, err) + + dynamicTx.Data = payload + tx = NewTx(dynamicTx) + txSignature, err = crypto.Sign(signer.Hash(tx).Bytes(), userKey) + require.NoError(t, err) + signedTx, err = tx.WithSignature(signer, txSignature) + require.NoError(t, err) + + _, err = DecodeAndVerifyMetaTxParams(signedTx, true) + require.Equal(t, err, ErrInvalidGasFeeSponsorSig) + + // Test ErrGasFeeSponsorMismatch + dynamicTx.Data = depositABICalldata + payload, err = generateMetaTxDataV2(dynamicTx, expireHeight, 80, gasFeeSponsorAddr, gasFeeSponsorKey2) + require.NoError(t, err) + + dynamicTx.Data = payload + tx = NewTx(dynamicTx) + txSignature, err = crypto.Sign(signer.Hash(tx).Bytes(), userKey) + require.NoError(t, err) + signedTx, err = tx.WithSignature(signer, txSignature) + require.NoError(t, err) + + _, err = DecodeAndVerifyMetaTxParams(signedTx, true) require.Equal(t, err, ErrGasFeeSponsorMismatch) // Test ErrGasFeeSponsorMismatch @@ -238,6 +365,6 @@ func TestDecodeAndVerifyMetaTxParams(t *testing.T) { signedTx, err = tx.WithSignature(signer, txSignature) require.NoError(t, err) - _, err = DecodeAndVerifyMetaTxParams(signedTx) + _, err = DecodeAndVerifyMetaTxParams(signedTx, false) require.Equal(t, err, ErrInvalidSponsorPercent) } diff --git a/core/types/transaction.go b/core/types/transaction.go index 02a7e1dca8..d6f13ec106 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -354,6 +354,15 @@ func (tx *Transaction) ETHValue() *big.Int { return nil } +// ETHTxValue returns the BVM_ETH to be transferred in the deposit tx. +// This returns nil if there is nothing to transfer, or if this is not a deposit tx. +func (tx *Transaction) ETHTxValue() *big.Int { + if dep, ok := tx.inner.(*DepositTx); ok { + return dep.EthTxValue + } + return nil +} + // IsDepositTx returns true if the transaction is a deposit tx type. func (tx *Transaction) IsDepositTx() bool { return tx.Type() == DepositTxType @@ -365,12 +374,13 @@ func (tx *Transaction) IsSystemTx() bool { return tx.inner.isSystemTx() } -// Cost returns gas * gasPrice + value. +// Cost returns (gas * gasPrice) + (blobGas * blobGasPrice) + value. func (tx *Transaction) Cost() *big.Int { total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())) if tx.Type() == BlobTxType { total.Add(total, new(big.Int).Mul(tx.BlobGasFeeCap(), new(big.Int).SetUint64(tx.BlobGas()))) } + total.Add(total, tx.Value()) return total } diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index dcf0e2c921..4253380ec4 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -54,6 +54,7 @@ type txJSON struct { From *common.Address `json:"from,omitempty"` Mint *hexutil.Big `json:"mint,omitempty"` EthValue *hexutil.Big `json:"ethValue,omitempty"` + EthTxValue *hexutil.Big `json:"ethTxValue,omitempty"` IsSystemTx *bool `json:"isSystemTx,omitempty"` // Access list transaction fields: @@ -141,6 +142,9 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { if itx.EthValue != nil { enc.EthValue = (*hexutil.Big)(itx.EthValue) } + if itx.EthTxValue != nil { + enc.EthTxValue = (*hexutil.Big)(itx.EthTxValue) + } enc.IsSystemTx = &itx.IsSystemTransaction // other fields will show up as null. } @@ -419,6 +423,8 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { itx.Mint = (*big.Int)(dec.Mint) // ethValue may be omitted or nil if there is nothing to mint. itx.EthValue = (*big.Int)(dec.EthValue) + // ethValue may be omitted or nil if there is nothing to transfer to msg.To. + itx.EthTxValue = (*big.Int)(dec.EthTxValue) if dec.Data == nil { return errors.New("missing required field 'input' in transaction") } diff --git a/eth/api_backend.go b/eth/api_backend.go index 55a1f7e569..8b8fa8f45b 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -294,7 +294,7 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, tx *types.Transaction) error return err } if err = b.eth.seqRPCService.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data)); err != nil { - return fmt.Errorf("failed to forward tx to sequencer, please try again") + return fmt.Errorf("failed to forward tx to sequencer, please try again. Error message: '%w'", err) } if b.disableTxPool { return nil diff --git a/eth/state_accessor.go b/eth/state_accessor.go index c5f34822e1..d5eff61744 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -210,9 +210,11 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, } // Recompute transactions up to the target index. signer := types.MakeSigner(eth.blockchain.Config(), block.Number()) + rules := eth.blockchain.Config().Rules(block.Number(), false, block.Time()) + for idx, tx := range block.Transactions() { // Assemble the transaction call message and return if the requested offset - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), &rules) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil, eth.blockchain.Config(), statedb) if idx == txIndex { diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 1e831ef40b..28c4c2ca5c 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -291,10 +291,11 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed var ( signer = types.MakeSigner(api.backend.ChainConfig(), task.block.Number()) blockCtx = core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx), nil, api.backend.ChainConfig(), task.statedb) + rules = api.backend.ChainConfig().Rules(task.block.Number(), false, task.block.Time()) ) // Trace all the transactions contained within for i, tx := range task.block.Transactions() { - msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee(), &rules) txctx := &Context{ BlockHash: task.block.Hash(), BlockNumber: task.block.Number(), @@ -578,13 +579,14 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config chainConfig = api.backend.ChainConfig() vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil, chainConfig, statedb) deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) + rules = api.backend.ChainConfig().Rules(block.Number(), false, block.Time()) ) for i, tx := range block.Transactions() { if err := ctx.Err(); err != nil { return nil, err } var ( - msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee(), &rules) txContext = core.NewEVMTxContext(msg) vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) ) @@ -655,10 +657,11 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil, api.backend.ChainConfig(), statedb) signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) results = make([]*txTraceResult, len(txs)) + rules = api.backend.ChainConfig().Rules(block.Number(), false, block.Time()) ) for i, tx := range txs { // Generate the next state snapshot fast without tracing - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), &rules) txctx := &Context{ BlockHash: blockHash, BlockNumber: block.Number(), @@ -698,10 +701,11 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat pend.Add(1) go func() { defer pend.Done() + rules := api.backend.ChainConfig().Rules(block.Number(), false, block.Time()) // Fetch and execute the next transaction trace tasks for task := range jobs { blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil, api.backend.ChainConfig(), task.statedb) - msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee(), &rules) txctx := &Context{ BlockHash: blockHash, BlockNumber: block.Number(), @@ -721,6 +725,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat // Feed the transactions into the tracers and return var failed error blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil, api.backend.ChainConfig(), statedb) + rules := api.backend.ChainConfig().Rules(block.Number(), false, block.Time()) txloop: for i, tx := range txs { // Send the trace task over for execution @@ -733,7 +738,7 @@ txloop: } // Generate the next state snapshot fast without tracing - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), &rules) statedb.SetTxContext(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { @@ -810,10 +815,11 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Note: This copies the config, to not screw up the main config chainConfig, canon = overrideConfig(chainConfig, config.Overrides) } + rules := chainConfig.Rules(block.Number(), false, block.Time()) for i, tx := range block.Transactions() { // Prepare the transaction for un-traced execution var ( - msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee(), &rules) txContext = core.NewEVMTxContext(msg) vmConf vm.Config dump *os.File diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index a2d00f3edc..de8b60ca6c 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -253,7 +253,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block // Recompute transactions up to the target index. signer := types.MakeSigner(b.chainConfig, block.Number()) for idx, tx := range block.Transactions() { - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), ¶ms.Rules{}) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), b.chain, nil, b.chainConfig, statedb) if idx == txIndex { diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 62182e3a82..8c32b16cbe 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -145,7 +145,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, nil, ¶ms.Rules{}) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } @@ -220,7 +220,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, nil, ¶ms.Rules{}) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } @@ -314,7 +314,7 @@ func TestZeroValueToNotExitCall(t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, nil, ¶ms.Rules{}) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 8cd5a42bc0..25cc0a2d13 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" @@ -108,8 +109,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string return fmt.Errorf("failed to create call tracer: %v", err) } evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) - - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, nil, ¶ms.Rules{}) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index f578e2f0f5..3972d4cb95 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/tests" ) @@ -115,7 +116,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, nil, ¶ms.Rules{}) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 7c5ec65650..835dfa9448 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -88,7 +88,7 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableReturnData: false, }) evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + msg, err := core.TransactionToMessage(tx, signer, nil, ¶ms.Rules{}) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9e2039c8d7..213dd990da 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1503,6 +1503,7 @@ type RPCTransaction struct { SourceHash *common.Hash `json:"sourceHash,omitempty"` Mint *hexutil.Big `json:"mint,omitempty"` EthValue *hexutil.Big `json:"ethValue,omitempty"` + EthTxValue *hexutil.Big `json:"ethTxValue,omitempty"` IsSystemTx *bool `json:"isSystemTx,omitempty"` } @@ -1543,6 +1544,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber } result.Mint = (*hexutil.Big)(tx.Mint()) result.EthValue = (*hexutil.Big)(tx.ETHValue()) + result.EthTxValue = (*hexutil.Big)(tx.ETHTxValue()) if receipt != nil && receipt.DepositNonce != nil { result.Nonce = hexutil.Uint64(*receipt.DepositNonce) } diff --git a/les/state_accessor.go b/les/state_accessor.go index b6ba7f8f04..a7065fcaca 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -58,9 +58,10 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. } // Recompute transactions up to the target index. signer := types.MakeSigner(leth.blockchain.Config(), block.Number()) + rules := leth.chainConfig.Rules(block.Number(), false, block.Time()) for idx, tx := range block.Transactions() { // Assemble the transaction call message and return if the requested offset - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), &rules) txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil, leth.blockchain.Config(), statedb) statedb.SetTxContext(tx.Hash(), idx) diff --git a/light/txpool.go b/light/txpool.go index 6c79e6ecfb..5a122089d0 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -379,7 +379,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error return txpool.ErrNegativeValue } - metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx) + metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, pool.config.IsMetaTxV2(header.Time)) if err != nil { return err } diff --git a/params/config.go b/params/config.go index a7aad50fff..924ffced7d 100644 --- a/params/config.go +++ b/params/config.go @@ -458,7 +458,11 @@ type ChainConfig struct { MergeNetsplitBlock *big.Int `json:"mergeNetsplitBlock,omitempty"` // Virtual fork after The Merge to use as a network splitter // Mantle upgrade configs - BaseFeeTime *uint64 `json:"baseFeeTime,omitempty"` // Mantle BaseFee switch time (nil = no fork, 0 = already on mantle baseFee) + BaseFeeTime *uint64 `json:"baseFeeTime,omitempty"` // Mantle BaseFee switch time (nil = no fork, 0 = already on mantle baseFee) + BVMETHMintUpgradeTime *uint64 `json:"bvmETHMintUpgradeTime,omitempty"` // BVM_ETH mint upgrade switch time (nil = no fork, 0 = already on) + + // MetaTx upgrade config + MetaTxUpgradeTime *uint64 `json:"metaTxUpgradeTime,omitempty"` // MetaTxUpgradeTime switch time ( nil = no fork, 0 = already forked) // Fork scheduling was switched from blocks to timestamps here @@ -677,6 +681,16 @@ func (c *ChainConfig) IsMantleBaseFee(time uint64) bool { return isTimestampForked(c.BaseFeeTime, time) } +// IsMantleBVMETHMintUpgrade returns whether time is either equal to the BVM_ETH mint upgrade fork time or greater. +func (c *ChainConfig) IsMantleBVMETHMintUpgrade(time uint64) bool { + return isTimestampForked(c.BVMETHMintUpgradeTime, time) +} + +// IsMetaTxV2 returns whether time is either equal to the MetaTx fork time or greater. +func (c *ChainConfig) IsMetaTxV2(time uint64) bool { + return isTimestampForked(c.MetaTxUpgradeTime, time) +} + // IsArrowGlacier returns whether num is either equal to the Arrow Glacier (EIP-4345) fork block or greater. func (c *ChainConfig) IsArrowGlacier(num *big.Int) bool { return isBlockForked(c.ArrowGlacierBlock, num) @@ -1046,8 +1060,9 @@ type Rules struct { IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool IsMerge, IsShanghai, isCancun, isPrague bool - IsMantleBaseFee bool + IsMantleBaseFee, IsMantleBVMETHMintUpgrade bool IsOptimismBedrock, IsOptimismRegolith bool + IsMetaTxV2 bool } // Rules ensures c's ChainID is not nil. @@ -1057,22 +1072,24 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules chainID = new(big.Int) } return Rules{ - ChainID: new(big.Int).Set(chainID), - IsHomestead: c.IsHomestead(num), - IsEIP150: c.IsEIP150(num), - IsEIP155: c.IsEIP155(num), - IsEIP158: c.IsEIP158(num), - IsByzantium: c.IsByzantium(num), - IsConstantinople: c.IsConstantinople(num), - IsPetersburg: c.IsPetersburg(num), - IsIstanbul: c.IsIstanbul(num), - IsBerlin: c.IsBerlin(num), - IsLondon: c.IsLondon(num), - IsMerge: isMerge, - IsMantleBaseFee: c.IsMantleBaseFee(timestamp), - IsShanghai: c.IsShanghai(timestamp), - isCancun: c.IsCancun(timestamp), - isPrague: c.IsPrague(timestamp), + ChainID: new(big.Int).Set(chainID), + IsHomestead: c.IsHomestead(num), + IsEIP150: c.IsEIP150(num), + IsEIP155: c.IsEIP155(num), + IsEIP158: c.IsEIP158(num), + IsByzantium: c.IsByzantium(num), + IsConstantinople: c.IsConstantinople(num), + IsPetersburg: c.IsPetersburg(num), + IsIstanbul: c.IsIstanbul(num), + IsBerlin: c.IsBerlin(num), + IsLondon: c.IsLondon(num), + IsMerge: isMerge, + IsMantleBaseFee: c.IsMantleBaseFee(timestamp), + IsMantleBVMETHMintUpgrade: c.IsMantleBVMETHMintUpgrade(timestamp), + IsShanghai: c.IsShanghai(timestamp), + isCancun: c.IsCancun(timestamp), + isPrague: c.IsPrague(timestamp), + IsMetaTxV2: c.IsMetaTxV2(timestamp), // Optimism IsOptimismBedrock: c.IsOptimismBedrock(num), IsOptimismRegolith: c.IsOptimismRegolith(timestamp),