[#1126] Use WALK function in tests & cosmetic changes.

This commit is contained in:
tal
2020-11-30 14:08:25 +02:00
parent 5a3da43dd4
commit e17a98b7ba
4 changed files with 374 additions and 536 deletions

View File

@@ -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")
}

View File

@@ -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
}
}

View File

@@ -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")
}

View File

@@ -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")
}