// AskForOneBlock is for testing only, so you can ask for a specific block height // and see what goes wrong func (s *SPVCon) AskForOneBlock(h int32) error { var hdr wire.BlockHeader var err error dbTip := int32(h) s.headerMutex.Lock() // seek to header we need _, err = s.headerFile.Seek(int64((dbTip)*80), os.SEEK_SET) if err != nil { return err } err = hdr.Deserialize(s.headerFile) // read header, done w/ file for now s.headerMutex.Unlock() // unlock after reading 1 header if err != nil { log.Printf("header deserialize error!\n") return err } bHash := hdr.BlockSha() // create inventory we're asking for iv1 := wire.NewInvVect(wire.InvTypeWitnessBlock, &bHash) gdataMsg := wire.NewMsgGetData() // add inventory err = gdataMsg.AddInvVect(iv1) if err != nil { return err } hah := NewRootAndHeight(bHash, h) s.outMsgQueue <- gdataMsg s.blockQueue <- hah // push height and mroot of requested block on queue return nil }
// checkProofOfWork ensures the block header bits which indicate the target // difficulty is in min/max range and that the block hash is less than the // target difficulty as claimed. // // The flags modify the behavior of this function as follows: // - BFNoPoWCheck: The check to ensure the block hash is less than the target // difficulty is not performed. func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, flags BehaviorFlags) error { // The target difficulty must be larger than zero. target := CompactToBig(header.Bits) if target.Sign() <= 0 { str := fmt.Sprintf("block target difficulty of %064x is too low", target) return ruleError(ErrUnexpectedDifficulty, str) } // The target difficulty must be less than the maximum allowed. if target.Cmp(powLimit) > 0 { str := fmt.Sprintf("block target difficulty of %064x is "+ "higher than max of %064x", target, powLimit) return ruleError(ErrUnexpectedDifficulty, str) } // The block hash must be less than the claimed target unless the flag // to avoid proof of work checks is set. if flags&BFNoPoWCheck != BFNoPoWCheck { // The block hash must be less than the claimed target. hash := header.BlockHash() hashNum := HashToBig(&hash) if hashNum.Cmp(target) > 0 { str := fmt.Sprintf("block hash of %064x is higher than "+ "expected max of %064x", hashNum, target) return ruleError(ErrHighHash, str) } } return nil }
/* calcDiff returns a bool given two block headers. This bool is true if the correct dificulty adjustment is seen in the "next" header. Only feed it headers n-2016 and n-1, otherwise it will calculate a difficulty when no adjustment should take place, and return false. Note that the epoch is actually 2015 blocks long, which is confusing. */ func calcDiffAdjust(start, end wire.BlockHeader, p *chaincfg.Params) uint32 { duration := end.Timestamp.UnixNano() - start.Timestamp.UnixNano() if duration < minRetargetTimespan { log.Printf("whoa there, block %s off-scale high 4X diff adjustment!", end.BlockSha().String()) duration = minRetargetTimespan } else if duration > maxRetargetTimespan { log.Printf("Uh-oh! block %s off-scale low 0.25X diff adjustment!\n", end.BlockSha().String()) duration = maxRetargetTimespan } // calculation of new 32-byte difficulty target // first turn the previous target into a big int prevTarget := blockchain.CompactToBig(start.Bits) // new target is old * duration... newTarget := new(big.Int).Mul(prevTarget, big.NewInt(duration)) // divided by 2 weeks newTarget.Div(newTarget, big.NewInt(int64(targetTimespan))) // clip again if above minimum target (too easy) if newTarget.Cmp(p.PowLimit) > 0 { newTarget.Set(p.PowLimit) } // calculate and return 4-byte 'bits' difficulty from 32-byte target return blockchain.BigToCompact(newTarget) }
func (s *SPVCon) AskForHeaders() error { var hdr wire.BlockHeader ghdr := wire.NewMsgGetHeaders() ghdr.ProtocolVersion = s.localVersion s.headerMutex.Lock() // start header file ops info, err := s.headerFile.Stat() if err != nil { return err } headerFileSize := info.Size() if headerFileSize == 0 || headerFileSize%80 != 0 { // header file broken return fmt.Errorf("Header file not a multiple of 80 bytes") } // seek to 80 bytes from end of file ns, err := s.headerFile.Seek(-80, os.SEEK_END) if err != nil { log.Printf("can't seek\n") return err } log.Printf("suk to offset %d (should be near the end\n", ns) // get header from last 80 bytes of file err = hdr.Deserialize(s.headerFile) if err != nil { log.Printf("can't Deserialize") return err } s.headerMutex.Unlock() // done with header file cHash := hdr.BlockSha() err = ghdr.AddBlockLocatorHash(&cHash) if err != nil { return err } fmt.Printf("get headers message has %d header hashes, first one is %s\n", len(ghdr.BlockLocatorHashes), ghdr.BlockLocatorHashes[0].String()) s.outMsgQueue <- ghdr return nil }
// solveBlock attempts to find a nonce which makes the passed block header hash // to a value less than the target difficulty. When a successful solution is // found true is returned and the nonce field of the passed header is updated // with the solution. False is returned if no solution exists. func solveBlock(header *wire.BlockHeader, targetDifficulty *big.Int) bool { // sbResult is used by the solver goroutines to send results. type sbResult struct { found bool nonce uint32 } // solver accepts a block header and a nonce range to test. It is // intended to be run as a goroutine. quit := make(chan bool) results := make(chan sbResult) solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint32) { // We need to modify the nonce field of the header, so make sure // we work with a copy of the original header. for i := startNonce; i >= startNonce && i <= stopNonce; i++ { select { case <-quit: return default: hdr.Nonce = i hash := hdr.BlockHash() if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 { results <- sbResult{true, i} return } } } results <- sbResult{false, 0} } startNonce := uint32(0) stopNonce := uint32(math.MaxUint32) numCores := uint32(runtime.NumCPU()) noncesPerCore := (stopNonce - startNonce) / numCores for i := uint32(0); i < numCores; i++ { rangeStart := startNonce + (noncesPerCore * i) rangeStop := startNonce + (noncesPerCore * (i + 1)) - 1 if i == numCores-1 { rangeStop = stopNonce } go solver(*header, rangeStart, rangeStop) } for i := uint32(0); i < numCores; i++ { result := <-results if result.found { close(quit) header.Nonce = result.nonce return true } } return false }
/* checkProofOfWork verifies the header hashes into something lower than specified by the 4-byte bits field. */ func checkProofOfWork(header wire.BlockHeader, p *chaincfg.Params) bool { target := blockchain.CompactToBig(header.Bits) // The target must more than 0. Why can you even encode negative... if target.Sign() <= 0 { log.Printf("block target %064x is neagtive(??)\n", target.Bytes()) return false } // The target must be less than the maximum allowed (difficulty 1) if target.Cmp(p.PowLimit) > 0 { log.Printf("block target %064x is "+ "higher than max of %064x", target, p.PowLimit.Bytes()) return false } // The header hash must be less than the claimed target in the header. blockHash := header.BlockSha() hashNum := blockchain.ShaHashToBig(&blockHash) if hashNum.Cmp(target) > 0 { log.Printf("block hash %064x is higher than "+ "required target of %064x", hashNum, target) return false } return true }
// AskForMerkBlocks requests blocks from current to last // right now this asks for 1 block per getData message. // Maybe it's faster to ask for many in a each message? func (s *SPVCon) AskForBlocks() error { var hdr wire.BlockHeader s.headerMutex.Lock() // lock just to check filesize stat, err := os.Stat(headerFileName) s.headerMutex.Unlock() // checked, unlock endPos := stat.Size() headerTip := int32(endPos/80) - 1 // move back 1 header length to read dbTip, err := s.TS.GetDBSyncHeight() if err != nil { return err } fmt.Printf("dbTip %d headerTip %d\n", dbTip, headerTip) if dbTip > headerTip { return fmt.Errorf("error- db longer than headers! shouldn't happen.") } if dbTip == headerTip { // nothing to ask for; set wait state and return fmt.Printf("no blocks to request, entering wait state\n") fmt.Printf("%d bytes received\n", s.RBytes) s.inWaitState <- true // also advertise any unconfirmed txs here s.Rebroadcast() return nil } fmt.Printf("will request blocks %d to %d\n", dbTip+1, headerTip) if !s.HardMode { // don't send this in hardmode! that's the whole point // create initial filter filt, err := s.TS.GimmeFilter() if err != nil { return err } // send filter s.SendFilter(filt) fmt.Printf("sent filter %x\n", filt.MsgFilterLoad().Filter) } // loop through all heights where we want merkleblocks. for dbTip < headerTip { dbTip++ // we're requesting the next header // load header from file s.headerMutex.Lock() // seek to header we need _, err = s.headerFile.Seek(int64((dbTip)*80), os.SEEK_SET) if err != nil { return err } err = hdr.Deserialize(s.headerFile) // read header, done w/ file for now s.headerMutex.Unlock() // unlock after reading 1 header if err != nil { log.Printf("header deserialize error!\n") return err } bHash := hdr.BlockSha() // create inventory we're asking for iv1 := new(wire.InvVect) // if hardmode, ask for legit blocks, none of this ralphy stuff if s.HardMode { iv1 = wire.NewInvVect(wire.InvTypeWitnessBlock, &bHash) } else { // ah well iv1 = wire.NewInvVect(wire.InvTypeFilteredWitnessBlock, &bHash) } gdataMsg := wire.NewMsgGetData() // add inventory err = gdataMsg.AddInvVect(iv1) if err != nil { return err } hah := NewRootAndHeight(hdr.BlockSha(), dbTip) if dbTip == headerTip { // if this is the last block, indicate finality hah.final = true } // waits here most of the time for the queue to empty out s.blockQueue <- hah // push height and mroot of requested block on queue s.outMsgQueue <- gdataMsg } return nil }
// IngestHeaders takes in a bunch of headers and appends them to the // local header file, checking that they fit. If there's no headers, // it assumes we're done and returns false. If it worked it assumes there's // more to request and returns true. func (s *SPVCon) IngestHeaders(m *wire.MsgHeaders) (bool, error) { gotNum := int64(len(m.Headers)) if gotNum > 0 { fmt.Printf("got %d headers. Range:\n%s - %s\n", gotNum, m.Headers[0].BlockSha().String(), m.Headers[len(m.Headers)-1].BlockSha().String()) } else { log.Printf("got 0 headers, we're probably synced up") return false, nil } s.headerMutex.Lock() defer s.headerMutex.Unlock() var err error // seek to last header _, err = s.headerFile.Seek(-80, os.SEEK_END) if err != nil { return false, err } var last wire.BlockHeader err = last.Deserialize(s.headerFile) if err != nil { return false, err } prevHash := last.BlockSha() endPos, err := s.headerFile.Seek(0, os.SEEK_END) if err != nil { return false, err } tip := int32(endPos/80) - 1 // move back 1 header length to read // check first header returned to make sure it fits on the end // of our header file if !m.Headers[0].PrevBlock.IsEqual(&prevHash) { // delete 100 headers if this happens! Dumb reorg. log.Printf("reorg? header msg doesn't fit. points to %s, expect %s", m.Headers[0].PrevBlock.String(), prevHash.String()) if endPos < 8080 { // jeez I give up, back to genesis s.headerFile.Truncate(80) } else { err = s.headerFile.Truncate(endPos - 8000) if err != nil { return false, fmt.Errorf("couldn't truncate header file") } } return true, fmt.Errorf("Truncated header file to try again") } for _, resphdr := range m.Headers { // write to end of file err = resphdr.Serialize(s.headerFile) if err != nil { return false, err } // advance chain tip tip++ // check last header worked := CheckHeader(s.headerFile, tip, s.TS.Param) if !worked { if endPos < 8080 { // jeez I give up, back to genesis s.headerFile.Truncate(80) } else { err = s.headerFile.Truncate(endPos - 8000) if err != nil { return false, fmt.Errorf("couldn't truncate header file") } } // probably should disconnect from spv node at this point, // since they're giving us invalid headers. return true, fmt.Errorf( "Header %d - %s doesn't fit, dropping 100 headers.", resphdr.BlockSha().String(), tip) } } log.Printf("Headers to height %d OK.", tip) return true, nil }
// checkBlockHeaderContext peforms several validation checks on the block header // which depend on its position within the block chain. // // The flags modify the behavior of this function as follows: // - BFFastAdd: All checks except those involving comparing the header against // the checkpoints are not performed. // // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode *blockNode, flags BehaviorFlags) error { // The genesis block is valid by definition. if prevNode == nil { return nil } fastAdd := flags&BFFastAdd == BFFastAdd if !fastAdd { // Ensure the difficulty specified in the block header matches // the calculated difficulty based on the previous block and // difficulty retarget rules. expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode, header.Timestamp) if err != nil { return err } blockDifficulty := header.Bits if blockDifficulty != expectedDifficulty { str := "block difficulty of %d is not the expected value of %d" str = fmt.Sprintf(str, blockDifficulty, expectedDifficulty) return ruleError(ErrUnexpectedDifficulty, str) } // Ensure the timestamp for the block header is after the // median time of the last several blocks (medianTimeBlocks). medianTime, err := b.calcPastMedianTime(prevNode) if err != nil { log.Errorf("calcPastMedianTime: %v", err) return err } if !header.Timestamp.After(medianTime) { str := "block timestamp of %v is not after expected %v" str = fmt.Sprintf(str, header.Timestamp, medianTime) return ruleError(ErrTimeTooOld, str) } } // The height of this block is one more than the referenced previous // block. blockHeight := prevNode.height + 1 // Ensure chain matches up to predetermined checkpoints. blockHash := header.BlockHash() if !b.verifyCheckpoint(blockHeight, &blockHash) { str := fmt.Sprintf("block at height %d does not match "+ "checkpoint hash", blockHeight) return ruleError(ErrBadCheckpoint, str) } // Find the previous checkpoint and prevent blocks which fork the main // chain before it. This prevents storage of new, otherwise valid, // blocks which build off of old blocks that are likely at a much easier // difficulty and therefore could be used to waste cache and disk space. checkpointBlock, err := b.findPreviousCheckpoint() if err != nil { return err } if checkpointBlock != nil && blockHeight < checkpointBlock.Height() { str := fmt.Sprintf("block at height %d forks the main chain "+ "before the previous checkpoint at height %d", blockHeight, checkpointBlock.Height()) return ruleError(ErrForkTooOld, str) } // Reject outdated block versions once a majority of the network // has upgraded. These were originally voted on by BIP0034, // BIP0065, and BIP0066. params := b.chainParams if header.Version < 2 && blockHeight >= params.BIP0034Height || header.Version < 3 && blockHeight >= params.BIP0066Height || header.Version < 4 && blockHeight >= params.BIP0065Height { str := "new blocks with version %d are no longer valid" str = fmt.Sprintf(str, header.Version) return ruleError(ErrBlockVersionTooOld, str) } return nil }
func CheckHeader(r io.ReadSeeker, height int32, p *chaincfg.Params) bool { var err error var cur, prev, epochStart wire.BlockHeader // don't try to verfy the genesis block. That way madness lies. if height == 0 { return true } // initial load of headers // load epochstart, previous and current. // get the header from the epoch start, up to 2016 blocks ago _, err = r.Seek(int64(80*(height-(height%epochLength))), os.SEEK_SET) if err != nil { log.Printf(err.Error()) return false } err = epochStart.Deserialize(r) if err != nil { log.Printf(err.Error()) return false } // log.Printf("start epoch at height %d ", height-(height%epochLength)) // seek to n-1 header _, err = r.Seek(int64(80*(height-1)), os.SEEK_SET) if err != nil { log.Printf(err.Error()) return false } // read in n-1 err = prev.Deserialize(r) if err != nil { log.Printf(err.Error()) return false } // seek to curHeight header and read in _, err = r.Seek(int64(80*(height)), os.SEEK_SET) if err != nil { log.Printf(err.Error()) return false } err = cur.Deserialize(r) if err != nil { log.Printf(err.Error()) return false } // get hash of n-1 header prevHash := prev.BlockSha() // check if headers link together. That whole 'blockchain' thing. if prevHash.IsEqual(&cur.PrevBlock) == false { log.Printf("Headers %d and %d don't link.\n", height-1, height) log.Printf("%s - %s", prev.BlockSha().String(), cur.BlockSha().String()) return false } rightBits := epochStart.Bits // normal, no adjustment; Dn = Dn-1 // see if we're on a difficulty adjustment block if (height)%epochLength == 0 { // if so, check if difficulty adjustment is valid. // That whole "controlled supply" thing. // calculate diff n based on n-2016 ... n-1 rightBits = calcDiffAdjust(epochStart, prev, p) // done with adjustment, save new ephochStart header epochStart = cur log.Printf("Update epoch at height %d", height) } else { // not a new epoch // if on testnet, check for difficulty nerfing if p.ResetMinDifficulty && cur.Timestamp.After( prev.Timestamp.Add(targetSpacing*2)) { // fmt.Printf("nerf %d ", curHeight) rightBits = p.PowLimitBits // difficulty 1 } if cur.Bits != rightBits { log.Printf("Block %d %s incorrect difficuly. Read %x, expect %x\n", height, cur.BlockSha().String(), cur.Bits, rightBits) return false } } // check if there's a valid proof of work. That whole "Bitcoin" thing. if !checkProofOfWork(cur, p) { log.Printf("Block %d Bad proof of work.\n", height) return false } return true // it must have worked if there's no errors and got to the end. }