github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/invs.go (about) 1 package network 2 3 import ( 4 "fmt" 5 //"time" 6 "bytes" 7 "encoding/binary" 8 9 "github.com/piotrnar/gocoin/client/common" 10 "github.com/piotrnar/gocoin/lib/btc" 11 "github.com/piotrnar/gocoin/lib/chain" 12 ) 13 14 const ( 15 MSG_WITNESS_FLAG = 0x40000000 16 17 MSG_TX = 1 18 MSG_BLOCK = 2 19 MSG_CMPCT_BLOCK = 4 20 MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG 21 MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG 22 ) 23 24 func blockReceived(bh *btc.Uint256) (ok bool) { 25 MutexRcv.Lock() 26 _, ok = ReceivedBlocks[bh.BIdx()] 27 MutexRcv.Unlock() 28 return 29 } 30 31 func hash2invid(hash []byte) uint64 { 32 return binary.LittleEndian.Uint64(hash[4:12]) 33 } 34 35 // Make sure c.Mutex is locked when calling it 36 func (c *OneConnection) InvStore(typ uint32, hash []byte) { 37 inv_id := hash2invid(hash) 38 if len(c.InvDone.History) < MAX_INV_HISTORY { 39 c.InvDone.History = append(c.InvDone.History, inv_id) 40 c.InvDone.Map[inv_id] = typ 41 c.InvDone.Idx++ 42 return 43 } 44 if c.InvDone.Idx == MAX_INV_HISTORY { 45 c.InvDone.Idx = 0 46 } 47 delete(c.InvDone.Map, c.InvDone.History[c.InvDone.Idx]) 48 c.InvDone.History[c.InvDone.Idx] = inv_id 49 c.InvDone.Map[inv_id] = typ 50 c.InvDone.Idx++ 51 } 52 53 func (c *OneConnection) ProcessInv(pl []byte) { 54 if len(pl) < 37 { 55 //println(c.PeerAddr.Ip(), "inv payload too short", len(pl)) 56 c.DoS("InvEmpty") 57 return 58 } 59 c.Mutex.Lock() 60 c.X.InvsRecieved++ 61 c.Mutex.Unlock() 62 63 cnt, of := btc.VLen(pl) 64 if of == 0 || len(pl) != of+36*cnt { 65 println("inv payload length mismatch", len(pl), of, cnt) 66 c.DoS("InvErr") 67 return 68 } 69 70 for i := 0; i < cnt; i++ { 71 typ := binary.LittleEndian.Uint32(pl[of : of+4]) 72 c.Mutex.Lock() 73 c.InvStore(typ, pl[of+4:of+36]) 74 ahr := c.X.AllHeadersReceived 75 c.Mutex.Unlock() 76 //common.CountSafe(fmt.Sprint("InvGot-", typ)) 77 if typ == MSG_BLOCK { 78 bhash := btc.NewUint256(pl[of+4 : of+36]) 79 if !ahr { 80 common.CountSafe("InvBlockIgnored") 81 } else { 82 if !blockReceived(bhash) { 83 MutexRcv.Lock() 84 if b2g, ok := BlocksToGet[bhash.BIdx()]; ok { 85 if c.Node.Height < b2g.Block.Height { 86 c.Node.Height = b2g.Block.Height 87 } 88 common.CountSafe("InvBlockFresh") 89 //println(c.PeerAddr.Ip(), c.Node.Version, "also knows the block", b2g.Block.Height, bhash.String()) 90 c.MutexSetBool(&c.X.GetBlocksDataNow, true) 91 } else { 92 common.CountSafe("InvBlockNew") 93 c.ReceiveHeadersNow() 94 //println(c.PeerAddr.Ip(), c.Node.Version, "possibly new block", bhash.String()) 95 } 96 MutexRcv.Unlock() 97 } else { 98 common.CountSafe("InvBlockOld") 99 } 100 } 101 } else if typ == MSG_TX { 102 if common.AcceptTx() { 103 c.TxInvNotify(pl[of+4 : of+36]) 104 } else { 105 common.CountSafe("InvTxIgnored") 106 } 107 } 108 of += 36 109 } 110 111 return 112 } 113 114 func NetRouteInv(typ uint32, h *btc.Uint256, fromConn *OneConnection) uint32 { 115 var fee_spkb uint64 116 if typ == MSG_TX { 117 TxMutex.Lock() 118 if tx, ok := TransactionsToSend[h.BIdx()]; ok { 119 fee_spkb = (1000 * tx.Fee) / uint64(tx.VSize()) 120 } else { 121 println("NetRouteInv: txid", h.String(), "not in mempool") 122 } 123 TxMutex.Unlock() 124 } 125 return NetRouteInvExt(typ, h, fromConn, fee_spkb) 126 } 127 128 // NetRouteInvExt is called from the main thread (or from a UI). 129 func NetRouteInvExt(typ uint32, h *btc.Uint256, fromConn *OneConnection, fee_spkb uint64) (cnt uint32) { 130 common.CountSafe(fmt.Sprint("NetRouteInv", typ)) 131 132 // Prepare the inv 133 inv := new([36]byte) 134 binary.LittleEndian.PutUint32(inv[0:4], typ) 135 copy(inv[4:36], h.Bytes()) 136 137 // Append it to PendingInvs in each open connection 138 Mutex_net.Lock() 139 for _, v := range OpenCons { 140 if v != fromConn { // except the one that this inv came from 141 send_inv := true 142 v.Mutex.Lock() 143 if typ == MSG_TX { 144 if v.Node.DoNotRelayTxs { 145 send_inv = false 146 common.CountSafe("SendInvNoTxNode") 147 } else if v.X.MinFeeSPKB > 0 && uint64(v.X.MinFeeSPKB) > fee_spkb { 148 send_inv = false 149 common.CountSafe("SendInvFeeTooLow") 150 } 151 152 /* This is to prevent sending own txs to "spying" peers: 153 else if fromConn==nil && v.X.InvsRecieved==0 { 154 send_inv = false 155 common.CountSafe("SendInvOwnBlocked") 156 } 157 */ 158 } 159 if send_inv { 160 if len(v.PendingInvs) < 500 { 161 if typ, ok := v.InvDone.Map[hash2invid(inv[4:36])]; ok { 162 common.CountSafe(fmt.Sprint("SendInvSame-", typ)) 163 } else { 164 v.PendingInvs = append(v.PendingInvs, inv) 165 cnt++ 166 if typ == MSG_BLOCK { 167 v.sendInvsNow.Set() // for faster block propagation 168 } 169 } 170 } else { 171 common.CountSafe("SendInvFull") 172 } 173 } 174 v.Mutex.Unlock() 175 } 176 } 177 Mutex_net.Unlock() 178 return 179 } 180 181 // Call this function only when BlockIndexAccess is locked 182 func addInvBlockBranch(inv map[[32]byte]bool, bl *chain.BlockTreeNode, stop *btc.Uint256) { 183 if len(inv) >= 500 || bl.BlockHash.Equal(stop) { 184 return 185 } 186 inv[bl.BlockHash.Hash] = true 187 for i := range bl.Childs { 188 if len(inv) >= 500 { 189 return 190 } 191 addInvBlockBranch(inv, bl.Childs[i], stop) 192 } 193 } 194 195 func (c *OneConnection) GetBlocks(pl []byte) { 196 h2get, hashstop, e := parseLocatorsPayload(pl) 197 198 if e != nil || len(h2get) < 1 || hashstop == nil { 199 println("GetBlocks: error parsing payload from", c.PeerAddr.Ip()) 200 c.DoS("BadGetBlks") 201 return 202 } 203 204 invs := make(map[[32]byte]bool, 500) 205 for i := range h2get { 206 common.BlockChain.BlockIndexAccess.Lock() 207 if bl, ok := common.BlockChain.BlockIndex[h2get[i].BIdx()]; ok { 208 // make sure that this block is in our main chain 209 common.Last.Mutex.Lock() 210 end := common.Last.Block 211 common.Last.Mutex.Unlock() 212 for ; end != nil && end.Height >= bl.Height; end = end.Parent { 213 if end == bl { 214 addInvBlockBranch(invs, bl, hashstop) // Yes - this is the main chain 215 if len(invs) > 0 { 216 common.BlockChain.BlockIndexAccess.Unlock() 217 218 inv := new(bytes.Buffer) 219 btc.WriteVlen(inv, uint64(len(invs))) 220 for k := range invs { 221 binary.Write(inv, binary.LittleEndian, uint32(2)) 222 inv.Write(k[:]) 223 } 224 c.SendRawMsg("inv", inv.Bytes()) 225 return 226 } 227 } 228 } 229 } 230 common.BlockChain.BlockIndexAccess.Unlock() 231 } 232 233 common.CountSafe("GetblksMissed") 234 return 235 } 236 237 func (c *OneConnection) SendInvs() (res bool) { 238 b_txs := new(bytes.Buffer) 239 b_blk := new(bytes.Buffer) 240 var c_blk []*btc.Uint256 241 242 c.Mutex.Lock() 243 if len(c.PendingInvs) > 0 { 244 for i := range c.PendingInvs { 245 var inv_sent_otherwise bool 246 typ := binary.LittleEndian.Uint32((*c.PendingInvs[i])[:4]) 247 c.InvStore(typ, (*c.PendingInvs[i])[4:36]) 248 if typ == MSG_BLOCK { 249 if c.Node.SendCmpctVer >= 1 && c.Node.HighBandwidth { 250 c_blk = append(c_blk, btc.NewUint256((*c.PendingInvs[i])[4:])) 251 inv_sent_otherwise = true 252 } else if c.Node.SendHeaders { 253 // convert block inv to block header 254 common.BlockChain.BlockIndexAccess.Lock() 255 bl := common.BlockChain.BlockIndex[btc.NewUint256((*c.PendingInvs[i])[4:]).BIdx()] 256 if bl != nil { 257 b_blk.Write(bl.BlockHeader[:]) 258 b_blk.Write([]byte{0}) // 0 txs 259 } 260 common.BlockChain.BlockIndexAccess.Unlock() 261 inv_sent_otherwise = true 262 } 263 } 264 265 if !inv_sent_otherwise { 266 b_txs.Write((*c.PendingInvs[i])[:]) 267 } 268 } 269 res = true 270 } 271 c.PendingInvs = nil 272 c.sendInvsNow.Clr() 273 c.Mutex.Unlock() 274 275 if len(c_blk) > 0 { 276 for _, h := range c_blk { 277 c.SendCmpctBlk(h) 278 } 279 } 280 281 if b_blk.Len() > 0 { 282 common.CountSafe("InvSentAsHeader") 283 var b [5]byte 284 ll := btc.PutVlen(b[:], b_blk.Len()/81) 285 c.SendRawMsg("headers", append(b[:ll], b_blk.Bytes()...)) 286 } 287 288 if b_txs.Len() > 0 { 289 var b [5]byte 290 ll := btc.PutVlen(b[:], b_txs.Len()/36) 291 c.SendRawMsg("inv", append(b[:ll], b_txs.Bytes()...)) 292 } 293 294 return 295 }