Decred test_008_gettxout
This commit is contained in:
		
							parent
							
								
									74c7072926
								
							
						
					
					
						commit
						d527ec4974
					
				@ -30,7 +30,7 @@ from typing import Optional
 | 
				
			|||||||
from sqlalchemy.orm import sessionmaker, scoped_session
 | 
					from sqlalchemy.orm import sessionmaker, scoped_session
 | 
				
			||||||
from sqlalchemy.orm.session import close_all_sessions
 | 
					from sqlalchemy.orm.session import close_all_sessions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .interface import Curves
 | 
					from .interface.base import Curves
 | 
				
			||||||
from .interface.part import PARTInterface, PARTInterfaceAnon, PARTInterfaceBlind
 | 
					from .interface.part import PARTInterface, PARTInterfaceAnon, PARTInterfaceBlind
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import __version__
 | 
					from . import __version__
 | 
				
			||||||
@ -2334,9 +2334,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
                msg_buf.proof_signature = proof_sig
 | 
					                msg_buf.proof_signature = proof_sig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if len(proof_utxos) > 0:
 | 
					                if len(proof_utxos) > 0:
 | 
				
			||||||
                    msg_buf.proof_utxos = bytes()
 | 
					                    msg_buf.proof_utxos = ci_to.encodeProofUtxos(proof_utxos)
 | 
				
			||||||
                    for utxo in proof_utxos:
 | 
					 | 
				
			||||||
                        msg_buf.proof_utxos += utxo[0] + utxo[1].to_bytes(2, 'big')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                contract_count = self.getNewContractId()
 | 
					                contract_count = self.getNewContractId()
 | 
				
			||||||
                msg_buf.pkhash_buyer = getKeyID(self.getContractPubkey(dt.datetime.fromtimestamp(now).date(), contract_count))
 | 
					                msg_buf.pkhash_buyer = getKeyID(self.getContractPubkey(dt.datetime.fromtimestamp(now).date(), contract_count))
 | 
				
			||||||
@ -3356,10 +3354,11 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ci = self.ci(coin_type)
 | 
					        ci = self.ci(coin_type)
 | 
				
			||||||
        if coin_type in (Coins.NAV, ):
 | 
					        if coin_type in (Coins.NAV, Coins.DCR):
 | 
				
			||||||
            wif_prefix = chainparams[coin_type][self.chain]['key_prefix']
 | 
					            wif_prefix = chainparams[coin_type][self.chain]['key_prefix']
 | 
				
			||||||
            prevout = ci.find_prevout_info(txn, txn_script)
 | 
					            prevout = ci.find_prevout_info(txn, txn_script)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
 | 
					            # TODO: Sign in bsx for all coins
 | 
				
			||||||
            wif_prefix = chainparams[Coins.PART][self.chain]['key_prefix']
 | 
					            wif_prefix = chainparams[Coins.PART][self.chain]['key_prefix']
 | 
				
			||||||
            txjs = self.callcoinrpc(Coins.PART, 'decoderawtransaction', [txn])
 | 
					            txjs = self.callcoinrpc(Coins.PART, 'decoderawtransaction', [txn])
 | 
				
			||||||
            if ci.using_segwit():
 | 
					            if ci.using_segwit():
 | 
				
			||||||
@ -3414,7 +3413,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
        options = {}
 | 
					        options = {}
 | 
				
			||||||
        if self.coin_clients[coin_type]['use_segwit']:
 | 
					        if self.coin_clients[coin_type]['use_segwit']:
 | 
				
			||||||
            options['force_segwit'] = True
 | 
					            options['force_segwit'] = True
 | 
				
			||||||
        if coin_type in (Coins.NAV, ):
 | 
					        if coin_type in (Coins.NAV, Coins.DCR):
 | 
				
			||||||
            refund_sig = ci.getTxSignature(refund_txn, prevout, privkey)
 | 
					            refund_sig = ci.getTxSignature(refund_txn, prevout, privkey)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            refund_sig = self.callcoinrpc(Coins.PART, 'createsignaturewithkey', [refund_txn, prevout, privkey, 'ALL', options])
 | 
					            refund_sig = self.callcoinrpc(Coins.PART, 'createsignaturewithkey', [refund_txn, prevout, privkey, 'ALL', options])
 | 
				
			||||||
@ -3432,7 +3431,7 @@ class BasicSwap(BaseApp):
 | 
				
			|||||||
            script += format(OpCodes.OP_PUSHDATA1, '02x') + format(len(txn_script), '02x') + txn_script.hex()
 | 
					            script += format(OpCodes.OP_PUSHDATA1, '02x') + format(len(txn_script), '02x') + txn_script.hex()
 | 
				
			||||||
            refund_txn = ci.setTxScriptSig(bytes.fromhex(refund_txn), 0, bytes.fromhex(script)).hex()
 | 
					            refund_txn = ci.setTxScriptSig(bytes.fromhex(refund_txn), 0, bytes.fromhex(script)).hex()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if coin_type in (Coins.NAV, ):
 | 
					        if coin_type in (Coins.NAV, Coins.DCR):
 | 
				
			||||||
            # Only checks signature
 | 
					            # Only checks signature
 | 
				
			||||||
            ro = ci.verifyRawTransaction(refund_txn, [prevout])
 | 
					            ro = ci.verifyRawTransaction(refund_txn, [prevout])
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
 | 
				
			|||||||
@ -4,14 +4,9 @@
 | 
				
			|||||||
# Distributed under the MIT software license, see the accompanying
 | 
					# Distributed under the MIT software license, see the accompanying
 | 
				
			||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
					# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import threading
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from enum import IntEnum
 | 
					from enum import IntEnum
 | 
				
			||||||
from .util import (
 | 
					from .util import (
 | 
				
			||||||
    COIN,
 | 
					    COIN,
 | 
				
			||||||
    make_int,
 | 
					 | 
				
			||||||
    format_amount,
 | 
					 | 
				
			||||||
    TemporaryError,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
XMR_COIN = 10 ** 12
 | 
					XMR_COIN = 10 ** 12
 | 
				
			||||||
@ -180,11 +175,11 @@ chainparams = {
 | 
				
			|||||||
            'max_amount': 100000 * COIN,
 | 
					            'max_amount': 100000 * COIN,
 | 
				
			||||||
            'name': 'testnet3',
 | 
					            'name': 'testnet3',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        'regtest': {
 | 
					        'regtest': {  # simnet
 | 
				
			||||||
            'rpcport': 18656,
 | 
					            'rpcport': 18656,
 | 
				
			||||||
            'pubkey_address': 0x0e00,
 | 
					            'pubkey_address': 0x0e91,
 | 
				
			||||||
            'script_address': 0x0ddb,
 | 
					            'script_address': 0x0e6c,
 | 
				
			||||||
            'key_prefix': 0x22fe,
 | 
					            'key_prefix': 0x2307,
 | 
				
			||||||
            'bip44': 1,
 | 
					            'bip44': 1,
 | 
				
			||||||
            'min_amount': 1000,
 | 
					            'min_amount': 1000,
 | 
				
			||||||
            'max_amount': 100000 * COIN,
 | 
					            'max_amount': 100000 * COIN,
 | 
				
			||||||
@ -422,89 +417,3 @@ def getCoinIdFromTicker(ticker):
 | 
				
			|||||||
        return ticker_map[ticker.lower()]
 | 
					        return ticker_map[ticker.lower()]
 | 
				
			||||||
    except Exception:
 | 
					    except Exception:
 | 
				
			||||||
        raise ValueError('Unknown coin')
 | 
					        raise ValueError('Unknown coin')
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class CoinInterface:
 | 
					 | 
				
			||||||
    def __init__(self, network):
 | 
					 | 
				
			||||||
        self.setDefaults()
 | 
					 | 
				
			||||||
        self._network = network
 | 
					 | 
				
			||||||
        self._mx_wallet = threading.Lock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setDefaults(self):
 | 
					 | 
				
			||||||
        self._unknown_wallet_seed = True
 | 
					 | 
				
			||||||
        self._restore_height = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def make_int(self, amount_in: int, r: int = 0) -> int:
 | 
					 | 
				
			||||||
        return make_int(amount_in, self.exp(), r=r)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def format_amount(self, amount_in, conv_int=False, r=0):
 | 
					 | 
				
			||||||
        amount_int = make_int(amount_in, self.exp(), r=r) if conv_int else amount_in
 | 
					 | 
				
			||||||
        return format_amount(amount_int, self.exp())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def coin_name(self) -> str:
 | 
					 | 
				
			||||||
        coin_chainparams = chainparams[self.coin_type()]
 | 
					 | 
				
			||||||
        if coin_chainparams.get('use_ticker_as_name', False):
 | 
					 | 
				
			||||||
            return coin_chainparams['ticker']
 | 
					 | 
				
			||||||
        return coin_chainparams['name'].capitalize()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def ticker(self) -> str:
 | 
					 | 
				
			||||||
        ticker = chainparams[self.coin_type()]['ticker']
 | 
					 | 
				
			||||||
        if self._network == 'testnet':
 | 
					 | 
				
			||||||
            ticker = 't' + ticker
 | 
					 | 
				
			||||||
        elif self._network == 'regtest':
 | 
					 | 
				
			||||||
            ticker = 'rt' + ticker
 | 
					 | 
				
			||||||
        return ticker
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getExchangeTicker(self, exchange_name: str) -> str:
 | 
					 | 
				
			||||||
        return chainparams[self.coin_type()]['ticker']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getExchangeName(self, exchange_name: str) -> str:
 | 
					 | 
				
			||||||
        return chainparams[self.coin_type()]['name']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def ticker_mainnet(self) -> str:
 | 
					 | 
				
			||||||
        ticker = chainparams[self.coin_type()]['ticker']
 | 
					 | 
				
			||||||
        return ticker
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def min_amount(self) -> int:
 | 
					 | 
				
			||||||
        return chainparams[self.coin_type()][self._network]['min_amount']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def max_amount(self) -> int:
 | 
					 | 
				
			||||||
        return chainparams[self.coin_type()][self._network]['max_amount']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setWalletSeedWarning(self, value: bool) -> None:
 | 
					 | 
				
			||||||
        self._unknown_wallet_seed = value
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setWalletRestoreHeight(self, value: int) -> None:
 | 
					 | 
				
			||||||
        self._restore_height = value
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def knownWalletSeed(self) -> bool:
 | 
					 | 
				
			||||||
        return not self._unknown_wallet_seed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def chainparams(self):
 | 
					 | 
				
			||||||
        return chainparams[self.coin_type()]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def chainparams_network(self):
 | 
					 | 
				
			||||||
        return chainparams[self.coin_type()][self._network]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def has_segwit(self) -> bool:
 | 
					 | 
				
			||||||
        return chainparams[self.coin_type()].get('has_segwit', True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def is_transient_error(self, ex) -> bool:
 | 
					 | 
				
			||||||
        if isinstance(ex, TemporaryError):
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        str_error: str = str(ex).lower()
 | 
					 | 
				
			||||||
        if 'not enough unlocked money' in str_error:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        if 'no unlocked balance' in str_error:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        if 'transaction was rejected by daemon' in str_error:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        if 'invalid unlocked_balance' in str_error:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        if 'daemon is busy' in str_error:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        if 'timed out' in str_error:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        if 'request-sent' in str_error:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Copyright (c) 2023 tecnovert
 | 
					 | 
				
			||||||
# Distributed under the MIT software license, see the accompanying
 | 
					 | 
				
			||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from enum import IntEnum
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Curves(IntEnum):
 | 
					 | 
				
			||||||
    secp256k1 = 1
 | 
					 | 
				
			||||||
    ed25519 = 2
 | 
					 | 
				
			||||||
							
								
								
									
										148
									
								
								basicswap/interface/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								basicswap/interface/base.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,148 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2024 tecnovert
 | 
				
			||||||
 | 
					# Distributed under the MIT software license, see the accompanying
 | 
				
			||||||
 | 
					# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import threading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from enum import IntEnum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from basicswap.chainparams import (
 | 
				
			||||||
 | 
					    chainparams,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.util import (
 | 
				
			||||||
 | 
					    ensure,
 | 
				
			||||||
 | 
					    i2b, b2i,
 | 
				
			||||||
 | 
					    make_int,
 | 
				
			||||||
 | 
					    format_amount,
 | 
				
			||||||
 | 
					    TemporaryError,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.util.ecc import (
 | 
				
			||||||
 | 
					    ep,
 | 
				
			||||||
 | 
					    getSecretInt,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from coincurve.dleag import (
 | 
				
			||||||
 | 
					    verify_secp256k1_point
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from coincurve.keys import (
 | 
				
			||||||
 | 
					    PublicKey,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Curves(IntEnum):
 | 
				
			||||||
 | 
					    secp256k1 = 1
 | 
				
			||||||
 | 
					    ed25519 = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CoinInterface:
 | 
				
			||||||
 | 
					    def __init__(self, network):
 | 
				
			||||||
 | 
					        self.setDefaults()
 | 
				
			||||||
 | 
					        self._network = network
 | 
				
			||||||
 | 
					        self._mx_wallet = threading.Lock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setDefaults(self):
 | 
				
			||||||
 | 
					        self._unknown_wallet_seed = True
 | 
				
			||||||
 | 
					        self._restore_height = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def make_int(self, amount_in: int, r: int = 0) -> int:
 | 
				
			||||||
 | 
					        return make_int(amount_in, self.exp(), r=r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_amount(self, amount_in, conv_int=False, r=0):
 | 
				
			||||||
 | 
					        amount_int = make_int(amount_in, self.exp(), r=r) if conv_int else amount_in
 | 
				
			||||||
 | 
					        return format_amount(amount_int, self.exp())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def coin_name(self) -> str:
 | 
				
			||||||
 | 
					        coin_chainparams = chainparams[self.coin_type()]
 | 
				
			||||||
 | 
					        if coin_chainparams.get('use_ticker_as_name', False):
 | 
				
			||||||
 | 
					            return coin_chainparams['ticker']
 | 
				
			||||||
 | 
					        return coin_chainparams['name'].capitalize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def ticker(self) -> str:
 | 
				
			||||||
 | 
					        ticker = chainparams[self.coin_type()]['ticker']
 | 
				
			||||||
 | 
					        if self._network == 'testnet':
 | 
				
			||||||
 | 
					            ticker = 't' + ticker
 | 
				
			||||||
 | 
					        elif self._network == 'regtest':
 | 
				
			||||||
 | 
					            ticker = 'rt' + ticker
 | 
				
			||||||
 | 
					        return ticker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getExchangeTicker(self, exchange_name: str) -> str:
 | 
				
			||||||
 | 
					        return chainparams[self.coin_type()]['ticker']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getExchangeName(self, exchange_name: str) -> str:
 | 
				
			||||||
 | 
					        return chainparams[self.coin_type()]['name']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def ticker_mainnet(self) -> str:
 | 
				
			||||||
 | 
					        ticker = chainparams[self.coin_type()]['ticker']
 | 
				
			||||||
 | 
					        return ticker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def min_amount(self) -> int:
 | 
				
			||||||
 | 
					        return chainparams[self.coin_type()][self._network]['min_amount']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def max_amount(self) -> int:
 | 
				
			||||||
 | 
					        return chainparams[self.coin_type()][self._network]['max_amount']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setWalletSeedWarning(self, value: bool) -> None:
 | 
				
			||||||
 | 
					        self._unknown_wallet_seed = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setWalletRestoreHeight(self, value: int) -> None:
 | 
				
			||||||
 | 
					        self._restore_height = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def knownWalletSeed(self) -> bool:
 | 
				
			||||||
 | 
					        return not self._unknown_wallet_seed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def chainparams(self):
 | 
				
			||||||
 | 
					        return chainparams[self.coin_type()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def chainparams_network(self):
 | 
				
			||||||
 | 
					        return chainparams[self.coin_type()][self._network]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def has_segwit(self) -> bool:
 | 
				
			||||||
 | 
					        return chainparams[self.coin_type()].get('has_segwit', True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_transient_error(self, ex) -> bool:
 | 
				
			||||||
 | 
					        if isinstance(ex, TemporaryError):
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        str_error: str = str(ex).lower()
 | 
				
			||||||
 | 
					        if 'not enough unlocked money' in str_error:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        if 'no unlocked balance' in str_error:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        if 'transaction was rejected by daemon' in str_error:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        if 'invalid unlocked_balance' in str_error:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        if 'daemon is busy' in str_error:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        if 'timed out' in str_error:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        if 'request-sent' in str_error:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setConfTarget(self, new_conf_target: int) -> None:
 | 
				
			||||||
 | 
					        ensure(new_conf_target >= 1 and new_conf_target < 33, 'Invalid conf_target value')
 | 
				
			||||||
 | 
					        self._conf_target = new_conf_target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def walletRestoreHeight(self) -> int:
 | 
				
			||||||
 | 
					        return self._restore_height
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Secp256k1Interface(CoinInterface):
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def curve_type():
 | 
				
			||||||
 | 
					        return Curves.secp256k1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getNewSecretKey(self) -> bytes:
 | 
				
			||||||
 | 
					        return i2b(getSecretInt())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getPubkey(self, privkey):
 | 
				
			||||||
 | 
					        return PublicKey.from_secret(privkey).format()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verifyKey(self, k: bytes) -> bool:
 | 
				
			||||||
 | 
					        i = b2i(k)
 | 
				
			||||||
 | 
					        return (i < ep.o and i > 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verifyPubkey(self, pubkey_bytes: bytes) -> bool:
 | 
				
			||||||
 | 
					        return verify_secp256k1_point(pubkey_bytes)
 | 
				
			||||||
@ -5,24 +5,32 @@
 | 
				
			|||||||
# Distributed under the MIT software license, see the accompanying
 | 
					# Distributed under the MIT software license, see the accompanying
 | 
				
			||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
					# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import json
 | 
					 | 
				
			||||||
import base64
 | 
					import base64
 | 
				
			||||||
import hashlib
 | 
					import hashlib
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import traceback
 | 
					import traceback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from basicswap.contrib.test_framework import segwit_addr
 | 
					from basicswap.basicswap_util import (
 | 
				
			||||||
 | 
					    getVoutByAddress,
 | 
				
			||||||
from basicswap.interface import (
 | 
					    getVoutByScriptPubKey,
 | 
				
			||||||
    Curves)
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.contrib.test_framework import (
 | 
				
			||||||
 | 
					    segwit_addr,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.interface.base import (
 | 
				
			||||||
 | 
					    Secp256k1Interface,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from basicswap.util import (
 | 
					from basicswap.util import (
 | 
				
			||||||
    ensure,
 | 
					    ensure,
 | 
				
			||||||
    b2h, i2b, b2i, i2h)
 | 
					    b2h, i2b, b2i, i2h,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from basicswap.util.ecc import (
 | 
					from basicswap.util.ecc import (
 | 
				
			||||||
    ep,
 | 
					    ep,
 | 
				
			||||||
    pointToCPK, CPKToPoint,
 | 
					    pointToCPK, CPKToPoint,
 | 
				
			||||||
    getSecretInt)
 | 
					)
 | 
				
			||||||
from basicswap.util.script import (
 | 
					from basicswap.util.script import (
 | 
				
			||||||
    decodeScriptNum,
 | 
					    decodeScriptNum,
 | 
				
			||||||
    getCompactSizeLen,
 | 
					    getCompactSizeLen,
 | 
				
			||||||
@ -42,14 +50,14 @@ from basicswap.util.crypto import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
from coincurve.keys import (
 | 
					from coincurve.keys import (
 | 
				
			||||||
    PrivateKey,
 | 
					    PrivateKey,
 | 
				
			||||||
    PublicKey)
 | 
					    PublicKey,
 | 
				
			||||||
from coincurve.dleag import (
 | 
					)
 | 
				
			||||||
    verify_secp256k1_point)
 | 
					 | 
				
			||||||
from coincurve.ecdsaotves import (
 | 
					from coincurve.ecdsaotves import (
 | 
				
			||||||
    ecdsaotves_enc_sign,
 | 
					    ecdsaotves_enc_sign,
 | 
				
			||||||
    ecdsaotves_enc_verify,
 | 
					    ecdsaotves_enc_verify,
 | 
				
			||||||
    ecdsaotves_dec_sig,
 | 
					    ecdsaotves_dec_sig,
 | 
				
			||||||
    ecdsaotves_rec_enc_key)
 | 
					    ecdsaotves_rec_enc_key
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from basicswap.contrib.test_framework.messages import (
 | 
					from basicswap.contrib.test_framework.messages import (
 | 
				
			||||||
    COIN,
 | 
					    COIN,
 | 
				
			||||||
@ -70,12 +78,13 @@ from basicswap.contrib.test_framework.script import (
 | 
				
			|||||||
    OP_DROP,
 | 
					    OP_DROP,
 | 
				
			||||||
    OP_HASH160, OP_EQUAL,
 | 
					    OP_HASH160, OP_EQUAL,
 | 
				
			||||||
    SIGHASH_ALL,
 | 
					    SIGHASH_ALL,
 | 
				
			||||||
    SegwitV0SignatureHash)
 | 
					    SegwitV0SignatureHash,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from basicswap.basicswap_util import (
 | 
					from basicswap.basicswap_util import (
 | 
				
			||||||
    TxLockTypes)
 | 
					    TxLockTypes
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from basicswap.chainparams import CoinInterface, Coins
 | 
					from basicswap.chainparams import Coins
 | 
				
			||||||
from basicswap.rpc import make_rpc_func, openrpc
 | 
					from basicswap.rpc import make_rpc_func, openrpc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -111,25 +120,6 @@ def find_vout_for_address_from_txobj(tx_obj, addr: str) -> int:
 | 
				
			|||||||
    raise RuntimeError("Vout not found for address: txid={}, addr={}".format(tx_obj['txid'], addr))
 | 
					    raise RuntimeError("Vout not found for address: txid={}, addr={}".format(tx_obj['txid'], addr))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Secp256k1Interface(CoinInterface):
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def curve_type():
 | 
					 | 
				
			||||||
        return Curves.secp256k1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getNewSecretKey(self) -> bytes:
 | 
					 | 
				
			||||||
        return i2b(getSecretInt())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getPubkey(self, privkey):
 | 
					 | 
				
			||||||
        return PublicKey.from_secret(privkey).format()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def verifyKey(self, k: bytes) -> bool:
 | 
					 | 
				
			||||||
        i = b2i(k)
 | 
					 | 
				
			||||||
        return (i < ep.o and i > 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def verifyPubkey(self, pubkey_bytes: bytes) -> bool:
 | 
					 | 
				
			||||||
        return verify_secp256k1_point(pubkey_bytes)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class BTCInterface(Secp256k1Interface):
 | 
					class BTCInterface(Secp256k1Interface):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
@ -185,7 +175,7 @@ class BTCInterface(Secp256k1Interface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def getExpectedSequence(lockType: int, lockVal: int) -> int:
 | 
					    def getExpectedSequence(lockType: int, lockVal: int) -> int:
 | 
				
			||||||
        assert (lockVal >= 1), 'Bad lockVal'
 | 
					        ensure(lockVal >= 1, 'Bad lockVal')
 | 
				
			||||||
        if lockType == TxLockTypes.SEQUENCE_LOCK_BLOCKS:
 | 
					        if lockType == TxLockTypes.SEQUENCE_LOCK_BLOCKS:
 | 
				
			||||||
            return lockVal
 | 
					            return lockVal
 | 
				
			||||||
        if lockType == TxLockTypes.SEQUENCE_LOCK_TIME:
 | 
					        if lockType == TxLockTypes.SEQUENCE_LOCK_TIME:
 | 
				
			||||||
@ -271,10 +261,6 @@ class BTCInterface(Secp256k1Interface):
 | 
				
			|||||||
    def close_rpc(self, rpc_conn):
 | 
					    def close_rpc(self, rpc_conn):
 | 
				
			||||||
        rpc_conn.close()
 | 
					        rpc_conn.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setConfTarget(self, new_conf_target: int) -> None:
 | 
					 | 
				
			||||||
        ensure(new_conf_target >= 1 and new_conf_target < 33, 'Invalid conf_target value')
 | 
					 | 
				
			||||||
        self._conf_target = new_conf_target
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def testDaemonRPC(self, with_wallet=True) -> None:
 | 
					    def testDaemonRPC(self, with_wallet=True) -> None:
 | 
				
			||||||
        self.rpc_wallet('getwalletinfo' if with_wallet else 'getblockchaininfo')
 | 
					        self.rpc_wallet('getwalletinfo' if with_wallet else 'getblockchaininfo')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -321,9 +307,6 @@ class BTCInterface(Secp256k1Interface):
 | 
				
			|||||||
        rv['locked_utxos'] = len(self.rpc_wallet('listlockunspent'))
 | 
					        rv['locked_utxos'] = len(self.rpc_wallet('listlockunspent'))
 | 
				
			||||||
        return rv
 | 
					        return rv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def walletRestoreHeight(self) -> int:
 | 
					 | 
				
			||||||
        return self._restore_height
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getWalletRestoreHeight(self) -> int:
 | 
					    def getWalletRestoreHeight(self) -> int:
 | 
				
			||||||
        start_time = self.rpc_wallet('getwalletinfo')['keypoololdest']
 | 
					        start_time = self.rpc_wallet('getwalletinfo')['keypoololdest']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1249,7 +1232,7 @@ class BTCInterface(Secp256k1Interface):
 | 
				
			|||||||
                'vout': utxo['vout']})
 | 
					                'vout': utxo['vout']})
 | 
				
			||||||
        return rv, chain_height
 | 
					        return rv, chain_height
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def withdrawCoin(self, value, addr_to, subfee):
 | 
					    def withdrawCoin(self, value: float, addr_to: str, subfee: bool):
 | 
				
			||||||
        params = [addr_to, value, '', '', subfee, True, self._conf_target]
 | 
					        params = [addr_to, value, '', '', subfee, True, self._conf_target]
 | 
				
			||||||
        return self.rpc_wallet('sendtoaddress', params)
 | 
					        return self.rpc_wallet('sendtoaddress', params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1435,6 +1418,12 @@ class BTCInterface(Secp256k1Interface):
 | 
				
			|||||||
        prove_utxos = []  # TODO: Send specific utxos
 | 
					        prove_utxos = []  # TODO: Send specific utxos
 | 
				
			||||||
        return (sign_for_addr, signature, prove_utxos)
 | 
					        return (sign_for_addr, signature, prove_utxos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encodeProofUtxos(self, proof_utxos):
 | 
				
			||||||
 | 
					        packed_utxos = bytes()
 | 
				
			||||||
 | 
					        for utxo in proof_utxos:
 | 
				
			||||||
 | 
					            packed_utxos += utxo[0] + utxo[1].to_bytes(2, 'big')
 | 
				
			||||||
 | 
					        return packed_utxos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def decodeProofUtxos(self, msg_utxos):
 | 
					    def decodeProofUtxos(self, msg_utxos):
 | 
				
			||||||
        proof_utxos = []
 | 
					        proof_utxos = []
 | 
				
			||||||
        if len(msg_utxos) > 0:
 | 
					        if len(msg_utxos) > 0:
 | 
				
			||||||
@ -1555,6 +1544,24 @@ class BTCInterface(Secp256k1Interface):
 | 
				
			|||||||
            tx_vsize += 323 if redeem else 287
 | 
					            tx_vsize += 323 if redeem else 287
 | 
				
			||||||
        return tx_vsize
 | 
					        return tx_vsize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def find_prevout_info(self, txn_hex: str, txn_script: bytes):
 | 
				
			||||||
 | 
					        txjs = self.rpc('decoderawtransaction', [txn_hex])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.using_segwit():
 | 
				
			||||||
 | 
					            p2wsh = self.getScriptDest(txn_script)
 | 
				
			||||||
 | 
					            n = getVoutByScriptPubKey(txjs, p2wsh.hex())
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            addr_to = self.encode_p2sh(txn_script)
 | 
				
			||||||
 | 
					            n = getVoutByAddress(txjs, addr_to)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            'txid': txjs['txid'],
 | 
				
			||||||
 | 
					            'vout': n,
 | 
				
			||||||
 | 
					            'scriptPubKey': txjs['vout'][n]['scriptPubKey']['hex'],
 | 
				
			||||||
 | 
					            'redeemScript': txn_script.hex(),
 | 
				
			||||||
 | 
					            'amount': txjs['vout'][n]['value']
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def testBTCInterface():
 | 
					def testBTCInterface():
 | 
				
			||||||
    print('TODO: testBTCInterface')
 | 
					    print('TODO: testBTCInterface')
 | 
				
			||||||
 | 
				
			|||||||
@ -5,10 +5,20 @@
 | 
				
			|||||||
# Distributed under the MIT software license, see the accompanying
 | 
					# Distributed under the MIT software license, see the accompanying
 | 
				
			||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
					# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import base64
 | 
				
			||||||
 | 
					import hashlib
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from basicswap.basicswap_util import (
 | 
				
			||||||
 | 
					    getVoutByScriptPubKey,
 | 
				
			||||||
 | 
					    TxLockTypes
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from basicswap.chainparams import Coins
 | 
					from basicswap.chainparams import Coins
 | 
				
			||||||
from basicswap.interface.btc import Secp256k1Interface
 | 
					from basicswap.interface.btc import Secp256k1Interface
 | 
				
			||||||
 | 
					from basicswap.util import (
 | 
				
			||||||
 | 
					    ensure,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from basicswap.util.address import (
 | 
					from basicswap.util.address import (
 | 
				
			||||||
    b58decode,
 | 
					    b58decode,
 | 
				
			||||||
    b58encode,
 | 
					    b58encode,
 | 
				
			||||||
@ -18,6 +28,9 @@ from basicswap.util.crypto import (
 | 
				
			|||||||
    hash160,
 | 
					    hash160,
 | 
				
			||||||
    ripemd160,
 | 
					    ripemd160,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.util.script import (
 | 
				
			||||||
 | 
					    SerialiseNumCompact,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from basicswap.util.extkey import ExtKeyPair
 | 
					from basicswap.util.extkey import ExtKeyPair
 | 
				
			||||||
from basicswap.util.integer import encode_varint
 | 
					from basicswap.util.integer import encode_varint
 | 
				
			||||||
from basicswap.interface.dcr.rpc import make_rpc_func
 | 
					from basicswap.interface.dcr.rpc import make_rpc_func
 | 
				
			||||||
@ -25,10 +38,15 @@ from .messages import CTransaction, CTxOut, SigHashType, TxSerializeType
 | 
				
			|||||||
from .script import push_script_data, OP_HASH160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_CHECKSIG
 | 
					from .script import push_script_data, OP_HASH160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_CHECKSIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from coincurve.keys import (
 | 
					from coincurve.keys import (
 | 
				
			||||||
    PrivateKey
 | 
					    PrivateKey,
 | 
				
			||||||
 | 
					    PublicKey,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SEQUENCE_LOCKTIME_GRANULARITY = 9  # 512 seconds
 | 
				
			||||||
 | 
					SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22)
 | 
				
			||||||
 | 
					SEQUENCE_LOCKTIME_MASK = 0x0000f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SigHashSerializePrefix: int = 1
 | 
					SigHashSerializePrefix: int = 1
 | 
				
			||||||
SigHashSerializeWitness: int = 3
 | 
					SigHashSerializeWitness: int = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -136,6 +154,20 @@ class DCRInterface(Secp256k1Interface):
 | 
				
			|||||||
    def txoType():
 | 
					    def txoType():
 | 
				
			||||||
        return CTxOut
 | 
					        return CTxOut
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def getExpectedSequence(lockType: int, lockVal: int) -> int:
 | 
				
			||||||
 | 
					        ensure(lockVal >= 1, 'Bad lockVal')
 | 
				
			||||||
 | 
					        if lockType == TxLockTypes.SEQUENCE_LOCK_BLOCKS:
 | 
				
			||||||
 | 
					            return lockVal
 | 
				
			||||||
 | 
					        if lockType == TxLockTypes.SEQUENCE_LOCK_TIME:
 | 
				
			||||||
 | 
					            secondsLocked = lockVal
 | 
				
			||||||
 | 
					            # Ensure the locked time is never less than lockVal
 | 
				
			||||||
 | 
					            if secondsLocked % (1 << SEQUENCE_LOCKTIME_GRANULARITY) != 0:
 | 
				
			||||||
 | 
					                secondsLocked += (1 << SEQUENCE_LOCKTIME_GRANULARITY)
 | 
				
			||||||
 | 
					            secondsLocked >>= SEQUENCE_LOCKTIME_GRANULARITY
 | 
				
			||||||
 | 
					            return secondsLocked | SEQUENCE_LOCKTIME_TYPE_FLAG
 | 
				
			||||||
 | 
					        raise ValueError('Unknown lock type')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, coin_settings, network, swap_client=None):
 | 
					    def __init__(self, coin_settings, network, swap_client=None):
 | 
				
			||||||
        super().__init__(network)
 | 
					        super().__init__(network)
 | 
				
			||||||
        self._rpc_host = coin_settings.get('rpchost', '127.0.0.1')
 | 
					        self._rpc_host = coin_settings.get('rpchost', '127.0.0.1')
 | 
				
			||||||
@ -148,6 +180,8 @@ class DCRInterface(Secp256k1Interface):
 | 
				
			|||||||
            self.rpc_wallet = make_rpc_func(coin_settings['walletrpcport'], self._rpcauth, host=self._rpc_host)
 | 
					            self.rpc_wallet = make_rpc_func(coin_settings['walletrpcport'], self._rpcauth, host=self._rpc_host)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self.rpc_wallet = None
 | 
					            self.rpc_wallet = None
 | 
				
			||||||
 | 
					        self.blocks_confirmed = coin_settings['blocks_confirmed']
 | 
				
			||||||
 | 
					        self.setConfTarget(coin_settings['conf_target'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._use_segwit = coin_settings['use_segwit']
 | 
					        self._use_segwit = coin_settings['use_segwit']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -161,6 +195,13 @@ class DCRInterface(Secp256k1Interface):
 | 
				
			|||||||
        checksum = blake256(blake256(data))
 | 
					        checksum = blake256(blake256(data))
 | 
				
			||||||
        return b58encode(data + checksum[0:4])
 | 
					        return b58encode(data + checksum[0:4])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sh_to_address(self, sh: bytes) -> str:
 | 
				
			||||||
 | 
					        assert (len(sh) == 20)
 | 
				
			||||||
 | 
					        prefix = self.chainparams_network()['script_address']
 | 
				
			||||||
 | 
					        data = prefix.to_bytes(2, 'big') + sh
 | 
				
			||||||
 | 
					        checksum = blake256(blake256(data))
 | 
				
			||||||
 | 
					        return b58encode(data + checksum[0:4])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def decode_address(self, address: str) -> bytes:
 | 
					    def decode_address(self, address: str) -> bytes:
 | 
				
			||||||
        addr_data = b58decode(address)
 | 
					        addr_data = b58decode(address)
 | 
				
			||||||
        if addr_data is None:
 | 
					        if addr_data is None:
 | 
				
			||||||
@ -198,7 +239,18 @@ class DCRInterface(Secp256k1Interface):
 | 
				
			|||||||
        return self._use_segwit
 | 
					        return self._use_segwit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getWalletInfo(self):
 | 
					    def getWalletInfo(self):
 | 
				
			||||||
 | 
					        rv = {}
 | 
				
			||||||
        rv = self.rpc_wallet('getinfo')
 | 
					        rv = self.rpc_wallet('getinfo')
 | 
				
			||||||
 | 
					        wi = self.rpc_wallet('walletinfo')
 | 
				
			||||||
 | 
					        balances = self.rpc_wallet('getbalance')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        default_account_bal = balances['balances'][0]  # 0 always default?
 | 
				
			||||||
 | 
					        rv['balance'] = default_account_bal['spendable']
 | 
				
			||||||
 | 
					        rv['unconfirmed_balance'] = default_account_bal['unconfirmed']
 | 
				
			||||||
 | 
					        rv['immature_balance'] = default_account_bal['immaturecoinbaserewards'] + default_account_bal['immaturestakegeneration']
 | 
				
			||||||
 | 
					        rv['encrypted'] = True
 | 
				
			||||||
 | 
					        rv['locked'] = True if wi['unlocked'] is False else False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return rv
 | 
					        return rv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getSeedHash(self, seed: bytes) -> bytes:
 | 
					    def getSeedHash(self, seed: bytes) -> bytes:
 | 
				
			||||||
@ -251,6 +303,13 @@ class DCRInterface(Secp256k1Interface):
 | 
				
			|||||||
        tx = self.loadTx(tx_bytes)
 | 
					        tx = self.loadTx(tx_bytes)
 | 
				
			||||||
        return tx.serialize(TxSerializeType.NoWitness)
 | 
					        return tx.serialize(TxSerializeType.NoWitness)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getTxSignature(self, tx_hex: str, prevout_data, key_wif: str) -> str:
 | 
				
			||||||
 | 
					        sig_type, key = self.decodeKey(key_wif)
 | 
				
			||||||
 | 
					        redeem_script = bytes.fromhex(prevout_data['redeemScript'])
 | 
				
			||||||
 | 
					        sig = self.signTx(key, bytes.fromhex(tx_hex), 0, redeem_script, self.make_int(prevout_data['amount']))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return sig.hex()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getScriptDest(self, script: bytes) -> bytes:
 | 
					    def getScriptDest(self, script: bytes) -> bytes:
 | 
				
			||||||
        # P2SH
 | 
					        # P2SH
 | 
				
			||||||
        script_hash = self.pkh(script)
 | 
					        script_hash = self.pkh(script)
 | 
				
			||||||
@ -258,8 +317,205 @@ class DCRInterface(Secp256k1Interface):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return OP_HASH160.to_bytes(1) + len(script_hash).to_bytes(1) + script_hash + OP_EQUAL.to_bytes(1)
 | 
					        return OP_HASH160.to_bytes(1) + len(script_hash).to_bytes(1) + script_hash + OP_EQUAL.to_bytes(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encodeScriptDest(self, script_dest: bytes) -> str:
 | 
				
			||||||
 | 
					        script_hash = script_dest[2:-1]  # Extract hash from script
 | 
				
			||||||
 | 
					        return self.sh_to_address(script_hash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getPubkeyHashDest(self, pkh: bytes) -> bytes:
 | 
					    def getPubkeyHashDest(self, pkh: bytes) -> bytes:
 | 
				
			||||||
        # P2PKH
 | 
					        # P2PKH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert len(pkh) == 20
 | 
					        assert len(pkh) == 20
 | 
				
			||||||
        return OP_DUP.to_bytes(1) + OP_HASH160.to_bytes(1) + len(pkh).to_bytes(1) + pkh + OP_EQUALVERIFY.to_bytes(1) + OP_CHECKSIG.to_bytes(1)
 | 
					        return OP_DUP.to_bytes(1) + OP_HASH160.to_bytes(1) + len(pkh).to_bytes(1) + pkh + OP_EQUALVERIFY.to_bytes(1) + OP_CHECKSIG.to_bytes(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_fee_rate(self, conf_target: int = 2) -> (float, str):
 | 
				
			||||||
 | 
					        chain_client_settings = self._sc.getChainClientSettings(self.coin_type())  # basicswap.json
 | 
				
			||||||
 | 
					        override_feerate = chain_client_settings.get('override_feerate', None)
 | 
				
			||||||
 | 
					        if override_feerate:
 | 
				
			||||||
 | 
					            self._log.debug('Fee rate override used for %s: %f', self.coin_name(), override_feerate)
 | 
				
			||||||
 | 
					            return override_feerate, 'override_feerate'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        min_relay_fee = chain_client_settings.get('min_relay_fee', None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def try_get_fee_rate(self, conf_target):
 | 
				
			||||||
 | 
					            # TODO: How to estimate required fee?
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                fee_rate: float = self.rpc_wallet('walletinfo')['txfee']
 | 
				
			||||||
 | 
					                assert (fee_rate > 0.0), 'Non positive feerate'
 | 
				
			||||||
 | 
					                return fee_rate, 'paytxfee'
 | 
				
			||||||
 | 
					            except Exception:
 | 
				
			||||||
 | 
					                fee_rate: float = self.rpc('getnetworkinfo')['relayfee']
 | 
				
			||||||
 | 
					                return fee_rate, 'relayfee'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fee_rate, rate_src = try_get_fee_rate(self, conf_target)
 | 
				
			||||||
 | 
					        if min_relay_fee and min_relay_fee > fee_rate:
 | 
				
			||||||
 | 
					            self._log.warning('Feerate {} ({}) is below min relay fee {} for {}'.format(self.format_amount(fee_rate, True, 1), rate_src, self.format_amount(min_relay_fee, True, 1), self.coin_name()))
 | 
				
			||||||
 | 
					            return min_relay_fee, 'min_relay_fee'
 | 
				
			||||||
 | 
					        return fee_rate, rate_src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getNewAddress(self, use_segwit: bool = True, label: str = 'swap_receive') -> str:
 | 
				
			||||||
 | 
					        return self.rpc_wallet('getnewaddress')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getProofOfFunds(self, amount_for, extra_commit_bytes):
 | 
				
			||||||
 | 
					        # TODO: Lock unspent and use same output/s to fund bid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unspents_by_addr = dict()
 | 
				
			||||||
 | 
					        unspents = self.rpc_wallet('listunspent')
 | 
				
			||||||
 | 
					        if unspents is None:
 | 
				
			||||||
 | 
					            unspents = []
 | 
				
			||||||
 | 
					        for u in unspents:
 | 
				
			||||||
 | 
					            if u['spendable'] is not True:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            if u['address'] not in unspents_by_addr:
 | 
				
			||||||
 | 
					                unspents_by_addr[u['address']] = {'total': 0, 'utxos': []}
 | 
				
			||||||
 | 
					            utxo_amount: int = self.make_int(u['amount'], r=1)
 | 
				
			||||||
 | 
					            unspents_by_addr[u['address']]['total'] += utxo_amount
 | 
				
			||||||
 | 
					            unspents_by_addr[u['address']]['utxos'].append((utxo_amount, u['txid'], u['vout'], u['tree']))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        max_utxos: int = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        viable_addrs = []
 | 
				
			||||||
 | 
					        for addr, data in unspents_by_addr.items():
 | 
				
			||||||
 | 
					            if data['total'] >= amount_for:
 | 
				
			||||||
 | 
					                # Sort from largest to smallest amount
 | 
				
			||||||
 | 
					                sorted_utxos = sorted(data['utxos'], key=lambda x: x[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # Max outputs required to reach amount_for
 | 
				
			||||||
 | 
					                utxos_req: int = 0
 | 
				
			||||||
 | 
					                sum_value: int = 0
 | 
				
			||||||
 | 
					                for utxo in sorted_utxos:
 | 
				
			||||||
 | 
					                    sum_value += utxo[0]
 | 
				
			||||||
 | 
					                    utxos_req += 1
 | 
				
			||||||
 | 
					                    if sum_value >= amount_for:
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if utxos_req <= max_utxos:
 | 
				
			||||||
 | 
					                    viable_addrs.append(addr)
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ensure(len(viable_addrs) > 0, 'Could not find address with enough funds for proof')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sign_for_addr: str = random.choice(viable_addrs)
 | 
				
			||||||
 | 
					        self._log.debug('sign_for_addr %s', sign_for_addr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        prove_utxos = []
 | 
				
			||||||
 | 
					        sorted_utxos = sorted(unspents_by_addr[sign_for_addr]['utxos'], key=lambda x: x[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        hasher = hashlib.sha256()
 | 
				
			||||||
 | 
					        sum_value: int = 0
 | 
				
			||||||
 | 
					        for utxo in sorted_utxos:
 | 
				
			||||||
 | 
					            sum_value += utxo[0]
 | 
				
			||||||
 | 
					            outpoint = (bytes.fromhex(utxo[1]), utxo[2], utxo[3])
 | 
				
			||||||
 | 
					            prove_utxos.append(outpoint)
 | 
				
			||||||
 | 
					            hasher.update(outpoint[0])
 | 
				
			||||||
 | 
					            hasher.update(outpoint[1].to_bytes(2, 'big'))
 | 
				
			||||||
 | 
					            hasher.update(outpoint[2].to_bytes(1))
 | 
				
			||||||
 | 
					            if sum_value >= amount_for:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					        utxos_hash = hasher.digest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        signature = self.rpc_wallet('signmessage', [sign_for_addr, sign_for_addr + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (sign_for_addr, signature, prove_utxos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def withdrawCoin(self, value: float, addr_to: str, subfee: bool = False) -> str:
 | 
				
			||||||
 | 
					        if subfee:
 | 
				
			||||||
 | 
					            raise ValueError('TODO')
 | 
				
			||||||
 | 
					        params = [addr_to, value]
 | 
				
			||||||
 | 
					        return self.rpc_wallet('sendtoaddress', params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def isAddressMine(self, address: str, or_watch_only: bool = False) -> bool:
 | 
				
			||||||
 | 
					        addr_info = self.rpc('validateaddress', [address])
 | 
				
			||||||
 | 
					        return addr_info.get('ismine', False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encodeProofUtxos(self, proof_utxos):
 | 
				
			||||||
 | 
					        packed_utxos = bytes()
 | 
				
			||||||
 | 
					        for utxo in proof_utxos:
 | 
				
			||||||
 | 
					            packed_utxos += utxo[0] + utxo[1].to_bytes(2, 'big') + utxo[2].to_bytes(1)
 | 
				
			||||||
 | 
					        return packed_utxos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decodeProofUtxos(self, msg_utxos):
 | 
				
			||||||
 | 
					        proof_utxos = []
 | 
				
			||||||
 | 
					        if len(msg_utxos) > 0:
 | 
				
			||||||
 | 
					            num_utxos = len(msg_utxos) // 34
 | 
				
			||||||
 | 
					            p: int = 0
 | 
				
			||||||
 | 
					            for i in range(num_utxos):
 | 
				
			||||||
 | 
					                proof_utxos.append((msg_utxos[p: p + 32], int.from_bytes(msg_utxos[p + 32: p + 34], 'big'), msg_utxos[p + 34]))
 | 
				
			||||||
 | 
					                p += 35
 | 
				
			||||||
 | 
					        return proof_utxos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verifyProofOfFunds(self, address: str, signature: bytes, utxos, extra_commit_bytes: bytes):
 | 
				
			||||||
 | 
					        hasher = hashlib.sha256()
 | 
				
			||||||
 | 
					        sum_value: int = 0
 | 
				
			||||||
 | 
					        for outpoint in utxos:
 | 
				
			||||||
 | 
					            hasher.update(outpoint[0])
 | 
				
			||||||
 | 
					            hasher.update(outpoint[1].to_bytes(2, 'big'))
 | 
				
			||||||
 | 
					            hasher.update(outpoint[2].to_bytes(1))
 | 
				
			||||||
 | 
					        utxos_hash = hasher.digest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        passed = self.verifyMessage(address, address + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex(), signature)
 | 
				
			||||||
 | 
					        ensure(passed is True, 'Proof of funds signature invalid')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sum_value: int = 0
 | 
				
			||||||
 | 
					        for outpoint in utxos:
 | 
				
			||||||
 | 
					            txout = self.rpc('gettxout', [outpoint[0].hex(), outpoint[1], outpoint[2]])
 | 
				
			||||||
 | 
					            sum_value += self.make_int(txout['value'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return sum_value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verifyMessage(self, address: str, message: str, signature: str, message_magic: str = None) -> bool:
 | 
				
			||||||
 | 
					        if message_magic is None:
 | 
				
			||||||
 | 
					            message_magic = self.chainparams()['message_magic']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        message_bytes = SerialiseNumCompact(len(message_magic)) + bytes(message_magic, 'utf-8') + SerialiseNumCompact(len(message)) + bytes(message, 'utf-8')
 | 
				
			||||||
 | 
					        message_hash = blake256(message_bytes)
 | 
				
			||||||
 | 
					        signature_bytes = base64.b64decode(signature)
 | 
				
			||||||
 | 
					        rec_id = (signature_bytes[0] - 27) & 3
 | 
				
			||||||
 | 
					        signature_bytes = signature_bytes[1:] + bytes((rec_id,))
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            pubkey = PublicKey.from_signature_and_message(signature_bytes, message_hash, hasher=None)
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            self._log.info('verifyMessage failed: ' + str(e))
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        address_hash = self.decode_address(address)[2:]
 | 
				
			||||||
 | 
					        pubkey_hash = ripemd160(blake256(pubkey.format()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return True if address_hash == pubkey_hash else False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def signTxWithWallet(self, tx) -> bytes:
 | 
				
			||||||
 | 
					        return bytes.fromhex(self.rpc('signrawtransaction', [tx.hex()])['hex'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def createRawFundedTransaction(self, addr_to: str, amount: int, sub_fee: bool = False, lock_unspents: bool = True) -> str:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # amount can't be a string, else: Failed to parse request: parameter #2 'amounts' must be type float64 (got string)
 | 
				
			||||||
 | 
					        float_amount = float(self.format_amount(amount))
 | 
				
			||||||
 | 
					        txn = self.rpc('createrawtransaction', [[], {addr_to: float_amount}])
 | 
				
			||||||
 | 
					        fee_rate, fee_src = self.get_fee_rate(self._conf_target)
 | 
				
			||||||
 | 
					        self._log.debug(f'Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}')
 | 
				
			||||||
 | 
					        options = {
 | 
				
			||||||
 | 
					            'lockUnspents': lock_unspents,
 | 
				
			||||||
 | 
					            'feeRate': fee_rate,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if sub_fee:
 | 
				
			||||||
 | 
					            options['subtractFeeFromOutputs'] = [0,]
 | 
				
			||||||
 | 
					        return self.rpc_wallet('fundrawtransaction', [txn, 'default', options])['hex']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def createRawSignedTransaction(self, addr_to, amount) -> str:
 | 
				
			||||||
 | 
					        txn_funded = self.createRawFundedTransaction(addr_to, amount)
 | 
				
			||||||
 | 
					        return self.rpc_wallet('signrawtransaction', [txn_funded])['hex']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index: bool = False):
 | 
				
			||||||
 | 
					        self._log.debug('TODO: getLockTxHeight')
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def find_prevout_info(self, txn_hex: str, txn_script: bytes):
 | 
				
			||||||
 | 
					        txjs = self.rpc('decoderawtransaction', [txn_hex])
 | 
				
			||||||
 | 
					        n = getVoutByScriptPubKey(txjs, self.getScriptDest(txn_script).hex())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            'txid': txjs['txid'],
 | 
				
			||||||
 | 
					            'vout': n,
 | 
				
			||||||
 | 
					            'scriptPubKey': txjs['vout'][n]['scriptPubKey']['hex'],
 | 
				
			||||||
 | 
					            'redeemScript': txn_script.hex(),
 | 
				
			||||||
 | 
					            'amount': txjs['vout'][n]['value']
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -42,5 +42,4 @@ def push_script_data(data_array: bytearray, data: bytes) -> None:
 | 
				
			|||||||
    else:
 | 
					    else:
 | 
				
			||||||
        data_array += bytes((OP_PUSHDATA4,)) + len_data.to_bytes(4, 'little')
 | 
					        data_array += bytes((OP_PUSHDATA4,)) + len_data.to_bytes(4, 'little')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print('[rm] data_array', (data_array + data).hex())
 | 
					 | 
				
			||||||
    data_array += data
 | 
					    data_array += data
 | 
				
			||||||
 | 
				
			|||||||
@ -5,8 +5,8 @@
 | 
				
			|||||||
# Distributed under the MIT software license, see the accompanying
 | 
					# Distributed under the MIT software license, see the accompanying
 | 
				
			||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
					# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import random
 | 
					 | 
				
			||||||
import hashlib
 | 
					import hashlib
 | 
				
			||||||
 | 
					import random
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .btc import BTCInterface, find_vout_for_address_from_txobj
 | 
					from .btc import BTCInterface, find_vout_for_address_from_txobj
 | 
				
			||||||
from basicswap.util import (
 | 
					from basicswap.util import (
 | 
				
			||||||
 | 
				
			|||||||
@ -24,8 +24,9 @@ from coincurve.dleag import (
 | 
				
			|||||||
    verify_ed25519_point,
 | 
					    verify_ed25519_point,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from basicswap.interface import (
 | 
					from basicswap.interface.base import (
 | 
				
			||||||
    Curves)
 | 
					    Curves,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from basicswap.util import (
 | 
					from basicswap.util import (
 | 
				
			||||||
    i2b, b2i, b2h,
 | 
					    i2b, b2i, b2h,
 | 
				
			||||||
    dumpj,
 | 
					    dumpj,
 | 
				
			||||||
@ -36,7 +37,8 @@ from basicswap.util.network import (
 | 
				
			|||||||
from basicswap.rpc_xmr import (
 | 
					from basicswap.rpc_xmr import (
 | 
				
			||||||
    make_xmr_rpc_func,
 | 
					    make_xmr_rpc_func,
 | 
				
			||||||
    make_xmr_rpc2_func)
 | 
					    make_xmr_rpc2_func)
 | 
				
			||||||
from basicswap.chainparams import XMR_COIN, CoinInterface, Coins
 | 
					from basicswap.chainparams import XMR_COIN, Coins
 | 
				
			||||||
 | 
					from basicswap.interface.base import CoinInterface
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class XMRInterface(CoinInterface):
 | 
					class XMRInterface(CoinInterface):
 | 
				
			||||||
@ -239,9 +241,6 @@ class XMRInterface(CoinInterface):
 | 
				
			|||||||
            rv['locked'] = False
 | 
					            rv['locked'] = False
 | 
				
			||||||
            return rv
 | 
					            return rv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def walletRestoreHeight(self):
 | 
					 | 
				
			||||||
        return self._restore_height
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def getMainWalletAddress(self) -> str:
 | 
					    def getMainWalletAddress(self) -> str:
 | 
				
			||||||
        with self._mx_wallet:
 | 
					        with self._mx_wallet:
 | 
				
			||||||
            self.openWallet(self._wallet_filename)
 | 
					            self.openWallet(self._wallet_filename)
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ from sqlalchemy.orm import scoped_session
 | 
				
			|||||||
from basicswap.util import (
 | 
					from basicswap.util import (
 | 
				
			||||||
    ensure,
 | 
					    ensure,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from basicswap.interface import Curves
 | 
					from basicswap.interface.base import Curves
 | 
				
			||||||
from basicswap.chainparams import (
 | 
					from basicswap.chainparams import (
 | 
				
			||||||
    Coins,
 | 
					    Coins,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
@ -1182,7 +1182,7 @@ def finalise_daemon(d):
 | 
				
			|||||||
        d.handle.send_signal(signal.CTRL_C_EVENT if os.name == 'nt' else signal.SIGINT)
 | 
					        d.handle.send_signal(signal.CTRL_C_EVENT if os.name == 'nt' else signal.SIGINT)
 | 
				
			||||||
        d.handle.wait(timeout=120)
 | 
					        d.handle.wait(timeout=120)
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
        logging.info(f'Error {e} for process {d.pid}')
 | 
					        logging.info(f'Error {e} for process {d.handle.pid}')
 | 
				
			||||||
    for fp in [d.handle.stdout, d.handle.stderr, d.handle.stdin] + d.files:
 | 
					    for fp in [d.handle.stdout, d.handle.stderr, d.handle.stdin] + d.files:
 | 
				
			||||||
        if fp:
 | 
					        if fp:
 | 
				
			||||||
            fp.close()
 | 
					            fp.close()
 | 
				
			||||||
@ -1262,7 +1262,7 @@ def initialise_wallets(particl_wallet_mnemonic, with_coins, data_dir, settings,
 | 
				
			|||||||
                            coin_args += ['-hdseed={}'.format(swap_client.getWalletKey(Coins.FIRO, 1).hex())]
 | 
					                            coin_args += ['-hdseed={}'.format(swap_client.getWalletKey(Coins.FIRO, 1).hex())]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        daemons.append(startDaemon(coin_settings['datadir'], coin_settings['bindir'], filename, daemon_args + coin_args))
 | 
					                        daemons.append(startDaemon(coin_settings['datadir'], coin_settings['bindir'], filename, daemon_args + coin_args))
 | 
				
			||||||
                        swap_client.setDaemonPID(c, daemons[-1].pid)
 | 
					                        swap_client.setDaemonPID(c, daemons[-1].handle.pid)
 | 
				
			||||||
                swap_client.setCoinRunParams(c)
 | 
					                swap_client.setCoinRunParams(c)
 | 
				
			||||||
                swap_client.createCoinInterface(c)
 | 
					                swap_client.createCoinInterface(c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -7,8 +7,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import subprocess
 | 
					 | 
				
			||||||
import select
 | 
					import select
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import basicswap.config as cfg
 | 
					import basicswap.config as cfg
 | 
				
			||||||
@ -29,11 +29,12 @@ from basicswap.interface.dcr.messages import (
 | 
				
			|||||||
from tests.basicswap.common import (
 | 
					from tests.basicswap.common import (
 | 
				
			||||||
    stopDaemons,
 | 
					    stopDaemons,
 | 
				
			||||||
    waitForRPC,
 | 
					    waitForRPC,
 | 
				
			||||||
 | 
					    wait_for_balance,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from tests.basicswap.util import (
 | 
					from tests.basicswap.util import (
 | 
				
			||||||
 | 
					    read_json_api,
 | 
				
			||||||
    REQUIRED_SETTINGS,
 | 
					    REQUIRED_SETTINGS,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					 | 
				
			||||||
from tests.basicswap.test_xmr import BaseTest, test_delay_event
 | 
					from tests.basicswap.test_xmr import BaseTest, test_delay_event
 | 
				
			||||||
from basicswap.interface.dcr import DCRInterface
 | 
					from basicswap.interface.dcr import DCRInterface
 | 
				
			||||||
from basicswap.interface.dcr.messages import CTransaction, CTxIn, COutPoint
 | 
					from basicswap.interface.dcr.messages import CTransaction, CTxIn, COutPoint
 | 
				
			||||||
@ -107,7 +108,7 @@ def prepareDCDDataDir(datadir, node_id, conf_file, dir_prefix, num_nodes=3):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Test(BaseTest):
 | 
					class Test(BaseTest):
 | 
				
			||||||
    __test__ = True
 | 
					    __test__ = True
 | 
				
			||||||
    test_coin_from = Coins.DCR
 | 
					    test_coin = Coins.DCR
 | 
				
			||||||
    dcr_daemons = []
 | 
					    dcr_daemons = []
 | 
				
			||||||
    start_ltc_nodes = False
 | 
					    start_ltc_nodes = False
 | 
				
			||||||
    start_xmr_nodes = False
 | 
					    start_xmr_nodes = False
 | 
				
			||||||
@ -122,7 +123,7 @@ class Test(BaseTest):
 | 
				
			|||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def prepareExtraCoins(cls):
 | 
					    def prepareExtraCoins(cls):
 | 
				
			||||||
        if not cls.restore_instance:
 | 
					        if not cls.restore_instance:
 | 
				
			||||||
            ci0 = cls.swap_clients[0].ci(cls.test_coin_from)
 | 
					            ci0 = cls.swap_clients[0].ci(cls.test_coin)
 | 
				
			||||||
            assert (ci0.rpc_wallet('getnewaddress') == cls.dcr_mining_addr)
 | 
					            assert (ci0.rpc_wallet('getnewaddress') == cls.dcr_mining_addr)
 | 
				
			||||||
            cls.dcr_ticket_account = ci0.rpc_wallet('getaccount', [cls.dcr_mining_addr, ])
 | 
					            cls.dcr_ticket_account = ci0.rpc_wallet('getaccount', [cls.dcr_mining_addr, ])
 | 
				
			||||||
            ci0.rpc('generate', [110,])
 | 
					            ci0.rpc('generate', [110,])
 | 
				
			||||||
@ -140,7 +141,7 @@ class Test(BaseTest):
 | 
				
			|||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def coins_loop(cls):
 | 
					    def coins_loop(cls):
 | 
				
			||||||
        super(Test, cls).coins_loop()
 | 
					        super(Test, cls).coins_loop()
 | 
				
			||||||
        ci0 = cls.swap_clients[0].ci(cls.test_coin_from)
 | 
					        ci0 = cls.swap_clients[0].ci(cls.test_coin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        num_passed: int = 0
 | 
					        num_passed: int = 0
 | 
				
			||||||
        for i in range(5):
 | 
					        for i in range(5):
 | 
				
			||||||
@ -148,7 +149,10 @@ class Test(BaseTest):
 | 
				
			|||||||
                ci0.rpc_wallet('purchaseticket', [cls.dcr_ticket_account, 0.1, 0])
 | 
					                ci0.rpc_wallet('purchaseticket', [cls.dcr_ticket_account, 0.1, 0])
 | 
				
			||||||
                num_passed += 1
 | 
					                num_passed += 1
 | 
				
			||||||
            except Exception as e:
 | 
					            except Exception as e:
 | 
				
			||||||
                logging.warning('coins_loop purchaseticket {}'.format(e))
 | 
					                if 'double spend' in str(e):
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    logging.warning('coins_loop purchaseticket {}'.format(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if num_passed >= 5:
 | 
					            if num_passed >= 5:
 | 
				
			||||||
@ -230,8 +234,30 @@ class Test(BaseTest):
 | 
				
			|||||||
            'blocks_confirmed': 1,
 | 
					            'blocks_confirmed': 1,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def prepare_balance(self, coin, amount: float, port_target_node: int, port_take_from_node: int, test_balance: bool = True) -> None:
 | 
				
			||||||
 | 
					        delay_iterations = 20
 | 
				
			||||||
 | 
					        delay_time = 3
 | 
				
			||||||
 | 
					        coin_ticker: str = coin.name
 | 
				
			||||||
 | 
					        balance_type: str = 'balance'
 | 
				
			||||||
 | 
					        address_type: str = 'deposit_address'
 | 
				
			||||||
 | 
					        js_w = read_json_api(port_target_node, 'wallets')
 | 
				
			||||||
 | 
					        current_balance: float = float(js_w[coin_ticker][balance_type])
 | 
				
			||||||
 | 
					        if test_balance and current_balance >= amount:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        post_json = {
 | 
				
			||||||
 | 
					            'value': amount,
 | 
				
			||||||
 | 
					            'address': js_w[coin_ticker][address_type],
 | 
				
			||||||
 | 
					            'subfee': False,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        json_rv = read_json_api(port_take_from_node, 'wallets/{}/withdraw'.format(coin_ticker.lower()), post_json)
 | 
				
			||||||
 | 
					        assert (len(json_rv['txid']) == 64)
 | 
				
			||||||
 | 
					        wait_for_amount: float = amount
 | 
				
			||||||
 | 
					        if not test_balance:
 | 
				
			||||||
 | 
					            wait_for_amount += current_balance
 | 
				
			||||||
 | 
					        wait_for_balance(test_delay_event, 'http://127.0.0.1:{}/json/wallets/{}'.format(port_target_node, coin_ticker.lower()), balance_type, wait_for_amount, iterations=delay_iterations, delay_time=delay_time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_0001_decred_address(self):
 | 
					    def test_0001_decred_address(self):
 | 
				
			||||||
        logging.info('---------- Test {}'.format(self.test_coin_from.name))
 | 
					        logging.info('---------- Test {}'.format(self.test_coin.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        coin_settings = {'rpcport': 0, 'rpcauth': 'none'}
 | 
					        coin_settings = {'rpcport': 0, 'rpcauth': 'none'}
 | 
				
			||||||
        coin_settings.update(REQUIRED_SETTINGS)
 | 
					        coin_settings.update(REQUIRED_SETTINGS)
 | 
				
			||||||
@ -249,7 +275,7 @@ class Test(BaseTest):
 | 
				
			|||||||
        assert (data[2:] == pkh)
 | 
					        assert (data[2:] == pkh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for i, sc in enumerate(self.swap_clients):
 | 
					        for i, sc in enumerate(self.swap_clients):
 | 
				
			||||||
            loop_ci = sc.ci(self.test_coin_from)
 | 
					            loop_ci = sc.ci(self.test_coin)
 | 
				
			||||||
            root_key = sc.getWalletKey(Coins.DCR, 1)
 | 
					            root_key = sc.getWalletKey(Coins.DCR, 1)
 | 
				
			||||||
            masterpubkey = loop_ci.rpc_wallet('getmasterpubkey')
 | 
					            masterpubkey = loop_ci.rpc_wallet('getmasterpubkey')
 | 
				
			||||||
            masterpubkey_data = loop_ci.decode_address(masterpubkey)[4:]
 | 
					            masterpubkey_data = loop_ci.decode_address(masterpubkey)[4:]
 | 
				
			||||||
@ -261,13 +287,13 @@ class Test(BaseTest):
 | 
				
			|||||||
                assert (seed_hash == hash160(masterpubkey_data))
 | 
					                assert (seed_hash == hash160(masterpubkey_data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_001_segwit(self):
 | 
					    def test_001_segwit(self):
 | 
				
			||||||
        logging.info('---------- Test {} segwit'.format(self.test_coin_from.name))
 | 
					        logging.info('---------- Test {} segwit'.format(self.test_coin.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        swap_clients = self.swap_clients
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
        ci0 = swap_clients[0].ci(self.test_coin_from)
 | 
					        ci0 = swap_clients[0].ci(self.test_coin)
 | 
				
			||||||
        assert (ci0.using_segwit() is True)
 | 
					        assert (ci0.using_segwit() is True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        addr_out = ci0.rpc_wallet('getnewaddress')
 | 
					        addr_out = ci0.getNewAddress()
 | 
				
			||||||
        addr_info = ci0.rpc_wallet('validateaddress', [addr_out,])
 | 
					        addr_info = ci0.rpc_wallet('validateaddress', [addr_out,])
 | 
				
			||||||
        assert (addr_info['isvalid'] is True)
 | 
					        assert (addr_info['isvalid'] is True)
 | 
				
			||||||
        assert (addr_info['ismine'] is True)
 | 
					        assert (addr_info['ismine'] is True)
 | 
				
			||||||
@ -294,13 +320,13 @@ class Test(BaseTest):
 | 
				
			|||||||
        assert (f_decoded['txid'] == ctx.TxHash().hex())
 | 
					        assert (f_decoded['txid'] == ctx.TxHash().hex())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_003_signature_hash(self):
 | 
					    def test_003_signature_hash(self):
 | 
				
			||||||
        logging.info('---------- Test {} signature_hash'.format(self.test_coin_from.name))
 | 
					        logging.info('---------- Test {} signature_hash'.format(self.test_coin.name))
 | 
				
			||||||
        # Test that signing a transaction manually produces the same result when signed with the wallet
 | 
					        # Test that signing a transaction manually produces the same result when signed with the wallet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        swap_clients = self.swap_clients
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
        ci0 = swap_clients[0].ci(self.test_coin_from)
 | 
					        ci0 = swap_clients[0].ci(self.test_coin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        utxos = ci0.rpc_wallet('listunspent')
 | 
					        utxos = ci0.getNewAddress()
 | 
				
			||||||
        addr_out = ci0.rpc_wallet('getnewaddress')
 | 
					        addr_out = ci0.rpc_wallet('getnewaddress')
 | 
				
			||||||
        rtx = ci0.rpc_wallet('createrawtransaction', [[], {addr_out: 2.0}])
 | 
					        rtx = ci0.rpc_wallet('createrawtransaction', [[], {addr_out: 2.0}])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -342,15 +368,18 @@ class Test(BaseTest):
 | 
				
			|||||||
        assert (len(sent_txid) == 64)
 | 
					        assert (len(sent_txid) == 64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_004_csv(self):
 | 
					    def test_004_csv(self):
 | 
				
			||||||
        logging.info('---------- Test {} csv'.format(self.test_coin_from.name))
 | 
					        logging.info('---------- Test {} csv'.format(self.test_coin.name))
 | 
				
			||||||
        swap_clients = self.swap_clients
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
        ci0 = swap_clients[0].ci(self.test_coin_from)
 | 
					        ci0 = swap_clients[0].ci(self.test_coin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        script = bytearray()
 | 
					        script = bytearray()
 | 
				
			||||||
        push_script_data(script, bytes((3,)))
 | 
					        push_script_data(script, bytes((3,)))
 | 
				
			||||||
        script += OP_CHECKSEQUENCEVERIFY.to_bytes(1)
 | 
					        script += OP_CHECKSEQUENCEVERIFY.to_bytes(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        script_dest = ci0.getScriptDest(script)
 | 
					        script_dest = ci0.getScriptDest(script)
 | 
				
			||||||
 | 
					        script_info = ci0.rpc_wallet('decodescript', [script_dest.hex(),])
 | 
				
			||||||
 | 
					        script_addr = ci0.encodeScriptDest(script_dest)
 | 
				
			||||||
 | 
					        assert (script_info['addresses'][0] == script_addr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        prevout_amount: int = ci0.make_int(1.1)
 | 
					        prevout_amount: int = ci0.make_int(1.1)
 | 
				
			||||||
        tx = CTransaction()
 | 
					        tx = CTransaction()
 | 
				
			||||||
@ -388,7 +417,7 @@ class Test(BaseTest):
 | 
				
			|||||||
        push_script_data(signature_script, script)
 | 
					        push_script_data(signature_script, script)
 | 
				
			||||||
        tx_spend.vin[0].signature_script = signature_script
 | 
					        tx_spend.vin[0].signature_script = signature_script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        addr_out = ci0.rpc_wallet('getnewaddress')
 | 
					        addr_out = ci0.getNewAddress()
 | 
				
			||||||
        pkh = ci0.decode_address(addr_out)[2:]
 | 
					        pkh = ci0.decode_address(addr_out)[2:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tx_spend.vout.append(ci0.txoType()())
 | 
					        tx_spend.vout.append(ci0.txoType()())
 | 
				
			||||||
@ -399,6 +428,7 @@ class Test(BaseTest):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            sent_spend_txid = ci0.rpc_wallet('sendrawtransaction', [tx_spend_hex, ])
 | 
					            sent_spend_txid = ci0.rpc_wallet('sendrawtransaction', [tx_spend_hex, ])
 | 
				
			||||||
 | 
					            logging.info('Sent tx spending csv output, txid: {}'.format(sent_spend_txid))
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            assert ('transaction sequence locks on inputs not met' in str(e))
 | 
					            assert ('transaction sequence locks on inputs not met' in str(e))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
@ -415,6 +445,135 @@ class Test(BaseTest):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        assert (sent_spend_txid is not None)
 | 
					        assert (sent_spend_txid is not None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_005_watchonly(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} watchonly'.format(self.test_coin.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					        ci0 = swap_clients[0].ci(self.test_coin)
 | 
				
			||||||
 | 
					        ci1 = swap_clients[1].ci(self.test_coin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr = ci0.getNewAddress()
 | 
				
			||||||
 | 
					        pkh = ci0.decode_address(addr)[2:]
 | 
				
			||||||
 | 
					        addr_info = ci0.rpc_wallet('validateaddress', [addr,])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr_script = ci0.getPubkeyHashDest(pkh).hex()
 | 
				
			||||||
 | 
					        script_info = ci0.rpc_wallet('decodescript', [addr_script,])
 | 
				
			||||||
 | 
					        assert (addr in script_info['addresses'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Importscript doesn't import an address
 | 
				
			||||||
 | 
					        ci1.rpc_wallet('importscript', [addr_script,])
 | 
				
			||||||
 | 
					        addr_info1 = ci1.rpc_wallet('validateaddress', [addr,])
 | 
				
			||||||
 | 
					        assert (addr_info1.get('ismine', False) is False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Would need to run a second wallet daemon?
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            ro = ci1.rpc_wallet('importpubkey', [addr_info['pubkey'],])
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            assert ('public keys may only be imported by watching-only wallets' in str(e))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            logging.info('Expected importpubkey to fail on non watching-only wallet')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        chain_height_last = ci1.getChainHeight()
 | 
				
			||||||
 | 
					        txid = ci0.rpc_wallet('sendtoaddress', [addr, 1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        found_txid = None
 | 
				
			||||||
 | 
					        for i in range(20):
 | 
				
			||||||
 | 
					            if found_txid is not None:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            chain_height_now = ci1.getChainHeight()
 | 
				
			||||||
 | 
					            while chain_height_last <= chain_height_now:
 | 
				
			||||||
 | 
					                if found_txid is not None:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    check_hash = ci1.rpc('getblockhash', [chain_height_last + 1, ])
 | 
				
			||||||
 | 
					                except Exception as e:
 | 
				
			||||||
 | 
					                    logging.warning('getblockhash {} failed {}'.format(chain_height_last + 1, e))
 | 
				
			||||||
 | 
					                    test_delay_event.wait(1)
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                chain_height_last += 1
 | 
				
			||||||
 | 
					                check_hash = ci1.rpc('getblockhash', [chain_height_last, ])
 | 
				
			||||||
 | 
					                block_tx = ci1.rpc('getblock', [check_hash, True, True])
 | 
				
			||||||
 | 
					                for tx in block_tx['rawtx']:
 | 
				
			||||||
 | 
					                    if found_txid is not None:
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                    for txo in tx['vout']:
 | 
				
			||||||
 | 
					                        if addr_script == txo['scriptPubKey']['hex']:
 | 
				
			||||||
 | 
					                            found_txid = tx['txid']
 | 
				
			||||||
 | 
					                            logging.info('found_txid {}'.format(found_txid))
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            test_delay_event.wait(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert (found_txid is not None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_008_gettxout(self):
 | 
				
			||||||
 | 
					        logging.info('---------- Test {} gettxout'.format(self.test_coin.name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ci0 = self.swap_clients[0].ci(self.test_coin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr = ci0.getNewAddress()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        test_amount: float = 1.0
 | 
				
			||||||
 | 
					        txid = ci0.withdrawCoin(test_amount, addr)
 | 
				
			||||||
 | 
					        assert len(txid) == 64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unspents = None
 | 
				
			||||||
 | 
					        for i in range(30):
 | 
				
			||||||
 | 
					            unspents = ci0.rpc_wallet('listunspent', [0, 999999999, [addr,]])
 | 
				
			||||||
 | 
					            if unspents is None:
 | 
				
			||||||
 | 
					                unspents = []
 | 
				
			||||||
 | 
					            if len(unspents) > 0:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            test_delay_event.wait(1)
 | 
				
			||||||
 | 
					        assert (len(unspents) == 1)
 | 
				
			||||||
 | 
					        utxo = unspents[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        txout = ci0.rpc('gettxout', [utxo['txid'], utxo['vout'], utxo['tree']])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Lock utxo so it's not spent for tickets, while waiting for depth
 | 
				
			||||||
 | 
					        rv = ci0.rpc_wallet('lockunspent', [False, [utxo, ]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def wait_for_depth():
 | 
				
			||||||
 | 
					            for i in range(20):
 | 
				
			||||||
 | 
					                logging.info('Waiting for txout depth, iter {}'.format(i))
 | 
				
			||||||
 | 
					                txout = ci0.rpc('gettxout', [utxo['txid'], utxo['vout'], utxo['tree']])
 | 
				
			||||||
 | 
					                if txout['confirmations'] > 0:
 | 
				
			||||||
 | 
					                    return txout
 | 
				
			||||||
 | 
					                test_delay_event.wait(1)
 | 
				
			||||||
 | 
					            raise ValueError('prevout not confirmed')
 | 
				
			||||||
 | 
					        txout = wait_for_depth()
 | 
				
			||||||
 | 
					        assert (txout['confirmations'] > 0)
 | 
				
			||||||
 | 
					        assert (addr in txout['scriptPubKey']['addresses'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr_out = ci0.getNewAddress()
 | 
				
			||||||
 | 
					        rtx = ci0.rpc_wallet('createrawtransaction', [[utxo, ], {addr_out: test_amount - 0.0001}])
 | 
				
			||||||
 | 
					        stx = ci0.rpc_wallet('signrawtransaction', [rtx])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        chain_height_before_send = ci0.getChainHeight()
 | 
				
			||||||
 | 
					        sent_txid = ci0.rpc_wallet('sendrawtransaction', [stx['hex'], ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # NOTE: UTXO is still found when spent in the mempool (tested in loop, not delay from wallet to core)
 | 
				
			||||||
 | 
					        txout = ci0.rpc('gettxout', [utxo['txid'], utxo['vout'], utxo['tree']])
 | 
				
			||||||
 | 
					        assert (addr in txout['scriptPubKey']['addresses'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in range(20):
 | 
				
			||||||
 | 
					            txout = ci0.rpc('gettxout', [utxo['txid'], utxo['vout'], utxo['tree']])
 | 
				
			||||||
 | 
					            if txout is None:
 | 
				
			||||||
 | 
					                logging.info('txout spent, height before spent {}, height spent {}'.format(chain_height_before_send, ci0.getChainHeight()))
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            test_delay_event.wait(1)
 | 
				
			||||||
 | 
					        assert (txout is None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info('Testing getProofOfFunds')
 | 
				
			||||||
 | 
					        require_amount: int = ci0.make_int(1)
 | 
				
			||||||
 | 
					        funds_proof = ci0.getProofOfFunds(require_amount, 'test'.encode('utf-8'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info('Testing verifyProofOfFunds')
 | 
				
			||||||
 | 
					        amount_proved = ci0.verifyProofOfFunds(funds_proof[0], funds_proof[1], funds_proof[2], 'test'.encode('utf-8'))
 | 
				
			||||||
 | 
					        assert (amount_proved >= require_amount)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
 | 
				
			|||||||
@ -13,10 +13,10 @@ from basicswap.db import (
 | 
				
			|||||||
    Concepts,
 | 
					    Concepts,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from basicswap.basicswap import (
 | 
					from basicswap.basicswap import (
 | 
				
			||||||
    Coins,
 | 
					 | 
				
			||||||
    SwapTypes,
 | 
					 | 
				
			||||||
    BidStates,
 | 
					    BidStates,
 | 
				
			||||||
 | 
					    Coins,
 | 
				
			||||||
    DebugTypes,
 | 
					    DebugTypes,
 | 
				
			||||||
 | 
					    SwapTypes,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from basicswap.basicswap_util import (
 | 
					from basicswap.basicswap_util import (
 | 
				
			||||||
    TxLockTypes,
 | 
					    TxLockTypes,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user