├── README.md ├── calc_k_test.go ├── figure2_test.go ├── figure3_test.go ├── figure4_test.go ├── godag ├── CalcBlue.go ├── Order.go └── bench_test.go ├── main.go ├── pics ├── Fig.3.png └── Fig.4.jpg └── utils └── ChainSim.go /README.md: -------------------------------------------------------------------------------- 1 | # GoDAG 2 | BlockDAG algorithms Go Lang simulation. 3 | 4 | BlockChain (for example Bitcoin, Etherum, etc.) is just a 'k=0' special subtype of BlockDAG, that's why they suffer from the highly restrictive throughput. DAG is the future! 5 | 6 | Thanks **Yonatan Sompolinsky** and **Aviv Zohar** for the most important contributions on blockDAG research, and their great paper "PHANTOM: A Scalable BlockDAG protocol" on [International Association for Cryptologic Research (IACR)](https://eprint.iacr.org/2018/104.pdf) in Feb. 2018. 7 | 8 | They setup a start-up company to develop BlockDAG since Q4 2017, their website: [https://www.daglabs.com]. And there's an official DAGlabs slack channel: [https://daglabs.slack.com]. 9 | 10 | --- 11 | 12 | # How to build this simulation 13 | 14 | Here is my Go Lang environment, the other Go version such as 1.9 should also be OK. 15 | ```bash 16 | $ go version 17 | go version go1.10.1 darwin/amd64 18 | ``` 19 | To run the simulation for the example on the paper page 7 Fig.3 or page 16 Fig.4, for algorithm 1 **Selection of a blue set**. 20 | 21 | ```bash 22 | $ go test -run=Fig3 23 | $ go test -run=Fig4 24 | ``` 25 | 26 | To add a new example DAG to see the DAG blue selection behaviour, it's quite easy. For example, to test a DAG in this figure 'Fig.4', just add a piece of codes like this: 27 | ![Fig.4](https://github.com/garyyu/go-dag/blob/master/pics/Fig.4.jpg) 28 | 29 | ```golang 30 | ChainAddBlock("Genesis", []string{}, chain) 31 | 32 | ChainAddBlock("B", []string{"Genesis"}, chain) 33 | ChainAddBlock("C", []string{"Genesis"}, chain) 34 | ChainAddBlock("D", []string{"Genesis"}, chain) 35 | ChainAddBlock("E", []string{"Genesis"}, chain) 36 | 37 | ChainAddBlock("F", []string{"B","C"}, chain) 38 | ChainAddBlock("I", []string{"C","D"}, chain) 39 | ChainAddBlock("H", []string{"E"}, chain) 40 | 41 | ChainAddBlock("J", []string{"F","D"}, chain) 42 | ChainAddBlock("L", []string{"F"}, chain) 43 | ChainAddBlock("K", []string{"J","I","E"}, chain) 44 | ChainAddBlock("N", []string{"D","H"}, chain) 45 | 46 | ChainAddBlock("M", []string{"L","K"}, chain) 47 | ChainAddBlock("O", []string{"K"}, chain) 48 | ChainAddBlock("P", []string{"K"}, chain) 49 | ChainAddBlock("Q", []string{"N"}, chain) 50 | 51 | ChainAddBlock("R", []string{"O","P","N"}, chain) 52 | 53 | ChainAddBlock("S", []string{"Q"}, chain) 54 | ChainAddBlock("T", []string{"S"}, chain) 55 | ChainAddBlock("U", []string{"T"}, chain) 56 | ``` 57 | 58 | # Click to Run the demo 59 | 60 | And *Run it in Go Playground* : ![#c5f015](https://placehold.it/15/c5f015/000000?text=+)[Click to Run](https://play.golang.org/p/n8ckn-8X0CA)![#c5f015](https://placehold.it/15/c5f015/000000?text=+). You can **edit** your blockDAG example and **run** the 'blue selection algorithm' online! 61 | The output will be like this: 62 | ```console 63 | - BlockDAG Algorithms Simulation - Algorithm 1: Selection of a blue set. - 64 | chainInitialize(): done. blocks= 20 65 | blue set selection done. blue blocks = (G).B.C.D.F.I.J.K.O.P.M.R.(V). total blue: 13 66 | ``` 67 | # Other Tests 68 | 69 | To run the simulation for the example on page 3 Fig.2, page 8 "C. Step #2", for algorithm 2 **Ordering of the DAG**. 70 | 71 | ```bash 72 | $ go test -run=Fig2 73 | ``` 74 | 75 | To run the benchmark test: 76 | 77 | ```bash 78 | $ go test ./phantom -bench=Blocks -benchmem 79 | 80 | $ go test ./ -test.bench BlueSelection -benchmem -run=^$ 81 | 82 | $ go test ./ -test.bench BlockOrdering -benchmem -run=^$ 83 | ``` 84 | # Engagement 85 | 86 | Please join us the BlockDAG discussion on [https://godag.github.io](https://godag.github.io). 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /calc_k_test.go: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2018 The godag Authors 3 | // This file is part of the godag library. 4 | // 5 | // The godag library is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Lesser General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // The godag library is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with the godag library. If not, see . 17 | 18 | package main 19 | 20 | import ( 21 | "testing" 22 | "math" 23 | "math/big" 24 | ) 25 | // Calculate K according to formula: k(D_{max},δ). For fraction λ 26 | // 27 | func TestCalK(t *testing.T) { 28 | 29 | Dmax := 15.0 30 | Lambdaλ := 0.2 // 0.1 ~ 0.9 31 | 32 | targetδ := big.NewFloat(0.001) 33 | 34 | t1 := 2.0 * Dmax * Lambdaλ 35 | t2 := math.Exp(float64(-t1)) 36 | t3 := new(big.Float).Quo(big.NewFloat(t2), big.NewFloat(1.0-t2)) 37 | 38 | println("Dmax=", Dmax, " λ=", Lambdaλ, " δ=", targetδ.String()) 39 | println("t1='2*Dmax*λ'=", t1, " t2=exp(-2 * Dmax * λ)=", t2, " t3=t2/(1-t2)=", t3.String()) 40 | println("") 41 | 42 | if t1-float64(int64(t1)) > 0 { 43 | t.Errorf("error! '2*Dmax*λ' must be an integer (easy to calculate). but now it's %f", t1) 44 | return 45 | } 46 | 47 | for k := 0; k <= 50; k++ { 48 | sum := big.NewInt(0) 49 | for j := k + 1; j <= k+1000; j++ { // instead of using ∞, we only loop 1000 to get rough sum. 50 | 51 | numerator := new(big.Int).Exp(big.NewInt(int64(t1)), big.NewInt(int64(j)), nil) 52 | jFactorial := new(big.Int).MulRange(1, int64(j)) // calculate j! 53 | 54 | t4 := new(big.Int).Div(numerator.Lsh(numerator, 32), jFactorial) 55 | sum = sum.Add(sum, t4) 56 | if t4.Cmp(big.NewInt(0)) == 0 { 57 | //println("k'=",k, " break at j=", j, " (2*Dmax*λ)^j=", numerator.String(), " (j!)=", jFactorial.String(), " t4=(2*Dmax*λ)^j/(j!)=", t4.String(), " sum=", sum.String()) 58 | break 59 | } 60 | } 61 | 62 | sum.Rsh(sum, 32) 63 | 64 | sumFloat := new(big.Float) 65 | sumFloat.SetInt(sum) 66 | sumFloat.Mul(sumFloat, t3) 67 | 68 | result := sumFloat.Cmp(targetδ) 69 | // -1 if x < y 70 | if result < 0 { 71 | println("if k'=", k, " probability=", sumFloat.String(), " found it! this is the minimum k'.") 72 | break 73 | }else { 74 | println("if k'=", k, " probability=", sumFloat.String(), " sum=", sum.String()) 75 | } 76 | } 77 | } 78 | 79 | 80 | // Calculate K according to formula: k(D_{max},δ). For integer λ 81 | // 82 | func TestCalKBigRate(t *testing.T) { 83 | 84 | Dmax := 15.0 85 | Lambdaλ := 1.0 86 | 87 | targetδ := big.NewFloat(0.01 ) 88 | 89 | t1 := 2.0 * Dmax * Lambdaλ 90 | t2 := math.Exp( float64(-t1) ) 91 | t3 := new(big.Float).Quo(big.NewFloat(1.0-t2), big.NewFloat(t2*10000.0)) 92 | 93 | println("Dmax=", Dmax, " λ=", Lambdaλ, " δ=", targetδ.String()) 94 | println("t1='2*Dmax*λ'=", t1, " t2=exp(-2 * Dmax * λ)=", t2, " t3=(1-t2)/t2=", t3.String()) 95 | println("") 96 | 97 | targetδ.Mul(targetδ, big.NewFloat(10000.0)) 98 | 99 | if Lambdaλ < 1.0 { 100 | t.Errorf("error! λ must be integer (use 'TestCalK' for λ<1.0 ). but now it's %f", Lambdaλ) 101 | return 102 | } 103 | 104 | if t1 - float64(int64(t1)) > 0 { 105 | t.Errorf("error! '2*Dmax*λ' must be an integer (easy to calculate). but now it's %f", t1) 106 | return 107 | } 108 | 109 | t3BigInt := new(big.Int) 110 | t3.Int(t3BigInt) 111 | 112 | for k:=0; k<=1000; k++ { 113 | sum := big.NewInt(0) 114 | for j := k+1; j<=k+1000; j++ { // instead of using ∞, we only loop 1000 to get rough sum. 115 | 116 | nominator := new(big.Int).Exp(big.NewInt(int64(t1)), big.NewInt(int64(j)), nil) 117 | jFactorial := new(big.Int).MulRange(1, int64(j)) // calculate (j!) 118 | 119 | t4 := new(big.Int).Div(nominator, jFactorial) 120 | sum = sum.Add(sum, t4) 121 | if t4.Cmp(big.NewInt(0)) == 0 { 122 | //println("k=",k, " j=", j, " (2*Dmax*λ)^j=", nominator.String(), " (j!)=", jFactorial.String(), " t4=", t4.String(), " sum=", sum.String()) 123 | break 124 | } 125 | } 126 | 127 | dv := new(big.Int).Div(sum, t3BigInt) 128 | targetδInt := new(big.Int) 129 | targetδ.Int(targetδInt) 130 | result := dv.Cmp(targetδInt) 131 | if result <= 0 { 132 | println("if k'=", k, " probability=", dv.String(), "/10000. found it! this is the minimum k'.", " sum=", sum.String()) 133 | break 134 | }else{ 135 | println("if k'=", k, " probability=", dv.String(), "/10000. sum=", sum.String()) 136 | } 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /figure2_test.go: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2018 The godag Authors 3 | // This file is part of the godag library. 4 | // 5 | // The godag library is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Lesser General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // The godag library is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with the godag library. If not, see . 17 | 18 | package main 19 | 20 | import ( 21 | "fmt" 22 | ."github.com/garyyu/go-dag/godag" 23 | ."github.com/garyyu/go-dag/utils" 24 | "testing" 25 | "bytes" 26 | ) 27 | 28 | 29 | func chainFig2Initialize() map[string]*Block{ 30 | 31 | //initial an empty chain 32 | chain := make(map[string]*Block) 33 | 34 | //add blocks 35 | 36 | ChainAddBlock("Genesis", []string{}, chain) 37 | 38 | ChainAddBlock("B", []string{"Genesis"}, chain) 39 | ChainAddBlock("C", []string{"Genesis"}, chain) 40 | ChainAddBlock("D", []string{"Genesis"}, chain) 41 | ChainAddBlock("E", []string{"Genesis"}, chain) 42 | 43 | ChainAddBlock("F", []string{"B","C"}, chain) 44 | ChainAddBlock("G", []string{"C","D"}, chain) 45 | ChainAddBlock("H", []string{"E"}, chain) 46 | 47 | ChainAddBlock("I", []string{"F","D"}, chain) 48 | ChainAddBlock("J", []string{"B","G","E"}, chain) 49 | ChainAddBlock("K", []string{"D","H"}, chain) 50 | 51 | tips := FindTips(chain) 52 | tipsName := LTPQ(tips, true) // LTPQ is not relevant here, I just use it to get Tips name. 53 | ChainAddBlock("Virtual", tipsName, chain) 54 | 55 | return chain 56 | } 57 | 58 | 59 | 60 | // Tests Algorithm 2 Ordering of the DAG, with the example on paper page 3 Fig.2 61 | // 62 | func TestFig2(t *testing.T) { 63 | 64 | var actual bytes.Buffer 65 | var expected string 66 | 67 | fmt.Println("\n- BlockDAG Algorithms Simulation - Algorithm 2: Ordering of the DAG. -") 68 | fmt.Println("- The example on Fig.2 -") 69 | 70 | chain := chainFig2Initialize() 71 | 72 | fmt.Println("chainInitialize(): done. blocks=", len(chain)-1) 73 | 74 | orderedList := Order(chain, 3) 75 | 76 | // print the result of blue sets 77 | 78 | ltpq := LTPQ(chain, true) 79 | 80 | fmt.Print("blue set selection done. blue blocks = ") 81 | nBlueBlocks := 0 82 | actual.Reset() 83 | for _, name := range ltpq { 84 | block := chain[name] 85 | if IsBlueBlock(block)==true { 86 | if name=="Genesis" || name=="Virtual" { 87 | actual.WriteString(fmt.Sprintf("(%s).",name[:1])) 88 | }else { 89 | actual.WriteString(name+".") 90 | } 91 | 92 | nBlueBlocks++ 93 | } 94 | } 95 | fmt.Println(actual.String(), " total blue:", nBlueBlocks) 96 | 97 | expected = "(G).B.C.D.F.G.I.J.(V)." 98 | if actual.String() != expected { 99 | t.Errorf("blue selection test not matched. expected=%s", expected) 100 | } 101 | 102 | // print the result of ordered blocks of this chain 103 | 104 | fmt.Print("ordered chain blocks = ") 105 | actual.Reset() 106 | for _, name := range orderedList { 107 | if name=="Genesis" || name=="Virtual" { 108 | actual.WriteString(fmt.Sprintf("(%s).",name[:1])) 109 | } else { 110 | actual.WriteString(name+".") 111 | } 112 | } 113 | fmt.Println(actual.String()) 114 | 115 | expected = "(G).B.C.D.F.E.G.J.I.H.K.(V)." 116 | if actual.String() != expected { 117 | t.Errorf("block ordering test not matched. expected=%s", expected) 118 | } 119 | } 120 | 121 | 122 | func BenchmarkBlockOrdering(b *testing.B) { 123 | 124 | for i := 0; i < b.N; i++ { 125 | chain := chainFig2Initialize() 126 | Order(chain, 3) 127 | } 128 | } -------------------------------------------------------------------------------- /figure3_test.go: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2018 The godag Authors 3 | // This file is part of the godag library. 4 | // 5 | // The godag library is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Lesser General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // The godag library is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with the godag library. If not, see . 17 | 18 | package main 19 | 20 | import ( 21 | "fmt" 22 | ."github.com/garyyu/go-dag/godag" 23 | ."github.com/garyyu/go-dag/utils" 24 | "testing" 25 | "bytes" 26 | ) 27 | 28 | 29 | func chainFig3Initialize() map[string]*Block{ 30 | 31 | //initial an empty chain 32 | chain := make(map[string]*Block) 33 | 34 | //add blocks 35 | 36 | ChainAddBlock("Genesis", []string{}, chain) 37 | 38 | ChainAddBlock("B", []string{"Genesis"}, chain) 39 | ChainAddBlock("C", []string{"Genesis"}, chain) 40 | ChainAddBlock("D", []string{"Genesis"}, chain) 41 | ChainAddBlock("E", []string{"Genesis"}, chain) 42 | 43 | ChainAddBlock("F", []string{"B","C"}, chain) 44 | ChainAddBlock("H", []string{"C","D","E"}, chain) 45 | ChainAddBlock("I", []string{"E"}, chain) 46 | 47 | ChainAddBlock("J", []string{"F","H"}, chain) 48 | ChainAddBlock("K", []string{"B","H","I"}, chain) 49 | ChainAddBlock("L", []string{"D","I"}, chain) 50 | ChainAddBlock("M", []string{"F","K"}, chain) 51 | 52 | tips := FindTips(chain) 53 | tipsName := LTPQ(tips, true) // LTPQ is not relevant here, I just use it to get Tips name. 54 | ChainAddBlock("Virtual", tipsName, chain) 55 | 56 | return chain 57 | } 58 | 59 | 60 | // Tests Algorithm 1 Selection of a blue set, with the example on paper page 7 Fig.3 61 | // 62 | func TestFig3(t *testing.T) { 63 | 64 | var actual bytes.Buffer 65 | var expected string 66 | 67 | fmt.Println("\n- BlockDAG Algorithms Simulation - Algorithm 1: Selection of a blue set. -") 68 | fmt.Println("- The example on Fig.3. -") 69 | 70 | chain := chainFig3Initialize() 71 | 72 | fmt.Println("chainInitialize(): done. blocks=", len(chain)-1) 73 | 74 | CalcBlue(chain, 3, chain["Virtual"]) 75 | 76 | // print the result of blue sets 77 | 78 | ltpq := LTPQ(chain, true) 79 | 80 | fmt.Print("blue set selection done. blue blocks = ") 81 | nBlueBlocks := 0 82 | actual.Reset() 83 | for _, name := range ltpq { 84 | block := chain[name] 85 | if IsBlueBlock(block)==true { 86 | if name=="Genesis" || name=="Virtual" { 87 | actual.WriteString(fmt.Sprintf("(%s).",name[:1])) 88 | }else { 89 | actual.WriteString(name+".") 90 | } 91 | 92 | nBlueBlocks++ 93 | } 94 | } 95 | fmt.Println(actual.String(), " total blue:", nBlueBlocks) 96 | 97 | expected = "(G).C.D.E.I.H.J.K.M.(V)." 98 | if actual.String() != expected { 99 | t.Errorf("blue selection test not matched. expected=%s", expected) 100 | } 101 | } 102 | 103 | func BenchmarkBlueSelection(b *testing.B) { 104 | 105 | for i := 0; i < b.N; i++ { 106 | chain := chainFig3Initialize() 107 | CalcBlue(chain, 3, chain["Virtual"]) 108 | } 109 | } 110 | 111 | -------------------------------------------------------------------------------- /figure4_test.go: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2018 The godag Authors 3 | // This file is part of the godag library. 4 | // 5 | // The godag library is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Lesser General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // The godag library is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with the godag library. If not, see . 17 | 18 | package main 19 | 20 | import ( 21 | "fmt" 22 | ."github.com/garyyu/go-dag/godag" 23 | ."github.com/garyyu/go-dag/utils" 24 | "testing" 25 | "bytes" 26 | ) 27 | 28 | 29 | func chainFig4Initialize() map[string]*Block{ 30 | 31 | //initial an empty chain 32 | chain := make(map[string]*Block) 33 | 34 | //add blocks 35 | 36 | ChainAddBlock("Genesis", []string{}, chain) 37 | 38 | ChainAddBlock("B", []string{"Genesis"}, chain) 39 | ChainAddBlock("C", []string{"Genesis"}, chain) 40 | ChainAddBlock("D", []string{"Genesis"}, chain) 41 | ChainAddBlock("E", []string{"Genesis"}, chain) 42 | 43 | ChainAddBlock("F", []string{"B","C"}, chain) 44 | ChainAddBlock("I", []string{"C","D"}, chain) 45 | ChainAddBlock("H", []string{"E"}, chain) 46 | 47 | ChainAddBlock("J", []string{"F","D"}, chain) 48 | ChainAddBlock("L", []string{"F"}, chain) 49 | ChainAddBlock("K", []string{"J","I","E"}, chain) 50 | ChainAddBlock("N", []string{"D","H"}, chain) 51 | 52 | ChainAddBlock("M", []string{"L","K"}, chain) 53 | ChainAddBlock("O", []string{"K"}, chain) 54 | ChainAddBlock("P", []string{"K"}, chain) 55 | ChainAddBlock("Q", []string{"N"}, chain) 56 | 57 | ChainAddBlock("R", []string{"O","P","N"}, chain) 58 | 59 | ChainAddBlock("S", []string{"Q"}, chain) 60 | ChainAddBlock("T", []string{"S"}, chain) 61 | ChainAddBlock("U", []string{"T"}, chain) 62 | 63 | tips := FindTips(chain) 64 | tipsName := LTPQ(tips, true) // LTPQ is not relevant here, I just use it to get Tips name. 65 | ChainAddBlock("Virtual", tipsName, chain) 66 | 67 | return chain 68 | } 69 | 70 | 71 | // Tests Algorithm 1 Selection of a blue set, with the example on paper page 16 Fig.4 72 | // 73 | func TestFig4(t *testing.T) { 74 | 75 | var actual bytes.Buffer 76 | var expected string 77 | 78 | fmt.Println("\n- BlockDAG Algorithms Simulation - Algorithm 1: Selection of a blue set. -") 79 | fmt.Println("- The example on Fig.4. -") 80 | 81 | chain := chainFig4Initialize() 82 | 83 | fmt.Println("chainInitialize(): done. blocks=", len(chain)-1) 84 | 85 | //CalcBlue(chain, 3, chain["Virtual"]) 86 | orderedList := Order(chain, 3) 87 | 88 | // print the result of blue sets 89 | 90 | ltpq := LTPQ(chain, true) 91 | 92 | fmt.Print("blue set selection done. blue blocks = ") 93 | nBlueBlocks := 0 94 | actual.Reset() 95 | for _, name := range ltpq { 96 | block := chain[name] 97 | if IsBlueBlock(block)==true { 98 | if name=="Genesis" || name=="Virtual" { 99 | actual.WriteString(fmt.Sprintf("(%s).",name[:1])) 100 | }else { 101 | actual.WriteString(name+".") 102 | } 103 | 104 | nBlueBlocks++ 105 | } 106 | } 107 | fmt.Println(actual.String(), " total blue:", nBlueBlocks) 108 | 109 | expected = "(G).B.C.D.F.I.J.K.O.P.M.R.(V)." 110 | if actual.String() != expected { 111 | t.Errorf("blue selection test not matched. expected=%s", expected) 112 | } 113 | 114 | // print the result of ordered blocks of this chain 115 | 116 | fmt.Print("ordered chain blocks = ") 117 | actual.Reset() 118 | for _, name := range orderedList { 119 | if name=="Genesis" || name=="Virtual" { 120 | actual.WriteString(fmt.Sprintf("(%s).",name[:1])) 121 | } else { 122 | actual.WriteString(name+".") 123 | } 124 | } 125 | fmt.Println(actual.String()) 126 | 127 | expected = "(G).B.C.D.F.I.J.E.K.O.P.L.M.H.N.R.Q.S.T.U.(V)." 128 | if actual.String() != expected { 129 | t.Errorf("block ordering test not matched. expected=%s", expected) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /godag/CalcBlue.go: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2018 The godag Authors 3 | // This file is part of the godag library. 4 | // 5 | // The godag library is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Lesser General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // The godag library is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with the godag library. If not, see . 17 | 18 | package godag 19 | 20 | import ( 21 | "fmt" 22 | "os" 23 | "sort" 24 | ) 25 | 26 | type Block struct { 27 | Name string // Name used for simulation. In reality, block header hash can be used as the 'Name' of a block. 28 | Score int 29 | SizeOfPastSet int // Size of its past set (blocks). It's fixed number once a block is created, and can't change anymore. 30 | Prev map[string]*Block 31 | Next map[string]*Block // Block don't have this info, it comes from the analysis of existing chain 32 | Blue map[string]bool // Blue is relative to each Tip 33 | } 34 | 35 | /* 36 | * if a block's Next is not in G, then it's a Tip 37 | */ 38 | func FindTips(G map[string]*Block) map[string]*Block { 39 | 40 | tips := make(map[string]*Block) 41 | 42 | checkNextBlock: 43 | for k, v := range G { 44 | 45 | if len(v.Next)==0 { 46 | tips[k] = v 47 | }else{ 48 | for next := range v.Next { 49 | if _,ok := G[next]; ok { 50 | continue checkNextBlock 51 | } 52 | } 53 | 54 | tips[v.Name] = v 55 | } 56 | } 57 | 58 | return tips 59 | } 60 | 61 | func pastSet(B *Block, past map[string]*Block){ 62 | 63 | for k, v := range B.Prev { 64 | 65 | if _,ok := past[k]; !ok { 66 | pastSet(v, past) 67 | } 68 | past[k] = v 69 | } 70 | } 71 | 72 | func futureSet(B *Block, future map[string]*Block){ 73 | 74 | for k, v := range B.Next { 75 | 76 | if _,ok := future[k]; !ok { 77 | futureSet(v, future) 78 | } 79 | future[k] = v 80 | } 81 | } 82 | 83 | 84 | func countBlue(G map[string]*Block, tip *Block) int{ 85 | 86 | var blueBlocks = 0 87 | for _, v := range G { 88 | if blue, ok := v.Blue[tip.Name]; ok { 89 | if blue { 90 | blueBlocks++ 91 | } 92 | } else if v.Name=="Genesis"{ 93 | blueBlocks++ 94 | } 95 | 96 | } 97 | 98 | return blueBlocks 99 | } 100 | 101 | func antiCone(G map[string]*Block, B *Block) map[string]*Block{ 102 | 103 | anticone := make(map[string]*Block) 104 | 105 | past := make(map[string]*Block) 106 | pastSet(B, past) 107 | 108 | future := make(map[string]*Block) 109 | futureSet(B, future) 110 | 111 | for name, block := range G { 112 | if _,ok := past[name]; ok { 113 | continue // block not in B's past 114 | } 115 | 116 | if _,ok := future[name]; ok { 117 | continue // block not in B's future 118 | } 119 | 120 | if name==B.Name { 121 | continue // block not B 122 | } 123 | 124 | anticone[name] = block // then this block belongs to anticone 125 | } 126 | 127 | return anticone 128 | } 129 | 130 | func IsBlueBlock(B *Block) bool { 131 | 132 | if B==nil { 133 | return false 134 | } 135 | 136 | if B.Name=="Genesis" { 137 | return true 138 | } 139 | 140 | for _,blue := range B.Blue { 141 | if blue==true { 142 | return true 143 | } 144 | } 145 | 146 | return false 147 | } 148 | 149 | func SizeOfPastSet(B *Block) int{ 150 | 151 | past := make(map[string]*Block) 152 | pastSet(B, past) 153 | return len(past) 154 | } 155 | 156 | /* 157 | * lexicographical topological priority queue 158 | */ 159 | func LTPQ(G map[string]*Block, ascending bool) []string{ 160 | 161 | ltpq := make([]struct { 162 | Name string 163 | SizeOfPastSet int 164 | }, len(G)) 165 | 166 | i:=0 167 | for _, block := range G { 168 | ltpq[i].Name, ltpq[i].SizeOfPastSet = block.Name, block.SizeOfPastSet 169 | i++ 170 | } 171 | 172 | /* 173 | * The priority of a block is represented by the size of its past set; in case of ties, the block with lowest hash ID is chosen. 174 | */ 175 | sort.Slice(ltpq, func(i, j int) bool { 176 | return ltpq[i].SizeOfPastSet < ltpq[j].SizeOfPastSet || (ltpq[i].SizeOfPastSet == ltpq[j].SizeOfPastSet && ltpq[i].Name < ltpq[j].Name) 177 | }) 178 | 179 | priorityQue := make([]string, len(ltpq)) 180 | 181 | if ascending==true { // asc: little first 182 | for i := 0; i < len(ltpq); i++ { 183 | priorityQue[i] = ltpq[i].Name 184 | } 185 | }else{ // desc: big first 186 | for i,j := len(ltpq)-1,0; i >= 0; i,j = i-1,j+1 { 187 | priorityQue[j] = ltpq[i].Name 188 | } 189 | } 190 | 191 | return priorityQue 192 | } 193 | 194 | func CalcBlue(G map[string]*Block, k int, topTip *Block){ 195 | 196 | if len(G)==1 { 197 | if _,ok := G["Genesis"]; ok { 198 | return 199 | }else{ 200 | fmt.Println("CalcBlue(): error! len(G)=1 but not Genesis block") 201 | os.Exit(-1) 202 | } 203 | } else if len(G)==0 { 204 | fmt.Println("CalcBlue(): error! impossible to reach here. len(G)=0") 205 | os.Exit(-1) 206 | } 207 | 208 | // step 4 209 | tips := FindTips(G) 210 | if len(tips)==0 { 211 | fmt.Println("calcBlue(): error! impossible! Tips Empty.") 212 | os.Exit(-1) 213 | } 214 | 215 | maxBlue := -1 216 | var Bmax *Block = nil 217 | 218 | // step 4' Starting from the highest scoring tip (to be continued... for the moment, I use reversed LTPQ.) 219 | var ltpq = LTPQ(tips, false) 220 | 221 | for _, name := range ltpq { 222 | tip := tips[name] // step 4' 223 | 224 | past := make(map[string]*Block) 225 | pastSet(tip, past) 226 | 227 | //fmt.Println("calcBlue(): info of next recursive call - tip=", tip.Name, " past=", len(past)) 228 | 229 | // step 5 230 | CalcBlue(past, k, tip) 231 | 232 | // step 6 233 | blueBlocks := countBlue(past, tip) 234 | if blueBlocks>maxBlue { 235 | maxBlue = blueBlocks 236 | Bmax = tip 237 | } else if blueBlocks==maxBlue { 238 | // tie-breaking 239 | /* 240 | * Important Note: in some cases, the tie-breaking method will decide the final blue selection result! not always converge to maximum k-cluster SubDAG. 241 | * research to be continued. 242 | */ 243 | if tip.Name < Bmax.Name { 244 | Bmax = tip 245 | } 246 | } 247 | } 248 | 249 | if Bmax==nil { 250 | fmt.Println("calcBlue(): error! impossible! Bmax=nil.") 251 | os.Exit(-1) 252 | } 253 | 254 | // step 7 255 | for _, v := range G { 256 | for name, blue := range v.Blue { 257 | for _, tip := range tips { 258 | if blue == true && name != Bmax.Name && name==tip.Name { 259 | v.Blue[name] = false // clear all other tips blue blocks, only keep the Bmax blue ones 260 | } 261 | } 262 | } 263 | } 264 | Bmax.Blue[Bmax.Name] = true // BLUEk(G) = BLUEk(Bmax) U {Bmax} 265 | 266 | // step 8 267 | anticoneBmax := antiCone(G, Bmax) 268 | ltpq = LTPQ(anticoneBmax, true) // in 'some' topological ordering: LTPQ 269 | for _, name := range ltpq { 270 | 271 | B := anticoneBmax[name] 272 | 273 | // step 9 274 | nBlueAnticone := 0 275 | anticone := antiCone(G, B) 276 | for _, block := range anticone { 277 | if IsBlueBlock(block)==true { 278 | nBlueAnticone++ 279 | } 280 | } 281 | 282 | if nBlueAnticone<=k { 283 | B.Blue[Bmax.Name] = true // step 10 284 | } 285 | } 286 | 287 | // additional step: replace Blue[Bmax] with [topTip] 288 | for _, B := range G { 289 | if blue, ok := B.Blue[Bmax.Name]; ok && blue==true { 290 | B.Blue[Bmax.Name] = false 291 | B.Blue[topTip.Name] = true 292 | } 293 | } 294 | 295 | } 296 | -------------------------------------------------------------------------------- /godag/Order.go: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2018 The godag Authors 3 | // This file is part of the godag library. 4 | // 5 | // The g-dag library is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Lesser General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // The godag library is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with the godag library. If not, see . 17 | 18 | package godag 19 | 20 | 21 | func Intersection(A map[string]*Block, B map[string]*Block) map[string]*Block{ 22 | 23 | intersection := make(map[string]*Block) 24 | 25 | for _, blockA := range A { 26 | for _, blockB := range B { 27 | if blockA.Name==blockB.Name { 28 | intersection[blockA.Name] = blockA 29 | } 30 | } 31 | } 32 | 33 | return intersection 34 | } 35 | 36 | 37 | func Order(chain map[string]*Block, k int) []string { 38 | 39 | // step 2 40 | topo_queue := make([]*Block, 0) 41 | map_queue := make(map[string]*Block) // store the blocks already in queue 42 | 43 | // step 3 44 | ordered_list := make([]string, 0) 45 | map_list := make(map[string]int) // store the order of the blocks 46 | var order = 0 47 | 48 | // step 4 49 | CalcBlue(chain, k, chain["Virtual"]) 50 | 51 | // step 5 52 | topo_queue = append(topo_queue, chain["Genesis"]) // FIFO Queue Push 53 | map_queue["Genesis"] = chain["Genesis"] 54 | 55 | // step 6 56 | var B *Block = nil 57 | for len(topo_queue)>0 { 58 | 59 | B = topo_queue[0] // step 7. FIFO Queue Pop 60 | topo_queue = topo_queue[1:] // discard top element of FIFO Queue 61 | 62 | // step 8 63 | ordered_list = append(ordered_list, B.Name) 64 | map_list[B.Name] = order 65 | order ++ 66 | 67 | // step 9 68 | childrenB := B.Next 69 | 70 | // step 9' in 'some' topological ordering: LTPQ 71 | ltpq := LTPQ(childrenB, true) 72 | 73 | for _, name := range ltpq { 74 | C := childrenB[name] // step 9" 75 | 76 | if IsBlueBlock(C)==true { 77 | // step 10 78 | pastC := make(map[string]*Block) 79 | pastSet(C, pastC) 80 | 81 | anticoneB := antiCone(chain, B) 82 | intersection := Intersection(pastC, anticoneB) 83 | 84 | // step 10' in 'some' topological ordering: LTPQ 85 | ltpq2 := LTPQ(intersection, true) 86 | 87 | for _, name := range ltpq2 { 88 | D := intersection[name] // step 10" 89 | 90 | if _,okk := map_list[D.Name]; !okk{ 91 | // step 11 92 | if _,ok3 := map_queue[D.Name]; !ok3 { // the queue should avoid duplicating elements 93 | topo_queue = append(topo_queue, D) // FIFO Queue Push 94 | map_queue[D.Name] = D 95 | } 96 | } 97 | } 98 | 99 | // step 12 100 | if _,ok4 := map_queue[C.Name]; !ok4 { // the queue should avoid duplicating elements 101 | topo_queue = append(topo_queue, C) // FIFO Queue Push 102 | map_queue[C.Name] = C 103 | } 104 | } 105 | } 106 | } 107 | 108 | return ordered_list 109 | } -------------------------------------------------------------------------------- /godag/bench_test.go: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2018 The godag Authors 3 | // This file is part of the godag library. 4 | // 5 | // The godag library is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Lesser General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // The godag library is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with the godag library. If not, see . 17 | 18 | package godag 19 | 20 | import "testing" 21 | 22 | var testBlock *Block = nil 23 | 24 | func BenchmarkBlocks(b *testing.B) { 25 | 26 | 27 | for i := 0; i < b.N; i++ { 28 | testBlock = new(Block) 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2018 The godag Authors 3 | // This file is part of the godag library. 4 | // 5 | // The godag library is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU Lesser General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // The godag library is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU Lesser General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU Lesser General Public License 16 | // along with the godag library. If not, see . 17 | 18 | package main 19 | 20 | import "fmt" 21 | 22 | func main() { 23 | 24 | fmt.Println("[ BlockDAG Algorithm Simulation ]") 25 | 26 | } 27 | -------------------------------------------------------------------------------- /pics/Fig.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garyyu/go-dag/5a03543bee33be72ca336d6159d7d61bcf534e30/pics/Fig.3.png -------------------------------------------------------------------------------- /pics/Fig.4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garyyu/go-dag/5a03543bee33be72ca336d6159d7d61bcf534e30/pics/Fig.4.jpg -------------------------------------------------------------------------------- /utils/ChainSim.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The godag Authors 2 | // This file is part of the godag library. 3 | // 4 | // The godag library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-phantom library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the godag library. If not, see . 16 | 17 | package utils 18 | 19 | import ( 20 | "os" 21 | "fmt" 22 | ."github.com/garyyu/go-dag/godag" 23 | ) 24 | 25 | 26 | func ChainAddBlock(Name string, References []string, chain map[string]*Block) *Block{ 27 | 28 | //create this block 29 | thisBlock := Block{Name, -1, -1,make(map[string]*Block), make(map[string]*Block),make(map[string]bool)} 30 | 31 | //add references 32 | for _, Reference := range References { 33 | prev, ok := chain[Reference] 34 | if ok { 35 | thisBlock.Prev[Reference] = prev 36 | prev.Next[Name] = &thisBlock 37 | }else{ 38 | fmt.Println("chainAddBlock(): error! block reference invalid. block name =", Name, " references=", Reference) 39 | os.Exit(-1) 40 | } 41 | } 42 | 43 | thisBlock.SizeOfPastSet = SizeOfPastSet(&thisBlock) 44 | 45 | //add this block to the chain 46 | chain[Name] = &thisBlock 47 | return &thisBlock 48 | } 49 | --------------------------------------------------------------------------------