Commit 735ef774 authored by Mark Tyneway's avatar Mark Tyneway

l2geth: prevent too low of fees from getting through

There was a bug in the floating point math that allowed
for transactions with extremely low fees to get through.
This PR fixes that bug as well as adds additional test
cases.
parent fc297257
---
'@eth-optimism/l2geth': patch
---
Fix a bug in the fee logic that allowed for fees that were too low to get through
...@@ -3,6 +3,7 @@ package fees ...@@ -3,6 +3,7 @@ package fees
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -115,7 +116,7 @@ func PaysEnough(opts *PaysEnoughOpts) error { ...@@ -115,7 +116,7 @@ func PaysEnough(opts *PaysEnoughOpts) error {
return fmt.Errorf("%w: no expected fee", errMissingInput) return fmt.Errorf("%w: no expected fee", errMissingInput)
} }
fee := opts.ExpectedFee fee := new(big.Int).Set(opts.ExpectedFee)
// Allow for a downward buffer to protect against L1 gas price volatility // Allow for a downward buffer to protect against L1 gas price volatility
if opts.ThresholdDown != nil { if opts.ThresholdDown != nil {
fee = mulByFloat(fee, opts.ThresholdDown) fee = mulByFloat(fee, opts.ThresholdDown)
...@@ -129,7 +130,7 @@ func PaysEnough(opts *PaysEnoughOpts) error { ...@@ -129,7 +130,7 @@ func PaysEnough(opts *PaysEnoughOpts) error {
if opts.ThresholdUp != nil { if opts.ThresholdUp != nil {
// overpaying = user fee - expected fee // overpaying = user fee - expected fee
overpaying := new(big.Int).Sub(opts.UserFee, opts.ExpectedFee) overpaying := new(big.Int).Sub(opts.UserFee, opts.ExpectedFee)
threshold := mulByFloat(overpaying, opts.ThresholdUp) threshold := mulByFloat(opts.ExpectedFee, opts.ThresholdUp)
// if overpaying > threshold, return error // if overpaying > threshold, return error
if overpaying.Cmp(threshold) == 1 { if overpaying.Cmp(threshold) == 1 {
return ErrFeeTooHigh return ErrFeeTooHigh
...@@ -140,9 +141,10 @@ func PaysEnough(opts *PaysEnoughOpts) error { ...@@ -140,9 +141,10 @@ func PaysEnough(opts *PaysEnoughOpts) error {
func mulByFloat(num *big.Int, float *big.Float) *big.Int { func mulByFloat(num *big.Int, float *big.Float) *big.Int {
n := new(big.Float).SetUint64(num.Uint64()) n := new(big.Float).SetUint64(num.Uint64())
n = n.Mul(n, float) product := n.Mul(n, float)
value, _ := float.Uint64() pfloat, _ := product.Float64()
return new(big.Int).SetUint64(value) rounded := math.Ceil(pfloat)
return new(big.Int).SetUint64(uint64(rounded))
} }
// calculateL1GasLimit computes the L1 gasLimit based on the calldata and // calculateL1GasLimit computes the L1 gasLimit based on the calldata and
......
...@@ -157,13 +157,40 @@ func TestPaysEnough(t *testing.T) { ...@@ -157,13 +157,40 @@ func TestPaysEnough(t *testing.T) {
}, },
"fee-threshold-up": { "fee-threshold-up": {
opts: &PaysEnoughOpts{ opts: &PaysEnoughOpts{
UserFee: common.Big3, UserFee: common.Big256,
ExpectedFee: common.Big1, ExpectedFee: common.Big1,
ThresholdUp: new(big.Float).SetFloat64(1.5), ThresholdUp: new(big.Float).SetFloat64(1.5),
ThresholdDown: nil, ThresholdDown: nil,
}, },
err: ErrFeeTooHigh, err: ErrFeeTooHigh,
}, },
"fee-too-low-high": {
opts: &PaysEnoughOpts{
UserFee: new(big.Int).SetUint64(10_000),
ExpectedFee: new(big.Int).SetUint64(1),
ThresholdUp: new(big.Float).SetFloat64(3),
ThresholdDown: new(big.Float).SetFloat64(0.8),
},
err: ErrFeeTooHigh,
},
"fee-too-low-down": {
opts: &PaysEnoughOpts{
UserFee: new(big.Int).SetUint64(1),
ExpectedFee: new(big.Int).SetUint64(10_000),
ThresholdUp: new(big.Float).SetFloat64(3),
ThresholdDown: new(big.Float).SetFloat64(0.8),
},
err: ErrFeeTooLow,
},
"fee-too-low-down-2": {
opts: &PaysEnoughOpts{
UserFee: new(big.Int).SetUint64(0),
ExpectedFee: new(big.Int).SetUint64(10_000),
ThresholdUp: new(big.Float).SetFloat64(3),
ThresholdDown: new(big.Float).SetFloat64(0.8),
},
err: ErrFeeTooLow,
},
} }
for name, tt := range tests { for name, tt := range tests {
......
...@@ -858,9 +858,6 @@ func (s *SyncService) verifyFee(tx *types.Transaction) error { ...@@ -858,9 +858,6 @@ func (s *SyncService) verifyFee(tx *types.Transaction) error {
// Only count the calldata here as the overhead of the fully encoded // Only count the calldata here as the overhead of the fully encoded
// RLP transaction is handled inside of EncodeL2GasLimit // RLP transaction is handled inside of EncodeL2GasLimit
expectedTxGasLimit := fees.EncodeTxGasLimit(tx.Data(), l1GasPrice, l2GasLimit, l2GasPrice) expectedTxGasLimit := fees.EncodeTxGasLimit(tx.Data(), l1GasPrice, l2GasLimit, l2GasPrice)
if err != nil {
return err
}
// This should only happen if the unscaled transaction fee is greater than 18.44 ETH // This should only happen if the unscaled transaction fee is greater than 18.44 ETH
if !expectedTxGasLimit.IsUint64() { if !expectedTxGasLimit.IsUint64() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment