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

     1  package network
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"time"
     7  
     8  	"github.com/piotrnar/gocoin/client/common"
     9  	"github.com/piotrnar/gocoin/lib/btc"
    10  	"github.com/piotrnar/gocoin/lib/chain"
    11  )
    12  
    13  const (
    14  	PH_STATUS_NEW   = 1
    15  	PH_STATUS_FRESH = 2
    16  	PH_STATUS_OLD   = 3
    17  	PH_STATUS_ERROR = 4
    18  	PH_STATUS_FATAL = 5
    19  )
    20  
    21  func (c *OneConnection) ProcessNewHeader(hdr []byte) (int, *OneBlockToGet) {
    22  	var ok bool
    23  	var b2g *OneBlockToGet
    24  	bl, _ := btc.NewBlock(hdr)
    25  
    26  	c.Mutex.Lock()
    27  	c.InvStore(MSG_BLOCK, bl.Hash.Hash[:])
    28  	c.Mutex.Unlock()
    29  
    30  	if _, ok = DiscardedBlocks[bl.Hash.BIdx()]; ok {
    31  		common.CountSafe("HdrRejected")
    32  		//fmt.Println("", bl.Hash.String(), "-header for already rejected block")
    33  		return PH_STATUS_FATAL, nil
    34  	}
    35  
    36  	if _, ok = ReceivedBlocks[bl.Hash.BIdx()]; ok {
    37  		common.CountSafe("HeaderOld")
    38  		//fmt.Println("", i, bl.Hash.String(), "-already received")
    39  		return PH_STATUS_OLD, nil
    40  	}
    41  
    42  	if b2g, ok = BlocksToGet[bl.Hash.BIdx()]; ok {
    43  		common.CountSafe("HeaderFresh")
    44  		//fmt.Println(c.PeerAddr.Ip(), "block", bl.Hash.String(), " not new but get it")
    45  		return PH_STATUS_FRESH, b2g
    46  	}
    47  
    48  	common.CountSafe("HeaderNew")
    49  	//fmt.Println("", i, bl.Hash.String(), " - NEW!")
    50  
    51  	common.BlockChain.BlockIndexAccess.Lock()
    52  	defer common.BlockChain.BlockIndexAccess.Unlock()
    53  
    54  	if dos, _, er := common.BlockChain.PreCheckBlock(bl); er != nil {
    55  		common.CountSafe("PreCheckBlockFail")
    56  		//println("PreCheckBlock err", dos, er.Error())
    57  		if dos {
    58  			return PH_STATUS_FATAL, nil
    59  		} else {
    60  			return PH_STATUS_ERROR, nil
    61  		}
    62  	}
    63  
    64  	node := common.BlockChain.AcceptHeader(bl)
    65  	b2g = &OneBlockToGet{Started: c.LastMsgTime, Block: bl, BlockTreeNode: node, InProgress: 0}
    66  	AddB2G(b2g)
    67  	if node.Height > LastCommitedHeader.Height {
    68  		LastCommitedHeader = node
    69  		//println("LastCommitedHeader:", LastCommitedHeader.Height, "-change to", LastCommitedHeader.BlockHash.String())
    70  	} else {
    71  		//println("LastCommitedHeader:", LastCommitedHeader.Height, "new:", node.Height, "-keep!")
    72  	}
    73  
    74  	if common.LastTrustedBlockMatch(node.BlockHash) {
    75  		common.SetUint32(&common.LastTrustedBlockHeight, node.Height)
    76  		for node != nil {
    77  			node.Trusted.Set()
    78  			node = node.Parent
    79  		}
    80  	}
    81  	b2g.Block.Trusted.Store(b2g.BlockTreeNode.Trusted.Get())
    82  
    83  	return PH_STATUS_NEW, b2g
    84  }
    85  
    86  func (c *OneConnection) HandleHeaders(pl []byte) (new_headers_got int) {
    87  	var highest_block_found uint32
    88  
    89  	c.MutexSetBool(&c.X.GetHeadersInProgress, false)
    90  
    91  	b := bytes.NewReader(pl)
    92  	cnt, e := btc.ReadVLen(b)
    93  	if e != nil {
    94  		println("HandleHeaders:", e.Error(), c.PeerAddr.Ip())
    95  		return
    96  	}
    97  
    98  	if cnt > 2000 {
    99  		println("HandleHeaders: too many headers", cnt, c.PeerAddr.Ip(), c.Node.Agent)
   100  		c.DoS("HdrErrX")
   101  		return
   102  	}
   103  
   104  	HeadersReceived.Add(1)
   105  
   106  	if cnt > 0 {
   107  		MutexRcv.Lock()
   108  		defer MutexRcv.Unlock()
   109  
   110  		for i := 0; i < int(cnt); i++ {
   111  			hdr := make([]byte, 80)
   112  
   113  			if n, _ := b.Read(hdr); n != 80 {
   114  				println("HandleHeaders: pl too short 1", c.PeerAddr.Ip(), c.Node.Agent)
   115  				c.DoS("HdrErr1")
   116  				return
   117  			}
   118  
   119  			if _, e = btc.ReadVLen(b); e != nil {
   120  				println("HandleHeaders: pl too short 2", c.PeerAddr.Ip(), c.Node.Agent)
   121  				c.DoS("HdrErr2")
   122  				return
   123  			}
   124  
   125  			sta, b2g := c.ProcessNewHeader(hdr)
   126  			if b2g == nil {
   127  				if sta == PH_STATUS_FATAL {
   128  					//println("c.DoS(BadHeader)")
   129  					c.DoS("BadHeader")
   130  					return
   131  				} else if sta == PH_STATUS_ERROR {
   132  					//println("c.Misbehave(BadHeader)")
   133  					c.Misbehave("BadHeader", 50) // do it 20 times and you are banned
   134  				}
   135  			} else {
   136  				if sta == PH_STATUS_NEW {
   137  					if cnt == 1 {
   138  						b2g.SendInvs = true
   139  					}
   140  					new_headers_got++
   141  				}
   142  				if b2g.Block.Height > highest_block_found {
   143  					highest_block_found = b2g.Block.Height
   144  				}
   145  				if c.Node.Height < b2g.Block.Height {
   146  					c.Mutex.Lock()
   147  					c.Node.Height = b2g.Block.Height
   148  					c.Mutex.Unlock()
   149  				}
   150  				c.MutexSetBool(&c.X.GetBlocksDataNow, true)
   151  				if b2g.TmPreproc.IsZero() { // do not overwrite TmPreproc (in case of PH_STATUS_FRESH)
   152  					b2g.TmPreproc = time.Now()
   153  				}
   154  			}
   155  		}
   156  	} else {
   157  		common.CountSafe("EmptyHeadersRcvd")
   158  		HeadersReceived.Add(4)
   159  	}
   160  
   161  	c.Mutex.Lock()
   162  	c.X.LastHeadersEmpty = highest_block_found <= c.X.LastHeadersHeightAsk
   163  	c.X.TotalNewHeadersCount += new_headers_got
   164  	if new_headers_got == 0 {
   165  		c.X.AllHeadersReceived = true
   166  	}
   167  	c.Mutex.Unlock()
   168  
   169  	return
   170  }
   171  
   172  func (c *OneConnection) ReceiveHeadersNow() {
   173  	c.Mutex.Lock()
   174  	if c.X.Debug {
   175  		println(c.ConnID, "- ReceiveHeadersNow()")
   176  	}
   177  	c.X.AllHeadersReceived = false
   178  	c.Mutex.Unlock()
   179  }
   180  
   181  // GetHeaders handles getheaders protocol command.
   182  // https://en.bitcoin.it/wiki/Protocol_specification#getheaders
   183  func (c *OneConnection) GetHeaders(pl []byte) {
   184  	h2get, hashstop, e := parseLocatorsPayload(pl)
   185  
   186  	if e != nil {
   187  		//println(time.Now().Format("2006-01-02 15:04:05"), c.ConnID, "GetHeaders: error parsing payload from", c.PeerAddr.Ip(), c.Node.Agent, e.Error())
   188  		c.DoS("BadGetHdrsA")
   189  		return
   190  	}
   191  
   192  	if len(h2get) > 101 || hashstop == nil {
   193  		//println("GetHeaders: too many locators", len(h2get), "or missing hashstop", hashstop, "from", c.PeerAddr.Ip(), c.Node.Agent, e.Error())
   194  		c.DoS("BadGetHdrsB")
   195  		return
   196  	}
   197  
   198  	var best_block, last_block *chain.BlockTreeNode
   199  
   200  	//common.Last.Mutex.Lock()
   201  	MutexRcv.Lock()
   202  	last_block = LastCommitedHeader
   203  	MutexRcv.Unlock()
   204  	//common.Last.Mutex.Unlock()
   205  
   206  	common.BlockChain.BlockIndexAccess.Lock()
   207  
   208  	//println("GetHeaders", len(h2get), hashstop.String())
   209  	if len(h2get) > 0 {
   210  		for i := range h2get {
   211  			if bl, ok := common.BlockChain.BlockIndex[h2get[i].BIdx()]; ok {
   212  				if best_block == nil || bl.Height > best_block.Height {
   213  					//println(" ... bbl", i, bl.Height, bl.BlockHash.String())
   214  					best_block = bl
   215  				}
   216  			}
   217  		}
   218  	} else {
   219  		best_block = common.BlockChain.BlockIndex[hashstop.BIdx()]
   220  	}
   221  
   222  	if best_block == nil {
   223  		common.CountSafe("GetHeadersBadBlock")
   224  		best_block = common.BlockChain.BlockTreeRoot
   225  	}
   226  
   227  	var resp []byte
   228  	var cnt uint32
   229  
   230  	defer func() {
   231  		// If we get a hash of an old orphaned blocks, FindPathTo() will panic, so...
   232  		if r := recover(); r != nil {
   233  			common.CountSafe("GetHeadersOrphBlk")
   234  		}
   235  
   236  		common.BlockChain.BlockIndexAccess.Unlock()
   237  
   238  		// send the response
   239  		out := new(bytes.Buffer)
   240  		btc.WriteVlen(out, uint64(cnt))
   241  		out.Write(resp)
   242  		c.SendRawMsg("headers", out.Bytes())
   243  	}()
   244  
   245  	for cnt < 2000 {
   246  		if last_block.Height <= best_block.Height {
   247  			break
   248  		}
   249  		best_block = best_block.FindPathTo(last_block)
   250  		if best_block == nil {
   251  			break
   252  		}
   253  		resp = append(resp, append(best_block.BlockHeader[:], 0)...) // 81st byte is always zero
   254  		cnt++
   255  	}
   256  
   257  	// Note: the deferred function will be called before exiting
   258  
   259  	return
   260  }
   261  
   262  func (c *OneConnection) sendGetHeaders() {
   263  	MutexRcv.Lock()
   264  	lb := LastCommitedHeader
   265  	MutexRcv.Unlock()
   266  	min_height := int(lb.Height) - chain.MovingCheckopintDepth
   267  	if min_height < 0 {
   268  		min_height = 0
   269  	}
   270  
   271  	var cnt uint64
   272  	var step int
   273  	step = 1
   274  	hashes := make([]byte, 0, 50*32)
   275  	for cnt < 50 /*it should never get that far, but just in case...*/ {
   276  		hashes = append(hashes, lb.BlockHash.Hash[:]...)
   277  		cnt++
   278  		//println(" geth", cnt, "height", lb.Height, lb.BlockHash.String())
   279  		if int(lb.Height) <= min_height {
   280  			break
   281  		}
   282  		for tmp := 0; tmp < step && lb != nil && int(lb.Height) > min_height; tmp++ {
   283  			lb = lb.Parent
   284  		}
   285  		if lb == nil {
   286  			break
   287  		}
   288  		if cnt >= 10 {
   289  			step = step * 2
   290  		}
   291  	}
   292  
   293  	bmsg := bytes.NewBuffer(make([]byte, 0, 4+9+len(hashes)+32))
   294  	binary.Write(bmsg, binary.LittleEndian, common.Version)
   295  	btc.WriteVlen(bmsg, cnt)
   296  	bmsg.Write(hashes)
   297  	bmsg.Write(make([]byte, 32)) // null_stop
   298  
   299  	c.SendRawMsg("getheaders", bmsg.Bytes())
   300  	c.X.LastHeadersHeightAsk = lb.Height
   301  	c.MutexSetBool(&c.X.GetHeadersInProgress, true)
   302  	c.X.GetHeadersTimeOutAt = time.Now().Add(GetHeadersTimeout)
   303  	c.X.GetHeadersSentAtPingCnt = c.X.PingSentCnt
   304  
   305  	/*if c.X.Debug {
   306  		println(c.ConnID, "- GetHeadersSentAtPingCnt", c.X.GetHeadersSentAtPingCnt)
   307  	}*/
   308  }