mirror of
https://github.com/aljazceru/nutshell.git
synced 2025-12-20 18:44:20 +01:00
* refactor nut14 * format * tests * update tests * more tests * format * adjust wallet tests -> 32 bytes preimages * format
553 lines
22 KiB
Python
553 lines
22 KiB
Python
import asyncio
|
|
import copy
|
|
import hashlib
|
|
import secrets
|
|
from typing import List
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
|
|
from cashu.core.base import HTLCWitness, Proof
|
|
from cashu.core.crypto.secp import PrivateKey
|
|
from cashu.core.htlc import HTLCSecret
|
|
from cashu.core.migrations import migrate_databases
|
|
from cashu.core.p2pk import SigFlags
|
|
from cashu.core.secret import SecretKind
|
|
from cashu.wallet import migrations
|
|
from cashu.wallet.wallet import Wallet
|
|
from cashu.wallet.wallet import Wallet as Wallet1
|
|
from cashu.wallet.wallet import Wallet as Wallet2
|
|
from tests.conftest import SERVER_ENDPOINT
|
|
from tests.helpers import pay_if_regtest
|
|
|
|
|
|
async def assert_err(f, msg):
|
|
"""Compute f() and expect an error message 'msg'."""
|
|
try:
|
|
await f
|
|
except Exception as exc:
|
|
if msg not in str(exc.args[0]):
|
|
raise Exception(f"Expected error: {msg}, got: {exc.args[0]}")
|
|
return
|
|
raise Exception(f"Expected error: {msg}, got no error")
|
|
|
|
|
|
def assert_amt(proofs: List[Proof], expected: int):
|
|
"""Assert amounts the proofs contain."""
|
|
assert [p.amount for p in proofs] == expected
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
async def wallet1():
|
|
wallet1 = await Wallet1.with_db(
|
|
SERVER_ENDPOINT, "test_data/wallet_p2pk_1", "wallet1"
|
|
)
|
|
await migrate_databases(wallet1.db, migrations)
|
|
await wallet1.load_mint()
|
|
yield wallet1
|
|
|
|
|
|
@pytest_asyncio.fixture(scope="function")
|
|
async def wallet2():
|
|
wallet2 = await Wallet2.with_db(
|
|
SERVER_ENDPOINT, "test_data/wallet_p2pk_2", "wallet2"
|
|
)
|
|
await migrate_databases(wallet2.db, migrations)
|
|
wallet2.private_key = PrivateKey(secrets.token_bytes(32), raw=True)
|
|
await wallet2.load_mint()
|
|
yield wallet2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_htlc_secret(wallet1: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(preimage=preimage)
|
|
assert secret.data == preimage_hash
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_split(wallet1: Wallet, wallet2: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(preimage=preimage)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
for p in send_proofs:
|
|
assert HTLCSecret.deserialize(p.secret).data == preimage_hash
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_preimage(wallet1: Wallet, wallet2: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(preimage=preimage)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
for p in send_proofs:
|
|
p.witness = HTLCWitness(preimage=preimage).json()
|
|
await wallet2.redeem(send_proofs)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_wrong_preimage(wallet1: Wallet, wallet2: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=f"{preimage[:-5]}11111"
|
|
) # wrong preimage
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
for p in send_proofs:
|
|
p.witness = HTLCWitness(preimage=preimage).json()
|
|
await assert_err(
|
|
wallet1.redeem(send_proofs), "Mint Error: HTLC preimage does not match."
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_no_signature(wallet1: Wallet, wallet2: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage, hashlock_pubkeys=[pubkey_wallet1]
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
for p in send_proofs:
|
|
p.witness = HTLCWitness(preimage=preimage).json()
|
|
await assert_err(
|
|
wallet2.redeem(send_proofs),
|
|
"Mint Error: no signatures in proof.",
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_wrong_signature(wallet1: Wallet, wallet2: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage, hashlock_pubkeys=[pubkey_wallet1]
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
signatures = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s in zip(send_proofs, signatures):
|
|
p.witness = HTLCWitness(
|
|
preimage=preimage, signatures=[f"{s[:-5]}11111"]
|
|
).json() # wrong signature
|
|
|
|
await assert_err(
|
|
wallet2.redeem(send_proofs),
|
|
"Mint Error: signature threshold not met",
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_correct_signature(wallet1: Wallet, wallet2: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage, hashlock_pubkeys=[pubkey_wallet1]
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
signatures = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s in zip(send_proofs, signatures):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[s]).json()
|
|
|
|
await wallet1.redeem(send_proofs)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_2_of_1_signatures(wallet1: Wallet, wallet2: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet1, pubkey_wallet2],
|
|
hashlock_n_sigs=1,
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
signatures1 = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
signatures2 = wallet2.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s1, s2 in zip(send_proofs, signatures1, signatures2):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[s1, s2]).json()
|
|
|
|
await wallet2.redeem(send_proofs)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_2_of_2_signatures(wallet1: Wallet, wallet2: Wallet):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet1, pubkey_wallet2],
|
|
hashlock_n_sigs=2,
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
signatures1 = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
signatures2 = wallet2.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s1, s2 in zip(send_proofs, signatures1, signatures2):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[s1, s2]).json()
|
|
|
|
await wallet2.redeem(send_proofs)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_2_of_2_signatures_with_duplicate_pubkeys(
|
|
wallet1: Wallet, wallet2: Wallet
|
|
):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = pubkey_wallet1
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet1, pubkey_wallet2],
|
|
hashlock_n_sigs=2,
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
signatures1 = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
signatures2 = wallet2.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s1, s2 in zip(send_proofs, signatures1, signatures2):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[s1, s2]).json()
|
|
|
|
await assert_err(
|
|
wallet2.redeem(send_proofs),
|
|
"Mint Error: pubkeys must be unique.",
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_3_of_3_signatures_but_only_2_provided(
|
|
wallet1: Wallet, wallet2: Wallet
|
|
):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet1, pubkey_wallet2],
|
|
hashlock_n_sigs=3,
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
signatures1 = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
signatures2 = wallet2.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s1, s2 in zip(send_proofs, signatures1, signatures2):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[s1, s2]).json()
|
|
|
|
await assert_err(
|
|
wallet2.redeem(send_proofs),
|
|
"Mint Error: not enough pubkeys (2) or signatures (2) present for n_sigs (3).",
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_with_2_of_3_signatures_with_2_valid_and_1_invalid_provided(
|
|
wallet1: Wallet, wallet2: Wallet
|
|
):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
privatekey_wallet3 = PrivateKey(secrets.token_bytes(32), raw=True)
|
|
assert privatekey_wallet3.pubkey
|
|
pubkey_wallet3 = privatekey_wallet3.pubkey.serialize().hex()
|
|
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet1, pubkey_wallet2, pubkey_wallet3],
|
|
hashlock_n_sigs=2,
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
signatures1 = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
signatures2 = wallet2.signatures_proofs_sig_inputs(send_proofs)
|
|
signatures3 = [f"{s[:-5]}11111" for s in signatures1] # wrong signature
|
|
for p, s1, s2, s3 in zip(send_proofs, signatures1, signatures2, signatures3):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[s1, s2, s3]).json()
|
|
|
|
await wallet2.redeem(send_proofs)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_hashlock_wrong_signature_timelock_correct_signature(
|
|
wallet1: Wallet, wallet2: Wallet
|
|
):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet2],
|
|
locktime_seconds=2,
|
|
locktime_pubkeys=[pubkey_wallet1],
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
signatures = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s in zip(send_proofs, signatures):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[s]).json()
|
|
|
|
# should error because we used wallet2 signatures for the hash lock
|
|
await assert_err(
|
|
wallet1.redeem(send_proofs),
|
|
"Mint Error: signature threshold not met",
|
|
)
|
|
|
|
await asyncio.sleep(2)
|
|
# should succeed since lock time has passed and we provided wallet1 signature for timelock
|
|
await wallet1.redeem(send_proofs)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_hashlock_wrong_signature_timelock_wrong_signature(
|
|
wallet1: Wallet, wallet2: Wallet
|
|
):
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet2],
|
|
locktime_seconds=2,
|
|
locktime_pubkeys=[pubkey_wallet1, pubkey_wallet2],
|
|
locktime_n_sigs=2,
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
signatures = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s in zip(send_proofs, signatures):
|
|
p.witness = HTLCWitness(
|
|
preimage=preimage, signatures=[f"{s[:-5]}11111"]
|
|
).json() # wrong signature
|
|
|
|
# should error because we used wallet2 signatures for the hash lock
|
|
await assert_err(
|
|
wallet1.redeem(send_proofs),
|
|
"Mint Error: signature threshold not met. 0 < 1.",
|
|
)
|
|
|
|
await asyncio.sleep(2)
|
|
# should fail since lock time has passed and we provided not enough signatures for the timelock locktime_n_sigs
|
|
await assert_err(
|
|
wallet1.redeem(send_proofs),
|
|
"Mint Error: signature threshold not met. 1 < 2.",
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_redeem_timelock_2_of_2_signatures(wallet1: Wallet, wallet2: Wallet):
|
|
"""Testing the 2-of-2 timelock (refund) signature case."""
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet2],
|
|
locktime_seconds=2,
|
|
locktime_pubkeys=[pubkey_wallet1, pubkey_wallet2],
|
|
locktime_n_sigs=2,
|
|
)
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
send_proofs_copy = send_proofs.copy()
|
|
|
|
signatures = wallet1.signatures_proofs_sig_inputs(send_proofs)
|
|
for p, s in zip(send_proofs, signatures):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[s]).json()
|
|
|
|
# should error because we used wallet2 signatures for the hash lock
|
|
await assert_err(
|
|
wallet1.redeem(send_proofs),
|
|
"Mint Error: signature threshold not met. 0 < 1.",
|
|
)
|
|
|
|
await asyncio.sleep(2)
|
|
# locktime has passed
|
|
|
|
# should fail. lock time has passed but we provided only wallet1 signature for timelock, we need 2 though
|
|
await assert_err(
|
|
wallet1.redeem(send_proofs),
|
|
"Mint Error: not enough pubkeys (2) or signatures (1) present for n_sigs (2).",
|
|
)
|
|
|
|
# let's add the second signature
|
|
send_proofs_copy = wallet2.sign_p2pk_sig_inputs(send_proofs_copy)
|
|
|
|
# now we can redeem it
|
|
await wallet1.redeem(send_proofs_copy)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_sigall_behavior(wallet1: Wallet, wallet2: Wallet):
|
|
"""Test HTLC with SIG_ALL flag, requiring signatures on both inputs and outputs."""
|
|
# Mint tokens for testing
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
|
|
# Setup HTLC parameters
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
|
|
# Create HTLC lock with SIG_ALL flag
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=preimage,
|
|
hashlock_pubkeys=[pubkey_wallet2],
|
|
hashlock_n_sigs=1,
|
|
)
|
|
|
|
# Modify the secret to use SIG_ALL
|
|
secret.tags["sigflag"] = SigFlags.SIG_ALL.value
|
|
|
|
# Send tokens with this HTLC lock
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
|
|
# verify sigflag is SIG_ALL
|
|
assert HTLCSecret.from_secret(secret).kind == SecretKind.HTLC.value
|
|
assert HTLCSecret.from_secret(secret).sigflag == SigFlags.SIG_ALL
|
|
|
|
# first redeem fails because no preimage
|
|
await assert_err(
|
|
wallet2.redeem(send_proofs), "Mint Error: no HTLC preimage provided"
|
|
)
|
|
|
|
# we add the preimage to the proof
|
|
for p in send_proofs:
|
|
p.witness = HTLCWitness(preimage=preimage).json()
|
|
|
|
# Should succeed, redeem adds signatures to the proof
|
|
await wallet2.redeem(send_proofs)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_htlc_n_sigs_refund_locktime(wallet1: Wallet, wallet2: Wallet):
|
|
"""Test HTLC with n_sigs_refund parameter requiring multiple signatures for refund after locktime."""
|
|
# Create a third wallet for the third signature
|
|
wallet3 = await Wallet.with_db(
|
|
SERVER_ENDPOINT, "test_data/wallet_htlc_3", "wallet3"
|
|
)
|
|
await migrate_databases(wallet3.db, migrations)
|
|
wallet3.private_key = PrivateKey(secrets.token_bytes(32), raw=True)
|
|
await wallet3.load_mint()
|
|
|
|
# Mint tokens for testing
|
|
mint_quote = await wallet1.request_mint(64)
|
|
await pay_if_regtest(mint_quote.request)
|
|
await wallet1.mint(64, quote_id=mint_quote.quote)
|
|
|
|
# Setup parameters
|
|
preimage = "0000000000000000000000000000000000000000000000000000000000000000"
|
|
pubkey_wallet1 = await wallet1.create_p2pk_pubkey()
|
|
pubkey_wallet2 = await wallet2.create_p2pk_pubkey()
|
|
pubkey_wallet3 = await wallet3.create_p2pk_pubkey()
|
|
|
|
# Wrong preimage - making it so we can only spend via locktime
|
|
wrong_preimage = "1111111111111111111111111111111111111111111111111111111111111111"
|
|
|
|
# Create HTLC with:
|
|
# 1. Timelock in the past
|
|
# 2. Three refund pubkeys with 2-of-3 signature requirement
|
|
secret = await wallet1.create_htlc_lock(
|
|
preimage=wrong_preimage, # this ensures we can't redeem via preimage
|
|
hashlock_pubkeys=[pubkey_wallet2],
|
|
locktime_seconds=-200000,
|
|
locktime_pubkeys=[pubkey_wallet1, pubkey_wallet2, pubkey_wallet3],
|
|
locktime_n_sigs=2, # require 2 of 3 signatures for refund
|
|
)
|
|
|
|
# # Send tokens with this lock
|
|
_, send_proofs = await wallet1.swap_to_send(wallet1.proofs, 8, secret_lock=secret)
|
|
send_proofs_copy = copy.deepcopy(send_proofs)
|
|
|
|
# # First, try correct preimage but should fail as we're using wrong preimage hash
|
|
# for p in send_proofs:
|
|
# p.witness = HTLCWitness(preimage=preimage).json()
|
|
|
|
# await assert_err(
|
|
# wallet2.redeem(send_proofs), "Mint Error: HTLC preimage does not match"
|
|
# )
|
|
|
|
# # Wait for locktime to pass
|
|
# await asyncio.sleep(2)
|
|
|
|
# Try redeeming with only 1 signature after locktime
|
|
signatures1 = wallet1.signatures_proofs_sig_inputs(send_proofs_copy)
|
|
for p, sig in zip(send_proofs_copy, signatures1):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[sig]).json()
|
|
|
|
# Should fail because we need 2 signatures
|
|
await assert_err(
|
|
wallet1.redeem(send_proofs_copy),
|
|
"Mint Error: not enough pubkeys (3) or signatures (1) present for n_sigs (2)",
|
|
)
|
|
|
|
# Make a fresh copy and add 2 signatures
|
|
send_proofs_copy2 = copy.deepcopy(send_proofs)
|
|
signatures1 = wallet1.signatures_proofs_sig_inputs(send_proofs_copy2)
|
|
signatures2 = wallet2.signatures_proofs_sig_inputs(send_proofs_copy2)
|
|
|
|
for p, sig1, sig2 in zip(send_proofs_copy2, signatures1, signatures2):
|
|
p.witness = HTLCWitness(preimage=preimage, signatures=[sig1, sig2]).json()
|
|
|
|
# Should succeed with 2 of 3 signatures after locktime
|
|
await wallet1.redeem(send_proofs_copy2)
|