utreexo 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2db0a1b0bb2ecdc1be9477aee62cce16fc9ea24f1febd7c877f819a986ce6250
4
- data.tar.gz: c35c3beafff28204c419f650236856b53e89a87e56270886976742718f399fd3
3
+ metadata.gz: f4df7507cb6f4a66261406b31c0b74ca81070e94fe747f328b09bbbfbf8ce38d
4
+ data.tar.gz: 275f3c81ee3acf4b1ecfd21dde961c8ac3b5f63627314268f8ef1cec45dd7d7e
5
5
  SHA512:
6
- metadata.gz: 183873eaba066f361498edd47c1e94edc9d6d3bc891babb1443775dc45de345759c20de657e22eea7d2fcab1554228462675bc7cc58ab5939006c805772a1db0
7
- data.tar.gz: 885eeb50daefb5b4cea34fc8463c17bed69e51577e92b2eaec2b24111ed3cf51cbc6606ef763d7e501b3a92455b0b2c00ec82bcc6eded81663116211837cf091
6
+ metadata.gz: 492bb3babdb31e371d615ebeafa0b25e58fc9e0d6e5a43a3aae934aa719ed580bc9d4507ec5565a65db0c246d6aa3110626f812fc76ee37512adebd1a5ed2bfb
7
+ data.tar.gz: f4b19fdf737d28475b0f6ce88f1a3f211b6ebdeb9811b391c3779aa9fc88092cb695c25d45685577d6824fbfceb1dba58b959ebf0b6b83a07e3424e90b6cf223
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Utreexorb [![Build Status](https://travis-ci.org/chaintope/utreexorb.svg?branch=master)](https://travis-ci.org/chaintope/utreexorb) [![Gem Version](https://badge.fury.io/rb/utreexorb.svg)](https://badge.fury.io/rb/utreexorb) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
1
+ # Utreexorb [![Build Status](https://travis-ci.org/chaintope/utreexorb.svg?branch=master)](https://travis-ci.org/chaintope/utreexorb) [![Gem Version](https://badge.fury.io/rb/utreexo.svg)](https://badge.fury.io/rb/utreexo) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
2
2
 
3
3
  This library is a Ruby implementation of [Utreexo](https://github.com/mit-dci/utreexo/blob/master/utreexo.pdf).
4
4
 
@@ -26,15 +26,15 @@ Or install it yourself as:
26
26
  require 'utreexo'
27
27
 
28
28
  # initialize forest.
29
-
30
29
  f = Utreexo::Forest.new
31
30
 
32
31
  # add element to forest.
33
32
  f.add('a00000aa00000000000000000000000000000000000000000000000000000000')
34
33
  f.add('a00100aa00000000000000000000000000000000000000000000000000000000')
35
- f.add('a00200aa00000000000000000000000000000000000000000000000000000000')
36
- f.add('a00300aa00000000000000000000000000000000000000000000000000000000')
37
- f.add('a00400aa00000000000000000000000000000000000000000000000000000000')
34
+ # if you want to tracking proof, set tracking flag to true.
35
+ f.add('a00200aa00000000000000000000000000000000000000000000000000000000', true)
36
+ f.add('a00300aa00000000000000000000000000000000000000000000000000000000', true)
37
+ f.add('a00400aa00000000000000000000000000000000000000000000000000000000', true)
38
38
 
39
39
  # forest has 2 tree, height 2, height 0
40
40
  # accumulator root for height 2 tree
@@ -43,14 +43,33 @@ f.acc[2]
43
43
  # accumulator root for height 0 tree
44
44
  f.acc[0]
45
45
  => 'a00400aa00000000000000000000000000000000000000000000000000000000'
46
+
47
+ # show forest.
48
+ puts f
49
+ 07:2d04
50
+ |---------------\ |---------------\
51
+ 05:736b 06:1a8e
52
+ |-------\ |-------\ |-------\ |-------\
53
+ 00:???? 01:???? 02:a002 03:a003 04:a004
54
+ ```
55
+
56
+ ### Get proof
57
+
58
+ If leaf tracking enabled, you can get its proof. If you add or remove element to the forest, the position and inclusion proof of the leaf being tracked are updated.
59
+
60
+ ```ruby
61
+ proof = f.proof('a00200aa00000000000000000000000000000000000000000000000000000000')
62
+ => [2] leaf = a00200aa00000000000000000000000000000000000000000000000000000000, siblings = ["a00300aa00000000000000000000000000000000000000000000000000000000", "736b3e12120637186a0a8eef8ce45ed69b39119182cc749b793f05de3996f464"]
63
+
64
+ # get all tracking proofs
65
+ proofs = f.proofs
46
66
  ```
47
67
 
48
68
  ### Verify element.
49
69
 
50
70
  ```ruby
51
71
  # proof for 3rd element
52
- proof = Utreexo::Proof.new(2, 'a00200aa00000000000000000000000000000000000000000000000000000000',
53
- ['a00300aa00000000000000000000000000000000000000000000000000000000', '736b3e12120637186a0a8eef8ce45ed69b39119182cc749b793f05de3996f464'])
72
+ proof = f.proof('a00200aa00000000000000000000000000000000000000000000000000000000')
54
73
 
55
74
  f.include?(proof)
56
75
  => true
@@ -59,8 +78,6 @@ f.include?(proof)
59
78
  ### Remove element from forest.
60
79
 
61
80
  ```ruby
62
- proof = Utreexo::Proof.new(2, 'a00200aa00000000000000000000000000000000000000000000000000000000',
63
- ['a00300aa00000000000000000000000000000000000000000000000000000000', '736b3e12120637186a0a8eef8ce45ed69b39119182cc749b793f05de3996f464'])
64
81
  f.remove(proof)
65
82
 
66
83
  # If delete 3rd element, last item move to 3rd element position, and root hash changed.
@@ -8,4 +8,14 @@ module Utreexo
8
8
  autoload :Forest, 'utreexo/forest'
9
9
  autoload :Proof, 'utreexo/proof'
10
10
 
11
+ module_function
12
+
13
+ # Calculate parent hash
14
+ # @param [String] left left node hash with hex format.
15
+ # @param [String] right left node hash with hex format.
16
+ # @return [String] a parent hash with hex format.
17
+ def parent(left, right)
18
+ Blake2b.hex([left + right].pack('H*'))
19
+ end
20
+
11
21
  end
@@ -6,23 +6,32 @@ module Utreexo
6
6
  attr_accessor :num_leaves
7
7
 
8
8
  # accumulator
9
- attr_accessor :acc
9
+ attr_reader :acc
10
+
11
+ # tracking proofs
12
+ attr_reader :proofs
10
13
 
11
14
  def initialize
12
- @height = 0
13
15
  @num_leaves = 0
14
- @forest = []
15
16
  @acc = []
17
+ @proofs = []
16
18
  end
17
19
 
18
20
  # Add element to forest.
19
21
  # @param [String] leaf an element hash to be added with hex format.
20
- def add(leaf)
22
+ def add(leaf, track = false)
21
23
  n = leaf
22
24
  h = 0
23
25
  r = acc[h]
26
+ proofs << Utreexo::Proof.new(num_leaves, leaf) if track
24
27
  until r.nil? do
25
- n = parent(r, n)
28
+ # Update siblings for tracking proofs
29
+ p1 = find_proof(r)
30
+ p1.each{|p|p.siblings << n}
31
+ p2 = find_proof(n)
32
+ p2.each{|p|p.siblings << r}
33
+
34
+ n = Utreexo.parent(r, n)
26
35
  acc[h] = nil
27
36
  h += 1
28
37
  r = acc[h]
@@ -37,19 +46,40 @@ module Utreexo
37
46
  raise Utreexo::Error, 'The target element does not exist in the forest.' unless include?(proof)
38
47
  n = nil
39
48
  h = 0
49
+ @num_leaves -= 1
50
+ fl = forest_leaves
51
+ # update acc hash
52
+ is_switch = false
53
+ has_single_leaf = !acc[0].nil?
40
54
  while h < proof.siblings.length do
41
- p = proof.siblings[h]
55
+ s = proof.siblings[h]
42
56
  if !n.nil?
43
- n = parent(p, n)
57
+ n = ((1<<h) & proof.position) == 0 ? Utreexo.parent(n, s) : Utreexo.parent(s, n)
44
58
  elsif acc[h].nil?
45
- acc[h] = p
59
+ acc[h] = s
46
60
  else
47
- n = proof.right? ? parent(p, acc[h]) : parent(acc[h], p)
61
+ # pickup switch pair
62
+ is_switch = true
63
+ if has_single_leaf
64
+ switch_single_leaf(proof, s)
65
+ else
66
+ switch_leaf_block(proof, fl, h)
67
+ end
68
+ n = (((1<<h) & proof.position) == 0) ? Utreexo.parent(acc[h], s) : Utreexo.parent(s, acc[h])
48
69
  acc[h] = nil
49
70
  end
50
71
  h += 1
51
72
  end
52
73
  acc[h] = n
74
+
75
+ proofs.sort!{|a, b| a.position <=> b.position}
76
+ proofs.delete(proof)
77
+
78
+ # Update proofs
79
+ remove_unnecessary_siblings!(proof)
80
+ update_position!(proof) unless is_switch
81
+
82
+ proofs.sort!{|a, b| a.position <=> b.position}
53
83
  end
54
84
 
55
85
  # Whether the element exists in the forest
@@ -60,26 +90,259 @@ module Utreexo
60
90
  n = proof.payload
61
91
  proof.siblings.each_with_index do |sibling, height|
62
92
  if ((1<<height) & proof.position) == 0
63
- n = parent(n, sibling)
93
+ n = Utreexo.parent(n, sibling)
64
94
  else
65
- n = parent(sibling, n)
95
+ n = Utreexo.parent(sibling, n)
66
96
  end
67
97
  end
68
98
  n == root
69
99
  end
70
100
 
101
+ # Get the current proof being tracked specified by leaf. If not tracking, return nil.
102
+ # @param [String] leaf
103
+ # @return [Utreexo::Proof]
104
+ def proof(leaf)
105
+ proofs.find{|p|p.payload == leaf}
106
+ end
107
+
71
108
  # get current height of the highest tree
72
109
  # @return [Integer] current height of the highest tree
73
- def height
110
+ def highest_height
74
111
  i = acc.reverse.find_index{|i|!i.nil?}
75
112
  i ||= 0
76
113
  acc.length - i
77
114
  end
78
115
 
116
+ # show forest
117
+ def to_s
118
+ h = highest_height
119
+ outs = []
120
+ index = 0
121
+ h.times do |i|
122
+ diff = Math.log2(num_leaves) == (h - 1) ? i + 1 : i
123
+ row_len = 1 << (h - diff)
124
+ out = ''
125
+ line = ''
126
+ row_items = (num_leaves / (2**i))
127
+ row_len.times do |j|
128
+ out << "#{index.to_s.rjust(2, '0')}:"
129
+ node = (j.even? && (row_items - 1) == j) ? acc[i] : find_node(i, j)
130
+ out << (node ? "#{node[0..3]} " : "???? ")
131
+ out << (" " * (2 ** (3 + i)))[0...-8]
132
+ index += 1
133
+ break if (j + 2) > row_items
134
+ end
135
+ outs << out
136
+ break if i == (h - 1)
137
+ (row_len / 2).times do
138
+ line << '|'
139
+ line << ('--------' * (2 ** (i))).chop
140
+ line << "\\"
141
+ line << (" " * (2 ** (3 + i))).chop
142
+ end
143
+ outs << line
144
+ end
145
+ outs.reverse.join("\n")
146
+ end
147
+
79
148
  private
80
149
 
81
- def parent(left, right)
82
- Blake2b.hex([left + right].pack('H*'))
150
+ def find_node(height, index)
151
+ return find_proof_at(index)&.payload if height == 0
152
+ return acc[self.highest_height - 1] if height == (self.highest_height - 1)
153
+ if height == 1
154
+ p1 = find_proof_at(index * 2)
155
+ return Utreexo.parent(p1.payload, p1.siblings[0]) if p1
156
+ p2 = find_proof_at(index * 2 + 1)
157
+ return Utreexo.parent(p2.siblings[0], p2.payload) if p2
158
+ end
159
+ left_pos = (2 ** height) * index
160
+ if index.even?
161
+ left_pos += (2 ** height)
162
+ else
163
+ left_pos -= (2 ** height)
164
+ end
165
+ right_pos = left_pos + (2 ** height - 1)
166
+ targets = proofs.select{|p| left_pos <= p.position && p.position <= right_pos}
167
+ p = targets.find{|p|!p.siblings[height].nil?}
168
+ p.siblings[height] if p
169
+ end
170
+
171
+ # Calculate the proof associated with the target(self or parent).
172
+ # @param [String] target a target hash.
173
+ # @return [Array[Utreexo::Proof]] target proofs.
174
+ def find_proof(target)
175
+ proofs.select do |p|
176
+ n = p.payload
177
+ p.siblings.each_with_index do|s, h|
178
+ if ((1<<h) & p.position) == 0
179
+ n = Utreexo.parent(n, s)
180
+ else
181
+ n = Utreexo.parent(s, n)
182
+ end
183
+ end
184
+ n == target
185
+ end
186
+ end
187
+
188
+ def find_proof_at(index)
189
+ proofs.find{|p|p.position == index}
190
+ end
191
+
192
+ def remove_unnecessary_siblings!(proof)
193
+ return unless proof.siblings.size > 0
194
+ target = proof.payload
195
+ proofs.select{|p|p.siblings.include?(target)}.each do |p|
196
+ p.siblings = p.siblings[0...p.siblings.index(target)]
197
+ end
198
+ proof.siblings.each_with_index do |s, h|
199
+ target = ((1<<h) & proof.position) == 0 ? Utreexo.parent(target, s) : Utreexo.parent(s, target)
200
+ proofs.select{|p|p.siblings.include?(target)}.each do |p|
201
+ p.siblings = p.siblings[0...p.siblings.index(target)]
202
+ end
203
+ end
204
+ end
205
+
206
+ # update position
207
+ # @param [Utreexo::Proof] proof removed proof
208
+ def update_position!(proof)
209
+ proof_pos = proof.position
210
+ start_index = 0
211
+ h = highest_height
212
+ h.times do |i|
213
+ half_pos = 2 ** (h - (i + 1))
214
+ threshold = half_pos + start_index
215
+ proofs.each do |p|
216
+ next if p.position < start_index
217
+ if (highest_height - 1) == i
218
+ p.position -= 1 if proof.left?
219
+ elsif proof_pos < threshold
220
+ if p.position >= threshold
221
+ p.position -= half_pos
222
+ else
223
+ p.position += half_pos
224
+ end
225
+ end
226
+ end
227
+ proof_pos += half_pos if proof_pos < threshold
228
+ start_index += half_pos
229
+ end
230
+ end
231
+
232
+ # Get the number of leaves of each tree in the forest in ascending order.
233
+ # @return [Array[Integer]] the number of leaves of each tree in the forest.
234
+ def forest_leaves
235
+ acc.map.with_index{|a, i| a.nil? ? nil : 2 ** i}.compact.reverse
236
+ end
237
+
238
+ # Get the start index and end index of the leaf of the tree with the target leaf number in the forest.
239
+ # @param [Integer] forest_trees the number of leaves of each tree in the forest.
240
+ # @param [Integer] target_tree Number of leaves in target tree.
241
+ # @return [Range] start index and end index at the target tree.
242
+ def target_tree_range(forest_trees, target_tree)
243
+ i = forest_trees.index(target_tree)
244
+ left_pos = 0
245
+ i.times do |index|
246
+ left_pos += forest_trees[index]
247
+ end
248
+ left_pos..(left_pos + target_tree - 1)
249
+ end
250
+
251
+ # Get leaf tree height at pos.
252
+ # @param [Integer] pos leaf position.
253
+ # @return [Integer] tree height.
254
+ def tree_height(pos)
255
+ r = 0..0
256
+ tree_heights = acc.map.with_index{|a, i| a ? i : nil}.reverse
257
+ tree_heights[tree_heights.index(highest_height - 1)..-1].each do |h|
258
+ next unless h
259
+ r = (r.last...(r.last + 2**h))
260
+ return h if r.include?(pos)
261
+ end
262
+ end
263
+
264
+ # Switch one leaf and remove leaf in the forest.
265
+ # @param [Utreexo::Proof] proof proof of removed leaf
266
+ # @param [String] s currently being processed
267
+ def switch_single_leaf(proof, s)
268
+ p0 = proof(acc[0])
269
+ if p0
270
+ p0.siblings = proof.siblings
271
+ p0.position = proof.position
272
+ end
273
+ ps = proof(s)
274
+ ps.siblings[0] = acc[0] if ps
275
+
276
+ target = proof.payload
277
+ new_target = acc[0]
278
+ proof.siblings.each_with_index do |s, h|
279
+ if ((1<<h) & proof.position) == 0
280
+ target = Utreexo.parent(target, s)
281
+ new_target = Utreexo.parent(new_target, s)
282
+ else
283
+ target = Utreexo.parent(s, target)
284
+ new_target = Utreexo.parent(s, new_target)
285
+ end
286
+ proofs.select{|p|p.siblings.include?(target)}.each do |p|
287
+ p.siblings[p.siblings.index(target)] = new_target
288
+ end
289
+ end
290
+ end
291
+
292
+ # Switch leaf blocks to be removed in the forest.
293
+ # @param [Utreexo::Proof] proof proof of removed leaf
294
+ # @param [Integer] fl leaves count in the forest.
295
+ # @param [Integer] h the height currently being processed
296
+ def switch_leaf_block(proof, fl, h)
297
+ switch_size = 2 ** h
298
+ sw_to_range = target_tree_range(fl, switch_size)
299
+ sw_to = sw_to_range.map {|pos|proofs.find{|p|p.position == pos}}
300
+ sw_from_range = proof.switch_range(switch_size)
301
+ sw_from = {}
302
+ sw_from_range.each {|pos|sw_from[pos] = proofs.find{|p|p.position == pos}}
303
+ height = proof.tree_height
304
+ # sort from tree
305
+ sorted_from = sw_from.sort{|(k1, v1), (k2, v2)| proof.same_subtree_height(k2) <=> proof.same_subtree_height(k1)}
306
+
307
+ sw_from_range.each.with_index do |pos, i|
308
+ from = sorted_from[i][1]
309
+ to = sw_to[i]
310
+ if from
311
+ from.position = sw_to_range.first + i unless proof == from
312
+ from.siblings.clear if from.payload == proof.siblings[0] # proof's sibling
313
+ end
314
+ if to
315
+ to.position = sw_from_range.first + i
316
+ to.siblings = to.siblings.slice(0, h) + proof.siblings.slice(-(height - h), height - h)
317
+ end
318
+ end
319
+
320
+ # Update another branch's siblings in the same tree as the proof
321
+ updated_parents = proof.switched_parents(acc[h], h)
322
+ # right branch
323
+ branch = (sw_from_range.last + 1)..(proof.tree_leaves - 1)
324
+ branch.each do |pos|
325
+ p = find_proof_at(pos)
326
+ next unless p
327
+ sub_h = proof.same_subtree_height(pos) - 1
328
+ p.siblings = p.siblings.slice(0, sub_h)
329
+ (height - sub_h).times do |i|
330
+ target = acc[p.siblings.size]
331
+ target = i.even? ? updated_parents[i] : proof.siblings[p.siblings.size] unless target
332
+ p.siblings << target
333
+ end
334
+ end
335
+ # left branch
336
+ branch = 0..(sw_from_range.first - 1)
337
+ branch.each do |pos|
338
+ p = find_proof_at(pos)
339
+ next unless p
340
+ branch_height = Math.log2(branch.size).to_i
341
+ p.siblings = p.siblings.slice(0, branch_height)
342
+ (height - branch_height).times do
343
+ p.siblings << (acc[p.siblings.size] ? acc[p.siblings.size] : proof.siblings[p.siblings.size])
344
+ end
345
+ end
83
346
  end
84
347
 
85
348
  end
@@ -1,9 +1,9 @@
1
1
  module Utreexo
2
2
  class Proof
3
3
 
4
- attr_reader :position # where at the bottom of the tree it sits
5
- attr_reader :payload # hash of the thing itself (what's getting proved)
6
- attr_reader :siblings # hash of siblings up to a root
4
+ attr_accessor :position # where at the bottom of the tree it sits
5
+ attr_reader :payload # hash of the thing itself (what's getting proved)
6
+ attr_accessor :siblings # hash of siblings up to a root
7
7
 
8
8
  # initialize
9
9
  # @param [Integer] position Where at the bottom of the tree it sits
@@ -27,5 +27,73 @@ module Utreexo
27
27
  position.even?
28
28
  end
29
29
 
30
+ # Show proof
31
+ def to_s
32
+ "[#{position}] leaf = #{payload}, siblings = #{siblings}"
33
+ end
34
+
35
+ # Return tree height containing this element
36
+ # @return [Integer] tree height
37
+ def tree_height
38
+ siblings.size
39
+ end
40
+
41
+ # Returns the number of leaves in the tree that contains this element.
42
+ # @return [Integer] the number of leaves in the tree
43
+ def tree_leaves
44
+ 2 ** tree_height
45
+ end
46
+
47
+ # Return the position of this proof's pair leaf.
48
+ # @return [Integer] the position of this proof's pair leaf
49
+ def pair_pos
50
+ right? ? position - 1 : position + 1
51
+ end
52
+
53
+ # Returns the position of the leaf that is switched together with this proof, when switching this proof.
54
+ # @param [Integer] leaves the number of leaves to be switched.
55
+ # @return [Range] Leaf range to be switched.
56
+ def switch_range(leaves)
57
+ l = tree_leaves
58
+ unit = l / leaves
59
+ unit.times do |i|
60
+ range = ((i * leaves)...((i + 1) * leaves))
61
+ return range.first..(range.last - 1) if range.include?(position)
62
+ end
63
+ end
64
+
65
+ # When replacing the parent of +height+ of this proof with +parent+ argument, returns the list of parent nodes to be updated.
66
+ # @param [String] parent the parent value to replace
67
+ # @param [Integer] parent_height the parent height to replace
68
+ # @return [Array[String]] a list of updated parents
69
+ def switched_parents(parent, parent_height)
70
+ n = parent
71
+ (tree_height - parent_height).times.map do |i|
72
+ if ((1<<(i + parent_height)) & position) == 0
73
+ n = Utreexo.parent(n, siblings[parent_height + i])
74
+ else
75
+ n = Utreexo.parent(siblings[parent_height + i], n)
76
+ end
77
+ n
78
+ end
79
+ end
80
+
81
+ # Get the height at which the leaves of +pos+ will be the same tree as the leaves of this proof.
82
+ # @param [Integer] pos target position
83
+ # @return [Integer] height
84
+ def same_subtree_height(pos)
85
+ raise Utreexo::Error, "pos: #{pos} does not in tree." unless (0...tree_leaves).include?(pos)
86
+ return 0 if position == pos
87
+ (tree_height + 1).times do |i|
88
+ same_group = false
89
+ groups = tree_leaves / (2 ** i)
90
+ (tree_leaves / groups).times do |j|
91
+ group = ((groups * j)...(groups * (j + 1)))
92
+ same_group = true if group.include?(position) && group.include?(pos)
93
+ end
94
+ return tree_height - (i - 1) unless same_group
95
+ end
96
+ end
97
+
30
98
  end
31
99
  end
@@ -1,3 +1,3 @@
1
1
  module Utreexo
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: utreexo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-16 00:00:00.000000000 Z
11
+ date: 2019-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: blake2b