spec/bitcoin/protocol/tx_spec.rb in bitcoin-ruby-0.0.5 vs spec/bitcoin/protocol/tx_spec.rb in bitcoin-ruby-0.0.6

- old
+ new

@@ -47,10 +47,27 @@ tx.hash.size.should == 64 tx.hash.should == "6e9dd16625b62cfcd4bf02edb89ca1f5a8c30c4b1601507090fb28e59f2d02b4" tx.binary_hash.should == "\xB4\x02-\x9F\xE5(\xFB\x90pP\x01\x16K\f\xC3\xA8\xF5\xA1\x9C\xB8\xED\x02\xBF\xD4\xFC,\xB6%f\xD1\x9Dn" end + it '#normalized_hash' do + tx = Tx.new( @payload[0] ) + tx.normalized_hash.size.should == 64 + tx.normalized_hash.should == "402e30100b6937cc13828ca096377c93afc0ff227ad2f249245e5b1db9123a39" + + 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" + end + it '#to_payload' do tx = Tx.new( @payload[0] ) tx.to_payload.size.should == @payload[0].size tx.to_payload.should == @payload[0] end @@ -181,10 +198,57 @@ tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-9fb65b7304aaa77ac9580823c2c06b259cc42591e5cce66d76a81b6f51cc5c28.json')) tx.hash.should == "9fb65b7304aaa77ac9580823c2c06b259cc42591e5cce66d76a81b6f51cc5c28" outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-a6ce7081addade7676cd2af75c4129eba6bf5e179a19c40c7d4cf6a5fe595954.json')) outpoint_tx.hash.should == "a6ce7081addade7676cd2af75c4129eba6bf5e179a19c40c7d4cf6a5fe595954" tx.verify_input_signature(0, outpoint_tx).should == true + + # drop OP_CODESEPARATOR in subscript for signature_hash_for_input + tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json')) + tx.hash.should == "46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa" + outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json')) + outpoint_tx.hash.should == "bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224" + tx.verify_input_signature(0, outpoint_tx).should == true + + # drop OP_CODESEPARATOR in subscript for signature_hash_for_input + tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json')) + tx.hash.should == "aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8" + outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json')) + outpoint_tx.hash.should == "326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e" + tx.verify_input_signature(0, outpoint_tx).should == true + + # drop multisig OP_CODESEPARATOR in subscript for signature_hash_for_input + tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json')) + tx.hash.should == "6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190" + outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json')) + outpoint_tx.hash.should == "a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944" + tx.verify_input_signature(0, outpoint_tx).should == true + + # drop multisig OP_CODESEPARATOR in subscript for signature_hash_for_input when used in ScriptSig + tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json')) + tx.hash.should == "eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb" + outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json')) + outpoint_tx.hash.should == "b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d" + tx.verify_input_signature(1, outpoint_tx).should == true + + # OP_DUP OP_HASH160 + tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json')) + tx.hash.should == "5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f" + outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json')) + outpoint_tx.hash.should == "b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9" + tx.verify_input_signature(0, outpoint_tx).should == true + outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json')) + outpoint_tx.hash.should == "ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742" + tx.verify_input_signature(1, outpoint_tx).should == true + + # testnet3 e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009 + tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json')) + tx.hash.should == "e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009" + 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 + } end it '#sign_input_signature' do prev_tx = Tx.new( fixtures_file('rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin') ) prev_tx.hash.should == "2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a" @@ -242,20 +306,126 @@ #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 } end + + it "#legacy_sigops_count" do + Tx.new(@payload[0]).legacy_sigops_count.should == 2 + Tx.new(@payload[1]).legacy_sigops_count.should == 2 + Tx.new(@payload[2]).legacy_sigops_count.should == 2 + + # Test sig ops count in inputs too. + tx = Tx.new + txin = TxIn.new + txin.script_sig = Bitcoin::Script.from_string("10 OP_CHECKMULTISIGVERIFY OP_CHECKSIGVERIFY").to_binary + tx.add_in(txin) + txout = TxOut.new + txout.pk_script = Bitcoin::Script.from_string("5 OP_CHECKMULTISIG OP_CHECKSIG").to_binary + tx.add_out(txout) + tx.legacy_sigops_count.should == (20 + 1 + 20 + 1) + + end + + describe "Tx - is_final?" do + it "should be final if lock_time == 0" do + tx = Tx.new + tx.lock_time = 0 + tx.is_final?(0,0).should == true + + # even if has non-final input: + txin = TxIn.new + txin.sequence = "\x01\x00\x00\x00" + tx.add_in(txin) + tx.is_final?(0,0).should == true + end + + it "should be final if lock_time is below block_height" do + tx = Tx.new + txin = TxIn.new + txin.sequence = "\x01\x00\x00\x00" + tx.add_in(txin) + tx.lock_time = 6543 + tx.is_final?(6000,0).should == false + tx.is_final?(6543,0).should == false # when equal to block height, still not final + tx.is_final?(6544,0).should == true + tx.is_final?(9999,0).should == true + end + + it "should be final if lock_time is below timestamp" do + tx = Tx.new + txin = TxIn.new + txin.sequence = "\xff\xff\xff\xff" + tx.add_in(txin) + txin = TxIn.new + txin.sequence = "\x01\x00\x00\x00" + tx.add_in(txin) + tx.lock_time = Bitcoin::LOCKTIME_THRESHOLD # when equal, interpreted as threshold + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD - 1).should == false + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD).should == false # when equal to timestamp, still not final + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 1).should == true + + tx.lock_time = Bitcoin::LOCKTIME_THRESHOLD + 666 + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 1).should == false + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 666).should == false # when equal to timestamp, still not final + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 667).should == true + end + + it "should be final if all inputs are finalized regardless of lock_time" do + tx = Tx.new + txin = TxIn.new + txin.sequence = "\xff\xff\xff\xff" + tx.add_in(txin) + txin = TxIn.new + txin.sequence = "\xff\xff\xff\xff" + tx.add_in(txin) + + tx.lock_time = 6543 + tx.is_final?(6000,0).should == true + tx.is_final?(6543,0).should == true + tx.is_final?(6544,0).should == true + tx.is_final?(9999,0).should == true + + tx.lock_time = Bitcoin::LOCKTIME_THRESHOLD + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD - 1).should == true + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD).should == true + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 1).should == true + + tx.lock_time = Bitcoin::LOCKTIME_THRESHOLD + 666 + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 1).should == true + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 666).should == true + tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 667).should == true + end + + end it '#calculate_minimum_fee' do tx = Tx.new( fixtures_file('rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin') ) tx.minimum_relay_fee.should == 0 tx.minimum_block_fee.should == 0 tx = Tx.from_json(fixtures_file('bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json')) tx.minimum_relay_fee.should == 0 tx.minimum_block_fee.should == 10_000 end + it '#calculate_minimum_fee for litecoin' do + tx = Tx.from_json(fixtures_file('litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json')) + tx.minimum_relay_fee.should == 0 + tx.minimum_block_fee.should == 30_000 + Bitcoin.network = :litecoin # change to litecoin + tx.minimum_relay_fee.should == 0 + tx.minimum_block_fee.should == 5_900_000 + end + + it "should compare transactions" do + tx1 = Tx.new( @payload[0] ) + tx2 = Tx.new( @payload[1] ) + (tx1 == Bitcoin::P::Tx.from_json(tx1.to_json)).should == true + (tx1 == tx2).should == false + (tx1 == nil).should == false + end + describe "Tx - BIP Scripts" do it "should do OP_CHECKMULTISIG" do # checkmultisig without checkhashverify tx = Tx.from_json(fixtures_file('23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json')) @@ -264,10 +434,15 @@ # p2sh + multisig transaction from mainnet tx = Tx.from_json( fixtures_file('rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json') ) prev_tx = Tx.from_json( fixtures_file("rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json") ) tx.verify_input_signature(0, prev_tx).should == true + + # checkmultisig for testnet3 tx: 2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1 input index 1 + tx = Tx.from_json( fixtures_file('tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json') ) + prev_tx = Tx.from_json( fixtures_file("tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json") ) + tx.verify_input_signature(1, prev_tx).should == true end it "should do P2SH with inner OP_CHECKMULTISIG (BIP 0016)" do tx = Tx.from_json(fixtures_file('3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json')) prev_tx = Tx.from_json(fixtures_file('35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json')) @@ -292,8 +467,16 @@ prev_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json')) prev_tx.hash.should == "44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a" tx.verify_input_signature(0, prev_tx).should == true end - end + it "should do OP_CHECKMULTISIG with OP_0 used as a pubkey" do + tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json')) + tx.hash.should == "6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba" + prev_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json')) + prev_tx.hash.should == "4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9" + tx.verify_input_signature(0, prev_tx).should == true + end + end + end