// submitBlock submits the passed block to network after ensuring it passes all // of the consensus validation rules. func (m *CPUMiner) submitBlock(block *dcrutil.Block) bool { m.submitBlockLock.Lock() defer m.submitBlockLock.Unlock() _, latestHeight := m.server.blockManager.chainState.Best() // Be sure to set this so ProcessBlock doesn't fail! - Decred block.SetHeight(latestHeight + 1) // 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, blockchain.BFNone) if err != nil { // Anything other than a rule violation is an unexpected error, // so log that error as an internal error. if rErr, ok := err.(blockchain.RuleError); !ok { minrLog.Errorf("Unexpected error while processing "+ "block submitted via CPU miner: %v", err) return false } else { // Occasionally errors are given out for timing errors with // ResetMinDifficulty and high block works that is above // the target. Feed these to debug. if m.server.chainParams.ResetMinDifficulty && rErr.ErrorCode == blockchain.ErrHighHash { minrLog.Debugf("Block submitted via CPU miner rejected "+ "because of ResetMinDifficulty time sync failure: %v", err) return false } else { // Other rule errors should be reported. minrLog.Errorf("Block submitted via CPU miner rejected: %v", err) return false } } } if isOrphan { minrLog.Errorf("Block submitted via CPU miner is an orphan building "+ "on parent %v", block.MsgBlock().Header.PrevBlock) return false } // The block was accepted. coinbaseTxOuts := block.MsgBlock().Transactions[0].TxOut coinbaseTxGenerated := int64(0) for _, out := range coinbaseTxOuts { coinbaseTxGenerated += out.Value } minrLog.Infof("Block submitted via CPU miner accepted (hash %s, "+ "height %v, amount %v)", block.Sha(), block.Height(), dcrutil.Amount(coinbaseTxGenerated)) return true }
// loadReorgBlocks reads files containing decred block data (bzipped but // otherwise in the format bitcoind writes) from disk and returns them as an // array of dcrutil.Block. This is copied from the blockchain package, which // itself largely borrowed it from the test code in this package. func loadReorgBlocks(filename string) ([]*dcrutil.Block, error) { filename = filepath.Join("../blockchain/testdata/", filename) fi, err := os.Open(filename) if err != nil { return nil, err } bcStream := bzip2.NewReader(fi) defer fi.Close() // Create a buffer of the read file bcBuf := new(bytes.Buffer) bcBuf.ReadFrom(bcStream) // Create decoder from the buffer and a map to store the data bcDecoder := gob.NewDecoder(bcBuf) blockchain := make(map[int64][]byte) // Decode the blockchain into the map if err := bcDecoder.Decode(&blockchain); err != nil { return nil, err } var block *dcrutil.Block blocks := make([]*dcrutil.Block, 0, len(blockchain)) for height := int64(0); height < int64(len(blockchain)); height++ { block, err = dcrutil.NewBlockFromBytes(blockchain[height]) if err != nil { return blocks, err } block.SetHeight(height) blocks = append(blocks, block) } return blocks, 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: // - BFDryRun: The memory chain index will not be pruned and no accept // notification will be sent since the block is not being accepted. // // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) maybeAcceptBlock(block *dcrutil.Block, flags BehaviorFlags) (bool, error) { 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.Debugf("getPrevNodeFromBlock: %v", err) return false, 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) // The block must pass all of the validation rules which depend on the // position of the block within the block chain. err = b.checkBlockContext(block, prevNode, flags) if err != nil { return false, err } // Prune stake nodes and block nodes which are no longer needed before // creating a new node. if !dryRun { err := b.pruner.pruneChainIfNeeded() if err != nil { return false, 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). blockHeader := &block.MsgBlock().Header newNode := newBlockNode(blockHeader, block.Sha(), blockHeight, ticketsSpentInBlock(block), ticketsRevokedInBlock(block), voteVersionsInBlock(block, b.chainParams)) if prevNode != nil { newNode.parent = prevNode newNode.height = blockHeight newNode.workSum.Add(prevNode.workSum, newNode.workSum) } // Fetching a stake node could enable a new DoS vector, so restrict // this only to blocks that are recent in history. if newNode.height < b.bestNode.height-minMemoryNodes { newNode.stakeNode, err = b.fetchStakeNode(newNode) if err != nil { return false, err } newNode.stakeUndoData = newNode.stakeNode.UndoData() } // 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. var onMainChain bool onMainChain, err = b.connectBestChain(newNode, block, flags) if err != nil { return false, 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.chainLock.Unlock() b.sendNotification(NTBlockAccepted, &BlockAcceptedNtfnsData{onMainChain, block}) b.chainLock.Lock() } return onMainChain, 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: // - 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 *dcrutil.Block, flags BehaviorFlags) (bool, error) { 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.Debugf("getPrevNodeFromBlock: %v", err) return false, 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) // The block must pass all of the validation rules which depend on the // position of the block within the block chain. err = b.checkBlockContext(block, prevNode, flags) if err != nil { return false, err } // Prune block nodes which are no longer needed before creating // a new node. if !dryRun { err = b.pruneBlockNodes() if err != nil { return false, 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). blockHeader := &block.MsgBlock().Header var voteBitsStake []uint16 for _, stx := range block.STransactions() { if is, _ := stake.IsSSGen(stx); is { vb := stake.GetSSGenVoteBits(stx) voteBitsStake = append(voteBitsStake, vb) } } newNode := newBlockNode(blockHeader, block.Sha(), blockHeight, voteBitsStake) 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. var onMainChain bool onMainChain, err = b.connectBestChain(newNode, block, flags) if err != nil { return false, 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, &BlockAcceptedNtfnsData{onMainChain, block}) } return onMainChain, nil }