mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-06 10:48:11 +01:00
Fix BTC witness size estimate.
This commit is contained in:
@@ -351,7 +351,7 @@ class BTCInterface(CoinInterface):
|
||||
if self.sc._restrict_unknown_seed_wallets:
|
||||
ensure(addr_info['hdseedid'] == self._expect_seedid_hex, 'unexpected seedid')
|
||||
|
||||
def get_fee_rate(self, conf_target=2):
|
||||
def get_fee_rate(self, conf_target: int = 2):
|
||||
try:
|
||||
fee_rate = self.rpc_callback('estimatesmartfee', [conf_target])['feerate']
|
||||
assert (fee_rate > 0.0), 'Non positive feerate'
|
||||
@@ -410,8 +410,8 @@ class BTCInterface(CoinInterface):
|
||||
assert (len(pk) == 33)
|
||||
return self.pkh_to_address(hash160(pk))
|
||||
|
||||
def getNewSecretKey(self):
|
||||
return getSecretInt()
|
||||
def getNewSecretKey(self) -> bytes:
|
||||
return i2b(getSecretInt())
|
||||
|
||||
def getPubkey(self, privkey):
|
||||
return PublicKey.from_secret(privkey).format()
|
||||
@@ -486,7 +486,7 @@ class BTCInterface(CoinInterface):
|
||||
def fundSCLockTx(self, tx_bytes, feerate, vkbv=None):
|
||||
return self.fundTx(tx_bytes, feerate)
|
||||
|
||||
def extractScriptLockRefundScriptValues(self, script_bytes):
|
||||
def extractScriptLockRefundScriptValues(self, script_bytes: bytes):
|
||||
script_len = len(script_bytes)
|
||||
ensure(script_len > 73, 'Bad script length')
|
||||
ensure_op(script_bytes[0] == OP_IF)
|
||||
@@ -517,7 +517,7 @@ class BTCInterface(CoinInterface):
|
||||
|
||||
return pk1, pk2, csv_val, pk3
|
||||
|
||||
def genScriptLockRefundTxScript(self, Kal, Kaf, csv_val):
|
||||
def genScriptLockRefundTxScript(self, Kal, Kaf, csv_val) -> CScript:
|
||||
|
||||
Kal_enc = Kal if len(Kal) == 33 else self.encodePubkey(Kal)
|
||||
Kaf_enc = Kaf if len(Kaf) == 33 else self.encodePubkey(Kaf)
|
||||
@@ -553,7 +553,7 @@ class BTCInterface(CoinInterface):
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = int(tx_fee_rate * vsize // 1000)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
@@ -588,7 +588,7 @@ class BTCInterface(CoinInterface):
|
||||
dummy_witness_stack = self.getScriptLockRefundSpendTxDummyWitness(script_lock_refund)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = int(tx_fee_rate * vsize // 1000)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
@@ -624,7 +624,7 @@ class BTCInterface(CoinInterface):
|
||||
dummy_witness_stack = self.getScriptLockRefundSwipeTxDummyWitness(script_lock_refund)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = int(tx_fee_rate * vsize // 1000)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
@@ -633,7 +633,7 @@ class BTCInterface(CoinInterface):
|
||||
|
||||
return tx.serialize()
|
||||
|
||||
def createSCLockSpendTx(self, tx_lock_bytes, script_lock, pkh_dest, tx_fee_rate, vkbv=None):
|
||||
def createSCLockSpendTx(self, tx_lock_bytes, script_lock, pkh_dest, tx_fee_rate, vkbv=None, fee_info={}):
|
||||
tx_lock = self.loadTx(tx_lock_bytes)
|
||||
output_script = self.getScriptDest(script_lock)
|
||||
locked_n = findOutput(tx_lock, output_script)
|
||||
@@ -653,9 +653,14 @@ class BTCInterface(CoinInterface):
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = int(tx_fee_rate * vsize // 1000)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
fee_info['fee_paid'] = pay_fee
|
||||
fee_info['rate_used'] = tx_fee_rate
|
||||
fee_info['witness_bytes'] = witness_bytes
|
||||
fee_info['vsize'] = vsize
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
@@ -1070,7 +1075,7 @@ class BTCInterface(CoinInterface):
|
||||
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||
witness_bytes = 109
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = int(fee_rate * vsize // 1000)
|
||||
pay_fee = round(fee_rate * vsize / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, vsize, fee: {fee_rate}, {vsize}, {pay_fee}.')
|
||||
return pay_fee
|
||||
|
||||
@@ -1244,37 +1249,37 @@ class BTCInterface(CoinInterface):
|
||||
# Only one prevout exists
|
||||
return 0
|
||||
|
||||
def getScriptLockTxDummyWitness(self, script):
|
||||
def getScriptLockTxDummyWitness(self, script: bytes):
|
||||
return [
|
||||
b''.hex(),
|
||||
bytes(72).hex(),
|
||||
bytes(72).hex(),
|
||||
bytes(len(script)).hex()
|
||||
b'',
|
||||
bytes(72),
|
||||
bytes(72),
|
||||
bytes(len(script))
|
||||
]
|
||||
|
||||
def getScriptLockRefundSpendTxDummyWitness(self, script):
|
||||
def getScriptLockRefundSpendTxDummyWitness(self, script: bytes):
|
||||
return [
|
||||
b''.hex(),
|
||||
bytes(72).hex(),
|
||||
bytes(72).hex(),
|
||||
bytes((1,)).hex(),
|
||||
bytes(len(script)).hex()
|
||||
b'',
|
||||
bytes(72),
|
||||
bytes(72),
|
||||
bytes((1,)),
|
||||
bytes(len(script))
|
||||
]
|
||||
|
||||
def getScriptLockRefundSwipeTxDummyWitness(self, script):
|
||||
def getScriptLockRefundSwipeTxDummyWitness(self, script: bytes):
|
||||
return [
|
||||
bytes(72).hex(),
|
||||
b''.hex(),
|
||||
bytes(len(script)).hex()
|
||||
bytes(72),
|
||||
b'',
|
||||
bytes(len(script))
|
||||
]
|
||||
|
||||
def getWitnessStackSerialisedLength(self, witness_stack):
|
||||
length = getCompactSizeLen(len(witness_stack))
|
||||
for e in witness_stack:
|
||||
length += getWitnessElementLen(len(e) // 2) # hex -> bytes
|
||||
length += getWitnessElementLen(len(e))
|
||||
|
||||
# See core SerializeTransaction
|
||||
length += 32 + 4 + 1 + 4 # vinDummy
|
||||
length += 1 # vinDummy
|
||||
length += 1 # flags
|
||||
return length
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class DASHInterface(BTCInterface):
|
||||
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = int(fee_rate * size // 1000)
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
return pay_fee
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ class FIROInterface(BTCInterface):
|
||||
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = int(fee_rate * size // 1000)
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
return pay_fee
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ from basicswap.contrib.test_framework.script import (
|
||||
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
|
||||
)
|
||||
from basicswap.util import (
|
||||
i2b,
|
||||
ensure,
|
||||
make_int,
|
||||
TemporaryError,
|
||||
@@ -113,7 +112,7 @@ class PARTInterface(BTCInterface):
|
||||
def getWitnessStackSerialisedLength(self, witness_stack):
|
||||
length = getCompactSizeLen(len(witness_stack))
|
||||
for e in witness_stack:
|
||||
length += getWitnessElementLen(len(e) // 2) # hex -> bytes
|
||||
length += getWitnessElementLen(len(e))
|
||||
return length
|
||||
|
||||
def getWalletRestoreHeight(self) -> int:
|
||||
@@ -169,7 +168,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
def createSCLockTx(self, value: int, script: bytearray, vkbv) -> bytes:
|
||||
|
||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||
ephemeral_key = i2b(self.getNewSecretKey())
|
||||
ephemeral_key = self.getNewSecretKey()
|
||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||
assert (len(ephemeral_pubkey) == 33)
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
@@ -208,7 +207,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_bytes.hex()])
|
||||
assert (self.getTxid(tx_lock_bytes).hex() == lock_tx_obj['txid'])
|
||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||
ephemeral_key = i2b(self.getNewSecretKey())
|
||||
ephemeral_key = self.getNewSecretKey()
|
||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||
assert (len(ephemeral_pubkey) == 33)
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
@@ -231,9 +230,10 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
|
||||
# Set dummy witness data for fee estimation
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||
zero_change_key = i2b(self.getNewSecretKey())
|
||||
zero_change_key = self.getNewSecretKey()
|
||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||
inputs_info = {'0': {'value': input_blinded_info['amount'], 'blind': input_blinded_info['blind'], 'witnessstack': dummy_witness_stack}}
|
||||
outputs_info = rv['amounts']
|
||||
@@ -279,9 +279,10 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
|
||||
# Set dummy witness data for fee estimation
|
||||
dummy_witness_stack = self.getScriptLockRefundSpendTxDummyWitness(script_lock_refund)
|
||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||
zero_change_key = i2b(self.getNewSecretKey())
|
||||
zero_change_key = self.getNewSecretKey()
|
||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||
inputs_info = {'0': {'value': input_blinded_info['amount'], 'blind': input_blinded_info['blind'], 'witnessstack': dummy_witness_stack}}
|
||||
outputs_info = rv['amounts']
|
||||
@@ -483,9 +484,9 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
|
||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||
zero_change_key = i2b(self.getNewSecretKey())
|
||||
zero_change_key = self.getNewSecretKey()
|
||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||
inputs_info = {'0': {'value': blinded_info['amount'], 'blind': blinded_info['blind'], 'witnessstack': dummy_witness_stack}}
|
||||
inputs_info = {'0': {'value': blinded_info['amount'], 'blind': blinded_info['blind'], 'witnessstack': [x.hex() for x in dummy_witness_stack]}}
|
||||
outputs_info = rv['amounts']
|
||||
options = {
|
||||
'changepubkey': zero_change_pubkey.hex(),
|
||||
@@ -605,9 +606,10 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
|
||||
# Set dummy witness data for fee estimation
|
||||
dummy_witness_stack = self.getScriptLockRefundSwipeTxDummyWitness(script_lock_refund)
|
||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||
zero_change_key = i2b(self.getNewSecretKey())
|
||||
zero_change_key = self.getNewSecretKey()
|
||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||
inputs_info = {'0': {'value': input_blinded_info['amount'], 'blind': input_blinded_info['blind'], 'witnessstack': dummy_witness_stack}}
|
||||
outputs_info = rv['amounts']
|
||||
|
||||
@@ -95,7 +95,7 @@ class PIVXInterface(BTCInterface):
|
||||
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = int(fee_rate * size // 1000)
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
return pay_fee
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from coincurve.dleag import (
|
||||
from basicswap.interface import (
|
||||
Curves)
|
||||
from basicswap.util import (
|
||||
i2b,
|
||||
dumpj,
|
||||
ensure,
|
||||
make_int,
|
||||
@@ -206,12 +207,13 @@ class XMRInterface(CoinInterface):
|
||||
self.openWallet(self._wallet_filename)
|
||||
return self.rpc_wallet_cb('create_address', {'account_index': 0})['address']
|
||||
|
||||
def get_fee_rate(self, conf_target=2):
|
||||
def get_fee_rate(self, conf_target: int = 2):
|
||||
self._log.warning('TODO - estimate fee rate?')
|
||||
return 0.0, 'unused'
|
||||
|
||||
def getNewSecretKey(self) -> bytes:
|
||||
return edu.get_secret()
|
||||
# Note: Returned bytes are in big endian order
|
||||
return i2b(edu.get_secret())
|
||||
|
||||
def pubkey(self, key: bytes) -> bytes:
|
||||
return edf.scalarmult_B(key)
|
||||
|
||||
Reference in New Issue
Block a user