├── 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 | 
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* : [Click to Run](https://play.golang.org/p/n8ckn-8X0CA). 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 |
--------------------------------------------------------------------------------