spec/bitcoin/protocol/tx_spec.rb in bitcoin-ruby-0.0.6 vs spec/bitcoin/protocol/tx_spec.rb in bitcoin-ruby-0.0.7
- old
+ new
@@ -50,22 +50,22 @@
end
it '#normalized_hash' do
tx = Tx.new( @payload[0] )
tx.normalized_hash.size.should == 64
- tx.normalized_hash.should == "402e30100b6937cc13828ca096377c93afc0ff227ad2f249245e5b1db9123a39"
+ tx.normalized_hash.should == "393a12b91d5b5e2449f2d27a22ffc0af937c3796a08c8213cc37690b10302e40"
new_tx = JSON.parse(tx.to_json)
script = Bitcoin::Script.from_string(new_tx['in'][0]['scriptSig'])
script.chunks[0].bitcoin_pushdata = Bitcoin::Script::OP_PUSHDATA2
script.chunks[0].bitcoin_pushdata_length = script.chunks[0].bytesize
new_tx['in'][0]['scriptSig'] = script.to_string
new_tx = Bitcoin::P::Tx.from_hash(new_tx)
new_tx.hash.should != tx.hash
new_tx.normalized_hash.size.should == 64
- new_tx.normalized_hash.should == "402e30100b6937cc13828ca096377c93afc0ff227ad2f249245e5b1db9123a39"
+ new_tx.normalized_hash.should == "393a12b91d5b5e2449f2d27a22ffc0af937c3796a08c8213cc37690b10302e40"
end
it '#to_payload' do
tx = Tx.new( @payload[0] )
tx.to_payload.size.should == @payload[0].size
@@ -122,17 +122,129 @@
tx.hash.should == 'ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5'
# coinbase tx with non-default sequence
tx = Tx.from_json( json=fixtures_file('0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json'))
Tx.new( tx.to_payload ).to_json.should == json
+
+ # toshi format
+ Tx.from_json(fixtures_file('rawtx-02-toshi.json')).to_payload.should == Tx.from_json(fixtures_file('rawtx-02.json')).to_payload
+ Tx.from_json(fixtures_file('rawtx-03-toshi.json')).to_payload.should == Tx.from_json(fixtures_file('rawtx-03.json')).to_payload
+ Tx.from_json(fixtures_file('coinbase-toshi.json')).to_payload.should == Tx.from_json(fixtures_file('coinbase.json')).to_payload
end
it 'Tx.binary_from_json' do
Tx.binary_from_json( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json') ).should ==
fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin')
end
+
+ it 'compares arrays of bytes' do
+ # This function is used in validating an ECDSA signature's S value
+ c1 = []
+ c2 = []
+ Bitcoin::Script::compare_big_endian(c1, c2).should == 0
+
+ c1 = [0]
+ c2 = []
+ Bitcoin::Script::compare_big_endian(c1, c2).should == 0
+
+ c1 = []
+ c2 = [0]
+ Bitcoin::Script::compare_big_endian(c1, c2).should == 0
+
+ c1 = [5]
+ c2 = [5]
+ Bitcoin::Script::compare_big_endian(c1, c2).should == 0
+
+ c1 = [04]
+ c2 = [5]
+ Bitcoin::Script::compare_big_endian(c1, c2).should == -1
+
+ c1 = [4]
+ c2 = [05]
+ Bitcoin::Script::compare_big_endian(c1, c2).should == -1
+
+ c1 = [5]
+ c2 = [4]
+ Bitcoin::Script::compare_big_endian(c1, c2).should == 1
+
+ c1 = [05]
+ c2 = [004]
+ Bitcoin::Script::compare_big_endian(c1, c2).should == 1
+ end
+
+ it 'validates ECDSA signature format' do
+ # TX 3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae
+ sig_orig = ["304502210088984573e3e4f33db7df6aea313f1ce67a3ef3532ea89991494c7f018258371802206ceefc9291450dbd40d834f249658e0f64662d52a41cf14e20c9781144f2fe0701"].pack("H*")
+ Bitcoin::Script::is_der_signature?(sig_orig).should == true
+ Bitcoin::Script::is_defined_hashtype_signature?(sig_orig).should == true
+
+ # Trimmed to be too short
+ sig = sig_orig.slice(0, 8)
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # Zero-padded to be too long
+ sig = String.new(sig_orig)
+ sig << 0x00
+ sig << 0x00
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # Wrong first byte
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[0] = 0x20
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # Length byte broken
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[1] = 0x20
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # Incorrect R value type
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[2] = 0x03
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # R value length infeasibly long
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[3] = sig_orig.size - 4
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # Negative R value
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[4] = 0x80 | sig_bytes[4]
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # R value excessively padded
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[5] = 0x00
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # Incorrect S value type
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[37] = 0x03
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # Zero S length
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[38] = 0x00
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+
+ # Negative S value
+ sig_bytes = sig_orig.unpack("C*")
+ sig_bytes[39] = 0x80 | sig_bytes[39]
+ sig = sig_bytes.pack("C*")
+ Bitcoin::Script::is_der_signature?(sig).should == false
+ end
+
it '#verify_input_signature' do
# transaction-2 of block-170
tx = Tx.new( fixtures_file('rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin') )
tx.hash.should == "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16"
@@ -245,9 +357,40 @@
prev_txs = {}
tx.in.map{|i| i.previous_output }.uniq.each{|i| prev_txs[i] = Bitcoin::P::Tx.from_json(fixtures_file("tx-#{i}.json")) }
tx.in.each.with_index{|i,idx|
tx.verify_input_signature(idx, prev_txs[i.previous_output]).should == true
}
+
+ # BIP62 rule #2 - spend transaction has operations in its signature
+ tx = Tx.new( fixtures_file('rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin') )
+ tx.hash.should == "3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e"
+ outpoint_tx = Tx.new( fixtures_file('rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin') )
+ outpoint_tx.hash.should == "04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4"
+ tx.verify_input_signature(0, outpoint_tx, Time.now.to_i).should == true
+ tx.verify_input_signature(0, outpoint_tx, Time.now.to_i, verify_sigpushonly: true).should == false
+
+ # BIP62 rule #6 - spend transaction has an unused "0" on the signature stack
+ tx = Tx.new( fixtures_file('rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin') )
+ tx.hash.should == "0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f"
+ outpoint_tx = Tx.new( fixtures_file('rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin') )
+ outpoint_tx.hash.should == "f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5"
+ tx.verify_input_signature(0, outpoint_tx, Time.now.to_i).should == true
+ tx.verify_input_signature(0, outpoint_tx, Time.now.to_i, verify_cleanstack: true).should == false
+
+ # Ensure BIP62 is applied to P2SH scripts
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json'))
+ tx.hash.should == "7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d"
+ outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json'))
+ outpoint_tx.hash.should == "3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477"
+ tx.verify_input_signature(0, outpoint_tx).should == true
+ tx.verify_input_signature(0, outpoint_tx, Time.now.to_i, verify_low_s: true).should == false
+
+ # testnet3 P2SH check
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json'))
+ tx.hash.should == "156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a"
+ outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json'))
+ outpoint_tx.hash.should == "8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c"
+ tx.verify_input_signature(0, outpoint_tx).should == true
end
it '#sign_input_signature' do
prev_tx = Tx.new( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin') )
prev_tx.hash.should == "2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a"