merkle 0.3.1 → 0.4.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 +4 -4
- data/.ruby-version +1 -1
- data/lib/merkle/abstract_tree.rb +2 -1
- data/lib/merkle/custom_tree.rb +33 -61
- data/lib/merkle/proof.rb +16 -1
- data/lib/merkle/util.rb +4 -1
- data/lib/merkle/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 900af6789d1eda0ed4bf1c4d8b3575a3b7c5789d4e5a84abe43304c49122a92b
|
|
4
|
+
data.tar.gz: d250d0b1ed630a82d8aa4fe66347319172d200b1416e95260a51a54135e539fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2bd898709716daecc0b73f006e8d3367ad8a01d9e2a40fae32a1819cc62d85229ff671f38d217dd1ffef2f548be56ca485c42b097348ed95d382fde51fdc7743
|
|
7
|
+
data.tar.gz: 3b95465023c7edb109836caa261ef4a0bdd376ab042742392f7381204d715111ce0f8fab01d6fb94d7917862341d9cec7d4b14d8f55dcb34f7af755dec6ef2ad
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ruby-
|
|
1
|
+
ruby-4.0.0
|
data/lib/merkle/abstract_tree.rb
CHANGED
|
@@ -20,6 +20,7 @@ module Merkle
|
|
|
20
20
|
|
|
21
21
|
# Create tree from +elements+. For each element in elements,
|
|
22
22
|
# we compute a tagged hash, which becomes the leaf value.
|
|
23
|
+
# The resulting leaves are hex strings, the same representation as leaves passed to #initialize.
|
|
23
24
|
# @param [Merkle::Config] config Configuration for merkle tree.
|
|
24
25
|
# @param [Array] elements An array of element that will be hashed to become leaves.
|
|
25
26
|
# @param [String] leaf_tag An optional tag to use when computing the leaf hash.
|
|
@@ -28,7 +29,7 @@ module Merkle
|
|
|
28
29
|
raise ArgumentError, 'elements must be Array' unless elements.is_a?(Array)
|
|
29
30
|
raise ArgumentError, 'leaf_tag must be string' unless leaf_tag.is_a?(String)
|
|
30
31
|
leaves = elements.map do |element|
|
|
31
|
-
config.tagged_hash(element, leaf_tag)
|
|
32
|
+
config.tagged_hash(element, leaf_tag).unpack1('H*')
|
|
32
33
|
end
|
|
33
34
|
self.new(config: config, leaves: leaves)
|
|
34
35
|
end
|
data/lib/merkle/custom_tree.rb
CHANGED
|
@@ -129,77 +129,49 @@ module Merkle
|
|
|
129
129
|
|
|
130
130
|
# Override siblings_with_directions for proof generation
|
|
131
131
|
def siblings_with_directions(leaf_index)
|
|
132
|
-
all_leaves = extract_leaves(@leaves)
|
|
133
|
-
target_leaf = all_leaves[leaf_index]
|
|
134
132
|
siblings = []
|
|
135
133
|
directions = []
|
|
136
|
-
|
|
137
|
-
# Build proof by finding the path to the target leaf
|
|
138
|
-
proof_path = build_proof_path(@leaves, target_leaf)
|
|
139
|
-
|
|
140
|
-
proof_path.each do |level_info|
|
|
141
|
-
next if level_info[:siblings].empty?
|
|
142
|
-
|
|
143
|
-
level_info[:siblings].each do |sibling|
|
|
144
|
-
siblings << sibling[:hash]
|
|
145
|
-
directions << sibling[:direction]
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
134
|
+
collect_path(@leaves, leaf_index, siblings, directions)
|
|
149
135
|
[siblings, directions]
|
|
150
136
|
end
|
|
151
|
-
|
|
152
|
-
#
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
level_siblings << { hash: sibling_hash, direction: direction }
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
return child_path + [{ siblings: level_siblings }]
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
nil
|
|
174
|
-
else
|
|
175
|
-
# Leaf node
|
|
176
|
-
if node == target_leaf
|
|
177
|
-
[]
|
|
178
|
-
else
|
|
179
|
-
nil
|
|
180
|
-
end
|
|
137
|
+
|
|
138
|
+
# Walk down the structure toward the leaf at +index+ (counted within +node+), collecting the
|
|
139
|
+
# sibling hash and its direction at each branch. Siblings are collected deepest first, the
|
|
140
|
+
# order Proof#valid? folds them in.
|
|
141
|
+
# The descent is driven by the index rather than by the leaf value, so duplicate leaf hashes
|
|
142
|
+
# still yield the proof for the requested position.
|
|
143
|
+
# @param [Object] node A subtree (nested Array) or a leaf hash.
|
|
144
|
+
# @param [Integer] index The leaf index within +node+.
|
|
145
|
+
# @param [Array] siblings Collected sibling hashes(binary format).
|
|
146
|
+
# @param [Array] directions Collected directions(0: left, 1: right).
|
|
147
|
+
def collect_path(node, index, siblings, directions)
|
|
148
|
+
return unless node.is_a?(Array)
|
|
149
|
+
|
|
150
|
+
if node.length == 1
|
|
151
|
+
# Single child contributes no sibling, its hash is passed through unchanged.
|
|
152
|
+
return collect_path(node[0], index, siblings, directions)
|
|
181
153
|
end
|
|
182
|
-
end
|
|
183
154
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
end
|
|
191
|
-
nil
|
|
155
|
+
left, right = node
|
|
156
|
+
left_leaves = leaf_count(left)
|
|
157
|
+
if index < left_leaves
|
|
158
|
+
collect_path(left, index, siblings, directions)
|
|
159
|
+
siblings << compute_node_hash(right)
|
|
160
|
+
directions << 1 # sibling is on the right
|
|
192
161
|
else
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
else
|
|
197
|
-
current_index[0] += 1
|
|
198
|
-
nil
|
|
199
|
-
end
|
|
162
|
+
collect_path(right, index - left_leaves, siblings, directions)
|
|
163
|
+
siblings << compute_node_hash(left)
|
|
164
|
+
directions << 0 # sibling is on the left
|
|
200
165
|
end
|
|
201
166
|
end
|
|
202
167
|
|
|
168
|
+
# Count the leaves under +node+.
|
|
169
|
+
# @param [Object] node A subtree (nested Array) or a leaf hash.
|
|
170
|
+
# @return [Integer] Number of leaves.
|
|
171
|
+
def leaf_count(node)
|
|
172
|
+
node.is_a?(Array) ? node.sum { |child| leaf_count(child) } : 1
|
|
173
|
+
end
|
|
174
|
+
|
|
203
175
|
# Not used in custom tree - structure is determined by nested array
|
|
204
176
|
def build_next_level(nodes)
|
|
205
177
|
raise NotImplementedError, "CustomTree uses structure-based computation"
|
data/lib/merkle/proof.rb
CHANGED
|
@@ -2,21 +2,36 @@ module Merkle
|
|
|
2
2
|
class Proof
|
|
3
3
|
include Util
|
|
4
4
|
|
|
5
|
+
# Upper bound on the number of siblings, i.e. the depth of the tree the proof came from.
|
|
6
|
+
# A proof longer than this cannot correspond to any realistic tree, so it is rejected
|
|
7
|
+
# rather than hashed.
|
|
8
|
+
MAX_SIBLINGS = 64
|
|
9
|
+
|
|
5
10
|
attr_reader :config, :root, :leaf, :siblings, :directions
|
|
6
11
|
|
|
7
12
|
# Constructor.
|
|
8
13
|
# @param [Merkle::Config] config
|
|
9
14
|
# @param [String] root
|
|
10
15
|
# @param [String] leaf
|
|
11
|
-
# @param [Array] siblings
|
|
16
|
+
# @param [Array] siblings An array of sibling hashes(64-character hex strings).
|
|
12
17
|
# @param [Array] directions Array of positions at each level(0: left, 1: right),
|
|
13
18
|
# only required if sort_hashes is false in config.
|
|
14
19
|
def initialize(config:, root:, leaf:, siblings:, directions: [])
|
|
15
20
|
raise ArgumentError, 'config must be a Merkle::Config' unless config.is_a?(Merkle::Config)
|
|
16
21
|
raise ArgumentError, 'root must be string' unless root.is_a?(String)
|
|
17
22
|
raise ArgumentError, 'leaf must be string' unless leaf.is_a?(String)
|
|
23
|
+
raise ArgumentError, 'siblings must be an Array' unless siblings.is_a?(Array)
|
|
24
|
+
raise ArgumentError, "siblings must not exceed #{MAX_SIBLINGS} elements" if siblings.length > MAX_SIBLINGS
|
|
25
|
+
siblings.each do |sibling|
|
|
26
|
+
raise ArgumentError, 'sibling must be string' unless sibling.is_a?(String)
|
|
27
|
+
raise ArgumentError, 'sibling must be a 64-character hex string' unless sibling.match?(/\A[0-9a-fA-F]{64}\z/)
|
|
28
|
+
end
|
|
18
29
|
raise ArgumentError, 'directions must be an Array' unless directions.is_a?(Array)
|
|
19
30
|
raise ArgumentError, 'No directions are required because sorted_hash is enabled' if config.sort_hashes && !directions.empty?
|
|
31
|
+
unless config.sort_hashes
|
|
32
|
+
raise ArgumentError, 'directions must have the same length as siblings' unless directions.length == siblings.length
|
|
33
|
+
raise ArgumentError, 'direction must be 0 or 1' unless directions.all? { |direction| direction == 0 || direction == 1 }
|
|
34
|
+
end
|
|
20
35
|
@config = config
|
|
21
36
|
@root = root
|
|
22
37
|
@leaf = leaf
|
data/lib/merkle/util.rb
CHANGED
|
@@ -2,12 +2,15 @@ module Merkle
|
|
|
2
2
|
module Util
|
|
3
3
|
|
|
4
4
|
# Check whether +data+ is hex string or not.
|
|
5
|
+
# An odd-length string is not a hex string. Treating it as one would let
|
|
6
|
+
# +pack('H*')+ pad the missing nibble with zero, so 'abc' and 'abc0' would
|
|
7
|
+
# collide.
|
|
5
8
|
# @param [String] data
|
|
6
9
|
# @return [Boolean]
|
|
7
10
|
# @raise [ArgumentError]
|
|
8
11
|
def hex_string?(data)
|
|
9
12
|
raise ArgumentError, 'data must be string' unless data.is_a?(String)
|
|
10
|
-
data.match?(/\A[0-9a-fA-F]+\z/)
|
|
13
|
+
data.length.even? && data.match?(/\A[0-9a-fA-F]+\z/)
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
# Convert hex string +data+ to binary.
|
data/lib/merkle/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: merkle
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- azuchi
|
|
@@ -54,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
55
|
version: '0'
|
|
56
56
|
requirements: []
|
|
57
|
-
rubygems_version:
|
|
57
|
+
rubygems_version: 4.0.3
|
|
58
58
|
specification_version: 4
|
|
59
59
|
summary: A Ruby library for Merkle trees
|
|
60
60
|
test_files: []
|