github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/data.go (about) 1 package network 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "io" 7 "io/ioutil" 8 "strings" 9 "time" 10 11 "github.com/piotrnar/gocoin/client/common" 12 "github.com/piotrnar/gocoin/lib/btc" 13 ) 14 15 func (c *OneConnection) ProcessGetData(pl []byte) { 16 //println(c.PeerAddr.Ip(), "getdata") 17 b := bytes.NewReader(pl) 18 cnt, e := btc.ReadVLen(b) 19 if e != nil { 20 println("ProcessGetData:", e.Error(), c.PeerAddr.Ip()) 21 return 22 } 23 24 if b.Len() != int(cnt)*36 { 25 println(c.ConnID, "Inconsistent getdata message:", b.Len(), "!=", cnt*36) 26 c.DoS("GetDataLenERR") 27 return 28 } 29 30 if c.unfinished_getdata != nil { 31 //println(c.ConnID, "appending pending getdata with", cnt, "more invs") 32 if c.unfinished_getdata.Len()+b.Len() > 36*50000 { 33 c.DoS("GetDataTooBigA") 34 } else { 35 io.Copy(c.unfinished_getdata, b) 36 common.CountSafe("GetDataPauseExt") 37 } 38 return 39 } 40 c.processGetData(b) 41 } 42 43 func (c *OneConnection) processGetData(b *bytes.Reader) { 44 var typ uint32 45 var h [36]byte 46 for b.Len() > 0 { 47 if c.SendingPaused() { 48 // note that this function should not be called when c.unfinished_getdata is not nil 49 c.unfinished_getdata = new(bytes.Buffer) 50 //println(c.ConnID, "postpone getdata for", b.Len()/36, "invs") 51 io.Copy(c.unfinished_getdata, b) 52 common.CountSafe("GetDataPaused") 53 break 54 } 55 56 b.Read(h[:]) 57 58 typ = binary.LittleEndian.Uint32(h[:4]) 59 c.Mutex.Lock() 60 c.InvStore(typ, h[4:36]) 61 c.Mutex.Unlock() 62 63 if typ == MSG_BLOCK || typ == MSG_WITNESS_BLOCK { 64 if typ == MSG_BLOCK { 65 common.CountSafe("GetdataBlock") 66 } else { 67 common.CountSafe("GetdataBlockSw") 68 } 69 hash := btc.NewUint256(h[4:]) 70 crec, _, er := common.BlockChain.Blocks.BlockGetExt(hash) 71 72 if er == nil { 73 bl := crec.Data 74 if typ == MSG_BLOCK { 75 // remove witness data from the block 76 if crec.Block == nil { 77 crec.Block, _ = btc.NewBlock(bl) 78 } 79 if crec.Block.NoWitnessData == nil { 80 crec.Block.BuildNoWitnessData() 81 } 82 //println("block size", len(crec.Data), "->", len(bl)) 83 bl = crec.Block.NoWitnessData 84 } 85 c.SendRawMsg("block", bl) 86 } else { 87 //fmt.Println("BlockGetExt-2 failed for", hash.String(), er.Error()) 88 //notfound = append(notfound, h[:]...) 89 } 90 } else if typ == MSG_TX || typ == MSG_WITNESS_TX { 91 if typ == MSG_TX { 92 common.CountSafe("GetdataTx") 93 } else { 94 common.CountSafe("GetdataTxSw") 95 } 96 // ransaction 97 TxMutex.Lock() 98 if tx, ok := TransactionsToSend[btc.NewUint256(h[4:]).BIdx()]; ok && tx.Blocked == 0 { 99 tx.SentCnt++ 100 tx.Lastsent = time.Now() 101 TxMutex.Unlock() 102 if tx.SegWit == nil || typ == MSG_WITNESS_TX { 103 c.SendRawMsg("tx", tx.Raw) 104 } else { 105 c.SendRawMsg("tx", tx.Serialize()) 106 } 107 } else { 108 TxMutex.Unlock() 109 //notfound = append(notfound, h[:]...) 110 } 111 } else if typ == MSG_CMPCT_BLOCK { 112 common.CountSafe("GetdataCmpctBlk") 113 if !c.SendCmpctBlk(btc.NewUint256(h[4:])) { 114 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "asked for CmpctBlk we don't have", btc.NewUint256(h[4:]).String()) 115 if c.Misbehave("GetCmpctBlk", 100) { 116 break 117 } 118 } 119 } else { 120 common.CountSafe("GetdataTypeInvalid") 121 } 122 } 123 } 124 125 // netBlockReceived is called from a net conn thread. 126 func netBlockReceived(conn *OneConnection, b []byte) { 127 if len(b) < 100 { 128 conn.DoS("ShortBlock") 129 return 130 } 131 132 hash := btc.NewSha2Hash(b[:80]) 133 idx := hash.BIdx() 134 //println("got block data", hash.String()) 135 136 MutexRcv.Lock() 137 138 // the blocks seems to be fine 139 if rb, got := ReceivedBlocks[idx]; got { 140 rb.Cnt++ 141 Fetch.BlockBytesWasted += uint64(len(b)) 142 Fetch.BlockSameRcvd++ 143 conn.Mutex.Lock() 144 delete(conn.GetBlockInProgress, idx) 145 conn.Mutex.Unlock() 146 MutexRcv.Unlock() 147 return 148 } 149 150 // remove from BlocksToGet: 151 b2g := BlocksToGet[idx] 152 if b2g == nil { 153 //println("Block", hash.String(), " from", conn.PeerAddr.Ip(), conn.Node.Agent, " was not expected") 154 155 var sta int 156 sta, b2g = conn.ProcessNewHeader(b[:80]) 157 if b2g == nil { 158 if sta == PH_STATUS_FATAL { 159 println("Unrequested Block: FAIL - Ban", conn.PeerAddr.Ip(), conn.Node.Agent) 160 conn.DoS("BadUnreqBlock") 161 } else { 162 common.CountSafe("ErrUnreqBlock") 163 } 164 //conn.Disconnect() 165 MutexRcv.Unlock() 166 return 167 } 168 if sta == PH_STATUS_NEW { 169 b2g.SendInvs = true 170 } 171 //println(c.ConnID, " - taking this new block") 172 common.CountSafe("UnxpectedBlockNEW") 173 } 174 175 //println("block", b2g.BlockTreeNode.Height," len", len(b), " got from", conn.PeerAddr.Ip(), b2g.InProgress) 176 177 prev_block_raw := b2g.Block.Raw // in case if it's a corrupt one 178 b2g.Block.Raw = b 179 if conn.X.Authorized { 180 b2g.Block.Trusted.Set() 181 } 182 183 er := common.BlockChain.PostCheckBlock(b2g.Block) 184 if er != nil { 185 println("Corrupt block", hash.String(), b2g.BlockTreeNode.Height) 186 println(" ... received from", conn.PeerAddr.Ip(), er.Error()) 187 //ioutil.WriteFile(hash.String()+"-"+conn.PeerAddr.Ip()+".bin", b, 0700) 188 conn.DoS("BadBlock") 189 190 // We don't need to remove from conn.GetBlockInProgress as we're disconnecting 191 // ... decreasing of b2g.InProgress will also be done then. 192 193 if b2g.Block.MerkleRootMatch() && !strings.Contains(er.Error(), "RPC_Result:bad-witness-nonce-size") { 194 println(" <- It was a wrongly mined one - give it up") 195 DelB2G(idx) //remove it from BlocksToGet 196 if b2g.BlockTreeNode == LastCommitedHeader { 197 LastCommitedHeader = LastCommitedHeader.Parent 198 } 199 common.BlockChain.DeleteBranch(b2g.BlockTreeNode, delB2G_callback) 200 } else { 201 println(" <- Merkle Root not matching - discard the data:", len(b2g.Block.Txs), b2g.Block.TxCount, 202 b2g.Block.TxOffset, b2g.Block.NoWitnessSize, b2g.Block.BlockWeight, b2g.TotalInputs) 203 // We just recived a corrupt copy from the peer. We will ask another peer for it. 204 // But discard the data we extracted from this one, so it won't confuse us later. 205 b2g.Block.Raw = prev_block_raw 206 b2g.Block.NoWitnessSize, b2g.Block.BlockWeight, b2g.TotalInputs = 0, 0, 0 207 b2g.Block.TxCount, b2g.Block.TxOffset = 0, 0 208 b2g.Block.Txs = nil 209 } 210 211 MutexRcv.Unlock() 212 return 213 } 214 215 orb := &OneReceivedBlock{TmStart: b2g.Started, TmPreproc: b2g.TmPreproc, 216 TmDownload: conn.LastMsgTime, FromConID: conn.ConnID, DoInvs: b2g.SendInvs} 217 218 conn.Mutex.Lock() 219 bip := conn.GetBlockInProgress[idx] 220 if bip == nil { 221 //println(conn.ConnID, "received unrequested block", hash.String()) 222 common.CountSafe("UnreqBlockRcvd") 223 conn.cntInc("NewBlock!") 224 orb.TxMissing = -2 225 } else { 226 delete(conn.GetBlockInProgress, idx) 227 conn.cntInc("NewBlock") 228 orb.TxMissing = -1 229 } 230 conn.blocksreceived = append(conn.blocksreceived, time.Now()) 231 conn.Mutex.Unlock() 232 233 ReceivedBlocks[idx] = orb 234 DelB2G(idx) //remove it from BlocksToGet if no more pending downloads 235 236 store_on_disk := len(BlocksToGet) > 10 && common.GetBool(&common.CFG.Memory.CacheOnDisk) && len(b2g.Block.Raw) > 16*1024 237 MutexRcv.Unlock() 238 239 var bei *btc.BlockExtraInfo 240 241 size := len(b2g.Block.Raw) 242 if store_on_disk { 243 if e := ioutil.WriteFile(common.TempBlocksDir()+hash.String(), b2g.Block.Raw, 0600); e == nil { 244 bei = new(btc.BlockExtraInfo) 245 *bei = b2g.Block.BlockExtraInfo 246 b2g.Block = nil 247 } else { 248 println("write tmp block:", e.Error()) 249 } 250 } 251 252 NetBlocks <- &BlockRcvd{Conn: conn, Block: b2g.Block, BlockTreeNode: b2g.BlockTreeNode, 253 OneReceivedBlock: orb, BlockExtraInfo: bei, Size: size} 254 } 255 256 // parseLocatorsPayload parses the payload of "getblocks" or "getheaders" messages. 257 // It reads Version and VLen followed by the number of locators. 258 // Return zero-ed stop_hash is not present in the payload. 259 func parseLocatorsPayload(pl []byte) (h2get []*btc.Uint256, hashstop *btc.Uint256, er error) { 260 var cnt uint64 261 var ver uint32 262 263 b := bytes.NewReader(pl) 264 265 // version 266 if er = binary.Read(b, binary.LittleEndian, &ver); er != nil { 267 return 268 } 269 270 // hash count 271 if cnt, er = btc.ReadVLen(b); er != nil { 272 return 273 } 274 275 // block locator hashes 276 if cnt > 0 { 277 h2get = make([]*btc.Uint256, cnt) 278 for i := 0; i < int(cnt); i++ { 279 h2get[i] = new(btc.Uint256) 280 if _, er = b.Read(h2get[i].Hash[:]); er != nil { 281 return 282 } 283 } 284 } 285 286 // hash_stop 287 hashstop = new(btc.Uint256) 288 b.Read(hashstop.Hash[:]) // if not there, don't make a big deal about it 289 290 return 291 } 292 293 // Call it with locked MutexRcv 294 func getBlockToFetch(max_height uint32, cnt_in_progress, avg_block_size uint) (lowest_found *OneBlockToGet) { 295 for _, v := range BlocksToGet { 296 if v.InProgress == cnt_in_progress && v.Block.Height <= max_height && 297 (lowest_found == nil || v.Block.Height < lowest_found.Block.Height) { 298 lowest_found = v 299 } 300 } 301 return 302 } 303 304 var Fetc struct { 305 HeightA uint64 306 HeightB uint64 307 HeightC uint64 308 HeightD uint64 309 B2G uint64 310 C [6]uint64 311 } 312 313 var Fetch struct { 314 NoBlocksToGet uint64 315 HasBlocksExpired uint64 316 MaxCountInProgress uint64 317 MaxBytesInProgress uint64 318 NoWitness uint64 319 Nothing uint64 320 BlksCntMax [6]uint64 321 ReachEndOfLoop uint64 322 ReachMaxCnt uint64 323 ReachMaxData uint64 324 325 BlockBytesWasted uint64 326 BlockSameRcvd uint64 327 328 CacheEmpty uint64 329 LastCacheEmpty time.Time 330 } 331 332 func (c *OneConnection) GetBlockData() (yes bool) { 333 //MAX_GETDATA_FORWARD 334 // Need to send getdata...? 335 MutexRcv.Lock() 336 defer MutexRcv.Unlock() 337 338 if LowestIndexToBlocksToGet == 0 || len(BlocksToGet) == 0 { 339 Fetch.NoBlocksToGet++ 340 // wake up in one minute, just in case 341 c.nextGetData = time.Now().Add(60 * time.Second) 342 return 343 } 344 345 c.Mutex.Lock() 346 if c.X.BlocksExpired > 0 { // Do not fetch blocks from nodes that had not given us some in the past 347 c.Mutex.Unlock() 348 Fetch.HasBlocksExpired++ 349 return 350 } 351 cbip := len(c.GetBlockInProgress) 352 c.Mutex.Unlock() 353 354 if cbip >= MAX_PEERS_BLOCKS_IN_PROGRESS { 355 Fetch.MaxCountInProgress++ 356 // wake up in a few seconds, maybe some blocks will complete by then 357 c.nextGetData = time.Now().Add(1 * time.Second) 358 return 359 } 360 361 avg_block_size := common.AverageBlockSize.Get() 362 block_data_in_progress := cbip * avg_block_size 363 364 if block_data_in_progress > 0 && (block_data_in_progress+avg_block_size) > MAX_GETDATA_FORWARD { 365 Fetch.MaxBytesInProgress++ 366 // wake up in a few seconds, maybe some blocks will complete by then 367 c.nextGetData = time.Now().Add(1 * time.Second) // wait for some blocks to complete 368 return 369 } 370 371 var cnt uint64 372 var block_type uint32 373 374 if (c.Node.Services & btc.SERVICE_SEGWIT) != 0 { 375 block_type = MSG_WITNESS_BLOCK 376 } else { 377 block_type = MSG_BLOCK 378 } 379 380 // We can issue getdata for this peer 381 // Let's look for the lowest height block in BlocksToGet that isn't being downloaded yet 382 383 last_block_height := common.Last.BlockHeight() 384 max_height := last_block_height + uint32(common.SyncMaxCacheBytes.Get()/avg_block_size) 385 386 Fetc.HeightA = uint64(last_block_height) 387 Fetc.HeightB = uint64(LowestIndexToBlocksToGet) 388 Fetc.HeightC = uint64(max_height) 389 390 if max_height > last_block_height+MAX_BLOCKS_FORWARD_CNT { 391 max_height = last_block_height + MAX_BLOCKS_FORWARD_CNT 392 } 393 max_max_height := max_height 394 if max_max_height > c.Node.Height { 395 max_max_height = c.Node.Height 396 } 397 if max_max_height > LastCommitedHeader.Height { 398 max_max_height = LastCommitedHeader.Height 399 } 400 401 if common.BlockChain.Consensus.Enforce_SEGWIT != 0 && (c.Node.Services&btc.SERVICE_SEGWIT) == 0 { // no segwit node 402 if max_height >= common.BlockChain.Consensus.Enforce_SEGWIT-1 { 403 max_height = common.BlockChain.Consensus.Enforce_SEGWIT - 1 404 if max_height <= LowestIndexToBlocksToGet { 405 Fetch.NoWitness++ 406 c.nextGetData = time.Now().Add(time.Hour) // never do getdata 407 return 408 } 409 } 410 } 411 412 Fetc.HeightD = uint64(max_height) 413 414 max_blocks_at_once := common.GetUint32(&common.CFG.Net.MaxBlockAtOnce) 415 max_blocks_forward := max_height - last_block_height 416 invs := new(bytes.Buffer) 417 var cnt_in_progress uint32 418 var lowest_found *OneBlockToGet 419 420 for { 421 // Find block to fetch: 422 max_height = last_block_height + max_blocks_forward/(cnt_in_progress+1) 423 if max_height > max_max_height { 424 max_height = max_max_height 425 } 426 if max_height < LowestIndexToBlocksToGet { 427 Fetch.BlksCntMax[cnt_in_progress]++ 428 break 429 } 430 for bh := LowestIndexToBlocksToGet; bh <= max_height; bh++ { 431 if idxlst, ok := IndexToBlocksToGet[bh]; ok { 432 for _, idx := range idxlst { 433 v := BlocksToGet[idx] 434 if uint32(v.InProgress) == cnt_in_progress { 435 c.Mutex.Lock() 436 _, ok := c.GetBlockInProgress[idx] 437 c.Mutex.Unlock() 438 if !ok { 439 lowest_found = v 440 goto found_it 441 } 442 } 443 } 444 } 445 } 446 447 // If we came here, we did not find it. 448 if cnt_in_progress++; cnt_in_progress >= max_blocks_at_once { 449 Fetch.ReachEndOfLoop++ 450 break 451 } 452 continue 453 454 found_it: 455 Fetc.C[lowest_found.InProgress]++ 456 457 binary.Write(invs, binary.LittleEndian, block_type) 458 invs.Write(lowest_found.BlockHash.Hash[:]) 459 lowest_found.InProgress++ 460 cnt++ 461 462 c.Mutex.Lock() 463 c.GetBlockInProgress[lowest_found.BlockHash.BIdx()] = 464 &oneBlockDl{hash: lowest_found.BlockHash, start: time.Now(), SentAtPingCnt: c.X.PingSentCnt} 465 cbip = len(c.GetBlockInProgress) 466 c.Mutex.Unlock() 467 468 if cbip >= MAX_PEERS_BLOCKS_IN_PROGRESS { 469 Fetch.ReachMaxCnt++ 470 break // no more than 2000 blocks in progress / peer 471 } 472 block_data_in_progress += avg_block_size 473 if block_data_in_progress > MAX_GETDATA_FORWARD { 474 Fetch.ReachMaxData++ 475 break 476 } 477 } 478 479 Fetc.B2G = uint64(cnt) 480 481 if cnt == 0 { 482 //println(c.ConnID, "fetch nothing", cbip, block_data_in_progress, max_height-common.Last.BlockHeight(), cnt_in_progress) 483 Fetch.Nothing++ 484 // wake up in a few seconds, maybe it will be different next time 485 c.nextGetData = time.Now().Add(5 * time.Second) 486 return 487 } 488 489 bu := new(bytes.Buffer) 490 btc.WriteVlen(bu, uint64(cnt)) 491 pl := append(bu.Bytes(), invs.Bytes()...) 492 //println(c.ConnID, "fetching", cnt, "new blocks ->", cbip) 493 c.SendRawMsg("getdata", pl) 494 yes = true 495 496 // we don't set c.nextGetData here, as it will be done in tick.go after "block" message 497 c.Mutex.Lock() 498 // we will come back here only after receiving half of the blocks that we have requested 499 c.keepBlocksOver = 3 * len(c.GetBlockInProgress) / 4 500 c.Mutex.Unlock() 501 502 return 503 }