mssmt 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: c2bf77bb8efa4e33fe73b3bd1b54e577e02097fadda8420924caf8df0ad9df21
4
- data.tar.gz: a5715d5289504147a0b76f8c3935910df965cae05df2a284136735389f8daa27
3
+ metadata.gz: bc50f962065de3f0ceaeebaa7715ab5e32d03a882d7df11022e6e4bc90b459d1
4
+ data.tar.gz: 020cb3cfedb35c8c3a6cd123d9b772e666e9b9fe19f301c07acc6ca27a193ca6
5
5
  SHA512:
6
- metadata.gz: 30e1c75109384039d6d87ed9702e7bfbe2848e499285460b67da66dd80668e32590ce409d5deedd64a08ed5cae6f558d40817c73275a271c0b92c6a0f1cab8e0
7
- data.tar.gz: 6231535740549dfdb317e122945464ec92789c24479f164058ebdb06a3bfb2cabd688a0b6c0eca4c48881e9a891f5b72532a7609b4ec27edf132f9986f6ad558
6
+ metadata.gz: 468f823bbba1cff9549b5977a98270ec215e3ccf7b8057bcdd0642fd99da280233a0a821254fc2fb4c294d638ccaa6443cf04abd8a99fd4e61de6f2c51f169a1
7
+ data.tar.gz: 17f30d1180c82d4b4862c752b842e725e5d9527f1ad7f80b5e8eeb3e0d2573b127531afbfda474017103b97b21acbf3d1f1e891398686fdf06256dd3d246dc06
data/README.md CHANGED
@@ -36,9 +36,9 @@ tree.insert(key, leaf)
36
36
  # Get root node
37
37
  root_node = tree.root_node
38
38
  # Root hash
39
- root_hash = tree.root_hash
39
+ root_hash = tree.root_hash.unpack1('H*')
40
40
  # or
41
- root_node.node_hash
41
+ root_node.node_hash.unpack1('H*')
42
42
 
43
43
  # Get leaf node in tree
44
44
  leaf = tree.get(key)
@@ -21,5 +21,10 @@ module MSSMT
21
21
  "#{left.node_hash}#{right.node_hash}#{[@sum].pack("Q>")}"
22
22
  )
23
23
  end
24
+
25
+ def ==(other)
26
+ return false unless other.is_a?(BranchNode)
27
+ node_hash == other.node_hash
28
+ end
24
29
  end
25
30
  end
@@ -30,5 +30,10 @@ module MSSMT
30
30
  def empty?
31
31
  (value.nil? or value.empty?) && sum.zero?
32
32
  end
33
+
34
+ def ==(other)
35
+ return false unless other.is_a?(LeafNode)
36
+ node_hash == other.node_hash
37
+ end
33
38
  end
34
39
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+ module MSSMT
3
+ # Merkle proof for MS-SMT.
4
+ class Proof
5
+ attr_reader :nodes
6
+
7
+ # Constructor
8
+ # @param [Array(MSSMT::BranchNode|MSSMT::LeafNode)] nodes Siblings that should be hashed with the leaf and
9
+ # its parents to arrive at the root of the MS-SMT.
10
+ def initialize(nodes)
11
+ @nodes = nodes
12
+ end
13
+
14
+ # Compresses a merkle proof by replacing its empty nodes with a bit vector.
15
+ # @return [MSSMT::CompressedProof]
16
+ def compress
17
+ bits = Array.new(nodes.length, false)
18
+ compact_nodes = []
19
+ nodes.each.each_with_index do |node, i|
20
+ # puts "#{node.node_hash}:#{Tree.empty_tree[Tree::MAX_LEVEL - 1].node_hash}"
21
+ if node.node_hash == Tree.empty_tree[Tree::MAX_LEVEL - 1].node_hash
22
+ bits[i] = true
23
+ else
24
+ compact_nodes << node
25
+ end
26
+ end
27
+ CompressedProof.new(compact_nodes, bits)
28
+ end
29
+
30
+ def ==(other)
31
+ return false unless other.is_a?(Proof)
32
+ nodes == other.nodes
33
+ end
34
+ end
35
+
36
+ # Compressed MS-SMT merkle proof.
37
+ # Since merkle proofs for a MS-SMT are always constant size (255 nodes),
38
+ # we replace its empty nodes by a bit vector.
39
+ class CompressedProof < Proof
40
+ attr_reader :bits
41
+
42
+ # Constructor
43
+ # @param [Array(MSSMT::BranchNode|MSSMT::LeafNode)] nodes Siblings that should be hashed with the leaf and
44
+ # its parents to arrive at the root of the MS-SMT.
45
+ # @param [Array] bits +bits+ determines whether a sibling node within a proof is part of the empty tree.
46
+ # This allows us to efficiently compress proofs by not including any pre-computed nodes.
47
+ def initialize(nodes, bits)
48
+ super(nodes)
49
+ @bits = bits
50
+ end
51
+
52
+ # Decompress a compressed merkle proof by replacing its bit vector with the empty nodes it represents.
53
+ # @return [MSSMT::Proof]
54
+ def decompress
55
+ count = 0
56
+ full_nodes = []
57
+ bits.each.with_index do |b, i|
58
+ if b
59
+ full_nodes << Tree.empty_tree[Tree::MAX_LEVEL - i]
60
+ else
61
+ full_nodes << nodes[count]
62
+ count += 1
63
+ end
64
+ end
65
+ Proof.new(full_nodes)
66
+ end
67
+ end
68
+ end
data/lib/mssmt/tree.rb CHANGED
@@ -10,10 +10,29 @@ module MSSMT
10
10
  # Index of the last bit for MS-SMT keys
11
11
  LAST_BIT_INDEX = MAX_LEVEL - 1
12
12
 
13
- attr_reader :empty_tree, :store
13
+ attr_reader :store
14
+
15
+ def self.empty_tree
16
+ @empty_tree ||= build_empty_tree
17
+ end
18
+
19
+ # Generate empty tree.
20
+ # @return [Array]
21
+ def self.build_empty_tree
22
+ tree = Array.new(MSSMT::Tree::MAX_LEVEL + 1)
23
+ tree[MSSMT::Tree::MAX_LEVEL] = MSSMT::LeafNode.empty_leaf
24
+ MSSMT::Tree::MAX_LEVEL.times do |i|
25
+ branch =
26
+ MSSMT::BranchNode.new(
27
+ tree[MSSMT::Tree::MAX_LEVEL - i],
28
+ tree[MSSMT::Tree::MAX_LEVEL - i]
29
+ )
30
+ tree[MSSMT::Tree::MAX_LEVEL - (i + 1)] = branch
31
+ end
32
+ tree.freeze
33
+ end
14
34
 
15
35
  def initialize(store: MSSMT::Store::DefaultStore.new)
16
- @empty_tree = build_empty_tree
17
36
  @store = store
18
37
  end
19
38
 
@@ -26,7 +45,7 @@ module MSSMT
26
45
  # Get root node in tree.
27
46
  # @return [MSSMT::BranchNode]
28
47
  def root_node
29
- store.root.nil? ? empty_tree[0] : store.root
48
+ store.root.nil? ? Tree.empty_tree[0] : store.root
30
49
  end
31
50
 
32
51
  # Insert a leaf node at the given key within the MS-SMT.
@@ -49,10 +68,10 @@ module MSSMT
49
68
  root =
50
69
  walk_up(key, leaf, siblings) do |i, _, _, parent|
51
70
  prev_parent = prev_parents[MSSMT::Tree::MAX_LEVEL - 1 - i]
52
- unless prev_parent == empty_tree[i].node_hash
71
+ unless prev_parent == Tree.empty_tree[i].node_hash
53
72
  store.delete_branch(prev_parent)
54
73
  end
55
- unless parent.node_hash == empty_tree[i].node_hash
74
+ unless parent.node_hash == Tree.empty_tree[i].node_hash
56
75
  store.insert_branch(parent)
57
76
  end
58
77
  end
@@ -76,19 +95,19 @@ module MSSMT
76
95
 
77
96
  # Generate a merkle proof for the leaf node found at the given +key+.
78
97
  # @param [String] key key with hex format.
79
- # @return [Array] merkle proof
98
+ # @return [MSSMT::Proof] merkle proof
80
99
  def merkle_proof(key)
81
100
  proof = Array.new(MAX_LEVEL)
82
101
  walk_down(key) { |i, _, sibling, _| proof[MAX_LEVEL - 1 - i] = sibling }
83
- proof
102
+ MSSMT::Proof.new(proof)
84
103
  end
85
104
 
86
105
  # Verify whether a merkle proof for the leaf found at the given key is valid.
87
106
  # @param [String] key key with hex format.
88
107
  # @param [MSSMT::LeafNode] leaf leaf node.
89
- # @param [Array] proof merkle proof.
108
+ # @param [MSSMT::Proof] proof merkle proof.
90
109
  def valid_merkle_proof?(key, leaf, proof)
91
- root_hash == walk_up(key, leaf, proof).node_hash
110
+ root_hash == walk_up(key, leaf, proof.nodes).node_hash
92
111
  end
93
112
 
94
113
  private
@@ -110,7 +129,7 @@ module MSSMT
110
129
  end
111
130
 
112
131
  def get_node(height, key)
113
- return empty_tree[height] if empty_tree[height].node_hash == key
132
+ return Tree.empty_tree[height] if Tree.empty_tree[height].node_hash == key
114
133
  store.branches[key] || store.leaves[key]
115
134
  end
116
135
 
@@ -152,21 +171,5 @@ module MSSMT
152
171
  value = [key].pack("H*")[idx / 8].ord
153
172
  (value >> (idx % 8)) & 1
154
173
  end
155
-
156
- # Generate empty tree.
157
- # @return [Array]
158
- def build_empty_tree
159
- tree = Array.new(MSSMT::Tree::MAX_LEVEL + 1)
160
- tree[MSSMT::Tree::MAX_LEVEL] = MSSMT::LeafNode.empty_leaf
161
- MSSMT::Tree::MAX_LEVEL.times do |i|
162
- branch =
163
- MSSMT::BranchNode.new(
164
- tree[MSSMT::Tree::MAX_LEVEL - i],
165
- tree[MSSMT::Tree::MAX_LEVEL - i]
166
- )
167
- tree[MSSMT::Tree::MAX_LEVEL - (i + 1)] = branch
168
- end
169
- tree
170
- end
171
174
  end
172
175
  end
data/lib/mssmt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MSSMT
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/mssmt.rb CHANGED
@@ -12,4 +12,6 @@ module MSSMT
12
12
  autoload :LeafNode, "mssmt/leaf_node"
13
13
  autoload :BranchNode, "mssmt/branch_node"
14
14
  autoload :Tree, "mssmt/tree"
15
+ autoload :Proof, "mssmt/proof"
16
+ autoload :CompressedProof, "mssmt/proof"
15
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mssmt
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: 2022-10-17 00:00:00.000000000 Z
11
+ date: 2022-10-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby implementation of Merkle Sum Sparse Merkle Tree
14
14
  email:
@@ -33,6 +33,7 @@ files:
33
33
  - lib/mssmt.rb
34
34
  - lib/mssmt/branch_node.rb
35
35
  - lib/mssmt/leaf_node.rb
36
+ - lib/mssmt/proof.rb
36
37
  - lib/mssmt/store.rb
37
38
  - lib/mssmt/store/default_store.rb
38
39
  - lib/mssmt/tree.rb