pochette 0.2.0 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ddd8603e5bf4c3adc3f2372f140da6656ee4ca3
4
- data.tar.gz: 43c1f00598313990af9a6f0b8467f7761b15aa3a
3
+ metadata.gz: 232aedff3f83a84ce22d5c17d27cfb9663b8d2a5
4
+ data.tar.gz: f53d4c8ddef9552dc4be39640dbb6d2ae0c429f7
5
5
  SHA512:
6
- metadata.gz: 4ca387a7203f8f227276a828776c21fdffc972b253d736c4eb3e1eee099946575fecb9d40a972f20c9b60058d9239cc0e38e4ee7ea7357c8bf03c2f71924bf08
7
- data.tar.gz: f355e1d37c0c90d22ebd8a981cea1de955765a8dcb208ce4db5de156c2c4480524a28f0115433f02bf2f4125ebec18a30fd326bd44cac8d5e0d7860e6ad767f9
6
+ metadata.gz: bc2996d2eece872696c85d2bb2ef1a72f7b703ead2c945ae114e6c201eeb8b40a5f16c51ed8d960e793227d673afee28ca1ccfa9a04ea7c1b718fc6dc0600ffb
7
+ data.tar.gz: 88c8709b576c2e68d7a1b2689fc193c7c94679100df7d0913d342173853952eb4b3c99187027277381b254590ddb0c83df2e71dcccec555c2bb7b5e24433d6e6
data/README.md CHANGED
@@ -20,7 +20,8 @@ and broadcasted.
20
20
 
21
21
  The Pochette::TrezorTransactionBuilder class extends Pochette::TransactionBuilder
22
22
  including transactions, inputs and outputs that are formatted in a way they can
23
- be passed directly to a Trezor device for signing.
23
+ be passed directly to a Trezor device for signing. You can even build transactions
24
+ for multisig addresses to be signed by several trezors.
24
25
 
25
26
  ## Table of contents
26
27
  - [Installation and Setup](#installation-and-setup)
@@ -187,15 +188,34 @@ If you're using [Trezor Connect](https://github.com/trezor/connect) for signing
187
188
  then you won't need to pass in the transactions.
188
189
 
189
190
  #### Receives
190
- The TransactionBuilder's initializer receives a single options hash with:
191
+ The TrezorTransactionBuilder's initializer receives a single options hash with:
191
192
 
192
193
  <dl>
193
- <dt>addresses:</dt>
194
+ <dt>bip32_addresses:</dt>
194
195
  <dd>
195
196
  List of addresses in wallet. We will be spending their unspent outputs.
196
197
  Each address is represented as a pair, with the public address string
197
198
  and the BIP32 path as a list of integers, for example:
199
+
200
+ <pre>
198
201
  ['public-address-as-string', [44, 1, 3, 11]]
202
+ </pre>
203
+
204
+ If you're spending from a multisig address then you should provide
205
+ all the root xpubs ( session.getPublicKey([]) ) from your different trezor devices, the path as a list of
206
+ integers and the M number (as in M out of N).
207
+ The actual bitcoin address will be derived from these.
208
+ The following example is for a multi-sig address where 2 out of 3 trezors can sign.
209
+ The address is generated from the public key at path [42, 1, 1] in each trezor.
210
+
211
+ <pre>
212
+ [['xpub661MyMwAqRbcGCmcnz4JtnieVyuvgQFGqZqw3KS1g9khndpF3segkAYbYCKKaQ9Di2ZuWLaZU4Axt7TrKq41aVYx8XTbDbQFzhhDMntKLU5',
213
+ 'xpub661MyMwAqRbcFwc3Nmz8WmMU9okGmeVSmuprwNHCVsfhy6vMyg6g79octqwNftK4g62TMWmb7UtVpnAWnANzqwtKrCDFe2UaDCv1HoErssE'
214
+ 'xpub661MyMwAqRbcGkqPSKVkwTMtFZzEpbWXjM4t1Dv1XQbfMxtyLRGupWkp3fcSCDtp6nd1AUrRtq8tnFGTYgkY1pB9muwzaBDnJSMo2rVENhz'],
215
+ [42,1,1],
216
+ 2]
217
+ <pre>
218
+
199
219
  </dd>
200
220
  <dt>outputs:</dt>
201
221
  <dd>
@@ -250,6 +270,53 @@ A hash with
250
270
  { address_n: [42,1,1],
251
271
  prev_hash: "956b30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968e40",
252
272
  prev_index: 1}
273
+
274
+ When spending from a multisig address, the inputs will also include the multisig structure,
275
+ with nodes and everything your trezor needs to sign.
276
+ Notice there's a 'signatures' key inside 'multisig', you should sign the transaction as is,
277
+ then populate the corresponding value in 'signatures' and keep signing until you have
278
+ all the required signatures.
279
+
280
+ { address_n: [42,1,1],
281
+ prev_hash: "eeeb30c3c4335f019dbee60c60d76994319473acac356f774c7858cd5c968eee",
282
+ prev_index: 0,
283
+ script_type: 'SPENDMULTISIG',
284
+ multisig: {
285
+ signatures: ['','',''],
286
+ m: 2,
287
+ pubkeys: [
288
+ { address_n: [42,1,1],
289
+ node: {
290
+ chain_code: 'a6d47170817f78094180f1a7a3a9df7634df75fa9604d71b87e92a5a6bf9d30a',
291
+ depth: 0,
292
+ child_num: 0,
293
+ fingerprint: 0,
294
+ path: [],
295
+ public_key: '03142b0a6fa6943e7276ddc42582c6b169243d289ff17e7c8101797047eed90c9b',
296
+ }
297
+ },
298
+ { address_n: [42,1,1],
299
+ node: {
300
+ chain_code: '8c9151740446b9e0063ca934df66c5e14121a0b4d8a360748f1b19bfef675460',
301
+ depth: 0,
302
+ child_num: 0,
303
+ fingerprint: 0,
304
+ path: [],
305
+ public_key: '027565ceb190647ec5c566805ebc5cb6166ae2ee1d4995495f61b9eff371ec0e61',
306
+ }
307
+ },
308
+ { address_n: [42,1,1],
309
+ node: {
310
+ chain_code: 'de5bc5918414df3777ff52ae733bdbc87431485cfd39aea65da6133e183ef68a',
311
+ depth: 0,
312
+ child_num: 0,
313
+ fingerprint: 0,
314
+ path: [],
315
+ public_key: '028776ff18f0f3808d6d42749a6e2baee5c75c3f7ae07445403a3a5690d580a0af',
316
+ }
317
+ }
318
+ ]
319
+ }
253
320
  </dd>
254
321
  <dt>trezor_outputs:</dt>
255
322
  <dd>
@@ -3,6 +3,7 @@ require "bitcoin_rpc"
3
3
  require "active_support"
4
4
  require "active_support/core_ext"
5
5
  require "bitcoin"
6
+ require "money-tree"
6
7
  require "contracts"
7
8
  C = Contracts
8
9
 
@@ -13,7 +14,7 @@ module Pochette
13
14
 
14
15
  Contract C::Bool => C::Bool
15
16
  def self.testnet=(v)
16
- Bitcoin.network = v ? :testnet : :bitcoin
17
+ Bitcoin.network = v ? :testnet3 : :bitcoin
17
18
  @testnet = v
18
19
  end
19
20
 
@@ -3,7 +3,6 @@
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
-
7
6
  class Pochette::TransactionBuilder
8
7
  include Contracts::Core
9
8
 
@@ -1,10 +1,12 @@
1
1
  # Same as TransactionBuilder but outputs a transaction hash with all the
2
2
  # required data to create and sign a transaction using a BitcoinTrezor.
3
-
4
3
  class Pochette::TrezorTransactionBuilder < Pochette::TransactionBuilder
5
4
 
6
5
  Contract ({
7
- :bip32_addresses => C::ArrayOf[[String, C::ArrayOf[Integer]]],
6
+ :bip32_addresses => C::ArrayOf[C::Or[
7
+ [C::Or[String, C::ArrayOf[String]], C::ArrayOf[Integer]],
8
+ [C::Or[String, C::ArrayOf[String]], C::ArrayOf[Integer], C::Maybe[Integer]]
9
+ ]],
8
10
  :outputs => C::Maybe[C::ArrayOf[[String, C::Num]]],
9
11
  :utxo_blacklist => C::Maybe[C::ArrayOf[[String, Integer]]],
10
12
  :change_address => C::Maybe[String],
@@ -32,7 +34,9 @@ class Pochette::TrezorTransactionBuilder < Pochette::TransactionBuilder
32
34
  :trezor_inputs => C::ArrayOf[{
33
35
  address_n: C::ArrayOf[Integer],
34
36
  prev_hash: String,
35
- prev_index: Integer
37
+ prev_index: Integer,
38
+ script_type: C::Maybe[C::Any],
39
+ multisig: C::Maybe[C::Any],
36
40
  }],
37
41
  :trezor_outputs => C::ArrayOf[{
38
42
  script_type: String,
@@ -59,17 +63,55 @@ protected
59
63
  self.errors = [:no_bip32_addresses_given]
60
64
  return
61
65
  end
62
- options[:addresses] = options[:bip32_addresses].collect(&:first)
66
+ options[:addresses] = options[:bip32_addresses].collect{|a| address_from_bip32(a) }
63
67
  self.bip32_address_lookup = options[:bip32_addresses].reduce({}) do |accum, addr|
64
- accum[addr.first] = addr.last
68
+ accum[address_from_bip32(addr)] = addr
65
69
  accum
66
70
  end
67
71
  end
68
72
 
73
+ # Bip32 addresses may look like an address with a bip32 path, or
74
+ # an array of xpubs, bip32 path and M (as in M of N) for multisig p2sh addresses.
75
+ def address_from_bip32(array)
76
+ if array.first.is_a?(String)
77
+ array.first
78
+ else
79
+ public_keys = array.first.collect do |x|
80
+ MoneyTree::Node.from_bip32(x).node_for_path(array[1].join('/')).public_key.key
81
+ end
82
+ address, _ = Bitcoin.pubkeys_to_p2sh_multisig_address(array.last, *public_keys)
83
+ address
84
+ end
85
+ end
86
+
69
87
  def build_trezor_inputs
70
88
  self.trezor_inputs = inputs.collect do |input|
71
- { address_n: bip32_address_lookup[input[0]],
89
+ address = bip32_address_lookup[input[0]]
90
+ hash = { address_n: address[1],
72
91
  prev_hash: input[1], prev_index: input[2] }
92
+ if address.size == 3
93
+ xpubs = address.first
94
+ m = address.last
95
+ hash[:script_type] = 'SPENDMULTISIG'
96
+ hash[:multisig] = {
97
+ signatures: [''] * xpubs.size,
98
+ m: m,
99
+ pubkeys: xpubs.collect do |xpub|
100
+ node = MoneyTree::Node.from_bip32(xpub)
101
+ { address_n: address[1],
102
+ node: {
103
+ chain_code: node.chain_code.to_s(16),
104
+ depth: 0,
105
+ child_num: 0,
106
+ fingerprint: 0,
107
+ path: [],
108
+ public_key: node.public_key.key,
109
+ }
110
+ }
111
+ end
112
+ }
113
+ end
114
+ hash
73
115
  end
74
116
  end
75
117
 
@@ -1,3 +1,3 @@
1
1
  module Pochette
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency "activesupport", "~> 4.2"
24
24
  spec.add_dependency "bitcoin_rpc", "~> 0.1.1"
25
25
  spec.add_dependency "bitcoin-ruby", "~> 0.0.7"
26
+ spec.add_dependency "money-tree", "~> 0.9.0"
26
27
  spec.add_dependency "contracts", "~> 0.12.0"
27
28
 
28
29
  spec.add_development_dependency "bundler", "~> 1.9"
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.2.0
4
+ version: 0.2.2
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-11-03 00:00:00.000000000 Z
12
+ date: 2015-12-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: 0.0.7
56
+ - !ruby/object:Gem::Dependency
57
+ name: money-tree
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 0.9.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 0.9.0
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: contracts
58
72
  requirement: !ruby/object:Gem::Requirement