Skip to content
Open
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
4 changes: 4 additions & 0 deletions cl/merkle_tree/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ func packBitsInto(dst [][32]byte, bytes []byte) [][32]byte {
}

func parseBitlist(dst, buf []byte) ([]byte, uint64) {
// A bitlist without its sentinel bit has no recoverable length.
if len(buf) == 0 || buf[len(buf)-1] == 0 {
Comment thread
AskAlexSharov marked this conversation as resolved.
return dst, 0
Comment thread
AskAlexSharov marked this conversation as resolved.
}
msb := uint8(bits.Len8(buf[len(buf)-1])) - 1
size := uint64(8*(len(buf)-1) + int(msb))

Expand Down
17 changes: 17 additions & 0 deletions cl/merkle_tree/merkle_root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,20 @@ func TestProgressiveContainerProofRejectsOversizedSchema(t *testing.T) {
_, err := merkle_tree.ProgressiveContainerProofAll(0, schema...)
require.Error(t, err)
}

func TestBitlistRootWithLimitNoSentinel(t *testing.T) {
const limit = 2048

empty, err := merkle_tree.BitlistRootWithLimit([]byte{}, limit)
require.NoError(t, err)

for _, malformed := range [][]byte{{0x00}, {0xff, 0x00}} {
root, err := merkle_tree.BitlistRootWithLimit(malformed, limit)
require.NoError(t, err)
require.Equal(t, empty, root)
}

wellFormed, err := merkle_tree.BitlistRootWithLimit([]byte{0x03}, limit)
require.NoError(t, err)
require.NotEqual(t, empty, wellFormed)
}
6 changes: 3 additions & 3 deletions cl/phase1/network/backward_beacon_downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"errors"
"fmt"
"io"
"math"
"net/http"
"slices"
"strings"
Expand All @@ -38,6 +37,7 @@ import (
"github.com/erigontech/erigon/cl/sentinel/peers"
"github.com/erigontech/erigon/common"
"github.com/erigontech/erigon/common/log/v3"
"github.com/erigontech/erigon/common/math"
"github.com/erigontech/erigon/db/kv"
"github.com/erigontech/erigon/db/snapshotsync/freezeblocks"
)
Expand Down Expand Up @@ -199,8 +199,8 @@ func (b *BackwardBeaconDownloader) RequestMore(ctx context.Context) error {
// Falls back to the beacon API when P2P is unavailable and an HTTP URL is configured.
func (b *BackwardBeaconDownloader) fetchBlockRange(ctx context.Context) ([]*cltypes.SignedBeaconBlock, error) {
const count = uint64(64)
start := b.slotToDownload.Load() - count + 1
if start > b.slotToDownload.Load() { // overflow check
start, underflow := math.SafeSub(b.slotToDownload.Load(), count-1)
if underflow {
start = 0
}

Expand Down
6 changes: 6 additions & 0 deletions common/math/integer.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ func SafeAdd(x, y uint64) (uint64, bool) {
return sum, carryOut != 0
}

// SafeSub returns x-y and checks for underflow.
func SafeSub(x, y uint64) (uint64, bool) {
diff, borrowOut := bits.Sub64(x, y, 0)
return diff, borrowOut != 0
}

// NextPowerOfTwo returns the least power of two at or above n, and 1 for
// n == 0; n above 1<<63 wraps to 0.
func NextPowerOfTwo(n uint64) uint64 {
Expand Down
17 changes: 17 additions & 0 deletions common/math/integer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,20 @@ func TestNextPowerOfTwo(t *testing.T) {
assert.Equal(t, tc.want, NextPowerOfTwo(tc.in), "n=%d", tc.in)
}
}

func TestSafeSub(t *testing.T) {
for _, tc := range []struct {
x, y, want uint64
underflow bool
}{
{9, 4, 5, false},
{4, 4, 0, false},
{0, 1, ^uint64(0), true},
{4, 9, ^uint64(0) - 4, true},
{^uint64(0), ^uint64(0), 0, false},
} {
got, underflow := SafeSub(tc.x, tc.y)
assert.Equal(t, tc.want, got, "%d-%d", tc.x, tc.y)
assert.Equal(t, tc.underflow, underflow, "%d-%d", tc.x, tc.y)
}
}
11 changes: 8 additions & 3 deletions execution/execmodule/notification_dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/erigontech/erigon/common"
"github.com/erigontech/erigon/common/log/v3"
"github.com/erigontech/erigon/common/math"
"github.com/erigontech/erigon/db/kv"
"github.com/erigontech/erigon/db/rawdb"
"github.com/erigontech/erigon/db/state/execctx"
Expand Down Expand Up @@ -109,9 +110,13 @@ func (d *Dispatcher) Dispatch(
// Genesis (block 0): notify from block 0.
notifyFrom = 0
default:
heightSpan := min(finishProgressAfter-finishProgressBefore, 1024)
notifyFrom = finishProgressAfter - heightSpan
notifyFrom++
// finishProgressAfter can trail finishProgressBefore, and a wrapped
// span would clamp to 1024 and notify over a range that never ran.
span, underflow := math.SafeSub(finishProgressAfter, finishProgressBefore)
if underflow {
span = 0
}
notifyFrom = finishProgressAfter - min(span, 1024) + 1
}
notifyTo := finishProgressAfter + 1 //[from, to)

Expand Down
8 changes: 6 additions & 2 deletions execution/protocol/aa/aa_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func ValidateAATransaction(
}
validationGasUsed = preTxCost

if err := chargeGas(header, tx, gasPool, ibs, preTxCost); err != nil {
if err := chargeGas(header, tx, gasPool, ibs); err != nil {
return nil, 0, err
}

Expand Down Expand Up @@ -317,7 +317,11 @@ func ExecuteAATransaction(
return 0, 0, err
}

gasPool.AddGas(params.TxAAGas + tx.ValidationGasLimit + tx.PaymasterValidationGasLimit + tx.GasLimit + tx.PostOpGasLimit - gasUsed)
totalGasLimit, ok := tx.TotalGasLimit(params.TxAAGas)
if !ok {
return 0, 0, fmt.Errorf("%w: RIP-7560 gas limits sum overflows uint64", protocol.ErrGasLimitReached)
}
gasPool.AddGas(totalGasLimit - gasUsed)

return executionStatus, gasUsed, nil
}
Expand Down
15 changes: 12 additions & 3 deletions execution/protocol/aa/aa_gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ func chargeGas(
tx *types.AccountAbstractionTransaction,
gasPool *protocol.GasPool,
ibs *state.IntraBlockState,
preTxCost uint64,
) error {
baseFee := header.BaseFee
effectiveGasTip := tx.GetEffectiveGasTip(baseFee)
effectiveGasPrice := new(uint256.Int).Add(baseFee, &effectiveGasTip)

totalGasLimit := preTxCost + tx.ValidationGasLimit + tx.PaymasterValidationGasLimit + tx.GasLimit + tx.PostOpGasLimit
// RIP-7560 maxPossibleGasCost is AA_BASE_GAS_COST plus the four declared
// limits. preTxCost carries the dynamic calldata charges too, which the
// validation frame already takes out of ValidationGasLimit — charging it
// here would precharge more than refundGas and the gas pool ever return.
totalGasLimit, ok := tx.TotalGasLimit(params.TxAAGas)
if !ok {
return fmt.Errorf("%w: RIP-7560 gas limits sum overflows uint64", protocol.ErrGasLimitReached)
}
preCharge := new(uint256.Int).SetUint64(totalGasLimit)
preCharge = preCharge.Mul(preCharge, effectiveGasPrice)

Expand Down Expand Up @@ -62,7 +68,10 @@ func refundGas(
effectiveGasPrice := new(uint256.Int).Add(baseFee, &effectiveGasTip)
actualGasCost := new(uint256.Int).Mul(effectiveGasPrice, new(uint256.Int).SetUint64(gasUsed))

totalGasLimit := params.TxAAGas + tx.ValidationGasLimit + tx.PaymasterValidationGasLimit + tx.GasLimit + tx.PostOpGasLimit
totalGasLimit, ok := tx.TotalGasLimit(params.TxAAGas)
if !ok {
return fmt.Errorf("%w: RIP-7560 gas limits sum overflows uint64", protocol.ErrGasLimitReached)
}
preCharge := new(uint256.Int).SetUint64(totalGasLimit)
preCharge = preCharge.Mul(preCharge, effectiveGasPrice)

Expand Down
3 changes: 2 additions & 1 deletion execution/protocol/txn_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/erigontech/erigon/common"
"github.com/erigontech/erigon/common/dbg"
"github.com/erigontech/erigon/common/log/v3"
"github.com/erigontech/erigon/common/math"
"github.com/erigontech/erigon/common/u256"
"github.com/erigontech/erigon/execution/protocol/mdgas"
"github.com/erigontech/erigon/execution/protocol/params"
Expand Down Expand Up @@ -304,7 +305,7 @@ func (st *TxnExecutor) preCheck(gasBailout bool, intrinsicGasResult mdgas.Intrin
return upfrontTxnFees{}, &nonceError{err: ErrNonceTooHigh, from: from, txNonce: msgNonce, stateNonce: stNonce}
} else if stNonce > msgNonce {
return upfrontTxnFees{}, &nonceError{err: ErrNonceTooLow, from: from, txNonce: msgNonce, stateNonce: stNonce}
} else if stNonce+1 < stNonce {
} else if _, overflow := math.SafeAdd(stNonce, 1); overflow {
return upfrontTxnFees{}, fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
from, stNonce)
}
Expand Down
21 changes: 20 additions & 1 deletion execution/types/aa_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/holiman/uint256"

"github.com/erigontech/erigon/common"
"github.com/erigontech/erigon/common/math"
"github.com/erigontech/erigon/execution/abi"
"github.com/erigontech/erigon/execution/chain"
"github.com/erigontech/erigon/execution/protocol/mdgas"
Expand Down Expand Up @@ -114,8 +115,26 @@ func (tx *AccountAbstractionTransaction) GetFeeCap() *uint256.Int {
return tx.FeeCap
}

// TotalGasLimit reports whether base plus the declared gas limits fits in a uint64.
func (tx *AccountAbstractionTransaction) TotalGasLimit(base uint64) (uint64, bool) {
total := base
for _, gas := range [...]uint64{tx.ValidationGasLimit, tx.PaymasterValidationGasLimit, tx.GasLimit, tx.PostOpGasLimit} {
sum, overflow := math.SafeAdd(total, gas)
if overflow {
return 0, false
}
total = sum
}
return total, true
}

func (tx *AccountAbstractionTransaction) GetGasLimit() uint64 {
return params.TxAAGas + tx.ValidationGasLimit + tx.PaymasterValidationGasLimit + tx.GasLimit + tx.PostOpGasLimit
// Saturate: the interface cannot report overflow, and a wrapped-small total would pass gas checks.
total, ok := tx.TotalGasLimit(params.TxAAGas)
if !ok {
return math.MaxUint64
}
return total
}

func (tx *AccountAbstractionTransaction) GetTipCap() *uint256.Int {
Expand Down
15 changes: 15 additions & 0 deletions execution/types/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,3 +876,18 @@ func TestTypedTxEmptyToErrorMessage(t *testing.T) {
})
}
}

func TestAATotalGasLimitOverflow(t *testing.T) {
t.Parallel()

tx := &AccountAbstractionTransaction{ValidationGasLimit: 1, PaymasterValidationGasLimit: 2, PostOpGasLimit: 4}
tx.GasLimit = 8
total, ok := tx.TotalGasLimit(16)
assert.True(t, ok)
assert.Equal(t, uint64(31), total)

overflowing := &AccountAbstractionTransaction{ValidationGasLimit: ^uint64(0), PaymasterValidationGasLimit: 1}
_, ok = overflowing.TotalGasLimit(params.TxAAGas)
assert.False(t, ok)
assert.Equal(t, ^uint64(0), overflowing.GetGasLimit())
}
Loading