github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/invs.go (about)

     1  package network
     2  
     3  import (
     4  	"fmt"
     5  	//"time"
     6  	"bytes"
     7  	"encoding/binary"
     8  
     9  	"github.com/piotrnar/gocoin/client/common"
    10  	"github.com/piotrnar/gocoin/lib/btc"
    11  	"github.com/piotrnar/gocoin/lib/chain"
    12  )
    13  
    14  const (
    15  	MSG_WITNESS_FLAG = 0x40000000
    16  
    17  	MSG_TX            = 1
    18  	MSG_BLOCK         = 2
    19  	MSG_CMPCT_BLOCK   = 4
    20  	MSG_WITNESS_TX    = MSG_TX | MSG_WITNESS_FLAG
    21  	MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG
    22  )
    23  
    24  func blockReceived(bh *btc.Uint256) (ok bool) {
    25  	MutexRcv.Lock()
    26  	_, ok = ReceivedBlocks[bh.BIdx()]
    27  	MutexRcv.Unlock()
    28  	return
    29  }
    30  
    31  func hash2invid(hash []byte) uint64 {
    32  	return binary.LittleEndian.Uint64(hash[4:12])
    33  }
    34  
    35  // Make sure c.Mutex is locked when calling it
    36  func (c *OneConnection) InvStore(typ uint32, hash []byte) {
    37  	inv_id := hash2invid(hash)
    38  	if len(c.InvDone.History) < MAX_INV_HISTORY {
    39  		c.InvDone.History = append(c.InvDone.History, inv_id)
    40  		c.InvDone.Map[inv_id] = typ
    41  		c.InvDone.Idx++
    42  		return
    43  	}
    44  	if c.InvDone.Idx == MAX_INV_HISTORY {
    45  		c.InvDone.Idx = 0
    46  	}
    47  	delete(c.InvDone.Map, c.InvDone.History[c.InvDone.Idx])
    48  	c.InvDone.History[c.InvDone.Idx] = inv_id
    49  	c.InvDone.Map[inv_id] = typ
    50  	c.InvDone.Idx++
    51  }
    52  
    53  func (c *OneConnection) ProcessInv(pl []byte) {
    54  	if len(pl) < 37 {
    55  		//println(c.PeerAddr.Ip(), "inv payload too short", len(pl))
    56  		c.DoS("InvEmpty")
    57  		return
    58  	}
    59  	c.Mutex.Lock()
    60  	c.X.InvsRecieved++
    61  	c.Mutex.Unlock()
    62  
    63  	cnt, of := btc.VLen(pl)
    64  	if of == 0 || len(pl) != of+36*cnt {
    65  		println("inv payload length mismatch", len(pl), of, cnt)
    66  		c.DoS("InvErr")
    67  		return
    68  	}
    69  
    70  	for i := 0; i < cnt; i++ {
    71  		typ := binary.LittleEndian.Uint32(pl[of : of+4])
    72  		c.Mutex.Lock()
    73  		c.InvStore(typ, pl[of+4:of+36])
    74  		ahr := c.X.AllHeadersReceived
    75  		c.Mutex.Unlock()
    76  		//common.CountSafe(fmt.Sprint("InvGot-", typ))
    77  		if typ == MSG_BLOCK {
    78  			bhash := btc.NewUint256(pl[of+4 : of+36])
    79  			if !ahr {
    80  				common.CountSafe("InvBlockIgnored")
    81  			} else {
    82  				if !blockReceived(bhash) {
    83  					MutexRcv.Lock()
    84  					if b2g, ok := BlocksToGet[bhash.BIdx()]; ok {
    85  						if c.Node.Height < b2g.Block.Height {
    86  							c.Node.Height = b2g.Block.Height
    87  						}
    88  						common.CountSafe("InvBlockFresh")
    89  						//println(c.PeerAddr.Ip(), c.Node.Version, "also knows the block", b2g.Block.Height, bhash.String())
    90  						c.MutexSetBool(&c.X.GetBlocksDataNow, true)
    91  					} else {
    92  						common.CountSafe("InvBlockNew")
    93  						c.ReceiveHeadersNow()
    94  						//println(c.PeerAddr.Ip(), c.Node.Version, "possibly new block", bhash.String())
    95  					}
    96  					MutexRcv.Unlock()
    97  				} else {
    98  					common.CountSafe("InvBlockOld")
    99  				}
   100  			}
   101  		} else if typ == MSG_TX {
   102  			if common.AcceptTx() {
   103  				c.TxInvNotify(pl[of+4 : of+36])
   104  			} else {
   105  				common.CountSafe("InvTxIgnored")
   106  			}
   107  		}
   108  		of += 36
   109  	}
   110  
   111  	return
   112  }
   113  
   114  func NetRouteInv(typ uint32, h *btc.Uint256, fromConn *OneConnection) uint32 {
   115  	var fee_spkb uint64
   116  	if typ == MSG_TX {
   117  		TxMutex.Lock()
   118  		if tx, ok := TransactionsToSend[h.BIdx()]; ok {
   119  			fee_spkb = (1000 * tx.Fee) / uint64(tx.VSize())
   120  		} else {
   121  			println("NetRouteInv: txid", h.String(), "not in mempool")
   122  		}
   123  		TxMutex.Unlock()
   124  	}
   125  	return NetRouteInvExt(typ, h, fromConn, fee_spkb)
   126  }
   127  
   128  // NetRouteInvExt is called from the main thread (or from a UI).
   129  func NetRouteInvExt(typ uint32, h *btc.Uint256, fromConn *OneConnection, fee_spkb uint64) (cnt uint32) {
   130  	common.CountSafe(fmt.Sprint("NetRouteInv", typ))
   131  
   132  	// Prepare the inv
   133  	inv := new([36]byte)
   134  	binary.LittleEndian.PutUint32(inv[0:4], typ)
   135  	copy(inv[4:36], h.Bytes())
   136  
   137  	// Append it to PendingInvs in each open connection
   138  	Mutex_net.Lock()
   139  	for _, v := range OpenCons {
   140  		if v != fromConn { // except the one that this inv came from
   141  			send_inv := true
   142  			v.Mutex.Lock()
   143  			if typ == MSG_TX {
   144  				if v.Node.DoNotRelayTxs {
   145  					send_inv = false
   146  					common.CountSafe("SendInvNoTxNode")
   147  				} else if v.X.MinFeeSPKB > 0 && uint64(v.X.MinFeeSPKB) > fee_spkb {
   148  					send_inv = false
   149  					common.CountSafe("SendInvFeeTooLow")
   150  				}
   151  
   152  				/* This is to prevent sending own txs to "spying" peers:
   153  				else if fromConn==nil && v.X.InvsRecieved==0 {
   154  					send_inv = false
   155  					common.CountSafe("SendInvOwnBlocked")
   156  				}
   157  				*/
   158  			}
   159  			if send_inv {
   160  				if len(v.PendingInvs) < 500 {
   161  					if typ, ok := v.InvDone.Map[hash2invid(inv[4:36])]; ok {
   162  						common.CountSafe(fmt.Sprint("SendInvSame-", typ))
   163  					} else {
   164  						v.PendingInvs = append(v.PendingInvs, inv)
   165  						cnt++
   166  						if typ == MSG_BLOCK {
   167  							v.sendInvsNow.Set() // for faster block propagation
   168  						}
   169  					}
   170  				} else {
   171  					common.CountSafe("SendInvFull")
   172  				}
   173  			}
   174  			v.Mutex.Unlock()
   175  		}
   176  	}
   177  	Mutex_net.Unlock()
   178  	return
   179  }
   180  
   181  // Call this function only when BlockIndexAccess is locked
   182  func addInvBlockBranch(inv map[[32]byte]bool, bl *chain.BlockTreeNode, stop *btc.Uint256) {
   183  	if len(inv) >= 500 || bl.BlockHash.Equal(stop) {
   184  		return
   185  	}
   186  	inv[bl.BlockHash.Hash] = true
   187  	for i := range bl.Childs {
   188  		if len(inv) >= 500 {
   189  			return
   190  		}
   191  		addInvBlockBranch(inv, bl.Childs[i], stop)
   192  	}
   193  }
   194  
   195  func (c *OneConnection) GetBlocks(pl []byte) {
   196  	h2get, hashstop, e := parseLocatorsPayload(pl)
   197  
   198  	if e != nil || len(h2get) < 1 || hashstop == nil {
   199  		println("GetBlocks: error parsing payload from", c.PeerAddr.Ip())
   200  		c.DoS("BadGetBlks")
   201  		return
   202  	}
   203  
   204  	invs := make(map[[32]byte]bool, 500)
   205  	for i := range h2get {
   206  		common.BlockChain.BlockIndexAccess.Lock()
   207  		if bl, ok := common.BlockChain.BlockIndex[h2get[i].BIdx()]; ok {
   208  			// make sure that this block is in our main chain
   209  			common.Last.Mutex.Lock()
   210  			end := common.Last.Block
   211  			common.Last.Mutex.Unlock()
   212  			for ; end != nil && end.Height >= bl.Height; end = end.Parent {
   213  				if end == bl {
   214  					addInvBlockBranch(invs, bl, hashstop) // Yes - this is the main chain
   215  					if len(invs) > 0 {
   216  						common.BlockChain.BlockIndexAccess.Unlock()
   217  
   218  						inv := new(bytes.Buffer)
   219  						btc.WriteVlen(inv, uint64(len(invs)))
   220  						for k := range invs {
   221  							binary.Write(inv, binary.LittleEndian, uint32(2))
   222  							inv.Write(k[:])
   223  						}
   224  						c.SendRawMsg("inv", inv.Bytes())
   225  						return
   226  					}
   227  				}
   228  			}
   229  		}
   230  		common.BlockChain.BlockIndexAccess.Unlock()
   231  	}
   232  
   233  	common.CountSafe("GetblksMissed")
   234  	return
   235  }
   236  
   237  func (c *OneConnection) SendInvs() (res bool) {
   238  	b_txs := new(bytes.Buffer)
   239  	b_blk := new(bytes.Buffer)
   240  	var c_blk []*btc.Uint256
   241  
   242  	c.Mutex.Lock()
   243  	if len(c.PendingInvs) > 0 {
   244  		for i := range c.PendingInvs {
   245  			var inv_sent_otherwise bool
   246  			typ := binary.LittleEndian.Uint32((*c.PendingInvs[i])[:4])
   247  			c.InvStore(typ, (*c.PendingInvs[i])[4:36])
   248  			if typ == MSG_BLOCK {
   249  				if c.Node.SendCmpctVer >= 1 && c.Node.HighBandwidth {
   250  					c_blk = append(c_blk, btc.NewUint256((*c.PendingInvs[i])[4:]))
   251  					inv_sent_otherwise = true
   252  				} else if c.Node.SendHeaders {
   253  					// convert block inv to block header
   254  					common.BlockChain.BlockIndexAccess.Lock()
   255  					bl := common.BlockChain.BlockIndex[btc.NewUint256((*c.PendingInvs[i])[4:]).BIdx()]
   256  					if bl != nil {
   257  						b_blk.Write(bl.BlockHeader[:])
   258  						b_blk.Write([]byte{0}) // 0 txs
   259  					}
   260  					common.BlockChain.BlockIndexAccess.Unlock()
   261  					inv_sent_otherwise = true
   262  				}
   263  			}
   264  
   265  			if !inv_sent_otherwise {
   266  				b_txs.Write((*c.PendingInvs[i])[:])
   267  			}
   268  		}
   269  		res = true
   270  	}
   271  	c.PendingInvs = nil
   272  	c.sendInvsNow.Clr()
   273  	c.Mutex.Unlock()
   274  
   275  	if len(c_blk) > 0 {
   276  		for _, h := range c_blk {
   277  			c.SendCmpctBlk(h)
   278  		}
   279  	}
   280  
   281  	if b_blk.Len() > 0 {
   282  		common.CountSafe("InvSentAsHeader")
   283  		var b [5]byte
   284  		ll := btc.PutVlen(b[:], b_blk.Len()/81)
   285  		c.SendRawMsg("headers", append(b[:ll], b_blk.Bytes()...))
   286  	}
   287  
   288  	if b_txs.Len() > 0 {
   289  		var b [5]byte
   290  		ll := btc.PutVlen(b[:], b_txs.Len()/36)
   291  		c.SendRawMsg("inv", append(b[:ll], b_txs.Bytes()...))
   292  	}
   293  
   294  	return
   295  }