mssmt 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2bf77bb8efa4e33fe73b3bd1b54e577e02097fadda8420924caf8df0ad9df21
4
- data.tar.gz: a5715d5289504147a0b76f8c3935910df965cae05df2a284136735389f8daa27
3
+ metadata.gz: e99668bb044c1670cee5d94e301f8113321c04ce9ac06b697fcfa8eabf1668f9
4
+ data.tar.gz: 7928cf9abd46c85c505dc770c2cbcce33e82ce98c2f7f772872792366bcd6eaa
5
5
  SHA512:
6
- metadata.gz: 30e1c75109384039d6d87ed9702e7bfbe2848e499285460b67da66dd80668e32590ce409d5deedd64a08ed5cae6f558d40817c73275a271c0b92c6a0f1cab8e0
7
- data.tar.gz: 6231535740549dfdb317e122945464ec92789c24479f164058ebdb06a3bfb2cabd688a0b6c0eca4c48881e9a891f5b72532a7609b4ec27edf132f9986f6ad558
6
+ metadata.gz: 1b0b1648a73caed3a29c764c688b8077dea1beaa65da3772429470fc1ca1eab352b11128538e0e3b4daa2e8c578452360a562a08bd69bebe3bebadd228070f74
7
+ data.tar.gz: 653ad3b9270105b25a9dfea42414918cc0640cdc9359a7c9c01c712ee503f6689d81ee74796510099c3827fae53209fd2a7325392a36541f64b39447482d6e30
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)
@@ -16,10 +16,19 @@ module MSSMT
16
16
  @left = left
17
17
  @right = right
18
18
  @sum = left.sum + right.sum
19
+ warn("sum:#{@sum} cause overflow.") if @sum > 0xffffffffffffffff # TODO
20
+ @sum = (@sum & 0xffffffffffffffff)
19
21
  @node_hash =
20
22
  Digest::SHA256.digest(
21
23
  "#{left.node_hash}#{right.node_hash}#{[@sum].pack("Q>")}"
22
24
  )
23
25
  end
26
+
27
+ # Check whether same branch|computed node or not.
28
+ # @return [Boolean]
29
+ def ==(other)
30
+ return false unless [BranchNode, ComputedNode].include?(other.class)
31
+ node_hash == other.node_hash && sum == other.sum
32
+ end
24
33
  end
25
34
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MSSMT
4
+ # Node within a MS-SMT that has already had its node_hash and sum computed, i.e., its preimage is not available.
5
+ class ComputedNode
6
+ attr_reader :node_hash, :sum
7
+
8
+ # Constructor
9
+ # @param [String] node_hash node hash with binary fomat.
10
+ # @param [Integer] sum
11
+ def initialize(node_hash, sum)
12
+ @node_hash = node_hash
13
+ warn("sum: #{sum} cause overflow.") if sum > 0xffffffffffffffff # TODO
14
+ @sum = sum
15
+ end
16
+
17
+ def ==(other)
18
+ return false unless [BranchNode, ComputedNode].include?(other.class)
19
+ node_hash == other.node_hash && sum == other.sum
20
+ end
21
+ end
22
+ end
@@ -10,7 +10,8 @@ module MSSMT
10
10
  # @param [Integer] sum integer value associated with the value
11
11
  def initialize(value, sum)
12
12
  @value = value
13
- @sum = sum
13
+ warn("sum: #{sum} cause overflow.") if sum > 0xffffffffffffffff # TODO
14
+ @sum = sum & 0xffffffffffffffff
14
15
  end
15
16
 
16
17
  # Generate empty leaf node.
@@ -30,5 +31,10 @@ module MSSMT
30
31
  def empty?
31
32
  (value.nil? or value.empty?) && sum.zero?
32
33
  end
34
+
35
+ def ==(other)
36
+ return false unless other.is_a?(LeafNode)
37
+ node_hash == other.node_hash
38
+ end
33
39
  end
34
40
  end
@@ -0,0 +1,125 @@
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
+ if node.node_hash == Tree.empty_tree[Tree::MAX_LEVEL - i].node_hash
21
+ bits[i] = true
22
+ else
23
+ compact_nodes << node
24
+ end
25
+ end
26
+ CompressedProof.new(compact_nodes, bits)
27
+ end
28
+
29
+ def ==(other)
30
+ return false unless other.is_a?(Proof)
31
+ nodes == other.nodes
32
+ end
33
+ end
34
+
35
+ # Compressed MS-SMT merkle proof.
36
+ # Since merkle proofs for a MS-SMT are always constant size (255 nodes),
37
+ # we replace its empty nodes by a bit vector.
38
+ class CompressedProof < Proof
39
+ attr_reader :bits
40
+
41
+ # Constructor
42
+ # @param [Array(MSSMT::BranchNode|MSSMT::LeafNode)] nodes Siblings that should be hashed with the leaf and
43
+ # its parents to arrive at the root of the MS-SMT.
44
+ # @param [Array] bits +bits+ determines whether a sibling node within a proof is part of the empty tree.
45
+ # This allows us to efficiently compress proofs by not including any pre-computed nodes.
46
+ def initialize(nodes, bits)
47
+ super(nodes)
48
+ @bits = bits
49
+ end
50
+
51
+ # Decode compressed proof.
52
+ # @param [String] compressed proof with binary fomat.
53
+ # @return [MSSMT::CompressedProof]
54
+ def self.decode(data)
55
+ buf = data.is_a?(StringIO) ? data : StringIO.new(data)
56
+ nodes_len = buf.read(2).unpack1("n")
57
+ nodes =
58
+ nodes_len.times.map do
59
+ ComputedNode.new(buf.read(32), buf.read(8).unpack1("Q>"))
60
+ end
61
+ bytes = buf.read(MSSMT::Tree::MAX_LEVEL / 8)
62
+ bits = unpack_bits(bytes.unpack("C*"))
63
+ CompressedProof.new(nodes, bits)
64
+ end
65
+
66
+ # Decompress a compressed merkle proof by replacing its bit vector with the empty nodes it represents.
67
+ # @return [MSSMT::Proof]
68
+ def decompress
69
+ count = 0
70
+ full_nodes = []
71
+ bits.each.with_index do |b, i|
72
+ if b
73
+ full_nodes << Tree.empty_tree[Tree::MAX_LEVEL - i]
74
+ else
75
+ full_nodes << nodes[count]
76
+ count += 1
77
+ end
78
+ end
79
+ Proof.new(full_nodes)
80
+ end
81
+
82
+ # Encode the compressed proof.
83
+ # @return [String] encoded string.
84
+ def encode
85
+ buf = [nodes.length].pack("n")
86
+ nodes.each do |node|
87
+ buf << node.node_hash
88
+ buf << [node.sum].pack("Q>")
89
+ end
90
+ buf << pack_bits.pack("C*")
91
+ buf
92
+ end
93
+
94
+ def ==(other)
95
+ return false unless other.is_a?(CompressedProof)
96
+ bits == other.bits && nodes == other.nodes
97
+ end
98
+
99
+ private
100
+
101
+ def pack_bits
102
+ bytes = Array.new((bits.length + 8 - 1) / 8, 0)
103
+ bits.each_with_index do |b, i|
104
+ next unless b
105
+ byte_index = i / 8
106
+ bit_index = i % 8
107
+ bytes[byte_index] |= (1 << bit_index)
108
+ end
109
+ bytes
110
+ end
111
+
112
+ def self.unpack_bits(bytes)
113
+ bit_len = bytes.length * 8
114
+ bits = Array.new(bit_len, false)
115
+ bit_len.times do |i|
116
+ byte_index = i / 8
117
+ bit_index = i % 8
118
+ bits[i] = ((bytes[byte_index] >> bit_index) & 1) == 1
119
+ end
120
+ bits
121
+ end
122
+
123
+ private_class_method :unpack_bits
124
+ end
125
+ 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.3.0"
5
5
  end
data/lib/mssmt.rb CHANGED
@@ -11,5 +11,8 @@ module MSSMT
11
11
  autoload :Store, "mssmt/store"
12
12
  autoload :LeafNode, "mssmt/leaf_node"
13
13
  autoload :BranchNode, "mssmt/branch_node"
14
+ autoload :ComputedNode, "mssmt/computed_node"
14
15
  autoload :Tree, "mssmt/tree"
16
+ autoload :Proof, "mssmt/proof"
17
+ autoload :CompressedProof, "mssmt/proof"
15
18
  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.3.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-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby implementation of Merkle Sum Sparse Merkle Tree
14
14
  email:
@@ -32,7 +32,9 @@ files:
32
32
  - bin/setup
33
33
  - lib/mssmt.rb
34
34
  - lib/mssmt/branch_node.rb
35
+ - lib/mssmt/computed_node.rb
35
36
  - lib/mssmt/leaf_node.rb
37
+ - lib/mssmt/proof.rb
36
38
  - lib/mssmt/store.rb
37
39
  - lib/mssmt/store/default_store.rb
38
40
  - lib/mssmt/tree.rb