|
2 | 2 | Test Transaction |
3 | 3 | """ |
4 | 4 |
|
5 | | -from cryptos.transaction import Tx |
6 | 5 | from io import BytesIO |
7 | 6 |
|
| 7 | +from cryptos.transaction import Tx, TxIn, TxOut, Script |
| 8 | +from cryptos.btc_address import address_to_pkb_hash |
| 9 | +from cryptos.keys import sk_to_pk, pk_to_sec |
| 10 | +from cryptos.ecdsa import sign |
| 11 | + |
8 | 12 | def test_legacy_decode(): |
9 | 13 |
|
10 | 14 | # Example taken from Programming Bitcoin, Chapter 5 |
@@ -38,7 +42,7 @@ def test_legacy_decode(): |
38 | 42 | # validate the transaction as Bitcoin law-abiding and cryptographically authentic |
39 | 43 | assert tx.validate() |
40 | 44 |
|
41 | | - # fudge the r in the (r,s) digital signature tuple, this should break validation because CEHCKSIG will fail |
| 45 | + # fudge the r in the (r,s) digital signature tuple, this should break validation because CHECKSIG will fail |
42 | 46 | sigb = tx.tx_ins[0].script_sig.cmds[0] |
43 | 47 | sigb2 = sigb[:6] + bytes([(sigb[6] + 1) % 255]) + sigb[7:] |
44 | 48 | tx.tx_ins[0].script_sig.cmds[0] = sigb2 |
@@ -79,3 +83,56 @@ def test_segwit_decode(): |
79 | 83 | # check correct decoding/encoding |
80 | 84 | raw2 = tx.encode() |
81 | 85 | assert raw == raw2 |
| 86 | + |
| 87 | +def test_create_tx(): |
| 88 | + # this example follows Programming Bitcoin Chapter 7 |
| 89 | + |
| 90 | + # define the inputs of our aspiring transaction |
| 91 | + prev_tx = bytes.fromhex('0d6fe5213c0b3291f208cba8bfb59b7476dffacc4e5cb66f6eb20a080843a299') |
| 92 | + prev_index = 13 |
| 93 | + tx_in = TxIn(prev_tx, prev_index, net='test') |
| 94 | + |
| 95 | + # change output that goes back to us |
| 96 | + amount = int(0.33 * 1e8) # 0.33 tBTC in units of satoshi |
| 97 | + pkb_hash = address_to_pkb_hash('mzx5YhAH9kNHtcN481u6WkjeHjYtVeKVh2') |
| 98 | + script = Script([118, 169, pkb_hash, 136, 172]) # OP_DUP, OP_HASH160, <hash>, OP_EQUALVERIFY, OP_CHECKSIG |
| 99 | + tx_out_change = TxOut(amount=amount, script_pubkey=script) |
| 100 | + |
| 101 | + # target output that goes to a lucky recepient |
| 102 | + amount = int(0.1 * 1e8) # 0.1 tBTC in units of satoshi |
| 103 | + pkb_hash = address_to_pkb_hash('mnrVtF8DWjMu839VW3rBfgYaAfKk8983Xf') |
| 104 | + script = Script([118, 169, pkb_hash, 136, 172]) # OP_DUP, OP_HASH160, <hash>, OP_EQUALVERIFY, OP_CHECKSIG |
| 105 | + tx_out_target = TxOut(amount=amount, script_pubkey=script) |
| 106 | + |
| 107 | + # create the desired transaction object |
| 108 | + tx = Tx(1, [tx_in], [tx_out_change, tx_out_target]) |
| 109 | + |
| 110 | + # validate the intended fee of 0.01 tBTC |
| 111 | + assert tx.fee() == int(0.01 * 1e8) |
| 112 | + |
| 113 | + # produce the unlocking script for this p2pkh tx: [<signature>, <pubkey>] |
| 114 | + |
| 115 | + # first produce the <pubkey> that will satisfy OP_EQUALVERIFY on the locking script |
| 116 | + sk = 8675309 # the secret key that produced the public key that produced the hash that is on that input tx's locking script |
| 117 | + pk = sk_to_pk(sk) |
| 118 | + sec = pk_to_sec(pk, compressed=True) # sec encoded public key as bytes |
| 119 | + # okay but anyone with the knowledge of the public key could have done this part if this public |
| 120 | + # key was previously used (and hence revealed) somewhere on the blockchain |
| 121 | + |
| 122 | + # now produce the digital signature that will satisfy the OP_CHECKSIG on the locking script |
| 123 | + enc = tx.encode(sig_index=0) |
| 124 | + sig = sign(sk, enc) # only and uniquely the person with the secret key can do this |
| 125 | + der = sig.encode() |
| 126 | + der_and_type = der + b'\x01' # 1 = SIGHASH_ALL, indicating this der signature encoded "ALL" of the tx |
| 127 | + |
| 128 | + # set the unlocking script into the transaction |
| 129 | + tx_in.script_sig = Script([der_and_type, sec]) |
| 130 | + |
| 131 | + # final check: ensure that our manually constructed transaction is all valid and ready to send out to the wild |
| 132 | + assert tx.validate() |
| 133 | + |
| 134 | + # peace of mind: fudge the signature and try again |
| 135 | + der = der[:6] + bytes([(der[6] + 1) % 255]) + der[7:] |
| 136 | + der_and_type = der + b'\x01' |
| 137 | + tx_in.script_sig = Script([der_and_type, sec]) |
| 138 | + assert not tx.validate() |
0 commit comments