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 }