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"