Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 29 additions & 36 deletions pool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ func (p *Pool) StartPollingMinSuggestedGasPrice(ctx context.Context) {

// AddTx adds a transaction to the pool with the pending state
func (p *Pool) AddTx(ctx context.Context, tx types.Transaction, ip string) error {
if err := p.validateTx(ctx, tx); err != nil {
poolTx := NewTransaction(tx, ip, false, p)
if err := p.validateTx(ctx, *poolTx); err != nil {
return err
}

Expand All @@ -87,17 +88,7 @@ func (p *Pool) AddTx(ctx context.Context, tx types.Transaction, ip string) error

// StoreTx adds a transaction to the pool with the pending state
func (p *Pool) StoreTx(ctx context.Context, tx types.Transaction, ip string, isWIP bool) error {
poolTx := Transaction{
Transaction: tx,
Status: TxStatusPending,
IsClaims: false,
ReceivedAt: time.Now(),
IsWIP: isWIP,
IP: ip,
}

poolTx.IsClaims = poolTx.IsClaimTx(p.l2BridgeAddr, p.cfg.FreeClaimGasLimit)

poolTx := NewTransaction(tx, ip, isWIP, p)
// Execute transaction to calculate its zkCounters
preexecutionResponse, err := p.PreExecuteTx(ctx, tx)
if err != nil {
Expand Down Expand Up @@ -133,7 +124,7 @@ func (p *Pool) StoreTx(ctx context.Context, tx types.Transaction, ip string, isW
}
poolTx.ZKCounters = preexecutionResponse.usedZkCounters

return p.storage.AddTx(ctx, poolTx)
return p.storage.AddTx(ctx, *poolTx)
}

// PreExecuteTx executes a transaction to calculate its zkCounters
Expand Down Expand Up @@ -210,41 +201,43 @@ func (p *Pool) IsTxPending(ctx context.Context, hash common.Hash) (bool, error)
return p.storage.IsTxPending(ctx, hash)
}

func (p *Pool) validateTx(ctx context.Context, tx types.Transaction) error {
func (p *Pool) validateTx(ctx context.Context, poolTx Transaction) error {
// check chain id
txChainID := tx.ChainId().Uint64()
txChainID := poolTx.ChainId().Uint64()
if txChainID != p.chainID && txChainID != 0 {
return ErrInvalidChainID
}

// Accept only legacy transactions until EIP-2718/2930 activates.
if tx.Type() != types.LegacyTxType {
if poolTx.Type() != types.LegacyTxType {
return ErrTxTypeNotSupported
}

// Reject transactions over defined size to prevent DOS attacks
if tx.Size() > p.cfg.MaxTxBytesSize {
if poolTx.Size() > p.cfg.MaxTxBytesSize {
return ErrOversizedData
}

// Reject transactions with a gas price lower than the minimum gas price
p.minSuggestedGasPriceMux.RLock()
gasPriceCmp := tx.GasPrice().Cmp(p.minSuggestedGasPrice)
p.minSuggestedGasPriceMux.RUnlock()
if gasPriceCmp == -1 {
return ErrGasPrice
// Reject transactions with a gas price lower than the minimum gas price if not a claim (claims are free)
if !poolTx.IsClaims {
p.minSuggestedGasPriceMux.RLock()
gasPriceCmp := poolTx.GasPrice().Cmp(p.minSuggestedGasPrice)
p.minSuggestedGasPriceMux.RUnlock()
if gasPriceCmp == -1 {
return ErrGasPrice
}
}

// Transactions can't be negative. This may never happen using RLP decoded
// transactions but may occur if you create a transaction using the RPC.
if tx.Value().Sign() < 0 {
if poolTx.Value().Sign() < 0 {
return ErrNegativeValue
}
// Make sure the transaction is signed properly.
if err := state.CheckSignature(tx); err != nil {
if err := state.CheckSignature(poolTx.Transaction); err != nil {
return ErrInvalidSender
}
from, err := state.GetSender(tx)
from, err := state.GetSender(poolTx.Transaction)
if err != nil {
return ErrInvalidSender
}
Expand All @@ -259,7 +252,7 @@ func (p *Pool) validateTx(ctx context.Context, tx types.Transaction) error {
return err
}
// Ensure the transaction adheres to nonce ordering
if nonce > tx.Nonce() {
if nonce > poolTx.Nonce() {
return ErrNonceTooLow
}

Expand All @@ -270,22 +263,22 @@ func (p *Pool) validateTx(ctx context.Context, tx types.Transaction) error {
return err
}

if balance.Cmp(tx.Cost()) < 0 {
if balance.Cmp(poolTx.Cost()) < 0 {
return ErrInsufficientFunds
}

// Ensure the transaction has more gas than the basic tx fee.
intrGas, err := IntrinsicGas(tx)
// Ensure the transaction has more gas than the basic poolTx fee.
intrGas, err := IntrinsicGas(poolTx.Transaction)
if err != nil {
return err
}
if tx.Gas() < intrGas {
if poolTx.Gas() < intrGas {
return ErrIntrinsicGas
}

// try to get a transaction from the pool with the same nonce to check
// if the new one has a price bump
oldTxs, err := p.storage.GetTxsByFromAndNonce(ctx, from, tx.Nonce())
oldTxs, err := p.storage.GetTxsByFromAndNonce(ctx, from, poolTx.Nonce())
if err != nil {
return err
}
Expand All @@ -300,20 +293,20 @@ func (p *Pool) validateTx(ctx context.Context, tx types.Transaction) error {
}

oldTxPrice := new(big.Int).Mul(oldTx.GasPrice(), new(big.Int).SetUint64(oldTx.Gas()))
txPrice := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
txPrice := new(big.Int).Mul(poolTx.GasPrice(), new(big.Int).SetUint64(poolTx.Gas()))

if oldTx.Hash() == tx.Hash() {
if oldTx.Hash() == poolTx.Hash() {
return ErrAlreadyKnown
}

// if old Tx Price is higher than the new tx price, it returns an error
// if old Tx Price is higher than the new poolTx price, it returns an error
if oldTxPrice.Cmp(txPrice) > 0 {
return ErrReplaceUnderpriced
}
}

// Executor field size requirements check
if err := p.checkTxFieldCompatibilityWithExecutor(ctx, tx); err != nil {
if err := p.checkTxFieldCompatibilityWithExecutor(ctx, poolTx.Transaction); err != nil {
return err
}

Expand Down
15 changes: 15 additions & 0 deletions pool/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ type Transaction struct {
IP string
}

// NewTransaction creates a new transaction
func NewTransaction(tx types.Transaction, ip string, isWIP bool, p *Pool) *Transaction {
poolTx := Transaction{
Transaction: tx,
Status: TxStatusPending,
IsClaims: false,
ReceivedAt: time.Now(),
IsWIP: isWIP,
IP: ip,
}

poolTx.IsClaims = poolTx.IsClaimTx(p.l2BridgeAddr, p.cfg.FreeClaimGasLimit)
return &poolTx
}

// IsClaimTx checks, if tx is a claim tx
func (tx *Transaction) IsClaimTx(l2BridgeAddr common.Address, freeClaimGasLimit uint64) bool {
if tx.To() == nil {
Expand Down