Skip to content

Commit

Permalink
core/state, txpool: handle new sponsored transaction, adjust gas check
Browse files Browse the repository at this point in the history
This commit adjust the gas check to correctly check gas fee from payer of
transaction and value from sender in txpool and in preCheck before applying
transaction.

This commit also enables EIP-2718 to support typed transaction envelop in txpool
after Miko hardfork without enabling Berlin hardfork.
  • Loading branch information
minh-bq committed Nov 1, 2023
1 parent e4681ac commit 19cd64e
Show file tree
Hide file tree
Showing 8 changed files with 900 additions and 44 deletions.
269 changes: 268 additions & 1 deletion core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package core
import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"io/ioutil"
"math/big"
"math/rand"
Expand All @@ -28,6 +27,9 @@ import (
"testing"
"time"

"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/eth/tracers/logger"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
Expand Down Expand Up @@ -3337,3 +3339,268 @@ func TestEIP1559Transition(t *testing.T) {
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
}
}

func TestSponsoredTxTransitionBeforeMiko(t *testing.T) {
var chainConfig params.ChainConfig

chainConfig.HomesteadBlock = common.Big0
chainConfig.EIP150Block = common.Big0
chainConfig.EIP155Block = common.Big0
chainConfig.ChainID = big.NewInt(2020)

engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()

recipient := common.HexToAddress("1000000000000000000000000000000000000001")
senderKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
payerKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}

gspec := &Genesis{
Config: &chainConfig,
}
genesis := gspec.MustCommit(db)
chain, err := NewBlockChain(db, nil, &chainConfig, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create blockchain, err %s", err)
}
defer chain.Stop()
mikoSigner := types.NewMikoSigner(big.NewInt(2020))

innerTx := types.SponsoredTx{
ChainID: big.NewInt(2020),
Nonce: 1,
GasTipCap: big.NewInt(100000),
GasFeeCap: big.NewInt(100000),
Gas: 1000,
To: &recipient,
Value: big.NewInt(10),
ExpiredTime: 1000,
}

innerTx.PayerR, innerTx.PayerS, innerTx.PayerV, err = types.PayerSign(
payerKey,
mikoSigner,
crypto.PubkeyToAddress(senderKey.PublicKey),
&innerTx,
)
if err != nil {
t.Fatalf("Payer fails to sign transaction, err %s", err)
}

tx, err := types.SignNewTx(senderKey, mikoSigner, &innerTx)
if err != nil {
t.Fatalf("Fail to sign transaction, err %s", err)
}

block := GenerateBadBlock(genesis, engine, types.Transactions{tx}, &chainConfig)
_, err = chain.InsertChain(types.Blocks{block})
want := fmt.Errorf("could not apply tx %d [%v]: %w", 0, tx.Hash().String(), ErrTxTypeNotSupported)
if err == nil || err.Error() != want.Error() {
t.Fatalf("Expect error %s, get %s", want, err)
}
}

func TestSponsoredTxTransition(t *testing.T) {
var chainConfig params.ChainConfig

chainConfig.HomesteadBlock = common.Big0
chainConfig.EIP150Block = common.Big0
chainConfig.EIP155Block = common.Big0
chainConfig.MikoBlock = common.Big0
chainConfig.ChainID = big.NewInt(2020)

engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()

recipient := common.HexToAddress("1000000000000000000000000000000000000001")
senderKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
senderAddr := crypto.PubkeyToAddress(senderKey.PublicKey)
payerKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
payerAddr := crypto.PubkeyToAddress(payerKey.PublicKey)
adminKey, err := crypto.GenerateKey()
if err != nil {
t.Fatal(err)
}
adminAddr := crypto.PubkeyToAddress(adminKey.PublicKey)

gspec := &Genesis{
Config: &chainConfig,
Timestamp: 2000,
Alloc: GenesisAlloc{
adminAddr: {Balance: math.BigPow(10, 18)},
},
}
genesis := gspec.MustCommit(db)
chain, err := NewBlockChain(db, nil, &chainConfig, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create blockchain, err %s", err)
}
defer chain.Stop()
mikoSigner := types.NewMikoSigner(big.NewInt(2020))

// 1. Same payer and sender in sponsored tx
innerTx := types.SponsoredTx{
ChainID: big.NewInt(2020),
Nonce: 0,
GasTipCap: big.NewInt(100000),
GasFeeCap: big.NewInt(100000),
Gas: 30000,
To: &recipient,
Value: big.NewInt(10),
ExpiredTime: 1000,
}

innerTx.PayerR, innerTx.PayerS, innerTx.PayerV, err = types.PayerSign(
payerKey,
mikoSigner,
crypto.PubkeyToAddress(payerKey.PublicKey),
&innerTx,
)
if err != nil {
t.Fatalf("Payer fails to sign transaction, err %s", err)
}

sponsoredTx, err := types.SignNewTx(payerKey, mikoSigner, &innerTx)
if err != nil {
t.Fatalf("Fail to sign transaction, err %s", err)
}

block := GenerateBadBlock(genesis, engine, types.Transactions{sponsoredTx}, &chainConfig)
_, err = chain.InsertChain(types.Blocks{block})
if err == nil || !errors.Is(err, types.ErrSamePayerSenderSponsoredTx) {
t.Fatalf("Expect error %s, get %s", types.ErrSamePayerSenderSponsoredTx, err)
}

// 2. Expired sponsored tx

innerTx.PayerR, innerTx.PayerS, innerTx.PayerV, err = types.PayerSign(
payerKey,
mikoSigner,
crypto.PubkeyToAddress(senderKey.PublicKey),
&innerTx,
)
if err != nil {
t.Fatalf("Payer fails to sign transaction, err %s", err)
}

sponsoredTx, err = types.SignNewTx(senderKey, mikoSigner, &innerTx)
if err != nil {
t.Fatalf("Fail to sign transaction, err %s", err)
}

block = GenerateBadBlock(genesis, engine, types.Transactions{sponsoredTx}, &chainConfig)
_, err = chain.InsertChain(types.Blocks{block})
if err == nil || !errors.Is(err, ErrExpiredSponsoredTx) {
t.Fatalf("Expect error %s, get %s", ErrExpiredSponsoredTx, err)
}

// 3. Gas tip cap and gas fee cap are different
innerTx.ExpiredTime = 3000
innerTx.GasTipCap = new(big.Int).Add(innerTx.GasFeeCap, common.Big1)
innerTx.PayerR, innerTx.PayerS, innerTx.PayerV, err = types.PayerSign(
payerKey,
mikoSigner,
crypto.PubkeyToAddress(senderKey.PublicKey),
&innerTx,
)
if err != nil {
t.Fatalf("Payer fails to sign transaction, err %s", err)
}

sponsoredTx, err = types.SignNewTx(senderKey, mikoSigner, &innerTx)
if err != nil {
t.Fatalf("Fail to sign transaction, err %s", err)
}

block = GenerateBadBlock(genesis, engine, types.Transactions{sponsoredTx}, &chainConfig)
_, err = chain.InsertChain(types.Blocks{block})
if err == nil || !errors.Is(err, ErrDifferentFeeCapTipCap) {
t.Fatalf("Expect error %s, get %s", ErrDifferentFeeCapTipCap, err)
}

// 4. Payer does not have sufficient fund
innerTx.GasTipCap = innerTx.GasFeeCap
innerTx.PayerR, innerTx.PayerS, innerTx.PayerV, err = types.PayerSign(
payerKey,
mikoSigner,
crypto.PubkeyToAddress(senderKey.PublicKey),
&innerTx,
)
if err != nil {
t.Fatalf("Payer fails to sign transaction, err %s", err)
}

sponsoredTx, err = types.SignNewTx(senderKey, mikoSigner, &innerTx)
if err != nil {
t.Fatalf("Fail to sign transaction, err %s", err)
}

block = GenerateBadBlock(genesis, engine, types.Transactions{sponsoredTx}, &chainConfig)
_, err = chain.InsertChain(types.Blocks{block})
if err == nil || !errors.Is(err, ErrInsufficientPayerFunds) {
t.Fatalf("Expect error %s, get %s", ErrInsufficientPayerFunds, err)
}

// 5. Sender does not have sufficient fund
gasFee := new(big.Int).Mul(innerTx.GasFeeCap, new(big.Int).SetUint64(innerTx.Gas))
blocks, _ := GenerateChain(&chainConfig, genesis, engine, db, 1, func(i int, bg *BlockGen) {
tx, err := types.SignTx(types.NewTransaction(0, payerAddr, gasFee, params.TxGas, bg.header.BaseFee, nil), mikoSigner, adminKey)
if err != nil {
t.Fatal(err)
}

bg.AddTx(tx)
}, true)
_, err = chain.InsertChain(blocks)
if err != nil {
t.Fatalf("Failed to insert blocks, err %s", err)
}

block = GenerateBadBlock(blocks[0], engine, types.Transactions{sponsoredTx}, &chainConfig)
_, err = chain.InsertChain(types.Blocks{block})
if err == nil || !errors.Is(err, ErrInsufficientSenderFunds) {
t.Fatalf("Expect error %s, get %s", ErrInsufficientSenderFunds, err)
}

// 5. Successfully add tx
blocks, _ = GenerateChain(&chainConfig, blocks[0], engine, db, 1, func(i int, bg *BlockGen) {
tx, err := types.SignTx(types.NewTransaction(1, senderAddr, innerTx.Value, params.TxGas, bg.header.BaseFee, nil), mikoSigner, adminKey)
if err != nil {
t.Fatal(err)
}

bg.AddTx(tx)
bg.AddTx(sponsoredTx)
}, true)
_, err = chain.InsertChain(blocks)
if err != nil {
t.Fatalf("Failed to insert blocks, err %s", err)
}

statedb, _ := chain.State()
// Check sender's balance after sponsored tx
have := statedb.GetBalance(senderAddr)
want := common.Big0
if have.Cmp(want) != 0 {
t.Fatalf("Expect sender's balance %d, get %d", want.Uint64(), have.Uint64())
}
// Check payer's balance after sponsored tx
// Transfer tx costs 21000 gas so 9000 gas is refunded
want = new(big.Int).Mul(innerTx.GasFeeCap, big.NewInt(9000))
have = statedb.GetBalance(payerAddr)
if have.Cmp(want) != 0 {
t.Fatalf("Expect sender's balance %d, get %d", want.Uint64(), have.Uint64())
}
}
15 changes: 15 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,19 @@ var (

// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
ErrSenderNoEOA = errors.New("sender not an eoa")

// ErrExpiredSponsoredTx is returned if the sponsored transaction is expired.
ErrExpiredSponsoredTx = errors.New("sponsored transaction is expired")

// ErrInsufficientPayerFunds is returned if the gas fee cost of executing a transaction
// is higher than the balance of the payer's account.
ErrInsufficientPayerFunds = errors.New("insufficient payer funds for gas * price")

// ErrInsufficientSenderFunds is returned if the value in transaction
// is higher than the balance of the user's account.
ErrInsufficientSenderFunds = errors.New("insufficient sender funds for value")

// ErrDifferentFeeCapTipCap is returned if fee cap and tip cap are different
// when dynamic gas fee is not supported
ErrDifferentFeeCapTipCap = errors.New("gas fee cap and gas tip cap are different")
)
5 changes: 4 additions & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,12 @@ func applyTransaction(
from := msg.From()

// Check if sender and recipient are blacklisted
payer := msg.Payer()
if config.Consortium != nil && config.IsOdysseus(blockNumber) {
contractAddr := config.BlacklistContractAddress
if state.IsAddressBlacklisted(statedb, contractAddr, &from) || state.IsAddressBlacklisted(statedb, contractAddr, msg.To()) {
if state.IsAddressBlacklisted(statedb, contractAddr, &from) ||
state.IsAddressBlacklisted(statedb, contractAddr, msg.To()) ||
state.IsAddressBlacklisted(statedb, contractAddr, &payer) {
return nil, nil, ErrAddressBlacklisted
}
}
Expand Down
Loading

0 comments on commit 19cd64e

Please sign in to comment.