{"id":28630982,"url":"https://github.com/piotrmurach/merkle_tree","last_synced_at":"2025-06-12T13:09:32.744Z","repository":{"id":56883323,"uuid":"173639193","full_name":"piotrmurach/merkle_tree","owner":"piotrmurach","description":"A merkle tree is a data structure used for efficiently summarizing sets of data, often one-time signatures.","archived":false,"fork":false,"pushed_at":"2024-03-18T22:48:56.000Z","size":54,"stargazers_count":82,"open_issues_count":1,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-08-08T19:42:19.706Z","etag":null,"topics":["cryptocurrency","merkle-tree","ruby","ruby-gem","tree-structure"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/piotrmurach.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":"piotrmurach"}},"created_at":"2019-03-03T22:46:21.000Z","updated_at":"2024-07-09T09:50:25.000Z","dependencies_parsed_at":"2024-03-18T23:48:12.084Z","dependency_job_id":null,"html_url":"https://github.com/piotrmurach/merkle_tree","commit_stats":{"total_commits":56,"total_committers":2,"mean_commits":28.0,"dds":"0.017857142857142905","last_synced_commit":"9d8d24638b6005c90aa3691f814ba15efcd77e7e"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/piotrmurach/merkle_tree","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Fmerkle_tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Fmerkle_tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Fmerkle_tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Fmerkle_tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/piotrmurach","download_url":"https://codeload.github.com/piotrmurach/merkle_tree/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrmurach%2Fmerkle_tree/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259470950,"owners_count":22862999,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cryptocurrency","merkle-tree","ruby","ruby-gem","tree-structure"],"created_at":"2025-06-12T13:09:31.474Z","updated_at":"2025-06-12T13:09:32.669Z","avatar_url":"https://github.com/piotrmurach.png","language":"Ruby","funding_links":["https://github.com/sponsors/piotrmurach"],"categories":[],"sub_categories":[],"readme":"# MerkleTree\n\n[![Gem Version](https://badge.fury.io/rb/merkle_tree.svg)][gem]\n[![Actions CI](https://github.com/piotrmurach/merkle_tree/workflows/CI/badge.svg?branch=master)][gh_actions_ci]\n[![Build status](https://ci.appveyor.com/api/projects/status/kcbi55cyq2wlfuhc?svg=true)][appveyor]\n[![Maintainability](https://api.codeclimate.com/v1/badges/ce00667c8785a62cd892/maintainability)][codeclimate]\n[![Coverage Status](https://coveralls.io/repos/piotrmurach/merkle_tree/badge.svg)][coverage]\n[![Inline docs](https://inch-ci.org/github/piotrmurach/merkle_tree.svg?branch=master)][inchpages]\n\n[gem]: https://badge.fury.io/rb/merkle_tree\n[gh_actions_ci]: https://github.com/piotrmurach/merkle_tree/actions?query=workflow%3ACI\n[appveyor]: https://ci.appveyor.com/project/piotrmurach/merkle-tree\n[codeclimate]: https://codeclimate.com/github/piotrmurach/merkle_tree/maintainability\n[coverage]: https://coveralls.io/r/piotrmurach/merkle_tree\n[inchpages]: https://inch-ci.org/github/piotrmurach/merkle_tree\n\n\u003e 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.\n\nA 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.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'merkle_tree'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install merkle_tree\n\n## Contents\n\n* [1. Usage](#1-usage)\n* [2. API](#2-api)\n  * [2.1 root](#21-root)\n  * [2.2 leaves](#22-leaves)\n  * [2.3 subtree](#23-subtree)\n  * [2.4 height](#24-height)\n  * [2.5 size](#25-size)\n  * [2.6 include?](#26-include)\n  * [2.7 add](#27-add)\n  * [2.8 update](#28-update)\n  * [2.9 to_h](#29-to_h)\n  * [2.10 to_s](#210-to_s)\n  * [2.11 :digest](#211-digest)\n* [3. See Also](#3-see-also)\n\n## 1. Usage\n\nCreate a new *MerkleTree* by passing all the messages to be hashed:\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\")\n```\n\nThen you can use the tree to verify if a message belongs:\n\n```ruby\nmerkle_tree.include?(\"L3\", 2) # =\u003e true\n```\n\nOr change the message to a new content:\n\n```ruby\nmerkle_tree.update(\"L3*\", 2)\n```\n\nYou can also check the tree height:\n\n```ruby\nmerkle_tree.height # =\u003e 2\n```\n\nAnd how many nodes it has:\n\n```ruby\nmerkle_tree.size # =\u003e 7\n```\n\nFinally, you can print the contents of the tree to see all the signatures:\n\n```ruby\nmerkle_tree.to_s\n# =\u003e\n# 63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439\n#   f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c\n#     dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0\n#     d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d\n#   8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a\n#     842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80\n#     4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c\n```\n\n## 2. API\n\n### 2.1 root\n\nTo 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.\n\nFor example, given a tree with 4 messages\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\")\n```\n\nA full tree of one-time signatures will be available:\n\n```ruby\nmerkle_tree.root\n# =\u003e\n# MerkleTree::Node\n#  @value=\"63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439\"\n#  @left=MerkleTree::Node\n#    @value=\"f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c\"\n#    @left=MerkleTree::Leaf\n#      @value=\"dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0\"\n#    @right=MerkleTree::Leaf\n#      @value=\"d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d\"\n#  @right=MerkleTree::Node\n#    @value=\"8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a\"\n#    @left=\n#     MerkleTree::Leaf\n#      @value=\"842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80\"\n#   @right=MerkleTree::Leaf\n#     @value=\"4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c\"\n```\n\nSince the root is a node you can retrieve it's signature using the `value` call:\n\n```ruby\nmerkle_tree.root.value\n# =\u003e \"63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439\"\n```\n\n### 2.2 leaves\n\nYou can access all the leaves and their one-time signatures using the `leaves` method:\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\")\n\nmerkle_tree.leaves\n# =\u003e\n# [\n# \u003cMerkleTree::Leaf @value=\"dffe8596...\", @left_index=0, @right_index=0, @height=0\u003e,\n# \u003cMerkleTree::Leaf @value=\"d76354d8...\", @left_index=1, @right_index=1, @height=0\u003e,\n# \u003cMerkleTree::Leaf @value=\"842983de...\", @left_index=2, @right_index=2, @height=0\u003e,\n# \u003cMerkleTree::Leaf @value=\"4a5a97c6...\", @left_index=3, @right_index=3, @height=0\u003e\n# ]\n```\n\n### 2.3 subtree\n\nTo access a part of Merkle tree use `subtree` with the index of the one-time signature:\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\", \"L5\", \"L6\", \"L7\", \"L8\")\n\nmerkle_tree.subtree(2).to_h\n# =\u003e\n# {\n#   value: \"63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439\",\n#   left: {\n#     value: \"f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c\",\n#     left: { value: \"dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0\" },\n#     right: { value: \"d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d\" }\n#   },\n#   right: {\n#     value: \"8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a\",\n#     left: { value: \"842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80\" },\n#     right: { value: \"4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c\" }\n#   }\n# }\n```\n\n### 2.4 height\n\nEvery 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`.\n\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\", \"L5\", \"L6\", \"L7\", \"L8\")\n```\n\nAnd since `8 = 2^3` then the height:\n\n```ruby\nmerkle_tree.height # =\u003e 3\n```\n\n### 2.5 size\n\nYou can check total number of tree nodes with `size` or `length` calls:\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\", \"L5\", \"L6\", \"L7\", \"L8\")\nmerkle_tree.size\n# =\u003e 15\n```\n\n### 2.6 include?\n\nYou 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.\n\nGiven a tree with 4 messages:\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\")\n```\n\nTo 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:\n\n```ruby\nmerkle_tree.include?(\"L3\", 2) # =\u003e true\n```\n\nConversely, if the message is not part of one-time signature at position indexed:\n\n```ruby\nmerkle_tree.include?(\"invalid\", 2) # =\u003e false\n````\n\n### 2.7 add\n\nTo add new messages to already existing tree use `add` or `\u003c\u003c` method. This action will create a new merkle root and increase tree height.\n\nA merkle tree with 4 messages:\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\")\n```\n\nWill have the following properties:\n\n```ruby\nmerkle_tree.leaves.size\n# =\u003e 4\nmerkle_tree.height\n# =\u003e 2\nmerkle_tree.size\n# =\u003e 7\n```\n\nTo add `L5` and `L6` messages do:\n\n```ruby\nmerkle_tree.add(\"L5\", \"L6\")\n```\n\nThis will expand tree to have:\n\n```ruby\nmerkle_tree.leaves.size\n# =\u003e 6\nmerkle_tree.height\n# =\u003e 3\nmerkle_tree.size\n# =\u003e 15\n```\n\n### 2.8 update\n\nYou 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.\n\nFor example, given the tree:\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\")\n```\n\nTo update message from `L3` to `L3*` do:\n\n```ruby\nmerkle_tree.update(\"L3*\", 2)\n# =\u003e\n# #\u003cMerkleTree::Leaf @value=\"e9a1dd00f5c5e848f6ca6d8660c5191d76ac5dd8867b7a8b08fb59c5ed2806db\" ... \u003e\n```\n\n### 2.9 to_h\n\nYou can dump the whole structure of the tree with `to_h` method:\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\")\n\nmerkle_tree.to_h\n# =\u003e\n# root: {\n#   value: \"63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439\",\n#   left: {\n#     value: \"f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c\",\n#     left: { value: \"dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0\" },\n#     right: { value: \"d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d\" }\n#   },\n#   right: {\n#     value: \"8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a\",\n#     left: { value: \"842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80\" },\n#     right: { value: \"4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c\" }\n#   }\n# }\n```\n\n### 2.10 to_s\n\n```ruby\nmerkle_tree = MerkleTree.new(\"L1\", \"L2\", \"L3\", \"L4\")\n```\n\nYou can print merkle tree out to string:\n\n```ruby\nmerkle_tree.to_s\n# =\u003e\n# 63442ffc2d48a92c8ba746659331f273748ccede648b27f4eacf00cb0786c439\n#   f2b92f33b56466fce14bc2ccf6a92f6edfcd8111446644c20221d6ae831dd67c\n#     dffe8596427fc50e8f64654a609af134d45552f18bbecef90b31135a9e7acaa0\n#     d76354d8457898445bb69e0dc0dc95fb74cc3cf334f8c1859162a16ad0041f8d\n#   8f75b0c1b3d1c0bb2eda264a43f8fdc5c72c853c95fbf2b01c1d5a3e12c6fe9a\n#     842983de8fb1d277a3fad5c8295c7a14317c458718a10c5a35b23e7f992a5c80\n#     4a5a97c6433c4c062457e9335709d57493e75527809d8a9586c141e591ac9f2c\n```\n\n### 2.11 `:digest`\n\nBy 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).  \n\nTo 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:\n\n```ruby\nMerkleTree.new(\"L1\",..., digest: -\u003e (val) { OpenSSL::Digest::SHA256.hexdigest(val) })\n```\n\n## 3. See Also\n\n- [splay_tree](https://github.com/piotrmurach/splay_tree) – A self-balancing binary tree with amortised access.\n\n## Development\n\nAfter 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.\n\nTo 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).\n\n## Contributing\n\nBug 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).\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone 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).\n\n## Copyright\n\nCopyright (c) 2019 Piotr Murach. See [LICENSE.txt](https://github.com/piotrmurach/tty-pie_chart/blob/master/LICENSE.txt) for further details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrmurach%2Fmerkle_tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpiotrmurach%2Fmerkle_tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrmurach%2Fmerkle_tree/lists"}