core, eth: for types with accurate size calcs, return uint64, not float (#26046)

* core, eth: for types with accurate size calcs, return uint64, not float

* core/types: proper tx size tests

* core/types: extend tx size test with decoded sizes, fix error

* core/txpool: fix linter

Co-authored-by: Martin Holst Swende <martin@swende.se>
This commit is contained in:
Péter Szilágyi 2022-10-26 15:23:07 +03:00 committed by GitHub
parent 5bed24dd77
commit c4a662176e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 137 additions and 64 deletions

View File

@ -1913,7 +1913,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
// Import all the pruned blocks to make the state available // Import all the pruned blocks to make the state available
var ( var (
blocks []*types.Block blocks []*types.Block
memory common.StorageSize memory uint64
) )
for i := len(hashes) - 1; i >= 0; i-- { for i := len(hashes) - 1; i >= 0; i-- {
// Append the next block to our batch // Append the next block to our batch

View File

@ -595,7 +595,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
return core.ErrTxTypeNotSupported return core.ErrTxTypeNotSupported
} }
// Reject transactions over defined size to prevent DOS attacks // Reject transactions over defined size to prevent DOS attacks
if uint64(tx.Size()) > txMaxSize { if tx.Size() > txMaxSize {
return ErrOversizedData return ErrOversizedData
} }
// Transactions can't be negative. This may never happen using RLP decoded // Transactions can't be negative. This may never happen using RLP decoded

View File

@ -263,7 +263,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
return err return err
} }
b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
b.size.Store(common.StorageSize(rlp.ListSize(size))) b.size.Store(rlp.ListSize(size))
return nil return nil
} }
@ -322,14 +322,14 @@ func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} }
// Size returns the true RLP encoded storage size of the block, either by encoding // Size returns the true RLP encoded storage size of the block, either by encoding
// and returning it, or returning a previously cached value. // and returning it, or returning a previously cached value.
func (b *Block) Size() common.StorageSize { func (b *Block) Size() uint64 {
if size := b.size.Load(); size != nil { if size := b.size.Load(); size != nil {
return size.(common.StorageSize) return size.(uint64)
} }
c := writeCounter(0) c := writeCounter(0)
rlp.Encode(&c, b) rlp.Encode(&c, b)
b.size.Store(common.StorageSize(c)) b.size.Store(uint64(c))
return common.StorageSize(c) return uint64(c)
} }
// SanityCheck can be used to prevent that unbounded fields are // SanityCheck can be used to prevent that unbounded fields are
@ -338,7 +338,7 @@ func (b *Block) SanityCheck() error {
return b.header.SanityCheck() return b.header.SanityCheck()
} }
type writeCounter common.StorageSize type writeCounter uint64
func (c *writeCounter) Write(b []byte) (int, error) { func (c *writeCounter) Write(b []byte) (int, error) {
*c += writeCounter(len(b)) *c += writeCounter(len(b))

View File

@ -53,7 +53,7 @@ func TestBlockEncoding(t *testing.T) {
check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e")) check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743)) check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc))) check("Size", block.Size(), uint64(len(blockEnc)))
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil) tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
@ -90,7 +90,7 @@ func TestEIP1559BlockEncoding(t *testing.T) {
check("Hash", block.Hash(), common.HexToHash("c7252048cd273fe0dac09650027d07f0e3da4ee0675ebbb26627cea92729c372")) check("Hash", block.Hash(), common.HexToHash("c7252048cd273fe0dac09650027d07f0e3da4ee0675ebbb26627cea92729c372"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743)) check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc))) check("Size", block.Size(), uint64(len(blockEnc)))
check("BaseFee", block.BaseFee(), new(big.Int).SetUint64(params.InitialBaseFee)) check("BaseFee", block.BaseFee(), new(big.Int).SetUint64(params.InitialBaseFee))
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil) tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil)
@ -153,7 +153,7 @@ func TestEIP2718BlockEncoding(t *testing.T) {
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017")) check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743)) check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc))) check("Size", block.Size(), uint64(len(blockEnc)))
// Create legacy tx. // Create legacy tx.
to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87") to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")

View File

@ -131,7 +131,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
var inner LegacyTx var inner LegacyTx
err := s.Decode(&inner) err := s.Decode(&inner)
if err == nil { if err == nil {
tx.setDecoded(&inner, int(rlp.ListSize(size))) tx.setDecoded(&inner, rlp.ListSize(size))
} }
return err return err
default: default:
@ -142,7 +142,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
} }
inner, err := tx.decodeTyped(b) inner, err := tx.decodeTyped(b)
if err == nil { if err == nil {
tx.setDecoded(inner, len(b)) tx.setDecoded(inner, uint64(len(b)))
} }
return err return err
} }
@ -158,7 +158,7 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
if err != nil { if err != nil {
return err return err
} }
tx.setDecoded(&data, len(b)) tx.setDecoded(&data, uint64(len(b)))
return nil return nil
} }
// It's an EIP2718 typed transaction envelope. // It's an EIP2718 typed transaction envelope.
@ -166,7 +166,7 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
if err != nil { if err != nil {
return err return err
} }
tx.setDecoded(inner, len(b)) tx.setDecoded(inner, uint64(len(b)))
return nil return nil
} }
@ -190,11 +190,11 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
} }
// setDecoded sets the inner transaction and size after decoding. // setDecoded sets the inner transaction and size after decoding.
func (tx *Transaction) setDecoded(inner TxData, size int) { func (tx *Transaction) setDecoded(inner TxData, size uint64) {
tx.inner = inner tx.inner = inner
tx.time = time.Now() tx.time = time.Now()
if size > 0 { if size > 0 {
tx.size.Store(common.StorageSize(size)) tx.size.Store(size)
} }
} }
@ -372,16 +372,21 @@ func (tx *Transaction) Hash() common.Hash {
return h return h
} }
// Size returns the true RLP encoded storage size of the transaction, either by // Size returns the true encoded storage size of the transaction, either by encoding
// encoding and returning it, or returning a previously cached value. // and returning it, or returning a previously cached value.
func (tx *Transaction) Size() common.StorageSize { func (tx *Transaction) Size() uint64 {
if size := tx.size.Load(); size != nil { if size := tx.size.Load(); size != nil {
return size.(common.StorageSize) return size.(uint64)
} }
c := writeCounter(0) c := writeCounter(0)
rlp.Encode(&c, &tx.inner) rlp.Encode(&c, &tx.inner)
tx.size.Store(common.StorageSize(c))
return common.StorageSize(c) size := uint64(c)
if tx.Type() != LegacyTxType {
size += 1 // type byte
}
tx.size.Store(size)
return size
} }
// WithSignature returns a new transaction with the given signature. // WithSignature returns a new transaction with the given signature.

View File

@ -51,55 +51,55 @@ type txJSON struct {
} }
// MarshalJSON marshals as JSON with a hash. // MarshalJSON marshals as JSON with a hash.
func (t *Transaction) MarshalJSON() ([]byte, error) { func (tx *Transaction) MarshalJSON() ([]byte, error) {
var enc txJSON var enc txJSON
// These are set for all tx types. // These are set for all tx types.
enc.Hash = t.Hash() enc.Hash = tx.Hash()
enc.Type = hexutil.Uint64(t.Type()) enc.Type = hexutil.Uint64(tx.Type())
// Other fields are set conditionally depending on tx type. // Other fields are set conditionally depending on tx type.
switch tx := t.inner.(type) { switch itx := tx.inner.(type) {
case *LegacyTx: case *LegacyTx:
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.Gas = (*hexutil.Uint64)(&tx.Gas) enc.Gas = (*hexutil.Uint64)(&itx.Gas)
enc.GasPrice = (*hexutil.Big)(tx.GasPrice) enc.GasPrice = (*hexutil.Big)(itx.GasPrice)
enc.Value = (*hexutil.Big)(tx.Value) enc.Value = (*hexutil.Big)(itx.Value)
enc.Data = (*hexutil.Bytes)(&tx.Data) enc.Data = (*hexutil.Bytes)(&itx.Data)
enc.To = t.To() enc.To = tx.To()
enc.V = (*hexutil.Big)(tx.V) enc.V = (*hexutil.Big)(itx.V)
enc.R = (*hexutil.Big)(tx.R) enc.R = (*hexutil.Big)(itx.R)
enc.S = (*hexutil.Big)(tx.S) enc.S = (*hexutil.Big)(itx.S)
case *AccessListTx: case *AccessListTx:
enc.ChainID = (*hexutil.Big)(tx.ChainID) enc.ChainID = (*hexutil.Big)(itx.ChainID)
enc.AccessList = &tx.AccessList enc.AccessList = &itx.AccessList
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.Gas = (*hexutil.Uint64)(&tx.Gas) enc.Gas = (*hexutil.Uint64)(&itx.Gas)
enc.GasPrice = (*hexutil.Big)(tx.GasPrice) enc.GasPrice = (*hexutil.Big)(itx.GasPrice)
enc.Value = (*hexutil.Big)(tx.Value) enc.Value = (*hexutil.Big)(itx.Value)
enc.Data = (*hexutil.Bytes)(&tx.Data) enc.Data = (*hexutil.Bytes)(&itx.Data)
enc.To = t.To() enc.To = tx.To()
enc.V = (*hexutil.Big)(tx.V) enc.V = (*hexutil.Big)(itx.V)
enc.R = (*hexutil.Big)(tx.R) enc.R = (*hexutil.Big)(itx.R)
enc.S = (*hexutil.Big)(tx.S) enc.S = (*hexutil.Big)(itx.S)
case *DynamicFeeTx: case *DynamicFeeTx:
enc.ChainID = (*hexutil.Big)(tx.ChainID) enc.ChainID = (*hexutil.Big)(itx.ChainID)
enc.AccessList = &tx.AccessList enc.AccessList = &itx.AccessList
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce) enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.Gas = (*hexutil.Uint64)(&tx.Gas) enc.Gas = (*hexutil.Uint64)(&itx.Gas)
enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap) enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap)
enc.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap) enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap)
enc.Value = (*hexutil.Big)(tx.Value) enc.Value = (*hexutil.Big)(itx.Value)
enc.Data = (*hexutil.Bytes)(&tx.Data) enc.Data = (*hexutil.Bytes)(&itx.Data)
enc.To = t.To() enc.To = tx.To()
enc.V = (*hexutil.Big)(tx.V) enc.V = (*hexutil.Big)(itx.V)
enc.R = (*hexutil.Big)(tx.R) enc.R = (*hexutil.Big)(itx.R)
enc.S = (*hexutil.Big)(tx.S) enc.S = (*hexutil.Big)(itx.S)
} }
return json.Marshal(&enc) return json.Marshal(&enc)
} }
// UnmarshalJSON unmarshals from JSON. // UnmarshalJSON unmarshals from JSON.
func (t *Transaction) UnmarshalJSON(input []byte) error { func (tx *Transaction) UnmarshalJSON(input []byte) error {
var dec txJSON var dec txJSON
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
return err return err
@ -268,7 +268,7 @@ func (t *Transaction) UnmarshalJSON(input []byte) error {
} }
// Now set the inner transaction. // Now set the inner transaction.
t.setDecoded(inner, 0) tx.setDecoded(inner, 0)
// TODO: check hash here? // TODO: check hash here?
return nil return nil

View File

@ -531,3 +531,71 @@ func assertEqual(orig *Transaction, cpy *Transaction) error {
} }
return nil return nil
} }
func TestTransactionSizes(t *testing.T) {
signer := NewLondonSigner(big.NewInt(123))
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
to := common.HexToAddress("0x01")
for i, txdata := range []TxData{
&AccessListTx{
ChainID: big.NewInt(123),
Nonce: 0,
To: nil,
Value: big.NewInt(1000),
Gas: 21000,
GasPrice: big.NewInt(100000),
},
&LegacyTx{
Nonce: 1,
GasPrice: big.NewInt(500),
Gas: 1000000,
To: &to,
Value: big.NewInt(1),
},
&AccessListTx{
ChainID: big.NewInt(123),
Nonce: 1,
GasPrice: big.NewInt(500),
Gas: 1000000,
To: &to,
Value: big.NewInt(1),
AccessList: AccessList{
AccessTuple{
Address: common.HexToAddress("0x01"),
StorageKeys: []common.Hash{common.HexToHash("0x01")},
}},
},
&DynamicFeeTx{
ChainID: big.NewInt(123),
Nonce: 1,
Gas: 1000000,
To: &to,
Value: big.NewInt(1),
GasTipCap: big.NewInt(500),
GasFeeCap: big.NewInt(500),
},
} {
tx, err := SignNewTx(key, signer, txdata)
if err != nil {
t.Fatalf("test %d: %v", i, err)
}
bin, _ := tx.MarshalBinary()
// Check initial calc
if have, want := int(tx.Size()), len(bin); have != want {
t.Errorf("test %d: size wrong, have %d want %d", i, have, want)
}
// Check cached version too
if have, want := int(tx.Size()), len(bin); have != want {
t.Errorf("test %d: (cached) size wrong, have %d want %d", i, have, want)
}
// Check unmarshalled version too
utx := new(Transaction)
if err := utx.UnmarshalBinary(bin); err != nil {
t.Fatalf("test %d: failed to unmarshal tx: %v", i, err)
}
if have, want := int(utx.Size()), len(bin); have != want {
t.Errorf("test %d: (unmarshalled) size wrong, have %d want %d", i, have, want)
}
}
}

View File

@ -369,7 +369,7 @@ func (q *queue) Results(block bool) []*fetchResult {
size += receipt.Size() size += receipt.Size()
} }
for _, tx := range result.Transactions { for _, tx := range result.Transactions {
size += tx.Size() size += common.StorageSize(tx.Size())
} }
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size + q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
(1-common.StorageSize(blockCacheSizeWeight))*q.resultSize (1-common.StorageSize(blockCacheSizeWeight))*q.resultSize

View File

@ -82,7 +82,7 @@ func (p *Peer) broadcastTransactions() {
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ { for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
if tx := p.txpool.Get(queue[i]); tx != nil { if tx := p.txpool.Get(queue[i]); tx != nil {
txs = append(txs, tx) txs = append(txs, tx)
size += tx.Size() size += common.StorageSize(tx.Size())
} }
hashesCount++ hashesCount++
} }

View File

@ -373,7 +373,7 @@ func (q *queue) Results(block bool) []*fetchResult {
size += receipt.Size() size += receipt.Size()
} }
for _, tx := range result.Transactions { for _, tx := range result.Transactions {
size += tx.Size() size += common.StorageSize(tx.Size())
} }
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size + q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
(1-common.StorageSize(blockCacheSizeWeight))*q.resultSize (1-common.StorageSize(blockCacheSizeWeight))*q.resultSize