def test_listutxos_and_freeze(self): self.daemon.auth_disabled = True agent = get_nontor_agent() pre_addr = self.get_route_root() pre_addr += "/wallet/" pre_addr += self.daemon.wallet_name addr = pre_addr + "/utxos" addr = addr.encode() yield self.do_request(agent, b"GET", addr, None, self.process_listutxos_response) # Test of freezing is currently very primitive: we only # check that the action was accepted; a full test would # involve checking that spending the coin works or doesn't # work, as expected. addr = pre_addr + "/freeze" addr = addr.encode() utxostr = self.mixdepth1_utxos[0]["utxo"] body = BytesProducer( json.dumps({ "utxo-string": utxostr, "freeze": True }).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_utxo_freeze) body = BytesProducer( json.dumps({ "utxo-string": utxostr, "freeze": False }).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_utxo_freeze)
def test_session(self): agent = get_nontor_agent() addr = self.get_route_root() addr += "/session" addr = addr.encode() yield self.do_request(agent, b"GET", addr, None, self.process_session_response)
def test_direct_send_and_display_wallet(self): """ First spend a coin, then check the balance via the display wallet output. """ self.daemon.auth_disabled = True agent = get_nontor_agent() addr = self.get_route_root() addr += "/wallet/" addr += self.daemon.wallet_name addr += "/taker/direct-send" addr = addr.encode() body = BytesProducer( json.dumps({ "mixdepth": "1", "amount_sats": "100000000", "destination": "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br" }).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_direct_send_response) # before querying the wallet display, set a label to check: labeladdr = self.daemon.services["wallet"].get_addr(0, 0, 0) self.daemon.services["wallet"].set_address_label( labeladdr, "test-wallet-rpc-label") # force the wallet service txmonitor to wake up, to see the new # tx before querying /display: self.daemon.services["wallet"].transaction_monitor() addr = self.get_route_root() addr += "/wallet/" addr += self.daemon.wallet_name addr += "/display" addr = addr.encode() yield self.do_request(agent, b"GET", addr, None, self.process_wallet_display_response)
def test_get_seed(self): self.daemon.auth_disabled = True agent = get_nontor_agent() addr = self.get_route_root() addr += "/wallet/" addr += self.daemon.wallet_name addr += "/getseed" addr = addr.encode() yield self.do_request(agent, b"GET", addr, None, self.process_get_seed_response)
def test_gettimelockaddress(self): self.daemon.auth_disabled = True agent = get_nontor_agent() addr = self.get_route_root() addr += "/wallet/" addr += self.daemon.wallet_name addr += "/address/timelock/new/2023-02" addr = addr.encode() yield self.do_request(agent, b"GET", addr, None, self.process_new_addr_response)
def do_test_payment(self, wc1, wc2, amt=1.1): wallet_structures = [self.wallet_structure] * 2 wallet_cls = (wc1, wc2) self.wallet_services = [] self.wallet_services.append(make_wallets_to_list(make_wallets( 1, wallet_structures=[wallet_structures[0]], mean_amt=self.mean_amt, wallet_cls=wallet_cls[0]))[0]) self.wallet_services.append(make_wallets_to_list(make_wallets( 1, wallet_structures=[wallet_structures[1]], mean_amt=self.mean_amt, wallet_cls=wallet_cls[1]))[0]) jm_single().bc_interface.tickchain() sync_wallets(self.wallet_services) # For accounting purposes, record the balances # at the start. self.rsb = getbals(self.wallet_services[0], 0) self.ssb = getbals(self.wallet_services[1], 0) self.cj_amount = int(amt * 10**8) def cbStopListening(): return self.port.stopListening() b78rm = JMBIP78ReceiverManager(self.wallet_services[0], 0, self.cj_amount, 47083) resource = DummyBIP78ReceiverResource(jmprint, cbStopListening, b78rm) self.site = Site(resource) self.site.displayTracebacks = False # NB The connectivity aspects of the onion-based BIP78 setup # are time heavy. This server is TCP only. self.port = reactor.listenTCP(47083, self.site) self.addCleanup(cbStopListening) # setup of spender bip78_btc_amount = amount_to_btc(amount_to_sat(self.cj_amount)) bip78_uri = encode_bip21_uri(str(b78rm.receiving_address), {"amount": bip78_btc_amount, "pj": b"http://127.0.0.1:47083"}, safe=":/") self.manager = parse_payjoin_setup(bip78_uri, self.wallet_services[1], 0) self.manager.mode = "testing" success, msg = make_payment_psbt(self.manager) assert success, msg params = make_payjoin_request_params(self.manager) # avoiding backend daemon (testing only jmclient code here), # we send the http request manually: serv = b"http://127.0.0.1:47083" agent = get_nontor_agent() body = BytesProducer(self.manager.initial_psbt.to_base64().encode("utf-8")) url_parts = list(wrapped_urlparse(serv)) url_parts[4] = urlencode(params).encode("utf-8") destination_url = urlparse.urlunparse(url_parts) d = agent.request(b"POST", destination_url, Headers({"Content-Type": ["text/plain"]}), bodyProducer=body) d.addCallback(bip78_receiver_response, self.manager) return d
def test_getaddress(self): """ Tests that we can source a valid address for deposits using getaddress. """ self.daemon.auth_disabled = True agent = get_nontor_agent() addr = self.get_route_root() addr += "/wallet/" addr += self.daemon.wallet_name addr += "/address/new/3" addr = addr.encode() yield self.do_request(agent, b"GET", addr, None, self.process_new_addr_response)
def getAgentDestination(self, server, params=None): tor_url_data = is_hs_uri(server) if tor_url_data: # note: SSL over Tor not supported at the moment: agent = get_tor_agent(self.socks5_host, self.socks5_port) else: agent = get_nontor_agent(self.tls_whitelist) destination_url = server.encode("utf-8") url_parts = list(wrapped_urlparse(destination_url)) if params: url_parts[4] = urlencode(params).encode("utf-8") destination_url = urlparse.urlunparse(url_parts) return (agent, destination_url)
def test_maker_start_stop(self): """ Tests that we can start the maker service. As for the taker coinjoin test, this is currently a simple/artificial test, only checking return status codes and state updates, but not checking that an actual backend maker service is started. """ self.daemon.auth_disabled = True agent = get_nontor_agent() addr_start = self.get_route_root() addr_start += "/wallet/" addr_start += self.daemon.wallet_name addr = addr_start + "/maker/start" addr = addr.encode() body = BytesProducer( json.dumps({ "txfee": "0", "cjfee_a": "1000", "cjfee_r": "0.0002", "ordertype": "reloffer", "minsize": "1000000" }).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_maker_start) # For the second phase, since we are not currently processing # via actual backend connections, we need to mock the client # protocol instance that requests shutdown of all message channels: class DummyMakerClientProto(object): def request_mc_shutdown(self): jlog.info("Message channel shutdown request registered.") self.daemon.services["maker"].clientfactory.proto_client = \ DummyMakerClientProto() addr = addr_start + "/maker/stop" addr = addr.encode() yield self.do_request(agent, b"GET", addr, None, self.process_maker_stop)
def test_do_coinjoin(self): """ This slightly weird test curently only tests *requesting* a coinjoin; because there are no makers running in the test suite, the Taker will give up early due to the empty orderbook, but that is OK since this API call only makes the request. """ self.daemon.auth_disabled = True # in normal operations, the RPC call will trigger # the jmclient to connect to an *existing* daemon # that was created on startup, but here, that daemon # does not yet exist, so we will get 503 Backend Not Ready, # unless we manually create it: scon, ccon = start_reactor( jm_single().config.get("DAEMON", "daemon_host"), jm_single().config.getint("DAEMON", "daemon_port"), None, daemon=True, rs=False) # must be manually set: self.scon = scon agent = get_nontor_agent() addr = self.get_route_root() addr += "/wallet/" addr += self.daemon.wallet_name addr += "/taker/coinjoin" addr = addr.encode() body = BytesProducer( json.dumps({ "mixdepth": "1", "amount_sats": "22000000", "counterparties": "2", "destination": "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br" }).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_do_coinjoin_response)
def test_create_list_lock_unlock(self): """ A batch of tests in sequence here, so we can track the state of a created wallet and check it is what is expected. We test create first, so we have a wallet. 1. create a wallet and have it persisted to disk in ./wallets, and get a token. 2. lock that wallet. 3. create a second wallet as above. 4. list wallets and check they contain the new wallet. 5. lock the existing wallet service, using the token. 6. Unlock the original wallet with /unlock, get a token. 7. Unlock the second wallet with /unlock, get a token. """ # before starting, we have to shut down the existing # wallet service (usually this would be `lock`): self.daemon.services["wallet"] = None self.daemon.stopService() self.daemon.auth_disabled = False wfn1 = self.get_wallet_file_name(1) wfn2 = self.get_wallet_file_name(2) self.wfnames = [wfn1, wfn2] agent = get_nontor_agent() root = self.get_route_root() # 1. Create first addr = root + "/wallet/create" addr = addr.encode() body = BytesProducer( json.dumps({ "walletname": wfn1, "password": "******", "wallettype": "sw-fb" }).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_create_wallet_response) # 1a. Session request with valid token; should succeed yield self.do_session_request(agent, root, self.authorized_session_request_handler, token=self.jwt_token) # 1b. Session request without token, even though one is active; should succeed yield self.do_session_request(agent, root, self.authorized_session_request_handler) # 2. now *lock* addr = root + "/wallet/" + wfn1 + "/lock" addr = addr.encode() jlog.info("Using address: {}".format(addr)) yield self.do_request(agent, b"GET", addr, None, self.process_lock_response, token=self.jwt_token) # 2a. Session request with now invalid token; should fail yield self.do_session_request( agent, root, self.unauthorized_session_request_handler, token=self.jwt_token) # 2b. Session request without token, should still succeed. yield self.do_session_request(agent, root, self.authorized_session_request_handler) # 3. Create this secondary wallet (so we can test re-unlock) addr = root + "/wallet/create" addr = addr.encode() body = BytesProducer( json.dumps({ "walletname": wfn2, "password": "******", "wallettype": "sw" }).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_create_wallet_response) # 4. List wallets addr = root + "/wallet/all" addr = addr.encode() # does not require a token, though we just got one. yield self.do_request(agent, b"GET", addr, None, self.process_list_wallets_response) # 5. now *lock* the active. addr = root + "/wallet/" + wfn2 + "/lock" addr = addr.encode() jlog.info("Using address: {}".format(addr)) yield self.do_request(agent, b"GET", addr, None, self.process_lock_response, token=self.jwt_token) # wallet service should now be stopped. # 6. Unlock the original wallet addr = root + "/wallet/" + wfn1 + "/unlock" addr = addr.encode() body = BytesProducer(json.dumps({"password": "******"}).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_unlock_response) # 7. Unlock the second wallet again addr = root + "/wallet/" + wfn2 + "/unlock" addr = addr.encode() body = BytesProducer(json.dumps({"password": "******"}).encode()) yield self.do_request(agent, b"POST", addr, body, self.process_unlock_response)
def start_test_taker(wallet_service, i, num_ygs): # this rpc manager has auth disabled, # and the wallet_service is set manually, # so no unlock etc. mgr = TWalletRPCManager() mgr.daemon.services["wallet"] = wallet_service # because we are manually setting the wallet_service # of the JMWalletDaemon instance, we do not follow the # usual flow of `initialize_wallet_service`, we do not set # the auth token or start the websocket; so we must manually # sync the wallet, including bypassing any restart callback: def dummy_restart_callback(msg): log.warn("Ignoring rescan request from backend wallet service: " + msg) mgr.daemon.services["wallet"].add_restart_callback(dummy_restart_callback) mgr.daemon.wallet_name = wallet_name mgr.daemon.services["wallet"].startService() def get_client_factory(): clientfactory = RegtestJMClientProtocolFactory(mgr.daemon.taker, proto_type="TAKER") clientfactory.i = i clientfactory.set_directory_nodes(directory_node_indices) return clientfactory mgr.daemon.get_client_factory = get_client_factory # before preparing the RPC call to the wallet daemon, # we decide a coinjoin destination, counterparty count and amount. # Choosing a destination in the wallet is a bit easier because # we can query the mixdepth balance at the end. coinjoin_destination = mgr.daemon.services["wallet"].get_internal_addr(4) cj_amount = 22000000 def n_cps_from_n_ygs(n): if n > 4: return n - 2 if n > 2: return 2 assert False, "Need at least 3 yield generators to test" n_cps = n_cps_from_n_ygs(num_ygs) # once the taker is finished we sanity check before # shutting down: def dummy_taker_finished(res, fromtx=False, waittime=0.0, txdetails=None): jmprint("Taker is finished") # check that the funds have arrived. mbal = mgr.daemon.services["wallet"].get_balance_by_mixdepth()[4] assert mbal == cj_amount jmprint("Funds: {} sats successfully arrived into mixdepth 4.".format(cj_amount)) stop_reactor() mgr.daemon.taker_finished = dummy_taker_finished mgr.start() agent = get_nontor_agent() addr = mgr.get_route_root() addr += "/wallet/" addr += mgr.daemon.wallet_name addr += "/taker/coinjoin" addr = addr.encode() body = BytesProducer(json.dumps({"mixdepth": "1", "amount_sats": cj_amount, "counterparties": str(n_cps), "destination": coinjoin_destination}).encode()) yield mgr.do_request(agent, b"POST", addr, body, process_coinjoin_response)