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

pull/20234/head
Péter Szilágyi 5 years ago
parent 049e17116e
commit 9938d954c8
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
  1. 9
      core/tx_pool.go
  2. 21
      eth/fetcher/block_fetcher.go
  3. 54
      eth/fetcher/metrics.go
  4. 931
      eth/fetcher/tx_fetcher.go
  5. 1672
      eth/fetcher/tx_fetcher_test.go
  6. 59
      eth/handler.go
  7. 139
      eth/metrics.go
  8. 316
      eth/peer.go
  9. 7
      eth/protocol.go
  10. 25
      eth/protocol_test.go
  11. 75
      eth/sync.go
  12. 8
      fuzzbuzz.yaml
  13. 1
      tests/fuzzers/txfetcher/corpus/0151ee1d0db4c74d3bcdfa4f7396a4c8538748c9-2
  14. BIN
      tests/fuzzers/txfetcher/corpus/020dd7b492a6eb34ff0b7d8ee46189422c37e4a7-6
  15. 12
      tests/fuzzers/txfetcher/corpus/021d1144e359233c496e22c3250609b11b213e9f-4
  16. 15
      tests/fuzzers/txfetcher/corpus/0d28327b1fb52c1ba02a6eb96675c31633921bb2-2
  17. 1
      tests/fuzzers/txfetcher/corpus/0fcd827b57ded58e91f7ba2ac2b7ea4d25ebedca-7
  18. BIN
      tests/fuzzers/txfetcher/corpus/109bc9b8fd4fef63493e104c703c79bc4a5e8d34-6
  19. BIN
      tests/fuzzers/txfetcher/corpus/163785ab002746452619f31e8dfcb4549e6f8b6e-6
  20. 11
      tests/fuzzers/txfetcher/corpus/1adfa6b9ddf5766220c8ff7ede2926ca241bb947-3
  21. BIN
      tests/fuzzers/txfetcher/corpus/1b9a02e9a48fea1d2fc3fb77946ada278e152079-4
  22. 1
      tests/fuzzers/txfetcher/corpus/1e14c7ea1faef92890988061b5abe96db7190f98-7
  23. BIN
      tests/fuzzers/txfetcher/corpus/1e7d05f00e99cbf3ff0ef1cd7ea8dd07ad6dff23-6
  24. BIN
      tests/fuzzers/txfetcher/corpus/1ec95e347fd522e6385b5091aa81aa2485be4891-4
  25. BIN
      tests/fuzzers/txfetcher/corpus/1fbfa5d214060d2a0905846a589fd6f78d411451-4
  26. 1
      tests/fuzzers/txfetcher/corpus/1fd84ee194e791783a7f18f0a6deab8efe05fc04-2
  27. 3
      tests/fuzzers/txfetcher/corpus/21e76b9fca21d94d97f860c1c82f40697a83471b-8
  28. BIN
      tests/fuzzers/txfetcher/corpus/220a87fed0c92474923054094eb7aff14289cf5e-4
  29. 12
      tests/fuzzers/txfetcher/corpus/23ddcd66aa92fe3d78b7f5b6e7cddb1b55c5f5df-3
  30. BIN
      tests/fuzzers/txfetcher/corpus/2441d249faf9a859e38c49f6e305b394280c6ea5-1
  31. BIN
      tests/fuzzers/txfetcher/corpus/2da1f0635e11283b1927974f418aadd8837ad31e-7
  32. BIN
      tests/fuzzers/txfetcher/corpus/2e1853fbf8efe40098b1583224fe3b5f335e7037-6
  33. BIN
      tests/fuzzers/txfetcher/corpus/2f25490dc49c103d653843ed47324b310ee7105e-7
  34. 10
      tests/fuzzers/txfetcher/corpus/30494b85bb60ad7f099fa49d427007a761620d8f-5
  35. 15
      tests/fuzzers/txfetcher/corpus/316024ca3aaf09c1de5258733ff5fe3d799648d3-4
  36. BIN
      tests/fuzzers/txfetcher/corpus/32a089e2c439a91f4c1b67a13d52429bcded0dd9-7
  37. 1
      tests/fuzzers/txfetcher/corpus/33ec1dc0bfeb93d16edee3c07125fec6ac1aa17d-2
  38. BIN
      tests/fuzzers/txfetcher/corpus/37a0d207700b52caa005ec8aeb344dcb13150ed2-5
  39. BIN
      tests/fuzzers/txfetcher/corpus/382f59c66d0ddb6747d3177263279789ca15c2db-5
  40. 7
      tests/fuzzers/txfetcher/corpus/3a010483a4ad8d7215447ce27e0fac3791235c99-4
  41. BIN
      tests/fuzzers/txfetcher/corpus/3a3b717fcfe7ffb000b906e5a76f32248a576bf7-6
  42. 2
      tests/fuzzers/txfetcher/corpus/3c37f6d58b8029971935f127f53e6aaeba558445-6
  43. 1
      tests/fuzzers/txfetcher/corpus/3c73b63bafa9f535c882ec17189adaf02b58f432-6
  44. BIN
      tests/fuzzers/txfetcher/corpus/3d11500c4f66b20c73bbdfb1a7bddd7bbf92b29c-5
  45. BIN
      tests/fuzzers/txfetcher/corpus/3d8b5bf36c80d6f65802280039f85421f32b5055-6
  46. 1
      tests/fuzzers/txfetcher/corpus/3f99c546a3962256176d566c19e3fffb62072078-1
  47. BIN
      tests/fuzzers/txfetcher/corpus/408ec46539af27acd82b3d01e863597030882458-8
  48. 1
      tests/fuzzers/txfetcher/corpus/436154e5bb6487673f6642e6d2a582c01b083c08-8
  49. BIN
      tests/fuzzers/txfetcher/corpus/45f565cd14b8de1ba2e925047ce776c2682b4b8d-3
  50. 3
      tests/fuzzers/txfetcher/corpus/4a0a12f5b033c8c160cc3b5133692ea1e92c6cdf-7
  51. BIN
      tests/fuzzers/txfetcher/corpus/550f15ef65230cc4dcfab7fea67de212d9212ff8-8
  52. BIN
      tests/fuzzers/txfetcher/corpus/5552213d659fef900a194c52718ffeffdc72d043-3
  53. 1
      tests/fuzzers/txfetcher/corpus/5570ef82893a9b9b9158572d43a7de7537121d2d-1
  54. BIN
      tests/fuzzers/txfetcher/corpus/5e10f734f8af4116fbd164d96eec67aa53e6228c-5
  55. BIN
      tests/fuzzers/txfetcher/corpus/608200b402488b3989ec8ec5f4190ccb537b8ea4-4
  56. 1
      tests/fuzzers/txfetcher/corpus/61e89c3fbdf9eff74bd250ea73cc2e61f8ca0d97-5
  57. BIN
      tests/fuzzers/txfetcher/corpus/62817a48c78fbf2c12fcdc5ca58e2ca60c43543a-7
  58. 1
      tests/fuzzers/txfetcher/corpus/6782da8f1a432a77306d60d2ac2470c35b98004f-3
  59. BIN
      tests/fuzzers/txfetcher/corpus/68fb55290cb9d6da5b259017c34bcecf96c944aa-5
  60. 1
      tests/fuzzers/txfetcher/corpus/6a5059bc86872526241d21ab5dae9f0afd3b9ae1-3
  61. 1
      tests/fuzzers/txfetcher/corpus/717928e0e2d478c680c6409b173552ca98469ba5-6
  62. BIN
      tests/fuzzers/txfetcher/corpus/71d22f25419543e437f249ca437823b87ac926b1-6
  63. 2
      tests/fuzzers/txfetcher/corpus/7312a0f31ae5d773ed4fd74abc7521eb14754683-8
  64. BIN
      tests/fuzzers/txfetcher/corpus/76e413a50dc8861e3756e556f796f1737bec2675-4
  65. 14
      tests/fuzzers/txfetcher/corpus/78480977d5c07386b06e9b37f5c82f5ed86c2f09-3
  66. 10
      tests/fuzzers/txfetcher/corpus/7a113cd3c178934cdb64353af86d51462d7080a4-5
  67. 9
      tests/fuzzers/txfetcher/corpus/7ea9f71020f3eb783f743f744eba8d8ca4b2582f-3
  68. 1
      tests/fuzzers/txfetcher/corpus/84f8c275f3ffbaf8c32c21782af13de10e7de28b-3
  69. BIN
      tests/fuzzers/txfetcher/corpus/85dfe7ddee0e52aa19115c0ebb9ed28a14e488c6-5
  70. BIN
      tests/fuzzers/txfetcher/corpus/87bba5b1e3da38fed8cb5a9bc5c8baa819e83d05-5
  71. BIN
      tests/fuzzers/txfetcher/corpus/8a9ebedfbfec584d8b22761e6121dc1ca0248548-4
  72. BIN
      tests/fuzzers/txfetcher/corpus/8ff3bd49f93079e5e1c7f8f2461ba7ee612900c3-5
  73. 1
      tests/fuzzers/txfetcher/corpus/9034aaf45143996a2b14465c352ab0c6fa26b221-2
  74. 1
      tests/fuzzers/txfetcher/corpus/92cefdc6251d04896349a464b29be03d6bb04c3d-2
  75. BIN
      tests/fuzzers/txfetcher/corpus/9613e580ccb69df7c9074f0e2f6886ac6b34ca55-5
  76. BIN
      tests/fuzzers/txfetcher/corpus/98afc8970a680fdc4aee0b5d48784f650c566b75-6
  77. BIN
      tests/fuzzers/txfetcher/corpus/9dfc92f4ca2ece0167096fca6751ff314765f08b-8
  78. 5
      tests/fuzzers/txfetcher/corpus/9ebcbbfdaf0e98c87652e57226a4d8a35170c67d-4
  79. 2
      tests/fuzzers/txfetcher/corpus/9ff520eb8b8319a5fdafbe4d1cbb02a75058d93b-7
  80. BIN
      tests/fuzzers/txfetcher/corpus/a0b57a12e25ac5adcedb2a5c45915f0f62aee869-4
  81. BIN
      tests/fuzzers/txfetcher/corpus/a2684adccf16e036b051c12f283734fa803746e8-6
  82. BIN
      tests/fuzzers/txfetcher/corpus/a37305974cf477ecfe65fa92f37b1f51dea25910-4
  83. BIN
      tests/fuzzers/txfetcher/corpus/a7eb43926bd14b1f62a66a33107776e487434d32-7
  84. 2
      tests/fuzzers/txfetcher/corpus/a8f7c254eb64a40fd2a77b79979c7bbdac6a760c-4
  85. BIN
      tests/fuzzers/txfetcher/corpus/a9a8f287d6af24e47d8db468e8f967aa44fb5a1f-7
  86. 1
      tests/fuzzers/txfetcher/corpus/aa7444d8e326158046862590a0db993c07aef372-7
  87. 8
      tests/fuzzers/txfetcher/corpus/ae4593626d8796e079a358c2395a4f6c9ddd6a44-6
  88. BIN
      tests/fuzzers/txfetcher/corpus/b2942d4413a66939cda7db93020dee79eb17788c-9
  89. BIN
      tests/fuzzers/txfetcher/corpus/b4614117cdfd147d38f4e8a4d85f5a2bb99a6a4f-5
  90. 1
      tests/fuzzers/txfetcher/corpus/b631ef3291fa405cd6517d11f4d1b9b6d02912d4-2
  91. BIN
      tests/fuzzers/txfetcher/corpus/b7a91e338cc11f50ebdb2c414610efc4d5be3137-4
  92. 1
      tests/fuzzers/txfetcher/corpus/b858cb282617fb0956d960215c8e84d1ccf909c6-2
  93. 1
      tests/fuzzers/txfetcher/corpus/bc9d570aacf3acd39600feda8e72a293a4667da4-1
  94. 2
      tests/fuzzers/txfetcher/corpus/be7eed35b245b5d5d2adcdb4c67f07794eb86b24-3
  95. 4
      tests/fuzzers/txfetcher/corpus/c010b0cd70c7edbc5bd332fc9e2e91c6a1cbcdc4-5
  96. 2
      tests/fuzzers/txfetcher/corpus/c1690698607eb0f4c4244e9f9629968be4beb6bc-8
  97. BIN
      tests/fuzzers/txfetcher/corpus/c1f435e4f53a9a17578d9e8c4789860f962a1379-6
  98. BIN
      tests/fuzzers/txfetcher/corpus/c298a75334c3acf04bd129a8867447a25c8bacf8-7
  99. BIN
      tests/fuzzers/txfetcher/corpus/c42287c7d225e530e822f23bbbba6819a9e48f38-6
  100. BIN
      tests/fuzzers/txfetcher/corpus/c4cdbb891f3ee76476b7375d5ed51691fed95421-10
  101. Some files were not shown because too many files have changed in this diff Show More

@ -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():

@ -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)
}

@ -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,71 +150,101 @@ 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. select {
if len(txAnnos) > 0 { case hashes := <-p.txBroadcast:
// New batch of transactions to be broadcast, queue them (with cap)
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:])]
}
case <-done:
done = nil
case <-fail:
return
case <-p.term:
return
}
}
}
// announceTransactions is a write loop that schedules transaction broadcasts
// to the remote peer. The goal is to have an async writer that does not lock up
// node internals and at the same time rate limits queued data.
func (p *peer) announceTransactions() {
var (
queue []common.Hash // Queue of hashes to announce as transaction stubs
done chan struct{} // Non-nil if background announcer is running
fail = make(chan error) // Channel used to receive network error
)
for {
// If there's no in-flight announce running, check if a new one is needed
if done == nil && len(queue) > 0 {
// Pile transaction hashes until we reach our allowed network limit
var ( var (
hashes []common.Hash hashes []common.Hash
pending []common.Hash pending []common.Hash
size common.StorageSize size common.StorageSize
) )
for i := 0; i < len(txAnnos) && size < txsyncPackSize; i++ { for i := 0; i < len(queue) && size < txsyncPackSize; i++ {
if tx := p.getPooledTx(txAnnos[i]); tx != nil { if p.getPooledTx(queue[i]) != nil {
pending = append(pending, txAnnos[i]) pending = append(pending, queue[i])
size += common.HashLength size += common.HashLength
} }
hashes = append(hashes, txAnnos[i]) hashes = append(hashes, queue[i])
} }
txAnnos = txAnnos[:copy(txAnnos, txAnnos[len(hashes):])] queue = queue[:copy(queue, queue[len(hashes):])]
// If there's anything available to transfer, fire up an async writer
if len(pending) > 0 { if len(pending) > 0 {
done = make(chan struct{}) done = make(chan struct{})
go func() { go func() {
if err := p.SendNewTransactionHashes(pending); err != nil { if err := p.sendPooledTransactionHashes(pending); err != nil {
errch <- err fail <- err
return return
} }
close(done) close(done)
@ -227,95 +252,20 @@ func (p *peer) broadcastTxs() {
}() }()
} }
} }
} // Transfer goroutine may or may not have been started, listen for events
for {
scheduleTask()
select { select {
case hashes := <-p.txPropagation:
if len(txProps) == maxQueuedTxs {
continue
}
if len(txProps)+len(hashes) > maxQueuedTxs {
hashes = hashes[:maxQueuedTxs-len(txProps)]
}
txProps = append(txProps, hashes...)
case hashes := <-p.txAnnounce: case hashes := <-p.txAnnounce:
if len(txAnnos) == maxQueuedTxAnns { // New batch of transactions to be broadcast, queue them (with cap)
continue queue = append(queue, hashes...)
} if len(queue) > maxQueuedTxAnns {
if len(txAnnos)+len(hashes) > maxQueuedTxAnns { // Fancy copy and resize to ensure buffer doesn't grow indefinitely
hashes = hashes[:maxQueuedTxAnns-len(txAnnos)] queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])]
}
txAnnos = append(txAnnos, hashes...)
case <-done:
done = nil
case <-errch:
return
case <-p.term:
return
}
}
}
// retrievalTxs is a write loop which is responsible for retrieving transaction
// from the remote peer. The goal is to have an async writer that does not lock
// up node internals. If there are too many requests queued, then new arrival
// requests will be dropped silently so that we can ensure the memory assumption
// is fixed for each peer.
func (p *peer) retrievalTxs() {
var (
requests []common.Hash // Queue of transaction requests to the peer
done chan struct{} // Non-nil if background network sender routine is active.
errch = 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 {
select {
case hashes := <-p.txRetrieval:
if len(requests) == maxQueuedTxRetrieval {
continue
}
if len(requests)+len(hashes) > maxQueuedTxRetrieval {
hashes = hashes[:maxQueuedTxRetrieval-len(requests)]
}
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 {
return p2p.Send(p.rw, TxMsg, txs)
} }
// AsyncSendTransactions queues list of transactions propagation to a remote // AsyncSendTransactions queues a list of transactions (by hash) to eventually
// peer. If the peer's broadcast queue is full, the event is silently dropped. // propagate to a remote peer. The number of pending sends are capped (new ones
// will force old sends to be 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 {

@ -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()) // Remove the transactions that will be sent.
size += common.HashLength s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])]
} if len(s.txs) == 0 {
// Remove the transactions that will be sent. delete(pending, s.p.ID())
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) }()
} }
// 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,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½Ώ½οοοΏοΏ½#οΏοΏ½&οΏ½οΏ½

@ -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

@ -0,0 +1 @@
0000000000000000000000000000000000000000000000000000000000000000000000000

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

@ -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_

@ -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_

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

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

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

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

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

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

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

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

@ -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

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

@ -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 @@
&<EFBFBD><EFBFBD>w<EFBFBD><EFBFBD><EFBFBD>€<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD>
<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD>!<EFBFBD>"<EFBFBD>#<EFBFBD>$<EFBFBD>%<EFBFBD>&<EFBFBD>'<EFBFBD>(<EFBFBD>)<EFBFBD>*<EFBFBD>+<EFBFBD>,<EFBFBD>-<EFBFBD>.<EFBFBD>/<EFBFBD><EFBFBD>0

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

@ -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<EFBFBD> jA0CQQDU
y2ptGsuSmgUtWj3NM9xuwYPm+Z/F84K6+ARYiZ6Yj013sovGKUFfYAqVXVlxtIX
qyUBnu3Xh9ps8ZfjLZO7BAkEAlT4R5Yl6cGhaJQYFZHOde3JEMhNRcVFMO8dDaFeo
f9Oeos0Uot

@ -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 @@
<EFBFBD><EFBFBD> <EFBFBD>
<EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD>!<EFBFBD>"<EFBFBD>#<EFBFBD>$<EFBFBD>%<EFBFBD>&<EFBFBD>'<EFBFBD>(<EFBFBD>)<EFBFBD>*<EFBFBD>+<EFBFBD>,<EFBFBD>-<EFBFBD>.<EFBFBD>/<EFBFBD><EFBFBD>0

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

Loading…
Cancel
Save