diff --git a/domain/consensus/processes/ghostdag2/ghostdagimpl.go b/domain/consensus/processes/ghostdag2/ghostdagimpl.go index fc4afaf44..44bb7f06b 100644 --- a/domain/consensus/processes/ghostdag2/ghostdagimpl.go +++ b/domain/consensus/processes/ghostdag2/ghostdagimpl.go @@ -13,15 +13,6 @@ type ghostdagHelper struct { dagTopologyManager model.DAGTopologyManager } -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") -} - -// New instantiates a new GHOSTDAGHelper -like a factory func New( databaseContext model.DBReader, dagTopologyManager model.DAGTopologyManager, @@ -37,7 +28,7 @@ func New( } /* --------------------------------------------- */ -//K, GHOSTDAGDataStore + func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error { var maxNum uint64 = 0 var myScore uint64 = 0 @@ -46,7 +37,7 @@ func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error if err != nil { return err } - var selectedParent *externalapi.DomainHash = blockParents[0] + var selectedParent = blockParents[0] for _, parent := range blockParents { blockData, err := gh.dataStore.Get(gh.dbAccess, parent) if err != nil { @@ -63,20 +54,16 @@ func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error } myScore = maxNum - /* Goal: iterate h's past and divide it to : blue, blues, reds. - Notes: - 1. If block A is in B's reds group (for block B that belong to the blue group) it will never be in blue(or blues). - */ - - var blues = make([]*externalapi.DomainHash, 0) - var reds = make([]*externalapi.DomainHash, 0) + /* 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) mergeSetArr, err := gh.findMergeSet(blockParents, selectedParent) if err != nil { return err } - //STOP HERE + err = gh.sortByBlueScore(mergeSetArr) if err != nil { return err @@ -86,45 +73,39 @@ func (gh *ghostdagHelper) GHOSTDAG(blockCandidate *externalapi.DomainHash) error return err } - for _, d := range mergeSetArr { - if *d == *selectedParent { - if !contains(selectedParent, blues) { - blues = append(blues, selectedParent) + for _, mergeSetBlock := range mergeSetArr { + if *mergeSetBlock == *selectedParent { + if !contains(selectedParent, mergeSetBlues) { + mergeSetBlues = append(mergeSetBlues, selectedParent) blueSet = append(blueSet, selectedParent) } continue } - err := gh.divideBlueRed(selectedParent, d, &blues, &reds, &blueSet) + err := gh.divideBlueRed(selectedParent, mergeSetBlock, &mergeSetBlues, &mergeSetReds, &blueSet) if err != nil { return err } } - myScore += uint64(len(blues)) - /* Finial Data: - 1. BlueScore => myScore - 2. blues - 3. reds - 4. selectedParent + myScore += uint64(len(mergeSetBlues)) - */ e := model.BlockGHOSTDAGData{ BlueScore: myScore, SelectedParent: selectedParent, - MergeSetBlues: blues, - MergeSetReds: reds, + MergeSetBlues: mergeSetBlues, + MergeSetReds: mergeSetReds, } gh.dataStore.Stage(blockCandidate, &e) return nil } /* --------isMoreHash(w, selectedParent)----------------*/ -func ismoreHash(w *externalapi.DomainHash, selectedParent *externalapi.DomainHash) bool { - //Check if w is more then selectedParent - for i := len(w) - 1; i >= 0; i-- { +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 w[i] < selectedParent[i]: + case parent[i] < selectedParent[i]: return false - case w[i] > selectedParent[i]: + case parent[i] > selectedParent[i]: return true } } @@ -132,7 +113,7 @@ func ismoreHash(w *externalapi.DomainHash, selectedParent *externalapi.DomainHas } /* 1. blue = selectedParent.blue + blues - 2. h is not connected to at most K blocks (from the blue group) + 2. not connected to at most K blocks (from the blue group) 3. for each block at blue , check if not destroy */ @@ -141,16 +122,16 @@ func (gh *ghostdagHelper) divideBlueRed(selectedParent *externalapi.DomainHash, blues *[]*externalapi.DomainHash, reds *[]*externalapi.DomainHash, blueSet *[]*externalapi.DomainHash) error { var k = int(gh.k) counter := 0 - // check if anticone with desiredBlock. + var suspectsBlues = make([]*externalapi.DomainHash, 0) - var isMergeBlue = true + isMergeBlue := true //check that not-connected to at most k. for _, block := range *blueSet { - isAnt, err := gh.isAnticone(block, desiredBlock) + isAnticone, err := gh.isAnticone(block, desiredBlock) if err != nil { return err } - if isAnt { + if isAnticone { counter++ suspectsBlues = append(suspectsBlues, block) } @@ -192,128 +173,51 @@ func (gh *ghostdagHelper) divideBlueRed(selectedParent *externalapi.DomainHash, return nil } -// OldisAnticone -//var chain = selectedParent -//var stop = false -//for chain != nil { /*nil -> after genesis*/ -// // iterate on the selected parent chain, for each node in the chain i check also for his mergeSet. -// isValid, err := gh.validateKCluster(chain, desiredBlock, &counter, blueSet) -// if err != nil { -// return err -// } -// if !isValid { -// stop = true -// break -// } -// /* Check valid for the blues of the chain */ -// blockData, err := gh.dataStore.Get(gh.dbAccess, chain) -// if err != nil { -// return err -// } -// -// for _, b := range blockData.MergeSetBlues { -// isValid2, err := gh.validateKCluster(b, desiredBlock, &counter, blueSet) -// if err != nil { -// return err -// } -// if !isValid2 { -// stop = true -// break -// } -// } -// -// if stop { -// break -// } -// //chain = gh.dataStore.Get(gh.dbAccess, chain).SelectedParent -// blockData2, err := gh.dataStore.Get(gh.dbAccess, chain) -// if err != nil { -// return err -// } -// chain = blockData2.SelectedParent -//} -//if stop { -// if !contains(desiredBlock, *reds) { -// *reds = append(*reds, desiredBlock) -// } -//} else { -// var isBlue bool = true -// -// for _, e := range *blues { -// isDestroyed, err := gh.checkIfDestroy(e, blues) -// if err != nil { -// return err -// } -// if isDestroyed { -// isBlue = false -// break -// } -// } -// if !isBlue { -// if !contains(desiredBlock, *reds) { -// *reds = append(*reds, desiredBlock) -// } -// } else { -// if !contains(desiredBlock, *blues) { -// *blues = append(*blues, desiredBlock) -// } -// if !contains(desiredBlock, *blueSet) { -// *blueSet = append(*blueSet, desiredBlock) -// } -// } -//} - /* ---------------isAnticone-------------------------- */ -func (gh *ghostdagHelper) isAnticone(h1, h2 *externalapi.DomainHash) (bool, error) { - //return !isInPast(h1, h2) && !isInPast(h1,h2) - //return !gh.dagTopologyManager.IsAncestorOf(h1, h2) && !gh.dagTopologyManager.IsAncestorOf(h2, h1) - isAB, err := gh.dagTopologyManager.IsAncestorOf(h1, h2) +func (gh *ghostdagHelper) isAnticone(blockA, blockB *externalapi.DomainHash) (bool, error) { + isAAncestorOfAB, err := gh.dagTopologyManager.IsAncestorOf(blockA, blockB) if err != nil { return false, err } - isBA, err := gh.dagTopologyManager.IsAncestorOf(h2, h1) + isBAncestorOfA, err := gh.dagTopologyManager.IsAncestorOf(blockB, blockA) if err != nil { return false, err } - return !isAB && !isBA, nil + 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 = int(gh.k) - isAnt, err := gh.isAnticone(chain, checkedBlock) + var k = int(gh.k) + isAnticone, err := gh.isAnticone(chain, checkedBlock) if err != nil { return false, err } - if isAnt { - //if n := gh.isAnticone(chain, checkedBlock); n { + if isAnticone { if *counter > k { return false, nil } - //if gh.checkIfDestroy(chain, blueSet) { - ifDes, err := gh.checkIfDestroy(chain, blueSet) + ifDestroy, err := gh.checkIfDestroy(chain, blueSet) if err != nil { return false, err } - if ifDes { + if ifDestroy { return false, nil } *counter++ return true, nil } - isAnt2, err := gh.dagTopologyManager.IsAncestorOf(checkedBlock, chain) + isAncestorOf, err := gh.dagTopologyManager.IsAncestorOf(checkedBlock, chain) if err != nil { return false, err } - //if gh.dagTopologyManager.IsAncestorOf(checkedBlock, chain) { - if isAnt2 { + if isAncestorOf { dataStore, err := gh.BlockData(chain) if err != nil { return false, err } - if g := dataStore.MergeSetReds; contains(checkedBlock, g) { - //if g := gh.dataStore.Get(gh.dbAccess, chain).MergeSetReds; contains(checkedBlock, g) { + if mergeSetReds := dataStore.MergeSetReds; contains(checkedBlock, mergeSetReds) { return false, nil } } else { @@ -324,9 +228,9 @@ func (gh *ghostdagHelper) validateKCluster(chain *externalapi.DomainHash, checke } /*----------------contains-------------------------- */ -func contains(s *externalapi.DomainHash, g []*externalapi.DomainHash) bool { - for _, r := range g { - if *r == *s { +func contains(item *externalapi.DomainHash, items []*externalapi.DomainHash) bool { + for _, r := range items { + if *r == *item { return true } } @@ -335,17 +239,16 @@ func contains(s *externalapi.DomainHash, g []*externalapi.DomainHash) bool { /* ----------------checkIfDestroy------------------- */ /* find number of not-connected in his blue*/ -func (gh *ghostdagHelper) checkIfDestroy(block_blue *externalapi.DomainHash, blueSet *[]*externalapi.DomainHash) (bool, error) { +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 = int(gh.k) + var k = int(gh.k) counter := 0 for _, blue := range *blueSet { - //if gh.isAnticone(s2, chain) { - isAnt, err := gh.isAnticone(blue, block_blue) + isAnticone, err := gh.isAnticone(blue, blockBlue) if err != nil { return true, err } - if isAnt { + if isAnticone { counter++ } if counter > k { @@ -356,38 +259,36 @@ func (gh *ghostdagHelper) checkIfDestroy(block_blue *externalapi.DomainHash, blu } /* ----------------findMergeSet------------------- */ -func (gh *ghostdagHelper) findMergeSet(h []*externalapi.DomainHash, selectedParent *externalapi.DomainHash) ([]*externalapi.DomainHash, error) { +func (gh *ghostdagHelper) findMergeSet(parents []*externalapi.DomainHash, selectedParent *externalapi.DomainHash) ([]*externalapi.DomainHash, error) { allMergeSet := make([]*externalapi.DomainHash, 0) - var nodeQueue = make([]*externalapi.DomainHash, 0) - for _, g := range h { - if !contains(g, nodeQueue) { - nodeQueue = append(nodeQueue, g) + blockQueue := make([]*externalapi.DomainHash, 0) + for _, parent := range parents { + if !contains(parent, blockQueue) { + blockQueue = append(blockQueue, parent) } } - for len(nodeQueue) > 0 { /*return boolean */ - ha := nodeQueue[0] - nodeQueue = nodeQueue[1:] - if *selectedParent == *ha { - if !contains(ha, allMergeSet) { - allMergeSet = append(allMergeSet, ha) + for len(blockQueue) > 0 { + block := blockQueue[0] + blockQueue = blockQueue[1:] + if *selectedParent == *block { + if !contains(block, allMergeSet) { + allMergeSet = append(allMergeSet, block) } continue } - //if isInPast(ha, selectedParent){ - //if gh.dagTopologyManager.IsAncestorOf(ha, selectedParent) { - isanc, err := gh.dagTopologyManager.IsAncestorOf(ha, selectedParent) + isancestorOf, err := gh.dagTopologyManager.IsAncestorOf(block, selectedParent) if err != nil { return nil, err } - if isanc { + if isancestorOf { continue } - if !contains(ha, allMergeSet) { - allMergeSet = append(allMergeSet, ha) + if !contains(block, allMergeSet) { + allMergeSet = append(allMergeSet, block) } - err = gh.insertParent(ha, &nodeQueue) + err = gh.insertParent(block, &blockQueue) if err != nil { return nil, err } @@ -398,41 +299,38 @@ func (gh *ghostdagHelper) findMergeSet(h []*externalapi.DomainHash, selectedPare /* ----------------insertParent------------------- */ /* Insert all parents to the queue*/ -func (gh *ghostdagHelper) insertParent(h *externalapi.DomainHash, q1 *[]*externalapi.DomainHash) error { - parents, err := gh.dagTopologyManager.Parents(h) +func (gh *ghostdagHelper) insertParent(child *externalapi.DomainHash, queue *[]*externalapi.DomainHash) error { + parents, err := gh.dagTopologyManager.Parents(child) if err != nil { return err } - for _, v := range parents { - if contains(v, *q1) { + for _, parent := range parents { + if contains(parent, *queue) { continue } - *q1 = append(*q1, v) + *queue = append(*queue, parent) } return nil } /* ----------------findBlueSet------------------- */ -func (gh *ghostdagHelper) findBlueSet(blueSet *[]*externalapi.DomainHash, h *externalapi.DomainHash) error { - for h != nil { - if !contains(h, *blueSet) { - *blueSet = append(*blueSet, h) +func (gh *ghostdagHelper) findBlueSet(blueSet *[]*externalapi.DomainHash, selectedParent *externalapi.DomainHash) error { + for selectedParent != nil { + if !contains(selectedParent, *blueSet) { + *blueSet = append(*blueSet, selectedParent) } - //blueSet = append(gh.dataStore.Get(gh.dbAccess, h).MergeSetBlues, blueSet) //change - //for _, v := range gh.dataStore.Get(gh.dbAccess, h).MergeSetBlues { - blockData, err := gh.dataStore.Get(gh.dbAccess, h) + blockData, err := gh.dataStore.Get(gh.dbAccess, selectedParent) if err != nil { return err } mergeSetBlue := blockData.MergeSetBlues - for _, v := range mergeSetBlue { - if contains(v, *blueSet) { + for _, blue := range mergeSetBlue { + if contains(blue, *blueSet) { continue } - *blueSet = append(*blueSet, v) + *blueSet = append(*blueSet, blue) } - //h = gh.dataStore.Get(gh.dbAccess, h).SelectedParent - h = blockData.SelectedParent + selectedParent = blockData.SelectedParent } return nil } @@ -441,7 +339,7 @@ func (gh *ghostdagHelper) findBlueSet(blueSet *[]*externalapi.DomainHash, h *ext func (gh *ghostdagHelper) sortByBlueScore(arr []*externalapi.DomainHash) error { var err error = nil - sort.SliceStable(arr, func(i, j int) bool { + sort.Slice(arr, func(i, j int) bool { blockRight, error := gh.dataStore.Get(gh.dbAccess, arr[i]) if error != nil { @@ -471,5 +369,11 @@ func (gh *ghostdagHelper) sortByBlueScore(arr []*externalapi.DomainHash) error { func (gh *ghostdagHelper) BlockData(blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) { return gh.dataStore.Get(gh.dbAccess, blockHash) - //last +} +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/ghostdag_test.go b/domain/consensus/processes/ghostdag_test.go index 22e849f6b..9ae62d9e8 100644 --- a/domain/consensus/processes/ghostdag_test.go +++ b/domain/consensus/processes/ghostdag_test.go @@ -6,31 +6,10 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2" "github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager" + "reflect" "testing" ) -/*----------------contains-------------------------- */ -func contains(s *externalapi.DomainHash, g []*externalapi.DomainHash) bool { - for _, r := range g { - if *r == *s { - return true - } - } - return false -} - -func DeepEqualHashArrays(runtime, expected []*externalapi.DomainHash) bool { - if len(runtime) != len(expected) { - return false - } - for _, hash := range runtime { - if !contains(hash, expected) { - return false - } - } - return true -} - func TestGHOSTDA(t *testing.T) { type implManager struct { @@ -443,34 +422,22 @@ func TestGHOSTDA(t *testing.T) { t.Fatalf("Test #%d failed: expected selected parent %v but got %v.", testNum+1, testBlockData.expectedSelectedParent, ghostdagData.SelectedParent) } - if !DeepEqualHashArrays(testBlockData.expectedMergeSetBlues, ghostdagData.MergeSetBlues) { + if !reflect.DeepEqual(testBlockData.expectedMergeSetBlues, ghostdagData.MergeSetBlues) { t.Fatalf("Test #%d failed: expected merge set blues %v but got %v.", testNum+1, testBlockData.expectedMergeSetBlues, ghostdagData.MergeSetBlues) } - if !DeepEqualHashArrays(testBlockData.expectedMergeSetReds, ghostdagData.MergeSetReds) { + if !reflect.DeepEqual(testBlockData.expectedMergeSetReds, ghostdagData.MergeSetReds) { t.Fatalf("Test #%d failed: expected merge set reds %v but got %v.", testNum+1, testBlockData.expectedMergeSetReds, ghostdagData.MergeSetReds) } } fmt.Printf(" Test success!\n\n") - //fmt.Printf("Test %d successfully finished. \n", testStruct.testNum+1) - - dagTopology := &DAGTopologyManagerImpl{ - parentsMap: make(map[externalapi.DomainHash][]*externalapi.DomainHash), - } + dagTopology.parentsMap = make(map[externalapi.DomainHash][]*externalapi.DomainHash) dagTopology.parentsMap[*genesisHash] = nil - - ghostdagDataStore := &GHOSTDAGDataStoreImpl{ - dagMap: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData), - } - ghostdagDataStore.dagMap[*genesisHash] = &model.BlockGHOSTDAGData{ - BlueScore: 1, - SelectedParent: nil, - MergeSetBlues: nil, - MergeSetReds: nil, - BluesAnticoneSizes: nil, - } + blockGHOSTDAGDataGenesis := ghostdagDataStore.dagMap[*genesisHash] + ghostdagDataStore.dagMap = make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData) + ghostdagDataStore.dagMap[*genesisHash] = blockGHOSTDAGDataGenesis } } @@ -502,16 +469,14 @@ func (ds *GHOSTDAGDataStoreImpl) Commit(dbTx model.DBTransaction) error { // return nil //} func (ds *GHOSTDAGDataStoreImpl) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) { - v, ok := ds.dagMap[*blockHash] - if ok { - return v, nil + blockData, isExist := ds.dagMap[*blockHash] + if isExist { + return blockData, nil } return nil, nil } -//candidateBluesAnticoneSizes = make(map[externalapi.DomainHash]model.KType, gm.k) type DAGTopologyManagerImpl struct { - //dagMap map[*externalapi.DomainHash] *model.BlockGHOSTDAGData parentsMap map[externalapi.DomainHash][]*externalapi.DomainHash } @@ -525,12 +490,11 @@ func (dt *DAGTopologyManagerImpl) AddTip(tipHash *externalapi.DomainHash) error //Implemented// func (dt *DAGTopologyManagerImpl) Parents(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) { - v, ok := dt.parentsMap[*blockHash] - if !ok { - return make([]*externalapi.DomainHash, 0), nil + blockData, isExist := dt.parentsMap[*blockHash] + if !isExist { + return []*externalapi.DomainHash{}, nil } - - return v, nil + return blockData, nil } func (dt *DAGTopologyManagerImpl) Children(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) { @@ -546,22 +510,24 @@ func (dt *DAGTopologyManagerImpl) IsChildOf(blockHashA *externalapi.DomainHash, } //Implemented// -func (dt *DAGTopologyManagerImpl) IsAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) { - bParents, ok := dt.parentsMap[*blockHashB] - if !ok { +func (dt *DAGTopologyManagerImpl) IsAncestorOf(hashBlockA *externalapi.DomainHash, hashBlockB *externalapi.DomainHash) (bool, error) { + blockBParents, isOk := dt.parentsMap[*hashBlockB] + if !isOk { return false, nil } - for _, parent := range bParents { - if *parent == *blockHashA { + + for _, parentOfB := range blockBParents { + if *parentOfB == *hashBlockA { return true, nil } } - for _, y := range bParents { - isAnc, err := dt.IsAncestorOf(blockHashA, y) + + for _, parentOfB := range blockBParents { + isAncestorOf, err := dt.IsAncestorOf(hashBlockA, parentOfB) if err != nil { return false, err } - if isAnc { + if isAncestorOf { return true, nil } } diff --git a/domain/consensus/processes/ghostdagmanager/ghostdagWithJson_test.go b/domain/consensus/processes/ghostdagmanager/ghostdagWithJson_test.go new file mode 100644 index 000000000..81f53ba3b --- /dev/null +++ b/domain/consensus/processes/ghostdagmanager/ghostdagWithJson_test.go @@ -0,0 +1,269 @@ +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, + } + + 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 + } + + return nil + }) + if err != nil { + t.Fatal(err) + } +} + +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) error { + ds.dagMap[*blockHash] = blockGHOSTDAGData + return nil +} + +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/ghostdagWithJson_test.go b/domain/consensus/testdata/ghostdagWithJson_test.go deleted file mode 100644 index 7e0e3ad16..000000000 --- a/domain/consensus/testdata/ghostdagWithJson_test.go +++ /dev/null @@ -1,301 +0,0 @@ -package testdata - -import ( - "encoding/json" - "fmt" - "github.com/kaspanet/kaspad/domain/consensus/model" - "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" - "github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2" - "github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager" - "github.com/kaspanet/kaspad/domain/dagconfig" - "io/ioutil" - "os" - "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 dagconfig.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 dirName = "./dags" - items, err := ioutil.ReadDir(dirName) - if err != nil { - t.Fatalf("Failed read dir %s: %v.", dirName, err) - } - for _, item := range items { - if item.IsDir() { - fmt.Printf("Please change path above. you are now on: %s./n", item.Name()) - } else { - jsonFile, err := os.Open(dirName + "/" + item.Name()) - if err != nil { - t.Fatalf("Failed opening the json file %s: %v", item.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 //change genesis ID from type string to type []byte - copy(genesisHash[:], test.GenesisID) - - dagTopology.parentsMap[genesisHash] = nil - ghostdagDataStore.dagMap[genesisHash] = &model.BlockGHOSTDAGData{ - BlueScore: 0, - SelectedParent: nil, - MergeSetBlues: nil, - MergeSetReds: nil, - BluesAnticoneSizes: nil, - } - - //NOTE: FOR ADDING/REMOVING AN IMPLEMENTATION CHANGE BELOW: - implementationFactories := []implManager{ - {ghostdagmanager.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 := infoStrToByte(testBlockData.ID) - dagTopology.parentsMap[*blockID] = infoStrToByteArray(testBlockData.Parents) - - err := g.GHOSTDAG(blockID) - if err != nil { - t.Fatalf("Test failed: Impl: %s, error on GHOSTDAG - block %s: %s.", - factory.implName, testBlockData.ID, err) - } - ghostdagData, err := ghostdagDataStore.Get(nil, blockID) - if err != nil { - t.Fatalf("\nTEST FAILED: Impl: %s, \nBlock: %s, \nError: ghostdagDataStore error: %v.", - factory.implName, testBlockData.ID, err) - } - - if testBlockData.Score != (ghostdagData.BlueScore) { - t.Fatalf("\nTEST FAILED: Impl: %s, \nBlock: %s, \nError: expected blue score %d but got %d.", - factory.implName, testBlockData.ID, testBlockData.Score, ghostdagData.BlueScore) - } - - if *infoStrToByte(testBlockData.SelectedParent) != *ghostdagData.SelectedParent { - t.Fatalf("\nTEST FAILED: Impl: %s, \nBlock: %s, \nError: expected selected parent %v but got %v.", - factory.implName, testBlockData.ID, testBlockData.SelectedParent, string(ghostdagData.SelectedParent[:])) - } - - if !DeepEqualHashArrays(infoStrToByteArray(testBlockData.MergeSetBlues), ghostdagData.MergeSetBlues) { - t.Fatalf("\nTEST FAILED: Impl: %s, \nBlock: %s, \nError: expected merge set blues %v but got %v.", - factory.implName, testBlockData.ID, testBlockData.MergeSetBlues, hashToString(ghostdagData.MergeSetBlues)) - } - - if !DeepEqualHashArrays(infoStrToByteArray(testBlockData.MergeSetReds), ghostdagData.MergeSetReds) { - t.Fatalf("\nTEST FAILED: Impl: %s, \nBlock: %s, \nError: expected merge set reds %v but got %v.", - factory.implName, testBlockData.ID, testBlockData.MergeSetReds, hashToString(ghostdagData.MergeSetReds)) - } - - } - - dagTopology := &DAGTopologyManagerImpl{ - parentsMap: make(map[externalapi.DomainHash][]*externalapi.DomainHash), - } - dagTopology.parentsMap[genesisHash] = nil - - ghostdagDataStore := &GHOSTDAGDataStoreImpl{ - dagMap: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData), - } - ghostdagDataStore.dagMap[genesisHash] = &model.BlockGHOSTDAGData{ - BlueScore: 1, - SelectedParent: nil, - MergeSetBlues: nil, - MergeSetReds: nil, - BluesAnticoneSizes: nil, - } - } - } - } - fmt.Printf("All tests passed!\n") -} - -func hashToString(arr []*externalapi.DomainHash) []string { - var strArr = make([]string, len(arr)) - if len(arr) == 0 { - return strArr - } - for i, hash := range arr { - strArr[i] = string(hash[:]) - } - return strArr -} - -func infoStrToByte(strID string) *externalapi.DomainHash { - var domainHash externalapi.DomainHash - copy(domainHash[:], strID) - return &domainHash -} - -func infoStrToByteArray(strIDArr []string) []*externalapi.DomainHash { - var domainHashArr []*externalapi.DomainHash - for _, strID := range strIDArr { - domainHashArr = append(domainHashArr, infoStrToByte(strID)) - } - return domainHashArr -} - -func contains(hash *externalapi.DomainHash, hashArr []*externalapi.DomainHash) bool { - for _, item := range hashArr { - if *item == *hash { - return true - } - } - return false -} - -func DeepEqualHashArrays(runtime, expected []*externalapi.DomainHash) bool { - if len(runtime) != len(expected) { - return false - } - for _, hash := range runtime { - if !contains(hash, expected) { - return false - } - } - return true -} - -/* ---------------------- */ -type GHOSTDAGDataStoreImpl struct { - dagMap map[externalapi.DomainHash]*model.BlockGHOSTDAGData -} - -func (ds *GHOSTDAGDataStoreImpl) Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData *model.BlockGHOSTDAGData) error { - ds.dagMap[*blockHash] = blockGHOSTDAGData - return nil -} - -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 make([]*externalapi.DomainHash, 0), nil - } - - return v, nil -} - -func (dt *DAGTopologyManagerImpl) Children(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) { - panic("unimplemented") -} - -func (dt *DAGTopologyManagerImpl) IsParentOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) { - panic("unimplemented") -} - -func (dt *DAGTopologyManagerImpl) IsChildOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) { - panic("unimplemented") -} - -func (dt *DAGTopologyManagerImpl) IsAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) { - bParents, ok := dt.parentsMap[*blockHashB] - if !ok { - return false, nil - } - for _, parent := range bParents { - if *parent == *blockHashA { - return true, nil - } - } - for _, y := range bParents { - isAnc, err := dt.IsAncestorOf(blockHashA, y) - if err != nil { - return false, err - } - if isAnc { - 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") -}