mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-06 22:26:47 +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
|
// Connect the passed block to the DAG. This also handles validation of the
|
||||||
// transaction scripts.
|
// transaction scripts.
|
||||||
|
@ -19,7 +19,7 @@ func TestAncestorErrors(t *testing.T) {
|
|||||||
defer teardownFunc()
|
defer teardownFunc()
|
||||||
|
|
||||||
node := newTestNode(dag, newSet(), int32(0x10000000), 0, time.Unix(0, 0))
|
node := newTestNode(dag, newSet(), int32(0x10000000), 0, time.Unix(0, 0))
|
||||||
node.chainHeight = 2
|
node.blueScore = 2
|
||||||
ancestor := node.SelectedAncestor(3)
|
ancestor := node.SelectedAncestor(3)
|
||||||
if ancestor != nil {
|
if ancestor != nil {
|
||||||
t.Errorf("TestAncestorErrors: Ancestor() unexpectedly returned a node. Expected: <nil>")
|
t.Errorf("TestAncestorErrors: Ancestor() unexpectedly returned a node. Expected: <nil>")
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package blockdag
|
package blockdag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/util"
|
|
||||||
"github.com/kaspanet/kaspad/util/daghash"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BlockLocator is used to help locate a specific block. The algorithm for
|
// BlockLocator is used to help locate a specific block. The algorithm for
|
||||||
@ -29,7 +29,7 @@ type BlockLocator []*daghash.Hash
|
|||||||
// known.
|
// known.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// 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()
|
dag.dagLock.RLock()
|
||||||
defer dag.dagLock.RUnlock()
|
defer dag.dagLock.RUnlock()
|
||||||
startNode := dag.index.LookupNode(startHash)
|
startNode := dag.index.LookupNode(startHash)
|
||||||
@ -40,15 +40,6 @@ func (dag *BlockDAG) BlockLocatorFromHashes(startHash, stopHash *daghash.Hash) B
|
|||||||
return dag.blockLocator(startNode, stopNode)
|
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.
|
// 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
|
// The default value for the start node is the selected tip, and the default
|
||||||
// values of the stop node is the genesis block.
|
// 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.
|
// See the BlockLocator type comments for more details.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the DAG state lock held (for reads).
|
// 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.
|
// Use the selected tip if requested.
|
||||||
if startNode == nil {
|
if startNode == nil {
|
||||||
startNode = dag.virtual.selectedParent
|
startNode = dag.virtual.selectedParent
|
||||||
@ -70,51 +61,36 @@ func (dag *BlockDAG) blockLocator(startNode, stopNode *blockNode) BlockLocator {
|
|||||||
// block locator won't contain the start node.
|
// block locator won't contain the start node.
|
||||||
startNode = startNode.selectedParent
|
startNode = startNode.selectedParent
|
||||||
|
|
||||||
// If the start node or the stop node are not in the
|
node := startNode
|
||||||
// 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)
|
|
||||||
|
|
||||||
step := uint64(1)
|
step := uint64(1)
|
||||||
for node := startNode; node != nil; {
|
locator := make(BlockLocator, 0)
|
||||||
|
for node != nil {
|
||||||
locator = append(locator, node.hash)
|
locator = append(locator, node.hash)
|
||||||
|
|
||||||
// Nothing more to add once the stop node has been added.
|
// 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
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate chainHeight of previous node to include ensuring the
|
// Calculate blueScore of previous node to include ensuring the
|
||||||
// final node is stopNode.
|
// final node is stopNode.
|
||||||
nextChainHeight := node.chainHeight - step
|
nextBlueScore := node.blueScore - step
|
||||||
if nextChainHeight < stopNode.chainHeight {
|
if nextBlueScore < stopNode.blueScore {
|
||||||
nextChainHeight = stopNode.chainHeight
|
nextBlueScore = stopNode.blueScore
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk backwards through the nodes to the correct ancestor.
|
// walk backwards through the nodes to the correct ancestor.
|
||||||
node = node.SelectedAncestor(nextChainHeight)
|
node = node.SelectedAncestor(nextBlueScore)
|
||||||
|
|
||||||
// Double the distance between included hashes.
|
// Double the distance between included hashes.
|
||||||
step *= 2
|
step *= 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return locator
|
return locator, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindNextLocatorBoundaries returns the lowest unknown block locator, hash
|
// 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 is the double sha 256 of the block.
|
||||||
hash *daghash.Hash
|
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
|
// Some fields from block headers to aid in reconstructing headers
|
||||||
// from memory. These must be treated as immutable and are intentionally
|
// from memory. These must be treated as immutable and are intentionally
|
||||||
// ordered to avoid padding on 64-bit platforms.
|
// ordered to avoid padding on 64-bit platforms.
|
||||||
@ -107,13 +104,6 @@ type blockNode struct {
|
|||||||
isFinalized bool
|
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
|
// 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).
|
// anticone of its selected parent (parent with highest blue score).
|
||||||
// selectedParentAnticone is used to update reachability data we store for future reachability queries.
|
// 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 {
|
if err != nil {
|
||||||
panic(errors.Wrap(err, "unexpected error in GHOSTDAG"))
|
panic(errors.Wrap(err, "unexpected error in GHOSTDAG"))
|
||||||
}
|
}
|
||||||
node.chainHeight = calculateChainHeight(node)
|
|
||||||
}
|
}
|
||||||
return node, selectedParentAnticone
|
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
|
// 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.
|
// This function is safe for concurrent access.
|
||||||
func (node *blockNode) SelectedAncestor(chainHeight uint64) *blockNode {
|
func (node *blockNode) SelectedAncestor(blueScore uint64) *blockNode {
|
||||||
if chainHeight < 0 || chainHeight > node.chainHeight {
|
if blueScore < 0 || blueScore > node.blueScore {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n := node
|
n := node
|
||||||
for ; n != nil && n.chainHeight != chainHeight; n = n.selectedParent {
|
for n != nil && n.blueScore > blueScore {
|
||||||
// Intentionally left blank
|
n = n.selectedParent
|
||||||
}
|
}
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// RelativeAncestor returns the ancestor block node a relative 'distance' of
|
// RelativeAncestor returns the ancestor block node a relative 'distance' of
|
||||||
// chain-blocks before this node. This is equivalent to calling Ancestor with
|
// blue blocks before this node. This is equivalent to calling Ancestor with
|
||||||
// the node's chain-height minus provided distance.
|
// the node's blue score minus provided distance.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (node *blockNode) RelativeAncestor(distance uint64) *blockNode {
|
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
|
// 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
|
return removedChainHashes, addedChainHashes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainHeight return the chain-height of the selected tip. In other words - it returns
|
// SelectedTipBlueScore returns the blue score of the selected tip.
|
||||||
// the length of the dag's selected-parent chain
|
func (dag *BlockDAG) SelectedTipBlueScore() uint64 {
|
||||||
func (dag *BlockDAG) ChainHeight() uint64 {
|
return dag.selectedTip().blueScore
|
||||||
return dag.selectedTip().chainHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VirtualBlueScore returns the blue score of the current virtual block
|
// 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
|
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
|
// ChildHashesByHash returns the child hashes of the block with the given hash in the
|
||||||
// DAG.
|
// DAG.
|
||||||
//
|
//
|
||||||
@ -1608,81 +1593,6 @@ func (dag *BlockDAG) SelectedParentHash(blockHash *daghash.Hash) (*daghash.Hash,
|
|||||||
return node.selectedParent.hash, nil
|
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
|
// getBlueBlocksHashesBetween returns the hashes of the blocks after the provided
|
||||||
// start hash until the provided stop hash is reached, or up to the
|
// start hash until the provided stop hash is reached, or up to the
|
||||||
// provided max number of block hashes.
|
// provided max number of block hashes.
|
||||||
@ -2055,8 +1965,8 @@ func New(config *Config) (*BlockDAG, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectedTip := dag.selectedTip()
|
selectedTip := dag.selectedTip()
|
||||||
log.Infof("DAG state (chain height %d, hash %s)",
|
log.Infof("DAG state (blue score %d, hash %s)",
|
||||||
selectedTip.chainHeight, selectedTip.hash)
|
selectedTip.blueScore, selectedTip.hash)
|
||||||
|
|
||||||
return dag, nil
|
return dag, nil
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/dagconfig"
|
"github.com/kaspanet/kaspad/dagconfig"
|
||||||
"github.com/kaspanet/kaspad/database"
|
"github.com/kaspanet/kaspad/database"
|
||||||
"github.com/kaspanet/kaspad/txscript"
|
"github.com/kaspanet/kaspad/txscript"
|
||||||
@ -271,7 +268,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||||||
TxID: *targetTx.ID(),
|
TxID: *targetTx.ID(),
|
||||||
Index: 0,
|
Index: 0,
|
||||||
}
|
}
|
||||||
prevUtxoChainHeight := uint64(numBlocksToGenerate) - 4
|
prevUtxoBlueScore := uint64(numBlocksToGenerate) - 4
|
||||||
|
|
||||||
// Obtain the past median time from the PoV of the input created above.
|
// 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
|
// 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
|
// the MTP will be calculated from the PoV of the yet-to-be-mined
|
||||||
// block.
|
// block.
|
||||||
nextMedianTime := node.PastMedianTime(dag).Unix()
|
nextMedianTime := node.PastMedianTime(dag).Unix()
|
||||||
nextBlockChainHeight := int32(numBlocksToGenerate) + 1
|
nextBlockBlueScore := int32(numBlocksToGenerate) + 1
|
||||||
|
|
||||||
// Add an additional transaction which will serve as our unconfirmed
|
// Add an additional transaction which will serve as our unconfirmed
|
||||||
// output.
|
// output.
|
||||||
@ -369,7 +366,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||||||
utxoSet: utxoSet,
|
utxoSet: utxoSet,
|
||||||
want: &SequenceLock{
|
want: &SequenceLock{
|
||||||
Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1,
|
Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1,
|
||||||
BlockBlueScore: int64(prevUtxoChainHeight) + 3,
|
BlockBlueScore: int64(prevUtxoBlueScore) + 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Transaction with a single input. The input's sequence number
|
// Transaction with a single input. The input's sequence number
|
||||||
@ -382,7 +379,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||||||
utxoSet: utxoSet,
|
utxoSet: utxoSet,
|
||||||
want: &SequenceLock{
|
want: &SequenceLock{
|
||||||
Seconds: -1,
|
Seconds: -1,
|
||||||
BlockBlueScore: int64(prevUtxoChainHeight) + 2,
|
BlockBlueScore: int64(prevUtxoBlueScore) + 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// A transaction with two inputs with lock times expressed in
|
// A transaction with two inputs with lock times expressed in
|
||||||
@ -421,7 +418,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||||||
utxoSet: utxoSet,
|
utxoSet: utxoSet,
|
||||||
want: &SequenceLock{
|
want: &SequenceLock{
|
||||||
Seconds: -1,
|
Seconds: -1,
|
||||||
BlockBlueScore: int64(prevUtxoChainHeight) + 10,
|
BlockBlueScore: int64(prevUtxoBlueScore) + 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// A transaction with multiple inputs. Two inputs are time
|
// A transaction with multiple inputs. Two inputs are time
|
||||||
@ -447,7 +444,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||||||
utxoSet: utxoSet,
|
utxoSet: utxoSet,
|
||||||
want: &SequenceLock{
|
want: &SequenceLock{
|
||||||
Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1,
|
Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1,
|
||||||
BlockBlueScore: int64(prevUtxoChainHeight) + 8,
|
BlockBlueScore: int64(prevUtxoBlueScore) + 8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// A transaction with a single unconfirmed input. As the input
|
// A transaction with a single unconfirmed input. As the input
|
||||||
@ -463,7 +460,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||||||
mempool: true,
|
mempool: true,
|
||||||
want: &SequenceLock{
|
want: &SequenceLock{
|
||||||
Seconds: -1,
|
Seconds: -1,
|
||||||
BlockBlueScore: int64(nextBlockChainHeight) + 1,
|
BlockBlueScore: int64(nextBlockBlueScore) + 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// A transaction with a single unconfirmed input. The input has
|
// 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)
|
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) {
|
func TestNew(t *testing.T) {
|
||||||
|
@ -717,8 +717,6 @@ func (dag *BlockDAG) deserializeBlockNode(blockRow []byte) (*blockNode, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node.chainHeight = calculateChainHeight(node)
|
|
||||||
|
|
||||||
return node, nil
|
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
|
// The threshold state for the window that contains the genesis block is
|
||||||
// defined by definition.
|
// defined by definition.
|
||||||
confirmationWindow := checker.MinerConfirmationWindow()
|
confirmationWindow := checker.MinerConfirmationWindow()
|
||||||
if prevNode == nil || (prevNode.chainHeight+1) < confirmationWindow {
|
if prevNode == nil || (prevNode.blueScore+1) < confirmationWindow {
|
||||||
return ThresholdDefined, nil
|
return ThresholdDefined, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the ancestor that is the last block of the previous confirmation
|
// 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
|
// 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.
|
// the state is the same for all blocks within a given window.
|
||||||
prevNode = prevNode.SelectedAncestor(prevNode.chainHeight -
|
prevNode = prevNode.SelectedAncestor(prevNode.blueScore -
|
||||||
(prevNode.chainHeight+1)%confirmationWindow)
|
(prevNode.blueScore+1)%confirmationWindow)
|
||||||
|
|
||||||
// Iterate backwards through each of the previous confirmation windows
|
// Iterate backwards through each of the previous confirmation windows
|
||||||
// to find the most recently cached threshold state.
|
// 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.
|
// - BFFastAdd: No checks are performed.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the dag state lock held (for writes).
|
// 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 !fastAdd {
|
||||||
if err := dag.validateDifficulty(header, bluestParent); err != nil {
|
if err := dag.validateDifficulty(header, bluestParent); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -661,7 +661,7 @@ func (dag *BlockDAG) checkBlockContext(block *util.Block, parents blockSet, flag
|
|||||||
|
|
||||||
// Perform all block header related validation checks.
|
// Perform all block header related validation checks.
|
||||||
header := &block.MsgBlock().Header
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,34 +29,34 @@ func TestSequenceLocksActive(t *testing.T) {
|
|||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
seqLock *SequenceLock
|
seqLock *SequenceLock
|
||||||
blockChainHeight uint64
|
blockBlueScore uint64
|
||||||
mtp time.Time
|
mtp time.Time
|
||||||
|
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
// Block based sequence lock with equal block height.
|
// Block based sequence lock with equal block blue score.
|
||||||
{seqLock: seqLock(1000, -1), blockChainHeight: 1001, mtp: time.Unix(9, 0), want: true},
|
{seqLock: seqLock(1000, -1), blockBlueScore: 1001, mtp: time.Unix(9, 0), want: true},
|
||||||
|
|
||||||
// Time based sequence lock with mtp past the absolute time.
|
// 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.
|
// Block based sequence lock with current blue score below seq lock block blue score.
|
||||||
{seqLock: seqLock(1000, -1), blockChainHeight: 90, mtp: time.Unix(9, 0), want: false},
|
{seqLock: seqLock(1000, -1), blockBlueScore: 90, mtp: time.Unix(9, 0), want: false},
|
||||||
|
|
||||||
// Time based sequence lock with current time before lock time.
|
// 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.
|
// Block based sequence lock at the same blue score, so shouldn't yet be active.
|
||||||
{seqLock: seqLock(1000, -1), blockChainHeight: 1000, mtp: time.Unix(9, 0), want: false},
|
{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.
|
// 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))
|
t.Logf("Running %d sequence locks tests", len(tests))
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
got := SequenceLockActive(test.seqLock,
|
got := SequenceLockActive(test.seqLock,
|
||||||
test.blockChainHeight, test.mtp)
|
test.blockBlueScore, test.mtp)
|
||||||
if got != test.want {
|
if got != test.want {
|
||||||
t.Fatalf("SequenceLockActive #%d got %v want %v", i,
|
t.Fatalf("SequenceLockActive #%d got %v want %v", i,
|
||||||
got, test.want)
|
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
|
// Checks that a block is valid if it has timestamp equals to past median time
|
||||||
chainHeight := tip.chainHeight + 1
|
|
||||||
node := newTestNode(dag, setFromSlice(tip),
|
node := newTestNode(dag, setFromSlice(tip),
|
||||||
blockVersion,
|
blockVersion,
|
||||||
dag.powMaxBits,
|
dag.powMaxBits,
|
||||||
tip.PastMedianTime(dag))
|
tip.PastMedianTime(dag))
|
||||||
|
|
||||||
header := node.Header()
|
header := node.Header()
|
||||||
err := dag.checkBlockHeaderContext(header, node.parents.bluest(), chainHeight, false)
|
err := dag.checkBlockHeaderContext(header, node.parents.bluest(), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("TestPastMedianTime: unexpected error from checkBlockHeaderContext: %v"+
|
t.Errorf("TestPastMedianTime: unexpected error from checkBlockHeaderContext: %v"+
|
||||||
"(a block with timestamp equals to past median time should be valid)", err)
|
"(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
|
// Checks that a block is valid if its timestamp is after past median time
|
||||||
chainHeight = tip.chainHeight + 1
|
|
||||||
node = newTestNode(dag, setFromSlice(tip),
|
node = newTestNode(dag, setFromSlice(tip),
|
||||||
blockVersion,
|
blockVersion,
|
||||||
dag.powMaxBits,
|
dag.powMaxBits,
|
||||||
tip.PastMedianTime(dag).Add(time.Second))
|
tip.PastMedianTime(dag).Add(time.Second))
|
||||||
|
|
||||||
header = node.Header()
|
header = node.Header()
|
||||||
err = dag.checkBlockHeaderContext(header, node.parents.bluest(), chainHeight, false)
|
err = dag.checkBlockHeaderContext(header, node.parents.bluest(), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("TestPastMedianTime: unexpected error from checkBlockHeaderContext: %v"+
|
t.Errorf("TestPastMedianTime: unexpected error from checkBlockHeaderContext: %v"+
|
||||||
"(a block with timestamp bigger than past median time should be valid)", err)
|
"(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
|
// Checks that a block is invalid if its timestamp is before past median time
|
||||||
chainHeight = tip.chainHeight + 1
|
|
||||||
node = newTestNode(dag, setFromSlice(tip),
|
node = newTestNode(dag, setFromSlice(tip),
|
||||||
blockVersion,
|
blockVersion,
|
||||||
0,
|
0,
|
||||||
tip.PastMedianTime(dag).Add(-time.Second))
|
tip.PastMedianTime(dag).Add(-time.Second))
|
||||||
|
|
||||||
header = node.Header()
|
header = node.Header()
|
||||||
err = dag.checkBlockHeaderContext(header, node.parents.bluest(), chainHeight, false)
|
err = dag.checkBlockHeaderContext(header, node.parents.bluest(), false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("TestPastMedianTime: unexpected success: block should be invalid if its timestamp is before past median time")
|
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:
|
case ThresholdLockedIn:
|
||||||
window := checker.MinerConfirmationWindow()
|
window := checker.MinerConfirmationWindow()
|
||||||
activationChainHeight := window - (node.chainHeight % window)
|
activationBlueScore := window - (node.blueScore % window)
|
||||||
log.Warnf("Unknown new rules are about to activate in "+
|
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
|
// standardCoinbaseScript returns a standard script suitable for use as the
|
||||||
// signature script of the coinbase transaction of a new block. In particular,
|
// 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.
|
// it starts with the block blue score.
|
||||||
func standardCoinbaseScript(nextBlockHeight uint64, extraNonce uint64) ([]byte, error) {
|
func standardCoinbaseScript(nextBlueScore uint64, extraNonce uint64) ([]byte, error) {
|
||||||
return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)).
|
return txscript.NewScriptBuilder().AddInt64(int64(nextBlueScore)).
|
||||||
AddInt64(int64(extraNonce)).Script()
|
AddInt64(int64(extraNonce)).Script()
|
||||||
}
|
}
|
||||||
|
|
||||||
// createCoinbaseTx returns a coinbase transaction paying an appropriate
|
// 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,
|
func createCoinbaseTx(coinbaseScript []byte, nextBlueScore uint64,
|
||||||
addr util.Address, mineTo []wire.TxOut,
|
addr util.Address, mineTo []wire.TxOut,
|
||||||
net *dagconfig.Params) (*util.Tx, error) {
|
net *dagconfig.Params) (*util.Tx, error) {
|
||||||
@ -133,13 +133,14 @@ func createCoinbaseTx(coinbaseScript []byte, nextBlueScore uint64,
|
|||||||
// initialized), then the timestamp of the previous block will be used plus 1
|
// 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
|
// second is used. Passing nil for the previous block results in a block that
|
||||||
// builds off of the genesis block for the specified chain.
|
// builds off of the genesis block for the specified chain.
|
||||||
func CreateBlock(parentBlock *util.Block, inclusionTxs []*util.Tx,
|
func CreateBlock(parentBlock *util.Block, parentBlueScore uint64,
|
||||||
blockVersion int32, blockTime time.Time, miningAddr util.Address,
|
inclusionTxs []*util.Tx, blockVersion int32, blockTime time.Time,
|
||||||
mineTo []wire.TxOut, net *dagconfig.Params, powMaxBits uint32) (*util.Block, error) {
|
miningAddr util.Address, mineTo []wire.TxOut, net *dagconfig.Params,
|
||||||
|
powMaxBits uint32) (*util.Block, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
parentHash *daghash.Hash
|
parentHash *daghash.Hash
|
||||||
blockChainHeight uint64
|
blockBlueScore uint64
|
||||||
parentBlockTime time.Time
|
parentBlockTime time.Time
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -147,13 +148,12 @@ func CreateBlock(parentBlock *util.Block, inclusionTxs []*util.Tx,
|
|||||||
// that builds off of the genesis block for the chain.
|
// that builds off of the genesis block for the chain.
|
||||||
if parentBlock == nil {
|
if parentBlock == nil {
|
||||||
parentHash = net.GenesisHash
|
parentHash = net.GenesisHash
|
||||||
blockChainHeight = 1
|
|
||||||
parentBlockTime = net.GenesisBlock.Header.Timestamp.Add(time.Minute)
|
parentBlockTime = net.GenesisBlock.Header.Timestamp.Add(time.Minute)
|
||||||
} else {
|
} else {
|
||||||
parentHash = parentBlock.Hash()
|
parentHash = parentBlock.Hash()
|
||||||
blockChainHeight = parentBlock.ChainHeight() + 1
|
|
||||||
parentBlockTime = parentBlock.MsgBlock().Header.Timestamp
|
parentBlockTime = parentBlock.MsgBlock().Header.Timestamp
|
||||||
}
|
}
|
||||||
|
blockBlueScore = parentBlueScore + 1
|
||||||
|
|
||||||
// If a target block time was specified, then use that as the header's
|
// If a target block time was specified, then use that as the header's
|
||||||
// timestamp. Otherwise, add one second to the parent block unless
|
// 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)
|
extraNonce := uint64(0)
|
||||||
coinbaseScript, err := standardCoinbaseScript(blockChainHeight, extraNonce)
|
coinbaseScript, err := standardCoinbaseScript(blockBlueScore, extraNonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockChainHeight,
|
coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockBlueScore,
|
||||||
miningAddr, mineTo, net)
|
miningAddr, mineTo, net)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -440,17 +440,14 @@ func (h *Harness) GenerateAndSubmitBlockWithCustomCoinbaseOutputs(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedTipBlueScore := selectedTip.BlueScore
|
|
||||||
mBlock, err := h.Node.GetBlock(selectedTipHash, nil)
|
mBlock, err := h.Node.GetBlock(selectedTipHash, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
parentBlock := util.NewBlock(mBlock)
|
parentBlock := util.NewBlock(mBlock)
|
||||||
parentBlock.SetChainHeight(selectedTipBlueScore)
|
|
||||||
|
|
||||||
// Create a new block including the specified transactions
|
// 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)
|
blockTime, h.wallet.coinbaseAddr, mineTo, h.ActiveNet, h.powMaxBits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -58,10 +58,6 @@ type Config struct {
|
|||||||
// associated with.
|
// associated with.
|
||||||
DAGParams *dagconfig.Params
|
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
|
// MedianTimePast defines the function to use in order to access the
|
||||||
// median time past calculated from the point-of-view of the current
|
// median time past calculated from the point-of-view of the current
|
||||||
// selected tip.
|
// selected tip.
|
||||||
@ -683,14 +679,13 @@ func (mp *TxPool) RemoveDoubleSpends(tx *util.Tx) {
|
|||||||
// helper for maybeAcceptTransaction.
|
// helper for maybeAcceptTransaction.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the mempool lock held (for writes).
|
// 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
|
// Add the transaction to the pool and mark the referenced outpoints
|
||||||
// as spent by the pool.
|
// as spent by the pool.
|
||||||
txD := &TxDesc{
|
txD := &TxDesc{
|
||||||
TxDesc: mining.TxDesc{
|
TxDesc: mining.TxDesc{
|
||||||
Tx: tx,
|
Tx: tx,
|
||||||
Added: time.Now(),
|
Added: time.Now(),
|
||||||
Height: height,
|
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
FeePerKB: fee * 1000 / uint64(tx.MsgTx().SerializeSize()),
|
FeePerKB: fee * 1000 / uint64(tx.MsgTx().SerializeSize()),
|
||||||
},
|
},
|
||||||
@ -789,7 +784,7 @@ func (mp *TxPool) FetchTransaction(txID *daghash.TxID) (*util.Tx, error) {
|
|||||||
// more details.
|
// more details.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the mempool lock held (for writes).
|
// 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()
|
txID := tx.ID()
|
||||||
|
|
||||||
// Don't accept the transaction if it already exists in the pool. This
|
// 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.
|
// Add to transaction pool.
|
||||||
bestHeight := mp.cfg.DAGChainHeight()
|
txD, err := mp.addTransaction(tx, txFee, parentsInPool)
|
||||||
txD, err := mp.addTransaction(tx, bestHeight, nextBlockBlueScore, txFee, parentsInPool)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -1047,7 +1041,7 @@ func (mp *TxPool) MaybeAcceptTransaction(tx *util.Tx, isNew bool) ([]*daghash.Tx
|
|||||||
defer mp.cfg.DAG.RUnlock()
|
defer mp.cfg.DAG.RUnlock()
|
||||||
mp.mtx.Lock()
|
mp.mtx.Lock()
|
||||||
defer mp.mtx.Unlock()
|
defer mp.mtx.Unlock()
|
||||||
hashes, txD, err := mp.maybeAcceptTransaction(tx, isNew, true)
|
hashes, txD, err := mp.maybeAcceptTransaction(tx, true)
|
||||||
|
|
||||||
return hashes, txD, err
|
return hashes, txD, err
|
||||||
}
|
}
|
||||||
@ -1089,7 +1083,7 @@ func (mp *TxPool) processOrphans(acceptedTx *util.Tx) []*TxDesc {
|
|||||||
// Potentially accept an orphan into the tx pool.
|
// Potentially accept an orphan into the tx pool.
|
||||||
for _, tx := range orphans {
|
for _, tx := range orphans {
|
||||||
missing, txD, err := mp.maybeAcceptTransaction(
|
missing, txD, err := mp.maybeAcceptTransaction(
|
||||||
tx, true, false)
|
tx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The orphan is now invalid, so there
|
// The orphan is now invalid, so there
|
||||||
// is no way any other orphans which
|
// 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()
|
defer mp.mtx.Unlock()
|
||||||
|
|
||||||
// Potentially accept the transaction to the memory pool.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1315,7 +1309,6 @@ func (mp *TxPool) RawMempoolVerbose() map[string]*rpcmodel.GetRawMempoolVerboseR
|
|||||||
Size: int32(tx.MsgTx().SerializeSize()),
|
Size: int32(tx.MsgTx().SerializeSize()),
|
||||||
Fee: util.Amount(desc.Fee).ToKAS(),
|
Fee: util.Amount(desc.Fee).ToKAS(),
|
||||||
Time: desc.Added.Unix(),
|
Time: desc.Added.Unix(),
|
||||||
Height: desc.Height,
|
|
||||||
Depends: make([]string, 0),
|
Depends: make([]string, 0),
|
||||||
}
|
}
|
||||||
for _, txIn := range tx.MsgTx().TxIn {
|
for _, txIn := range tx.MsgTx().TxIn {
|
||||||
|
@ -349,7 +349,6 @@ func newPoolHarness(t *testing.T, dagParams *dagconfig.Params, numOutputs uint32
|
|||||||
MaxTxVersion: 1,
|
MaxTxVersion: 1,
|
||||||
},
|
},
|
||||||
DAGParams: ¶ms,
|
DAGParams: ¶ms,
|
||||||
DAGChainHeight: fDAG.BlueScore,
|
|
||||||
MedianTimePast: fDAG.MedianTimePast,
|
MedianTimePast: fDAG.MedianTimePast,
|
||||||
CalcSequenceLockNoLock: calcSequenceLock,
|
CalcSequenceLockNoLock: calcSequenceLock,
|
||||||
SigCache: nil,
|
SigCache: nil,
|
||||||
@ -542,8 +541,8 @@ func TestProcessTransaction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Checks that a coinbase transaction cannot be added to the mempool
|
//Checks that a coinbase transaction cannot be added to the mempool
|
||||||
curHeight := harness.dag.BlueScore()
|
currentBlueScore := harness.dag.BlueScore()
|
||||||
coinbase, err := harness.CreateCoinbaseTx(curHeight+1, 1)
|
coinbase, err := harness.CreateCoinbaseTx(currentBlueScore+1, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("CreateCoinbaseTx: %v", err)
|
t.Errorf("CreateCoinbaseTx: %v", err)
|
||||||
}
|
}
|
||||||
@ -638,7 +637,7 @@ func TestProcessTransaction(t *testing.T) {
|
|||||||
t.Fatalf("PayToAddrScript: unexpected error: %v", err)
|
t.Fatalf("PayToAddrScript: unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
p2shTx := util.NewTx(wire.NewNativeMsgTx(1, nil, []*wire.TxOut{{Value: 5000000000, ScriptPubKey: p2shScriptPubKey}}))
|
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)
|
t.Fatalf("AddTx unexpectedly failed. Error: %s", err)
|
||||||
} else if !isAccepted {
|
} else if !isAccepted {
|
||||||
t.Fatalf("AddTx unexpectedly didn't add tx %s", p2shTx.ID())
|
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 is the time when the entry was added to the source pool.
|
||||||
Added time.Time
|
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 is the total fee the transaction associated with the entry pays.
|
||||||
Fee uint64
|
Fee uint64
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import "encoding/json"
|
|||||||
type GetBlockHeaderVerboseResult struct {
|
type GetBlockHeaderVerboseResult struct {
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
Confirmations uint64 `json:"confirmations"`
|
Confirmations uint64 `json:"confirmations"`
|
||||||
Height uint64 `json:"height"`
|
|
||||||
Version int32 `json:"version"`
|
Version int32 `json:"version"`
|
||||||
VersionHex string `json:"versionHex"`
|
VersionHex string `json:"versionHex"`
|
||||||
HashMerkleRoot string `json:"hashMerkleRoot"`
|
HashMerkleRoot string `json:"hashMerkleRoot"`
|
||||||
@ -33,7 +32,6 @@ type GetBlockVerboseResult struct {
|
|||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
Confirmations uint64 `json:"confirmations"`
|
Confirmations uint64 `json:"confirmations"`
|
||||||
Size int32 `json:"size"`
|
Size int32 `json:"size"`
|
||||||
Height uint64 `json:"height"`
|
|
||||||
BlueScore uint64 `json:"blueScore"`
|
BlueScore uint64 `json:"blueScore"`
|
||||||
IsChainBlock bool `json:"isChainBlock"`
|
IsChainBlock bool `json:"isChainBlock"`
|
||||||
Version int32 `json:"version"`
|
Version int32 `json:"version"`
|
||||||
@ -265,7 +263,6 @@ type GetRawMempoolVerboseResult struct {
|
|||||||
Size int32 `json:"size"`
|
Size int32 `json:"size"`
|
||||||
Fee float64 `json:"fee"`
|
Fee float64 `json:"fee"`
|
||||||
Time int64 `json:"time"`
|
Time int64 `json:"time"`
|
||||||
Height uint64 `json:"height"`
|
|
||||||
Depends []string `json:"depends"`
|
Depends []string `json:"depends"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,16 +37,16 @@ const (
|
|||||||
// FilteredBlockAddedNtfn defines the filteredBlockAdded JSON-RPC
|
// FilteredBlockAddedNtfn defines the filteredBlockAdded JSON-RPC
|
||||||
// notification.
|
// notification.
|
||||||
type FilteredBlockAddedNtfn struct {
|
type FilteredBlockAddedNtfn struct {
|
||||||
ChainHeight uint64
|
BlueScore uint64
|
||||||
Header string
|
Header string
|
||||||
SubscribedTxs []string
|
SubscribedTxs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFilteredBlockAddedNtfn returns a new instance which can be used to
|
// NewFilteredBlockAddedNtfn returns a new instance which can be used to
|
||||||
// issue a filteredBlockAdded JSON-RPC notification.
|
// 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{
|
return &FilteredBlockAddedNtfn{
|
||||||
ChainHeight: chainHeight,
|
BlueScore: blueScore,
|
||||||
Header: header,
|
Header: header,
|
||||||
SubscribedTxs: subscribedTxs,
|
SubscribedTxs: subscribedTxs,
|
||||||
}
|
}
|
||||||
|
@ -35,14 +35,14 @@ func TestRPCServerWebsocketNotifications(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "filteredBlockAdded",
|
name: "filteredBlockAdded",
|
||||||
newNtfn: func() (interface{}, error) {
|
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{} {
|
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{
|
unmarshalled: &rpcmodel.FilteredBlockAddedNtfn{
|
||||||
ChainHeight: 100000,
|
BlueScore: 100,
|
||||||
Header: "header",
|
Header: "header",
|
||||||
SubscribedTxs: []string{"tx0", "tx1"},
|
SubscribedTxs: []string{"tx0", "tx1"},
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package p2p
|
package p2p
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/kaspanet/kaspad/peer"
|
"github.com/kaspanet/kaspad/peer"
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
)
|
)
|
||||||
@ -8,15 +9,19 @@ import (
|
|||||||
// OnGetBlockLocator is invoked when a peer receives a getlocator kaspa
|
// OnGetBlockLocator is invoked when a peer receives a getlocator kaspa
|
||||||
// message.
|
// message.
|
||||||
func (sp *Peer) OnGetBlockLocator(_ *peer.Peer, msg *wire.MsgGetBlockLocator) {
|
func (sp *Peer) OnGetBlockLocator(_ *peer.Peer, msg *wire.MsgGetBlockLocator) {
|
||||||
locator := sp.server.DAG.BlockLocatorFromHashes(msg.StartHash, msg.StopHash)
|
locator, err := sp.server.DAG.BlockLocatorFromHashes(msg.StartHash, msg.StopHash)
|
||||||
|
if err != nil || len(locator) == 0 {
|
||||||
if len(locator) == 0 {
|
warning := fmt.Sprintf("Couldn't build a block locator between blocks "+
|
||||||
peerLog.Infof("Couldn't build a block locator between blocks %s and %s"+
|
"%s and %s that was requested from peer %s", msg.StartHash, msg.StopHash, sp)
|
||||||
" that was requested from peer %s",
|
if err != nil {
|
||||||
sp)
|
warning = fmt.Sprintf("%s: %s", warning, err)
|
||||||
|
}
|
||||||
|
peerLog.Warnf(warning)
|
||||||
|
sp.Disconnect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := sp.PushBlockLocatorMsg(locator)
|
|
||||||
|
err = sp.PushBlockLocatorMsg(locator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
peerLog.Errorf("Failed to send block locator message to peer %s: %s",
|
peerLog.Errorf("Failed to send block locator message to peer %s: %s",
|
||||||
sp, err)
|
sp, err)
|
||||||
|
@ -1662,7 +1662,6 @@ func NewServer(listenAddrs []string, db database.DB, dagParams *dagconfig.Params
|
|||||||
MaxTxVersion: 1,
|
MaxTxVersion: 1,
|
||||||
},
|
},
|
||||||
DAGParams: dagParams,
|
DAGParams: dagParams,
|
||||||
DAGChainHeight: func() uint64 { return s.DAG.ChainHeight() },
|
|
||||||
MedianTimePast: func() time.Time { return s.DAG.CalcPastMedianTime() },
|
MedianTimePast: func() time.Time { return s.DAG.CalcPastMedianTime() },
|
||||||
CalcSequenceLockNoLock: func(tx *util.Tx, utxoSet blockdag.UTXOSet) (*blockdag.SequenceLock, error) {
|
CalcSequenceLockNoLock: func(tx *util.Tx, utxoSet blockdag.UTXOSet) (*blockdag.SequenceLock, error) {
|
||||||
return s.DAG.CalcSequenceLockNoLock(tx, utxoSet, true)
|
return s.DAG.CalcSequenceLockNoLock(tx, utxoSet, true)
|
||||||
|
@ -210,23 +210,19 @@ func buildGetBlockVerboseResult(s *Server, block *util.Block, isVerboseTx bool)
|
|||||||
params := s.cfg.DAGParams
|
params := s.cfg.DAGParams
|
||||||
blockHeader := block.MsgBlock().Header
|
blockHeader := block.MsgBlock().Header
|
||||||
|
|
||||||
// Get the block chain height.
|
blockBlueScore, err := s.cfg.DAG.BlueScoreByBlockHash(hash)
|
||||||
blockChainHeight, err := s.cfg.DAG.BlockChainHeightByHash(hash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
context := "Failed to obtain block height"
|
context := "Could not get block blue score"
|
||||||
return nil, internalRPCError(err.Error(), context)
|
return nil, internalRPCError(err.Error(), context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the hashes for the next blocks unless there are none.
|
// 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)
|
childHashes, err := s.cfg.DAG.ChildHashesByHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
context := "No next block"
|
context := "No next block"
|
||||||
return nil, internalRPCError(err.Error(), context)
|
return nil, internalRPCError(err.Error(), context)
|
||||||
}
|
}
|
||||||
nextHashStrings = daghash.Strings(childHashes)
|
childHashStrings := daghash.Strings(childHashes)
|
||||||
}
|
|
||||||
|
|
||||||
blockConfirmations, err := s.cfg.DAG.BlockConfirmationsByHashNoLock(hash)
|
blockConfirmations, err := s.cfg.DAG.BlockConfirmationsByHashNoLock(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -234,12 +230,6 @@ func buildGetBlockVerboseResult(s *Server, block *util.Block, isVerboseTx bool)
|
|||||||
return nil, internalRPCError(err.Error(), context)
|
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)
|
selectedParentHash, err := s.cfg.DAG.SelectedParentHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
context := "Could not get block selected parent"
|
context := "Could not get block selected parent"
|
||||||
@ -264,13 +254,12 @@ func buildGetBlockVerboseResult(s *Server, block *util.Block, isVerboseTx bool)
|
|||||||
Nonce: blockHeader.Nonce,
|
Nonce: blockHeader.Nonce,
|
||||||
Time: blockHeader.Timestamp.Unix(),
|
Time: blockHeader.Timestamp.Unix(),
|
||||||
Confirmations: blockConfirmations,
|
Confirmations: blockConfirmations,
|
||||||
Height: blockChainHeight,
|
|
||||||
BlueScore: blockBlueScore,
|
BlueScore: blockBlueScore,
|
||||||
IsChainBlock: isChainBlock,
|
IsChainBlock: isChainBlock,
|
||||||
Size: int32(block.MsgBlock().SerializeSize()),
|
Size: int32(block.MsgBlock().SerializeSize()),
|
||||||
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
||||||
Difficulty: getDifficultyRatio(blockHeader.Bits, params),
|
Difficulty: getDifficultyRatio(blockHeader.Bits, params),
|
||||||
NextHashes: nextHashStrings,
|
NextHashes: childHashStrings,
|
||||||
}
|
}
|
||||||
|
|
||||||
if isVerboseTx {
|
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.
|
// 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)
|
|
||||||
if err != nil {
|
|
||||||
context := "Failed to obtain block height"
|
|
||||||
return nil, internalRPCError(err.Error(), context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the hashes for the next blocks unless there are none.
|
// 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)
|
childHashes, err := s.cfg.DAG.ChildHashesByHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
context := "No next block"
|
context := "No next block"
|
||||||
return nil, internalRPCError(err.Error(), context)
|
return nil, internalRPCError(err.Error(), context)
|
||||||
}
|
}
|
||||||
nextHashStrings = daghash.Strings(childHashes)
|
childHashStrings := daghash.Strings(childHashes)
|
||||||
}
|
|
||||||
|
|
||||||
blockConfirmations, err := s.cfg.DAG.BlockConfirmationsByHash(hash)
|
blockConfirmations, err := s.cfg.DAG.BlockConfirmationsByHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -74,12 +64,11 @@ func handleGetBlockHeader(s *Server, cmd interface{}, closeChan <-chan struct{})
|
|||||||
blockHeaderReply := rpcmodel.GetBlockHeaderVerboseResult{
|
blockHeaderReply := rpcmodel.GetBlockHeaderVerboseResult{
|
||||||
Hash: c.Hash,
|
Hash: c.Hash,
|
||||||
Confirmations: blockConfirmations,
|
Confirmations: blockConfirmations,
|
||||||
Height: blockChainHeight,
|
|
||||||
Version: blockHeader.Version,
|
Version: blockHeader.Version,
|
||||||
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
||||||
HashMerkleRoot: blockHeader.HashMerkleRoot.String(),
|
HashMerkleRoot: blockHeader.HashMerkleRoot.String(),
|
||||||
AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(),
|
AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(),
|
||||||
NextHashes: nextHashStrings,
|
NextHashes: childHashStrings,
|
||||||
ParentHashes: daghash.Strings(blockHeader.ParentHashes),
|
ParentHashes: daghash.Strings(blockHeader.ParentHashes),
|
||||||
SelectedParentHash: selectedParentHash.String(),
|
SelectedParentHash: selectedParentHash.String(),
|
||||||
Nonce: blockHeader.Nonce,
|
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
|
// No point in generating templates or processing proposals before
|
||||||
// the DAG is synced. Note that we make a special check for when
|
// 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.
|
// because in that state IsCurrent may still return true.
|
||||||
currentChainHeight := s.cfg.DAG.ChainHeight()
|
currentBlueScore := s.cfg.DAG.SelectedTipBlueScore()
|
||||||
if (currentChainHeight != 0 && !s.cfg.SyncMgr.IsCurrent()) ||
|
if (currentBlueScore != 0 && !s.cfg.SyncMgr.IsCurrent()) ||
|
||||||
(currentChainHeight == 0 && !s.cfg.CPUMiner.ShouldMineOnGenesis()) {
|
(currentBlueScore == 0 && !s.cfg.CPUMiner.ShouldMineOnGenesis()) {
|
||||||
return nil, &rpcmodel.RPCError{
|
return nil, &rpcmodel.RPCError{
|
||||||
Code: rpcmodel.ErrRPCClientInInitialDownload,
|
Code: rpcmodel.ErrRPCClientInInitialDownload,
|
||||||
Message: "Kaspa is downloading blocks...",
|
Message: "Kaspa is downloading blocks...",
|
||||||
|
@ -665,8 +665,7 @@ func (m *wsNotificationManager) notifyFilteredBlockAdded(clients map[chan struct
|
|||||||
"added notification: %s", err)
|
"added notification: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ntfn := rpcmodel.NewFilteredBlockAddedNtfn(block.ChainHeight(),
|
ntfn := rpcmodel.NewFilteredBlockAddedNtfn(block.BlueScore(), hex.EncodeToString(w.Bytes()), nil)
|
||||||
hex.EncodeToString(w.Bytes()), nil)
|
|
||||||
|
|
||||||
// Search for relevant transactions for each client and save them
|
// Search for relevant transactions for each client and save them
|
||||||
// serialized in hex encoding for the notification.
|
// serialized in hex encoding for the notification.
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/util/daghash"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
@ -20,10 +19,6 @@ import (
|
|||||||
type OutOfRangeError string
|
type OutOfRangeError string
|
||||||
|
|
||||||
const (
|
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 is the index of the coinbase transaction in every block
|
||||||
CoinbaseTransactionIndex = 0
|
CoinbaseTransactionIndex = 0
|
||||||
)
|
)
|
||||||
@ -41,9 +36,9 @@ type Block struct {
|
|||||||
msgBlock *wire.MsgBlock // Underlying MsgBlock
|
msgBlock *wire.MsgBlock // Underlying MsgBlock
|
||||||
serializedBlock []byte // Serialized bytes for the block
|
serializedBlock []byte // Serialized bytes for the block
|
||||||
blockHash *daghash.Hash // Cached block hash
|
blockHash *daghash.Hash // Cached block hash
|
||||||
chainHeight uint64 // Selected-chain height
|
|
||||||
transactions []*Tx // Transactions
|
transactions []*Tx // Transactions
|
||||||
txnsGenerated bool // ALL wrapped transactions generated
|
txnsGenerated bool // ALL wrapped transactions generated
|
||||||
|
blueScore uint64 // Blue score
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgBlock returns the underlying wire.MsgBlock for the Block.
|
// MsgBlock returns the underlying wire.MsgBlock for the Block.
|
||||||
@ -189,17 +184,6 @@ func (b *Block) TxLoc() ([]wire.TxLoc, error) {
|
|||||||
return txLocs, err
|
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.
|
// IsGenesis returns whether or not this block is the genesis block.
|
||||||
func (b *Block) IsGenesis() bool {
|
func (b *Block) IsGenesis() bool {
|
||||||
return b.MsgBlock().Header.IsGenesis()
|
return b.MsgBlock().Header.IsGenesis()
|
||||||
@ -215,12 +199,21 @@ func (b *Block) Timestamp() time.Time {
|
|||||||
return b.msgBlock.Header.Timestamp
|
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
|
// NewBlock returns a new instance of a kaspa block given an underlying
|
||||||
// wire.MsgBlock. See Block.
|
// wire.MsgBlock. See Block.
|
||||||
func NewBlock(msgBlock *wire.MsgBlock) *Block {
|
func NewBlock(msgBlock *wire.MsgBlock) *Block {
|
||||||
return &Block{
|
return &Block{
|
||||||
msgBlock: msgBlock,
|
msgBlock: msgBlock,
|
||||||
chainHeight: BlockHeightUnknown,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,14 +29,6 @@ func TestBlock(t *testing.T) {
|
|||||||
spew.Sdump(msgBlock), spew.Sdump(&Block100000))
|
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.
|
// Hash for block 100,000.
|
||||||
wantHashStr := "839a8e072e6d402128f6f9a32ffc012e471e071e8ef8405552b1e58ef7b681f0"
|
wantHashStr := "839a8e072e6d402128f6f9a32ffc012e471e071e8ef8405552b1e58ef7b681f0"
|
||||||
wantHash, err := daghash.NewHashFromStr(wantHashStr)
|
wantHash, err := daghash.NewHashFromStr(wantHashStr)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user