├── .gitignore ├── Scaling Bitcoin Milan Presentation.pdf ├── ordering ├── README.md ├── graph.go ├── ordering.go ├── addnode.go ├── main.go └── jute_test.go ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.html 3 | *.out 4 | -------------------------------------------------------------------------------- /Scaling Bitcoin Milan Presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Taek42/jute/HEAD/Scaling Bitcoin Milan Presentation.pdf -------------------------------------------------------------------------------- /ordering/README.md: -------------------------------------------------------------------------------- 1 | Jute Ordering 2 | ============= 3 | 4 | This is an intuitive but unoptimized implementation of the Jute ordering 5 | algorithm. Jute is an algorithm for ordering proof-of-work blocks from an 6 | arbitrary DAG into a strict order, such that security against 51% attacks can 7 | be achieved, and mining fairness can be achieved. 8 | 9 | graph.go contains definitions for the basic data structures, addnode.go 10 | contains the code for adding a node to the DAG (including the required voting 11 | step), and ordering.go contains code for taking an existing graph and deriving 12 | the secure ordering according to Jute. 13 | 14 | main.go does not contain any core code, however it does contain code that can 15 | create orderings and produce SageMath code for generating visualizations of the 16 | graphs. 17 | 18 | jute\_test.go contains most of the testing that has been performed. At the 19 | moment, it is somewhat sparse, and therefore the Jute code may contain bugs. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Taek42 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ordering/graph.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | ) 6 | 7 | // nodeName is the string version of a node's numerical counter. 8 | type nodeName string 9 | 10 | // edgeName is the name of an edge connecting two nodes. The name takes the 11 | // form "childName" + "-" + "parentName". 12 | type edgeName string 13 | 14 | // edge returns the name of the edge that is created when the child commits to 15 | // the parent. 16 | func edge(child, parent nodeName) edgeName { 17 | return edgeName(child + "-" + parent) 18 | } 19 | 20 | // GraphNode defines a node in a graph. The algorithm needs to traverse the 21 | // graph both forward and backwards, so parents must point to all children, and 22 | // children must point to all parents. 23 | // 24 | // Each node has the full list of votes for all edges as the were when this 25 | // block was the tip block. 26 | type GraphNode struct { 27 | // These values are inherent to the node and will not change. 28 | // 29 | // The edgeVotes indicates the edges that this node casts a vote for. 30 | name nodeName 31 | edgeVotes []edgeName 32 | parents []*GraphNode 33 | relativeVoteGraph map[edgeName]int 34 | 35 | // The set of children can grow as more nodes are added to the graph. 36 | // Adding a child will never change the relative vote graph, or the voting 37 | // decisions of the parent. 38 | children []*GraphNode 39 | 40 | // Each node knows the graph salt. 41 | salt string 42 | } 43 | 44 | // Graph is the base type that is used to build out a graph of nodes. 45 | type Graph struct { 46 | // nameCounter enables the graphViewer to assign unique names to each node. 47 | nameCounter int 48 | 49 | // genesisNode is the oldest node in the tree. 50 | genesisNode *GraphNode 51 | 52 | // the salt used to make sure that rng decisions are different from 53 | // run-to-run, especially useful during testing. 54 | salt string 55 | } 56 | 57 | // GenesisNode returns the genesis node of the graph. 58 | func (g *Graph) GenesisNode() *GraphNode { 59 | return g.genesisNode 60 | } 61 | 62 | // NewGraph initializes a graph with a genesis node that has no children and 63 | // returns the graph. 64 | func NewGraph() *Graph { 65 | saltBase := make([]byte, 32) 66 | rand.Read(saltBase) 67 | return &Graph{ 68 | nameCounter: 0, 69 | genesisNode: &GraphNode{ 70 | name: "0", 71 | edgeVotes: make([]edgeName, 0), 72 | parents: make([]*GraphNode, 0), 73 | relativeVoteGraph: make(map[edgeName]int), 74 | 75 | children: make([]*GraphNode, 0), 76 | }, 77 | salt: string(saltBase), 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ordering/ordering.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | ) 7 | 8 | // directUnorderedAncestors returns a list of all ancestors of 'tip' that are 9 | // not ordered, yet are direct children of ordered blocks. 10 | func directUnorderedAncestors(ordered map[nodeName]bool, tip *GraphNode) (duas []*GraphNode) { 11 | unvisited := tip.parents 12 | for len(unvisited) != 0 { 13 | // Pop a node off of the unvisited stack. 14 | ancestor := unvisited[0] 15 | unvisited = unvisited[1:] 16 | if ordered[ancestor.name] { 17 | // All ancestors of this node are by definition also ordred, no 18 | // unordered ancestors to be found here. 19 | continue 20 | } 21 | 22 | // Find any edges which are pointing from an ordered node to the 23 | // ancestor. If there is one, this ancestor is a direct unordered 24 | // ancestor. 25 | for _, parent := range ancestor.parents { 26 | if ordered[parent.name] { 27 | duas = append(duas, ancestor) 28 | break 29 | } 30 | } 31 | 32 | // Add all of the ancestor's parents to the unvisted list. 33 | unvisited = append(unvisited, ancestor.parents...) 34 | } 35 | return duas 36 | } 37 | 38 | // nextUnorderedAncestor picks from a list of unordered ancestors the next 39 | // ancestor in the ordering. nextUnorderedAncestor assumes that all input nodes 40 | // are direct children of ordered nodes. 41 | func nextUnorderedAncestor(potentials []*GraphNode, tip *GraphNode) *GraphNode { 42 | // Use the hash of the tip block and the hash of each child to 43 | // deterministically choose a winner from the set of potential winners. 44 | var winningHash [32]byte 45 | var winningNode *GraphNode 46 | for _, potential := range potentials { 47 | pHash := sha256.Sum256([]byte("salt" + tip.name + potential.name)) 48 | if bytes.Compare(winningHash[:], pHash[:]) < 0 { 49 | winningHash = pHash 50 | winningNode = potential 51 | } 52 | } 53 | return winningNode 54 | } 55 | 56 | // relativeOrdering returns the sorted graph from the perspective of the 57 | // calling node. 58 | func (gn *GraphNode) relativeOrdering() []*GraphNode { 59 | // Find the genesis block. 60 | current := gn 61 | for len(current.parents) != 0 { 62 | current = current.parents[0] 63 | } 64 | 65 | // Grab an ordering, grabbing one bock in the primary chain at a time. 66 | var ordering []*GraphNode 67 | ordered := make(map[nodeName]bool) 68 | for { 69 | // Before 'current' can be added to the ordering, all ancestors must be 70 | // added to the ordering. 71 | for duas := directUnorderedAncestors(ordered, current); len(duas) != 0; duas = directUnorderedAncestors(ordered, current) { 72 | winner := nextUnorderedAncestor(duas, current) 73 | ordering = append(ordering, winner) 74 | ordered[winner.name] = true 75 | } 76 | ordering = append(ordering, current) 77 | ordered[current.name] = true 78 | 79 | // If the original node has been added to the graph, the ordering is 80 | // complete. 81 | if current == gn { 82 | break 83 | } 84 | _, current = primaryEdge(current, gn) 85 | } 86 | return ordering 87 | } 88 | -------------------------------------------------------------------------------- /ordering/addnode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | "strconv" 7 | ) 8 | 9 | // primaryEdge uses the vote graph to determine which edge of the provided 10 | // parent is the primary edge. 11 | func primaryEdge(parent *GraphNode, tip *GraphNode) (edgeName, *GraphNode) { 12 | // Other nodes may have added edges to the parent which are not actually 13 | // visible from the tip block. Reduce the set of considered edges to only 14 | // those edges that are visible from the tip block. 15 | var visibleChildren []*GraphNode 16 | for _, child := range parent.children { 17 | e := edge(child.name, parent.name) 18 | if _, exists := tip.relativeVoteGraph[e]; exists { 19 | visibleChildren = append(visibleChildren, child) 20 | } 21 | } 22 | 23 | // The child with the most votes on its edge to the parent wins. If 24 | // multiple children have the winning number of edge votes, select between 25 | // them randomly using the hash of the tip block as a seed. 26 | // 27 | // As this is a simulation, the names of the blocks are used to seed the 28 | // rng in lieu of their hashes. 29 | winningVotes := 0 30 | var winningHash [32]byte 31 | var winner *GraphNode 32 | for _, child := range visibleChildren { 33 | e := edge(child.name, parent.name) 34 | votes := tip.relativeVoteGraph[e] 35 | childHash := sha256.Sum256([]byte(tip.salt + string(tip.name+child.name))) 36 | if votes > winningVotes { 37 | winningVotes = votes 38 | winningHash = childHash 39 | winner = child 40 | } else if votes == winningVotes && bytes.Compare(winningHash[:], childHash[:]) < 0 { 41 | winningHash = childHash 42 | winner = child 43 | } 44 | } 45 | return edge(winner.name, parent.name), winner 46 | } 47 | 48 | // CreateNode will take a list of parent nodes, create a graph node from that 49 | // list, and then add that node to the graph, returning the node. 50 | func (g *Graph) CreateNode(parents ...*GraphNode) *GraphNode { 51 | g.nameCounter++ 52 | tip := &GraphNode{ 53 | name: nodeName(strconv.Itoa(g.nameCounter)), 54 | parents: parents, 55 | relativeVoteGraph: make(map[edgeName]int), 56 | 57 | salt: g.salt, 58 | } 59 | 60 | // Perform a DFS on all ancestors of the tip to build the relative vote 61 | // graph. The relative vote graph of the tip block is the number of votes 62 | // that each ancestor edge has given the tip block's ancestry. 63 | visited := make(map[nodeName]bool) 64 | var addEdges func(parents []*GraphNode, childName nodeName) 65 | addEdges = func(parents []*GraphNode, childName nodeName) { 66 | for _, parent := range parents { 67 | // Add the parent-child edge. 68 | e := edge(childName, parent.name) 69 | tip.relativeVoteGraph[e] += 0 70 | 71 | // Skip this parent if the parent has already voted. 72 | if visited[parent.name] { 73 | continue 74 | } 75 | visited[parent.name] = true 76 | 77 | // Add all of the votes from the parent to the tip.relativeVoteGraph. 78 | for _, vote := range parent.edgeVotes { 79 | tip.relativeVoteGraph[vote]++ 80 | } 81 | addEdges(parent.parents, parent.name) 82 | } 83 | } 84 | addEdges(tip.parents, tip.name) 85 | 86 | // Add the tip block as a child to each of its parents. 87 | for _, parent := range tip.parents { 88 | parent.children = append(parent.children, tip) 89 | } 90 | 91 | // Discover the primary edges for this tip block and vote for them. 92 | current := g.genesisNode 93 | for len(current.children) != 0 { 94 | e, winningChild := primaryEdge(current, tip) 95 | tip.relativeVoteGraph[e]++ 96 | tip.edgeVotes = append(tip.edgeVotes, e) 97 | current = winningChild 98 | } 99 | return tip 100 | } 101 | -------------------------------------------------------------------------------- /ordering/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // main.go contains code to generate SageMath graphs of the Jute orderings of 4 | // various graphs. None of the core code for the Jute ordering algorithm is in 5 | // this file. See graph.go, then addnode.go, then finally ordering.go. 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // RelativeOrdering sorts the graph using the supplied node as the tip, then 14 | // prints the resulting ordering. 15 | func (gn *GraphNode) RelativeOrdering() string { 16 | relativeOrdering := gn.relativeOrdering() 17 | s := fmt.Sprint(relativeOrdering[0].name) 18 | for i := 1; i < len(relativeOrdering); i++ { 19 | s = fmt.Sprint(s, "-") 20 | s = fmt.Sprint(s, relativeOrdering[i].name) 21 | } 22 | return s 23 | } 24 | 25 | // SageGen returns a string that can be fed into Sage to create a visualization 26 | // of the longest chain and the votes for each edge in that chain. 27 | func (g *Graph) SageGen(tip *GraphNode) string { 28 | s := fmt.Sprintln("G = DiGraph()") 29 | for edge, weight := range tip.relativeVoteGraph { 30 | // Parse the edge name into its components. 31 | nodes := strings.Split(string(edge), "-") 32 | s = fmt.Sprint(s, "G.add_edge("+nodes[0]+", "+nodes[1]+", "+strconv.Itoa(int(weight))+")\n") 33 | } 34 | relativeOrdering := tip.RelativeOrdering() 35 | s = fmt.Sprint(s, "H = G.plot(edge_labels=True, layout='acyclic', edge_color='grey')\n") 36 | s = fmt.Sprint(s, "H.show(title=\""+relativeOrdering+"\", figsize=(5,16))\n") 37 | filename := relativeOrdering + ".png" 38 | s = fmt.Sprintf("%sH.save(filename=\"/home/user/plots/%s\", title=\""+relativeOrdering+"\", figsize=(5,16))\n", s, filename) 39 | return s 40 | } 41 | 42 | // Build some graphs, and then print some code that can be used to generate the 43 | // graphs in SageMath. 44 | func main() { 45 | // Diamond Graph 46 | gDiamond := NewGraph() 47 | d1 := gDiamond.CreateNode(gDiamond.GenesisNode()) 48 | d2 := gDiamond.CreateNode(gDiamond.GenesisNode()) 49 | d3 := gDiamond.CreateNode(d1, d2) 50 | fmt.Printf("\n# Diamond Graph\n%s", gDiamond.SageGen(d3)) 51 | 52 | // Pentagon Graph 53 | gPentagon := NewGraph() 54 | p1 := gPentagon.CreateNode(gPentagon.GenesisNode()) 55 | p2 := gPentagon.CreateNode(gPentagon.GenesisNode()) 56 | p3 := gPentagon.CreateNode(p1) 57 | p4 := gPentagon.CreateNode(p2, p3) 58 | fmt.Printf("\n# Pentagon Graph\n%s", gPentagon.SageGen(p4)) 59 | 60 | // Double Diamond Graph 61 | gDDiamond := NewGraph() 62 | dd1 := gDDiamond.CreateNode(gDDiamond.GenesisNode()) 63 | dd2 := gDDiamond.CreateNode(gDDiamond.GenesisNode()) 64 | dd3 := gDDiamond.CreateNode(dd1, dd2) 65 | dd4 := gDDiamond.CreateNode(dd2) 66 | dd5 := gDDiamond.CreateNode(dd3, dd4) 67 | fmt.Printf("\n# Double Diamond Graph\n%s", gDDiamond.SageGen(dd5)) 68 | 69 | // Nested Diamond Graph 70 | ndg := NewGraph() 71 | nd1 := ndg.CreateNode(ndg.GenesisNode()) 72 | nd2 := ndg.CreateNode(ndg.GenesisNode()) 73 | nd3 := ndg.CreateNode(nd1) 74 | nd4 := ndg.CreateNode(nd1, nd2) 75 | nd5 := ndg.CreateNode(nd2) 76 | nd6 := ndg.CreateNode(nd3, nd4) 77 | nd7 := ndg.CreateNode(nd4, nd5) 78 | nd8 := ndg.CreateNode(nd6, nd7) 79 | nd9 := ndg.CreateNode(nd8) 80 | nd10 := ndg.CreateNode(nd9) 81 | nd11 := ndg.CreateNode(nd10) 82 | fmt.Printf("\n# Nested Diamond Graph\n%s", ndg.SageGen(nd11)) 83 | 84 | // Ongoing Graph. 85 | og := NewGraph() 86 | o1 := og.CreateNode(og.GenesisNode()) 87 | o2 := og.CreateNode(og.GenesisNode()) 88 | o3 := og.CreateNode(og.GenesisNode()) 89 | o4 := og.CreateNode(og.GenesisNode()) 90 | o5 := og.CreateNode(og.GenesisNode()) 91 | o11 := og.CreateNode(o1, o2) 92 | o12 := og.CreateNode(o1, o2, o3) 93 | o13 := og.CreateNode(o2, o3, o4) 94 | o14 := og.CreateNode(o3, o4, o5) 95 | o15 := og.CreateNode(o4, o5, o5) 96 | o21 := og.CreateNode(o11, o12) 97 | o22 := og.CreateNode(o11, o12, o13) 98 | o23 := og.CreateNode(o12, o13, o14) 99 | o24 := og.CreateNode(o13, o14, o15) 100 | o25 := og.CreateNode(o14, o15, o15) 101 | o31 := og.CreateNode(o21, o22) 102 | o32 := og.CreateNode(o21, o22, o23) 103 | o33 := og.CreateNode(o22, o23, o24) 104 | o34 := og.CreateNode(o23, o24, o25) 105 | o35 := og.CreateNode(o24, o25, o25) 106 | o41 := og.CreateNode(o31, o32) 107 | o42 := og.CreateNode(o31, o32, o33) 108 | o43 := og.CreateNode(o32, o33, o34) 109 | o44 := og.CreateNode(o33, o34, o35) 110 | o45 := og.CreateNode(o34, o35, o35) 111 | o51 := og.CreateNode(o41, o42, o43, o44, o45) 112 | fmt.Printf("\n# Ongoing Graph\n%s", og.SageGen(o51)) 113 | 114 | // Impossibility Proof Graph 115 | ip := NewGraph() 116 | ip1 := ip.CreateNode(ip.GenesisNode()) 117 | ip2 := ip.CreateNode(ip.GenesisNode()) 118 | ip3 := ip.CreateNode(ip1) 119 | ip4 := ip.CreateNode(ip2) 120 | ip5 := ip.CreateNode(ip3) 121 | ip6 := ip.CreateNode(ip4, ip5) 122 | ip7 := ip.CreateNode(ip4) 123 | ip8 := ip.CreateNode(ip7) 124 | ip9 := ip.CreateNode(ip8) 125 | ip10 := ip.CreateNode(ip9) 126 | ip11 := ip.CreateNode(ip10, ip6) 127 | fmt.Printf("\n# Impossibility Proof Graph\n%s", ip.SageGen(ip11)) 128 | 129 | // Abstain Graph 130 | a := NewGraph() 131 | a1 := a.CreateNode(a.GenesisNode()) 132 | a2 := a.CreateNode(a1) 133 | a3 := a.CreateNode(a2) 134 | a4 := a.CreateNode(a3) 135 | a5 := a.CreateNode(a4) 136 | a6 := a.CreateNode(a.GenesisNode()) 137 | a7 := a.CreateNode(a6) 138 | a8 := a.CreateNode(a7) 139 | a9 := a.CreateNode(a8) 140 | a10 := a.CreateNode(a9) 141 | a11 := a.CreateNode(a10) 142 | a12 := a.CreateNode(a11) 143 | a13 := a.CreateNode(a12) 144 | a14 := a.CreateNode(a13) 145 | a15 := a.CreateNode(a14) 146 | a16 := a.CreateNode(a15) 147 | a17 := a.CreateNode(a16) 148 | a18 := a.CreateNode(a17, a5) 149 | fmt.Printf("\n# Abstain Graph\n%s", a.SageGen(a18)) 150 | 151 | // Leech Graph 152 | l := NewGraph() 153 | l1 := l.CreateNode(l.GenesisNode()) 154 | l2 := l.CreateNode(l1) 155 | l3 := l.CreateNode(l2) 156 | l4 := l.CreateNode(l3) 157 | l5 := l.CreateNode(l4) 158 | l6 := l.CreateNode(l5) 159 | l7 := l.CreateNode(l6) 160 | l8 := l.CreateNode(l7) 161 | l9 := l.CreateNode(l8) 162 | la := l.CreateNode(l9) 163 | lb := l.CreateNode(la) 164 | lc := l.CreateNode(lb) 165 | ld := l.CreateNode(l.GenesisNode()) 166 | le := l.CreateNode(ld, l2) 167 | lf := l.CreateNode(le, l3) 168 | lg := l.CreateNode(lf, l5) 169 | lh := l.CreateNode(lg, l7) 170 | li := l.CreateNode(lh, l8) 171 | lj := l.CreateNode(li, l9) 172 | lk := l.CreateNode(lj, lb) 173 | lm := l.CreateNode(lk, lc) 174 | fmt.Printf("\n# Leech Graph\n%s", l.SageGen(lm)) 175 | 176 | // Low Latency Adversary Graph 177 | lla := NewGraph() 178 | lla1 := lla.CreateNode(lla.GenesisNode()) 179 | lla2 := lla.CreateNode(lla.GenesisNode()) 180 | lla3 := lla.CreateNode(lla.GenesisNode()) 181 | lla4 := lla.CreateNode(lla.GenesisNode()) 182 | lla5 := lla.CreateNode(lla.GenesisNode()) 183 | lla6 := lla.CreateNode(lla5) 184 | lla7 := lla.CreateNode(lla1, lla2, lla3, lla4, lla6) 185 | lla8 := lla.CreateNode(lla6) 186 | lla9 := lla.CreateNode(lla8) 187 | lla10 := lla.CreateNode(lla7, lla9) 188 | fmt.Printf("\n# Low Latenct Adversary Graph\n%s", lla.SageGen(lla10)) 189 | } 190 | -------------------------------------------------------------------------------- /ordering/jute_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // TestEmptyGraph creates a simple graph with just a genesis block and checks for 8 | // correctness of ordering. 9 | func TestEmptyGraph(t *testing.T) { 10 | // Create the empty shape. 11 | // 12 | // O 13 | // 14 | g := NewGraph() 15 | ordering := g.GenesisNode().relativeOrdering() 16 | if len(ordering) != 1 { 17 | t.Fatal("There should be exactly one node in the relative ordering of a new graph") 18 | } 19 | if ordering[0].name != g.GenesisNode().name { 20 | t.Error("Genesis node is not ordered correctly:", ordering[0].name, g.GenesisNode().name) 21 | } 22 | } 23 | 24 | // TestBarGraph creates a graph with a genesis block and a single child and 25 | // checks for correctness of ordering. 26 | func TestBarGraph(t *testing.T) { 27 | // Create the bar shape. 28 | // 29 | // O 30 | // | 31 | // O 32 | // 33 | g := NewGraph() 34 | c := g.CreateNode(g.GenesisNode()) 35 | ordering := c.relativeOrdering() 36 | if len(ordering) != 2 { 37 | t.Fatal("There should be 2 nodes in the bar graph") 38 | } 39 | if ordering[0].name != g.GenesisNode().name { 40 | t.Error("Genesis node is not ordered correctly:", ordering[0].name, g.GenesisNode().name) 41 | } 42 | if ordering[1].name != c.name { 43 | t.Error("Genesis node is not ordered correctly:", ordering[1].name, c.name) 44 | } 45 | } 46 | 47 | // TestLongBarGraph creates a graph with a genesis block and two progressive 48 | // children and checks for correctness of ordering. 49 | func TestLongBarGraph(t *testing.T) { 50 | // Create the long bar shape. 51 | // 52 | // O 53 | // | 54 | // O 55 | // | 56 | // O 57 | // 58 | g := NewGraph() 59 | n1 := g.CreateNode(g.GenesisNode()) 60 | n2 := g.CreateNode(n1) 61 | 62 | // Check the ordering on n1. 63 | ordering1 := n1.relativeOrdering() 64 | if len(ordering1) != 2 { 65 | t.Fatal("There should be 2 nodes in the long bar graph") 66 | } 67 | if ordering1[0].name != g.GenesisNode().name { 68 | t.Error("Genesis node is not ordered correctly:", ordering1[0].name, g.GenesisNode().name) 69 | } 70 | if ordering1[1].name != n1.name { 71 | t.Error("Genesis node is not ordered correctly:", ordering1[1].name, n1.name) 72 | } 73 | 74 | // Check the ordering on n2. 75 | ordering2 := n2.relativeOrdering() 76 | if len(ordering2) != 3 { 77 | t.Fatal("There should be 3 nodes in the long bar graph") 78 | } 79 | if ordering2[0].name != g.GenesisNode().name { 80 | t.Error("Genesis node is not ordered correctly:", ordering2[0].name, g.GenesisNode().name) 81 | } 82 | if ordering2[1].name != n1.name { 83 | t.Error("Genesis node is not ordered correctly:", ordering2[1].name, n1.name) 84 | } 85 | if ordering2[2].name != n2.name { 86 | t.Error("Genesis node is not ordered correctly:", ordering2[2].name, n2.name) 87 | } 88 | } 89 | 90 | // TestPentagon creates a pentagon graph and checks for correctness of 91 | // ordering. 92 | func TestPentagon(t *testing.T) { 93 | // Create the pentagon shape. 94 | // 95 | // O 96 | // / \ 97 | // O O 98 | // | | 99 | // O | 100 | // \ / 101 | // O 102 | // 103 | g := NewGraph() 104 | l1 := g.CreateNode(g.GenesisNode()) 105 | l2 := g.CreateNode(l1) 106 | r1 := g.CreateNode(g.GenesisNode()) 107 | m := g.CreateNode(l2, r1) 108 | 109 | // Check the ordering on l1. 110 | ordering1 := l1.relativeOrdering() 111 | if len(ordering1) != 2 { 112 | t.Fatal("There should be 2 nodes in the pentagon graph") 113 | } 114 | if ordering1[0].name != g.GenesisNode().name { 115 | t.Error("Genesis node is not ordered correctly:", ordering1[0].name, g.GenesisNode().name) 116 | } 117 | if ordering1[1].name != l1.name { 118 | t.Error("Genesis node is not ordered correctly:", ordering1[1].name, l1.name) 119 | } 120 | 121 | // Check the ordering on l2. 122 | ordering2 := l2.relativeOrdering() 123 | if len(ordering2) != 3 { 124 | t.Fatal("There should be 3 nodes in the pentagon graph") 125 | } 126 | if ordering2[0].name != g.GenesisNode().name { 127 | t.Error("Genesis node is not ordered correctly:", ordering2[0].name, g.GenesisNode().name) 128 | } 129 | if ordering2[1].name != l1.name { 130 | t.Error("Genesis node is not ordered correctly:", ordering2[1].name, l1.name) 131 | } 132 | if ordering2[2].name != l2.name { 133 | t.Error("Genesis node is not ordered correctly:", ordering2[2].name, l2.name) 134 | } 135 | 136 | // Check the ordering on r1. 137 | ordering3 := r1.relativeOrdering() 138 | if len(ordering3) != 2 { 139 | t.Fatal("There should be 2 nodes in the pentagon graph") 140 | } 141 | if ordering3[0].name != g.GenesisNode().name { 142 | t.Error("Genesis node is not ordered correctly:", ordering3[0].name, g.GenesisNode().name) 143 | } 144 | if ordering3[1].name != r1.name { 145 | t.Error("Genesis node is not ordered correctly:", ordering3[1].name, r1.name) 146 | } 147 | 148 | // Check the ordering on m. 149 | ordering4 := m.relativeOrdering() 150 | if len(ordering4) != 5 { 151 | t.Fatal("There should be 5 nodes in the pentagon graph") 152 | } 153 | if ordering4[0].name != g.GenesisNode().name { 154 | t.Error("Genesis node is not ordered correctly:", ordering4[0].name, g.GenesisNode().name) 155 | } 156 | if ordering4[1].name != l1.name { 157 | t.Error("Genesis node is not ordered correctly:", ordering4[1].name, l1.name) 158 | } 159 | if ordering4[2].name != l2.name { 160 | t.Error("Genesis node is not ordered correctly:", ordering4[2].name, l2.name) 161 | } 162 | if ordering4[3].name != r1.name { 163 | t.Error("Genesis node is not ordered correctly:", ordering4[3].name, r1.name) 164 | } 165 | } 166 | 167 | // TestDiamond creates a diamond graph and checks for correctness of ordering. 168 | func TestDiamond(t *testing.T) { 169 | // Create the diamond shape. 170 | // 171 | // O 172 | // / \ 173 | // O O 174 | // \ / 175 | // O 176 | // 177 | g := NewGraph() 178 | l := g.CreateNode(g.GenesisNode()) 179 | r := g.CreateNode(g.GenesisNode()) 180 | m := g.CreateNode(l, r) 181 | 182 | // Check the ordering on l. 183 | ordering1 := l.relativeOrdering() 184 | if len(ordering1) != 2 { 185 | t.Fatal("There should be 2 nodes in the diamond graph") 186 | } 187 | if ordering1[0].name != g.GenesisNode().name { 188 | t.Error("Genesis node is not ordered correctly:", ordering1[0].name, g.GenesisNode().name) 189 | } 190 | if ordering1[1].name != l.name { 191 | t.Error("Genesis node is not ordered correctly:", ordering1[1].name, l.name) 192 | } 193 | 194 | // Check the ordering on r. 195 | ordering2 := r.relativeOrdering() 196 | if len(ordering2) != 2 { 197 | t.Fatal("There should be 2 nodes in the diamond graph") 198 | } 199 | if ordering2[0].name != g.GenesisNode().name { 200 | t.Error("Genesis node is not ordered correctly:", ordering2[0].name, g.GenesisNode().name) 201 | } 202 | if ordering2[1].name != r.name { 203 | t.Error("Genesis node is not ordered correctly:", ordering2[1].name, r.name) 204 | } 205 | 206 | // Check the ordering on m. 207 | ordering3 := m.relativeOrdering() 208 | if len(ordering3) != 4 { 209 | t.Fatal("There should be 4 nodes in the diamond graph") 210 | } 211 | if ordering3[0].name != g.GenesisNode().name { 212 | t.Error("Genesis node is not ordered correctly:", ordering3[0].name, g.GenesisNode().name) 213 | } 214 | if ordering3[1].name == l.name { 215 | // RNG favored l. 216 | if ordering3[2].name != r.name { 217 | t.Error("Ordering is incorrect") 218 | } 219 | } else if ordering3[1].name == r.name { 220 | // RNG favored r. 221 | if ordering3[2].name != l.name { 222 | t.Error("Ordering is incorrect") 223 | } 224 | } else { 225 | t.Error("Ordering is incorrect") 226 | } 227 | if ordering3[3].name != m.name { 228 | t.Error("Genesis node is not ordered correctly:", ordering3[3].name, m.name) 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jute: More Scalable, More Decentralized Proof-of-Work Consensus 2 | =============================================================== 3 | 4 | **Used in conjunction with [this presentation](Scaling Bitcoin Milan Presentation.pdf)** 5 | 6 | Jute is a proof-of-work consensus algorithm drawing heavily from the Bitcoin 7 | consensus algorithm. Jute assumes a network of economically rational miners 8 | where no single party or conglomerate controls more than 51% of the hashate. 9 | The jute algorithm solves the problem of selfish mining, eliminates orphans, 10 | and paves a path leading toward shorter block times and higher network 11 | throughput. This is achieved by replacing the linked-list, longest chain 12 | consensus algorithm with an algorithm that allows blocks to have many parents, 13 | creating a DAG. A sorting algorithm is applied to the DAG to get an exact 14 | ordering of blocks that is safe from reordering/reorganization except in the 15 | face of a 51% attacker. 16 | 17 | The result is a consensus algorithm which eliminates orphan-rate based miner 18 | centralization, eliminates orphan based selfish mining attacks, allows the 19 | block time to be safely reduced by a substantial amount, and allows the 20 | throughput of the network to be safely increased by a substantial amount. 21 | 22 | ## Related work 23 | 24 | A disclaimer: though a lot of the related work is interesting and was very 25 | helpful in guiding the design of jute, much of the work linked below has 26 | security problems or other shortcomings that are not discussed in the links 27 | provided. 28 | 29 | [Weak Blocks - The Good and the Bad](http://popeller.io/index.php/2016/01/19/weak-blocks-the-good-and-the-bad/) [(more)](https://gist.github.com/jl2012/2db9e434ce7beb8e13354604305f6d77) [(more)](https://gist.github.com/erasmospunk/23040383b7620b525df0) [(more)](https://diyhpl.us/wiki/transcripts/scalingbitcoin/hong-kong/invertible-bloom-lookup-tables-and-weak-block-propagation-performance/) [(mandatory weak blocks)](https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-November/011707.html) 30 | [Braiding the Blockchain](https://scalingbitcoin.org/hongkong2015/presentations/DAY2/2_breaking_the_chain_1_mcelrath.pdf) [(transacript)](https://diyhpl.us/wiki/transcripts/scalingbitcoin/hong-kong/braiding-the-blockchain/) 31 | [Inclusive Block Chain Protocols](http://fc15.ifca.ai/preproceedings/paper_101.pdf) 32 | [bip-soft-blocks.mediawiki](https://gist.github.com/erasmospunk/23040383b7620b525df0) 33 | [Block Publication Incentives for Miners](https://petertodd.org/2016/block-publication-incentives-for-miners) 34 | [Accelerating Bitcoin's Transaction Processing (GHOST)](https://eprint.iacr.org/2013/881.pdf) 35 | [Secure High Rate Transaction Processing in Bitcoin (GHOST)](http://fc15.ifca.ai/preproceedings/paper_30.pdf) 36 | [DAG-Coin](https://bitcointalk.org/index.php?topic=1177633.0) [(draft paper)](https://bitslog.files.wordpress.com/2015/09/dagcoin-v41.pdf) [(blog post)](https://diyhpl.us/wiki/transcripts/scalingbitcoin/hong-kong/braiding-the-blockchain/) 37 | [Bitcoin-NG: A Scalable Blockchain Protocol](https://arxiv.org/pdf/1510.02037.pdf) 38 | [Ethereum GHOST Protocol](https://blog.ethereum.org/2014/07/11/toward-a-12-second-block-time/) [(in whitepaper)](https://github.com/ethereum/wiki/wiki/White-Paper#modified-ghost-implementation) 39 | [Re: [Bitcoin-development] Tree-chains preliminary summary](https://www.mail-archive.com/bitcoin-development@lists.sourceforge.net/msg04388.html) 40 | [The Tangle](https://www.weusecoins.com/assets/pdf/library/Tangle%20-%20a%20cryptocurrency%20for%20Internet-of-Things%20industry%20-%20blockchain%20alternative.pdf) 41 | [Reduce Orphaning Risk and Improve Zero-Confirmation Security with Subchains](https://www.bitcoinunlimited.info/resources/subchains.pdf) 42 | 43 | ## Shortcomings with Nakamoto Consensus 44 | 45 | ##### Orphans 46 | 47 | Blocks take time to propagate. The miner who finds a block can propagate it to 48 | all their local nodes almost instantly. The rest of the network must wait for 49 | the block to travel the network, spending more time working on an outdated and 50 | having a higher orphan rate. Miners with more hashrate get a bigger boost from 51 | their instant propagation, and they get that boost more frequently (as they 52 | find more blocks). This is a centralization pressure. When the block time is 53 | substantially higher than the network propagation time (as it is in Bitcoin), 54 | the effect is greatly reduced. 55 | 56 | Jute all but eliminates orphans, eliminating this centralizing effect along 57 | with them. One of the chief reasons that a high block time is necessary gets 58 | eliminated, and a nontrivial mining centralization pressure is eliminated as 59 | well. 60 | 61 | ##### Selfish Mining 62 | 63 | Miners with sufficient hashrate or sufficient network superiority are able to 64 | perform strategic block witholding and block propagation strategies such that 65 | their profitability increases by them increasing the relative orphan rates of 66 | their competitors. Because Jute mostly eliminates orphans, orphan-based 67 | selfish mining is no longer an issue. 68 | 69 | ###### Papers and Links Related to Selfish Mining: 70 | [Majority is Not Enough: Bitcoin Mining is Vulnerable](https://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf) 71 | [Optimal Selfish Mining Strategies in Bitcoin](http://fc16.ifca.ai/preproceedings/30_Sapirshtein.pdf) 72 | [Stubborn Mining: Generalizing Selfish Mining and Combining with an Eclipse Attack](https://eprint.iacr.org/2015/796.pdf) 73 | [Block Publication Incentives for Miners](https://petertodd.org/2016/block-publication-incentives-for-miners) 74 | 75 | ##### Poorly Utilized Network 76 | 77 | The bitcoin network spends most of it's life idle. Every 10 minutes, a 1MB 78 | block propagates, and then the majority of the nodes are quiet again for 79 | approximately the whole next 10 minutes. Miners especially must have low 80 | latency setups, which means mining in certain parts of the world becomes 81 | infeasible (or at least less profitable), and it means that the amount of 82 | initial capital needed for mining is higher. Large miners can more easily 83 | amortize initial capital costs meaning that larger, more centralized miners 84 | experience higher per-hashrate profitability. 85 | 86 | Things like [compact blocks](https://bitcoincore.org/en/2016/06/07/compact-blocks-faq/), 87 | [the relay network](http://bitcoinrelaynetwork.org/), 88 | [FIBRE](http://bluematt.bitcoin.ninja/2016/07/07/relay-networks/), 89 | [weak blocks](http://popeller.io/index.php/2016/01/19/weak-blocks-the-good-and-the-bad/), 90 | and other fast-relay schemes all help to improve network utilization, however 91 | most of these strategies rely on pre-forwarding transactions and do not handle 92 | adversarial blocks very well. 93 | 94 | With jute, miners can remain competitive without seeing their competitors 95 | blocks immediately. The reduced emphasis on network latency means that the 96 | block time can be lowered, and optimizations can focus more heavily on 97 | throughput. The lowered blocktime also means a more consistent load applied to 98 | the network, which itself is an optimization for throughput. 99 | 100 | ###### Papers and Links Related to Bitcoin's Network: 101 | [On Scaling Decentralized Blockchains (A Position Paper)](http://fc16.ifca.ai/bitcoin/papers/CDE+16.pdf) 102 | 103 | ##### High Variance Mining Rewards 104 | 105 | The long block times in Bitcoin means that smaller mining operations may only 106 | be able to find a few blocks per month or year, following a poisson 107 | distribution. This means that there is very high variance in month-to-month 108 | income for small solo miners, which makes solo mining substantially less 109 | practical without many tens of thousands of dollars of mining hardware. Bitcoin 110 | addresses this primarily thorugh the use of mining pools, which is a 111 | centralization pressure. 112 | 113 | The lower block time of Jute means more payouts to miners, greatly reducing the 114 | minimum-hashpower requirement for practical solo mining, reducing a significant 115 | miner centralization pressure in the Bitcoin ecosystem. 116 | 117 | ##### High Variance Confirmation Times 118 | 119 | The time between blocks follows an exponential distribution. It's common for 120 | blocks to be more than 30 minutes apart. This hurts the user experience, as the 121 | difference between 10 minutes and 30 minutes can be significant in daily life. 122 | By lowering the block time, Jute reduces the variance in confirmation times. 123 | 124 | It should be noted that a single confirmation in Jute is much weaker than a 125 | single confirmation in Bitcoin. Instead, one must wait until a particular 126 | ordering is being endorsed by a majority of the hashrate, which can take a 127 | couple of network propagation cycles. Confirmation times with jute are still in 128 | the ballpark of 5-10 minutes, but with much lower variance. 129 | 130 | ## Requirements for a Competitor to Nakamoto Consensus 131 | 132 | The goal of jute is to replace Nakamoto consensus as the primary mechanism for 133 | acheiving decentralized consensus. To be a proper replacement, it must be able 134 | to provide all of the benefits that Nakamoto consensus can provide, as well as 135 | provide additional features that Nakamoto consensus is unable to provide. 136 | 137 | ##### Immutable History 138 | 139 | Once history is added to consensus with a sufficient number of confirmations, 140 | it must be provably difficult to alter that history. Each additional 141 | confirmation should confirm only that history, and the network should converge 142 | such that the entire network is confirming only that history. Additionally, the 143 | amount of work required to alter the history must be equal to the total amount 144 | of work confirming the history. 145 | 146 | ##### Incentive Compatibility 147 | 148 | Miners should make the optimal amount of money by following the protocol as 149 | prescribed. Deviations from the protocol should either be non-harmful to 150 | network, or more ideally should result in a significant reduction in 151 | profitability. 152 | 153 | The incentive compatibility ideally applies to miners having up to 50% of the 154 | hashrate. 155 | 156 | ##### Censorship Resistance 157 | 158 | Groups controlling less than 50% of the hashrate should be unable to prevent a 159 | transaction from being included in the network. 160 | 161 | ##### Fast Double Spend Resisitance 162 | 163 | Confidence around the security of a transaction should be achieved consistently 164 | in under two hours. Confidence around smaller transactions should be achieved 165 | consistently in under half an hour. 166 | 167 | ##### Accessible Resource Requirements 168 | 169 | The median consumer desktop connected via a median internet connection should 170 | be able to participate as a fully validating node, even when the network is 171 | under attack. This means that in the worst case, only a moderate amount of 172 | strain is placed on the CPU, the memory, the hard drive, and the network 173 | bandwidth. 174 | 175 | ##### SPV Support 176 | 177 | Devices with limited resources, such as cell phones, should be able to verify 178 | incoming transactions by only adding the assumption that the history with the 179 | most work is a valid history. 180 | 181 | ##### Hashrate Profitability Fairness 182 | 183 | Miners with more hashrate should not see higher profitability per-hashrate. Put 184 | another way, miners should see a perfectly linear increase in profitability as 185 | they centralize and add hashrate, as opposed to seeing superlinear 186 | profitability gains. 187 | 188 | ##### Network Profitability Fairness 189 | 190 | Miners with superior network connections should not see higher profitability 191 | compared to miners with inferior network connections, so long as the inferior 192 | miner has as much connectivity as the average consumer. 193 | 194 | ## Jute Introduction 195 | 196 | Jute is a block based consensus algorithm where blocks are allowed to have 197 | multiple parents, effectively converting the blockchain into a directed 198 | acyclic graph or DAG. 199 | 200 | The DAG is converted into a linear history through a specific sorting algorithm 201 | (explained below). Using that algorithm as a foundation, we are able to achieve 202 | all of the security properties described above. 203 | 204 | Jute is an inclusive consensus protocol, which means all blocks are accepted 205 | into the linear history, even blocks that have invalid transactions. Nodes are 206 | able to determine which transactions are valid by looking at the final ordering 207 | and ignoring any invalid transactions. 208 | 209 | Miners are able to commit to the valid transactions such that SPV nodes are 210 | able to securely tell which transactions within a block are invalidated by 211 | conflicting history. This means that SPV nodes can achieve secuirty equivalent 212 | to the security of SPV nodes in Nakamoto consensus, even though the history can 213 | contain invalid transactions. 214 | 215 | ## The Jute Sorting Algorithm 216 | 217 | The genesis block must be the first block in the blockchain. All other blocks 218 | must have the genesis block as either a direct or indirect ancestor. Blocks are 219 | allowed to have multiple parents. Blocks are not allowed to have parents which 220 | are ancestors of other parents. 221 | 222 | ![Example DAG](http://i.imgur.com/IHMN24h.jpg) 223 | 224 | A block that has no children is called a tip block. Multiple tip blocks may 225 | exist simultaneously, resulting in multiple alternate histories. The sorting 226 | algorithm provided below indicates which tip block is considered the canonical 227 | history. A miner will add every known tip block as a parent of the block they 228 | are working on. 229 | 230 | A direct child-parent connection is called an edge. Each block except for the 231 | tip block will have edges to one or more children. 232 | 233 | ### Weighting Edges 234 | 235 | Jute establishes a linear ordering for blocks by identifying and leveraging 236 | primary edges. Each block that is added to the consensus DAG gets to vote for a 237 | set of primary edges, adding a single vote to each priamry edge. 238 | 239 | Primary edges are chosen starting from the genesis block. All edges from the 240 | genesis block to child blocks are considered, and the single edge with the most 241 | votes is chosen as the primary edge. That edge receives another vote, and then 242 | the process is repeated until the new block is reached, at which point the 243 | voting is complete. 244 | 245 | If multiple edges have the same winning number of votes, one is selected 246 | randomly using a random number generator seeded by the hash of the parent block 247 | appended to the hash of the new block. 248 | 249 | ##### Pseudocode for Primary Edge Voting: 250 | ``` 251 | var newBlock // a newly solved block extending the chain 252 | current := genesisBlock 253 | for current != newBlock { 254 | winningChildren := {} 255 | maxVotes := -1 256 | for child := range current.children { 257 | votes := edge(child, current).numVotes 258 | if votes > maxVotes { 259 | winningChildren = {child} 260 | maxVotes = votes 261 | } else if votes == maxVotes { 262 | winningChildren = append(winningChildren, child) 263 | } 264 | } 265 | if len(winningChildren) > 1 { 266 | rng := seedRNG(Hash(newBlock, current)) 267 | winningChildren = rng.Randomize(winningChildren) 268 | } 269 | 270 | edge(winningChildren[0], current).numVotes++ 271 | current = winningChild 272 | } 273 | ``` 274 | 275 | A full golang implementation can be found in [ordering/addnode.go](ordering/addnode.go) 276 | 277 | ##### Security Intuition Around the Edge Voting 278 | 279 | The primary edges are selected by popularity. Once the network has converged on 280 | a particular edge as the primary edge, every block that gets produced will vote 281 | for that same edge. That means that the gap between that edge and any competing 282 | edge will be growing at a rate of 1 block per block that is generated by the 283 | network. 284 | 285 | Any miner attempting to cause an alternate history will have to produce enough 286 | blocks to overcome the inital difference between the edges, while also keeping 287 | up with the entire rest of the network as new blocks get produced. This 288 | requires more than 50% hashrate. 289 | 290 | This is equivalent to the immutability provided by Nakamoto consensus - once 291 | all miners are confirming a certain piece of history, either luck or a full 50% 292 | of the network hashrate is required to alter that history. 293 | 294 | The probability that a miner with less than 50% hashrate can alter history 295 | decreases exponentially in the number of confirmations on that history. It is 296 | not unreasonable that a 25% hashrate miner would get lucky enough to alter 297 | history with 3 confirmations, but it is exceedingly unlikely that a 25% 298 | hashrate miner would be able to alter history with 1000 confirmations. 299 | 300 | ### The Jute Ordering Algorithm 301 | 302 | Blocks are ordered in Jute based on the edge votes. The genesis block is set as 303 | the first block in history. Then the primary edge from the genesis block to a 304 | child is identified using the same method as above. We will label the 305 | associated child as the primary child. 306 | 307 | All ancestors of the primary child must be included in the ordering before the 308 | child can be included. Once all ancestors are included, the next primary child 309 | is identified and the algorithm is repeated until a tip block is reached and 310 | all ancestors of that tip block have been included in the ordering. 311 | 312 | If the primary child has multiple unordered ancestors, all edges leading from a 313 | block in the ordering directly to an unordered ancestor are observed. The votes 314 | are tallied on each edge as seen by the primary child (meaning that votes from 315 | decendents of or siblings of the primary child are ignored). The edge with the 316 | greatest number of votes is selected, tiebreaking using a random number 317 | generator seeded by the hash of the primary child and the unordered ancestor. 318 | Recursively, the winner is set as the new primary child, and the ordering 319 | algorithm is applied until the original primary child is reached. If the 320 | original primary child still has unordered ancestors at that point, the 321 | algorithm is repeated until the primary child has no more unordered ancestors. 322 | 323 | Psuedocode for Ordering Blocks: 324 | ``` 325 | var ordering []block 326 | current := genesisBlock 327 | for { 328 | for len(current.unorderedAncestors) != 0 { 329 | var candidates []block 330 | for unorderedAncestor := range unorderedAncestors { 331 | for parent := unorderedAncestor.parents { 332 | if ordering.Contains(parent) { 333 | candidates = append(candidates, parent) 334 | break 335 | } 336 | } 337 | } 338 | rng := seedRNG(Hash(current)) 339 | candidates = rng.Randomize(candidates) 340 | ordering = append(ordering, candidates[0]) 341 | } 342 | 343 | ordering = append(ordering, current) 344 | 345 | if current == tipBlock { 346 | break 347 | } 348 | current = current.primaryEdge.Parent // defined in Primary Edge Voting 349 | } 350 | ``` 351 | A full golang implementation can be found in [ordering/ordering.go](ordering/ordering.go) 352 | 353 | ### Practical Jute Today 354 | 355 | + Deploy the jute consensus algorithm 356 | + Set the block time to 6 seconds 357 | + Set the block size to 50kb 358 | + Set SPV to commit to block ranges after they have 50+ confirmations (5 minutes) 359 | + Don't allow merges of blocks if their most recent parent already has 200+ confirmations 360 | + Ignore miner fee complications because they shouldn't come into play too much at 5 second confirmation times 361 | 362 | Realistically, by the time this was implemented we'd have worked through a lot 363 | more of the issues. I have casually handwaved over things like the need to 364 | update the difficulty adjustment algorithm, the timestamp algorithm, and 365 | probably a whole host of other protocol-level things that this interferes with. 366 | Largely though I don't think those are hard things to adjust, you just need to 367 | be careful and methodical. 368 | 369 | #### Acknowledgements 370 | 371 | Thanks to Jonathan Harvey-Buschel for contributions + research collaboration. 372 | Thanks to Andrew Poelstra for review + corrections. 373 | Thanks to Bob McElrath for active research on DAG based consensus, which has influenced this document. 374 | --------------------------------------------------------------------------------