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

     1  package network
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"io"
     7  	"io/ioutil"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/piotrnar/gocoin/client/common"
    12  	"github.com/piotrnar/gocoin/lib/btc"
    13  )
    14  
    15  func (c *OneConnection) ProcessGetData(pl []byte) {
    16  	//println(c.PeerAddr.Ip(), "getdata")
    17  	b := bytes.NewReader(pl)
    18  	cnt, e := btc.ReadVLen(b)
    19  	if e != nil {
    20  		println("ProcessGetData:", e.Error(), c.PeerAddr.Ip())
    21  		return
    22  	}
    23  
    24  	if b.Len() != int(cnt)*36 {
    25  		println(c.ConnID, "Inconsistent getdata message:", b.Len(), "!=", cnt*36)
    26  		c.DoS("GetDataLenERR")
    27  		return
    28  	}
    29  
    30  	if c.unfinished_getdata != nil {
    31  		//println(c.ConnID, "appending pending getdata with", cnt, "more invs")
    32  		if c.unfinished_getdata.Len()+b.Len() > 36*50000 {
    33  			c.DoS("GetDataTooBigA")
    34  		} else {
    35  			io.Copy(c.unfinished_getdata, b)
    36  			common.CountSafe("GetDataPauseExt")
    37  		}
    38  		return
    39  	}
    40  	c.processGetData(b)
    41  }
    42  
    43  func (c *OneConnection) processGetData(b *bytes.Reader) {
    44  	var typ uint32
    45  	var h [36]byte
    46  	for b.Len() > 0 {
    47  		if c.SendingPaused() {
    48  			// note that this function should not be called when c.unfinished_getdata is not nil
    49  			c.unfinished_getdata = new(bytes.Buffer)
    50  			//println(c.ConnID, "postpone getdata for", b.Len()/36, "invs")
    51  			io.Copy(c.unfinished_getdata, b)
    52  			common.CountSafe("GetDataPaused")
    53  			break
    54  		}
    55  
    56  		b.Read(h[:])
    57  
    58  		typ = binary.LittleEndian.Uint32(h[:4])
    59  		c.Mutex.Lock()
    60  		c.InvStore(typ, h[4:36])
    61  		c.Mutex.Unlock()
    62  
    63  		if typ == MSG_BLOCK || typ == MSG_WITNESS_BLOCK {
    64  			if typ == MSG_BLOCK {
    65  				common.CountSafe("GetdataBlock")
    66  			} else {
    67  				common.CountSafe("GetdataBlockSw")
    68  			}
    69  			hash := btc.NewUint256(h[4:])
    70  			crec, _, er := common.BlockChain.Blocks.BlockGetExt(hash)
    71  
    72  			if er == nil {
    73  				bl := crec.Data
    74  				if typ == MSG_BLOCK {
    75  					// remove witness data from the block
    76  					if crec.Block == nil {
    77  						crec.Block, _ = btc.NewBlock(bl)
    78  					}
    79  					if crec.Block.NoWitnessData == nil {
    80  						crec.Block.BuildNoWitnessData()
    81  					}
    82  					//println("block size", len(crec.Data), "->", len(bl))
    83  					bl = crec.Block.NoWitnessData
    84  				}
    85  				c.SendRawMsg("block", bl)
    86  			} else {
    87  				//fmt.Println("BlockGetExt-2 failed for", hash.String(), er.Error())
    88  				//notfound = append(notfound, h[:]...)
    89  			}
    90  		} else if typ == MSG_TX || typ == MSG_WITNESS_TX {
    91  			if typ == MSG_TX {
    92  				common.CountSafe("GetdataTx")
    93  			} else {
    94  				common.CountSafe("GetdataTxSw")
    95  			}
    96  			// ransaction
    97  			TxMutex.Lock()
    98  			if tx, ok := TransactionsToSend[btc.NewUint256(h[4:]).BIdx()]; ok && tx.Blocked == 0 {
    99  				tx.SentCnt++
   100  				tx.Lastsent = time.Now()
   101  				TxMutex.Unlock()
   102  				if tx.SegWit == nil || typ == MSG_WITNESS_TX {
   103  					c.SendRawMsg("tx", tx.Raw)
   104  				} else {
   105  					c.SendRawMsg("tx", tx.Serialize())
   106  				}
   107  			} else {
   108  				TxMutex.Unlock()
   109  				//notfound = append(notfound, h[:]...)
   110  			}
   111  		} else if typ == MSG_CMPCT_BLOCK {
   112  			common.CountSafe("GetdataCmpctBlk")
   113  			if !c.SendCmpctBlk(btc.NewUint256(h[4:])) {
   114  				println(c.ConnID, c.PeerAddr.Ip(), c.Node.Agent, "asked for CmpctBlk we don't have", btc.NewUint256(h[4:]).String())
   115  				if c.Misbehave("GetCmpctBlk", 100) {
   116  					break
   117  				}
   118  			}
   119  		} else {
   120  			common.CountSafe("GetdataTypeInvalid")
   121  		}
   122  	}
   123  }
   124  
   125  // netBlockReceived is called from a net conn thread.
   126  func netBlockReceived(conn *OneConnection, b []byte) {
   127  	if len(b) < 100 {
   128  		conn.DoS("ShortBlock")
   129  		return
   130  	}
   131  
   132  	hash := btc.NewSha2Hash(b[:80])
   133  	idx := hash.BIdx()
   134  	//println("got block data", hash.String())
   135  
   136  	MutexRcv.Lock()
   137  
   138  	// the blocks seems to be fine
   139  	if rb, got := ReceivedBlocks[idx]; got {
   140  		rb.Cnt++
   141  		Fetch.BlockBytesWasted += uint64(len(b))
   142  		Fetch.BlockSameRcvd++
   143  		conn.Mutex.Lock()
   144  		delete(conn.GetBlockInProgress, idx)
   145  		conn.Mutex.Unlock()
   146  		MutexRcv.Unlock()
   147  		return
   148  	}
   149  
   150  	// remove from BlocksToGet:
   151  	b2g := BlocksToGet[idx]
   152  	if b2g == nil {
   153  		//println("Block", hash.String(), " from", conn.PeerAddr.Ip(), conn.Node.Agent, " was not expected")
   154  
   155  		var sta int
   156  		sta, b2g = conn.ProcessNewHeader(b[:80])
   157  		if b2g == nil {
   158  			if sta == PH_STATUS_FATAL {
   159  				println("Unrequested Block: FAIL - Ban", conn.PeerAddr.Ip(), conn.Node.Agent)
   160  				conn.DoS("BadUnreqBlock")
   161  			} else {
   162  				common.CountSafe("ErrUnreqBlock")
   163  			}
   164  			//conn.Disconnect()
   165  			MutexRcv.Unlock()
   166  			return
   167  		}
   168  		if sta == PH_STATUS_NEW {
   169  			b2g.SendInvs = true
   170  		}
   171  		//println(c.ConnID, " - taking this new block")
   172  		common.CountSafe("UnxpectedBlockNEW")
   173  	}
   174  
   175  	//println("block", b2g.BlockTreeNode.Height," len", len(b), " got from", conn.PeerAddr.Ip(), b2g.InProgress)
   176  
   177  	prev_block_raw := b2g.Block.Raw // in case if it's a corrupt one
   178  	b2g.Block.Raw = b
   179  	if conn.X.Authorized {
   180  		b2g.Block.Trusted.Set()
   181  	}
   182  
   183  	er := common.BlockChain.PostCheckBlock(b2g.Block)
   184  	if er != nil {
   185  		println("Corrupt block", hash.String(), b2g.BlockTreeNode.Height)
   186  		println(" ... received from", conn.PeerAddr.Ip(), er.Error())
   187  		//ioutil.WriteFile(hash.String()+"-"+conn.PeerAddr.Ip()+".bin", b, 0700)
   188  		conn.DoS("BadBlock")
   189  
   190  		// We don't need to remove from conn.GetBlockInProgress as we're disconnecting
   191  		// ... decreasing of b2g.InProgress will also be done then.
   192  
   193  		if b2g.Block.MerkleRootMatch() && !strings.Contains(er.Error(), "RPC_Result:bad-witness-nonce-size") {
   194  			println(" <- It was a wrongly mined one - give it up")
   195  			DelB2G(idx) //remove it from BlocksToGet
   196  			if b2g.BlockTreeNode == LastCommitedHeader {
   197  				LastCommitedHeader = LastCommitedHeader.Parent
   198  			}
   199  			common.BlockChain.DeleteBranch(b2g.BlockTreeNode, delB2G_callback)
   200  		} else {
   201  			println(" <- Merkle Root not matching - discard the data:", len(b2g.Block.Txs), b2g.Block.TxCount,
   202  				b2g.Block.TxOffset, b2g.Block.NoWitnessSize, b2g.Block.BlockWeight, b2g.TotalInputs)
   203  			// We just recived a corrupt copy from the peer. We will ask another peer for it.
   204  			// But discard the data we extracted from this one, so it won't confuse us later.
   205  			b2g.Block.Raw = prev_block_raw
   206  			b2g.Block.NoWitnessSize, b2g.Block.BlockWeight, b2g.TotalInputs = 0, 0, 0
   207  			b2g.Block.TxCount, b2g.Block.TxOffset = 0, 0
   208  			b2g.Block.Txs = nil
   209  		}
   210  
   211  		MutexRcv.Unlock()
   212  		return
   213  	}
   214  
   215  	orb := &OneReceivedBlock{TmStart: b2g.Started, TmPreproc: b2g.TmPreproc,
   216  		TmDownload: conn.LastMsgTime, FromConID: conn.ConnID, DoInvs: b2g.SendInvs}
   217  
   218  	conn.Mutex.Lock()
   219  	bip := conn.GetBlockInProgress[idx]
   220  	if bip == nil {
   221  		//println(conn.ConnID, "received unrequested block", hash.String())
   222  		common.CountSafe("UnreqBlockRcvd")
   223  		conn.cntInc("NewBlock!")
   224  		orb.TxMissing = -2
   225  	} else {
   226  		delete(conn.GetBlockInProgress, idx)
   227  		conn.cntInc("NewBlock")
   228  		orb.TxMissing = -1
   229  	}
   230  	conn.blocksreceived = append(conn.blocksreceived, time.Now())
   231  	conn.Mutex.Unlock()
   232  
   233  	ReceivedBlocks[idx] = orb
   234  	DelB2G(idx) //remove it from BlocksToGet if no more pending downloads
   235  
   236  	store_on_disk := len(BlocksToGet) > 10 && common.GetBool(&common.CFG.Memory.CacheOnDisk) && len(b2g.Block.Raw) > 16*1024
   237  	MutexRcv.Unlock()
   238  
   239  	var bei *btc.BlockExtraInfo
   240  
   241  	size := len(b2g.Block.Raw)
   242  	if store_on_disk {
   243  		if e := ioutil.WriteFile(common.TempBlocksDir()+hash.String(), b2g.Block.Raw, 0600); e == nil {
   244  			bei = new(btc.BlockExtraInfo)
   245  			*bei = b2g.Block.BlockExtraInfo
   246  			b2g.Block = nil
   247  		} else {
   248  			println("write tmp block:", e.Error())
   249  		}
   250  	}
   251  
   252  	NetBlocks <- &BlockRcvd{Conn: conn, Block: b2g.Block, BlockTreeNode: b2g.BlockTreeNode,
   253  		OneReceivedBlock: orb, BlockExtraInfo: bei, Size: size}
   254  }
   255  
   256  // parseLocatorsPayload parses the payload of "getblocks" or "getheaders" messages.
   257  // It reads Version and VLen followed by the number of locators.
   258  // Return zero-ed stop_hash is not present in the payload.
   259  func parseLocatorsPayload(pl []byte) (h2get []*btc.Uint256, hashstop *btc.Uint256, er error) {
   260  	var cnt uint64
   261  	var ver uint32
   262  
   263  	b := bytes.NewReader(pl)
   264  
   265  	// version
   266  	if er = binary.Read(b, binary.LittleEndian, &ver); er != nil {
   267  		return
   268  	}
   269  
   270  	// hash count
   271  	if cnt, er = btc.ReadVLen(b); er != nil {
   272  		return
   273  	}
   274  
   275  	// block locator hashes
   276  	if cnt > 0 {
   277  		h2get = make([]*btc.Uint256, cnt)
   278  		for i := 0; i < int(cnt); i++ {
   279  			h2get[i] = new(btc.Uint256)
   280  			if _, er = b.Read(h2get[i].Hash[:]); er != nil {
   281  				return
   282  			}
   283  		}
   284  	}
   285  
   286  	// hash_stop
   287  	hashstop = new(btc.Uint256)
   288  	b.Read(hashstop.Hash[:]) // if not there, don't make a big deal about it
   289  
   290  	return
   291  }
   292  
   293  // Call it with locked MutexRcv
   294  func getBlockToFetch(max_height uint32, cnt_in_progress, avg_block_size uint) (lowest_found *OneBlockToGet) {
   295  	for _, v := range BlocksToGet {
   296  		if v.InProgress == cnt_in_progress && v.Block.Height <= max_height &&
   297  			(lowest_found == nil || v.Block.Height < lowest_found.Block.Height) {
   298  			lowest_found = v
   299  		}
   300  	}
   301  	return
   302  }
   303  
   304  var Fetc struct {
   305  	HeightA uint64
   306  	HeightB uint64
   307  	HeightC uint64
   308  	HeightD uint64
   309  	B2G     uint64
   310  	C       [6]uint64
   311  }
   312  
   313  var Fetch struct {
   314  	NoBlocksToGet      uint64
   315  	HasBlocksExpired   uint64
   316  	MaxCountInProgress uint64
   317  	MaxBytesInProgress uint64
   318  	NoWitness          uint64
   319  	Nothing            uint64
   320  	BlksCntMax         [6]uint64
   321  	ReachEndOfLoop     uint64
   322  	ReachMaxCnt        uint64
   323  	ReachMaxData       uint64
   324  
   325  	BlockBytesWasted uint64
   326  	BlockSameRcvd    uint64
   327  
   328  	CacheEmpty     uint64
   329  	LastCacheEmpty time.Time
   330  }
   331  
   332  func (c *OneConnection) GetBlockData() (yes bool) {
   333  	//MAX_GETDATA_FORWARD
   334  	// Need to send getdata...?
   335  	MutexRcv.Lock()
   336  	defer MutexRcv.Unlock()
   337  
   338  	if LowestIndexToBlocksToGet == 0 || len(BlocksToGet) == 0 {
   339  		Fetch.NoBlocksToGet++
   340  		// wake up in one minute, just in case
   341  		c.nextGetData = time.Now().Add(60 * time.Second)
   342  		return
   343  	}
   344  
   345  	c.Mutex.Lock()
   346  	if c.X.BlocksExpired > 0 { // Do not fetch blocks from nodes that had not given us some in the past
   347  		c.Mutex.Unlock()
   348  		Fetch.HasBlocksExpired++
   349  		return
   350  	}
   351  	cbip := len(c.GetBlockInProgress)
   352  	c.Mutex.Unlock()
   353  
   354  	if cbip >= MAX_PEERS_BLOCKS_IN_PROGRESS {
   355  		Fetch.MaxCountInProgress++
   356  		// wake up in a few seconds, maybe some blocks will complete by then
   357  		c.nextGetData = time.Now().Add(1 * time.Second)
   358  		return
   359  	}
   360  
   361  	avg_block_size := common.AverageBlockSize.Get()
   362  	block_data_in_progress := cbip * avg_block_size
   363  
   364  	if block_data_in_progress > 0 && (block_data_in_progress+avg_block_size) > MAX_GETDATA_FORWARD {
   365  		Fetch.MaxBytesInProgress++
   366  		// wake up in a few seconds, maybe some blocks will complete by then
   367  		c.nextGetData = time.Now().Add(1 * time.Second) // wait for some blocks to complete
   368  		return
   369  	}
   370  
   371  	var cnt uint64
   372  	var block_type uint32
   373  
   374  	if (c.Node.Services & btc.SERVICE_SEGWIT) != 0 {
   375  		block_type = MSG_WITNESS_BLOCK
   376  	} else {
   377  		block_type = MSG_BLOCK
   378  	}
   379  
   380  	// We can issue getdata for this peer
   381  	// Let's look for the lowest height block in BlocksToGet that isn't being downloaded yet
   382  
   383  	last_block_height := common.Last.BlockHeight()
   384  	max_height := last_block_height + uint32(common.SyncMaxCacheBytes.Get()/avg_block_size)
   385  
   386  	Fetc.HeightA = uint64(last_block_height)
   387  	Fetc.HeightB = uint64(LowestIndexToBlocksToGet)
   388  	Fetc.HeightC = uint64(max_height)
   389  
   390  	if max_height > last_block_height+MAX_BLOCKS_FORWARD_CNT {
   391  		max_height = last_block_height + MAX_BLOCKS_FORWARD_CNT
   392  	}
   393  	max_max_height := max_height
   394  	if max_max_height > c.Node.Height {
   395  		max_max_height = c.Node.Height
   396  	}
   397  	if max_max_height > LastCommitedHeader.Height {
   398  		max_max_height = LastCommitedHeader.Height
   399  	}
   400  
   401  	if common.BlockChain.Consensus.Enforce_SEGWIT != 0 && (c.Node.Services&btc.SERVICE_SEGWIT) == 0 { // no segwit node
   402  		if max_height >= common.BlockChain.Consensus.Enforce_SEGWIT-1 {
   403  			max_height = common.BlockChain.Consensus.Enforce_SEGWIT - 1
   404  			if max_height <= LowestIndexToBlocksToGet {
   405  				Fetch.NoWitness++
   406  				c.nextGetData = time.Now().Add(time.Hour) // never do getdata
   407  				return
   408  			}
   409  		}
   410  	}
   411  
   412  	Fetc.HeightD = uint64(max_height)
   413  
   414  	max_blocks_at_once := common.GetUint32(&common.CFG.Net.MaxBlockAtOnce)
   415  	max_blocks_forward := max_height - last_block_height
   416  	invs := new(bytes.Buffer)
   417  	var cnt_in_progress uint32
   418  	var lowest_found *OneBlockToGet
   419  
   420  	for {
   421  		// Find block to fetch:
   422  		max_height = last_block_height + max_blocks_forward/(cnt_in_progress+1)
   423  		if max_height > max_max_height {
   424  			max_height = max_max_height
   425  		}
   426  		if max_height < LowestIndexToBlocksToGet {
   427  			Fetch.BlksCntMax[cnt_in_progress]++
   428  			break
   429  		}
   430  		for bh := LowestIndexToBlocksToGet; bh <= max_height; bh++ {
   431  			if idxlst, ok := IndexToBlocksToGet[bh]; ok {
   432  				for _, idx := range idxlst {
   433  					v := BlocksToGet[idx]
   434  					if uint32(v.InProgress) == cnt_in_progress {
   435  						c.Mutex.Lock()
   436  						_, ok := c.GetBlockInProgress[idx]
   437  						c.Mutex.Unlock()
   438  						if !ok {
   439  							lowest_found = v
   440  							goto found_it
   441  						}
   442  					}
   443  				}
   444  			}
   445  		}
   446  
   447  		// If we came here, we did not find it.
   448  		if cnt_in_progress++; cnt_in_progress >= max_blocks_at_once {
   449  			Fetch.ReachEndOfLoop++
   450  			break
   451  		}
   452  		continue
   453  
   454  	found_it:
   455  		Fetc.C[lowest_found.InProgress]++
   456  
   457  		binary.Write(invs, binary.LittleEndian, block_type)
   458  		invs.Write(lowest_found.BlockHash.Hash[:])
   459  		lowest_found.InProgress++
   460  		cnt++
   461  
   462  		c.Mutex.Lock()
   463  		c.GetBlockInProgress[lowest_found.BlockHash.BIdx()] =
   464  			&oneBlockDl{hash: lowest_found.BlockHash, start: time.Now(), SentAtPingCnt: c.X.PingSentCnt}
   465  		cbip = len(c.GetBlockInProgress)
   466  		c.Mutex.Unlock()
   467  
   468  		if cbip >= MAX_PEERS_BLOCKS_IN_PROGRESS {
   469  			Fetch.ReachMaxCnt++
   470  			break // no more than 2000 blocks in progress / peer
   471  		}
   472  		block_data_in_progress += avg_block_size
   473  		if block_data_in_progress > MAX_GETDATA_FORWARD {
   474  			Fetch.ReachMaxData++
   475  			break
   476  		}
   477  	}
   478  
   479  	Fetc.B2G = uint64(cnt)
   480  
   481  	if cnt == 0 {
   482  		//println(c.ConnID, "fetch nothing", cbip, block_data_in_progress, max_height-common.Last.BlockHeight(), cnt_in_progress)
   483  		Fetch.Nothing++
   484  		// wake up in a few seconds, maybe it will be different next time
   485  		c.nextGetData = time.Now().Add(5 * time.Second)
   486  		return
   487  	}
   488  
   489  	bu := new(bytes.Buffer)
   490  	btc.WriteVlen(bu, uint64(cnt))
   491  	pl := append(bu.Bytes(), invs.Bytes()...)
   492  	//println(c.ConnID, "fetching", cnt, "new blocks ->", cbip)
   493  	c.SendRawMsg("getdata", pl)
   494  	yes = true
   495  
   496  	// we don't set c.nextGetData here, as it will be done in tick.go after "block" message
   497  	c.Mutex.Lock()
   498  	// we will come back here only after receiving half of the blocks that we have requested
   499  	c.keepBlocksOver = 3 * len(c.GetBlockInProgress) / 4
   500  	c.Mutex.Unlock()
   501  
   502  	return
   503  }