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 +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +7 -7
- data/README.rdoc +1 -1
- data/examples/simple_network_monitor_and_util.rb +8 -0
- data/lib/bitcoin.rb +57 -17
- data/lib/bitcoin/builder.rb +15 -10
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +3 -2
- data/lib/bitcoin/ffi/secp256k1.rb +143 -81
- data/lib/bitcoin/key.rb +4 -6
- data/lib/bitcoin/logger.rb +11 -0
- data/lib/bitcoin/protocol.rb +19 -6
- data/lib/bitcoin/protocol/aux_pow.rb +62 -62
- data/lib/bitcoin/protocol/block.rb +20 -15
- data/lib/bitcoin/protocol/handler.rb +4 -0
- data/lib/bitcoin/protocol/parser.rb +30 -29
- data/lib/bitcoin/protocol/reject.rb +38 -0
- data/lib/bitcoin/protocol/tx.rb +19 -3
- data/lib/bitcoin/protocol/txin.rb +14 -8
- data/lib/bitcoin/script.rb +6 -1
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +4 -0
- data/spec/bitcoin/builder_spec.rb +11 -0
- data/spec/bitcoin/dogecoin_spec.rb +6 -6
- data/spec/bitcoin/protocol/addr_spec.rb +11 -1
- data/spec/bitcoin/protocol/aux_pow_spec.rb +9 -9
- data/spec/bitcoin/protocol/block_spec.rb +1 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -2
- data/spec/bitcoin/protocol/parser_spec.rb +50 -0
- data/spec/bitcoin/protocol/reject.rb +17 -0
- data/spec/bitcoin/protocol/tx_spec.rb +5 -1
- data/spec/bitcoin/script/script_spec.rb +1 -1
- data/spec/bitcoin/secp256k1_spec.rb +8 -0
- data/spec/bitcoin/spec_helper.rb +6 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db1b92d6514cc112f7141841b658fe16fc23cdad
|
4
|
+
data.tar.gz: 2a4a5e0e7215cc7739d8208322ac361ef1beb988
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63c618a352af13e722f280e84ef6801ded898c65db7f346eb410fa417c310c89c643d74e66474a975281326bae0d9e4b70316a6c2fb4b4e9a8f54c8462ce241c
|
7
|
+
data.tar.gz: 421b29bcb069e3f6de8d3c8484aece5d6bd09a9f0695866f47baa6308e02ef6fa5b267a350aea9741aaea19b5395fce35456111e24f787ec6819498df199b02a
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
bitcoin-ruby (0.0.
|
4
|
+
bitcoin-ruby (0.0.8)
|
5
5
|
|
6
6
|
GEM
|
7
|
-
remote:
|
7
|
+
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
bacon (1.2.0)
|
10
|
-
eventmachine (1.0.
|
11
|
-
ffi (1.9.
|
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.
|
15
|
+
minitest (5.8.2)
|
16
16
|
rake (10.4.2)
|
17
|
-
scrypt (2.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.
|
34
|
+
1.10.6
|
data/README.rdoc
CHANGED
@@ -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
|
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
|
|
data/lib/bitcoin.rb
CHANGED
@@ -276,12 +276,23 @@ module Bitcoin
|
|
276
276
|
end
|
277
277
|
|
278
278
|
def sign_data(key, data)
|
279
|
-
sig =
|
280
|
-
|
281
|
-
sig
|
282
|
-
|
283
|
-
|
284
|
-
|
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
|
-
|
314
|
-
|
315
|
-
|
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:
|
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: "
|
837
|
-
|
838
|
-
|
839
|
-
|
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
|
data/lib/bitcoin/builder.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
187
|
-
|
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
|
-
#
|
6
|
-
#
|
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
|
-
#
|
4
|
-
#
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
#
|
26
|
-
attach_function :
|
24
|
+
# secp256k1_context_t* secp256k1_context_create(int flags)
|
25
|
+
attach_function :secp256k1_context_create, [:int], :pointer
|
27
26
|
|
28
|
-
#
|
29
|
-
attach_function :
|
27
|
+
# secp256k1_context_t* secp256k1_context_clone(const secp256k1_context_t* ctx)
|
28
|
+
attach_function :secp256k1_context_clone, [:pointer], :pointer
|
30
29
|
|
31
|
-
#
|
32
|
-
attach_function :
|
30
|
+
# void secp256k1_context_destroy(secp256k1_context_t* ctx)
|
31
|
+
attach_function :secp256k1_context_destroy, [:pointer], :void
|
33
32
|
|
34
|
-
# int
|
35
|
-
attach_function :
|
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 @
|
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.
|
87
|
+
def self.with_context(flags=nil, seed=nil)
|
51
88
|
init
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
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.
|
68
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
84
|
-
|
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
|
-
|
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
|
-
|
137
|
+
while true do
|
138
|
+
break if secp256k1_ecdsa_sign(context, msg32, sig, siglen, seckey, nil, nil)
|
139
|
+
end
|
91
140
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
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
|
-
|
105
|
-
|
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
|
-
|
109
|
-
|
172
|
+
while true do
|
173
|
+
break if secp256k1_ecdsa_sign_compact(context, msg32, sig64, seckey, nil, nil, rec_id)
|
174
|
+
end
|
110
175
|
|
111
|
-
|
112
|
-
|
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
|
-
|
182
|
+
with_context do |context|
|
183
|
+
return nil if signature.bytesize != 65
|
121
184
|
|
122
|
-
|
185
|
+
version = signature.unpack('C')[0]
|
186
|
+
return nil if version < 27 || version > 34
|
123
187
|
|
124
|
-
|
125
|
-
|
188
|
+
compressed = version >= 31 ? true : false
|
189
|
+
version -= 4 if compressed
|
126
190
|
|
127
|
-
|
128
|
-
|
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
|
-
|
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
|
-
|
199
|
+
return nil unless result
|
137
200
|
|
138
|
-
|
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
|