NMC and CLTV, abs lock values still to be verified
This commit is contained in:
		
							parent
							
								
									222fbb492f
								
							
						
					
					
						commit
						307b8ab8bf
					
				@ -126,6 +126,9 @@ class TxTypes(IntEnum):  # For PooledAddress
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
SEQUENCE_LOCK_BLOCKS = 1
 | 
					SEQUENCE_LOCK_BLOCKS = 1
 | 
				
			||||||
SEQUENCE_LOCK_TIME = 2
 | 
					SEQUENCE_LOCK_TIME = 2
 | 
				
			||||||
 | 
					ABS_LOCK_BLOCKS = 3
 | 
				
			||||||
 | 
					ABS_LOCK_TIME = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SEQUENCE_LOCKTIME_GRANULARITY = 9  # 512 seconds
 | 
					SEQUENCE_LOCKTIME_GRANULARITY = 9  # 512 seconds
 | 
				
			||||||
SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22)
 | 
					SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22)
 | 
				
			||||||
SEQUENCE_LOCKTIME_MASK = 0x0000ffff
 | 
					SEQUENCE_LOCKTIME_MASK = 0x0000ffff
 | 
				
			||||||
@ -183,6 +186,10 @@ def getLockName(lock_type):
 | 
				
			|||||||
        return 'Sequence lock, blocks'
 | 
					        return 'Sequence lock, blocks'
 | 
				
			||||||
    if lock_type == SEQUENCE_LOCK_TIME:
 | 
					    if lock_type == SEQUENCE_LOCK_TIME:
 | 
				
			||||||
        return 'Sequence lock, time'
 | 
					        return 'Sequence lock, time'
 | 
				
			||||||
 | 
					    if lock_type == ABS_LOCK_BLOCKS:
 | 
				
			||||||
 | 
					        return 'blocks'
 | 
				
			||||||
 | 
					    if lock_type == ABS_LOCK_TIME:
 | 
				
			||||||
 | 
					        return 'time'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def getExpectedSequence(lockType, lockVal, coin_type):
 | 
					def getExpectedSequence(lockType, lockVal, coin_type):
 | 
				
			||||||
@ -206,7 +213,7 @@ def decodeSequence(lock_value):
 | 
				
			|||||||
    return lock_value & SEQUENCE_LOCKTIME_MASK
 | 
					    return lock_value & SEQUENCE_LOCKTIME_MASK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def buildContractScriptCSV(sequence, secret_hash, pkh_redeem, pkh_refund):
 | 
					def buildContractScript(lock_val, secret_hash, pkh_redeem, pkh_refund, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY):
 | 
				
			||||||
    script = bytearray([
 | 
					    script = bytearray([
 | 
				
			||||||
        OpCodes.OP_IF,
 | 
					        OpCodes.OP_IF,
 | 
				
			||||||
        OpCodes.OP_SIZE,
 | 
					        OpCodes.OP_SIZE,
 | 
				
			||||||
@ -222,9 +229,9 @@ def buildContractScriptCSV(sequence, secret_hash, pkh_redeem, pkh_refund):
 | 
				
			|||||||
            0x14]) \
 | 
					            0x14]) \
 | 
				
			||||||
        + pkh_redeem \
 | 
					        + pkh_redeem \
 | 
				
			||||||
        + bytearray([OpCodes.OP_ELSE, ]) \
 | 
					        + bytearray([OpCodes.OP_ELSE, ]) \
 | 
				
			||||||
        + SerialiseNum(sequence) \
 | 
					        + SerialiseNum(lock_val) \
 | 
				
			||||||
        + bytearray([
 | 
					        + bytearray([
 | 
				
			||||||
            OpCodes.OP_CHECKSEQUENCEVERIFY,
 | 
					            op_lock,
 | 
				
			||||||
            OpCodes.OP_DROP,
 | 
					            OpCodes.OP_DROP,
 | 
				
			||||||
            OpCodes.OP_DUP,
 | 
					            OpCodes.OP_DUP,
 | 
				
			||||||
            OpCodes.OP_HASH160,
 | 
					            OpCodes.OP_HASH160,
 | 
				
			||||||
@ -590,6 +597,7 @@ class BasicSwap():
 | 
				
			|||||||
            'watched_outputs': [],
 | 
					            'watched_outputs': [],
 | 
				
			||||||
            'last_height_checked': last_height_checked,
 | 
					            'last_height_checked': last_height_checked,
 | 
				
			||||||
            'use_segwit': use_segwit,
 | 
					            'use_segwit': use_segwit,
 | 
				
			||||||
 | 
					            'use_csv': chain_client_settings.get('use_csv', True),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def start(self):
 | 
					    def start(self):
 | 
				
			||||||
@ -710,8 +718,16 @@ class BasicSwap():
 | 
				
			|||||||
    def validateOfferLockValue(self, coin_from, coin_to, lock_type, lock_value):
 | 
					    def validateOfferLockValue(self, coin_from, coin_to, lock_type, lock_value):
 | 
				
			||||||
        if lock_type == OfferMessage.SEQUENCE_LOCK_TIME:
 | 
					        if lock_type == OfferMessage.SEQUENCE_LOCK_TIME:
 | 
				
			||||||
            assert(lock_value >= 2 * 60 * 60 and lock_value <= 96 * 60 * 60), 'Invalid lock_value time'
 | 
					            assert(lock_value >= 2 * 60 * 60 and lock_value <= 96 * 60 * 60), 'Invalid lock_value time'
 | 
				
			||||||
 | 
					            assert(self.coin_clients[coin_from]['use_csv'] and self.coin_clients[coin_to]['use_csv']), 'Both coins need CSV activated.'
 | 
				
			||||||
        elif lock_type == OfferMessage.SEQUENCE_LOCK_BLOCKS:
 | 
					        elif lock_type == OfferMessage.SEQUENCE_LOCK_BLOCKS:
 | 
				
			||||||
            assert(lock_value >= 5 and lock_value <= 1000), 'Invalid lock_value blocks'
 | 
					            assert(lock_value >= 5 and lock_value <= 1000), 'Invalid lock_value blocks'
 | 
				
			||||||
 | 
					            assert(self.coin_clients[coin_from]['use_csv'] and self.coin_clients[coin_to]['use_csv']), 'Both coins need CSV activated.'
 | 
				
			||||||
 | 
					        elif lock_type == ABS_LOCK_TIME:
 | 
				
			||||||
 | 
					            # TODO: range?
 | 
				
			||||||
 | 
					            assert(lock_value >= 4 * 60 * 60 and lock_value <= 96 * 60 * 60), 'Invalid lock_value time'
 | 
				
			||||||
 | 
					        elif lock_type == ABS_LOCK_BLOCKS:
 | 
				
			||||||
 | 
					            # TODO: range?
 | 
				
			||||||
 | 
					            assert(lock_value >= 10 and lock_value <= 1000), 'Invalid lock_value blocks'
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            raise ValueError('Unknown locktype')
 | 
					            raise ValueError('Unknown locktype')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -872,7 +888,7 @@ class BasicSwap():
 | 
				
			|||||||
    def getReceiveAddressForCoin(self, coin_type):
 | 
					    def getReceiveAddressForCoin(self, coin_type):
 | 
				
			||||||
        if coin_type == Coins.PART:
 | 
					        if coin_type == Coins.PART:
 | 
				
			||||||
            new_addr = self.callcoinrpc(Coins.PART, 'getnewaddress')
 | 
					            new_addr = self.callcoinrpc(Coins.PART, 'getnewaddress')
 | 
				
			||||||
        elif coin_type == Coins.LTC or coin_type == Coins.BTC:
 | 
					        elif coin_type == Coins.LTC or coin_type == Coins.BTC or coin_type == Coins.NMC:
 | 
				
			||||||
            args = []
 | 
					            args = []
 | 
				
			||||||
            if self.coin_clients[coin_type]['use_segwit']:
 | 
					            if self.coin_clients[coin_type]['use_segwit']:
 | 
				
			||||||
                args = ['swap_receive', 'bech32']
 | 
					                args = ['swap_receive', 'bech32']
 | 
				
			||||||
@ -1123,15 +1139,22 @@ class BasicSwap():
 | 
				
			|||||||
        coin_from = Coins(offer.coin_from)
 | 
					        coin_from = Coins(offer.coin_from)
 | 
				
			||||||
        bid_date = dt.datetime.fromtimestamp(bid.created_at).date()
 | 
					        bid_date = dt.datetime.fromtimestamp(bid.created_at).date()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # TODO: Use CLTV for coins without CSV
 | 
					 | 
				
			||||||
        sequence = getExpectedSequence(offer.lock_type, offer.lock_value, coin_from)
 | 
					 | 
				
			||||||
        secret = self.getContractSecret(bid_date, bid.contract_count)
 | 
					        secret = self.getContractSecret(bid_date, bid.contract_count)
 | 
				
			||||||
        secret_hash = hashlib.sha256(secret).digest()
 | 
					        secret_hash = hashlib.sha256(secret).digest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pubkey_refund = self.getContractPubkey(bid_date, bid.contract_count)
 | 
					        pubkey_refund = self.getContractPubkey(bid_date, bid.contract_count)
 | 
				
			||||||
        pkhash_refund = getKeyID(pubkey_refund)
 | 
					        pkhash_refund = getKeyID(pubkey_refund)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        script = buildContractScriptCSV(sequence, secret_hash, bid.pkhash_buyer, pkhash_refund)
 | 
					        if offer.lock_type < ABS_LOCK_BLOCKS:
 | 
				
			||||||
 | 
					            sequence = getExpectedSequence(offer.lock_type, offer.lock_value, coin_from)
 | 
				
			||||||
 | 
					            script = buildContractScript(sequence, secret_hash, bid.pkhash_buyer, pkhash_refund)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            if offer.lock_type == ABS_LOCK_BLOCKS:
 | 
				
			||||||
 | 
					                lock_value = self.callcoinrpc(coin_from, 'getblockchaininfo')['blocks'] + offer.lock_value
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                lock_value = int(time.time()) + offer.lock_value
 | 
				
			||||||
 | 
					            logging.debug('initiate %s lock_value %d %d', coin_from, offer.lock_value, lock_value)
 | 
				
			||||||
 | 
					            script = buildContractScript(lock_value, secret_hash, bid.pkhash_buyer, pkhash_refund, OpCodes.OP_CHECKLOCKTIMEVERIFY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        p2sh = self.callcoinrpc(Coins.PART, 'decodescript', [script.hex()])['p2sh']
 | 
					        p2sh = self.callcoinrpc(Coins.PART, 'decodescript', [script.hex()])['p2sh']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1141,7 +1164,7 @@ class BasicSwap():
 | 
				
			|||||||
        txn = self.createInitiateTxn(coin_from, bid_id, bid)
 | 
					        txn = self.createInitiateTxn(coin_from, bid_id, bid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Store the signed refund txn in case wallet is locked when refund is possible
 | 
					        # Store the signed refund txn in case wallet is locked when refund is possible
 | 
				
			||||||
        refund_txn = self.createRefundTxn(coin_from, txn, bid, script)
 | 
					        refund_txn = self.createRefundTxn(coin_from, txn, offer, bid, script)
 | 
				
			||||||
        bid.initiate_txn_refund = bytes.fromhex(refund_txn)
 | 
					        bid.initiate_txn_refund = bytes.fromhex(refund_txn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        txid = self.submitTxn(coin_from, txn)
 | 
					        txid = self.submitTxn(coin_from, txn)
 | 
				
			||||||
@ -1153,7 +1176,7 @@ class BasicSwap():
 | 
				
			|||||||
            txid = self.submitTxn(coin_from, bid.initiate_txn_refund.hex())
 | 
					            txid = self.submitTxn(coin_from, bid.initiate_txn_refund.hex())
 | 
				
			||||||
            self.log.error('Submit refund_txn unexpectedly worked: ' + txid)
 | 
					            self.log.error('Submit refund_txn unexpectedly worked: ' + txid)
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            if 'non-BIP68-final' not in str(e):
 | 
					            if 'non-BIP68-final' not in str(e) and 'non-final' not in str(e):
 | 
				
			||||||
                self.log.error('Submit refund_txn unexpected error' + str(e))
 | 
					                self.log.error('Submit refund_txn unexpected error' + str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if txid is not None:
 | 
					        if txid is not None:
 | 
				
			||||||
@ -1269,14 +1292,22 @@ class BasicSwap():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        bid_date = dt.datetime.fromtimestamp(bid.created_at).date()
 | 
					        bid_date = dt.datetime.fromtimestamp(bid.created_at).date()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Participate txn is locked for half the time of the initiate txn
 | 
					 | 
				
			||||||
        lock_value = decodeSequence(offer.lock_value) // 2
 | 
					 | 
				
			||||||
        sequence = getExpectedSequence(offer.lock_type, lock_value, coin_to)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        secret_hash = extractScriptSecretHash(bid.initiate_script)
 | 
					        secret_hash = extractScriptSecretHash(bid.initiate_script)
 | 
				
			||||||
        pkhash_seller = bid.pkhash_seller
 | 
					        pkhash_seller = bid.pkhash_seller
 | 
				
			||||||
        pkhash_buyer_refund = bid.pkhash_buyer
 | 
					        pkhash_buyer_refund = bid.pkhash_buyer
 | 
				
			||||||
        bid.participate_script = buildContractScriptCSV(sequence, secret_hash, pkhash_seller, pkhash_buyer_refund)
 | 
					
 | 
				
			||||||
 | 
					        # Participate txn is locked for half the time of the initiate txn
 | 
				
			||||||
 | 
					        lock_value = offer.lock_value // 2
 | 
				
			||||||
 | 
					        if offer.lock_type < ABS_LOCK_BLOCKS:
 | 
				
			||||||
 | 
					            sequence = getExpectedSequence(offer.lock_type, lock_value, coin_to)
 | 
				
			||||||
 | 
					            bid.participate_script = buildContractScript(sequence, secret_hash, pkhash_seller, pkhash_buyer_refund)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            if offer.lock_type == ABS_LOCK_BLOCKS:
 | 
				
			||||||
 | 
					                contract_lock_value = self.callcoinrpc(coin_to, 'getblockchaininfo')['blocks'] + lock_value
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                contract_lock_value = int(time.time()) + lock_value
 | 
				
			||||||
 | 
					            logging.debug('participate %s lock_value %d %d', coin_to, lock_value, contract_lock_value)
 | 
				
			||||||
 | 
					            bid.participate_script = buildContractScript(contract_lock_value, secret_hash, pkhash_seller, pkhash_buyer_refund, OpCodes.OP_CHECKLOCKTIMEVERIFY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createParticipateTxn(self, bid_id, bid, offer):
 | 
					    def createParticipateTxn(self, bid_id, bid, offer):
 | 
				
			||||||
        self.log.debug('createParticipateTxn')
 | 
					        self.log.debug('createParticipateTxn')
 | 
				
			||||||
@ -1302,7 +1333,7 @@ class BasicSwap():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        txn_signed = self.callcoinrpc(coin_to, 'signrawtransactionwithwallet', [txn_funded])['hex']
 | 
					        txn_signed = self.callcoinrpc(coin_to, 'signrawtransactionwithwallet', [txn_funded])['hex']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        refund_txn = self.createRefundTxn(coin_to, txn_signed, bid, bid.participate_script, tx_type=TxTypes.PTX_REFUND)
 | 
					        refund_txn = self.createRefundTxn(coin_to, txn_signed, offer, bid, bid.participate_script, tx_type=TxTypes.PTX_REFUND)
 | 
				
			||||||
        bid.participate_txn_refund = bytes.fromhex(refund_txn)
 | 
					        bid.participate_txn_refund = bytes.fromhex(refund_txn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        chain_height = self.callcoinrpc(coin_to, 'getblockchaininfo')['blocks']
 | 
					        chain_height = self.callcoinrpc(coin_to, 'getblockchaininfo')['blocks']
 | 
				
			||||||
@ -1433,7 +1464,7 @@ class BasicSwap():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return redeem_txn
 | 
					        return redeem_txn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createRefundTxn(self, coin_type, txn, bid, txn_script, addr_refund_out=None, tx_type=TxTypes.ITX_REFUND):
 | 
					    def createRefundTxn(self, coin_type, txn, offer, bid, txn_script, addr_refund_out=None, tx_type=TxTypes.ITX_REFUND):
 | 
				
			||||||
        self.log.debug('createRefundTxn')
 | 
					        self.log.debug('createRefundTxn')
 | 
				
			||||||
        if self.coin_clients[coin_type]['connection_type'] != 'rpc':
 | 
					        if self.coin_clients[coin_type]['connection_type'] != 'rpc':
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
@ -1459,7 +1490,11 @@ class BasicSwap():
 | 
				
			|||||||
            'redeemScript': txn_script.hex(),
 | 
					            'redeemScript': txn_script.hex(),
 | 
				
			||||||
            'amount': prev_amount}
 | 
					            'amount': prev_amount}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sequence = DeserialiseNum(txn_script, 64)
 | 
					        lock_value = DeserialiseNum(txn_script, 64)
 | 
				
			||||||
 | 
					        if offer.lock_type < ABS_LOCK_BLOCKS:
 | 
				
			||||||
 | 
					            sequence = lock_value
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            sequence = 1
 | 
				
			||||||
        prevout_s = ' in={}:{}:{}'.format(txjs['txid'], vout, sequence)
 | 
					        prevout_s = ' in={}:{}:{}'.format(txjs['txid'], vout, sequence)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fee_rate = self.getFeeRateForCoin(coin_type)
 | 
					        fee_rate = self.getFeeRateForCoin(coin_type)
 | 
				
			||||||
@ -1488,6 +1523,9 @@ class BasicSwap():
 | 
				
			|||||||
        else:
 | 
					        else:
 | 
				
			||||||
            refund_txn = self.calltx('-btcmode -create nversion=2' + prevout_s + output_to)
 | 
					            refund_txn = self.calltx('-btcmode -create nversion=2' + prevout_s + output_to)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if offer.lock_type == ABS_LOCK_BLOCKS or offer.lock_type == ABS_LOCK_TIME:
 | 
				
			||||||
 | 
					            refund_txn = self.calltx('{} locktime={}'.format(refund_txn, lock_value))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        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
 | 
				
			||||||
@ -1691,7 +1729,7 @@ class BasicSwap():
 | 
				
			|||||||
                    self.initiateTxnConfirmed(bid_id, bid, offer)
 | 
					                    self.initiateTxnConfirmed(bid_id, bid, offer)
 | 
				
			||||||
                    save_bid = True
 | 
					                    save_bid = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Bid times out if buyer doesn't see tx in chain within
 | 
					            # Bid times out if buyer doesn't see tx in chain within INITIATE_TX_TIMEOUT seconds
 | 
				
			||||||
            if bid.state_time + INITIATE_TX_TIMEOUT < int(time.time()):
 | 
					            if bid.state_time + INITIATE_TX_TIMEOUT < int(time.time()):
 | 
				
			||||||
                self.log.info('Swap timed out waiting for initiate tx for bid %s', bid_id.hex())
 | 
					                self.log.info('Swap timed out waiting for initiate tx for bid %s', bid_id.hex())
 | 
				
			||||||
                bid.setState(BidStates.SWAP_TIMEDOUT)
 | 
					                bid.setState(BidStates.SWAP_TIMEDOUT)
 | 
				
			||||||
@ -1757,7 +1795,7 @@ class BasicSwap():
 | 
				
			|||||||
                self.log.debug('Submitted initiate refund txn %s to %s chain for bid %s', txid, chainparams[coin_from]['name'], bid_id.hex())
 | 
					                self.log.debug('Submitted initiate refund txn %s to %s chain for bid %s', txid, chainparams[coin_from]['name'], bid_id.hex())
 | 
				
			||||||
                # State will update when spend is detected
 | 
					                # State will update when spend is detected
 | 
				
			||||||
            except Exception as e:
 | 
					            except Exception as e:
 | 
				
			||||||
                if 'non-BIP68-final (code 64)' not in str(e):
 | 
					                if 'non-BIP68-final (code 64)' not in str(e) and 'non-final' not in str(e):
 | 
				
			||||||
                    self.log.warning('Error trying to submit initiate refund txn: %s', str(e))
 | 
					                    self.log.warning('Error trying to submit initiate refund txn: %s', str(e))
 | 
				
			||||||
        if (bid.participate_txn_state == TxStates.TX_SENT or bid.participate_txn_state == TxStates.TX_CONFIRMED) \
 | 
					        if (bid.participate_txn_state == TxStates.TX_SENT or bid.participate_txn_state == TxStates.TX_CONFIRMED) \
 | 
				
			||||||
           and bid.participate_txn_refund is not None:
 | 
					           and bid.participate_txn_refund is not None:
 | 
				
			||||||
@ -1766,7 +1804,7 @@ class BasicSwap():
 | 
				
			|||||||
                self.log.debug('Submitted participate refund txn %s to %s chain for bid %s', txid, chainparams[coin_to]['name'], bid_id.hex())
 | 
					                self.log.debug('Submitted participate refund txn %s to %s chain for bid %s', txid, chainparams[coin_to]['name'], bid_id.hex())
 | 
				
			||||||
                # State will update when spend is detected
 | 
					                # State will update when spend is detected
 | 
				
			||||||
            except Exception as e:
 | 
					            except Exception as e:
 | 
				
			||||||
                if 'non-BIP68-final (code 64)' not in str(e):
 | 
					                if 'non-BIP68-final (code 64)' not in str(e) and 'non-final' not in str(e):
 | 
				
			||||||
                    self.log.warning('Error trying to submit participate refund txn: %s', str(e))
 | 
					                    self.log.warning('Error trying to submit participate refund txn: %s', str(e))
 | 
				
			||||||
        return False  # Bid is still active
 | 
					        return False  # Bid is still active
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2090,19 +2128,11 @@ class BasicSwap():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.log.debug('for bid %s', bid_accept_data.bid_msg_id.hex())
 | 
					        self.log.debug('for bid %s', bid_accept_data.bid_msg_id.hex())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        decoded_script = self.callcoinrpc(Coins.PART, 'decodescript', [bid_accept_data.contract_script.hex()])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # TODO: Verify script without decoding?
 | 
					 | 
				
			||||||
        prog = re.compile('OP_IF OP_SIZE 32 OP_EQUALVERIFY OP_SHA256 (\w+) OP_EQUALVERIFY OP_DUP OP_HASH160 (\w+) OP_ELSE (\d+) OP_CHECKSEQUENCEVERIFY OP_DROP OP_DUP OP_HASH160 (\w+) OP_ENDIF OP_EQUALVERIFY OP_CHECKSIG')
 | 
					 | 
				
			||||||
        rr = prog.match(decoded_script['asm'])
 | 
					 | 
				
			||||||
        if not rr:
 | 
					 | 
				
			||||||
            raise ValueError('Bad script')
 | 
					 | 
				
			||||||
        scriptvalues = rr.groups()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        bid_id = bid_accept_data.bid_msg_id
 | 
					        bid_id = bid_accept_data.bid_msg_id
 | 
				
			||||||
        bid, offer = self.getBidAndOffer(bid_id)
 | 
					        bid, offer = self.getBidAndOffer(bid_id)
 | 
				
			||||||
        assert(bid is not None and bid.was_sent is True), 'Unknown bidid'
 | 
					        assert(bid is not None and bid.was_sent is True), 'Unknown bidid'
 | 
				
			||||||
        assert(offer), 'Offer not found ' + bid.offer_id.hex()
 | 
					        assert(offer), 'Offer not found ' + bid.offer_id.hex()
 | 
				
			||||||
 | 
					        coin_from = Coins(offer.coin_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # assert(bid.expire_at > now), 'Bid expired'  # How much time over to accept
 | 
					        # assert(bid.expire_at > now), 'Bid expired'  # How much time over to accept
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2112,12 +2142,26 @@ class BasicSwap():
 | 
				
			|||||||
                return
 | 
					                return
 | 
				
			||||||
            raise ValueError('Wrong bid state: {}'.format(str(BidStates(bid.state))))
 | 
					            raise ValueError('Wrong bid state: {}'.format(str(BidStates(bid.state))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        coin_from = Coins(offer.coin_from)
 | 
					        use_csv = True if offer.lock_type < ABS_LOCK_BLOCKS else False
 | 
				
			||||||
        expect_sequence = getExpectedSequence(offer.lock_type, offer.lock_value, coin_from)
 | 
					
 | 
				
			||||||
 | 
					        # TODO: Verify script without decoding?
 | 
				
			||||||
 | 
					        decoded_script = self.callcoinrpc(Coins.PART, 'decodescript', [bid_accept_data.contract_script.hex()])
 | 
				
			||||||
 | 
					        lock_check_op = 'OP_CHECKSEQUENCEVERIFY' if use_csv else 'OP_CHECKLOCKTIMEVERIFY'
 | 
				
			||||||
 | 
					        prog = re.compile('OP_IF OP_SIZE 32 OP_EQUALVERIFY OP_SHA256 (\w+) OP_EQUALVERIFY OP_DUP OP_HASH160 (\w+) OP_ELSE (\d+) {} OP_DROP OP_DUP OP_HASH160 (\w+) OP_ENDIF OP_EQUALVERIFY OP_CHECKSIG'.format(lock_check_op))
 | 
				
			||||||
 | 
					        rr = prog.match(decoded_script['asm'])
 | 
				
			||||||
 | 
					        if not rr:
 | 
				
			||||||
 | 
					            raise ValueError('Bad script')
 | 
				
			||||||
 | 
					        scriptvalues = rr.groups()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert(len(scriptvalues[0]) == 64), 'Bad secret_hash length'
 | 
					        assert(len(scriptvalues[0]) == 64), 'Bad secret_hash length'
 | 
				
			||||||
        assert(bytes.fromhex(scriptvalues[1]) == bid.pkhash_buyer), 'pkhash_buyer mismatch'
 | 
					        assert(bytes.fromhex(scriptvalues[1]) == bid.pkhash_buyer), 'pkhash_buyer mismatch'
 | 
				
			||||||
        assert(int(scriptvalues[2]) == expect_sequence), 'sequence mismatch'
 | 
					
 | 
				
			||||||
 | 
					        if use_csv:
 | 
				
			||||||
 | 
					            expect_sequence = getExpectedSequence(offer.lock_type, offer.lock_value, coin_from)
 | 
				
			||||||
 | 
					            assert(int(scriptvalues[2]) == expect_sequence), 'sequence mismatch'
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.log.warning('TODO: validate absolute lock values')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert(len(scriptvalues[3]) == 40), 'pkhash_refund bad length'
 | 
					        assert(len(scriptvalues[3]) == 40), 'pkhash_refund bad length'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert(bid.accept_msg_id is None), 'Bid already accepted'
 | 
					        assert(bid.accept_msg_id is None), 'Bid already accepted'
 | 
				
			||||||
 | 
				
			|||||||
@ -22,3 +22,8 @@ LITECOIN_BINDIR = os.path.expanduser(os.getenv('LITECOIN_BINDIR', ''))
 | 
				
			|||||||
LITECOIND = os.getenv('LITECOIND', 'litecoind' + ('.exe' if os.name == 'nt' else ''))
 | 
					LITECOIND = os.getenv('LITECOIND', 'litecoind' + ('.exe' if os.name == 'nt' else ''))
 | 
				
			||||||
LITECOIN_CLI = os.getenv('LITECOIN_CLI', 'litecoin-cli' + ('.exe' if os.name == 'nt' else ''))
 | 
					LITECOIN_CLI = os.getenv('LITECOIN_CLI', 'litecoin-cli' + ('.exe' if os.name == 'nt' else ''))
 | 
				
			||||||
LITECOIN_TX = os.getenv('LITECOIN_TX', 'litecoin-tx' + ('.exe' if os.name == 'nt' else ''))
 | 
					LITECOIN_TX = os.getenv('LITECOIN_TX', 'litecoin-tx' + ('.exe' if os.name == 'nt' else ''))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NAMECOIN_BINDIR = os.path.expanduser(os.getenv('NAMECOIN_BINDIR', ''))
 | 
				
			||||||
 | 
					NAMECOIND = os.getenv('NAMECOIND', 'namecoind' + ('.exe' if os.name == 'nt' else ''))
 | 
				
			||||||
 | 
					NAMECOIN_CLI = os.getenv('NAMECOIN_CLI', 'namecoin-cli' + ('.exe' if os.name == 'nt' else ''))
 | 
				
			||||||
 | 
					NAMECOIN_TX = os.getenv('NAMECOIN_TX', 'namecoin-tx' + ('.exe' if os.name == 'nt' else ''))
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,8 @@ message OfferMessage {
 | 
				
			|||||||
        NOT_SET = 0;
 | 
					        NOT_SET = 0;
 | 
				
			||||||
        SEQUENCE_LOCK_BLOCKS = 1;
 | 
					        SEQUENCE_LOCK_BLOCKS = 1;
 | 
				
			||||||
        SEQUENCE_LOCK_TIME = 2;
 | 
					        SEQUENCE_LOCK_TIME = 2;
 | 
				
			||||||
 | 
					        ABS_LOCK_BLOCKS = 3;
 | 
				
			||||||
 | 
					        ABS_LOCK_TIME = 4;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    LockType lock_type = 7;
 | 
					    LockType lock_type = 7;
 | 
				
			||||||
    uint32 lock_value = 8;
 | 
					    uint32 lock_value = 8;
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,7 @@ import time
 | 
				
			|||||||
from urllib.request import urlretrieve
 | 
					from urllib.request import urlretrieve
 | 
				
			||||||
import urllib.parse
 | 
					import urllib.parse
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					import platform
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import gnupg
 | 
					import gnupg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -30,7 +31,10 @@ import basicswap.config as cfg
 | 
				
			|||||||
from basicswap.util import callrpc_cli
 | 
					from basicswap.util import callrpc_cli
 | 
				
			||||||
from bin.basicswap_run import startDaemon
 | 
					from bin.basicswap_run import startDaemon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BIN_ARCH = 'x86_64-linux-gnu.tar.gz'
 | 
					if platform.system() == 'Darwin':
 | 
				
			||||||
 | 
					    BIN_ARCH = 'osx64.tar.gz'
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    BIN_ARCH = 'x86_64-linux-gnu.tar.gz'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
known_coins = {
 | 
					known_coins = {
 | 
				
			||||||
    'particl': '0.18.1.0',
 | 
					    'particl': '0.18.1.0',
 | 
				
			||||||
@ -358,6 +362,7 @@ def main():
 | 
				
			|||||||
            'datadir': os.path.join(data_dir, 'namecoin'),
 | 
					            'datadir': os.path.join(data_dir, 'namecoin'),
 | 
				
			||||||
            'bindir': os.path.join(data_dir, 'bins', 'namecoin'),
 | 
					            'bindir': os.path.join(data_dir, 'bins', 'namecoin'),
 | 
				
			||||||
            'use_segwit': False,
 | 
					            'use_segwit': False,
 | 
				
			||||||
 | 
					            'use_csv': False,
 | 
				
			||||||
            'blocks_confirmed': 1
 | 
					            'blocks_confirmed': 1
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,6 @@ import signal
 | 
				
			|||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import basicswap.config as cfg
 | 
					 | 
				
			||||||
from basicswap import __version__
 | 
					from basicswap import __version__
 | 
				
			||||||
from basicswap.basicswap import BasicSwap
 | 
					from basicswap.basicswap import BasicSwap
 | 
				
			||||||
from basicswap.http_server import HttpThread
 | 
					from basicswap.http_server import HttpThread
 | 
				
			||||||
@ -66,17 +65,10 @@ def runClient(fp, dataDir, chain):
 | 
				
			|||||||
    for c, v in settings['chainclients'].items():
 | 
					    for c, v in settings['chainclients'].items():
 | 
				
			||||||
        if v['manage_daemon'] is True:
 | 
					        if v['manage_daemon'] is True:
 | 
				
			||||||
            logger.info('Starting {} daemon'.format(c.capitalize()))
 | 
					            logger.info('Starting {} daemon'.format(c.capitalize()))
 | 
				
			||||||
            if c == 'particl':
 | 
					
 | 
				
			||||||
                daemons.append(startDaemon(v['datadir'], v['bindir'], cfg.PARTICLD))
 | 
					            filename = c + 'd' + ('.exe' if os.name == 'nt' else '')
 | 
				
			||||||
                logger.info('Started {} {}'.format(cfg.PARTICLD, daemons[-1].pid))
 | 
					            daemons.append(startDaemon(v['datadir'], v['bindir'], filename))
 | 
				
			||||||
            elif c == 'bitcoin':
 | 
					            logger.info('Started {} {}'.format(filename, daemons[-1].pid))
 | 
				
			||||||
                daemons.append(startDaemon(v['datadir'], v['bindir'], cfg.BITCOIND))
 | 
					 | 
				
			||||||
                logger.info('Started {} {}'.format(cfg.BITCOIND, daemons[-1].pid))
 | 
					 | 
				
			||||||
            elif c == 'litecoin':
 | 
					 | 
				
			||||||
                daemons.append(startDaemon(v['datadir'], v['bindir'], cfg.LITECOIND))
 | 
					 | 
				
			||||||
                logger.info('Started {} {}'.format(cfg.LITECOIND, daemons[-1].pid))
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                logger.warning('Unknown chain', c)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    swap_client = BasicSwap(fp, dataDir, settings, chain)
 | 
					    swap_client = BasicSwap(fp, dataDir, settings, chain)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										545
									
								
								tests/test_nmc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										545
									
								
								tests/test_nmc.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,545 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2019 tecnovert
 | 
				
			||||||
 | 
					# Distributed under the MIT software license, see the accompanying
 | 
				
			||||||
 | 
					# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					basicswap]$ python tests/test_nmc.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import unittest
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					import signal
 | 
				
			||||||
 | 
					import threading
 | 
				
			||||||
 | 
					from urllib.request import urlopen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from basicswap.basicswap import (
 | 
				
			||||||
 | 
					    BasicSwap,
 | 
				
			||||||
 | 
					    Coins,
 | 
				
			||||||
 | 
					    SwapTypes,
 | 
				
			||||||
 | 
					    BidStates,
 | 
				
			||||||
 | 
					    TxStates,
 | 
				
			||||||
 | 
					    ABS_LOCK_BLOCKS,
 | 
				
			||||||
 | 
					    ABS_LOCK_TIME,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.util import (
 | 
				
			||||||
 | 
					    COIN,
 | 
				
			||||||
 | 
					    toWIF,
 | 
				
			||||||
 | 
					    callrpc_cli,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.key import (
 | 
				
			||||||
 | 
					    ECKey,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from basicswap.http_server import (
 | 
				
			||||||
 | 
					    HttpThread,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import basicswap.config as cfg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger()
 | 
				
			||||||
 | 
					logger.level = logging.DEBUG
 | 
				
			||||||
 | 
					if not len(logger.handlers):
 | 
				
			||||||
 | 
					    logger.addHandler(logging.StreamHandler(sys.stdout))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NUM_NODES = 3
 | 
				
			||||||
 | 
					BASE_PORT = 14792
 | 
				
			||||||
 | 
					BASE_RPC_PORT = 19792
 | 
				
			||||||
 | 
					BASE_ZMQ_PORT = 20792
 | 
				
			||||||
 | 
					PREFIX_SECRET_KEY_REGTEST = 0x2e
 | 
				
			||||||
 | 
					TEST_HTML_PORT = 1800
 | 
				
			||||||
 | 
					NMC_NODE = 3
 | 
				
			||||||
 | 
					BTC_NODE = 4
 | 
				
			||||||
 | 
					stop_test = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def prepareOtherDir(datadir, nodeId, conf_file='namecoin.conf'):
 | 
				
			||||||
 | 
					    node_dir = os.path.join(datadir, str(nodeId))
 | 
				
			||||||
 | 
					    if not os.path.exists(node_dir):
 | 
				
			||||||
 | 
					        os.makedirs(node_dir)
 | 
				
			||||||
 | 
					    filePath = os.path.join(node_dir, conf_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with open(filePath, 'w+') as fp:
 | 
				
			||||||
 | 
					        fp.write('regtest=1\n')
 | 
				
			||||||
 | 
					        fp.write('[regtest]\n')
 | 
				
			||||||
 | 
					        fp.write('port=' + str(BASE_PORT + nodeId) + '\n')
 | 
				
			||||||
 | 
					        fp.write('rpcport=' + str(BASE_RPC_PORT + nodeId) + '\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fp.write('daemon=0\n')
 | 
				
			||||||
 | 
					        fp.write('printtoconsole=0\n')
 | 
				
			||||||
 | 
					        fp.write('server=1\n')
 | 
				
			||||||
 | 
					        fp.write('discover=0\n')
 | 
				
			||||||
 | 
					        fp.write('listenonion=0\n')
 | 
				
			||||||
 | 
					        fp.write('bind=127.0.0.1\n')
 | 
				
			||||||
 | 
					        fp.write('findpeers=0\n')
 | 
				
			||||||
 | 
					        fp.write('debug=1\n')
 | 
				
			||||||
 | 
					        fp.write('debugexclude=libevent\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fp.write('acceptnonstdtxn=0\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def prepareDir(datadir, nodeId, network_key, network_pubkey):
 | 
				
			||||||
 | 
					    node_dir = os.path.join(datadir, str(nodeId))
 | 
				
			||||||
 | 
					    if not os.path.exists(node_dir):
 | 
				
			||||||
 | 
					        os.makedirs(node_dir)
 | 
				
			||||||
 | 
					    filePath = os.path.join(node_dir, 'particl.conf')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with open(filePath, 'w+') as fp:
 | 
				
			||||||
 | 
					        fp.write('regtest=1\n')
 | 
				
			||||||
 | 
					        fp.write('[regtest]\n')
 | 
				
			||||||
 | 
					        fp.write('port=' + str(BASE_PORT + nodeId) + '\n')
 | 
				
			||||||
 | 
					        fp.write('rpcport=' + str(BASE_RPC_PORT + nodeId) + '\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fp.write('daemon=0\n')
 | 
				
			||||||
 | 
					        fp.write('printtoconsole=0\n')
 | 
				
			||||||
 | 
					        fp.write('server=1\n')
 | 
				
			||||||
 | 
					        fp.write('discover=0\n')
 | 
				
			||||||
 | 
					        fp.write('listenonion=0\n')
 | 
				
			||||||
 | 
					        fp.write('bind=127.0.0.1\n')
 | 
				
			||||||
 | 
					        fp.write('findpeers=0\n')
 | 
				
			||||||
 | 
					        fp.write('debug=1\n')
 | 
				
			||||||
 | 
					        fp.write('debugexclude=libevent\n')
 | 
				
			||||||
 | 
					        fp.write('zmqpubsmsg=tcp://127.0.0.1:' + str(BASE_ZMQ_PORT + nodeId) + '\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fp.write('acceptnonstdtxn=0\n')
 | 
				
			||||||
 | 
					        fp.write('minstakeinterval=5\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in range(0, NUM_NODES):
 | 
				
			||||||
 | 
					            if nodeId == i:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            fp.write('addnode=127.0.0.1:%d\n' % (BASE_PORT + i))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if nodeId < 2:
 | 
				
			||||||
 | 
					            fp.write('spentindex=1\n')
 | 
				
			||||||
 | 
					            fp.write('txindex=1\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    basicswap_dir = os.path.join(datadir, str(nodeId), 'basicswap')
 | 
				
			||||||
 | 
					    if not os.path.exists(basicswap_dir):
 | 
				
			||||||
 | 
					        os.makedirs(basicswap_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    nmcdatadir = os.path.join(datadir, str(NMC_NODE))
 | 
				
			||||||
 | 
					    btcdatadir = os.path.join(datadir, str(BTC_NODE))
 | 
				
			||||||
 | 
					    settings_path = os.path.join(basicswap_dir, 'basicswap.json')
 | 
				
			||||||
 | 
					    settings = {
 | 
				
			||||||
 | 
					        'zmqhost': 'tcp://127.0.0.1',
 | 
				
			||||||
 | 
					        'zmqport': BASE_ZMQ_PORT + nodeId,
 | 
				
			||||||
 | 
					        'htmlhost': 'localhost',
 | 
				
			||||||
 | 
					        'htmlport': 12700 + nodeId,
 | 
				
			||||||
 | 
					        'network_key': network_key,
 | 
				
			||||||
 | 
					        'network_pubkey': network_pubkey,
 | 
				
			||||||
 | 
					        'chainclients': {
 | 
				
			||||||
 | 
					            'particl': {
 | 
				
			||||||
 | 
					                'connection_type': 'rpc',
 | 
				
			||||||
 | 
					                'manage_daemon': False,
 | 
				
			||||||
 | 
					                'rpcport': BASE_RPC_PORT + nodeId,
 | 
				
			||||||
 | 
					                'datadir': node_dir,
 | 
				
			||||||
 | 
					                'bindir': cfg.PARTICL_BINDIR,
 | 
				
			||||||
 | 
					                'blocks_confirmed': 2,  # Faster testing
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            'namecoin': {
 | 
				
			||||||
 | 
					                'connection_type': 'rpc',
 | 
				
			||||||
 | 
					                'manage_daemon': False,
 | 
				
			||||||
 | 
					                'rpcport': BASE_RPC_PORT + NMC_NODE,
 | 
				
			||||||
 | 
					                'datadir': nmcdatadir,
 | 
				
			||||||
 | 
					                'bindir': cfg.NAMECOIN_BINDIR,
 | 
				
			||||||
 | 
					                'use_csv': False,
 | 
				
			||||||
 | 
					                # 'use_segwit': True,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            'bitcoin': {
 | 
				
			||||||
 | 
					                'connection_type': 'rpc',
 | 
				
			||||||
 | 
					                'manage_daemon': False,
 | 
				
			||||||
 | 
					                'rpcport': BASE_RPC_PORT + BTC_NODE,
 | 
				
			||||||
 | 
					                'datadir': btcdatadir,
 | 
				
			||||||
 | 
					                'bindir': cfg.BITCOIN_BINDIR,
 | 
				
			||||||
 | 
					                'use_segwit': True,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'check_progress_seconds': 2,
 | 
				
			||||||
 | 
					        'check_watched_seconds': 4,
 | 
				
			||||||
 | 
					        'check_expired_seconds': 60
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    with open(settings_path, 'w') as fp:
 | 
				
			||||||
 | 
					        json.dump(settings, fp, indent=4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def startDaemon(nodeId, bin_dir=cfg.PARTICL_BINDIR, daemon_bin=cfg.PARTICLD):
 | 
				
			||||||
 | 
					    node_dir = os.path.join(cfg.DATADIRS, str(nodeId))
 | 
				
			||||||
 | 
					    daemon_bin = os.path.join(bin_dir, daemon_bin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    args = [daemon_bin, '-datadir=' + node_dir]
 | 
				
			||||||
 | 
					    logging.info('Starting node ' + str(nodeId) + ' ' + daemon_bin + ' ' + '-datadir=' + node_dir)
 | 
				
			||||||
 | 
					    return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def partRpc(cmd, node_id=0):
 | 
				
			||||||
 | 
					    return callrpc_cli(cfg.PARTICL_BINDIR, os.path.join(cfg.DATADIRS, str(node_id)), 'regtest', cmd, cfg.PARTICL_CLI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def btcRpc(cmd):
 | 
				
			||||||
 | 
					    return callrpc_cli(cfg.BITCOIN_BINDIR, os.path.join(cfg.DATADIRS, str(BTC_NODE)), 'regtest', cmd, cfg.BITCOIN_CLI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def nmcRpc(cmd):
 | 
				
			||||||
 | 
					    return callrpc_cli(cfg.NAMECOIN_BINDIR, os.path.join(cfg.DATADIRS, str(NMC_NODE)), 'regtest', cmd, cfg.NAMECOIN_CLI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def signal_handler(sig, frame):
 | 
				
			||||||
 | 
					    global stop_test
 | 
				
			||||||
 | 
					    print('signal {} detected.'.format(sig))
 | 
				
			||||||
 | 
					    stop_test = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def run_loop(self):
 | 
				
			||||||
 | 
					    while not stop_test:
 | 
				
			||||||
 | 
					        time.sleep(1)
 | 
				
			||||||
 | 
					        for c in self.swap_clients:
 | 
				
			||||||
 | 
					            c.update()
 | 
				
			||||||
 | 
					        nmcRpc('generatetoaddress 1 {}'.format(self.nmc_addr))
 | 
				
			||||||
 | 
					        btcRpc('generatetoaddress 1 {}'.format(self.btc_addr))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def waitForRPC(rpc_func, wallet=None):
 | 
				
			||||||
 | 
					    for i in range(5):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            rpc_func('getwalletinfo')
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        except Exception as ex:
 | 
				
			||||||
 | 
					            logging.warning('Can\'t connect to daemon RPC: %s.  Trying again in %d second/s.', str(ex), (1 + i))
 | 
				
			||||||
 | 
					            time.sleep(1 + i)
 | 
				
			||||||
 | 
					    raise ValueError('waitForRPC failed')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Test(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpClass(cls):
 | 
				
			||||||
 | 
					        super(Test, cls).setUpClass()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        eckey = ECKey()
 | 
				
			||||||
 | 
					        eckey.generate()
 | 
				
			||||||
 | 
					        cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes())
 | 
				
			||||||
 | 
					        cls.network_pubkey = eckey.get_pubkey().get_bytes().hex()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if os.path.isdir(cfg.DATADIRS):
 | 
				
			||||||
 | 
					            logging.info('Removing ' + cfg.DATADIRS)
 | 
				
			||||||
 | 
					            shutil.rmtree(cfg.DATADIRS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in range(NUM_NODES):
 | 
				
			||||||
 | 
					            prepareDir(cfg.DATADIRS, i, cls.network_key, cls.network_pubkey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        prepareOtherDir(cfg.DATADIRS, NMC_NODE)
 | 
				
			||||||
 | 
					        prepareOtherDir(cfg.DATADIRS, BTC_NODE, 'bitcoin.conf')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cls.daemons = []
 | 
				
			||||||
 | 
					        cls.swap_clients = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cls.daemons.append(startDaemon(BTC_NODE, cfg.BITCOIN_BINDIR, cfg.BITCOIND))
 | 
				
			||||||
 | 
					        logging.info('Started %s %d', cfg.BITCOIND, cls.daemons[-1].pid)
 | 
				
			||||||
 | 
					        cls.daemons.append(startDaemon(NMC_NODE, cfg.NAMECOIN_BINDIR, cfg.NAMECOIND))
 | 
				
			||||||
 | 
					        logging.info('Started %s %d', cfg.NAMECOIND, cls.daemons[-1].pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in range(NUM_NODES):
 | 
				
			||||||
 | 
					            cls.daemons.append(startDaemon(i))
 | 
				
			||||||
 | 
					            logging.info('Started %s %d', cfg.PARTICLD, cls.daemons[-1].pid)
 | 
				
			||||||
 | 
					        time.sleep(1)
 | 
				
			||||||
 | 
					        for i in range(NUM_NODES):
 | 
				
			||||||
 | 
					            basicswap_dir = os.path.join(os.path.join(cfg.DATADIRS, str(i)), 'basicswap')
 | 
				
			||||||
 | 
					            settings_path = os.path.join(basicswap_dir, 'basicswap.json')
 | 
				
			||||||
 | 
					            with open(settings_path) as fs:
 | 
				
			||||||
 | 
					                settings = json.load(fs)
 | 
				
			||||||
 | 
					            fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
 | 
				
			||||||
 | 
					            cls.swap_clients.append(BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i)))
 | 
				
			||||||
 | 
					            cls.swap_clients[-1].start()
 | 
				
			||||||
 | 
					        cls.swap_clients[0].callrpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
 | 
				
			||||||
 | 
					        cls.swap_clients[1].callrpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true'])
 | 
				
			||||||
 | 
					        cls.swap_clients[1].callrpc('getnewextaddress', ['lblExtTest'])
 | 
				
			||||||
 | 
					        cls.swap_clients[1].callrpc('rescanblockchain')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        waitForRPC(nmcRpc)
 | 
				
			||||||
 | 
					        num_blocks = 500
 | 
				
			||||||
 | 
					        logging.info('Mining %d namecoin blocks', num_blocks)
 | 
				
			||||||
 | 
					        cls.nmc_addr = nmcRpc('getnewaddress mining_addr legacy')
 | 
				
			||||||
 | 
					        nmcRpc('generatetoaddress {} {}'.format(num_blocks, cls.nmc_addr))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ro = nmcRpc('getblockchaininfo')
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            assert(ro['bip9_softforks']['csv']['status'] == 'active')
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            logging.info('nmc: csv is not active')
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            assert(ro['bip9_softforks']['segwit']['status'] == 'active')
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            logging.info('nmc: segwit is not active')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        waitForRPC(btcRpc)
 | 
				
			||||||
 | 
					        cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
 | 
				
			||||||
 | 
					        logging.info('Mining %d bitcoin blocks to %s', num_blocks, cls.btc_addr)
 | 
				
			||||||
 | 
					        btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ro = btcRpc('getblockchaininfo')
 | 
				
			||||||
 | 
					        assert(ro['bip9_softforks']['csv']['status'] == 'active')
 | 
				
			||||||
 | 
					        assert(ro['bip9_softforks']['segwit']['status'] == 'active')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ro = nmcRpc('getwalletinfo')
 | 
				
			||||||
 | 
					        print('nmcRpc', ro)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cls.http_threads = []
 | 
				
			||||||
 | 
					        host = '0.0.0.0'  # All interfaces (docker)
 | 
				
			||||||
 | 
					        for i in range(3):
 | 
				
			||||||
 | 
					            t = HttpThread(cls.swap_clients[i].fp, host, TEST_HTML_PORT + i, False, cls.swap_clients[i])
 | 
				
			||||||
 | 
					            cls.http_threads.append(t)
 | 
				
			||||||
 | 
					            t.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGINT, signal_handler)
 | 
				
			||||||
 | 
					        cls.update_thread = threading.Thread(target=run_loop, args=(cls,))
 | 
				
			||||||
 | 
					        cls.update_thread.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def tearDownClass(cls):
 | 
				
			||||||
 | 
					        global stop_test
 | 
				
			||||||
 | 
					        logging.info('Finalising')
 | 
				
			||||||
 | 
					        stop_test = True
 | 
				
			||||||
 | 
					        cls.update_thread.join()
 | 
				
			||||||
 | 
					        for t in cls.http_threads:
 | 
				
			||||||
 | 
					            t.stop()
 | 
				
			||||||
 | 
					            t.join()
 | 
				
			||||||
 | 
					        for c in cls.swap_clients:
 | 
				
			||||||
 | 
					            c.fp.close()
 | 
				
			||||||
 | 
					        for d in cls.daemons:
 | 
				
			||||||
 | 
					            logging.info('Terminating %d', d.pid)
 | 
				
			||||||
 | 
					            d.terminate()
 | 
				
			||||||
 | 
					            d.wait(timeout=10)
 | 
				
			||||||
 | 
					            if d.stdout:
 | 
				
			||||||
 | 
					                d.stdout.close()
 | 
				
			||||||
 | 
					            if d.stderr:
 | 
				
			||||||
 | 
					                d.stderr.close()
 | 
				
			||||||
 | 
					            if d.stdin:
 | 
				
			||||||
 | 
					                d.stdin.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(Test, cls).tearDownClass()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wait_for_offer(self, swap_client, offer_id):
 | 
				
			||||||
 | 
					        logging.info('wait_for_offer %s', offer_id.hex())
 | 
				
			||||||
 | 
					        for i in range(20):
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					            offers = swap_client.listOffers()
 | 
				
			||||||
 | 
					            for offer in offers:
 | 
				
			||||||
 | 
					                if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
 | 
					        raise ValueError('wait_for_offer timed out.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wait_for_bid(self, swap_client, bid_id):
 | 
				
			||||||
 | 
					        logging.info('wait_for_bid %s', bid_id.hex())
 | 
				
			||||||
 | 
					        for i in range(20):
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					            bids = swap_client.listBids()
 | 
				
			||||||
 | 
					            for bid in bids:
 | 
				
			||||||
 | 
					                if bid.bid_id == bid_id and bid.was_received:
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
 | 
					        raise ValueError('wait_for_bid timed out.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wait_for_in_progress(self, swap_client, bid_id, sent=False):
 | 
				
			||||||
 | 
					        logging.info('wait_for_in_progress %s', bid_id.hex())
 | 
				
			||||||
 | 
					        for i in range(20):
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					            swaps = swap_client.listSwapsInProgress()
 | 
				
			||||||
 | 
					            for b in swaps:
 | 
				
			||||||
 | 
					                if b[0] == bid_id:
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
 | 
					        raise ValueError('wait_for_in_progress timed out.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wait_for_bid_state(self, swap_client, bid_id, state, sent=False, seconds_for=30):
 | 
				
			||||||
 | 
					        logging.info('wait_for_bid_state %s %s', bid_id.hex(), str(state))
 | 
				
			||||||
 | 
					        for i in range(seconds_for):
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					            bid = swap_client.getBid(bid_id)
 | 
				
			||||||
 | 
					            if bid.state >= state:
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					        raise ValueError('wait_for_bid_state timed out.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wait_for_bid_tx_state(self, swap_client, bid_id, initiate_state, participate_state, seconds_for=30):
 | 
				
			||||||
 | 
					        logging.info('wait_for_bid_tx_state %s %s %s', bid_id.hex(), str(initiate_state), str(participate_state))
 | 
				
			||||||
 | 
					        for i in range(seconds_for):
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					            bid = swap_client.getBid(bid_id)
 | 
				
			||||||
 | 
					            if (initiate_state is None or bid.initiate_txn_state == initiate_state) \
 | 
				
			||||||
 | 
					               and (participate_state is None or bid.participate_txn_state == participate_state):
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					        raise ValueError('wait_for_bid_tx_state timed out.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_02_part_ltc(self):
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info('---------- Test PART to NMC')
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(Coins.PART, Coins.NMC, 100 * COIN, 0.1 * COIN, 100 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_offer(swap_clients[1], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[1].listOffers()
 | 
				
			||||||
 | 
					        assert(len(offers) == 1)
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid(swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_in_progress(swap_clients[1], bid_id, sent=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60)
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = json.loads(urlopen('http://localhost:1800/json').read())
 | 
				
			||||||
 | 
					        js_1 = json.loads(urlopen('http://localhost:1801/json').read())
 | 
				
			||||||
 | 
					        assert(js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert(js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_03_nmc_part(self):
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info('---------- Test NMC to PART')
 | 
				
			||||||
 | 
					        offer_id = swap_clients[1].postOffer(Coins.NMC, Coins.PART, 10 * COIN, 9.0 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_offer(swap_clients[0], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[0].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[0].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid(swap_clients[1], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[1].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_in_progress(swap_clients[0], bid_id, sent=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60)
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = json.loads(urlopen('http://localhost:1800/json').read())
 | 
				
			||||||
 | 
					        js_1 = json.loads(urlopen('http://localhost:1801/json').read())
 | 
				
			||||||
 | 
					        assert(js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert(js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_04_nmc_btc(self):
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info('---------- Test NMC to BTC')
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_offer(swap_clients[1], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[1].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid(swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_in_progress(swap_clients[1], bid_id, sent=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60)
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True, seconds_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0bid = json.loads(urlopen('http://localhost:1800/json/bids/{}'.format(bid_id.hex())).read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = json.loads(urlopen('http://localhost:1800/json').read())
 | 
				
			||||||
 | 
					        js_1 = json.loads(urlopen('http://localhost:1801/json').read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert(js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert(js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_05_refund(self):
 | 
				
			||||||
 | 
					        # Seller submits initiate txn, buyer doesn't respond
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info('---------- Test refund, NMC to BTC')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Note the lock value is absolute.
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST,
 | 
				
			||||||
 | 
					                                             ABS_LOCK_BLOCKS, 20)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_offer(swap_clients[1], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[1].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[1].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid(swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[1].abandonBid(bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60)
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[1], bid_id, BidStates.BID_ABANDONED, sent=True, seconds_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = json.loads(urlopen('http://localhost:1800/json').read())
 | 
				
			||||||
 | 
					        js_1 = json.loads(urlopen('http://localhost:1801/json').read())
 | 
				
			||||||
 | 
					        assert(js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert(js_1['num_swapping'] == 0 and js_1['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_06_self_bid(self):
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info('---------- Test same client, BTC to NMC')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0_before = json.loads(urlopen('http://localhost:1800/json').read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 10 * COIN, 10 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_offer(swap_clients[0], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[0].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[0].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid(swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid_tx_state(swap_clients[0], bid_id, TxStates.TX_REDEEMED, TxStates.TX_REDEEMED, seconds_for=60)
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, seconds_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0 = json.loads(urlopen('http://localhost:1800/json').read())
 | 
				
			||||||
 | 
					        assert(js_0['num_swapping'] == 0 and js_0['num_watched_outputs'] == 0)
 | 
				
			||||||
 | 
					        assert(js_0['num_recv_bids'] == js_0_before['num_recv_bids'] + 1 and js_0['num_sent_bids'] == js_0_before['num_sent_bids'] + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_07_error(self):
 | 
				
			||||||
 | 
					        swap_clients = self.swap_clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info('---------- Test error, BTC to NMC, set fee above bid value')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        js_0_before = json.loads(urlopen('http://localhost:1800/json').read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offer_id = swap_clients[0].postOffer(Coins.NMC, Coins.BTC, 0.001 * COIN, 1.0 * COIN, 0.001 * COIN, SwapTypes.SELLER_FIRST, ABS_LOCK_TIME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_offer(swap_clients[0], offer_id)
 | 
				
			||||||
 | 
					        offers = swap_clients[0].listOffers()
 | 
				
			||||||
 | 
					        for offer in offers:
 | 
				
			||||||
 | 
					            if offer.offer_id == offer_id:
 | 
				
			||||||
 | 
					                bid_id = swap_clients[0].postBid(offer_id, offer.amount_from)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid(swap_clients[0], bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].acceptBid(bid_id)
 | 
				
			||||||
 | 
					        swap_clients[0].coin_clients[Coins.BTC]['override_feerate'] = 10.0
 | 
				
			||||||
 | 
					        swap_clients[0].coin_clients[Coins.NMC]['override_feerate'] = 10.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wait_for_bid_state(swap_clients[0], bid_id, BidStates.BID_ERROR, seconds_for=60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def pass_99_delay(self):
 | 
				
			||||||
 | 
					        global stop_test
 | 
				
			||||||
 | 
					        logging.info('Delay')
 | 
				
			||||||
 | 
					        for i in range(60 * 5):
 | 
				
			||||||
 | 
					            if stop_test:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					            print('delay', i)
 | 
				
			||||||
 | 
					        stop_test = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    unittest.main()
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user