mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-09 07:36:43 +00:00
[DEV-72] Write Blues()
* [DEV-62] add phantom constructs to blocknode * [DEV-62] add phantom constructs to blocknode * [DEV-72] write blues() * [DEV-72] write blues() * [DEV-72] write blues() * [DEV-62] add comments to new phantom constructs in blocknode * Fixed dbIndexConnectBlock. (#33) * Fixed dbIndexConnectBlock. * Removed redundant check in storeFilter. * Created a new method to BlockHeader: IsGenesis. * [DEV-71] Implement BlockHeap (#35) * [DEV-71] Implemented BlockHeap. * [DEV-71] Removed irrelevant comment. * [DEV-71] Renamed variables in Pop() and split Less() to multiple lines. * [DEV-72] write blues() * [DEV-72] write blues() * [DEV-72] write blues() * [DEV-72] write blues tests * [DEV-72] write blues tests * [DEV-72] remove relevant past * [DEV-72] write blues tests * [DEV-72] write blues tests * [DEV-72] write blues tests * [DEV-72] write functions to order blockSet by hash and write blue tests * [DEV-72] add secret mining and censorship attack tests * [DEV-72] remove prints * [DEV-72] remove K from dagconfig.Params * [DEV-72] remove K from dagconfig.Params * [DEV-72] change blueScore to uint64 * [DEV-72] block V was missing, so renamed w -> v, x -> w etc * [DEV-72] use node.String instead of %v * [DEV-72] block V was missing, so renamed w -> v, x -> w etc * [DEV-72] add K to dagconfig.Params, and add expected reds to all phantom tests * [DEV-72] set K=10 and add comments to phantom and phantom tests * [DEV-72] fix formatting and add comments to TestPhantom * [DEV-72] fix grammar
This commit is contained in:
parent
2068ac299a
commit
904f2cf2e3
@ -59,7 +59,7 @@ func (b *BlockDAG) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) e
|
|||||||
// if the block ultimately gets connected to the main chain, it starts out
|
// if the block ultimately gets connected to the main chain, it starts out
|
||||||
// on a side chain.
|
// on a side chain.
|
||||||
blockHeader := &block.MsgBlock().Header
|
blockHeader := &block.MsgBlock().Header
|
||||||
newNode := newBlockNode(blockHeader, parents)
|
newNode := newBlockNode(blockHeader, parents, b.dagParams.K)
|
||||||
newNode.status = statusDataStored
|
newNode.status = statusDataStored
|
||||||
|
|
||||||
b.index.AddNode(newNode)
|
b.index.AddNode(newNode)
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package blockdag
|
package blockdag
|
||||||
|
|
||||||
import "container/heap"
|
import (
|
||||||
|
"container/heap"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
// baseHeap is an implementation for heap.Interface that sorts blocks by their height
|
// baseHeap is an implementation for heap.Interface that sorts blocks by their height
|
||||||
type baseHeap []*blockNode
|
type baseHeap []*blockNode
|
||||||
@ -22,7 +26,7 @@ func (h *baseHeap) Pop() interface{} {
|
|||||||
|
|
||||||
func (h baseHeap) Less(i, j int) bool {
|
func (h baseHeap) Less(i, j int) bool {
|
||||||
if h[i].height == h[j].height {
|
if h[i].height == h[j].height {
|
||||||
return HashToBig(&h[i].hash).Cmp(HashToBig(&h[j].hash)) > 0
|
return daghash.HashToBig(&h[i].hash).Cmp(daghash.HashToBig(&h[j].hash)) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return h[i].height > h[j].height
|
return h[i].height > h[j].height
|
||||||
|
@ -2,19 +2,20 @@ package blockdag
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
|
||||||
"github.com/daglabs/btcd/dagconfig"
|
"github.com/daglabs/btcd/dagconfig"
|
||||||
|
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestBlockHeap tests pushing, popping, and determining the length of the heap.
|
// TestBlockHeap tests pushing, popping, and determining the length of the heap.
|
||||||
func TestBlockHeap(t *testing.T) {
|
func TestBlockHeap(t *testing.T) {
|
||||||
block0Header := dagconfig.MainNetParams.GenesisBlock.Header
|
block0Header := dagconfig.MainNetParams.GenesisBlock.Header
|
||||||
block0 := newBlockNode(&block0Header, newSet())
|
block0 := newBlockNode(&block0Header, newSet(), dagconfig.MainNetParams.K)
|
||||||
|
|
||||||
block100000Header := Block100000.Header
|
block100000Header := Block100000.Header
|
||||||
block100000 := newBlockNode(&block100000Header, setFromSlice(block0))
|
block100000 := newBlockNode(&block100000Header, setFromSlice(block0), dagconfig.MainNetParams.K)
|
||||||
|
|
||||||
block0smallHash := newBlockNode(&block0Header, newSet())
|
block0smallHash := newBlockNode(&block0Header, newSet(), dagconfig.MainNetParams.K)
|
||||||
block0smallHash.hash = daghash.Hash{}
|
block0smallHash.hash = daghash.Hash{}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package blockdag
|
package blockdag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
@ -85,10 +86,10 @@ type blockNode struct {
|
|||||||
diffChild *blockNode
|
diffChild *blockNode
|
||||||
|
|
||||||
// blues are all blue blocks in this block's worldview that are in its selected parent anticone
|
// blues are all blue blocks in this block's worldview that are in its selected parent anticone
|
||||||
blues blockSet
|
blues []*blockNode
|
||||||
|
|
||||||
// blueScore is the count of all the blue blocks in this block's past (including itself)
|
// blueScore is the count of all the blue blocks in this block's past
|
||||||
blueScore int64
|
blueScore uint64
|
||||||
|
|
||||||
// utxoDiff is the UTXO of the block represented as a diff to the virtual block
|
// utxoDiff is the UTXO of the block represented as a diff to the virtual block
|
||||||
utxoDiff UtxoViewpoint
|
utxoDiff UtxoViewpoint
|
||||||
@ -124,10 +125,11 @@ type blockNode struct {
|
|||||||
// calculating the height and workSum from the respective fields on the first parent.
|
// calculating the height and workSum from the respective fields on the first parent.
|
||||||
// This function is NOT safe for concurrent access. It must only be called when
|
// This function is NOT safe for concurrent access. It must only be called when
|
||||||
// initially creating a node.
|
// initially creating a node.
|
||||||
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents blockSet) {
|
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents blockSet, phantomK uint32) {
|
||||||
*node = blockNode{
|
*node = blockNode{
|
||||||
hash: blockHeader.BlockHash(),
|
hash: blockHeader.BlockHash(),
|
||||||
parents: parents,
|
parents: parents,
|
||||||
|
children: make(blockSet),
|
||||||
workSum: CalcWork(blockHeader.Bits),
|
workSum: CalcWork(blockHeader.Bits),
|
||||||
version: blockHeader.Version,
|
version: blockHeader.Version,
|
||||||
bits: blockHeader.Bits,
|
bits: blockHeader.Bits,
|
||||||
@ -136,18 +138,35 @@ func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents block
|
|||||||
merkleRoot: blockHeader.MerkleRoot,
|
merkleRoot: blockHeader.MerkleRoot,
|
||||||
}
|
}
|
||||||
if len(parents) > 0 {
|
if len(parents) > 0 {
|
||||||
node.selectedParent = parents.first()
|
addNodeAsChildToParents(node)
|
||||||
node.height = node.selectedParent.height + 1
|
node.blues, node.selectedParent, node.blueScore = phantom(node, phantomK)
|
||||||
|
node.height = calculateNodeHeight(node)
|
||||||
node.workSum = node.workSum.Add(node.selectedParent.workSum, node.workSum)
|
node.workSum = node.workSum.Add(node.selectedParent.workSum, node.workSum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addNodeAsChildToParents(node *blockNode) {
|
||||||
|
for _, parent := range node.parents {
|
||||||
|
parent.children.add(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateNodeHeight(node *blockNode) int32 {
|
||||||
|
var maxHeight int32
|
||||||
|
for _, parent := range node.parents {
|
||||||
|
if maxHeight < parent.height {
|
||||||
|
maxHeight = parent.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxHeight + 1
|
||||||
|
}
|
||||||
|
|
||||||
// newBlockNode returns a new block node for the given block header and parent
|
// newBlockNode returns a new block node for the given block header and parent
|
||||||
// nodes, calculating the height and workSum from the respective fields on the
|
// nodes, calculating the height and workSum from the respective fields on the
|
||||||
// parent. This function is NOT safe for concurrent access.
|
// parent. This function is NOT safe for concurrent access.
|
||||||
func newBlockNode(blockHeader *wire.BlockHeader, parents blockSet) *blockNode {
|
func newBlockNode(blockHeader *wire.BlockHeader, parents blockSet, phantomK uint32) *blockNode {
|
||||||
var node blockNode
|
var node blockNode
|
||||||
initBlockNode(&node, blockHeader, parents)
|
initBlockNode(&node, blockHeader, parents, phantomK)
|
||||||
return &node
|
return &node
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,12 +254,17 @@ func (node *blockNode) CalcPastMedianTime() time.Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (node *blockNode) PrevHashes() []daghash.Hash {
|
func (node *blockNode) PrevHashes() []daghash.Hash {
|
||||||
prevHashes := make([]daghash.Hash, len(node.parents))
|
return node.parents.hashes()
|
||||||
for _, parent := range node.parents {
|
|
||||||
prevHashes = append(prevHashes, parent.hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return prevHashes
|
// isGenesis returns if the current block is the genesis block
|
||||||
|
func (node *blockNode) isGenesis() bool {
|
||||||
|
return len(node.parents) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string that contains the block hash and height.
|
||||||
|
func (node blockNode) String() string {
|
||||||
|
return fmt.Sprintf("%s (%d)", node.hash, node.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// blockIndex provides facilities for keeping track of an in-memory index of the
|
// blockIndex provides facilities for keeping track of an in-memory index of the
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package blockdag
|
package blockdag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||||
@ -23,17 +24,6 @@ func setFromSlice(blocks ...*blockNode) blockSet {
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
// toSlice converts a set of blocks into a slice
|
|
||||||
func (bs blockSet) toSlice() []*blockNode {
|
|
||||||
slice := []*blockNode{}
|
|
||||||
|
|
||||||
for _, block := range bs {
|
|
||||||
slice = append(slice, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
|
|
||||||
// add adds a block to this BlockSet
|
// add adds a block to this BlockSet
|
||||||
func (bs blockSet) add(block *blockNode) {
|
func (bs blockSet) add(block *blockNode) {
|
||||||
bs[block.hash] = block
|
bs[block.hash] = block
|
||||||
@ -118,7 +108,9 @@ func (bs blockSet) hashes() []daghash.Hash {
|
|||||||
for hash := range bs {
|
for hash := range bs {
|
||||||
hashes = append(hashes, hash)
|
hashes = append(hashes, hash)
|
||||||
}
|
}
|
||||||
|
sort.Slice(hashes, func(i, j int) bool {
|
||||||
|
return daghash.Less(&hashes[i], &hashes[j])
|
||||||
|
})
|
||||||
return hashes
|
return hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +124,20 @@ func (bs blockSet) first() *blockNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bs blockSet) String() string {
|
func (bs blockSet) String() string {
|
||||||
ids := []string{}
|
nodeStrs := make([]string, 0, len(bs))
|
||||||
for hash := range bs {
|
for _, node := range bs {
|
||||||
ids = append(ids, hash.String())
|
nodeStrs = append(nodeStrs, node.String())
|
||||||
}
|
}
|
||||||
return strings.Join(ids, ",")
|
return strings.Join(nodeStrs, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// anyChildInSet returns true iff any child of block is contained within this set
|
||||||
|
func (bs blockSet) anyChildInSet(block *blockNode) bool {
|
||||||
|
for _, child := range block.children {
|
||||||
|
if bs.contains(child) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
@ -343,14 +343,14 @@ func (b *BlockDAG) TstSetCoinbaseMaturity(maturity uint16) {
|
|||||||
b.dagParams.CoinbaseMaturity = maturity
|
b.dagParams.CoinbaseMaturity = maturity
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFakeDag returns a chain that is usable for syntetic tests. It is
|
// newTestDAG returns a DAG that is usable for syntetic tests. It is
|
||||||
// important to note that this chain has no database associated with it, so
|
// important to note that this chain has no database associated with it, so
|
||||||
// it is not usable with all functions and the tests must take care when making
|
// it is not usable with all functions and the tests must take care when making
|
||||||
// use of it.
|
// use of it.
|
||||||
func newFakeDAG(params *dagconfig.Params) *BlockDAG {
|
func newTestDAG(params *dagconfig.Params) *BlockDAG {
|
||||||
// Create a genesis block node and block index index populated with it
|
// Create a genesis block node and block index index populated with it
|
||||||
// for use when creating the fake chain below.
|
// for use when creating the fake chain below.
|
||||||
node := newBlockNode(¶ms.GenesisBlock.Header, newSet())
|
node := newBlockNode(¶ms.GenesisBlock.Header, newSet(), params.K)
|
||||||
index := newBlockIndex(nil, params)
|
index := newBlockIndex(nil, params)
|
||||||
index.AddNode(node)
|
index.AddNode(node)
|
||||||
|
|
||||||
@ -370,15 +370,15 @@ func newFakeDAG(params *dagconfig.Params) *BlockDAG {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFakeNode creates a block node connected to the passed parent with the
|
// newTestNode creates a block node connected to the passed parent with the
|
||||||
// provided fields populated and fake values for the other fields.
|
// provided fields populated and fake values for the other fields.
|
||||||
func newFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp time.Time) *blockNode {
|
func newTestNode(parents blockSet, blockVersion int32, bits uint32, timestamp time.Time, phantomK uint32) *blockNode {
|
||||||
// Make up a header and create a block node from it.
|
// Make up a header and create a block node from it.
|
||||||
header := &wire.BlockHeader{
|
header := &wire.BlockHeader{
|
||||||
Version: blockVersion,
|
Version: blockVersion,
|
||||||
PrevBlocks: []daghash.Hash{parent.hash}, // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
PrevBlocks: parents.hashes(),
|
||||||
Bits: bits,
|
Bits: bits,
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
}
|
}
|
||||||
return newBlockNode(header, setFromSlice(parent)) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
return newBlockNode(header, parents, phantomK)
|
||||||
}
|
}
|
||||||
|
@ -121,13 +121,13 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||||||
blockVersion := int32(0x20000000)
|
blockVersion := int32(0x20000000)
|
||||||
|
|
||||||
// Generate enough synthetic blocks for the rest of the test
|
// Generate enough synthetic blocks for the rest of the test
|
||||||
chain := newFakeDAG(netParams)
|
chain := newTestDAG(netParams)
|
||||||
node := chain.dag.SelectedTip()
|
node := chain.dag.SelectedTip()
|
||||||
blockTime := node.Header().Timestamp
|
blockTime := node.Header().Timestamp
|
||||||
numBlocksToGenerate := uint32(5)
|
numBlocksToGenerate := uint32(5)
|
||||||
for i := uint32(0); i < numBlocksToGenerate; i++ {
|
for i := uint32(0); i < numBlocksToGenerate; i++ {
|
||||||
blockTime = blockTime.Add(time.Second)
|
blockTime = blockTime.Add(time.Second)
|
||||||
node = newFakeNode(node, blockVersion, 0, blockTime)
|
node = newTestNode(setFromSlice(node), blockVersion, 0, blockTime, netParams.K)
|
||||||
chain.index.AddNode(node)
|
chain.index.AddNode(node)
|
||||||
chain.dag.SetTip(node)
|
chain.dag.SetTip(node)
|
||||||
}
|
}
|
||||||
@ -448,7 +448,7 @@ func TestLocateInventory(t *testing.T) {
|
|||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||||
// \-> 16a -> 17a
|
// \-> 16a -> 17a
|
||||||
tip := tstTip
|
tip := tstTip
|
||||||
dag := newFakeDAG(&dagconfig.MainNetParams)
|
dag := newTestDAG(&dagconfig.MainNetParams)
|
||||||
branch0Nodes := chainedNodes(setFromSlice(dag.dag.Genesis()), 18)
|
branch0Nodes := chainedNodes(setFromSlice(dag.dag.Genesis()), 18)
|
||||||
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 2)
|
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 2)
|
||||||
for _, node := range branch0Nodes {
|
for _, node := range branch0Nodes {
|
||||||
@ -788,20 +788,20 @@ func TestHeightToHashRange(t *testing.T) {
|
|||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||||
// \-> 16a -> 17a -> 18a (unvalidated)
|
// \-> 16a -> 17a -> 18a (unvalidated)
|
||||||
tip := tstTip
|
tip := tstTip
|
||||||
chain := newFakeDAG(&dagconfig.MainNetParams)
|
blockDAG := newTestDAG(&dagconfig.MainNetParams)
|
||||||
branch0Nodes := chainedNodes(setFromSlice(chain.dag.Genesis()), 18)
|
branch0Nodes := chainedNodes(setFromSlice(blockDAG.dag.Genesis()), 18)
|
||||||
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 3)
|
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 3)
|
||||||
for _, node := range branch0Nodes {
|
for _, node := range branch0Nodes {
|
||||||
chain.index.SetStatusFlags(node, statusValid)
|
blockDAG.index.SetStatusFlags(node, statusValid)
|
||||||
chain.index.AddNode(node)
|
blockDAG.index.AddNode(node)
|
||||||
}
|
}
|
||||||
for _, node := range branch1Nodes {
|
for _, node := range branch1Nodes {
|
||||||
if node.height < 18 {
|
if node.height < 18 {
|
||||||
chain.index.SetStatusFlags(node, statusValid)
|
blockDAG.index.SetStatusFlags(node, statusValid)
|
||||||
}
|
}
|
||||||
chain.index.AddNode(node)
|
blockDAG.index.AddNode(node)
|
||||||
}
|
}
|
||||||
chain.dag.SetTip(tip(branch0Nodes))
|
blockDAG.dag.SetTip(tip(branch0Nodes))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -856,7 +856,7 @@ func TestHeightToHashRange(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
hashes, err := chain.HeightToHashRange(test.startHeight, &test.endHash,
|
hashes, err := blockDAG.HeightToHashRange(test.startHeight, &test.endHash,
|
||||||
test.maxResults)
|
test.maxResults)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !test.expectError {
|
if !test.expectError {
|
||||||
@ -880,7 +880,7 @@ func TestIntervalBlockHashes(t *testing.T) {
|
|||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||||
// \-> 16a -> 17a -> 18a (unvalidated)
|
// \-> 16a -> 17a -> 18a (unvalidated)
|
||||||
tip := tstTip
|
tip := tstTip
|
||||||
chain := newFakeDAG(&dagconfig.MainNetParams)
|
chain := newTestDAG(&dagconfig.MainNetParams)
|
||||||
branch0Nodes := chainedNodes(setFromSlice(chain.dag.Genesis()), 18)
|
branch0Nodes := chainedNodes(setFromSlice(chain.dag.Genesis()), 18)
|
||||||
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 3)
|
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 3)
|
||||||
for _, node := range branch0Nodes {
|
for _, node := range branch0Nodes {
|
||||||
|
@ -11,11 +11,12 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||||
"github.com/daglabs/btcd/database"
|
"github.com/daglabs/btcd/database"
|
||||||
"github.com/daglabs/btcd/wire"
|
"github.com/daglabs/btcd/wire"
|
||||||
"github.com/daglabs/btcutil"
|
"github.com/daglabs/btcutil"
|
||||||
"encoding/json"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -838,7 +839,7 @@ func (b *BlockDAG) createDAGState() error {
|
|||||||
genesisBlock := btcutil.NewBlock(b.dagParams.GenesisBlock)
|
genesisBlock := btcutil.NewBlock(b.dagParams.GenesisBlock)
|
||||||
genesisBlock.SetHeight(0)
|
genesisBlock.SetHeight(0)
|
||||||
header := &genesisBlock.MsgBlock().Header
|
header := &genesisBlock.MsgBlock().Header
|
||||||
node := newBlockNode(header, nil)
|
node := newBlockNode(header, nil, b.dagParams.K)
|
||||||
node.status = statusDataStored | statusValid
|
node.status = statusDataStored | statusValid
|
||||||
b.dag.SetTip(node)
|
b.dag.SetTip(node)
|
||||||
|
|
||||||
@ -1025,7 +1026,7 @@ func (b *BlockDAG) initDAGState() error {
|
|||||||
// Initialize the block node for the block, connect it,
|
// Initialize the block node for the block, connect it,
|
||||||
// and add it to the block index.
|
// and add it to the block index.
|
||||||
node := &blockNodes[i]
|
node := &blockNodes[i]
|
||||||
initBlockNode(node, header, setFromSlice(parent)) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
initBlockNode(node, header, setFromSlice(parent), b.dagParams.K) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||||
node.status = status
|
node.status = status
|
||||||
b.index.addNode(node)
|
b.index.addNode(node)
|
||||||
|
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
package blockdag
|
package blockdag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/dagconfig"
|
||||||
"github.com/daglabs/btcd/wire"
|
"github.com/daglabs/btcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,17 +28,12 @@ func chainedNodes(parents blockSet, numNodes int) []*blockNode {
|
|||||||
// synthetic tests to work.
|
// synthetic tests to work.
|
||||||
header := wire.BlockHeader{Nonce: testNoncePrng.Uint32()}
|
header := wire.BlockHeader{Nonce: testNoncePrng.Uint32()}
|
||||||
header.PrevBlocks = tips.hashes()
|
header.PrevBlocks = tips.hashes()
|
||||||
nodes[i] = newBlockNode(&header, tips)
|
nodes[i] = newBlockNode(&header, tips, dagconfig.SimNetParams.K)
|
||||||
tips = setFromSlice(nodes[i])
|
tips = setFromSlice(nodes[i])
|
||||||
}
|
}
|
||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the block node as a human-readable name.
|
|
||||||
func (node blockNode) String() string {
|
|
||||||
return fmt.Sprintf("%s(%d)", node.hash, node.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
// tstTip is a convenience function to grab the tip of a chain of block nodes
|
// tstTip is a convenience function to grab the tip of a chain of block nodes
|
||||||
// created via chainedNodes.
|
// created via chainedNodes.
|
||||||
func tstTip(nodes []*blockNode) *blockNode {
|
func tstTip(nodes []*blockNode) *blockNode {
|
||||||
|
@ -7,8 +7,6 @@ package blockdag
|
|||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -21,20 +19,6 @@ var (
|
|||||||
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
|
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
|
||||||
)
|
)
|
||||||
|
|
||||||
// HashToBig converts a daghash.Hash into a big.Int that can be used to
|
|
||||||
// perform math comparisons.
|
|
||||||
func HashToBig(hash *daghash.Hash) *big.Int {
|
|
||||||
// A Hash is in little-endian, but the big package wants the bytes in
|
|
||||||
// big-endian, so reverse them.
|
|
||||||
buf := *hash
|
|
||||||
blen := len(buf)
|
|
||||||
for i := 0; i < blen/2; i++ {
|
|
||||||
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return new(big.Int).SetBytes(buf[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompactToBig converts a compact representation of a whole number N to an
|
// CompactToBig converts a compact representation of a whole number N to an
|
||||||
// unsigned 32-bit number. The representation is similar to IEEE754 floating
|
// unsigned 32-bit number. The representation is similar to IEEE754 floating
|
||||||
// point numbers.
|
// point numbers.
|
||||||
|
@ -342,7 +342,7 @@ func solveBlock(header *wire.BlockHeader) bool {
|
|||||||
default:
|
default:
|
||||||
hdr.Nonce = i
|
hdr.Nonce = i
|
||||||
hash := hdr.BlockHash()
|
hash := hdr.BlockHash()
|
||||||
if blockdag.HashToBig(&hash).Cmp(
|
if daghash.HashToBig(&hash).Cmp(
|
||||||
targetDifficulty) <= 0 {
|
targetDifficulty) <= 0 {
|
||||||
|
|
||||||
results <- sbResult{true, i}
|
results <- sbResult{true, i}
|
||||||
@ -1444,7 +1444,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
|||||||
// a uint256 is higher than the limit.
|
// a uint256 is higher than the limit.
|
||||||
b46.Header.Nonce++
|
b46.Header.Nonce++
|
||||||
blockHash := b46.BlockHash()
|
blockHash := b46.BlockHash()
|
||||||
hashNum := blockdag.HashToBig(&blockHash)
|
hashNum := daghash.HashToBig(&blockHash)
|
||||||
if hashNum.Cmp(g.params.PowLimit) >= 0 {
|
if hashNum.Cmp(g.params.PowLimit) >= 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
891
blockdag/phanom_test.go
Normal file
891
blockdag/phanom_test.go
Normal file
@ -0,0 +1,891 @@
|
|||||||
|
package blockdag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||||
|
|
||||||
|
"github.com/daglabs/btcd/dagconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testBlockData struct {
|
||||||
|
parents []string
|
||||||
|
id string //id is a virtual entity that is used only for tests so we can define relations between blocks without knowing their hash
|
||||||
|
expectedScore uint64
|
||||||
|
expectedSelectedParent string
|
||||||
|
expectedBlues []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type hashIDPair struct {
|
||||||
|
hash *daghash.Hash
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
//TestPhantom iterate over several dag simulations, and checks
|
||||||
|
//that the blue score, blue set and selected parent of each
|
||||||
|
//block calculated as expected
|
||||||
|
func TestPhantom(t *testing.T) {
|
||||||
|
netParams := dagconfig.SimNetParams
|
||||||
|
|
||||||
|
blockVersion := int32(0x20000000)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
k uint32
|
||||||
|
dagData []*testBlockData
|
||||||
|
virtualBlockID string
|
||||||
|
expectedReds []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
//Block hash order:DEBHICAKGJF
|
||||||
|
k: 1,
|
||||||
|
virtualBlockID: "K",
|
||||||
|
expectedReds: []string{"D"},
|
||||||
|
dagData: []*testBlockData{
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "B",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "C",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "D",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "E",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C"},
|
||||||
|
id: "F",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C", "D"},
|
||||||
|
id: "G",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"D", "B", "C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C", "E"},
|
||||||
|
id: "H",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"E", "B", "C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"E", "G"},
|
||||||
|
id: "I",
|
||||||
|
expectedScore: 5,
|
||||||
|
expectedSelectedParent: "G",
|
||||||
|
expectedBlues: []string{"G"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"F"},
|
||||||
|
id: "J",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "F",
|
||||||
|
expectedBlues: []string{"F"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"H", "I", "J"},
|
||||||
|
id: "K",
|
||||||
|
expectedScore: 9,
|
||||||
|
expectedSelectedParent: "H",
|
||||||
|
expectedBlues: []string{"I", "G", "J", "F", "H"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
//block hash order:DQKRLHOEBSIGUJNPCMTAFV
|
||||||
|
k: 2,
|
||||||
|
virtualBlockID: "V",
|
||||||
|
expectedReds: []string{"D", "J", "P"},
|
||||||
|
dagData: []*testBlockData{
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "B",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "C",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "D",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "E",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C"},
|
||||||
|
id: "F",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C"},
|
||||||
|
id: "G",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"G"},
|
||||||
|
id: "H",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "G",
|
||||||
|
expectedBlues: []string{"G"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"E"},
|
||||||
|
id: "I",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "E",
|
||||||
|
expectedBlues: []string{"E"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"E"},
|
||||||
|
id: "J",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "E",
|
||||||
|
expectedBlues: []string{"E"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"I"},
|
||||||
|
id: "K",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "I",
|
||||||
|
expectedBlues: []string{"I"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"K", "H"},
|
||||||
|
id: "L",
|
||||||
|
expectedScore: 5,
|
||||||
|
expectedSelectedParent: "K",
|
||||||
|
expectedBlues: []string{"K"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"F", "L"},
|
||||||
|
id: "M",
|
||||||
|
expectedScore: 10,
|
||||||
|
expectedSelectedParent: "F",
|
||||||
|
expectedBlues: []string{"L", "K", "H", "I", "E", "G", "B", "F"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"G", "K"},
|
||||||
|
id: "N",
|
||||||
|
expectedScore: 7,
|
||||||
|
expectedSelectedParent: "G",
|
||||||
|
expectedBlues: []string{"K", "I", "E", "B", "G"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"J", "N"},
|
||||||
|
id: "O",
|
||||||
|
expectedScore: 8,
|
||||||
|
expectedSelectedParent: "N",
|
||||||
|
expectedBlues: []string{"N"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"D"},
|
||||||
|
id: "P",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "D",
|
||||||
|
expectedBlues: []string{"D"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"O", "P"},
|
||||||
|
id: "Q",
|
||||||
|
expectedScore: 10,
|
||||||
|
expectedSelectedParent: "P",
|
||||||
|
expectedBlues: []string{"O", "N", "K", "I", "J", "E", "P"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"L", "Q"},
|
||||||
|
id: "R",
|
||||||
|
expectedScore: 11,
|
||||||
|
expectedSelectedParent: "Q",
|
||||||
|
expectedBlues: []string{"Q"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"M", "R"},
|
||||||
|
id: "S",
|
||||||
|
expectedScore: 15,
|
||||||
|
expectedSelectedParent: "M",
|
||||||
|
expectedBlues: []string{"R", "Q", "O", "N", "M"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"H", "F"},
|
||||||
|
id: "T",
|
||||||
|
expectedScore: 5,
|
||||||
|
expectedSelectedParent: "F",
|
||||||
|
expectedBlues: []string{"H", "G", "F"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"M", "T"},
|
||||||
|
id: "U",
|
||||||
|
expectedScore: 12,
|
||||||
|
expectedSelectedParent: "M",
|
||||||
|
expectedBlues: []string{"T", "M"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"S", "U"},
|
||||||
|
id: "V",
|
||||||
|
expectedScore: 18,
|
||||||
|
expectedSelectedParent: "S",
|
||||||
|
expectedBlues: []string{"U", "T", "S"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
//Block hash order:NRSHBUXJTFGPDVCKEQIOWLMA
|
||||||
|
k: 1,
|
||||||
|
virtualBlockID: "X",
|
||||||
|
expectedReds: []string{"D", "F", "G", "H", "J", "K", "L", "N", "O", "Q", "R", "S", "U", "V"},
|
||||||
|
dagData: []*testBlockData{
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "B",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "C",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "D",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "E",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "F",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "G",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C"},
|
||||||
|
id: "H",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C"},
|
||||||
|
id: "I",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "J",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"D"},
|
||||||
|
id: "K",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "D",
|
||||||
|
expectedBlues: []string{"D"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"D"},
|
||||||
|
id: "L",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "D",
|
||||||
|
expectedBlues: []string{"D"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"E"},
|
||||||
|
id: "M",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "E",
|
||||||
|
expectedBlues: []string{"E"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"E"},
|
||||||
|
id: "N",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "E",
|
||||||
|
expectedBlues: []string{"E"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"F", "G", "J"},
|
||||||
|
id: "O",
|
||||||
|
expectedScore: 5,
|
||||||
|
expectedSelectedParent: "G",
|
||||||
|
expectedBlues: []string{"J", "F", "G"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B", "M", "I"},
|
||||||
|
id: "P",
|
||||||
|
expectedScore: 6,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"I", "M", "C", "E", "B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"K", "E"},
|
||||||
|
id: "Q",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "E",
|
||||||
|
expectedBlues: []string{"K", "D", "E"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"L", "N"},
|
||||||
|
id: "R",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "L",
|
||||||
|
expectedBlues: []string{"L"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"I", "Q"},
|
||||||
|
id: "S",
|
||||||
|
expectedScore: 5,
|
||||||
|
expectedSelectedParent: "Q",
|
||||||
|
expectedBlues: []string{"Q"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"K", "P"},
|
||||||
|
id: "T",
|
||||||
|
expectedScore: 7,
|
||||||
|
expectedSelectedParent: "P",
|
||||||
|
expectedBlues: []string{"P"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"K", "L"},
|
||||||
|
id: "U",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "L",
|
||||||
|
expectedBlues: []string{"K", "L"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"U", "R"},
|
||||||
|
id: "V",
|
||||||
|
expectedScore: 6,
|
||||||
|
expectedSelectedParent: "U",
|
||||||
|
expectedBlues: []string{"R", "U"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"S", "U", "T"},
|
||||||
|
id: "W",
|
||||||
|
expectedScore: 8,
|
||||||
|
expectedSelectedParent: "T",
|
||||||
|
expectedBlues: []string{"T"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"V", "W", "H"},
|
||||||
|
id: "X",
|
||||||
|
expectedScore: 9,
|
||||||
|
expectedSelectedParent: "W",
|
||||||
|
expectedBlues: []string{"W"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
//Secret mining attack: The attacker is mining
|
||||||
|
//blocks B,C,D,E,F,G,T in secret without propagating
|
||||||
|
//them, so all blocks except T should be red, because
|
||||||
|
//they don't follow the rules of PHANTOM that require
|
||||||
|
//you to point to all the parents that you know, and
|
||||||
|
//propagate your block as soon as it's mined
|
||||||
|
|
||||||
|
//Block hash order: HRTGMKQBXDWSICYFONUPLEAJZ
|
||||||
|
k: 1,
|
||||||
|
virtualBlockID: "Y",
|
||||||
|
expectedReds: []string{"B", "C", "D", "E", "F", "G"},
|
||||||
|
dagData: []*testBlockData{
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "B",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "C",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C"},
|
||||||
|
id: "D",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"D"},
|
||||||
|
id: "E",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "D",
|
||||||
|
expectedBlues: []string{"D"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"E"},
|
||||||
|
id: "F",
|
||||||
|
expectedScore: 5,
|
||||||
|
expectedSelectedParent: "E",
|
||||||
|
expectedBlues: []string{"E"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"F"},
|
||||||
|
id: "G",
|
||||||
|
expectedScore: 6,
|
||||||
|
expectedSelectedParent: "F",
|
||||||
|
expectedBlues: []string{"F"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "H",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "I",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"H", "I"},
|
||||||
|
id: "J",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "I",
|
||||||
|
expectedBlues: []string{"H", "I"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"H", "I"},
|
||||||
|
id: "K",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "I",
|
||||||
|
expectedBlues: []string{"H", "I"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"I"},
|
||||||
|
id: "L",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "I",
|
||||||
|
expectedBlues: []string{"I"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"J", "K", "L"},
|
||||||
|
id: "M",
|
||||||
|
expectedScore: 6,
|
||||||
|
expectedSelectedParent: "J",
|
||||||
|
expectedBlues: []string{"K", "L", "J"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"J", "K", "L"},
|
||||||
|
id: "N",
|
||||||
|
expectedScore: 6,
|
||||||
|
expectedSelectedParent: "J",
|
||||||
|
expectedBlues: []string{"K", "L", "J"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"N", "M"},
|
||||||
|
id: "O",
|
||||||
|
expectedScore: 8,
|
||||||
|
expectedSelectedParent: "N",
|
||||||
|
expectedBlues: []string{"M", "N"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"N", "M"},
|
||||||
|
id: "P",
|
||||||
|
expectedScore: 8,
|
||||||
|
expectedSelectedParent: "N",
|
||||||
|
expectedBlues: []string{"M", "N"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"N", "M"},
|
||||||
|
id: "Q",
|
||||||
|
expectedScore: 8,
|
||||||
|
expectedSelectedParent: "N",
|
||||||
|
expectedBlues: []string{"M", "N"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"O", "P", "Q"},
|
||||||
|
id: "R",
|
||||||
|
expectedScore: 11,
|
||||||
|
expectedSelectedParent: "P",
|
||||||
|
expectedBlues: []string{"Q", "O", "P"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"O", "P", "Q"},
|
||||||
|
id: "S",
|
||||||
|
expectedScore: 11,
|
||||||
|
expectedSelectedParent: "P",
|
||||||
|
expectedBlues: []string{"Q", "O", "P"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"G", "S", "R"},
|
||||||
|
id: "T",
|
||||||
|
expectedScore: 13,
|
||||||
|
expectedSelectedParent: "S",
|
||||||
|
expectedBlues: []string{"R", "S"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"S", "R"},
|
||||||
|
id: "U",
|
||||||
|
expectedScore: 13,
|
||||||
|
expectedSelectedParent: "S",
|
||||||
|
expectedBlues: []string{"R", "S"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"T", "U"},
|
||||||
|
id: "V",
|
||||||
|
expectedScore: 15,
|
||||||
|
expectedSelectedParent: "U",
|
||||||
|
expectedBlues: []string{"T", "U"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"T", "U"},
|
||||||
|
id: "W",
|
||||||
|
expectedScore: 15,
|
||||||
|
expectedSelectedParent: "U",
|
||||||
|
expectedBlues: []string{"T", "U"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"T", "U"},
|
||||||
|
id: "X",
|
||||||
|
expectedScore: 15,
|
||||||
|
expectedSelectedParent: "U",
|
||||||
|
expectedBlues: []string{"T", "U"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"V", "W", "X"},
|
||||||
|
id: "Y",
|
||||||
|
expectedScore: 18,
|
||||||
|
expectedSelectedParent: "X",
|
||||||
|
expectedBlues: []string{"W", "V", "X"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
//Censorship mining attack: The attacker is mining blocks B,C,D,E,F,G in secret without propagating them,
|
||||||
|
//so all blocks except B should be red, because they don't follow the rules of
|
||||||
|
//PHANTOM that require you to point to all the parents that you know
|
||||||
|
|
||||||
|
//Block hash order:WZHOGBJMDSICRUYKTFQLEAPXN
|
||||||
|
k: 1,
|
||||||
|
virtualBlockID: "Y",
|
||||||
|
expectedReds: []string{"C", "D", "E", "F", "G"},
|
||||||
|
dagData: []*testBlockData{
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "B",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"B"},
|
||||||
|
id: "C",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "B",
|
||||||
|
expectedBlues: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"C"},
|
||||||
|
id: "D",
|
||||||
|
expectedScore: 3,
|
||||||
|
expectedSelectedParent: "C",
|
||||||
|
expectedBlues: []string{"C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"D"},
|
||||||
|
id: "E",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "D",
|
||||||
|
expectedBlues: []string{"D"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"E"},
|
||||||
|
id: "F",
|
||||||
|
expectedScore: 5,
|
||||||
|
expectedSelectedParent: "E",
|
||||||
|
expectedBlues: []string{"E"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"F"},
|
||||||
|
id: "G",
|
||||||
|
expectedScore: 6,
|
||||||
|
expectedSelectedParent: "F",
|
||||||
|
expectedBlues: []string{"F"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "H",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"A"},
|
||||||
|
id: "I",
|
||||||
|
expectedScore: 1,
|
||||||
|
expectedSelectedParent: "A",
|
||||||
|
expectedBlues: []string{"A"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"H", "I", "B"},
|
||||||
|
id: "J",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "I",
|
||||||
|
expectedBlues: []string{"H", "B", "I"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"H", "I", "B"},
|
||||||
|
id: "K",
|
||||||
|
expectedScore: 4,
|
||||||
|
expectedSelectedParent: "I",
|
||||||
|
expectedBlues: []string{"H", "B", "I"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"I"},
|
||||||
|
id: "L",
|
||||||
|
expectedScore: 2,
|
||||||
|
expectedSelectedParent: "I",
|
||||||
|
expectedBlues: []string{"I"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"J", "K", "L", "C"},
|
||||||
|
id: "M",
|
||||||
|
expectedScore: 7,
|
||||||
|
expectedSelectedParent: "K",
|
||||||
|
expectedBlues: []string{"J", "L", "K"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"J", "K", "L", "C"},
|
||||||
|
id: "N",
|
||||||
|
expectedScore: 7,
|
||||||
|
expectedSelectedParent: "K",
|
||||||
|
expectedBlues: []string{"J", "L", "K"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"N", "M", "D"},
|
||||||
|
id: "O",
|
||||||
|
expectedScore: 9,
|
||||||
|
expectedSelectedParent: "N",
|
||||||
|
expectedBlues: []string{"M", "N"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"N", "M", "D"},
|
||||||
|
id: "P",
|
||||||
|
expectedScore: 9,
|
||||||
|
expectedSelectedParent: "N",
|
||||||
|
expectedBlues: []string{"M", "N"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"N", "M", "D"},
|
||||||
|
id: "Q",
|
||||||
|
expectedScore: 9,
|
||||||
|
expectedSelectedParent: "N",
|
||||||
|
expectedBlues: []string{"M", "N"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"O", "P", "Q", "E"},
|
||||||
|
id: "R",
|
||||||
|
expectedScore: 12,
|
||||||
|
expectedSelectedParent: "P",
|
||||||
|
expectedBlues: []string{"O", "Q", "P"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"O", "P", "Q", "E"},
|
||||||
|
id: "S",
|
||||||
|
expectedScore: 12,
|
||||||
|
expectedSelectedParent: "P",
|
||||||
|
expectedBlues: []string{"O", "Q", "P"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"G", "S", "R"},
|
||||||
|
id: "T",
|
||||||
|
expectedScore: 14,
|
||||||
|
expectedSelectedParent: "R",
|
||||||
|
expectedBlues: []string{"S", "R"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"S", "R", "F"},
|
||||||
|
id: "U",
|
||||||
|
expectedScore: 14,
|
||||||
|
expectedSelectedParent: "R",
|
||||||
|
expectedBlues: []string{"S", "R"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"T", "U"},
|
||||||
|
id: "V",
|
||||||
|
expectedScore: 16,
|
||||||
|
expectedSelectedParent: "T",
|
||||||
|
expectedBlues: []string{"U", "T"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"T", "U"},
|
||||||
|
id: "W",
|
||||||
|
expectedScore: 16,
|
||||||
|
expectedSelectedParent: "T",
|
||||||
|
expectedBlues: []string{"U", "T"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"T", "U"},
|
||||||
|
id: "X",
|
||||||
|
expectedScore: 16,
|
||||||
|
expectedSelectedParent: "T",
|
||||||
|
expectedBlues: []string{"U", "T"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parents: []string{"V", "W", "X"},
|
||||||
|
id: "Y",
|
||||||
|
expectedScore: 19,
|
||||||
|
expectedSelectedParent: "W",
|
||||||
|
expectedBlues: []string{"V", "X", "W"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
netParams.K = test.k
|
||||||
|
// Generate enough synthetic blocks for the rest of the test
|
||||||
|
blockDAG := newTestDAG(&netParams)
|
||||||
|
genesisNode := blockDAG.dag.SelectedTip()
|
||||||
|
blockTime := genesisNode.Header().Timestamp
|
||||||
|
blockByIDMap := make(map[string]*blockNode)
|
||||||
|
idByBlockMap := make(map[*blockNode]string)
|
||||||
|
blockByIDMap["A"] = genesisNode
|
||||||
|
idByBlockMap[genesisNode] = "A"
|
||||||
|
|
||||||
|
for _, blockData := range test.dagData {
|
||||||
|
blockTime = blockTime.Add(time.Second)
|
||||||
|
parents := blockSet{}
|
||||||
|
for _, parentID := range blockData.parents {
|
||||||
|
parent := blockByIDMap[parentID]
|
||||||
|
parents.add(parent)
|
||||||
|
}
|
||||||
|
node := newTestNode(parents, blockVersion, 0, blockTime, test.k)
|
||||||
|
|
||||||
|
blockDAG.index.AddNode(node)
|
||||||
|
blockByIDMap[blockData.id] = node
|
||||||
|
idByBlockMap[node] = blockData.id
|
||||||
|
|
||||||
|
bluesIDs := make([]string, 0, len(node.blues))
|
||||||
|
for _, blue := range node.blues {
|
||||||
|
bluesIDs = append(bluesIDs, idByBlockMap[blue])
|
||||||
|
}
|
||||||
|
selectedParentID := idByBlockMap[node.selectedParent]
|
||||||
|
fullDataStr := fmt.Sprintf("blues: %v, selectedParent: %v, score: %v",
|
||||||
|
bluesIDs, selectedParentID, node.blueScore)
|
||||||
|
if blockData.expectedScore != node.blueScore {
|
||||||
|
t.Errorf("Test %d: Block %v expected to have score %v but got %v (fulldata: %v)",
|
||||||
|
i, blockData.id, blockData.expectedScore, node.blueScore, fullDataStr)
|
||||||
|
}
|
||||||
|
if blockData.expectedSelectedParent != selectedParentID {
|
||||||
|
t.Errorf("Test %d: Block %v expected to have selected parent %v but got %v (fulldata: %v)",
|
||||||
|
i, blockData.id, blockData.expectedSelectedParent, selectedParentID, fullDataStr)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(blockData.expectedBlues, bluesIDs) {
|
||||||
|
t.Errorf("Test %d: Block %v expected to have blues %v but got %v (fulldata: %v)",
|
||||||
|
i, blockData.id, blockData.expectedBlues, bluesIDs, fullDataStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reds := make(map[string]bool)
|
||||||
|
|
||||||
|
for id := range blockByIDMap {
|
||||||
|
reds[id] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for tip := blockByIDMap[test.virtualBlockID]; tip.selectedParent != nil; tip = tip.selectedParent {
|
||||||
|
tipID := idByBlockMap[tip]
|
||||||
|
delete(reds, tipID)
|
||||||
|
for _, blue := range tip.blues {
|
||||||
|
blueID := idByBlockMap[blue]
|
||||||
|
delete(reds, blueID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !checkReds(test.expectedReds, reds) {
|
||||||
|
redsIDs := make([]string, 0, len(reds))
|
||||||
|
for id := range reds {
|
||||||
|
redsIDs = append(redsIDs, id)
|
||||||
|
}
|
||||||
|
sort.Strings(redsIDs)
|
||||||
|
sort.Strings(test.expectedReds)
|
||||||
|
t.Errorf("Test %d: Expected reds %v but got %v", i, test.expectedReds, redsIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkReds(expectedReds []string, reds map[string]bool) bool {
|
||||||
|
if len(expectedReds) != len(reds) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, redID := range expectedReds {
|
||||||
|
if !reds[redID] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
105
blockdag/phantom.go
Normal file
105
blockdag/phantom.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package blockdag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// phantom calculates and returns the block's blue set, selected parent and blue score.
|
||||||
|
// Chain start is determined by going down the DAG through the selected path
|
||||||
|
// (follow the selected parent of each block) k + 1 steps.
|
||||||
|
// The blue set of a block are all blue blocks in its past.
|
||||||
|
// To optimize memory usage, for each block we are storing only the blue blocks in
|
||||||
|
// its selected parent's anticone that are in the future of the chain start
|
||||||
|
// as well as the selected parent itself - the rest of the
|
||||||
|
// blue set can be restored by traversing the selected parent chain and combining
|
||||||
|
// the .blues of all blocks in it.
|
||||||
|
// The blue score is the total number of blocks in this block's blue set
|
||||||
|
// of the selected parent. (the blue score of the genesis block is defined as 0)
|
||||||
|
// The selected parent is chosen by determining which block's parent will give this block the highest blue score.
|
||||||
|
func phantom(block *blockNode, k uint32) (blues []*blockNode, selectedParent *blockNode, score uint64) {
|
||||||
|
bestScore := uint64(0)
|
||||||
|
var bestParent *blockNode
|
||||||
|
var bestBlues []*blockNode
|
||||||
|
var bestHash *daghash.Hash
|
||||||
|
for _, parent := range block.parents {
|
||||||
|
chainStart := digToChainStart(parent, k)
|
||||||
|
candidates := blueCandidates(chainStart)
|
||||||
|
blues := traverseCandidates(block, candidates, parent)
|
||||||
|
score := uint64(len(blues)) + parent.blueScore
|
||||||
|
|
||||||
|
if score > bestScore || (score == bestScore && (bestHash == nil || daghash.Less(bestHash, &parent.hash))) {
|
||||||
|
bestScore = score
|
||||||
|
bestBlues = blues
|
||||||
|
bestParent = parent
|
||||||
|
bestHash = &parent.hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestBlues, bestParent, bestScore
|
||||||
|
}
|
||||||
|
|
||||||
|
// digToChainStart digs through the selected path and returns the block in depth k+1
|
||||||
|
func digToChainStart(parent *blockNode, k uint32) *blockNode {
|
||||||
|
current := parent
|
||||||
|
|
||||||
|
for i := uint32(0); i < k; i++ {
|
||||||
|
if current.isGenesis() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
current = current.selectedParent
|
||||||
|
}
|
||||||
|
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
func blueCandidates(chainStart *blockNode) blockSet {
|
||||||
|
candidates := newSet()
|
||||||
|
candidates.add(chainStart)
|
||||||
|
|
||||||
|
queue := []*blockNode{chainStart}
|
||||||
|
for len(queue) > 0 {
|
||||||
|
var current *blockNode
|
||||||
|
current, queue = queue[0], queue[1:]
|
||||||
|
|
||||||
|
children := current.children
|
||||||
|
for _, child := range children {
|
||||||
|
if !candidates.contains(child) {
|
||||||
|
candidates.add(child)
|
||||||
|
queue = append(queue, child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates
|
||||||
|
}
|
||||||
|
|
||||||
|
//traverseCandidates returns all the blocks that are in the future of the chain start and in the anticone of the selected parent
|
||||||
|
func traverseCandidates(newBlock *blockNode, candidates blockSet, selectedParent *blockNode) []*blockNode {
|
||||||
|
blues := []*blockNode{}
|
||||||
|
selectedParentPast := newSet()
|
||||||
|
queue := NewHeap()
|
||||||
|
visited := newSet()
|
||||||
|
|
||||||
|
for _, parent := range newBlock.parents {
|
||||||
|
queue.Push(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
for queue.Len() > 0 {
|
||||||
|
current := queue.Pop()
|
||||||
|
if candidates.contains(current) {
|
||||||
|
if current == selectedParent || selectedParentPast.anyChildInSet(current) {
|
||||||
|
selectedParentPast.add(current)
|
||||||
|
} else {
|
||||||
|
blues = append(blues, current)
|
||||||
|
}
|
||||||
|
for _, parent := range current.parents {
|
||||||
|
if !visited.contains(parent) {
|
||||||
|
visited.add(parent)
|
||||||
|
queue.Push(parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(blues, selectedParent)
|
||||||
|
}
|
@ -333,7 +333,7 @@ func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, flags Behavio
|
|||||||
if flags&BFNoPoWCheck != BFNoPoWCheck {
|
if flags&BFNoPoWCheck != BFNoPoWCheck {
|
||||||
// The block hash must be less than the claimed target.
|
// The block hash must be less than the claimed target.
|
||||||
hash := header.BlockHash()
|
hash := header.BlockHash()
|
||||||
hashNum := HashToBig(&hash)
|
hashNum := daghash.HashToBig(&hash)
|
||||||
if hashNum.Cmp(target) > 0 {
|
if hashNum.Cmp(target) > 0 {
|
||||||
str := fmt.Sprintf("block hash of %064x is higher than "+
|
str := fmt.Sprintf("block hash of %064x is higher than "+
|
||||||
"expected max of %064x", hashNum, target)
|
"expected max of %064x", hashNum, target)
|
||||||
@ -1202,6 +1202,6 @@ func (b *BlockDAG) CheckConnectBlockTemplate(block *btcutil.Block) error {
|
|||||||
// is not needed and thus extra work can be avoided.
|
// is not needed and thus extra work can be avoided.
|
||||||
view := NewUtxoViewpoint()
|
view := NewUtxoViewpoint()
|
||||||
view.SetTips(tips)
|
view.SetTips(tips)
|
||||||
newNode := newBlockNode(&header, b.dag.Tips())
|
newNode := newBlockNode(&header, b.dag.Tips(), b.dagParams.K)
|
||||||
return b.checkConnectBlock(newNode, block, view, nil)
|
return b.checkConnectBlock(newNode, block, view, nil)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ package daghash
|
|||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HashSize of array used to store hashes. See Hash.
|
// HashSize of array used to store hashes. See Hash.
|
||||||
@ -156,3 +157,32 @@ func Decode(dst *Hash, src string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashToBig converts a daghash.Hash into a big.Int that can be used to
|
||||||
|
// perform math comparisons.
|
||||||
|
func HashToBig(hash *Hash) *big.Int {
|
||||||
|
// A Hash is in little-endian, but the big package wants the bytes in
|
||||||
|
// big-endian, so reverse them.
|
||||||
|
buf := *hash
|
||||||
|
blen := len(buf)
|
||||||
|
for i := 0; i < blen/2; i++ {
|
||||||
|
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(big.Int).SetBytes(buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cmp compares hash and target and returns:
|
||||||
|
//
|
||||||
|
// -1 if hash < target
|
||||||
|
// 0 if hash == target
|
||||||
|
// +1 if hash > target
|
||||||
|
//
|
||||||
|
func (hash *Hash) Cmp(target *Hash) int {
|
||||||
|
return HashToBig(hash).Cmp(HashToBig(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
//Less returns true iff hash b is less than hash a
|
||||||
|
func Less(a *Hash, b *Hash) bool {
|
||||||
|
return a.Cmp(b) > 0
|
||||||
|
}
|
||||||
|
@ -41,6 +41,8 @@ var (
|
|||||||
simNetPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne)
|
simNetPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const phantomK = 10
|
||||||
|
|
||||||
// Checkpoint identifies a known good point in the block chain. Using
|
// Checkpoint identifies a known good point in the block chain. Using
|
||||||
// checkpoints allows a few optimizations for old blocks during initial download
|
// checkpoints allows a few optimizations for old blocks during initial download
|
||||||
// and also prevents forks from old blocks.
|
// and also prevents forks from old blocks.
|
||||||
@ -149,6 +151,8 @@ func (prefix Bech32Prefix) String() string {
|
|||||||
// used by Bitcoin applications to differentiate networks as well as addresses
|
// used by Bitcoin applications to differentiate networks as well as addresses
|
||||||
// and keys for one network from those intended for use on another network.
|
// and keys for one network from those intended for use on another network.
|
||||||
type Params struct {
|
type Params struct {
|
||||||
|
K uint32
|
||||||
|
|
||||||
// Name defines a human-readable identifier for the network.
|
// Name defines a human-readable identifier for the network.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
@ -259,6 +263,7 @@ type Params struct {
|
|||||||
|
|
||||||
// MainNetParams defines the network parameters for the main Bitcoin network.
|
// MainNetParams defines the network parameters for the main Bitcoin network.
|
||||||
var MainNetParams = Params{
|
var MainNetParams = Params{
|
||||||
|
K: phantomK,
|
||||||
Name: "mainnet",
|
Name: "mainnet",
|
||||||
Net: wire.MainNet,
|
Net: wire.MainNet,
|
||||||
DefaultPort: "8333",
|
DefaultPort: "8333",
|
||||||
@ -345,6 +350,7 @@ var MainNetParams = Params{
|
|||||||
// Bitcoin network. Not to be confused with the test Bitcoin network (version
|
// Bitcoin network. Not to be confused with the test Bitcoin network (version
|
||||||
// 3), this network is sometimes simply called "testnet".
|
// 3), this network is sometimes simply called "testnet".
|
||||||
var RegressionNetParams = Params{
|
var RegressionNetParams = Params{
|
||||||
|
K: phantomK,
|
||||||
Name: "regtest",
|
Name: "regtest",
|
||||||
Net: wire.TestNet,
|
Net: wire.TestNet,
|
||||||
DefaultPort: "18444",
|
DefaultPort: "18444",
|
||||||
@ -405,6 +411,7 @@ var RegressionNetParams = Params{
|
|||||||
// (version 3). Not to be confused with the regression test network, this
|
// (version 3). Not to be confused with the regression test network, this
|
||||||
// network is sometimes simply called "testnet".
|
// network is sometimes simply called "testnet".
|
||||||
var TestNet3Params = Params{
|
var TestNet3Params = Params{
|
||||||
|
K: phantomK,
|
||||||
Name: "testnet3",
|
Name: "testnet3",
|
||||||
Net: wire.TestNet3,
|
Net: wire.TestNet3,
|
||||||
DefaultPort: "18333",
|
DefaultPort: "18333",
|
||||||
@ -486,6 +493,7 @@ var TestNet3Params = Params{
|
|||||||
// following normal discovery rules. This is important as otherwise it would
|
// following normal discovery rules. This is important as otherwise it would
|
||||||
// just turn into another public testnet.
|
// just turn into another public testnet.
|
||||||
var SimNetParams = Params{
|
var SimNetParams = Params{
|
||||||
|
K: phantomK,
|
||||||
Name: "simnet",
|
Name: "simnet",
|
||||||
Net: wire.SimNet,
|
Net: wire.SimNet,
|
||||||
DefaultPort: "18555",
|
DefaultPort: "18555",
|
||||||
|
@ -44,7 +44,7 @@ func solveBlock(header *wire.BlockHeader, targetDifficulty *big.Int) bool {
|
|||||||
default:
|
default:
|
||||||
hdr.Nonce = i
|
hdr.Nonce = i
|
||||||
hash := hdr.BlockHash()
|
hash := hdr.BlockHash()
|
||||||
if blockdag.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
|
if daghash.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
|
||||||
select {
|
select {
|
||||||
case results <- sbResult{true, i}:
|
case results <- sbResult{true, i}:
|
||||||
return
|
return
|
||||||
|
@ -278,7 +278,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32,
|
|||||||
|
|
||||||
// The block is solved when the new block hash is less
|
// The block is solved when the new block hash is less
|
||||||
// than the target difficulty. Yay!
|
// than the target difficulty. Yay!
|
||||||
if blockdag.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
|
if daghash.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
|
||||||
m.updateHashes <- hashesCompleted
|
m.updateHashes <- hashesCompleted
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,7 @@ type BlockHeader struct {
|
|||||||
// Merkle tree reference to hash of all transactions for the block.
|
// Merkle tree reference to hash of all transactions for the block.
|
||||||
MerkleRoot daghash.Hash
|
MerkleRoot daghash.Hash
|
||||||
|
|
||||||
// Time the block was created. This is, unfortunately, encoded as a
|
// Time the block was created.
|
||||||
// uint32 on the wire and therefore is limited to 2106.
|
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
|
|
||||||
// Difficulty target for the block.
|
// Difficulty target for the block.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user