├── .github └── FUNDING.yml ├── lib ├── bitcoiner │ ├── version.rb │ ├── account_hash.rb │ ├── account.rb │ ├── transaction.rb │ └── client.rb └── bitcoiner.rb ├── .gitignore ├── Gemfile ├── test ├── test_helper.rb ├── account_hash_test.rb ├── transaction_test.rb ├── account_test.rb └── client_test.rb ├── Rakefile ├── CHANGELOG.md ├── LICENSE.txt ├── bitcoiner.gemspec └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | custom: https://github.com/NARKOZ/SponsorMe 3 | -------------------------------------------------------------------------------- /lib/bitcoiner/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bitcoiner 4 | VERSION = '0.2.1' 5 | end 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | Gemfile.lock 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } 6 | 7 | # Specify your gem's dependencies in bitcoiner.gemspec 8 | gemspec 9 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.unshift File.expand_path('../lib', __dir__) 4 | require 'bitcoiner' 5 | 6 | require 'minitest/autorun' 7 | require 'shoulda/context' 8 | require 'mocha/mini_test' 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/gem_tasks' 4 | require 'rake/testtask' 5 | 6 | Rake::TestTask.new(:test) do |t| 7 | t.libs << 'test' 8 | t.libs << 'lib' 9 | t.test_files = FileList['test/**/*_test.rb'] 10 | end 11 | 12 | task default: :test 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.2.1] - 2018-07-20 8 | ### Fixed 9 | - Pass `logger` from `Bitcoiner.new` to `Bitcoiner::Client` 10 | 11 | ## [0.2.0] 12 | ### Added 13 | - Log requests if logger is set 14 | -------------------------------------------------------------------------------- /lib/bitcoiner.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bitcoiner/version' 4 | require 'typhoeus' 5 | require 'json' 6 | require 'addressable' 7 | 8 | %w[client account account_hash transaction].each do |f| 9 | require File.join(File.dirname(__FILE__), 'bitcoiner', f) 10 | end 11 | 12 | module Bitcoiner 13 | def self.new(user, pass, host = '127.0.0.1:8332', logger: nil) 14 | Client.new user, pass, host, logger: logger 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/bitcoiner/account_hash.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bitcoiner 4 | class AccountHash < Hash 5 | def initialize(client, balance_hash) 6 | @client = client 7 | balance_hash.each_key do |name| 8 | self[name] = Account.new client, name 9 | end 10 | end 11 | 12 | def new(name) 13 | @client.request 'getnewaddress', name 14 | self[name] = Account.new @client, name 15 | self[name] 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/account_hash_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | class AccountHashTest < Minitest::Test 6 | context 'an AccountHash' do 7 | setup do 8 | @client = stub 9 | @ach = Bitcoiner::AccountHash.new @client, '' => 0.0, 'pi' => 3.14, 'john' => 100.0 10 | end 11 | 12 | should 'access accounts like a normal hash' do 13 | assert_equal 'pi', @ach['pi'].name 14 | assert_equal 'john', @ach['john'].name 15 | end 16 | 17 | context 'new method' do 18 | should 'make a new account with a given name' do 19 | @client.expects(:request) 20 | .once 21 | .with('getnewaddress', 'new test account') 22 | .returns('xxxnewtestaddress') 23 | 24 | @act = @ach.new 'new test account' 25 | assert_equal 'new test account', @act.name 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/bitcoiner/account.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bitcoiner 4 | class Account 5 | attr_accessor :name 6 | 7 | def initialize(client, name) 8 | @client = client 9 | @name = name 10 | end 11 | 12 | def inspect 13 | "#" 14 | end 15 | 16 | def send_to(destination, amount) 17 | txn_id = @client.request 'sendfrom', @name, destination, amount 18 | Transaction.new @clientm, self, txn_id 19 | end 20 | 21 | def balance(min_confirmations = 1) 22 | @balance ||= @client.request 'getbalance', @name, min_confirmations.to_i 23 | end 24 | 25 | def address 26 | @address ||= @client.request 'getaccountaddress', @name 27 | end 28 | 29 | def transactions 30 | txn_array = @client.request 'listtransactions', @name 31 | 32 | txn_array.map do |h| 33 | Transaction.new @client, self, h['txid'] 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/bitcoiner/transaction.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bitcoiner 4 | class Transaction 5 | attr_accessor :id, :account 6 | 7 | def initialize(client, account, id) 8 | @client = client 9 | @account = account 10 | @id = id 11 | end 12 | 13 | def detail_hash 14 | @detail_hash ||= @client.request 'gettransaction', @id 15 | end 16 | 17 | def inspect 18 | "#" 19 | rescue StandardError 20 | "#" 21 | end 22 | 23 | def amount 24 | detail_hash['amount'] 25 | end 26 | 27 | def confirmations 28 | detail_hash['confirmations'] 29 | rescue StandardError 30 | 0 31 | end 32 | 33 | def time 34 | @time ||= Time.at detail_hash['time'] 35 | end 36 | 37 | def confirmed?(min_confirmations = 6) 38 | confirmations > min_confirmations 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Bryce Kerley 2 | Copyright (c) 2017 Nihad Abbasov 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /bitcoiner.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('lib', __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'bitcoiner/version' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'bitcoiner' 9 | spec.version = Bitcoiner::VERSION 10 | spec.authors = ['Bryce Kerley', 'Nihad Abbasov'] 11 | spec.email = ['nihad@42na.in'] 12 | 13 | spec.summary = 'Control the bitcoin nework client over JSON-RPC.' 14 | spec.description = 'Automate your Bitcoin transactions with this Ruby interface to the bitcoind JSON-RPC API.' 15 | spec.homepage = 'https://github.com/NARKOZ/bitcoiner' 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 18 | f.match(%r{^(test|spec|features)/}) 19 | end 20 | spec.require_paths = ['lib'] 21 | 22 | spec.add_dependency 'addressable' 23 | spec.add_dependency 'typhoeus', '~> 1.3.0' 24 | 25 | spec.add_development_dependency 'bundler', '~> 2.0' 26 | spec.add_development_dependency 'minitest', '~> 5.0' 27 | spec.add_development_dependency 'mocha', '~> 1.1.0' 28 | spec.add_development_dependency 'rake', '~> 10.0' 29 | spec.add_development_dependency 'shoulda-context', '~> 1.2.2' 30 | end 31 | -------------------------------------------------------------------------------- /test/transaction_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | class TransactionTest < Minitest::Test 6 | context 'a transaction' do 7 | setup do 8 | @client = stub 9 | @acc = Bitcoiner::Account.new @client, 'pi' 10 | @txn = Bitcoiner::Transaction.new @client, @acc, 'testtxnid' 11 | end 12 | 13 | context 'with a detail_hash' do 14 | setup do 15 | @client.expects(:request) 16 | .once 17 | .with('gettransaction', 'testtxnid') 18 | .returns('amount' => 3.14, 19 | 'confirmations' => 420, 20 | 'txid' => 'testtxnid', 21 | 'time' => 1_234_567_890, 22 | 'details' => { 23 | 'account' => 'pi', 24 | 'address' => 'testaddress', 25 | 'category' => 'receive', 26 | 'amount' => 3.14 27 | }) 28 | end 29 | 30 | should 'have an amount and time' do 31 | assert_equal 3.14, @txn.amount 32 | assert_equal Time.at(1_234_567_890), @txn.time 33 | end 34 | 35 | should 'have confirmations and be confirmed' do 36 | assert_equal 420, @txn.confirmations 37 | assert @txn.confirmed? 38 | end 39 | end 40 | 41 | context 'without a detail_hash' do 42 | setup do 43 | @client.expects(:request) 44 | .at_least_once 45 | .with('gettransaction', 'testtxnid') 46 | .raises(ArgumentError) 47 | end 48 | 49 | should 'have a sane inspect' do 50 | assert_equal '#', @txn.inspect 51 | end 52 | 53 | should 'have zero confirmations and be unconfirmed' do 54 | assert_equal 0, @txn.confirmations 55 | assert !@txn.confirmed? 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitcoiner 2 | 3 | Automate your [Bitcoin](https://bitcoin.org/) transactions with this Ruby 4 | interface to the `bitcoind` JSON-RPC API. This is a fork of 5 | [bitcoind](https://github.com/bkerley/bitcoind) Ruby gem. 6 | 7 | ![Super Mario Coin](https://user-images.githubusercontent.com/253398/34371748-45c440f2-eae9-11e7-84ba-fddae754d59a.jpg) 8 | 9 | ## Installation 10 | 11 | Install it from rubygems: 12 | 13 | ``` 14 | gem install bitcoiner 15 | ``` 16 | 17 | Or add to a Gemfile: 18 | 19 | ```ruby 20 | gem 'bitcoiner' 21 | # gem 'bitcoiner', github: 'NARKOZ/bitcoiner' 22 | ``` 23 | 24 | ## Usage 25 | 26 | ### Connecting 27 | 28 | Before connecting, you will need to configure a username and password for 29 | `bitcoind`, and start `bitcoind`. Once that's done: 30 | 31 | ```ruby 32 | client = Bitcoiner.new 'username', 'password' # REPLACE WITH YOUR bitcoin.conf rpcuser/rpcpassword 33 | # => # 34 | ``` 35 | 36 | ### Account Balances 37 | 38 | You can get the balance of all addresses controlled by the client: 39 | 40 | ```ruby 41 | client.balance 42 | # => 12.34 43 | ``` 44 | 45 | You can also get a hash of all accounts the client controls: 46 | 47 | ```ruby 48 | client.accounts 49 | # => {"Your Address"=>#, "eve-online ransoms"=>#} 50 | ``` 51 | 52 | And of course each account has its own balance too: 53 | 54 | ```ruby 55 | ransom = client.accounts['eve-online ransoms'] 56 | # => # 57 | 58 | ransom.balance 59 | # => 2.19 60 | ``` 61 | 62 | ### Transactions 63 | 64 | You can get all the transactions in an account: 65 | 66 | ```ruby 67 | ransom.transactions 68 | # => [#] 69 | ``` 70 | 71 | You can send money from an account too: 72 | 73 | ```ruby 74 | ransom.send_to 'destinationaddress', 2 75 | # => # 76 | ``` 77 | 78 | ### Making Accounts 79 | 80 | Creating an account with an associated address is done through the accounts 81 | interface: 82 | 83 | ```ruby 84 | tiny_wings = client.accounts.new 'tiny wings ransoms' 85 | # => # 86 | 87 | tiny_wings.address 88 | # => "1KV5khnHbbHF2nNQkk7Pe5nPndEj43U27r" 89 | ``` 90 | 91 | ### Logging 92 | 93 | You may log requests (responses aren't logged) by setting a logger: 94 | 95 | ``` 96 | logger = Logger.new(STDOUT) 97 | client = Bitcoiner::Client.new('username', 'password', 'http://a.c', { 98 | logger: logger, 99 | }) 100 | ``` 101 | 102 | ## License 103 | 104 | Released under the MIT license. See LICENSE.txt for details. 105 | -------------------------------------------------------------------------------- /lib/bitcoiner/client.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Bitcoiner 4 | class Client 5 | 6 | DEFAULT_ID = 'jsonrpc'.freeze 7 | LOG_PREFIX = "[bitcoiner]".freeze 8 | 9 | attr_accessor :endpoint, :username, :password, :logger 10 | 11 | def initialize(user, pass, host, logger: nil) 12 | uri = Addressable::URI.heuristic_parse(host) 13 | self.username = uri.user || user 14 | self.password = uri.password || pass 15 | uri.user = uri.password = nil 16 | 17 | self.endpoint = uri.to_s 18 | 19 | self.logger = logger 20 | end 21 | 22 | def balance 23 | request 'getbalance' 24 | end 25 | 26 | def accounts 27 | balance_hash = request 'listaccounts' 28 | AccountHash.new self, balance_hash 29 | end 30 | 31 | def request(method_or_array_of_methods, *args) 32 | if method_or_array_of_methods.is_a?(Array) 33 | log("#{method_or_array_of_methods}") 34 | batch_request(method_or_array_of_methods) 35 | else 36 | log("#{method_or_array_of_methods}; args: #{args.inspect}") 37 | single_request(method_or_array_of_methods, *args) 38 | end 39 | end 40 | 41 | def inspect 42 | "#" 43 | end 44 | 45 | class JSONRPCError < RuntimeError; end 46 | 47 | private 48 | 49 | def post(body) 50 | msg = { 51 | endpoint: endpoint, 52 | username: username, 53 | body: body.to_json, 54 | }.to_s 55 | log(msg) 56 | 57 | Typhoeus.post( 58 | endpoint, 59 | userpwd: [username, password].join(":"), 60 | body: body.to_json, 61 | ) 62 | end 63 | 64 | def batch_request(methods_and_args) 65 | post_body = methods_and_args.map do |method, args| 66 | { 'method' => method, 'params' => args, 'id' => DEFAULT_ID } 67 | end 68 | response = post(post_body) 69 | parse_body(response) 70 | end 71 | 72 | def single_request(method, *args) 73 | post_body = { 'method' => method, 'params' => args, 'id' => DEFAULT_ID } 74 | response = post(post_body) 75 | parsed_response = parse_body(response) 76 | 77 | raise JSONRPCError, parsed_response['error'] if parsed_response['error'] 78 | 79 | parsed_response['result'] 80 | end 81 | 82 | def parse_body(response) 83 | if response.success? 84 | JSON.parse(response.body) 85 | else 86 | error_messages = %i[code return_code].each_with_object({}) do |attr, hash| 87 | hash[attr] = response.send(attr) 88 | end 89 | error_messages[:body] = response.body 90 | raise JSONRPCError, error_messages.map {|k, v| "#{k}: `#{v}`"}.join("; ") 91 | end 92 | end 93 | 94 | def log(msg) 95 | self.logger.info("#{LOG_PREFIX} #{msg}") if self.logger 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /test/account_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | class AccountTest < Minitest::Test 6 | context 'an Account' do 7 | setup do 8 | @client = stub 9 | @acc = Bitcoiner::Account.new @client, 'pi' 10 | end 11 | 12 | should 'have name' do 13 | assert_equal 'pi', @acc.name 14 | end 15 | 16 | should 'have a short but useful inspect' do 17 | assert_equal '#', @acc.inspect 18 | end 19 | 20 | should 'ask the client for a balance' do 21 | @client.expects(:request) 22 | .once 23 | .with('getbalance', 'pi', 1) 24 | .returns(3.14) 25 | 26 | @balance = @acc.balance 27 | 28 | assert_equal 3.14, @balance 29 | end 30 | 31 | should 'ask the client for an address' do 32 | @client.expects(:request) 33 | .once 34 | .with('getaccountaddress', 'pi') 35 | .returns('testaddress') 36 | 37 | @address = @acc.address 38 | 39 | assert_equal 'testaddress', @address 40 | end 41 | 42 | should 'have a list of transactions' do 43 | @client.expects(:request) 44 | .once 45 | .with('listtransactions', 'pi') 46 | .returns [ 47 | { 48 | 'account' => 'pi', 49 | 'address' => 'testaddress', 50 | 'category' => 'receive', 51 | 'amount' => 3.10, 52 | 'confirmations' => 310, 53 | 'txid' => '310', 54 | 'time' => 1_234_567_310 55 | }, 56 | { 57 | 'account' => 'pi', 58 | 'address' => 'testaddress', 59 | 'category' => 'receive', 60 | 'amount' => 3.11, 61 | 'confirmations' => 311, 62 | 'txid' => '311', 63 | 'time' => 1_234_567_311 64 | }, 65 | { 66 | 'account' => 'pi', 67 | 'address' => 'testaddress', 68 | 'category' => 'receive', 69 | 'amount' => 3.12, 70 | 'confirmations' => 312, 71 | 'txid' => '312', 72 | 'time' => 1_234_567_312 73 | } 74 | ] 75 | 76 | @txns = @acc.transactions 77 | 78 | @txns.each do |t| 79 | assert_kind_of Bitcoiner::Transaction, t 80 | end 81 | assert_equal %w[310 311 312], @txns.map(&:id) 82 | end 83 | 84 | context 'transactions' do 85 | should 'send money' do 86 | @client.expects(:request) 87 | .once 88 | .with('sendfrom', @acc.name, 'testdestinationaddress', 5) 89 | .returns('sentmoneytransactionid') 90 | 91 | @txn = @acc.send_to('testdestinationaddress', 5) 92 | 93 | assert_equal 'sentmoneytransactionid', @txn.id 94 | end 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /test/client_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'test_helper' 4 | 5 | class ClientTest < Minitest::Test 6 | context 'a Bitcoiner client' do 7 | setup do 8 | @bcd = Bitcoiner.new 'testuser', 'testpass' 9 | end 10 | 11 | should 'have a simple and useful inspect' do 12 | assert_equal( 13 | '#', 14 | @bcd.inspect 15 | ) 16 | end 17 | 18 | context 'balance operation' do 19 | setup do 20 | response = Typhoeus::Response.new( 21 | code: 200, 22 | body: "{\"result\":12.34000000,\"error\":null,\"id\":\"jsonrpc\"}\n" 23 | ) 24 | Typhoeus.stub('http://127.0.0.1:8332', userpwd: "testuser:testpass"). 25 | and_return(response) 26 | end 27 | 28 | teardown do 29 | Typhoeus::Expectation.clear 30 | end 31 | 32 | should 'get the balance' do 33 | @result = @bcd.balance 34 | assert_equal 12.34, @result 35 | end 36 | end 37 | 38 | context 'accounts operation' do 39 | setup do 40 | response = Typhoeus::Response.new( 41 | code: 200, 42 | body: "{\"result\":{\"\":0.0,\"Your Address\":0.0,\"pi\":3.14,\"ben\":100.00},\"error\":null,\"id\":\"jsonrpc\"}\n" 43 | ) 44 | Typhoeus.stub('http://127.0.0.1:8332', userpwd: "testuser:testpass"). 45 | and_return(response) 46 | end 47 | 48 | teardown do 49 | Typhoeus::Expectation.clear 50 | end 51 | 52 | should 'return a hash of Account objects' do 53 | @result = @bcd.accounts 54 | assert_kind_of Hash, @result 55 | @result.each do |k, a| 56 | assert_kind_of Bitcoiner::Account, a 57 | assert_equal k, a.name 58 | end 59 | 60 | assert_equal 'pi', @result['pi'].name 61 | end 62 | end 63 | 64 | context 'response is not successful' do 65 | setup do 66 | response = Typhoeus::Response.new( 67 | code: 500, 68 | return_code: :ok, 69 | # response supposedly includes body 70 | # https://github.com/bitcoin/bitcoin/issues/12673#issuecomment-372334718 71 | body: {some: "body"}.to_json, 72 | ) 73 | Typhoeus.stub('http://127.0.0.1:8332', userpwd: "testuser:testpass"). 74 | and_return(response) 75 | end 76 | 77 | teardown do 78 | Typhoeus::Expectation.clear 79 | end 80 | 81 | should 'raise JSONRPCError' do 82 | error = assert_raises(Bitcoiner::Client::JSONRPCError) do 83 | @bcd.request('listtransactions') 84 | end 85 | 86 | expected_error_message = [ 87 | 'code: `500`', 88 | 'return_code: `ok`', 89 | 'body: `{"some":"body"}`', 90 | ].join("; ") 91 | assert_equal expected_error_message, error.message 92 | end 93 | end 94 | 95 | context "batch calls" do 96 | setup do 97 | response = Typhoeus::Response.new( 98 | code: 200, 99 | body: [ 100 | {"result"=>{""=>0.0, "Your Address"=>0.0, "pi"=>3.14, "ben"=>100.0}, "error"=>nil, "id"=>"jsonrpc"}, 101 | {"result"=>{""=>0.0, "Your Address"=>0.0, "pi"=>3.14, "ben"=>100.1}, "error"=>nil, "id"=>"jsonrpc"} 102 | ].to_json 103 | ) 104 | Typhoeus.stub('http://127.0.0.1:8332', userpwd: "testuser:testpass"). 105 | and_return(response) 106 | end 107 | 108 | teardown do 109 | Typhoeus::Expectation.clear 110 | end 111 | 112 | should 'be able to execute a batch transaction' do 113 | @result = @bcd.request([["listaccounts", []],["listaccounts", []]]) 114 | assert_kind_of Array, @result 115 | 116 | assert_equal @result.count, 2 117 | assert_equal @result[0]["result"]["ben"], 100.0 118 | assert_equal @result[1]["result"]["ben"], 100.1 119 | end 120 | end 121 | end 122 | 123 | should 'allow setting of host separately from credentials' do 124 | client = Bitcoiner::Client.new('username', 'password', 'host.com') 125 | assert_equal 'http://host.com', client.endpoint 126 | assert_equal "username", client.username 127 | assert_equal "password", client.password 128 | end 129 | 130 | should 'allow setting of uri scheme' do 131 | client = Bitcoiner::Client.new('username', 'password', 'https://host.com') 132 | assert_equal 'https://host.com', client.endpoint 133 | end 134 | 135 | should 'prioritize the credentials in the host and strips them' do 136 | client = Bitcoiner::Client.new('username', 'password', 'https://abc:123@host.com') 137 | assert_equal 'https://host.com', client.endpoint 138 | assert_equal 'abc', client.username 139 | assert_equal '123', client.password 140 | end 141 | 142 | should 'have a customisable logger' do 143 | logger = Logger.new(STDOUT) 144 | 145 | client = Bitcoiner::Client.new('username', 'password', 'http://a.c', { 146 | logger: logger, 147 | }) 148 | 149 | assert_equal logger, client.logger 150 | end 151 | 152 | should 'logs requests if logger configured' do 153 | FileUtils.rm_rf "tmp" 154 | FileUtils.mkdir_p "tmp" 155 | logger = Logger.new("tmp/test.log") 156 | client = Bitcoiner::Client.new('username', 'password', 'http://a.c', { 157 | logger: logger, 158 | }) 159 | 160 | client.request("listtransactions") rescue # don't care that it fails 161 | 162 | f = File.read("tmp/test.log") 163 | assert f.include?("listtransactions") 164 | end 165 | end 166 | --------------------------------------------------------------------------------