pochette 0.1.1 → 0.1.3
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/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
|