straight 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +5 -1
- data/README.md +20 -10
- data/VERSION +1 -1
- data/lib/straight.rb +10 -1
- data/lib/straight/blockchain_adapter.rb +2 -13
- data/lib/straight/blockchain_adapters/biteasy_adapter.rb +74 -0
- data/lib/straight/blockchain_adapters/blockchain_info_adapter.rb +37 -17
- data/lib/straight/blockchain_adapters/mycelium_adapter.rb +144 -0
- data/lib/straight/exchange_rate_adapter.rb +20 -1
- data/lib/straight/exchange_rate_adapters/average_rate_adapter.rb +54 -0
- data/lib/straight/exchange_rate_adapters/bitpay_adapter.rb +5 -2
- data/lib/straight/exchange_rate_adapters/bitstamp_adapter.rb +2 -1
- data/lib/straight/exchange_rate_adapters/btce_adapter.rb +18 -0
- data/lib/straight/exchange_rate_adapters/coinbase_adapter.rb +2 -4
- data/lib/straight/exchange_rate_adapters/kraken_adapter.rb +18 -0
- data/lib/straight/exchange_rate_adapters/localbitcoins_adapter.rb +17 -0
- data/lib/straight/exchange_rate_adapters/okcoin_adapter.rb +18 -0
- data/lib/straight/gateway.rb +20 -9
- data/lib/straight/order.rb +46 -13
- data/spec/lib/blockchain_adapters/{helloblock_io_spec.rb → biteasy_adapter_spec.rb} +23 -18
- data/spec/lib/blockchain_adapters/{blockchain_info_spec.rb → blockchain_info_adapter_spec.rb} +8 -3
- data/spec/lib/blockchain_adapters/mycelium_adapter_spec.rb +54 -0
- data/spec/lib/exchange_rate_adapter_spec.rb +6 -1
- data/spec/lib/exchange_rate_adapters/average_rate_adapter_spec.rb +43 -0
- data/spec/lib/exchange_rate_adapters/bitpay_adapter_spec.rb +14 -1
- data/spec/lib/exchange_rate_adapters/bitstamp_adapter_spec.rb +14 -1
- data/spec/lib/exchange_rate_adapters/btce_adapter_spec.rb +27 -0
- data/spec/lib/exchange_rate_adapters/coinbase_adapter_spec.rb +14 -1
- data/spec/lib/exchange_rate_adapters/kraken_adapter_spec.rb +27 -0
- data/spec/lib/exchange_rate_adapters/localbitcoins_adapter_spec.rb +27 -0
- data/spec/lib/exchange_rate_adapters/okcoin_adapter_spec.rb +27 -0
- data/spec/lib/gateway_spec.rb +23 -5
- data/spec/lib/order_spec.rb +18 -2
- data/straight.gemspec +95 -0
- metadata +33 -6
- data/lib/straight/blockchain_adapters/helloblock_io_adapter.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ce549f0d3b951b8ce6e5d6e35ba868497b977e7
|
4
|
+
data.tar.gz: 5c44763aa3b9d35441e0d1bdd4824dac85238320
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4632fafae05cd13ab8443a6817806bca0e67fa0f7eddc03027944287e6c57c8437bd914f18589db3b9e39a91ac2a9e796d68015485132ff2e68774fe70f4f86
|
7
|
+
data.tar.gz: 0724e768858d85d0b3f9290d6570f75944167554b9b5ed5177cbb97c1ae76f5569b68730e5a77cba88db1b499413e486381b0dfe35681f5c8aab8051cbc0982d
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -20,6 +20,9 @@ GEM
|
|
20
20
|
oauth2
|
21
21
|
hashie (3.3.1)
|
22
22
|
highline (1.6.21)
|
23
|
+
httparty (0.13.3)
|
24
|
+
json (~> 1.8)
|
25
|
+
multi_xml (>= 0.5.2)
|
23
26
|
jeweler (2.0.1)
|
24
27
|
builder
|
25
28
|
bundler (>= 1.0)
|
@@ -61,7 +64,7 @@ GEM
|
|
61
64
|
rspec-mocks (3.1.0)
|
62
65
|
rspec-support (~> 3.1.0)
|
63
66
|
rspec-support (3.1.0)
|
64
|
-
satoshi-unit (0.1.
|
67
|
+
satoshi-unit (0.1.7)
|
65
68
|
thread_safe (0.3.4)
|
66
69
|
|
67
70
|
PLATFORMS
|
@@ -70,6 +73,7 @@ PLATFORMS
|
|
70
73
|
DEPENDENCIES
|
71
74
|
bundler (~> 1.0)
|
72
75
|
github_api (= 0.11.3)
|
76
|
+
httparty
|
73
77
|
jeweler (~> 2.0.1)
|
74
78
|
money-tree
|
75
79
|
rspec
|
data/README.md
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
Straight
|
2
2
|
========
|
3
3
|
> Receive bitcoin payments directly into your wallet
|
4
|
+
|
4
5
|
> Website: http://straight.romansnitko.com
|
5
6
|
|
6
|
-
Straight is a built-in stateless gateway
|
7
|
-
your online store. Drop in this library,
|
8
|
-
|
9
|
-
with your database.
|
7
|
+
Straight is a built-in stateless gateway library written in Ruby.
|
8
|
+
It allows you to receive bitcoin payments for your online store. Drop in this library,
|
9
|
+
set your public key and start receiving payments. Your BIP32-compatible wallet will
|
10
|
+
see payments automatically without any need for integration with your database.
|
10
11
|
|
11
12
|
Straight cares about security and privacy. No private keys are stored on the server,
|
12
13
|
each order uses unique payment address. Straight notifies your application when payment is
|
@@ -16,7 +17,7 @@ IMPORTANT: this is a gem, not a server. It has no state and is intended to use w
|
|
16
17
|
an application, such as Ruby On Rails. Most likely, you want
|
17
18
|
[straight-server](https://github.com/snitko/straight-server), it is a server,
|
18
19
|
which holds the state of all orders for you and has a RESTful API you can use
|
19
|
-
with any application written in
|
20
|
+
with any application written in any language or platform.
|
20
21
|
|
21
22
|
Bitcoin donations are appreciated: 1D3PknG4Lw1gFuJ9SYenA7pboF9gtXtdcD
|
22
23
|
|
@@ -61,6 +62,7 @@ Usage
|
|
61
62
|
gateway.name = 'my gateway'
|
62
63
|
|
63
64
|
# Set the callback for orders' status changes
|
65
|
+
# (see lib/straight/order.rb for status attribute values and their meanings)
|
64
66
|
#
|
65
67
|
gateway.order_callbacks = [
|
66
68
|
lambda { |order| puts "Order status changed to #{order.status}" }
|
@@ -101,8 +103,8 @@ When this module is included, it doesn't actually *include* all the methods, som
|
|
101
103
|
It is important specifically for getters and setters and as a general rule only getters and setters are prepended.
|
102
104
|
|
103
105
|
If you don't want to bother yourself with modules, please use `Straight::Order` class and simply create new instances of it.
|
104
|
-
However, if you are contributing to the library, all new functionality should go to either Straight::OrderModule::Includable or
|
105
|
-
Straight::OrderModule::Prependable (most likely the former).
|
106
|
+
However, if you are contributing to the library, all new functionality should go to either `Straight::OrderModule::Includable` or
|
107
|
+
`Straight::OrderModule::Prependable` (most likely the former).
|
106
108
|
|
107
109
|
|
108
110
|
Important Considerations
|
@@ -115,14 +117,22 @@ all orders should be indexed sequentially, not randomly.
|
|
115
117
|
Why can't we just derive new addresses from order UUID, or assign them to orders? The reason is that your
|
116
118
|
wallet will have to integrate with your very own database and it may be enormously cumbersome to implement
|
117
119
|
in a generic way. Alternative would be to create a wallet within Straight and make it generate and keep the
|
118
|
-
private keys, but this would be highly insecure. Keys stored on popular hosting solutions would quickly
|
119
|
-
all sorts of attacks to get money from them.
|
120
|
+
private keys, but this would be highly insecure. Keys stored on popular hosting solutions would quickly
|
121
|
+
invite all sorts of attacks to get money from them.
|
122
|
+
|
123
|
+
|
124
|
+
A note about Mycelium blockchain adapter
|
125
|
+
----------------------------------------
|
126
|
+
If you wish to use Mycelium blockchain adapter you MUST install bitcoind on your server (you may run it in offline mode, no need to download the whole blockchain!) and have a `bitcoin-cli` in your PATH. This
|
127
|
+
requirement is due to the need to parse raw bitcoin transaction received from Mycelium WAPI.
|
128
|
+
By default, Mycelium is included as a second (fallback) adapter and will only be used in case
|
129
|
+
BlockchainInfo one fails. It will not raise an exception until it actually tries to parse the trasaction
|
130
|
+
and finds there is no `bitcoin-cli` in PATH.
|
120
131
|
|
121
132
|
Requirements
|
122
133
|
------------
|
123
134
|
Ruby 2.1 or later.
|
124
135
|
|
125
|
-
|
126
136
|
Credits
|
127
137
|
-------
|
128
138
|
Authors:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/straight.rb
CHANGED
@@ -4,15 +4,24 @@ require 'json'
|
|
4
4
|
require 'uri'
|
5
5
|
require 'open-uri'
|
6
6
|
require 'yaml'
|
7
|
+
require 'singleton'
|
8
|
+
require 'httparty'
|
7
9
|
|
8
10
|
require_relative 'straight/blockchain_adapter'
|
9
11
|
require_relative 'straight/blockchain_adapters/blockchain_info_adapter'
|
10
|
-
require_relative 'straight/blockchain_adapters/
|
12
|
+
require_relative 'straight/blockchain_adapters/biteasy_adapter'
|
13
|
+
require_relative 'straight/blockchain_adapters/mycelium_adapter'
|
11
14
|
|
12
15
|
require_relative 'straight/exchange_rate_adapter'
|
13
16
|
require_relative 'straight/exchange_rate_adapters/bitpay_adapter'
|
14
17
|
require_relative 'straight/exchange_rate_adapters/coinbase_adapter'
|
15
18
|
require_relative 'straight/exchange_rate_adapters/bitstamp_adapter'
|
19
|
+
require_relative 'straight/exchange_rate_adapters/localbitcoins_adapter'
|
20
|
+
require_relative 'straight/exchange_rate_adapters/okcoin_adapter'
|
21
|
+
require_relative 'straight/exchange_rate_adapters/btce_adapter'
|
22
|
+
require_relative 'straight/exchange_rate_adapters/kraken_adapter'
|
23
|
+
require_relative 'straight/exchange_rate_adapters/average_rate_adapter'
|
24
|
+
|
16
25
|
|
17
26
|
require_relative 'straight/order'
|
18
27
|
require_relative 'straight/gateway'
|
@@ -5,24 +5,13 @@ module Straight
|
|
5
5
|
# all blockchain adapters as well as supplying some useful methods.
|
6
6
|
class Adapter
|
7
7
|
|
8
|
+
include Singleton
|
9
|
+
|
8
10
|
# Raised when blockchain data cannot be retrived for any reason.
|
9
11
|
# We're not really intereste in the precise reason, although it is
|
10
12
|
# stored in the message.
|
11
13
|
class RequestError < Exception; end
|
12
14
|
|
13
|
-
# This method is a wrapper for creating an HTTP request
|
14
|
-
# to various services that ancestors of this class may use
|
15
|
-
# to retrieve blockchain data. Why do we need a wrapper?
|
16
|
-
# Because it respects timeouts.
|
17
|
-
def http_request(url)
|
18
|
-
uri = URI.parse(url)
|
19
|
-
begin
|
20
|
-
http = uri.read(read_timeout: 4)
|
21
|
-
rescue OpenURI::HTTPError => e
|
22
|
-
raise RequestError, YAML::dump(e)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
15
|
def fetch_transaction(tid)
|
27
16
|
raise "Please implement #fetch_transaction in #{self.to_s}"
|
28
17
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Straight
|
2
|
+
module Blockchain
|
3
|
+
|
4
|
+
class BiteasyAdapter < Adapter
|
5
|
+
|
6
|
+
def self.mainnet_adapter
|
7
|
+
instance = self.instance
|
8
|
+
instance._initialize("https://api.biteasy.com/blockchain/v1")
|
9
|
+
instance
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.testnet_adapter
|
13
|
+
raise "Not Supported Yet"
|
14
|
+
end
|
15
|
+
|
16
|
+
def _initialize(base_url)
|
17
|
+
@base_url = base_url
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the current balance of the address
|
21
|
+
def fetch_balance_for(address)
|
22
|
+
JSON.parse(api_request("/addresses/#{address}"))['data']['balance']
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns transaction info for the tid
|
26
|
+
def fetch_transaction(tid, address: nil)
|
27
|
+
straighten_transaction JSON.parse(api_request("/transactions/#{tid}"), address: address)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns all transactions for the address
|
31
|
+
def fetch_transactions_for(address)
|
32
|
+
transactions = JSON.parse(api_request("/transactions?address=#{address}"))['data']['transactions']
|
33
|
+
transactions.map { |t| straighten_transaction(t, address: address) }
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
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)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Converts transaction info received from the source into the
|
53
|
+
# unified format expected by users of BlockchainAdapter instances.
|
54
|
+
def straighten_transaction(transaction, address: nil)
|
55
|
+
outs = []
|
56
|
+
total_amount = 0
|
57
|
+
transaction['data']['outputs'].each do |out|
|
58
|
+
total_amount += out['value'] if address.nil? || address == out['to_address']
|
59
|
+
outs << { amount: out['value'], receiving_address: out['to_address'] }
|
60
|
+
end
|
61
|
+
|
62
|
+
{
|
63
|
+
tid: transaction['data']['hash'],
|
64
|
+
total_amount: total_amount,
|
65
|
+
confirmations: transaction['data']['confirmations'],
|
66
|
+
outs: outs
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -4,36 +4,67 @@ module Straight
|
|
4
4
|
class BlockchainInfoAdapter < Adapter
|
5
5
|
|
6
6
|
def self.mainnet_adapter
|
7
|
-
self.
|
7
|
+
instance = self.instance
|
8
|
+
instance._initialize("https://blockchain.info")
|
9
|
+
instance
|
8
10
|
end
|
9
11
|
|
10
12
|
def self.testnet_adapter
|
11
13
|
raise "Not Supported Yet"
|
12
14
|
end
|
13
15
|
|
14
|
-
def
|
16
|
+
def _initialize(base_url)
|
15
17
|
@latest_block = { cache_timestamp: nil, block: nil }
|
16
18
|
@base_url = base_url
|
17
19
|
end
|
18
20
|
|
19
21
|
# Returns transaction info for the tid
|
20
22
|
def fetch_transaction(tid, address: nil)
|
21
|
-
straighten_transaction JSON.parse(
|
23
|
+
straighten_transaction JSON.parse(api_request("/rawtx/#{tid}"), address: address)
|
22
24
|
end
|
23
25
|
|
24
26
|
# Returns all transactions for the address
|
25
27
|
def fetch_transactions_for(address)
|
26
|
-
transactions = JSON.parse(
|
28
|
+
transactions = JSON.parse(api_request("/rawaddr/#{address}"))['txs']
|
27
29
|
transactions.map { |t| straighten_transaction(t, address: address) }
|
28
30
|
end
|
29
31
|
|
30
32
|
# Returns the current balance of the address
|
31
33
|
def fetch_balance_for(address)
|
32
|
-
JSON.parse(
|
34
|
+
JSON.parse(api_request("/rawaddr/#{address}"))['final_balance']
|
35
|
+
end
|
36
|
+
|
37
|
+
def latest_block(force_reload: false)
|
38
|
+
# If we checked Blockchain.info latest block data
|
39
|
+
# more than a minute ago, check again. Otherwise, use cached version.
|
40
|
+
if @latest_block[:cache_timestamp].nil? ||
|
41
|
+
@latest_block[:cache_timestamp] < (Time.now - 60) ||
|
42
|
+
force_reload
|
43
|
+
@latest_block = {
|
44
|
+
cache_timestamp: Time.now,
|
45
|
+
block: JSON.parse(api_request("/latestblock"))
|
46
|
+
}
|
47
|
+
else
|
48
|
+
@latest_block
|
49
|
+
end
|
33
50
|
end
|
34
51
|
|
35
52
|
private
|
36
53
|
|
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)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
37
68
|
# Converts transaction info received from the source into the
|
38
69
|
# unified format expected by users of BlockchainAdapter instances.
|
39
70
|
def straighten_transaction(transaction, address: nil)
|
@@ -59,19 +90,8 @@ module Straight
|
|
59
90
|
# a certain address without making any new requests to the Blockchain API.
|
60
91
|
def calculate_confirmations(transaction, force_latest_block_reload: false)
|
61
92
|
|
62
|
-
# If we checked Blockchain.info latest block data
|
63
|
-
# more than a minute ago, check again. Otherwise, use cached version.
|
64
|
-
if @latest_block[:cache_timestamp].nil? ||
|
65
|
-
@latest_block[:cache_timestamp] < (Time.now - 60) ||
|
66
|
-
force_latest_block_reload
|
67
|
-
@latest_block = {
|
68
|
-
cache_timestamp: Time.now,
|
69
|
-
block: JSON.parse(http_request("#{@base_url}/latestblock"))
|
70
|
-
}
|
71
|
-
end
|
72
|
-
|
73
93
|
if transaction["block_height"]
|
74
|
-
|
94
|
+
latest_block(force_reload: force_latest_block_reload)[:block]["height"] - transaction["block_height"] + 1
|
75
95
|
else
|
76
96
|
0
|
77
97
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module Straight
|
2
|
+
module Blockchain
|
3
|
+
|
4
|
+
class MyceliumAdapter < Adapter
|
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'
|
14
|
+
|
15
|
+
def self.mainnet_adapter
|
16
|
+
instance = self.instance
|
17
|
+
instance._initialize("https://mws2.mycelium.com/wapi/wapi")
|
18
|
+
instance
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.testnet_adapter
|
22
|
+
instance = self.instance
|
23
|
+
instance._initialize("https://node3.mycelium.com/wapitestnet/wapi")
|
24
|
+
instance
|
25
|
+
end
|
26
|
+
|
27
|
+
def _initialize(base_url)
|
28
|
+
@latest_block = { cache_timestamp: nil, block: nil }
|
29
|
+
@base_url = base_url
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns transaction info for the tid
|
33
|
+
def fetch_transaction(tid, address: nil)
|
34
|
+
transaction = api_request('getTransactions', { txIds: [tid] })['transactions'].first
|
35
|
+
straighten_transaction transaction, address: address
|
36
|
+
end
|
37
|
+
|
38
|
+
# Supposed to returns all transactions for the address, but
|
39
|
+
# currently actually returns the first one, since we only need one.
|
40
|
+
def fetch_transactions_for(address)
|
41
|
+
tid = api_request('queryTransactionInventory', { addresses: [address], limit: 1 })["txIds"].first
|
42
|
+
tid ? [fetch_transaction(tid, address: address)] : []
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the current balance of the address
|
46
|
+
def fetch_balance_for(address)
|
47
|
+
unspent = 0
|
48
|
+
api_request('queryUnspentOutputs', { addresses: [address]})['unspent'].each do |out|
|
49
|
+
unspent += out['value']
|
50
|
+
end
|
51
|
+
unspent
|
52
|
+
end
|
53
|
+
|
54
|
+
# Here we are using Blockchain.info API at this point because I'm not sure how
|
55
|
+
# to get latest block from Mycelium WAPI
|
56
|
+
def latest_block(force_reload: false)
|
57
|
+
# If we checked Blockchain.info latest block data
|
58
|
+
# more than a minute ago, check again. Otherwise, use cached version.
|
59
|
+
if @latest_block[:cache_timestamp].nil? ||
|
60
|
+
@latest_block[:cache_timestamp] < (Time.now - 60) ||
|
61
|
+
force_reload
|
62
|
+
@latest_block = {
|
63
|
+
cache_timestamp: Time.now,
|
64
|
+
block: JSON.parse(HTTParty.get("https://blockchain.info/latestblock", timeout: 4, verify: false).body)
|
65
|
+
}
|
66
|
+
else
|
67
|
+
@latest_block
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def api_request(method, params={})
|
74
|
+
begin
|
75
|
+
body = JSON.parse(HTTParty.post(
|
76
|
+
"#{@base_url}/#{method}",
|
77
|
+
body: params.merge({version: 1}).to_json,
|
78
|
+
headers: { 'Content-Type' => 'application/json' },
|
79
|
+
timeout: 15,
|
80
|
+
verify: false
|
81
|
+
).body)["r"]
|
82
|
+
rescue HTTParty::Error => e
|
83
|
+
raise RequestError, YAML::dump(e)
|
84
|
+
rescue JSON::ParserError => e
|
85
|
+
raise RequestError, YAML::dump(e)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Converts transaction info received from the source into the
|
90
|
+
# unified format expected by users of BlockchainAdapter instances.
|
91
|
+
def straighten_transaction(transaction, address: nil)
|
92
|
+
|
93
|
+
# Get the block number this transaction was included into
|
94
|
+
block_height = transaction['height']
|
95
|
+
tid = transaction['txid']
|
96
|
+
|
97
|
+
# Converting from Base64 to hex
|
98
|
+
transaction = transaction['binary'].unpack("m0").first.unpack("H*").first
|
99
|
+
|
100
|
+
# Decoding with bitcoin-cli
|
101
|
+
begin
|
102
|
+
transaction = JSON.parse(`bitcoin-cli decoderawtransaction #{transaction}`)
|
103
|
+
rescue Errno::ENOENT => e
|
104
|
+
if e.message == 'No such file or directory - bitcoin-cli'
|
105
|
+
raise NoBitcoindInstalled
|
106
|
+
else
|
107
|
+
raise e
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
outs = []
|
112
|
+
total_amount = 0
|
113
|
+
transaction['vout'].each do |out|
|
114
|
+
out['value'] = out['value']*10**8
|
115
|
+
total_amount += out['value'] if address.nil? || address == out['address']
|
116
|
+
outs << { amount: out['value'], receiving_address: out['address'] }
|
117
|
+
end
|
118
|
+
|
119
|
+
{
|
120
|
+
tid: tid,
|
121
|
+
total_amount: total_amount,
|
122
|
+
confirmations: calculate_confirmations(block_height),
|
123
|
+
outs: outs
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
# When we call #calculate_confirmations, it doesn't always make a new
|
128
|
+
# request to the blockchain API. Instead, it checks if cached_id matches the one in
|
129
|
+
# the hash. It's useful when we want to calculate confirmations for all transactions for
|
130
|
+
# a certain address without making any new requests to the Blockchain API.
|
131
|
+
def calculate_confirmations(block_height, force_latest_block_reload: false)
|
132
|
+
|
133
|
+
if block_height && block_height != -1
|
134
|
+
latest_block(force_reload: force_latest_block_reload)[:block]["height"] - block_height + 1
|
135
|
+
else
|
136
|
+
0
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|