eth: rework tx fetcher to use O(1) ops + manage network requests
This commit is contained in:
parent
049e17116e
commit
9938d954c8
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
@ -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, unknown, time.Now(), p.AsyncRequestTxs)
|
pm.txFetcher.Notify(p.id, hashes)
|
||||||
|
|
||||||
case msg.Code == GetPooledTransactionsMsg && p.version >= eth65:
|
case msg.Code == GetPooledTransactionsMsg && p.version >= eth65:
|
||||||
// Decode the retrieval message
|
// Decode the retrieval message
|
||||||
|
@ -763,9 +760,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
// Gather transactions until the fetch or network limits is reached
|
// Gather transactions until the fetch or network limits is reached
|
||||||
var (
|
var (
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
bytes int
|
bytes int
|
||||||
txs []rlp.RawValue
|
hashes []common.Hash
|
||||||
|
txs []rlp.RawValue
|
||||||
)
|
)
|
||||||
for bytes < softResponseLimit {
|
for bytes < softResponseLimit {
|
||||||
// Retrieve the hash of the next block
|
// Retrieve the hash of the next block
|
||||||
|
@ -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():
|
||||||
|
|
139
eth/metrics.go
139
eth/metrics.go
|
@ -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)
|
|
||||||
}
|
|
318
eth/peer.go
318
eth/peer.go
|
@ -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,15 +97,16 @@ 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
|
||||||
txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests
|
txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests
|
||||||
txRetrieval chan []common.Hash // Channel used to queue transaction retrieval requests
|
txAnnounce chan []common.Hash // Channel used to queue transaction announcement 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter, getPooledTx func(hash common.Hash) *types.Transaction) *peer {
|
func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter, getPooledTx func(hash common.Hash) *types.Transaction) *peer {
|
||||||
|
@ -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.
|
|
||||||
pick := func() []common.Hash {
|
|
||||||
var ret []common.Hash
|
|
||||||
if len(requests) > fetcher.MaxTransactionFetch {
|
|
||||||
ret = requests[:fetcher.MaxTransactionFetch]
|
|
||||||
} else {
|
|
||||||
ret = requests[:]
|
|
||||||
}
|
|
||||||
requests = requests[:copy(requests, requests[len(ret):])]
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
// send sends transactions retrieval request.
|
|
||||||
send := func(hashes []common.Hash, done chan struct{}) {
|
|
||||||
if err := p.RequestTxs(hashes); err != nil {
|
|
||||||
errch <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(done)
|
|
||||||
p.Log().Trace("Sent transaction retrieval request", "count", len(hashes))
|
|
||||||
}
|
|
||||||
for {
|
for {
|
||||||
select {
|
// If there's no in-flight announce running, check if a new one is needed
|
||||||
case hashes := <-p.txRetrieval:
|
if done == nil && len(queue) > 0 {
|
||||||
if len(requests) == maxQueuedTxRetrieval {
|
// Pile transaction hashes until we reach our allowed network limit
|
||||||
continue
|
var (
|
||||||
|
hashes []common.Hash
|
||||||
|
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
|
||||||
|
}
|
||||||
|
hashes = append(hashes, queue[i])
|
||||||
}
|
}
|
||||||
if len(requests)+len(hashes) > maxQueuedTxRetrieval {
|
queue = queue[:copy(queue, queue[len(hashes):])]
|
||||||
hashes = hashes[:maxQueuedTxRetrieval-len(requests)]
|
|
||||||
}
|
// If there's anything available to transfer, fire up an async writer
|
||||||
requests = append(requests, hashes...)
|
if len(pending) > 0 {
|
||||||
if done == nil {
|
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
go send(pick(), done)
|
go func() {
|
||||||
|
if err := p.sendPooledTransactionHashes(pending); err != nil {
|
||||||
|
fail <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
close(done)
|
||||||
|
p.Log().Trace("Sent transaction announcements", "count", len(pending))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Transfer goroutine may or may not have been started, listen for events
|
||||||
|
select {
|
||||||
|
case hashes := <-p.txAnnounce:
|
||||||
|
// New batch of transactions to be broadcast, queue them (with cap)
|
||||||
|
queue = append(queue, hashes...)
|
||||||
|
if len(queue) > maxQueuedTxAnns {
|
||||||
|
// Fancy copy and resize to ensure buffer doesn't grow indefinitely
|
||||||
|
queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])]
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
75
eth/sync.go
75
eth/sync.go
|
@ -38,13 +38,18 @@ 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,44 +89,26 @@ 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 {
|
for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ {
|
||||||
// Eth65 introduces transaction announcement https://github.com/ethereum/EIPs/pull/2464,
|
pack.txs = append(pack.txs, s.txs[i])
|
||||||
// only txhashes are transferred here.
|
size += s.txs[i].Size()
|
||||||
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++ {
|
|
||||||
pack.txs = append(pack.txs, s.txs[i])
|
|
||||||
size += s.txs[i].Size()
|
|
||||||
}
|
|
||||||
// Remove the transactions that will be sent.
|
|
||||||
s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])]
|
|
||||||
if len(s.txs) == 0 {
|
|
||||||
delete(pending, s.p.ID())
|
|
||||||
}
|
|
||||||
// Send the pack in the background.
|
|
||||||
s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size)
|
|
||||||
sending = true
|
|
||||||
go func() { done <- pack.p.SendNewTransactions(pack.txs) }()
|
|
||||||
}
|
}
|
||||||
|
// Remove the transactions that will be sent.
|
||||||
|
s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])]
|
||||||
|
if len(s.txs) == 0 {
|
||||||
|
delete(pending, s.p.ID())
|
||||||
|
}
|
||||||
|
// Send the pack in the background.
|
||||||
|
s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size)
|
||||||
|
sending = true
|
||||||
|
go func() { done <- pack.p.SendTransactions64(pack.txs) }()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pick chooses the next pending sync.
|
// pick chooses the next pending sync.
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ソス
|
Binary file not shown.
|
@ -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-
|
|
@ -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_
|
|
@ -0,0 +1 @@
|
||||||
|
π½apοΏοοοΏ½οΏ½οΏοΏΏ½½½ΏΏ½½οΏ½οΏ½Ώ½οΏοΏ½οΏοΣΜV½Ώ½οοοΏοΏ½#οΏοΏ½&οΏ½οΏ½
|
Binary file not shown.
Binary file not shown.
|
@ -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
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000000000000
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
<EFBFBD>&
|
|
@ -0,0 +1,3 @@
|
||||||
|
DtQvfQ+MULKZTXk78c
|
||||||
|
/fWkpxlQQ/+hgNzVtx9vWgJsafG7b0dA4AFjwVbFLmQcj2PprIMmPNQrooX
|
||||||
|
L
|
Binary file not shown.
|
@ -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_
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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
|
|
@ -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_
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
<EFBFBD>
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
lGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpet
|
||||||
|
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
|
||||||
|
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
|
||||||
|
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLmQcj2PprIMmPcQrooz8vp
|
||||||
|
jy4SHEg1AkEA/v13/5M47K9vCxmb8QeD/asydfsgS5TeuNi8DoUBEmiSJwma7FXY
|
||||||
|
fFUtxuvL7XvjwjN5
|
Binary file not shown.
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
LvhaJQHOe3EhRcdaFofeoogkjQfJB
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
И&^Ѓсo<D181>
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
đ˝apfffffffffffffffffffffffffffffffebadce6f48a0ź_3bbfd2364
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
DtQvfQ+MULKZTXk78c
|
||||||
|
/fWkpxlyEQQ/+hgNzVtx9vWgJsafG7b0dA4AFjwVbFLmQcj2PprIMmPNQg1Ak/7KCxmDgS5TDEmSJwFX
|
||||||
|
txLjbt4xTgeXVlXsjLZ
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
ð½ï½ï¿½Ù¯0,1,2,3,4,5,6,7,-3420794409,(2,a)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
88242871'392752200424491531672177074144720616417147514758635765020556616ソ
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
21888242871'392752200424452601091531672177074144720616417147514758635765020556616ソス
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
ソス
|
|
@ -0,0 +1 @@
|
||||||
|
LvhaJcdaFofenogkjQfJB
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
DtQvfQ+MULKZTXk78c
|
||||||
|
/fWkpxlyEQQ/+hgNzVtx9vWgJsafG7b0dA4AFjwVbFLmQcj2PprIMmPNQg1AkS5TDEmSJwFVlXsjLZ
|
Binary file not shown.
|
@ -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
|
|
@ -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-
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
KKtDlbjVeLKwZatTXtdZrhu+Jk7hx0xxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLQcmPcQETT YQ
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
<EFBFBD>
|
|
@ -0,0 +1 @@
|
||||||
|
<EFBFBD>39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319<EFBFBD><EFBFBD>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
l2+DulrKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQAB
|
||||||
|
AoGAGRzwwir7XvBOAy5tM/uV6e+Zf6anZzus1s1Y1ClbjbE6HXbnWWF/wbZGOpwVbFLmQet
|
||||||
|
3Zm4vD6MXc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
|
||||||
|
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nk+hgN4H
|
||||||
|
qzzVtxxr397vWrjr
|
|
@ -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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
lxtIX
|
||||||
|
qyU3X9ps8ZfjLZ45l6cGhaJQYZHOde3JEMhNRcVFMO8dJDaFe
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000@0000000000000
|
|
@ -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
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
&<26>o<EFBFBD>
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<EFBFBD>
|
|
@ -0,0 +1,2 @@
|
||||||
|
4LZmbRc6+MUVeLKXtdZr+Jk7hhgN4H
|
||||||
|
qzzVtxxr397vWrjrIgPbJpQvBsafG7b0dA4AFjwVbFLQcmPcQ SN_
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
Xc7jpTLryzTQIvVdfQbRc6+MUVeLKwZatTXtdZrhu+Jk7hx0nTPy8Jcb
|
||||||
|
uJqFk541aEw+mMogY/xEcfbWd6IOkp+4xqjlFLBEDytgbIECQQDvH/E6nhgN4H
|
||||||
|
qzzVtxx7vWrjrIgPbJpvfb
|
|
@ -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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue