Skip to content

Commit

Permalink
import upgrade to metatx to ensure sponsor doesn't equal to tx sender
Browse files Browse the repository at this point in the history
  • Loading branch information
abelliumnt authored and pandainzoo committed May 22, 2024
1 parent 87c2ac2 commit 1195f7a
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 36 deletions.
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.MetaTxV1UpgradeTime = mantleUpgradeChainConfig.MetaTxV1UpgradeTime
config.MetaTxV2UpgradeTime = mantleUpgradeChainConfig.MetaTxV2UpgradeTime
}

if overrides != nil && overrides.OverrideShanghai != nil {
Expand Down
19 changes: 12 additions & 7 deletions core/mantle_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,39 @@ var (
ChainID: params.MantleMainnetChainId,
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
MetaTxUpgradeTime: u64Ptr(0),
MetaTxV1UpgradeTime: u64Ptr(0),
MetaTxV2UpgradeTime: 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
MetaTxV1UpgradeTime: nil, //TODO set upgrade timestamp
MetaTxV2UpgradeTime: nil, //TODO set upgrade timestamp
}
MantleLocalUpgradeConfig = MantleUpgradeChainConfig{
ChainID: params.MantleLocalChainId,
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
MetaTxUpgradeTime: u64Ptr(0),
MetaTxV1UpgradeTime: u64Ptr(0),
MetaTxV2UpgradeTime: u64Ptr(0),
}
MantleDefaultUpgradeConfig = MantleUpgradeChainConfig{
BaseFeeTime: u64Ptr(0),
BVMETHMintUpgradeTime: u64Ptr(0),
MetaTxUpgradeTime: u64Ptr(0),
MetaTxV1UpgradeTime: u64Ptr(0),
MetaTxV2UpgradeTime: 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)
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)
MetaTxV1UpgradeTime *uint64 `json:"metaTxV1UpgradeTime"` // MetaTxV1UpgradeBlock identifies the current block height is using metaTx with MetaTxSignDataV2
MetaTxV2UpgradeTime *uint64 `json:"metaTxV2UpgradeTime"` // MetaTxV2UpgradeBlock identifies the current block height is using metaTx with MetaTxSignDataV3
}

func GetUpgradeConfigForMantle(chainID *big.Int) *MantleUpgradeChainConfig {
Expand Down
7 changes: 6 additions & 1 deletion core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,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 @@ -321,6 +321,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 Down
9 changes: 6 additions & 3 deletions core/txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,8 @@ 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 @@ -737,7 +738,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if replL1Cost := pool.l1CostFn(repl.RollupDataGas(), repl.IsDepositTx(), repl.To()); replL1Cost != nil { // add rollup cost
replCost = replCost.Add(cost, 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 +1527,8 @@ 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
25 changes: 18 additions & 7 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, isMetaTxV1Upgraded, isMetaTxV2Upgraded bool) (*MetaTxParams, error) {
if tx.Type() != DynamicFeeTxType {
return nil, nil
}
Expand All @@ -128,10 +129,20 @@ func DecodeAndVerifyMetaTxParams(tx *Transaction, isMetaTxUpgraded bool) (*MetaT
return nil, nil
}

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

if isMetaTxV2Upgraded {
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,
})
Expand Down
82 changes: 69 additions & 13 deletions core/types/meta_transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,10 @@ func generateMetaTxDataWithMockSig(dynamicTx *DynamicFeeTx, expireHeight uint64,
return append(MetaTxPrefix, metaTxDataBz...), nil
}

func generateMetaTxDataV2(dynamicTx *DynamicFeeTx, expireHeight uint64, sponsorPercent uint64,
func generateMetaTxDataV2(dynamicTx *DynamicFeeTx, msgSender common.Address, expireHeight uint64, sponsorPercent uint64,
gasFeeSponsorAddr common.Address, privateKey *ecdsa.PrivateKey) ([]byte, error) {
from := crypto.PubkeyToAddress(userKey.PublicKey)
metaTxSignData := &MetaTxSignDataV2{
From: from,
From: msgSender,
ChainID: dynamicTx.ChainID,
Nonce: dynamicTx.Nonce,
GasTipCap: dynamicTx.GasTipCap,
Expand Down Expand Up @@ -232,7 +231,7 @@ func TestDecodeAndVerifyMetaTxParams(t *testing.T) {
require.NoError(t, err)

// test normal metaTx
metaTxParams, err := DecodeAndVerifyMetaTxParams(signedTx, false)
metaTxParams, err := DecodeAndVerifyMetaTxParams(signedTx, false, false)
require.NoError(t, err)

require.Equal(t, gasFeeSponsorAddr.String(), metaTxParams.GasFeeSponsor.String())
Expand All @@ -250,7 +249,7 @@ func TestDecodeAndVerifyMetaTxParams(t *testing.T) {
signedTx, err = tx.WithSignature(signer, txSignature)
require.NoError(t, err)

_, err = DecodeAndVerifyMetaTxParams(signedTx, false)
_, err = DecodeAndVerifyMetaTxParams(signedTx, false, false)
require.Equal(t, err, ErrInvalidGasFeeSponsorSig)

// Test ErrGasFeeSponsorMismatch
Expand All @@ -265,7 +264,7 @@ func TestDecodeAndVerifyMetaTxParams(t *testing.T) {
signedTx, err = tx.WithSignature(signer, txSignature)
require.NoError(t, err)

_, err = DecodeAndVerifyMetaTxParams(signedTx, true)
_, err = DecodeAndVerifyMetaTxParams(signedTx, true, false)
require.Equal(t, err, ErrGasFeeSponsorMismatch)

// Test ErrGasFeeSponsorMismatch
Expand All @@ -280,7 +279,7 @@ func TestDecodeAndVerifyMetaTxParams(t *testing.T) {
signedTx, err = tx.WithSignature(signer, txSignature)
require.NoError(t, err)

_, err = DecodeAndVerifyMetaTxParams(signedTx, false)
_, err = DecodeAndVerifyMetaTxParams(signedTx, false, false)
require.Equal(t, err, ErrInvalidSponsorPercent)
}

Expand All @@ -305,7 +304,7 @@ func TestDecodeAndVerifyMetaTxParamsV2(t *testing.T) {
AccessList: nil,
}

payload, err := generateMetaTxDataV2(dynamicTx, expireHeight, 50, gasFeeSponsorAddr, gasFeeSponsorKey1)
payload, err := generateMetaTxDataV2(dynamicTx, crypto.PubkeyToAddress(userKey.PublicKey), expireHeight, 50, gasFeeSponsorAddr, gasFeeSponsorKey1)
require.NoError(t, err)

dynamicTx.Data = payload
Expand All @@ -317,7 +316,7 @@ func TestDecodeAndVerifyMetaTxParamsV2(t *testing.T) {
require.NoError(t, err)

// test normal metaTx
metaTxParams, err := DecodeAndVerifyMetaTxParams(signedTx, true)
metaTxParams, err := DecodeAndVerifyMetaTxParams(signedTx, true, false)
require.NoError(t, err)

require.Equal(t, gasFeeSponsorAddr.String(), metaTxParams.GasFeeSponsor.String())
Expand All @@ -335,12 +334,12 @@ func TestDecodeAndVerifyMetaTxParamsV2(t *testing.T) {
signedTx, err = tx.WithSignature(signer, txSignature)
require.NoError(t, err)

_, err = DecodeAndVerifyMetaTxParams(signedTx, true)
_, err = DecodeAndVerifyMetaTxParams(signedTx, true, false)
require.Equal(t, err, ErrInvalidGasFeeSponsorSig)

// Test ErrGasFeeSponsorMismatch
dynamicTx.Data = depositABICalldata
payload, err = generateMetaTxDataV2(dynamicTx, expireHeight, 80, gasFeeSponsorAddr, gasFeeSponsorKey2)
payload, err = generateMetaTxDataV2(dynamicTx, crypto.PubkeyToAddress(userKey.PublicKey), expireHeight, 80, gasFeeSponsorAddr, gasFeeSponsorKey2)
require.NoError(t, err)

dynamicTx.Data = payload
Expand All @@ -350,7 +349,7 @@ func TestDecodeAndVerifyMetaTxParamsV2(t *testing.T) {
signedTx, err = tx.WithSignature(signer, txSignature)
require.NoError(t, err)

_, err = DecodeAndVerifyMetaTxParams(signedTx, true)
_, err = DecodeAndVerifyMetaTxParams(signedTx, true, false)
require.Equal(t, err, ErrGasFeeSponsorMismatch)

// Test ErrGasFeeSponsorMismatch
Expand All @@ -365,6 +364,63 @@ func TestDecodeAndVerifyMetaTxParamsV2(t *testing.T) {
signedTx, err = tx.WithSignature(signer, txSignature)
require.NoError(t, err)

_, err = DecodeAndVerifyMetaTxParams(signedTx, false)
_, err = DecodeAndVerifyMetaTxParams(signedTx, false, false)
require.Equal(t, err, ErrInvalidSponsorPercent)
}

func TestDecodeAndVerifyMetaTxParamsV3(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,
}

userPrivateKey := gasFeeSponsorKey1
msgSender := crypto.PubkeyToAddress(userPrivateKey.PublicKey)
payload, err := generateMetaTxDataV2(dynamicTx, msgSender, 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(), userPrivateKey)
require.NoError(t, err)
signedTx, err := tx.WithSignature(signer, txSignature)
require.NoError(t, err)

// test normal metaTx
metaTxParams, err := DecodeAndVerifyMetaTxParams(signedTx, true, false)
require.NoError(t, err)

require.Equal(t, gasFeeSponsorAddr.String(), metaTxParams.GasFeeSponsor.String())
require.Equal(t, hexutil.Encode(depositABICalldata), hexutil.Encode(metaTxParams.Payload))

dynamicTx.Nonce = dynamicTx.Nonce + 1
payload, err = generateMetaTxDataV2(dynamicTx, msgSender, expireHeight, 50, gasFeeSponsorAddr, gasFeeSponsorKey1)
require.NoError(t, err)

dynamicTx.Data = payload
tx = NewTx(dynamicTx)
txSignature, err = crypto.Sign(signer.Hash(tx).Bytes(), userPrivateKey)
require.NoError(t, err)
signedTx, err = tx.WithSignature(signer, txSignature)
require.NoError(t, err)

// test normal metaTx
metaTxParams, err = DecodeAndVerifyMetaTxParams(signedTx, true, true)
require.Error(t, err, ErrSponsorMustNotEqualToSender)
}
69 changes: 69 additions & 0 deletions core/types/transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,75 @@ type Signer interface {
Equal(Signer) bool
}

type cancunSigner struct{ londonSigner }

// NewCancunSigner returns a signer that accepts
// - EIP-4844 blob transactions
// - EIP-1559 dynamic fee transactions
// - EIP-2930 access list transactions,
// - EIP-155 replay protected transactions, and
// - legacy Homestead transactions.
func NewCancunSigner(chainId *big.Int) Signer {
return cancunSigner{londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}}
}

func (s cancunSigner) Sender(tx *Transaction) (common.Address, error) {
if tx.Type() != BlobTxType {
return s.londonSigner.Sender(tx)
}
V, R, S := tx.RawSignatureValues()
// Blob txs are defined to use 0 and 1 as their recovery
// id, add 27 to become equivalent to unprotected Homestead signatures.
V = new(big.Int).Add(V, big.NewInt(27))
if tx.ChainId().Cmp(s.chainId) != 0 {
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
}
return recoverPlain(s.Hash(tx), R, S, V, true)
}

func (s cancunSigner) Equal(s2 Signer) bool {
x, ok := s2.(cancunSigner)
return ok && x.chainId.Cmp(s.chainId) == 0
}

func (s cancunSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
txdata, ok := tx.inner.(*BlobTx)
if !ok {
return s.londonSigner.SignatureValues(tx, sig)
}
// Check that chain ID of tx matches the signer. We also accept ID zero here,
// because it indicates that the chain ID was not specified in the tx.
if txdata.ChainID.Sign() != 0 && txdata.ChainID.ToBig().Cmp(s.chainId) != 0 {
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
}
R, S, _ = decodeSignature(sig)
V = big.NewInt(int64(sig[64]))
return R, S, V, nil
}

// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s cancunSigner) Hash(tx *Transaction) common.Hash {
if tx.Type() != BlobTxType {
return s.londonSigner.Hash(tx)
}
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasTipCap(),
tx.GasFeeCap(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
tx.BlobGasFeeCap(),
tx.BlobHashes(),
})
}

type londonSigner struct{ eip2930Signer }

// NewLondonSigner returns a signer that accepts
Expand Down
2 changes: 1 addition & 1 deletion light/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
return txpool.ErrNegativeValue
}

metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, pool.config.IsMetaTxV2(header.Time))
metaTxParams, err := types.DecodeAndVerifyMetaTxParams(tx, pool.config.IsMetaTxV2(header.Time), pool.config.IsMetaTxV3(header.Time))
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 1195f7a

Please sign in to comment.