Class: MerkleTree
- Inherits:
-
Object
- Object
- MerkleTree
- Defined in:
- lib/merkle_tree.rb,
lib/merkle_tree/leaf.rb,
lib/merkle_tree/node.rb,
lib/merkle_tree/version.rb
Overview
A binary tree of one-time signatures known as merkle tree.
Defined Under Namespace
Constant Summary collapse
- VERSION =
"0.2.0"
Instance Attribute Summary collapse
-
#digest ⇒ Object
readonly
The one way hash function.
-
#leaves ⇒ Object
readonly
All the leaf nodes.
-
#root ⇒ Object
readonly
The tree root node.
-
#width ⇒ Integer
readonly
The number of tree leaves.
Class Method Summary collapse
-
.default_digest ⇒ Proc
The default hash function using SHA 256.
Instance Method Summary collapse
-
#add(*messages) ⇒ Object
(also: #<<)
Add new message(s).
-
#auth_path(index) ⇒ Array[String,String]
Create an authentication path required to authenticate leaf with index.
-
#build(nodes) ⇒ Object
private
Calcualte the root value of this tree.
-
#empty? ⇒ Boolean
Check if this tree has any messages.
-
#height ⇒ Object
The tree height.
-
#include?(message, index) ⇒ Boolean
(also: #member?)
Verifies that an authentication path exists from leaf to root.
-
#initialize(*messages, digest: MerkleTree.default_digest) ⇒ MerkleTree
constructor
Create a new Merkle Tree.
-
#regeneration_path(index) ⇒ Node
The regeneration path from leaf to root.
-
#size ⇒ Object
(also: #length)
The number of nodes in this tree.
-
#subtree(index) ⇒ Node
Create subtree that contains message at index.
-
#to_h ⇒ Object
Hash representation of this tree.
-
#to_leaves(*messages) ⇒ Object
private
Convert messages to leaf data types.
-
#to_s(indent = "") ⇒ Object
String representation of this tree.
-
#traverse(node, index, path) ⇒ Object
private
Traverse tree from root to leaf collecting siblings hashes.
-
#update(message, index) ⇒ Leaf
Update a leaf at index position.
-
#visit(node, index, path) ⇒ Object
private
Visit all direct children collecting child nodes.
Constructor Details
#initialize(*messages, digest: MerkleTree.default_digest) ⇒ MerkleTree
Create a new Merkle Tree
48 49 50 51 52 53 54 55 |
# File 'lib/merkle_tree.rb', line 48 def initialize(*, digest: MerkleTree.default_digest) @root = Node::EMPTY @width = 0 @digest = digest @leaves = to_leaves(*) @width = @leaves.size @root = build(@leaves) end |
Instance Attribute Details
#digest ⇒ Object (readonly)
The one way hash function
26 27 28 |
# File 'lib/merkle_tree.rb', line 26 def digest @digest end |
#leaves ⇒ Object (readonly)
All the leaf nodes
16 17 18 |
# File 'lib/merkle_tree.rb', line 16 def leaves @leaves end |
#root ⇒ Object (readonly)
The tree root node
13 14 15 |
# File 'lib/merkle_tree.rb', line 13 def root @root end |
#width ⇒ Integer (readonly)
The number of tree leaves
23 24 25 |
# File 'lib/merkle_tree.rb', line 23 def width @width end |
Class Method Details
.default_digest ⇒ Proc
The default hash function using SHA 256
33 34 35 |
# File 'lib/merkle_tree.rb', line 33 def self.default_digest ->(val) { OpenSSL::Digest::SHA256.hexdigest(val) } end |
Instance Method Details
#add(*messages) ⇒ Object Also known as: <<
Add new message(s)
134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/merkle_tree.rb', line 134 def add(*) nodes = to_leaves(*) nodes.each { |node| @leaves << node } @width = @leaves.size top_node = Node::EMPTY while root.height != top_node.height top_node = build(nodes) nodes = [top_node, top_node] end @root = Node.build(@root, top_node) end |
#auth_path(index) ⇒ Array[String,String]
Create an authentication path required to authenticate leaf with index
174 175 176 |
# File 'lib/merkle_tree.rb', line 174 def auth_path(index) traverse(root, index, []) end |
#build(nodes) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Calcualte the root value of this tree
111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/merkle_tree.rb', line 111 def build(nodes) return Node::EMPTY if nodes.empty? if nodes.size == 1 return nodes[0] end parent_nodes = nodes.each_slice(2).map do |left, right| right = left if right.nil? # Duplicate odd nodes Node.build(left, right, digest: digest) end build(parent_nodes) end |
#empty? ⇒ Boolean
Check if this tree has any messages
76 77 78 |
# File 'lib/merkle_tree.rb', line 76 def empty? @root == Node::EMPTY end |
#height ⇒ Object
The tree height
83 84 85 |
# File 'lib/merkle_tree.rb', line 83 def height @root.height end |
#include?(message, index) ⇒ Boolean Also known as: member?
Verifies that an authentication path exists from leaf to root
187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/merkle_tree.rb', line 187 def include?(, index) return false if empty? leaf_hash = digest.() hash = auth_path(index).reverse_each.reduce(leaf_hash) do |h, (dir, sibling)| digest.(dir == :left ? sibling + h : h + sibling) end hash == root.value end |
#regeneration_path(index) ⇒ Node
The regeneration path from leaf to root
216 217 218 |
# File 'lib/merkle_tree.rb', line 216 def regeneration_path(index) visit(@root, index, [@root]) end |
#size ⇒ Object Also known as: length
The number of nodes in this tree
90 91 92 |
# File 'lib/merkle_tree.rb', line 90 def size @root.size end |
#subtree(index) ⇒ Node
Create subtree that contains message at index
101 102 103 |
# File 'lib/merkle_tree.rb', line 101 def subtree(index) root.subtree(index) end |
#to_h ⇒ Object
Hash representation of this tree
248 249 250 |
# File 'lib/merkle_tree.rb', line 248 def to_h { root: root.to_h } end |
#to_leaves(*messages) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Convert messages to leaf data types
63 64 65 66 67 68 69 70 71 |
# File 'lib/merkle_tree.rb', line 63 def to_leaves(*) if .size.odd? << .last.dup end .each_with_index.map do |msg, pos| Leaf.build(msg, pos, digest: digest) end end |
#to_s(indent = "") ⇒ Object
String representation of this tree
255 256 257 |
# File 'lib/merkle_tree.rb', line 255 def to_s(indent = "") root.to_s(indent) end |
#traverse(node, index, path) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Traverse tree from root to leaf collecting siblings hashes
158 159 160 161 162 163 164 |
# File 'lib/merkle_tree.rb', line 158 def traverse(node, index, path) return path if node.leaf? || node == Node::EMPTY path << node.sibling(index) traverse(node.subtree(index), index, path) end |
#update(message, index) ⇒ Leaf
Update a leaf at index position
231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/merkle_tree.rb', line 231 def update(, index) return if empty? leaf_hash = digest.() regeneration_path(index).reverse_each do |node| if node.leaf? node.value = leaf_hash else node.update(digest) end end.last end |
#visit(node, index, path) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Visit all direct children collecting child nodes
203 204 205 206 207 208 209 |
# File 'lib/merkle_tree.rb', line 203 def visit(node, index, path) return path if node.leaf? || node == Node::EMPTY path << node.child(index) visit(node.subtree(index), index, path) end |