├── .gitignore ├── .rspec ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bigchain_ruby.gemspec ├── bin ├── console └── setup ├── lib ├── bigchain_ruby.rb └── bigchain_ruby │ ├── base58.rb │ ├── connection.rb │ ├── create_operation.rb │ ├── crypto.rb │ ├── driver.rb │ ├── exceptions.rb │ ├── offchain.rb │ ├── pool.rb │ ├── round_robin_picker.rb │ ├── transfer_operation.rb │ ├── transport.rb │ ├── utils.rb │ └── version.rb └── spec ├── bigchain_ruby ├── base58_spec.rb ├── connection_spec.rb ├── crypto_spec.rb ├── exceptions_spec.rb ├── pool_spec.rb ├── round_robin_picker_spec.rb ├── transport_spec.rb └── utils_spec.rb ├── bigchain_ruby_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | rvm: 4 | - 2.3.0 5 | before_install: gem install bundler -v 1.13.6 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at adam.groves@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in bigchain_ruby.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BigchainRuby 2 | 3 | This is a ruby implementation of the [BigchainDB Driver](https://github.com/bigchaindb/bigchaindb-driver), which is written in python. 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | ```ruby 10 | gem 'bigchain_ruby' 11 | ``` 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install bigchain_ruby 20 | 21 | ## Usage 22 | 23 | TODO: Write usage instructions here 24 | 25 | ## Development 26 | 27 | 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. 28 | 29 | 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). 30 | 31 | ## Contributing 32 | 33 | Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bigchain_ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 34 | 35 | 36 | ## License 37 | 38 | The gem is available as open source under the terms of the [Apache Licence 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). 39 | 40 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /bigchain_ruby.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'bigchain_ruby/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "bigchain_ruby" 8 | spec.version = BigchainRuby::VERSION 9 | spec.authors = ["Adam Groves"] 10 | spec.email = ["adam.groves@gmail.com"] 11 | 12 | spec.summary = %q{BighchainDB driver written in ruby} 13 | spec.description = %q{BigchainDB driver written in ruby, following as closely as possible to the original python implementation} 14 | spec.homepage = "https://github.com/addywaddy/bigchain_ruby" 15 | spec.license = "MIT" 16 | 17 | # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' 18 | # to allow pushing to a single host or delete this section to allow pushing to any host. 19 | if spec.respond_to?(:metadata) 20 | spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'" 21 | else 22 | raise "RubyGems 2.0 or newer is required to protect against " \ 23 | "public gem pushes." 24 | end 25 | 26 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 27 | f.match(%r{^(test|spec|features)/}) 28 | end 29 | spec.bindir = "exe" 30 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 31 | spec.require_paths = ["lib"] 32 | 33 | spec.add_development_dependency "bundler", "~> 1.13" 34 | spec.add_development_dependency "rake", "~> 10.0" 35 | spec.add_development_dependency "rspec", "~> 3.0" 36 | spec.add_development_dependency "webmock", "~> 2.3.2" 37 | spec.add_development_dependency "pry", "~> 0.10.4" 38 | spec.add_runtime_dependency 'rbnacl', '~> 4.0.1' 39 | spec.add_runtime_dependency 'http', '~> 2.2.1' 40 | spec.add_runtime_dependency 'addressable', '~> 2.5.0' 41 | end 42 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "bigchain_ruby" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /lib/bigchain_ruby.rb: -------------------------------------------------------------------------------- 1 | require "bigchain_ruby/version" 2 | 3 | module BigchainRuby 4 | autoload :Base58, 'bigchain_ruby/base58' 5 | autoload :Crypto, 'bigchain_ruby/crypto' 6 | autoload :Connection, 'bigchain_ruby/connection' 7 | autoload :Pool, 'bigchain_ruby/pool' 8 | autoload :RoundRobinPicker, 'bigchain_ruby/round_robin_picker' 9 | autoload :Transport, 'bigchain_ruby/transport' 10 | autoload :Utils, 'bigchain_ruby/utils' 11 | autoload :TransferOperation, 'bigchain_ruby/transfer_operation' 12 | autoload :CreateOperation, 'bigchain_ruby/create_operation' 13 | autoload :Exceptions, 'bigchain_ruby/exceptions' 14 | end 15 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/base58.rb: -------------------------------------------------------------------------------- 1 | module BigchainRuby 2 | class Base58 3 | CHARS = %w( 4 | 123456789 5 | ABCDEFGHJKLMNPQRSTUVWXYZ 6 | abcdefghijkmnopqrstuvwxyz 7 | ) 8 | 9 | def self.decoding_table 10 | @decoding_table ||= CHARS.join.split('') 11 | .each_with_index 12 | .each_with_object({}) do |(c, i), store| 13 | store[c] = i 14 | end 15 | end 16 | 17 | def self.encoding_table 18 | @encoding_table ||= decoding_table.invert 19 | end 20 | 21 | def self.encode(data) 22 | cdata = data.unpack('C*') 23 | vlong = cdata.inject(0) { |store, v| store = v + store * 256; store } 24 | result = "" 25 | while vlong > 0 do 26 | result += encoding_table[vlong % 58] 27 | vlong /= 58 28 | end 29 | while cdata.first == 0 do 30 | result += '1' 31 | cdata = cdata[1..-1] 32 | end 33 | result.reverse 34 | end 35 | 36 | def self.decode(data_string) 37 | vlong = data_string.each_byte.inject(0) do |store, b| 38 | store = decoding_table[b.chr] + (58 * store) 39 | store 40 | end 41 | result = "" 42 | while vlong > 0 do 43 | result += (vlong % 256).chr 44 | vlong /= 256 45 | end 46 | while data_string[0] == '1' do 47 | result += "\x00" 48 | data_string = data_string[1..-1] 49 | end 50 | result.reverse 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/connection.rb: -------------------------------------------------------------------------------- 1 | require 'http' 2 | require 'pry' 3 | 4 | class BigchainRuby::Connection 5 | HttpResponse = Struct.new(:status_code, :headers, :data) 6 | 7 | attr_reader :node_url 8 | private :node_url 9 | 10 | def initialize(node_url, headers: {}) 11 | @node_url = node_url 12 | @headers = headers 13 | end 14 | 15 | def request(method, relative_path, json: {}, headers: {}) 16 | response = HTTP 17 | .headers(headers) 18 | .public_send(method, File.join(node_url, relative_path), json: json) 19 | 20 | HttpResponse.new(response.status, response.headers, JSON.parse(response.body)) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/create_operation.rb: -------------------------------------------------------------------------------- 1 | class BigchainRuby::CreateOperation 2 | # need to implement at least part of the cryptoconditions in order to complete this 3 | end 4 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/crypto.rb: -------------------------------------------------------------------------------- 1 | require 'rbnacl' 2 | class BigchainRuby::Crypto 3 | CryptoKeypair = Struct.new(:private_key, :public_key) 4 | 5 | def generate_keypair 6 | CryptoKeypair.new(private_key_to_base58, public_key_to_base58) 7 | end 8 | 9 | private 10 | 11 | def private_key_to_base58 12 | BigchainRuby::Base58.encode(signing_key.to_s) 13 | end 14 | 15 | def public_key_to_base58 16 | BigchainRuby::Base58.encode(signing_key.verify_key.to_s) 17 | end 18 | 19 | def signing_key 20 | @signing_key ||= ::RbNaCl::SigningKey.generate 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/driver.rb: -------------------------------------------------------------------------------- 1 | class BigchainRuby::Driver 2 | # need to implement at least part of the cryptoconditions in order to complete this 3 | end 4 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/exceptions.rb: -------------------------------------------------------------------------------- 1 | module BigchainRuby::Exceptions 2 | class BigchaindbException < StandardError; end 3 | 4 | class KeypairNotFoundException < BigchaindbException; end 5 | class InvalidPrivateKey < BigchaindbException; end 6 | class InvalidPublicKey < BigchaindbException; end 7 | class MissingPrivateKeyError < BigchaindbException; end 8 | 9 | class TransportError < BigchaindbException 10 | attr_reader :status_code, :error, :info 11 | 12 | def initialize(status_code, error = nil, info = nil) 13 | @status_code, @error, @info = status_code, error, info 14 | end 15 | end 16 | 17 | class ConnectionError < TransportError; end 18 | class BadRequest < TransportError; end 19 | class NotFoundError < TransportError; end 20 | 21 | HTTP_EXCEPTIONS = {400 => BadRequest, 404 => NotFoundError} 22 | end 23 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/offchain.rb: -------------------------------------------------------------------------------- 1 | class BigchainRuby::Offchain 2 | # need to implement at least part of the cryptoconditions in order to complete this 3 | end 4 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/pool.rb: -------------------------------------------------------------------------------- 1 | class BigchainRuby::Pool 2 | attr_reader :connections, :picker_class 3 | private :connections, :picker_class 4 | 5 | def initialize(connections, picker_class = BigchainRuby::RoundRobinPicker) 6 | @connections = connections 7 | @picker_class = picker_class 8 | end 9 | 10 | def get_connection 11 | if connections.length > 1 12 | picker.pick 13 | else 14 | connections.first 15 | end 16 | end 17 | 18 | private 19 | 20 | def picker 21 | @picker ||= picker_class.new(connections) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/round_robin_picker.rb: -------------------------------------------------------------------------------- 1 | class BigchainRuby::RoundRobinPicker 2 | attr_accessor :connections 3 | private :connections 4 | 5 | def initialize(connections) 6 | @connections = connections 7 | end 8 | 9 | def pick 10 | round_robin.next 11 | end 12 | 13 | private 14 | 15 | def round_robin 16 | @round_robin ||= connections.cycle 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/transfer_operation.rb: -------------------------------------------------------------------------------- 1 | class BigchainRuby::TransferOperation 2 | end 3 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/transport.rb: -------------------------------------------------------------------------------- 1 | require 'pry' 2 | class BigchainRuby::Transport 3 | attr_reader :pool, :node_urls, :headers 4 | private :pool, :node_urls, :headers 5 | 6 | def initialize(*node_urls, headers: {}) 7 | @node_urls = node_urls 8 | @headers = headers 9 | end 10 | 11 | def forward_request(method, path, json: {}, headers: {}) 12 | response = connection.request(method, path, json: json, headers: headers) 13 | response.data 14 | end 15 | 16 | private 17 | 18 | def connection 19 | pool.get_connection 20 | end 21 | 22 | def pool 23 | @pool ||= BigchainRuby::Pool.new(connections) 24 | end 25 | 26 | def connections 27 | node_urls.map do |node_url| 28 | BigchainRuby::Connection.new(node_url, headers: headers) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/utils.rb: -------------------------------------------------------------------------------- 1 | require 'pry' 2 | require 'addressable' 3 | 4 | class BigchainRuby::Utils 5 | DEFAULT_NODE = 'http://localhost:9984' 6 | 7 | OPTS_MAP = {'CREATE' => BigchainRuby::CreateOperation, 'TRANSFER' => BigchainRuby::TransferOperation} 8 | 9 | def get_default_port(schema) 10 | schema == 'https' ? 443 : 9984 11 | end 12 | 13 | def normalize_nodes(*nodes) 14 | return [DEFAULT_NODE] if nodes.empty? 15 | 16 | nodes.map do |node| 17 | if node 18 | uri = Addressable::URI.parse(node) 19 | unless uri.host 20 | uri = Addressable::URI.parse("http://#{node}") 21 | end 22 | uri.port ||= get_default_port(uri.scheme) 23 | uri.to_s 24 | else 25 | DEFAULT_NODE 26 | end 27 | end 28 | end 29 | 30 | private 31 | 32 | def normalize_operation(operation) 33 | operation_to_uppercase(operation) || operation_instantiate(operation) 34 | end 35 | 36 | def operation_to_uppercase(operation) 37 | operation.upcase 38 | rescue NoMethodError 39 | nil 40 | end 41 | 42 | def operation_instantiate(operation) 43 | OPTS_MAP.fetch(operation).new 44 | rescue KeyError 45 | nil 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/bigchain_ruby/version.rb: -------------------------------------------------------------------------------- 1 | module BigchainRuby 2 | VERSION = "0.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /spec/bigchain_ruby/base58_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe BigchainRuby::Base58 do 4 | let(:decoded) do 5 | [ 6 | 228, 41, 187, 106, 36, 49, 23, 153, 83, 94, 144, 7 | 119, 128, 108, 235, 63, 24, 31, 26, 133, 102, 222, 8 | 136, 99, 10, 40, 218, 207, 235, 122, 144, 58 9 | ].pack('C*') 10 | end 11 | let(:encoded) { 'GMes4ftDtxHjKyrg6tTHUv7JFprb9ArRQVR9jMAo67zq' } 12 | 13 | describe '.encode' do 14 | it 'encodes the provided data using base 58' do 15 | expect(described_class.encode(decoded)).to eq(encoded) 16 | end 17 | end 18 | 19 | describe '.decode' do 20 | it 'decodes the provided string using base 58' do 21 | expect(described_class.decode(encoded)).to eq(decoded) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/bigchain_ruby/connection_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe BigchainRuby::Connection do 4 | describe '#request' do 5 | subject { described_class.new('http://dummy.com') } 6 | 7 | context 'content type handling' do 8 | context 'content type json' do 9 | let(:response) do 10 | subject.request(:get, '/foo', json: {my: 'params'}, headers: {custom: :header} ) 11 | end 12 | 13 | before do 14 | stub_request(:get, "http://dummy.com/foo") 15 | .with( 16 | body: "{\"my\":\"params\"}", 17 | headers: { 18 | 'Content-Type'=>'application/json; charset=UTF-8', 19 | 'Custom'=>'header', 20 | } 21 | ) 22 | .to_return(:status => 200, :body => {my: 'response'}.to_json) 23 | end 24 | 25 | it 'returns the data and status code' do 26 | expect(response.status_code).to eq(200) 27 | expect(response.data).to eq({'my' => 'response'}) 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/bigchain_ruby/crypto_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe BigchainRuby::Crypto do 4 | describe '#generate_keypair' do 5 | subject { described_class.new.generate_keypair } 6 | 7 | it 'returns a new crypto keypair instance' do 8 | expect(subject).to be_a(BigchainRuby::Crypto::CryptoKeypair) 9 | expect(subject.private_key.length).to be > 40 10 | expect(subject.public_key.length).to be > 40 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/bigchain_ruby/exceptions_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module BigchainRuby::Exceptions 4 | describe TransportError do 5 | subject { described_class.new(404, :not_found, {foo: :bar}) } 6 | 7 | it 'accepts status code, error and info' do 8 | expect(subject.status_code).to eq(404) 9 | expect(subject.error).to eq(:not_found) 10 | expect(subject.info).to eq(foo: :bar) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/bigchain_ruby/pool_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe BigchainRuby::Pool do 4 | describe '#connection' do 5 | subject { described_class.new(connections) } 6 | 7 | context 'single connection' do 8 | let(:connection) { double } 9 | let(:connections) { [connection] } 10 | 11 | it 'returns the connection' do 12 | expect(subject.get_connection).to eq(connection) 13 | end 14 | end 15 | 16 | context 'several connections' do 17 | let(:connections) { [double, double, double] } 18 | let(:picker) { double } 19 | let(:picked_connection) { double } 20 | 21 | before do 22 | allow(BigchainRuby::RoundRobinPicker) 23 | .to receive(:new) 24 | .and_return(picker) 25 | allow(picker).to receive(:pick).and_return(picked_connection) 26 | end 27 | 28 | it 'returns the connection' do 29 | expect(subject.get_connection).to eq(picked_connection) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/bigchain_ruby/round_robin_picker_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe BigchainRuby::RoundRobinPicker do 4 | describe '#pick' do 5 | let(:first_connection) { double } 6 | let(:second_connection) { double } 7 | let(:connections) { [first_connection, second_connection] } 8 | subject { described_class.new(connections) } 9 | 10 | it 'does something' do 11 | expect(subject.pick).to eq(first_connection) 12 | expect(subject.pick).to eq(second_connection) 13 | expect(subject.pick).to eq(first_connection) 14 | expect(subject.pick).to eq(second_connection) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/bigchain_ruby/transport_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe BigchainRuby::Transport do 4 | describe '#forward_request' do 5 | let(:node_urls) { ['http://node1.com', 'http://node2.com'] } 6 | subject { described_class.new(*node_urls) } 7 | 8 | before do 9 | stub_request(:get, "http://node1.com/foo") 10 | .with(body: "{\"bar\":1}") 11 | .to_return(body: {it: :worked}.to_json) 12 | 13 | stub_request(:get, "http://node2.com/foo") 14 | .with(body: "{\"bar\":2}") 15 | .to_return(body: {it: :worked_again}.to_json) 16 | end 17 | 18 | it 'does something' do 19 | expect(subject.forward_request(:get, '/foo', json: {bar: 1})).to eq({'it' => 'worked'}) 20 | expect(subject.forward_request(:get, '/foo', json: {bar: 2})).to eq({'it' => 'worked_again'}) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/bigchain_ruby/utils_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe BigchainRuby::Utils do 4 | describe '#normalize_nodes' do 5 | subject { described_class.new } 6 | 7 | context 'single node' do 8 | let(:examples) do 9 | [ 10 | ['localhost', 'http://localhost:9984'], 11 | [nil, 'http://localhost:9984'], 12 | ['http://localhost', 'http://localhost:9984'], 13 | ['http://localhost:80', 'http://localhost:80'], 14 | ['https://node.xyz', 'https://node.xyz:443'], 15 | ['https://node.xyz/path', 'https://node.xyz:443/path'] 16 | ] 17 | end 18 | 19 | it 'returns a normalized version of the node location' do 20 | examples.each do |(provided, expected)| 21 | expect(subject.normalize_nodes(provided)).to eq([expected]) 22 | end 23 | end 24 | end 25 | 26 | context 'multiple nodes' do 27 | let(:examples) do 28 | [ 29 | [['localhost', nil],['http://localhost:9984', 'http://localhost:9984']], 30 | [['https://node.xyz', 'localhost'], ['https://node.xyz:443', 'http://localhost:9984']], 31 | ] 32 | end 33 | it 'returns normalized versions of the node locations' do 34 | examples.each do |(provided, expected)| 35 | expect(subject.normalize_nodes(*provided)).to eq(expected) 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/bigchain_ruby_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe BigchainRuby do 4 | it "has a version number" do 5 | expect(BigchainRuby::VERSION).not_to be nil 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) 2 | require "bigchain_ruby" 3 | require 'webmock/rspec' 4 | --------------------------------------------------------------------------------