func (c *OneConnection) SendInvs() (res bool) { b_txs := new(bytes.Buffer) b_blk := new(bytes.Buffer) var c_blk []*btc.Uint256 c.Mutex.Lock() if len(c.PendingInvs) > 0 { for i := range c.PendingInvs { var inv_sent_otherwise bool typ := binary.LittleEndian.Uint32((*c.PendingInvs[i])[:4]) c.InvStore(typ, (*c.PendingInvs[i])[4:36]) if typ == MSG_BLOCK { if c.Node.SendCmpctVer >= 1 && c.Node.HighBandwidth { c_blk = append(c_blk, btc.NewUint256((*c.PendingInvs[i])[4:])) inv_sent_otherwise = true } else if c.Node.SendHeaders { // convert block inv to block header common.BlockChain.BlockIndexAccess.Lock() bl := common.BlockChain.BlockIndex[btc.NewUint256((*c.PendingInvs[i])[4:]).BIdx()] if bl != nil { b_blk.Write(bl.BlockHeader[:]) b_blk.Write([]byte{0}) // 0 txs } common.BlockChain.BlockIndexAccess.Unlock() inv_sent_otherwise = true } } if !inv_sent_otherwise { b_txs.Write((*c.PendingInvs[i])[:]) } } res = true } c.PendingInvs = nil c.Mutex.Unlock() if len(c_blk) > 0 { for _, h := range c_blk { c.SendCmpctBlk(h) } } if b_blk.Len() > 0 { common.CountSafe("InvSentAsHeader") b := new(bytes.Buffer) btc.WriteVlen(b, uint64(b_blk.Len()/81)) c.SendRawMsg("headers", append(b.Bytes(), b_blk.Bytes()...)) //println("sent block's header(s)", b_blk.Len(), uint64(b_blk.Len()/81)) } if b_txs.Len() > 0 { b := new(bytes.Buffer) btc.WriteVlen(b, uint64(b_txs.Len()/36)) c.SendRawMsg("inv", append(b.Bytes(), b_txs.Bytes()...)) } return }
// LTC signing uses different seed string func HashFromMessage(msg []byte, out []byte) { const MessageMagic = "Litecoin Signed Message:\n" b := new(bytes.Buffer) btc.WriteVlen(b, uint32(len(MessageMagic))) b.Write([]byte(MessageMagic)) btc.WriteVlen(b, uint32(len(msg))) b.Write(msg) btc.ShaHash(b.Bytes(), out) }
func (c *OneConnection) SendCmpctBlk(hash *btc.Uint256) { crec := GetchBlockForBIP152(hash) if crec == nil { fmt.Println(c.ConnID, "cmpctblock not sent for", hash.String()) return } k0 := binary.LittleEndian.Uint64(crec.BIP152[8:16]) k1 := binary.LittleEndian.Uint64(crec.BIP152[16:24]) msg := new(bytes.Buffer) msg.Write(crec.Data[:80]) msg.Write(crec.BIP152[:8]) btc.WriteVlen(msg, uint64(len(crec.Block.Txs)-1)) // all except coinbase for i := 1; i < len(crec.Block.Txs); i++ { var lsb [8]byte var hasz *btc.Uint256 if c.Node.SendCmpctVer == 2 { hasz = crec.Block.Txs[i].WTxID() } else { hasz = crec.Block.Txs[i].Hash } binary.LittleEndian.PutUint64(lsb[:], siphash.Hash(k0, k1, hasz.Hash[:])) msg.Write(lsb[:6]) } msg.Write([]byte{1}) // one preffiled tx msg.Write([]byte{0}) // coinbase - index 0 if c.Node.SendCmpctVer == 2 { msg.Write(crec.Block.Txs[0].Raw) // coinbase - index 0 } else { crec.Block.Txs[0].WriteSerialized(msg) // coinbase - index 0 } c.SendRawMsg("cmpctblock", msg.Bytes()) }
func main() { if len(os.Args) != 5 { fmt.Println("This tool needs to be executed with 4 arguments:") fmt.Println(" 1) Name of the unsigned transaction file") fmt.Println(" 2) Input index to add the key & signature to") fmt.Println(" 3) Hex dump of the canonical signature") fmt.Println(" 4) Hex dump of the public key") return } tx := raw_tx_from_file(os.Args[1]) if tx == nil { return } in, er := strconv.ParseUint(os.Args[2], 10, 32) if er != nil { println("Input index:", er.Error()) return } if int(in) >= len(tx.TxIn) { println("Input index too big:", int(in), "/", len(tx.TxIn)) return } sig, er := hex.DecodeString(os.Args[3]) if er != nil { println("Signature:", er.Error()) return } pk, er := hex.DecodeString(os.Args[4]) if er != nil { println("Public key:", er.Error()) return } buf := new(bytes.Buffer) btc.WriteVlen(buf, uint64(len(sig))) buf.Write(sig) btc.WriteVlen(buf, uint64(len(pk))) buf.Write(pk) tx.TxIn[in].ScriptSig = buf.Bytes() write_tx_file(tx) }
func (c *OneConnection) SendOwnAddr() { if ExternalAddrLen() > 0 { buf := new(bytes.Buffer) btc.WriteVlen(buf, uint64(1)) binary.Write(buf, binary.LittleEndian, uint32(time.Now().Unix())) buf.Write(BestExternalAddr()) c.SendRawMsg("addr", buf.Bytes()) } }
func (col *CmpctBlockCollector) Assemble() []byte { bdat := new(bytes.Buffer) bdat.Write(col.Header) btc.WriteVlen(bdat, uint64(len(col.Txs))) for _, txd := range col.Txs { bdat.Write(txd.([]byte)) } return bdat.Bytes() }
func (c *OneConnection) ProcessGetBlockTxn(pl []byte) { if len(pl) < 34 { println(c.ConnID, "GetBlockTxnShort") c.DoS("GetBlockTxnShort") return } hash := btc.NewUint256(pl[:32]) crec := GetchBlockForBIP152(hash) if crec == nil { fmt.Println(c.ConnID, "GetBlockTxn aborting for", hash.String()) return } req := bytes.NewReader(pl[32:]) indexes_length, _ := btc.ReadVLen(req) if indexes_length == 0 { println(c.ConnID, "GetBlockTxnEmpty") c.DoS("GetBlockTxnEmpty") return } var exp_idx uint64 msg := new(bytes.Buffer) msg.Write(hash.Hash[:]) btc.WriteVlen(msg, indexes_length) for { idx, er := btc.ReadVLen(req) if er != nil { println(c.ConnID, "GetBlockTxnERR") c.DoS("GetBlockTxnERR") return } idx += exp_idx if int(idx) >= len(crec.Block.Txs) { println(c.ConnID, "GetBlockTxnIdx+") c.DoS("GetBlockTxnIdx+") return } if c.Node.SendCmpctVer == 2 { msg.Write(crec.Block.Txs[idx].Raw) // coinbase - index 0 } else { crec.Block.Txs[idx].WriteSerialized(msg) // coinbase - index 0 } if indexes_length == 1 { break } indexes_length-- exp_idx = idx + 1 } c.SendRawMsg("blocktxn", msg.Bytes()) }
func (c *OneConnection) SendAddr() { pers := peersdb.GetBestPeers(MaxAddrsPerMessage, nil) if len(pers) > 0 { buf := new(bytes.Buffer) btc.WriteVlen(buf, uint64(len(pers))) for i := range pers { binary.Write(buf, binary.LittleEndian, pers[i].Time) buf.Write(pers[i].NetAddr.Bytes()) } c.SendRawMsg("addr", buf.Bytes()) } }
func (c *OneConnection) GetBlocks(pl []byte) { h2get, hashstop, e := parseLocatorsPayload(pl) if e != nil || len(h2get) < 1 || hashstop == nil { println("GetBlocks: error parsing payload from", c.PeerAddr.Ip()) c.DoS("BadGetBlks") return } invs := make(map[[32]byte]bool, 500) for i := range h2get { common.BlockChain.BlockIndexAccess.Lock() if bl, ok := common.BlockChain.BlockIndex[h2get[i].BIdx()]; ok { // make sure that this block is in our main chain common.Last.Mutex.Lock() end := common.Last.Block common.Last.Mutex.Unlock() for ; end != nil && end.Height >= bl.Height; end = end.Parent { if end == bl { addInvBlockBranch(invs, bl, hashstop) // Yes - this is the main chain if common.DebugLevel > 0 { fmt.Println(c.PeerAddr.Ip(), "getblocks from", bl.Height, "stop at", hashstop.String(), "->", len(invs), "invs") } if len(invs) > 0 { common.BlockChain.BlockIndexAccess.Unlock() inv := new(bytes.Buffer) btc.WriteVlen(inv, uint32(len(invs))) for k, _ := range invs { binary.Write(inv, binary.LittleEndian, uint32(2)) inv.Write(k[:]) } c.SendRawMsg("inv", inv.Bytes()) return } } } } common.BlockChain.BlockIndexAccess.Unlock() } common.CountSafe("GetblksMissed") return }
// Handle getheaders protocol command // https://en.bitcoin.it/wiki/Protocol_specification#getheaders func (c *OneConnection) GetHeaders(pl []byte) { h2get, hashstop, e := parseLocatorsPayload(pl) if e != nil || hashstop == nil { println("GetHeaders: error parsing payload from", c.PeerAddr.Ip()) c.DoS("BadGetHdrs") return } if common.DebugLevel > 1 { println("GetHeaders", len(h2get), hashstop.String()) } var best_block, last_block *chain.BlockTreeNode common.BlockChain.BlockIndexAccess.Lock() if len(h2get) > 0 { for i := range h2get { if bl, ok := common.BlockChain.BlockIndex[h2get[i].BIdx()]; ok { if best_block == nil || bl.Height > best_block.Height { best_block = bl } } } } else { best_block = common.BlockChain.BlockIndex[hashstop.BIdx()] } last_block = common.BlockChain.BlockTreeEnd common.BlockChain.BlockIndexAccess.Unlock() var resp []byte var cnt uint32 for cnt < 2000 { best_block = best_block.FindPathTo(last_block) if best_block == nil { break } resp = append(resp, append(best_block.BlockHeader[:], 0)...) // 81st byte is always zero cnt++ } out := new(bytes.Buffer) btc.WriteVlen(out, cnt) out.Write(resp) c.SendRawMsg("headers", out.Bytes()) return }
func (c *OneConnection) SendInvs() (res bool) { b := new(bytes.Buffer) c.Mutex.Lock() if len(c.PendingInvs) > 0 { btc.WriteVlen(b, uint32(len(c.PendingInvs))) for i := range c.PendingInvs { b.Write((*c.PendingInvs[i])[:]) } res = true } c.PendingInvs = nil c.Mutex.Unlock() if res { c.SendRawMsg("inv", b.Bytes()) } return }
func (c *OneConnection) ProcessInv(pl []byte) { if len(pl) < 37 { //println(c.PeerAddr.Ip(), "inv payload too short", len(pl)) c.DoS("InvEmpty") return } c.InvsRecieved++ cnt, of := btc.VLen(pl) if len(pl) != of+36*cnt { println("inv payload length mismatch", len(pl), of, cnt) } var blinv2ask []byte for i := 0; i < cnt; i++ { typ := binary.LittleEndian.Uint32(pl[of : of+4]) common.CountSafe(fmt.Sprint("InvGot", typ)) if typ == 2 { if blockWanted(pl[of+4 : of+36]) { blinv2ask = append(blinv2ask, pl[of+4:of+36]...) } } else if typ == 1 { if common.CFG.TXPool.Enabled { c.TxInvNotify(pl[of+4 : of+36]) } } of += 36 } if len(blinv2ask) > 0 { bu := new(bytes.Buffer) btc.WriteVlen(bu, uint64(len(blinv2ask)/32)) for i := 0; i < len(blinv2ask); i += 32 { bh := btc.NewUint256(blinv2ask[i : i+32]) c.Mutex.Lock() c.GetBlockInProgress[bh.BIdx()] = &oneBlockDl{hash: bh, start: time.Now()} c.Mutex.Unlock() binary.Write(bu, binary.LittleEndian, uint32(2)) bu.Write(bh.Hash[:]) } c.SendRawMsg("getdata", bu.Bytes()) } return }
func (c *one_net_conn) ping_idle() { c.ping.Lock() if c.ping.inProgress { if time.Now().After(c.ping.timeSent.Add(PING_TIMEOUT)) { c.store_ping_result() c.ping.Unlock() //fmt.Println(c.peerip, "ping timeout", c.ping.seq) } else { c.ping.Unlock() time.Sleep(time.Millisecond) } } else if c.ping.now { //fmt.Println("ping", c.peerip, c.ping.seq) c.ping.inProgress = true c.ping.timeSent = time.Now() c.ping.now = false if false { rand.Read(c.ping.pattern[:]) c.ping.Unlock() c.sendmsg("ping", c.ping.pattern[:]) } else { b := new(bytes.Buffer) btc.WriteVlen(b, PING_FETCH_BLOCKS) BlocksMutex.Lock() for i := uint32(1); ; i++ { binary.Write(b, binary.LittleEndian, uint32(2)) btg := BlocksToGet[i] b.Write(btg[:]) if i == PING_FETCH_BLOCKS { c.ping.lastBlock = btc.NewUint256(btg[:]) break } } BlocksMutex.Unlock() c.ping.bytes = 0 c.ping.Unlock() c.sendmsg("getdata", b.Bytes()) //fmt.Println("ping sent", c.ping.lastBlock.String()) } } else { c.ping.Unlock() time.Sleep(10 * time.Millisecond) } }
func (c *OneConnection) sendGetHeaders() { MutexRcv.Lock() lb := LastCommitedHeader MutexRcv.Unlock() min_height := int(lb.Height) - chain.MovingCheckopintDepth if min_height < 0 { min_height = 0 } blks := new(bytes.Buffer) var cnt uint64 var step int step = 1 for cnt < 50 /*it shoudl never get that far, but just in case...*/ { blks.Write(lb.BlockHash.Hash[:]) cnt++ //println(" geth", cnt, "height", lb.Height, lb.BlockHash.String()) if int(lb.Height) <= min_height { break } for tmp := 0; tmp < step && lb != nil && int(lb.Height) > min_height; tmp++ { lb = lb.Parent } if lb == nil { break } if cnt >= 10 { step = step * 2 } } var null_stop [32]byte blks.Write(null_stop[:]) bhdr := new(bytes.Buffer) binary.Write(bhdr, binary.LittleEndian, common.Version) btc.WriteVlen(bhdr, cnt) c.SendRawMsg("getheaders", append(bhdr.Bytes(), blks.Bytes()...)) c.X.LastHeadersHeightAsk = lb.Height c.X.GetHeadersInProgress = true c.X.GetHeadersTimeout = time.Now().Add(NO_DATA_TIMEOUT) }
// Commit the given add/del transactions to UTXO and Wnwind DBs func (db *UnspentDB) CommitBlockTxs(changes *BlockChanges, blhash []byte) (e error) { undo_fn := fmt.Sprint(db.dir, changes.Height) if changes.UndoData != nil || (changes.Height%db.UnwindBufLen) == 0 { bu := new(bytes.Buffer) bu.Write(blhash) if changes.UndoData != nil { for _, xx := range changes.UndoData { bin := xx.Serialize(true) btc.WriteVlen(bu, uint64(len(bin))) bu.Write(bin) } } ioutil.WriteFile(db.dir+"tmp", bu.Bytes(), 0666) os.Rename(db.dir+"tmp", undo_fn+".tmp") } db.nosync() db.commit(changes) if changes.LastKnownHeight <= changes.Height { db.Sync() } os.Rename(undo_fn+".tmp", undo_fn) if db.LastBlockHash == nil { db.LastBlockHash = make([]byte, 32) } copy(db.LastBlockHash, blhash) db.LastBlockHeight = changes.Height if changes.Height > db.UnwindBufLen { os.Remove(fmt.Sprint(db.dir, changes.Height-db.UnwindBufLen)) } return }
// Handle getheaders protocol command // https://en.bitcoin.it/wiki/Protocol_specification#getheaders func (c *OneConnection) GetHeaders(pl []byte) { h2get, hashstop, e := parseLocatorsPayload(pl) if e != nil || hashstop == nil { println("GetHeaders: error parsing payload from", c.PeerAddr.Ip()) c.DoS("BadGetHdrs") return } if common.DebugLevel > 1 { println("GetHeaders", len(h2get), hashstop.String()) } var best_block, last_block *chain.BlockTreeNode //common.Last.Mutex.Lock() MutexRcv.Lock() last_block = LastCommitedHeader MutexRcv.Unlock() //common.Last.Mutex.Unlock() common.BlockChain.BlockIndexAccess.Lock() //println("GetHeaders", len(h2get), hashstop.String()) if len(h2get) > 0 { for i := range h2get { if bl, ok := common.BlockChain.BlockIndex[h2get[i].BIdx()]; ok { if best_block == nil || bl.Height > best_block.Height { //println(" ... bbl", i, bl.Height, bl.BlockHash.String()) best_block = bl } } } } else { best_block = common.BlockChain.BlockIndex[hashstop.BIdx()] } if best_block == nil { common.CountSafe("GetHeadersBadBlock") best_block = common.BlockChain.BlockTreeRoot } //best_bl_ch := len(best_block.Childs) //last_block = common.BlockChain.BlockTreeEnd var resp []byte var cnt uint32 defer func() { // If we get a hash of an old orphaned blocks, FindPathTo() will panic, so... if r := recover(); r != nil { common.CountSafe("GetHeadersOrphBlk") /* err, ok := r.(error) if !ok { err = fmt.Errorf("pkg: %v", r) } // This happens the you receive request for headers from an orphaned block fmt.Println("GetHeaders panic recovered:", err.Error()) fmt.Println("Cnt:", cnt, " len(h2get):", len(h2get)) if best_block!=nil { fmt.Println("BestBlock:", best_block.Height, best_block.BlockHash.String(), len(best_block.Childs), best_bl_ch) } if last_block!=nil { fmt.Println("LastBlock:", last_block.Height, last_block.BlockHash.String(), len(last_block.Childs)) } */ } common.BlockChain.BlockIndexAccess.Unlock() // send the response out := new(bytes.Buffer) btc.WriteVlen(out, uint64(cnt)) out.Write(resp) c.SendRawMsg("headers", out.Bytes()) }() for cnt < 2000 { if last_block.Height <= best_block.Height { break } best_block = best_block.FindPathTo(last_block) if best_block == nil { //println("FindPathTo failed", last_block.BlockHash.String(), cnt) //println("resp:", hex.EncodeToString(resp)) break } resp = append(resp, append(best_block.BlockHeader[:], 0)...) // 81st byte is always zero cnt++ } // Note: the deferred function will be called before exiting return }
func (c *one_net_conn) getnextblock() { var cnt, lensofar int b := new(bytes.Buffer) vl := new(bytes.Buffer) BlocksMutex.Lock() if BlocksComplete > BlocksIndex { fmt.Println("dupa", BlocksComplete, BlocksIndex) BlocksIndex = BlocksComplete } blocks_from := BlocksIndex avg_len := avg_block_size() max_block_forward := uint32((MemForBlocks - BlocksCachedSize) / uint(avg_len)) if max_block_forward < MIN_BLOCKS_AHEAD { max_block_forward = MIN_BLOCKS_AHEAD } else if max_block_forward > MAX_BLOCKS_AHEAD { max_block_forward = MAX_BLOCKS_AHEAD } if BlocksComplete+max_block_forward < blocks_from { COUNTER("BGAP") max_block_forward = blocks_from - BlocksComplete + 1 } var prot int for secondloop := false; cnt < 10e3 && lensofar < GETBLOCKS_BYTES_ONCE; secondloop = true { if prot == 20e3 { println("stuck in getnextblock()", BlocksIndex, blocks_from, max_block_forward, BlocksComplete, LastBlockHeight, _DoBlocks, secondloop) break } prot++ if secondloop && BlocksIndex == blocks_from { if BlocksComplete == LastBlockHeight { _DoBlocks = false } else { COUNTER("WRAP") time.Sleep(1e8) } break } BlocksIndex++ if BlocksIndex > BlocksComplete+max_block_forward || BlocksIndex > LastBlockHeight { //fmt.Println("wrap", BlocksIndex, BlocksComplete) BlocksIndex = BlocksComplete } if _, done := BlocksCached[BlocksIndex]; done { //fmt.Println(" cached ->", BlocksIndex) continue } bh, ok := BlocksToGet[BlocksIndex] if !ok { //fmt.Println(" toget ->", BlocksIndex) continue } cbip := BlocksInProgress[bh] if cbip == nil { cbip = &one_bip{Height: BlocksIndex, Count: 1} cbip.Conns = make(map[uint32]bool, MaxNetworkConns) } else { if cbip.Conns[c.id] { //fmt.Println(" cbip.Conns ->", c.id) continue } cbip.Count++ } cbip.Conns[c.id] = true c.inprogress++ BlocksInProgress[bh] = cbip b.Write([]byte{2, 0, 0, 0}) b.Write(bh[:]) cnt++ lensofar += avg_len } BlocksMutex.Unlock() btc.WriteVlen(vl, uint32(cnt)) c.sendmsg("getdata", append(vl.Bytes(), b.Bytes()...)) c.last_blk_rcvd = time.Now() }
func (c *OneConnection) ProcessGetData(pl []byte) { var notfound []byte //println(c.PeerAddr.Ip(), "getdata") b := bytes.NewReader(pl) cnt, e := btc.ReadVLen(b) if e != nil { println("ProcessGetData:", e.Error(), c.PeerAddr.Ip()) return } for i := 0; i < int(cnt); i++ { var typ uint32 var h [36]byte n, _ := b.Read(h[:]) if n != 36 { println("ProcessGetData: pl too short", c.PeerAddr.Ip()) return } typ = binary.LittleEndian.Uint32(h[:4]) c.Mutex.Lock() c.InvStore(typ, h[4:36]) c.Mutex.Unlock() common.CountSafe(fmt.Sprintf("GetdataType-%x", typ)) if typ == MSG_BLOCK || typ == MSG_WITNESS_BLOCK { crec, _, er := common.BlockChain.Blocks.BlockGetExt(btc.NewUint256(h[4:])) //bl, _, er := common.BlockChain.Blocks.BlockGet(btc.NewUint256(h[4:])) if er == nil { bl := crec.Data if typ == MSG_BLOCK { // remove witness data from the block if crec.Block == nil { crec.Block, _ = btc.NewBlock(bl) } if crec.Block.OldData == nil { crec.Block.BuildTxList() } //println("block size", len(crec.Data), "->", len(bl)) bl = crec.Block.OldData } c.SendRawMsg("block", bl) } else { notfound = append(notfound, h[:]...) } } else if typ == MSG_TX || typ == MSG_WITNESS_TX { // transaction TxMutex.Lock() if tx, ok := TransactionsToSend[btc.NewUint256(h[4:]).BIdx()]; ok && tx.Blocked == 0 { tx.SentCnt++ tx.Lastsent = time.Now() TxMutex.Unlock() if tx.SegWit == nil || typ == MSG_WITNESS_TX { c.SendRawMsg("tx", tx.Data) } else { c.SendRawMsg("tx", tx.Serialize()) } } else { TxMutex.Unlock() notfound = append(notfound, h[:]...) } } else if typ == MSG_CMPCT_BLOCK { c.SendCmpctBlk(btc.NewUint256(h[4:])) } else { if common.DebugLevel > 0 { println("getdata for type", typ, "not supported yet") } if typ > 0 && typ <= 3 /*3 is a filtered block(we dont support it)*/ { notfound = append(notfound, h[:]...) } } } if len(notfound) > 0 { buf := new(bytes.Buffer) btc.WriteVlen(buf, uint64(len(notfound)/36)) buf.Write(notfound) c.SendRawMsg("notfound", buf.Bytes()) } }
func (c *one_net_conn) getnextblock() { if len(BlockQueue)*avg_block_size() > MEM_CACHE { COUNTER("GDFU") time.Sleep(100 * time.Millisecond) return } var cnt int b := new(bytes.Buffer) vl := new(bytes.Buffer) avs := avg_block_size() blks_to_get := uint32(MEM_CACHE / avs) max_cnt_to_get := (MAX_GET_FROM_PEER / avs) + 1 if max_cnt_to_get > MAX_BLOCKS_AT_ONCE { max_cnt_to_get = MAX_BLOCKS_AT_ONCE } BlocksMutex.Lock() FetchBlocksTo = BlocksComplete + blks_to_get if FetchBlocksTo > LastBlockHeight { FetchBlocksTo = LastBlockHeight } bl_stage := uint32(0) for curblk := BlocksComplete; cnt < max_cnt_to_get; curblk++ { if curblk > FetchBlocksTo { if bl_stage == MAX_SAME_BLOCKS_AT_ONCE { break } bl_stage++ curblk = BlocksComplete } if _, done := BlocksCached[curblk]; done { continue } bh, ok := BlocksToGet[curblk] if !ok { continue } cbip := BlocksInProgress[bh] if cbip == nil { // if not in progress then we always take it cbip = &one_bip{Height: curblk} cbip.Conns = make(map[uint32]bool, MaxNetworkConns) } else if cbip.Count != bl_stage || cbip.Conns[c.id] { continue } if LastBlockAsked < curblk { LastBlockAsked = curblk } cbip.Count = bl_stage + 1 cbip.Conns[c.id] = true c.inprogress++ BlocksInProgress[bh] = cbip b.Write([]byte{2, 0, 0, 0}) b.Write(bh[:]) cnt++ } BlocksMutex.Unlock() if cnt > 0 { btc.WriteVlen(vl, uint64(cnt)) c.sendmsg("getdata", append(vl.Bytes(), b.Bytes()...)) COUNTER("GDYE") } else { COUNTER("GDNO") time.Sleep(100 * time.Millisecond) } c.Lock() c.last_blk_rcvd = time.Now() c.Unlock() }
func (c *OneConnection) ProcessGetData(pl []byte) { var notfound []byte //println(c.PeerAddr.Ip(), "getdata") b := bytes.NewReader(pl) cnt, e := btc.ReadVLen(b) if e != nil { println("ProcessGetData:", e.Error(), c.PeerAddr.Ip()) return } for i := 0; i < int(cnt); i++ { var typ uint32 var h [36]byte n, _ := b.Read(h[:]) if n != 36 { println("ProcessGetData: pl too short", c.PeerAddr.Ip()) return } typ = binary.LittleEndian.Uint32(h[:4]) common.CountSafe(fmt.Sprint("GetdataType", typ)) if typ == 2 { uh := btc.NewUint256(h[4:]) bl, _, er := common.BlockChain.Blocks.BlockGet(uh) if er == nil { c.SendRawMsg("block", bl) } else { notfound = append(notfound, h[:]...) } } else if typ == 1 { // transaction uh := btc.NewUint256(h[4:]) TxMutex.Lock() if tx, ok := TransactionsToSend[uh.BIdx()]; ok && tx.Blocked == 0 { tx.SentCnt++ tx.Lastsent = time.Now() TxMutex.Unlock() c.SendRawMsg("tx", tx.Data) } else { TxMutex.Unlock() notfound = append(notfound, h[:]...) } } else { if common.DebugLevel > 0 { println("getdata for type", typ, "not supported yet") } if typ > 0 && typ <= 3 /*3 is a filtered block(we dont support it)*/ { notfound = append(notfound, h[:]...) } } } if len(notfound) > 0 { buf := new(bytes.Buffer) btc.WriteVlen(buf, uint32(len(notfound)/36)) buf.Write(notfound) c.SendRawMsg("notfound", buf.Bytes()) } }
func (c *OneConnection) ProcessCmpctBlock(pl []byte) { if len(pl) < 90 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error A", hex.EncodeToString(pl)) c.DoS("CmpctBlkErrA") return } MutexRcv.Lock() defer MutexRcv.Unlock() var tmp_hdr [81]byte copy(tmp_hdr[:80], pl[:80]) sta, b2g := c.ProcessNewHeader(tmp_hdr[:]) // ProcessNewHeader() needs byte(0) after the header, // but don't try to change it to ProcessNewHeader(append(pl[:80], 0)) as it'd overwrite pl[80] if b2g == nil { /*fmt.Println(c.ConnID, "Don't process CompactBlk", btc.NewSha2Hash(pl[:80]), hex.EncodeToString(pl[80:88]), "->", sta)*/ common.CountSafe("CmpctBlockHdrNo") if sta == PH_STATUS_ERROR { c.ReceiveHeadersNow() // block doesn't connect so ask for the headers c.Misbehave("BadCmpct", 50) // do it 20 times and you are banned } else if sta == PH_STATUS_FATAL { c.DoS("BadCmpct") } return } /*fmt.Println() fmt.Println(c.ConnID, "Received CmpctBlock enf:", common.BlockChain.Consensus.Enforce_SEGWIT, " serwice:", (c.Node.Services&SERVICE_SEGWIT)!=0, " height:", b2g.Block.Height, " ver:", c.Node.SendCmpctVer) */ if common.BlockChain.Consensus.Enforce_SEGWIT != 0 && c.Node.SendCmpctVer < 2 { if b2g.Block.Height >= common.BlockChain.Consensus.Enforce_SEGWIT { common.CountSafe("CmpctBlockIgnore") println("Ignore compact block", b2g.Block.Height, "from non-segwit node", c.ConnID) if (c.Node.Services & SERVICE_SEGWIT) != 0 { // it only makes sense to ask this node for block's data, if it supports segwit c.X.GetBlocksDataNow = true } return } } /* var sta_s = []string{"???", "NEW", "FRESH", "OLD", "ERROR", "FATAL"} fmt.Println(c.ConnID, "Compact Block len", len(pl), "for", btc.NewSha2Hash(pl[:80]).String()[48:], "#", b2g.Block.Height, sta_s[sta], " inp:", b2g.InProgress) */ // if we got here, we shall download this block if c.Node.Height < b2g.Block.Height { c.Node.Height = b2g.Block.Height } if b2g.InProgress >= uint(common.CFG.Net.MaxBlockAtOnce) { common.CountSafe("CmpctBlockMaxInProg") //fmt.Println(c.ConnID, " - too many in progress") return } var n, idx, shortidscnt, shortidx_idx, prefilledcnt int col := new(CmpctBlockCollector) col.Header = b2g.Block.Raw[:80] offs := 88 shortidscnt, n = btc.VLen(pl[offs:]) if shortidscnt < 0 || n > 3 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error B", hex.EncodeToString(pl)) c.DoS("CmpctBlkErrB") return } offs += n shortidx_idx = offs shortids := make(map[uint64]*OneTxToSend, shortidscnt) for i := 0; i < int(shortidscnt); i++ { if len(pl[offs:offs+6]) < 6 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error B2", hex.EncodeToString(pl)) c.DoS("CmpctBlkErrB2") return } shortids[ShortIDToU64(pl[offs:offs+6])] = nil offs += 6 } prefilledcnt, n = btc.VLen(pl[offs:]) if prefilledcnt < 0 || n > 3 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error C", hex.EncodeToString(pl)) c.DoS("CmpctBlkErrC") return } offs += n col.Txs = make([]interface{}, prefilledcnt+shortidscnt) exp := 0 for i := 0; i < int(prefilledcnt); i++ { idx, n = btc.VLen(pl[offs:]) if idx < 0 || n > 3 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error D", hex.EncodeToString(pl)) c.DoS("CmpctBlkErrD") return } idx += exp offs += n n = btc.TxSize(pl[offs:]) if n == 0 { println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "cmpctblock error E", hex.EncodeToString(pl)) c.DoS("CmpctBlkErrE") return } col.Txs[idx] = pl[offs : offs+n] //fmt.Println(" prefilledtxn", i, idx, ":", hex.EncodeToString(pl[offs:offs+n])) //btc.NewSha2Hash(pl[offs:offs+n]).String()) offs += n exp = int(idx) + 1 } // calculate K0 and K1 params for siphash-4-2 sha := sha256.New() sha.Write(pl[:88]) kks := sha.Sum(nil) col.K0 = binary.LittleEndian.Uint64(kks[0:8]) col.K1 = binary.LittleEndian.Uint64(kks[8:16]) var cnt_found int TxMutex.Lock() for _, v := range TransactionsToSend { var hash2take *btc.Uint256 if c.Node.SendCmpctVer == 2 { hash2take = v.Tx.WTxID() } else { hash2take = v.Tx.Hash } sid := siphash.Hash(col.K0, col.K1, hash2take.Hash[:]) & 0xffffffffffff if ptr, ok := shortids[sid]; ok { if ptr != nil { common.CountSafe("ShortIDSame") println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "Same short ID - abort") return } shortids[sid] = v cnt_found++ } } var msg *bytes.Buffer missing := len(shortids) - cnt_found //fmt.Println(c.ConnID, c.Node.SendCmpctVer, "ShortIDs", cnt_found, "/", shortidscnt, " Prefilled", prefilledcnt, " Missing", missing, " MemPool:", len(TransactionsToSend)) col.Missing = missing if missing > 0 { msg = new(bytes.Buffer) msg.Write(b2g.Block.Hash.Hash[:]) btc.WriteVlen(msg, uint64(missing)) exp = 0 col.Sid2idx = make(map[uint64]int, missing) } for n = 0; n < len(col.Txs); n++ { switch col.Txs[n].(type) { case []byte: // prefilled transaction default: sid := ShortIDToU64(pl[shortidx_idx : shortidx_idx+6]) if t2s, ok := shortids[sid]; ok { if t2s != nil { col.Txs[n] = t2s.Data } else { col.Txs[n] = sid col.Sid2idx[sid] = n if missing > 0 { btc.WriteVlen(msg, uint64(n-exp)) exp = n + 1 } } } else { panic(fmt.Sprint("Tx idx ", n, " is missing - this should not happen!!!")) } shortidx_idx += 6 } } TxMutex.Unlock() if missing == 0 { //sta := time.Now() b2g.Block.UpdateContent(col.Assemble()) //sto := time.Now() er := common.BlockChain.PostCheckBlock(b2g.Block) if er != nil { println(c.ConnID, "Corrupt CmpctBlkA") ioutil.WriteFile(b2g.Hash.String()+".bin", b2g.Block.Raw, 0700) //c.DoS("BadCmpctBlockA") return } //fmt.Println(c.ConnID, "Instatnt PostCheckBlock OK #", b2g.Block.Height, sto.Sub(sta), time.Now().Sub(sta)) idx := b2g.Block.Hash.BIdx() c.Mutex.Lock() c.counters["NewCBlock"]++ c.blocksreceived = append(c.blocksreceived, time.Now()) c.Mutex.Unlock() orb := &OneReceivedBlock{TmStart: b2g.Started, TmPreproc: time.Now(), FromConID: c.ConnID} ReceivedBlocks[idx] = orb DelB2G(idx) //remove it from BlocksToGet if no more pending downloads NetBlocks <- &BlockRcvd{Conn: c, Block: b2g.Block, BlockTreeNode: b2g.BlockTreeNode, OneReceivedBlock: orb} } else { b2g.TmPreproc = time.Now() b2g.InProgress++ c.Mutex.Lock() c.GetBlockInProgress[b2g.Block.Hash.BIdx()] = &oneBlockDl{hash: b2g.Block.Hash, start: time.Now(), col: col} c.Mutex.Unlock() c.SendRawMsg("getblocktxn", msg.Bytes()) //fmt.Println(c.ConnID, "Send getblocktxn for", col.Missing, "/", shortidscnt, "missing txs. ", msg.Len(), "bytes") } }
func (c *OneConnection) GetBlockData() (yes bool) { //MAX_GETDATA_FORWARD // Need to send getdata...? MutexRcv.Lock() defer MutexRcv.Unlock() if LowestIndexToBlocksToGet == 0 || len(BlocksToGet) == 0 { c.IncCnt("FetchNoBlocksToGet", 1) // wake up in one minute, just in case c.nextGetData = time.Now().Add(60 * time.Second) return } cbip := c.BlksInProgress() if cbip >= MAX_PEERS_BLOCKS_IN_PROGRESS { c.IncCnt("FetchMaxCountInProgress", 1) // wake up in a few seconds, maybe some blocks will complete by then c.nextGetData = time.Now().Add(5 * time.Second) return } avg_block_size := int(common.GetAverageBlockSize()) block_data_in_progress := cbip * avg_block_size if block_data_in_progress+avg_block_size > MAX_GETDATA_FORWARD { c.IncCnt("FetchMaxBytesInProgress", 1) // wake up in a few seconds, maybe some blocks will complete by then c.nextGetData = time.Now().Add(5 * time.Second) // wait for some blocks to complete return } var cnt uint64 var block_type uint32 if (c.Node.Services & SERVICE_SEGWIT) != 0 { block_type = MSG_WITNESS_BLOCK } else { block_type = MSG_BLOCK } // We can issue getdata for this peer // Let's look for the lowest height block in BlocksToGet that isn't being downloaded yet common.Last.Mutex.Lock() max_height := common.Last.Block.Height + uint32(MAX_BLOCKS_FORWARD_SIZ/avg_block_size) if max_height > common.Last.Block.Height+MAX_BLOCKS_FORWARD_CNT { max_height = common.Last.Block.Height + MAX_BLOCKS_FORWARD_CNT } common.Last.Mutex.Unlock() if max_height > c.Node.Height { max_height = c.Node.Height } if max_height > LastCommitedHeader.Height { max_height = LastCommitedHeader.Height } if common.BlockChain.Consensus.Enforce_SEGWIT != 0 && (c.Node.Services&SERVICE_SEGWIT) == 0 { // no segwit node if max_height >= common.BlockChain.Consensus.Enforce_SEGWIT-1 { max_height = common.BlockChain.Consensus.Enforce_SEGWIT - 1 if max_height <= common.Last.Block.Height { c.IncCnt("FetchNoWitness", 1) c.nextGetData = time.Now().Add(3600 * time.Second) // never do getdata return } } } invs := new(bytes.Buffer) var cnt_in_progress uint for { var lowest_found *OneBlockToGet // Get block to fetch: for bh := LowestIndexToBlocksToGet; bh <= max_height; bh++ { if idxlst, ok := IndexToBlocksToGet[bh]; ok { for _, idx := range idxlst { v := BlocksToGet[idx] if v.InProgress == cnt_in_progress && (lowest_found == nil || v.Block.Height < lowest_found.Block.Height) { c.Mutex.Lock() if _, ok := c.GetBlockInProgress[idx]; !ok { lowest_found = v } c.Mutex.Unlock() } } } } if lowest_found == nil { cnt_in_progress++ if cnt_in_progress >= uint(common.CFG.Net.MaxBlockAtOnce) { break } continue } binary.Write(invs, binary.LittleEndian, block_type) invs.Write(lowest_found.BlockHash.Hash[:]) lowest_found.InProgress++ cnt++ c.Mutex.Lock() c.GetBlockInProgress[lowest_found.BlockHash.BIdx()] = &oneBlockDl{hash: lowest_found.BlockHash, start: time.Now()} cbip = len(c.GetBlockInProgress) c.Mutex.Unlock() if cbip >= MAX_PEERS_BLOCKS_IN_PROGRESS { break // no more than 2000 blocks in progress / peer } block_data_in_progress += avg_block_size if block_data_in_progress > MAX_GETDATA_FORWARD { break } } if cnt == 0 { //println(c.ConnID, "fetch nothing", cbip, block_data_in_progress, max_height-common.Last.Block.Height, cnt_in_progress) c.IncCnt("FetchNothing", 1) // wake up in a few seconds, maybe it will be different next time c.nextGetData = time.Now().Add(5 * time.Second) return } bu := new(bytes.Buffer) btc.WriteVlen(bu, uint64(cnt)) pl := append(bu.Bytes(), invs.Bytes()...) //println(c.ConnID, "fetching", cnt, "new blocks ->", cbip) c.SendRawMsg("getdata", pl) yes = true return }