github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/cblk.go (about) 1 package network 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "encoding/binary" 7 "encoding/hex" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "sync" 12 "time" 13 14 "github.com/piotrnar/gocoin/client/common" 15 "github.com/piotrnar/gocoin/lib/btc" 16 "github.com/piotrnar/gocoin/lib/chain" 17 "github.com/piotrnar/gocoin/lib/others/siphash" 18 ) 19 20 var ( 21 CompactBlocksMutex sync.Mutex 22 ) 23 24 type CmpctBlockCollector struct { 25 Header []byte 26 Txs []interface{} // either []byte of uint64 27 K0, K1 uint64 28 Sid2idx map[uint64]int 29 Missing int 30 } 31 32 func ShortIDToU64(d []byte) uint64 { 33 return uint64(d[0]) | (uint64(d[1]) << 8) | (uint64(d[2]) << 16) | 34 (uint64(d[3]) << 24) | (uint64(d[4]) << 32) | (uint64(d[5]) << 40) 35 } 36 37 func (col *CmpctBlockCollector) Assemble() []byte { 38 bdat := new(bytes.Buffer) 39 bdat.Write(col.Header) 40 btc.WriteVlen(bdat, uint64(len(col.Txs))) 41 for _, txd := range col.Txs { 42 bdat.Write(txd.([]byte)) 43 } 44 return bdat.Bytes() 45 } 46 47 func GetchBlockForBIP152(hash *btc.Uint256) (crec *chain.BlckCachRec) { 48 CompactBlocksMutex.Lock() 49 defer CompactBlocksMutex.Unlock() 50 51 crec, _, _ = common.BlockChain.Blocks.BlockGetExt(hash) 52 if crec == nil { 53 //fmt.Println("BlockGetExt failed for", hash.String(), er.Error()) 54 return 55 } 56 57 if crec.Block == nil { 58 crec.Block, _ = btc.NewBlock(crec.Data) 59 if crec.Block == nil { 60 fmt.Println("GetchBlockForBIP152: btc.NewBlock() failed for", hash.String()) 61 return 62 } 63 } 64 65 if len(crec.Block.Txs) == 0 { 66 if crec.Block.BuildTxList() != nil { 67 fmt.Println("GetchBlockForBIP152: bl.BuildTxList() failed for", hash.String()) 68 return 69 } 70 } 71 72 if len(crec.BIP152) != 24 { 73 crec.BIP152 = make([]byte, 24) 74 copy(crec.BIP152[:8], crec.Data[48:56]) // set the nonce to 8 middle-bytes of block's merkle_root 75 sha := sha256.New() 76 sha.Write(crec.Data[:80]) 77 sha.Write(crec.BIP152[:8]) 78 copy(crec.BIP152[8:24], sha.Sum(nil)[0:16]) 79 } 80 81 return 82 } 83 84 func (c *OneConnection) SendCmpctBlk(hash *btc.Uint256) bool { 85 crec := GetchBlockForBIP152(hash) 86 if crec == nil { 87 //fmt.Println(c.ConnID, "cmpctblock not sent:", c.Node.Agent, hash.String()) 88 return false 89 } 90 91 k0 := binary.LittleEndian.Uint64(crec.BIP152[8:16]) 92 k1 := binary.LittleEndian.Uint64(crec.BIP152[16:24]) 93 94 msg := new(bytes.Buffer) 95 msg.Write(crec.Data[:80]) 96 msg.Write(crec.BIP152[:8]) 97 btc.WriteVlen(msg, uint64(len(crec.Block.Txs)-1)) // all except coinbase 98 for i := 1; i < len(crec.Block.Txs); i++ { 99 var lsb [8]byte 100 var hasz *btc.Uint256 101 if c.Node.SendCmpctVer == 2 { 102 hasz = crec.Block.Txs[i].WTxID() 103 } else { 104 hasz = &crec.Block.Txs[i].Hash 105 } 106 binary.LittleEndian.PutUint64(lsb[:], siphash.Hash(k0, k1, hasz.Hash[:])) 107 msg.Write(lsb[:6]) 108 } 109 msg.Write([]byte{1}) // one preffiled tx 110 msg.Write([]byte{0}) // coinbase - index 0 111 if c.Node.SendCmpctVer == 2 { 112 msg.Write(crec.Block.Txs[0].Raw) // coinbase - index 0 113 } else { 114 crec.Block.Txs[0].WriteSerialized(msg) // coinbase - index 0 115 } 116 c.SendRawMsg("cmpctblock", msg.Bytes()) 117 return true 118 } 119 120 func (c *OneConnection) ProcessGetBlockTxn(pl []byte) { 121 if len(pl) < 34 { 122 println(c.ConnID, "GetBlockTxnShort") 123 c.DoS("GetBlockTxnShort") 124 return 125 } 126 hash := btc.NewUint256(pl[:32]) 127 crec := GetchBlockForBIP152(hash) 128 if crec == nil { 129 fmt.Println(c.ConnID, "GetBlockTxn aborting for", hash.String()) 130 return 131 } 132 133 req := bytes.NewReader(pl[32:]) 134 indexes_length, _ := btc.ReadVLen(req) 135 if indexes_length == 0 { 136 println(c.ConnID, "GetBlockTxnEmpty") 137 c.DoS("GetBlockTxnEmpty") 138 return 139 } 140 141 var exp_idx uint64 142 msg := new(bytes.Buffer) 143 144 msg.Write(hash.Hash[:]) 145 btc.WriteVlen(msg, indexes_length) 146 147 for { 148 idx, er := btc.ReadVLen(req) 149 if er != nil { 150 println(c.ConnID, "GetBlockTxnERR") 151 c.DoS("GetBlockTxnERR") 152 return 153 } 154 idx += exp_idx 155 if int(idx) >= len(crec.Block.Txs) { 156 println(c.ConnID, "GetBlockTxnIdx+") 157 c.DoS("GetBlockTxnIdx+") 158 return 159 } 160 if c.Node.SendCmpctVer == 2 { 161 msg.Write(crec.Block.Txs[idx].Raw) // coinbase - index 0 162 } else { 163 crec.Block.Txs[idx].WriteSerialized(msg) // coinbase - index 0 164 } 165 if indexes_length == 1 { 166 break 167 } 168 indexes_length-- 169 exp_idx = idx + 1 170 } 171 172 c.SendRawMsg("blocktxn", msg.Bytes()) 173 } 174 175 func delB2G_callback(hash *btc.Uint256) { 176 DelB2G(hash.BIdx()) 177 } 178 179 func (c *OneConnection) ProcessCmpctBlock(pl []byte) { 180 if len(pl) < 90 { 181 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error A", hex.EncodeToString(pl)) 182 c.DoS("CmpctBlkErrA") 183 return 184 } 185 186 MutexRcv.Lock() 187 defer MutexRcv.Unlock() 188 189 sta, b2g := c.ProcessNewHeader(pl[:80]) 190 191 if b2g == nil { 192 common.CountSafe("CmpctBlockHdrNo") 193 if sta == PH_STATUS_ERROR { 194 c.ReceiveHeadersNow() // block doesn't connect so ask for the headers 195 c.Misbehave("BadCmpct", 50) // do it 20 times and you are banned 196 } else if sta == PH_STATUS_FATAL { 197 c.DoS("BadCmpct") 198 } 199 return 200 } 201 if sta == PH_STATUS_NEW { 202 b2g.SendInvs = true 203 } 204 205 if common.BlockChain.Consensus.Enforce_SEGWIT != 0 && c.Node.SendCmpctVer < 2 { 206 if b2g.Block.Height >= common.BlockChain.Consensus.Enforce_SEGWIT { 207 common.CountSafe("CmpctBlockIgnore") 208 println("Ignore compact block", b2g.Block.Height, "from non-segwit node", c.ConnID) 209 if (c.Node.Services & btc.SERVICE_SEGWIT) != 0 { 210 // it only makes sense to ask this node for block's data, if it supports segwit 211 c.MutexSetBool(&c.X.GetBlocksDataNow, true) 212 } 213 return 214 } 215 } 216 217 // if we got here, we shall download this block 218 if c.Node.Height < b2g.Block.Height { 219 c.Mutex.Lock() 220 c.Node.Height = b2g.Block.Height 221 c.Mutex.Unlock() 222 } 223 224 if b2g.InProgress >= uint(common.CFG.Net.MaxBlockAtOnce) { 225 common.CountSafe("CmpctBlockMaxInProg") 226 //fmt.Println(c.ConnID, " - too many in progress") 227 return 228 } 229 230 var n, idx, shortidscnt, shortidx_idx, prefilledcnt int 231 232 col := new(CmpctBlockCollector) 233 col.Header = b2g.Block.Raw[:80] 234 235 offs := 88 236 shortidscnt, n = btc.VLen(pl[offs:]) 237 if n == 0 || shortidscnt < 0 || n > 3 { 238 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error B", hex.EncodeToString(pl)) 239 c.DoS("CmpctBlkErrB") 240 return 241 } 242 offs += n 243 shortidx_idx = offs 244 shortids := make(map[uint64][]byte, shortidscnt) 245 for i := 0; i < int(shortidscnt); i++ { 246 if len(pl[offs:offs+6]) < 6 { 247 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error B2", hex.EncodeToString(pl)) 248 c.DoS("CmpctBlkErrB2") 249 return 250 } 251 shortids[ShortIDToU64(pl[offs:offs+6])] = nil 252 offs += 6 253 } 254 255 prefilledcnt, n = btc.VLen(pl[offs:]) 256 if n == 0 || prefilledcnt < 0 || n > 3 { 257 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error C", hex.EncodeToString(pl)) 258 c.DoS("CmpctBlkErrC") 259 return 260 } 261 offs += n 262 263 col.Txs = make([]interface{}, prefilledcnt+shortidscnt) 264 265 exp := 0 266 for i := 0; i < int(prefilledcnt); i++ { 267 idx, n = btc.VLen(pl[offs:]) 268 if n == 0 || idx < 0 || n > 3 { 269 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error D", hex.EncodeToString(pl)) 270 c.DoS("CmpctBlkErrD") 271 return 272 } 273 idx += exp 274 offs += n 275 n = btc.TxSize(pl[offs:]) 276 if n == 0 { 277 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error E", hex.EncodeToString(pl)) 278 c.DoS("CmpctBlkErrE") 279 return 280 } 281 col.Txs[idx] = pl[offs : offs+n] 282 offs += n 283 exp = int(idx) + 1 284 } 285 286 // calculate K0 and K1 params for siphash-4-2 287 sha := sha256.New() 288 sha.Write(pl[:88]) 289 kks := sha.Sum(nil) 290 col.K0 = binary.LittleEndian.Uint64(kks[0:8]) 291 col.K1 = binary.LittleEndian.Uint64(kks[8:16]) 292 293 var cnt_found int 294 295 TxMutex.Lock() 296 297 for _, v := range TransactionsToSend { 298 var hash2take *btc.Uint256 299 if c.Node.SendCmpctVer == 2 { 300 hash2take = v.Tx.WTxID() 301 } else { 302 hash2take = &v.Tx.Hash 303 } 304 sid := siphash.Hash(col.K0, col.K1, hash2take.Hash[:]) & 0xffffffffffff 305 if ptr, ok := shortids[sid]; ok { 306 if ptr != nil { 307 common.CountSafe("ShortIDSame") 308 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "Same short ID - abort") 309 return 310 } 311 shortids[sid] = v.Raw 312 cnt_found++ 313 } 314 } 315 316 for _, v := range TransactionsRejected { 317 if v.Tx == nil { 318 continue 319 } 320 var hash2take *btc.Uint256 321 if c.Node.SendCmpctVer == 2 { 322 hash2take = v.WTxID() 323 } else { 324 hash2take = &v.Hash 325 } 326 sid := siphash.Hash(col.K0, col.K1, hash2take.Hash[:]) & 0xffffffffffff 327 if ptr, ok := shortids[sid]; ok { 328 if ptr != nil { 329 common.CountSafe("ShortIDSame") 330 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "Same short ID - abort") 331 return 332 } 333 shortids[sid] = v.Raw 334 cnt_found++ 335 common.CountSafe(fmt.Sprint("CmpctBlkUseRej-", v.Reason)) 336 } 337 } 338 339 var msg *bytes.Buffer 340 341 missing := len(shortids) - cnt_found 342 //fmt.Println(c.ConnID, c.Node.SendCmpctVer, "ShortIDs", cnt_found, "/", shortidscnt, " Prefilled", prefilledcnt, " Missing", missing, " MemPool:", len(TransactionsToSend)) 343 col.Missing = missing 344 if missing > 0 { 345 msg = new(bytes.Buffer) 346 msg.Write(b2g.Block.Hash.Hash[:]) 347 btc.WriteVlen(msg, uint64(missing)) 348 exp = 0 349 col.Sid2idx = make(map[uint64]int, missing) 350 } 351 for n = 0; n < len(col.Txs); n++ { 352 switch col.Txs[n].(type) { 353 case []byte: // prefilled transaction 354 355 default: 356 sid := ShortIDToU64(pl[shortidx_idx : shortidx_idx+6]) 357 if ptr, ok := shortids[sid]; ok { 358 if ptr != nil { 359 col.Txs[n] = ptr 360 } else { 361 col.Txs[n] = sid 362 col.Sid2idx[sid] = n 363 if missing > 0 { 364 btc.WriteVlen(msg, uint64(n-exp)) 365 exp = n + 1 366 } 367 } 368 } else { 369 panic(fmt.Sprint("Tx idx ", n, " is missing - this should not happen!!!")) 370 } 371 shortidx_idx += 6 372 } 373 } 374 TxMutex.Unlock() 375 376 if missing == 0 { 377 //sta := time.Now() 378 b2g.Block.UpdateContent(col.Assemble()) 379 //sto := time.Now() 380 bidx := b2g.Block.Hash.BIdx() 381 er := common.BlockChain.PostCheckBlock(b2g.Block) 382 if er != nil { 383 println(c.ConnID, "Corrupt CmpctBlkA") 384 ioutil.WriteFile(b2g.Hash.String()+".bin", b2g.Block.Raw, 0700) 385 386 if b2g.Block.MerkleRootMatch() { 387 println("It was a wrongly mined one - clean it up") 388 DelB2G(bidx) //remove it from BlocksToGet 389 if b2g.BlockTreeNode == LastCommitedHeader { 390 LastCommitedHeader = LastCommitedHeader.Parent 391 } 392 common.BlockChain.DeleteBranch(b2g.BlockTreeNode, delB2G_callback) 393 } 394 395 //c.DoS("BadCmpctBlockA") 396 return 397 } 398 //fmt.Println(c.ConnID, "Instatnt PostCheckBlock OK #", b2g.Block.Height, sto.Sub(sta), time.Now().Sub(sta)) 399 c.Mutex.Lock() 400 c.cntInc("NewCBlock") 401 c.blocksreceived = append(c.blocksreceived, time.Now()) 402 c.Mutex.Unlock() 403 orb := &OneReceivedBlock{TmStart: b2g.Started, TmPreproc: time.Now(), FromConID: c.ConnID, DoInvs: b2g.SendInvs} 404 ReceivedBlocks[bidx] = orb 405 DelB2G(bidx) //remove it from BlocksToGet if no more pending downloads 406 if c.X.Authorized { 407 b2g.Block.Trusted.Set() 408 } 409 NetBlocks <- &BlockRcvd{Conn: c, Block: b2g.Block, BlockTreeNode: b2g.BlockTreeNode, OneReceivedBlock: orb} 410 } else { 411 if b2g.TmPreproc.IsZero() { // do not overwrite TmPreproc if already set 412 b2g.TmPreproc = time.Now() 413 } 414 b2g.InProgress++ 415 c.Mutex.Lock() 416 c.GetBlockInProgress[b2g.Block.Hash.BIdx()] = &oneBlockDl{hash: b2g.Block.Hash, start: time.Now(), col: col, SentAtPingCnt: c.X.PingSentCnt} 417 c.Mutex.Unlock() 418 c.SendRawMsg("getblocktxn", msg.Bytes()) 419 //fmt.Println(c.ConnID, "Send getblocktxn for", col.Missing, "/", shortidscnt, "missing txs. ", msg.Len(), "bytes") 420 } 421 } 422 423 func (c *OneConnection) ProcessBlockTxn(pl []byte) { 424 if len(pl) < 33 { 425 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn error A", hex.EncodeToString(pl)) 426 c.DoS("BlkTxnErrLen") 427 return 428 } 429 hash := btc.NewUint256(pl[:32]) 430 le, n := btc.VLen(pl[32:]) 431 if n == 0 || le < 0 || n > 3 { 432 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn error B", hex.EncodeToString(pl)) 433 c.DoS("BlkTxnErrCnt") 434 return 435 } 436 MutexRcv.Lock() 437 defer MutexRcv.Unlock() 438 439 idx := hash.BIdx() 440 441 c.Mutex.Lock() 442 bip := c.GetBlockInProgress[idx] 443 if bip == nil { 444 //println(time.Now().Format("2006-01-02 15:04:05"), c.ConnID, "BlkTxnNoBIP:", c.PeerAddr.Ip(), c.Node.Agent, hash.String()) 445 c.Mutex.Unlock() 446 common.CountSafe("UnxpBlockTxnA") 447 c.cntInc("BlkTxnNoBIP") 448 c.Misbehave("BlkTxnErrBip", 100) 449 return 450 } 451 col := bip.col 452 if col == nil { 453 c.Mutex.Unlock() 454 println("BlkTxnNoCOL:", c.PeerAddr.Ip(), c.Node.Agent, hash.String()) 455 common.CountSafe("UnxpBlockTxnB") 456 c.cntInc("BlkTxnNoCOL") 457 c.Misbehave("BlkTxnNoCOL", 100) 458 return 459 } 460 delete(c.GetBlockInProgress, idx) 461 c.Mutex.Unlock() 462 463 // the blocks seems to be fine 464 if rb, got := ReceivedBlocks[idx]; got { 465 rb.Cnt++ 466 common.CountSafe("BlkTxnSameRcvd") 467 //fmt.Println(c.ConnID, "BlkTxn size", len(pl), "for", hash.String()[48:],"- already have") 468 return 469 } 470 471 b2g := BlocksToGet[idx] 472 if b2g == nil { 473 // This may happen if we received this block already and it was invalid 474 println("BlockTxn: Block isn't in BlocksToGet anymore", hash.String()) 475 common.CountSafe("BlkTxnNoB2G") 476 return 477 } 478 //b2g.InProgress-- 479 480 //fmt.Println(c.ConnID, "BlockTxn size", len(pl), "-", le, "new txs for block #", b2g.Block.Height) 481 482 offs := 32 + n 483 for offs < len(pl) { 484 n = btc.TxSize(pl[offs:]) 485 if n == 0 { 486 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn corrupt TX") 487 c.DoS("BlkTxnErrTx") 488 return 489 } 490 raw_tx := pl[offs : offs+n] 491 var tx_hash btc.Uint256 492 tx_hash.Calc(raw_tx) 493 if common.GetBool(&common.CFG.TXPool.Debug) { 494 if f, _ := os.OpenFile("missing_txs.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660); f != nil { 495 _tx, _ := btc.NewTx(raw_tx) 496 _tx.SetHash(raw_tx) 497 fmt.Fprintf(f, "%s: Tx %s was missing in bock %d\n", 498 time.Now().Format("2006-01-02 15:04:05"), _tx.Hash.String(), b2g.Block.Height) 499 f.Close() 500 } 501 } 502 offs += n 503 504 sid := siphash.Hash(col.K0, col.K1, tx_hash.Hash[:]) & 0xffffffffffff 505 if idx, ok := col.Sid2idx[sid]; ok { 506 col.Txs[idx] = raw_tx 507 } else { 508 common.CountSafe("ShortIDUnknown") 509 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "blocktxn TX (short) ID unknown") 510 return 511 } 512 } 513 514 //println(c.ConnID, "Received the rest of compact block version", c.Node.SendCmpctVer) 515 516 //sta := time.Now() 517 b2g.Block.UpdateContent(col.Assemble()) 518 //sto := time.Now() 519 er := common.BlockChain.PostCheckBlock(b2g.Block) 520 if er != nil { 521 println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "Corrupt CmpctBlkB") 522 //c.DoS("BadCmpctBlockB") 523 ioutil.WriteFile(b2g.Hash.String()+".bin", b2g.Block.Raw, 0700) 524 525 if b2g.Block.MerkleRootMatch() { 526 println("It was a wrongly mined one - clean it up") 527 DelB2G(idx) //remove it from BlocksToGet 528 if b2g.BlockTreeNode == LastCommitedHeader { 529 LastCommitedHeader = LastCommitedHeader.Parent 530 } 531 common.BlockChain.DeleteBranch(b2g.BlockTreeNode, delB2G_callback) 532 } 533 534 return 535 } 536 DelB2G(idx) 537 //fmt.Println(c.ConnID, "PostCheckBlock OK #", b2g.Block.Height, sto.Sub(sta), time.Now().Sub(sta)) 538 c.Mutex.Lock() 539 c.cntInc("NewTBlock") 540 c.blocksreceived = append(c.blocksreceived, time.Now()) 541 c.Mutex.Unlock() 542 orb := &OneReceivedBlock{TmStart: b2g.Started, TmPreproc: b2g.TmPreproc, 543 TmDownload: c.LastMsgTime, TxMissing: col.Missing, FromConID: c.ConnID, DoInvs: b2g.SendInvs} 544 ReceivedBlocks[idx] = orb 545 if c.X.Authorized { 546 b2g.Block.Trusted.Set() 547 } 548 NetBlocks <- &BlockRcvd{Conn: c, Block: b2g.Block, BlockTreeNode: b2g.BlockTreeNode, OneReceivedBlock: orb} 549 }