coin-op 0.3.0 → 0.4.0

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: 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