// 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(block *btcutil.Block, powLimit *big.Int, flags BehaviorFlags) error { // The target difficulty must be larger than zero. target := CompactToBig(block.MsgBlock().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. blockHash, err := block.Sha() if err != nil { return err } hashNum := ShaHashToBig(blockHash) 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 }
// NotifyBlockConnected creates and marshalls a JSON message to notify // of a new block connected to the main chain. The notification is sent // to each connected wallet. func (s *rpcServer) NotifyBlockConnected(block *btcutil.Block) { hash, err := block.Sha() if err != nil { rpcsLog.Error("Bad block; connected block notification dropped.") return } // TODO: remove int32 type conversion. ntfn := btcws.NewBlockConnectedNtfn(hash.String(), int32(block.Height())) mntfn, _ := json.Marshal(ntfn) s.ws.walletNotificationMaster <- mntfn // Inform any interested parties about txs mined in this block. s.ws.Lock() for _, tx := range block.Transactions() { if clist, ok := s.ws.minedTxNotifications[*tx.Sha()]; ok { var enext *list.Element for e := clist.Front(); e != nil; e = enext { enext = e.Next() c := e.Value.(walletChan) // TODO: remove int32 type conversion after // the int64 -> int32 switch is made. ntfn := btcws.NewTxMinedNtfn(tx.Sha().String(), hash.String(), int32(block.Height()), block.MsgBlock().Header.Timestamp.Unix(), tx.Index()) mntfn, _ := json.Marshal(ntfn) c <- mntfn s.ws.removeMinedTxRequest(c, tx.Sha()) } } } s.ws.Unlock() }
// 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. func checkProofOfWork(block *btcutil.Block, powLimit *big.Int) error { // The target difficulty must be larger than zero. target := CompactToBig(block.MsgBlock().Header.Bits) if target.Sign() <= 0 { str := fmt.Sprintf("block target difficulty of %064x is too low", target) return RuleError(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(str) } // The block hash must be less than the claimed target. blockHash, err := block.Sha() if err != nil { return err } hashNum := ShaHashToBig(blockHash) if hashNum.Cmp(target) > 0 { str := fmt.Sprintf("block hash of %064x is higher than "+ "expected max of %064x", hashNum, target) return RuleError(str) } return nil }
// newBlockNotifyCheckTxOut is a helper function to iterate through // each transaction output of a new block and perform any checks and // notify listening frontends when necessary. func (s *rpcServer) newBlockNotifyCheckTxOut(block *btcutil.Block, tx *btcutil.Tx, spent []bool) { for wltNtfn, cxt := range s.ws.requests.m { for i, txout := range tx.MsgTx().TxOut { _, txaddrhash, err := btcscript.ScriptToAddrHash(txout.PkScript) if err != nil { log.Debug("Error getting payment address from tx; dropping any Tx notifications.") break } for addr, id := range cxt.txRequests { if !bytes.Equal(addr[:], txaddrhash) { continue } blkhash, err := block.Sha() if err != nil { log.Error("Error getting block sha; dropping Tx notification.") break } txaddr, err := btcutil.EncodeAddress(txaddrhash, s.server.btcnet) if err != nil { log.Error("Error encoding address; dropping Tx notification.") break } reply := &btcjson.Reply{ Result: struct { Sender string `json:"sender"` Receiver string `json:"receiver"` BlockHash string `json:"blockhash"` Height int64 `json:"height"` TxHash string `json:"txhash"` Index uint32 `json:"index"` Amount int64 `json:"amount"` PkScript string `json:"pkscript"` Spent bool `json:"spent"` }{ Sender: "Unknown", // TODO(jrick) Receiver: txaddr, BlockHash: blkhash.String(), Height: block.Height(), TxHash: tx.Sha().String(), Index: uint32(i), Amount: txout.Value, PkScript: btcutil.Base58Encode(txout.PkScript), Spent: spent[i], }, Error: nil, Id: &id, } replyBytes, err := json.Marshal(reply) if err != nil { log.Errorf("RPCS: Unable to marshal tx notification: %v", err) continue } wltNtfn <- replyBytes } } } }
// NotifyForTxOuts iterates through all outputs of a tx, performing any // necessary notifications for wallets. If a non-nil block is passed, // additional block information is passed with the notifications. func (s *rpcServer) NotifyForTxOuts(tx *btcutil.Tx, block *btcutil.Block) { // Nothing to do if nobody is listening for transaction notifications. if len(s.ws.txNotifications) == 0 { return } for i, txout := range tx.MsgTx().TxOut { _, addrs, _, err := btcscript.ExtractPkScriptAddrs( txout.PkScript, s.server.btcnet) if err != nil { continue } for _, addr := range addrs { // Only support pay-to-pubkey-hash right now. if _, ok := addr.(*btcutil.AddressPubKeyHash); !ok { continue } encodedAddr := addr.EncodeAddress() if idlist, ok := s.ws.txNotifications[encodedAddr]; ok { for e := idlist.Front(); e != nil; e = e.Next() { n := e.Value.(ntfnChan) ntfn := &btcws.ProcessedTxNtfn{ Receiver: encodedAddr, TxID: tx.Sha().String(), TxOutIndex: uint32(i), Amount: txout.Value, PkScript: hex.EncodeToString(txout.PkScript), // TODO(jrick): hardcoding unspent is WRONG and needs // to be either calculated from other block txs, or dropped. Spent: false, } if block != nil { blkhash, err := block.Sha() if err != nil { rpcsLog.Error("Error getting block sha; dropping Tx notification") break } ntfn.BlockHeight = int32(block.Height()) ntfn.BlockHash = blkhash.String() ntfn.BlockIndex = tx.Index() ntfn.BlockTime = block.MsgBlock().Header.Timestamp.Unix() } else { ntfn.BlockHeight = -1 ntfn.BlockIndex = -1 } n <- ntfn } } } } }
// CheckConnectBlock performs several checks to confirm connecting the passed // block to the main chain does not violate any rules. An example of some of // the checks performed are ensuring connecting the block would not cause any // duplicate transaction hashes for old transactions that aren't already fully // spent, double spends, exceeding the maximum allowed signature operations // per block, invalid values in relation to the expected block subisidy, or // fail transaction script validation. // // This function is NOT safe for concurrent access. func (b *BlockChain) CheckConnectBlock(block *btcutil.Block) error { prevNode := b.bestChain blockSha, _ := block.Sha() newNode := newBlockNode(&block.MsgBlock().Header, blockSha, block.Height()) if prevNode != nil { newNode.parent = prevNode newNode.workSum.Add(prevNode.workSum, newNode.workSum) } return b.checkConnectBlock(newNode, block) }
// NotifyBlockConnected creates and marshalls a JSON message to notify // of a new block connected to the main chain. The notification is sent // to each connected wallet. func (s *rpcServer) NotifyBlockConnected(block *btcutil.Block) { s.ws.walletListeners.RLock() for wltNtfn := range s.ws.walletListeners.m { // Create notification with basic information filled in. // This data is the same for every connected wallet. hash, err := block.Sha() if err != nil { log.Error("Bad block; connected block notification dropped.") return } ntfnResult := struct { Hash string `json:"hash"` Height int64 `json:"height"` MinedTXs []string `json:"minedtxs"` }{ Hash: hash.String(), Height: block.Height(), } // Fill in additional wallet-specific notifications. If there // is no request context for this wallet, no need to give this // wallet any extra notifications. if cxt := s.ws.requests.m[wltNtfn]; cxt != nil { // Create list of all txs created by this wallet that appear in this // block. minedTxShas := make([]string, 0, len(cxt.minedTxRequests)) // TxShas does not return a non-nil error. txShaList, _ := block.TxShas() txList := s.server.db.FetchTxByShaList(txShaList) for _, txReply := range txList { if txReply.Err != nil { continue } if _, ok := cxt.minedTxRequests[*txReply.Sha]; ok { minedTxShas = append(minedTxShas, txReply.Sha.String()) s.ws.requests.RemoveMinedTxRequest(wltNtfn, txReply.Sha) } } ntfnResult.MinedTXs = minedTxShas } var id interface{} = "btcd:blockconnected" ntfn := btcjson.Reply{ Result: ntfnResult, Id: &id, } m, _ := json.Marshal(ntfn) wltNtfn <- m } s.ws.walletListeners.RUnlock() }
// NotifyBlockDisconnected creates and marshals a JSON message to notify // of a new block disconnected from the main chain. The notification is sent // to each connected wallet. func (s *rpcServer) NotifyBlockDisconnected(block *btcutil.Block) { hash, err := block.Sha() if err != nil { rpcsLog.Error("Bad block; connected block notification dropped.") return } // TODO: remove int32 type conversion. ntfn := btcws.NewBlockDisconnectedNtfn(hash.String(), int32(block.Height())) mntfn, _ := json.Marshal(ntfn) s.ws.walletNotificationMaster <- mntfn }
// NotifyBlockDisconnected creates and marshals a JSON message to notify // of a new block disconnected from the main chain. The notification is sent // to each connected wallet. func (s *rpcServer) NotifyBlockDisconnected(block *btcutil.Block) { hash, err := block.Sha() if err != nil { rpcsLog.Error("Bad block; connected block notification dropped") return } // TODO: remove int32 type conversion. ntfn := btcws.NewBlockDisconnectedNtfn(hash.String(), int32(block.Height())) for ntfnChan, rc := range s.ws.connections { if rc.blockUpdates { ntfnChan <- ntfn } } }
// addOrphanBlock adds the passed block (which is already determined to be // an orphan prior calling this function) to the orphan pool. It lazily cleans // up any expired blocks so a separate cleanup poller doesn't need to be run. // It also imposes a maximum limit on the number of outstanding orphan // blocks and will remove the oldest received orphan block if the limit is // exceeded. func (b *BlockChain) addOrphanBlock(block *btcutil.Block) { // Remove expired orphan blocks. for _, oBlock := range b.orphans { if time.Now().After(oBlock.expiration) { b.removeOrphanBlock(oBlock) continue } // Update the oldest orphan block pointer so it can be discarded // in case the orphan pool fills up. if b.oldestOrphan == nil || oBlock.expiration.Before(b.oldestOrphan.expiration) { b.oldestOrphan = oBlock } } // Limit orphan blocks to prevent memory exhaustion. if len(b.orphans)+1 > maxOrphanBlocks { // Remove the oldest orphan to make room for the new one. b.removeOrphanBlock(b.oldestOrphan) b.oldestOrphan = nil } // Get the block sha. It is safe to ignore the error here since any // errors would've been caught prior to calling this function. blockSha, _ := block.Sha() // Protect concurrent access. This is intentionally done here instead // of near the top since removeOrphanBlock does its own locking and // the range iterator is not invalidated by removing map entries. b.orphanLock.Lock() b.orphanLock.Unlock() // Insert the block into the orphan map with an expiration time // 1 hour from now. expiration := time.Now().Add(time.Hour) oBlock := &orphanBlock{ block: block, expiration: expiration, } b.orphans[*blockSha] = oBlock // Add to previous hash lookup index for faster dependency lookups. prevHash := &block.MsgBlock().Header.PrevBlock b.prevOrphans[*prevHash] = append(b.prevOrphans[*prevHash], oBlock) return }
// submitBlock submits the passed block to network after ensuring it passes all // of the consensus validation rules. func (m *CPUMiner) submitBlock(block *btcutil.Block) bool { m.submitBlockLock.Lock() defer m.submitBlockLock.Unlock() // Ensure the block is not stale since a new block could have shown up // while the solution was being found. Typically that condition is // detected and all work on the stale block is halted to start work on // a new block, but the check only happens periodically, so it is // possible a block was found and submitted in between. latestHash, _ := m.server.blockManager.chainState.Best() msgBlock := block.MsgBlock() if !msgBlock.Header.PrevBlock.IsEqual(latestHash) { minrLog.Debugf("Block submitted via CPU miner with previous "+ "block %s is stale", msgBlock.Header.PrevBlock) return false } // Process this block using the same rules as blocks coming from other // nodes. This will in turn relay it to the network like normal. isOrphan, err := m.server.blockManager.ProcessBlock(block) if err != nil { // Anything other than a rule violation is an unexpected error, // so log that error as an internal error. if _, ok := err.(btcchain.RuleError); !ok { minrLog.Errorf("Unexpected error while processing "+ "block submitted via CPU miner: %v", err) return false } minrLog.Debugf("Block submitted via CPU miner rejected: %v", err) return false } if isOrphan { minrLog.Debugf("Block submitted via CPU miner is an orphan") return false } // The block was accepted. blockSha, _ := block.Sha() coinbaseTx := block.MsgBlock().Transactions[0].TxOut[0] minrLog.Infof("Block submitted via CPU miner accepted (hash %s, "+ "amount %v)", blockSha, btcutil.Amount(coinbaseTx.Value)) return true }
// NotifyBlockDisconnected creates and marshals a JSON message to notify // of a new block disconnected from the main chain. The notification is sent // to each connected wallet. func (s *rpcServer) NotifyBlockDisconnected(block *btcutil.Block) { var id interface{} = "btcd:blockdisconnected" hash, err := block.Sha() if err != nil { log.Error("Bad block; connected block notification dropped.") return } ntfn := btcjson.Reply{ Result: struct { Hash string `json:"hash"` Height int64 `json:"height"` }{ Hash: hash.String(), Height: block.Height(), }, Id: &id, } m, _ := json.Marshal(ntfn) s.ws.walletNotificationMaster <- m }
func MakeBlock(block *btcutil.Block, previous *Block) *Block { transactions := make([]ads.ADS, 0) for _, transaction := range block.Transactions() { t := &Transaction{ MsgTx: *transaction.MsgTx(), } t.SetCachedHash(sha.Hash(*transaction.Sha())) transactions = append(transactions, t) } b := &Block{ Header: block.MsgBlock().Header, Previous: previous, Transactions: transactions, } hash, _ := block.Sha() b.SetCachedHash(sha.Hash(*hash)) return b }
// InsertBlock inserts the block data and transaction data from a block // into the database. func (db *SqliteDb) InsertBlock(block *btcutil.Block) (height int64, err error) { db.dbLock.Lock() defer db.dbLock.Unlock() blocksha, err := block.Sha() if err != nil { log.Warnf("Failed to compute block sha %v", blocksha) return } mblock := block.MsgBlock() rawMsg, pver, err := block.Bytes() if err != nil { log.Warnf("Failed to obtain raw block sha %v", blocksha) return } txloc, err := block.TxLoc() if err != nil { log.Warnf("Failed to obtain raw block sha %v", blocksha) return } // Insert block into database newheight, err := db.insertBlockData(blocksha, &mblock.Header.PrevBlock, pver, rawMsg) if err != nil { log.Warnf("Failed to insert block %v %v %v", blocksha, &mblock.Header.PrevBlock, err) return } // At least two blocks in the long past were generated by faulty // miners, the sha of the transaction exists in a previous block, // detect this condition and 'accept' the block. for txidx, tx := range mblock.Transactions { var txsha btcwire.ShaHash txsha, err = tx.TxSha(pver) if err != nil { log.Warnf("failed to compute tx name block %v idx %v err %v", blocksha, txidx, err) return } // Some old blocks contain duplicate transactions // Attempt to cleanly bypass this problem // http://blockexplorer.com/b/91842 // http://blockexplorer.com/b/91880 if newheight == 91842 { dupsha, err := btcwire.NewShaHashFromStr("d5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599") if err != nil { panic("invalid sha string in source") } if txsha == *dupsha { log.Tracef("skipping sha %v %v", dupsha, newheight) continue } } if newheight == 91880 { dupsha, err := btcwire.NewShaHashFromStr("e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468") if err != nil { panic("invalid sha string in source") } if txsha == *dupsha { log.Tracef("skipping sha %v %v", dupsha, newheight) continue } } spentbuflen := (len(tx.TxOut) + 7) / 8 spentbuf := make([]byte, spentbuflen, spentbuflen) err = db.insertTx(&txsha, newheight, txloc[txidx].TxStart, txloc[txidx].TxLen, spentbuf) if err != nil { log.Warnf("block %v idx %v failed to insert tx %v %v err %v", &blocksha, newheight, &txsha, txidx, err) var oBlkIdx int64 oBlkIdx, _, _, err = db.fetchLocationBySha(&txsha) log.Warnf("oblkidx %v err %v", oBlkIdx, err) return } } db.syncPoint() return newheight, nil }
// maybeAcceptBlock potentially accepts a block into the memory block chain. // It performs several validation checks which depend on its position within // the block chain before adding it. The block is expected to have already gone // through ProcessBlock before calling this function with it. // // The flags modify the behavior of this function as follows: // - BFFastAdd: The somewhat expensive BIP0034 validation is not performed. // - BFDryRun: The memory chain index will not be pruned and no accept // notification will be sent since the block is not being accepted. func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error { fastAdd := flags&BFFastAdd == BFFastAdd dryRun := flags&BFDryRun == BFDryRun // Get a block node for the block previous to this one. Will be nil // if this is the genesis block. prevNode, err := b.getPrevNodeFromBlock(block) if err != nil { log.Errorf("getPrevNodeFromBlock: %v", err) return err } // The height of this block is one more than the referenced previous // block. blockHeight := int64(0) if prevNode != nil { blockHeight = prevNode.height + 1 } block.SetHeight(blockHeight) blockHeader := &block.MsgBlock().Header 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, block.MsgBlock().Header.Timestamp) if err != nil { return err } blockDifficulty := blockHeader.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 !blockHeader.Timestamp.After(medianTime) { str := "block timestamp of %v is not after expected %v" str = fmt.Sprintf(str, blockHeader.Timestamp, medianTime) return ruleError(ErrTimeTooOld, str) } // Ensure all transactions in the block are finalized. for _, tx := range block.Transactions() { if !IsFinalizedTransaction(tx, blockHeight, blockHeader.Timestamp) { str := fmt.Sprintf("block contains "+ "unfinalized transaction %v", tx.Sha()) return ruleError(ErrUnfinalizedTx, str) } } } // Ensure chain matches up to predetermined checkpoints. // It's safe to ignore the error on Sha since it's already cached. blockHash, _ := block.Sha() 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) } if !fastAdd { // Reject version 1 blocks once a majority of the network has // upgraded. This is part of BIP0034. if blockHeader.Version < 2 { if b.isMajorityVersion(2, prevNode, b.netParams.BlockV1RejectNumRequired, b.netParams.BlockV1RejectNumToCheck) { str := "new blocks with version %d are no " + "longer valid" str = fmt.Sprintf(str, blockHeader.Version) return ruleError(ErrBlockVersionTooOld, str) } } // Ensure coinbase starts with serialized block heights for // blocks whose version is the serializedHeightVersion or // newer once a majority of the network has upgraded. This is // part of BIP0034. if blockHeader.Version >= serializedHeightVersion { if b.isMajorityVersion(serializedHeightVersion, prevNode, b.netParams.CoinbaseBlockHeightNumRequired, b.netParams.CoinbaseBlockHeightNumToCheck) { expectedHeight := int64(0) if prevNode != nil { expectedHeight = prevNode.height + 1 } coinbaseTx := block.Transactions()[0] err := checkSerializedHeight(coinbaseTx, expectedHeight) if err != nil { return err } } } } // Prune block nodes which are no longer needed before creating // a new node. if !dryRun { err = b.pruneBlockNodes() if err != nil { return err } } // Create a new block node for the block and add it to the in-memory // block chain (could be either a side chain or the main chain). newNode := newBlockNode(blockHeader, blockHash, blockHeight) if prevNode != nil { newNode.parent = prevNode newNode.height = blockHeight newNode.workSum.Add(prevNode.workSum, newNode.workSum) } // Connect the passed block to the chain while respecting proper chain // selection according to the chain with the most proof of work. This // also handles validation of the transaction scripts. err = b.connectBestChain(newNode, block, flags) if err != nil { return err } // Notify the caller that the new block was accepted into the block // chain. The caller would typically want to react by relaying the // inventory to other peers. if !dryRun { b.sendNotification(NTBlockAccepted, block) } return nil }
// ProcessBlock is the main workhorse for handling insertion of new blocks into // the block chain. It includes functionality such as rejecting duplicate // blocks, ensuring blocks follow all rules, orphan handling, and insertion into // the block chain along with best chain selection and reorganization. func (b *BlockChain) ProcessBlock(block *btcutil.Block, fastAdd bool) error { blockHash, err := block.Sha() if err != nil { return err } log.Tracef("Processing block %v", blockHash) // The block must not already exist in the main chain or side chains. if b.blockExists(blockHash) { str := fmt.Sprintf("already have block %v", blockHash) return RuleError(str) } // The block must not already exist as an orphan. if _, exists := b.orphans[*blockHash]; exists { str := fmt.Sprintf("already have block (orphan) %v", blockHash) return RuleError(str) } // Perform preliminary sanity checks on the block and its transactions. err = CheckBlockSanity(block, b.chainParams().PowLimit) if err != nil { return err } // Find the latest known checkpoint and perform some additional checks // based on the checkpoint. This provides a few nice properties such as // preventing forks from blocks before the last checkpoint, rejecting // easy to mine, but otherwise bogus, blocks that could be used to eat // memory, and ensuring expected (versus claimed) proof of work // requirements since the last checkpoint are met. blockHeader := &block.MsgBlock().Header checkpointBlock, err := b.findLatestKnownCheckpoint() if err != nil { return err } if checkpointBlock != nil { // Ensure the block timestamp is after the checkpoint timestamp. checkpointHeader := &checkpointBlock.MsgBlock().Header checkpointTime := checkpointHeader.Timestamp if blockHeader.Timestamp.Before(checkpointTime) { str := fmt.Sprintf("block %v has timestamp %v before "+ "last checkpoint timestamp %v", blockHash, blockHeader.Timestamp, checkpointTime) return RuleError(str) } if !fastAdd { // Even though the checks prior to now have already ensured the // proof of work exceeds the claimed amount, the claimed amount // is a field in the block header which could be forged. This // check ensures the proof of work is at least the minimum // expected based on elapsed time since the last checkpoint and // maximum adjustment allowed by the retarget rules. duration := blockHeader.Timestamp.Sub(checkpointTime) requiredTarget := CompactToBig(b.calcEasiestDifficulty( checkpointHeader.Bits, duration)) currentTarget := CompactToBig(blockHeader.Bits) if currentTarget.Cmp(requiredTarget) > 0 { str := fmt.Sprintf("block target difficulty of %064x "+ "is too low when compared to the previous "+ "checkpoint", currentTarget) return RuleError(str) } } } // Handle orphan blocks. prevHash := &blockHeader.PrevBlock if !prevHash.IsEqual(zeroHash) && !b.blockExists(prevHash) { // Add the orphan block to the orphan pool. log.Infof("Adding orphan block %v with parent %v", blockHash, prevHash) b.addOrphanBlock(block) // Notify the caller so it can request missing blocks. b.sendNotification(NTOrphanBlock, blockHash) return nil } // The block has passed all context independent checks and appears sane // enough to potentially accept it into the block chain. err = b.maybeAcceptBlock(block, fastAdd) if err != nil { return err } // Accept any orphan blocks that depend on this block (they are no // longer orphans) and repeat for those accepted blocks until there are // no more. err = b.processOrphans(blockHash) if err != nil { return err } log.Debugf("Accepted block %v", blockHash) return nil }
// InsertBlock inserts raw block and transaction data from a block into the // database. The first block inserted into the database will be treated as the // genesis block. Every subsequent block insert requires the referenced parent // block to already exist. This is part of the btcdb.Db interface // implementation. func (db *MemDb) InsertBlock(block *btcutil.Block) (int64, error) { db.Lock() defer db.Unlock() if db.closed { return 0, ErrDbClosed } blockHash, err := block.Sha() if err != nil { return 0, err } // Reject the insert if the previously reference block does not exist // except in the case there are no blocks inserted yet where the first // inserted block is assumed to be a genesis block. msgBlock := block.MsgBlock() if _, exists := db.blocksBySha[msgBlock.Header.PrevBlock]; !exists { if len(db.blocks) > 0 { return 0, btcdb.ErrPrevShaMissing } } // Build a map of in-flight transactions because some of the inputs in // this block could be referencing other transactions earlier in this // block which are not yet in the chain. txInFlight := map[btcwire.ShaHash]int{} transactions := block.Transactions() for i, tx := range transactions { txInFlight[*tx.Sha()] = i } // Loop through all transactions and inputs to ensure there are no error // conditions that would prevent them from be inserted into the db. // Although these checks could could be done in the loop below, checking // for error conditions up front means the code below doesn't have to // deal with rollback on errors. newHeight := int64(len(db.blocks)) for i, tx := range transactions { // Two old blocks contain duplicate transactions due to being // mined by faulty miners and accepted by the origin Satoshi // client. Rules have since been added to the ensure this // problem can no longer happen, but the two duplicate // transactions which were originally accepted are forever in // the block chain history and must be dealth with specially. // http://blockexplorer.com/b/91842 // http://blockexplorer.com/b/91880 if newHeight == 91842 && tx.Sha().IsEqual(dupTxHash91842) { continue } if newHeight == 91880 && tx.Sha().IsEqual(dupTxHash91880) { continue } for _, txIn := range tx.MsgTx().TxIn { if isCoinbaseInput(txIn) { continue } // It is acceptable for a transaction input to reference // the output of another transaction in this block only // if the referenced transaction comes before the // current one in this block. prevOut := &txIn.PreviousOutPoint if inFlightIndex, ok := txInFlight[prevOut.Hash]; ok { if i <= inFlightIndex { log.Warnf("InsertBlock: requested hash "+ " of %s does not exist in-flight", tx.Sha()) return 0, btcdb.ErrTxShaMissing } } else { originTxns, exists := db.txns[prevOut.Hash] if !exists { log.Warnf("InsertBlock: requested hash "+ "of %s by %s does not exist", prevOut.Hash, tx.Sha()) return 0, btcdb.ErrTxShaMissing } originTxD := originTxns[len(originTxns)-1] if prevOut.Index > uint32(len(originTxD.spentBuf)) { log.Warnf("InsertBlock: requested hash "+ "of %s with index %d does not "+ "exist", tx.Sha(), prevOut.Index) return 0, btcdb.ErrTxShaMissing } } } // Prevent duplicate transactions in the same block. if inFlightIndex, exists := txInFlight[*tx.Sha()]; exists && inFlightIndex < i { log.Warnf("Block contains duplicate transaction %s", tx.Sha()) return 0, btcdb.ErrDuplicateSha } // Prevent duplicate transactions unless the old one is fully // spent. if txns, exists := db.txns[*tx.Sha()]; exists { txD := txns[len(txns)-1] if !isFullySpent(txD) { log.Warnf("Attempt to insert duplicate "+ "transaction %s", tx.Sha()) return 0, btcdb.ErrDuplicateSha } } } db.blocks = append(db.blocks, msgBlock) db.blocksBySha[*blockHash] = newHeight // Insert information about eacj transaction and spend all of the // outputs referenced by the inputs to the transactions. for i, tx := range block.Transactions() { // Insert the transaction data. txD := tTxInsertData{ blockHeight: newHeight, offset: i, spentBuf: make([]bool, len(tx.MsgTx().TxOut)), } db.txns[*tx.Sha()] = append(db.txns[*tx.Sha()], &txD) // Spend all of the inputs. for _, txIn := range tx.MsgTx().TxIn { // Coinbase transaction has no inputs. if isCoinbaseInput(txIn) { continue } // Already checked for existing and valid ranges above. prevOut := &txIn.PreviousOutPoint originTxns := db.txns[prevOut.Hash] originTxD := originTxns[len(originTxns)-1] originTxD.spentBuf[prevOut.Index] = true } } return newHeight, nil }
// ProcessBlock is the main workhorse for handling insertion of new blocks into // the block chain. It includes functionality such as rejecting duplicate // blocks, ensuring blocks follow all rules, orphan handling, and insertion into // the block chain along with best chain selection and reorganization. // // It returns a bool which indicates whether or not the block is an orphan and // any errors that occurred during processing. The returned bool is only valid // when the error is nil. func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) { fastAdd := flags&BFFastAdd == BFFastAdd dryRun := flags&BFDryRun == BFDryRun blockHash, err := block.Sha() if err != nil { return false, err } log.Tracef("Processing block %v", blockHash) // The block must not already exist in the main chain or side chains. exists, err := b.blockExists(blockHash) if err != nil { return false, err } if exists { str := fmt.Sprintf("already have block %v", blockHash) return false, ruleError(ErrDuplicateBlock, str) } // The block must not already exist as an orphan. if _, exists := b.orphans[*blockHash]; exists { str := fmt.Sprintf("already have block (orphan) %v", blockHash) return false, ruleError(ErrDuplicateBlock, str) } // Perform preliminary sanity checks on the block and its transactions. err = checkBlockSanity(block, b.netParams.PowLimit, flags) if err != nil { return false, err } // Find the previous checkpoint and perform some additional checks based // on the checkpoint. This provides a few nice properties such as // preventing old side chain blocks before the last checkpoint, // rejecting easy to mine, but otherwise bogus, blocks that could be // used to eat memory, and ensuring expected (versus claimed) proof of // work requirements since the previous checkpoint are met. blockHeader := &block.MsgBlock().Header checkpointBlock, err := b.findPreviousCheckpoint() if err != nil { return false, err } if checkpointBlock != nil { // Ensure the block timestamp is after the checkpoint timestamp. checkpointHeader := &checkpointBlock.MsgBlock().Header checkpointTime := checkpointHeader.Timestamp if blockHeader.Timestamp.Before(checkpointTime) { str := fmt.Sprintf("block %v has timestamp %v before "+ "last checkpoint timestamp %v", blockHash, blockHeader.Timestamp, checkpointTime) return false, ruleError(ErrCheckpointTimeTooOld, str) } if !fastAdd { // Even though the checks prior to now have already ensured the // proof of work exceeds the claimed amount, the claimed amount // is a field in the block header which could be forged. This // check ensures the proof of work is at least the minimum // expected based on elapsed time since the last checkpoint and // maximum adjustment allowed by the retarget rules. duration := blockHeader.Timestamp.Sub(checkpointTime) requiredTarget := CompactToBig(b.calcEasiestDifficulty( checkpointHeader.Bits, duration)) currentTarget := CompactToBig(blockHeader.Bits) if currentTarget.Cmp(requiredTarget) > 0 { str := fmt.Sprintf("block target difficulty of %064x "+ "is too low when compared to the previous "+ "checkpoint", currentTarget) return false, ruleError(ErrDifficultyTooLow, str) } } } // Handle orphan blocks. prevHash := &blockHeader.PrevBlock if !prevHash.IsEqual(zeroHash) { prevHashExists, err := b.blockExists(prevHash) if err != nil { return false, err } if !prevHashExists { if !dryRun { log.Infof("Adding orphan block %v with parent %v", blockHash, prevHash) b.addOrphanBlock(block) } return true, nil } } // The block has passed all context independent checks and appears sane // enough to potentially accept it into the block chain. err = b.maybeAcceptBlock(block, flags) if err != nil { return false, err } // Don't process any orphans or log when the dry run flag is set. if !dryRun { // Accept any orphan blocks that depend on this block (they are // no longer orphans) and repeat for those accepted blocks until // there are no more. err := b.processOrphans(blockHash, flags) if err != nil { return false, err } log.Debugf("Accepted block %v", blockHash) } return false, nil }
// connectBestChain handles connecting the passed block to the chain while // respecting proper chain selection according to the chain with the most // proof of work. In the typical case, the new block simply extends the main // chain. However, it may also be extending (or creating) a side chain (fork) // which may or may not end up becoming the main chain depending on which fork // cumulatively has the most proof of work. // The fastAdd argument avoids the call to checkConnectBlock which does // several expensive transaction validation operations. func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fastAdd bool) error { // We haven't selected a best chain yet or we are extending the main // (best) chain with a new block. This is the most common case. if b.bestChain == nil || node.parent.hash.IsEqual(b.bestChain.hash) { // Perform several checks to verify the block can be connected // to the main chain (including whatever reorganization might // be necessary to get this node to the main chain) without // violating any rules and without actually connecting the // block. if !fastAdd { err := b.checkConnectBlock(node, block) if err != nil { return err } } // Connect the block to the main chain. err := b.connectBlock(node, block) if err != nil { return err } // Connect the parent node to this node. if node.parent != nil { node.parent.children = append(node.parent.children, node) } return nil } if fastAdd { bsha, _ := block.Sha() log.Warnf("fastAdd set in the side chain case? %v\n", bsha) } // We're extending (or creating) a side chain which may or may not // become the main chain, but in either case we need the block stored // for future processing, so add the block to the side chain holding // cache. log.Debugf("Adding block %v to side chain cache", node.hash) b.blockCache[*node.hash] = block b.index[*node.hash] = node // Connect the parent node to this node. node.inMainChain = false node.parent.children = append(node.parent.children, node) // We're extending (or creating) a side chain, but the cumulative // work for this new side chain is not enough to make it the new chain. if node.workSum.Cmp(b.bestChain.workSum) <= 0 { // Find the fork point. fork := node for ; fork.parent != nil; fork = fork.parent { if fork.inMainChain { break } } // Log information about how the block is forking the chain. if fork.hash.IsEqual(node.parent.hash) { log.Infof("FORK: Block %v forks the chain at height %d"+ "/block %v, but does not cause a reorganize", node.hash, fork.height, fork.hash) } else { log.Infof("EXTEND FORK: Block %v extends a side chain "+ "which forks the chain at height %d/block %v", node.hash, fork.height, fork.hash) } return nil } // We're extending (or creating) a side chain and the cumulative work // for this new side chain is more than the old best chain, so this side // chain needs to become the main chain. In order to accomplish that, // find the common ancestor of both sides of the fork, disconnect the // blocks that form the (now) old fork from the main chain, and attach // the blocks that form the new chain to the main chain starting at the // common ancenstor (the point where the chain forked). detachNodes, attachNodes := b.getReorganizeNodes(node) // Reorganize the chain. log.Infof("REORGANIZE: Block %v is causing a reorganize.", node.hash) err := b.reorganizeChain(detachNodes, attachNodes) if err != nil { return err } return nil }
// InsertBlock inserts raw block and transaction data from a block into the // database. The first block inserted into the database will be treated as the // genesis block. Every subsequent block insert requires the referenced parent // block to already exist. func (db *LevelDb) InsertBlock(block *btcutil.Block) (height int64, rerr error) { db.dbLock.Lock() defer db.dbLock.Unlock() defer func() { if rerr == nil { rerr = db.processBatches() } else { db.lBatch().Reset() } }() blocksha, err := block.Sha() if err != nil { log.Warnf("Failed to compute block sha %v", blocksha) return 0, err } mblock := block.MsgBlock() rawMsg, err := block.Bytes() if err != nil { log.Warnf("Failed to obtain raw block sha %v", blocksha) return 0, err } txloc, err := block.TxLoc() if err != nil { log.Warnf("Failed to obtain raw block sha %v", blocksha) return 0, err } // Insert block into database newheight, err := db.insertBlockData(blocksha, &mblock.Header.PrevBlock, rawMsg) if err != nil { log.Warnf("Failed to insert block %v %v %v", blocksha, &mblock.Header.PrevBlock, err) return 0, err } // At least two blocks in the long past were generated by faulty // miners, the sha of the transaction exists in a previous block, // detect this condition and 'accept' the block. for txidx, tx := range mblock.Transactions { txsha, err := block.TxSha(txidx) if err != nil { log.Warnf("failed to compute tx name block %v idx %v err %v", blocksha, txidx, err) return 0, err } spentbuflen := (len(tx.TxOut) + 7) / 8 spentbuf := make([]byte, spentbuflen, spentbuflen) if len(tx.TxOut)%8 != 0 { for i := uint(len(tx.TxOut) % 8); i < 8; i++ { spentbuf[spentbuflen-1] |= (byte(1) << i) } } err = db.insertTx(txsha, newheight, txloc[txidx].TxStart, txloc[txidx].TxLen, spentbuf) if err != nil { log.Warnf("block %v idx %v failed to insert tx %v %v err %v", blocksha, newheight, &txsha, txidx, err) return 0, err } // Some old blocks contain duplicate transactions // Attempt to cleanly bypass this problem by marking the // first as fully spent. // http://blockexplorer.com/b/91812 dup in 91842 // http://blockexplorer.com/b/91722 dup in 91880 if newheight == 91812 { dupsha, err := btcwire.NewShaHashFromStr("d5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599") if err != nil { panic("invalid sha string in source") } if txsha.IsEqual(dupsha) { // marking TxOut[0] as spent po := btcwire.NewOutPoint(dupsha, 0) txI := btcwire.NewTxIn(po, []byte("garbage")) var spendtx btcwire.MsgTx spendtx.AddTxIn(txI) err = db.doSpend(&spendtx) if err != nil { log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, &txsha, txidx, err) } } } if newheight == 91722 { dupsha, err := btcwire.NewShaHashFromStr("e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468") if err != nil { panic("invalid sha string in source") } if txsha.IsEqual(dupsha) { // marking TxOut[0] as spent po := btcwire.NewOutPoint(dupsha, 0) txI := btcwire.NewTxIn(po, []byte("garbage")) var spendtx btcwire.MsgTx spendtx.AddTxIn(txI) err = db.doSpend(&spendtx) if err != nil { log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, &txsha, txidx, err) } } } err = db.doSpend(tx) if err != nil { log.Warnf("block %v idx %v failed to spend tx %v %v err %v", blocksha, newheight, txsha, txidx, err) return 0, err } } return newheight, nil }
// maybeAcceptBlock potentially accepts a block into the memory block chain. // It performs several validation checks which depend on its position within // the block chain before adding it. The block is expected to have already gone // through ProcessBlock before calling this function with it. // The fastAdd argument modifies the behavior of the function by avoiding the // somewhat expensive operation: BIP34 validation, it also passes the argument // down to connectBestChain() func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, fastAdd bool) error { // Get a block node for the block previous to this one. Will be nil // if this is the genesis block. prevNode, err := b.getPrevNodeFromBlock(block) if err != nil { log.Errorf("getPrevNodeFromBlock: %v", err) return err } // The height of this block is one more than the referenced previous // block. blockHeight := int64(0) if prevNode != nil { blockHeight = prevNode.height + 1 } block.SetHeight(blockHeight) blockHeader := &block.MsgBlock().Header 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, block) if err != nil { return err } blockDifficulty := blockHeader.Bits if blockDifficulty != expectedDifficulty { str := "block difficulty of %d is not the expected value of %d" str = fmt.Sprintf(str, blockDifficulty, expectedDifficulty) return RuleError(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 !blockHeader.Timestamp.After(medianTime) { str := "block timestamp of %v is not after expected %v" str = fmt.Sprintf(str, blockHeader.Timestamp, medianTime) return RuleError(str) } // Ensure all transactions in the block are finalized. for _, tx := range block.Transactions() { if !IsFinalizedTransaction(tx, blockHeight, blockHeader.Timestamp) { str := fmt.Sprintf("block contains "+ "unfinalized transaction %v", tx.Sha()) return RuleError(str) } } } // Ensure chain matches up to predetermined checkpoints. // It's safe to ignore the error on Sha since it's already cached. blockHash, _ := block.Sha() if !b.verifyCheckpoint(blockHeight, blockHash) { // TODO(davec): This should probably be a distinct error type // (maybe CheckpointError). Since this error shouldn't happen // unless the peer is connected to a rogue network serving up an // alternate chain, the caller would likely need to react by // disconnecting peers and rolling back the chain to the last // known good point. str := fmt.Sprintf("block at height %d does not match "+ "checkpoint hash", blockHeight) return RuleError(str) } if !fastAdd { // Reject version 1 blocks once a majority of the network has // upgraded. // Rules: // 95% (950 / 1000) for main network // 75% (75 / 100) for the test network // This is part of BIP_0034. if blockHeader.Version == 1 { minRequired := uint64(950) numToCheck := uint64(1000) if b.btcnet == btcwire.TestNet3 || b.btcnet == btcwire.TestNet { minRequired = 75 numToCheck = 100 } if b.isMajorityVersion(2, prevNode, minRequired, numToCheck) { str := "new blocks with version %d are no longer valid" str = fmt.Sprintf(str, blockHeader.Version) return RuleError(str) } } // Ensure coinbase starts with serialized block heights for // blocks whose version is the serializedHeightVersion or // newer once a majority of the network has upgraded. // Rules: // 75% (750 / 1000) for main network // 51% (51 / 100) for the test network // This is part of BIP_0034. if blockHeader.Version >= serializedHeightVersion { minRequired := uint64(750) numToCheck := uint64(1000) if b.btcnet == btcwire.TestNet3 || b.btcnet == btcwire.TestNet { minRequired = 51 numToCheck = 100 } if b.isMajorityVersion(serializedHeightVersion, prevNode, minRequired, numToCheck) { expectedHeight := int64(0) if prevNode != nil { expectedHeight = prevNode.height + 1 } coinbaseTx := block.Transactions()[0] err := checkSerializedHeight(coinbaseTx, expectedHeight) if err != nil { return err } } } } // Prune block nodes which are no longer needed before creating // a new node. err = b.pruneBlockNodes() if err != nil { return err } // Create a new block node for the block and add it to the in-memory // block chain (could be either a side chain or the main chain). newNode := newBlockNode(blockHeader, blockHash, blockHeight) if prevNode != nil { newNode.parent = prevNode newNode.height = blockHeight newNode.workSum.Add(prevNode.workSum, newNode.workSum) } // Connect the passed block to the chain while respecting proper chain // selection according to the chain with the most proof of work. This // also handles validation of the transaction scripts. err = b.connectBestChain(newNode, block, fastAdd) if err != nil { return err } // Notify the caller that the new block was accepted into the block // chain. The caller would typically want to react by relaying the // inventory to other peers. b.sendNotification(NTBlockAccepted, block) return nil }
// IsCheckpointCandidate returns whether or not the passed block is a good // checkpoint candidate. // // The factors used to determine a good checkpoint are: // - The block must be in the main chain // - The block must be at least 'CheckpointConfirmations' blocks prior to the // current end of the main chain // - The timestamps for the blocks before and after the checkpoint must have // timestamps which are also before and after the checkpoint, respectively // (due to the median time allowance this is not always the case) // - The block must not contain any strange transaction such as those with // nonstandard scripts func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) { // Checkpoints must be enabled. if b.noCheckpoints { return false, fmt.Errorf("checkpoints are disabled") } blockHash, err := block.Sha() if err != nil { return false, err } // A checkpoint must be in the main chain. if !b.db.ExistsSha(blockHash) { return false, nil } // A checkpoint must be at least CheckpointConfirmations blocks before // the end of the main chain. blockHeight := block.Height() _, mainChainHeight, err := b.db.NewestSha() if err != nil { return false, err } if blockHeight > (mainChainHeight - CheckpointConfirmations) { return false, nil } // Get the previous block. prevHash := &block.MsgBlock().Header.PrevBlock prevBlock, err := b.db.FetchBlockBySha(prevHash) if err != nil { return false, err } // Get the next block. nextHash, err := b.db.FetchBlockShaByHeight(blockHeight + 1) if err != nil { return false, err } nextBlock, err := b.db.FetchBlockBySha(nextHash) if err != nil { return false, err } // A checkpoint must have timestamps for the block and the blocks on // either side of it in order (due to the median time allowance this is // not always the case). prevTime := prevBlock.MsgBlock().Header.Timestamp curTime := block.MsgBlock().Header.Timestamp nextTime := nextBlock.MsgBlock().Header.Timestamp if prevTime.After(curTime) || nextTime.Before(curTime) { return false, nil } // A checkpoint must have transactions that only contain standard // scripts. for _, tx := range block.Transactions() { if isNonstandardTransaction(tx) { return false, nil } } return true, nil }
// rescanBlock rescans all transactions in a single block. This is a // helper function for handleRescan. func rescanBlock(s *rpcServer, cmd *btcws.RescanCmd, c handlerChans, blk *btcutil.Block) { for _, tx := range blk.Transactions() { var txReply *btcdb.TxListReply txouts: for txOutIdx, txout := range tx.MsgTx().TxOut { _, addrs, _, err := btcscript.ExtractPkScriptAddrs( txout.PkScript, s.server.btcnet) if err != nil { continue txouts } for _, addr := range addrs { encodedAddr := addr.EncodeAddress() if _, ok := cmd.Addresses[encodedAddr]; !ok { continue } // TODO(jrick): This lookup is expensive and can be avoided // if the wallet is sent the previous outpoints for all inputs // of the tx, so any can removed from the utxo set (since // they are, as of this tx, now spent). if txReply == nil { txReplyList, err := s.server.db.FetchTxBySha(tx.Sha()) if err != nil { rpcsLog.Errorf("Tx Sha %v not found by db", tx.Sha()) continue txouts } for i := range txReplyList { if txReplyList[i].Height == blk.Height() { txReply = txReplyList[i] break } } } // Sha never errors. blksha, _ := blk.Sha() ntfn := &btcws.ProcessedTxNtfn{ Receiver: encodedAddr, Amount: txout.Value, TxID: tx.Sha().String(), TxOutIndex: uint32(txOutIdx), PkScript: hex.EncodeToString(txout.PkScript), BlockHash: blksha.String(), BlockHeight: int32(blk.Height()), BlockIndex: tx.Index(), BlockTime: blk.MsgBlock().Header.Timestamp.Unix(), Spent: txReply.TxSpent[txOutIdx], } select { case <-c.disconnected: return default: c.n <- ntfn } } } } }