def test_ed25519(self): assert (encodepoint(edf.B) == b'Xfffffffffffffffffffffffffffffff') assert ( b2h(encodepoint(hashToEd25519(encodepoint(edf.B)))) == '13b663e5e06bf5301c77473bb2fc5beb51e4046e9b7efef2f6d1a324cb8b1094') test_point_2 = '97ab9932634c2a71ded409c73e84d64487dcc224f9728fde24ef3327782e68c3' assert ( b2h(encodepoint(hashToEd25519(h2b(test_point_2)))) == 'ade1232c101e6e42564b97ac2b38387a509df0a31d38e36bf4bdf4ad2f4f5573')
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 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')
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 main(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__doc__) parser.add_argument( '-v', '--version', action='version', version='%(prog)s {version}'.format(version=__version__)) parser.add_argument('swap_state_file', help='Path to the swap state file') parser.add_argument('opts', nargs=argparse.REMAINDER) args = parser.parse_args() swapinfo = SwapInfo() if os.path.isfile(args.swap_state_file): swapinfo.importFromFile(args.swap_state_file) if len(args.opts) == 0: method = 'info' else: method = args.opts[0] if method == 'info': printSwapInfo(swapinfo) return 0 elif method == 'init': data = toJSON(args.opts[1]) initialiseSwap(swapinfo, data) print('success') elif method == 'processmsg': msg = h2b(args.opts[1]) swapinfo.processMessage(msg) print('success') elif method == 'msg1f': msg = swapinfo.packageInitMsgToFollower() print(b2h(msg)) elif method == 'msg1l': msg = swapinfo.packageInitMsgToLeader() print(b2h(msg)) elif method == 'msg2f': msg = swapinfo.packageMSG2F() print(b2h(msg)) elif method == 'msg3l': msg = swapinfo.packageMSG3L() print(b2h(msg)) elif method == 'msg4f': msg = swapinfo.packageMSG4F() print(b2h(msg)) elif method == 'msg5f': msg = swapinfo.packageMSG5F() print(b2h(msg)) elif method == 'publishalocktx': txid = swapinfo.publishALockTx() print(txid) elif method == 'publishalockrefundtx': txid = swapinfo.publishALockRefundTx() print(txid) elif method == 'confirmalocktx': rv = swapinfo.hasALockTxConfirmed() print('True' if rv else 'False') return 0 elif method == 'publishblocktx': txid = swapinfo.publishBLockTx() print(txid) elif method == 'confirmblocktx': rv = swapinfo.hasBLockTxConfirmed() print('True' if rv else 'False') return 0 elif method == 'publishalockspendtx': txid = swapinfo.publishALockSpendTx() print(txid) elif method == 'findalockspendtx': rv = swapinfo.findALockSpendTx() print('True' if rv else 'False') elif method == 'redeemblocktx': dest = args.opts[1] txid = swapinfo.redeemBLockTx(dest) print(txid) elif method == 'publishalockrefundspendtx': txid = swapinfo.publishALockRefundSpendTx() print(txid) elif method == 'getkal': print(i2h(swapinfo.kal)) return 0 elif method == 'publishalockrefundspendftx': dest = h2b(args.opts[1]) txid = swapinfo.publishALockRefundSpendToFTx(dest) print(txid) elif method == 'findalockrefundspendtx': rv = swapinfo.findALockRefundSpendTx() print('True' if rv else 'False') else: raise ValueError('Invalid method') swapinfo.exportToFile(args.swap_state_file) return 0