def test_makeInt12(self): def test_case(vs, vf, expect_int): i = make_int(vs, 12) assert (i == expect_int and isinstance(i, int)) i = make_int(vf, 12) assert (i == expect_int and isinstance(i, int)) vs_out = format_amount(i, 12) # Strip for i in range(7): if vs_out[-1] == '0': vs_out = vs_out[:-1] if '.' in vs: assert (vs_out == vs) else: assert (vs_out[:-2] == vs) test_case('0.123456789', 0.123456789, 123456789000) test_case('0.123456789123', 0.123456789123, 123456789123) try: make_int('0.1234567891234', 12) assert (False) except Exception as e: assert (str(e) == 'Mantissa too long') validate_amount('0.123456789123', 12) try: validate_amount('0.1234567891234', 12) assert (False) except Exception as e: assert ('Too many decimal places' in str(e)) try: validate_amount(0.1234567891234, 12) assert (False) except Exception as e: assert ('Too many decimal places' in str(e))
def test_case(vs, vf, expect_int): i = make_int(vs, 12) assert (i == expect_int and isinstance(i, int)) i = make_int(vf, 12) assert (i == expect_int and isinstance(i, int)) vs_out = format_amount(i, 12) # Strip for i in range(7): if vs_out[-1] == '0': vs_out = vs_out[:-1] if '.' in vs: assert (vs_out == vs) else: assert (vs_out[:-2] == vs)
def test_make_int(self): def test_case(vs, vf, expect_int): i = make_int(vs) assert (i == expect_int and isinstance(i, int)) i = make_int(vf) assert (i == expect_int and isinstance(i, int)) vs_out = format_amount(i, 8) # Strip for i in range(7): if vs_out[-1] == '0': vs_out = vs_out[:-1] if '.' in vs: assert (vs_out == vs) else: assert (vs_out[:-2] == vs) test_case('0', 0, 0) test_case('1', 1, 100000000) test_case('10', 10, 1000000000) test_case('0.00899999', 0.00899999, 899999) test_case('899999.0', 899999.0, 89999900000000) test_case('899999.00899999', 899999.00899999, 89999900899999) test_case('0.0', 0.0, 0) test_case('1.0', 1.0, 100000000) test_case('1.1', 1.1, 110000000) test_case('1.2', 1.2, 120000000) test_case('0.00899991', 0.00899991, 899991) test_case('0.0089999', 0.0089999, 899990) test_case('0.0089991', 0.0089991, 899910) test_case('0.123', 0.123, 12300000) test_case('123000.000123', 123000.000123, 12300000012300) try: make_int('0.123456789') assert (False) except Exception as e: assert (str(e) == 'Mantissa too long') validate_amount('0.12345678') # floor assert (make_int('0.123456789', r=-1) == 12345678) # Round up assert (make_int('0.123456789', r=1) == 12345679)
def test_04_follower_recover_b_lock_tx(self): ID_ALICE_SWAP = os.path.join(TEST_DIR, 'test_04_alice_swap_state') + '.json' ID_BOB_SWAP = os.path.join(TEST_DIR, 'test_04_bob_swap_state') + '.json' alice_btc_start = make_int( callnoderpc(ID_ALICE_PART, 'getbalances')['mine']['trusted']) bob_btc_start = make_int( callnoderpc(ID_BOB_PART, 'getbalances')['mine']['trusted']) alice_xmr_start = self.callxmrnodewallet(ID_ALICE_XMR, 'get_balance')['balance'] bob_xmr_start = self.callxmrnodewallet(ID_BOB_XMR, 'get_balance')['balance'] logging.info( 'Test start wallet states:\nalice_btc_start: %ld\nbob_btc_start: %ld\nalice_xmr_start: %ld\nbob_xmr_start: %ld', alice_btc_start, bob_btc_start, alice_xmr_start, bob_xmr_start) # Same steps as in test_01_swap_successful self.startSwap(ID_ALICE_SWAP, ID_BOB_SWAP, 3, 4) msg2f = callSwapTool(ID_ALICE_SWAP, 'msg2f') callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg2f) msg3l = callSwapTool(ID_BOB_SWAP, 'msg3l') callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg3l) msg4f = callSwapTool(ID_ALICE_SWAP, 'msg4f') a_lock_txid = callSwapTool(ID_ALICE_SWAP, 'publishalocktx').strip() logging.info( 'Bob verifies the lock spend tx and encrypted signature from Alice.' ) callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg4f) logging.info('Bob waits for the script-chain lock tx to confirm.') num_tries = 30 for i in range(1 + num_tries): rv = callSwapTool(ID_BOB_SWAP, 'confirmalocktx') print('confirmalocktx', rv) if rv.strip() == 'True': break if i >= num_tries: raise ValueError( 'Timed out waiting for script-chain lock tx to confirm.') logging.info('Then publishes the second-chain lock tx.') b_lock_txid = callSwapTool(ID_BOB_SWAP, 'publishblocktx') logging.info( 'Alice waits for the scriptless-chain lock tx to confirm.') num_tries = 120 for i in range(1 + num_tries): rv = callSwapTool(ID_ALICE_SWAP, 'confirmblocktx') print('confirmblocktx', rv) if rv.strip() == 'True': break if i >= num_tries: raise ValueError( 'Timed out waiting for scriptless-chain lock tx to confirm.' ) time.sleep(2) logging.info( 'Alice detects a problem with the scriptless-chain lock tx and decides to cancel the swap' ) a_lock_refund_txid = self.publishALockRefundTx(ID_ALICE_PART, ID_ALICE_SWAP) # Import key to receive refund in wallet. Simple method for testing. kal = callSwapTool(ID_ALICE_SWAP, 'getkal') kal_wif = bytes_to_wif(h2b(kal), prefix=0x2e) callnoderpc(ID_ALICE_PART, 'importprivkey', [kal_wif, 'swap refund']) alockrefundspendtxid = callSwapTool(ID_ALICE_SWAP, 'publishalockrefundspendtx') rv = callnoderpc(ID_ALICE_PART, 'getbalances') print('getbalances', dumpj(rv)) alice_btc = make_int(rv['mine']['trusted']) + make_int( rv['mine']['untrusted_pending']) logging.info('alice_btc %ld', alice_btc) logging.info('Bob waits for Alice to spend the lock refund tx.') num_tries = 20 for i in range(1 + num_tries): rv = callSwapTool(ID_BOB_SWAP, 'findalockrefundspendtx') print('findalockrefundspendtx', rv) if rv.strip() == 'True': break if i >= num_tries: raise ValueError( 'Timed out waiting for script-chain lock refund spend tx to confirm.' ) time.sleep(1) logging.info('Then he can recover his scriptless-chain lock tx coin.') self.callxmrnodewallet(ID_BOB_XMR, 'open_wallet', {'filename': 'testwallet'}) xmr_addr_bob = self.callxmrnodewallet(ID_BOB_XMR, 'get_address')['address'] rv = callSwapTool(ID_BOB_SWAP, 'redeemblocktx', str_param=xmr_addr_bob) print('redeemblocktx', rv) self.callxmrnodewallet(ID_BOB_XMR, 'close_wallet') self.callxmrnodewallet(ID_BOB_XMR, 'open_wallet', {'filename': 'testwallet'})
def test_03_follower_recover_a_lock_tx(self): ID_ALICE_SWAP = os.path.join(TEST_DIR, 'test_03_alice_swap_state') + '.json' ID_BOB_SWAP = os.path.join(TEST_DIR, 'test_03_bob_swap_state') + '.json' alice_btc_start = make_int( callnoderpc(ID_ALICE_PART, 'getbalances')['mine']['trusted']) bob_btc_start = make_int( callnoderpc(ID_BOB_PART, 'getbalances')['mine']['trusted']) alice_xmr_start = self.callxmrnodewallet(ID_ALICE_XMR, 'get_balance')['balance'] bob_xmr_start = self.callxmrnodewallet(ID_BOB_XMR, 'get_balance')['balance'] logging.info( 'Test start wallet states:\nalice_btc_start: %ld\nbob_btc_start: %ld\nalice_xmr_start: %ld\nbob_xmr_start: %ld', alice_btc_start, bob_btc_start, alice_xmr_start, bob_xmr_start) # Same steps as in test_01_swap_successful self.startSwap(ID_ALICE_SWAP, ID_BOB_SWAP, 3, 4) msg2f = callSwapTool(ID_ALICE_SWAP, 'msg2f') callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg2f) msg3l = callSwapTool(ID_BOB_SWAP, 'msg3l') callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg3l) msg4f = callSwapTool(ID_ALICE_SWAP, 'msg4f') a_lock_txid = callSwapTool(ID_ALICE_SWAP, 'publishalocktx').strip() logging.info('Alice stops responding here.') # Wait for the mining node to receive the tx for i in range(10): try: callnoderpc(0, 'getrawtransaction', [a_lock_txid]) break except Exception as e: print('Waiting for node 0 to see tx', str(e)) time.sleep(1) a_lock_refund_txid = self.publishALockRefundTx(ID_BOB_PART, ID_BOB_SWAP) # Wait for the mining node to receive the tx for i in range(10): try: callnoderpc(0, 'getrawtransaction', [a_lock_refund_txid]) break except Exception as e: print('Waiting for node 0 to see tx', str(e)) time.sleep(1) btc_addr_bob = callnoderpc( ID_BOB_PART, 'getnewaddress', ['bob\'s addr', False, False, False, 'bech32']) ignr, a_pkhash_f = segwit_addr.decode('rtpw', btc_addr_bob) assert (a_pkhash_f is not None) a_lock_refund_spend_txid = self.publishALockRefundFollowerSpendTx( ID_BOB_PART, ID_BOB_SWAP, a_pkhash_f) rv = callnoderpc(ID_BOB_PART, 'getbalances') print('getbalances', dumpj(rv)) bob_btc_end = make_int(rv['mine']['trusted']) + make_int( rv['mine']['untrusted_pending']) logging.info('bob_btc_end %ld', bob_btc_end) assert (bob_btc_end > bob_btc_start)
def test_02_leader_recover_a_lock_tx(self): ID_ALICE_SWAP = os.path.join(TEST_DIR, 'test_02_alice_swap_state') + '.json' ID_BOB_SWAP = os.path.join(TEST_DIR, 'test_02_bob_swap_state') + '.json' alice_btc_start = make_int( callnoderpc(ID_ALICE_PART, 'getbalances')['mine']['trusted']) bob_btc_start = make_int( callnoderpc(ID_BOB_PART, 'getbalances')['mine']['trusted']) alice_xmr_start = self.callxmrnodewallet(ID_ALICE_XMR, 'get_balance')['balance'] bob_xmr_start = self.callxmrnodewallet(ID_BOB_XMR, 'get_balance')['balance'] logging.info( 'Test start wallet states:\nalice_btc_start: %ld\nbob_btc_start: %ld\nalice_xmr_start: %ld\nbob_xmr_start: %ld', alice_btc_start, bob_btc_start, alice_xmr_start, bob_xmr_start) self.startSwap(ID_ALICE_SWAP, ID_BOB_SWAP, 2, 3) logging.info( 'Alice creates the script-chain lock and refund txns and signs the refund tx, sends to Bob.' ) msg2f = callSwapTool(ID_ALICE_SWAP, 'msg2f') logging.info( 'Bob verifies the txns and signs the refund tx and creates an encrypted signature for the refund spend tx encumbered by Alice\'s coin B key share.' ) callSwapTool(ID_BOB_SWAP, 'processmsg', str_param=msg2f) msg3l = callSwapTool(ID_BOB_SWAP, 'msg3l') logging.info( 'Alice verifies the signature and encrypted signature from Bob.') callSwapTool(ID_ALICE_SWAP, 'processmsg', str_param=msg3l) logging.info( 'Creates the lock spend tx and signs an encrypted signature encumbered by Bob\'s coin B key share' ) msg4f = callSwapTool(ID_ALICE_SWAP, 'msg4f') logging.info('Publishes the script-chain lock tx.') a_lock_txid = callSwapTool(ID_ALICE_SWAP, 'publishalocktx').strip() # Wait for the mining node to receive the tx for i in range(10): try: callnoderpc(0, 'getrawtransaction', [a_lock_txid]) break except Exception as e: print('Waiting for node 0 to see tx', str(e)) time.sleep(1) logging.info('Bob stops responding here.') alice_btc = make_int( callnoderpc(ID_ALICE_PART, 'getbalances')['mine']['trusted']) logging.info('alice_btc %ld', alice_btc) a_lock_refund_txid = self.publishALockRefundTx(ID_ALICE_PART, ID_ALICE_SWAP) # Import key to receive refund in wallet. Simple method for testing. kal = callSwapTool(ID_ALICE_SWAP, 'getkal') kal_wif = bytes_to_wif(h2b(kal), prefix=0x2e) callnoderpc(ID_ALICE_PART, 'importprivkey', [kal_wif, 'swap refund']) alockrefundspendtxid = callSwapTool(ID_ALICE_SWAP, 'publishalockrefundspendtx') rv = callnoderpc(ID_ALICE_PART, 'getbalances') alice_btc_end = make_int(rv['mine']['trusted']) + make_int( rv['mine']['untrusted_pending']) logging.info('alice_btc_end %ld', alice_btc_end) assert (alice_btc_end > alice_btc)
def initialiseSwap(swapinfo, data): try: a_coin = CoinIds[data['a_coin']] except Exception as e: raise ValueError('Unknown A coin type') # Coin A must have a script if a_coin == CoinIds.XMR: raise ValueError('Bad A coin type') try: b_coin = CoinIds[data['b_coin']] except Exception as e: raise ValueError('Unknown B coin type') swapinfo.a_connect = data['a_connect'] swapinfo.b_connect = data['b_connect'] ai = makeInterface(a_coin, swapinfo.a_connect) bi = makeInterface(b_coin, swapinfo.b_connect) a_amount = make_int(data['a_amount'], ai.exp()) b_amount = make_int(data['b_amount'], bi.exp()) a_feerate = make_int(data['a_feerate'], ai.exp()) b_feerate = make_int(data['b_feerate'], bi.exp()) if 'a_addr_f' in data: # Decode p2wpkh address addr = data['a_addr_f'] addr_split = addr.split('1') if len(addr_split) != 2: raise ValueError('Invalid bech32 address') if addr_split[0] not in ['bc', 'pw', 'bcrt', 'rtpw']: raise ValueError('Unknown bech32 hrp') ignr, pkh = segwit_addr.decode(addr_split[0], addr) a_pkhash_f = bytes(pkh) else: a_pkhash_f = h2b(data['a_pkhash_f']) lock1 = data['lock1'] if 'lock1' in data else 100 lock2 = data['lock2'] if 'lock2' in data else 101 restore_height_b = data[ 'b_restore_height'] if 'b_restore_height' in data else 0 check_a_lock_tx_inputs = data[ 'check_a_lock_tx_inputs'] if 'check_a_lock_tx_inputs' in data else True swapinfo.setSwapParameters(a_coin, a_amount, b_coin, b_amount, a_feerate, b_feerate, a_pkhash_f, lock1=lock1, lock2=lock2, restore_height_b=restore_height_b, check_a_lock_tx_inputs=check_a_lock_tx_inputs) if data['side'].lower() == 'a': swapinfo.initialiseLeader(ai, bi) elif data['side'].lower() == 'b': swapinfo.initialiseFollower(ai, bi) else: raise ValueError('Unknown swap side')