spec/bitcoin/protocol/tx_spec.rb in bitcoin-ruby-0.0.11 vs spec/bitcoin/protocol/tx_spec.rb in bitcoin-ruby-0.0.12
- old
+ new
@@ -28,30 +28,37 @@
}.should.not.raise Exception
proc{
Tx.new( @payload[0][0..20] )
}.should.raise Exception
+
+ # Deserializing a new, empty transaction works
+ Tx.new(Tx.new.to_payload)
end
it '#parse_data' do
tx = Tx.new( nil )
tx.hash.should == nil
tx.parse_data( @payload[0] ).should == true
tx.hash.size.should == 64
+ tx.payload.should == @payload[0]
tx = Tx.new( nil )
tx.parse_data( @payload[0] + "AAAA" ).should == "AAAA"
tx.hash.size.should == 64
+ tx.payload.should == @payload[0]
end
it '#parse_witness_data' do
tx = Tx.new( @payload[3] )
tx.hash.size.should == 64
+ tx.payload.should == @payload[3]
tx = Tx.new( @payload[3] + "AAAA" )
tx.hash.size.should == 64
+ tx.payload.should == @payload[3]
end
it '#hash' do
tx = Tx.new( @payload[0] )
tx.hash.size.should == 64
@@ -108,10 +115,11 @@
end
it 'Tx.from_hash' do
orig_tx = Tx.new( @payload[0] )
tx = Tx.from_hash( orig_tx.to_hash )
+ tx.payload.should == @payload[0]
tx.to_payload.size.should == @payload[0].size
tx.to_payload.should == @payload[0]
tx.to_hash.should == orig_tx.to_hash
Tx.binary_from_hash( orig_tx.to_hash ).should == @payload[0]
@@ -120,10 +128,11 @@
.message.should == "Tx hash mismatch! Claimed: 6e9dd16625b62cfcd4bf02edb89ca1f5a8c30c4b1601507090fb28e59f2d02b4, Actual: 395cd28c334ac84ed125ec5ccd5bc29eadcc96b79c337d0a87a19df64ea3b548"
# witness tx(P2WPKH)
orig_tx = Tx.new( @payload[3] )
tx = Tx.from_hash( orig_tx.to_hash )
+ tx.payload.should == @payload[3]
tx.to_witness_payload.size.should == @payload[3].size
tx.to_witness_payload.should == @payload[3]
tx.to_hash == orig_tx.to_hash
end
@@ -461,68 +470,84 @@
# P2SH-P2WSH
tx = Bitcoin::P::Tx.new('0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000'.htb)
tx.verify_witness_input_signature(0, 'a9149993a429037b5d912407a71c252019287b8d27a587'.htb, 987654321).should == true
end
- it '#sign_input_signature' do
- prev_tx = Tx.new( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin') )
- prev_tx.hash.should == "2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a"
+ describe '#signature_hash_for_input' do
+ it 'sighash_all' do
+ prev_tx = Tx.new( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin') )
+ prev_tx.hash.should == "2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a"
- key = Bitcoin.open_key("56e28a425a7b588973b5db962a09b1aca7bdc4a7268cdd671d03c52a997255dc",
- pubkey="04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3")
- new_tx = Tx.new(nil)
- new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
- new_tx.add_out( TxOut.value_to_address(1000000, "1BVJWLTCtjA8wRivvrCiwjNdL6KjdMUCTZ") )
- signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
- sig = Bitcoin.sign_data(key, signature_hash)
- new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
+ key = Bitcoin.open_key("56e28a425a7b588973b5db962a09b1aca7bdc4a7268cdd671d03c52a997255dc",
+ pubkey="04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3")
+ new_tx = Tx.new(nil)
+ new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
+ new_tx.add_out( TxOut.value_to_address(1000000, "1BVJWLTCtjA8wRivvrCiwjNdL6KjdMUCTZ") )
+ signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
+ sig = Bitcoin.sign_data(key, signature_hash)
+ new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
- new_tx = Tx.new( new_tx.to_payload )
- new_tx.hash.should != nil
- new_tx.verify_input_signature(0, prev_tx).should == true
+ new_tx = Tx.new( new_tx.to_payload )
+ new_tx.hash.should != nil
+ new_tx.verify_input_signature(0, prev_tx).should == true
- prev_tx = Tx.new( fixtures_file('rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin') )
- prev_tx.hash.should == "14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984"
+ prev_tx = Tx.new( fixtures_file('rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin') )
+ prev_tx.hash.should == "14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984"
- key = Bitcoin.open_key("115ceda6c1e02d41ce65c35a30e82fb325fe3f815898a09e1a5d28bb1cc92c6e",
- pubkey="0409d103127d26ce93ee41f1b9b1ed4c1c243acf48e31eb5c4d88ad0342ccc010a1a8d838846cf7337f2b44bc73986c0a3cb0568fa93d068b2c8296ce8d47b1545")
- new_tx = Tx.new(nil)
- new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
- pk_script = Bitcoin::Script.to_address_script("1FEYAh1x5jeKQMPPuv3bKnKvbgVAqXvqjW")
- new_tx.add_out( TxOut.new(1000000, pk_script) )
- signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
- sig = Bitcoin.sign_data(key, signature_hash)
- new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
+ key = Bitcoin.open_key("115ceda6c1e02d41ce65c35a30e82fb325fe3f815898a09e1a5d28bb1cc92c6e",
+ pubkey="0409d103127d26ce93ee41f1b9b1ed4c1c243acf48e31eb5c4d88ad0342ccc010a1a8d838846cf7337f2b44bc73986c0a3cb0568fa93d068b2c8296ce8d47b1545")
+ new_tx = Tx.new(nil)
+ new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
+ pk_script = Bitcoin::Script.to_address_script("1FEYAh1x5jeKQMPPuv3bKnKvbgVAqXvqjW")
+ new_tx.add_out( TxOut.new(1000000, pk_script) )
+ signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
+ sig = Bitcoin.sign_data(key, signature_hash)
+ new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
- new_tx = Tx.new( new_tx.to_payload )
- new_tx.hash.should != nil
- new_tx.verify_input_signature(0, prev_tx).should == true
+ new_tx = Tx.new( new_tx.to_payload )
+ new_tx.hash.should != nil
+ new_tx.verify_input_signature(0, prev_tx).should == true
- prev_tx = Tx.new( fixtures_file('rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin') )
- prev_tx.hash.should == "b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d"
+ prev_tx = Tx.new( fixtures_file('rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin') )
+ prev_tx.hash.should == "b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d"
- key = Bitcoin.open_key("56e28a425a7b588973b5db962a09b1aca7bdc4a7268cdd671d03c52a997255dc",
- pubkey="04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3")
- new_tx = Tx.new(nil)
- new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
- new_tx.add_out( TxOut.value_to_address(1000000, "14yz7fob6Q16hZu4nXfmv1kRJpSYaFtet5") )
- signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
- sig = Bitcoin.sign_data(key, signature_hash)
- new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
+ key = Bitcoin.open_key("56e28a425a7b588973b5db962a09b1aca7bdc4a7268cdd671d03c52a997255dc",
+ pubkey="04324c6ebdcf079db6c9209a6b715b955622561262cde13a8a1df8ae0ef030eaa1552e31f8be90c385e27883a9d82780283d19507d7fa2e1e71a1d11bc3a52caf3")
+ new_tx = Tx.new(nil)
+ new_tx.add_in( TxIn.new(prev_tx.binary_hash, 0, 0) )
+ new_tx.add_out( TxOut.value_to_address(1000000, "14yz7fob6Q16hZu4nXfmv1kRJpSYaFtet5") )
+ signature_hash = new_tx.signature_hash_for_input(0, prev_tx)
+ sig = Bitcoin.sign_data(key, signature_hash)
+ new_tx.in[0].script_sig = Bitcoin::Script.to_pubkey_script_sig(sig, [pubkey].pack("H*"))
- new_tx = Tx.new( new_tx.to_payload )
- new_tx.hash.should != nil
- new_tx.verify_input_signature(0, prev_tx).should == true
+ new_tx = Tx.new( new_tx.to_payload )
+ new_tx.hash.should != nil
+ new_tx.verify_input_signature(0, prev_tx).should == true
+ end
- #File.open("rawtx-#{new_tx.hash}.bin",'wb'){|f| f.print new_tx.to_payload }
- prev_tx = Tx.new( fixtures_file('rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin') )
- prev_tx.hash.should == "52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2"
- #File.open("rawtx-#{prev_tx.hash}.json",'wb'){|f| f.print prev_tx.to_json }
+ it 'sighash JSON tests' do
+ test_cases = JSON.parse(fixtures_file('sighash.json'))
+ test_cases.each do |test_case|
+ # Single element arrays in tests are comments.
+ next if test_case.length == 1
+
+ transaction = Bitcoin::Protocol::Tx.new(test_case[0].htb)
+ subscript = test_case[1].htb
+ input_index = test_case[2].to_i
+ hash_type = test_case[3]
+ amount = 0
+ expected_sighash = test_case[4].htb_reverse
+
+ actual_sighash = transaction.signature_hash_for_input(
+ input_index, subscript, hash_type, amount, 0)
+ actual_sighash.should == expected_sighash
+ end
+ end
end
it '#signature_hash_for_witness_input' do
# P2WPKH https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#Native_P2WPKH
tx = Tx.new('0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000'.htb)
@@ -756,7 +781,111 @@
tx.lexicographical_sort!
tx.out[0].pk_script.bth.should == 'aaaaaaaa'
tx.out[1].pk_script.bth.should == 'bbbbbbbb'
tx.out[2].pk_script.bth.should == 'cccccccc'
end
-
+
+ describe 'verify_input_signature' do
+ def parse_script(script_str)
+ script = Bitcoin::Script.new('')
+
+ buf = ""
+ script_str.split.each do |token|
+ opcode = Bitcoin::Script::OPCODES_PARSE_STRING[token] ||
+ Bitcoin::Script::OPCODES_PARSE_STRING['OP_' + token]
+ if opcode
+ buf << [opcode].pack('C')
+ next
+ end
+
+ data =
+ case token
+ when /\A-?\d+\z/
+ i = token.to_i
+ opcode =
+ case i
+ when -1 then Bitcoin::Script::OP_1NEGATE
+ when 0 then Bitcoin::Script::OP_0
+ when 1 then Bitcoin::Script::OP_1
+ when 2..16 then Bitcoin::Script::OP_2 + i - 2
+ end
+
+ if opcode
+ [opcode].pack('C')
+ else
+ Bitcoin::Script.pack_pushdata(script.cast_to_string(i))
+ end
+ when /\A'(.*)'\z/ then Bitcoin::Script.pack_pushdata($1)
+ when /\A0x([0-9a-fA-F]+)\z/ then $1.htb
+ else raise "Unexpected token #{token}"
+ end
+ buf << data
+ end
+ buf
+ end
+
+ def parse_flags(flags_str)
+ flags_str.split(',').each_with_object({}) do |flag_str, opts|
+ case flag_str.to_sym
+ when :STRICTENC then opts[:verify_strictenc] = true
+ when :DERSIG then opts[:verify_dersig] = true
+ when :LOW_S then opts[:verify_low_s] = true
+ when :SIGPUSHONLY then opts[:verify_sigpushonly] = true
+ when :MINIMALDATA then opts[:verify_minimaldata] = true
+ when :CLEANSTACK then opts[:verify_cleanstack] = true
+ when :SIGHASH_FORKID then opts[:fork_id] = 0
+ end
+ end
+ end
+
+ it 'script JSON tests' do
+ test_cases = JSON.parse(fixtures_file('script_tests.json'))
+ test_cases.each_with_index do |test_case, i|
+ # Single element arrays in tests are comments.
+ next if test_case.length == 1
+
+ value =
+ if test_case[0].is_a?(Array)
+ (test_case.shift[0] * 10**8).to_i
+ else
+ 0
+ end
+
+ # TODO: Implement these opcodes correctly
+ next if test_case[0].match(/CHECKLOCKTIMEVERIFY|CHECKSEQUENCEVERIFY|RESERVED|0x50|VERIF|VERNOTIF/)
+ next if test_case[1].match(/CHECKLOCKTIMEVERIFY|CHECKSEQUENCEVERIFY|RESERVED|0x50|VERIF|VERNOTIF/)
+
+ script_sig = parse_script(test_case[0])
+ script_pubkey = parse_script(test_case[1])
+ opts = parse_flags(test_case[2])
+ expect_success = test_case[3] == 'OK'
+
+ # A lot of the test cases are failing, so for now we only test the SIGHASH_FORKID ones.
+ # TODO: Get this spec passing without this line.
+ next unless opts[:fork_id]
+
+ crediting_tx = Tx.new
+ crediting_tx.add_in(TxIn.new)
+ crediting_tx.in[0].prev_out_hash = TxIn::NULL_HASH
+ crediting_tx.in[0].prev_out_index = TxIn::COINBASE_INDEX
+ crediting_tx.in[0].script_sig = parse_script('0 0')
+ crediting_tx.add_out(TxOut.new)
+ crediting_tx.out[0].value = value
+ crediting_tx.out[0].pk_script = script_pubkey
+ crediting_tx.refresh_hash
+
+ spending_tx = Tx.new
+ spending_tx.add_in(TxIn.new)
+ spending_tx.in[0].prev_out_hash = crediting_tx.binary_hash
+ spending_tx.in[0].prev_out_index = 0
+ spending_tx.in[0].script_sig = script_sig
+ spending_tx.add_out(TxOut.new)
+ spending_tx.out[0].value = value
+ spending_tx.out[0].pk_script = ''
+ spending_tx.refresh_hash
+
+ success = spending_tx.verify_input_signature(0, crediting_tx, Time.now.to_i, opts)
+ success.should == expect_success
+ end
+ end
+ end
end