README.md
# MerkleTree
[![Gem Version](https://badge.fury.io/rb/merkle_tree.svg)][gem]
[![Actions CI](https://github.com/piotrmurach/merkle_tree/workflows/CI/badge.svg?branch=master)][gh_actions_ci]
[![Build status](https://ci.appveyor.com/api/projects/status/kcbi55cyq2wlfuhc?svg=true)][appveyor]
[![Maintainability](https://api.codeclimate.com/v1/badges/ce00667c8785a62cd892/maintainability)][codeclimate]
[![Coverage Status](https://coveralls.io/repos/piotrmurach/merkle_tree/badge.svg)][coverage]
[![Inline docs](https://inch-ci.org/github/piotrmurach/merkle_tree.svg?branch=master)][inchpages]
[gem]: https://badge.fury.io/rb/merkle_tree
[gh_actions_ci]: https://github.com/piotrmurach/merkle_tree/actions?query=workflow%3ACI
[appveyor]: https://ci.appveyor.com/project/piotrmurach/merkle-tree
[codeclimate]: https://codeclimate.com/github/piotrmurach/merkle_tree/maintainability
[coverage]: https://coveralls.io/r/piotrmurach/merkle_tree
[inchpages]: https://inch-ci.org/github/piotrmurach/merkle_tree
> A binary tree of one-time signatures known as merkle tree. Often used in distributed systems such as Git, Cassandra or Bitcoin for efficiently summarizing sets of data.
A binary tree originally developed to authenticate a large number of public keys with a single value, namely the root of the tree. The merkle root is usually available publicly. Each node in the tree contains a cryptographic hash of node values of its children. The N values/messages that need to be authenticated are placed at the N leaves of the tree. A leaf can store an arbitrary value, usually a public authentication key, that is a cryptographic hash of the value that needs to be authenticated. A leaf can be then verified by publicly available merkle tree root value and its authentication path.
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'merkle_tree'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install merkle_tree
## Contents
* [1. Usage](#1-usage)
* [2. API](#2-api)
* [2.1 root](#21-root)
* [2.2 leaves](#22-leaves)
* [2.3 subtree](#23-subtree)
* [2.4 height](#24-height)
* [2.5 size](#25-size)
* [2.6 include?](#26-include)
* [2.7 add](#27-add)
* [2.8 update](#28-update)
* [2.9 to_h](#29-to_h)
* [2.10 to_s](#210-to_s)
* [2.11 :digest](#211-digest)
* [3. See Also](#3-see-also)
## 1. Usage
Create a new *MerkleTree* by passing all the messages to be hashed:
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
```
Then you can use the tree to verify if a message belongs:
```ruby
merkle_tree.include?("L3", 2) # => true
```
Or change the message to a new content:
```ruby
merkle_tree.update("L3*", 2)
```
You can also check the tree height:
```ruby
merkle_tree.height # => 2
```
And how many nodes it has:
```ruby
merkle_tree.size # => 7
```
Finally, you can print the contents of the tree to see all the signatures:
```ruby
merkle_tree.to_s
# =>
# 63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439
# f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c
# dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0
# d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d
# 8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a
# 842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80
# 4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c
```
## 2. API
### 2.1 root
To access the root node of the merkle tree use the `root` method that will return the tree structure with all its children and their signatures.
For example, given a tree with 4 messages
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
```
A full tree of one-time signatures will be available:
```ruby
merkle_tree.root
# =>
# MerkleTree::Node
# @value="63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439"
# @left=MerkleTree::Node
# @value="f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c"
# @left=MerkleTree::Leaf
# @value="dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0"
# @right=MerkleTree::Leaf
# @value="d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d"
# @right=MerkleTree::Node
# @value="8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a"
# @left=
# MerkleTree::Leaf
# @value="842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80"
# @right=MerkleTree::Leaf
# @value="4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c"
```
Since the root is a node you can retrieve it's signature using the `value` call:
```ruby
merkle_tree.root.value
# => "63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439"
```
### 2.2 leaves
You can access all the leaves and their one-time signatures using the `leaves` method:
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
merkle_tree.leaves
# =>
# [
# <MerkleTree::Leaf @value="dffe8596...", @left_index=0, @right_index=0, @height=0>,
# <MerkleTree::Leaf @value="d76354d8...", @left_index=1, @right_index=1, @height=0>,
# <MerkleTree::Leaf @value="842983de...", @left_index=2, @right_index=2, @height=0>,
# <MerkleTree::Leaf @value="4a5a97c6...", @left_index=3, @right_index=3, @height=0>
# ]
```
### 2.3 subtree
To access a part of Merkle tree use `subtree` with the index of the one-time signature:
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8")
merkle_tree.subtree(2).to_h
# =>
# {
# value: "63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439",
# left: {
# value: "f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c",
# left: { value: "dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0" },
# right: { value: "d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d" }
# },
# right: {
# value: "8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a",
# left: { value: "842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80" },
# right: { value: "4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c" }
# }
# }
```
### 2.4 height
Every leaf in the tree has height 0 and the hash function of two leaves has height 1. So the tree has a total height of `H` if there is `N` leaves so that `N = 2^H`.
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8")
```
And since `8 = 2^3` then the height:
```ruby
merkle_tree.height # => 3
```
### 2.5 size
You can check total number of tree nodes with `size` or `length` calls:
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4", "L5", "L6", "L7", "L8")
merkle_tree.size
# => 15
```
### 2.6 include?
You can verify that a leaf belongs to merkle tree, namely, there is an authentication path or merkle path from the leaf to the root. This operation is `log2N` where N is number of leaves.
Given a tree with 4 messages:
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
```
To check if a message `L3` is contained in one of the one-time signatures, use the `include?` or `member?` method passing the message and position index:
```ruby
merkle_tree.include?("L3", 2) # => true
```
Conversely, if the message is not part of one-time signature at position indexed:
```ruby
merkle_tree.include?("invalid", 2) # => false
````
### 2.7 add
To add new messages to already existing tree use `add` or `<<` method. This action will create a new merkle root and increase tree height.
A merkle tree with 4 messages:
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
```
Will have the following properties:
```ruby
merkle_tree.leaves.size
# => 4
merkle_tree.height
# => 2
merkle_tree.size
# => 7
```
To add `L5` and `L6` messages do:
```ruby
merkle_tree.add("L5", "L6")
```
This will expand tree to have:
```ruby
merkle_tree.leaves.size
# => 6
merkle_tree.height
# => 3
merkle_tree.size
# => 15
```
### 2.8 update
You can replace any merkle tree message with a new one using the `update` call, which accepts a new message as a first argument and the index of the message to replace.
For example, given the tree:
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
```
To update message from `L3` to `L3*` do:
```ruby
merkle_tree.update("L3*", 2)
# =>
# #<MerkleTree::Leaf @value="e9a1dd00f5c5e848f6ca6d8660c5191d76ac5dd8867b7a8b08fb59c5ed2806db" ... >
```
### 2.9 to_h
You can dump the whole structure of the tree with `to_h` method:
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
merkle_tree.to_h
# =>
# root: {
# value: "63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439",
# left: {
# value: "f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c",
# left: { value: "dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0" },
# right: { value: "d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d" }
# },
# right: {
# value: "8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a",
# left: { value: "842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80" },
# right: { value: "4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c" }
# }
# }
```
### 2.10 to_s
```ruby
merkle_tree = MerkleTree.new("L1", "L2", "L3", "L4")
```
You can print merkle tree out to string:
```ruby
merkle_tree.to_s
# =>
# 63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439
# f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c
# dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0
# d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d
# 8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a
# 842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80
# 4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c
```
### 2.11 `:digest`
By default the `SHA-256` is used to create one-time signatures using Ruby's `openssl` package. You can see different [OpenSSl::Digest](https://ruby-doc.org/stdlib-2.6.1/libdoc/openssl/rdoc/OpenSSL/Digest.html).
To provide your own algorithm use the `:digest` keyword and as value a lambda that will produce message hash. For example, to use `SHA-512` message digest algorithm do:
```ruby
MerkleTree.new("L1",..., digest: -> (val) { OpenSSL::Digest::SHA256.hexdigest(val) })
```
## 3. See Also
- [splay_tree](https://github.com/piotrmurach/splay_tree) – A self-balancing binary tree with amortised access.
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/piotrmurach/merkle_tree. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/piotrmurach/merkle_tree/blob/master/CODE_OF_CONDUCT.md).
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
## Code of Conduct
Everyone interacting in the MerkleTree project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/merkle_tree/blob/master/CODE_OF_CONDUCT.md).
## Copyright
Copyright (c) 2019 Piotr Murach. See [LICENSE.txt](https://github.com/piotrmurach/tty-pie_chart/blob/master/LICENSE.txt) for further details.