func retry_cached_blocks() bool { if len(network.CachedBlocks) == 0 { return false } accepted_cnt := 0 for k, v := range network.CachedBlocks { common.Busy("Cache.CheckBlock " + v.Block.Hash.String()) e, dos, maybelater := common.BlockChain.CheckBlock(v.Block) if e == nil { common.Busy("Cache.AcceptBlock " + v.Block.Hash.String()) e := LocalAcceptBlock(v.Block, v.Conn) if e == nil { //fmt.Println("*** Old block accepted", common.BlockChain.BlockTreeEnd.Height) common.CountSafe("BlocksFromCache") delete(network.CachedBlocks, k) accepted_cnt++ break // One at a time should be enough } else { fmt.Println("retry AcceptBlock:", e.Error()) v.Conn.DoS("BadCachedBlock1") delete(network.CachedBlocks, k) } } else { if !maybelater { fmt.Println("retry CheckBlock:", e.Error()) common.CountSafe("BadCachedBlocks") if dos { v.Conn.DoS("BadCachedBlock2") } delete(network.CachedBlocks, k) } } } return accepted_cnt > 0 && len(network.CachedBlocks) > 0 }
// Parese network's "addr" message func (c *OneConnection) ParseAddr(pl []byte) { b := bytes.NewBuffer(pl) cnt, _ := btc.ReadVLen(b) for i := 0; i < int(cnt); i++ { var buf [30]byte n, e := b.Read(buf[:]) if n != len(buf) || e != nil { common.CountSafe("AddrError") c.DoS("AddrError") //println("ParseAddr:", n, e) break } a := peersdb.NewPeer(buf[:]) if !sys.ValidIp4(a.Ip4[:]) { c.Misbehave("AddrLocal", 2) } else if time.Unix(int64(a.Time), 0).Before(time.Now().Add(time.Minute)) { if time.Now().Before(time.Unix(int64(a.Time), 0).Add(peersdb.ExpirePeerAfter)) { k := qdb.KeyType(a.UniqID()) v := peersdb.PeerDB.Get(k) if v != nil { a.Banned = peersdb.NewPeer(v[:]).Banned } peersdb.PeerDB.Put(k, a.Bytes()) } else { common.CountSafe("AddrStale") } } else { c.Misbehave("AddrFuture", 5) } } }
// This function is called from a net conn thread func netBlockReceived(conn *OneConnection, b []byte) { bl, e := btc.NewBlock(b) if e != nil { conn.DoS("BrokenBlock") println("NewBlock:", e.Error()) return } idx := bl.Hash.BIdx() MutexRcv.Lock() if rb, got := ReceivedBlocks[idx]; got { rb.Cnt++ MutexRcv.Unlock() common.CountSafe("BlockSameRcvd") return } orb := &OneReceivedBlock{Time: time.Now()} if bip, ok := conn.GetBlockInProgress[idx]; ok { orb.TmDownload = orb.Time.Sub(bip.start) conn.Mutex.Lock() delete(conn.GetBlockInProgress, idx) conn.Mutex.Unlock() } else { common.CountSafe("UnxpectedBlockRcvd") } ReceivedBlocks[idx] = orb MutexRcv.Unlock() NetBlocks <- &BlockRcvd{Conn: conn, Block: bl} }
// Handle incoming "tx" msg func (c *OneConnection) ParseTxNet(pl []byte) { if uint32(len(pl)) > atomic.LoadUint32(&common.CFG.TXPool.MaxTxSize) { common.CountSafe("TxRejectedBig") return } tx, le := btc.NewTx(pl) if tx == nil { c.DoS("TxRejectedBroken") return } if le != len(pl) { c.DoS("TxRejectedLenMismatch") return } if len(tx.TxIn) < 1 { c.Misbehave("TxRejectedNoInputs", 100) return } tx.SetHash(pl) NeedThisTx(tx.Hash, func() { // This body is called with a locked TxMutex tx.Raw = pl select { case NetTxs <- &TxRcvd{conn: c, tx: tx, raw: pl}: TransactionsPending[tx.Hash.BIdx()] = true default: common.CountSafe("TxRejectedFullQ") //println("NetTxsFULL") } }) }
// Handle incoming "tx" msg func (c *OneConnection) ParseTxNet(pl []byte) { tid := btc.NewSha2Hash(pl) NeedThisTx(tid, func() { // This body is called with a locked TxMutex if uint32(len(pl)) > atomic.LoadUint32(&common.CFG.TXPool.MaxTxSize) { common.CountSafe("TxTooBig") RejectTx(tid, len(pl), TX_REJECTED_TOO_BIG) return } tx, le := btc.NewTx(pl) if tx == nil { RejectTx(tid, len(pl), TX_REJECTED_FORMAT) c.DoS("TxBroken") return } if le != len(pl) { RejectTx(tid, len(pl), TX_REJECTED_LEN_MISMATCH) c.DoS("TxLenMismatch") return } if len(tx.TxIn) < 1 { RejectTx(tid, len(pl), TX_REJECTED_EMPTY_INPUT) c.DoS("TxNoInputs") return } tx.Hash = tid select { case NetTxs <- &TxRcvd{conn: c, tx: tx, raw: pl}: TransactionsPending[tid.BIdx()] = true default: common.CountSafe("NetTxsFULL") } }) }
// This function is called from the main thread (or from an UI) func NetRouteInv(typ uint32, h *btc.Uint256, fromConn *OneConnection) (cnt uint) { common.CountSafe(fmt.Sprint("NetRouteInv", typ)) // Prepare the inv inv := new([36]byte) binary.LittleEndian.PutUint32(inv[0:4], typ) copy(inv[4:36], h.Bytes()) // Append it to PendingInvs in each open connection Mutex_net.Lock() for _, v := range OpenCons { if v != fromConn { // except the one that this inv came from v.Mutex.Lock() if v.Node.DoNotRelayTxs && typ == 1 { // This node does not want tx inv (it came with its version message) common.CountSafe("SendInvNoTxNode") } else { if fromConn == nil && v.InvsRecieved == 0 { // Do not broadcast own txs to nodes that never sent any invs to us common.CountSafe("SendInvOwnBlocked") } else if len(v.PendingInvs) < 500 { v.PendingInvs = append(v.PendingInvs, inv) cnt++ } else { common.CountSafe("SendInvIgnored") } } v.Mutex.Unlock() } } Mutex_net.Unlock() return }
func ExpirePeers() { peerdb_mutex.Lock() var delcnt uint32 now := time.Now() todel := make([]qdb.KeyType, PeerDB.Count()) PeerDB.Browse(func(k qdb.KeyType, v []byte) uint32 { ptim := binary.LittleEndian.Uint32(v[0:4]) if now.After(time.Unix(int64(ptim), 0).Add(ExpirePeerAfter)) { todel[delcnt] = k // we cannot call Del() from here delcnt++ } return 0 }) if delcnt > 0 { common.CountSafeAdd("PeersExpired", uint64(delcnt)) for delcnt > 0 { delcnt-- PeerDB.Del(todel[delcnt]) } common.CountSafe("PeerDefragsDone") PeerDB.Defrag() } else { common.CountSafe("PeerDefragsNone") } peerdb_mutex.Unlock() }
// Called from the blockchain thread func HandleNetBlock(newbl *network.BlockRcvd) { if CheckParentDiscarded(newbl.BlockTreeNode) { common.CountSafe("DiscardFreshBlockA") retryCachedBlocks = len(network.CachedBlocks) > 0 return } if !common.BlockChain.HasAllParents(newbl.BlockTreeNode) { // it's not linking - keep it for later network.CachedBlocks = append(network.CachedBlocks, newbl) common.CountSafe("BlockPostone") return } common.Busy("LocalAcceptBlock " + newbl.Hash.String()) e := LocalAcceptBlock(newbl) if e != nil { common.CountSafe("DiscardFreshBlockB") fmt.Println("AcceptBlock:", e.Error()) newbl.Conn.DoS("LocalAcceptBl") } else { //println("block", newbl.Block.Height, "accepted") retryCachedBlocks = retry_cached_blocks() } }
// Parese network's "addr" message func ParseAddr(pl []byte) { b := bytes.NewBuffer(pl) cnt, _ := btc.ReadVLen(b) for i := 0; i < int(cnt); i++ { var buf [30]byte n, e := b.Read(buf[:]) if n != len(buf) || e != nil { common.CountSafe("AddrError") //println("ParseAddr:", n, e) break } a := NewPeer(buf[:]) if !ValidIp4(a.Ip4[:]) { common.CountSafe("AddrInvalid") } else if time.Unix(int64(a.Time), 0).Before(time.Now().Add(time.Minute)) { if time.Now().Before(time.Unix(int64(a.Time), 0).Add(ExpirePeerAfter)) { k := qdb.KeyType(a.UniqID()) v := PeerDB.Get(k) if v != nil { a.Banned = NewPeer(v[:]).Banned } PeerDB.Put(k, a.Bytes()) } else { common.CountSafe("AddrStale") } } else { common.CountSafe("AddrInFuture") } } }
func retry_cached_blocks() bool { var idx int common.CountSafe("RedoCachedBlks") for idx < len(network.CachedBlocks) { newbl := network.CachedBlocks[idx] if CheckParentDiscarded(newbl.BlockTreeNode) { common.CountSafe("DiscardCachedBlock") network.CachedBlocks = append(network.CachedBlocks[:idx], network.CachedBlocks[idx+1:]...) return len(network.CachedBlocks) > 0 } if common.BlockChain.HasAllParents(newbl.BlockTreeNode) { common.Busy("Cache.LocalAcceptBlock " + newbl.Block.Hash.String()) e := LocalAcceptBlock(newbl) if e != nil { fmt.Println("AcceptBlock:", e.Error()) newbl.Conn.DoS("LocalAcceptBl") } if usif.Exit_now { return false } // remove it from cache network.CachedBlocks = append(network.CachedBlocks[:idx], network.CachedBlocks[idx+1:]...) return len(network.CachedBlocks) > 0 } else { idx++ } } return false }
func TxMined(h *btc.Uint256) { TxMutex.Lock() if rec, ok := TransactionsToSend[h.Hash]; ok { common.CountSafe("TxMinedToSend") for i := range rec.Spent { delete(SpentOutputs, rec.Spent[i]) } delete(TransactionsToSend, h.Hash) } if _, ok := TransactionsRejected[h.BIdx()]; ok { common.CountSafe("TxMinedRejected") deleteRejected(h.BIdx()) } if _, ok := TransactionsPending[h.Hash]; ok { common.CountSafe("TxMinedPending") delete(TransactionsPending, h.Hash) } wtg := WaitingForInputs[h.BIdx()] TxMutex.Unlock() // Try to redo waiting txs if wtg != nil { common.CountSafe("TxMinedGotInput") RetryWaitingForInput(wtg) } }
func (c *OneConnection) Misbehave(why string, how_much int) { common.CountSafe("Bad" + why) c.Mutex.Lock() c.misbehave += how_much if c.misbehave >= 100 { common.CountSafe("BanMisbehave") c.banit = true c.broken = true } c.Mutex.Unlock() }
func txChecker(h *btc.Uint256) bool { TxMutex.Lock() rec, ok := TransactionsToSend[h.BIdx()] TxMutex.Unlock() if ok && rec.Own != 0 { return false // Assume own txs as non-trusted } if ok { common.CountSafe("TxScrBoosted") } else { common.CountSafe("TxScrMissed") } return ok }
// This function is called from the main thread (or from an UI) func NetRouteInvExt(typ uint32, h *btc.Uint256, fromConn *OneConnection, fee_spkb uint64) (cnt uint) { common.CountSafe(fmt.Sprint("NetRouteInv", typ)) // Prepare the inv inv := new([36]byte) binary.LittleEndian.PutUint32(inv[0:4], typ) copy(inv[4:36], h.Bytes()) // Append it to PendingInvs in each open connection Mutex_net.Lock() for _, v := range OpenCons { if v != fromConn { // except the one that this inv came from send_inv := true v.Mutex.Lock() if typ == MSG_TX { if v.Node.DoNotRelayTxs { send_inv = false common.CountSafe("SendInvNoTxNode") } else if v.X.MinFeeSPKB > 0 && uint64(v.X.MinFeeSPKB) > fee_spkb { send_inv = false common.CountSafe("SendInvFeeTooLow") } /* This is to prevent sending own txs to "spying" peers: else if fromConn==nil && v.X.InvsRecieved==0 { send_inv = false common.CountSafe("SendInvOwnBlocked") } */ } if send_inv { if len(v.PendingInvs) < 500 { if typ, ok := v.InvDone.Map[hash2invid(inv[4:36])]; ok { common.CountSafe(fmt.Sprint("SendInvSame-", typ)) } else { v.PendingInvs = append(v.PendingInvs, inv) cnt++ } } else { common.CountSafe("SendInvFull") } } v.Mutex.Unlock() } } Mutex_net.Unlock() return }
func (c *OneConnection) HandleVersion(pl []byte) error { if len(pl) >= 80 /*Up to, includiong, the nonce */ { c.Mutex.Lock() c.Node.Version = binary.LittleEndian.Uint32(pl[0:4]) if bytes.Equal(pl[72:80], nonce[:]) { c.Mutex.Unlock() return errors.New("Connecting to ourselves") } if c.Node.Version < MIN_PROTO_VERSION { c.Mutex.Unlock() return errors.New("Client version too low") } c.Node.Services = binary.LittleEndian.Uint64(pl[4:12]) c.Node.Timestamp = binary.LittleEndian.Uint64(pl[12:20]) c.Node.ReportedIp4 = binary.BigEndian.Uint32(pl[40:44]) if len(pl) >= 86 { le, of := btc.VLen(pl[80:]) of += 80 c.Node.Agent = string(pl[of : of+le]) of += le if len(pl) >= of+4 { c.Node.Height = binary.LittleEndian.Uint32(pl[of : of+4]) c.X.GetBlocksDataNow = true of += 4 if len(pl) > of && pl[of] == 0 { c.Node.DoNotRelayTxs = true } } } c.Mutex.Unlock() if sys.ValidIp4(pl[40:44]) { ExternalIpMutex.Lock() _, use_this_ip := ExternalIp4[c.Node.ReportedIp4] if !use_this_ip { // New IP use_this_ip = true for x, v := range IgnoreExternalIpFrom { if c.Node.Agent == v { use_this_ip = false common.CountSafe(fmt.Sprint("IgnoreExtIP", x)) break } } if use_this_ip { fmt.Printf("New external IP %d.%d.%d.%d from %s\n> ", pl[40], pl[41], pl[42], pl[43], c.Node.Agent) } } if use_this_ip { ExternalIp4[c.Node.ReportedIp4] = [2]uint{ExternalIp4[c.Node.ReportedIp4][0] + 1, uint(time.Now().Unix())} } ExternalIpMutex.Unlock() } } else { return errors.New("version message too short") } c.SendRawMsg("verack", []byte{}) return nil }
func (c *OneConnection) SendPendingData() bool { if c.SendBufProd != c.SendBufCons { bytes_to_send := c.SendBufProd - c.SendBufCons if bytes_to_send < 0 { bytes_to_send += SendBufSize } if c.SendBufCons+bytes_to_send > SendBufSize { bytes_to_send = SendBufSize - c.SendBufCons } n, e := common.SockWrite(c.Conn, c.sendBuf[c.SendBufCons:c.SendBufCons+bytes_to_send]) if n > 0 { c.Mutex.Lock() c.X.LastSent = time.Now() c.X.BytesSent += uint64(n) n += c.SendBufCons if n >= SendBufSize { c.SendBufCons = 0 } else { c.SendBufCons = n } c.Mutex.Unlock() } else if time.Now().After(c.X.LastSent.Add(AnySendTimeout)) { common.CountSafe("PeerSendTimeout") c.Disconnect() } else if e != nil { if common.DebugLevel > 0 { println(c.PeerAddr.Ip(), "Connection Broken during send") } c.Disconnect() } } return c.SendBufProd != c.SendBufCons }
func (c *OneConnection) TryPing() bool { if common.PingPeerEvery == 0 { return false // pinging disabled in global config } if c.Node.Version <= 60000 { return false // insufficient protocol version } if time.Now().Before(c.LastPingSent.Add(common.PingPeerEvery)) { return false // not yet... } if c.PingInProgress != nil { if common.DebugLevel > 0 { println(c.PeerAddr.Ip(), "ping timeout") } common.CountSafe("PingTimeout") c.HandlePong() // this will set PingInProgress to nil } c.PingInProgress = make([]byte, 8) rand.Read(c.PingInProgress[:]) c.SendRawMsg("ping", c.PingInProgress) c.LastPingSent = time.Now() //println(c.PeerAddr.Ip(), "ping...") return true }
func (c *OneConnection) Misbehave(why string, how_much int) (res bool) { c.Mutex.Lock() if !c.banit { common.CountSafe("Bad" + why) c.misbehave += how_much if c.misbehave >= 1000 { common.CountSafe("BanMisbehave") res = true c.banit = true c.broken = true //print("Ban " + c.PeerAddr.Ip() + " (" + c.Node.Agent + ") because " + why + "\n> ") } } c.Mutex.Unlock() return }
// This function should be called only when OutConsActive >= MaxOutCons func drop_slowest_peer() { var worst_ping int var worst_conn *OneConnection Mutex_net.Lock() for _, v := range OpenCons { if v.Incoming && InConsActive < atomic.LoadUint32(&common.CFG.Net.MaxInCons) { // If this is an incoming connection, but we are not full yet, ignore it continue } v.Mutex.Lock() ap := v.GetAveragePing() v.Mutex.Unlock() if ap > worst_ping { worst_ping = ap worst_conn = v } } if worst_conn != nil { if common.DebugLevel > 0 { println("Droping slowest peer", worst_conn.PeerAddr.Ip(), "/", worst_ping, "ms") } worst_conn.Disconnect() common.CountSafe("PeersDropped") } Mutex_net.Unlock() }
func RetryWaitingForInput(wtg *OneWaitingList) { for k, t := range wtg.Ids { pendtxrcv := TransactionsRejected[k].Wait4Input.TxRcvd if HandleNetTx(pendtxrcv, true) { common.CountSafe("TxRetryAccepted") if common.DebugLevel > 0 { fmt.Println(pendtxrcv.tx.Hash.String(), "accepted after", time.Now().Sub(t).String()) } } else { common.CountSafe("TxRetryRejected") if common.DebugLevel > 0 { fmt.Println(pendtxrcv.tx.Hash.String(), "still rejected", TransactionsRejected[k].Reason) } } } }
func (c *OneConnection) DoS() { common.CountSafe("BannedNodes") c.Mutex.Lock() c.banit = true c.broken = true c.Mutex.Unlock() }
// Called from the blockchain thread func HandleNetBlock(newbl *network.BlockRcvd) { common.CountSafe("HandleNetBlock") bl := newbl.Block common.Busy("CheckBlock " + bl.Hash.String()) e, dos, maybelater := common.BlockChain.CheckBlock(bl) if e != nil { if maybelater { network.AddBlockToCache(bl, newbl.Conn) } else { fmt.Println(dos, e.Error()) if dos { newbl.Conn.DoS("CheckBlock") } } } else { common.Busy("LocalAcceptBlock " + bl.Hash.String()) e = LocalAcceptBlock(bl, newbl.Conn) if e == nil { retryCachedBlocks = retry_cached_blocks() } else { fmt.Println("AcceptBlock:", e.Error()) newbl.Conn.DoS("LocalAcceptBl") } } }
func EC_Verify(k, s, h []byte) bool { res := newec.Verify(k, s, h) if !res { common.CountSafe("ECVerifyFail") } return res }
func (c *OneConnection) DoS(why string) { common.CountSafe("Ban" + why) c.Mutex.Lock() c.banit = true c.broken = true c.Mutex.Unlock() }
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()) common.CountSafe("GetBlksBadPayload") c.DoS() 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 }
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 }
func (c *OneConnection) DoS(why string) { common.CountSafe("Ban" + why) c.Mutex.Lock() c.banit = true c.broken = true //print("BAN " + c.PeerAddr.Ip() + " (" + c.Node.Agent + ") because " + why + "\n> ") c.Mutex.Unlock() }
// Expire cached blocks func ExpireCachedBlocks() { for k, v := range CachedBlocks { if v.Time.Add(ExpireCachedAfter).Before(time.Now()) { delete(CachedBlocks, k) common.CountSafe("BlockExpired") } } }
// Called from network threads func blockWanted(h []byte) (yes bool) { idx := btc.NewUint256(h).BIdx() MutexRcv.Lock() _, ok := ReceivedBlocks[idx] MutexRcv.Unlock() if !ok { if atomic.LoadUint32(&common.CFG.Net.MaxBlockAtOnce) == 0 || !blocksLimitReached(idx) { yes = true common.CountSafe("BlockWanted") } else { common.CountSafe("BlockInProgress") } } else { common.CountSafe("BlockUnwanted") } return }
func isRoutable(rec *OneTxToSend) bool { if !common.CFG.TXRoute.Enabled { common.CountSafe("TxRouteDisabled") rec.Blocked = TX_REJECTED_DISABLED return false } if uint32(len(rec.Data)) > atomic.LoadUint32(&common.CFG.TXRoute.MaxTxSize) { common.CountSafe("TxRouteTooBig") rec.Blocked = TX_REJECTED_TOO_BIG return false } if rec.Fee < (uint64(rec.VSize()) * atomic.LoadUint64(&common.CFG.TXRoute.FeePerByte)) { common.CountSafe("TxRouteLowFee") rec.Blocked = TX_REJECTED_LOW_FEE return false } return true }