[NOD-1316] Refactor TestGHOSTDAG to enable arbitrary DAGs (#899)

* Add VirtualBlueHashes to BlockDAG

* Refactor TestGHOSTDAG to read DAGs from json files

* Added a new DAG for the ghostdag test suite

* Pass BehaviorFlags to delayed blocks
This commit is contained in:
Elichai Turkel 2020-08-25 14:00:43 +03:00 committed by GitHub
parent bbb9dfa4cd
commit c82a951a24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 698 additions and 181 deletions

View File

@ -285,6 +285,17 @@ func (dag *BlockDAG) SelectedTipBlueScore() uint64 {
return dag.selectedTip().blueScore
}
// VirtualBlueHashes returns the blue of the current virtual block
func (dag *BlockDAG) VirtualBlueHashes() []*daghash.Hash {
dag.RLock()
defer dag.RUnlock()
hashes := make([]*daghash.Hash, len(dag.virtual.blues))
for i, blue := range dag.virtual.blues {
hashes[i] = blue.hash
}
return hashes
}
// VirtualBlueScore returns the blue score of the current virtual block
func (dag *BlockDAG) VirtualBlueScore() uint64 {
return dag.virtual.blueScore

View File

@ -12,6 +12,7 @@ import (
type delayedBlock struct {
block *util.Block
processTime mstime.Time
flags BehaviorFlags
}
func (dag *BlockDAG) isKnownDelayedBlock(hash *daghash.Hash) bool {
@ -19,12 +20,13 @@ func (dag *BlockDAG) isKnownDelayedBlock(hash *daghash.Hash) bool {
return exists
}
func (dag *BlockDAG) addDelayedBlock(block *util.Block, delay time.Duration) error {
func (dag *BlockDAG) addDelayedBlock(block *util.Block, flags BehaviorFlags, delay time.Duration) error {
processTime := dag.Now().Add(delay)
log.Debugf("Adding block to delayed blocks queue (block hash: %s, process time: %s)", block.Hash().String(), processTime)
delayedBlock := &delayedBlock{
block: block,
processTime: processTime,
flags: flags,
}
dag.delayedBlocks[*block.Hash()] = delayedBlock
@ -42,7 +44,7 @@ func (dag *BlockDAG) processDelayedBlocks() error {
break
}
delayedBlock := dag.popDelayedBlock()
_, _, err := dag.processBlockNoLock(delayedBlock.block, BFAfterDelay)
_, _, err := dag.processBlockNoLock(delayedBlock.block, delayedBlock.flags|BFAfterDelay)
if err != nil {
log.Errorf("Error while processing delayed block (block %s): %s", delayedBlock.block.Hash().String(), err)
// Rule errors should not be propagated as they refer only to the delayed block,

View File

@ -1,7 +1,10 @@
package blockdag
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
@ -13,12 +16,19 @@ import (
"github.com/kaspanet/kaspad/util/daghash"
)
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 block struct {
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
Parents []string
}
type testData struct {
K dagconfig.KType
GenesisID string
ExpectedReds []string
Blocks []block
}
// TestGHOSTDAG iterates over several dag simulations, and checks
@ -26,158 +36,26 @@ type testBlockData struct {
// block are calculated as expected.
func TestGHOSTDAG(t *testing.T) {
dagParams := dagconfig.SimnetParams
err := filepath.Walk("./testdata/dags/", func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
var test testData
file, err := os.Open(path)
if err != nil {
t.Fatalf("TestGHOSTDAG: failed opening file: %s", path)
}
decoder := json.NewDecoder(file)
decoder.DisallowUnknownFields()
err = decoder.Decode(&test)
if err != nil {
t.Fatalf("TestGHOSTDAG: test: %s, failed decoding json: %v", info.Name(), err)
}
tests := []struct {
k dagconfig.KType
expectedReds []string
dagData []*testBlockData
}{
{
k: 3,
expectedReds: []string{"F", "G", "H", "I", "O", "P"},
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{"A"},
id: "D",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"C", "D"},
id: "E",
expectedScore: 4,
expectedSelectedParent: "C",
expectedBlues: []string{"C", "D"},
},
{
parents: []string{"A"},
id: "F",
expectedScore: 1,
expectedSelectedParent: "A",
expectedBlues: []string{"A"},
},
{
parents: []string{"F"},
id: "G",
expectedScore: 2,
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{"E", "G"},
id: "J",
expectedScore: 5,
expectedSelectedParent: "E",
expectedBlues: []string{"E"},
},
{
parents: []string{"J"},
id: "K",
expectedScore: 6,
expectedSelectedParent: "J",
expectedBlues: []string{"J"},
},
{
parents: []string{"I", "K"},
id: "L",
expectedScore: 7,
expectedSelectedParent: "K",
expectedBlues: []string{"K"},
},
{
parents: []string{"L"},
id: "M",
expectedScore: 8,
expectedSelectedParent: "L",
expectedBlues: []string{"L"},
},
{
parents: []string{"M"},
id: "N",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"M"},
id: "O",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"M"},
id: "P",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"M"},
id: "Q",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"M"},
id: "R",
expectedScore: 9,
expectedSelectedParent: "M",
expectedBlues: []string{"M"},
},
{
parents: []string{"R"},
id: "S",
expectedScore: 10,
expectedSelectedParent: "R",
expectedBlues: []string{"R"},
},
{
parents: []string{"N", "O", "P", "Q", "S"},
id: "T",
expectedScore: 13,
expectedSelectedParent: "S",
expectedBlues: []string{"S", "Q", "N"},
},
},
},
}
for i, test := range tests {
func() {
resetExtraNonceForTest()
dagParams.K = test.k
dag, teardownFunc, err := DAGSetup(fmt.Sprintf("TestGHOSTDAG%d", i), true, Config{
dagParams.K = test.K
dag, teardownFunc, err := DAGSetup(fmt.Sprintf("TestGHOSTDAG %s", info.Name()), true, Config{
DAGParams: &dagParams,
})
if err != nil {
@ -188,32 +66,33 @@ func TestGHOSTDAG(t *testing.T) {
genesisNode := dag.genesis
blockByIDMap := make(map[string]*blockNode)
idByBlockMap := make(map[*blockNode]string)
blockByIDMap["A"] = genesisNode
idByBlockMap[genesisNode] = "A"
blockByIDMap[test.GenesisID] = genesisNode
idByBlockMap[genesisNode] = test.GenesisID
for _, blockData := range test.dagData {
for _, blockData := range test.Blocks {
parents := blockSet{}
for _, parentID := range blockData.parents {
for _, parentID := range blockData.Parents {
parent := blockByIDMap[parentID]
parents.add(parent)
}
block, err := PrepareBlockForTest(dag, parents.hashes(), nil)
if err != nil {
t.Fatalf("TestGHOSTDAG: block %v got unexpected error from PrepareBlockForTest: %v", blockData.id, err)
t.Fatalf("TestGHOSTDAG: block %s got unexpected error from PrepareBlockForTest: %v", blockData.ID,
err)
}
utilBlock := util.NewBlock(block)
isOrphan, isDelayed, err := dag.ProcessBlock(utilBlock, BFNoPoWCheck)
if err != nil {
t.Fatalf("TestGHOSTDAG: dag.ProcessBlock got unexpected error for block %v: %v", blockData.id, err)
t.Fatalf("TestGHOSTDAG: dag.ProcessBlock got unexpected error for block %s: %v", blockData.ID, err)
}
if isDelayed {
t.Fatalf("TestGHOSTDAG: block %s "+
"is too far in the future", blockData.id)
"is too far in the future", blockData.ID)
}
if isOrphan {
t.Fatalf("TestGHOSTDAG: block %v was unexpectedly orphan", blockData.id)
t.Fatalf("TestGHOSTDAG: block %s was unexpectedly orphan", blockData.ID)
}
node, ok := dag.index.LookupNode(utilBlock.Hash())
@ -221,8 +100,8 @@ func TestGHOSTDAG(t *testing.T) {
t.Fatalf("block %s does not exist in the DAG", utilBlock.Hash())
}
blockByIDMap[blockData.id] = node
idByBlockMap[node] = blockData.id
blockByIDMap[blockData.ID] = node
idByBlockMap[node] = blockData.ID
bluesIDs := make([]string, 0, len(node.blues))
for _, blue := range node.blues {
@ -231,17 +110,17 @@ func TestGHOSTDAG(t *testing.T) {
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.ExpectedScore != node.blueScore {
t.Errorf("Test %s: Block %s expected to have score %v but got %v (fulldata: %v)",
info.Name(), 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 blockData.ExpectedSelectedParent != selectedParentID {
t.Errorf("Test %s: Block %s expected to have selected parent %v but got %v (fulldata: %v)",
info.Name(), 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)
if !reflect.DeepEqual(blockData.ExpectedBlues, bluesIDs) {
t.Errorf("Test %s: Block %s expected to have blues %v but got %v (fulldata: %v)",
info.Name(), blockData.ID, blockData.ExpectedBlues, bluesIDs, fullDataStr)
}
}
@ -259,16 +138,22 @@ func TestGHOSTDAG(t *testing.T) {
delete(reds, blueID)
}
}
if !checkReds(test.expectedReds, reds) {
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)
sort.Strings(test.ExpectedReds)
t.Errorf("Test %s: Expected reds %v but got %v", info.Name(), test.ExpectedReds, redsIDs)
}
}()
return nil
})
if err != nil {
t.Fatal(err)
}
}

View File

@ -86,7 +86,7 @@ func (dag *BlockDAG) checkBlockDelay(block *util.Block, flags BehaviorFlags) (is
}
if isDelayed {
err := dag.addDelayedBlock(block, delay)
err := dag.addDelayedBlock(block, flags, delay)
if err != nil {
return false, err
}
@ -114,7 +114,7 @@ func (dag *BlockDAG) checkMissingParents(block *util.Block, flags BehaviorFlags)
if isParentDelayed {
// Add Millisecond to ensure that parent process time will be after its child.
delay += time.Millisecond
err := dag.addDelayedBlock(block, delay)
err := dag.addDelayedBlock(block, flags, delay)
if err != nil {
return false, false, err
}

233
domain/blockdag/testdata/dags/dag0.json vendored Normal file
View File

@ -0,0 +1,233 @@
{
"K": 4,
"GenesisID": "A",
"ExpectedReds": [
"Q",
"H",
"I"
],
"Blocks": [
{
"ID": "B",
"ExpectedScore": 1,
"ExpectedSelectedParent": "A",
"ExpectedBlues": [
"A"
],
"Parents": [
"A"
]
},
{
"ID": "C",
"ExpectedScore": 2,
"ExpectedSelectedParent": "B",
"ExpectedBlues": [
"B"
],
"Parents": [
"B"
]
},
{
"ID": "D",
"ExpectedScore": 1,
"ExpectedSelectedParent": "A",
"ExpectedBlues": [
"A"
],
"Parents": [
"A"
]
},
{
"ID": "E",
"ExpectedScore": 4,
"ExpectedSelectedParent": "C",
"ExpectedBlues": [
"C",
"D"
],
"Parents": [
"C",
"D"
]
},
{
"ID": "F",
"ExpectedScore": 1,
"ExpectedSelectedParent": "A",
"ExpectedBlues": [
"A"
],
"Parents": [
"A"
]
},
{
"ID": "G",
"ExpectedScore": 2,
"ExpectedSelectedParent": "F",
"ExpectedBlues": [
"F"
],
"Parents": [
"F"
]
},
{
"ID": "H",
"ExpectedScore": 1,
"ExpectedSelectedParent": "A",
"ExpectedBlues": [
"A"
],
"Parents": [
"A"
]
},
{
"ID": "I",
"ExpectedScore": 1,
"ExpectedSelectedParent": "A",
"ExpectedBlues": [
"A"
],
"Parents": [
"A"
]
},
{
"ID": "J",
"ExpectedScore": 7,
"ExpectedSelectedParent": "E",
"ExpectedBlues": [
"E",
"F",
"G"
],
"Parents": [
"E",
"G"
]
},
{
"ID": "K",
"ExpectedScore": 8,
"ExpectedSelectedParent": "J",
"ExpectedBlues": [
"J"
],
"Parents": [
"J"
]
},
{
"ID": "L",
"ExpectedScore": 9,
"ExpectedSelectedParent": "K",
"ExpectedBlues": [
"K"
],
"Parents": [
"I",
"K"
]
},
{
"ID": "M",
"ExpectedScore": 10,
"ExpectedSelectedParent": "L",
"ExpectedBlues": [
"L"
],
"Parents": [
"L"
]
},
{
"ID": "N",
"ExpectedScore": 11,
"ExpectedSelectedParent": "M",
"ExpectedBlues": [
"M"
],
"Parents": [
"M"
]
},
{
"ID": "O",
"ExpectedScore": 11,
"ExpectedSelectedParent": "M",
"ExpectedBlues": [
"M"
],
"Parents": [
"M"
]
},
{
"ID": "P",
"ExpectedScore": 11,
"ExpectedSelectedParent": "M",
"ExpectedBlues": [
"M"
],
"Parents": [
"M"
]
},
{
"ID": "Q",
"ExpectedScore": 11,
"ExpectedSelectedParent": "M",
"ExpectedBlues": [
"M"
],
"Parents": [
"M"
]
},
{
"ID": "R",
"ExpectedScore": 11,
"ExpectedSelectedParent": "M",
"ExpectedBlues": [
"M"
],
"Parents": [
"M"
]
},
{
"ID": "S",
"ExpectedScore": 12,
"ExpectedSelectedParent": "R",
"ExpectedBlues": [
"R"
],
"Parents": [
"R"
]
},
{
"ID": "T",
"ExpectedScore": 16,
"ExpectedSelectedParent": "S",
"ExpectedBlues": [
"S",
"P",
"N",
"O"
],
"Parents": [
"N",
"O",
"P",
"Q",
"S"
]
}
]
}

386
domain/blockdag/testdata/dags/dag1.json vendored Normal file
View File

@ -0,0 +1,386 @@
{
"K": 4,
"GenesisID": "0",
"ExpectedReds": [
"12",
"30",
"6",
"27",
"4",
"16",
"7",
"23",
"24",
"11",
"15",
"19",
"9"
],
"Blocks": [
{
"ID": "1",
"ExpectedScore": 1,
"ExpectedSelectedParent": "0",
"ExpectedBlues": [
"0"
],
"Parents": [
"0"
]
},
{
"ID": "2",
"ExpectedScore": 1,
"ExpectedSelectedParent": "0",
"ExpectedBlues": [
"0"
],
"Parents": [
"0"
]
},
{
"ID": "3",
"ExpectedScore": 1,
"ExpectedSelectedParent": "0",
"ExpectedBlues": [
"0"
],
"Parents": [
"0"
]
},
{
"ID": "4",
"ExpectedScore": 2,
"ExpectedSelectedParent": "1",
"ExpectedBlues": [
"1"
],
"Parents": [
"1"
]
},
{
"ID": "5",
"ExpectedScore": 3,
"ExpectedSelectedParent": "2",
"ExpectedBlues": [
"2",
"3"
],
"Parents": [
"2",
"3"
]
},
{
"ID": "6",
"ExpectedScore": 2,
"ExpectedSelectedParent": "3",
"ExpectedBlues": [
"3"
],
"Parents": [
"3"
]
},
{
"ID": "7",
"ExpectedScore": 3,
"ExpectedSelectedParent": "6",
"ExpectedBlues": [
"6"
],
"Parents": [
"6"
]
},
{
"ID": "8",
"ExpectedScore": 3,
"ExpectedSelectedParent": "2",
"ExpectedBlues": [
"2",
"1"
],
"Parents": [
"1",
"2"
]
},
{
"ID": "9",
"ExpectedScore": 5,
"ExpectedSelectedParent": "5",
"ExpectedBlues": [
"5",
"6"
],
"Parents": [
"5",
"6"
]
},
{
"ID": "10",
"ExpectedScore": 5,
"ExpectedSelectedParent": "8",
"ExpectedBlues": [
"8",
"4"
],
"Parents": [
"8",
"4"
]
},
{
"ID": "11",
"ExpectedScore": 7,
"ExpectedSelectedParent": "9",
"ExpectedBlues": [
"9",
"7"
],
"Parents": [
"7",
"9"
]
},
{
"ID": "12",
"ExpectedScore": 8,
"ExpectedSelectedParent": "9",
"ExpectedBlues": [
"9",
"8",
"10"
],
"Parents": [
"10",
"9"
]
},
{
"ID": "13",
"ExpectedScore": 6,
"ExpectedSelectedParent": "8",
"ExpectedBlues": [
"8",
"3",
"5"
],
"Parents": [
"5",
"8"
]
},
{
"ID": "14",
"ExpectedScore": 8,
"ExpectedSelectedParent": "13",
"ExpectedBlues": [
"13",
"10"
],
"Parents": [
"13",
"10"
]
},
{
"ID": "15",
"ExpectedScore": 9,
"ExpectedSelectedParent": "11",
"ExpectedBlues": [
"11",
"13"
],
"Parents": [
"11",
"13"
]
},
{
"ID": "16",
"ExpectedScore": 8,
"ExpectedSelectedParent": "11",
"ExpectedBlues": [
"11"
],
"Parents": [
"11"
]
},
{
"ID": "17",
"ExpectedScore": 9,
"ExpectedSelectedParent": "14",
"ExpectedBlues": [
"14"
],
"Parents": [
"14"
]
},
{
"ID": "18",
"ExpectedScore": 7,
"ExpectedSelectedParent": "13",
"ExpectedBlues": [
"13"
],
"Parents": [
"13"
]
},
{
"ID": "19",
"ExpectedScore": 10,
"ExpectedSelectedParent": "15",
"ExpectedBlues": [
"15"
],
"Parents": [
"18",
"15"
]
},
{
"ID": "20",
"ExpectedScore": 10,
"ExpectedSelectedParent": "17",
"ExpectedBlues": [
"17"
],
"Parents": [
"16",
"17"
]
},
{
"ID": "21",
"ExpectedScore": 12,
"ExpectedSelectedParent": "20",
"ExpectedBlues": [
"20",
"18"
],
"Parents": [
"18",
"20"
]
},
{
"ID": "22",
"ExpectedScore": 13,
"ExpectedSelectedParent": "21",
"ExpectedBlues": [
"21"
],
"Parents": [
"19",
"21"
]
},
{
"ID": "23",
"ExpectedScore": 11,
"ExpectedSelectedParent": "17",
"ExpectedBlues": [
"17",
"12"
],
"Parents": [
"12",
"17"
]
},
{
"ID": "24",
"ExpectedScore": 13,
"ExpectedSelectedParent": "23",
"ExpectedBlues": [
"23",
"20"
],
"Parents": [
"20",
"23"
]
},
{
"ID": "25",
"ExpectedScore": 13,
"ExpectedSelectedParent": "21",
"ExpectedBlues": [
"21"
],
"Parents": [
"21"
]
},
{
"ID": "26",
"ExpectedScore": 15,
"ExpectedSelectedParent": "22",
"ExpectedBlues": [
"22",
"25"
],
"Parents": [
"22",
"24",
"25"
]
},
{
"ID": "27",
"ExpectedScore": 9,
"ExpectedSelectedParent": "16",
"ExpectedBlues": [
"16"
],
"Parents": [
"16"
]
},
{
"ID": "28",
"ExpectedScore": 14,
"ExpectedSelectedParent": "25",
"ExpectedBlues": [
"25"
],
"Parents": [
"23",
"25"
]
},
{
"ID": "29",
"ExpectedScore": 17,
"ExpectedSelectedParent": "26",
"ExpectedBlues": [
"26",
"28"
],
"Parents": [
"26",
"28"
]
},
{
"ID": "30",
"ExpectedScore": 10,
"ExpectedSelectedParent": "27",
"ExpectedBlues": [
"27"
],
"Parents": [
"27"
]
}
]
}