coin-op 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45dac9e7491f6421517aef93700c6063e977d752
4
- data.tar.gz: f32da4cc0cfb9b004fff31a5523dd90c7ad5c847
3
+ metadata.gz: fb3f0c14ebb0842dde25e82a66276a7f8696ee02
4
+ data.tar.gz: e9cb449657d9ee89a43c779da8ef4e5d25997ffa
5
5
  SHA512:
6
- metadata.gz: c41d2028acf3753edf97b35d111c8d3bd1df92b02f5a9ba079c8d13fa89681f386bf3ceb8d0e96cefe18819913fc35f68c0e61a860977e32cf6a8d141824df64
7
- data.tar.gz: f8b07a487c25628ba0af70f50a48f56a544ab3766a2d4186b92a31b4d86a4393042655fa0a83c96dc3b913d2cb7a68f2b44221797cff12b06dc7b69b1181e2d5
6
+ metadata.gz: 3dddf2781e3e9d3aaade199ab6cf30979138f796457fa8d8dfe81d0c656e4224f027571310fd5521980a6dda2baaf102ed50347bde2ebf98aa941aed79e110b9
7
+ data.tar.gz: 92a224650077948d9a84580349b379e2089b2dc5c4f9ed7ab0f3a916b2588b5c585f1e5a363dfaa89db39be507ccbb9b289476211a12497265162e9c871b82d2
Binary file
data.tar.gz.sig CHANGED
@@ -1 +1 @@
1
- zW����&oM.Rꌹ°5�Ş��!��m'G���.����a C�/v7 �{t˝H��+ ����V���,�OɓNk��1�foV���x��.mK�^���-�,^�oH[#Ln��!�/�a������l4��'}�� A�Y�4+
1
+ j C=��aYbc�X��
@@ -1,4 +1,61 @@
1
- require "bitcoin"
1
+ require 'bitcoin'
2
+
3
+ Bitcoin::NETWORKS[:dogecoin] = Bitcoin::NETWORKS[:litecoin].merge({
4
+ project: :dogecoin,
5
+ magic_head: "\xc0\xc0\xc0\xc0",
6
+ address_version: "1e",
7
+ p2sh_version: "16",
8
+ privkey_version: "9e",
9
+ default_port: 22556,
10
+ protocol_version: 70003,
11
+ max_money: 100_000_000_000 * Bitcoin::COIN,
12
+ min_tx_fee: Bitcoin::COIN,
13
+ min_relay_tx_fee: Bitcoin::COIN,
14
+ free_tx_bytes: 26_000,
15
+ dust: Bitcoin::COIN,
16
+ per_dust_fee: true,
17
+ coinbase_maturity: 30,
18
+ coinbase_maturity_new: 240,
19
+ reward_base: 500_000 * Bitcoin::COIN,
20
+ reward_halving: 100_000,
21
+ retarget_interval: 240,
22
+ retarget_time: 14400, # 4 hours
23
+ retarget_time_new: 60, # 1 minute
24
+ target_spacing: 60, # block interval
25
+ dns_seeds: [
26
+ "seed.dogechain.info",
27
+ "seed.dogecoin.com",
28
+ ],
29
+ genesis_hash: "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691",
30
+ proof_of_work_limit: 0x1e0fffff,
31
+ alert_pubkeys: [],
32
+ known_nodes: [
33
+ "daemons.chain.so",
34
+ "bootstrap.chain.so",
35
+ ],
36
+ checkpoints: {
37
+ 0 => "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691",
38
+ 42279 => "8444c3ef39a46222e87584ef956ad2c9ef401578bd8b51e8e4b9a86ec3134d3a",
39
+ 42400 => "557bb7c17ed9e6d4a6f9361cfddf7c1fc0bdc394af7019167442b41f507252b4",
40
+ 104679 => "35eb87ae90d44b98898fec8c39577b76cb1eb08e1261cfc10706c8ce9a1d01cf",
41
+ 128370 => "3f9265c94cab7dc3bd6a2ad2fb26c8845cb41cff437e0a75ae006997b4974be6",
42
+ 145000 => "cc47cae70d7c5c92828d3214a266331dde59087d4a39071fa76ddfff9b7bde72",
43
+ 165393 => "7154efb4009e18c1c6a6a79fc6015f48502bcd0a1edd9c20e44cd7cbbe2eeef1",
44
+ 186774 => "3c712c49b34a5f34d4b963750d6ba02b73e8a938d2ee415dcda141d89f5cb23a",
45
+ 199992 => "3408ff829b7104eebaf61fd2ba2203ef2a43af38b95b353e992ef48f00ebb190",
46
+ 225000 => "be148d9c5eab4a33392a6367198796784479720d06bfdd07bd547fe934eea15a",
47
+ 250000 => "0e4bcfe8d970979f7e30e2809ab51908d435677998cf759169407824d4f36460",
48
+ 270639 => "c587a36dd4f60725b9dd01d99694799bef111fc584d659f6756ab06d2a90d911",
49
+ 299742 => "1cc89c0c8a58046bf0222fe131c099852bd9af25a80e07922918ef5fb39d6742",
50
+ 323141 => "60c9f919f9b271add6ef5671e9538bad296d79f7fdc6487ba702bf2ba131d31d",
51
+ 339202 => "8c29048df5ae9df38a67ea9470fdd404d281a3a5c6f33080cd5bf14aa496ab03"
52
+ },
53
+ auxpow_chain_id: 0x0062,
54
+ # Doge-specific hard-fork cutoffs
55
+ difficulty_change_block: 145000,
56
+ maturity_change_block: 145000,
57
+ auxpow_start_block: 371337
58
+ })
2
59
 
3
60
  # bitcoin-ruby is not multi-network friendly. It's also a hassle
4
61
  # to tell what network you're using if you don't already know.
@@ -7,12 +64,185 @@ Bitcoin::NETWORKS.each do |name, definition|
7
64
  definition[:name] = name
8
65
  end
9
66
 
67
+ module Bitcoin
68
+ module OpenSSL_EC
69
+ attach_function :d2i_ECDSA_SIG, [:pointer, :pointer, :long], :pointer
70
+ attach_function :i2d_ECDSA_SIG, [:pointer, :pointer], :int
71
+ attach_function :OPENSSL_free, :CRYPTO_free, [:pointer], :void
72
+ attach_function :BN_rshift1, [:pointer, :pointer], :int
73
+ attach_function :BN_sub, [:pointer, :pointer, :pointer], :int
74
+ attach_function :BN_num_bits, [:pointer], :int
75
+
76
+ def self.BN_num_bytes(ptr); (BN_num_bits(ptr) + 7) / 8; end
77
+
78
+ # repack signature for OpenSSL 1.0.1k handling of DER signatures
79
+ # https://github.com/bitcoin/bitcoin/pull/5634/files
80
+ def self.repack_der_signature(signature)
81
+ init_ffi_ssl
82
+
83
+ return false if signature.empty?
84
+
85
+ # New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first.
86
+ norm_der = FFI::MemoryPointer.new(:pointer)
87
+ sig_ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, FFI::MemoryPointer.from_string(signature))
88
+
89
+ norm_sig = d2i_ECDSA_SIG(nil, sig_ptr, signature.bytesize)
90
+
91
+ derlen = i2d_ECDSA_SIG(norm_sig, norm_der)
92
+ ECDSA_SIG_free(norm_sig)
93
+ return false if derlen <= 0
94
+
95
+ ret = norm_der.read_pointer.read_string(derlen)
96
+ OPENSSL_free(norm_der.read_pointer)
97
+
98
+ ret
99
+ end
100
+
101
+ # Regenerate a DER-encoded signature such that the S-value complies with the BIP62
102
+ # specification.
103
+ #
104
+ def self.signature_to_low_s(signature)
105
+ init_ffi_ssl
106
+
107
+ temp = signature.unpack("C*")
108
+ length_r = temp[3]
109
+ length_s = temp[5+length_r]
110
+ sig = FFI::MemoryPointer.from_string(signature)
111
+
112
+ # Calculate the lower s value
113
+ s = BN_bin2bn(sig[6 + length_r], length_s, BN_new())
114
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
115
+ group, order, halforder, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_new(), BN_CTX_new()
116
+
117
+ EC_GROUP_get_order(group, order, ctx)
118
+ BN_rshift1(halforder, order)
119
+ if BN_cmp(s, halforder) > 0
120
+ BN_sub(s, order, s)
121
+ end
122
+
123
+ BN_free(halforder)
124
+ BN_free(order)
125
+ BN_CTX_free(ctx)
126
+
127
+ buf = FFI::MemoryPointer.new(:uint8, BN_num_bytes(s))
128
+ BN_bn2bin(s, buf)
129
+ length_s = BN_num_bytes(s)
130
+ # p buf.read_string(length_s).unpack("H*")
131
+
132
+ # Re-encode the signature in DER format
133
+ sig = [0x30, 0, 0x02, length_r]
134
+ sig.concat(temp.slice(4, length_r))
135
+ sig << 0x02
136
+ sig << length_s
137
+ sig.concat(buf.read_string(length_s).unpack("C*"))
138
+ sig[1] = sig.size - 2
139
+
140
+ BN_free(s)
141
+ EC_KEY_free(eckey)
142
+
143
+ sig.pack("C*")
144
+ end
145
+
146
+ end
147
+
148
+ module Util
149
+ def verify_signature(hash, signature, public_key)
150
+ key = bitcoin_elliptic_curve
151
+ key.public_key = ::OpenSSL::PKey::EC::Point.from_hex(key.group, public_key)
152
+ signature = Bitcoin::OpenSSL_EC.repack_der_signature(signature)
153
+ if signature
154
+ key.dsa_verify_asn1(hash, signature)
155
+ else
156
+ false
157
+ end
158
+ rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
159
+ false
160
+ end
161
+ end
162
+
163
+ class Key
164
+ # Sign +data+ with the key.
165
+ # key1 = Bitcoin::Key.generate
166
+ # sig = key1.sign("some data")
167
+ def sign(data)
168
+ sig = @key.dsa_sign_asn1(data)
169
+ if Script::is_low_der_signature?(sig)
170
+ sig
171
+ else
172
+ Bitcoin::OpenSSL_EC.signature_to_low_s(sig)
173
+ end
174
+ end
175
+
176
+ # Verify signature +sig+ for +data+.
177
+ # key2 = Bitcoin::Key.new(nil, key1.pub)
178
+ # key2.verify("some data", sig)
179
+ def verify(data, sig)
180
+ regenerate_pubkey unless @key.public_key
181
+ sig = Bitcoin::OpenSSL_EC.repack_der_signature(sig)
182
+ if sig
183
+ @key.dsa_verify_asn1(data, sig)
184
+ else
185
+ false
186
+ end
187
+ end
188
+ end
189
+
190
+ class Script
191
+ attr_reader :raw, :chunks, :debug, :stack
192
+
193
+ # Loosely correlates with IsLowDERSignature() from interpreter.cpp
194
+ def self.is_low_der_signature?(sig)
195
+ s = sig.unpack("C*")
196
+
197
+ length_r = s[3]
198
+ length_s = s[5+length_r]
199
+ s_val = s.slice(6 + length_r, length_s)
200
+
201
+ # If the S value is above the order of the curve divided by two, its
202
+ # complement modulo the order could have been used instead, which is
203
+ # one byte shorter when encoded correctly.
204
+ max_mod_half_order = [
205
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
206
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
207
+ 0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,
208
+ 0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0]
209
+
210
+ compare_big_endian(s_val, [0]) > 0 &&
211
+ compare_big_endian(s_val, max_mod_half_order) <= 0
212
+ end
213
+
214
+ # Compares two arrays of bytes
215
+ def self.compare_big_endian(c1, c2)
216
+ c1, c2 = c1.dup, c2.dup # Clone the arrays
217
+
218
+ while c1.size > c2.size
219
+ return 1 if c1.shift > 0
220
+ end
221
+
222
+ while c2.size > c1.size
223
+ return -1 if c2.shift > 0
224
+ end
225
+
226
+ c1.size.times{|idx| return c1[idx] - c2[idx] if c1[idx] != c2[idx] }
227
+ 0
228
+ end
229
+ end
230
+
231
+ end
10
232
 
11
233
  # BIP 32 Hierarchical Deterministic Wallets
12
234
  require "money-tree"
13
235
 
14
236
  # establish the namespace
15
237
  module CoinOp
238
+ @bitcoin_mutex = Mutex.new
239
+
240
+ def self.syncbit(network, &block)
241
+ @bitcoin_mutex.synchronize do
242
+ Bitcoin.network = network
243
+ block.call
244
+ end
245
+ end
16
246
  module Bit
17
247
  end
18
248
  end
@@ -30,3 +260,4 @@ require_relative "bit/fee"
30
260
  # Augmented functionality
31
261
  require_relative "bit/multi_wallet"
32
262
 
263
+
@@ -12,7 +12,7 @@ module CoinOp::Bit
12
12
  # that deviate from the customary single signature.
13
13
  #
14
14
  # Returns the estimated fee in satoshis.
15
- def estimate(unspents, payees, tx_size=nil)
15
+ def estimate(unspents, payees, tx_size=nil, network:)
16
16
  # https://en.bitcoin.it/wiki/Transaction_fees
17
17
 
18
18
  # dupe because we'll need to add a change output
@@ -21,7 +21,7 @@ module CoinOp::Bit
21
21
  unspent_total = unspents.inject(0) {|sum, output| sum += output.value}
22
22
  payee_total = payees.inject(0) {|sum, payee| sum += payee.value}
23
23
  nominal_change = unspent_total - payee_total
24
- payees << Output.new(:value => nominal_change)
24
+ payees << Output.new(value: nominal_change, network: network)
25
25
 
26
26
  tx_size ||= estimate_tx_size(unspents.size, payees.size)
27
27
  min = payees.min_by {|payee| payee.value }
@@ -37,17 +37,17 @@ module CoinOp::Bit
37
37
  if small && big_outputs && high_priority
38
38
  0
39
39
  else
40
- fee_for_bytes(tx_size)
40
+ fee_for_bytes(tx_size, network: network)
41
41
  end
42
42
 
43
43
  end
44
44
 
45
- def fee_for_bytes(bytes)
45
+ def fee_for_bytes(bytes, network:)
46
46
  # https://en.bitcoin.it/wiki/Transaction_fees
47
47
  # > the reference implementation will round up the transaction size to the
48
48
  # > next thousand bytes and add a fee of 0.1 mBTC (0.0001 BTC) per thousand bytes
49
49
  size = (bytes / 1000) + 1
50
- Bitcoin.network[:min_tx_fee] * size
50
+ CoinOp.syncbit(network) { Bitcoin.network[:min_tx_fee] * size }
51
51
  end
52
52
 
53
53
  # From http://bitcoinfees.com. This estimation is only valid for
@@ -19,13 +19,13 @@ module CoinOp::Bit
19
19
  # * script_sig_asm - the string form of the scriptSig for this input
20
20
  #
21
21
  def initialize(options={})
22
- @transaction, @index, @output =
23
- options.values_at :transaction, :index, :output
22
+ @transaction, @index, @output, @network =
23
+ options.values_at :transaction, :index, :output, :network
24
24
 
25
25
  script_sig_asm = options[:script_sig_asm]
26
26
 
27
27
  unless @output.is_a? Output
28
- @output = Output.new(@output)
28
+ @output = Output.new(@output, network: @network)
29
29
  end
30
30
 
31
31
  @native = Bitcoin::Protocol::TxIn.new
@@ -1,27 +1,15 @@
1
- require "money-tree"
2
- require "bitcoin"
3
-
4
1
  module CoinOp::Bit
5
2
 
6
3
  class MultiWallet
7
4
  include CoinOp::Encodings
8
5
 
9
- NetworkMap = {
10
- :testnet3 => :bitcoin_testnet,
11
- :bitcoin_testnet => :bitcoin_testnet,
12
- :bitcoin => :bitcoin
13
- }
14
-
15
- def self.generate(names, network_name=:testnet3)
16
- unless network = NetworkMap[network_name]
17
- raise ArgumentError, "Unknown network #{network_name}"
18
- end
6
+ def self.generate(names)
19
7
  masters = {}
20
8
  names.each do |name|
21
9
  name = name.to_sym
22
- masters[name] = MoneyTree::Master.new(:network => network)
10
+ masters[name] = MoneyTree::Master.new
23
11
  end
24
- self.new(:private => masters, :network => network)
12
+ self.new(private: masters)
25
13
  end
26
14
 
27
15
  attr_reader :trees
@@ -30,7 +18,6 @@ module CoinOp::Bit
30
18
  @private_trees = {}
31
19
  @public_trees = {}
32
20
  @trees = {}
33
- @network = NetworkMap[options.include? :network ? options[:network] : :testnet3]
34
21
 
35
22
  # FIXME: we should allow this.
36
23
  # if !private_trees
@@ -57,7 +44,7 @@ module CoinOp::Bit
57
44
  when MoneyTree::Node
58
45
  arg
59
46
  when String
60
- MoneyTree::Node.from_serialized_address(arg)
47
+ MoneyTree::Node.from_bip32(arg)
61
48
  else
62
49
  raise "Unusable type: #{node.class}"
63
50
  end
@@ -83,14 +70,14 @@ module CoinOp::Bit
83
70
  names.each do |name|
84
71
  name = name.to_sym
85
72
  tree = @private_trees.delete(name)
86
- address = tree.to_serialized_address
87
- @public_trees[name] = MoneyTree::Master.from_serialized_address(address)
73
+ serialized_priv = tree.to_bip32
74
+ @public_trees[name] = MoneyTree::Master.from_bip32(serialized_priv)
88
75
  end
89
76
  end
90
77
 
91
78
  def import(addresses)
92
79
  addresses.each do |name, address|
93
- node = MoneyTree::Master.from_serialized_address(address)
80
+ node = MoneyTree::Master.from_bip32(address)
94
81
  if node.private_key
95
82
  @private_trees[name] = node
96
83
  else
@@ -99,35 +86,34 @@ module CoinOp::Bit
99
86
  end
100
87
  end
101
88
 
102
- def private_seed(name)
89
+ def private_seed(name, network:)
103
90
  raise "No such node: '#{name}'" unless (node = @private_trees[name.to_sym])
104
- node.to_serialized_address(:private)
91
+ node.to_bip32(:private, network: network)
105
92
  end
106
93
 
107
94
  alias_method :private_address, :private_seed
108
95
 
109
- def public_seed(name)
96
+ def public_seed(name, network:)
110
97
  name = name.to_sym
111
98
  if node = (@public_trees[name] || @private_trees[name])
112
- node.to_serialized_address
99
+ node.to_bip32(network: network)
113
100
  else
114
101
  raise "No such node: '#{name}'"
115
102
  end
116
103
  end
117
104
 
118
-
119
- def private_seeds
105
+ def private_seeds(network:)
120
106
  out = {}
121
107
  @private_trees.each do |name, tree|
122
- out[name] = self.private_address(name)
108
+ out[name] = self.private_address(name, network: network)
123
109
  end
124
110
  out
125
111
  end
126
112
 
127
- def public_seeds
113
+ def public_seeds(network:)
128
114
  out = {}
129
115
  @private_trees.each do |name, node|
130
- out[name] = node.to_serialized_address
116
+ out[name] = node.to_bip32(network: network)
131
117
  end
132
118
  out
133
119
  end
@@ -141,7 +127,6 @@ module CoinOp::Bit
141
127
  :path => path,
142
128
  :private => {},
143
129
  :public => {},
144
- :network => @network
145
130
  }
146
131
  @private_trees.each do |name, node|
147
132
  options[:private][name] = node.node_for_path(path)
@@ -211,7 +196,10 @@ module CoinOp::Bit
211
196
  combined = {}
212
197
  sig_dicts.each do |sig_dict|
213
198
  sig_dict.each do |tree, signature|
214
- combined[tree] = decode_base58(signature)
199
+ decoded_sig = decode_base58(signature)
200
+ low_s_der_sig = Bitcoin::Script.is_low_der_signature?(decoded_sig) ?
201
+ decoded_sig : Bitcoin::OpenSSL_EC.signature_to_low_s(decoded_sig)
202
+ combined[tree] = Bitcoin::OpenSSL_EC.repack_der_signature(low_s_der_sig)
215
203
  end
216
204
  end
217
205
 
@@ -226,6 +214,13 @@ module CoinOp::Bit
226
214
  class MultiNode
227
215
  include CoinOp::Encodings
228
216
 
217
+ CODE_TO_NETWORK = {
218
+ 0 => :bitcoin,
219
+ 1 => :testnet3,
220
+ 2 => :litecoin,
221
+ 3 => :dogecoin
222
+ }
223
+
229
224
  attr_reader :path, :private, :public, :keys, :public_keys
230
225
  def initialize(options)
231
226
  @path = options[:path]
@@ -234,7 +229,6 @@ module CoinOp::Bit
234
229
  @public_keys = {}
235
230
  @private = options[:private]
236
231
  @public = options[:public]
237
- @network = options[:network]
238
232
 
239
233
  @private.each do |name, node|
240
234
  key = Bitcoin::Key.new(node.private_key.to_hex, node.public_key.to_hex)
@@ -246,10 +240,14 @@ module CoinOp::Bit
246
240
  end
247
241
  end
248
242
 
243
+ def network
244
+ CODE_TO_NETWORK.fetch(@path.split('/')[2].to_i)
245
+ end
246
+
249
247
  def script(m=2)
250
248
  # m of n
251
249
  keys = @public_keys.sort_by {|name, key| name }.map {|name, key| key.pub }
252
- Script.new(:public_keys => keys, :needed => m, :network => @network)
250
+ Script.new(public_keys: keys, needed: m, network: network)
253
251
  end
254
252
 
255
253
  def address
@@ -259,21 +257,14 @@ module CoinOp::Bit
259
257
  alias_method :p2sh_address, :address
260
258
 
261
259
  def p2sh_script
262
- Script.new(:address => self.script.p2sh_address, :network => @network)
263
- end
264
-
265
- def sign(name, value)
266
- raise "No such key: '#{name}'" unless (key = @keys[name.to_sym])
267
- # \x01 means the hash type is SIGHASH_ALL
268
- # https://en.bitcoin.it/wiki/OP_CHECKSIG#Hashtype_SIGHASH_ALL_.28default.29
269
- key.sign(value) + "\x01"
260
+ Script.new(:address => self.script.p2sh_address, network: network)
270
261
  end
271
262
 
272
263
  def signatures(value, names:)
273
264
  out = {}
274
265
  @keys.each do |name, key|
275
266
  next unless names.include?(name)
276
- out[name] = base58(self.sign(name, value))
267
+ out[name] = base58(key.sign(value))
277
268
  end
278
269
  out
279
270
  end
@@ -20,7 +20,9 @@ module CoinOp::Bit
20
20
  # or :address (a valid Bitcoin address)
21
21
  # * :metadata (a Hash with arbitrary contents)
22
22
  #
23
- def initialize(options)
23
+ def initialize(options, network: nil)
24
+ network_name = options[:network] || network
25
+ raise(ArgumentError, 'Network cannot be nil!') unless network_name
24
26
  if options[:transaction]
25
27
  @transaction = options[:transaction]
26
28
  elsif options[:transaction_hash]
@@ -36,9 +38,9 @@ module CoinOp::Bit
36
38
  @metadata[:confirmations] ||= confirmations
37
39
 
38
40
  if options[:script]
39
- @script = Script.new(options[:script])
41
+ @script = Script.new(options[:script], network: network_name)
40
42
  elsif @address
41
- @script = Script.new(:address => @address)
43
+ @script = Script.new(address: @address, network: network_name)
42
44
  end
43
45
 
44
46
 
@@ -95,3 +97,4 @@ module CoinOp::Bit
95
97
  end
96
98
 
97
99
  end
100
+
@@ -27,46 +27,42 @@ module CoinOp::Bit
27
27
  # The name of the crypto-currency network may also be specified. It
28
28
  # defaults to :testnet3. Names supplied in this manner must correspond
29
29
  # to the names in the ::Bitcoin::NETWORKS Hash.
30
- def initialize(options)
31
- # Doing the rescue in case the input argument is a String.
32
- network_name = (options[:network] || :testnet3) rescue :testnet3
30
+ # TODO: PLEASE refactor this. should not accept either string or hash
31
+ def initialize(options, network: nil)
32
+ network_name = network || (options[:network] || :testnet3) rescue :testnet3
33
33
  @network = Bitcoin::NETWORKS[network_name]
34
- Bitcoin.network = network_name
35
34
 
36
35
  # literals
37
- if options.is_a? String
38
- @blob = Bitcoin::Script.binary_from_string options
39
- elsif string = options[:string]
40
- @blob = Bitcoin::Script.binary_from_string string
41
- elsif options[:blob]
42
- @blob = options[:blob]
43
- elsif options[:hex]
44
- @blob = decode_hex(options[:hex])
45
- # arguments for constructing
46
- else
47
- if address = options[:address]
48
- unless Bitcoin::valid_address?(address)
49
- raise ArgumentError, "Invalid address: #{address}"
50
- end
51
- @blob = Bitcoin::Script.to_address_script(address)
52
- elsif public_key = options[:public_key]
53
- @blob = Bitcoin::Script.to_pubkey_script(public_key)
54
- elsif (keys = options[:public_keys]) && (needed = options[:needed])
55
- @blob = Bitcoin::Script.to_multisig_script(needed, *keys)
56
- elsif signatures = options[:signatures]
57
- @blob = Bitcoin::Script.to_multisig_script_sig(*signatures)
36
+ CoinOp.syncbit(network_name) do
37
+ if options.is_a? String
38
+ @blob = Bitcoin::Script.binary_from_string options
39
+ elsif string = options[:string]
40
+ @blob = Bitcoin::Script.binary_from_string string
41
+ elsif options[:blob]
42
+ @blob = options[:blob]
43
+ elsif options[:hex]
44
+ @blob = decode_hex(options[:hex])
45
+ # arguments for constructing
58
46
  else
59
- raise ArgumentError
47
+ if address = options[:address]
48
+ unless Bitcoin::valid_address?(address)
49
+ raise ArgumentError, "Invalid address: #{address}"
50
+ end
51
+ @blob = Bitcoin::Script.to_address_script(address)
52
+ elsif public_key = options[:public_key]
53
+ @blob = Bitcoin::Script.to_pubkey_script(public_key)
54
+ elsif (keys = options[:public_keys]) && (needed = options[:needed])
55
+ @blob = Bitcoin::Script.to_multisig_script(needed, *keys)
56
+ elsif signatures = options[:signatures]
57
+ @blob = Bitcoin::Script.to_multisig_script_sig(*signatures)
58
+ else
59
+ raise ArgumentError
60
+ end
60
61
  end
62
+ @native = Bitcoin::Script.new @blob
63
+ @hex = hex(@blob)
64
+ @string = @native.to_string
61
65
  end
62
-
63
- @hex = hex(@blob)
64
- @native = Bitcoin::Script.new @blob
65
- @string = @native.to_string
66
- end
67
-
68
- def address
69
- @native.get_p2sh_address
70
66
  end
71
67
 
72
68
  def to_s
@@ -109,7 +105,9 @@ module CoinOp::Bit
109
105
  end
110
106
 
111
107
  def hash160
112
- Bitcoin.hash160(@hex)
108
+ CoinOp.syncbit(@network[:name]) do
109
+ @native.get_hash160 || Bitcoin.hash160(@hex)
110
+ end
113
111
  end
114
112
 
115
113
  # Generate the script that uses a P2SH address.
@@ -117,19 +115,25 @@ module CoinOp::Bit
117
115
  # can probably be removed, as I think it is equivalent to
118
116
  # Script.new :address => some_p2sh_address
119
117
  def p2sh_script
120
- self.class.new Bitcoin::Script.to_p2sh_script(self.hash160)
118
+ CoinOp.syncbit(@network[:name]) do |b|
119
+ self.class.new Bitcoin::Script.to_p2sh_script(self.hash160)
120
+ end
121
121
  end
122
122
 
123
123
  def p2sh_address
124
- Bitcoin.hash160_to_p2sh_address(self.hash160)
124
+ Bitcoin.encode_address(self.hash160, Bitcoin::NETWORKS[@network[:name]][:p2sh_version])
125
125
  end
126
126
 
127
+ alias_method :address, :p2sh_address
128
+
127
129
  # Generate a P2SH script_sig for the current script, using the
128
130
  # supplied options, which will, in the case of a multisig input,
129
131
  # be {:signatures => array_of_signatures}.
130
132
  def p2sh_sig(options)
131
133
  string = Script.new(options).to_s
132
- Bitcoin::Script.binary_from_string("#{string} #{self.to_hex}")
134
+ CoinOp.syncbit(@network[:name]) do
135
+ Bitcoin::Script.binary_from_string("#{string} #{self.to_hex}")
136
+ end
133
137
  end
134
138
 
135
139
  end
@@ -12,26 +12,27 @@ module CoinOp::Bit
12
12
 
13
13
  # Construct a Transaction from a data structure of nested Hashes
14
14
  # and Arrays.
15
- def self.data(data)
15
+ def self.data(data, network:)
16
16
  version, lock_time, fee, inputs, outputs, confirmations =
17
17
  data.values_at :version, :lock_time, :fee, :inputs, :outputs, :confirmations
18
18
 
19
19
  transaction = self.new(
20
20
  :fee => fee,
21
21
  :version => version, :lock_time => lock_time,
22
- :confirmations => confirmations
22
+ :confirmations => confirmations,
23
+ network: network
23
24
  )
24
25
 
25
- outputs.each do |data|
26
- transaction.add_output Output.new(data)
26
+ outputs.each do |output_hash|
27
+ transaction.add_output(Output.new(output_hash, network: network))
27
28
  end
28
29
 
29
30
  #FIXME: we're not handling sig_scripts for already signed inputs.
30
31
 
31
32
  if inputs
32
33
  # TODO: use #each instead of #each_with_index
33
- inputs.each_with_index do |data, index|
34
- transaction.add_input(data)
34
+ inputs.each_with_index do |input_hash, index|
35
+ transaction.add_input(input_hash, network: network)
35
36
 
36
37
  ## FIXME: verify that the supplied and computed sig_hashes match
37
38
  #puts :sig_hashes_match => (data[:sig_hash] == input.sig_hash)
@@ -101,6 +102,7 @@ module CoinOp::Bit
101
102
  @native = Bitcoin::Protocol::Tx.new
102
103
  @inputs = []
103
104
  @outputs = []
105
+ @network = options[:network]
104
106
  @fee_override = options[:fee]
105
107
  @confirmations = options[:confirmations]
106
108
  end
@@ -141,7 +143,6 @@ module CoinOp::Bit
141
143
  valid = true
142
144
  @inputs.each_with_index do |input, index|
143
145
  # TODO: confirm whether we need to mess with the block_timestamp arg
144
-
145
146
  unless self.native.verify_input_signature(index, input.output.transaction.native)
146
147
  valid = false
147
148
  bad_inputs << index
@@ -157,14 +158,15 @@ module CoinOp::Bit
157
158
  # * an instance of Output
158
159
  # * a Hash describing an Output
159
160
  #
160
- def add_input(input)
161
+ def add_input(input, network:)
161
162
  # TODO: allow specifying prev_tx and index with a Hash.
162
163
  # Possibly stop using SparseInput.
163
164
 
164
165
  unless input.is_a?(Input)
165
166
  input = Input.new input.merge(
166
- :transaction => self,
167
- :index => @inputs.size,
167
+ transaction: self,
168
+ index: @inputs.size,
169
+ network: network
168
170
  )
169
171
  end
170
172
 
@@ -178,7 +180,7 @@ module CoinOp::Bit
178
180
  # Takes either an Output or a Hash describing an output.
179
181
  def add_output(output)
180
182
  unless output.is_a? Output
181
- output = Output.new(output)
183
+ output = Output.new(output, network: @network)
182
184
  end
183
185
 
184
186
  index = @outputs.size
@@ -222,12 +224,13 @@ module CoinOp::Bit
222
224
  # Typically used only by #to_json.
223
225
  def to_hash
224
226
  {
227
+ :confirmations => self.confirmations.nil? ? 0 : self.confirmations,
225
228
  :version => self.version,
226
229
  :lock_time => self.lock_time,
227
230
  :hash => self.hex_hash,
228
231
  :fee => self.fee,
229
232
  :inputs => self.inputs,
230
- :outputs => self.outputs,
233
+ :outputs => self.outputs
231
234
  }
232
235
  end
233
236
 
@@ -235,10 +238,14 @@ module CoinOp::Bit
235
238
  self.to_hash.to_json(*a)
236
239
  end
237
240
 
238
- # Compute the digest for a given input. In most cases, you need to provide
239
- # the script. Which script to supply can be confusing, especially in the
240
- # case of P2SH outputs.
241
- # TODO: explain the above more clearly.
241
+ # Compute the digest for a given input. This is the value that is actually
242
+ # signed in a transaction.
243
+ # e.g. I want to spend UTXO0 - I provide the UTXO0 as an input.
244
+ # I provide as a script the script to which UTXO0 was paid.
245
+ # If UTX0 was paid to a P2SH address, the script here needs to be the correct
246
+ # m-of-n structure, which may well not be part of the input.output object
247
+ # - This works here because we default to 2-of-3 and have consistent ordering
248
+ # When we support multiple m-of-n's, this may become a prollem.
242
249
  def sig_hash(input, script=nil)
243
250
  # We only allow SIGHASH_ALL at this time
244
251
  # https://en.bitcoin.it/wiki/OP_CHECKSIG#Hashtype_SIGHASH_ALL_.28default.29
@@ -286,7 +293,7 @@ module CoinOp::Bit
286
293
  end
287
294
 
288
295
  def fee_override
289
- @fee_override || self.estimate_fee
296
+ @fee_override || self.estimate_fee(network: @network)
290
297
  end
291
298
 
292
299
  # Estimate the fee in satoshis for this transaction. Takes an optional
@@ -363,3 +370,4 @@ module CoinOp::Bit
363
370
  end
364
371
 
365
372
  end
373
+
@@ -1,5 +1,5 @@
1
1
  # Ruby bindings for libsodium, a port of DJB's NaCl crypto library
2
- require "rbnacl/libsodium"
2
+ require "rbnacl"
3
3
  require "openssl"
4
4
 
5
5
  module CoinOp
@@ -1,3 +1,3 @@
1
1
  module CoinOp
2
- VERSION = "0.3.0"
3
- end
2
+ VERSION = "0.4.0"
3
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coin-op
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew King
@@ -32,7 +32,7 @@ cert_chain:
32
32
  tdc4VS7IlSRxlZ3dBOgiigy9GXpJ+7F831AqjxL39EPwdr7RguTNz+pi//RKaT/U
33
33
  IlpVB+Xfk0vQdP7iYfjGxDzUf0FACMjsR95waJmadKW1Iy6STw2hwPhYIQz1Hu1A
34
34
  -----END CERTIFICATE-----
35
- date: 2015-05-04 00:00:00.000000000 Z
35
+ date: 2015-05-19 00:00:00.000000000 Z
36
36
  dependencies:
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: bitcoin-ruby
metadata.gz.sig CHANGED
Binary file