Refactor several network-specific params to btcnet.

This commit refactors the code to make use of the btcnet package for
network-specific parameters instead of switching on the specific network.

For example, the percentage of the network that needs to run version 2
blocks is different between testnet and mainnet.  Previously the code was
using a switch to choose these values.  With this refactor, those
parameters are part of the network parameters provided when creating a new
chain instance.

Another example is checkpoints, which have been moved to btcnet so they
can be specified by the caller instead of hard coded into this package.
As a result, the checkpoints for the standard networks are now specified
in the btcnet package.

This makes it easier to add new networks such as a testnet4 should it
become needed.  It also allows callers to define their own custom network
parameters without having to modify the code of the package to add new
switch cases for the custom network.
This commit is contained in:
Dave Collins 2014-05-26 10:27:50 -05:00
parent d4082e4f24
commit 4579cfb71b
11 changed files with 106 additions and 306 deletions

View file

@ -7,7 +7,6 @@ package btcchain
import ( import (
"fmt" "fmt"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire"
) )
// maybeAcceptBlock potentially accepts a block into the memory block chain. // maybeAcceptBlock potentially accepts a block into the memory block chain.
@ -109,21 +108,12 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, fastAdd bool) error
if !fastAdd { if !fastAdd {
// Reject version 1 blocks once a majority of the network has // Reject version 1 blocks once a majority of the network has
// upgraded. // upgraded. This is part of BIP0034.
// Rules:
// 95% (950 / 1000) for main network
// 75% (75 / 100) for the test network
// This is part of BIP_0034.
if blockHeader.Version == 1 { if blockHeader.Version == 1 {
minRequired := uint64(950) if b.isMajorityVersion(2, prevNode,
numToCheck := uint64(1000) b.netParams.BlockV1RejectNumRequired,
if b.btcnet == btcwire.TestNet3 || b.btcnet == b.netParams.BlockV1RejectNumToCheck) {
btcwire.TestNet {
minRequired = 75
numToCheck = 100
}
if b.isMajorityVersion(2, prevNode, minRequired,
numToCheck) {
str := "new blocks with version %d are no longer valid" str := "new blocks with version %d are no longer valid"
str = fmt.Sprintf(str, blockHeader.Version) str = fmt.Sprintf(str, blockHeader.Version)
return RuleError(str) return RuleError(str)
@ -132,21 +122,13 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, fastAdd bool) error
// Ensure coinbase starts with serialized block heights for // Ensure coinbase starts with serialized block heights for
// blocks whose version is the serializedHeightVersion or // blocks whose version is the serializedHeightVersion or
// newer once a majority of the network has upgraded. // newer once a majority of the network has upgraded. This is
// Rules: // part of BIP0034.
// 75% (750 / 1000) for main network
// 51% (51 / 100) for the test network
// This is part of BIP_0034.
if blockHeader.Version >= serializedHeightVersion { 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, if b.isMajorityVersion(serializedHeightVersion,
prevNode, minRequired, numToCheck) { prevNode,
b.netParams.CoinbaseBlockHeightNumRequired,
b.netParams.CoinbaseBlockHeightNumToCheck) {
expectedHeight := int64(0) expectedHeight := int64(0)
if prevNode != nil { if prevNode != nil {

View file

@ -41,7 +41,7 @@ func (b *BlockChain) BlockLocatorFromHash(hash *btcwire.ShaHash) BlockLocator {
locator = append(locator, hash) locator = append(locator, hash)
// Nothing more to do if a locator for the genesis hash was requested. // Nothing more to do if a locator for the genesis hash was requested.
if hash.IsEqual(b.chainParams().GenesisHash) { if hash.IsEqual(b.netParams.GenesisHash) {
return locator return locator
} }
@ -124,7 +124,7 @@ func (b *BlockChain) BlockLocatorFromHash(hash *btcwire.ShaHash) BlockLocator {
} }
// Append the appropriate genesis block. // Append the appropriate genesis block.
locator = append(locator, b.chainParams().GenesisHash) locator = append(locator, b.netParams.GenesisHash)
return locator return locator
} }

View file

@ -9,6 +9,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/conformal/btcdb" "github.com/conformal/btcdb"
"github.com/conformal/btcnet"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"math/big" "math/big"
@ -141,7 +142,8 @@ func removeChildNode(children []*blockNode, node *blockNode) []*blockNode {
// selection with reorganization. // selection with reorganization.
type BlockChain struct { type BlockChain struct {
db btcdb.Db db btcdb.Db
btcnet btcwire.BitcoinNet netParams *btcnet.Params
checkpointsByHeight map[int64]*btcnet.Checkpoint
notifications NotificationCallback notifications NotificationCallback
root *blockNode root *blockNode
bestChain *blockNode bestChain *blockNode
@ -154,7 +156,7 @@ type BlockChain struct {
blockCache map[btcwire.ShaHash]*btcutil.Block blockCache map[btcwire.ShaHash]*btcutil.Block
noVerify bool noVerify bool
noCheckpoints bool noCheckpoints bool
nextCheckpoint *Checkpoint nextCheckpoint *btcnet.Checkpoint
checkpointBlock *btcutil.Block checkpointBlock *btcutil.Block
} }
@ -500,7 +502,7 @@ func (b *BlockChain) getPrevNodeFromNode(node *blockNode) (*blockNode, error) {
} }
// Genesis block. // Genesis block.
if node.hash.IsEqual(b.chainParams().GenesisHash) { if node.hash.IsEqual(b.netParams.GenesisHash) {
return nil, nil return nil, nil
} }
@ -633,7 +635,7 @@ func (b *BlockChain) isMajorityVersion(minVer uint32, startNode *blockNode, numR
func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error) { func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error) {
// Genesis block. // Genesis block.
if startNode == nil { if startNode == nil {
return b.chainParams().GenesisBlock.Header.Timestamp, nil return b.netParams.GenesisBlock.Header.Timestamp, nil
} }
// Create a slice of the previous few block timestamps used to calculate // Create a slice of the previous few block timestamps used to calculate
@ -1015,10 +1017,21 @@ func (b *BlockChain) IsCurrent() bool {
// Notification and NotificationType for details on the types and contents of // Notification and NotificationType for details on the types and contents of
// notifications. The provided callback can be nil if the caller is not // notifications. The provided callback can be nil if the caller is not
// interested in receiving notifications. // interested in receiving notifications.
func New(db btcdb.Db, btcnet btcwire.BitcoinNet, c NotificationCallback) *BlockChain { func New(db btcdb.Db, params *btcnet.Params, c NotificationCallback) *BlockChain {
// Generate a checkpoint by height map from the provided checkpoints.
var checkpointsByHeight map[int64]*btcnet.Checkpoint
if len(params.Checkpoints) > 0 {
checkpointsByHeight = make(map[int64]*btcnet.Checkpoint)
for i := range params.Checkpoints {
checkpoint := &params.Checkpoints[i]
checkpointsByHeight[checkpoint.Height] = checkpoint
}
}
b := BlockChain{ b := BlockChain{
db: db, db: db,
btcnet: btcnet, netParams: params,
checkpointsByHeight: checkpointsByHeight,
notifications: c, notifications: c,
root: nil, root: nil,
bestChain: nil, bestChain: nil,

View file

@ -6,6 +6,7 @@ package btcchain
import ( import (
"fmt" "fmt"
"github.com/conformal/btcnet"
"github.com/conformal/btcscript" "github.com/conformal/btcscript"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
@ -15,64 +16,6 @@ import (
// best block chain that a good checkpoint candidate must be. // best block chain that a good checkpoint candidate must be.
const CheckpointConfirmations = 2016 const CheckpointConfirmations = 2016
// Checkpoint identifies a known good point in the block chain. Using
// checkpoints allows a few optimizations for old blocks during initial download
// and also prevents forks from old blocks.
//
// Each checkpoint is selected by the core developers based upon several
// factors. See the documentation for IsCheckpointCandidate for details
// on the selection criteria.
//
// As alluded to above, this package provides an IsCheckpointCandidate function
// which programatically identifies a block as a checkpoint candidate. The idea
// is that candidates are reviewed by a developer to make the final decision and
// then manually added to the list of checkpoints.
type Checkpoint struct {
Height int64
Hash *btcwire.ShaHash
}
// checkpointData groups checkpoints and other pertinent checkpoint data into
// a single type.
type checkpointData struct {
// Checkpoints ordered from oldest to newest.
checkpoints []Checkpoint
// A map that will be automatically generated with the heights from
// the checkpoints as keys.
checkpointsByHeight map[int64]*Checkpoint
}
// checkpointDataMainNet contains checkpoint data for the main network.
var checkpointDataMainNet = checkpointData{
checkpoints: []Checkpoint{
{11111, newShaHashFromStr("0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")},
{33333, newShaHashFromStr("000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")},
{74000, newShaHashFromStr("0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")},
{105000, newShaHashFromStr("00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")},
{134444, newShaHashFromStr("00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")},
{168000, newShaHashFromStr("000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")},
{193000, newShaHashFromStr("000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")},
{210000, newShaHashFromStr("000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")},
{216116, newShaHashFromStr("00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")},
{225430, newShaHashFromStr("00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")},
{250000, newShaHashFromStr("000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")},
{267300, newShaHashFromStr("000000000000000a83fbd660e918f218bf37edd92b748ad940483c7c116179ac")},
{279000, newShaHashFromStr("0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")},
{300255, newShaHashFromStr("0000000000000000162804527c6e9b9f0563a280525f9d08c12041def0a0f3b2")},
},
checkpointsByHeight: nil, // Automatically generated in init.
}
// checkpointDataTestNet3 contains checkpoint data for the test network (version
// 3).
var checkpointDataTestNet3 = checkpointData{
checkpoints: []Checkpoint{
{546, newShaHashFromStr("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")},
},
checkpointsByHeight: nil, // Automatically generated in init.
}
// newShaHashFromStr converts the passed big-endian hex string into a // newShaHashFromStr converts the passed big-endian hex string into a
// btcwire.ShaHash. It only differs from the one available in btcwire in that // btcwire.ShaHash. It only differs from the one available in btcwire in that
// it ignores the error since it will only (and must only) be called with // it ignores the error since it will only (and must only) be called with
@ -89,38 +32,26 @@ func (b *BlockChain) DisableCheckpoints(disable bool) {
b.noCheckpoints = disable b.noCheckpoints = disable
} }
// checkpointData returns the appropriate checkpoint data set depending on the
// network configured for the block chain.
func (b *BlockChain) checkpointData() *checkpointData {
switch b.btcnet {
case btcwire.TestNet3:
return &checkpointDataTestNet3
case btcwire.MainNet:
return &checkpointDataMainNet
}
return nil
}
// Checkpoints returns a slice of checkpoints (regardless of whether they are // Checkpoints returns a slice of checkpoints (regardless of whether they are
// already known). When checkpoints are disabled or there are no checkpoints // already known). When checkpoints are disabled or there are no checkpoints
// for the active network, it will return nil. // for the active network, it will return nil.
func (b *BlockChain) Checkpoints() []Checkpoint { func (b *BlockChain) Checkpoints() []btcnet.Checkpoint {
if b.noCheckpoints || b.checkpointData() == nil { if b.noCheckpoints || len(b.netParams.Checkpoints) == 0 {
return nil return nil
} }
return b.checkpointData().checkpoints return b.netParams.Checkpoints
} }
// LatestCheckpoint returns the most recent checkpoint (regardless of whether it // LatestCheckpoint returns the most recent checkpoint (regardless of whether it
// is already known). When checkpoints are disabled or there are no checkpoints // is already known). When checkpoints are disabled or there are no checkpoints
// for the active network, it will return nil. // for the active network, it will return nil.
func (b *BlockChain) LatestCheckpoint() *Checkpoint { func (b *BlockChain) LatestCheckpoint() *btcnet.Checkpoint {
if b.noCheckpoints || b.checkpointData() == nil { if b.noCheckpoints || len(b.netParams.Checkpoints) == 0 {
return nil return nil
} }
checkpoints := b.checkpointData().checkpoints checkpoints := b.netParams.Checkpoints
return &checkpoints[len(checkpoints)-1] return &checkpoints[len(checkpoints)-1]
} }
@ -128,12 +59,12 @@ func (b *BlockChain) LatestCheckpoint() *Checkpoint {
// match the hard-coded checkpoint data. It also returns true if there is no // match the hard-coded checkpoint data. It also returns true if there is no
// checkpoint data for the passed block height. // checkpoint data for the passed block height.
func (b *BlockChain) verifyCheckpoint(height int64, hash *btcwire.ShaHash) bool { func (b *BlockChain) verifyCheckpoint(height int64, hash *btcwire.ShaHash) bool {
if b.noCheckpoints || b.checkpointData() == nil { if b.noCheckpoints || len(b.netParams.Checkpoints) == 0 {
return true return true
} }
// Nothing to check if there is no checkpoint data for the block height. // Nothing to check if there is no checkpoint data for the block height.
checkpoint, exists := b.checkpointData().checkpointsByHeight[height] checkpoint, exists := b.checkpointsByHeight[height]
if !exists { if !exists {
return true return true
} }
@ -152,12 +83,12 @@ func (b *BlockChain) verifyCheckpoint(height int64, hash *btcwire.ShaHash) bool
// associated block. It returns nil if a checkpoint can't be found (this should // associated block. It returns nil if a checkpoint can't be found (this should
// really only happen for blocks before the first checkpoint). // really only happen for blocks before the first checkpoint).
func (b *BlockChain) findPreviousCheckpoint() (*btcutil.Block, error) { func (b *BlockChain) findPreviousCheckpoint() (*btcutil.Block, error) {
if b.noCheckpoints || b.checkpointData() == nil { if b.noCheckpoints || len(b.netParams.Checkpoints) == 0 {
return nil, nil return nil, nil
} }
// No checkpoints. // No checkpoints.
checkpoints := b.checkpointData().checkpoints checkpoints := b.netParams.Checkpoints
numCheckpoints := len(checkpoints) numCheckpoints := len(checkpoints)
if numCheckpoints == 0 { if numCheckpoints == 0 {
return nil, nil return nil, nil
@ -275,6 +206,9 @@ func isNonstandardTransaction(tx *btcutil.Tx) bool {
// (due to the median time allowance this is not always the case) // (due to the median time allowance this is not always the case)
// - The block must not contain any strange transaction such as those with // - The block must not contain any strange transaction such as those with
// nonstandard scripts // nonstandard scripts
//
// The intent is that candidates are reviewed by a developer to make the final
// decision and then manually added to the list of checkpoints for a network.
func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) { func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) {
// Checkpoints must be enabled. // Checkpoints must be enabled.
if b.noCheckpoints { if b.noCheckpoints {
@ -339,20 +273,3 @@ func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) {
return true, nil return true, nil
} }
// init is called on package load.
func init() {
// Generate the checkpoint by height maps from the checkpoint data
// when the package loads.
checkpointInitializeList := []*checkpointData{
&checkpointDataMainNet,
&checkpointDataTestNet3,
}
for _, data := range checkpointInitializeList {
data.checkpointsByHeight = make(map[int64]*Checkpoint)
for i := range data.checkpoints {
checkpoint := &data.checkpoints[i]
data.checkpointsByHeight[checkpoint.Height] = checkpoint
}
}
}

View file

@ -10,6 +10,7 @@ import (
"github.com/conformal/btcdb" "github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb" _ "github.com/conformal/btcdb/ldb"
_ "github.com/conformal/btcdb/memdb" _ "github.com/conformal/btcdb/memdb"
"github.com/conformal/btcnet"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"os" "os"
@ -110,6 +111,6 @@ func chainSetup(dbName string) (*btcchain.BlockChain, func(), error) {
return nil, nil, err return nil, nil, err
} }
chain := btcchain.New(db, btcwire.MainNet, nil) chain := btcchain.New(db, btcnet.MainNetParams, nil)
return chain, teardown, nil return chain, teardown, nil
} }

View file

@ -193,19 +193,12 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
durationVal := int64(duration) durationVal := int64(duration)
adjustmentFactor := big.NewInt(retargetAdjustmentFactor) adjustmentFactor := big.NewInt(retargetAdjustmentFactor)
// Choose the correct proof of work limit for the active network.
powLimit := b.chainParams().PowLimit
powLimitBits := b.chainParams().PowLimitBits
// The test network rules allow minimum difficulty blocks after more // The test network rules allow minimum difficulty blocks after more
// than twice the desired amount of time needed to generate a block has // than twice the desired amount of time needed to generate a block has
// elapsed. // elapsed.
switch b.btcnet { if b.netParams.ResetMinDifficulty {
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
if durationVal > int64(targetSpacing)*2 { if durationVal > int64(targetSpacing)*2 {
return powLimitBits return b.netParams.PowLimitBits
} }
} }
@ -214,14 +207,14 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
// the number of retargets for the duration and starting difficulty // the number of retargets for the duration and starting difficulty
// multiplied by the max adjustment factor. // multiplied by the max adjustment factor.
newTarget := CompactToBig(bits) newTarget := CompactToBig(bits)
for durationVal > 0 && newTarget.Cmp(powLimit) < 0 { for durationVal > 0 && newTarget.Cmp(b.netParams.PowLimit) < 0 {
newTarget.Mul(newTarget, adjustmentFactor) newTarget.Mul(newTarget, adjustmentFactor)
durationVal -= maxRetargetTimespan durationVal -= maxRetargetTimespan
} }
// Limit new value to the proof of work limit. // Limit new value to the proof of work limit.
if newTarget.Cmp(powLimit) > 0 { if newTarget.Cmp(b.netParams.PowLimit) > 0 {
newTarget.Set(powLimit) newTarget.Set(b.netParams.PowLimit)
} }
return BigToCompact(newTarget) return BigToCompact(newTarget)
@ -232,9 +225,10 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, error) { func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, error) {
// Search backwards through the chain for the last block without // Search backwards through the chain for the last block without
// the special rule applied. // the special rule applied.
powLimitBits := b.chainParams().PowLimitBits
iterNode := startNode iterNode := startNode
for iterNode != nil && iterNode.height%BlocksPerRetarget != 0 && iterNode.bits == powLimitBits { for iterNode != nil && iterNode.height%BlocksPerRetarget != 0 &&
iterNode.bits == b.netParams.PowLimitBits {
// Get the previous block node. This function is used over // Get the previous block node. This function is used over
// simply accessing iterNode.parent directly as it will // simply accessing iterNode.parent directly as it will
// dynamically create previous block nodes as needed. This // dynamically create previous block nodes as needed. This
@ -250,7 +244,7 @@ func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, er
// Return the found difficulty or the minimum difficulty if no // Return the found difficulty or the minimum difficulty if no
// appropriate block was found. // appropriate block was found.
lastBits := powLimitBits lastBits := b.netParams.PowLimitBits
if iterNode != nil { if iterNode != nil {
lastBits = iterNode.bits lastBits = iterNode.bits
} }
@ -263,32 +257,24 @@ func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, er
// the exported version uses the current best chain as the previous block node // the exported version uses the current best chain as the previous block node
// while this function accepts any block node. // while this function accepts any block node.
func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTime time.Time) (uint32, error) { func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTime time.Time) (uint32, error) {
// Choose the correct proof of work limit for the active network.
powLimit := b.chainParams().PowLimit
powLimitBits := b.chainParams().PowLimitBits
// Genesis block. // Genesis block.
if lastNode == nil { if lastNode == nil {
return powLimitBits, nil return b.netParams.PowLimitBits, nil
} }
// Return the previous block's difficulty requirements if this block // Return the previous block's difficulty requirements if this block
// is not at a difficulty retarget interval. // is not at a difficulty retarget interval.
if (lastNode.height+1)%BlocksPerRetarget != 0 { if (lastNode.height+1)%BlocksPerRetarget != 0 {
// The difficulty rules differ between networks.
switch b.btcnet {
// The test network rules allow minimum difficulty blocks after // The test network rules allow minimum difficulty blocks after
// more than twice the desired amount of time needed to generate // more than twice the desired amount of time needed to generate
// a block has elapsed. // a block has elapsed.
case btcwire.TestNet: if b.netParams.ResetMinDifficulty {
fallthrough
case btcwire.TestNet3:
// Return minimum difficulty when more than twice the // Return minimum difficulty when more than twice the
// desired amount of time needed to generate a block has // desired amount of time needed to generate a block has
// elapsed. // elapsed.
allowMinTime := lastNode.timestamp.Add(targetSpacing * 2) allowMinTime := lastNode.timestamp.Add(targetSpacing * 2)
if newBlockTime.After(allowMinTime) { if newBlockTime.After(allowMinTime) {
return powLimitBits, nil return b.netParams.PowLimitBits, nil
} }
// The block was mined within the desired timeframe, so // The block was mined within the desired timeframe, so
@ -299,16 +285,12 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
return 0, err return 0, err
} }
return prevBits, nil return prevBits, nil
}
// For the main network (or any unrecognized networks), simply // For the main network (or any unrecognized networks), simply
// return the previous block's difficulty. // return the previous block's difficulty requirements.
case btcwire.MainNet:
fallthrough
default:
// Return the previous block's difficulty requirements.
return lastNode.bits, nil return lastNode.bits, nil
} }
}
// Get the block node at the previous retarget (targetTimespan days // Get the block node at the previous retarget (targetTimespan days
// worth of blocks). // worth of blocks).
@ -350,8 +332,8 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
newTarget.Div(newTarget, big.NewInt(int64(targetTimespan))) newTarget.Div(newTarget, big.NewInt(int64(targetTimespan)))
// Limit new value to the proof of work limit. // Limit new value to the proof of work limit.
if newTarget.Cmp(powLimit) > 0 { if newTarget.Cmp(b.netParams.PowLimit) > 0 {
newTarget.Set(powLimit) newTarget.Set(b.netParams.PowLimit)
} }
// Log new target difficulty and return it. The new target logging is // Log new target difficulty and return it. The new target logging is

12
doc.go
View file

@ -73,8 +73,8 @@ intentionally causes an error by attempting to process a duplicate block.
"github.com/conformal/btcchain" "github.com/conformal/btcchain"
"github.com/conformal/btcdb" "github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb" _ "github.com/conformal/btcdb/ldb"
"github.com/conformal/btcnet"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"os" "os"
) )
@ -85,20 +85,20 @@ intentionally causes an error by attempting to process a duplicate block.
// calls to os.Remove would not be used either, but again, we want // calls to os.Remove would not be used either, but again, we want
// a complete working example here, so we make sure to remove the // a complete working example here, so we make sure to remove the
// database. // database.
dbName := "example.db" dbName := "exampledb"
_ = os.Remove(dbName) _ = os.RemoveAll(dbName)
db, err := btcdb.CreateDB("leveldb", dbName) db, err := btcdb.CreateDB("leveldb", dbName)
if err != nil { if err != nil {
fmt.Printf("Failed to create database: %v\n", err) fmt.Printf("Failed to create database: %v\n", err)
return return
} }
defer os.Remove(dbName) // Ignore error. defer os.RemoveAll(dbName) // Ignore error.
defer db.Close() defer db.Close()
// Insert the main network genesis block. This is part of the initial // Insert the main network genesis block. This is part of the initial
// database setup. Like above, this typically would not be needed when // database setup. Like above, this typically would not be needed when
// opening an existing database. // opening an existing database.
genesisBlock := btcutil.NewBlock(&btcwire.GenesisBlock) genesisBlock := btcutil.NewBlock(btcnet.MainNetParams.GenesisBlock)
_, err = db.InsertBlock(genesisBlock) _, err = db.InsertBlock(genesisBlock)
if err != nil { if err != nil {
fmt.Printf("Failed to insert genesis block: %v\n", err) fmt.Printf("Failed to insert genesis block: %v\n", err)
@ -107,7 +107,7 @@ intentionally causes an error by attempting to process a duplicate block.
// Create a new BlockChain instance using the underlying database for // Create a new BlockChain instance using the underlying database for
// the main bitcoin network and ignore notifications. // the main bitcoin network and ignore notifications.
chain := btcchain.New(db, btcwire.MainNet, nil) chain := btcchain.New(db, btcnet.MainNetParams, nil)
// Process a block. For this example, we are going to intentionally // Process a block. For this example, we are going to intentionally
// cause an error by trying to process the genesis block which already // cause an error by trying to process the genesis block which already

101
params.go
View file

@ -1,101 +0,0 @@
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package btcchain
import (
"github.com/conformal/btcwire"
"math/big"
)
// Params houses parameters unique to various bitcoin networks such as the main
// network and test networks. See ChainParams.
type Params struct {
// GenesisBlock is the genesis block for the specific network.
GenesisBlock *btcwire.MsgBlock
// GenesisHash is the genesis block hash for the specific network.
GenesisHash *btcwire.ShaHash
// PowLimit is the highest proof of work value a bitcoin block can have
// for the specific network.
PowLimit *big.Int
// PowLimitBits is the highest proof of work value a bitcoin block can
// have represented in compact form. See CompactToBig for more details
// on compact form.
PowLimitBits uint32
// SubsidyHalvingInterval is the interval of blocks at which the
// baseSubsidy is continually halved. Mathematically this is:
// baseSubsidy / 2^(height/SubsidyHalvingInterval)
SubsidyHalvingInterval int64
}
// mainPowLimit is the highest proof of work value a bitcoin block can have for
// the main network. It is the value 2^224 - 1.
var mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne)
// mainNetParams contains parameters specific to the main network
// (btcwire.MainNet).
var mainNetParams = Params{
GenesisBlock: &btcwire.GenesisBlock,
GenesisHash: &btcwire.GenesisHash,
PowLimit: mainPowLimit,
PowLimitBits: BigToCompact(mainPowLimit),
SubsidyHalvingInterval: 210000,
}
// regressionPowLimit is the highest proof of work value a bitcoin block can
// have for the regression test network. It is the value 2^255 - 1.
var regressionPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne)
// regressionParams contains parameters specific to the regression test network
// (btcwire.TestNet).
var regressionParams = Params{
GenesisBlock: &btcwire.TestNetGenesisBlock,
GenesisHash: &btcwire.TestNetGenesisHash,
PowLimit: regressionPowLimit,
PowLimitBits: BigToCompact(regressionPowLimit),
SubsidyHalvingInterval: 150,
}
// testNetPowLimit is the highest proof of work value a bitcoin block can have
// for the test network (version 3). It is the value 2^224 - 1.
var testNetPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne)
// testNet3Params contains parameters specific to the test network (version 3)
// (btcwire.TestNet3).
var testNet3Params = Params{
GenesisBlock: &btcwire.TestNet3GenesisBlock,
GenesisHash: &btcwire.TestNet3GenesisHash,
PowLimit: testNetPowLimit,
PowLimitBits: BigToCompact(testNetPowLimit),
SubsidyHalvingInterval: 210000,
}
// chainParams returns chain parameters specific to the bitcoin network
// associated with the BlockChain instance.
func (b *BlockChain) chainParams() *Params {
return ChainParams(b.btcnet)
}
// ChainParams returns chain parameters specific to the passed bitcoin network.
// It returns the parameters for btcwire.MainNet if the passed network is not
// supported.
func ChainParams(btcnet btcwire.BitcoinNet) *Params {
switch btcnet {
case btcwire.TestNet:
return &regressionParams
case btcwire.TestNet3:
return &testNet3Params
// Return main net by default.
case btcwire.MainNet:
fallthrough
default:
return &mainNetParams
}
}

View file

@ -112,7 +112,7 @@ func (b *BlockChain) ProcessBlock(block *btcutil.Block, fastAdd bool) error {
} }
// Perform preliminary sanity checks on the block and its transactions. // Perform preliminary sanity checks on the block and its transactions.
err = CheckBlockSanity(block, b.chainParams().PowLimit) err = CheckBlockSanity(block, b.netParams.PowLimit)
if err != nil { if err != nil {
return err return err
} }

View file

@ -8,6 +8,7 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"github.com/conformal/btcdb" "github.com/conformal/btcdb"
"github.com/conformal/btcnet"
"github.com/conformal/btcscript" "github.com/conformal/btcscript"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
@ -165,10 +166,13 @@ func isBIP0030Node(node *blockNode) bool {
// //
// At the target block generation rate for the main network, this is // At the target block generation rate for the main network, this is
// approximately every 4 years. // approximately every 4 years.
func CalcBlockSubsidy(height int64, btcnet btcwire.BitcoinNet) int64 { func CalcBlockSubsidy(height int64, netParams *btcnet.Params) int64 {
if netParams.SubsidyHalvingInterval == 0 {
return baseSubsidy
}
// Equivalent to: baseSubsidy / 2^(height/subsidyHalvingInterval) // Equivalent to: baseSubsidy / 2^(height/subsidyHalvingInterval)
return baseSubsidy >> return baseSubsidy >> uint(height/int64(netParams.SubsidyHalvingInterval))
uint(height/ChainParams(btcnet).SubsidyHalvingInterval)
} }
// CheckTransactionSanity performs some preliminary checks on a transaction to // CheckTransactionSanity performs some preliminary checks on a transaction to
@ -728,7 +732,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block) er
// The coinbase for the Genesis block is not spendable, so just return // The coinbase for the Genesis block is not spendable, so just return
// now. // now.
if node.hash.IsEqual(b.chainParams().GenesisHash) && b.bestChain == nil { if node.hash.IsEqual(b.netParams.GenesisHash) && b.bestChain == nil {
return nil return nil
} }
@ -836,7 +840,8 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block) er
for _, txOut := range transactions[0].MsgTx().TxOut { for _, txOut := range transactions[0].MsgTx().TxOut {
totalSatoshiOut += txOut.Value totalSatoshiOut += txOut.Value
} }
expectedSatoshiOut := CalcBlockSubsidy(node.height, b.btcnet) + totalFees expectedSatoshiOut := CalcBlockSubsidy(node.height, b.netParams) +
totalFees
if totalSatoshiOut > expectedSatoshiOut { if totalSatoshiOut > expectedSatoshiOut {
str := fmt.Sprintf("coinbase transaction for block pays %v "+ str := fmt.Sprintf("coinbase transaction for block pays %v "+
"which is more than expected value of %v", "which is more than expected value of %v",

View file

@ -6,6 +6,7 @@ package btcchain_test
import ( import (
"github.com/conformal/btcchain" "github.com/conformal/btcchain"
"github.com/conformal/btcnet"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"math" "math"
@ -39,7 +40,7 @@ func TestCheckConnectBlock(t *testing.T) {
} }
func TestCheckBlockSanity(t *testing.T) { func TestCheckBlockSanity(t *testing.T) {
powLimit := btcchain.ChainParams(btcwire.MainNet).PowLimit powLimit := btcnet.MainNetParams.PowLimit
block := btcutil.NewBlock(&Block100000) block := btcutil.NewBlock(&Block100000)
err := btcchain.CheckBlockSanity(block, powLimit) err := btcchain.CheckBlockSanity(block, powLimit)
if err != nil { if err != nil {