pochette 0.2.0 → 0.2.2

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