def test_segwit(self): raw_tx = unhexlify( '01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) tx.tx_ins[0]._script_pubkey = Script.parse( unhexlify('a9144733f37cf4db86fbc2efed2500b4f4e49f31202387')) tx.tx_ins[0]._value = 1000000000 want = b'b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a' self.assertEqual(hexlify(tx.hash_prevouts()), want) want = b'18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198' self.assertEqual(hexlify(tx.hash_sequence()), want) want = b'de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83' self.assertEqual(hexlify(tx.hash_outputs()), want) want = b'01000000b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001976a91479091972186c449eb1ded22b78e40d009bdf008988ac00ca9a3b00000000feffffffde984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c839204000001000000' self.assertEqual(hexlify(tx.sig_hash_preimage_bip143(0, 1)), want) self.assertEqual(tx.serialize(), raw_tx) self.assertTrue(tx.verify()) raw_tx = unhexlify( '01000000000101712e5b4e97ab549d50ca60a4f5968b2225215e9fab82dae4720078711406972f0000000017160014848202fc47fb475289652fbd1912cc853ecb0096feffffff0232360000000000001976a914121ae7a2d55d2f0102ccc117cbcb70041b0e037f88ac10270000000000001976a914ec0be50951651261765cfa71d7bd41c7b9245bb388ac024830450221009263c7de80c297d5b21aba846cf6f0a970e1d339568167d1e4c1355c7711bc1602202c9312b8d32fd9c7acc54c46cab50eb7255ce3c012214c41fe1ad91bccb16a13012102ebdf6fc448431a2bd6380f912a0fa6ca291ca3340e79b6f0c1fdaff73cf54061075a0700' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertEqual(tx.serialize(), raw_tx) self.assertTrue(tx.verify()) raw_tx = unhexlify( '020000000001081446a37707dc7ea5cf61806dd904f4ef4f0875a1ae0677cb2558af8154294db5070000001716001420b884a7838912a368ed6529f920edf39c0eabf6ffffffff3ec88712de2bc1812829a407e22c477e416df9437ef18e15c80cb6d415928f18000000001716001427dbca14f538846467a4b5ba0bc08014854db9c5ffffffff3176ffed5b1de43807d223216a82c6f68637ab7dbe2b585e184341f3a7d594cb0000000017160014d8f1c025669899719d87b3b21ca8e8fc2b139f15ffffffff54d8e606273ca57a3a559f3ce56619ad473701e19e5d2b4346d5082f47656fc3000000006b4830450221009447ef2f0ffb9e133f351161c2c3885f43c15d4b7682d7188011b07658537355022044c76b845efe00f1984d7c375a6af94280b4464917ea8274fa6eafa614647c83012102282deea2ecd8a0807ccfd51f9df5e1fd4bc9505b7e7e41ba922964ed59078c0cffffffff05392ab19a8d84d9db21d01d83bb2fc9c78a7b9555a7900a9b7b314a4f52d6990b00000017160014602845b585bdbb8a3e0cb6f7d2e54b50da1dc2caffffffff3ea224000c9023d3329f9fe10f0980deb05df5a089a77b8bda326476d0676b60000000006a4730440220499c7c60669b3b2f6e7a3535aa695d26c879cbf904c2a4d27abe8e9ce603b21b022022b126f3a3eb3800d56fc4b120e62b2bf8656160f42477709ab94c08488a57440121033fd51607ad8c7b20678ce90dc00e67305468dc5359f10d2b8e12bde1aad6b70affffffff2b380df0036fb56ce6abb604100c4475c44c5f78eb1ccb7eb3c8c68d4c07964401000000171600141222eb5661b5239b625a66596bf51c057dd6e36effffffff044f03ae06fe2cf464f0c06621f005f8584b17c83df3c274cc0532d92b0b4cbf030000001716001430128f1e88752aff630253a09502433615591817ffffffff0ae7546101000000001976a914fe129300f08787df2433ea1f179855ad631f521588ac7060b506000000001976a9141e52cea2417015d68a395961ded221e8773af94c88acfa331d000000000017a914718a41bebbfcfe7fc6f1713566e0e494062ccfe1877e905900000000001976a914d516332ccf4513a86e02103cb3723fcc4cc6d10488acf1523f000000000017a9149a1f25e8b22e06b94b36992690173927663e7a67873ca02f00000000001976a914781bc2683d9b8ca38a4805a2b3a3209c607f643988ac5b1fa5020000000017a914add3e0a14b450f7d68ec264d6c3eb3c4948ec611875e6f1800000000001976a91483449d8cfe9fdfbb16cb5632ca7b2ebb63ae90ab88aca342bc000000000017a914087d0060b24f68430474c5d048990158fd1ca7fb8700204e00000000001976a9147d79d91ffcc4d2ab74da606d22e60b794526244388ac0247304402200f6479c8a098d0d32e80023a6841273cb3bf2d9190c4ca9efed2d5a4b453d510022026c34fd235ba7a5e4f4dff2470b97b218672694738766fd3a6e562b1d73e9eff01210224af4a7a92c9967f3683fa3e0206daa5b928a84b35cae4de7427b0c0b1f7e0bf02473044022052c438047e0e7b44ff7c4e2072b79a69738420b9355d13a5c9d1094e3532968e02204eaaf65f26e11e346d23b39102194bed1ebc2c3e37521a9f00509616449fdb130121023638167aa99a910f4a4275b5bb628cc26facbfa5bf31f5cf2a6d554bc8db606002473044022014fa5476e6d80665113cc2b2555cc9ccb1c18333118a49a353dd1a932986ecab022035cdde8443957562510f0ee52e8ee400ca27f56785266ce4615ace4d1afb7c210121034ea7a539ad591d853b3ed679e5cf212801f063f0540ce1b9ee1085a2f6eac6aa0002483045022100ef9b368dd2498909df5cadde878c6ef708dfcbc3f0ca603c127bf531baff2be902201dd537223c6e67f5776b4ecf1a2baacfc0e198600431016a2f6e4928e2efc254012102e6b2a2dae2045633814c126ebeceebc036fc55a40f7aa56d03633b4f3cb407bf0002483045022100e426e718989f4141767bb5861d6d66c5b3d4b76bee849e8a05d0def1399b9a0c0220256b3c2e7e7e7e2cf7481e9e37feb6b1a877f13e396071f5c30af78557f89c0d012102d82c01881a7458f936b4c551e48adee3949585f842ece86a77e14c0976119ec602483045022100f4e5291e42dbe421eda30d670caf284b7d675ed439df64371d14d9dac028646502202e04dcea07a7d8bfc59f95172c87cdf9f2fa4f97559971cd08e2efd50cf3acb4012103a5c6e168f4acf6470b018fb9b86ef9ae58ba1834c3646a2ed1bebbfb94acf83500000000' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertEqual(tx.serialize(), raw_tx) self.assertTrue(tx.verify())
def test_coinbase_height(self): raw_tx = unhexlify( '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5e03d71b07254d696e656420627920416e74506f6f6c20626a31312f4542312f4144362f43205914293101fabe6d6d678e2c8c34afc36896e7d9402824ed38e856676ee94bfdb0c6c4bcd8b2e5666a0400000000000000c7270000a5e00e00ffffffff01faf20b58000000001976a914338c84849423992471bffb1a54a8d9b1d69dc28a88ac00000000' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertEqual(tx.coinbase_height(), 465879) raw_tx = unhexlify( '0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertIsNone(tx.coinbase_height())
def test_fee(self): raw_tx = unhexlify( '0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertEqual(tx.fee(), 40000) raw_tx = unhexlify( '010000000456919960ac691763688d3d3bcea9ad6ecaf875df5339e148a1fc61c6ed7a069e010000006a47304402204585bcdef85e6b1c6af5c2669d4830ff86e42dd205c0e089bc2a821657e951c002201024a10366077f87d6bce1f7100ad8cfa8a064b39d4e8fe4ea13a7b71aa8180f012102f0da57e85eec2934a82a585ea337ce2f4998b50ae699dd79f5880e253dafafb7feffffffeb8f51f4038dc17e6313cf831d4f02281c2a468bde0fafd37f1bf882729e7fd3000000006a47304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a7160121035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937feffffff567bf40595119d1bb8a3037c356efd56170b64cbcc160fb028fa10704b45d775000000006a47304402204c7c7818424c7f7911da6cddc59655a70af1cb5eaf17c69dadbfc74ffa0b662f02207599e08bc8023693ad4e9527dc42c34210f7a7d1d1ddfc8492b654a11e7620a0012102158b46fbdff65d0172b7989aec8850aa0dae49abfb84c81ae6e5b251a58ace5cfeffffffd63a5e6c16e620f86f375925b21cabaf736c779f88fd04dcad51d26690f7f345010000006a47304402200633ea0d3314bea0d95b3cd8dadb2ef79ea8331ffe1e61f762c0f6daea0fabde022029f23b3e9c30f080446150b23852028751635dcee2be669c2a1686a4b5edf304012103ffd6f4a67e94aba353a00882e563ff2722eb4cff0ad6006e86ee20dfe7520d55feffffff0251430f00000000001976a914ab0c0b2e98b1ab6dbf67d4750b0a56244948a87988ac005a6202000000001976a9143c82d7df364eb6c75be8c80df2b3eda8db57397088ac46430600' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertEqual(tx.fee(), 140500)
def parse(cls, s): version = little_endian_to_int(s.read(4)) prev_block = s.read(32)[::-1] merkle_root = s.read(32)[::-1] timestamp = little_endian_to_int(s.read(4)) bits = s.read(4) nonce = s.read(4) num_tx = read_varint(s) txs = [] for i in range(num_tx): if i == 0: txs.append(Tx.parse(s, coinbase_tx=True)) else: txs.append(Tx.parse(s)) return cls(version, prev_block, merkle_root, timestamp, bits, nonce, txs)
def test_is_coinbase(self): raw_tx = unhexlify( '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5e03d71b07254d696e656420627920416e74506f6f6c20626a31312f4542312f4144362f43205914293101fabe6d6d678e2c8c34afc36896e7d9402824ed38e856676ee94bfdb0c6c4bcd8b2e5666a0400000000000000c7270000a5e00e00ffffffff01faf20b58000000001976a914338c84849423992471bffb1a54a8d9b1d69dc28a88ac00000000' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertTrue(tx.is_coinbase())
def test_exercise_4(self): last_block_hex = '000000000d65610b5af03d73ed67704713c9b734d87cf4b970d39a0416dd80f9' last_block = bytes.fromhex(last_block_hex) secret = little_endian_to_int( hash256(b'Jimmy Song Programming Blockchain')) private_key = PrivateKey(secret=secret) addr = private_key.point.address(testnet=True) h160 = decode_base58(addr) target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv' self.assertEqual(addr, target_address) filter_size = 30 filter_num_functions = 5 filter_tweak = 90210 # FILL THIS IN target_h160 = decode_base58(target_address) target_script = p2pkh_script(target_h160) fee = 5000 # fee in satoshis node = SimpleNode('tbtc.programmingblockchain.com', testnet=True, logging=False) bf = BloomFilter(filter_size, filter_num_functions, filter_tweak) bf.add(h160) node.handshake() node.send(b'filterload', bf.filterload()) getheaders_message = GetHeadersMessage(start_block=last_block) node.send(getheaders_message.command, getheaders_message.serialize()) headers_envelope = node.wait_for_commands([HeadersMessage.command]) stream = headers_envelope.stream() headers = HeadersMessage.parse(stream) get_data_message = GetDataMessage() for block in headers.blocks: self.assertTrue(block.check_pow()) if last_block is not None: self.assertEqual(block.prev_block, last_block) last_block = block.hash() get_data_message.add_data(FILTERED_BLOCK_DATA_TYPE, last_block) node.send(get_data_message.command, get_data_message.serialize()) prev_tx = None while prev_tx is None: envelope = node.wait_for_commands([b'merkleblock', b'tx']) stream = envelope.stream() if envelope.command == b'merkleblock': mb = MerkleBlock.parse(stream) self.assertTrue(mb.is_valid()) else: prev = Tx.parse(stream, testnet=True) for i, tx_out in enumerate(prev.tx_outs): if tx_out.script_pubkey.address(testnet=True) == addr: prev_tx = prev.hash() prev_index = i prev_amount = tx_out.amount break tx_in = TxIn(prev_tx, prev_index) output_amount = prev_amount - fee tx_out = TxOut(output_amount, target_script) tx_obj = Tx(1, [tx_in], [tx_out], 0, testnet=True) tx_obj.sign_input(0, private_key) self.assertEqual( tx_obj.serialize().hex(), '010000000194e631abb9e1079ec72a1616a3aa0111c614e65b96a6a4420e2cc6af9e6cc96e000000006a47304402203cc8c56abe1c0dd043afa9eb125dafbebdde2dd4cd7abf0fb1aae0667a22006e02203c95b74d0f0735bbf1b261d36e077515b6939fc088b9d7c1b7030a5e494596330121021cdd761c7eb1c90c0af0a5963e94bf0203176b4662778d32bd6d7ab5d8628b32ffffffff01f8829800000000001976a914ad346f8eb57dee9a37981716e498120ae80e44f788ac00000000' )
def sign_psbt(self, serialized_psbt): """ Sign PSBT. Currently assumes inputs are ordered the same in unsigned tx and psbt. For now, only SIGHASH_ALL is approved. - If a non-witness UTXO is provided, its hash must match the hash specified in the prevout - TODO If a witness UTXO is provided, no non-witness signature may be created - TODO If a redeemScript is provided, the scriptPubKey must be for that redeemScript - TODO If a witnessScript is provided, the scriptPubKey or the redeemScript must be for that witnessScript - TODO If a sighash type is provided, the signer must check that the sighash is acceptable. If unacceptable, they must fail. - If a sighash type is not provided, the signer signs using SIGHASH_ALL """ psbt_si = Signer(serialized_psbt) tx_obj = Tx.parse(BytesIO(psbt_si.get_unsigned_tx())) for i in range(len(psbt_si.psbt.maps["inputs"])): curr_input = psbt_si.psbt.maps["inputs"][i] # check prev_tx_id if IN_NON_WITNESS_UTXO in curr_input: psbt_tx_id = hash256(curr_input[IN_NON_WITNESS_UTXO]) gutx_tx_id = tx_obj.tx_ins[i].prev_tx if psbt_tx_id != gutx_tx_id: raise PSBTError( f"UTXO {i} and Unsigned TX input {i} have different prev_tx_id: {psbt_tx_id} vs {gutx_tx_id}" ) # TODO SegWit elif IN_WITNESS_UTXO in curr_input: pass # TODO Handle RedeemScripts # Look for redeemScripts if IN_REDEEM_SCRIPT in curr_input: raise NotImplementedError( "Redeem Scripts not signable Yet. Unknown how to find ScriptPubKey" ) # read sighash sighash_type = psbt_si.get_sighash_type(i) if sighash_type is None: sighash_type = SIGHASH_ALL elif sighash_type != SIGHASH_ALL: raise NotImplementedError( "Other sighash types not yet supported.") # sign pubkey_sec, fingerprint, path = psbt_si.get_bip32_info(i) if fingerprint.hex() == self.master_fingerprint(): privkey = self.derive_key(path, priv=True, absolute=True).to_priv_key() if privkey.point.sec() == pubkey_sec: script_sig = tx_obj.sign_input(i, privkey, sighash_type) if not script_sig: raise SignatureError(f"Signing of input {i} failed.") sig = script_sig.cmds[0] psbt_si.add_partial_sig(sig, pubkey_sec, i) else: raise PSBTWarning( f"Private Key does not match pubkey provided. Skipping input {i}..." ) else: raise PSBTWarning( f"Fingerprint does not match this wallet. Skipping input {i}..." ) return psbt_si.serialize()
def _vavidity_checking(self): # check for unsigned tx if GLOBAL_UNSIGNED_TX not in self.maps["global"]: raise PSBTError("Invalid PSBT: Missing unsigned tx.") else: tx_obj = Tx.parse(BytesIO(self.maps["global"][GLOBAL_UNSIGNED_TX])) for i in tx_obj.tx_ins: if len(i.script_sig.cmds) > 0 or len(i.witness_program) > 1: raise PSBTError("Invalid PSBT: Transaction in global map has ScriptSig or scriptWitness") gutx_ins_count = len(tx_obj.tx_ins) psbt_ins_count = len(self.maps["inputs"]) if gutx_ins_count == 0: raise PSBTError("Invalid PSBT: unsigned tx missing inputs") elif psbt_ins_count == 0: raise PSBTError("Invalid PSBT: no PSBT inputs") elif gutx_ins_count != psbt_ins_count: raise PSBTError('Invalid PSBT: number of inputs in unsigned transaction and PSBT do not match') gutx_outs_count = len(tx_obj.tx_outs) psbt_outs_count = len(tx_obj.tx_outs) if gutx_outs_count == 0: raise PSBTError('Invalid PSBT: unsigned transaction missing outputs') elif psbt_outs_count == 0: raise PSBTError('Invalid PSBT: psbt has outputs') elif gutx_outs_count != psbt_outs_count: raise PSBTError('Invalid PSBT: number of outputs in unsigned transaction and PSBT do not match')
def test_verify_input2(self): raw_tx = unhexlify( '0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a385aa0836f01d5e4789e6bd304d87221a000000db00483045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4ee942a8993701483045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402201475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b95abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c568700000000' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertTrue(tx.verify_input(0))
def test_serialize(self): raw_tx = unhexlify( '0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertEqual(tx.serialize(), raw_tx)
def test_exercise_2(self): hex_tx = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000' stream = BytesIO(bytes.fromhex(hex_tx)) coinbase = Tx.parse(stream) self.assertEqual( coinbase.tx_ins[0].script_sig.instructions[2], b'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks' )
def test_example_4(self): modified_tx = bytes.fromhex('0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000001976a914a802fc56c704ce87c42d7c92eb75e7896bdc41ae88acfeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac1943060001000000') h256 = hash256(modified_tx) z = int.from_bytes(h256, 'big') stream = BytesIO(bytes.fromhex('0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600')) transaction = Tx.parse(stream) tx_in = transaction.tx_ins[0] combined_script = tx_in.script_sig + tx_in.script_pubkey() self.assertTrue(combined_script.evaluate(z))
def parse(cls, s): b = cls.parse_header(s) num_txs = read_varint(s) tx_hashes = [] for _ in range(num_txs): t = Tx.parse(s) tx_hashes.append(t.hash()) b.tx_hashes = tx_hashes return b
def test_exercise_9(self): hex_tx = '01000000012f5ab4d2666744a44864a63162060c2ae36ab0a2375b1c2b6b43077ed5dcbed6000000006a473044022034177d53fcb8e8cba62432c5f6cc3d11c16df1db0bce20b874cfc61128b529e1022040c2681a2845f5eb0c46adb89585604f7bf8397b82db3517afb63f8e3d609c990121035e8b10b675477614809f3dde7fd0e33fb898af6d86f51a65a54c838fddd417a5feffffff02c5872e00000000001976a91441b835c78fb1406305727d8925ff315d90f9bbc588acae2e1700000000001976a914c300e84d277c6c7bcf17190ebc4e7744609f8b0c88ac31470600' stream = BytesIO(bytes.fromhex(hex_tx)) index = 0 t = Tx.parse(stream) tx_in = t.tx_ins[index] z = t.sig_hash(index) combined_script = tx_in.script_sig + tx_in.script_pubkey() self.assertTrue(combined_script.evaluate(z))
def listen_for_channel_request(peer): request = json.loads(peer.receive()) funding_tx = Tx.parse(BytesIO(bytes.fromhex(request['funding_tx']))) if (funding_tx.verify()): new_channel = Channel(peer, request['remote_amt'], request['local_amt'], funding_tx) return new_channel else: print("Invalid Channel Request") return None
def test_sig_hash(self): raw_tx = unhexlify( '0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) tx.testnet = True hash_type = SIGHASH_ALL want = int( '27e0c5994dec7824e56dec6b2fcb342eb7cdb0d0957c2fce9882f715e85d81a6', 16) self.assertEqual(tx.sig_hash(0, hash_type), want)
def parse(cls, s, testnet=False): """Takes a byte stream and parses a block. Returns a Block object""" b = cls.parse_header(s) num_txs = read_varint(s) b.txs = [] b.tx_hashes = [] b.tx_lookup = {} for _ in range(num_txs): t = Tx.parse(s, testnet=testnet) b.txs.append(t) b.tx_hashes.append(t.hash()) b.tx_lookup[t.hash()] = t return b
def test_exercise_7(self): hex_tx = '010000000456919960ac691763688d3d3bcea9ad6ecaf875df5339e148a1fc61c6ed7a069e010000006a47304402204585bcdef85e6b1c6af5c2669d4830ff86e42dd205c0e089bc2a821657e951c002201024a10366077f87d6bce1f7100ad8cfa8a064b39d4e8fe4ea13a7b71aa8180f012102f0da57e85eec2934a82a585ea337ce2f4998b50ae699dd79f5880e253dafafb7feffffffeb8f51f4038dc17e6313cf831d4f02281c2a468bde0fafd37f1bf882729e7fd3000000006a47304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a7160121035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937feffffff567bf40595119d1bb8a3037c356efd56170b64cbcc160fb028fa10704b45d775000000006a47304402204c7c7818424c7f7911da6cddc59655a70af1cb5eaf17c69dadbfc74ffa0b662f02207599e08bc8023693ad4e9527dc42c34210f7a7d1d1ddfc8492b654a11e7620a0012102158b46fbdff65d0172b7989aec8850aa0dae49abfb84c81ae6e5b251a58ace5cfeffffffd63a5e6c16e620f86f375925b21cabaf736c779f88fd04dcad51d26690f7f345010000006a47304402200633ea0d3314bea0d95b3cd8dadb2ef79ea8331ffe1e61f762c0f6daea0fabde022029f23b3e9c30f080446150b23852028751635dcee2be669c2a1686a4b5edf304012103ffd6f4a67e94aba353a00882e563ff2722eb4cff0ad6006e86ee20dfe7520d55feffffff0251430f00000000001976a914ab0c0b2e98b1ab6dbf67d4750b0a56244948a87988ac005a6202000000001976a9143c82d7df364eb6c75be8c80df2b3eda8db57397088ac46430600' stream = BytesIO(bytes.fromhex(hex_tx)) t = Tx.parse(stream) input_sum = 0 for tx_in in t.tx_ins: value = tx_in.value() input_sum += value output_sum = 0 for tx_out in t.tx_outs: value = tx_out.amount output_sum += value fee = input_sum - output_sum self.assertEqual(fee, 140500)
def parse(cls, stream): if stream.read(4) != MAGIC: raise PSBTError("Invalid PSBT: MAGIC") if stream.read(1) != HEAD_SEPARATOR: raise PSBTError("Invalid PSBT: Head Separator 0xff missing") new_map = { "global":{}, "inputs":[], "outputs":[] } expect_global = True in_count = 0 out_count = 0 while expect_global or in_count or out_count: try: new_key, new_val = cls.parse_pair(stream) except IndexError: raise PSBTError("Parsing Error") if expect_global: # separator 0x00 reached. End of Globals if new_key is None: expect_global = False continue new_map["global"][new_key] = new_val if new_key == GLOBAL_UNSIGNED_TX: unsigned_tx = Tx.parse(BytesIO(new_val)) in_count = len(unsigned_tx.tx_ins) out_count = len(unsigned_tx.tx_outs) # create correct number of empty inputs and outputs [new_map["inputs"].append({}) for _ in range(in_count)] [new_map["outputs"].append({}) for _ in range(out_count)] elif in_count: # separator means end of input if new_key is None: in_count -= 1 continue current = abs(len(new_map["inputs"]) - in_count) new_map["inputs"][current][new_key] = new_val elif out_count: if new_key is None: out_count -= 1 continue current = abs(len(new_map["outputs"]) - out_count) new_map["outputs"][current][new_key] = new_val return cls(maps=new_map)
def test_exercise_3(self): hex_tx = '0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a385aa0836f01d5e4789e6bd304d87221a000000db00483045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4ee942a8993701483045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402201475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b95abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c568700000000' hex_sec = '03b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb71' hex_der = '3045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e754022' hex_redeem_script = '475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152ae' sec = bytes.fromhex(hex_sec) der = bytes.fromhex(hex_der) redeem_script = Script.parse(BytesIO(bytes.fromhex(hex_redeem_script))) stream = BytesIO(bytes.fromhex(hex_tx)) tx_obj = Tx.parse(stream) tx_obj.tx_ins[0].script_sig = redeem_script s = tx_obj.serialize() + int_to_little_endian(SIGHASH_ALL, 4) z = int.from_bytes(hash256(s), 'big') point = S256Point.parse(sec) sig = Signature.parse(der) self.assertTrue(point.verify(z, sig))
def test_parse_inputs(self): raw_tx = unhexlify( '0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600' ) stream = BytesIO(raw_tx) tx = Tx.parse(stream) self.assertEqual(len(tx.tx_ins), 1) want = unhexlify( 'd1c789a9c60383bf715f3f6ad9d14b91fe55f3deb369fe5d9280cb1a01793f81') self.assertEqual(tx.tx_ins[0].prev_tx, want) self.assertEqual(tx.tx_ins[0].prev_index, 0) want = unhexlify( '483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a' ) self.assertEqual(tx.tx_ins[0].script_sig.serialize(), want) self.assertEqual(tx.tx_ins[0].sequence, 0xfffffffe)
def __init__(self, serialized_psbt): self.tx_obj = Tx.parse(BytesIO(self.psbt.maps["global"][GLOBAL_UNSIGNED_TX])) for i in range(len(self.psbt.maps["inputs"])): curr_input = self.psbt.maps["inputs"][i] if self._is_witness_input(curr_input): try: # Add final scriptWit self.tx_obj.tx_ins[i].witness_program = curr_input[IN_FINAL_SCRIPTWITNESS] except KeyError: raise PSBTError("PSBT input is missing finalized scriptWitness") if IN_FINAL_SCRIPTSIG in curr_input: self.tx_obj.tx_ins[i].script_sig = Script.parse(curr_input[IN_FINAL_SCRIPTSIG]) else: try: self.tx_obj.tx_ins[i].script_sig = Script.parse(curr_input[IN_FINAL_SCRIPTSIG]) except KeyError: raise PSBTError("PSBT input is missing finalized scriptSig")
def test_exercise_10(self): block_hex = '0000000000044b01a9440b34f582fe171c7b8642fedd0ebfccf8fdf6a1810900' block_hash = bytes.fromhex(block_hex) node = SimpleNode('tbtc.programmingblockchain.com', testnet=True) node.handshake() getdata = GetDataMessage() getdata.add_data(BLOCK_DATA_TYPE, block_hash) node.send(getdata.command, getdata.serialize()) block_envelope = node.wait_for_commands([b'block']) stream = block_envelope.stream() b = Block.parse(stream) self.assertTrue(b.check_pow()) num_txs = read_varint(stream) tx_hashes = [] for _ in range(num_txs): t = Tx.parse(stream) tx_hashes.append(t.hash()) b.tx_hashes = tx_hashes self.assertTrue(b.validate_merkle_root())
def test_example_5(self): last_block_hex = '00000000000538d5c2246336644f9a4956551afb44ba47278759ec55ea912e19' address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv' h160 = decode_base58(address) node = SimpleNode('tbtc.programmingblockchain.com', testnet=True, logging=False) bf = BloomFilter(30, 5, 90210) bf.add(h160) node.handshake() node.send(b'filterload', bf.filterload()) start_block = bytes.fromhex(last_block_hex) getheaders_message = GetHeadersMessage(start_block=start_block) node.send(b'getheaders', getheaders_message.serialize()) headers_envelope = node.wait_for_commands({b'headers'}) stream = headers_envelope.stream() headers = HeadersMessage.parse(stream) get_data_message = GetDataMessage() for b in headers.blocks: if not b.check_pow(): raise RuntimeError('proof of work is invalid') get_data_message.add_data(FILTERED_BLOCK_DATA_TYPE, b.hash()) node.send(b'getdata', get_data_message.serialize()) found = False while not found: envelope = node.wait_for_commands({b'merkleblock', b'tx'}) stream = envelope.stream() if envelope.command == b'merkleblock': mb = MerkleBlock.parse(stream) if not mb.is_valid(): raise RuntimeError('invalid merkle proof') else: prev_tx_obj = Tx.parse(stream, testnet=True) for i, tx_out in enumerate(prev_tx_obj.tx_outs): if tx_out.script_pubkey.address(testnet=True) == address: self.assertEqual( prev_tx_obj.id(), 'e3930e1e566ca9b75d53b0eb9acb7607f547e1182d1d22bd4b661cfe18dcddf1' ) self.assertEqual(i, 0) found = True break
def parse(cls, s): '''Takes a byte stream and parses a block. Returns a BlockHeader object''' # s.read(n) will read n bytes from the stream # version - 4 bytes, little endian, interpret as int version = little_endian_to_int(s.read(4)) # prev_block - 32 bytes, little endian (use [::-1] to reverse) prev_block = s.read(32)[::-1] # merkle_root - 32 bytes, little endian (use [::-1] to reverse) merkle_root = s.read(32)[::-1] # timestamp - 4 bytes, little endian, interpret as int timestamp = little_endian_to_int(s.read(4)) # bits - 4 bytes bits = s.read(4) # nonce - 4 bytes nonce = s.read(4) # read the actual transactions num_txns = read_varint(s) txns = [Tx.parse(s) for _ in range(num_txns)] # initialize class return cls(version, prev_block, merkle_root, timestamp, bits, nonce, txns)
def add_partial_sig(self, new_sig, pubkey, idx=None): if idx is None: idx = self._get_input_index(pubkey) # Note: Assumes that the sighash type is only the last byte of sig. # Note: Assumes inputs in psbt and indexed the same as in unsigned tx this_sighash = little_to_int(new_sig[-1:]) if idx is not None: if not self.check_sighash(idx=idx, sighash=this_sighash): raise PSBTError(f"Sighash type {this_sighash} on Signature does not match specified Sighash ({self.get_sighash_type(idx)}) for this input.") curr_input = self.psbt.maps["inputs"][idx] if IN_NON_WITNESS_UTXO in curr_input: global_txid = Tx.parse(BytesIO(self.psbt.maps["global"][GLOBAL_UNSIGNED_TX])).tx_ins[idx].prev_tx curr_txid = hash256(curr_input[IN_NON_WITNESS_UTXO])#[::-1] if global_txid != curr_txid: raise PSBTError("UTXO of this input does not match the UTXO specified in global unsigned Tx") elif IN_WITNESS_UTXO in curr_input: raise NotImplementedError("SegWit Not yet implemented.") self.psbt.maps["inputs"][idx][IN_PARTIAL_SIG+pubkey] = new_sig else: raise PSBTError("Signature cannot be added. The Pubkey provided is not avaialbe in this PSBT") return
1574e6b3c192ecfb52cc8984ee7b6c568700000000' hex_sec = '03b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4b\ b71' hex_der = '3045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8ee\ f53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402\ 2' hex_redeem_script = '475221022626e955ea6ea6d98850c994f9107b036b1334f18ca88\ 30bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7f\ bdbd4bb7152ae' sec = bytes.fromhex(hex_sec) der = bytes.fromhex(hex_der) redeem_script = Script.parse(BytesIO(bytes.fromhex(hex_redeem_script))) stream = BytesIO(bytes.fromhex(hex_tx)) tx_obj = Tx.parse(stream) s = int_to_little_endian(tx_obj.version, 4) s += encode_varint(len(tx_obj.tx_ins)) i = tx_obj.tx_ins[0] s += TxIn(i.prev_tx, i.prev_index, redeem_script, i.sequence).serialize() s += encode_varint(len(tx_obj.tx_outs)) for tx_out in tx_obj.tx_outs: s += tx_out.serialize() s += int_to_little_endian(tx_obj.locktime, 4) s += int_to_little_endian(SIGHASH_ALL, 4) z = int.from_bytes(hash256(s), 'big') point = S256Point.parse(sec) sig = Signature.parse(der) print(point.verify(z, sig))
def add_witness_utxo(self, idx, tx_hex, vout): #BROKEN tx_obj = Tx.parse(BytesIO(tx_hex)) outpoint = tx_obj.tx_outs[vout].serialize() self.psbt.maps["inputs"][idx][IN_WITNESS_UTXO] = outpoint
def get_tx_obj(self): return Tx.parse(BytesIO(self.maps["global"][GLOBAL_UNSIGNED_TX]))
from io import BytesIO from ecc import S256Point, Signature from helper import encode_varint, hash256, int_to_little_endian, little_endian_to_int, read_varint from script import Script from tx import Tx, SIGHASH_ALL, TxIn hex_tx = '0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a385aa0836f01d5e4789e6bd304d87221a000000db00483045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4ee942a8993701483045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402201475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b95abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c568700000000' hex_sec = '03b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb71' hex_der = '3045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e754022' hex_redeem_script = '475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152ae' sec = bytes.fromhex(hex_sec) der = bytes.fromhex(hex_der) redeem_script = Script.parse(BytesIO(bytes.fromhex(hex_redeem_script))) stream = BytesIO(bytes.fromhex(hex_tx)) tx = Tx.parse(stream, False) dat_s = int_to_little_endian(tx.version, 4) dat_s += encode_varint(len(tx.tx_ins)) dat_s += TxIn( prev_tx = tx.tx_ins[0].prev_tx, prev_index = tx.tx_ins[0].prev_index, script_sig = redeem_script, sequence = tx.tx_ins[0].sequence, ).serialize() dat_s += encode_varint(len(tx.tx_outs)) for tx_out in tx.tx_outs: dat_s += tx_out.serialize() dat_s += int_to_little_endian(tx.locktime, 4) dat_s += int_to_little_endian(SIGHASH_ALL, 4) dat_h256 = hash256(dat_s) dat_z = int.from_bytes(dat_h256, 'big')