pochette 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +567 -46
- data/lib/pochette.rb +12 -0
- data/lib/pochette/backends/bitcoin_core.rb +4 -0
- data/lib/pochette/backends/blockchain_info.rb +111 -0
- data/lib/pochette/backends/trendy.rb +7 -0
- data/lib/pochette/transaction_builder.rb +11 -19
- data/lib/pochette/trezor_transaction_builder.rb +4 -5
- data/lib/pochette/version.rb +1 -1
- data/pochette.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c505a9f75cdfc003b454e87cd97c48f8fc11aeb7
|
4
|
+
data.tar.gz: 9e6c94633014b5d0f1ab9328dd1d9f0edea20ca1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba821bb23b235bb6ca4b9a2249c6ca6a3844665bcfe1a967e16066da4976e37d1cf01ce7e7004cc83c522fc84359d0a419e14a352264c1a4a5aa570a8b3b55cf
|
7
|
+
data.tar.gz: 2b312820cb91d983c0638473d957a13b4f45fe99044aa4361eede24d61d1ec638b2da51fcc4243e1f8c9d13a2d96107e0f687e0a9a9a8ea6869e76ab348f3f3e
|
data/README.md
CHANGED
@@ -8,14 +8,38 @@ bitcoin deposits to preparing transactions with bip32 paths instead of input add
|
|
8
8
|
ready to be signed with a [Trezor Device](https://www.bitcointrezor.com/)
|
9
9
|
|
10
10
|
Pochette offers a common interface to full bitcoin nodes like
|
11
|
-
[Bitcoin Core](https://bitcoin.org/en/download)
|
11
|
+
[Bitcoin Core](https://bitcoin.org/en/download)
|
12
|
+
[Blockchain.info](https://blockchain.info/api) or [Toshi](http://toshi.io)
|
12
13
|
and will let you run several instances of each one of them simultaneously
|
13
|
-
always choosing the most recent node to query.
|
14
|
+
always choosing the most recent node to query.
|
14
15
|
|
15
16
|
It also provides a Pochette::TransactionBuilder class which receives a list
|
16
|
-
of 'source' addresses and a list of recipients as "address/amount" pairs
|
17
|
-
|
18
|
-
|
17
|
+
of 'source' addresses and a list of recipients as "address/amount" pairs and
|
18
|
+
uses them to select unspent outputs and build a raw transaction to be signed
|
19
|
+
and broadcasted.
|
20
|
+
|
21
|
+
The Pochette::TrezorTransactionBuilder class extends Pochette::TransactionBuilder
|
22
|
+
including transactions, inputs and outputs that are formatted in a way they can
|
23
|
+
be passed directly to a Trezor device for signing.
|
24
|
+
|
25
|
+
## Table of contents
|
26
|
+
- [Installation and Setup](#installation-and-setup)
|
27
|
+
- [Pochette::TransactionBuilder](#the-pochettetransactionbuilder)
|
28
|
+
- [Pochette::TrezorTransactionBuilder](#the-pochettetrezortransactionbuilder)
|
29
|
+
- [Backend API](#backend-api)
|
30
|
+
- [incoming_for(addresses, min_date)](#incoming_foraddresses-min_date)
|
31
|
+
- [balances_for(addresses, confirmations)](#balances_foraddresses-confirmations)
|
32
|
+
- [list_unspent(addresses)](#list_unspentaddresses)
|
33
|
+
- [list_transactions(txids)](#list_transactionstxids)
|
34
|
+
- [block_height](#block_height)
|
35
|
+
- [pushtx(hex)](#pushtxhex)
|
36
|
+
- Supported Backends
|
37
|
+
- [BitcoinCore Backend](#bitcoincore-backend)
|
38
|
+
- [BlockchainInfo Backend](#blockchaininfo-backend)
|
39
|
+
- [Toshi Backend](#toshi-backend)
|
40
|
+
- [Trendy Backend](#trendy-backend)
|
41
|
+
|
42
|
+
## Installation and Setup
|
19
43
|
|
20
44
|
Add this line to your application's Gemfile:
|
21
45
|
|
@@ -31,55 +55,522 @@ Or install it yourself as:
|
|
31
55
|
|
32
56
|
$ gem install pochette
|
33
57
|
|
58
|
+
You will probably want to setup Pochette with a global default backend,
|
59
|
+
the backend can also be configured separately for each instance of
|
60
|
+
a Pochette::TransactionBuilder
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
>>> Pochette.backend = Pochette::Backends::BlockchainInfo.new
|
64
|
+
>>> Pochette::TransactionBuilder.backend = Pochette::Backends::BlockchainInfo.new
|
65
|
+
>>> Pochette::TrezorTransactionBuilder.backend = Pochette::Backends::BlockchainInfo.new
|
66
|
+
```
|
67
|
+
|
68
|
+
Pochette can also be configured to use the bitcoin testnet, this will change
|
69
|
+
the default network used by the Bitcoin gem and may alter the way backends work too.
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
>>> Pochette.testnet = true
|
73
|
+
```
|
74
|
+
|
34
75
|
## The Pochette::TransactionBuilder
|
35
76
|
|
36
77
|
The TransactionBuilder builds transactions from a list of source addresses and a list of recipients,
|
37
|
-
using
|
78
|
+
using a configured backend to fetch unspent outputs and related transaction data.
|
38
79
|
Instantiating will perform all the required queries, you'll be left with a
|
39
80
|
TransactionBuilder object that is either valid? or not, and if valid,
|
40
81
|
you can query the results via to_hash.
|
41
82
|
|
83
|
+
#### Receives
|
42
84
|
The TransactionBuilder's initializer receives a single options hash with:
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
85
|
+
|
86
|
+
<dl>
|
87
|
+
<dt>addresses:</dt>
|
88
|
+
<dd>
|
89
|
+
List of addresses in wallet.
|
90
|
+
We will be spending their unspent outputs.
|
91
|
+
</dd>
|
92
|
+
<dt>outputs:</dt>
|
93
|
+
<dd>
|
94
|
+
List of pairs [recipient_address, amount]
|
95
|
+
This will not be all the final outputs in the transaction,
|
96
|
+
as a 'change' output may be adted if needed.
|
97
|
+
</dd>
|
98
|
+
<dt>utxo_blacklist:</dt>
|
99
|
+
<dd>
|
100
|
+
Optional. List of utxos to ignore, a list of pairs [transaction hash, position]
|
101
|
+
</dd>
|
102
|
+
<dt>change_address:</dt>
|
103
|
+
<dd>
|
104
|
+
Optional. Change address to use. Will default to the first source address.
|
105
|
+
</dd>
|
106
|
+
<dt>fee_per_kb:</dt>
|
107
|
+
<dd>
|
108
|
+
Optional. Defaults to 10000 satoshis.
|
109
|
+
</dd>
|
110
|
+
<dt>spend_all:</dt>
|
111
|
+
<dd>
|
112
|
+
Optional. Boolean. Wether to spend all available utxos or just select enough to
|
113
|
+
cover the given outputs.
|
114
|
+
</dd>
|
115
|
+
</dl>
|
116
|
+
|
117
|
+
#### Returns
|
118
|
+
|
119
|
+
A hash with
|
120
|
+
|
121
|
+
<dl>
|
122
|
+
<dt>input_total:</dt>
|
123
|
+
<dd>The sum of all input amounts, in satoshis.</dd>
|
124
|
+
<dt>output_total:</dt>
|
125
|
+
<dd>The sum of all outputs, in satoshis.</dd>
|
126
|
+
<dt>fee:</dt>
|
127
|
+
<dd>fee to pay (input_total - output_total).</dd>
|
128
|
+
<dt>outputs:</dt>
|
129
|
+
<dd>Array of [destination address, amount in satoshis]</dd>
|
130
|
+
<dt>inputs:</dt>
|
131
|
+
<dd>Array of [input address, utxo transaction hash, utxo position, amount]</dd>
|
132
|
+
<dt>utxos_to_blacklist:</dt>
|
133
|
+
<dd>
|
134
|
+
Transaction inputs formatted to be used as utxo_blacklist on another
|
135
|
+
TransactionBuilder.
|
136
|
+
</dd>
|
137
|
+
</dl>
|
138
|
+
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
>>> require 'pochette'
|
142
|
+
>>> backend = Pochette::Backends::BlockchainInfo.new
|
143
|
+
>>> transaction = Pochette::TransactionBuilder.new({
|
144
|
+
addresses: ["2NAHscN6XVqUPzBSJHC3fhkeF5SQVxiR9p9"],
|
145
|
+
outputs: [
|
146
|
+
["mvtUvWSWCU7knrcMVzjcKJgjL1LdekLK5q", 1_0000_0000],
|
147
|
+
],
|
148
|
+
utxo_blacklist: [
|
149
|
+
["0ded7f014fa3213e9b000bc81b8151bc6f2f926b9afea6e3643c8ad658353c72", 1]
|
150
|
+
],
|
151
|
+
change_address: 'mgaTEUM4ZE9kLiK58FcffwHfvxpte5CfvE',
|
152
|
+
fee_per_kb: 10000,
|
153
|
+
spend_all: false,
|
154
|
+
backend: backend
|
155
|
+
})
|
156
|
+
>>> transaction.valid?
|
157
|
+
=> true
|
158
|
+
>>> transaction.as_hash
|
159
|
+
=> {
|
160
|
+
input_total: 2_0000_0000,
|
161
|
+
output_total: 1_9999_0000,
|
162
|
+
fee: 10000,
|
163
|
+
outputs: [
|
164
|
+
["mvtUvWSWCU7knrcMVzjcKJgjL1LdekLK5q", 1_0000_0000],
|
165
|
+
["mgaTEUM4ZE9kLiK58FcffwHfvxpte5CfvE", 9999_0000],
|
166
|
+
],
|
167
|
+
inputs: [
|
168
|
+
[ "2NAHscN6XVqUPzBSJHC3fhkeF5SQVxiR9p9",
|
169
|
+
"956b30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968e40",
|
170
|
+
1,
|
171
|
+
200000000
|
172
|
+
],
|
173
|
+
],
|
174
|
+
utxos_to_blacklist: [
|
175
|
+
["956b30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968e40", 1]
|
176
|
+
],
|
177
|
+
}
|
178
|
+
```
|
179
|
+
|
180
|
+
## The Pochette::TrezorTransactionBuilder
|
181
|
+
|
182
|
+
Builds a transaction like TransactionBuilder but includes transaction data
|
183
|
+
and formats inputs and outputs in a way that can be sent directly to your trezor
|
184
|
+
device.
|
185
|
+
If you're using [Trezor Connect](https://github.com/trezor/connect) for signing
|
186
|
+
then you won't need to pass in the transactions.
|
187
|
+
|
188
|
+
#### Receives
|
189
|
+
The TransactionBuilder's initializer receives a single options hash with:
|
190
|
+
|
191
|
+
<dl>
|
192
|
+
<dt>addresses:</dt>
|
193
|
+
<dd>
|
194
|
+
List of addresses in wallet. We will be spending their unspent outputs.
|
68
195
|
Each address is represented as a pair, with the public address string
|
69
196
|
and the BIP32 path as a list of integers, for example:
|
70
197
|
['public-address-as-string', [44, 1, 3, 11]]
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
198
|
+
</dd>
|
199
|
+
<dt>outputs:</dt>
|
200
|
+
<dd>
|
201
|
+
List of pairs [recipient_address, amount]
|
202
|
+
This will not be all the final outputs in the transaction,
|
203
|
+
as a 'change' output may be adted if needed.
|
204
|
+
</dd>
|
205
|
+
<dt>utxo_blacklist:</dt>
|
206
|
+
<dd>
|
207
|
+
Optional. List of utxos to ignore, a list of pairs [transaction hash, position]
|
208
|
+
</dd>
|
209
|
+
<dt>change_address:</dt>
|
210
|
+
<dd>
|
211
|
+
Optional. Change address to use. Will default to the first source address.
|
212
|
+
</dd>
|
213
|
+
<dt>fee_per_kb:</dt>
|
214
|
+
<dd>
|
215
|
+
Optional. Defaults to 10000 satoshis.
|
216
|
+
</dd>
|
217
|
+
<dt>spend_all:</dt>
|
218
|
+
<dd>
|
219
|
+
Optional. Boolean. Wether to spend all available utxos or just select enough to
|
220
|
+
cover the given outputs.
|
221
|
+
</dd>
|
222
|
+
</dl>
|
223
|
+
|
224
|
+
#### Returns
|
225
|
+
|
226
|
+
A hash with
|
227
|
+
|
228
|
+
<dl>
|
229
|
+
<dt>input_total:</dt>
|
230
|
+
<dd>The sum of all input amounts, in satoshis.</dd>
|
231
|
+
<dt>output_total:</dt>
|
232
|
+
<dd>The sum of all outputs, in satoshis.</dd>
|
233
|
+
<dt>fee:</dt>
|
234
|
+
<dd>fee to pay (input_total - output_total).</dd>
|
235
|
+
<dt>outputs:</dt>
|
236
|
+
<dd>Array of [destination address, amount in satoshis]</dd>
|
237
|
+
<dt>inputs:</dt>
|
238
|
+
<dd>Array of [input address, utxo transaction hash, utxo position, amount]</dd>
|
239
|
+
<dt>utxos_to_blacklist:</dt>
|
240
|
+
<dd>
|
241
|
+
Transaction inputs formatted to be used as utxo_blacklist on another
|
242
|
+
TransactionBuilder.
|
243
|
+
</dd>
|
244
|
+
<dt>transactions:</dt>
|
245
|
+
<dd>Transaction data for each input.</dd>
|
246
|
+
<dt>trezor_inputs:</dt>
|
247
|
+
<dd>
|
248
|
+
List of inputs as hashes with bip32 paths instead of addresses
|
249
|
+
{ address_n: [42,1,1],
|
250
|
+
prev_hash: "956b30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968e40",
|
251
|
+
prev_index: 1}
|
252
|
+
</dd>
|
253
|
+
<dt>trezor_outputs:</dt>
|
254
|
+
<dd>
|
255
|
+
List of outputs as Hashes with:
|
76
256
|
{ script_type: 'PAYTOADDRESS',
|
77
257
|
address: '1address-as-string',
|
78
|
-
amount:
|
258
|
+
amount: amount in satoshis }
|
259
|
+
</dd>
|
260
|
+
</dl>
|
79
261
|
|
80
|
-
|
262
|
+
```ruby
|
263
|
+
>>> require 'pochette'
|
264
|
+
>>> backend = Pochette::Backends::BlockchainInfo.new
|
265
|
+
>>> transaction = Pochette::TransactionBuilder.new({
|
266
|
+
addresses: [
|
267
|
+
["2NAHscN6XVqUPzBSJHC3fhkeF5SQVxiR9p9", [44, 1, 3, 11]]
|
268
|
+
],
|
269
|
+
outputs: [
|
270
|
+
["mvtUvWSWCU7knrcMVzjcKJgjL1LdekLK5q", 1_0000_0000],
|
271
|
+
],
|
272
|
+
utxo_blacklist: [
|
273
|
+
["0ded7f014fa3213e9b000bc81b8151bc6f2f926b9afea6e3643c8ad658353c72", 1]
|
274
|
+
],
|
275
|
+
change_address: 'mgaTEUM4ZE9kLiK58FcffwHfvxpte5CfvE',
|
276
|
+
fee_per_kb: 10000,
|
277
|
+
spend_all: false
|
278
|
+
})
|
279
|
+
>>> transaction.valid?
|
280
|
+
=> true
|
281
|
+
>>> transaction.as_hash
|
282
|
+
=> {
|
283
|
+
input_total: 2_0000_0000,
|
284
|
+
output_total: 1_9999_0000,
|
285
|
+
fee: 10000,
|
286
|
+
outputs: [
|
287
|
+
["mvtUvWSWCU7knrcMVzjcKJgjL1LdekLK5q", 1_0000_0000],
|
288
|
+
["mgaTEUM4ZE9kLiK58FcffwHfvxpte5CfvE", 9999_0000],
|
289
|
+
],
|
290
|
+
trezor_outputs: [
|
291
|
+
{ script_type: 'PAYTOADDRESS',
|
292
|
+
address: "mvtUvWSWCU7knrcMVzjcKJgjL1LdekLK5q",
|
293
|
+
amount: 1_0000_0000 },
|
294
|
+
{ script_type: 'PAYTOADDRESS',
|
295
|
+
address: "mgaTEUM4ZE9kLiK58FcffwHfvxpte5CfvE",
|
296
|
+
amount: 9999_0000 },
|
297
|
+
],
|
298
|
+
inputs: [
|
299
|
+
[ "2NAHscN6XVqUPzBSJHC3fhkeF5SQVxiR9p9",
|
300
|
+
"956b30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968e40",
|
301
|
+
1,
|
302
|
+
200000000
|
303
|
+
],
|
304
|
+
],
|
305
|
+
trezor_inputs: [
|
306
|
+
{ address_n: [43,1,3,11],
|
307
|
+
prev_hash: "956b30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968e40",
|
308
|
+
prev_index: 1 },
|
309
|
+
],
|
310
|
+
transactions: [
|
311
|
+
{ hash: "956b30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968e40",
|
312
|
+
version: "1",
|
313
|
+
lock_time: "0",
|
314
|
+
inputs: [
|
315
|
+
{ prev_hash: "158d6bbe586b4e00347f992e8296532d69f902d0ead32d964b6c87d4f8f0d3ea",
|
316
|
+
prev_index: 0,
|
317
|
+
sequence: 4294967295,
|
318
|
+
script_sig: "SCRIPTSCRIPTSCRIPT"
|
319
|
+
}
|
320
|
+
],
|
321
|
+
bin_outputs: [
|
322
|
+
{ amount: 1234568, script_pubkey: "76a914988cb8253f4e28be6e8bfded1b4aa11c646e1a8588ac" },
|
323
|
+
{ amount: 200000000, script_pubkey: "76a914988cb8253f4e28be6e8bfded1b4aa11c646e1a8588ac"}
|
324
|
+
]
|
325
|
+
}
|
326
|
+
],
|
327
|
+
utxos_to_blacklist: [
|
328
|
+
["956b30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968e40", 1]
|
329
|
+
],
|
330
|
+
}
|
331
|
+
```
|
332
|
+
|
333
|
+
## Backend API
|
334
|
+
|
335
|
+
Pochette offers a common interface to full bitcoin nodes like
|
336
|
+
[Bitcoin Core](https://bitcoin.org/en/download)
|
337
|
+
[Blockchain.info](https://blockchain.info/api) or [Toshi](http://toshi.io)
|
338
|
+
and will let you run several instances of each one of them simultaneously
|
339
|
+
always choosing the most recent node to query.
|
340
|
+
|
341
|
+
## incoming_for(addresses, min_date)
|
342
|
+
|
343
|
+
The incoming_for method is useful when registering deposits received to
|
344
|
+
a number of bitcoin addresses.
|
345
|
+
|
346
|
+
#### Receives
|
347
|
+
- addresses: A list of public bitcoin addresses to check for incoming transactions.
|
348
|
+
- min_date: Do not check for deposits earlier than this date. This is only to prevent
|
349
|
+
fetching too many results if the backend was to return too many,
|
350
|
+
each backend may apply its own limits so higher value here is not guaranteed to
|
351
|
+
fetch more results.
|
352
|
+
|
353
|
+
#### Returns
|
354
|
+
A list with
|
355
|
+
|
356
|
+
- Amount received, in satoshis.
|
357
|
+
- Address which received the deposit.
|
358
|
+
- Transaction hash for the deposit.
|
359
|
+
- Confirmations for the transaction.
|
360
|
+
- Position of this deposit in the transaction outputs list.
|
361
|
+
- Senders, as a comma-separated list of addresses (no whitespaces)
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
>>> require 'pochette'
|
365
|
+
>>> backend = Pochette::Backends::BlockchainInfo.new
|
366
|
+
>>> addresses = [
|
367
|
+
'mjfa56Keq7PXRKgdPSDB6eWLp4aaAVcj6L',
|
368
|
+
'mwZE4QfzzriE7nsgHSWbgmtT7s6SDysYvP',
|
369
|
+
'mvrDG7Ts6Mq9ejhZxdsQLjbScycVaktqsg',
|
370
|
+
'mxYzRdJfPk8PcaKSsSzNkX85mMfNcr2CGr',
|
371
|
+
'mzbXim4u1Nq4J2kVggu471pZL3ahxNkmE9',
|
372
|
+
'mhLAgRz5f1YogfYBZCDFSRt3ceeKBPVEKg',
|
373
|
+
]
|
374
|
+
>>> Pochette.backend.incoming_for(addresses, 1.day.ago)
|
375
|
+
=> [
|
376
|
+
[ 500000, "mjfa56Keq7PXRKgdPSDB6eWLp4aaAVcj6L",
|
377
|
+
"fb401691795a73e0160252c00af18327a15006fcdf877ccca0c116809669032e", 1629, 0,
|
378
|
+
"my2hmDuD9XjmtQWFu9HyyNAsE5WGSDBKpQ"],
|
379
|
+
[100000, "mxYzRdJfPk8PcaKSsSzNkX85mMfNcr2CGr",
|
380
|
+
"250978b77fe1310d6c72239d9e9589d7ac3dc6edf1b2412806ace5104553da34", 1648, 1,
|
381
|
+
"mv9ES7SmQQQ8dpMravBKsLWukgxU2DXfFs"],
|
382
|
+
[500000, "mvrDG7Ts6Mq9ejhZxdsQLjbScycVaktqsg",
|
383
|
+
"d9afd460b0a5e065fdd87bf97cb1843a29ea588c59daabd1609794e8166bb75f", 1648, 0,
|
384
|
+
"my2hmDuD9XjmtQWFu9HyyNAsE5WGSDBKpQ"],
|
385
|
+
[ 100000, "mxYzRdJfPk8PcaKSsSzNkX85mMfNcr2CGr",
|
386
|
+
"5bd72a4aa7818f47ac8943e3e17519be00c46530760860e608d898d728b9d46e", 553, 1,
|
387
|
+
"mvUhgW1ZcUju181bvwEhmZu2x2sRdbV4y2"],
|
388
|
+
[ 500000, "mwZE4QfzzriE7nsgHSWbgmtT7s6SDysYvP",
|
389
|
+
"b252037526ecb616ab5901552abb903f00bf73400a1fc49b5b5bd699b84bce77", 1632, 0,
|
390
|
+
"my2hmDuD9XjmtQWFu9HyyNAsE5WGSDBKpQ"],
|
391
|
+
[ 500000, "mzbXim4u1Nq4J2kVggu471pZL3ahxNkmE9",
|
392
|
+
"ff768084764a05d1de72628432c0a4419538b2786089ec8ad009f6096bc69fe1", 1660, 0,
|
393
|
+
"my2hmDuD9XjmtQWFu9HyyNAsE5WGSDBKpQ"]
|
394
|
+
]
|
395
|
+
```
|
396
|
+
|
397
|
+
## balances_for(addresses, confirmations)
|
398
|
+
|
399
|
+
Gets confirmed and unconfirmed sent, received and total balances for the given
|
400
|
+
addresses. It's useful for payment processing as you can see what's the total
|
401
|
+
amount seen on the network for a given address, and the final confirmed amount as well.
|
402
|
+
|
403
|
+
#### Receives
|
404
|
+
- addresses: A list of public bitcoin addresses to check balances for.
|
405
|
+
- confirmations: How many confirmations to use for the 'confirmed' amounts.
|
406
|
+
|
407
|
+
#### Returns
|
408
|
+
A hash where keys are public addresses and values ara a list of
|
409
|
+
|
410
|
+
- Confirmed received
|
411
|
+
- Confirmed sent
|
412
|
+
- Confirmed balance
|
413
|
+
- Unconfirmed received
|
414
|
+
- Unconfirmed sent
|
415
|
+
- Unconfirmed balance
|
416
|
+
|
417
|
+
```ruby
|
418
|
+
>>> require 'pochette'
|
419
|
+
>>> backend = Pochette::Backends::BlockchainInfo.new
|
420
|
+
>>> addresses = [
|
421
|
+
'mjfa56Keq7PXRKgdPSDB6eWLp4aaAVcj6L',
|
422
|
+
'mwZE4QfzzriE7nsgHSWbgmtT7s6SDysYvP',
|
423
|
+
'mvrDG7Ts6Mq9ejhZxdsQLjbScycVaktqsg',
|
424
|
+
'mxYzRdJfPk8PcaKSsSzNkX85mMfNcr2CGr',
|
425
|
+
'mzbXim4u1Nq4J2kVggu471pZL3ahxNkmE9',
|
426
|
+
'mhLAgRz5f1YogfYBZCDFSRt3ceeKBPVEKg',
|
427
|
+
]
|
428
|
+
>>> backend.balances_for(addresses, 6)
|
429
|
+
>>> [
|
430
|
+
"mhLAgRz5f1YogfYBZCDFSRt3ceeKBPVEKg" =>
|
431
|
+
[0.00544426, 0.00544426, 0.0, 0.00544426, 0.00544426, 0.0],
|
432
|
+
"mjfa56Keq7PXRKgdPSDB6eWLp4aaAVcj6L" =>
|
433
|
+
[0.005, 0.0, 0.005, 0.006, 0.0, 0.006],
|
434
|
+
"mvrDG7Ts6Mq9ejhZxdsQLjbScycVaktqsg" =>
|
435
|
+
[0.005, 0.0, 0.005, 0.005, 0.0, 0.005],
|
436
|
+
"mwZE4QfzzriE7nsgHSWbgmtT7s6SDysYvP" =>
|
437
|
+
[0.005, 0.0, 0.005, 0.005, 0.0, 0.005],
|
438
|
+
"mxYzRdJfPk8PcaKSsSzNkX85mMfNcr2CGr" =>
|
439
|
+
[0.002, 0.0, 0.002, 0.002, 0.0, 0.002],
|
440
|
+
"mzbXim4u1Nq4J2kVggu471pZL3ahxNkmE9" =>
|
441
|
+
[0.005, 0.0, 0.005, 0.005, 0.0, 0.005],
|
442
|
+
```
|
81
443
|
|
82
|
-
##
|
444
|
+
## list_unspent(addresses)
|
445
|
+
|
446
|
+
Gets unspent transaction outputs (a.k.a. utxos) for all the given addresses.
|
447
|
+
You may not need to use this directly, but rather through a
|
448
|
+
Pochette::TransactionBuilder which is smart about selecting utxos.
|
449
|
+
|
450
|
+
#### Receives
|
451
|
+
- addresses: A list of public bitcoin addresses to check balances for.
|
452
|
+
|
453
|
+
#### Returns
|
454
|
+
A list of list, each of them is
|
455
|
+
|
456
|
+
- Address
|
457
|
+
- Transaction Hash
|
458
|
+
- Output position in transaction
|
459
|
+
- Unspent amount
|
460
|
+
|
461
|
+
```ruby
|
462
|
+
>>> require 'pochette'
|
463
|
+
>>> backend = Pochette::Backends::BlockchainInfo.new
|
464
|
+
>>> addresses = [
|
465
|
+
'mjfa56Keq7PXRKgdPSDB6eWLp4aaAVcj6L',
|
466
|
+
'mwZE4QfzzriE7nsgHSWbgmtT7s6SDysYvP',
|
467
|
+
'mvrDG7Ts6Mq9ejhZxdsQLjbScycVaktqsg',
|
468
|
+
'mxYzRdJfPk8PcaKSsSzNkX85mMfNcr2CGr',
|
469
|
+
'mzbXim4u1Nq4J2kVggu471pZL3ahxNkmE9',
|
470
|
+
'mhLAgRz5f1YogfYBZCDFSRt3ceeKBPVEKg',
|
471
|
+
]
|
472
|
+
>>> backend.list_unspent(addresses).should == [
|
473
|
+
["mjfa56Keq7PXRKgdPSDB6eWLp4aaAVcj6L",
|
474
|
+
"fb401691795a73e0160252c00af18327a15006fcdf877ccca0c116809669032e", 0, 500000],
|
475
|
+
["mxYzRdJfPk8PcaKSsSzNkX85mMfNcr2CGr",
|
476
|
+
"250978b77fe1310d6c72239d9e9589d7ac3dc6edf1b2412806ace5104553da34", 1, 100000],
|
477
|
+
["mvrDG7Ts6Mq9ejhZxdsQLjbScycVaktqsg",
|
478
|
+
"d9afd460b0a5e065fdd87bf97cb1843a29ea588c59daabd1609794e8166bb75f", 0, 500000],
|
479
|
+
["mxYzRdJfPk8PcaKSsSzNkX85mMfNcr2CGr",
|
480
|
+
"5bd72a4aa7818f47ac8943e3e17519be00c46530760860e608d898d728b9d46e", 1, 100000],
|
481
|
+
["mwZE4QfzzriE7nsgHSWbgmtT7s6SDysYvP",
|
482
|
+
"b252037526ecb616ab5901552abb903f00bf73400a1fc49b5b5bd699b84bce77", 0, 500000],
|
483
|
+
["mjfa56Keq7PXRKgdPSDB6eWLp4aaAVcj6L",
|
484
|
+
"9142d7a8e96124a36db9708dd21afa4ac81f15a77bd85c06f16e808a4d700da2", 1, 100000],
|
485
|
+
["mzbXim4u1Nq4J2kVggu471pZL3ahxNkmE9",
|
486
|
+
"ff768084764a05d1de72628432c0a4419538b2786089ec8ad009f6096bc69fe1", 0, 500000]
|
487
|
+
]
|
488
|
+
```
|
489
|
+
|
490
|
+
## list_transactions(txids)
|
491
|
+
|
492
|
+
List full transaction data for the given transaction hashes. The output
|
493
|
+
is formatted for Trezor as required by their multi-step signature process.
|
494
|
+
You may not want to use this directly and use Pochette::TrezorTransactionBuilder
|
495
|
+
instead.
|
496
|
+
|
497
|
+
#### Receives
|
498
|
+
- Transactions: A list of Transaction ids
|
499
|
+
|
500
|
+
#### Returns
|
501
|
+
A list of ruby hashes will transaction data for each id passed.
|
502
|
+
|
503
|
+
```ruby
|
504
|
+
>>> require 'pochette'
|
505
|
+
>>> backend = Pochette::Backends::BlockchainInfo.new
|
506
|
+
>>> transactions = [
|
507
|
+
"fb401691795a73e0160252c00af18327a15006fcdf877ccca0c116809669032e",
|
508
|
+
"250978b77fe1310d6c72239d9e9589d7ac3dc6edf1b2412806ace5104553da34",
|
509
|
+
]
|
510
|
+
>>> backend.list_transactions(transactions)
|
511
|
+
=> [{ hash: "fb401691795a73e0160252c00af18327a15006fcdf877ccca0c116809669032e",
|
512
|
+
version: 1,
|
513
|
+
lock_time: 0,
|
514
|
+
inputs: [
|
515
|
+
{ prev_hash: "f948600a52719ec63947bd47105411c2cb032bda91b08b25bb7b1de22f1f7458",
|
516
|
+
prev_index: 1,
|
517
|
+
sequence: 4294967295,
|
518
|
+
script_sig: "483045022100898b773c1cf095d5c1608a892673ad067387663e00327e7b88dd48cc4eb9cf2a02203a53336afd4440ea52c6a55e07ac60035dc386cb01b5b6810efac018f0346fad01210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415e"
|
519
|
+
}
|
520
|
+
],
|
521
|
+
bin_outputs: [
|
522
|
+
{ amount: 500000, script_pubkey: "76a9142d81b210deb7e22475cd7f2fda0bf582dddc9da788ac" },
|
523
|
+
{ amount: 276607256, script_pubkey: "76a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac"}
|
524
|
+
]
|
525
|
+
},
|
526
|
+
{ hash: "250978b77fe1310d6c72239d9e9589d7ac3dc6edf1b2412806ace5104553da34",
|
527
|
+
version: 1,
|
528
|
+
lock_time: 0,
|
529
|
+
inputs: [
|
530
|
+
{ prev_hash: "8505c663d0414b678827eed85ba9e7652e616c19ff1be871f6b083d5ed400a20",
|
531
|
+
prev_index: 0,
|
532
|
+
sequence: 4294967295,
|
533
|
+
script_sig: "47304402203ac7cb9afe1a14189b63807aff301ef6bb8507f6dc0471bcee5362282a8c3e38022012c3bfe9680f013735f0d6d012d6420383c0e3a1dbf010f97be9cc834a93d1f601210221af8672ff613d2ea198d8dadcc387a36ef47d7cba6f541221db61b96aa20149"
|
534
|
+
}
|
535
|
+
],
|
536
|
+
bin_outputs: [
|
537
|
+
{ amount: 999293000, script_pubkey: "76a91426beab63a5fb7b2103929e91b65f339a2c5b285088ac"},
|
538
|
+
{ amount: 100000, script_pubkey: "76a914badcbfae4d83a52dc2c8f68605663adc9d4922a688ac"}
|
539
|
+
]
|
540
|
+
}
|
541
|
+
]
|
542
|
+
```
|
543
|
+
|
544
|
+
## block_height
|
545
|
+
|
546
|
+
Get the latest block height for this backend. Always in the main branch.
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
>>> require 'pochette'
|
550
|
+
>>> backend = Pochette::Backends::BlockchainInfo.new
|
551
|
+
>>> backend.get_height
|
552
|
+
=> 376152
|
553
|
+
```
|
554
|
+
|
555
|
+
## pushtx
|
556
|
+
|
557
|
+
Propagates a raw transaction to the network.
|
558
|
+
|
559
|
+
#### Receives
|
560
|
+
- transaction: A raw transaction in hex format
|
561
|
+
|
562
|
+
#### Returns
|
563
|
+
The transaction id (hash)
|
564
|
+
|
565
|
+
```ruby
|
566
|
+
>>> require 'pochette'
|
567
|
+
>>> hex = "0100000001d11a6cc978fc41aaf5b24fc5c8ddde71fb91ffdba9579cd62ba20fc284b2446c000000008a47304402206d2f98829a9e5017ade2c084a8b821625c35aeaa633f718b1c348906afbe68b00220094cb8ee519adcebe866e655532abab818aa921144bd98a12491481931d2383a014104e318459c24b89c0928cec3c9c7842ae749dcca78673149179af9155c80f3763642989df3ffe34ab205d02e2efd07e9a34db2f00ed8b821dd5bb087ff64df2c9effffffff0280f0fa02000000001976a9149b754a70b9a3dbb64f65db01d164ef51101c18d788ac40aeeb02000000001976a914aadf5d54eda13070d39af72eb5ce40b1d3b8282588ac00000000"
|
568
|
+
>>> backend = Pochette::Backends::BlockchainInfo.new
|
569
|
+
>>> backend.pushtx(hex)
|
570
|
+
=> 'fb92420f73af6d25f5fab93435bc6b8ebfff3a07c02abd053f0923ae296fe380'
|
571
|
+
```
|
572
|
+
|
573
|
+
## BitcoinCore backend
|
83
574
|
|
84
575
|
Pochette will connect to your bitcoin-core node via JSON-RPC, using the
|
85
576
|
[bitcoin-rpc gem](https://github.com/bitex-la/bitcoin-rpc)
|
@@ -91,13 +582,41 @@ To properly use Pochette you need to be running your bitcoin node with setting t
|
|
91
582
|
Also, if you're creating new addresses and want bitcoin-core to track them you'll want to import
|
92
583
|
them using the bitcoin-rpc gem, like so:
|
93
584
|
|
94
|
-
|
585
|
+
```ruby
|
586
|
+
>>> BitcoinRpc::Client.new('http://user:pass@your_server').importaddress('1PUBLICADDRESS', '', false)
|
587
|
+
```
|
95
588
|
|
96
589
|
Setting up bitcoin-core as a backend can be done like this:
|
97
590
|
|
98
|
-
|
591
|
+
```ruby
|
592
|
+
>>> Pochette.backend = Pochette::Backends::BitcoinCore.new('http://user:pass@your_server')
|
593
|
+
```
|
594
|
+
|
595
|
+
## BlockchainInfo backend
|
596
|
+
|
597
|
+
Pochette can use blockchain.info's public API to fetch unspent outputs,
|
598
|
+
sleeping a bit after each request to prevent blockchain.info from banning your IP.
|
599
|
+
This backend is probably the slowest one but also the one that's more convenient for
|
600
|
+
testing and managing small wallets.
|
601
|
+
|
602
|
+
This backend is not usable for testnet transactions, all queries will be done to the
|
603
|
+
main network.
|
99
604
|
|
100
|
-
|
605
|
+
You can create a blockchain.info backend like this
|
606
|
+
|
607
|
+
```ruby
|
608
|
+
>>> Pochette::Backends::BlockchainInfo.new
|
609
|
+
```
|
610
|
+
|
611
|
+
The default cooldown time is 1 second after each request, but if you have a Blockchain.info
|
612
|
+
API key you can configure your backend like so:
|
613
|
+
|
614
|
+
```ruby
|
615
|
+
>>> Pochette::Backends::BlockchainInfo.cooldown = 0.1 # Make the cooldown a tenth of a second
|
616
|
+
>>> Pochette.backend = Pochette::Backends::BlockchainInfo.new("your_api_key")
|
617
|
+
```
|
618
|
+
|
619
|
+
## Toshi backend
|
101
620
|
|
102
621
|
Pochette will connect to your Toshi node's postgres database directly.
|
103
622
|
It's provided as a separate gem as it depends on the pg gem which needs local
|
@@ -105,7 +624,7 @@ postgres extensions to work. You may need to add some extra indexes to your post
|
|
105
624
|
to speed things up when using Pochette.
|
106
625
|
[See the gem readme](https://github.com/bitex-la/pochette-toshi) for more info.
|
107
626
|
|
108
|
-
##
|
627
|
+
## Trendy Backend
|
109
628
|
|
110
629
|
Pochette provides a higher level Backend Pochette::Backends::Trendy which chooses
|
111
630
|
between a pool of available backends always using the one at the highest block height,
|
@@ -114,9 +633,11 @@ between a pool of available backends always using the one at the highest block h
|
|
114
633
|
This is useful for automatic fallbacks and redundancy, you could also mix Toshi and Bitcoin-Core
|
115
634
|
backends and use whatever looks more up to date.
|
116
635
|
|
117
|
-
|
118
|
-
|
119
|
-
|
636
|
+
```ruby
|
637
|
+
>>> alpha = Pochette::Backends::BitcoinCore.new('http://user:pass@alpha_host')
|
638
|
+
>>> beta = Pochette::Backends::BitcoinCore.new('http://user:pass@beta_host')
|
639
|
+
>>> Pochette.backend = Pochette::Backends::Trendy.new([alpha, beta])
|
640
|
+
```
|
120
641
|
|
121
642
|
## Development
|
122
643
|
|
data/lib/pochette.rb
CHANGED
@@ -6,12 +6,24 @@ require "bitcoin"
|
|
6
6
|
|
7
7
|
module Pochette
|
8
8
|
mattr_accessor :backend
|
9
|
+
|
10
|
+
def self.testnet=(v)
|
11
|
+
@testnet = v
|
12
|
+
Bitcoin.network = v ? :testnet : :bitcoin
|
13
|
+
end
|
14
|
+
def self.testnet
|
15
|
+
@testnet
|
16
|
+
end
|
17
|
+
def self.testnet?
|
18
|
+
self.testnet
|
19
|
+
end
|
9
20
|
|
10
21
|
module Backends
|
11
22
|
end
|
12
23
|
end
|
13
24
|
|
14
25
|
require "pochette/backends/bitcoin_core"
|
26
|
+
require "pochette/backends/blockchain_info"
|
15
27
|
require "pochette/backends/trendy"
|
16
28
|
require "pochette/transaction_builder"
|
17
29
|
require "pochette/trezor_transaction_builder"
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# A bitcoin backend that uses blockchain.info to retrieve information.
|
2
|
+
# See Pochette::Backends::Trendy to learn more about the backend
|
3
|
+
# interface and contract.
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
class Pochette::Backends::BlockchainInfo
|
7
|
+
cattr_accessor(:cooldown){1}
|
8
|
+
attr_accessor :api_key
|
9
|
+
|
10
|
+
def initialize(key = nil)
|
11
|
+
self.api_key = key
|
12
|
+
end
|
13
|
+
|
14
|
+
def list_unspent(addresses)
|
15
|
+
json = get_json("unspent", {active: addresses.join('|'), format: 'json'})
|
16
|
+
json['unspent_outputs'].collect do |utxo|
|
17
|
+
address = Bitcoin::Script.new(utxo['script'].htb).get_address
|
18
|
+
[address, utxo['tx_hash_big_endian'], utxo['tx_output_n'].to_i, utxo['value']]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def balances_for(addresses, confirmations)
|
23
|
+
json = get_json("multiaddr", {active: addresses.join('|'), format: 'json'})
|
24
|
+
result = {}
|
25
|
+
json['addresses'].collect do |a|
|
26
|
+
address = a['address']
|
27
|
+
sent = get_json("q/getsentbyaddress/#{address}",
|
28
|
+
{confirmations: confirmations, format: 'json'})
|
29
|
+
received = get_json("q/getreceivedbyaddress/#{address}",
|
30
|
+
{confirmations: confirmations, format: 'json'})
|
31
|
+
result[address] = [
|
32
|
+
sat(received), sat(sent), sat(received - sent),
|
33
|
+
sat(a['total_received']), sat(a['total_sent']), sat(a['final_balance'])]
|
34
|
+
end
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
def sat(x)
|
39
|
+
x.to_d / 1_0000_0000
|
40
|
+
end
|
41
|
+
|
42
|
+
def incoming_for(addresses, min_date)
|
43
|
+
return unless latest_block = block_height
|
44
|
+
addresses.in_groups_of(50, false).collect do |group|
|
45
|
+
incoming_for_helper(group, latest_block)
|
46
|
+
end.flatten(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
def incoming_for_helper(addresses, latest_block)
|
50
|
+
json = get_json("multiaddr", {active: addresses.join('|'), format: 'json'})
|
51
|
+
|
52
|
+
json['txs'].collect do |transaction|
|
53
|
+
transaction['out'].collect do |out|
|
54
|
+
next unless addresses.include? out['addr']
|
55
|
+
confirmations = latest_block - transaction['block_height'].to_i
|
56
|
+
senders = transaction['inputs'].collect{ |i| i['prev_out']['addr'] }.join(',')
|
57
|
+
[ out['value'].to_d, out['addr'], transaction['hash'], confirmations, out['n'], senders ]
|
58
|
+
end.compact
|
59
|
+
end.flatten(1)
|
60
|
+
end
|
61
|
+
|
62
|
+
def list_transactions(txids)
|
63
|
+
return nil if txids.empty?
|
64
|
+
txids.collect do |txid|
|
65
|
+
tx = get_json("rawtx/#{txid}", {format: 'json'})
|
66
|
+
inputs = tx['inputs'].collect do |i|
|
67
|
+
prevhash = get_json("rawtx/#{i['prev_out']['tx_index']}", {format: 'json'})['hash']
|
68
|
+
{ prev_hash: prevhash,
|
69
|
+
prev_index: i['prev_out']['n'],
|
70
|
+
sequence: i['sequence'],
|
71
|
+
script_sig: i['script']
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
outputs = tx['out'].collect do |o|
|
76
|
+
{ amount: o['value'].to_i, script_pubkey: o['script'] }
|
77
|
+
end
|
78
|
+
|
79
|
+
{ hash: tx['hash'], version: tx['ver'], lock_time: tx['lock_time'],
|
80
|
+
inputs: inputs, bin_outputs: outputs}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def block_height
|
85
|
+
get_json("latestblock", {format: 'json'})['height'].to_i
|
86
|
+
end
|
87
|
+
|
88
|
+
def pushtx(hex)
|
89
|
+
uri = URI.parse("https://blockchain.info/pushtx")
|
90
|
+
params = { "tx" => hex }
|
91
|
+
params['api_code'] = api_key if api_key
|
92
|
+
Net::HTTP.post_form(uri, params)
|
93
|
+
Bitcoin::Protocol::Tx.new(hex.htb).hash
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_json(path, params={})
|
97
|
+
params['api_code'] = api_key if api_key
|
98
|
+
query = params.empty? ? '' : "?#{params.to_query}"
|
99
|
+
retries = 30
|
100
|
+
begin
|
101
|
+
raw_response = open("https://blockchain.info/#{path}#{query}").read
|
102
|
+
sleep cooldown
|
103
|
+
Oj.load(raw_response)
|
104
|
+
rescue OpenURI::HTTPError => e
|
105
|
+
raise if retries < 0 || e.message.to_i != 429
|
106
|
+
retries -= 1
|
107
|
+
sleep (cooldown * 5)
|
108
|
+
retry
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -42,6 +42,13 @@ class Pochette::Backends::Trendy
|
|
42
42
|
# [[address, txid, position (vout), amount (in satoshis)], ...]
|
43
43
|
def list_unspent(addresses)
|
44
44
|
backend.list_unspent(addresses)
|
45
|
+
rescue OpenURI::HTTPError => e
|
46
|
+
# Blockchain.info returns 500 when there are no unspent outputs
|
47
|
+
if e.io.read == "No free outputs to spend"
|
48
|
+
return []
|
49
|
+
else
|
50
|
+
raise
|
51
|
+
end
|
45
52
|
end
|
46
53
|
|
47
54
|
# Gets information for the given transactions
|
@@ -3,25 +3,13 @@
|
|
3
3
|
# Instantiating will perform all the given queries, you'll be left with a
|
4
4
|
# TransactionBuilder object that is either valid? or not, and if valid
|
5
5
|
# you can query the results via to_hash.
|
6
|
-
# Options:
|
7
|
-
# addresses:
|
8
|
-
# List of addresses in wallet.
|
9
|
-
# We will be spending their unspent outputs.
|
10
|
-
# outputs:
|
11
|
-
# List of pairs [recipient_address, amount]
|
12
|
-
# This will not be all the final outputs in the transaction,
|
13
|
-
# as a 'change' output may be added if needed.
|
14
|
-
# utxo_blacklist:
|
15
|
-
# List of utxos to ignore, a list of pairs [transaction hash, position]
|
16
|
-
# change_address:
|
17
|
-
# Change address to use. Will default to the first source address.
|
18
|
-
# fee_per_kb:
|
19
|
-
# Defaults to 10000 satoshis.
|
20
|
-
# spend_all:
|
21
|
-
# Wether to spend all available utxos or just select enough to
|
22
|
-
# cover the given outputs.
|
23
6
|
|
24
7
|
class Pochette::TransactionBuilder
|
8
|
+
# Backend can be set globally, or independently for each class and instance.
|
9
|
+
class_attribute :backend
|
10
|
+
def self.backend
|
11
|
+
@backend || Pochette.backend
|
12
|
+
end
|
25
13
|
|
26
14
|
cattr_accessor(:dust_size){ 546 }
|
27
15
|
cattr_accessor(:output_size){ 149 }
|
@@ -30,6 +18,7 @@ class Pochette::TransactionBuilder
|
|
30
18
|
cattr_accessor(:default_fee_per_kb){ 10000 }
|
31
19
|
|
32
20
|
def initialize(options)
|
21
|
+
self.backend = options[:backend] if options[:backend]
|
33
22
|
initialize_options(options)
|
34
23
|
return unless valid?
|
35
24
|
initialize_fee
|
@@ -42,10 +31,13 @@ class Pochette::TransactionBuilder
|
|
42
31
|
|
43
32
|
def as_hash
|
44
33
|
return nil unless valid?
|
45
|
-
{
|
34
|
+
{ input_total: inputs_amount,
|
35
|
+
output_total: outputs_amount,
|
46
36
|
fee: inputs_amount - outputs_amount,
|
47
37
|
inputs: inputs,
|
48
|
-
outputs: outputs
|
38
|
+
outputs: outputs,
|
39
|
+
utxos_to_blacklist: inputs.collect{|i| [i[1], i[2]] },
|
40
|
+
}
|
49
41
|
end
|
50
42
|
|
51
43
|
def valid?
|
@@ -44,11 +44,10 @@ class Pochette::TrezorTransactionBuilder < Pochette::TransactionBuilder
|
|
44
44
|
|
45
45
|
def as_hash
|
46
46
|
return nil unless valid?
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
transactions: transactions}
|
47
|
+
super.merge(
|
48
|
+
trezor_inputs: trezor_inputs,
|
49
|
+
trezor_outputs: trezor_outputs,
|
50
|
+
transactions: transactions)
|
52
51
|
end
|
53
52
|
|
54
53
|
protected
|
data/lib/pochette/version.rb
CHANGED
data/pochette.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pochette
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nubis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-09-
|
12
|
+
date: 2015-09-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -123,6 +123,20 @@ dependencies:
|
|
123
123
|
- - "~>"
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: 0.8.0
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: byebug
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
126
140
|
description: "Pochette is a Bitcoin Wallet backend offering a common\n interface
|
127
141
|
to several bitcoin nodes so you can build single purpose wallets.\n You can pass
|
128
142
|
in a bunch of addresses and outputs and it will select the\n appropriate unspent
|
@@ -146,6 +160,7 @@ files:
|
|
146
160
|
- bin/setup
|
147
161
|
- lib/pochette.rb
|
148
162
|
- lib/pochette/backends/bitcoin_core.rb
|
163
|
+
- lib/pochette/backends/blockchain_info.rb
|
149
164
|
- lib/pochette/backends/trendy.rb
|
150
165
|
- lib/pochette/transaction_builder.rb
|
151
166
|
- lib/pochette/trezor_transaction_builder.rb
|