swaps: more precise tx size estimation for claim tx when RBF-ing
This commit is contained in:
@@ -11,7 +11,7 @@ from .crypto import sha256, hash_160
|
|||||||
from .ecc import ECPrivkey
|
from .ecc import ECPrivkey
|
||||||
from .bitcoin import (script_to_p2wsh, opcodes, p2wsh_nested_script, push_script,
|
from .bitcoin import (script_to_p2wsh, opcodes, p2wsh_nested_script, push_script,
|
||||||
is_segwit_address, construct_witness)
|
is_segwit_address, construct_witness)
|
||||||
from .transaction import PartialTxInput, PartialTxOutput, PartialTransaction
|
from .transaction import PartialTxInput, PartialTxOutput, PartialTransaction, Transaction, TxInput
|
||||||
from .transaction import script_GetOp, match_script_against_template, OPPushDataGeneric, OPPushDataPubkey
|
from .transaction import script_GetOp, match_script_against_template, OPPushDataGeneric, OPPushDataPubkey
|
||||||
from .util import log_exceptions
|
from .util import log_exceptions
|
||||||
from .lnutil import REDEEM_AFTER_DOUBLE_SPENT_DELAY, ln_dummy_address
|
from .lnutil import REDEEM_AFTER_DOUBLE_SPENT_DELAY, ln_dummy_address
|
||||||
@@ -246,7 +246,7 @@ class SwapManager(Logger):
|
|||||||
assert self.lnwatcher
|
assert self.lnwatcher
|
||||||
privkey = os.urandom(32)
|
privkey = os.urandom(32)
|
||||||
pubkey = ECPrivkey(privkey).get_public_key_bytes(compressed=True)
|
pubkey = ECPrivkey(privkey).get_public_key_bytes(compressed=True)
|
||||||
lnaddr, invoice = await self.lnworker.create_invoice(
|
lnaddr, invoice = self.lnworker.create_invoice(
|
||||||
amount_msat=lightning_amount_sat * 1000,
|
amount_msat=lightning_amount_sat * 1000,
|
||||||
message='swap',
|
message='swap',
|
||||||
expiry=3600 * 24,
|
expiry=3600 * 24,
|
||||||
@@ -535,9 +535,12 @@ class SwapManager(Logger):
|
|||||||
send_amount += self.get_claim_fee()
|
send_amount += self.get_claim_fee()
|
||||||
return send_amount
|
return send_amount
|
||||||
|
|
||||||
def get_swap_by_tx(self, tx):
|
def get_swap_by_tx(self, tx: Transaction) -> Optional[SwapData]:
|
||||||
# determine if tx is spending from a swap
|
# determine if tx is spending from a swap
|
||||||
txin = tx.inputs()[0]
|
txin = tx.inputs()[0]
|
||||||
|
return self.get_swap_by_claim_txin(txin)
|
||||||
|
|
||||||
|
def get_swap_by_claim_txin(self, txin: TxInput) -> Optional[SwapData]:
|
||||||
for key, swap in self.swaps.items():
|
for key, swap in self.swaps.items():
|
||||||
if txin.prevout.txid.hex() == swap.funding_txid:
|
if txin.prevout.txid.hex() == swap.funding_txid:
|
||||||
return swap
|
return swap
|
||||||
@@ -549,10 +552,29 @@ class SwapManager(Logger):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def sign_tx(self, tx, swap):
|
def add_txin_info(self, txin: PartialTxInput) -> None:
|
||||||
|
"""Add some info to a claim txin.
|
||||||
|
note: even without signing, this is useful for tx size estimation.
|
||||||
|
"""
|
||||||
|
swap = self.get_swap_by_claim_txin(txin)
|
||||||
|
if not swap:
|
||||||
|
return
|
||||||
|
preimage = swap.preimage if swap.is_reverse else 0
|
||||||
|
witness_script = swap.redeem_script
|
||||||
|
txin.script_type = 'p2wsh'
|
||||||
|
txin.num_sig = 1 # hack so that txin not considered "is_complete"
|
||||||
|
txin.script_sig = b''
|
||||||
|
txin.witness_script = witness_script
|
||||||
|
sig_dummy = b'\x00' * 71 # DER-encoded ECDSA sig, with low S and low R
|
||||||
|
witness = [sig_dummy, preimage, witness_script]
|
||||||
|
txin.witness_sizehint = len(bytes.fromhex(construct_witness(witness)))
|
||||||
|
|
||||||
|
def sign_tx(self, tx: PartialTransaction, swap: SwapData) -> None:
|
||||||
preimage = swap.preimage if swap.is_reverse else 0
|
preimage = swap.preimage if swap.is_reverse else 0
|
||||||
witness_script = swap.redeem_script
|
witness_script = swap.redeem_script
|
||||||
txin = tx.inputs()[0]
|
txin = tx.inputs()[0]
|
||||||
|
assert len(tx.inputs()) == 1, f"expected 1 input for swap claim tx. found {len(tx.inputs())}"
|
||||||
|
assert txin.prevout.txid.hex() == swap.funding_txid
|
||||||
txin.script_type = 'p2wsh'
|
txin.script_type = 'p2wsh'
|
||||||
txin.script_sig = b''
|
txin.script_sig = b''
|
||||||
txin.witness_script = witness_script
|
txin.witness_script = witness_script
|
||||||
|
|||||||
@@ -712,6 +712,8 @@ class Transaction:
|
|||||||
if not txin.is_segwit():
|
if not txin.is_segwit():
|
||||||
return construct_witness([])
|
return construct_witness([])
|
||||||
|
|
||||||
|
if estimate_size and txin.witness_sizehint is not None:
|
||||||
|
return '00' * txin.witness_sizehint
|
||||||
if _type in ('address', 'unknown') and estimate_size:
|
if _type in ('address', 'unknown') and estimate_size:
|
||||||
_type = cls.guess_txintype_from_address(txin.address)
|
_type = cls.guess_txintype_from_address(txin.address)
|
||||||
pubkeys, sig_list = cls.get_siglist(txin, estimate_size=estimate_size)
|
pubkeys, sig_list = cls.get_siglist(txin, estimate_size=estimate_size)
|
||||||
@@ -1213,6 +1215,7 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||||||
self.spent_txid = None # type: Optional[str] # txid of the spender
|
self.spent_txid = None # type: Optional[str] # txid of the spender
|
||||||
self._is_p2sh_segwit = None # type: Optional[bool] # None means unknown
|
self._is_p2sh_segwit = None # type: Optional[bool] # None means unknown
|
||||||
self._is_native_segwit = None # type: Optional[bool] # None means unknown
|
self._is_native_segwit = None # type: Optional[bool] # None means unknown
|
||||||
|
self.witness_sizehint = None # type: Optional[int] # byte size of serialized complete witness, for tx size est
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def utxo(self):
|
def utxo(self):
|
||||||
|
|||||||
@@ -1929,10 +1929,13 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
|
|||||||
address = self.get_txin_address(txin)
|
address = self.get_txin_address(txin)
|
||||||
# note: we add input utxos regardless of is_mine
|
# note: we add input utxos regardless of is_mine
|
||||||
self._add_input_utxo_info(txin, ignore_network_issues=ignore_network_issues, address=address)
|
self._add_input_utxo_info(txin, ignore_network_issues=ignore_network_issues, address=address)
|
||||||
if not self.is_mine(address):
|
is_mine = self.is_mine(address)
|
||||||
|
if not is_mine:
|
||||||
is_mine = self._learn_derivation_path_for_address_from_txinout(txin, address)
|
is_mine = self._learn_derivation_path_for_address_from_txinout(txin, address)
|
||||||
if not is_mine:
|
if not is_mine:
|
||||||
return
|
if self.lnworker:
|
||||||
|
self.lnworker.swap_manager.add_txin_info(txin)
|
||||||
|
return
|
||||||
# set script_type first, as later checks might rely on it:
|
# set script_type first, as later checks might rely on it:
|
||||||
txin.script_type = self.get_txin_type(address)
|
txin.script_type = self.get_txin_type(address)
|
||||||
txin.num_sig = self.m if isinstance(self, Multisig_Wallet) else 1
|
txin.num_sig = self.m if isinstance(self, Multisig_Wallet) else 1
|
||||||
|
|||||||
Reference in New Issue
Block a user