github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/eth/protocols/eth/broadcast.go (about) 1 // Copyright 2021 The adkgo Authors 2 // This file is part of the adkgo library (adapted for adkgo from go--ethereum v1.10.8). 3 // 4 // the adkgo library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // the adkgo library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the adkgo library. If not, see <http://www.gnu.org/licenses/>. 16 17 package eth 18 19 import ( 20 "math/big" 21 22 "github.com/aidoskuneen/adk-node/common" 23 "github.com/aidoskuneen/adk-node/core/types" 24 ) 25 26 const ( 27 // This is the target size for the packs of transactions or announcements. A 28 // pack can get larger than this if a single transactions exceeds this size. 29 maxTxPacketSize = 100 * 1024 30 ) 31 32 // blockPropagation is a block propagation event, waiting for its turn in the 33 // broadcast queue. 34 type blockPropagation struct { 35 block *types.Block 36 td *big.Int 37 } 38 39 // broadcastBlocks is a write loop that multiplexes blocks and block accouncements 40 // to the remote peer. The goal is to have an async writer that does not lock up 41 // node internals and at the same time rate limits queued data. 42 func (p *Peer) broadcastBlocks() { 43 for { 44 select { 45 case prop := <-p.queuedBlocks: 46 if err := p.SendNewBlock(prop.block, prop.td); err != nil { 47 return 48 } 49 p.Log().Trace("Propagated block", "number", prop.block.Number(), "hash", prop.block.Hash(), "td", prop.td) 50 51 case block := <-p.queuedBlockAnns: 52 if err := p.SendNewBlockHashes([]common.Hash{block.Hash()}, []uint64{block.NumberU64()}); err != nil { 53 return 54 } 55 p.Log().Trace("Announced block", "number", block.Number(), "hash", block.Hash()) 56 57 case <-p.term: 58 return 59 } 60 } 61 } 62 63 // broadcastTransactions is a write loop that schedules transaction broadcasts 64 // to the remote peer. The goal is to have an async writer that does not lock up 65 // node internals and at the same time rate limits queued data. 66 func (p *Peer) broadcastTransactions() { 67 var ( 68 queue []common.Hash // Queue of hashes to broadcast as full transactions 69 done chan struct{} // Non-nil if background broadcaster is running 70 fail = make(chan error, 1) // Channel used to receive network error 71 failed bool // Flag whether a send failed, discard everything onward 72 ) 73 for { 74 // If there's no in-flight broadcast running, check if a new one is needed 75 if done == nil && len(queue) > 0 { 76 // Pile transaction until we reach our allowed network limit 77 var ( 78 hashes []common.Hash 79 txs []*types.Transaction 80 size common.StorageSize 81 ) 82 for i := 0; i < len(queue) && size < maxTxPacketSize; i++ { 83 if tx := p.txpool.Get(queue[i]); tx != nil { 84 txs = append(txs, tx) 85 size += tx.Size() 86 } 87 hashes = append(hashes, queue[i]) 88 } 89 queue = queue[:copy(queue, queue[len(hashes):])] 90 91 // If there's anything available to transfer, fire up an async writer 92 if len(txs) > 0 { 93 done = make(chan struct{}) 94 go func() { 95 if err := p.SendTransactions(txs); err != nil { 96 fail <- err 97 return 98 } 99 close(done) 100 p.Log().Trace("Sent transactions", "count", len(txs)) 101 }() 102 } 103 } 104 // Transfer goroutine may or may not have been started, listen for events 105 select { 106 case hashes := <-p.txBroadcast: 107 // If the connection failed, discard all transaction events 108 if failed { 109 continue 110 } 111 // New batch of transactions to be broadcast, queue them (with cap) 112 queue = append(queue, hashes...) 113 if len(queue) > maxQueuedTxs { 114 // Fancy copy and resize to ensure buffer doesn't grow indefinitely 115 queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxs:])] 116 } 117 118 case <-done: 119 done = nil 120 121 case <-fail: 122 failed = true 123 124 case <-p.term: 125 return 126 } 127 } 128 } 129 130 // announceTransactions is a write loop that schedules transaction broadcasts 131 // to the remote peer. The goal is to have an async writer that does not lock up 132 // node internals and at the same time rate limits queued data. 133 func (p *Peer) announceTransactions() { 134 var ( 135 queue []common.Hash // Queue of hashes to announce as transaction stubs 136 done chan struct{} // Non-nil if background announcer is running 137 fail = make(chan error, 1) // Channel used to receive network error 138 failed bool // Flag whether a send failed, discard everything onward 139 ) 140 for { 141 // If there's no in-flight announce running, check if a new one is needed 142 if done == nil && len(queue) > 0 { 143 // Pile transaction hashes until we reach our allowed network limit 144 var ( 145 count int 146 pending []common.Hash 147 size common.StorageSize 148 ) 149 for count = 0; count < len(queue) && size < maxTxPacketSize; count++ { 150 if p.txpool.Get(queue[count]) != nil { 151 pending = append(pending, queue[count]) 152 size += common.HashLength 153 } 154 } 155 // Shift and trim queue 156 queue = queue[:copy(queue, queue[count:])] 157 158 // If there's anything available to transfer, fire up an async writer 159 if len(pending) > 0 { 160 done = make(chan struct{}) 161 go func() { 162 if err := p.sendPooledTransactionHashes(pending); err != nil { 163 fail <- err 164 return 165 } 166 close(done) 167 p.Log().Trace("Sent transaction announcements", "count", len(pending)) 168 }() 169 } 170 } 171 // Transfer goroutine may or may not have been started, listen for events 172 select { 173 case hashes := <-p.txAnnounce: 174 // If the connection failed, discard all transaction events 175 if failed { 176 continue 177 } 178 // New batch of transactions to be broadcast, queue them (with cap) 179 queue = append(queue, hashes...) 180 if len(queue) > maxQueuedTxAnns { 181 // Fancy copy and resize to ensure buffer doesn't grow indefinitely 182 queue = queue[:copy(queue, queue[len(queue)-maxQueuedTxAnns:])] 183 } 184 185 case <-done: 186 done = nil 187 188 case <-fail: 189 failed = true 190 191 case <-p.term: 192 return 193 } 194 } 195 }