diff --git a/txpool/pool.go b/txpool/pool.go index 0d038bfab..66925979c 100644 --- a/txpool/pool.go +++ b/txpool/pool.go @@ -229,13 +229,13 @@ const BaseFeeSubPoolLimit = 1024 const QueuedSubPoolLimit = 1024 type Nonce2Tx struct { - btree.BTree + *btree.BTree } type SenderInfo struct { - balance uint256.Int - nonce uint64 - nonce2Tx *Nonce2Tx // sorted map of nonce => *MetaTx + balance uint256.Int + nonce uint64 + txNonce2Tx *Nonce2Tx // sorted map of nonce => *MetaTx } type nonce2TxItem struct { @@ -261,7 +261,7 @@ func OnNewBlocks(senderInfo map[uint64]SenderInfo, minedTxs []*TxSlot, protocolB panic("not implemented yet") } // delete mined transactions from everywhere - sender.nonce2Tx.Ascend(func(i btree.Item) bool { + sender.txNonce2Tx.Ascend(func(i btree.Item) bool { it := i.(*nonce2TxItem) if it.MetaTx.Tx.nonce > sender.nonce { return false @@ -269,7 +269,7 @@ func OnNewBlocks(senderInfo map[uint64]SenderInfo, minedTxs []*TxSlot, protocolB // TODO: save local transactions to cache with TTL, in case of re-org - to restore isLocal flag of re-injected transactions // del from nonce2tx mapping - sender.nonce2Tx.Delete(i) + sender.txNonce2Tx.Delete(i) // del from sub-pool switch it.MetaTx.currentSubPool { case PendingSubPool: @@ -319,8 +319,8 @@ func Unwind(senderInfo map[uint64]SenderInfo, unwindedTxs []*TxSlot, pending *Su // с наибольшим effectiveTip. Кстати, интересно, как это правильно вычислять // наверное нужно просто брать с наибольшим tip // implement it for all inserts - sender.nonce2Tx.Has(&nonce2TxItem{mt}) - sender.nonce2Tx.ReplaceOrInsert(&nonce2TxItem{mt}) + sender.txNonce2Tx.Has(&nonce2TxItem{mt}) + sender.txNonce2Tx.ReplaceOrInsert(&nonce2TxItem{mt}) pending.UnsafeAdd(mt, PendingSubPool) } pending.EnforceInvariants() @@ -329,7 +329,7 @@ func Unwind(senderInfo map[uint64]SenderInfo, unwindedTxs []*TxSlot, pending *Su func onSenderChange(sender SenderInfo, protocolBaseFee, blockBaseFee uint64) { prevNonce := -1 accumulatedSenderSpent := uint256.NewInt(0) - sender.nonce2Tx.Ascend(func(i btree.Item) bool { + sender.txNonce2Tx.Ascend(func(i btree.Item) bool { it := i.(*nonce2TxItem) // Sender has enough balance for: gasLimit x feeCap + transferred_value diff --git a/txpool/pool_fuzz_test.go b/txpool/pool_fuzz_test.go index 9492b59bd..552ca6e93 100644 --- a/txpool/pool_fuzz_test.go +++ b/txpool/pool_fuzz_test.go @@ -7,6 +7,7 @@ import ( "encoding/binary" "testing" + "github.com/google/btree" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" ) @@ -81,14 +82,46 @@ func FuzzTwoQueue(f *testing.F) { }) } -func poolsFromFuzzBytes(s1, s2, s3 []uint8, nonce, sender, values []byte) (pending, baseFee, queued *SubPool, ok bool) { - if len(nonce) == 0 || len(nonce)%8 != 0 { +func u64Slice(in []byte) ([]uint64, bool) { + if len(in) == 0 || len(in)%8 != 0 { + return nil, false + } + res := make([]uint64, len(in)/8) + for i := 0; i < len(res)-1; i++ { + res[i] = binary.BigEndian.Uint64(in[i*8:]) + } + return res, true +} +func u256Slice(in []byte) ([]uint256.Int, bool) { + if len(in) == 0 || len(in)%32 != 0 { + return nil, false + } + res := make([]uint256.Int, len(in)/32) + for i := 0; i < len(res)-1; i++ { + res[i].SetBytes(in[i*32 : (i+1)*32]) + } + return res, true +} + +func poolsFromFuzzBytes(s1, s2, s3 []uint8, rawTxNonce, rawValues, rawSender, rawSenderNonce, rawSenderBalance []byte) (pending, baseFee, queued *SubPool, ok bool) { + txNonce, ok := u64Slice(rawTxNonce) + if !ok { return nil, nil, nil, false } - if len(sender) == 0 || len(sender)%8 != 0 { + values, ok := u256Slice(rawValues) + if !ok { return nil, nil, nil, false } - if len(values) == 0 || len(values)%32 != 0 { + sender, ok := u64Slice(rawSender) + if !ok { + return nil, nil, nil, false + } + senderNonce, ok := u64Slice(rawSenderNonce) + if !ok { + return nil, nil, nil, false + } + senderBalance, ok := u256Slice(rawSenderBalance) + if !ok { return nil, nil, nil, false } @@ -108,69 +141,77 @@ func poolsFromFuzzBytes(s1, s2, s3 []uint8, nonce, sender, values []byte) (pendi } } - iNonce, iSender, iValue := 0, 0, 0 - pending, baseFee, queued = NewSubPool(), NewSubPool(), NewSubPool() - var vb [4]uint64 - for _, s := range s1 { - for i := 0; i < 4; i++ { - vb[i] = binary.BigEndian.Uint64(values[(iValue*8)%len(values):]) - iValue++ + iTx, iSender := 0, 0 + senders := map[uint64]SenderInfo{} + + for _, id := range sender { + _, ok = senders[id] + if ok { + continue } - value := uint256.Int(vb) + + senders[id] = SenderInfo{ + nonce: senderNonce[int(id)%len(senderNonce)], + balance: senderBalance[int(id)%len(senderBalance)], + txNonce2Tx: &Nonce2Tx{btree.New(32)}, + } + } + + pending, baseFee, queued = NewSubPool(), NewSubPool(), NewSubPool() + for _, s := range s1 { mt := &MetaTx{SubPool: SubPoolMarker(s & 0b11111), Tx: &TxSlot{ - nonce: binary.BigEndian.Uint64(nonce[(iNonce*8)%len(nonce):]), - senderID: binary.BigEndian.Uint64(sender[(iSender*8)%len(sender):]), - value: value, + nonce: txNonce[iTx%len(txNonce)], + senderID: sender[iTx%len(sender)], + value: values[iTx%len(values)], }} + senders[mt.Tx.senderID].txNonce2Tx.ReplaceOrInsert(&nonce2TxItem{mt}) pending.Add(mt, PendingSubPool) - iNonce++ + iTx++ iSender++ } for _, s := range s2 { - for i := 0; i < 4; i++ { - vb[i] = binary.BigEndian.Uint64(values[(iValue*8)%len(values):]) - iValue++ - } - value := uint256.Int(vb) mt := &MetaTx{SubPool: SubPoolMarker(s & 0b11111), Tx: &TxSlot{ - nonce: binary.BigEndian.Uint64(nonce[(iNonce*8)%len(nonce):]), - senderID: binary.BigEndian.Uint64(sender[(iSender*8)%len(sender):]), - value: value, + nonce: txNonce[iTx%len(txNonce)], + senderID: sender[iTx%len(sender)], + value: values[iTx%len(values)], }} + senders[mt.Tx.senderID].txNonce2Tx.ReplaceOrInsert(&nonce2TxItem{mt}) baseFee.Add(mt, BaseFeeSubPool) - iNonce++ + iTx++ iSender++ } for _, s := range s3 { - for i := 0; i < 4; i++ { - vb[i] = binary.BigEndian.Uint64(values[(iValue*8)%len(values):]) - iValue++ - } - value := uint256.Int(vb) mt := &MetaTx{SubPool: SubPoolMarker(s & 0b11111), Tx: &TxSlot{ - nonce: binary.BigEndian.Uint64(nonce[(iNonce*8)%len(nonce):]), - senderID: binary.BigEndian.Uint64(sender[(iSender*8)%len(sender):]), - value: value, + nonce: txNonce[iTx%len(txNonce)], + senderID: sender[iTx%len(sender)], + value: values[iTx%len(values)], }} + senders[mt.Tx.senderID].txNonce2Tx.ReplaceOrInsert(&nonce2TxItem{mt}) queued.Add(mt, QueuedSubPool) - iNonce++ + iTx++ iSender++ } return pending, baseFee, queued, true } -func FuzzPromoteStep4(f *testing.F) { - var nNonce = [8]byte{1} - var nAddr = [20]byte{1} - f.Add([]uint8{0b11111, 0b10001, 0b10101, 0b00001, 0b00000}, []uint8{0b11111, 0b10001, 0b10101, 0b00001, 0b00000}, []uint8{0b11111, 0b10001, 0b10101, 0b00001, 0b00000}, nNonce[:], nAddr[:], uint256.NewInt(123).Bytes()) - f.Add([]uint8{0b11111}, []uint8{0b11111}, []uint8{0b11110, 0b0, 0b1010}, nNonce[:], nAddr[:], uint256.NewInt(678).Bytes()) - f.Add([]uint8{0b11000, 0b00101, 0b000111}, []uint8{0b11000, 0b00101, 0b000111}, []uint8{0b11000, 0b00101, 0b000111}, nNonce[:], nAddr[:], uint256.NewInt(987654321).Bytes()) - f.Fuzz(func(t *testing.T, s1, s2, s3 []uint8, nonce, sender, values []byte) { +func FuzzPromoteStep5(f *testing.F) { + var u64 = [8]byte{1} + var u256 = [32]byte{1} + f.Add( + []uint8{0b11111, 0b10001, 0b10101, 0b00001, 0b00000}, []uint8{0b11111, 0b10001, 0b10101, 0b00001, 0b00000}, []uint8{0b11111, 0b10001, 0b10101, 0b00001, 0b00000}, + u64[:], u64[:], u64[:], u256[:], u256[:]) + f.Add( + []uint8{0b11111}, []uint8{0b11111}, []uint8{0b11110, 0b0, 0b1010}, + u64[:], u64[:], u64[:], u256[:], u256[:]) + f.Add( + []uint8{0b11000, 0b00101, 0b000111}, []uint8{0b11000, 0b00101, 0b000111}, []uint8{0b11000, 0b00101, 0b000111}, + u64[:], u64[:], u64[:], u256[:], u256[:]) + f.Fuzz(func(t *testing.T, s1, s2, s3 []uint8, txNonce, values, sender, senderNonce, senderBalance []byte) { t.Parallel() assert := assert.New(t) - pending, baseFee, queued, ok := poolsFromFuzzBytes(s1, s2, s3, nonce, sender, values) + pending, baseFee, queued, ok := poolsFromFuzzBytes(s1, s2, s3, txNonce, values, sender, senderNonce, senderBalance) if !ok { t.Skip() } diff --git a/txpool/types.go b/txpool/types.go index ee63041a3..915bc6a51 100644 --- a/txpool/types.go +++ b/txpool/types.go @@ -94,6 +94,7 @@ func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int) (slot *TxSl if len(payload) == 0 { return nil, sender, 0, fmt.Errorf("%s: empty rlp", ParseTransactionErrorPrefix) } + slot = &TxSlot{} // Compute transaction hash ctx.keccak1.Reset() ctx.keccak2.Reset()