tests: Add prefunded itx and xmr protocol tests

This commit is contained in:
tecnovert
2022-12-11 01:26:42 +02:00
parent 80df3b1a34
commit 6860279faa
15 changed files with 564 additions and 39 deletions

View File

@@ -1194,16 +1194,22 @@ class BasicSwap(BaseApp):
ensure(amount_to > ci_to.min_amount(), 'To amount below min value for chain')
ensure(amount_to < ci_to.max_amount(), 'To amount above max value for chain')
def validateOfferLockValue(self, coin_from, coin_to, lock_type, lock_value):
def validateOfferLockValue(self, swap_type, coin_from, coin_to, lock_type, lock_value):
coin_from_has_csv = self.coin_clients[coin_from]['use_csv']
coin_to_has_csv = self.coin_clients[coin_to]['use_csv']
if lock_type == OfferMessage.SEQUENCE_LOCK_TIME:
ensure(lock_value >= self.min_sequence_lock_seconds and lock_value <= self.max_sequence_lock_seconds, 'Invalid lock_value time')
ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
if swap_type == SwapTypes.XMR_SWAP:
ensure(coin_from_has_csv, 'Coin from needs CSV activated.')
else:
ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
elif lock_type == OfferMessage.SEQUENCE_LOCK_BLOCKS:
ensure(lock_value >= 5 and lock_value <= 1000, 'Invalid lock_value blocks')
ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
if swap_type == SwapTypes.XMR_SWAP:
ensure(coin_from_has_csv, 'Coin from needs CSV activated.')
else:
ensure(coin_from_has_csv and coin_to_has_csv, 'Both coins need CSV activated.')
elif lock_type == TxLockTypes.ABS_LOCK_TIME:
# TODO: range?
ensure(not coin_from_has_csv or not coin_to_has_csv, 'Should use CSV.')
@@ -1262,7 +1268,7 @@ class BasicSwap(BaseApp):
self.validateSwapType(coin_from_t, coin_to_t, swap_type)
self.validateOfferAmounts(coin_from_t, coin_to_t, amount, rate, min_bid_amount)
self.validateOfferLockValue(coin_from_t, coin_to_t, lock_type, lock_value)
self.validateOfferLockValue(swap_type, coin_from_t, coin_to_t, lock_type, lock_value)
self.validateOfferValidTime(swap_type, coin_from_t, coin_to_t, valid_for_seconds)
offer_addr_to = self.getOfferAddressTo(extra_options)
@@ -3916,7 +3922,7 @@ class BasicSwap(BaseApp):
self.validateSwapType(coin_from, coin_to, offer_data.swap_type)
self.validateOfferAmounts(coin_from, coin_to, offer_data.amount_from, offer_data.rate, offer_data.min_bid_amount)
self.validateOfferLockValue(coin_from, coin_to, offer_data.lock_type, offer_data.lock_value)
self.validateOfferLockValue(offer_data.swap_type, coin_from, coin_to, offer_data.lock_type, offer_data.lock_value)
self.validateOfferValidTime(offer_data.swap_type, coin_from, coin_to, offer_data.time_valid)
ensure(msg['sent'] + offer_data.time_valid >= now, 'Offer expired')

View File

@@ -321,7 +321,7 @@ chainparams = {
'rpcport': 28888,
'pubkey_address': 65,
'script_address': 178,
'key_prefix': 185,
'key_prefix': 239,
'hrp': '',
'bip44': 1,
'min_amount': 1000,

View File

@@ -92,7 +92,7 @@ def findOutput(tx, script_pk: bytes):
return None
def find_vout_for_address_from_txobj(tx_obj, addr) -> int:
def find_vout_for_address_from_txobj(tx_obj, addr: str) -> int:
"""
Locate the vout index of the given transaction sending to the
given address. Raises runtime error exception if not found.
@@ -1033,7 +1033,15 @@ class BTCInterface(CoinInterface):
return None
'''
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)
self._log.info(f'BLockSpendTx fee_rate, vsize, fee: {fee_rate}, {vsize}, {pay_fee}.')
return pay_fee
def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee: int, restore_height: int) -> bytes:
self._log.info('spendBLockTx %s:\n', chain_b_lock_txid.hex())
wtx = self.rpc_callback('gettransaction', [chain_b_lock_txid.hex(), ])
lock_tx = self.loadTx(bytes.fromhex(wtx['hex']))
@@ -1054,12 +1062,8 @@ class BTCInterface(CoinInterface):
scriptSig=self.getScriptScriptSig(script_lock)))
tx.vout.append(self.txoType()(cb_swap_value, self.getScriptForPubkeyHash(pkh_to)))
witness_bytes = 109
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
pay_fee = int(b_fee * vsize // 1000)
pay_fee = self.getBLockSpendTxFee(tx, b_fee)
tx.vout[0].nValue = cb_swap_value - pay_fee
self._log.info('spendBLockTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
chain_b_lock_txid.hex(), b_fee, vsize, pay_fee)
b_lock_spend_tx = tx.serialize()
b_lock_spend_tx = self.signTxWithKey(b_lock_spend_tx, kbs)
@@ -1366,10 +1370,8 @@ class BTCInterface(CoinInterface):
except Exception as ex:
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
return None
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
return {'txid': txid_hex, 'amount': 0, 'height': rv['blockheight']}
return None

View File

@@ -9,6 +9,10 @@ from .btc import BTCInterface
from basicswap.chainparams import Coins
from basicswap.util.address import decodeAddress
from mnemonic import Mnemonic
from basicswap.contrib.test_framework.script import (
CScript,
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
)
class DASHInterface(BTCInterface):
@@ -37,8 +41,32 @@ class DASHInterface(BTCInterface):
return False
def withdrawCoin(self, value, addr_to, subfee):
params = [addr_to, value, '', '', subfee]
params = [addr_to, value, '', '', subfee, False, False, self._conf_target]
return self.rpc_callback('sendtoaddress', params)
def getSpendableBalance(self):
return self.make_int(self.rpc_callback('getwalletinfo')['balance'])
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
# Return P2PKH
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
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)
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
return pay_fee
def findTxnByHash(self, txid_hex: str):
# Only works for wallet txns
try:
rv = self.rpc_callback('gettransaction', [txid_hex])
except Exception as ex:
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
return None
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
block_height = self.getBlockHeader(rv['blockhash'])['height']
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
return None

View File

@@ -151,7 +151,6 @@ class FIROInterface(BTCInterface):
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
# Return P2PKH
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
def getScriptDest(self, script: bytearray) -> bytearray:
@@ -184,3 +183,27 @@ class FIROInterface(BTCInterface):
def getSpendableBalance(self):
return self.make_int(self.rpc_callback('getwalletinfo')['balance'])
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)
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
return pay_fee
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
key_wif = self.encodeKey(key)
rv = self.rpc_callback('signrawtransaction', [tx.hex(), [], [key_wif, ]])
return bytes.fromhex(rv['hex'])
def findTxnByHash(self, txid_hex: str):
# Only works for wallet txns
try:
rv = self.rpc_callback('gettransaction', [txid_hex])
except Exception as ex:
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
return None
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
block_height = self.getBlockHeader(rv['blockhash'])['height']
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
return None

View File

@@ -15,6 +15,13 @@ from .contrib.pivx_test_framework.messages import (
ToHex,
FromHex,
CTransaction)
from basicswap.contrib.test_framework.script import (
CScript,
OP_DUP,
OP_HASH160,
OP_CHECKSIG,
OP_EQUALVERIFY,
)
class PIVXInterface(BTCInterface):
@@ -80,3 +87,31 @@ class PIVXInterface(BTCInterface):
tx = CTransaction()
tx.deserialize(BytesIO(tx_bytes))
return tx
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
# Return P2PKH
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
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)
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
return pay_fee
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
key_wif = self.encodeKey(key)
rv = self.rpc_callback('signrawtransaction', [tx.hex(), [], [key_wif, ]])
return bytes.fromhex(rv['hex'])
def findTxnByHash(self, txid_hex: str):
# Only works for wallet txns
try:
rv = self.rpc_callback('gettransaction', [txid_hex])
except Exception as ex:
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
return None
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
block_height = self.getBlockHeader(rv['blockhash'])['height']
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
return None

View File

@@ -7,6 +7,9 @@
from basicswap.script import (
OpCodes,
)
from basicswap.util.script import (
getP2WSH,
)
class ProtocolInterface:
@@ -22,3 +25,7 @@ class ProtocolInterface:
def getMockScriptScriptPubkey(self, ci) -> bytearray:
script = self.getMockScript()
return ci.get_p2wsh_script_pubkey(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script)
def getMockAddrTo(self, ci):
script = self.getMockScript()
return ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)

View File

@@ -10,9 +10,6 @@ from basicswap.db import (
from basicswap.util import (
SerialiseNum,
)
from basicswap.util.script import (
getP2WSH,
)
from basicswap.script import (
OpCodes,
)
@@ -77,8 +74,7 @@ class AtomicSwapInterface(ProtocolInterface):
swap_type = SwapTypes.SELLER_FIRST
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
script = self.getMockScript()
addr_to = ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)
addr_to = self.getMockAddrTo(ci)
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
return bytes.fromhex(funded_tx)

View File

@@ -9,9 +9,6 @@ from sqlalchemy.orm import scoped_session
from basicswap.util import (
ensure,
)
from basicswap.util.script import (
getP2WSH,
)
from basicswap.chainparams import (
Coins,
)
@@ -104,8 +101,7 @@ class XmrSwapInterface(ProtocolInterface):
return CScript([2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG)])
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
script = self.getMockScript()
addr_to = ci.encode_p2wsh(getP2WSH(script)) if ci._use_segwit else ci.encode_p2sh(script)
addr_to = self.getMockAddrTo(ci)
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
return bytes.fromhex(funded_tx)