mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-04 05:06:43 +00:00
[NOD-616] Remove blockNode.chainHeight (#586)
* [NOD-616] Remove unused methods from BlockDAG. * [NOD-616] Remove Height from GetRawMempoolVerboseResult and TxDesc. * [NOD-616] Replaced BlockDAG.ChainHeight with SelectedTipBlueScore. * [NOD-616] Remove the unused BlockChainHeightByHash. * [NOD-616] Remove the unused blockChainHeight from checkBlockHeaderContext. * [NOD-616] Remove chainHeight from util.Block. * [NOD-616] Remove TestChainHeight. * [NOD-616] Update unknown rule activation warning to use blueScore. * [NOD-616] Update thresholdState to use blueScore instead of chainHeight. * [NOD-616] Update blockLocator to use blueScore instead of chainHeight. * [NOD-616] Remove blockNode.chainHeight. * [NOD-616] Fix comments and variable names. * [NOD-616] Replace a weird for loop with a while loop. * [NOD-616] Fix a comment. * [NOD-616] Remove pre-allocation in blockLocator. * [NOD-616] Coalesce checks that startHash and stopHash are not the same into the same condition. * [NOD-616] Fix a comment. * [NOD-616] Remove weird blueScore logic around childHashStrings. * [NOD-616] Fix hash pointer comparison. * [NOD-616] Fix a comment. * [NOD-616] Add ban score to peers misusing GetBlockLocator. * [NOD-616] Replace adding ban score with disconnecting. * [NOD-616] Add blueScore to FilteredBlockAddedNtfn.
This commit is contained in:
parent
8b8e73feb5
commit
359b16fca9
@ -80,7 +80,7 @@ func (dag *BlockDAG) maybeAcceptBlock(block *util.Block, flags BehaviorFlags) er
|
||||
}
|
||||
}
|
||||
|
||||
block.SetChainHeight(newNode.chainHeight)
|
||||
block.SetBlueScore(newNode.blueScore)
|
||||
|
||||
// Connect the passed block to the DAG. This also handles validation of the
|
||||
// transaction scripts.
|
||||
|
@ -19,7 +19,7 @@ func TestAncestorErrors(t *testing.T) {
|
||||
defer teardownFunc()
|
||||
|
||||
node := newTestNode(dag, newSet(), int32(0x10000000), 0, time.Unix(0, 0))
|
||||
node.chainHeight = 2
|
||||
node.blueScore = 2
|
||||
ancestor := node.SelectedAncestor(3)
|
||||
if ancestor != nil {
|
||||
t.Errorf("TestAncestorErrors: Ancestor() unexpectedly returned a node. Expected: <nil>")
|
||||
|
@ -1,8 +1,8 @@
|
||||
package blockdag
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// BlockLocator is used to help locate a specific block. The algorithm for
|
||||
@ -29,7 +29,7 @@ type BlockLocator []*daghash.Hash
|
||||
// known.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (dag *BlockDAG) BlockLocatorFromHashes(startHash, stopHash *daghash.Hash) BlockLocator {
|
||||
func (dag *BlockDAG) BlockLocatorFromHashes(startHash, stopHash *daghash.Hash) (BlockLocator, error) {
|
||||
dag.dagLock.RLock()
|
||||
defer dag.dagLock.RUnlock()
|
||||
startNode := dag.index.LookupNode(startHash)
|
||||
@ -40,15 +40,6 @@ func (dag *BlockDAG) BlockLocatorFromHashes(startHash, stopHash *daghash.Hash) B
|
||||
return dag.blockLocator(startNode, stopNode)
|
||||
}
|
||||
|
||||
// LatestBlockLocator returns a block locator for the current tips of the DAG.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (dag *BlockDAG) LatestBlockLocator() BlockLocator {
|
||||
dag.dagLock.RLock()
|
||||
defer dag.dagLock.RUnlock()
|
||||
return dag.blockLocator(nil, nil)
|
||||
}
|
||||
|
||||
// blockLocator returns a block locator for the passed start and stop nodes.
|
||||
// The default value for the start node is the selected tip, and the default
|
||||
// values of the stop node is the genesis block.
|
||||
@ -56,7 +47,7 @@ func (dag *BlockDAG) LatestBlockLocator() BlockLocator {
|
||||
// See the BlockLocator type comments for more details.
|
||||
//
|
||||
// This function MUST be called with the DAG state lock held (for reads).
|
||||
func (dag *BlockDAG) blockLocator(startNode, stopNode *blockNode) BlockLocator {
|
||||
func (dag *BlockDAG) blockLocator(startNode, stopNode *blockNode) (BlockLocator, error) {
|
||||
// Use the selected tip if requested.
|
||||
if startNode == nil {
|
||||
startNode = dag.virtual.selectedParent
|
||||
@ -70,51 +61,36 @@ func (dag *BlockDAG) blockLocator(startNode, stopNode *blockNode) BlockLocator {
|
||||
// block locator won't contain the start node.
|
||||
startNode = startNode.selectedParent
|
||||
|
||||
// If the start node or the stop node are not in the
|
||||
// virtual's selected parent chain, we replace them with their
|
||||
// closest selected parent that is part of the virtual's
|
||||
// selected parent chain.
|
||||
for !dag.IsInSelectedParentChain(stopNode.hash) {
|
||||
stopNode = stopNode.selectedParent
|
||||
}
|
||||
|
||||
for !dag.IsInSelectedParentChain(startNode.hash) {
|
||||
startNode = startNode.selectedParent
|
||||
}
|
||||
|
||||
// Calculate the max number of entries that will ultimately be in the
|
||||
// block locator. See the description of the algorithm for how these
|
||||
// numbers are derived.
|
||||
|
||||
// startNode.hash + stopNode.hash.
|
||||
// Then floor(log2(startNode.chainHeight-stopNode.chainHeight)) entries for the skip portion.
|
||||
maxEntries := 2 + util.FastLog2Floor(startNode.chainHeight-stopNode.chainHeight)
|
||||
locator := make(BlockLocator, 0, maxEntries)
|
||||
|
||||
node := startNode
|
||||
step := uint64(1)
|
||||
for node := startNode; node != nil; {
|
||||
locator := make(BlockLocator, 0)
|
||||
for node != nil {
|
||||
locator = append(locator, node.hash)
|
||||
|
||||
// Nothing more to add once the stop node has been added.
|
||||
if node.chainHeight == stopNode.chainHeight {
|
||||
if node.blueScore <= stopNode.blueScore {
|
||||
if node != stopNode {
|
||||
return nil, errors.Errorf("startNode and stopNode are " +
|
||||
"not in the same selected parent chain.")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Calculate chainHeight of previous node to include ensuring the
|
||||
// Calculate blueScore of previous node to include ensuring the
|
||||
// final node is stopNode.
|
||||
nextChainHeight := node.chainHeight - step
|
||||
if nextChainHeight < stopNode.chainHeight {
|
||||
nextChainHeight = stopNode.chainHeight
|
||||
nextBlueScore := node.blueScore - step
|
||||
if nextBlueScore < stopNode.blueScore {
|
||||
nextBlueScore = stopNode.blueScore
|
||||
}
|
||||
|
||||
// walk backwards through the nodes to the correct ancestor.
|
||||
node = node.SelectedAncestor(nextChainHeight)
|
||||
node = node.SelectedAncestor(nextBlueScore)
|
||||
|
||||
// Double the distance between included hashes.
|
||||
step *= 2
|
||||
}
|
||||
|
||||
return locator
|
||||
return locator, nil
|
||||
}
|
||||
|
||||
// FindNextLocatorBoundaries returns the lowest unknown block locator, hash
|
||||
|
@ -83,9 +83,6 @@ type blockNode struct {
|
||||
// hash is the double sha 256 of the block.
|
||||
hash *daghash.Hash
|
||||
|
||||
// chainHeight is the number of hops you need to go down the selected parent chain in order to get to the genesis block.
|
||||
chainHeight uint64
|
||||
|
||||
// Some fields from block headers to aid in reconstructing headers
|
||||
// from memory. These must be treated as immutable and are intentionally
|
||||
// ordered to avoid padding on 64-bit platforms.
|
||||
@ -107,13 +104,6 @@ type blockNode struct {
|
||||
isFinalized bool
|
||||
}
|
||||
|
||||
func calculateChainHeight(node *blockNode) uint64 {
|
||||
if node.isGenesis() {
|
||||
return 0
|
||||
}
|
||||
return node.selectedParent.chainHeight + 1
|
||||
}
|
||||
|
||||
// newBlockNode returns a new block node for the given block header and parents, and the
|
||||
// anticone of its selected parent (parent with highest blue score).
|
||||
// selectedParentAnticone is used to update reachability data we store for future reachability queries.
|
||||
@ -146,7 +136,6 @@ func (dag *BlockDAG) newBlockNode(blockHeader *wire.BlockHeader, parents blockSe
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "unexpected error in GHOSTDAG"))
|
||||
}
|
||||
node.chainHeight = calculateChainHeight(node)
|
||||
}
|
||||
return node, selectedParentAnticone
|
||||
}
|
||||
@ -183,31 +172,31 @@ func (node *blockNode) Header() *wire.BlockHeader {
|
||||
}
|
||||
}
|
||||
|
||||
// SelectedAncestor returns the ancestor block node at the provided chain-height by following
|
||||
// SelectedAncestor returns the ancestor block node at the provided blue score by following
|
||||
// the selected-parents chain backwards from this node. The returned block will be nil when a
|
||||
// height is requested that is after the height of the passed node.
|
||||
// blue score is requested that is higher than the blue score of the passed node.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (node *blockNode) SelectedAncestor(chainHeight uint64) *blockNode {
|
||||
if chainHeight < 0 || chainHeight > node.chainHeight {
|
||||
func (node *blockNode) SelectedAncestor(blueScore uint64) *blockNode {
|
||||
if blueScore < 0 || blueScore > node.blueScore {
|
||||
return nil
|
||||
}
|
||||
|
||||
n := node
|
||||
for ; n != nil && n.chainHeight != chainHeight; n = n.selectedParent {
|
||||
// Intentionally left blank
|
||||
for n != nil && n.blueScore > blueScore {
|
||||
n = n.selectedParent
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// RelativeAncestor returns the ancestor block node a relative 'distance' of
|
||||
// chain-blocks before this node. This is equivalent to calling Ancestor with
|
||||
// the node's chain-height minus provided distance.
|
||||
// blue blocks before this node. This is equivalent to calling Ancestor with
|
||||
// the node's blue score minus provided distance.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (node *blockNode) RelativeAncestor(distance uint64) *blockNode {
|
||||
return node.SelectedAncestor(node.chainHeight - distance)
|
||||
return node.SelectedAncestor(node.blueScore - distance)
|
||||
}
|
||||
|
||||
// CalcPastMedianTime returns the median time of the previous few blocks
|
||||
|
@ -1,97 +0,0 @@
|
||||
package blockdag
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/dagconfig"
|
||||
"github.com/kaspanet/kaspad/wire"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChainHeight(t *testing.T) {
|
||||
// Create a new database and DAG instance to run tests against.
|
||||
params := dagconfig.SimnetParams
|
||||
params.K = 2
|
||||
dag, teardownFunc, err := DAGSetup("TestChainHeight", Config{
|
||||
DAGParams: ¶ms,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("TestChainHeight: Failed to setup DAG instance: %s", err)
|
||||
}
|
||||
defer teardownFunc()
|
||||
|
||||
block0 := dag.dagParams.GenesisBlock
|
||||
block1 := prepareAndProcessBlock(t, dag, block0)
|
||||
block2 := prepareAndProcessBlock(t, dag, block0)
|
||||
block3 := prepareAndProcessBlock(t, dag, block0)
|
||||
block4 := prepareAndProcessBlock(t, dag, block1, block2, block3)
|
||||
block5 := prepareAndProcessBlock(t, dag, block1, block2, block3)
|
||||
block6 := prepareAndProcessBlock(t, dag, block1, block2, block3)
|
||||
block7 := prepareAndProcessBlock(t, dag, block0)
|
||||
block8 := prepareAndProcessBlock(t, dag, block7)
|
||||
block9 := prepareAndProcessBlock(t, dag, block8)
|
||||
block10 := prepareAndProcessBlock(t, dag, block9, block6)
|
||||
|
||||
// Because nodes 7 & 8 were mined secretly, block10's selected
|
||||
// parent will be block6, although block9 is higher. So in this
|
||||
// case, block10.height and block10.chainHeight will be different
|
||||
|
||||
tests := []struct {
|
||||
block *wire.MsgBlock
|
||||
expectedChainHeight uint64
|
||||
}{
|
||||
{
|
||||
block: block0,
|
||||
expectedChainHeight: 0,
|
||||
},
|
||||
{
|
||||
block: block1,
|
||||
expectedChainHeight: 1,
|
||||
},
|
||||
{
|
||||
block: block2,
|
||||
expectedChainHeight: 1,
|
||||
},
|
||||
{
|
||||
block: block3,
|
||||
expectedChainHeight: 1,
|
||||
},
|
||||
{
|
||||
block: block4,
|
||||
expectedChainHeight: 2,
|
||||
},
|
||||
{
|
||||
block: block5,
|
||||
expectedChainHeight: 2,
|
||||
},
|
||||
{
|
||||
block: block6,
|
||||
expectedChainHeight: 2,
|
||||
},
|
||||
{
|
||||
block: block7,
|
||||
expectedChainHeight: 1,
|
||||
},
|
||||
{
|
||||
block: block8,
|
||||
expectedChainHeight: 2,
|
||||
},
|
||||
{
|
||||
block: block9,
|
||||
expectedChainHeight: 3,
|
||||
},
|
||||
{
|
||||
block: block10,
|
||||
expectedChainHeight: 3,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
node := dag.index.LookupNode(test.block.BlockHash())
|
||||
if node.chainHeight != test.expectedChainHeight {
|
||||
t.Errorf("block %s expected chain height %v but got %v", node, test.expectedChainHeight, node.chainHeight)
|
||||
}
|
||||
if calculateChainHeight(node) != test.expectedChainHeight {
|
||||
t.Errorf("block %s expected calculated chain height %v but got %v", node, test.expectedChainHeight, node.chainHeight)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
100
blockdag/dag.go
100
blockdag/dag.go
@ -1516,10 +1516,9 @@ func (dag *BlockDAG) SelectedParentChain(startHash *daghash.Hash) ([]*daghash.Ha
|
||||
return removedChainHashes, addedChainHashes, nil
|
||||
}
|
||||
|
||||
// ChainHeight return the chain-height of the selected tip. In other words - it returns
|
||||
// the length of the dag's selected-parent chain
|
||||
func (dag *BlockDAG) ChainHeight() uint64 {
|
||||
return dag.selectedTip().chainHeight
|
||||
// SelectedTipBlueScore returns the blue score of the selected tip.
|
||||
func (dag *BlockDAG) SelectedTipBlueScore() uint64 {
|
||||
return dag.selectedTip().blueScore
|
||||
}
|
||||
|
||||
// VirtualBlueScore returns the blue score of the current virtual block
|
||||
@ -1561,20 +1560,6 @@ func (dag *BlockDAG) HeaderByHash(hash *daghash.Hash) (*wire.BlockHeader, error)
|
||||
return node.Header(), nil
|
||||
}
|
||||
|
||||
// BlockChainHeightByHash returns the chain height of the block with the given
|
||||
// hash in the DAG.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (dag *BlockDAG) BlockChainHeightByHash(hash *daghash.Hash) (uint64, error) {
|
||||
node := dag.index.LookupNode(hash)
|
||||
if node == nil {
|
||||
str := fmt.Sprintf("block %s is not in the DAG", hash)
|
||||
return 0, errNotInDAG(str)
|
||||
}
|
||||
|
||||
return node.chainHeight, nil
|
||||
}
|
||||
|
||||
// ChildHashesByHash returns the child hashes of the block with the given hash in the
|
||||
// DAG.
|
||||
//
|
||||
@ -1608,81 +1593,6 @@ func (dag *BlockDAG) SelectedParentHash(blockHash *daghash.Hash) (*daghash.Hash,
|
||||
return node.selectedParent.hash, nil
|
||||
}
|
||||
|
||||
// ChainHeightToHashRange returns a range of block hashes for the given start chain
|
||||
// height and end hash, inclusive on both ends. The hashes are for all blocks that
|
||||
// are ancestors of endHash with height greater than or equal to startChainHeight.
|
||||
// The end hash must belong to a block that is known to be valid.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (dag *BlockDAG) ChainHeightToHashRange(startChainHeight uint64,
|
||||
endHash *daghash.Hash, maxResults int) ([]*daghash.Hash, error) {
|
||||
|
||||
endNode := dag.index.LookupNode(endHash)
|
||||
if endNode == nil {
|
||||
return nil, errors.Errorf("no known block header with hash %s", endHash)
|
||||
}
|
||||
if !dag.index.NodeStatus(endNode).KnownValid() {
|
||||
return nil, errors.Errorf("block %s is not yet validated", endHash)
|
||||
}
|
||||
endChainHeight := endNode.chainHeight
|
||||
|
||||
if startChainHeight < 0 {
|
||||
return nil, errors.Errorf("start chain height (%d) is below 0", startChainHeight)
|
||||
}
|
||||
if startChainHeight > endChainHeight {
|
||||
return nil, errors.Errorf("start chain height (%d) is past end chain height (%d)",
|
||||
startChainHeight, endChainHeight)
|
||||
}
|
||||
|
||||
resultsLength := int(endChainHeight - startChainHeight + 1)
|
||||
if resultsLength > maxResults {
|
||||
return nil, errors.Errorf("number of results (%d) would exceed max (%d)",
|
||||
resultsLength, maxResults)
|
||||
}
|
||||
|
||||
// Walk backwards from endChainHeight to startChainHeight, collecting block hashes.
|
||||
node := endNode
|
||||
hashes := make([]*daghash.Hash, resultsLength)
|
||||
for i := resultsLength - 1; i >= 0; i-- {
|
||||
hashes[i] = node.hash
|
||||
node = node.selectedParent
|
||||
}
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
// IntervalBlockHashes returns hashes for all blocks that are ancestors of
|
||||
// endHash where the block height is a positive multiple of interval.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (dag *BlockDAG) IntervalBlockHashes(endHash *daghash.Hash, interval uint64,
|
||||
) ([]*daghash.Hash, error) {
|
||||
|
||||
endNode := dag.index.LookupNode(endHash)
|
||||
if endNode == nil {
|
||||
return nil, errors.Errorf("no known block header with hash %s", endHash)
|
||||
}
|
||||
if !dag.index.NodeStatus(endNode).KnownValid() {
|
||||
return nil, errors.Errorf("block %s is not yet validated", endHash)
|
||||
}
|
||||
endChainHeight := endNode.chainHeight
|
||||
|
||||
resultsLength := endChainHeight / interval
|
||||
hashes := make([]*daghash.Hash, resultsLength)
|
||||
|
||||
dag.virtual.mtx.Lock()
|
||||
defer dag.virtual.mtx.Unlock()
|
||||
|
||||
blockNode := endNode
|
||||
for index := endChainHeight / interval; index > 0; index-- {
|
||||
blockHeight := index * interval
|
||||
blockNode = blockNode.SelectedAncestor(blockHeight)
|
||||
|
||||
hashes[index-1] = blockNode.hash
|
||||
}
|
||||
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
// getBlueBlocksHashesBetween returns the hashes of the blocks after the provided
|
||||
// start hash until the provided stop hash is reached, or up to the
|
||||
// provided max number of block hashes.
|
||||
@ -2055,8 +1965,8 @@ func New(config *Config) (*BlockDAG, error) {
|
||||
}
|
||||
|
||||
selectedTip := dag.selectedTip()
|
||||
log.Infof("DAG state (chain height %d, hash %s)",
|
||||
selectedTip.chainHeight, selectedTip.hash)
|
||||
log.Infof("DAG state (blue score %d, hash %s)",
|
||||
selectedTip.blueScore, selectedTip.hash)
|
||||
|
||||
return dag, nil
|
||||
}
|
||||
|
@ -8,12 +8,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"math/rand"
|
||||
|
||||
"github.com/kaspanet/kaspad/dagconfig"
|
||||
"github.com/kaspanet/kaspad/database"
|
||||
"github.com/kaspanet/kaspad/txscript"
|
||||
@ -271,7 +268,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
TxID: *targetTx.ID(),
|
||||
Index: 0,
|
||||
}
|
||||
prevUtxoChainHeight := uint64(numBlocksToGenerate) - 4
|
||||
prevUtxoBlueScore := uint64(numBlocksToGenerate) - 4
|
||||
|
||||
// Obtain the past median time from the PoV of the input created above.
|
||||
// The past median time for the input is the past median time from the PoV
|
||||
@ -283,7 +280,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
// the MTP will be calculated from the PoV of the yet-to-be-mined
|
||||
// block.
|
||||
nextMedianTime := node.PastMedianTime(dag).Unix()
|
||||
nextBlockChainHeight := int32(numBlocksToGenerate) + 1
|
||||
nextBlockBlueScore := int32(numBlocksToGenerate) + 1
|
||||
|
||||
// Add an additional transaction which will serve as our unconfirmed
|
||||
// output.
|
||||
@ -369,7 +366,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
utxoSet: utxoSet,
|
||||
want: &SequenceLock{
|
||||
Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1,
|
||||
BlockBlueScore: int64(prevUtxoChainHeight) + 3,
|
||||
BlockBlueScore: int64(prevUtxoBlueScore) + 3,
|
||||
},
|
||||
},
|
||||
// Transaction with a single input. The input's sequence number
|
||||
@ -382,7 +379,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
utxoSet: utxoSet,
|
||||
want: &SequenceLock{
|
||||
Seconds: -1,
|
||||
BlockBlueScore: int64(prevUtxoChainHeight) + 2,
|
||||
BlockBlueScore: int64(prevUtxoBlueScore) + 2,
|
||||
},
|
||||
},
|
||||
// A transaction with two inputs with lock times expressed in
|
||||
@ -421,7 +418,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
utxoSet: utxoSet,
|
||||
want: &SequenceLock{
|
||||
Seconds: -1,
|
||||
BlockBlueScore: int64(prevUtxoChainHeight) + 10,
|
||||
BlockBlueScore: int64(prevUtxoBlueScore) + 10,
|
||||
},
|
||||
},
|
||||
// A transaction with multiple inputs. Two inputs are time
|
||||
@ -447,7 +444,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
utxoSet: utxoSet,
|
||||
want: &SequenceLock{
|
||||
Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1,
|
||||
BlockBlueScore: int64(prevUtxoChainHeight) + 8,
|
||||
BlockBlueScore: int64(prevUtxoBlueScore) + 8,
|
||||
},
|
||||
},
|
||||
// A transaction with a single unconfirmed input. As the input
|
||||
@ -463,7 +460,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
mempool: true,
|
||||
want: &SequenceLock{
|
||||
Seconds: -1,
|
||||
BlockBlueScore: int64(nextBlockChainHeight) + 1,
|
||||
BlockBlueScore: int64(nextBlockBlueScore) + 1,
|
||||
},
|
||||
},
|
||||
// A transaction with a single unconfirmed input. The input has
|
||||
@ -544,214 +541,6 @@ func TestCalcPastMedianTime(t *testing.T) {
|
||||
t.Errorf("TestCalcPastMedianTime: expected past median time of block %v to be %v seconds from genesis but got %v", test.blockNumber, test.expectedSecondsSinceGenesis, secondsSinceGenesis)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// nodeHashes is a convenience function that returns the hashes for all of the
|
||||
// passed indexes of the provided nodes. It is used to construct expected hash
|
||||
// slices in the tests.
|
||||
func nodeHashes(nodes []*blockNode, indexes ...int) []*daghash.Hash {
|
||||
hashes := make([]*daghash.Hash, 0, len(indexes))
|
||||
for _, idx := range indexes {
|
||||
hashes = append(hashes, nodes[idx].hash)
|
||||
}
|
||||
return hashes
|
||||
}
|
||||
|
||||
// testNoncePrng provides a deterministic prng for the nonce in generated fake
|
||||
// nodes. The ensures that the node have unique hashes.
|
||||
var testNoncePrng = rand.New(rand.NewSource(0))
|
||||
|
||||
// chainedNodes returns the specified number of nodes constructed such that each
|
||||
// subsequent node points to the previous one to create a chain. The first node
|
||||
// will point to the passed parent which can be nil if desired.
|
||||
func chainedNodes(dag *BlockDAG, parents blockSet, numNodes int) []*blockNode {
|
||||
nodes := make([]*blockNode, numNodes)
|
||||
tips := parents
|
||||
for i := 0; i < numNodes; i++ {
|
||||
// This is invalid, but all that is needed is enough to get the
|
||||
// synthetic tests to work.
|
||||
header := wire.BlockHeader{
|
||||
Nonce: testNoncePrng.Uint64(),
|
||||
HashMerkleRoot: &daghash.ZeroHash,
|
||||
AcceptedIDMerkleRoot: &daghash.ZeroHash,
|
||||
UTXOCommitment: &daghash.ZeroHash,
|
||||
}
|
||||
header.ParentHashes = tips.hashes()
|
||||
nodes[i], _ = dag.newBlockNode(&header, tips)
|
||||
tips = setFromSlice(nodes[i])
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
// testTip is a convenience function to grab the tip of a chain of block nodes
|
||||
// created via chainedNodes.
|
||||
func testTip(nodes []*blockNode) *blockNode {
|
||||
return nodes[len(nodes)-1]
|
||||
}
|
||||
|
||||
// TestChainHeightToHashRange ensures that fetching a range of block hashes by start
|
||||
// chain height and end hash works as expected.
|
||||
func TestChainHeightToHashRange(t *testing.T) {
|
||||
// Construct a synthetic block DAG with a block index consisting of
|
||||
// the following structure.
|
||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||
// \-> 16a -> 17a -> 18a (unvalidated)
|
||||
tip := testTip
|
||||
dag := newTestDAG(&dagconfig.SimnetParams)
|
||||
branch0Nodes := chainedNodes(dag, setFromSlice(dag.genesis), 18)
|
||||
branch1Nodes := chainedNodes(dag, setFromSlice(branch0Nodes[14]), 3)
|
||||
for _, node := range branch0Nodes {
|
||||
dag.index.SetStatusFlags(node, statusValid)
|
||||
dag.index.AddNode(node)
|
||||
}
|
||||
for _, node := range branch1Nodes {
|
||||
if node.chainHeight < 18 {
|
||||
dag.index.SetStatusFlags(node, statusValid)
|
||||
}
|
||||
dag.index.AddNode(node)
|
||||
}
|
||||
dag.virtual.SetTips(setFromSlice(tip(branch0Nodes)))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
startChainHeight uint64 // locator for requested inventory
|
||||
endHash *daghash.Hash // stop hash for locator
|
||||
maxResults int // max to locate, 0 = wire const
|
||||
hashes []*daghash.Hash // expected located hashes
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "blocks below tip",
|
||||
startChainHeight: 11,
|
||||
endHash: branch0Nodes[14].hash,
|
||||
maxResults: 10,
|
||||
hashes: nodeHashes(branch0Nodes, 10, 11, 12, 13, 14),
|
||||
},
|
||||
{
|
||||
name: "blocks on main chain",
|
||||
startChainHeight: 15,
|
||||
endHash: branch0Nodes[17].hash,
|
||||
maxResults: 10,
|
||||
hashes: nodeHashes(branch0Nodes, 14, 15, 16, 17),
|
||||
},
|
||||
{
|
||||
name: "blocks on stale chain",
|
||||
startChainHeight: 15,
|
||||
endHash: branch1Nodes[1].hash,
|
||||
maxResults: 10,
|
||||
hashes: append(nodeHashes(branch0Nodes, 14),
|
||||
nodeHashes(branch1Nodes, 0, 1)...),
|
||||
},
|
||||
{
|
||||
name: "invalid start chain height",
|
||||
startChainHeight: 19,
|
||||
endHash: branch0Nodes[17].hash,
|
||||
maxResults: 10,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "too many results",
|
||||
startChainHeight: 1,
|
||||
endHash: branch0Nodes[17].hash,
|
||||
maxResults: 10,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "unvalidated block",
|
||||
startChainHeight: 15,
|
||||
endHash: branch1Nodes[2].hash,
|
||||
maxResults: 10,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
hashes, err := dag.ChainHeightToHashRange(test.startChainHeight, test.endHash,
|
||||
test.maxResults)
|
||||
if err != nil {
|
||||
if !test.expectError {
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(hashes, test.hashes) {
|
||||
t.Errorf("%s: unxpected hashes -- got %v, want %v",
|
||||
test.name, hashes, test.hashes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestIntervalBlockHashes ensures that fetching block hashes at specified
|
||||
// intervals by end hash works as expected.
|
||||
func TestIntervalBlockHashes(t *testing.T) {
|
||||
// Construct a synthetic block DAG with a block index consisting of
|
||||
// the following structure.
|
||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||
// \-> 16a -> 17a -> 18a (unvalidated)
|
||||
tip := testTip
|
||||
dag := newTestDAG(&dagconfig.SimnetParams)
|
||||
branch0Nodes := chainedNodes(dag, setFromSlice(dag.genesis), 18)
|
||||
branch1Nodes := chainedNodes(dag, setFromSlice(branch0Nodes[14]), 3)
|
||||
for _, node := range branch0Nodes {
|
||||
dag.index.SetStatusFlags(node, statusValid)
|
||||
dag.index.AddNode(node)
|
||||
}
|
||||
for _, node := range branch1Nodes {
|
||||
if node.chainHeight < 18 {
|
||||
dag.index.SetStatusFlags(node, statusValid)
|
||||
}
|
||||
dag.index.AddNode(node)
|
||||
}
|
||||
dag.virtual.SetTips(setFromSlice(tip(branch0Nodes)))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
endHash *daghash.Hash
|
||||
interval uint64
|
||||
hashes []*daghash.Hash
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "blocks on main chain",
|
||||
endHash: branch0Nodes[17].hash,
|
||||
interval: 8,
|
||||
hashes: nodeHashes(branch0Nodes, 7, 15),
|
||||
},
|
||||
{
|
||||
name: "blocks on stale chain",
|
||||
endHash: branch1Nodes[1].hash,
|
||||
interval: 8,
|
||||
hashes: append(nodeHashes(branch0Nodes, 7),
|
||||
nodeHashes(branch1Nodes, 0)...),
|
||||
},
|
||||
{
|
||||
name: "no results",
|
||||
endHash: branch0Nodes[17].hash,
|
||||
interval: 20,
|
||||
hashes: []*daghash.Hash{},
|
||||
},
|
||||
{
|
||||
name: "unvalidated block",
|
||||
endHash: branch1Nodes[2].hash,
|
||||
interval: 8,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
hashes, err := dag.IntervalBlockHashes(test.endHash, test.interval)
|
||||
if err != nil {
|
||||
if !test.expectError {
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(hashes, test.hashes) {
|
||||
t.Errorf("%s: unxpected hashes -- got %v, want %v",
|
||||
test.name, hashes, test.hashes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
|
@ -717,8 +717,6 @@ func (dag *BlockDAG) deserializeBlockNode(blockRow []byte) (*blockNode, error) {
|
||||
}
|
||||
}
|
||||
|
||||
node.chainHeight = calculateChainHeight(node)
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
|
@ -130,15 +130,15 @@ func (dag *BlockDAG) thresholdState(prevNode *blockNode, checker thresholdCondit
|
||||
// The threshold state for the window that contains the genesis block is
|
||||
// defined by definition.
|
||||
confirmationWindow := checker.MinerConfirmationWindow()
|
||||
if prevNode == nil || (prevNode.chainHeight+1) < confirmationWindow {
|
||||
if prevNode == nil || (prevNode.blueScore+1) < confirmationWindow {
|
||||
return ThresholdDefined, nil
|
||||
}
|
||||
|
||||
// Get the ancestor that is the last block of the previous confirmation
|
||||
// window in order to get its threshold state. This can be done because
|
||||
// the state is the same for all blocks within a given window.
|
||||
prevNode = prevNode.SelectedAncestor(prevNode.chainHeight -
|
||||
(prevNode.chainHeight+1)%confirmationWindow)
|
||||
prevNode = prevNode.SelectedAncestor(prevNode.blueScore -
|
||||
(prevNode.blueScore+1)%confirmationWindow)
|
||||
|
||||
// Iterate backwards through each of the previous confirmation windows
|
||||
// to find the most recently cached threshold state.
|
||||
|
@ -555,7 +555,7 @@ func (dag *BlockDAG) checkBlockSanity(block *util.Block, flags BehaviorFlags) (t
|
||||
// - BFFastAdd: No checks are performed.
|
||||
//
|
||||
// This function MUST be called with the dag state lock held (for writes).
|
||||
func (dag *BlockDAG) checkBlockHeaderContext(header *wire.BlockHeader, bluestParent *blockNode, blockChainHeight uint64, fastAdd bool) error {
|
||||
func (dag *BlockDAG) checkBlockHeaderContext(header *wire.BlockHeader, bluestParent *blockNode, fastAdd bool) error {
|
||||
if !fastAdd {
|
||||
if err := dag.validateDifficulty(header, bluestParent); err != nil {
|
||||
return err
|
||||
@ -661,7 +661,7 @@ func (dag *BlockDAG) checkBlockContext(block *util.Block, parents blockSet, flag
|
||||
|
||||
// Perform all block header related validation checks.
|
||||
header := &block.MsgBlock().Header
|
||||
if err = dag.checkBlockHeaderContext(header, bluestParent, block.ChainHeight(), fastAdd); err != nil {
|
||||
if err = dag.checkBlockHeaderContext(header, bluestParent, fastAdd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -28,35 +28,35 @@ func TestSequenceLocksActive(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
seqLock *SequenceLock
|
||||
blockChainHeight uint64
|
||||
mtp time.Time
|
||||
seqLock *SequenceLock
|
||||
blockBlueScore uint64
|
||||
mtp time.Time
|
||||
|
||||
want bool
|
||||
}{
|
||||
// Block based sequence lock with equal block height.
|
||||
{seqLock: seqLock(1000, -1), blockChainHeight: 1001, mtp: time.Unix(9, 0), want: true},
|
||||
// Block based sequence lock with equal block blue score.
|
||||
{seqLock: seqLock(1000, -1), blockBlueScore: 1001, mtp: time.Unix(9, 0), want: true},
|
||||
|
||||
// Time based sequence lock with mtp past the absolute time.
|
||||
{seqLock: seqLock(-1, 30), blockChainHeight: 2, mtp: time.Unix(31, 0), want: true},
|
||||
{seqLock: seqLock(-1, 30), blockBlueScore: 2, mtp: time.Unix(31, 0), want: true},
|
||||
|
||||
// Block based sequence lock with current height below seq lock block height.
|
||||
{seqLock: seqLock(1000, -1), blockChainHeight: 90, mtp: time.Unix(9, 0), want: false},
|
||||
// Block based sequence lock with current blue score below seq lock block blue score.
|
||||
{seqLock: seqLock(1000, -1), blockBlueScore: 90, mtp: time.Unix(9, 0), want: false},
|
||||
|
||||
// Time based sequence lock with current time before lock time.
|
||||
{seqLock: seqLock(-1, 30), blockChainHeight: 2, mtp: time.Unix(29, 0), want: false},
|
||||
{seqLock: seqLock(-1, 30), blockBlueScore: 2, mtp: time.Unix(29, 0), want: false},
|
||||
|
||||
// Block based sequence lock at the same height, so shouldn't yet be active.
|
||||
{seqLock: seqLock(1000, -1), blockChainHeight: 1000, mtp: time.Unix(9, 0), want: false},
|
||||
// Block based sequence lock at the same blue score, so shouldn't yet be active.
|
||||
{seqLock: seqLock(1000, -1), blockBlueScore: 1000, mtp: time.Unix(9, 0), want: false},
|
||||
|
||||
// Time based sequence lock with current time equal to lock time, so shouldn't yet be active.
|
||||
{seqLock: seqLock(-1, 30), blockChainHeight: 2, mtp: time.Unix(30, 0), want: false},
|
||||
{seqLock: seqLock(-1, 30), blockBlueScore: 2, mtp: time.Unix(30, 0), want: false},
|
||||
}
|
||||
|
||||
t.Logf("Running %d sequence locks tests", len(tests))
|
||||
for i, test := range tests {
|
||||
got := SequenceLockActive(test.seqLock,
|
||||
test.blockChainHeight, test.mtp)
|
||||
test.blockBlueScore, test.mtp)
|
||||
if got != test.want {
|
||||
t.Fatalf("SequenceLockActive #%d got %v want %v", i,
|
||||
got, test.want)
|
||||
@ -515,42 +515,39 @@ func TestPastMedianTime(t *testing.T) {
|
||||
}
|
||||
|
||||
// Checks that a block is valid if it has timestamp equals to past median time
|
||||
chainHeight := tip.chainHeight + 1
|
||||
node := newTestNode(dag, setFromSlice(tip),
|
||||
blockVersion,
|
||||
dag.powMaxBits,
|
||||
tip.PastMedianTime(dag))
|
||||
|
||||
header := node.Header()
|
||||
err := dag.checkBlockHeaderContext(header, node.parents.bluest(), chainHeight, false)
|
||||
err := dag.checkBlockHeaderContext(header, node.parents.bluest(), false)
|
||||
if err != nil {
|
||||
t.Errorf("TestPastMedianTime: unexpected error from checkBlockHeaderContext: %v"+
|
||||
"(a block with timestamp equals to past median time should be valid)", err)
|
||||
}
|
||||
|
||||
// Checks that a block is valid if its timestamp is after past median time
|
||||
chainHeight = tip.chainHeight + 1
|
||||
node = newTestNode(dag, setFromSlice(tip),
|
||||
blockVersion,
|
||||
dag.powMaxBits,
|
||||
tip.PastMedianTime(dag).Add(time.Second))
|
||||
|
||||
header = node.Header()
|
||||
err = dag.checkBlockHeaderContext(header, node.parents.bluest(), chainHeight, false)
|
||||
err = dag.checkBlockHeaderContext(header, node.parents.bluest(), false)
|
||||
if err != nil {
|
||||
t.Errorf("TestPastMedianTime: unexpected error from checkBlockHeaderContext: %v"+
|
||||
"(a block with timestamp bigger than past median time should be valid)", err)
|
||||
}
|
||||
|
||||
// Checks that a block is invalid if its timestamp is before past median time
|
||||
chainHeight = tip.chainHeight + 1
|
||||
node = newTestNode(dag, setFromSlice(tip),
|
||||
blockVersion,
|
||||
0,
|
||||
tip.PastMedianTime(dag).Add(-time.Second))
|
||||
|
||||
header = node.Header()
|
||||
err = dag.checkBlockHeaderContext(header, node.parents.bluest(), chainHeight, false)
|
||||
err = dag.checkBlockHeaderContext(header, node.parents.bluest(), false)
|
||||
if err == nil {
|
||||
t.Errorf("TestPastMedianTime: unexpected success: block should be invalid if its timestamp is before past median time")
|
||||
}
|
||||
|
@ -250,9 +250,9 @@ func (dag *BlockDAG) warnUnknownRuleActivations(node *blockNode) error {
|
||||
|
||||
case ThresholdLockedIn:
|
||||
window := checker.MinerConfirmationWindow()
|
||||
activationChainHeight := window - (node.chainHeight % window)
|
||||
activationBlueScore := window - (node.blueScore % window)
|
||||
log.Warnf("Unknown new rules are about to activate in "+
|
||||
"%d blocks (bit %d)", activationChainHeight, bit)
|
||||
"%d blueScore (bit %d)", activationBlueScore, bit)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,14 +87,14 @@ func solveBlock(header *wire.BlockHeader, targetDifficulty *big.Int) bool {
|
||||
|
||||
// standardCoinbaseScript returns a standard script suitable for use as the
|
||||
// signature script of the coinbase transaction of a new block. In particular,
|
||||
// it starts with the block height that is required by version 2 blocks.
|
||||
func standardCoinbaseScript(nextBlockHeight uint64, extraNonce uint64) ([]byte, error) {
|
||||
return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)).
|
||||
// it starts with the block blue score.
|
||||
func standardCoinbaseScript(nextBlueScore uint64, extraNonce uint64) ([]byte, error) {
|
||||
return txscript.NewScriptBuilder().AddInt64(int64(nextBlueScore)).
|
||||
AddInt64(int64(extraNonce)).Script()
|
||||
}
|
||||
|
||||
// createCoinbaseTx returns a coinbase transaction paying an appropriate
|
||||
// subsidy based on the passed block height to the provided address.
|
||||
// subsidy based on the passed block blue score to the provided address.
|
||||
func createCoinbaseTx(coinbaseScript []byte, nextBlueScore uint64,
|
||||
addr util.Address, mineTo []wire.TxOut,
|
||||
net *dagconfig.Params) (*util.Tx, error) {
|
||||
@ -133,27 +133,27 @@ func createCoinbaseTx(coinbaseScript []byte, nextBlueScore uint64,
|
||||
// initialized), then the timestamp of the previous block will be used plus 1
|
||||
// second is used. Passing nil for the previous block results in a block that
|
||||
// builds off of the genesis block for the specified chain.
|
||||
func CreateBlock(parentBlock *util.Block, inclusionTxs []*util.Tx,
|
||||
blockVersion int32, blockTime time.Time, miningAddr util.Address,
|
||||
mineTo []wire.TxOut, net *dagconfig.Params, powMaxBits uint32) (*util.Block, error) {
|
||||
func CreateBlock(parentBlock *util.Block, parentBlueScore uint64,
|
||||
inclusionTxs []*util.Tx, blockVersion int32, blockTime time.Time,
|
||||
miningAddr util.Address, mineTo []wire.TxOut, net *dagconfig.Params,
|
||||
powMaxBits uint32) (*util.Block, error) {
|
||||
|
||||
var (
|
||||
parentHash *daghash.Hash
|
||||
blockChainHeight uint64
|
||||
parentBlockTime time.Time
|
||||
parentHash *daghash.Hash
|
||||
blockBlueScore uint64
|
||||
parentBlockTime time.Time
|
||||
)
|
||||
|
||||
// If the parent block isn't specified, then we'll construct a block
|
||||
// that builds off of the genesis block for the chain.
|
||||
if parentBlock == nil {
|
||||
parentHash = net.GenesisHash
|
||||
blockChainHeight = 1
|
||||
parentBlockTime = net.GenesisBlock.Header.Timestamp.Add(time.Minute)
|
||||
} else {
|
||||
parentHash = parentBlock.Hash()
|
||||
blockChainHeight = parentBlock.ChainHeight() + 1
|
||||
parentBlockTime = parentBlock.MsgBlock().Header.Timestamp
|
||||
}
|
||||
blockBlueScore = parentBlueScore + 1
|
||||
|
||||
// If a target block time was specified, then use that as the header's
|
||||
// timestamp. Otherwise, add one second to the parent block unless
|
||||
@ -167,11 +167,11 @@ func CreateBlock(parentBlock *util.Block, inclusionTxs []*util.Tx,
|
||||
}
|
||||
|
||||
extraNonce := uint64(0)
|
||||
coinbaseScript, err := standardCoinbaseScript(blockChainHeight, extraNonce)
|
||||
coinbaseScript, err := standardCoinbaseScript(blockBlueScore, extraNonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockChainHeight,
|
||||
coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockBlueScore,
|
||||
miningAddr, mineTo, net)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -440,17 +440,14 @@ func (h *Harness) GenerateAndSubmitBlockWithCustomCoinbaseOutputs(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectedTipBlueScore := selectedTip.BlueScore
|
||||
mBlock, err := h.Node.GetBlock(selectedTipHash, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parentBlock := util.NewBlock(mBlock)
|
||||
parentBlock.SetChainHeight(selectedTipBlueScore)
|
||||
|
||||
// Create a new block including the specified transactions
|
||||
newBlock, err := CreateBlock(parentBlock, txns, blockVersion,
|
||||
newBlock, err := CreateBlock(parentBlock, selectedTip.BlueScore, txns, blockVersion,
|
||||
blockTime, h.wallet.coinbaseAddr, mineTo, h.ActiveNet, h.powMaxBits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -58,10 +58,6 @@ type Config struct {
|
||||
// associated with.
|
||||
DAGParams *dagconfig.Params
|
||||
|
||||
// DAGChainHeight defines the function to use to access the chain
|
||||
// height of the DAG
|
||||
DAGChainHeight func() uint64
|
||||
|
||||
// MedianTimePast defines the function to use in order to access the
|
||||
// median time past calculated from the point-of-view of the current
|
||||
// selected tip.
|
||||
@ -683,14 +679,13 @@ func (mp *TxPool) RemoveDoubleSpends(tx *util.Tx) {
|
||||
// helper for maybeAcceptTransaction.
|
||||
//
|
||||
// This function MUST be called with the mempool lock held (for writes).
|
||||
func (mp *TxPool) addTransaction(tx *util.Tx, height uint64, blueScore uint64, fee uint64, parentsInPool []*wire.Outpoint) (*TxDesc, error) {
|
||||
func (mp *TxPool) addTransaction(tx *util.Tx, fee uint64, parentsInPool []*wire.Outpoint) (*TxDesc, error) {
|
||||
// Add the transaction to the pool and mark the referenced outpoints
|
||||
// as spent by the pool.
|
||||
txD := &TxDesc{
|
||||
TxDesc: mining.TxDesc{
|
||||
Tx: tx,
|
||||
Added: time.Now(),
|
||||
Height: height,
|
||||
Fee: fee,
|
||||
FeePerKB: fee * 1000 / uint64(tx.MsgTx().SerializeSize()),
|
||||
},
|
||||
@ -789,7 +784,7 @@ func (mp *TxPool) FetchTransaction(txID *daghash.TxID) (*util.Tx, error) {
|
||||
// more details.
|
||||
//
|
||||
// This function MUST be called with the mempool lock held (for writes).
|
||||
func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rejectDupOrphans bool) ([]*daghash.TxID, *TxDesc, error) {
|
||||
func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, rejectDupOrphans bool) ([]*daghash.TxID, *TxDesc, error) {
|
||||
txID := tx.ID()
|
||||
|
||||
// Don't accept the transaction if it already exists in the pool. This
|
||||
@ -1018,8 +1013,7 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rejectDupOrphans bo
|
||||
}
|
||||
|
||||
// Add to transaction pool.
|
||||
bestHeight := mp.cfg.DAGChainHeight()
|
||||
txD, err := mp.addTransaction(tx, bestHeight, nextBlockBlueScore, txFee, parentsInPool)
|
||||
txD, err := mp.addTransaction(tx, txFee, parentsInPool)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -1047,7 +1041,7 @@ func (mp *TxPool) MaybeAcceptTransaction(tx *util.Tx, isNew bool) ([]*daghash.Tx
|
||||
defer mp.cfg.DAG.RUnlock()
|
||||
mp.mtx.Lock()
|
||||
defer mp.mtx.Unlock()
|
||||
hashes, txD, err := mp.maybeAcceptTransaction(tx, isNew, true)
|
||||
hashes, txD, err := mp.maybeAcceptTransaction(tx, true)
|
||||
|
||||
return hashes, txD, err
|
||||
}
|
||||
@ -1089,7 +1083,7 @@ func (mp *TxPool) processOrphans(acceptedTx *util.Tx) []*TxDesc {
|
||||
// Potentially accept an orphan into the tx pool.
|
||||
for _, tx := range orphans {
|
||||
missing, txD, err := mp.maybeAcceptTransaction(
|
||||
tx, true, false)
|
||||
tx, false)
|
||||
if err != nil {
|
||||
// The orphan is now invalid, so there
|
||||
// is no way any other orphans which
|
||||
@ -1176,7 +1170,7 @@ func (mp *TxPool) ProcessTransaction(tx *util.Tx, allowOrphan bool, tag Tag) ([]
|
||||
defer mp.mtx.Unlock()
|
||||
|
||||
// Potentially accept the transaction to the memory pool.
|
||||
missingParents, txD, err := mp.maybeAcceptTransaction(tx, true, true)
|
||||
missingParents, txD, err := mp.maybeAcceptTransaction(tx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1315,7 +1309,6 @@ func (mp *TxPool) RawMempoolVerbose() map[string]*rpcmodel.GetRawMempoolVerboseR
|
||||
Size: int32(tx.MsgTx().SerializeSize()),
|
||||
Fee: util.Amount(desc.Fee).ToKAS(),
|
||||
Time: desc.Added.Unix(),
|
||||
Height: desc.Height,
|
||||
Depends: make([]string, 0),
|
||||
}
|
||||
for _, txIn := range tx.MsgTx().TxIn {
|
||||
|
@ -349,7 +349,6 @@ func newPoolHarness(t *testing.T, dagParams *dagconfig.Params, numOutputs uint32
|
||||
MaxTxVersion: 1,
|
||||
},
|
||||
DAGParams: ¶ms,
|
||||
DAGChainHeight: fDAG.BlueScore,
|
||||
MedianTimePast: fDAG.MedianTimePast,
|
||||
CalcSequenceLockNoLock: calcSequenceLock,
|
||||
SigCache: nil,
|
||||
@ -542,8 +541,8 @@ func TestProcessTransaction(t *testing.T) {
|
||||
}
|
||||
|
||||
//Checks that a coinbase transaction cannot be added to the mempool
|
||||
curHeight := harness.dag.BlueScore()
|
||||
coinbase, err := harness.CreateCoinbaseTx(curHeight+1, 1)
|
||||
currentBlueScore := harness.dag.BlueScore()
|
||||
coinbase, err := harness.CreateCoinbaseTx(currentBlueScore+1, 1)
|
||||
if err != nil {
|
||||
t.Errorf("CreateCoinbaseTx: %v", err)
|
||||
}
|
||||
@ -638,7 +637,7 @@ func TestProcessTransaction(t *testing.T) {
|
||||
t.Fatalf("PayToAddrScript: unexpected error: %v", err)
|
||||
}
|
||||
p2shTx := util.NewTx(wire.NewNativeMsgTx(1, nil, []*wire.TxOut{{Value: 5000000000, ScriptPubKey: p2shScriptPubKey}}))
|
||||
if isAccepted, err := harness.txPool.mpUTXOSet.AddTx(p2shTx.MsgTx(), curHeight+1); err != nil {
|
||||
if isAccepted, err := harness.txPool.mpUTXOSet.AddTx(p2shTx.MsgTx(), currentBlueScore+1); err != nil {
|
||||
t.Fatalf("AddTx unexpectedly failed. Error: %s", err)
|
||||
} else if !isAccepted {
|
||||
t.Fatalf("AddTx unexpectedly didn't add tx %s", p2shTx.ID())
|
||||
|
@ -32,10 +32,6 @@ type TxDesc struct {
|
||||
// Added is the time when the entry was added to the source pool.
|
||||
Added time.Time
|
||||
|
||||
// Height is the block height when the entry was added to the the source
|
||||
// pool.
|
||||
Height uint64
|
||||
|
||||
// Fee is the total fee the transaction associated with the entry pays.
|
||||
Fee uint64
|
||||
|
||||
|
@ -12,7 +12,6 @@ import "encoding/json"
|
||||
type GetBlockHeaderVerboseResult struct {
|
||||
Hash string `json:"hash"`
|
||||
Confirmations uint64 `json:"confirmations"`
|
||||
Height uint64 `json:"height"`
|
||||
Version int32 `json:"version"`
|
||||
VersionHex string `json:"versionHex"`
|
||||
HashMerkleRoot string `json:"hashMerkleRoot"`
|
||||
@ -33,7 +32,6 @@ type GetBlockVerboseResult struct {
|
||||
Hash string `json:"hash"`
|
||||
Confirmations uint64 `json:"confirmations"`
|
||||
Size int32 `json:"size"`
|
||||
Height uint64 `json:"height"`
|
||||
BlueScore uint64 `json:"blueScore"`
|
||||
IsChainBlock bool `json:"isChainBlock"`
|
||||
Version int32 `json:"version"`
|
||||
@ -265,7 +263,6 @@ type GetRawMempoolVerboseResult struct {
|
||||
Size int32 `json:"size"`
|
||||
Fee float64 `json:"fee"`
|
||||
Time int64 `json:"time"`
|
||||
Height uint64 `json:"height"`
|
||||
Depends []string `json:"depends"`
|
||||
}
|
||||
|
||||
|
@ -37,16 +37,16 @@ const (
|
||||
// FilteredBlockAddedNtfn defines the filteredBlockAdded JSON-RPC
|
||||
// notification.
|
||||
type FilteredBlockAddedNtfn struct {
|
||||
ChainHeight uint64
|
||||
BlueScore uint64
|
||||
Header string
|
||||
SubscribedTxs []string
|
||||
}
|
||||
|
||||
// NewFilteredBlockAddedNtfn returns a new instance which can be used to
|
||||
// issue a filteredBlockAdded JSON-RPC notification.
|
||||
func NewFilteredBlockAddedNtfn(chainHeight uint64, header string, subscribedTxs []string) *FilteredBlockAddedNtfn {
|
||||
func NewFilteredBlockAddedNtfn(blueScore uint64, header string, subscribedTxs []string) *FilteredBlockAddedNtfn {
|
||||
return &FilteredBlockAddedNtfn{
|
||||
ChainHeight: chainHeight,
|
||||
BlueScore: blueScore,
|
||||
Header: header,
|
||||
SubscribedTxs: subscribedTxs,
|
||||
}
|
||||
|
@ -35,14 +35,14 @@ func TestRPCServerWebsocketNotifications(t *testing.T) {
|
||||
{
|
||||
name: "filteredBlockAdded",
|
||||
newNtfn: func() (interface{}, error) {
|
||||
return rpcmodel.NewCommand("filteredBlockAdded", 100000, "header", []string{"tx0", "tx1"})
|
||||
return rpcmodel.NewCommand("filteredBlockAdded", 100, "header", []string{"tx0", "tx1"})
|
||||
},
|
||||
staticNtfn: func() interface{} {
|
||||
return rpcmodel.NewFilteredBlockAddedNtfn(100000, "header", []string{"tx0", "tx1"})
|
||||
return rpcmodel.NewFilteredBlockAddedNtfn(100, "header", []string{"tx0", "tx1"})
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"filteredBlockAdded","params":[100000,"header",["tx0","tx1"]],"id":null}`,
|
||||
marshalled: `{"jsonrpc":"1.0","method":"filteredBlockAdded","params":[100,"header",["tx0","tx1"]],"id":null}`,
|
||||
unmarshalled: &rpcmodel.FilteredBlockAddedNtfn{
|
||||
ChainHeight: 100000,
|
||||
BlueScore: 100,
|
||||
Header: "header",
|
||||
SubscribedTxs: []string{"tx0", "tx1"},
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/peer"
|
||||
"github.com/kaspanet/kaspad/wire"
|
||||
)
|
||||
@ -8,15 +9,19 @@ import (
|
||||
// OnGetBlockLocator is invoked when a peer receives a getlocator kaspa
|
||||
// message.
|
||||
func (sp *Peer) OnGetBlockLocator(_ *peer.Peer, msg *wire.MsgGetBlockLocator) {
|
||||
locator := sp.server.DAG.BlockLocatorFromHashes(msg.StartHash, msg.StopHash)
|
||||
|
||||
if len(locator) == 0 {
|
||||
peerLog.Infof("Couldn't build a block locator between blocks %s and %s"+
|
||||
" that was requested from peer %s",
|
||||
sp)
|
||||
locator, err := sp.server.DAG.BlockLocatorFromHashes(msg.StartHash, msg.StopHash)
|
||||
if err != nil || len(locator) == 0 {
|
||||
warning := fmt.Sprintf("Couldn't build a block locator between blocks "+
|
||||
"%s and %s that was requested from peer %s", msg.StartHash, msg.StopHash, sp)
|
||||
if err != nil {
|
||||
warning = fmt.Sprintf("%s: %s", warning, err)
|
||||
}
|
||||
peerLog.Warnf(warning)
|
||||
sp.Disconnect()
|
||||
return
|
||||
}
|
||||
err := sp.PushBlockLocatorMsg(locator)
|
||||
|
||||
err = sp.PushBlockLocatorMsg(locator)
|
||||
if err != nil {
|
||||
peerLog.Errorf("Failed to send block locator message to peer %s: %s",
|
||||
sp, err)
|
||||
|
@ -1662,7 +1662,6 @@ func NewServer(listenAddrs []string, db database.DB, dagParams *dagconfig.Params
|
||||
MaxTxVersion: 1,
|
||||
},
|
||||
DAGParams: dagParams,
|
||||
DAGChainHeight: func() uint64 { return s.DAG.ChainHeight() },
|
||||
MedianTimePast: func() time.Time { return s.DAG.CalcPastMedianTime() },
|
||||
CalcSequenceLockNoLock: func(tx *util.Tx, utxoSet blockdag.UTXOSet) (*blockdag.SequenceLock, error) {
|
||||
return s.DAG.CalcSequenceLockNoLock(tx, utxoSet, true)
|
||||
|
@ -210,23 +210,19 @@ func buildGetBlockVerboseResult(s *Server, block *util.Block, isVerboseTx bool)
|
||||
params := s.cfg.DAGParams
|
||||
blockHeader := block.MsgBlock().Header
|
||||
|
||||
// Get the block chain height.
|
||||
blockChainHeight, err := s.cfg.DAG.BlockChainHeightByHash(hash)
|
||||
blockBlueScore, err := s.cfg.DAG.BlueScoreByBlockHash(hash)
|
||||
if err != nil {
|
||||
context := "Failed to obtain block height"
|
||||
context := "Could not get block blue score"
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
|
||||
// Get the hashes for the next blocks unless there are none.
|
||||
var nextHashStrings []string
|
||||
if blockChainHeight < s.cfg.DAG.ChainHeight() { //TODO: (Ori) This is probably wrong. Done only for compilation
|
||||
childHashes, err := s.cfg.DAG.ChildHashesByHash(hash)
|
||||
if err != nil {
|
||||
context := "No next block"
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
nextHashStrings = daghash.Strings(childHashes)
|
||||
childHashes, err := s.cfg.DAG.ChildHashesByHash(hash)
|
||||
if err != nil {
|
||||
context := "No next block"
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
childHashStrings := daghash.Strings(childHashes)
|
||||
|
||||
blockConfirmations, err := s.cfg.DAG.BlockConfirmationsByHashNoLock(hash)
|
||||
if err != nil {
|
||||
@ -234,12 +230,6 @@ func buildGetBlockVerboseResult(s *Server, block *util.Block, isVerboseTx bool)
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
|
||||
blockBlueScore, err := s.cfg.DAG.BlueScoreByBlockHash(hash)
|
||||
if err != nil {
|
||||
context := "Could not get block blue score"
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
|
||||
selectedParentHash, err := s.cfg.DAG.SelectedParentHash(hash)
|
||||
if err != nil {
|
||||
context := "Could not get block selected parent"
|
||||
@ -264,13 +254,12 @@ func buildGetBlockVerboseResult(s *Server, block *util.Block, isVerboseTx bool)
|
||||
Nonce: blockHeader.Nonce,
|
||||
Time: blockHeader.Timestamp.Unix(),
|
||||
Confirmations: blockConfirmations,
|
||||
Height: blockChainHeight,
|
||||
BlueScore: blockBlueScore,
|
||||
IsChainBlock: isChainBlock,
|
||||
Size: int32(block.MsgBlock().SerializeSize()),
|
||||
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
||||
Difficulty: getDifficultyRatio(blockHeader.Bits, params),
|
||||
NextHashes: nextHashStrings,
|
||||
NextHashes: childHashStrings,
|
||||
}
|
||||
|
||||
if isVerboseTx {
|
||||
|
@ -40,23 +40,13 @@ func handleGetBlockHeader(s *Server, cmd interface{}, closeChan <-chan struct{})
|
||||
|
||||
// The verbose flag is set, so generate the JSON object and return it.
|
||||
|
||||
// Get the block chain height from chain.
|
||||
blockChainHeight, err := s.cfg.DAG.BlockChainHeightByHash(hash)
|
||||
// Get the hashes for the next blocks unless there are none.
|
||||
childHashes, err := s.cfg.DAG.ChildHashesByHash(hash)
|
||||
if err != nil {
|
||||
context := "Failed to obtain block height"
|
||||
context := "No next block"
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
|
||||
// Get the hashes for the next blocks unless there are none.
|
||||
var nextHashStrings []string
|
||||
if blockChainHeight < s.cfg.DAG.ChainHeight() { //TODO: (Ori) This is probably wrong. Done only for compilation
|
||||
childHashes, err := s.cfg.DAG.ChildHashesByHash(hash)
|
||||
if err != nil {
|
||||
context := "No next block"
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
nextHashStrings = daghash.Strings(childHashes)
|
||||
}
|
||||
childHashStrings := daghash.Strings(childHashes)
|
||||
|
||||
blockConfirmations, err := s.cfg.DAG.BlockConfirmationsByHash(hash)
|
||||
if err != nil {
|
||||
@ -74,12 +64,11 @@ func handleGetBlockHeader(s *Server, cmd interface{}, closeChan <-chan struct{})
|
||||
blockHeaderReply := rpcmodel.GetBlockHeaderVerboseResult{
|
||||
Hash: c.Hash,
|
||||
Confirmations: blockConfirmations,
|
||||
Height: blockChainHeight,
|
||||
Version: blockHeader.Version,
|
||||
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
||||
HashMerkleRoot: blockHeader.HashMerkleRoot.String(),
|
||||
AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(),
|
||||
NextHashes: nextHashStrings,
|
||||
NextHashes: childHashStrings,
|
||||
ParentHashes: daghash.Strings(blockHeader.ParentHashes),
|
||||
SelectedParentHash: selectedParentHash.String(),
|
||||
Nonce: blockHeader.Nonce,
|
||||
|
@ -106,11 +106,11 @@ func handleGetBlockTemplate(s *Server, cmd interface{}, closeChan <-chan struct{
|
||||
|
||||
// No point in generating templates or processing proposals before
|
||||
// the DAG is synced. Note that we make a special check for when
|
||||
// we have nothing besides the genesis block (chainHeight == 0),
|
||||
// we have nothing besides the genesis block (blueScore == 0),
|
||||
// because in that state IsCurrent may still return true.
|
||||
currentChainHeight := s.cfg.DAG.ChainHeight()
|
||||
if (currentChainHeight != 0 && !s.cfg.SyncMgr.IsCurrent()) ||
|
||||
(currentChainHeight == 0 && !s.cfg.CPUMiner.ShouldMineOnGenesis()) {
|
||||
currentBlueScore := s.cfg.DAG.SelectedTipBlueScore()
|
||||
if (currentBlueScore != 0 && !s.cfg.SyncMgr.IsCurrent()) ||
|
||||
(currentBlueScore == 0 && !s.cfg.CPUMiner.ShouldMineOnGenesis()) {
|
||||
return nil, &rpcmodel.RPCError{
|
||||
Code: rpcmodel.ErrRPCClientInInitialDownload,
|
||||
Message: "Kaspa is downloading blocks...",
|
||||
|
@ -665,8 +665,7 @@ func (m *wsNotificationManager) notifyFilteredBlockAdded(clients map[chan struct
|
||||
"added notification: %s", err)
|
||||
return
|
||||
}
|
||||
ntfn := rpcmodel.NewFilteredBlockAddedNtfn(block.ChainHeight(),
|
||||
hex.EncodeToString(w.Bytes()), nil)
|
||||
ntfn := rpcmodel.NewFilteredBlockAddedNtfn(block.BlueScore(), hex.EncodeToString(w.Bytes()), nil)
|
||||
|
||||
// Search for relevant transactions for each client and save them
|
||||
// serialized in hex encoding for the notification.
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
@ -20,10 +19,6 @@ import (
|
||||
type OutOfRangeError string
|
||||
|
||||
const (
|
||||
// BlockHeightUnknown is the value returned for a block height that is unknown.
|
||||
// This is typically because the block has not been inserted into the DAG yet.
|
||||
BlockHeightUnknown = math.MaxUint64
|
||||
|
||||
// CoinbaseTransactionIndex is the index of the coinbase transaction in every block
|
||||
CoinbaseTransactionIndex = 0
|
||||
)
|
||||
@ -41,9 +36,9 @@ type Block struct {
|
||||
msgBlock *wire.MsgBlock // Underlying MsgBlock
|
||||
serializedBlock []byte // Serialized bytes for the block
|
||||
blockHash *daghash.Hash // Cached block hash
|
||||
chainHeight uint64 // Selected-chain height
|
||||
transactions []*Tx // Transactions
|
||||
txnsGenerated bool // ALL wrapped transactions generated
|
||||
blueScore uint64 // Blue score
|
||||
}
|
||||
|
||||
// MsgBlock returns the underlying wire.MsgBlock for the Block.
|
||||
@ -189,17 +184,6 @@ func (b *Block) TxLoc() ([]wire.TxLoc, error) {
|
||||
return txLocs, err
|
||||
}
|
||||
|
||||
// ChainHeight returns the saved chan height of the block . This value
|
||||
// will be BlockHeightUnknown if it hasn't already explicitly been set.
|
||||
func (b *Block) ChainHeight() uint64 {
|
||||
return b.chainHeight
|
||||
}
|
||||
|
||||
// SetChainHeight sets the chain height of the block.
|
||||
func (b *Block) SetChainHeight(chainHeight uint64) {
|
||||
b.chainHeight = chainHeight
|
||||
}
|
||||
|
||||
// IsGenesis returns whether or not this block is the genesis block.
|
||||
func (b *Block) IsGenesis() bool {
|
||||
return b.MsgBlock().Header.IsGenesis()
|
||||
@ -215,12 +199,21 @@ func (b *Block) Timestamp() time.Time {
|
||||
return b.msgBlock.Header.Timestamp
|
||||
}
|
||||
|
||||
// BlueScore returns this block's blue score.
|
||||
func (b *Block) BlueScore() uint64 {
|
||||
return b.blueScore
|
||||
}
|
||||
|
||||
// SetBlueScore sets the blue score of the block.
|
||||
func (b *Block) SetBlueScore(blueScore uint64) {
|
||||
b.blueScore = blueScore
|
||||
}
|
||||
|
||||
// NewBlock returns a new instance of a kaspa block given an underlying
|
||||
// wire.MsgBlock. See Block.
|
||||
func NewBlock(msgBlock *wire.MsgBlock) *Block {
|
||||
return &Block{
|
||||
msgBlock: msgBlock,
|
||||
chainHeight: BlockHeightUnknown,
|
||||
msgBlock: msgBlock,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,14 +29,6 @@ func TestBlock(t *testing.T) {
|
||||
spew.Sdump(msgBlock), spew.Sdump(&Block100000))
|
||||
}
|
||||
|
||||
// Ensure block chain height set and get work properly.
|
||||
wantChainHeight := uint64(100000)
|
||||
b.SetChainHeight(wantChainHeight)
|
||||
if gotChainHeight := b.ChainHeight(); gotChainHeight != wantChainHeight {
|
||||
t.Errorf("ChainHeight: mismatched chain height - got %v, want %v",
|
||||
gotChainHeight, wantChainHeight)
|
||||
}
|
||||
|
||||
// Hash for block 100,000.
|
||||
wantHashStr := "839a8e072e6d402128f6f9a32ffc012e471e071e8ef8405552b1e58ef7b681f0"
|
||||
wantHash, err := daghash.NewHashFromStr(wantHashStr)
|
||||
|
Loading…
x
Reference in New Issue
Block a user