diff --git a/domain/consensus/processes/ghostdag2/ghostdagimpl.go b/domain/consensus/processes/ghostdag2/ghostdagimpl.go new file mode 100644 index 000000000..867f52a8e --- /dev/null +++ b/domain/consensus/processes/ghostdag2/ghostdagimpl.go @@ -0,0 +1,381 @@ +package ghostdag2 + +import ( + "github.com/kaspanet/kaspad/domain/consensus/model" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "sort" +) + +type ghostdagHelper struct { + k model.KType + dataStore model.GHOSTDAGDataStore + dbAccess model.DBReader + dagTopologyManager model.DAGTopologyManager +} + +// New creates a new instance of this alternative ghostdag impl +func New( + databaseContext model.DBReader, + dagTopologyManager model.DAGTopologyManager, + ghostdagDataStore model.GHOSTDAGDataStore, + k model.KType) model.GHOSTDAGManager { + + return &ghostdagHelper{ + dbAccess: databaseContext, + dagTopologyManager: dagTopologyManager, + dataStore: ghostdagDataStore, + k: k, + } +} + +/* --------------------------------------------- */ + +func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error { + var maxNum uint64 = 0 + var myScore uint64 = 0 + /* find the selectedParent */ + blockParents, err := gh.dagTopologyManager.Parents(blockCandidate) + if err != nil { + return err + } + var selectedParent = blockParents[0] + for _, parent := range blockParents { + blockData, err := gh.dataStore.Get(gh.dbAccess, parent) + if err != nil { + return err + } + blockScore := blockData.BlueScore + if blockScore > maxNum { + selectedParent = parent + maxNum = blockScore + } + if blockScore == maxNum && ismoreHash(parent, selectedParent) { + selectedParent = parent + } + } + myScore = maxNum + + /* Goal: iterate blockCandidate's mergeSet and divide it to : blue, blues, reds. */ + var mergeSetBlues = make([]*externalapi.DomainHash, 0) + var mergeSetReds = make([]*externalapi.DomainHash, 0) + var blueSet = make([]*externalapi.DomainHash, 0) + + mergeSetBlues = append(mergeSetBlues, selectedParent) + + mergeSetArr, err := gh.findMergeSet(blockParents, selectedParent) + if err != nil { + return err + } + + err = gh.sortByBlueScore(mergeSetArr) + if err != nil { + return err + } + err = gh.findBlueSet(&blueSet, selectedParent) + if err != nil { + return err + } + + for _, mergeSetBlock := range mergeSetArr { + if *mergeSetBlock == *selectedParent { + if !contains(selectedParent, mergeSetBlues) { + mergeSetBlues = append(mergeSetBlues, selectedParent) + blueSet = append(blueSet, selectedParent) + } + continue + } + err := gh.divideBlueRed(selectedParent, mergeSetBlock, &mergeSetBlues, &mergeSetReds, &blueSet) + if err != nil { + return err + } + } + myScore += uint64(len(mergeSetBlues)) + + e := model.BlockGHOSTDAGData{ + BlueScore: myScore, + SelectedParent: selectedParent, + MergeSetBlues: mergeSetBlues, + MergeSetReds: mergeSetReds, + } + gh.dataStore.Stage(blockCandidate, &e) + return nil +} + +/* --------isMoreHash(w, selectedParent)----------------*/ +func ismoreHash(parent *externalapi.DomainHash, selectedParent *externalapi.DomainHash) bool { + //Check if parentHash is more then selectedParentHash + for i := len(parent) - 1; i >= 0; i-- { + switch { + case parent[i] < selectedParent[i]: + return false + case parent[i] > selectedParent[i]: + return true + } + } + return false +} + +/* 1. blue = selectedParent.blue + blues + 2. not connected to at most K blocks (from the blue group) + 3. for each block at blue , check if not destroy +*/ + +/* ---------------divideBluesReds--------------------- */ +func (gh *ghostdagHelper) divideBlueRed(selectedParent *externalapi.DomainHash, desiredBlock *externalapi.DomainHash, + blues *[]*externalapi.DomainHash, reds *[]*externalapi.DomainHash, blueSet *[]*externalapi.DomainHash) error { + var k = int(gh.k) + counter := 0 + + var suspectsBlues = make([]*externalapi.DomainHash, 0) + isMergeBlue := true + //check that not-connected to at most k. + for _, block := range *blueSet { + isAnticone, err := gh.isAnticone(block, desiredBlock) + if err != nil { + return err + } + if isAnticone { + counter++ + suspectsBlues = append(suspectsBlues, block) + } + if counter > k { + isMergeBlue = false + break + } + } + if !isMergeBlue { + if !contains(desiredBlock, *reds) { + *reds = append(*reds, desiredBlock) + } + return nil + } + + // check that the k-cluster of each blue is still valid. + for _, blue := range suspectsBlues { + isDestroyed, err := gh.checkIfDestroy(blue, blueSet) + if err != nil { + return err + } + if isDestroyed { + isMergeBlue = false + break + } + } + if !isMergeBlue { + if !contains(desiredBlock, *reds) { + *reds = append(*reds, desiredBlock) + } + return nil + } + if !contains(desiredBlock, *blues) { + *blues = append(*blues, desiredBlock) + } + if !contains(desiredBlock, *blueSet) { + *blueSet = append(*blueSet, desiredBlock) + } + return nil +} + +/* ---------------isAnticone-------------------------- */ +func (gh *ghostdagHelper) isAnticone(blockA, blockB *externalapi.DomainHash) (bool, error) { + isAAncestorOfAB, err := gh.dagTopologyManager.IsAncestorOf(blockA, blockB) + if err != nil { + return false, err + } + isBAncestorOfA, err := gh.dagTopologyManager.IsAncestorOf(blockB, blockA) + if err != nil { + return false, err + } + return !isAAncestorOfAB && !isBAncestorOfA, nil + +} + +/* ----------------validateKCluster------------------- */ +func (gh *ghostdagHelper) validateKCluster(chain *externalapi.DomainHash, checkedBlock *externalapi.DomainHash, counter *int, blueSet *[]*externalapi.DomainHash) (bool, error) { + var k = int(gh.k) + isAnticone, err := gh.isAnticone(chain, checkedBlock) + if err != nil { + return false, err + } + if isAnticone { + if *counter > k { + return false, nil + } + ifDestroy, err := gh.checkIfDestroy(chain, blueSet) + if err != nil { + return false, err + } + if ifDestroy { + return false, nil + } + *counter++ + return true, nil + } + isAncestorOf, err := gh.dagTopologyManager.IsAncestorOf(checkedBlock, chain) + if err != nil { + return false, err + } + if isAncestorOf { + dataStore, err := gh.BlockData(chain) + if err != nil { + return false, err + } + if mergeSetReds := dataStore.MergeSetReds; contains(checkedBlock, mergeSetReds) { + return false, nil + } + } else { + return true, nil + } + + return false, nil +} + +/*----------------contains-------------------------- */ +func contains(item *externalapi.DomainHash, items []*externalapi.DomainHash) bool { + for _, r := range items { + if *r == *item { + return true + } + } + return false +} + +/* ----------------checkIfDestroy------------------- */ +/* find number of not-connected in his blue*/ +func (gh *ghostdagHelper) checkIfDestroy(blockBlue *externalapi.DomainHash, blueSet *[]*externalapi.DomainHash) (bool, error) { + // Goal: check that the K-cluster of each block in the blueSet is not destroyed when adding the block to the mergeSet. + var k = int(gh.k) + counter := 0 + for _, blue := range *blueSet { + isAnticone, err := gh.isAnticone(blue, blockBlue) + if err != nil { + return true, err + } + if isAnticone { + counter++ + } + if counter > k { + return true, nil + } + } + return false, nil +} + +/* ----------------findMergeSet------------------- */ +func (gh *ghostdagHelper) findMergeSet(parents []*externalapi.DomainHash, selectedParent *externalapi.DomainHash) ([]*externalapi.DomainHash, error) { + + allMergeSet := make([]*externalapi.DomainHash, 0) + blockQueue := make([]*externalapi.DomainHash, 0) + for _, parent := range parents { + if !contains(parent, blockQueue) { + blockQueue = append(blockQueue, parent) + } + + } + for len(blockQueue) > 0 { + block := blockQueue[0] + blockQueue = blockQueue[1:] + if *selectedParent == *block { + if !contains(block, allMergeSet) { + allMergeSet = append(allMergeSet, block) + } + continue + } + isancestorOf, err := gh.dagTopologyManager.IsAncestorOf(block, selectedParent) + if err != nil { + return nil, err + } + if isancestorOf { + continue + } + if !contains(block, allMergeSet) { + allMergeSet = append(allMergeSet, block) + } + err = gh.insertParent(block, &blockQueue) + if err != nil { + return nil, err + } + + } + return allMergeSet, nil +} + +/* ----------------insertParent------------------- */ +/* Insert all parents to the queue*/ +func (gh *ghostdagHelper) insertParent(child *externalapi.DomainHash, queue *[]*externalapi.DomainHash) error { + parents, err := gh.dagTopologyManager.Parents(child) + if err != nil { + return err + } + for _, parent := range parents { + if contains(parent, *queue) { + continue + } + *queue = append(*queue, parent) + } + return nil +} + +/* ----------------findBlueSet------------------- */ +func (gh *ghostdagHelper) findBlueSet(blueSet *[]*externalapi.DomainHash, selectedParent *externalapi.DomainHash) error { + for selectedParent != nil { + if !contains(selectedParent, *blueSet) { + *blueSet = append(*blueSet, selectedParent) + } + blockData, err := gh.dataStore.Get(gh.dbAccess, selectedParent) + if err != nil { + return err + } + mergeSetBlue := blockData.MergeSetBlues + for _, blue := range mergeSetBlue { + if contains(blue, *blueSet) { + continue + } + *blueSet = append(*blueSet, blue) + } + selectedParent = blockData.SelectedParent + } + return nil +} + +/* ----------------sortByBlueScore------------------- */ +func (gh *ghostdagHelper) sortByBlueScore(arr []*externalapi.DomainHash) error { + + var err error = nil + sort.Slice(arr, func(i, j int) bool { + + blockLeft, error := gh.dataStore.Get(gh.dbAccess, arr[i]) + if error != nil { + err = error + return false + } + + blockRight, error := gh.dataStore.Get(gh.dbAccess, arr[j]) + if error != nil { + err = error + return false + } + + if blockLeft.BlueScore < blockRight.BlueScore { + return true + } + if blockLeft.BlueScore == blockRight.BlueScore { + return ismoreHash(arr[j], arr[i]) + } + return false + }) + return err +} + +/* --------------------------------------------- */ + +func (gh *ghostdagHelper) BlockData(blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) { + return gh.dataStore.Get(gh.dbAccess, blockHash) +} +func (gh *ghostdagHelper) ChooseSelectedParent(blockHashes ...*externalapi.DomainHash) (*externalapi.DomainHash, error) { + panic("implement me") +} + +func (gh *ghostdagHelper) Less(blockHashA *externalapi.DomainHash, ghostdagDataA *model.BlockGHOSTDAGData, blockHashB *externalapi.DomainHash, ghostdagDataB *model.BlockGHOSTDAGData) bool { + panic("implement me") +} diff --git a/domain/consensus/processes/ghostdagmanager/ghostdag_test.go b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go new file mode 100644 index 000000000..bd01b100e --- /dev/null +++ b/domain/consensus/processes/ghostdagmanager/ghostdag_test.go @@ -0,0 +1,273 @@ +package ghostdagmanager + +import ( + "encoding/json" + "github.com/kaspanet/kaspad/domain/consensus/model" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2" + "os" + "path/filepath" + "reflect" + "testing" +) + +// TestGHOSTDAG iterates over several dag simulations, and checks +// that the blue score, blue set and selected parent of each +// block are calculated as expected. +func TestGHOSTDAG(t *testing.T) { + + type block struct { + ID string `json:"ID"` + Score uint64 `json:"ExpectedScore"` + SelectedParent string `json:"ExpectedSelectedParent"` + MergeSetReds []string `json:"ExpectedReds"` + MergeSetBlues []string `json:"ExpectedBlues"` + Parents []string `json:"Parents"` + } + + // json struct: + type testDag struct { + K model.KType `json:"K"` + GenesisID string `json:"GenesisID"` + ExpectedMergeSetReds []string `json:"ExpectedReds"` + Blocks []block `json:"Blocks"` + } + + type implManager struct { + function func( + databaseContext model.DBReader, + dagTopologyManager model.DAGTopologyManager, + ghostdagDataStore model.GHOSTDAGDataStore, + k model.KType) model.GHOSTDAGManager + implName string + } + + dagTopology := &DAGTopologyManagerImpl{ + parentsMap: make(map[externalapi.DomainHash][]*externalapi.DomainHash), + } + + ghostdagDataStore := &GHOSTDAGDataStoreImpl{ + dagMap: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData), + } + var blockGHOSTDAGDataGenesis = &model.BlockGHOSTDAGData{ + BlueScore: 0, + SelectedParent: nil, + MergeSetBlues: nil, + MergeSetReds: nil, + BluesAnticoneSizes: nil, + } + + var testsCounter int + err := filepath.Walk("../../testdata/dags", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + jsonFile, err := os.Open(path) + if err != nil { + t.Fatalf("TestGHOSTDAG : failed opening the json file %s: %v", info.Name(), err) + } + defer jsonFile.Close() + var test testDag + decoder := json.NewDecoder(jsonFile) + decoder.DisallowUnknownFields() + err = decoder.Decode(&test) + if err != nil { + t.Fatalf("TestGHOSTDAG:failed decoding json: %v", err) + } + + var genesisHash externalapi.DomainHash + copy(genesisHash[:], test.GenesisID) + + dagTopology.parentsMap[genesisHash] = nil + + ghostdagDataStore.dagMap[genesisHash] = blockGHOSTDAGDataGenesis + + //NOTE: FOR ADDING/REMOVING AN IMPLEMENTATION CHANGE BELOW: + implementationFactories := []implManager{ + {New, "Original"}, + {ghostdag2.New, "Tal's impl"}, + } + + for _, factory := range implementationFactories { + + g := factory.function(nil, dagTopology, ghostdagDataStore, model.KType(test.K)) + for _, testBlockData := range test.Blocks { + + blockID := StringToByte(testBlockData.ID) + dagTopology.parentsMap[*blockID] = StringToByteArray(testBlockData.Parents) + + err := g.GHOSTDAG(blockID) + if err != nil { + t.Fatalf("Test failed: \n Impl: %s,FileName: %s \n error on GHOSTDAG - block %s: %s.", + factory.implName, info.Name(), testBlockData.ID, err) + } + ghostdagData, err := ghostdagDataStore.Get(nil, blockID) + if err != nil { + t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: ghostdagDataStore error: %v.", + factory.implName, info.Name(), testBlockData.ID, err) + } + + if testBlockData.Score != (ghostdagData.BlueScore) { + t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected blue score %d but got %d.", + factory.implName, info.Name(), testBlockData.ID, testBlockData.Score, ghostdagData.BlueScore) + } + + if *StringToByte(testBlockData.SelectedParent) != *ghostdagData.SelectedParent { + t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected selected parent %v but got %v.", + factory.implName, info.Name(), testBlockData.ID, testBlockData.SelectedParent, string(ghostdagData.SelectedParent[:])) + } + + if !reflect.DeepEqual(StringToByteArray(testBlockData.MergeSetBlues), ghostdagData.MergeSetBlues) { + t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected merge set blues %v but got %v.", + factory.implName, info.Name(), testBlockData.ID, testBlockData.MergeSetBlues, hashesToStrings(ghostdagData.MergeSetBlues)) + } + + if !reflect.DeepEqual(StringToByteArray(testBlockData.MergeSetReds), ghostdagData.MergeSetReds) { + t.Fatalf("\nTEST FAILED:\n Impl: %s, FileName: %s \nBlock: %s, \nError: expected merge set reds %v but got %v.", + factory.implName, info.Name(), testBlockData.ID, testBlockData.MergeSetReds, hashesToStrings(ghostdagData.MergeSetReds)) + } + + } + dagTopology.parentsMap = make(map[externalapi.DomainHash][]*externalapi.DomainHash) + dagTopology.parentsMap[genesisHash] = nil + ghostdagDataStore.dagMap = make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData) + ghostdagDataStore.dagMap[genesisHash] = blockGHOSTDAGDataGenesis + } + + testsCounter++ + return nil + }) + if err != nil { + t.Fatal(err) + } + if testsCounter != 3 { + t.Fatalf("Expected 3 test files, ran %d instead", testsCounter) + } +} + +func hashesToStrings(arr []*externalapi.DomainHash) []string { + var strArr = make([]string, len(arr)) + for i, hash := range arr { + strArr[i] = string(hash[:]) + } + return strArr +} + +func StringToByte(strID string) *externalapi.DomainHash { + var domainHash externalapi.DomainHash + copy(domainHash[:], strID) + return &domainHash +} + +func StringToByteArray(stringIDArr []string) []*externalapi.DomainHash { + domainHashArr := make([]*externalapi.DomainHash, len(stringIDArr)) + for i, strID := range stringIDArr { + domainHashArr[i] = StringToByte(strID) + } + return domainHashArr +} + +/* ---------------------- */ +type GHOSTDAGDataStoreImpl struct { + dagMap map[externalapi.DomainHash]*model.BlockGHOSTDAGData +} + +func (ds *GHOSTDAGDataStoreImpl) Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData *model.BlockGHOSTDAGData) { + ds.dagMap[*blockHash] = blockGHOSTDAGData +} + +func (ds *GHOSTDAGDataStoreImpl) IsStaged() bool { + panic("implement me") +} + +func (ds *GHOSTDAGDataStoreImpl) Discard() { + panic("implement me") +} + +func (ds *GHOSTDAGDataStoreImpl) Commit(dbTx model.DBTransaction) error { + panic("implement me") +} + +func (ds *GHOSTDAGDataStoreImpl) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) { + v, ok := ds.dagMap[*blockHash] + if ok { + return v, nil + } + return nil, nil +} + +type DAGTopologyManagerImpl struct { + parentsMap map[externalapi.DomainHash][]*externalapi.DomainHash +} + +func (dt *DAGTopologyManagerImpl) Tips() ([]*externalapi.DomainHash, error) { + panic("implement me") +} + +func (dt *DAGTopologyManagerImpl) AddTip(tipHash *externalapi.DomainHash) error { + panic("implement me") +} + +func (dt *DAGTopologyManagerImpl) Parents(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) { + v, ok := dt.parentsMap[*blockHash] + if !ok { + return []*externalapi.DomainHash{}, nil + } + + return v, nil +} + +func (dt *DAGTopologyManagerImpl) Children(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) { + panic("unimplemented") +} + +func (dt *DAGTopologyManagerImpl) IsParentOf(hashBlockA *externalapi.DomainHash, hashBlockB *externalapi.DomainHash) (bool, error) { + panic("unimplemented") +} + +func (dt *DAGTopologyManagerImpl) IsChildOf(hashBlockA *externalapi.DomainHash, hashBlockB *externalapi.DomainHash) (bool, error) { + panic("unimplemented") +} + +func (dt *DAGTopologyManagerImpl) IsAncestorOf(hashBlockA *externalapi.DomainHash, hashBlockB *externalapi.DomainHash) (bool, error) { + blockBParents, isOk := dt.parentsMap[*hashBlockB] + if !isOk { + return false, nil + } + + for _, parentOfB := range blockBParents { + if *parentOfB == *hashBlockA { + return true, nil + } + } + + for _, parentOfB := range blockBParents { + isAncestorOf, err := dt.IsAncestorOf(hashBlockA, parentOfB) + if err != nil { + return false, err + } + if isAncestorOf { + return true, nil + } + } + return false, nil + +} + +func (dt *DAGTopologyManagerImpl) IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) { + panic("unimplemented") +} + +func (dt *DAGTopologyManagerImpl) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) { + panic("unimplemented") +} +func (dt *DAGTopologyManagerImpl) IsInSelectedParentChainOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) { + panic("unimplemented") +} + +func (dt *DAGTopologyManagerImpl) SetParents(blockHash *externalapi.DomainHash, parentHashes []*externalapi.DomainHash) error { + panic("unimplemented") +} diff --git a/domain/consensus/testdata/dags/dag0.json b/domain/consensus/testdata/dags/dag0.json new file mode 100644 index 000000000..8c36f23b4 --- /dev/null +++ b/domain/consensus/testdata/dags/dag0.json @@ -0,0 +1,252 @@ +{ + "K": 4, + "GenesisID": "A", + "ExpectedReds": [ + "Q", + "H", + "I" + ], + "Blocks": [ + { + "ID": "B", + "ExpectedScore": 1, + "ExpectedSelectedParent": "A", + "ExpectedReds": [], + "ExpectedBlues": [ + "A" + ], + "Parents": [ + "A" + ] + }, + { + "ID": "C", + "ExpectedScore": 2, + "ExpectedSelectedParent": "B", + "ExpectedReds": [], + "ExpectedBlues": [ + "B" + ], + "Parents": [ + "B" + ] + }, + { + "ID": "D", + "ExpectedScore": 1, + "ExpectedSelectedParent": "A", + "ExpectedReds": [], + "ExpectedBlues": [ + "A" + ], + "Parents": [ + "A" + ] + }, + { + "ID": "E", + "ExpectedScore": 4, + "ExpectedSelectedParent": "C", + "ExpectedReds": [], + "ExpectedBlues": [ + "C", + "D" + ], + "Parents": [ + "C", + "D" + ] + }, + { + "ID": "F", + "ExpectedScore": 1, + "ExpectedSelectedParent": "A", + "ExpectedReds": [], + "ExpectedBlues": [ + "A" + ], + "Parents": [ + "A" + ] + }, + { + "ID": "G", + "ExpectedScore": 2, + "ExpectedSelectedParent": "F", + "ExpectedReds": [], + "ExpectedBlues": [ + "F" + ], + "Parents": [ + "F" + ] + }, + { + "ID": "H", + "ExpectedScore": 1, + "ExpectedSelectedParent": "A", + "ExpectedReds": [], + "ExpectedBlues": [ + "A" + ], + "Parents": [ + "A" + ] + }, + { + "ID": "I", + "ExpectedScore": 1, + "ExpectedSelectedParent": "A", + "ExpectedReds": [], + "ExpectedBlues": [ + "A" + ], + "Parents": [ + "A" + ] + }, + { + "ID": "J", + "ExpectedScore": 7, + "ExpectedSelectedParent": "E", + "ExpectedReds": [], + "ExpectedBlues": [ + "E", + "F", + "G" + ], + "Parents": [ + "E", + "G" + ] + }, + { + "ID": "K", + "ExpectedScore": 8, + "ExpectedSelectedParent": "J", + "ExpectedReds": [], + "ExpectedBlues": [ + "J" + ], + "Parents": [ + "J" + ] + }, + { + "ID": "L", + "ExpectedScore": 9, + "ExpectedSelectedParent": "K", + "ExpectedReds": ["I"], + "ExpectedBlues": [ + "K" + ], + "Parents": [ + "I", + "K" + ] + }, + { + "ID": "M", + "ExpectedScore": 10, + "ExpectedSelectedParent": "L", + "ExpectedReds": [], + "ExpectedBlues": [ + "L" + ], + "Parents": [ + "L" + ] + }, + { + "ID": "N", + "ExpectedScore": 11, + "ExpectedSelectedParent": "M", + "ExpectedReds": [], + "ExpectedBlues": [ + "M" + ], + "Parents": [ + "M" + ] + }, + { + "ID": "O", + "ExpectedScore": 11, + "ExpectedSelectedParent": "M", + "ExpectedReds": [], + "ExpectedBlues": [ + "M" + ], + "Parents": [ + "M" + ] + }, + { + "ID": "2", + "ExpectedScore": 11, + "ExpectedSelectedParent": "M", + "ExpectedReds": [], + "ExpectedBlues": [ + "M" + ], + "Parents": [ + "M" + ] + }, + { + "ID": "Q", + "ExpectedScore": 11, + "ExpectedSelectedParent": "M", + "ExpectedReds": [], + "ExpectedBlues": [ + "M" + ], + "Parents": [ + "M" + ] + }, + { + "ID": "R", + "ExpectedScore": 11, + "ExpectedSelectedParent": "M", + "ExpectedReds": [], + "ExpectedBlues": [ + "M" + ], + "Parents": [ + "M" + ] + }, + { + "ID": "S", + "ExpectedScore": 12, + "ExpectedSelectedParent": "R", + "ExpectedReds": [], + "ExpectedBlues": [ + "R" + ], + "Parents": [ + "R" + ] + }, + { + "ID": "T", + "ExpectedScore": 16, + "ExpectedSelectedParent": "S", + "ExpectedReds": ["Q"], + "ExpectedBlues": [ + "S", + "2", + "N", + "O" + ], + "Parents": [ + "N", + "O", + "2", + "Q", + "S" + ] + } + ] +} diff --git a/domain/consensus/testdata/dags/dag1.json b/domain/consensus/testdata/dags/dag1.json new file mode 100644 index 000000000..d751b3c00 --- /dev/null +++ b/domain/consensus/testdata/dags/dag1.json @@ -0,0 +1,436 @@ +{ + "K": 4, + "GenesisID": "0", + "Blocks": [ + { + "ID": "1111", + "ExpectedScore": 1, + "ExpectedSelectedParent": "0", + "ExpectedReds": [], + "ExpectedBlues": [ + "0" + ], + "Parents": [ + "0" + ] + }, + { + "ID": "2", + "ExpectedScore": 1, + "ExpectedSelectedParent": "0", + "ExpectedReds": [], + "ExpectedBlues": [ + "0" + ], + "Parents": [ + "0" + ] + }, + { + "ID": "3", + "ExpectedScore": 1, + "ExpectedSelectedParent": "0", + "ExpectedReds": [], + "ExpectedBlues": [ + "0" + ], + "Parents": [ + "0" + ] + }, + { + "ID": "4", + "ExpectedScore": 2, + "ExpectedSelectedParent": "1111", + "ExpectedReds": [], + "ExpectedBlues": [ + "1111" + ], + "Parents": [ + "1111" + ] + }, + { + "ID": "5", + "ExpectedScore": 3, + "ExpectedSelectedParent": "3", + "ExpectedReds": [], + "ExpectedBlues": [ + "3", + "2" + ], + "Parents": [ + "2", + "3" + ] + }, + { + "ID": "6", + "ExpectedScore": 2, + "ExpectedSelectedParent": "3", + "ExpectedReds": [], + "ExpectedBlues": [ + "3" + ], + "Parents": [ + "3" + ] + }, + { + "ID": "7", + "ExpectedScore": 3, + "ExpectedSelectedParent": "6", + "ExpectedReds": [], + "ExpectedBlues": [ + "6" + ], + "Parents": [ + "6" + ] + }, + { + "ID": "8", + "ExpectedScore": 3, + "ExpectedSelectedParent": "1111", + "ExpectedReds": [], + "ExpectedBlues": [ + "1111", + "2" + ], + "Parents": [ + "1111", + "2" + ] + }, + { + "ID": "9", + "ExpectedScore": 5, + "ExpectedSelectedParent": "5", + "ExpectedReds": [], + "ExpectedBlues": [ + "5", + "6" + ], + "Parents": [ + "5", + "6" + ] + }, + { + "ID": "10", + "ExpectedScore": 5, + "ExpectedSelectedParent": "8", + "ExpectedReds": [], + "ExpectedBlues": [ + "8", + "4" + ], + "Parents": [ + "8", + "4" + ] + }, + { + "ID": "11", + "ExpectedScore": 7, + "ExpectedSelectedParent": "9", + "ExpectedReds": [], + "ExpectedBlues": [ + "9", + "7" + ], + "Parents": [ + "7", + "9" + ] + }, + { + "ID": "12", + "ExpectedScore": 8, + "ExpectedSelectedParent": "10", + "ExpectedReds": [ + "3", + "6" + ], + "ExpectedBlues": [ + "10", + "5", + "9" + ], + "Parents": [ + "10", + "9" + ] + }, + { + "ID": "13", + "ExpectedScore": 6, + "ExpectedSelectedParent": "8", + "ExpectedReds": [], + "ExpectedBlues": [ + "8", + "3", + "5" + ], + "Parents": [ + "5", + "8" + ] + }, + { + "ID": "14", + "ExpectedScore": 8, + "ExpectedSelectedParent": "13", + "ExpectedReds": [ + "4" + ], + "ExpectedBlues": [ + "13", + "10" + ], + "Parents": [ + "13", + "10" + ] + }, + { + "ID": "15", + "ExpectedScore": 9, + "ExpectedSelectedParent": "11", + "ExpectedReds": [ + "1111", + "8" + ], + "ExpectedBlues": [ + "11", + "13" + ], + "Parents": [ + "11", + "13" + ] + }, + { + "ID": "16", + "ExpectedScore": 8, + "ExpectedSelectedParent": "11", + "ExpectedReds": [], + "ExpectedBlues": [ + "11" + ], + "Parents": [ + "11" + ] + }, + { + "ID": "17", + "ExpectedScore": 9, + "ExpectedSelectedParent": "14", + "ExpectedReds": [], + "ExpectedBlues": [ + "14" + ], + "Parents": [ + "14" + ] + }, + { + "ID": "18", + "ExpectedScore": 7, + "ExpectedSelectedParent": "13", + "ExpectedReds": [], + "ExpectedBlues": [ + "13" + ], + "Parents": [ + "13" + ] + }, + { + "ID": "19", + "ExpectedScore": 10, + "ExpectedSelectedParent": "15", + "ExpectedReds": [ + "18" + ], + "ExpectedBlues": [ + "15" + ], + "Parents": [ + "18", + "15" + ] + }, + { + "ID": "20", + "ExpectedScore": 10, + "ExpectedSelectedParent": "17", + "ExpectedReds": [ + "6", + "7", + "9", + "11", + "16" + ], + "ExpectedBlues": [ + "17" + ], + "Parents": [ + "16", + "17" + ] + }, + { + "ID": "21", + "ExpectedScore": 12, + "ExpectedSelectedParent": "20", + "ExpectedReds": [], + "ExpectedBlues": [ + "20", + "18" + ], + "Parents": [ + "18", + "20" + ] + }, + { + "ID": "22", + "ExpectedScore": 13, + "ExpectedSelectedParent": "21", + "ExpectedReds": [ + "15", + "19" + ], + "ExpectedBlues": [ + "21" + ], + "Parents": [ + "19", + "21" + ] + }, + { + "ID": "23", + "ExpectedScore": 11, + "ExpectedSelectedParent": "17", + "ExpectedReds": [ + "6", + "9" + ], + "ExpectedBlues": [ + "17", + "12" + ], + "Parents": [ + "12", + "17" + ] + }, + { + "ID": "24", + "ExpectedScore": 13, + "ExpectedSelectedParent": "23", + "ExpectedReds": [ + "7", + "11", + "16" + ], + "ExpectedBlues": [ + "23", + "20" + ], + "Parents": [ + "20", + "23" + ] + }, + { + "ID": "25555", + "ExpectedScore": 13, + "ExpectedSelectedParent": "21", + "ExpectedReds": [], + "ExpectedBlues": [ + "21" + ], + "Parents": [ + "21" + ] + }, + { + "ID": "26", + "ExpectedScore": 15, + "ExpectedSelectedParent": "25555", + "ExpectedReds": [ + "12", + "15", + "19", + "23", + "24" + ], + "ExpectedBlues": [ + "25555", + "22" + ], + "Parents": [ + "22", + "24", + "25555" + ] + }, + { + "ID": "27", + "ExpectedScore": 9, + "ExpectedSelectedParent": "16", + "ExpectedReds": [], + "ExpectedBlues": [ + "16" + ], + "Parents": [ + "16" + ] + }, + { + "ID": "28", + "ExpectedScore": 14, + "ExpectedSelectedParent": "25555", + "ExpectedReds": [ + "12", + "23" + ], + "ExpectedBlues": [ + "25555" + ], + "Parents": [ + "23", + "25555" + ] + }, + { + "ID": "29", + "ExpectedScore": 17, + "ExpectedSelectedParent": "26", + "ExpectedReds": [], + "ExpectedBlues": [ + "26", + "28" + ], + "Parents": [ + "26", + "28" + ] + }, + { + "ID": "30", + "ExpectedScore": 10, + "ExpectedSelectedParent": "27", + "ExpectedReds": [], + "ExpectedBlues": [ + "27" + ], + "Parents": [ + "27" + ] + } + ] +} diff --git a/domain/consensus/testdata/dags/dag2.json b/domain/consensus/testdata/dags/dag2.json new file mode 100644 index 000000000..1be478e7f --- /dev/null +++ b/domain/consensus/testdata/dags/dag2.json @@ -0,0 +1,126 @@ +{ + "K": 18, + "GenesisID": "786", + "ExpectedReds": [], + "Blocks": [ + { + "ID": "21d", + "ExpectedScore": 1, + "ExpectedSelectedParent": "786", + "ExpectedReds": [], + "ExpectedBlues": [ + "786" + ], + "Parents": [ + "786" + ] + }, + { + "ID": "6ef", + "ExpectedScore": 2, + "ExpectedSelectedParent": "21d", + "ExpectedReds": [], + "ExpectedBlues": [ + "21d" + ], + "Parents": [ + "21d" + ] + }, + { + "ID": "c98", + "ExpectedScore": 3, + "ExpectedSelectedParent": "6ef", + "ExpectedReds": [], + "ExpectedBlues": [ + "6ef" + ], + "Parents": [ + "6ef" + ] + }, + { + "ID": "d1c", + "ExpectedScore": 1, + "ExpectedSelectedParent": "786", + "ExpectedReds": [], + "ExpectedBlues": [ + "786" + ], + "Parents": [ + "786" + ] + }, + { + "ID": "ec9", + "ExpectedScore": 5, + "ExpectedSelectedParent": "c98", + "ExpectedReds": [], + "ExpectedBlues": [ + "c98", + "d1c" + ], + "Parents": [ + "d1c", + "c98" + ] + }, + { + "ID": "f154", + "ExpectedScore": 1, + "ExpectedSelectedParent": "786", + "ExpectedReds": [], + "ExpectedBlues": [ + "786" + ], + "Parents": [ + "786" + ] + }, + { + "ID": "6c7", + "ExpectedScore": 4, + "ExpectedSelectedParent": "f154", + "ExpectedReds": [], + "ExpectedBlues": [ + "f154", + "d1c", + "21d" + ], + "Parents": [ + "d1c", + "21d", + "f154" + ] + }, + { + "ID": "015", + "ExpectedScore": 8, + "ExpectedSelectedParent": "ec9", + "ExpectedReds": [], + "ExpectedBlues": [ + "ec9", + "f154", + "6c7" + ], + "Parents": [ + "ec9", + "6c7" + ] + }, + { + "ID": "crash", + "ExpectedScore": 6, + "ExpectedSelectedParent": "6c7", + "ExpectedReds": [], + "ExpectedBlues": [ + "6c7", + "6ef" + ], + "Parents": [ + "6ef", + "6c7" + ] + } + ] +}