straight 0.2.3 → 1.0.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -0
  3. data/Gemfile +7 -5
  4. data/Gemfile.lock +17 -7
  5. data/README.md +3 -0
  6. data/Rakefile +9 -0
  7. data/VERSION +1 -1
  8. data/lib/straight.rb +9 -1
  9. data/lib/straight/address_providers/base.rb +28 -0
  10. data/lib/straight/address_providers/bip32.rb +22 -0
  11. data/lib/straight/blockchain_adapter.rb +17 -1
  12. data/lib/straight/blockchain_adapters/biteasy_adapter.rb +14 -13
  13. data/lib/straight/blockchain_adapters/blockchain_info_adapter.rb +15 -14
  14. data/lib/straight/blockchain_adapters/insight_adapter.rb +76 -0
  15. data/lib/straight/blockchain_adapters/mycelium_adapter.rb +74 -49
  16. data/lib/straight/exchange_rate_adapter.rb +2 -2
  17. data/lib/straight/exchange_rate_adapters/average_rate_adapter.rb +1 -1
  18. data/lib/straight/exchange_rate_adapters/okcoin_adapter.rb +1 -1
  19. data/lib/straight/faraday_monkeypatch.rb +22 -0
  20. data/lib/straight/gateway.rb +71 -34
  21. data/lib/straight/order.rb +43 -32
  22. data/straight.gemspec +20 -27
  23. metadata +30 -26
  24. data/spec/lib/blockchain_adapters/biteasy_adapter_spec.rb +0 -48
  25. data/spec/lib/blockchain_adapters/blockchain_info_adapter_spec.rb +0 -57
  26. data/spec/lib/blockchain_adapters/mycelium_adapter_spec.rb +0 -58
  27. data/spec/lib/exchange_rate_adapter_spec.rb +0 -55
  28. data/spec/lib/exchange_rate_adapters/average_rate_adapter_spec.rb +0 -43
  29. data/spec/lib/exchange_rate_adapters/bitpay_adapter_spec.rb +0 -27
  30. data/spec/lib/exchange_rate_adapters/bitstamp_adapter_spec.rb +0 -27
  31. data/spec/lib/exchange_rate_adapters/btce_adapter_spec.rb +0 -27
  32. data/spec/lib/exchange_rate_adapters/coinbase_adapter_spec.rb +0 -27
  33. data/spec/lib/exchange_rate_adapters/kraken_adapter_spec.rb +0 -27
  34. data/spec/lib/exchange_rate_adapters/localbitcoins_adapter_spec.rb +0 -27
  35. data/spec/lib/exchange_rate_adapters/okcoin_adapter_spec.rb +0 -27
  36. data/spec/lib/gateway_spec.rb +0 -98
  37. data/spec/lib/order_spec.rb +0 -128
  38. data/spec/spec_helper.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 47b083622bc343959f9614caf0a90263c117e490
4
- data.tar.gz: 7b6fd9a56ffae2d8e20d75c8f69c4bdcc5562978
3
+ metadata.gz: b6367d814a3c2ac159870bb0c8b6929f26c6c612
4
+ data.tar.gz: 525e7bce43d7e17a436103f36432fb637307b628
5
5
  SHA512:
6
- metadata.gz: 2c750fa3761f2967e448b3b7c8256e60b1815abe215a22bfad1f3c953283a3e46268735084cd8debc2b73d176e3486f81fbe576ed5f1eb789794641454c4dac4
7
- data.tar.gz: 9ca4bb9f861caf0d57d02ef2d415c5db3adba2fa1b4a3ff39e72272c6d311fd5a9f291d0471326efcbe82e33a565e596c2f9c2b724d9a38f0d53b7d7ed4a6730
6
+ metadata.gz: b5f817e6c86180289c7b6f3dbcad0d10e6d5f2e5de3ce7218c3f8d08fec9ac2851d7878ebd03b825bf0ddb88b096c9cfbfdbe31694c7a46be2458444bdcbdeac
7
+ data.tar.gz: f6f8afecb366765b9b80f2e471158bf38f614a640aeadddb155a1bdd129c24b0873d2f3ae936ce931458220ec6a6d74f822f805c69a4e276f4f9e9f82c95679d
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ notifications:
5
+ slack: mycelium-gear:aqCsWhyRsg9iRfre4VaBUvVP
data/Gemfile CHANGED
@@ -1,11 +1,11 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
- # Used to generate bip32 addresses
4
- gem 'money-tree', "0.9.0"
3
+ gem 'btcruby', '~> 1.0'
5
4
 
6
5
  # Used in exchange rate adapters
7
- gem 'satoshi-unit'
8
- gem 'httparty'
6
+ gem 'satoshi-unit', '~> 0.1'
7
+ gem 'httparty', '~> 0.13.5'
8
+ gem 'faraday'
9
9
 
10
10
  group :development do
11
11
  gem "bundler", "~> 1.0"
@@ -15,4 +15,6 @@ end
15
15
 
16
16
  group :test do
17
17
  gem 'rspec'
18
+ gem 'webmock'
19
+ gem 'vcr'
18
20
  end
data/Gemfile.lock CHANGED
@@ -1,8 +1,12 @@
1
1
  GEM
2
- remote: http://rubygems.org/
2
+ remote: https://rubygems.org/
3
3
  specs:
4
4
  addressable (2.3.6)
5
+ btcruby (1.0.3)
6
+ ffi (~> 1.9, >= 1.9.3)
5
7
  builder (3.2.2)
8
+ crack (0.4.2)
9
+ safe_yaml (~> 1.0.0)
6
10
  descendants_tracker (0.0.4)
7
11
  thread_safe (~> 0.3, >= 0.3.1)
8
12
  diff-lcs (1.2.5)
@@ -20,7 +24,7 @@ GEM
20
24
  oauth2
21
25
  hashie (3.3.1)
22
26
  highline (1.6.21)
23
- httparty (0.13.3)
27
+ httparty (0.13.5)
24
28
  json (~> 1.8)
25
29
  multi_xml (>= 0.5.2)
26
30
  jeweler (2.0.1)
@@ -35,8 +39,6 @@ GEM
35
39
  json (1.8.1)
36
40
  jwt (1.0.0)
37
41
  mini_portile (0.6.0)
38
- money-tree (0.9.0)
39
- ffi
40
42
  multi_json (1.10.1)
41
43
  multi_xml (0.5.5)
42
44
  multipart-post (2.0.0)
@@ -64,17 +66,25 @@ GEM
64
66
  rspec-mocks (3.1.0)
65
67
  rspec-support (~> 3.1.0)
66
68
  rspec-support (3.1.0)
69
+ safe_yaml (1.0.4)
67
70
  satoshi-unit (0.1.7)
68
71
  thread_safe (0.3.4)
72
+ vcr (2.9.3)
73
+ webmock (1.21.0)
74
+ addressable (>= 2.3.6)
75
+ crack (>= 0.3.2)
69
76
 
70
77
  PLATFORMS
71
78
  ruby
72
79
 
73
80
  DEPENDENCIES
81
+ btcruby (~> 1.0)
74
82
  bundler (~> 1.0)
83
+ faraday
75
84
  github_api (= 0.11.3)
76
- httparty
85
+ httparty (~> 0.13.5)
77
86
  jeweler (~> 2.0.1)
78
- money-tree (= 0.9.0)
79
87
  rspec
80
- satoshi-unit
88
+ satoshi-unit (~> 0.1)
89
+ vcr
90
+ webmock
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  Straight
2
2
  ========
3
+
4
+ [![Build Status](https://travis-ci.org/MyceliumGear/straight.svg)](https://travis-ci.org/MyceliumGear/straight)
5
+
3
6
  > Receive bitcoin payments directly into your wallet
4
7
 
5
8
  > Website: http://straight.romansnitko.com
data/Rakefile CHANGED
@@ -21,5 +21,14 @@ Jeweler::Tasks.new do |gem|
21
21
  gem.description = %Q{An engine for the Straight payment gateway software. Requires no state to be saved (that is, no storage or DB). Its responsibilities only include processing data coming from an actual gateway.}
22
22
  gem.email = "roman.snitko@gmail.com"
23
23
  gem.authors = ["Roman Snitko"]
24
+ gem.files.exclude 'spec/**/*'
24
25
  end
25
26
  Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ begin
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec)
31
+ task default: :spec
32
+ rescue LoadError
33
+ # no rspec available
34
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3
1
+ 1.0.0
data/lib/straight.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'money-tree'
1
+ require 'btcruby'
2
2
  require 'satoshi-unit'
3
3
  require 'json'
4
4
  require 'uri'
@@ -6,11 +6,18 @@ require 'open-uri'
6
6
  require 'yaml'
7
7
  require 'singleton'
8
8
  require 'httparty'
9
+ require 'faraday'
10
+ require_relative 'straight/faraday_monkeypatch'
11
+
12
+ module Straight
13
+ StraightError = Class.new(StandardError)
14
+ end
9
15
 
10
16
  require_relative 'straight/blockchain_adapter'
11
17
  require_relative 'straight/blockchain_adapters/blockchain_info_adapter'
12
18
  require_relative 'straight/blockchain_adapters/biteasy_adapter'
13
19
  require_relative 'straight/blockchain_adapters/mycelium_adapter'
20
+ require_relative 'straight/blockchain_adapters/insight_adapter'
14
21
 
15
22
  require_relative 'straight/exchange_rate_adapter'
16
23
  require_relative 'straight/exchange_rate_adapters/bitpay_adapter'
@@ -22,6 +29,7 @@ require_relative 'straight/exchange_rate_adapters/btce_adapter'
22
29
  require_relative 'straight/exchange_rate_adapters/kraken_adapter'
23
30
  require_relative 'straight/exchange_rate_adapters/average_rate_adapter'
24
31
 
32
+ require_relative 'straight/address_providers/bip32'
25
33
 
26
34
  require_relative 'straight/order'
27
35
  require_relative 'straight/gateway'
@@ -0,0 +1,28 @@
1
+ module Straight
2
+ module AddressProvider
3
+ class Base
4
+
5
+ attr_reader :gateway
6
+
7
+ def initialize(gateway)
8
+ @gateway = gateway
9
+ end
10
+
11
+ # @param [Hash] args see GatewayModule::Includable#new_order
12
+ # @return [String] bitcoin address
13
+ # Returns a Base58-encoded Bitcoin address to which the payment transaction
14
+ # is expected to arrive. keychain_id is an integer > 0 (hopefully not too large and hopefully
15
+ # the one a user of this class is going to properly increment) that is used to generate a
16
+ # an BIP32 bitcoin address deterministically.
17
+ def new_address(keychain_id:, **args)
18
+ raise NotImplementedError
19
+ end
20
+
21
+ # If this method returns true, then address provider is expected to define
22
+ # #new_address_and_amount which returns ['address', Integer(amount in satoshi)]
23
+ def takes_fees?
24
+ false
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'base'
2
+
3
+ module Straight
4
+ module AddressProvider
5
+ class Bip32 < Base
6
+ def new_address(keychain_id:, **args)
7
+ path =
8
+ if gateway.address_derivation_scheme.to_s.empty?
9
+ # First check the depth. If the depth is 4 use '/i' notation (Mycelium iOS wallet)
10
+ if gateway.keychain.depth > 3
11
+ keychain_id.to_s
12
+ else # Otherwise, use 'm/0/n' - both Electrum and Mycelium on Android
13
+ "m/0/#{keychain_id.to_s}"
14
+ end
15
+ else
16
+ gateway.address_derivation_scheme.to_s.downcase.sub('n', keychain_id.to_s)
17
+ end
18
+ gateway.keychain.derived_key(path).address.to_s
19
+ end
20
+ end
21
+ end
22
+ end
@@ -10,7 +10,14 @@ module Straight
10
10
  # Raised when blockchain data cannot be retrived for any reason.
11
11
  # We're not really intereste in the precise reason, although it is
12
12
  # stored in the message.
13
- class RequestError < Exception; end
13
+ class RequestError < StraightError; end
14
+
15
+ # Raised when an invalid address is used, for example a mainnet address
16
+ # is used on testnet and vice versa.
17
+ class BitcoinAddressInvalid < StraightError; end
18
+
19
+ # How much times try to connect to servers if ReadTimeout error appears
20
+ MAX_TRIES = 5
14
21
 
15
22
  def fetch_transaction(tid)
16
23
  raise "Please implement #fetch_transaction in #{self.to_s}"
@@ -34,5 +41,14 @@ module Straight
34
41
 
35
42
  end
36
43
 
44
+ # Look for the adapter without namespace if not found it in a specific module
45
+ # @return nil
46
+ def self.const_missing(name)
47
+ Kernel.const_get(name)
48
+ rescue NameError
49
+ puts "WARNING: No blockchain adapter with the name #{name.to_s} was found!"
50
+ nil
51
+ end
52
+
37
53
  end
38
54
  end
@@ -19,34 +19,35 @@ module Straight
19
19
 
20
20
  # Returns the current balance of the address
21
21
  def fetch_balance_for(address)
22
- JSON.parse(api_request("/addresses/#{address}"))['data']['balance']
22
+ api_request("/addresses/#{address}")['data']['balance']
23
23
  end
24
24
 
25
25
  # Returns transaction info for the tid
26
26
  def fetch_transaction(tid, address: nil)
27
- straighten_transaction JSON.parse(api_request("/transactions/#{tid}"), address: address)
27
+ straighten_transaction api_request("/transactions/#{tid}"), address: address
28
28
  end
29
29
 
30
30
  # Returns all transactions for the address
31
31
  def fetch_transactions_for(address)
32
- transactions = JSON.parse(api_request("/transactions?address=#{address}"))['data']['transactions']
32
+ transactions = api_request("/transactions?address=#{address}")['data']['transactions']
33
33
  transactions.map { |t| straighten_transaction(t, address: address) }
34
34
  end
35
35
 
36
36
  private
37
37
 
38
38
  def api_request(url)
39
- begin
40
- response = HTTParty.get("#{@base_url}/#{url}", timeout: 4, verify: false)
41
- unless response.code == 200
42
- raise RequestError, "Cannot access remote API, response code was #{response.code}"
43
- end
44
- response.body
45
- rescue HTTParty::Error => e
46
- raise RequestError, YAML::dump(e)
47
- rescue JSON::ParserError => e
48
- raise RequestError, YAML::dump(e)
39
+ conn = Faraday.new("#{@base_url}/#{url}", ssl: { verify: false }) do |faraday|
40
+ faraday.adapter Faraday.default_adapter
49
41
  end
42
+ result = conn.get
43
+ unless result.status == 200
44
+ raise RequestError, "Cannot access remote API, response code was #{result.code}"
45
+ end
46
+ JSON.parse(result.body)
47
+ rescue JSON::ParserError => e
48
+ raise RequestError, YAML::dump(e)
49
+ rescue => e
50
+ raise RequestError, YAML::dump(e)
50
51
  end
51
52
 
52
53
  # Converts transaction info received from the source into the
@@ -20,18 +20,18 @@ module Straight
20
20
 
21
21
  # Returns transaction info for the tid
22
22
  def fetch_transaction(tid, address: nil)
23
- straighten_transaction JSON.parse(api_request("/rawtx/#{tid}"), address: address)
23
+ straighten_transaction(api_request("/rawtx/#{tid}"), address: address)
24
24
  end
25
25
 
26
26
  # Returns all transactions for the address
27
27
  def fetch_transactions_for(address)
28
- transactions = JSON.parse(api_request("/rawaddr/#{address}"))['txs']
28
+ transactions = api_request("/rawaddr/#{address}")['txs']
29
29
  transactions.map { |t| straighten_transaction(t, address: address) }
30
30
  end
31
31
 
32
32
  # Returns the current balance of the address
33
33
  def fetch_balance_for(address)
34
- JSON.parse(api_request("/rawaddr/#{address}"))['final_balance']
34
+ api_request("/rawaddr/#{address}")['final_balance']
35
35
  end
36
36
 
37
37
  def latest_block(force_reload: false)
@@ -42,7 +42,7 @@ module Straight
42
42
  force_reload
43
43
  @latest_block = {
44
44
  cache_timestamp: Time.now,
45
- block: JSON.parse(api_request("/latestblock"))
45
+ block: api_request("/latestblock")
46
46
  }
47
47
  else
48
48
  @latest_block
@@ -52,17 +52,18 @@ module Straight
52
52
  private
53
53
 
54
54
  def api_request(url)
55
- begin
56
- response = HTTParty.get("#{@base_url}/#{url}", timeout: 4, verify: false)
57
- unless response.code == 200
58
- raise RequestError, "Cannot access remote API, response code was #{response.code}"
59
- end
60
- response.body
61
- rescue HTTParty::Error => e
62
- raise RequestError, YAML::dump(e)
63
- rescue JSON::ParserError => e
64
- raise RequestError, YAML::dump(e)
55
+ conn = Faraday.new(url: "#{@base_url}/#{url}", ssl: { verify: false }) do |faraday|
56
+ faraday.adapter Faraday.default_adapter
65
57
  end
58
+ result = conn.get
59
+ unless result.status == 200
60
+ raise RequestError, "Cannot access remote API, response code was #{result.code}"
61
+ end
62
+ JSON.parse(result.body)
63
+ rescue JSON::ParserError => e
64
+ raise RequestError, YAML::dump(e)
65
+ rescue => e
66
+ raise RequestError, YAML::dump(e)
66
67
  end
67
68
 
68
69
  # Converts transaction info received from the source into the
@@ -0,0 +1,76 @@
1
+ module Straight
2
+ module Blockchain
3
+
4
+ class InsightAdapter < Adapter
5
+
6
+ @@test_url = nil
7
+
8
+ def self.mainnet_adapter(main_url:, test_url: nil)
9
+ @@test_url = test_url
10
+ new(main_url)
11
+ end
12
+
13
+ def self.testnet_adapter
14
+ raise "Testnet not implemented" unless @@test_url
15
+ new(@@test_url)
16
+ end
17
+
18
+ def initialize(host_url)
19
+ @base_url = host_url
20
+ end
21
+
22
+ def fetch_transaction(tid, address: nil)
23
+ res = api_request("/tx/", tid)
24
+ straighten_transaction(res, address: address)
25
+ end
26
+
27
+ def fetch_transactions_for(address)
28
+ res = api_request("/addr/", address)
29
+ return [] if res["transactions"].empty?
30
+ [fetch_transaction(res["transactions"].first, address: address)]
31
+ end
32
+
33
+ def fetch_balance_for(address)
34
+ res = api_request("/addr/", address)
35
+ res["balanceSat"].to_i
36
+ end
37
+
38
+ private
39
+
40
+ def api_request(place, val)
41
+ req_url = @base_url + place + val
42
+ conn = Faraday.new(url: req_url, ssl: { verify: false }) do |faraday|
43
+ faraday.adapter Faraday.default_adapter
44
+ end
45
+ result = conn.get do |req|
46
+ req.headers['Content-Type'] = 'application/json'
47
+ end
48
+ JSON.parse(result.body)
49
+ rescue JSON::ParserError => e
50
+ raise BitcoinAddressInvalid, message: "address in question: #{val}" if e.message.include?("Invalid address")
51
+ raise RequestError, YAML::dump(e)
52
+ rescue => e
53
+ raise RequestError, YAML::dump(e)
54
+ end
55
+
56
+ def straighten_transaction(transaction, address: nil)
57
+ total_amount = 0
58
+ tid = transaction["txid"]
59
+ transaction["vout"].each do |o|
60
+ total_amount += Satoshi.new(o["value"]) if address.nil? || address == o["scriptPubKey"]["addresses"].first
61
+ end
62
+ confirmations = transaction["confirmations"]
63
+ outs = transaction["vout"].map { |o| {amount: Satoshi.new(o["value"]).to_i, receiving_address: o["scriptPubKey"]["addresses"].first} }
64
+
65
+ {
66
+ tid: tid,
67
+ total_amount: total_amount,
68
+ confirmations: confirmations || 0,
69
+ outs: outs || []
70
+ }
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+ end
@@ -3,30 +3,46 @@ module Straight
3
3
 
4
4
  class MyceliumAdapter < Adapter
5
5
 
6
- class NoBitcoindInstalled < Exception
7
- def message
8
- "You need to install bitcoind on your server and have a `bitcoin-cli` executable in PATH.\n" +
9
- "Note that you don't have to download the blockchain and you can run bitcoind in offline mode."
10
- end
11
- end
12
-
13
- require 'base64'
6
+ MAINNET_SERVERS = %w{
7
+ https://mws2.mycelium.com/wapi/wapi
8
+ https://mws6.mycelium.com/wapi/wapi
9
+ https://mws7.mycelium.com/wapi/wapi
10
+ }
11
+ TESTNET_SERVERS = %w{
12
+ https://node3.mycelium.com/wapitestnet/wapi
13
+ }
14
+ PINNED_CERTIFICATES = %w{
15
+ 6afe0e9b6806fa4a49fc6818512014332953f30101dad7b91e76c14e073c3134
16
+ }.to_set
14
17
 
15
18
  def self.mainnet_adapter
16
- instance = self.instance
17
- instance._initialize("https://mws2.mycelium.com/wapi/wapi")
18
- instance
19
+ new(testnet: false)
19
20
  end
20
-
21
+
21
22
  def self.testnet_adapter
22
- instance = self.instance
23
- instance._initialize("https://node3.mycelium.com/wapitestnet/wapi")
24
- instance
23
+ new(testnet: true)
25
24
  end
26
-
27
- def _initialize(base_url)
25
+
26
+ def initialize(testnet: false)
28
27
  @latest_block = { cache_timestamp: nil, block: nil }
29
- @base_url = base_url
28
+ @testnet = testnet
29
+ @api_servers = @testnet ? TESTNET_SERVERS : MAINNET_SERVERS
30
+ set_base_url
31
+ end
32
+
33
+ def testnet?
34
+ @testnet
35
+ end
36
+
37
+ # Set url for API request.
38
+ # @param num [Integer] a number of server in array
39
+ def set_base_url(num = 0)
40
+ return nil if num >= @api_servers.size
41
+ @base_url = @api_servers[num]
42
+ end
43
+
44
+ def next_server
45
+ set_base_url(@api_servers.index(@base_url) + 1)
30
46
  end
31
47
 
32
48
  # Returns transaction info for the tid
@@ -38,8 +54,15 @@ module Straight
38
54
  # Supposed to returns all transactions for the address, but
39
55
  # currently actually returns the first one, since we only need one.
40
56
  def fetch_transactions_for(address)
41
- tid = api_request('queryTransactionInventory', { addresses: [address], limit: 1 })["txIds"].first
42
- tid ? [fetch_transaction(tid, address: address)] : []
57
+ # API may return nil instead of an empty array if address turns out to be invalid
58
+ # (for example when trying to supply a testnet address instead of mainnet while using
59
+ # mainnet adapter.
60
+ if api_response = api_request('queryTransactionInventory', { addresses: [address], limit: 1 })
61
+ tid = api_response["txIds"].first
62
+ tid ? [fetch_transaction(tid, address: address)] : []
63
+ else
64
+ raise BitcoinAddressInvalid, message: "address in question: #{address}"
65
+ end
43
66
  end
44
67
 
45
68
  # Returns the current balance of the address
@@ -69,54 +92,56 @@ module Straight
69
92
  private
70
93
 
71
94
  def api_request(method, params={})
95
+ ssl_opts =
96
+ if testnet?
97
+ {verify: false}
98
+ else
99
+ {verify_callback: lambda { |preverify_ok, store_context|
100
+ end_cert = store_context.chain[0] # pinned invalid certificate
101
+ PINNED_CERTIFICATES.include?(OpenSSL::Digest::SHA256.hexdigest(end_cert.to_der)) || preverify_ok
102
+ }}
103
+ end
72
104
  begin
73
- body = JSON.parse(HTTParty.post(
74
- "#{@base_url}/#{method}",
75
- body: params.merge({version: 1}).to_json,
76
- headers: { 'Content-Type' => 'application/json' },
77
- timeout: 15,
78
- verify: false
79
- ).body)["r"]
80
- rescue HTTParty::Error => e
81
- raise RequestError, YAML::dump(e)
82
- rescue JSON::ParserError => e
83
- raise RequestError, YAML::dump(e)
105
+ conn = Faraday.new(url: "#{@base_url}/#{method}", ssl: ssl_opts) do |faraday|
106
+ faraday.request :url_encoded # form-encode POST params
107
+ faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
108
+ end
109
+ result = conn.post do |req|
110
+ req.body = params.merge({version: 1}).to_json
111
+ req.headers['Content-Type'] = 'application/json'
112
+ end
113
+ JSON.parse(result.body || '')['r']
114
+ rescue => e
115
+ next_server ? retry : raise(RequestError, YAML::dump(e))
84
116
  end
85
117
  end
86
118
 
87
119
  # Converts transaction info received from the source into the
88
120
  # unified format expected by users of BlockchainAdapter instances.
89
121
  def straighten_transaction(transaction, address: nil)
90
-
91
122
  # Get the block number this transaction was included into
92
123
  block_height = transaction['height']
93
124
  tid = transaction['txid']
94
125
 
95
- # Converting from Base64 to hex
96
- transaction = transaction['binary'].unpack("m0").first.unpack("H*").first
126
+ # Converting from Base64 to binary
127
+ transaction = transaction['binary'].unpack('m0')[0]
97
128
 
98
- # Decoding with bitcoin-cli
99
- begin
100
- transaction = JSON.parse(`bitcoin-cli decoderawtransaction #{transaction}`)
101
- rescue Errno::ENOENT => e
102
- if e.message == 'No such file or directory - bitcoin-cli'
103
- raise NoBitcoindInstalled
104
- else
105
- raise e
106
- end
107
- end
129
+ # Decoding
130
+ transaction = BTC::Transaction.new(data: transaction)
108
131
 
109
132
  outs = []
110
133
  total_amount = 0
111
- transaction['vout'].each do |out|
112
- out['value'] = out['value']*10**8
113
- total_amount += out['value'] if address.nil? || address == out['address']
114
- outs << { amount: out['value'], receiving_address: out['address'] }
134
+
135
+ transaction.outputs.each do |out|
136
+ amount = out.value
137
+ receiving_address = out.script.standard_address
138
+ total_amount += amount if address.nil? || address == receiving_address.to_s
139
+ outs << {amount: amount, receiving_address: receiving_address}
115
140
  end
116
141
 
117
142
  {
118
143
  tid: tid,
119
- total_amount: total_amount,
144
+ total_amount: total_amount.to_i,
120
145
  confirmations: calculate_confirmations(block_height),
121
146
  outs: outs
122
147
  }