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 (
"fmt"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
)
// 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 {
// 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.
// upgraded. This is part of BIP0034.
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) {
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(str)
@ -132,21 +122,13 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, fastAdd bool) error
// 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.
// newer once a majority of the network has upgraded. This is
// part of BIP0034.
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) {
prevNode,
b.netParams.CoinbaseBlockHeightNumRequired,
b.netParams.CoinbaseBlockHeightNumToCheck) {
expectedHeight := int64(0)
if prevNode != nil {

View file

@ -41,7 +41,7 @@ func (b *BlockChain) BlockLocatorFromHash(hash *btcwire.ShaHash) BlockLocator {
locator = append(locator, hash)
// 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
}
@ -124,7 +124,7 @@ func (b *BlockChain) BlockLocatorFromHash(hash *btcwire.ShaHash) BlockLocator {
}
// Append the appropriate genesis block.
locator = append(locator, b.chainParams().GenesisHash)
locator = append(locator, b.netParams.GenesisHash)
return locator
}

View file

@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"github.com/conformal/btcdb"
"github.com/conformal/btcnet"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"math/big"
@ -140,22 +141,23 @@ func removeChildNode(children []*blockNode, node *blockNode) []*blockNode {
// follow all rules, orphan handling, checkpoint handling, and best chain
// selection with reorganization.
type BlockChain struct {
db btcdb.Db
btcnet btcwire.BitcoinNet
notifications NotificationCallback
root *blockNode
bestChain *blockNode
index map[btcwire.ShaHash]*blockNode
depNodes map[btcwire.ShaHash][]*blockNode
orphans map[btcwire.ShaHash]*orphanBlock
prevOrphans map[btcwire.ShaHash][]*orphanBlock
oldestOrphan *orphanBlock
orphanLock sync.RWMutex
blockCache map[btcwire.ShaHash]*btcutil.Block
noVerify bool
noCheckpoints bool
nextCheckpoint *Checkpoint
checkpointBlock *btcutil.Block
db btcdb.Db
netParams *btcnet.Params
checkpointsByHeight map[int64]*btcnet.Checkpoint
notifications NotificationCallback
root *blockNode
bestChain *blockNode
index map[btcwire.ShaHash]*blockNode
depNodes map[btcwire.ShaHash][]*blockNode
orphans map[btcwire.ShaHash]*orphanBlock
prevOrphans map[btcwire.ShaHash][]*orphanBlock
oldestOrphan *orphanBlock
orphanLock sync.RWMutex
blockCache map[btcwire.ShaHash]*btcutil.Block
noVerify bool
noCheckpoints bool
nextCheckpoint *btcnet.Checkpoint
checkpointBlock *btcutil.Block
}
// DisableVerify provides a mechanism to disable transaction script validation
@ -500,7 +502,7 @@ func (b *BlockChain) getPrevNodeFromNode(node *blockNode) (*blockNode, error) {
}
// Genesis block.
if node.hash.IsEqual(b.chainParams().GenesisHash) {
if node.hash.IsEqual(b.netParams.GenesisHash) {
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) {
// Genesis block.
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
@ -1015,18 +1017,29 @@ func (b *BlockChain) IsCurrent() bool {
// Notification and NotificationType for details on the types and contents of
// notifications. The provided callback can be nil if the caller is not
// 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{
db: db,
btcnet: btcnet,
notifications: c,
root: nil,
bestChain: nil,
index: make(map[btcwire.ShaHash]*blockNode),
depNodes: make(map[btcwire.ShaHash][]*blockNode),
orphans: make(map[btcwire.ShaHash]*orphanBlock),
prevOrphans: make(map[btcwire.ShaHash][]*orphanBlock),
blockCache: make(map[btcwire.ShaHash]*btcutil.Block),
db: db,
netParams: params,
checkpointsByHeight: checkpointsByHeight,
notifications: c,
root: nil,
bestChain: nil,
index: make(map[btcwire.ShaHash]*blockNode),
depNodes: make(map[btcwire.ShaHash][]*blockNode),
orphans: make(map[btcwire.ShaHash]*orphanBlock),
prevOrphans: make(map[btcwire.ShaHash][]*orphanBlock),
blockCache: make(map[btcwire.ShaHash]*btcutil.Block),
}
return &b
}

View file

@ -6,6 +6,7 @@ package btcchain
import (
"fmt"
"github.com/conformal/btcnet"
"github.com/conformal/btcscript"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
@ -15,64 +16,6 @@ import (
// best block chain that a good checkpoint candidate must be.
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
// 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
@ -89,38 +32,26 @@ func (b *BlockChain) DisableCheckpoints(disable bool) {
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
// already known). When checkpoints are disabled or there are no checkpoints
// for the active network, it will return nil.
func (b *BlockChain) Checkpoints() []Checkpoint {
if b.noCheckpoints || b.checkpointData() == nil {
func (b *BlockChain) Checkpoints() []btcnet.Checkpoint {
if b.noCheckpoints || len(b.netParams.Checkpoints) == 0 {
return nil
}
return b.checkpointData().checkpoints
return b.netParams.Checkpoints
}
// LatestCheckpoint returns the most recent checkpoint (regardless of whether it
// is already known). When checkpoints are disabled or there are no checkpoints
// for the active network, it will return nil.
func (b *BlockChain) LatestCheckpoint() *Checkpoint {
if b.noCheckpoints || b.checkpointData() == nil {
func (b *BlockChain) LatestCheckpoint() *btcnet.Checkpoint {
if b.noCheckpoints || len(b.netParams.Checkpoints) == 0 {
return nil
}
checkpoints := b.checkpointData().checkpoints
checkpoints := b.netParams.Checkpoints
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
// checkpoint data for the passed block height.
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
}
// 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 {
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
// really only happen for blocks before the first checkpoint).
func (b *BlockChain) findPreviousCheckpoint() (*btcutil.Block, error) {
if b.noCheckpoints || b.checkpointData() == nil {
if b.noCheckpoints || len(b.netParams.Checkpoints) == 0 {
return nil, nil
}
// No checkpoints.
checkpoints := b.checkpointData().checkpoints
checkpoints := b.netParams.Checkpoints
numCheckpoints := len(checkpoints)
if numCheckpoints == 0 {
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)
// - The block must not contain any strange transaction such as those with
// 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) {
// Checkpoints must be enabled.
if b.noCheckpoints {
@ -339,20 +273,3 @@ func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) {
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/ldb"
_ "github.com/conformal/btcdb/memdb"
"github.com/conformal/btcnet"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"os"
@ -110,6 +111,6 @@ func chainSetup(dbName string) (*btcchain.BlockChain, func(), error) {
return nil, nil, err
}
chain := btcchain.New(db, btcwire.MainNet, nil)
chain := btcchain.New(db, btcnet.MainNetParams, nil)
return chain, teardown, nil
}

View file

@ -193,19 +193,12 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
durationVal := int64(duration)
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
// than twice the desired amount of time needed to generate a block has
// elapsed.
switch b.btcnet {
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
if b.netParams.ResetMinDifficulty {
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
// multiplied by the max adjustment factor.
newTarget := CompactToBig(bits)
for durationVal > 0 && newTarget.Cmp(powLimit) < 0 {
for durationVal > 0 && newTarget.Cmp(b.netParams.PowLimit) < 0 {
newTarget.Mul(newTarget, adjustmentFactor)
durationVal -= maxRetargetTimespan
}
// Limit new value to the proof of work limit.
if newTarget.Cmp(powLimit) > 0 {
newTarget.Set(powLimit)
if newTarget.Cmp(b.netParams.PowLimit) > 0 {
newTarget.Set(b.netParams.PowLimit)
}
return BigToCompact(newTarget)
@ -232,9 +225,10 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, error) {
// Search backwards through the chain for the last block without
// the special rule applied.
powLimitBits := b.chainParams().PowLimitBits
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
// simply accessing iterNode.parent directly as it will
// 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
// appropriate block was found.
lastBits := powLimitBits
lastBits := b.netParams.PowLimitBits
if iterNode != nil {
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
// while this function accepts any block node.
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.
if lastNode == nil {
return powLimitBits, nil
return b.netParams.PowLimitBits, nil
}
// Return the previous block's difficulty requirements if this block
// is not at a difficulty retarget interval.
if (lastNode.height+1)%BlocksPerRetarget != 0 {
// The difficulty rules differ between networks.
switch b.btcnet {
// The test network rules allow minimum difficulty blocks after
// more than twice the desired amount of time needed to generate
// a block has elapsed.
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
if b.netParams.ResetMinDifficulty {
// Return minimum difficulty when more than twice the
// desired amount of time needed to generate a block has
// elapsed.
allowMinTime := lastNode.timestamp.Add(targetSpacing * 2)
if newBlockTime.After(allowMinTime) {
return powLimitBits, nil
return b.netParams.PowLimitBits, nil
}
// The block was mined within the desired timeframe, so
@ -299,15 +285,11 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
return 0, err
}
return prevBits, nil
}
// For the main network (or any unrecognized networks), simply
// return the previous block's difficulty.
case btcwire.MainNet:
fallthrough
default:
// Return the previous block's difficulty requirements.
return lastNode.bits, nil
}
// return the previous block's difficulty requirements.
return lastNode.bits, nil
}
// Get the block node at the previous retarget (targetTimespan days
@ -350,8 +332,8 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
newTarget.Div(newTarget, big.NewInt(int64(targetTimespan)))
// Limit new value to the proof of work limit.
if newTarget.Cmp(powLimit) > 0 {
newTarget.Set(powLimit)
if newTarget.Cmp(b.netParams.PowLimit) > 0 {
newTarget.Set(b.netParams.PowLimit)
}
// 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/btcdb"
_ "github.com/conformal/btcdb/ldb"
"github.com/conformal/btcnet"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"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
// a complete working example here, so we make sure to remove the
// database.
dbName := "example.db"
_ = os.Remove(dbName)
dbName := "exampledb"
_ = os.RemoveAll(dbName)
db, err := btcdb.CreateDB("leveldb", dbName)
if err != nil {
fmt.Printf("Failed to create database: %v\n", err)
return
}
defer os.Remove(dbName) // Ignore error.
defer os.RemoveAll(dbName) // Ignore error.
defer db.Close()
// Insert the main network genesis block. This is part of the initial
// database setup. Like above, this typically would not be needed when
// opening an existing database.
genesisBlock := btcutil.NewBlock(&btcwire.GenesisBlock)
genesisBlock := btcutil.NewBlock(btcnet.MainNetParams.GenesisBlock)
_, err = db.InsertBlock(genesisBlock)
if err != nil {
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
// 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
// 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.
err = CheckBlockSanity(block, b.chainParams().PowLimit)
err = CheckBlockSanity(block, b.netParams.PowLimit)
if err != nil {
return err
}

View file

@ -8,6 +8,7 @@ import (
"encoding/binary"
"fmt"
"github.com/conformal/btcdb"
"github.com/conformal/btcnet"
"github.com/conformal/btcscript"
"github.com/conformal/btcutil"
"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
// 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)
return baseSubsidy >>
uint(height/ChainParams(btcnet).SubsidyHalvingInterval)
return baseSubsidy >> uint(height/int64(netParams.SubsidyHalvingInterval))
}
// 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
// now.
if node.hash.IsEqual(b.chainParams().GenesisHash) && b.bestChain == nil {
if node.hash.IsEqual(b.netParams.GenesisHash) && b.bestChain == nil {
return nil
}
@ -836,7 +840,8 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block) er
for _, txOut := range transactions[0].MsgTx().TxOut {
totalSatoshiOut += txOut.Value
}
expectedSatoshiOut := CalcBlockSubsidy(node.height, b.btcnet) + totalFees
expectedSatoshiOut := CalcBlockSubsidy(node.height, b.netParams) +
totalFees
if totalSatoshiOut > expectedSatoshiOut {
str := fmt.Sprintf("coinbase transaction for block pays %v "+
"which is more than expected value of %v",

View file

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