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:
parent
d4082e4f24
commit
4579cfb71b
11 changed files with 106 additions and 306 deletions
38
accept.go
38
accept.go
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
71
chain.go
71
chain.go
|
@ -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 := ¶ms.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
|
||||
}
|
||||
|
|
111
checkpoints.go
111
checkpoints.go
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
12
doc.go
|
@ -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
101
params.go
|
@ -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 ®ressionParams
|
||||
|
||||
case btcwire.TestNet3:
|
||||
return &testNet3Params
|
||||
|
||||
// Return main net by default.
|
||||
case btcwire.MainNet:
|
||||
fallthrough
|
||||
default:
|
||||
return &mainNetParams
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
15
validate.go
15
validate.go
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue