From b250dda4bbcd464e1aedd2ff7453e26e185d665d Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 20 Jan 2025 19:35:00 +0000 Subject: [PATCH] progress; ledger_db fixture ready --- .../counterpartycore/lib/api/composer.py | 14 +- .../counterpartycore/pytest/conftest.py | 3 +- .../pytest/fixtures/__init__.py | 0 .../pytest/fixtures/ledgerdb.py | 185 ++++++++++-------- .../pytest/fixtures/params.py | 9 +- .../counterpartycore/pytest/fixtures_test.py | 13 +- .../counterpartycore/pytest/mocks/bitcoind.py | 24 ++- .../counterpartycore/pytest/mocks/ledgerdb.py | 3 +- 8 files changed, 158 insertions(+), 93 deletions(-) create mode 100644 counterparty-core/counterpartycore/pytest/fixtures/__init__.py diff --git a/counterparty-core/counterpartycore/lib/api/composer.py b/counterparty-core/counterpartycore/lib/api/composer.py index b42725784a..76364abb4f 100644 --- a/counterparty-core/counterpartycore/lib/api/composer.py +++ b/counterparty-core/counterpartycore/lib/api/composer.py @@ -588,13 +588,15 @@ def utxos_to_txins(utxos: list): # Composition # ################## +OP_0 = "00" +OP_PUSHBYTES_33 = "21" +OP_PUSHBYTES_72 = "48" +OP_PUSHBYTES_73 = "49" + DUMMY_DER_SIG = "3045" + "00" * 69 + "01" -DUMMY_REEDEM_SCRIPT = DUMMY_DER_SIG +DUMMY_REEDEM_SCRIPT = OP_PUSHBYTES_72 + DUMMY_DER_SIG DUMMY_PUBKEY = "03" + 32 * "00" DUMMY_SCHNORR_SIG = "00" * 65 -OP_0 = "00" -OP_PUSHBYTES_72 = "48" -OP_PUSHBYTES_33 = "21" # dummies script_sig from https://learnmeabitcoin.com/technical/script/ @@ -605,13 +607,12 @@ def get_dummy_script_sig(script_pub_key): script_sig = OP_PUSHBYTES_72 + DUMMY_DER_SIG elif output_type == "P2PKH": script_sig = OP_PUSHBYTES_72 + DUMMY_DER_SIG + OP_PUSHBYTES_33 + DUMMY_PUBKEY - print("P2PKH", script_sig) elif output_type == "P2MS": asm = script.script_to_asm(script_pub_key) required_signatures = asm[0] script_sig = OP_0 + (required_signatures * DUMMY_DER_SIG) elif output_type == "P2SH": - script_sig = OP_0 + OP_PUSHBYTES_72 + DUMMY_DER_SIG + OP_PUSHBYTES_72 + DUMMY_REEDEM_SCRIPT + script_sig = OP_0 + OP_PUSHBYTES_72 + DUMMY_DER_SIG + OP_PUSHBYTES_73 + DUMMY_REEDEM_SCRIPT if script_sig is not None: return Script.from_raw(script_sig) return None @@ -643,6 +644,7 @@ def generate_dummy_signed_tx(tx, selected_utxos): dummy_witness = get_dummy_witness(utxo["script_pub_key"]) if dummy_witness is not None: dummy_signed_tx.witnesses.append(dummy_witness) + dummy_signed_tx.has_segwit = True return dummy_signed_tx diff --git a/counterparty-core/counterpartycore/pytest/conftest.py b/counterparty-core/counterpartycore/pytest/conftest.py index e80c2023f6..0278066798 100644 --- a/counterparty-core/counterpartycore/pytest/conftest.py +++ b/counterparty-core/counterpartycore/pytest/conftest.py @@ -1,3 +1,4 @@ from .mocks.bitcoind import bitcoind_mock +from .mocks.ledgerdb import ledger_db -__all__ = ["bitcoind_mock"] +__all__ = ["bitcoind_mock", "ledger_db"] diff --git a/counterparty-core/counterpartycore/pytest/fixtures/__init__.py b/counterparty-core/counterpartycore/pytest/fixtures/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/counterparty-core/counterpartycore/pytest/fixtures/ledgerdb.py b/counterparty-core/counterpartycore/pytest/fixtures/ledgerdb.py index 1a7f1d34d8..bb69c2cc8d 100644 --- a/counterparty-core/counterpartycore/pytest/fixtures/ledgerdb.py +++ b/counterparty-core/counterpartycore/pytest/fixtures/ledgerdb.py @@ -3,6 +3,11 @@ from .params import ADDR, MULTISIGADDR, P2SH_ADDR, P2WPKH_ADDR from .params import DEFAULT_PARAMS as DP +# 3 or 4 elements by transaction: +# 1. Transaction name +# 2. Compose parameters +# 3. Transaction construct parameters +# 4. Optional list of protocol changes to disable UNITTEST_FIXTURE = [ [ "burn", @@ -484,11 +489,11 @@ [ "broadcast", { - "source": ADDR[6], - "timestamp": 1388000004, + "source": ADDR[4], + "timestamp": 1388000002, "value": 1, "fee_fraction": 0.0, - "text": "options 1", + "text": "options 0", }, {"encoding": "multisig"}, {"options_require_memo": True}, @@ -496,11 +501,11 @@ [ "broadcast", { - "source": ADDR[6], - "timestamp": 1388000004, + "source": ADDR[4], + "timestamp": 1388000003, "value": 1, "fee_fraction": 0.0, - "text": "options 1", + "text": "lock", }, {"encoding": "multisig"}, ], @@ -548,7 +553,7 @@ [ "issuance", { - "address": ADDR[2], + "source": ADDR[2], "asset": "DIVIDEND", "quantity": 100, "transfer_destination": None, @@ -572,7 +577,7 @@ [ "issuance", { - "address": ADDR[0], + "source": ADDR[0], "asset": "PARENT", "quantity": DP["quantity"] * 1, "transfer_destination": None, @@ -586,7 +591,7 @@ [ "issuance", { - "address": ADDR[0], + "source": ADDR[0], "asset": "PARENT.already.issued", "quantity": DP["quantity"] * 1, "transfer_destination": None, @@ -599,115 +604,139 @@ ], [ "fairminter", - (ADDR[0], "FREEFAIRMIN", "", 0, 1, 10), + { + "source": ADDR[0], + "asset": "FREEFAIRMIN", + "asset_parent": "", + "price": 0, + "quantity_by_price": 1, + "max_mint_per_tx": 10, + }, {"encoding": "opreturn"}, - {"short_tx_type_id": True, "fairminter": True}, ], [ "fairminter", - (ADDR[0], "PAIDFAIRMIN", "", 10, 1, 0), + { + "source": ADDR[0], + "asset": "PAIDFAIRMIN", + "asset_parent": "", + "price": 10, + "quantity_by_price": 1, + "max_mint_per_tx": 0, + }, {"encoding": "opreturn"}, - {"short_tx_type_id": True, "fairminter": True}, ], [ "fairmint", - (ADDR[0], "FREEFAIRMIN", 0), + {"source": ADDR[0], "asset": "FREEFAIRMIN", "quantity": 0}, {"encoding": "opreturn"}, - {"short_tx_type_id": True, "fairminter": True}, ], [ "fairminter", - ( - ADDR[0], # source - "RAIDFAIRMIN", # asset - "", # asset_parent - 10, # price - 1, # quantity_by_price - 10, # max_mint_per_tx - 30, # hard_cap - 20, # premint_quantity - ), + { + "source": ADDR[0], + "asset": "RAIDFAIRMIN", + "asset_parent": "", + "price": 10, + "quantity_by_price": 1, + "max_mint_per_tx": 10, + "hard_cap": 30, + "premint_quantity": 20, + }, {"encoding": "opreturn"}, - {"short_tx_type_id": True, "fairminter": True}, ], [ "fairminter", - ( - ADDR[0], # source - "QAIDFAIRMIN", # asset - "", # asset_parent, - 10, # price=0, - 1, # quantity_by_price - 0, # max_mint_per_tx, - 50, # hard_cap=0, - 20, # premint_quantity=0, - 0, # start_block=0, - 0, # end_block=0, - 20, # soft_cap=0, - 400000, # soft_cap_deadline_block=0, - 0.5, # minted_asset_commission=0.0, - ), + { + "source": ADDR[0], + "asset": "QAIDFAIRMIN", + "asset_parent": "", + "price": 10, + "quantity_by_price": 1, + "max_mint_per_tx": 0, + "hard_cap": 50, + "premint_quantity": 20, + "start_block": 0, + "end_block": 0, + "soft_cap": 20, + "soft_cap_deadline_block": 400000, + "minted_asset_commission": 0.5, + }, {"encoding": "opreturn"}, - {"short_tx_type_id": True, "fairminter": True}, ], [ "fairminter", - ( - ADDR[1], # source - "A160361285792733729", # asset - "", # asset_parent, - 10, # price=0, - 1, # quantity_by_price - 0, # max_mint_per_tx, - 50, # hard_cap=0, - 20, # premint_quantity=0, - 0, # start_block=0, - 0, # end_block=0, - 20, # soft_cap=0, - 310520, # soft_cap_deadline_block=0, - 0.3, # minted_asset_commission=0.0, - False, # burn_payment=False, - True, # lock_description=False, - True, # lock_quantity - True, # divisible - "softcap description", - ), - {"encoding": "multisig"}, - {"short_tx_type_id": True, "fairminter": True}, + { + "source": ADDR[1], + "asset": "A160361285792733729", + "asset_parent": "", + "price": 10, + "quantity_by_price": 1, + "max_mint_per_tx": 0, + "hard_cap": 50, + "premint_quantity": 20, + "start_block": 0, + "end_block": 0, + "soft_cap": 20, + "soft_cap_deadline_block": 310520, + "minted_asset_commission": 0.3, + "burn_payment": False, + "lock_description": True, + "lock_quantity": True, + "divisible": True, + "description": "softcap description", + }, + {"encoding": "multisig"}, ], [ "fairmint", - (ADDR[1], "A160361285792733729", 10), + {"source": ADDR[1], "asset": "A160361285792733729", "quantity": 10}, {"encoding": "opreturn"}, - {"short_tx_type_id": True, "fairminter": True}, ], [ "fairmint", - (ADDR[1], "A160361285792733729", 20), + {"source": ADDR[1], "asset": "A160361285792733729", "quantity": 20}, {"encoding": "opreturn"}, - {"short_tx_type_id": True, "fairminter": True}, ], [ "attach", - (ADDR[0], "XCP", 100), + {"source": ADDR[0], "asset": "XCP", "quantity": 100}, {"encoding": "multisig"}, - {"short_tx_type_id": True, "utxo_support": True, "spend_utxo_to_detach": True}, ], [ "attach", - ( - ADDR[0], - "DIVISIBLE", - 1, - ), + { + "source": ADDR[0], + "asset": "DIVISIBLE", + "quantity": 1, + }, {"encoding": "multisig"}, - {"short_tx_type_id": True, "utxo_support": True, "spend_utxo_to_detach": True}, ], [ "issuance", - (ADDR[5], "TESTDISP", 1000, None, False, None, None, "Test dispensers asset"), + { + "source": ADDR[5], + "asset": "TESTDISP", + "quantity": 1000, + "transfer_destination": None, + "divisible": False, + "lock": None, + "reset": None, + "description": "Test dispensers asset", + }, {"encoding": "multisig"}, ], - ["dispenser", (ADDR[5], "TESTDISP", 100, 100, 100, 0), {"encoding": "opreturn"}], + [ + "dispenser", + { + "source": ADDR[5], + "asset": "TESTDISP", + "give_quantity": 100, + "escrow_quantity": 100, + "mainchainrate": 100, + "status": 0, + }, + {"encoding": "opreturn"}, + ], ["mine_empty_blocks", 703], ] diff --git a/counterparty-core/counterpartycore/pytest/fixtures/params.py b/counterparty-core/counterpartycore/pytest/fixtures/params.py index a60879150a..a73f362ea6 100644 --- a/counterparty-core/counterpartycore/pytest/fixtures/params.py +++ b/counterparty-core/counterpartycore/pytest/fixtures/params.py @@ -108,4 +108,11 @@ "0282b886c087eb37dc8182f14ba6cc3e9485ed618b95804d44aecc17c300b585b0" ) -P2WPKH_ADDR = ["tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx"] + +P2WPKH_ADDR = ["bcrt1qfaw3f6ryl9jn4f5l0x7qdccxyl82snmwkrcfh9"] +DEFAULT_PARAMS["privkey"]["bcrt1qfaw3f6ryl9jn4f5l0x7qdccxyl82snmwkrcfh9"] = ( + "fff4e9f45244db7694296879d9ffbf03758104f0d614aac252a8d7b5eca3427d" +) +DEFAULT_PARAMS["pubkey"]["bcrt1qfaw3f6ryl9jn4f5l0x7qdccxyl82snmwkrcfh9"] = ( + "02653194070e7b2fb47eda68d0412341c5a88cddc7f7635929bb1d6996264fd4fd" +) diff --git a/counterparty-core/counterpartycore/pytest/fixtures_test.py b/counterparty-core/counterpartycore/pytest/fixtures_test.py index 6b5814a97a..e830190fa1 100644 --- a/counterparty-core/counterpartycore/pytest/fixtures_test.py +++ b/counterparty-core/counterpartycore/pytest/fixtures_test.py @@ -1,6 +1,13 @@ +from .fixtures import ledgerdb + + def test_ledger(ledger_db): cursor = ledger_db.cursor() - blocks = cursor.execute("SELECT * FROM blocks").fetchall() burns = cursor.execute("SELECT * FROM burns").fetchall() - assert len(blocks) == 11 - assert len(burns) == 1 + transactions = cursor.execute("SELECT * FROM transactions").fetchall() + + tx_count = len([tx for tx in ledgerdb.UNITTEST_FIXTURE if tx[0] != "mine_empty_blocks"]) + burn_count = len([tx for tx in ledgerdb.UNITTEST_FIXTURE if tx[0] == "burn"]) + + assert len(transactions) == tx_count + assert len(burns) == burn_count diff --git a/counterparty-core/counterpartycore/pytest/mocks/bitcoind.py b/counterparty-core/counterpartycore/pytest/mocks/bitcoind.py index f71ef06be2..b38a4db44e 100644 --- a/counterparty-core/counterpartycore/pytest/mocks/bitcoind.py +++ b/counterparty-core/counterpartycore/pytest/mocks/bitcoind.py @@ -14,6 +14,7 @@ class MockTransactions(metaclass=helpers.SingletonMeta): def __init__(self): self.source_by_txid = {} + self.address_and_value_by_utxo = {} def list_unspent(self, source, allow_unconfirmed_inputs=True): construct_params = {} @@ -23,13 +24,10 @@ def list_unspent(self, source, allow_unconfirmed_inputs=True): construct_params["pubkeys"] = ",".join(pubkeys) script_pub_key = composer.address_to_script_pub_key(source, network="regtest").to_hex() - print("script_pub_key:", script_pub_key) - print("asm", script.script_to_asm(script_pub_key)) # deterministic txid from the source txid = check.dhash_string(f"{source}{list(self.source_by_txid.values()).count(source)}") self.source_by_txid[txid] = source - print("New txid:", txid) return [ { @@ -48,6 +46,15 @@ def get_vin_info(self, vin): is_segwit = composer.is_segwit_output(script_pub_key) return value, script_pub_key, is_segwit + def get_utxo_address_and_value(self, utxo): + return self.address_and_value_by_utxo[utxo] + + def save_address_and_value(self, decoded_tx): + address = script.script_to_address2(decoded_tx["vout"][-1]["script_pub_key"]) + value = decoded_tx["vout"][-1]["value"] + utxo = f"{decoded_tx['tx_id']}:0" + self.address_and_value_by_utxo[utxo] = (address, value) + def list_unspent(source, allow_unconfirmed_inputs=True): return MockTransactions().list_unspent(source, allow_unconfirmed_inputs) @@ -57,6 +64,10 @@ def get_vin_info(vin): return MockTransactions().get_vin_info(vin) +def get_utxo_address_and_value(utxo): + return MockTransactions().get_utxo_address_and_value(utxo) + + def satoshis_per_vbyte(): return 2 @@ -81,7 +92,13 @@ def mine_empty_blocks(db, blocks): def sendrawtransaction(db, rawtransaction): decoded_tx = deserialize.deserialize_tx(rawtransaction, parse_vouts=True) + MockTransactions().save_address_and_value(decoded_tx) mine_block(db, [decoded_tx]) + cursor = db.cursor() + transaction = cursor.execute( + "SELECT * FROM transactions WHERE tx_hash = ?", (decoded_tx["tx_id"],) + ).fetchone() + assert transaction is not None def is_valid_der(der): @@ -102,6 +119,7 @@ def bitcoind_mock(monkeypatch): monkeypatch.setattr(f"{bitcoind_module}.list_unspent", list_unspent) monkeypatch.setattr(f"{bitcoind_module}.satoshis_per_vbyte", satoshis_per_vbyte) monkeypatch.setattr(f"{bitcoind_module}.get_vin_info", get_vin_info) + monkeypatch.setattr(f"{bitcoind_module}.get_utxo_address_and_value", get_utxo_address_and_value) monkeypatch.setattr(f"{gettxinfo_module}.is_valid_der", is_valid_der) monkeypatch.setattr(f"{backend_module}.search_pubkey", search_pubkey) monkeypatch.setattr("counterpartycore.lib.messages.bet.date_passed", lambda x: False) diff --git a/counterparty-core/counterpartycore/pytest/mocks/ledgerdb.py b/counterparty-core/counterpartycore/pytest/mocks/ledgerdb.py index f4696e0e41..b25147c537 100644 --- a/counterparty-core/counterpartycore/pytest/mocks/ledgerdb.py +++ b/counterparty-core/counterpartycore/pytest/mocks/ledgerdb.py @@ -80,7 +80,8 @@ def ledger_db(bitcoind_mock): # dummy sign the transaction tx = Transaction.from_raw(tx["rawtransaction"]) unspent = bitcoind_mock.list_unspent(params["source"]) - signed_tx = composer.generate_dummy_signed_tx(tx, unspent).serialize() + signed_tx = composer.generate_dummy_signed_tx(tx, unspent) + signed_tx = signed_tx.serialize() # broadcast transaction bitcoind_mock.sendrawtransaction(db, signed_tx) # re-enable all protocol changes