eth: rework tx fetcher to use O(1) ops + manage network requests

This commit is contained in:
Péter Szilágyi 2020-01-22 16:39:43 +02:00
parent 049e17116e
commit 9938d954c8
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
128 changed files with 2867 additions and 871 deletions

View File

@ -18,7 +18,6 @@ package core
import ( import (
"errors" "errors"
"fmt"
"math" "math"
"math/big" "math/big"
"sort" "sort"
@ -53,6 +52,10 @@ const (
) )
var ( var (
// ErrAlreadyKnown is returned if the transactions is already contained
// within the pool.
ErrAlreadyKnown = errors.New("already known")
// ErrInvalidSender is returned if the transaction contains an invalid signature. // ErrInvalidSender is returned if the transaction contains an invalid signature.
ErrInvalidSender = errors.New("invalid sender") ErrInvalidSender = errors.New("invalid sender")
@ -579,7 +582,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
if pool.all.Get(hash) != nil { if pool.all.Get(hash) != nil {
log.Trace("Discarding already known transaction", "hash", hash) log.Trace("Discarding already known transaction", "hash", hash)
knownTxMeter.Mark(1) knownTxMeter.Mark(1)
return false, fmt.Errorf("known transaction: %x", hash) return false, ErrAlreadyKnown
} }
// If the transaction fails basic validation, discard it // If the transaction fails basic validation, discard it
if err := pool.validateTx(tx, local); err != nil { if err := pool.validateTx(tx, local); err != nil {
@ -786,7 +789,7 @@ func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
for i, tx := range txs { for i, tx := range txs {
// If the transaction is known, pre-set the error slot // If the transaction is known, pre-set the error slot
if pool.all.Get(tx.Hash()) != nil { if pool.all.Get(tx.Hash()) != nil {
errs[i] = fmt.Errorf("known transaction: %x", tx.Hash()) errs[i] = ErrAlreadyKnown
knownTxMeter.Mark(1) knownTxMeter.Mark(1)
continue continue
} }

View File

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
) )
const ( const (
@ -42,6 +43,26 @@ const (
blockLimit = 64 // Maximum number of unique blocks a peer may have delivered blockLimit = 64 // Maximum number of unique blocks a peer may have delivered
) )
var (
blockAnnounceInMeter = metrics.NewRegisteredMeter("eth/fetcher/block/announces/in", nil)
blockAnnounceOutTimer = metrics.NewRegisteredTimer("eth/fetcher/block/announces/out", nil)
blockAnnounceDropMeter = metrics.NewRegisteredMeter("eth/fetcher/block/announces/drop", nil)
blockAnnounceDOSMeter = metrics.NewRegisteredMeter("eth/fetcher/block/announces/dos", nil)
blockBroadcastInMeter = metrics.NewRegisteredMeter("eth/fetcher/block/broadcasts/in", nil)
blockBroadcastOutTimer = metrics.NewRegisteredTimer("eth/fetcher/block/broadcasts/out", nil)
blockBroadcastDropMeter = metrics.NewRegisteredMeter("eth/fetcher/block/broadcasts/drop", nil)
blockBroadcastDOSMeter = metrics.NewRegisteredMeter("eth/fetcher/block/broadcasts/dos", nil)
headerFetchMeter = metrics.NewRegisteredMeter("eth/fetcher/block/headers", nil)
bodyFetchMeter = metrics.NewRegisteredMeter("eth/fetcher/block/bodies", nil)
headerFilterInMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/headers/in", nil)
headerFilterOutMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/headers/out", nil)
bodyFilterInMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/bodies/in", nil)
bodyFilterOutMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/bodies/out", nil)
)
var ( var (
errTerminated = errors.New("terminated") errTerminated = errors.New("terminated")
) )

View File

@ -1,54 +0,0 @@
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
// Contains the metrics collected by the fetcher.
package fetcher
import (
"github.com/ethereum/go-ethereum/metrics"
)
var (
blockAnnounceInMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/block/announces/in", nil)
blockAnnounceOutTimer = metrics.NewRegisteredTimer("eth/fetcher/prop/block/announces/out", nil)
blockAnnounceDropMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/block/announces/drop", nil)
blockAnnounceDOSMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/block/announces/dos", nil)
blockBroadcastInMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/block/broadcasts/in", nil)
blockBroadcastOutTimer = metrics.NewRegisteredTimer("eth/fetcher/prop/block/broadcasts/out", nil)
blockBroadcastDropMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/block/broadcasts/drop", nil)
blockBroadcastDOSMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/block/broadcasts/dos", nil)
headerFetchMeter = metrics.NewRegisteredMeter("eth/fetcher/fetch/headers", nil)
bodyFetchMeter = metrics.NewRegisteredMeter("eth/fetcher/fetch/bodies", nil)
headerFilterInMeter = metrics.NewRegisteredMeter("eth/fetcher/filter/headers/in", nil)
headerFilterOutMeter = metrics.NewRegisteredMeter("eth/fetcher/filter/headers/out", nil)
bodyFilterInMeter = metrics.NewRegisteredMeter("eth/fetcher/filter/bodies/in", nil)
bodyFilterOutMeter = metrics.NewRegisteredMeter("eth/fetcher/filter/bodies/out", nil)
txAnnounceInMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/transaction/announces/in", nil)
txAnnounceDOSMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/transaction/announces/dos", nil)
txAnnounceSkipMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/transaction/announces/skip", nil)
txAnnounceUnderpriceMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/transaction/announces/underprice", nil)
txBroadcastInMeter = metrics.NewRegisteredMeter("eth/fetcher/prop/transaction/broadcasts/in", nil)
txFetchOutMeter = metrics.NewRegisteredMeter("eth/fetcher/fetch/transaction/out", nil)
txFetchSuccessMeter = metrics.NewRegisteredMeter("eth/fetcher/fetch/transaction/success", nil)
txFetchTimeoutMeter = metrics.NewRegisteredMeter("eth/fetcher/fetch/transaction/timeout", nil)
txFetchInvalidMeter = metrics.NewRegisteredMeter("eth/fetcher/fetch/transaction/invalid", nil)
txFetchDurationTimer = metrics.NewRegisteredTimer("eth/fetcher/fetch/transaction/duration", nil)
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@ const (
// The number is referenced from the size of tx pool. // The number is referenced from the size of tx pool.
txChanSize = 4096 txChanSize = 4096
// minimim number of peers to broadcast new blocks to // minimim number of peers to broadcast entire blocks and transactions too.
minBroadcastPeers = 4 minBroadcastPeers = 4
) )
@ -192,7 +192,15 @@ func NewProtocolManager(config *params.ChainConfig, checkpoint *params.TrustedCh
return n, err return n, err
} }
manager.blockFetcher = fetcher.NewBlockFetcher(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer) manager.blockFetcher = fetcher.NewBlockFetcher(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer)
manager.txFetcher = fetcher.NewTxFetcher(txpool.Has, txpool.AddRemotes, manager.removePeer)
fetchTx := func(peer string, hashes []common.Hash) error {
p := manager.peers.Peer(peer)
if p == nil {
return errors.New("unknown peer")
}
return p.RequestTxs(hashes)
}
manager.txFetcher = fetcher.NewTxFetcher(txpool.Has, txpool.AddRemotes, fetchTx)
return manager, nil return manager, nil
} }
@ -240,6 +248,8 @@ func (pm *ProtocolManager) removePeer(id string) {
// Unregister the peer from the downloader and Ethereum peer set // Unregister the peer from the downloader and Ethereum peer set
pm.downloader.UnregisterPeer(id) pm.downloader.UnregisterPeer(id)
pm.txFetcher.Drop(id)
if err := pm.peers.Unregister(id); err != nil { if err := pm.peers.Unregister(id); err != nil {
log.Error("Peer removal failed", "peer", id, "err", err) log.Error("Peer removal failed", "peer", id, "err", err)
} }
@ -263,7 +273,7 @@ func (pm *ProtocolManager) Start(maxPeers int) {
// start sync handlers // start sync handlers
go pm.syncer() go pm.syncer()
go pm.txsyncLoop() go pm.txsyncLoop64() // TODO(karalabe): Legacy initial tx echange, drop with eth/64.
} }
func (pm *ProtocolManager) Stop() { func (pm *ProtocolManager) Stop() {
@ -292,7 +302,7 @@ func (pm *ProtocolManager) Stop() {
} }
func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter, getPooledTx func(hash common.Hash) *types.Transaction) *peer { func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter, getPooledTx func(hash common.Hash) *types.Transaction) *peer {
return newPeer(pv, p, newMeteredMsgWriter(rw), getPooledTx) return newPeer(pv, p, rw, getPooledTx)
} }
// handle is the callback invoked to manage the life cycle of an eth peer. When // handle is the callback invoked to manage the life cycle of an eth peer. When
@ -316,9 +326,6 @@ func (pm *ProtocolManager) handle(p *peer) error {
p.Log().Debug("Ethereum handshake failed", "err", err) p.Log().Debug("Ethereum handshake failed", "err", err)
return err return err
} }
if rw, ok := p.rw.(*meteredMsgReadWriter); ok {
rw.Init(p.version)
}
// Register the peer locally // Register the peer locally
if err := pm.peers.Register(p); err != nil { if err := pm.peers.Register(p); err != nil {
p.Log().Error("Ethereum peer registration failed", "err", err) p.Log().Error("Ethereum peer registration failed", "err", err)
@ -740,20 +747,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Schedule all the unknown hashes for retrieval // Schedule all the unknown hashes for retrieval
var unknown []common.Hash
for _, hash := range hashes { for _, hash := range hashes {
// Mark the hashes as present at the remote node
p.MarkTransaction(hash) p.MarkTransaction(hash)
// Filter duplicated transaction announcement.
// Notably we only dedupliate announcement in txpool, check the rationale
// behind in EIP https://github.com/ethereum/EIPs/pull/2464.
if pm.txpool.Has(hash) {
continue
} }
unknown = append(unknown, hash) pm.txFetcher.Notify(p.id, hashes)
}
pm.txFetcher.Notify(p.id, unknown, time.Now(), p.AsyncRequestTxs)
case msg.Code == GetPooledTransactionsMsg && p.version >= eth65: case msg.Code == GetPooledTransactionsMsg && p.version >= eth65:
// Decode the retrieval message // Decode the retrieval message
@ -765,6 +762,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
var ( var (
hash common.Hash hash common.Hash
bytes int bytes int
hashes []common.Hash
txs []rlp.RawValue txs []rlp.RawValue
) )
for bytes < softResponseLimit { for bytes < softResponseLimit {
@ -783,13 +781,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if encoded, err := rlp.EncodeToBytes(tx); err != nil { if encoded, err := rlp.EncodeToBytes(tx); err != nil {
log.Error("Failed to encode transaction", "err", err) log.Error("Failed to encode transaction", "err", err)
} else { } else {
hashes = append(hashes, hash)
txs = append(txs, encoded) txs = append(txs, encoded)
bytes += len(encoded) bytes += len(encoded)
} }
} }
return p.SendTransactionRLP(txs) return p.SendPooledTransactionsRLP(hashes, txs)
case msg.Code == TxMsg: case msg.Code == TransactionMsg || (msg.Code == PooledTransactionsMsg && p.version >= eth65):
// Transactions arrived, make sure we have a valid and fresh chain to handle them // Transactions arrived, make sure we have a valid and fresh chain to handle them
if atomic.LoadUint32(&pm.acceptTxs) == 0 { if atomic.LoadUint32(&pm.acceptTxs) == 0 {
break break
@ -806,7 +805,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
} }
p.MarkTransaction(tx.Hash()) p.MarkTransaction(tx.Hash())
} }
pm.txFetcher.EnqueueTxs(p.id, txs) pm.txFetcher.Enqueue(p.id, txs, msg.Code == PooledTransactionsMsg)
default: default:
return errResp(ErrInvalidMsgCode, "%v", msg.Code) return errResp(ErrInvalidMsgCode, "%v", msg.Code)
@ -854,9 +853,9 @@ func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) {
} }
} }
// BroadcastTxs will propagate a batch of transactions to all peers which are not known to // BroadcastTransactions will propagate a batch of transactions to all peers which are not known to
// already have the given transaction. // already have the given transaction.
func (pm *ProtocolManager) BroadcastTxs(txs types.Transactions, propagate bool) { func (pm *ProtocolManager) BroadcastTransactions(txs types.Transactions, propagate bool) {
var ( var (
txset = make(map[*peer][]common.Hash) txset = make(map[*peer][]common.Hash)
annos = make(map[*peer][]common.Hash) annos = make(map[*peer][]common.Hash)
@ -894,7 +893,7 @@ func (pm *ProtocolManager) BroadcastTxs(txs types.Transactions, propagate bool)
} }
for peer, hashes := range annos { for peer, hashes := range annos {
if peer.version >= eth65 { if peer.version >= eth65 {
peer.AsyncSendTransactionHashes(hashes) peer.AsyncSendPooledTransactionHashes(hashes)
} else { } else {
peer.AsyncSendTransactions(hashes) peer.AsyncSendTransactions(hashes)
} }
@ -918,11 +917,11 @@ func (pm *ProtocolManager) txBroadcastLoop() {
case event := <-pm.txsCh: case event := <-pm.txsCh:
// For testing purpose only, disable propagation // For testing purpose only, disable propagation
if pm.broadcastTxAnnouncesOnly { if pm.broadcastTxAnnouncesOnly {
pm.BroadcastTxs(event.Txs, false) pm.BroadcastTransactions(event.Txs, false)
continue continue
} }
pm.BroadcastTxs(event.Txs, true) // First propagate transactions to peers pm.BroadcastTransactions(event.Txs, true) // First propagate transactions to peers
pm.BroadcastTxs(event.Txs, false) // Only then announce to the rest pm.BroadcastTransactions(event.Txs, false) // Only then announce to the rest
// Err() channel will be closed when unsubscribing. // Err() channel will be closed when unsubscribing.
case <-pm.txsSub.Err(): case <-pm.txsSub.Err():

View File

@ -1,139 +0,0 @@
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
package eth
import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/p2p"
)
var (
propTxnInPacketsMeter = metrics.NewRegisteredMeter("eth/prop/txns/in/packets", nil)
propTxnInTrafficMeter = metrics.NewRegisteredMeter("eth/prop/txns/in/traffic", nil)
propTxnOutPacketsMeter = metrics.NewRegisteredMeter("eth/prop/txns/out/packets", nil)
propTxnOutTrafficMeter = metrics.NewRegisteredMeter("eth/prop/txns/out/traffic", nil)
propHashInPacketsMeter = metrics.NewRegisteredMeter("eth/prop/hashes/in/packets", nil)
propHashInTrafficMeter = metrics.NewRegisteredMeter("eth/prop/hashes/in/traffic", nil)
propHashOutPacketsMeter = metrics.NewRegisteredMeter("eth/prop/hashes/out/packets", nil)
propHashOutTrafficMeter = metrics.NewRegisteredMeter("eth/prop/hashes/out/traffic", nil)
propBlockInPacketsMeter = metrics.NewRegisteredMeter("eth/prop/blocks/in/packets", nil)
propBlockInTrafficMeter = metrics.NewRegisteredMeter("eth/prop/blocks/in/traffic", nil)
propBlockOutPacketsMeter = metrics.NewRegisteredMeter("eth/prop/blocks/out/packets", nil)
propBlockOutTrafficMeter = metrics.NewRegisteredMeter("eth/prop/blocks/out/traffic", nil)
reqHeaderInPacketsMeter = metrics.NewRegisteredMeter("eth/req/headers/in/packets", nil)
reqHeaderInTrafficMeter = metrics.NewRegisteredMeter("eth/req/headers/in/traffic", nil)
reqHeaderOutPacketsMeter = metrics.NewRegisteredMeter("eth/req/headers/out/packets", nil)
reqHeaderOutTrafficMeter = metrics.NewRegisteredMeter("eth/req/headers/out/traffic", nil)
reqBodyInPacketsMeter = metrics.NewRegisteredMeter("eth/req/bodies/in/packets", nil)
reqBodyInTrafficMeter = metrics.NewRegisteredMeter("eth/req/bodies/in/traffic", nil)
reqBodyOutPacketsMeter = metrics.NewRegisteredMeter("eth/req/bodies/out/packets", nil)
reqBodyOutTrafficMeter = metrics.NewRegisteredMeter("eth/req/bodies/out/traffic", nil)
reqStateInPacketsMeter = metrics.NewRegisteredMeter("eth/req/states/in/packets", nil)
reqStateInTrafficMeter = metrics.NewRegisteredMeter("eth/req/states/in/traffic", nil)
reqStateOutPacketsMeter = metrics.NewRegisteredMeter("eth/req/states/out/packets", nil)
reqStateOutTrafficMeter = metrics.NewRegisteredMeter("eth/req/states/out/traffic", nil)
reqReceiptInPacketsMeter = metrics.NewRegisteredMeter("eth/req/receipts/in/packets", nil)
reqReceiptInTrafficMeter = metrics.NewRegisteredMeter("eth/req/receipts/in/traffic", nil)
reqReceiptOutPacketsMeter = metrics.NewRegisteredMeter("eth/req/receipts/out/packets", nil)
reqReceiptOutTrafficMeter = metrics.NewRegisteredMeter("eth/req/receipts/out/traffic", nil)
miscInPacketsMeter = metrics.NewRegisteredMeter("eth/misc/in/packets", nil)
miscInTrafficMeter = metrics.NewRegisteredMeter("eth/misc/in/traffic", nil)
miscOutPacketsMeter = metrics.NewRegisteredMeter("eth/misc/out/packets", nil)
miscOutTrafficMeter = metrics.NewRegisteredMeter("eth/misc/out/traffic", nil)
)
// meteredMsgReadWriter is a wrapper around a p2p.MsgReadWriter, capable of
// accumulating the above defined metrics based on the data stream contents.
type meteredMsgReadWriter struct {
p2p.MsgReadWriter // Wrapped message stream to meter
version int // Protocol version to select correct meters
}
// newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the
// metrics system is disabled, this function returns the original object.
func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter {
if !metrics.Enabled {
return rw
}
return &meteredMsgReadWriter{MsgReadWriter: rw}
}
// Init sets the protocol version used by the stream to know which meters to
// increment in case of overlapping message ids between protocol versions.
func (rw *meteredMsgReadWriter) Init(version int) {
rw.version = version
}
func (rw *meteredMsgReadWriter) ReadMsg() (p2p.Msg, error) {
// Read the message and short circuit in case of an error
msg, err := rw.MsgReadWriter.ReadMsg()
if err != nil {
return msg, err
}
// Account for the data traffic
packets, traffic := miscInPacketsMeter, miscInTrafficMeter
switch {
case msg.Code == BlockHeadersMsg:
packets, traffic = reqHeaderInPacketsMeter, reqHeaderInTrafficMeter
case msg.Code == BlockBodiesMsg:
packets, traffic = reqBodyInPacketsMeter, reqBodyInTrafficMeter
case rw.version >= eth63 && msg.Code == NodeDataMsg:
packets, traffic = reqStateInPacketsMeter, reqStateInTrafficMeter
case rw.version >= eth63 && msg.Code == ReceiptsMsg:
packets, traffic = reqReceiptInPacketsMeter, reqReceiptInTrafficMeter
case msg.Code == NewBlockHashesMsg:
packets, traffic = propHashInPacketsMeter, propHashInTrafficMeter
case msg.Code == NewBlockMsg:
packets, traffic = propBlockInPacketsMeter, propBlockInTrafficMeter
case msg.Code == TxMsg:
packets, traffic = propTxnInPacketsMeter, propTxnInTrafficMeter
}
packets.Mark(1)
traffic.Mark(int64(msg.Size))
return msg, err
}
func (rw *meteredMsgReadWriter) WriteMsg(msg p2p.Msg) error {
// Account for the data traffic
packets, traffic := miscOutPacketsMeter, miscOutTrafficMeter
switch {
case msg.Code == BlockHeadersMsg:
packets, traffic = reqHeaderOutPacketsMeter, reqHeaderOutTrafficMeter
case msg.Code == BlockBodiesMsg:
packets, traffic = reqBodyOutPacketsMeter, reqBodyOutTrafficMeter
case rw.version >= eth63 && msg.Code == NodeDataMsg:
packets, traffic = reqStateOutPacketsMeter, reqStateOutTrafficMeter
case rw.version >= eth63 && msg.Code == ReceiptsMsg:
packets, traffic = reqReceiptOutPacketsMeter, reqReceiptOutTrafficMeter
case msg.Code == NewBlockHashesMsg:
packets, traffic = propHashOutPacketsMeter, propHashOutTrafficMeter
case msg.Code == NewBlockMsg:
packets, traffic = propBlockOutPacketsMeter, propBlockOutTrafficMeter
case msg.Code == TxMsg:
packets, traffic = propTxnOutPacketsMeter, propTxnOutTrafficMeter
}
packets.Mark(1)
traffic.Mark(int64(msg.Size))
// Send the packet to the p2p layer
return rw.MsgReadWriter.WriteMsg(msg)
}

View File

@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/fetcher"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -43,17 +42,13 @@ const (
maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS) maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS)
// maxQueuedTxs is the maximum number of transactions to queue up before dropping // maxQueuedTxs is the maximum number of transactions to queue up before dropping
// broadcasts. // older broadcasts.
maxQueuedTxs = 4096 maxQueuedTxs = 4096
// maxQueuedTxAnns is the maximum number of transaction announcements to queue up // maxQueuedTxAnns is the maximum number of transaction announcements to queue up
// before dropping broadcasts. // before dropping older announcements.
maxQueuedTxAnns = 4096 maxQueuedTxAnns = 4096
// maxQueuedTxRetrieval is the maximum number of tx retrieval requests to queue up
// before dropping requests.
maxQueuedTxRetrieval = 4096
// maxQueuedBlocks is the maximum number of block propagations to queue up before // maxQueuedBlocks is the maximum number of block propagations to queue up before
// dropping broadcasts. There's not much point in queueing stale blocks, so a few // dropping broadcasts. There's not much point in queueing stale blocks, so a few
// that might cover uncles should be enough. // that might cover uncles should be enough.
@ -102,14 +97,15 @@ type peer struct {
td *big.Int td *big.Int
lock sync.RWMutex lock sync.RWMutex
knownTxs mapset.Set // Set of transaction hashes known to be known by this peer
knownBlocks mapset.Set // Set of block hashes known to be known by this peer knownBlocks mapset.Set // Set of block hashes known to be known by this peer
queuedBlocks chan *propEvent // Queue of blocks to broadcast to the peer queuedBlocks chan *propEvent // Queue of blocks to broadcast to the peer
queuedBlockAnns chan *types.Block // Queue of blocks to announce to the peer queuedBlockAnns chan *types.Block // Queue of blocks to announce to the peer
txPropagation chan []common.Hash // Channel used to queue transaction propagation requests
knownTxs mapset.Set // Set of transaction hashes known to be known by this peer
txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests
txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests
txRetrieval chan []common.Hash // Channel used to queue transaction retrieval requests
getPooledTx func(common.Hash) *types.Transaction // Callback used to retrieve transaction from txpool getPooledTx func(common.Hash) *types.Transaction // Callback used to retrieve transaction from txpool
term chan struct{} // Termination channel to stop the broadcaster term chan struct{} // Termination channel to stop the broadcaster
} }
@ -123,17 +119,16 @@ func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter, getPooledTx func(ha
knownBlocks: mapset.NewSet(), knownBlocks: mapset.NewSet(),
queuedBlocks: make(chan *propEvent, maxQueuedBlocks), queuedBlocks: make(chan *propEvent, maxQueuedBlocks),
queuedBlockAnns: make(chan *types.Block, maxQueuedBlockAnns), queuedBlockAnns: make(chan *types.Block, maxQueuedBlockAnns),
txPropagation: make(chan []common.Hash), txBroadcast: make(chan []common.Hash),
txAnnounce: make(chan []common.Hash), txAnnounce: make(chan []common.Hash),
txRetrieval: make(chan []common.Hash),
getPooledTx: getPooledTx, getPooledTx: getPooledTx,
term: make(chan struct{}), term: make(chan struct{}),
} }
} }
// broadcastBlocks is a write loop that multiplexes block propagations, // broadcastBlocks is a write loop that multiplexes blocks and block accouncements
// announcements into the remote peer. The goal is to have an async writer // to the remote peer. The goal is to have an async writer that does not lock up
// that does not lock up node internals. // node internals and at the same time rate limits queued data.
func (p *peer) broadcastBlocks() { func (p *peer) broadcastBlocks() {
for { for {
select { select {
@ -155,105 +150,60 @@ func (p *peer) broadcastBlocks() {
} }
} }
// broadcastTxs is a write loop that multiplexes transaction propagations, // broadcastTransactions is a write loop that schedules transaction broadcasts
// announcements into the remote peer. The goal is to have an async writer // to the remote peer. The goal is to have an async writer that does not lock up
// that does not lock up node internals. // node internals and at the same time rate limits queued data.
func (p *peer) broadcastTxs() { func (p *peer) broadcastTransactions() {
var ( var (
txProps []common.Hash // Queue of transaction propagations to the peer queue []common.Hash // Queue of hashes to broadcast as full transactions
txAnnos []common.Hash // Queue of transaction announcements to the peer done chan struct{} // Non-nil if background broadcaster is running
done chan struct{} // Non-nil if background network sender routine is active. fail = make(chan error) // Channel used to receive network error
errch = make(chan error) // Channel used to receive network error
) )
scheduleTask := func() { for {
// Short circuit if there already has a inflight task. // If there's no in-flight broadcast running, check if a new one is needed
if done != nil { if done == nil && len(queue) > 0 {
return // Pile transaction until we reach our allowed network limit
}
// Spin up transaction propagation task if there is any
// queued hashes.
if len(txProps) > 0 {
var ( var (
hashes []common.Hash hashes []common.Hash
txs []*types.Transaction txs []*types.Transaction
size common.StorageSize size common.StorageSize
) )
for i := 0; i < len(txProps) && size < txsyncPackSize; i++ { for i := 0; i < len(queue) && size < txsyncPackSize; i++ {
if tx := p.getPooledTx(txProps[i]); tx != nil { if tx := p.getPooledTx(queue[i]); tx != nil {
txs = append(txs, tx) txs = append(txs, tx)
size += tx.Size() size += tx.Size()
} }
hashes = append(hashes, txProps[i]) hashes = append(hashes, queue[i])
} }
txProps = txProps[:copy(txProps, txProps[len(hashes):])] queue = queue[:copy(queue, queue[len(hashes):])]
// If there's anything available to transfer, fire up an async writer
if len(txs) > 0 { if len(txs) > 0 {
done = make(chan struct{}) done = make(chan struct{})
go func() { go func() {
if err := p.SendNewTransactions(txs); err != nil { if err := p.sendTransactions(txs); err != nil {
errch <- err fail <- err
return return
} }
close(done) close(done)
p.Log().Trace("Sent transactions", "count", len(txs)) p.Log().Trace("Sent transactions", "count", len(txs))
}() }()
return
} }
} }
// Spin up transaction announcement task if there is any // Transfer goroutine may or may not have been started, listen for events
// queued hashes.
if len(txAnnos) > 0 {
var (
hashes []common.Hash
pending []common.Hash
size common.StorageSize
)
for i := 0; i < len(txAnnos) && size < txsyncPackSize; i++ {
if tx := p.getPooledTx(txAnnos[i]); tx != nil {
pending = append(pending, txAnnos[i])
size += common.HashLength
}
hashes = append(hashes, txAnnos[i])
}
txAnnos = txAnnos[:copy(txAnnos, txAnnos[len(hashes):])]
if len(pending) > 0 {
done = make(chan struct{})
go func() {
if err := p.SendNewTransactionHashes(pending); err != nil {
errch <- err
return
}
close(done)
p.Log().Trace("Sent transaction announcements", "count", len(pending))
}()
}
}
}
for {
scheduleTask()
select { select {
case hashes := <-p.txPropagation: case hashes := <-p.txBroadcast:
if len(txProps) == maxQueuedTxs { // New batch of transactions to be broadcast, queue them (with cap)
continue queue = append(queue, hashes...)
if len(queue) > maxQueuedTxs {
// Fancy copy and resize to ensure buffer doesn't grow indefinitely
queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])]
} }
if len(txProps)+len(hashes) > maxQueuedTxs {
hashes = hashes[:maxQueuedTxs-len(txProps)]
}
txProps = append(txProps, hashes...)
case hashes := <-p.txAnnounce:
if len(txAnnos) == maxQueuedTxAnns {
continue
}
if len(txAnnos)+len(hashes) > maxQueuedTxAnns {
hashes = hashes[:maxQueuedTxAnns-len(txAnnos)]
}
txAnnos = append(txAnnos, hashes...)
case <-done: case <-done:
done = nil done = nil
case <-errch: case <-fail:
return return
case <-p.term: case <-p.term:
@ -262,60 +212,60 @@ func (p *peer) broadcastTxs() {
} }
} }
// retrievalTxs is a write loop which is responsible for retrieving transaction // announceTransactions is a write loop that schedules transaction broadcasts
// from the remote peer. The goal is to have an async writer that does not lock // to the remote peer. The goal is to have an async writer that does not lock up
// up node internals. If there are too many requests queued, then new arrival // node internals and at the same time rate limits queued data.
// requests will be dropped silently so that we can ensure the memory assumption func (p *peer) announceTransactions() {
// is fixed for each peer.
func (p *peer) retrievalTxs() {
var ( var (
requests []common.Hash // Queue of transaction requests to the peer queue []common.Hash // Queue of hashes to announce as transaction stubs
done chan struct{} // Non-nil if background network sender routine is active. done chan struct{} // Non-nil if background announcer is running
errch = make(chan error) // Channel used to receive network error fail = make(chan error) // Channel used to receive network error
) )
// pick chooses a reasonble number of transaction hashes for retrieval. for {
pick := func() []common.Hash { // If there's no in-flight announce running, check if a new one is needed
var ret []common.Hash if done == nil && len(queue) > 0 {
if len(requests) > fetcher.MaxTransactionFetch { // Pile transaction hashes until we reach our allowed network limit
ret = requests[:fetcher.MaxTransactionFetch] var (
} else { hashes []common.Hash
ret = requests[:] pending []common.Hash
size common.StorageSize
)
for i := 0; i < len(queue) && size < txsyncPackSize; i++ {
if p.getPooledTx(queue[i]) != nil {
pending = append(pending, queue[i])
size += common.HashLength
} }
requests = requests[:copy(requests, requests[len(ret):])] hashes = append(hashes, queue[i])
return ret
} }
// send sends transactions retrieval request. queue = queue[:copy(queue, queue[len(hashes):])]
send := func(hashes []common.Hash, done chan struct{}) {
if err := p.RequestTxs(hashes); err != nil { // If there's anything available to transfer, fire up an async writer
errch <- err if len(pending) > 0 {
done = make(chan struct{})
go func() {
if err := p.sendPooledTransactionHashes(pending); err != nil {
fail <- err
return return
} }
close(done) close(done)
p.Log().Trace("Sent transaction retrieval request", "count", len(hashes)) p.Log().Trace("Sent transaction announcements", "count", len(pending))
}()
} }
for { }
// Transfer goroutine may or may not have been started, listen for events
select { select {
case hashes := <-p.txRetrieval: case hashes := <-p.txAnnounce:
if len(requests) == maxQueuedTxRetrieval { // New batch of transactions to be broadcast, queue them (with cap)
continue queue = append(queue, hashes...)
} if len(queue) > maxQueuedTxAnns {
if len(requests)+len(hashes) > maxQueuedTxRetrieval { // Fancy copy and resize to ensure buffer doesn't grow indefinitely
hashes = hashes[:maxQueuedTxRetrieval-len(requests)] queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])]
}
requests = append(requests, hashes...)
if done == nil {
done = make(chan struct{})
go send(pick(), done)
} }
case <-done: case <-done:
done = nil done = nil
if pending := pick(); len(pending) > 0 {
done = make(chan struct{})
go send(pending, done)
}
case <- errch: case <-fail:
return return
case <-p.term: case <-p.term:
@ -379,22 +329,22 @@ func (p *peer) MarkTransaction(hash common.Hash) {
p.knownTxs.Add(hash) p.knownTxs.Add(hash)
} }
// SendNewTransactionHashes sends a batch of transaction hashes to the peer and // SendTransactions64 sends transactions to the peer and includes the hashes
// includes the hashes in its transaction hash set for future reference. // in its transaction hash set for future reference.
func (p *peer) SendNewTransactionHashes(hashes []common.Hash) error { //
// Mark all the transactions as known, but ensure we don't overflow our limits // This method is legacy support for initial transaction exchange in eth/64 and
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { // prior. For eth/65 and higher use SendPooledTransactionHashes.
p.knownTxs.Pop() func (p *peer) SendTransactions64(txs types.Transactions) error {
} return p.sendTransactions(txs)
for _, hash := range hashes {
p.knownTxs.Add(hash)
}
return p2p.Send(p.rw, NewPooledTransactionHashesMsg, hashes)
} }
// SendNewTransactions sends transactions to the peer and includes the hashes // sendTransactions sends transactions to the peer and includes the hashes
// in its transaction hash set for future reference. // in its transaction hash set for future reference.
func (p *peer) SendNewTransactions(txs types.Transactions) error { //
// This method is a helper used by the async transaction sender. Don't call it
// directly as the queueing (memory) and transmission (bandwidth) costs should
// not be managed directly.
func (p *peer) sendTransactions(txs types.Transactions) error {
// Mark all the transactions as known, but ensure we don't overflow our limits // Mark all the transactions as known, but ensure we don't overflow our limits
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(txs)) { for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(txs)) {
p.knownTxs.Pop() p.knownTxs.Pop()
@ -402,18 +352,15 @@ func (p *peer) SendNewTransactions(txs types.Transactions) error {
for _, tx := range txs { for _, tx := range txs {
p.knownTxs.Add(tx.Hash()) p.knownTxs.Add(tx.Hash())
} }
return p2p.Send(p.rw, TxMsg, txs) return p2p.Send(p.rw, TransactionMsg, txs)
} }
func (p *peer) SendTransactionRLP(txs []rlp.RawValue) error { // AsyncSendTransactions queues a list of transactions (by hash) to eventually
return p2p.Send(p.rw, TxMsg, txs) // propagate to a remote peer. The number of pending sends are capped (new ones
} // will force old sends to be dropped)
// AsyncSendTransactions queues list of transactions propagation to a remote
// peer. If the peer's broadcast queue is full, the event is silently dropped.
func (p *peer) AsyncSendTransactions(hashes []common.Hash) { func (p *peer) AsyncSendTransactions(hashes []common.Hash) {
select { select {
case p.txPropagation <- hashes: case p.txBroadcast <- hashes:
// Mark all the transactions as known, but ensure we don't overflow our limits // Mark all the transactions as known, but ensure we don't overflow our limits
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) { for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
p.knownTxs.Pop() p.knownTxs.Pop()
@ -426,9 +373,27 @@ func (p *peer) AsyncSendTransactions(hashes []common.Hash) {
} }
} }
// AsyncSendTransactions queues list of transactions propagation to a remote // sendPooledTransactionHashes sends transaction hashes to the peer and includes
// peer. If the peer's broadcast queue is full, the event is silently dropped. // them in its transaction hash set for future reference.
func (p *peer) AsyncSendTransactionHashes(hashes []common.Hash) { //
// This method is a helper used by the async transaction announcer. Don't call it
// directly as the queueing (memory) and transmission (bandwidth) costs should
// not be managed directly.
func (p *peer) sendPooledTransactionHashes(hashes []common.Hash) error {
// Mark all the transactions as known, but ensure we don't overflow our limits
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
p.knownTxs.Pop()
}
for _, hash := range hashes {
p.knownTxs.Add(hash)
}
return p2p.Send(p.rw, NewPooledTransactionHashesMsg, hashes)
}
// AsyncSendPooledTransactionHashes queues a list of transactions hashes to eventually
// announce to a remote peer. The number of pending sends are capped (new ones
// will force old sends to be dropped)
func (p *peer) AsyncSendPooledTransactionHashes(hashes []common.Hash) {
select { select {
case p.txAnnounce <- hashes: case p.txAnnounce <- hashes:
// Mark all the transactions as known, but ensure we don't overflow our limits // Mark all the transactions as known, but ensure we don't overflow our limits
@ -443,6 +408,22 @@ func (p *peer) AsyncSendTransactionHashes(hashes []common.Hash) {
} }
} }
// SendPooledTransactionsRLP sends requested transactions to the peer and adds the
// hashes in its transaction hash set for future reference.
//
// Note, the method assumes the hashes are correct and correspond to the list of
// transactions being sent.
func (p *peer) SendPooledTransactionsRLP(hashes []common.Hash, txs []rlp.RawValue) error {
// Mark all the transactions as known, but ensure we don't overflow our limits
for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
p.knownTxs.Pop()
}
for _, hash := range hashes {
p.knownTxs.Add(hash)
}
return p2p.Send(p.rw, PooledTransactionsMsg, txs)
}
// SendNewBlockHashes announces the availability of a number of blocks through // SendNewBlockHashes announces the availability of a number of blocks through
// a hash notification. // a hash notification.
func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error { func (p *peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error {
@ -577,16 +558,6 @@ func (p *peer) RequestTxs(hashes []common.Hash) error {
return p2p.Send(p.rw, GetPooledTransactionsMsg, hashes) return p2p.Send(p.rw, GetPooledTransactionsMsg, hashes)
} }
// AsyncRequestTxs queues a tx retrieval request to a remote peer. If
// the peer's retrieval queue is full, the event is silently dropped.
func (p *peer) AsyncRequestTxs(hashes []common.Hash) {
select {
case p.txRetrieval <- hashes:
case <-p.term:
p.Log().Debug("Dropping transaction retrieval request", "count", len(hashes))
}
}
// Handshake executes the eth protocol handshake, negotiating version number, // Handshake executes the eth protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks. // network IDs, difficulties, head and genesis blocks.
func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error { func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error {
@ -746,9 +717,10 @@ func (ps *peerSet) Register(p *peer) error {
return errAlreadyRegistered return errAlreadyRegistered
} }
ps.peers[p.id] = p ps.peers[p.id] = p
go p.broadcastBlocks() go p.broadcastBlocks()
go p.broadcastTxs() go p.broadcastTransactions()
go p.retrievalTxs() go p.announceTransactions()
return nil return nil
} }

View File

@ -51,7 +51,7 @@ const protocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a prot
const ( const (
StatusMsg = 0x00 StatusMsg = 0x00
NewBlockHashesMsg = 0x01 NewBlockHashesMsg = 0x01
TxMsg = 0x02 TransactionMsg = 0x02
GetBlockHeadersMsg = 0x03 GetBlockHeadersMsg = 0x03
BlockHeadersMsg = 0x04 BlockHeadersMsg = 0x04
GetBlockBodiesMsg = 0x05 GetBlockBodiesMsg = 0x05
@ -64,10 +64,11 @@ const (
// New protocol message codes introduced in eth65 // New protocol message codes introduced in eth65
// //
// Previously these message ids(0x08, 0x09) were used by some // Previously these message ids were used by some legacy and unsupported
// legacy and unsupported eth protocols, reown them here. // eth protocols, reown them here.
NewPooledTransactionHashesMsg = 0x08 NewPooledTransactionHashesMsg = 0x08
GetPooledTransactionsMsg = 0x09 GetPooledTransactionsMsg = 0x09
PooledTransactionsMsg = 0x0a
) )
type errCode int type errCode int

View File

@ -62,7 +62,7 @@ func TestStatusMsgErrors63(t *testing.T) {
wantError error wantError error
}{ }{
{ {
code: TxMsg, data: []interface{}{}, code: TransactionMsg, data: []interface{}{},
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
}, },
{ {
@ -114,7 +114,7 @@ func TestStatusMsgErrors64(t *testing.T) {
wantError error wantError error
}{ }{
{ {
code: TxMsg, data: []interface{}{}, code: TransactionMsg, data: []interface{}{},
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"), wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
}, },
{ {
@ -258,7 +258,7 @@ func testRecvTransactions(t *testing.T, protocol int) {
defer p.close() defer p.close()
tx := newTestTransaction(testAccount, 0, 0) tx := newTestTransaction(testAccount, 0, 0)
if err := p2p.Send(p.app, TxMsg, []interface{}{tx}); err != nil { if err := p2p.Send(p.app, TransactionMsg, []interface{}{tx}); err != nil {
t.Fatalf("send error: %v", err) t.Fatalf("send error: %v", err)
} }
select { select {
@ -282,13 +282,16 @@ func testSendTransactions(t *testing.T, protocol int) {
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
defer pm.Stop() defer pm.Stop()
// Fill the pool with big transactions. // Fill the pool with big transactions (use a subscription to wait until all
// the transactions are announced to avoid spurious events causing extra
// broadcasts).
const txsize = txsyncPackSize / 10 const txsize = txsyncPackSize / 10
alltxs := make([]*types.Transaction, 100) alltxs := make([]*types.Transaction, 100)
for nonce := range alltxs { for nonce := range alltxs {
alltxs[nonce] = newTestTransaction(testAccount, uint64(nonce), txsize) alltxs[nonce] = newTestTransaction(testAccount, uint64(nonce), txsize)
} }
pm.txpool.AddRemotes(alltxs) pm.txpool.AddRemotes(alltxs)
time.Sleep(100 * time.Millisecond) // Wait until new tx even gets out of the system (lame)
// Connect several peers. They should all receive the pending transactions. // Connect several peers. They should all receive the pending transactions.
var wg sync.WaitGroup var wg sync.WaitGroup
@ -300,8 +303,6 @@ func testSendTransactions(t *testing.T, protocol int) {
seen[tx.Hash()] = false seen[tx.Hash()] = false
} }
for n := 0; n < len(alltxs) && !t.Failed(); { for n := 0; n < len(alltxs) && !t.Failed(); {
var txs []*types.Transaction
var hashes []common.Hash
var forAllHashes func(callback func(hash common.Hash)) var forAllHashes func(callback func(hash common.Hash))
switch protocol { switch protocol {
case 63: case 63:
@ -310,11 +311,15 @@ func testSendTransactions(t *testing.T, protocol int) {
msg, err := p.app.ReadMsg() msg, err := p.app.ReadMsg()
if err != nil { if err != nil {
t.Errorf("%v: read error: %v", p.Peer, err) t.Errorf("%v: read error: %v", p.Peer, err)
} else if msg.Code != TxMsg { continue
} else if msg.Code != TransactionMsg {
t.Errorf("%v: got code %d, want TxMsg", p.Peer, msg.Code) t.Errorf("%v: got code %d, want TxMsg", p.Peer, msg.Code)
continue
} }
var txs []*types.Transaction
if err := msg.Decode(&txs); err != nil { if err := msg.Decode(&txs); err != nil {
t.Errorf("%v: %v", p.Peer, err) t.Errorf("%v: %v", p.Peer, err)
continue
} }
forAllHashes = func(callback func(hash common.Hash)) { forAllHashes = func(callback func(hash common.Hash)) {
for _, tx := range txs { for _, tx := range txs {
@ -325,11 +330,15 @@ func testSendTransactions(t *testing.T, protocol int) {
msg, err := p.app.ReadMsg() msg, err := p.app.ReadMsg()
if err != nil { if err != nil {
t.Errorf("%v: read error: %v", p.Peer, err) t.Errorf("%v: read error: %v", p.Peer, err)
continue
} else if msg.Code != NewPooledTransactionHashesMsg { } else if msg.Code != NewPooledTransactionHashesMsg {
t.Errorf("%v: got code %d, want TxMsg", p.Peer, msg.Code) t.Errorf("%v: got code %d, want NewPooledTransactionHashesMsg", p.Peer, msg.Code)
continue
} }
var hashes []common.Hash
if err := msg.Decode(&hashes); err != nil { if err := msg.Decode(&hashes); err != nil {
t.Errorf("%v: %v", p.Peer, err) t.Errorf("%v: %v", p.Peer, err)
continue
} }
forAllHashes = func(callback func(hash common.Hash)) { forAllHashes = func(callback func(hash common.Hash)) {
for _, h := range hashes { for _, h := range hashes {

View File

@ -39,12 +39,17 @@ const (
type txsync struct { type txsync struct {
p *peer p *peer
hashes []common.Hash
txs []*types.Transaction txs []*types.Transaction
} }
// syncTransactions starts sending all currently pending transactions to the given peer. // syncTransactions starts sending all currently pending transactions to the given peer.
func (pm *ProtocolManager) syncTransactions(p *peer) { func (pm *ProtocolManager) syncTransactions(p *peer) {
// Assemble the set of transaction to broadcast or announce to the remote
// peer. Fun fact, this is quite an expensive operation as it needs to sort
// the transactions if the sorting is not cached yet. However, with a random
// order, insertions could overflow the non-executable queues and get dropped.
//
// TODO(karalabe): Figure out if we could get away with random order somehow
var txs types.Transactions var txs types.Transactions
pending, _ := pm.txpool.Pending() pending, _ := pm.txpool.Pending()
for _, batch := range pending { for _, batch := range pending {
@ -53,17 +58,29 @@ func (pm *ProtocolManager) syncTransactions(p *peer) {
if len(txs) == 0 { if len(txs) == 0 {
return return
} }
// The eth/65 protocol introduces proper transaction announcements, so instead
// of dripping transactions across multiple peers, just send the entire list as
// an announcement and let the remote side decide what they need (likely nothing).
if p.version >= eth65 {
hashes := make([]common.Hash, len(txs))
for i, tx := range txs {
hashes[i] = tx.Hash()
}
p.AsyncSendPooledTransactionHashes(hashes)
return
}
// Out of luck, peer is running legacy protocols, drop the txs over
select { select {
case pm.txsyncCh <- &txsync{p: p, txs: txs}: case pm.txsyncCh <- &txsync{p: p, txs: txs}:
case <-pm.quitSync: case <-pm.quitSync:
} }
} }
// txsyncLoop takes care of the initial transaction sync for each new // txsyncLoop64 takes care of the initial transaction sync for each new
// connection. When a new peer appears, we relay all currently pending // connection. When a new peer appears, we relay all currently pending
// transactions. In order to minimise egress bandwidth usage, we send // transactions. In order to minimise egress bandwidth usage, we send
// the transactions in small packs to one peer at a time. // the transactions in small packs to one peer at a time.
func (pm *ProtocolManager) txsyncLoop() { func (pm *ProtocolManager) txsyncLoop64() {
var ( var (
pending = make(map[enode.ID]*txsync) pending = make(map[enode.ID]*txsync)
sending = false // whether a send is active sending = false // whether a send is active
@ -72,30 +89,13 @@ func (pm *ProtocolManager) txsyncLoop() {
) )
// send starts a sending a pack of transactions from the sync. // send starts a sending a pack of transactions from the sync.
send := func(s *txsync) { send := func(s *txsync) {
if s.p.version >= eth65 {
panic("initial transaction syncer running on eth/65+")
}
// Fill pack with transactions up to the target size. // Fill pack with transactions up to the target size.
size := common.StorageSize(0) size := common.StorageSize(0)
pack.p = s.p pack.p = s.p
pack.hashes = pack.hashes[:0]
pack.txs = pack.txs[:0] pack.txs = pack.txs[:0]
if s.p.version >= eth65 {
// Eth65 introduces transaction announcement https://github.com/ethereum/EIPs/pull/2464,
// only txhashes are transferred here.
for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ {
pack.hashes = append(pack.hashes, s.txs[i].Hash())
size += common.HashLength
}
// Remove the transactions that will be sent.
s.txs = s.txs[:copy(s.txs, s.txs[len(pack.hashes):])]
if len(s.txs) == 0 {
delete(pending, s.p.ID())
}
// Send the pack in the background.
s.p.Log().Trace("Sending batch of transaction announcements", "count", len(pack.hashes), "bytes", size)
sending = true
go func() { done <- pack.p.SendNewTransactionHashes(pack.hashes) }()
} else {
// Legacy eth protocol doesn't have transaction announcement protocol
// message, transfer the whole pending transaction slice.
for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ { for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ {
pack.txs = append(pack.txs, s.txs[i]) pack.txs = append(pack.txs, s.txs[i])
size += s.txs[i].Size() size += s.txs[i].Size()
@ -108,8 +108,7 @@ func (pm *ProtocolManager) txsyncLoop() {
// Send the pack in the background. // Send the pack in the background.
s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size) s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size)
sending = true sending = true
go func() { done <- pack.p.SendNewTransactions(pack.txs) }() go func() { done <- pack.p.SendTransactions64(pack.txs) }()
}
} }
// pick chooses the next pending sync. // pick chooses the next pending sync.

View File

@ -26,6 +26,14 @@ targets:
function: Fuzz function: Fuzz
package: github.com/ethereum/go-ethereum/tests/fuzzers/trie package: github.com/ethereum/go-ethereum/tests/fuzzers/trie
checkout: github.com/ethereum/go-ethereum/ checkout: github.com/ethereum/go-ethereum/
- name: txfetcher
language: go
version: "1.13"
corpus: ./fuzzers/txfetcher/corpus
harness:
function: Fuzz
package: github.com/ethereum/go-ethereum/tests/fuzzers/txfetcher
checkout: github.com/ethereum/go-ethereum/
- name: whisperv6 - name: whisperv6
language: go language: go
version: "1.13" version: "1.13"

View File

@ -0,0 +1,12 @@
TESTING KEY-----
MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9
SjY1bIw4iAJm2gsvvZhIrCHS3l6afab4pZB
l2+XsDlrKBxKKtDrGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTtqJQIDAQAB
AoGAGRzwwir7XvBOAy5tuV6ef6anZzus1s1Y1Clb6HbnWWF/wbZGOpet
3m4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKZTXtdZrh+k7hx0nTP8Jcb
uqFk541awmMogY/EfbWd6IOkp+4xqjlFBEDytgbIECQQDvH/6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz84SHEg1Ak/7KCxmD/sfgS5TeuNi8DoUBEmiSJwm7FX
ftxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su43sjXNueLKH8+ph2UfQuU9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xl/DoCzjA0CQQDU
y2pGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtI痂
qUn3Xh9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JMhNRcVFMO8dDaFo
f9Oeos0UotgiDktdQHxdNEwLjQlJBz+OtwwA=---E RATTIEY-

View File

@ -0,0 +1,15 @@
¸&^£áo‡È—-----BEGIN RSA TESTING KEY-----
MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9
SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZB
l2+XsDulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB
AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU
y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtIX
qyUBnu3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo
f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA==
-----END RSA TESTING KEY-----Q_

View File

@ -0,0 +1 @@
π½apοΏοοοΏ½οΏ½οΏοΏΏ½½½ΏΏ½½οΏ½οΏ½Ώ½οΏοΏ½οΏοΣΜV½Ώ½οοοΏοΏ½#οΏοΏ½&οΏ½οΏ½

View File

@ -0,0 +1,11 @@
TAKBgDuLnQA3gey3VBznB39JUtxjeE6myuDkM/uGlfjb
S1w4iA5sBzzh8uxEbi4nW91IJm2gsvvZhICHS3l6ab4pZB
l2DulrKBxKKtD1rGxlG4LncabFn9vLZad2bSysqz/qTAUSTvqJQIDAQAB
AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
3Z4vMXc7jpTLryzTQIvVdfQbRc6+MUVeLKZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk54MogxEcfbWd6IOkp+4xqFLBEDtgbIECnk+hgN4H
qzzxxr397vWrjrIgbJpQvBv8QeeuNi8DoUBEmiSJwa7FXY
FUtxuvL7XvjwjN5B30pEbc6Iuyt7y4MQJBAIt21su4b3sjphy2tuUE9xblTu14qgHZ6+AiZovGKU--FfYAqVXVlxtIX
qyU3X9ps8ZfjLZ45l6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo
f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA==
-----END RSA T

View File

@ -0,0 +1 @@
0000000000000000000000000000000000000000000000000000000000000000000000000

View File

@ -0,0 +1 @@
<EFBFBD>&

View File

@ -0,0 +1,3 @@
DtQvfQ+MULKZTXk78c
/fWkpxlQQ/+hgNzVtx9vWgJsafG7b0dA4AFjwVbFLmQcj2PprIMmPNQrooX
L

View File

@ -0,0 +1,12 @@
4txjeVE6myuDqkM/uGlfjb9
SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZeIrCHS3l6afab4pZB
l2+XsDlrKBxKKtD1rGxlG4jncdabFn9gvLZad2bSysqz/qTAUSTvqJQIDAQAB
AoGAGRzwwXvBOAy5tM/uV6e+Zf6aZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
3Z4vD6Mc7pLryzTQIVdfQbRc6+MUVeLKZaTXtdZru+Jk70PJJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+gN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQ2PprIMPcQroo8vpjSHg1Ev14KxmQeDydfsgeuN8UBESJwm7F
UtuL7Xvjw50pNEbc6Iuyty4QJA21su4sjXNueLQphy2U
fQtuUE9txblTu14qN7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU
y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6ARYiZPYj1oGUFfYAVVxtI
qyBnu3X9pfLZOAkEAlT4R5Yl6cJQYZHOde3JEhNRcVFMO8dJFo
f9Oeos0UUhgiDkQxdEwLjQf7lJJz5OtwC=
-NRSA TESINGKEY-Q_

View File

@ -0,0 +1,10 @@
jXbnWWF/wbZGOpet
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
jy4SHEg1AkEA/v13/5M47K9vCxb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xl/DoCzjA0CQQDU
y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6Yj013sovGKUFfYAqVXVlxtIX
qyUBnu3Xh9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dDaFeo
f9Oeos0UotgiDktdQHxdNEwLjQfl

View File

@ -0,0 +1,15 @@
¸^áo‡È—----BEGIN RA TTING KEY-----
IIXgIBAAKBQDuLnQI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9
SjY1bIw4iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJmgsvvZhrCHSl6afab4pZB
l2+XsDulrKBxKKtD1rGxlG4LjcdabF9gvLZad2bSysqz/qTAUStTvqJQDAQAB
AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
3Z4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU
y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj043sovGKUFfYAqVXVlxtIX
qyUBnu3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFeo
f9Oeos0UUothgiDktdQHxdNEwLjQf7lJJBzV+5OtwswCWA==
-----END RSA TESTING KEY-----Q_

View File

@ -0,0 +1 @@
<EFBFBD>

View File

@ -0,0 +1,7 @@
lGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
fFUtxuvL7XvjwjN5

View File

@ -0,0 +1,2 @@
カネ哿ソス<03>スツ€<01><01><01><01><01><01><01><01><01><01>
<01> <01> <01><01><01><01><01><01><01><01><01><01><01><01><01><01><01><01><01><01><01><01><01>ス!<01>ス"<01>ス#<01>ス$<01>ス%<01>ス&<01>ス'<01>ス(<01>ス)<01>ス*<01>ス+<01>ス,<01>ス-<01>ス.<01>ス/ソス0

View File

@ -0,0 +1 @@
LvhaJQHOe3EhRcdaFofeoogkjQfJB

View File

@ -0,0 +1 @@
И&^Ѓсo<D181>

View File

@ -0,0 +1 @@
đ˝apfffffffffffffffffffffffffffffffebadce6f48a0ź_3bbfd2364

View File

@ -0,0 +1,3 @@
DtQvfQ+MULKZTXk78c
/fWkpxlyEQQ/+hgNzVtx9vWgJsafG7b0dA4AFjwVbFLmQcj2PprIMmPNQg1Ak/7KCxmDgS5TDEmSJwFX
txLjbt4xTgeXVlXsjLZ

View File

@ -0,0 +1 @@
ð½ï½ï¿½Ù¯0,1,2,3,4,5,6,7,-3420794409,(2,a)

View File

@ -0,0 +1 @@
88242871'392752200424491531672177074144720616417147514758635765020556616ソ

View File

@ -0,0 +1 @@
21888242871'392752200424452601091531672177074144720616417147514758635765020556616ソス

View File

@ -0,0 +1 @@
LvhaJcdaFofenogkjQfJB

View File

@ -0,0 +1,2 @@
DtQvfQ+MULKZTXk78c
/fWkpxlyEQQ/+hgNzVtx9vWgJsafG7b0dA4AFjwVbFLmQcj2PprIMmPNQg1AkS5TDEmSJwFVlXsjLZ

View File

@ -0,0 +1,14 @@
TESTING KEY-----
MIICXgIBAAKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9
SjY1bIw4iAJm2gsvvZhIrCHS3l6afab4pZB
l2+XsDulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB
AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xl/DoCzjA0CQQDU
y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj013sovGKUFfYAqVXVlxtIX
qyUBnu3Xh9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JEMhNRcVFMO8dDaFeo
f9Oeos0UotgiDktdQHxdNEwLjQflJJBzV+5OtwswCA=----EN RATESTI EY-----Q

View File

@ -0,0 +1,10 @@
l6afab4pZB
l2+XsDlrKBxKKtDrGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTtqJQIDAQAB
AoGAGRzwwir7XvBOAy5tuV6ef6anZzus1s1Y1Clb6HbnWWF/wbZGOpet
3m4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKZTXtdZrh+k7hx0nTP8Jcb
uqFk541awmMogY/EfbWd6IOkp+4xqjlFBEDytgbIECQQDvH/6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz84SHEg1Ak/7KCxmD/sfgS5TeuNi8DoUBEmiSJwm7FX
ftxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su43sjXNueLKH8+ph2UfQuU9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xl/DoCzjA0CQQDU
y2pGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6PYj13sovGKUFfYAqVXVlxtI痂
qUn3X9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYZHOde3JMhNRcVFMO8dDaFo
f9Oeos0UotgiDktdQHxdNEwLjQlJBz+OtwwA=---E ATTIEY-

View File

@ -0,0 +1,9 @@
l2+DulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB
AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xlp/DoCzjA0CQQDU

View File

@ -0,0 +1 @@
KKtDlbjVeLKwZatTXtdZrhu+Jk7hx0xxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLQcmPcQETT YQ

View File

@ -0,0 +1 @@
<EFBFBD>

View File

@ -0,0 +1 @@
<EFBFBD>39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319<EFBFBD><EFBFBD>

View File

@ -0,0 +1,5 @@
l2+DulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB
AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpwVbFLmQet
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
qzzVtxxr397vWrjr

View File

@ -0,0 +1,2 @@
&<26><>w<EFBFBD><77><03>€<01><01><01><01><01><01><01><01><01> <01>
<01> <01> <01> <01><01><01><01><><01><01><01><><7F><EFBFBD><01><01><01><01><01><01><01><01><01><01><01> <01>!<01>"<01>#<01>$<01>%<01>&<01>'<01>(<01>)<01>*<01>+<01>,<01>-<01>.<01>/<01><>0

View File

@ -0,0 +1,2 @@
lxtIX
qyU3X9ps8ZfjLZ45l6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFe

View File

@ -0,0 +1 @@
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000@0000000000000

View File

@ -0,0 +1,8 @@
9pmM gY/xEcfbWd6IOkp+4xqjlFLBEDytgbparsing /E6nk+hgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprLANGcQrooz8vp
jy4SHEg1AkEA/v13/@M47K9vCxb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
fFUtxuvL7XvjwjN5B30pNEbc6Iuyt7y4MQJBAIt21su4b3sjXNueLKH85Q+phy2U
fQtuUE9txblTu14q3N7gHRZB4ZMhFYyDy8CKrN2cPg/Fvyt0Xl/DoCz<43> jA0CQQDU
y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6Yj013sovGKUFfYAqVXVlxtIX
qyUBnu3Xh9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYFZHOde3JEMhNRcVFMO8dDaFeo
f9Oeos0Uot

View File

@ -0,0 +1 @@
&<26>o<EFBFBD>

View File

@ -0,0 +1 @@
<EFBFBD>

View File

@ -0,0 +1,2 @@
4LZmbRc6+MUVeLKXtdZr+Jk7hhgN4H
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLQcmPcQ SN_

View File

@ -0,0 +1,4 @@
Xc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nhgN4H
qzzVtxx7vWrjrIgPbJpvfb

View File

@ -0,0 +1,2 @@
<01><01> <01>
<01> <01> <01> <01><01><01><01><><01><01><01><><7F><EFBFBD><01><01><01><01><01><01><01><01><01><01><01> <01>!<01>"<01>#<01>$<01>%<01>&<01>'<01>(<01>)<01>*<01>+<01>,<01>-<01>.<01>/<01><>0

Some files were not shown because too many files have changed in this diff Show More