diff --git a/pycoin/coins/litecoin/__init__.py b/pycoin/coins/litecoin/__init__.py index ec98fe296..747ced3c4 100644 --- a/pycoin/coins/litecoin/__init__.py +++ b/pycoin/coins/litecoin/__init__.py @@ -1,6 +1,82 @@ -class Tx: - pass +from pycoin.block import Block +from pycoin.coins.bitcoin.Tx import Tx +from pycoin.satoshi.satoshi_int import parse_satoshi_int +from pycoin.satoshi.satoshi_string import parse_satoshi_string +from pycoin.satoshi.satoshi_struct import parse_struct -class Block: - pass +class LTCTx(Tx): + @classmethod + def parse(class_, f): + """Parse a Bitcoin transaction Tx. + :param f: a file-like object that contains a binary streamed transaction + """ + (version,) = parse_struct("L", f) + v1 = ord(f.read(1)) + is_segwit = v1 == 0 + has_mweb = False + if is_segwit: + flag = ord(f.read(1)) + if flag == 0: + raise ValueError("bad flag in segwit") + has_mweb = flag & 0x8 != 0 + is_segwit = flag & 0x1 != 0 + v1 = None + count = parse_satoshi_int(f, v=v1) + txs_in = [] + for i in range(count): + txs_in.append(class_.TxIn.parse(f)) + count = parse_satoshi_int(f) + txs_out = [] + for i in range(count): + txs_out.append(class_.TxOut.parse(f)) + if is_segwit: + for tx_in in txs_in: + stack = [] + count = parse_satoshi_int(f) + for i in range(count): + stack.append(parse_satoshi_string(f)) + tx_in.witness = stack + if has_mweb: + mweb_tx_type = ord(f.read(1)) + if mweb_tx_type: + # TODO: read serialized MWEB transaction data + # TODO: save this away + pass + (lock_time,) = parse_struct("L", f) + return class_(version, txs_in, txs_out, lock_time) + + +class LTCBlock(Block): + Tx = LTCTx + + @classmethod + def parse( + class_, + f, + include_transactions=True, + include_offsets=None, + check_merkle_hash=True, + ): + """ + Parse the Block from the file-like object + """ + block = class_.parse_as_header(f) + if include_transactions: + count = parse_struct("I", f)[0] + txs = block._parse_transactions(f, count, include_offsets=include_offsets) + block.set_txs(txs, check_merkle_hash=check_merkle_hash) + return block + + @classmethod + def _parse_transactions(class_, f, count, include_offsets=None): + txs = [] + for i in range(count): + if include_offsets: + offset_in_block = f.tell() + tx = class_.Tx.parse(f) + txs.append(tx) + if include_offsets: + tx.offset_in_block = offset_in_block + + return txs diff --git a/pycoin/symbols/ltc.py b/pycoin/symbols/ltc.py index 9dd5bdbdb..1e35e7045 100644 --- a/pycoin/symbols/ltc.py +++ b/pycoin/symbols/ltc.py @@ -1,7 +1,12 @@ +import struct + +from pycoin.coins.litecoin import LTCTx, LTCBlock from pycoin.networks.bitcoinish import create_bitcoinish_network +from pycoin.satoshi.satoshi_struct import parse_struct network = create_bitcoinish_network( network_name="Litecoin", symbol="LTC", subnet_name="mainnet", wif_prefix_hex="b0", sec_prefix="LTCSEC:", address_prefix_hex="30", pay_to_script_prefix_hex="32", - bip32_prv_prefix_hex="019d9cfe", bip32_pub_prefix_hex="019da462", bech32_hrp="ltc") + bip32_prv_prefix_hex="019d9cfe", bip32_pub_prefix_hex="019da462", bech32_hrp="ltc", + block=LTCBlock, tx=LTCTx) diff --git a/tests/litecoin_mweb_test.py b/tests/litecoin_mweb_test.py new file mode 100644 index 000000000..6817229f2 --- /dev/null +++ b/tests/litecoin_mweb_test.py @@ -0,0 +1,54 @@ +import unittest + +from pycoin.symbols.ltc import network + + +BLOCK_BLOB = bytes.fromhex( + "00000020aa9ae9f67b58a2293624a906cbcd538f54551b343ba11be46857eb779" + "44851a15ed60060b9d11ba304c819a03e72a2d6e2f56d329c49ddbe2167436964" + "86c65705245163ffff0f1e0000c23804010000000001010000000000000000000" + "000000000000000000000000000000000000000000000ffffffff1b039cf72600" + "000000737472616e676520656e6f7567680001000000ffffffff0239444125000" + "000001976a914fd22672c72b3e41b0cbc6052ccb048183aa1770f88ac00000000" + "00000000266a24aa21a9eda382d6ac11e42a2008bef156f8ea279a4b9d2e536ff" + "a119fd17c6facd8aeb7ac01200000000000000000000000000000000000000000" + "0000000000000000000000000000000001000000019eeb47d40744d90495a214d" + "1c41483a5fd2a7302552cfb9ed7b1538f701ed5240100000085483045022100e4" + "8ba89089eab3cf69c87cbe996e476bef6dc772500e7cc0be759909b4aae8c6022" + "071dc39100262cb9e6405ca1a579cdb1976423aec5308ba93d739437f5b3d1f03" + "012103d7dc4bc0c1b25a918bbda2431fc693c72940dbf81f8168413560e377e7d" + "293ef1976a9145bbb2e434952c656c061945a5243a40f867ca2c488acfdffffff" + "02102700000000000017a914614b04a4979915bc0b3d6560d8e47a24c9fd01728" + "770590e000000000017a914f35de0762f71f8dcbc23799838ca95250e6a8b3a87" + "0000000001000000000103584f0bb29fffb5bb7f6e3b8aee41b87a730ced08dc7" + "3c3c21aee96ea7f704342000000006a47304402205b6f956fedb21dbf83940375" + "dc3bf7e7049af2981602fd60d20fad1f089cfed1022012f54f02b957e7486e700" + "24c5b5bb980917398cc5de8862871b40d465663e72d01210279bc0d505cf765bb" + "837bf705cd6015d45e5b0fcb057e8adca26a311c76a76e55fdffffff9deab2a0e" + "f8d6f0ecc498c971d3865d46763726510a358d7c232ab9117b3009d000000006a" + "47304402200762be7239be9e54bc0f944643f2396c2af5b0d23c26b69237b257e" + "a2c9f9dbb0220324702aab637074c6ee0e63bdfe60a503d48df35b5398073b4aa" + "0d4b7d46562b01210279bc0d505cf765bb837bf705cd6015d45e5b0fcb057e8ad" + "ca26a311c76a76e55fdffffffc88912269e5fa3174f47f6d0cc587eeac00045ca" + "ed1c5ba53cda331a82c19558010000001716001493e3b88e659fe793dd14a3e31" + "6f62ac70418c20afdffffff0200c2eb0b000000001976a914701641f135c1706b" + "83f6aaef804582011d81fe4788acb49ceb0b0000000017a914bf0a93fed47f247" + "13270ac25d7ca9d86c03885128700000247304402204c0fed21dd655519211660" + "5ff4e220a8c76af6b7362127a203bde89289b6c6110220406748ec790859941d2" + "b90a488307ea95dffccbefc46f7d3c5072042ad9bb54e01210279bc0d505cf765" + "bb837bf705cd6015d45e5b0fcb057e8adca26a311c76a76e55000000000200000" + "0000801cb6ade7dca1add1bafbf69f78f8bbc3c63174c247f7cf6e00e645f8845" + "20841a0000000000ffffffff0163b98c3a9f320000225820c000742df1236e7d0" + "ac716292423b6f2333a4d7433ba9afd8173694e265a21c2000000000001809aee" + "1c4b539abb1d9c8d93de7944339217dfa788e55eea4256101070350bc44224548" + "b0000000000000000000000000000000000000000000000000000000000000000" + "c587d488ae5ad91ad2b19125de8702a816a87a7cf7123fb1fca1f58e342dc096e" + "ab97755e84e9abf23281be3c2d6865a643f95531dbec11f972bbd78961d49e500" + "000000000000000000000000000000000000000000000000000000000000009c4" + "700000000" +) + + +class LTCMWebTestCase(unittest.TestCase): + def test_parse_block(self): + block = network.Block.from_bin(BLOCK_BLOB)