bitcoin-ruby 0.0.11 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/bitcoin/key.rb +6 -0
- data/lib/bitcoin/protocol/tx.rb +95 -39
- data/lib/bitcoin/script.rb +41 -6
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/fixtures/script_tests.json +1947 -0
- data/spec/bitcoin/fixtures/sighash.json +1004 -0
- data/spec/bitcoin/key_spec.rb +7 -0
- data/spec/bitcoin/protocol/tx_spec.rb +175 -46
- metadata +7 -3
data/spec/bitcoin/key_spec.rb
CHANGED
@@ -252,6 +252,13 @@ describe "Bitcoin::Key" do
|
|
252
252
|
k.to_base58.should == "5JBAonQ4iGKFJxENExZghDtAS6YB8BsCw5mwpHSvZvP3Q2UxmT1"
|
253
253
|
end
|
254
254
|
|
255
|
+
it "should raise error for private key out of range." do
|
256
|
+
proc{Bitcoin::Key.new('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141')}.should.raise(Exception)
|
257
|
+
proc{Bitcoin::Key.new('00')}.should.raise(Exception)
|
258
|
+
proc{Bitcoin::Key.new('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140')}.should.not.raise(Exception)
|
259
|
+
proc{Bitcoin::Key.new('01')}.should.not.raise(Exception)
|
260
|
+
end
|
261
|
+
|
255
262
|
end
|
256
263
|
|
257
264
|
begin
|
@@ -30,6 +30,9 @@ describe 'Tx' do
|
|
30
30
|
proc{
|
31
31
|
Tx.new( @payload[0][0..20] )
|
32
32
|
}.should.raise Exception
|
33
|
+
|
34
|
+
# Deserializing a new, empty transaction works
|
35
|
+
Tx.new(Tx.new.to_payload)
|
33
36
|
end
|
34
37
|
|
35
38
|
it '#parse_data' do
|
@@ -38,18 +41,22 @@ describe 'Tx' do
|
|
38
41
|
tx.hash.should == nil
|
39
42
|
tx.parse_data( @payload[0] ).should == true
|
40
43
|
tx.hash.size.should == 64
|
44
|
+
tx.payload.should == @payload[0]
|
41
45
|
|
42
46
|
tx = Tx.new( nil )
|
43
47
|
tx.parse_data( @payload[0] + "AAAA" ).should == "AAAA"
|
44
48
|
tx.hash.size.should == 64
|
49
|
+
tx.payload.should == @payload[0]
|
45
50
|
end
|
46
51
|
|
47
52
|
it '#parse_witness_data' do
|
48
53
|
tx = Tx.new( @payload[3] )
|
49
54
|
tx.hash.size.should == 64
|
55
|
+
tx.payload.should == @payload[3]
|
50
56
|
|
51
57
|
tx = Tx.new( @payload[3] + "AAAA" )
|
52
58
|
tx.hash.size.should == 64
|
59
|
+
tx.payload.should == @payload[3]
|
53
60
|
end
|
54
61
|
|
55
62
|
it '#hash' do
|
@@ -110,6 +117,7 @@ describe 'Tx' do
|
|
110
117
|
it 'Tx.from_hash' do
|
111
118
|
orig_tx = Tx.new( @payload[0] )
|
112
119
|
tx = Tx.from_hash( orig_tx.to_hash )
|
120
|
+
tx.payload.should == @payload[0]
|
113
121
|
tx.to_payload.size.should == @payload[0].size
|
114
122
|
tx.to_payload.should == @payload[0]
|
115
123
|
tx.to_hash.should == orig_tx.to_hash
|
@@ -122,6 +130,7 @@ describe 'Tx' do
|
|
122
130
|
# witness tx(P2WPKH)
|
123
131
|
orig_tx = Tx.new( @payload[3] )
|
124
132
|
tx = Tx.from_hash( orig_tx.to_hash )
|
133
|
+
tx.payload.should == @payload[3]
|
125
134
|
tx.to_witness_payload.size.should == @payload[3].size
|
126
135
|
tx.to_witness_payload.should == @payload[3]
|
127
136
|
tx.to_hash == orig_tx.to_hash
|
@@ -463,64 +472,80 @@ describe 'Tx' do
|
|
463
472
|
tx.verify_witness_input_signature(0, 'a9149993a429037b5d912407a71c252019287b8d27a587'.htb, 987654321).should == true
|
464
473
|
end
|
465
474
|
|
466
|
-
|
467
|
-
|
468
|
-
|
475
|
+
describe '#signature_hash_for_input' do
|
476
|
+
it 'sighash_all' do
|
477
|
+
prev_tx = Tx.new( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin') )
|
478
|
+
prev_tx.hash.should == "2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a"
|
469
479
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
480
|
+
key = Bitcoin.open_key("56e28a425a7b588973b5db962a09b1aca7bdc4a7268cdd671d03c52a997255dc",
|
481
|
+
pubkey="04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3")
|
482
|
+
new_tx = Tx.new(nil)
|
483
|
+
new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
|
484
|
+
new_tx.add_out( TxOut.value_to_address(1000000, "1BVJWLTCtjA8wRivvrCiwjNdL6KjdMUCTZ") )
|
485
|
+
signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
|
486
|
+
sig = Bitcoin.sign_data(key, signature_hash)
|
487
|
+
new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
|
478
488
|
|
479
|
-
|
480
|
-
|
481
|
-
|
489
|
+
new_tx = Tx.new( new_tx.to_payload )
|
490
|
+
new_tx.hash.should != nil
|
491
|
+
new_tx.verify_input_signature(0, prev_tx).should == true
|
482
492
|
|
483
493
|
|
484
494
|
|
485
|
-
|
486
|
-
|
495
|
+
prev_tx = Tx.new( fixtures_file('rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin') )
|
496
|
+
prev_tx.hash.should == "14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984"
|
487
497
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
498
|
+
key = Bitcoin.open_key("115ceda6c1e02d41ce65c35a30e82fb325fe3f815898a09e1a5d28bb1cc92c6e",
|
499
|
+
pubkey="0409d103127d26ce93ee41f1b9b1ed4c1c243acf48e31eb5c4d88ad0342ccc010a1a8d838846cf7337f2b44bc73986c0a3cb0568fa93d068b2c8296ce8d47b1545")
|
500
|
+
new_tx = Tx.new(nil)
|
501
|
+
new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
|
502
|
+
pk_script = Bitcoin::Script.to_address_script("1FEYAh1x5jeKQMPPuv3bKnKvbgVAqXvqjW")
|
503
|
+
new_tx.add_out( TxOut.new(1000000, pk_script) )
|
504
|
+
signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
|
505
|
+
sig = Bitcoin.sign_data(key, signature_hash)
|
506
|
+
new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
|
497
507
|
|
498
|
-
|
499
|
-
|
500
|
-
|
508
|
+
new_tx = Tx.new( new_tx.to_payload )
|
509
|
+
new_tx.hash.should != nil
|
510
|
+
new_tx.verify_input_signature(0, prev_tx).should == true
|
501
511
|
|
502
512
|
|
503
513
|
|
504
|
-
|
505
|
-
|
514
|
+
prev_tx = Tx.new( fixtures_file('rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin') )
|
515
|
+
prev_tx.hash.should == "b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d"
|
506
516
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
517
|
+
key = Bitcoin.open_key("56e28a425a7b588973b5db962a09b1aca7bdc4a7268cdd671d03c52a997255dc",
|
518
|
+
pubkey="04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3")
|
519
|
+
new_tx = Tx.new(nil)
|
520
|
+
new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
|
521
|
+
new_tx.add_out( TxOut.value_to_address(1000000, "14yz7fob6Q16hZu4nXfmv1kRJpSYaFtet5") )
|
522
|
+
signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
|
523
|
+
sig = Bitcoin.sign_data(key, signature_hash)
|
524
|
+
new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
|
515
525
|
|
516
|
-
|
517
|
-
|
518
|
-
|
526
|
+
new_tx = Tx.new( new_tx.to_payload )
|
527
|
+
new_tx.hash.should != nil
|
528
|
+
new_tx.verify_input_signature(0, prev_tx).should == true
|
529
|
+
end
|
519
530
|
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
531
|
+
it 'sighash JSON tests' do
|
532
|
+
test_cases = JSON.parse(fixtures_file('sighash.json'))
|
533
|
+
test_cases.each do |test_case|
|
534
|
+
# Single element arrays in tests are comments.
|
535
|
+
next if test_case.length == 1
|
536
|
+
|
537
|
+
transaction = Bitcoin::Protocol::Tx.new(test_case[0].htb)
|
538
|
+
subscript = test_case[1].htb
|
539
|
+
input_index = test_case[2].to_i
|
540
|
+
hash_type = test_case[3]
|
541
|
+
amount = 0
|
542
|
+
expected_sighash = test_case[4].htb_reverse
|
543
|
+
|
544
|
+
actual_sighash = transaction.signature_hash_for_input(
|
545
|
+
input_index, subscript, hash_type, amount, 0)
|
546
|
+
actual_sighash.should == expected_sighash
|
547
|
+
end
|
548
|
+
end
|
524
549
|
end
|
525
550
|
|
526
551
|
it '#signature_hash_for_witness_input' do
|
@@ -758,5 +783,109 @@ describe 'Tx' do
|
|
758
783
|
tx.out[1].pk_script.bth.should == 'bbbbbbbb'
|
759
784
|
tx.out[2].pk_script.bth.should == 'cccccccc'
|
760
785
|
end
|
761
|
-
|
786
|
+
|
787
|
+
describe 'verify_input_signature' do
|
788
|
+
def parse_script(script_str)
|
789
|
+
script = Bitcoin::Script.new('')
|
790
|
+
|
791
|
+
buf = ""
|
792
|
+
script_str.split.each do |token|
|
793
|
+
opcode = Bitcoin::Script::OPCODES_PARSE_STRING[token] ||
|
794
|
+
Bitcoin::Script::OPCODES_PARSE_STRING['OP_' + token]
|
795
|
+
if opcode
|
796
|
+
buf << [opcode].pack('C')
|
797
|
+
next
|
798
|
+
end
|
799
|
+
|
800
|
+
data =
|
801
|
+
case token
|
802
|
+
when /\A-?\d+\z/
|
803
|
+
i = token.to_i
|
804
|
+
opcode =
|
805
|
+
case i
|
806
|
+
when -1 then Bitcoin::Script::OP_1NEGATE
|
807
|
+
when 0 then Bitcoin::Script::OP_0
|
808
|
+
when 1 then Bitcoin::Script::OP_1
|
809
|
+
when 2..16 then Bitcoin::Script::OP_2 + i - 2
|
810
|
+
end
|
811
|
+
|
812
|
+
if opcode
|
813
|
+
[opcode].pack('C')
|
814
|
+
else
|
815
|
+
Bitcoin::Script.pack_pushdata(script.cast_to_string(i))
|
816
|
+
end
|
817
|
+
when /\A'(.*)'\z/ then Bitcoin::Script.pack_pushdata($1)
|
818
|
+
when /\A0x([0-9a-fA-F]+)\z/ then $1.htb
|
819
|
+
else raise "Unexpected token #{token}"
|
820
|
+
end
|
821
|
+
buf << data
|
822
|
+
end
|
823
|
+
buf
|
824
|
+
end
|
825
|
+
|
826
|
+
def parse_flags(flags_str)
|
827
|
+
flags_str.split(',').each_with_object({}) do |flag_str, opts|
|
828
|
+
case flag_str.to_sym
|
829
|
+
when :STRICTENC then opts[:verify_strictenc] = true
|
830
|
+
when :DERSIG then opts[:verify_dersig] = true
|
831
|
+
when :LOW_S then opts[:verify_low_s] = true
|
832
|
+
when :SIGPUSHONLY then opts[:verify_sigpushonly] = true
|
833
|
+
when :MINIMALDATA then opts[:verify_minimaldata] = true
|
834
|
+
when :CLEANSTACK then opts[:verify_cleanstack] = true
|
835
|
+
when :SIGHASH_FORKID then opts[:fork_id] = 0
|
836
|
+
end
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
it 'script JSON tests' do
|
841
|
+
test_cases = JSON.parse(fixtures_file('script_tests.json'))
|
842
|
+
test_cases.each_with_index do |test_case, i|
|
843
|
+
# Single element arrays in tests are comments.
|
844
|
+
next if test_case.length == 1
|
845
|
+
|
846
|
+
value =
|
847
|
+
if test_case[0].is_a?(Array)
|
848
|
+
(test_case.shift[0] * 10**8).to_i
|
849
|
+
else
|
850
|
+
0
|
851
|
+
end
|
852
|
+
|
853
|
+
# TODO: Implement these opcodes correctly
|
854
|
+
next if test_case[0].match(/CHECKLOCKTIMEVERIFY|CHECKSEQUENCEVERIFY|RESERVED|0x50|VERIF|VERNOTIF/)
|
855
|
+
next if test_case[1].match(/CHECKLOCKTIMEVERIFY|CHECKSEQUENCEVERIFY|RESERVED|0x50|VERIF|VERNOTIF/)
|
856
|
+
|
857
|
+
script_sig = parse_script(test_case[0])
|
858
|
+
script_pubkey = parse_script(test_case[1])
|
859
|
+
opts = parse_flags(test_case[2])
|
860
|
+
expect_success = test_case[3] == 'OK'
|
861
|
+
|
862
|
+
# A lot of the test cases are failing, so for now we only test the SIGHASH_FORKID ones.
|
863
|
+
# TODO: Get this spec passing without this line.
|
864
|
+
next unless opts[:fork_id]
|
865
|
+
|
866
|
+
crediting_tx = Tx.new
|
867
|
+
crediting_tx.add_in(TxIn.new)
|
868
|
+
crediting_tx.in[0].prev_out_hash = TxIn::NULL_HASH
|
869
|
+
crediting_tx.in[0].prev_out_index = TxIn::COINBASE_INDEX
|
870
|
+
crediting_tx.in[0].script_sig = parse_script('0 0')
|
871
|
+
crediting_tx.add_out(TxOut.new)
|
872
|
+
crediting_tx.out[0].value = value
|
873
|
+
crediting_tx.out[0].pk_script = script_pubkey
|
874
|
+
crediting_tx.refresh_hash
|
875
|
+
|
876
|
+
spending_tx = Tx.new
|
877
|
+
spending_tx.add_in(TxIn.new)
|
878
|
+
spending_tx.in[0].prev_out_hash = crediting_tx.binary_hash
|
879
|
+
spending_tx.in[0].prev_out_index = 0
|
880
|
+
spending_tx.in[0].script_sig = script_sig
|
881
|
+
spending_tx.add_out(TxOut.new)
|
882
|
+
spending_tx.out[0].value = value
|
883
|
+
spending_tx.out[0].pk_script = ''
|
884
|
+
spending_tx.refresh_hash
|
885
|
+
|
886
|
+
success = spending_tx.verify_input_signature(0, crediting_tx, Time.now.to_i, opts)
|
887
|
+
success.should == expect_success
|
888
|
+
end
|
889
|
+
end
|
890
|
+
end
|
762
891
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitcoin-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- lian
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: This is a ruby library for interacting with the bitcoin protocol/network
|
14
14
|
email:
|
@@ -155,6 +155,8 @@ files:
|
|
155
155
|
- spec/bitcoin/fixtures/reorg/blk_3A.dat
|
156
156
|
- spec/bitcoin/fixtures/reorg/blk_4A.dat
|
157
157
|
- spec/bitcoin/fixtures/reorg/blk_5A.dat
|
158
|
+
- spec/bitcoin/fixtures/script_tests.json
|
159
|
+
- spec/bitcoin/fixtures/sighash.json
|
158
160
|
- spec/bitcoin/fixtures/testnet/block_0.bin
|
159
161
|
- spec/bitcoin/fixtures/testnet/block_1.bin
|
160
162
|
- spec/bitcoin/fixtures/testnet/block_2.bin
|
@@ -259,7 +261,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
259
261
|
version: 1.3.6
|
260
262
|
requirements: []
|
261
263
|
rubyforge_project: bitcoin-ruby
|
262
|
-
rubygems_version: 2.
|
264
|
+
rubygems_version: 2.5.2
|
263
265
|
signing_key:
|
264
266
|
specification_version: 4
|
265
267
|
summary: bitcoin utils and protocol in ruby
|
@@ -356,6 +358,8 @@ test_files:
|
|
356
358
|
- spec/bitcoin/fixtures/reorg/blk_3A.dat
|
357
359
|
- spec/bitcoin/fixtures/reorg/blk_4A.dat
|
358
360
|
- spec/bitcoin/fixtures/reorg/blk_5A.dat
|
361
|
+
- spec/bitcoin/fixtures/script_tests.json
|
362
|
+
- spec/bitcoin/fixtures/sighash.json
|
359
363
|
- spec/bitcoin/fixtures/testnet/block_0.bin
|
360
364
|
- spec/bitcoin/fixtures/testnet/block_1.bin
|
361
365
|
- spec/bitcoin/fixtures/testnet/block_2.bin
|