straight 0.2.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }