merkle_tree 0.1.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.
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree, '#leaves' do
4
+ it "returns empty array when tree has no leaves" do
5
+ merkle_tree = MerkleTree.new
6
+
7
+ expect(merkle_tree.leaves).to be_empty
8
+ end
9
+
10
+ it "returns all leaves" do
11
+ leaves = ["L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8"]
12
+ merkle_tree = MerkleTree.new(*leaves)
13
+
14
+ expect(merkle_tree.leaves.size).to eq(leaves.size)
15
+ end
16
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree, '#new' do
4
+ it "creates tree from even number of messages" do
5
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
6
+
7
+ expect(merkle_tree.to_h).to eq({
8
+ root: {
9
+ value: "63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439",
10
+ left: {
11
+ value: "f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c",
12
+ left: {
13
+ value: "dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0"
14
+ },
15
+ right: {
16
+ value: "d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d"
17
+ }
18
+ },
19
+ right: {
20
+ value: "8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a",
21
+ left: {
22
+ value: "842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80"
23
+ },
24
+ right: {
25
+ value: "4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c"
26
+ }
27
+ }
28
+ }
29
+ })
30
+ end
31
+
32
+ it "creates tree from odd number of messages by duplicating the last message" do
33
+ merkle_tree = MerkleTree.new("L1", "L2", "L3")
34
+
35
+ expect(merkle_tree.to_h).to eq({
36
+ root: {
37
+ value: "bdb1b6778b2923c883a078a6d8dbf40f99bb1a58bf5f650349f965bd8a222f43",
38
+ left: {
39
+ value: "f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c",
40
+ left: {
41
+ value: "dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0"
42
+ },
43
+ right: {
44
+ value: "d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d"
45
+ }
46
+ },
47
+ right: {
48
+ value: "5ca8ce04894dcfaacfe7b77d5f003b355ca0df2e0055d6c9fa3b006a8e56a2ba",
49
+ left: {
50
+ value: "842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80"
51
+ },
52
+ right: {
53
+ value: "842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80"
54
+ }
55
+ }
56
+ }
57
+ })
58
+ end
59
+
60
+ it "changes hashing function" do
61
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4",
62
+ digest: -> (val) { "(#{val}h)"})
63
+
64
+ expect(merkle_tree.to_h).to eq({
65
+ root: {
66
+ value: "(((L1h)(L2h)h)((L3h)(L4h)h)h)",
67
+ left: {
68
+ value: "((L1h)(L2h)h)",
69
+ left: { value: "(L1h)" },
70
+ right: { value: "(L2h)" },
71
+ },
72
+ right: {
73
+ value: "((L3h)(L4h)h)",
74
+ left: { value: "(L3h)" },
75
+ right: { value: "(L4h)" }
76
+ }
77
+ }
78
+ })
79
+ end
80
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree::Node, '::build' do
4
+ it "combines two leaf nodes" do
5
+ nodes = [
6
+ MerkleTree::Leaf.build("L1", 0),
7
+ MerkleTree::Leaf.build("L2", 1)
8
+ ]
9
+
10
+ node = MerkleTree::Node.build(*nodes)
11
+
12
+ expect(node.value).to eq('f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c')
13
+ expect(node.left).to eq(nodes[0])
14
+ expect(node.right).to eq(nodes[1])
15
+ expect(node.left_index).to eq(0)
16
+ expect(node.right_index).to eq(1)
17
+ end
18
+
19
+ it "combines leaf node with empty node" do
20
+ leaf = MerkleTree::Leaf.build("L1", 0)
21
+ nodes = [
22
+ leaf,
23
+ MerkleTree::Node::EMPTY
24
+ ]
25
+
26
+ node = MerkleTree::Node.build(*nodes)
27
+
28
+ expect(node.value).to eq("15253c068a787616f4a6580d34a099f5bde3991f771a5c8a7841638db7e69e24")
29
+
30
+ expect(node.left_index).to eq(0)
31
+ expect(node.right_index).to eq(MerkleTree::Node::UNDEFINED)
32
+ end
33
+
34
+ it "combines 2 nodes" do
35
+ nodes_left = [
36
+ MerkleTree::Leaf.build("L1", 0),
37
+ MerkleTree::Leaf.build("L2", 1)
38
+ ]
39
+
40
+ nodes_right = [
41
+ MerkleTree::Leaf.build("L3", 2),
42
+ MerkleTree::Leaf.build("L4", 3)
43
+ ]
44
+
45
+ node_left = MerkleTree::Node.build(*nodes_left)
46
+ node_right = MerkleTree::Node.build(*nodes_right)
47
+
48
+ node = MerkleTree::Node.build(node_left, node_right)
49
+
50
+ expect(node.value).to eq("63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439")
51
+ expect(node.left).to eq(node_left)
52
+ expect(node.right).to eq(node_right)
53
+ expect(node.left_index).to eq(0)
54
+ expect(node.right_index).to eq(3)
55
+ end
56
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree::Node, '#==' do
4
+ it "compares two different nodes" do
5
+ nodes_left = [
6
+ MerkleTree::Leaf.build("L1", 0),
7
+ MerkleTree::Leaf.build("L2", 1)
8
+ ]
9
+
10
+ nodes_right = [
11
+ MerkleTree::Leaf.build("L3", 2),
12
+ MerkleTree::Leaf.build("L4", 3)
13
+ ]
14
+
15
+ node_left = MerkleTree::Node.build(*nodes_left)
16
+ node_right = MerkleTree::Node.build(*nodes_right)
17
+
18
+ expect(node_left).to_not eq(node_right)
19
+ end
20
+
21
+ it "compares same node" do
22
+ nodes = [
23
+ MerkleTree::Leaf.build("L1", 0),
24
+ MerkleTree::Leaf.build("L2", 1)
25
+ ]
26
+
27
+ node = MerkleTree::Node.build(*nodes)
28
+
29
+ expect(node).to eq(node)
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree, '#root' do
4
+ it "returns empty root when no messages" do
5
+ merkle_tree = MerkleTree.new
6
+
7
+ expect(merkle_tree.root).to eq(MerkleTree::Node::EMPTY)
8
+ end
9
+
10
+ it "calculates root signature" do
11
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
12
+
13
+ expect(merkle_tree.root.value).to eq("63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439")
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree, '#size' do
4
+ it "returns 0 when tree has no messages" do
5
+ merkle_tree = MerkleTree.new
6
+
7
+ expect(merkle_tree.size).to eq(0)
8
+ end
9
+
10
+ it "returns 15 when tree has 8 messages" do
11
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8")
12
+
13
+ expect(merkle_tree.size).to eq(15)
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree, '#subtree' do
4
+ it "return empty node for non existent index" do
5
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
6
+
7
+ expect(merkle_tree.subtree(10)).to eq(MerkleTree::Node::EMPTY)
8
+ end
9
+
10
+ it "extracts subtree for a given index" do
11
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8")
12
+
13
+ expect(merkle_tree.subtree(2).to_h).to eq({
14
+ value: "63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439",
15
+ left: {
16
+ value: "f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c",
17
+ left: {
18
+ value: "dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0",
19
+ },
20
+ right: {
21
+ value: "d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d",
22
+ }
23
+ },
24
+ right: {
25
+ value: "8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a",
26
+ left: {
27
+ value: "842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80",
28
+ },
29
+ right: {
30
+ value: "4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c"
31
+ }
32
+ }
33
+ })
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree, '#to_s' do
4
+ it "prints all tree signatures indented by 2 spaces" do
5
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
6
+
7
+ expect(merkle_tree.to_s).to eq([
8
+ "63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439",
9
+ " f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c",
10
+ " dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0",
11
+ " d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d",
12
+ " 8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a",
13
+ " 842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80",
14
+ " 4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c"
15
+ ].join("\n"))
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MerkleTree, '#update' do
4
+ it "updates a leaf at index position with a new message" do
5
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
6
+ updated_merkle_tree = MerkleTree.new("L1", "L2", "L3*", "L4")
7
+ expected_leaf = MerkleTree::Leaf.build("L3*", 2)
8
+
9
+ updated_leaf = merkle_tree.update("L3*", 2)
10
+
11
+ expect(updated_leaf.value).to eq(expected_leaf.value)
12
+ expect(merkle_tree.root.value).to eq(updated_merkle_tree.root.value)
13
+ end
14
+
15
+ it "updates a leaf in tree with 8 messages with a new message" do
16
+ merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8")
17
+ updated_merkle_tree = MerkleTree.new("L1", "L2", "L3*", "L4", "L5", "L6", "L7", "L8")
18
+ expected_leaf = MerkleTree::Leaf.build("L3*", 2)
19
+
20
+ updated_leaf = merkle_tree.update("L3*", 2)
21
+
22
+ expect(updated_leaf.value).to eq(expected_leaf.value)
23
+ expect(merkle_tree.root.value).to eq(updated_merkle_tree.root.value)
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ desc 'Load gem inside irb console'
2
+ task :console do
3
+ require 'irb'
4
+ require 'irb/completion'
5
+ require File.join(__FILE__, '../../lib/merkle_tree')
6
+ ARGV.clear
7
+ IRB.start
8
+ end
9
+ task c: %w[ console ]
@@ -0,0 +1,9 @@
1
+ desc 'Measure code coverage'
2
+ task :coverage do
3
+ begin
4
+ original, ENV['COVERAGE'] = ENV['COVERAGE'], 'true'
5
+ Rake::Task['spec'].invoke
6
+ ensure
7
+ ENV['COVERAGE'] = original
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Run all specs'
5
+ RSpec::Core::RakeTask.new(:spec) do |task|
6
+ task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb'
7
+ end
8
+
9
+ namespace :spec do
10
+ desc 'Run unit specs'
11
+ RSpec::Core::RakeTask.new(:unit) do |task|
12
+ task.pattern = 'spec/unit{,/*/**}/*_spec.rb'
13
+ end
14
+
15
+ desc 'Run integration specs'
16
+ RSpec::Core::RakeTask.new(:integration) do |task|
17
+ task.pattern = 'spec/integration{,/*/**}/*_spec.rb'
18
+ end
19
+
20
+ desc 'Run integration specs'
21
+ RSpec::Core::RakeTask.new(:perf) do |task|
22
+ task.pattern = 'spec/perf{,/*/**}/*_spec.rb'
23
+ end
24
+ end
25
+
26
+ rescue LoadError
27
+ %w[spec spec:unit spec:integration].each do |name|
28
+ task name do
29
+ $stderr.puts "In order to run #{name}, do `gem install rspec`"
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: merkle_tree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Piotr Murach
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-03-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-benchmark
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A binary tree of one-time singatures known as a merkle tree. Often used
70
+ in distributed systems such as Git, Cassandra or Bitcoin for efficiently summarizing
71
+ sets of data.
72
+ email:
73
+ - me@piotrmurach.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - CHANGELOG.md
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/setup
84
+ - lib/merkle_tree.rb
85
+ - lib/merkle_tree/leaf.rb
86
+ - lib/merkle_tree/node.rb
87
+ - lib/merkle_tree/version.rb
88
+ - merkle_tree.gemspec
89
+ - spec/perf/speed_spec.rb
90
+ - spec/spec_helper.rb
91
+ - spec/unit/add_spec.rb
92
+ - spec/unit/auth_path_spec.rb
93
+ - spec/unit/empty_spec.rb
94
+ - spec/unit/height_spec.rb
95
+ - spec/unit/include_spec.rb
96
+ - spec/unit/leaf/build_spec.rb
97
+ - spec/unit/leaf/eql_spec.rb
98
+ - spec/unit/leaves_spec.rb
99
+ - spec/unit/new_spec.rb
100
+ - spec/unit/node/build_spec.rb
101
+ - spec/unit/node/eql_spec.rb
102
+ - spec/unit/root_spec.rb
103
+ - spec/unit/size_spec.rb
104
+ - spec/unit/subtree_spec.rb
105
+ - spec/unit/to_s_spec.rb
106
+ - spec/unit/update_spec.rb
107
+ - tasks/console.rake
108
+ - tasks/coverage.rake
109
+ - tasks/spec.rake
110
+ homepage: https://github.com/piotrmurach/merkle_tree
111
+ licenses:
112
+ - MIT
113
+ metadata:
114
+ homepage_uri: https://github.com/piotrmurach/merkle_tree
115
+ source_code_uri: https://github.com/piotrmurach/merkle_tree
116
+ changelog_uri: https://github.com/piotrmurach/merkle_tree/blob/master/CHANGELOG.md
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.7.3
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: A binary tree of one-time signatures known as a merkle tree.
137
+ test_files: []