From deba80e729580237fc00c5e1dd6242385cb553de Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 20 Dec 2024 09:28:56 -0700 Subject: [PATCH 1/2] Include tx compression level in calldata units cache --- core/types/transaction.go | 43 +++++++++++++++++++++- core/types/transaction_arbitrum_test.go | 49 +++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 core/types/transaction_arbitrum_test.go diff --git a/core/types/transaction.go b/core/types/transaction.go index 84208eaa21..6033095fac 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -64,8 +64,10 @@ type Transaction struct { inner TxData // Consensus contents of a transaction time time.Time // Time first seen locally (spam avoidance) - // Arbitrum cache: must be atomically accessed - CalldataUnits uint64 + // Arbitrum cache of the calldata units at a brotli compression level. + // The top 8 bits are the brotli compression level last used to compute this, + // and the remaining 56 bits are the calldata units at that compression level. + calldataUnitsForBrotliCompressionLevel atomic.Uint64 // caches hash atomic.Pointer[common.Hash] @@ -73,6 +75,43 @@ type Transaction struct { from atomic.Pointer[sigCache] } +// GetRawCachedCalldataUnits returns the cached brotli compression level and corresponding calldata units, +// or (0, 0) if the cache is empty. +func (tx *Transaction) GetRawCachedCalldataUnits() (uint64, uint64) { + repr := tx.calldataUnitsForBrotliCompressionLevel.Load() + cachedCompressionLevel := repr >> 56 + calldataUnits := repr & ((1 << 56) - 1) + return cachedCompressionLevel, calldataUnits +} + +// GetCachedCalldataUnits returns the cached calldata units for a given brotli compression level, +// returning nil if no cache is present or the cache is for a different compression level. +func (tx *Transaction) GetCachedCalldataUnits(requestedCompressionLevel uint64) *uint64 { + cachedCompressionLevel, cachedUnits := tx.GetRawCachedCalldataUnits() + if cachedUnits == 0 { + // empty cache + return nil + } + if cachedCompressionLevel != requestedCompressionLevel { + // wrong compression level + return nil + } + return &cachedUnits +} + +// SetCachedCalldataUnits sets the cached brotli compression level and corresponding calldata units, +// or clears the cache if the values are too large to fit (at least 2**8 and 2**56 respectively). +// Note that a zero calldataUnits is also treated as an empty cache. +func (tx *Transaction) SetCachedCalldataUnits(compressionLevel uint64, calldataUnits uint64) { + var repr uint64 + // Ensure the compressionLevel and calldataUnits will fit. + // Otherwise, just clear the cache. + if compressionLevel < 1<<8 && calldataUnits < 1<<56 { + repr = uint64(compressionLevel)<<56 | calldataUnits + } + tx.calldataUnitsForBrotliCompressionLevel.Store(repr) +} + // NewTx creates a new transaction. func NewTx(inner TxData) *Transaction { tx := new(Transaction) diff --git a/core/types/transaction_arbitrum_test.go b/core/types/transaction_arbitrum_test.go new file mode 100644 index 0000000000..e3db8dbc41 --- /dev/null +++ b/core/types/transaction_arbitrum_test.go @@ -0,0 +1,49 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import "testing" + +func TestTxCalldataUnitsCache(t *testing.T) { + tx := &Transaction{} + units := tx.GetCachedCalldataUnits(0) + if units != nil { + t.Errorf("unexpected initial cache present %v for compression 0", units) + } + units = tx.GetCachedCalldataUnits(1) + if units != nil { + t.Errorf("unexpected initial cache present %v for compression 1", units) + } + tx.SetCachedCalldataUnits(200, 1000) + units = tx.GetCachedCalldataUnits(100) + if units != nil { + t.Errorf("unexpected cached units %v present for incorrect compression 100", units) + } + units = tx.GetCachedCalldataUnits(0) + if units != nil { + t.Errorf("unexpected cached units %v present for incorrect compression 0", units) + } + units = tx.GetCachedCalldataUnits(200) + if units == nil || *units != 1000 { + t.Errorf("unexpected cached units %v for correct compression 200", units) + } + tx.SetCachedCalldataUnits(1, 1<<60) + units = tx.GetCachedCalldataUnits(1) + if units != nil { + t.Errorf("unexpected cache value %v present after reset", units) + } +} From b6a8f8999c8605bdb1d1eedb96bf6e1c31a6f054 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 20 Dec 2024 09:32:41 -0700 Subject: [PATCH 2/2] Fix useless conversion lint --- core/types/transaction.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 6033095fac..6e8ad56dea 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -107,7 +107,7 @@ func (tx *Transaction) SetCachedCalldataUnits(compressionLevel uint64, calldataU // Ensure the compressionLevel and calldataUnits will fit. // Otherwise, just clear the cache. if compressionLevel < 1<<8 && calldataUnits < 1<<56 { - repr = uint64(compressionLevel)<<56 | calldataUnits + repr = compressionLevel<<56 | calldataUnits } tx.calldataUnitsForBrotliCompressionLevel.Store(repr) }