mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-06 06:06:49 +00:00
[DEV-259] Allow to spend genesis coinbase, and use ProcessBlock to ad… (#187)
* [DEV-259] Allow to spend genesis coinbase, and use ProcessBlock to add genesis to the DAG like any other block * [DEV-259] fix IsCurrent to check genesis timestamp if needed * [DEV-259] add genesisPastUTXO as separate function
This commit is contained in:
parent
f06513aad7
commit
f615298453
@ -29,7 +29,10 @@ func (dag *BlockDAG) maybeAcceptBlock(block *util.Block, flags BehaviorFlags) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
bluestParent := parents.bluest()
|
bluestParent := parents.bluest()
|
||||||
blockHeight := parents.maxHeight() + 1
|
blockHeight := int32(0)
|
||||||
|
if !block.IsGenesis() {
|
||||||
|
blockHeight = parents.maxHeight() + 1
|
||||||
|
}
|
||||||
block.SetHeight(blockHeight)
|
block.SetHeight(blockHeight)
|
||||||
|
|
||||||
// The block must pass all of the validation rules which depend on the
|
// The block must pass all of the validation rules which depend on the
|
||||||
|
@ -498,12 +498,6 @@ func (dag *BlockDAG) connectToDAG(node *blockNode, parentNodes blockSet, block *
|
|||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
// This function MUST be called with the chain state lock held (for writes).
|
||||||
func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bool) error {
|
func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bool) error {
|
||||||
// The coinbase for the Genesis block is not spendable, so just return
|
|
||||||
// an error now.
|
|
||||||
if node.hash.IsEqual(dag.dagParams.GenesisHash) {
|
|
||||||
str := "the coinbase for the genesis block is not spendable"
|
|
||||||
return ruleError(ErrMissingTxOut, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// No warnings about unknown rules or versions until the DAG is
|
// No warnings about unknown rules or versions until the DAG is
|
||||||
// current.
|
// current.
|
||||||
@ -650,6 +644,9 @@ func (dag *BlockDAG) LastFinalityPointHash() *daghash.Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dag *BlockDAG) checkFinalityRulesAndGetFinalityPointCandidate(node *blockNode) (*blockNode, error) {
|
func (dag *BlockDAG) checkFinalityRulesAndGetFinalityPointCandidate(node *blockNode) (*blockNode, error) {
|
||||||
|
if node.isGenesis() {
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
var finalityPointCandidate *blockNode
|
var finalityPointCandidate *blockNode
|
||||||
finalityErr := ruleError(ErrFinality, "The last finality point is not in the selected chain of this block")
|
finalityErr := ruleError(ErrFinality, "The last finality point is not in the selected chain of this block")
|
||||||
|
|
||||||
@ -925,6 +922,9 @@ func (dag *BlockDAG) getTXO(outpointBlockNode *blockNode, outpoint wire.OutPoint
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (node *blockNode) validateFeeTransaction(dag *BlockDAG, acceptedTxData []*TxWithBlockHash, nodeTransactions []*util.Tx) error {
|
func (node *blockNode) validateFeeTransaction(dag *BlockDAG, acceptedTxData []*TxWithBlockHash, nodeTransactions []*util.Tx) error {
|
||||||
|
if node.isGenesis() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
expectedFeeTransaction, err := node.buildFeeTransaction(dag, acceptedTxData)
|
expectedFeeTransaction, err := node.buildFeeTransaction(dag, acceptedTxData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -968,8 +968,23 @@ type TxWithBlockHash struct {
|
|||||||
InBlock *daghash.Hash
|
InBlock *daghash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func genesisPastUTXO(virtual *virtualBlock) UTXOSet {
|
||||||
|
// The genesis has no past UTXO, so we create an empty UTXO
|
||||||
|
// set by creating a diff UTXO set with the virtual UTXO
|
||||||
|
// set, and adding all of its entries in toRemove
|
||||||
|
diff := NewUTXODiff()
|
||||||
|
for outPoint, entry := range virtual.utxoSet.utxoCollection {
|
||||||
|
diff.toRemove[outPoint] = entry
|
||||||
|
}
|
||||||
|
genesisPastUTXO := UTXOSet(NewDiffUTXOSet(virtual.utxoSet, diff))
|
||||||
|
return genesisPastUTXO
|
||||||
|
}
|
||||||
|
|
||||||
// pastUTXO returns the UTXO of a given block's past
|
// pastUTXO returns the UTXO of a given block's past
|
||||||
func (node *blockNode) pastUTXO(virtual *virtualBlock, db database.DB) (pastUTXO UTXOSet, acceptedTxData []*TxWithBlockHash, err error) {
|
func (node *blockNode) pastUTXO(virtual *virtualBlock, db database.DB) (pastUTXO UTXOSet, acceptedTxData []*TxWithBlockHash, err error) {
|
||||||
|
if node.isGenesis() {
|
||||||
|
return genesisPastUTXO(virtual), nil, nil
|
||||||
|
}
|
||||||
pastUTXO, err = node.selectedParent.restoreUTXO(virtual)
|
pastUTXO, err = node.selectedParent.restoreUTXO(virtual)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -1099,20 +1114,28 @@ func updateTipsUTXO(tips blockSet, virtual *virtualBlock, virtualUTXO UTXOSet) e
|
|||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for reads).
|
// This function MUST be called with the chain state lock held (for reads).
|
||||||
func (dag *BlockDAG) isCurrent() bool {
|
func (dag *BlockDAG) isCurrent() bool {
|
||||||
// Not current if the latest main (best) chain height is before the
|
// Not current if the virtual's selected tip height is less than
|
||||||
// latest known good checkpoint (when checkpoints are enabled).
|
// the latest known good checkpoint (when checkpoints are enabled).
|
||||||
checkpoint := dag.LatestCheckpoint()
|
checkpoint := dag.LatestCheckpoint()
|
||||||
if checkpoint != nil && dag.selectedTip().height < checkpoint.Height {
|
if checkpoint != nil && dag.selectedTip().height < checkpoint.Height {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not current if the latest best block has a timestamp before 24 hours
|
// Not current if the virtual's selected parent has a timestamp
|
||||||
// ago.
|
// before 24 hours ago. If the DAG is empty, we take the genesis
|
||||||
|
// block timestamp.
|
||||||
//
|
//
|
||||||
// The chain appears to be current if none of the checks reported
|
// The DAG appears to be current if none of the checks reported
|
||||||
// otherwise.
|
// otherwise.
|
||||||
|
var dagTimestamp int64
|
||||||
|
selectedTip := dag.selectedTip()
|
||||||
|
if selectedTip == nil {
|
||||||
|
dagTimestamp = dag.dagParams.GenesisBlock.Header.Timestamp.Unix()
|
||||||
|
} else {
|
||||||
|
dagTimestamp = selectedTip.timestamp
|
||||||
|
}
|
||||||
minus24Hours := dag.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix()
|
minus24Hours := dag.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix()
|
||||||
return dag.selectedTip().timestamp >= minus24Hours
|
return dagTimestamp >= minus24Hours
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCurrent returns whether or not the chain believes it is current. Several
|
// IsCurrent returns whether or not the chain believes it is current. Several
|
||||||
@ -1676,7 +1699,7 @@ func New(config *Config) (*BlockDAG, error) {
|
|||||||
for i := range config.Checkpoints {
|
for i := range config.Checkpoints {
|
||||||
checkpoint := &config.Checkpoints[i]
|
checkpoint := &config.Checkpoints[i]
|
||||||
if checkpoint.Height <= prevCheckpointHeight {
|
if checkpoint.Height <= prevCheckpointHeight {
|
||||||
return nil, AssertError("blockchain.New " +
|
return nil, AssertError("blockdag.New " +
|
||||||
"checkpoints are not sorted by height")
|
"checkpoints are not sorted by height")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1707,7 +1730,7 @@ func New(config *Config) (*BlockDAG, error) {
|
|||||||
prevOrphans: make(map[daghash.Hash][]*orphanBlock),
|
prevOrphans: make(map[daghash.Hash][]*orphanBlock),
|
||||||
warningCaches: newThresholdCaches(vbNumBits),
|
warningCaches: newThresholdCaches(vbNumBits),
|
||||||
deploymentCaches: newThresholdCaches(dagconfig.DefinedDeployments),
|
deploymentCaches: newThresholdCaches(dagconfig.DefinedDeployments),
|
||||||
blockCount: 1,
|
blockCount: 0,
|
||||||
SubnetworkStore: newSubnetworkStore(config.DB),
|
SubnetworkStore: newSubnetworkStore(config.DB),
|
||||||
subnetworkID: config.SubnetworkID,
|
subnetworkID: config.SubnetworkID,
|
||||||
}
|
}
|
||||||
@ -1719,11 +1742,6 @@ func New(config *Config) (*BlockDAG, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save a reference to the genesis block. Note that we may only get
|
|
||||||
// an index reference to it here because the index is uninitialized
|
|
||||||
// before initDAGState.
|
|
||||||
dag.genesis = index.LookupNode(params.GenesisHash)
|
|
||||||
|
|
||||||
// Initialize and catch up all of the currently active optional indexes
|
// Initialize and catch up all of the currently active optional indexes
|
||||||
// as needed.
|
// as needed.
|
||||||
if config.IndexManager != nil {
|
if config.IndexManager != nil {
|
||||||
@ -1733,6 +1751,23 @@ func New(config *Config) (*BlockDAG, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
genesis := index.LookupNode(params.GenesisHash)
|
||||||
|
|
||||||
|
if genesis == nil {
|
||||||
|
genesisBlock := util.NewBlock(dag.dagParams.GenesisBlock)
|
||||||
|
isOrphan, err := dag.ProcessBlock(genesisBlock, BFNone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isOrphan {
|
||||||
|
return nil, errors.New("Genesis block is unexpectedly orphan")
|
||||||
|
}
|
||||||
|
genesis = index.LookupNode(params.GenesisHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save a reference to the genesis block.
|
||||||
|
dag.genesis = genesis
|
||||||
|
|
||||||
// Initialize rule change threshold state caches.
|
// Initialize rule change threshold state caches.
|
||||||
if err := dag.initThresholdCaches(); err != nil {
|
if err := dag.initThresholdCaches(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -190,7 +190,7 @@ func TestHaveBlock(t *testing.T) {
|
|||||||
{hash: dagconfig.SimNetParams.GenesisHash.String(), want: true},
|
{hash: dagconfig.SimNetParams.GenesisHash.String(), want: true},
|
||||||
|
|
||||||
// Block 3b should be present (as a second child of Block 2).
|
// Block 3b should be present (as a second child of Block 2).
|
||||||
{hash: "7592764c1af29786ad0bdd21bd8ada1efe7f78f42f5f26a5c58d43531b39f0c4", want: true},
|
{hash: "08a3f0182ac8ff0326497f592d2e28b8b3b2b7e3fd77c7cb6f31ca872536cf7b", want: true},
|
||||||
|
|
||||||
// Block 100000 should be present (as an orphan).
|
// Block 100000 should be present (as an orphan).
|
||||||
{hash: "25d5494f3e1f895774c58034f1bd50f7b279e75db6007514affec8573ace4389", want: true},
|
{hash: "25d5494f3e1f895774c58034f1bd50f7b279e75db6007514affec8573ace4389", want: true},
|
||||||
@ -895,6 +895,7 @@ func TestValidateFeeTransaction(t *testing.T) {
|
|||||||
for i, tx := range transactions {
|
for i, tx := range transactions {
|
||||||
utilTxs[i] = util.NewTx(tx)
|
utilTxs[i] = util.NewTx(tx)
|
||||||
}
|
}
|
||||||
|
daghash.Sort(parentHashes)
|
||||||
msgBlock := &wire.MsgBlock{
|
msgBlock := &wire.MsgBlock{
|
||||||
Header: wire.BlockHeader{
|
Header: wire.BlockHeader{
|
||||||
Bits: dag.genesis.Header().Bits,
|
Bits: dag.genesis.Header().Bits,
|
||||||
|
@ -433,36 +433,10 @@ func dbPutDAGState(dbTx database.Tx, state *dagState) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// createDAGState initializes both the database and the DAG state to the
|
// createDAGState initializes both the database and the DAG state to the
|
||||||
// genesis block. This includes creating the necessary buckets and inserting
|
// genesis block. This includes creating the necessary buckets, so it
|
||||||
// the genesis block, so it must only be called on an uninitialized database.
|
// must only be called on an uninitialized database.
|
||||||
func (dag *BlockDAG) createDAGState() error {
|
func (dag *BlockDAG) createDAGState() error {
|
||||||
// Create a new node from the genesis block and set it as the DAG.
|
// Create the initial the database DAG state including creating the
|
||||||
genesisBlock := util.NewBlock(dag.dagParams.GenesisBlock)
|
|
||||||
genesisBlock.SetHeight(0)
|
|
||||||
header := &genesisBlock.MsgBlock().Header
|
|
||||||
node := newBlockNode(header, newSet(), dag.dagParams.K)
|
|
||||||
node.status = statusDataStored | statusValid
|
|
||||||
|
|
||||||
genesisCoinbase := genesisBlock.Transactions()[0].MsgTx()
|
|
||||||
genesisCoinbaseTxIn := genesisCoinbase.TxIn[0]
|
|
||||||
genesisCoinbaseTxOut := genesisCoinbase.TxOut[0]
|
|
||||||
genesisCoinbaseOutpoint := *wire.NewOutPoint(&genesisCoinbaseTxIn.PreviousOutPoint.TxID, genesisCoinbaseTxIn.PreviousOutPoint.Index)
|
|
||||||
genesisCoinbaseUTXOEntry := NewUTXOEntry(genesisCoinbaseTxOut, true, 0)
|
|
||||||
node.diff = &UTXODiff{
|
|
||||||
toAdd: utxoCollection{genesisCoinbaseOutpoint: genesisCoinbaseUTXOEntry},
|
|
||||||
toRemove: utxoCollection{},
|
|
||||||
}
|
|
||||||
|
|
||||||
dag.virtual.utxoSet.AddTx(genesisCoinbase, 0)
|
|
||||||
dag.virtual.SetTips(setFromSlice(node))
|
|
||||||
|
|
||||||
// Add the new node to the index which is used for faster lookups.
|
|
||||||
dag.index.addNode(node)
|
|
||||||
|
|
||||||
// Initiate the last finality point to the genesis block
|
|
||||||
dag.lastFinalityPoint = node
|
|
||||||
|
|
||||||
// Create the initial the database chain state including creating the
|
|
||||||
// necessary index buckets and inserting the genesis block.
|
// necessary index buckets and inserting the genesis block.
|
||||||
err := dag.db.Update(func(dbTx database.Tx) error {
|
err := dag.db.Update(func(dbTx database.Tx) error {
|
||||||
meta := dbTx.Metadata()
|
meta := dbTx.Metadata()
|
||||||
@ -506,35 +480,13 @@ func (dag *BlockDAG) createDAGState() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
// Save the genesis block to the block index database.
|
|
||||||
err = dbStoreBlockNode(dbTx, node)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the genesis block hash to height and height to hash
|
|
||||||
// mappings to the index.
|
|
||||||
err = dbPutBlockIndex(dbTx, &node.hash, node.height)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the current DAG state into the database.
|
|
||||||
state := &dagState{
|
|
||||||
TipHashes: dag.TipHashes(),
|
|
||||||
LastFinalityPoint: *genesisBlock.Hash(),
|
|
||||||
}
|
|
||||||
err = dbPutDAGState(dbTx, state)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the genesis block into the database.
|
|
||||||
return dbStoreBlock(dbTx, genesisBlock)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// initDAGState attempts to load and initialize the DAG state from the
|
// initDAGState attempts to load and initialize the DAG state from the
|
||||||
|
@ -60,6 +60,9 @@ const (
|
|||||||
// the current time.
|
// the current time.
|
||||||
ErrTimeTooNew
|
ErrTimeTooNew
|
||||||
|
|
||||||
|
// ErrNoParents indicates that the block is missing parents
|
||||||
|
ErrNoParents
|
||||||
|
|
||||||
// ErrWrongParentsOrder indicates that the block's parents are not ordered by hash, as expected
|
// ErrWrongParentsOrder indicates that the block's parents are not ordered by hash, as expected
|
||||||
ErrWrongParentsOrder
|
ErrWrongParentsOrder
|
||||||
|
|
||||||
@ -244,6 +247,8 @@ var errorCodeStrings = map[ErrorCode]string{
|
|||||||
ErrInvalidTime: "ErrInvalidTime",
|
ErrInvalidTime: "ErrInvalidTime",
|
||||||
ErrTimeTooOld: "ErrTimeTooOld",
|
ErrTimeTooOld: "ErrTimeTooOld",
|
||||||
ErrTimeTooNew: "ErrTimeTooNew",
|
ErrTimeTooNew: "ErrTimeTooNew",
|
||||||
|
ErrNoParents: "ErrNoParents",
|
||||||
|
ErrWrongParentsOrder: "ErrWrongParentsOrder",
|
||||||
ErrDifficultyTooLow: "ErrDifficultyTooLow",
|
ErrDifficultyTooLow: "ErrDifficultyTooLow",
|
||||||
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty",
|
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty",
|
||||||
ErrHighHash: "ErrHighHash",
|
ErrHighHash: "ErrHighHash",
|
||||||
|
@ -21,6 +21,8 @@ func TestErrorCodeStringer(t *testing.T) {
|
|||||||
{ErrInvalidTime, "ErrInvalidTime"},
|
{ErrInvalidTime, "ErrInvalidTime"},
|
||||||
{ErrTimeTooOld, "ErrTimeTooOld"},
|
{ErrTimeTooOld, "ErrTimeTooOld"},
|
||||||
{ErrTimeTooNew, "ErrTimeTooNew"},
|
{ErrTimeTooNew, "ErrTimeTooNew"},
|
||||||
|
{ErrNoParents, "ErrNoParents"},
|
||||||
|
{ErrWrongParentsOrder, "ErrWrongParentsOrder"},
|
||||||
{ErrDifficultyTooLow, "ErrDifficultyTooLow"},
|
{ErrDifficultyTooLow, "ErrDifficultyTooLow"},
|
||||||
{ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"},
|
{ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"},
|
||||||
{ErrHighHash, "ErrHighHash"},
|
{ErrHighHash, "ErrHighHash"},
|
||||||
|
@ -6,11 +6,12 @@ package blockdag_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/daglabs/btcd/wire"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/wire"
|
||||||
|
|
||||||
"github.com/daglabs/btcd/blockdag"
|
"github.com/daglabs/btcd/blockdag"
|
||||||
"github.com/daglabs/btcd/dagconfig"
|
"github.com/daglabs/btcd/dagconfig"
|
||||||
"github.com/daglabs/btcd/database"
|
"github.com/daglabs/btcd/database"
|
||||||
@ -70,7 +71,7 @@ func ExampleBlockDAG_ProcessBlock() {
|
|||||||
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
|
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Failed to process block: already have block ccc309e79328f036bdd6964adbed68ff374cfb878c7a797c0aae3fec4bf9b853
|
// Failed to process block: already have block 4f0fbe497b98f0ab3dd92a3be968d5c7623cbaa844ff9f19e2b94756337eb0b8
|
||||||
}
|
}
|
||||||
|
|
||||||
// This example demonstrates how to convert the compact "bits" in a block header
|
// This example demonstrates how to convert the compact "bits" in a block header
|
||||||
|
@ -429,6 +429,9 @@ func (idx *TxIndex) ConnectBlock(dbTx database.Tx, block *util.Block, _ *blockda
|
|||||||
// Increment the internal block ID to use for the block being connected
|
// Increment the internal block ID to use for the block being connected
|
||||||
// and add all of the transactions in the block to the index.
|
// and add all of the transactions in the block to the index.
|
||||||
newBlockID := idx.curBlockID + 1
|
newBlockID := idx.curBlockID + 1
|
||||||
|
if block.MsgBlock().Header.IsGenesis() {
|
||||||
|
newBlockID = 0
|
||||||
|
}
|
||||||
if err := dbAddTxIndexEntries(dbTx, block, newBlockID, acceptedTxsData); err != nil {
|
if err := dbAddTxIndexEntries(dbTx, block, newBlockID, acceptedTxsData); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3A.dat
vendored
BIN
blockdag/testdata/blk_3A.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3B.dat
vendored
BIN
blockdag/testdata/blk_3B.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3C.dat
vendored
BIN
blockdag/testdata/blk_3C.dat
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3D.dat
vendored
BIN
blockdag/testdata/blk_3D.dat
vendored
Binary file not shown.
@ -409,9 +409,15 @@ func (dag *BlockDAG) checkBlockHeaderSanity(header *wire.BlockHeader, flags Beha
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = checkBlockParentsOrder(header)
|
if len(header.ParentHashes) == 0 {
|
||||||
if err != nil {
|
if header.BlockHash() != *dag.dagParams.GenesisHash {
|
||||||
return err
|
return ruleError(ErrNoParents, "block has no parents")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = checkBlockParentsOrder(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A block timestamp must not have a greater precision than one second.
|
// A block timestamp must not have a greater precision than one second.
|
||||||
@ -497,14 +503,20 @@ func (dag *BlockDAG) checkBlockSanity(block *util.Block, flags BehaviorFlags) er
|
|||||||
"block is not a coinbase")
|
"block is not a coinbase")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsFeeTransaction(transactions[1]) {
|
isGenesis := block.MsgBlock().Header.IsGenesis()
|
||||||
|
if !isGenesis && !IsFeeTransaction(transactions[1]) {
|
||||||
return ruleError(ErrSecondTxNotFeeTransaction, "second transaction in "+
|
return ruleError(ErrSecondTxNotFeeTransaction, "second transaction in "+
|
||||||
"block is not a fee transaction")
|
"block is not a fee transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
txOffset := 2
|
||||||
|
if isGenesis {
|
||||||
|
txOffset = 1
|
||||||
|
}
|
||||||
|
|
||||||
// A block must not have more than one coinbase. And transactions must be
|
// A block must not have more than one coinbase. And transactions must be
|
||||||
// ordered by subnetwork
|
// ordered by subnetwork
|
||||||
for i, tx := range transactions[2:] {
|
for i, tx := range transactions[txOffset:] {
|
||||||
if IsCoinBase(tx) {
|
if IsCoinBase(tx) {
|
||||||
str := fmt.Sprintf("block contains second coinbase at "+
|
str := fmt.Sprintf("block contains second coinbase at "+
|
||||||
"index %d", i+2)
|
"index %d", i+2)
|
||||||
@ -678,13 +690,15 @@ func (dag *BlockDAG) checkBlockHeaderContext(header *wire.BlockHeader, bluestPar
|
|||||||
return ruleError(ErrUnexpectedDifficulty, str)
|
return ruleError(ErrUnexpectedDifficulty, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the timestamp for the block header is not before the
|
if !header.IsGenesis() {
|
||||||
// median time of the last several blocks (medianTimeBlocks).
|
// Ensure the timestamp for the block header is not before the
|
||||||
medianTime := bluestParent.CalcPastMedianTime()
|
// median time of the last several blocks (medianTimeBlocks).
|
||||||
if header.Timestamp.Before(medianTime) {
|
medianTime := bluestParent.CalcPastMedianTime()
|
||||||
str := "block timestamp of %v is not after expected %v"
|
if header.Timestamp.Before(medianTime) {
|
||||||
str = fmt.Sprintf(str, header.Timestamp, medianTime)
|
str := "block timestamp of %s is not after expected %s"
|
||||||
return ruleError(ErrTimeTooOld, str)
|
str = fmt.Sprintf(str, header.Timestamp.String(), medianTime.String())
|
||||||
|
return ruleError(ErrTimeTooOld, str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,7 +790,10 @@ func (dag *BlockDAG) checkBlockContext(block *util.Block, parents blockSet, blue
|
|||||||
|
|
||||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||||
if !fastAdd {
|
if !fastAdd {
|
||||||
blockTime := bluestParent.CalcPastMedianTime()
|
blockTime := header.Timestamp
|
||||||
|
if !block.IsGenesis() {
|
||||||
|
blockTime = bluestParent.CalcPastMedianTime()
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure all transactions in the block are finalized.
|
// Ensure all transactions in the block are finalized.
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.Transactions() {
|
||||||
@ -1048,9 +1065,12 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *blockNode, pastUTXO UTXOSet,
|
|||||||
|
|
||||||
scriptFlags := txscript.ScriptNoFlags
|
scriptFlags := txscript.ScriptNoFlags
|
||||||
|
|
||||||
// We obtain the MTP of the *previous* block in order to
|
// We obtain the MTP of the *previous* block (unless it's genesis block)
|
||||||
// determine if transactions in the current block are final.
|
// in order to determine if transactions in the current block are final.
|
||||||
medianTime := block.selectedParent.CalcPastMedianTime()
|
medianTime := block.Header().Timestamp
|
||||||
|
if !block.isGenesis() {
|
||||||
|
medianTime = block.selectedParent.CalcPastMedianTime()
|
||||||
|
}
|
||||||
|
|
||||||
// We also enforce the relative sequence number based
|
// We also enforce the relative sequence number based
|
||||||
// lock-times within the inputs of all transactions in this
|
// lock-times within the inputs of all transactions in this
|
||||||
|
@ -44,10 +44,10 @@ var genesisCoinbaseTx = wire.MsgTx{
|
|||||||
// genesisHash is the hash of the first block in the block chain for the main
|
// genesisHash is the hash of the first block in the block chain for the main
|
||||||
// network (genesis block).
|
// network (genesis block).
|
||||||
var genesisHash = daghash.Hash([daghash.HashSize]byte{ // Make go vet happy.
|
var genesisHash = daghash.Hash([daghash.HashSize]byte{ // Make go vet happy.
|
||||||
0x53, 0xb8, 0xf9, 0x4b, 0xec, 0x3f, 0xae, 0x0a,
|
0xb8, 0xb0, 0x7e, 0x33, 0x56, 0x47, 0xb9, 0xe2,
|
||||||
0x7c, 0x79, 0x7a, 0x8c, 0x87, 0xfb, 0x4c, 0x37,
|
0x19, 0x9f, 0xff, 0x44, 0xa8, 0xba, 0x3c, 0x62,
|
||||||
0xff, 0x68, 0xed, 0xdb, 0x4a, 0x96, 0xd6, 0xbd,
|
0xc7, 0xd5, 0x68, 0xe9, 0x3b, 0x2a, 0xd9, 0x3d,
|
||||||
0x36, 0xf0, 0x28, 0x93, 0xe7, 0x09, 0xc3, 0xcc,
|
0xab, 0xf0, 0x98, 0x7b, 0x49, 0xbe, 0x0f, 0x4f,
|
||||||
})
|
})
|
||||||
|
|
||||||
// genesisMerkleRoot is the hash of the first transaction in the genesis block
|
// genesisMerkleRoot is the hash of the first transaction in the genesis block
|
||||||
@ -69,7 +69,7 @@ var genesisBlock = wire.MsgBlock{
|
|||||||
IDMerkleRoot: genesisMerkleRoot,
|
IDMerkleRoot: genesisMerkleRoot,
|
||||||
Timestamp: time.Unix(0x5c3cafec, 0),
|
Timestamp: time.Unix(0x5c3cafec, 0),
|
||||||
Bits: 0x207fffff,
|
Bits: 0x207fffff,
|
||||||
Nonce: 0xbffffffffffffffa,
|
Nonce: 0,
|
||||||
},
|
},
|
||||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||||
}
|
}
|
||||||
|
@ -131,8 +131,8 @@ var genesisBlockBytes = []byte{
|
|||||||
0xc3, 0x54, 0xc9, 0xa7, 0x06, 0x8c, 0x23, 0x24,
|
0xc3, 0x54, 0xc9, 0xa7, 0x06, 0x8c, 0x23, 0x24,
|
||||||
0x3c, 0x53, 0x6d, 0x56, 0x23, 0xec, 0xaf, 0x3c,
|
0x3c, 0x53, 0x6d, 0x56, 0x23, 0xec, 0xaf, 0x3c,
|
||||||
0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f,
|
0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f,
|
||||||
0x20, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0xbf, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
|
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
@ -194,6 +194,11 @@ func (b *Block) SetHeight(height int32) {
|
|||||||
b.blockHeight = height
|
b.blockHeight = height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsGenesis returns whether or not this block is the genesis block.
|
||||||
|
func (b *Block) IsGenesis() bool {
|
||||||
|
return b.MsgBlock().Header.IsGenesis()
|
||||||
|
}
|
||||||
|
|
||||||
// NewBlock returns a new instance of a bitcoin block given an underlying
|
// NewBlock returns a new instance of a bitcoin block given an underlying
|
||||||
// wire.MsgBlock. See Block.
|
// wire.MsgBlock. See Block.
|
||||||
func NewBlock(msgBlock *wire.MsgBlock) *Block {
|
func NewBlock(msgBlock *wire.MsgBlock) *Block {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user