bitcoin-ruby 0.0.7 → 0.0.8

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: a8a85933bb6f2c36c0262cbfc0b254bfb5449e9d
4
- data.tar.gz: a61c9750f801b78ccb01350d2f777560f73bdb9b
3
+ metadata.gz: db1b92d6514cc112f7141841b658fe16fc23cdad
4
+ data.tar.gz: 2a4a5e0e7215cc7739d8208322ac361ef1beb988
5
5
  SHA512:
6
- metadata.gz: 7cb472579d3e819c18025e3fef9b2db6c36e3bdab701b6984482920ada6d07b736af0170512fc7598fd904d8c9104c311c64fa3f3bb87d77acc9310db49e1e71
7
- data.tar.gz: 1d0f4d2c551a89433620e87ec391be57edbca0af200df5e9df6b80ab6cd73b8092f36b4030caa3b12671f920a03b2f7dad5189c473e594427f09520641f0db01
6
+ metadata.gz: 63c618a352af13e722f280e84ef6801ded898c65db7f346eb410fa417c310c89c643d74e66474a975281326bae0d9e4b70316a6c2fb4b4e9a8f54c8462ce241c
7
+ data.tar.gz: 421b29bcb069e3f6de8d3c8484aece5d6bd09a9f0695866f47baa6308e02ef6fa5b267a350aea9741aaea19b5395fce35456111e24f787ec6819498df199b02a
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in bitcoin-ruby.gemspec
4
4
  gemspec
@@ -1,20 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bitcoin-ruby (0.0.7)
4
+ bitcoin-ruby (0.0.8)
5
5
 
6
6
  GEM
7
- remote: http://rubygems.org/
7
+ remote: https://rubygems.org/
8
8
  specs:
9
9
  bacon (1.2.0)
10
- eventmachine (1.0.7)
11
- ffi (1.9.8)
10
+ eventmachine (1.0.8)
11
+ ffi (1.9.10)
12
12
  ffi-compiler (0.1.3)
13
13
  ffi (>= 1.0.0)
14
14
  rake
15
- minitest (5.7.0)
15
+ minitest (5.8.2)
16
16
  rake (10.4.2)
17
- scrypt (2.0.0)
17
+ scrypt (2.0.2)
18
18
  ffi-compiler (>= 0.0.2)
19
19
  rake
20
20
 
@@ -31,4 +31,4 @@ DEPENDENCIES
31
31
  scrypt
32
32
 
33
33
  BUNDLED WITH
34
- 1.10.2
34
+ 1.10.6
@@ -127,7 +127,7 @@ If you want to control the Bitcoin::Script yourself, you can do so
127
127
  You need to know the previous output you want to spend (tx hash and output index),
128
128
  as well as the private key for the address required to sign for it.
129
129
 
130
- # use testnet so you don't acidentially your whole money!
130
+ # use testnet so you don't accidentally blow your whole money!
131
131
  Bitcoin.network = :testnet3
132
132
 
133
133
  # make the DSL methods available in your scope
@@ -9,6 +9,14 @@ class Bitcoin::Protocol::Parser; def log; stub=Object.new; def stub.method_missi
9
9
  module SimpleNode
10
10
  class Connection < EM::Connection
11
11
 
12
+ def on_ping(nonce)
13
+ send_data(Bitcoin::Protocol.pong_pkt(nonce)) if nonce
14
+ end
15
+
16
+ def on_reject(reject)
17
+ log.info { "reject #{reject}" }
18
+ end
19
+
12
20
  def on_tx(tx)
13
21
  log.info { "received transaction: #{tx.hash}" }
14
22
 
@@ -276,12 +276,23 @@ module Bitcoin
276
276
  end
277
277
 
278
278
  def sign_data(key, data)
279
- sig = key.dsa_sign_asn1(data)
280
- if Script.is_low_der_signature?(sig)
281
- sig
282
- else
283
- Bitcoin::OpenSSL_EC.signature_to_low_s(sig)
284
- end
279
+ sig = nil
280
+ loop {
281
+ sig = key.dsa_sign_asn1(data)
282
+ sig = if Script.is_low_der_signature?(sig)
283
+ sig
284
+ else
285
+ Bitcoin::OpenSSL_EC.signature_to_low_s(sig)
286
+ end
287
+
288
+ buf = sig + [Script::SIGHASH_TYPE[:all]].pack("C") # is_der_signature expects sig + sighash_type format
289
+ if Script.is_der_signature?(buf)
290
+ break
291
+ else
292
+ p ["Bitcoin#sign_data: invalid der signature generated, trying again.", data.unpack("H*")[0], sig.unpack("H*")[0]]
293
+ end
294
+ }
295
+ return sig
285
296
  end
286
297
 
287
298
  def verify_signature(hash, signature, public_key)
@@ -310,9 +321,13 @@ module Bitcoin
310
321
  end
311
322
 
312
323
  def bitcoin_signed_message_hash(message)
313
- # TODO: this will fail horribly on messages with len > 255. It's a cheap implementation of Bitcoin's CDataStream.
314
- data = "\x18Bitcoin Signed Message:\n" + [message.bytesize].pack("C") + message
315
- Digest::SHA256.digest(Digest::SHA256.digest(data))
324
+ message = message.dup.force_encoding('binary')
325
+
326
+ magic = "Bitcoin Signed Message:\n"
327
+ buf = Protocol.pack_var_int(magic.bytesize) + magic
328
+ buf << Protocol.pack_var_int(message.bytesize) + message
329
+
330
+ Digest::SHA256.digest(Digest::SHA256.digest(buf))
316
331
  end
317
332
 
318
333
  def sign_message(private_key_hex, public_key_hex, message)
@@ -552,11 +567,13 @@ module Bitcoin
552
567
  free_tx_bytes: 1_000,
553
568
  dust: CENT,
554
569
  per_dust_fee: false,
570
+ bip34_height: 227931,
555
571
  dns_seeds: [
556
572
  "seed.bitcoin.sipa.be",
557
573
  "dnsseed.bluematt.me",
558
574
  "dnsseed.bitcoin.dashjr.org",
559
575
  "bitseed.xf2.org",
576
+ "dnsseed.webbtc.com",
560
577
  ],
561
578
  genesis_hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
562
579
  proof_of_work_limit: 0x1d00ffff,
@@ -566,6 +583,7 @@ module Bitcoin
566
583
  'mining.bitcoin.cz',
567
584
  'blockchain.info',
568
585
  'blockexplorer.com',
586
+ 'webbtc.com',
569
587
  ],
570
588
  checkpoints: {
571
589
  11111 => "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d",
@@ -593,6 +611,7 @@ module Bitcoin
593
611
  extended_privkey_version: "04358394",
594
612
  extended_pubkey_version: "043587cf",
595
613
  default_port: 18333,
614
+ bip34_height: 21111,
596
615
  dns_seeds: [ ],
597
616
  genesis_hash: "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008",
598
617
  proof_of_work_limit: 0x1d07fff8,
@@ -604,7 +623,8 @@ module Bitcoin
604
623
  NETWORKS[:regtest] = NETWORKS[:testnet].merge({
605
624
  default_port: 18444,
606
625
  genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",
607
- proof_of_work_limit: (1 << 255) - 1,
626
+ proof_of_work_limit: 0x207fffff,
627
+ bip34_height: 0,
608
628
  })
609
629
 
610
630
  NETWORKS[:testnet3] = NETWORKS[:testnet].merge({
@@ -617,7 +637,9 @@ module Bitcoin
617
637
  "testnet-seed.bitcoin.schildbach.de",
618
638
  "testnet-seed.bitcoin.petertodd.org",
619
639
  "testnet-seed.bluematt.me",
640
+ "dnsseed.test.webbtc.com",
620
641
  ],
642
+ known_nodes: ["test.webbtc.com"],
621
643
  checkpoints: {
622
644
  # 542 contains invalid transaction
623
645
  542 => "0000000083c1f82cf72c6724f7a317325806384b06408bce7a4327f418dfd5ad",
@@ -811,11 +833,14 @@ module Bitcoin
811
833
  protocol_version: 35000,
812
834
  min_tx_fee: 50_000,
813
835
  per_dust_fee: true,
814
- dns_seeds: [],
836
+ dns_seeds: [
837
+ "nmc.seed.quisquis.de",
838
+ "dnsseed.namecoin.webbtc.com",
839
+ ],
815
840
  genesis_hash: "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
816
841
  known_nodes: [
817
842
  "bitcoin.tunl.in",
818
- "webbtc.com",
843
+ "namecoin.webbtc.com",
819
844
  "178.32.31.41",
820
845
  "78.47.86.43",
821
846
  "69.164.206.88",
@@ -833,11 +858,26 @@ module Bitcoin
833
858
  NETWORKS[:namecoin_testnet] = NETWORKS[:namecoin].merge({
834
859
  magic_head: "\xFA\xBF\xB5\xFE",
835
860
  default_port: 18334,
836
- genesis_hash: "00000001f8ab0d14bceaeb50d163b0bef15aecf62b87bd5f5c864d37f201db97",
837
- known_nodes: ["178.32.31.41"],
838
- checkpoints: {
839
- 0 => "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
840
- }
861
+ genesis_hash: "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008",
862
+ dns_seeds: [
863
+ "dnsseed.test.namecoin.webbtc.com",
864
+ ],
865
+ known_nodes: [
866
+ "test.namecoin.webbtc.com",
867
+ "nmctest.net",
868
+ "192.99.247.234"],
869
+ checkpoints: { }
870
+ })
871
+
872
+ NETWORKS[:namecoin_regtest] = NETWORKS[:namecoin_testnet].merge({
873
+ magic_head: "\xFA\xBF\xB5\xDA",
874
+ default_port: 18445,
875
+ genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",
876
+ dns_seeds: [], known_nodes: [],
877
+ proof_of_work_limit: 0x207fffff,
878
+ checkpoints: { },
879
+ address_versions: { pubkey_hash: "6f", script_hash: "c4" },
880
+ privkey_version: "ef",
841
881
  })
842
882
 
843
883
  end
@@ -14,7 +14,6 @@ module Bitcoin
14
14
  yield c
15
15
  c.block(target)
16
16
  end
17
- alias :blk :build_block
18
17
 
19
18
  # build a Bitcoin::Protocol::Tx.
20
19
  # see TxBuilder for details.
@@ -23,7 +22,6 @@ module Bitcoin
23
22
  yield c
24
23
  c.tx opts
25
24
  end
26
- alias :tx :build_tx
27
25
 
28
26
  # build a Bitcoin::Script.
29
27
  # see ScriptBuilder for details.
@@ -95,7 +93,7 @@ module Bitcoin
95
93
  @block.bits = Bitcoin.encode_compact_bits(target)
96
94
  t = Time.now
97
95
  @block.recalc_block_hash
98
- until @block.hash < target
96
+ until @block.hash.to_i(16) < target.to_i(16)
99
97
  @block.nonce += 1
100
98
  @block.recalc_block_hash
101
99
  if @block.nonce == 100000
@@ -156,9 +154,11 @@ module Bitcoin
156
154
  end
157
155
 
158
156
  # add an output to the transaction (see TxOutBuilder).
159
- def output
157
+ def output value = nil, recipient = nil, type = :address
160
158
  c = TxOutBuilder.new
161
- yield c
159
+ c.value(value) if value
160
+ c.to(recipient, type) if recipient
161
+ yield c if block_given?
162
162
  @outs << c
163
163
  end
164
164
 
@@ -173,18 +173,20 @@ module Bitcoin
173
173
  # case to specify a tx fee that should be left unclaimed by the
174
174
  # change output.
175
175
  def tx opts = {}
176
+ return @tx if @tx.hash
177
+
176
178
  if opts[:change_address] && !opts[:input_value]
177
179
  raise "Must give 'input_value' when auto-generating change output!"
178
180
  end
179
181
  @ins.each {|i| @tx.add_in(i.txin) }
180
182
  @outs.each {|o| @tx.add_out(o.txout) }
181
-
182
183
  if opts[:change_address]
183
- output_value = @tx.out.map(&:value).inject(:+)
184
+ output_value = @tx.out.map(&:value).inject(:+) || 0
184
185
  change_value = opts[:input_value] - output_value
185
186
  if opts[:leave_fee]
186
- if change_value >= @tx.minimum_block_fee
187
- change_value -= @tx.minimum_block_fee
187
+ fee = @tx.minimum_block_fee + (opts[:extra_fee] || 0)
188
+ if change_value >= fee
189
+ change_value -= fee
188
190
  else
189
191
  change_value = 0
190
192
  end
@@ -219,7 +221,7 @@ module Bitcoin
219
221
  def sig_hash_and_all_keys_exist?(inc, sig_script)
220
222
  return false unless @sig_hash && inc.has_keys?
221
223
  script = Bitcoin::Script.new(sig_script)
222
- return true if script.is_hash160? || script.is_pubkey?
224
+ return true if script.is_hash160? || script.is_pubkey? || (Bitcoin.namecoin? && script.is_namecoin?)
223
225
  if script.is_multisig?
224
226
  return inc.has_multiple_keys? && inc.key.size >= script.get_signatures_required
225
227
  end
@@ -426,6 +428,9 @@ module Bitcoin
426
428
 
427
429
  # Create a Bitcoin::Protocol::TxOut used by TxBuilder#output.
428
430
  #
431
+ # t.output 12345, address
432
+ # t.output 12345, p2sh_address, :script_hash
433
+ #
429
434
  # t.output {|o| o.value 12345; o.to address }
430
435
  #
431
436
  # t.output do |o|
@@ -2,8 +2,9 @@
2
2
 
3
3
  require 'ffi'
4
4
 
5
- # Wraps bitcoinconsensus.so (https://github.com/bitcoin/bitcoin)
6
- # commit: 90c71548c795787b008bc337cb9332f75d1bccdb
5
+ # binding for src/.libs/bitcoinconsensus.so (https://github.com/bitcoin/bitcoin)
6
+ # tag: v0.11.0
7
+ # commit: d26f951802c762de04fb68e1a112d611929920ba
7
8
 
8
9
  module Bitcoin
9
10
  module BitcoinConsensus
@@ -1,7 +1,8 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- # Wraps libsecp256k1 (https://github.com/bitcoin/secp256k1)
4
- # commit: 50cc6ab0625efda6dddf1dc86c1e2671f069b0d8
3
+ # bindings for secp256k1 inside bitcoin (https://github.com/bitcoin/bitcoin/tree/v0.11.0/src/secp256k1)
4
+ # tag: v0.11.0
5
+ # commit: d26f951802c762de04fb68e1a112d611929920ba
5
6
 
6
7
  require 'ffi'
7
8
 
@@ -16,128 +17,189 @@ module Bitcoin
16
17
  class_eval <<-RUBY
17
18
  ffi_lib [ %[#{file}] ]
18
19
 
19
- attach_function :secp256k1_start, [:int], :void
20
- attach_function :secp256k1_stop, [], :void
21
- attach_function :secp256k1_ec_seckey_verify, [:pointer], :int
22
- attach_function :secp256k1_ec_pubkey_verify, [:pointer, :int], :int
23
- attach_function :secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer, :int], :int
20
+ ##
21
+ # source: https://github.com/bitcoin/bitcoin/blob/v0.11.0/src/secp256k1/include/secp256k1.h
22
+ ##
24
23
 
25
- # int secp256k1_ecdsa_sign(const unsigned char *msg32, unsigned char *sig, int *siglen, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void *ndata)
26
- attach_function :secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
24
+ # secp256k1_context_t* secp256k1_context_create(int flags)
25
+ attach_function :secp256k1_context_create, [:int], :pointer
27
26
 
28
- # int secp256k1_ecdsa_verify(const unsigned char *msg32, const unsigned char *sig, int siglen, const unsigned char *pubkey, int pubkeylen)
29
- attach_function :secp256k1_ecdsa_verify, [:pointer, :pointer, :int, :pointer, :int], :int
27
+ # secp256k1_context_t* secp256k1_context_clone(const secp256k1_context_t* ctx)
28
+ attach_function :secp256k1_context_clone, [:pointer], :pointer
30
29
 
31
- # int secp256k1_ecdsa_sign_compact(const unsigned char *msg32, unsigned char *sig64, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void *ndata, int *recid)
32
- attach_function :secp256k1_ecdsa_sign_compact, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
30
+ # void secp256k1_context_destroy(secp256k1_context_t* ctx)
31
+ attach_function :secp256k1_context_destroy, [:pointer], :void
33
32
 
34
- # int secp256k1_ecdsa_recover_compact(const unsigned char *msg32, const unsigned char *sig64, unsigned char *pubkey, int *pubkeylen, int compressed, int recid)
35
- attach_function :secp256k1_ecdsa_recover_compact, [:pointer, :pointer, :pointer, :pointer, :int, :int], :int
33
+ # int secp256k1_ecdsa_verify(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig, int siglen, const unsigned char *pubkey, int pubkeylen)
34
+ attach_function :secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :int, :pointer, :int], :int
35
+
36
+ # int secp256k1_ecdsa_sign(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *sig, int *siglen, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void *ndata)
37
+ attach_function :secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
38
+
39
+ # int secp256k1_ecdsa_sign_compact(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *sig64, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void *ndata, int *recid)
40
+ attach_function :secp256k1_ecdsa_sign_compact, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
41
+
42
+ # int secp256k1_ecdsa_recover_compact(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, unsigned char *pubkey, int *pubkeylen, int compressed, int recid)
43
+ attach_function :secp256k1_ecdsa_recover_compact, [:pointer, :pointer, :pointer, :pointer, :pointer, :int, :int], :int
44
+
45
+ # int secp256k1_ec_seckey_verify(const secp256k1_context_t* ctx, const unsigned char *seckey)
46
+ attach_function :secp256k1_ec_seckey_verify, [:pointer, :pointer], :int
47
+
48
+ # int secp256k1_ec_pubkey_verify(const secp256k1_context_t* ctx, const unsigned char *pubkey, int pubkeylen)
49
+ attach_function :secp256k1_ec_pubkey_verify, [:pointer, :pointer, :int], :int
50
+
51
+ # int secp256k1_ec_pubkey_create(const secp256k1_context_t* ctx, unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed)
52
+ attach_function :secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer, :pointer, :int], :int
53
+
54
+ # int secp256k1_ec_pubkey_decompress(const secp256k1_context_t* ctx, unsigned char *pubkey, int *pubkeylen)
55
+ attach_function :secp256k1_ec_pubkey_decompress, [:pointer, :pointer, :pointer], :int
56
+
57
+ # int secp256k1_ec_privkey_export(const secp256k1_context_t* ctx, const unsigned char *seckey, unsigned char *privkey, int *privkeylen, int compressed)
58
+ attach_function :secp256k1_ec_privkey_export, [:pointer, :pointer, :pointer, :pointer, :int], :int
59
+
60
+ # int secp256k1_ec_privkey_import(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *privkey, int privkeylen)
61
+ attach_function :secp256k1_ec_privkey_import, [:pointer, :pointer, :pointer, :pointer], :int
62
+
63
+ # int secp256k1_ec_privkey_tweak_add(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *tweak)
64
+ attach_function :secp256k1_ec_privkey_tweak_add, [:pointer, :pointer, :pointer], :int
65
+
66
+ # int secp256k1_ec_pubkey_tweak_add(const secp256k1_context_t* ctx, unsigned char *pubkey, int pubkeylen, const unsigned char *tweak)
67
+ attach_function :secp256k1_ec_pubkey_tweak_add, [:pointer, :pointer, :int, :pointer], :int
68
+
69
+ # int secp256k1_ec_privkey_tweak_mul(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *tweak)
70
+ attach_function :secp256k1_ec_privkey_tweak_mul, [:pointer, :pointer, :pointer], :int
71
+
72
+ # int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context_t* ctx, unsigned char *pubkey, int pubkeylen, const unsigned char *tweak)
73
+ attach_function :secp256k1_ec_pubkey_tweak_mul, [:pointer, :pointer, :int, :pointer], :int
74
+
75
+ # int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *seed32)
76
+ attach_function :secp256k1_context_randomize, [:pointer, :pointer], :int
36
77
  RUBY
37
78
  end
38
79
 
39
80
  def self.init
40
- return if @secp256k1_started
41
-
42
- lib_path = [ ENV['SECP256K1_LIB_PATH'], 'vendor/secp256k1/.libs/libsecp256k1.so' ].find{|f| File.exists?(f.to_s) }
43
- lib_path = 'libsecp256k1.so' unless lib_path
81
+ return if @loaded
82
+ lib_path = [ ENV['SECP256K1_LIB_PATH'], 'vendor/bitcoin/src/secp256k1/.libs/libsecp256k1.so' ].find{|f| File.exists?(f.to_s) }
44
83
  ffi_load_functions(lib_path)
45
-
46
- secp256k1_start(SECP256K1_START_VERIFY | SECP256K1_START_SIGN)
47
- @secp256k1_started = true
84
+ @loaded = true
48
85
  end
49
86
 
50
- def self.generate_key_pair(compressed=true)
87
+ def self.with_context(flags=nil, seed=nil)
51
88
  init
52
-
53
- while true do
54
- priv_key = SecureRandom.random_bytes(32)
55
- priv_key_buf = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key)
56
- break if secp256k1_ec_seckey_verify(priv_key_buf)
89
+ flags = flags || (SECP256K1_START_VERIFY | SECP256K1_START_SIGN )
90
+ context = secp256k1_context_create(flags)
91
+
92
+ ret, tries, max = 0, 0, 20
93
+ while ret != 1
94
+ raise "secp256k1_context_randomize failed." if tries >= max
95
+ tries += 1
96
+ ret = secp256k1_context_randomize(context, FFI::MemoryPointer.from_string(seed || SecureRandom.random_bytes(32)))
57
97
  end
58
98
 
59
- pub_key_buf = FFI::MemoryPointer.new(:uchar, 65)
60
- pub_key_size = FFI::MemoryPointer.new(:int)
61
- result = secp256k1_ec_pubkey_create(pub_key_buf, pub_key_size, priv_key_buf, compressed ? 1 : 0)
62
- raise "error creating pubkey" unless result
63
-
64
- [ priv_key, pub_key_buf.read_string(pub_key_size.read_int) ]
99
+ yield(context) if block_given?
100
+ ensure
101
+ secp256k1_context_destroy(context)
65
102
  end
66
103
 
67
- def self.sign(data, priv_key)
68
- init
104
+ def self.generate_key_pair(compressed=true)
105
+ with_context do |context|
106
+
107
+ ret, tries, max = 0, 0, 20
108
+ while ret != 1
109
+ raise "secp256k1_ec_seckey_verify in generate_key_pair failed." if tries >= max
110
+ tries += 1
69
111
 
70
- msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
71
- seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
72
- raise "priv_key invalid" unless secp256k1_ec_seckey_verify(seckey)
112
+ priv_key = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, SecureRandom.random_bytes(32))
113
+ ret = secp256k1_ec_seckey_verify(context, priv_key)
114
+ end
73
115
 
74
- sig, siglen = FFI::MemoryPointer.new(:uchar, 72), FFI::MemoryPointer.new(:int).write_int(72)
116
+ pub_key, pub_key_length = FFI::MemoryPointer.new(:uchar, 65), FFI::MemoryPointer.new(:int)
117
+ result = secp256k1_ec_pubkey_create(context, pub_key, pub_key_length, priv_key, compressed ? 1 : 0)
118
+ raise "error creating pubkey" unless result
75
119
 
76
- while true do
77
- break if secp256k1_ecdsa_sign(msg32, sig, siglen, seckey, nil, nil)
120
+ [ priv_key.read_string(32), pub_key.read_string(pub_key_length.read_int) ]
78
121
  end
122
+ end
79
123
 
80
- sig.read_string(siglen.read_int)
124
+ def self.generate_key(compressed=true)
125
+ priv, pub = generate_key_pair(compressed)
126
+ Bitcoin::Key.new(priv.unpack("H*")[0], pub.unpack("H*")[0])
81
127
  end
82
128
 
83
- def self.verify(data, signature, pub_key)
84
- init
129
+ def self.sign(data, priv_key)
130
+ with_context do |context|
131
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
132
+ seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
133
+ raise "priv_key invalid" unless secp256k1_ec_seckey_verify(context, seckey)
85
134
 
86
- data_buf = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
87
- sig_buf = FFI::MemoryPointer.new(:uchar, signature.bytesize).put_bytes(0, signature)
88
- pub_buf = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key)
135
+ sig, siglen = FFI::MemoryPointer.new(:uchar, 72), FFI::MemoryPointer.new(:int).write_int(72)
89
136
 
90
- result = secp256k1_ecdsa_verify(data_buf, sig_buf, sig_buf.size, pub_buf, pub_buf.size)
137
+ while true do
138
+ break if secp256k1_ecdsa_sign(context, msg32, sig, siglen, seckey, nil, nil)
139
+ end
91
140
 
92
- case result
93
- when 0; false
94
- when 1; true
95
- when -1; raise "error invalid pubkey"
96
- when -2; raise "error invalid signature"
97
- else ; raise "error invalid result"
141
+ sig.read_string(siglen.read_int)
142
+ end
143
+ end
144
+
145
+ def self.verify(data, signature, pub_key)
146
+ with_context do |context|
147
+ data_buf = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
148
+ sig_buf = FFI::MemoryPointer.new(:uchar, signature.bytesize).put_bytes(0, signature)
149
+ pub_buf = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key)
150
+
151
+ result = secp256k1_ecdsa_verify(context, data_buf, sig_buf, sig_buf.size, pub_buf, pub_buf.size)
152
+
153
+ case result
154
+ when 0; false
155
+ when 1; true
156
+ when -1; raise "error invalid pubkey"
157
+ when -2; raise "error invalid signature"
158
+ else ; raise "error invalid result"
159
+ end
98
160
  end
99
161
  end
100
162
 
101
163
  def self.sign_compact(message, priv_key, compressed=true)
102
- init
164
+ with_context do |context|
165
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message)
166
+ sig64 = FFI::MemoryPointer.new(:uchar, 64)
167
+ rec_id = FFI::MemoryPointer.new(:int)
103
168
 
104
- msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message)
105
- sig64 = FFI::MemoryPointer.new(:uchar, 64)
106
- rec_id = FFI::MemoryPointer.new(:int)
169
+ seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
170
+ raise "priv_key invalid" unless secp256k1_ec_seckey_verify(context, seckey)
107
171
 
108
- seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
109
- raise "priv_key invalid" unless secp256k1_ec_seckey_verify(seckey)
172
+ while true do
173
+ break if secp256k1_ecdsa_sign_compact(context, msg32, sig64, seckey, nil, nil, rec_id)
174
+ end
110
175
 
111
- while true do
112
- break if secp256k1_ecdsa_sign_compact(msg32, sig64, seckey, nil, nil, rec_id)
176
+ header = [27 + rec_id.read_int + (compressed ? 4 : 0)].pack("C")
177
+ [ header, sig64.read_string(64) ].join
113
178
  end
114
-
115
- header = [27 + rec_id.read_int + (compressed ? 4 : 0)].pack("C")
116
- [ header, sig64.read_string(64) ].join
117
179
  end
118
180
 
119
181
  def self.recover_compact(message, signature)
120
- init
182
+ with_context do |context|
183
+ return nil if signature.bytesize != 65
121
184
 
122
- return nil if signature.bytesize != 65
185
+ version = signature.unpack('C')[0]
186
+ return nil if version < 27 || version > 34
123
187
 
124
- version = signature.unpack('C')[0]
125
- return nil if version < 27 || version > 34
188
+ compressed = version >= 31 ? true : false
189
+ version -= 4 if compressed
126
190
 
127
- compressed = version >= 31 ? true : false
128
- version -= 4 if compressed
191
+ recid = version - 27
192
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message)
193
+ sig64 = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, signature[1..-1])
194
+ pubkey = FFI::MemoryPointer.new(:uchar, pub_key_len = compressed ? 33 : 65)
195
+ pubkeylen = FFI::MemoryPointer.new(:int).write_int(pub_key_len)
129
196
 
130
- recid = version - 27
131
- msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message)
132
- sig64 = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, signature[1..-1])
133
- pubkey = FFI::MemoryPointer.new(:uchar, pub_key_len = compressed ? 33 : 65)
134
- pubkeylen = FFI::MemoryPointer.new(:int).write_int(pub_key_len)
197
+ result = secp256k1_ecdsa_recover_compact(context, msg32, sig64, pubkey, pubkeylen, (compressed ? 1 : 0), recid)
135
198
 
136
- result = secp256k1_ecdsa_recover_compact(msg32, sig64, pubkey, pubkeylen, (compressed ? 1 : 0), recid)
199
+ return nil unless result
137
200
 
138
- return nil unless result
139
-
140
- pubkey.read_bytes(pubkeylen.read_int)
201
+ pubkey.read_bytes(pubkeylen.read_int)
202
+ end
141
203
  end
142
204
 
143
205
  end