bitcoin-ruby 0.0.11 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|