2014-01-09 06:52:54 +01:00
|
|
|
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
2013-07-18 16:49:28 +02:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package btcchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"github.com/conformal/btcutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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.
|
2013-11-18 22:23:51 +01:00
|
|
|
// 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 {
|
2013-07-18 16:49:28 +02:00
|
|
|
// 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 {
|
2013-07-24 19:31:14 +02:00
|
|
|
log.Errorf("getPrevNodeFromBlock: %v", err)
|
2013-07-18 16:49:28 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-10-11 17:24:13 +02:00
|
|
|
// The height of this block is one more than the referenced previous
|
|
|
|
// block.
|
2013-07-18 16:49:28 +02:00
|
|
|
blockHeight := int64(0)
|
|
|
|
if prevNode != nil {
|
|
|
|
blockHeight = prevNode.height + 1
|
|
|
|
}
|
2013-07-29 23:58:48 +02:00
|
|
|
block.SetHeight(blockHeight)
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2014-01-19 19:38:31 +01:00
|
|
|
blockHeader := &block.MsgBlock().Header
|
2013-11-18 22:23:51 +01:00
|
|
|
if !fastAdd {
|
|
|
|
// Ensure the difficulty specified in the block header matches
|
|
|
|
// the calculated difficulty based on the previous block and
|
2014-01-09 18:36:25 +01:00
|
|
|
// difficulty retarget rules.
|
2014-03-02 19:17:36 +01:00
|
|
|
expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode,
|
|
|
|
block.MsgBlock().Header.Timestamp)
|
2013-11-18 22:23:51 +01:00
|
|
|
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)
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2013-11-18 22:23:51 +01:00
|
|
|
// 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)
|
2013-07-18 16:49:28 +02:00
|
|
|
return RuleError(str)
|
|
|
|
}
|
|
|
|
|
2013-11-18 22:23:51 +01:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2014-01-09 18:36:25 +01:00
|
|
|
|
2013-07-18 16:49:28 +02:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2014-02-21 22:03:44 +01:00
|
|
|
// 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()
|
2014-02-21 20:02:59 +01:00
|
|
|
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(str)
|
|
|
|
}
|
|
|
|
|
2013-11-18 22:23:51 +01:00
|
|
|
if !fastAdd {
|
|
|
|
// Reject version 1 blocks once a majority of the network has
|
2014-05-26 17:27:50 +02:00
|
|
|
// upgraded. This is part of BIP0034.
|
2013-11-18 22:23:51 +01:00
|
|
|
if blockHeader.Version == 1 {
|
2014-05-26 17:27:50 +02:00
|
|
|
if b.isMajorityVersion(2, prevNode,
|
|
|
|
b.netParams.BlockV1RejectNumRequired,
|
|
|
|
b.netParams.BlockV1RejectNumToCheck) {
|
|
|
|
|
2013-11-18 22:23:51 +01:00
|
|
|
str := "new blocks with version %d are no longer valid"
|
|
|
|
str = fmt.Sprintf(str, blockHeader.Version)
|
|
|
|
return RuleError(str)
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
|
2013-11-18 22:23:51 +01:00
|
|
|
// Ensure coinbase starts with serialized block heights for
|
|
|
|
// blocks whose version is the serializedHeightVersion or
|
2014-05-26 17:27:50 +02:00
|
|
|
// newer once a majority of the network has upgraded. This is
|
|
|
|
// part of BIP0034.
|
2013-11-18 22:23:51 +01:00
|
|
|
if blockHeader.Version >= serializedHeightVersion {
|
|
|
|
if b.isMajorityVersion(serializedHeightVersion,
|
2014-05-26 17:27:50 +02:00
|
|
|
prevNode,
|
|
|
|
b.netParams.CoinbaseBlockHeightNumRequired,
|
|
|
|
b.netParams.CoinbaseBlockHeightNumToCheck) {
|
2013-11-18 22:23:51 +01:00
|
|
|
|
|
|
|
expectedHeight := int64(0)
|
|
|
|
if prevNode != nil {
|
|
|
|
expectedHeight = prevNode.height + 1
|
|
|
|
}
|
|
|
|
coinbaseTx := block.Transactions()[0]
|
|
|
|
err := checkSerializedHeight(coinbaseTx,
|
|
|
|
expectedHeight)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
}
|
2014-01-28 20:45:49 +01:00
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2014-01-28 20:45:49 +01:00
|
|
|
// Prune block nodes which are no longer needed before creating
|
|
|
|
// a new node.
|
|
|
|
err = b.pruneBlockNodes()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2013-08-01 18:31:42 +02:00
|
|
|
}
|
|
|
|
|
2013-07-18 16:49:28 +02:00
|
|
|
// 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).
|
2014-01-19 20:00:12 +01:00
|
|
|
newNode := newBlockNode(blockHeader, blockHash, blockHeight)
|
2013-07-18 16:49:28 +02:00
|
|
|
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.
|
2013-11-18 22:23:51 +01:00
|
|
|
err = b.connectBestChain(newNode, block, fastAdd)
|
2013-07-18 16:49:28 +02:00
|
|
|
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
|
|
|
|
}
|