// 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 := peersdb.NewPeer(buf[:]) if !sys.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(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 { common.CountSafe("AddrInFuture") } } }
// 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") } }) }
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 }
// 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} }
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 }
// 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 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(why string) { common.CountSafe("Ban" + why) c.Mutex.Lock() c.banit = true c.broken = true c.Mutex.Unlock() }
// 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() }
// 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 (c *OneConnection) SendRawMsg(cmd string, pl []byte) (e error) { c.Mutex.Lock() if c.Send.Buf != nil { // Before adding more data to the buffer, check the limit if len(c.Send.Buf) > MaxSendBufferSize { c.Mutex.Unlock() if common.DebugLevel > 0 { println(c.PeerAddr.Ip(), "Peer Send Buffer Overflow") } c.Disconnect() common.CountSafe("PeerSendOverflow") return errors.New("Send buffer overflow") } } else { c.Send.LastSent = time.Now() } common.CountSafe("sent_" + cmd) common.CountSafeAdd("sbts_"+cmd, uint64(len(pl))) sbuf := make([]byte, 24+len(pl)) c.LastCmdSent = cmd c.LastBtsSent = uint32(len(pl)) binary.LittleEndian.PutUint32(sbuf[0:4], common.Version) copy(sbuf[0:4], common.Magic[:]) copy(sbuf[4:16], cmd) binary.LittleEndian.PutUint32(sbuf[16:20], uint32(len(pl))) sh := btc.Sha2Sum(pl[:]) copy(sbuf[20:24], sh[:4]) copy(sbuf[24:], pl) c.Send.Buf = append(c.Send.Buf, sbuf...) if common.DebugLevel < 0 { fmt.Println(cmd, len(c.Send.Buf), "->", c.PeerAddr.Ip()) } c.Mutex.Unlock() //println(len(c.Send.Buf), "queued for seding to", c.PeerAddr.Ip()) 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(len(rec.Data)) * atomic.LoadUint64(&common.CFG.TXRoute.FeePerByte)) { common.CountSafe("TxRouteLowFee") rec.Blocked = TX_REJECTED_LOW_FEE return false } if rec.Minout < atomic.LoadUint64(&common.CFG.TXRoute.MinVoutValue) { common.CountSafe("TxRouteDust") rec.Blocked = TX_REJECTED_DUST return false } return true }
// 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() if cnt == 0 { NetAlerts <- "WARNING: your tx has not been broadcasted to any peer" } return }
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, uint64(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 }
// This one shall only be called from the chain thread (this no protection) func AddBlockToCache(bl *btc.Block, conn *OneConnection) { // we use CachedBlocks only from one therad so no need for a mutex if len(CachedBlocks) == common.MaxCachedBlocks { // Remove the oldest one oldest := time.Now() var todel [btc.Uint256IdxLen]byte for k, v := range CachedBlocks { if v.Time.Before(oldest) { oldest = v.Time todel = k } } delete(CachedBlocks, todel) common.CountSafe("BlockCacheFull") } CachedBlocks[bl.Hash.BIdx()] = OneCachedBlock{Time: time.Now(), Block: bl, Conn: conn} }
func (c *OneConnection) ProcessInv(pl []byte) { if len(pl) < 37 { println(c.PeerAddr.Ip(), "inv payload too short", len(pl)) 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 }
// This function is called for each tx mined in a new block func TxMined(tx *btc.Tx) { h := tx.Hash TxMutex.Lock() if rec, ok := TransactionsToSend[h.BIdx()]; ok { common.CountSafe("TxMinedToSend") deleteToSend(rec) } if _, ok := TransactionsRejected[h.BIdx()]; ok { common.CountSafe("TxMinedRejected") deleteRejected(h.BIdx()) } if _, ok := TransactionsPending[h.BIdx()]; ok { common.CountSafe("TxMinedPending") delete(TransactionsPending, h.BIdx()) } // Go through all the inputs and make sure we are not leaving them in SpentOutputs for i := range tx.TxIn { idx := tx.TxIn[i].Input.UIdx() if val, ok := SpentOutputs[idx]; ok { if rec, _ := TransactionsToSend[val]; rec != nil { if rec.Own != 0 { common.CountSafe("TxMinedMalleabled") NetAlerts <- fmt.Sprint("Input from own ", rec.Tx.Hash.String(), " mined in ", tx.Hash.String()) } else { common.CountSafe("TxMinedOtherSpend") } deleteToSend(rec) } else { common.CountSafe("TxMinedSpentERROR") NetAlerts <- fmt.Sprint("WTF? Input from ", rec.Tx.Hash.String(), " in mem-spent, but tx not in the mem-pool") } delete(SpentOutputs, idx) } } wtg := WaitingForInputs[h.BIdx()] TxMutex.Unlock() // Try to redo waiting txs if wtg != nil { common.CountSafe("TxMinedGotInput") RetryWaitingForInput(wtg) } }
// Return false if we do not want to receive a data for this tx func NeedThisTx(id *btc.Uint256, cb func()) (res bool) { TxMutex.Lock() if _, present := TransactionsToSend[id.BIdx()]; present { //res = false } else if _, present := TransactionsRejected[id.BIdx()]; present { //res = false } else if _, present := TransactionsPending[id.BIdx()]; present { //res = false } else if txo, _ := common.BlockChain.Unspent.UnspentGet(&btc.TxPrevOut{Hash: id.Hash}); txo != nil { // This assumes that tx's out #0 has not been spent yet, which may not always be the case, but well... common.CountSafe("TxMinedRejected") } else { res = true if cb != nil { cb() } } TxMutex.Unlock() return }
func DoNetwork(ad *peersdb.PeerAddr) { var e error conn := NewConnection(ad) Mutex_net.Lock() if _, ok := OpenCons[ad.UniqID()]; ok { if common.DebugLevel > 0 { fmt.Println(ad.Ip(), "already connected") } common.CountSafe("ConnectingAgain") Mutex_net.Unlock() return } OpenCons[ad.UniqID()] = conn OutConsActive++ Mutex_net.Unlock() go func() { conn.NetConn, e = net.DialTimeout("tcp4", fmt.Sprintf("%d.%d.%d.%d:%d", ad.Ip4[0], ad.Ip4[1], ad.Ip4[2], ad.Ip4[3], ad.Port), TCPDialTimeout) if e == nil { conn.ConnectedAt = time.Now() if common.DebugLevel > 0 { println("Connected to", ad.Ip()) } conn.Run() } else { if common.DebugLevel > 0 { println("Could not connect to", ad.Ip()) } //println(e.Error()) } Mutex_net.Lock() delete(OpenCons, ad.UniqID()) OutConsActive-- Mutex_net.Unlock() ad.Dead() }() }
func BestExternalAddr() []byte { var best_ip, worst_ip uint32 var best_cnt, worst_tim uint ExternalIpMutex.Lock() if len(ExternalIp4) > 0 { for ip, rec := range ExternalIp4 { if worst_tim == 0 { worst_tim = rec[1] worst_ip = ip } if rec[0] > best_cnt { best_cnt = rec[0] best_ip = ip } else if rec[1] < worst_tim { worst_tim = rec[1] worst_ip = ip } } // Expire any extra IP if it has been stale for more than an hour if len(ExternalIp4) > 1 && uint(time.Now().Unix())-worst_tim > 3600 { common.CountSafe("ExternalIPExpire") delete(ExternalIp4, worst_ip) } } ExternalIpMutex.Unlock() res := make([]byte, 26) binary.LittleEndian.PutUint64(res[0:8], common.Services) // leave ip6 filled with zeros, except for the last 2 bytes: res[18], res[19] = 0xff, 0xff binary.BigEndian.PutUint32(res[20:24], best_ip) binary.BigEndian.PutUint16(res[24:26], common.DefaultTcpPort) return res }
func SendInvToRandomPeer(typ uint32, h *btc.Uint256) { common.CountSafe(fmt.Sprint("NetSendOneInv", 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 a random connection network.Mutex_net.Lock() idx := rand.Intn(len(network.OpenCons)) var cnt int for _, v := range network.OpenCons { if idx == cnt { v.Mutex.Lock() v.PendingInvs = append(v.PendingInvs, inv) v.Mutex.Unlock() break } cnt++ } network.Mutex_net.Unlock() return }
// Process that handles communication with a single peer func (c *OneConnection) Run() { c.SendVersion() c.Mutex.Lock() c.LastDataGot = time.Now() c.NextBlocksAsk = time.Now() // ask for blocks ASAP c.NextGetAddr = time.Now() // do getaddr ~10 seconds from now c.NextPing = time.Now().Add(5 * time.Second) // do first ping ~5 seconds from now c.Mutex.Unlock() for !c.IsBroken() { c.Mutex.Lock() c.LoopCnt++ c.Mutex.Unlock() cmd := c.FetchMessage() if c.IsBroken() { break } // Timeout ping in progress if c.PingInProgress != nil && time.Now().After(c.LastPingSent.Add(PingTimeout)) { if common.DebugLevel > 0 { println(c.PeerAddr.Ip(), "ping timeout") } common.CountSafe("PingTimeout") c.HandlePong() // this will set LastPingSent to nil } if cmd == nil { c.Tick() continue } c.Mutex.Lock() c.LastDataGot = time.Now() c.LastCmdRcvd = cmd.cmd c.LastBtsRcvd = uint32(len(cmd.pl)) c.Mutex.Unlock() c.PeerAddr.Alive() if common.DebugLevel < 0 { fmt.Println(c.PeerAddr.Ip(), "->", cmd.cmd, len(cmd.pl)) } if c.Send.Buf != nil && len(c.Send.Buf) > SendBufSizeHoldOn { common.CountSafe("hold_" + cmd.cmd) common.CountSafeAdd("hbts_"+cmd.cmd, uint64(len(cmd.pl))) continue } common.CountSafe("rcvd_" + cmd.cmd) common.CountSafeAdd("rbts_"+cmd.cmd, uint64(len(cmd.pl))) switch cmd.cmd { case "version": er := c.HandleVersion(cmd.pl) if er != nil { println("version:", er.Error()) c.Disconnect() } case "verack": c.VerackReceived = true if common.IsListenTCP() { c.SendOwnAddr() } case "inv": c.ProcessInv(cmd.pl) case "tx": if common.CFG.TXPool.Enabled { c.ParseTxNet(cmd.pl) } case "addr": ParseAddr(cmd.pl) case "block": //block received netBlockReceived(c, cmd.pl) case "getblocks": c.GetBlocks(cmd.pl) case "getdata": c.ProcessGetData(cmd.pl) case "getaddr": c.SendAddr() case "alert": c.HandleAlert(cmd.pl) case "ping": re := make([]byte, len(cmd.pl)) copy(re, cmd.pl) c.SendRawMsg("pong", re) case "pong": if c.PingInProgress == nil { common.CountSafe("PongUnexpected") } else if bytes.Equal(cmd.pl, c.PingInProgress) { c.HandlePong() } else { common.CountSafe("PongMismatch") } case "getheaders": c.GetHeaders(cmd.pl) case "notfound": common.CountSafe("NotFound") default: if common.DebugLevel > 0 { println(cmd.cmd, "from", c.PeerAddr.Ip()) } } } c.Mutex.Lock() ban := c.banit c.Mutex.Unlock() if ban { c.PeerAddr.Ban() common.CountSafe("PeersBanned") } else if c.Incoming { HammeringMutex.Lock() RecentlyDisconencted[c.PeerAddr.NetAddr.Ip4] = time.Now() HammeringMutex.Unlock() } if common.DebugLevel > 0 { println("Disconnected from", c.PeerAddr.Ip()) } c.NetConn.Close() }
// TCP server func tcp_server() { ad, e := net.ResolveTCPAddr("tcp4", fmt.Sprint("0.0.0.0:", common.DefaultTcpPort)) if e != nil { println("ResolveTCPAddr", e.Error()) return } lis, e := net.ListenTCP("tcp4", ad) if e != nil { println("ListenTCP", e.Error()) return } defer lis.Close() //fmt.Println("TCP server started at", ad.String()) for common.IsListenTCP() { common.CountSafe("NetServerLoops") Mutex_net.Lock() ica := InConsActive Mutex_net.Unlock() if ica < atomic.LoadUint32(&common.CFG.Net.MaxInCons) { lis.SetDeadline(time.Now().Add(time.Second)) tc, e := lis.AcceptTCP() if e == nil { var terminate bool if common.DebugLevel > 0 { fmt.Println("Incoming connection from", tc.RemoteAddr().String()) } ad, e := peersdb.NewPeerFromString(tc.RemoteAddr().String()) if e == nil { // Hammering protection HammeringMutex.Lock() ti, ok := RecentlyDisconencted[ad.NetAddr.Ip4] HammeringMutex.Unlock() if ok && time.Now().Sub(ti) < HammeringMinReconnect { //println(ad.Ip(), "is hammering within", time.Now().Sub(ti).String()) common.CountSafe("InConnHammer") ad.Ban() terminate = true } if !terminate { // Incoming IP passed all the initial checks - talk to it conn := NewConnection(ad) conn.ConnectedAt = time.Now() conn.Incoming = true conn.NetConn = tc Mutex_net.Lock() if _, ok := OpenCons[ad.UniqID()]; ok { //fmt.Println(ad.Ip(), "already connected") common.CountSafe("SameIpReconnect") Mutex_net.Unlock() terminate = true } else { OpenCons[ad.UniqID()] = conn InConsActive++ Mutex_net.Unlock() go func() { conn.Run() Mutex_net.Lock() delete(OpenCons, ad.UniqID()) InConsActive-- Mutex_net.Unlock() }() } } } else { if common.DebugLevel > 0 { println("NewPeerFromString:", e.Error()) } common.CountSafe("InConnRefused") terminate = true } // had any error occured - close teh TCP connection if terminate { tc.Close() } } } else { time.Sleep(1e9) } } Mutex_net.Lock() for _, c := range OpenCons { if c.Incoming { c.Disconnect() } } TCPServerStarted = false Mutex_net.Unlock() //fmt.Println("TCP server stopped") }
func (c *OneConnection) Tick() { c.Mutex.Lock() c.TicksCnt++ c.Mutex.Unlock() // Disconnect and ban useless peers (sych that don't send invs) if c.InvsRecieved == 0 && c.ConnectedAt.Add(15*time.Minute).Before(time.Now()) { c.DoS("PeerUseless") return } // Check no-data timeout if c.LastDataGot.Add(NoDataTimeout).Before(time.Now()) { c.Disconnect() common.CountSafe("NetNodataTout") if common.DebugLevel > 0 { println(c.PeerAddr.Ip(), "no data for", NoDataTimeout/time.Second, "seconds - disconnect") } return } if c.Send.Buf != nil { n, e := common.SockWrite(c.NetConn, c.Send.Buf) if n > 0 { c.Mutex.Lock() c.Send.LastSent = time.Now() c.BytesSent += uint64(n) if n >= len(c.Send.Buf) { c.Send.Buf = nil } else { tmp := make([]byte, len(c.Send.Buf)-n) copy(tmp, c.Send.Buf[n:]) c.Send.Buf = tmp } c.Mutex.Unlock() } else if time.Now().After(c.Send.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 } if !c.VerackReceived { // If we have no ack, do nothing more. return } // Ask node for new addresses...? if time.Now().After(c.NextGetAddr) { if peersdb.PeerDB.Count() > common.MaxPeersNeeded { // If we have a lot of peers, do not ask for more, to save bandwidth common.CountSafe("AddrEnough") } else { common.CountSafe("AddrWanted") c.SendRawMsg("getaddr", nil) } c.NextGetAddr = time.Now().Add(AskAddrsEvery) return } // Need to send some invs...? if c.SendInvs() { return } // Timeout getdata for blocks in progress, so the map does not grow to infinity for k, v := range c.GetBlockInProgress { if time.Now().After(v.start.Add(GetBlockTimeout)) { common.CountSafe("BlockGetTimeout") c.Mutex.Lock() delete(c.GetBlockInProgress, k) c.Mutex.Unlock() } } // Need to send getblocks...? if len(c.GetBlockInProgress) == 0 && c.getblocksNeeded() { return } // Ping if we dont do anything c.TryPing() }
func (c *OneConnection) FetchMessage() *BCmsg { var e error var n int for c.recv.hdr_len < 24 { n, e = common.SockRead(c.NetConn, c.recv.hdr[c.recv.hdr_len:24]) c.Mutex.Lock() c.recv.hdr_len += n if e != nil { c.Mutex.Unlock() c.HandleError(e) return nil } if c.recv.hdr_len >= 4 && !bytes.Equal(c.recv.hdr[:4], common.Magic[:]) { c.Mutex.Unlock() if common.DebugLevel > 0 { println("FetchMessage: Proto out of sync") } common.CountSafe("NetBadMagic") c.Disconnect() return nil } if c.broken { c.Mutex.Unlock() return nil } if c.recv.hdr_len >= 24 { c.recv.pl_len = binary.LittleEndian.Uint32(c.recv.hdr[16:20]) c.recv.cmd = strings.TrimRight(string(c.recv.hdr[4:16]), "\000") } c.Mutex.Unlock() } if c.recv.pl_len > 0 { if c.recv.dat == nil { msi := maxmsgsize(c.recv.cmd) if c.recv.pl_len > msi { //println(c.PeerAddr.Ip(), "Command", c.recv.cmd, "is going to be too big", c.recv.pl_len, msi) c.DoS("MsgTooBig") return nil } c.Mutex.Lock() c.recv.dat = make([]byte, c.recv.pl_len) c.recv.datlen = 0 c.Mutex.Unlock() } for c.recv.datlen < c.recv.pl_len { n, e = common.SockRead(c.NetConn, c.recv.dat[c.recv.datlen:]) if n > 0 { c.Mutex.Lock() c.recv.datlen += uint32(n) c.Mutex.Unlock() if c.recv.datlen > c.recv.pl_len { println(c.PeerAddr.Ip(), "is sending more of", c.recv.cmd, "then it should have", c.recv.datlen, c.recv.pl_len) c.DoS("MsgSizeMismatch") return nil } } if e != nil { c.HandleError(e) return nil } if c.broken { return nil } } } sh := btc.Sha2Sum(c.recv.dat) if !bytes.Equal(c.recv.hdr[20:24], sh[:4]) { //println(c.PeerAddr.Ip(), "Msg checksum error") c.DoS("MsgBadChksum") return nil } ret := new(BCmsg) ret.cmd = c.recv.cmd ret.pl = c.recv.dat c.Mutex.Lock() c.recv.dat = nil c.recv.hdr_len = 0 c.BytesReceived += uint64(24 + len(ret.pl)) c.Mutex.Unlock() return ret }
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, uint64(len(notfound)/36)) buf.Write(notfound) c.SendRawMsg("notfound", buf.Bytes()) } }
// Must be called from the chain's thread func HandleNetTx(ntx *TxRcvd, retry bool) (accepted bool) { common.CountSafe("HandleNetTx") tx := ntx.tx var totinp, totout uint64 var frommem bool TxMutex.Lock() if !retry { if _, present := TransactionsPending[tx.Hash.BIdx()]; !present { // It had to be mined in the meantime, so just drop it now TxMutex.Unlock() common.CountSafe("TxNotPending") return } delete(TransactionsPending, ntx.tx.Hash.BIdx()) } else { // In case case of retry, it is on the rejected list, // ... so remove it now to free any tied WaitingForInputs deleteRejected(tx.Hash.BIdx()) } pos := make([]*btc.TxOut, len(tx.TxIn)) spent := make([]uint64, len(tx.TxIn)) // Check if all the inputs exist in the chain for i := range tx.TxIn { spent[i] = tx.TxIn[i].Input.UIdx() if _, ok := SpentOutputs[spent[i]]; ok { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_DOUBLE_SPEND) TxMutex.Unlock() common.CountSafe("TxRejectedDoubleSpnd") return } inptx := btc.NewUint256(tx.TxIn[i].Input.Hash[:]) if txinmem, ok := TransactionsToSend[inptx.BIdx()]; common.CFG.TXPool.AllowMemInputs && ok { if int(tx.TxIn[i].Input.Vout) >= len(txinmem.TxOut) { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_BAD_INPUT) TxMutex.Unlock() common.CountSafe("TxRejectedBadInput") return } pos[i] = txinmem.TxOut[tx.TxIn[i].Input.Vout] common.CountSafe("TxInputInMemory") frommem = true } else { pos[i], _ = common.BlockChain.Unspent.UnspentGet(&tx.TxIn[i].Input) if pos[i] == nil { var newone bool if !common.CFG.TXPool.AllowMemInputs { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_NOT_MINED) TxMutex.Unlock() common.CountSafe("TxRejectedMemInput") return } // In this case, let's "save" it for later... missingid := btc.NewUint256(tx.TxIn[i].Input.Hash[:]) nrtx := RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_NO_TXOU) if nrtx != nil { nrtx.Wait4Input = &Wait4Input{missingTx: missingid, TxRcvd: ntx} // Add to waiting list: var rec *OneWaitingList if rec, _ = WaitingForInputs[nrtx.Wait4Input.missingTx.BIdx()]; rec == nil { rec = new(OneWaitingList) rec.TxID = nrtx.Wait4Input.missingTx rec.Ids = make(map[[btc.Uint256IdxLen]byte]time.Time) newone = true } rec.Ids[tx.Hash.BIdx()] = time.Now() WaitingForInputs[nrtx.Wait4Input.missingTx.BIdx()] = rec } TxMutex.Unlock() if newone { common.CountSafe("TxRejectedNoInpNew") } else { common.CountSafe("TxRejectedNoInpOld") } return } } totinp += pos[i].Value } // Check if total output value does not exceed total input minout := uint64(btc.MAX_MONEY) for i := range tx.TxOut { if tx.TxOut[i].Value < atomic.LoadUint64(&common.CFG.TXPool.MinVoutValue) { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_DUST) TxMutex.Unlock() common.CountSafe("TxRejectedDust") return } if tx.TxOut[i].Value < minout { minout = tx.TxOut[i].Value } totout += tx.TxOut[i].Value } if totout > totinp { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_OVERSPEND) TxMutex.Unlock() ntx.conn.DoS("TxOverspend") return } // Check for a proper fee fee := totinp - totout if fee < (uint64(len(ntx.raw)) * atomic.LoadUint64(&common.CFG.TXPool.FeePerByte)) { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_LOW_FEE) TxMutex.Unlock() common.CountSafe("TxRejectedLowFee") return } // Verify scripts for i := range tx.TxIn { if !script.VerifyTxScript(tx.TxIn[i].ScriptSig, pos[i].Pk_script, i, tx, true) { RejectTx(ntx.tx.Hash, len(ntx.raw), TX_REJECTED_SCRIPT_FAIL) TxMutex.Unlock() ntx.conn.DoS("TxScriptFail") return } } rec := &OneTxToSend{Data: ntx.raw, Spent: spent, Volume: totinp, Fee: fee, Firstseen: time.Now(), Tx: tx, Minout: minout} TransactionsToSend[tx.Hash.BIdx()] = rec for i := range spent { SpentOutputs[spent[i]] = tx.Hash.BIdx() } wtg := WaitingForInputs[tx.Hash.BIdx()] if wtg != nil { defer RetryWaitingForInput(wtg) // Redo waiting txs when leaving this function } TxMutex.Unlock() common.CountSafe("TxAccepted") if frommem { // Gocoin does not route txs that need unconfirmed inputs rec.Blocked = TX_REJECTED_NOT_MINED common.CountSafe("TxRouteNotMined") } else if isRoutable(rec) { rec.Invsentcnt += NetRouteInv(1, tx.Hash, ntx.conn) common.CountSafe("TxRouteOK") } accepted = true return }
func main() { var ptr *byte if unsafe.Sizeof(ptr) < 8 { fmt.Println("WARNING: Gocoin client shall be build for 64-bit arch. It will likely crash now.") } fmt.Println("Gocoin client version", lib.Version) runtime.GOMAXPROCS(runtime.NumCPU()) // It seems that Go does not do it by default // Disable Ctrl+C signal.Notify(killchan, os.Interrupt, os.Kill) defer func() { if r := recover(); r != nil { err, ok := r.(error) if !ok { err = fmt.Errorf("pkg: %v", r) } fmt.Println("main panic recovered:", err.Error()) fmt.Println(string(debug.Stack())) network.NetCloseAll() common.CloseBlockChain() peersdb.ClosePeerDB() sys.UnlockDatabaseDir() os.Exit(1) } }() common.InitConfig() host_init() // This will create the DB lock file and keep it open peersTick := time.Tick(5 * time.Minute) txPoolTick := time.Tick(time.Minute) netTick := time.Tick(time.Second) peersdb.Testnet = common.Testnet peersdb.ConnectOnly = common.CFG.ConnectOnly peersdb.Services = common.Services peersdb.InitPeers(common.GocoinHomeDir) common.Last.Block = common.BlockChain.BlockTreeEnd common.Last.Time = time.Unix(int64(common.Last.Block.Timestamp()), 0) if common.Last.Time.After(time.Now()) { common.Last.Time = time.Now() } for k, v := range common.BlockChain.BlockIndex { network.ReceivedBlocks[k] = &network.OneReceivedBlock{Time: time.Unix(int64(v.Timestamp()), 0)} } if common.CFG.TextUI.Enabled { go textui.MainThread() } if common.CFG.WebUI.Interface != "" { fmt.Println("Starting WebUI at", common.CFG.WebUI.Interface, "...") go webui.ServerThread(common.CFG.WebUI.Interface) } for !usif.Exit_now { common.CountSafe("MainThreadLoops") for retryCachedBlocks { retryCachedBlocks = retry_cached_blocks() // We have done one per loop - now do something else if pending... if len(network.NetBlocks) > 0 || len(usif.UiChannel) > 0 { break } } common.Busy("") select { case s := <-killchan: fmt.Println("Got signal:", s) usif.Exit_now = true continue case newbl := <-network.NetBlocks: common.CountSafe("MainNetBlock") HandleNetBlock(newbl) case newtx := <-network.NetTxs: common.CountSafe("MainNetTx") network.HandleNetTx(newtx, false) case newal := <-network.NetAlerts: common.CountSafe("MainNetAlert") fmt.Println("\007" + newal) textui.ShowPrompt() case <-netTick: common.CountSafe("MainNetTick") network.NetworkTick() case cmd := <-usif.UiChannel: common.CountSafe("MainUICmd") common.Busy("UI command") cmd.Handler(cmd.Param) cmd.Done.Done() continue case <-peersTick: peersdb.ExpirePeers() case <-txPoolTick: network.ExpireTxs() case <-time.After(time.Second / 2): common.CountSafe("MainThreadTouts") if !retryCachedBlocks { common.Busy("common.BlockChain.Idle()") if common.BlockChain.Idle() { common.CountSafe("ChainIdleUsed") } } continue } } network.NetCloseAll() peersdb.ClosePeerDB() if usif.DefragBlocksDB != 0 { defrag_db() } common.CloseBlockChain() sys.UnlockDatabaseDir() }