diff --git a/README.md b/README.md index 341ec291..c49b9888 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,10 @@ Running the [API tests](./src/HavenoClient.test.ts) is the best way to develop a [`HavenoClient.ts`](./src/HavenoClient.ts) provides the client interface to Haveno's backend daemon. -1. [Run a local Haveno test network](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md) and then shut down the arbitrator, Alice, and Bob or run them as daemons, e.g. `make alice-daemon-local`. You may omit the arbitrator registration steps since it's done automatically in the tests. +1. [Run a local Haveno test network](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md) and then shut down the arbitrator, user1, and user2 or run them as daemons, e.g. `make user1-daemon-local`. You may omit the arbitrator registration steps since it's done automatically in the tests. 2. Clone this project to the same parent directory as the haveno project: `git clone https://github.com/haveno-dex/haveno-ts` 3. In a new terminal, start envoy with the config in haveno-ts/config/envoy.test.yaml (change absolute path for your system): `docker run --rm --add-host host.docker.internal:host-gateway -it -v ~/git/haveno-ts/config/envoy.test.yaml:/envoy.test.yaml -p 8079:8079 -p 8080:8080 -p 8081:8081 -p 8082:8082 -p 8083:8083 -p 8084:8084 -p 8085:8085 -p 8086:8086 envoyproxy/envoy-dev:8a2143613d43d17d1eb35a24b4a4a4c432215606 -c /envoy.test.yaml` -4. In a new terminal, start the funding wallet. This wallet will be automatically funded in order to fund Alice and Bob during the tests.
For example: `cd ~/git/haveno && make funding-wallet-local`. +4. In a new terminal, start the funding wallet. This wallet will be funded automatically in order to fund the tests.
For example: `cd ~/git/haveno && make funding-wallet-local`. 5. Install protobuf compiler v3.19.1 or later for your system:
mac: `brew install protobuf`
linux: `apt install protobuf-compiler` diff --git a/src/HavenoClient.test.ts b/src/HavenoClient.test.ts index 88991e5c..9408ea80 100644 --- a/src/HavenoClient.test.ts +++ b/src/HavenoClient.test.ts @@ -36,7 +36,7 @@ enum BaseCurrencyNetwork { } const TestConfig = { - logLevel: 3, + logLevel: 2, baseCurrencyNetwork: getBaseCurrencyNetwork(), networkType: getBaseCurrencyNetwork() == BaseCurrencyNetwork.XMR_MAINNET ? monerojs.MoneroNetworkType.MAINNET : getBaseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? monerojs.MoneroNetworkType.TESTNET : monerojs.MoneroNetworkType.STAGENET, moneroBinsDir: "../haveno/.localnet", @@ -66,7 +66,7 @@ const TestConfig = { minimumFunding: BigInt("5000000000000") }, defaultHavenod: { - logProcessOutput: true, // log output for processes started by tests (except arbitrator, alice, and bob which are configured separately) + logProcessOutput: true, // log output for processes started by tests (except arbitrator, user1, and user2 which are configured separately) apiPassword: "apitest", walletUsername: "haveno_user", walletDefaultPassword: "password", // only used if account password not set @@ -81,14 +81,14 @@ const TestConfig = { accountPasswordRequired: false, accountPassword: "abctesting123", }, { - appName: "haveno-" + getBaseCurrencyNetwork() + "_alice", // alice + appName: "haveno-" + getBaseCurrencyNetwork() + "_user1", // user1 logProcessOutput: true, url: "http://localhost:8080", accountPasswordRequired: false, accountPassword: "abctesting456", walletUrl: "http://127.0.0.1:38091", }, { - appName: "haveno-" + getBaseCurrencyNetwork() + "_bob", // bob + appName: "haveno-" + getBaseCurrencyNetwork() + "_user2", // user2 logProcessOutput: true, url: "http://localhost:8081", accountPasswordRequired: false, @@ -122,8 +122,8 @@ const TestConfig = { ], proxyPorts: new Map([ // map proxied ports to havenod api and p2p ports ["8079", ["9998", "4444"]], // arbitrator - ["8080", ["9999", "5555"]], // alice - ["8081", ["10000", "6666"]], // bob + ["8080", ["9999", "5555"]], // user1 + ["8081", ["10000", "6666"]], // user2 ["8082", ["10001", "7777"]], ["8083", ["10002", "7778"]], ["8084", ["10003", "7779"]], @@ -167,12 +167,12 @@ interface PostOfferConfig { // clients const startupHavenods: HavenoClient[] = []; let arbitrator: HavenoClient; -let alice: HavenoClient; -let bob: HavenoClient; +let user1: HavenoClient; +let user2: HavenoClient; let monerod: any; let fundingWallet: any; -let aliceWallet: any; -let bobWallet: any; +let user1Wallet: any; +let user2Wallet: any; // track started haveno processes const HAVENO_PROCESSES: HavenoClient[] = []; @@ -205,18 +205,18 @@ beforeAll(async () => { startupHavenods.push((settledPromise as PromiseFulfilledResult).value); } - // assign arbitrator alice, bob + // assign arbitrator user1, user2 arbitrator = startupHavenods[0]; - alice = startupHavenods[1]; - bob = startupHavenods[2]; + user1 = startupHavenods[1]; + user2 = startupHavenods[2]; // register arbitrator dispute agent await arbitrator.registerDisputeAgent("arbitrator", TestConfig.arbitratorPrivKey); // connect monero clients monerod = await monerojs.connectToDaemonRpc(TestConfig.monerod.url, TestConfig.monerod.username, TestConfig.monerod.password); - aliceWallet = await monerojs.connectToWalletRpc(TestConfig.startupHavenods[1].walletUrl, TestConfig.defaultHavenod.walletUsername, TestConfig.startupHavenods[1].accountPasswordRequired ? TestConfig.startupHavenods[1].accountPassword : TestConfig.defaultHavenod.walletDefaultPassword); - bobWallet = await monerojs.connectToWalletRpc(TestConfig.startupHavenods[2].walletUrl, TestConfig.defaultHavenod.walletUsername, TestConfig.startupHavenods[2].accountPasswordRequired ? TestConfig.startupHavenods[2].accountPassword : TestConfig.defaultHavenod.walletDefaultPassword); + user1Wallet = await monerojs.connectToWalletRpc(TestConfig.startupHavenods[1].walletUrl, TestConfig.defaultHavenod.walletUsername, TestConfig.startupHavenods[1].accountPasswordRequired ? TestConfig.startupHavenods[1].accountPassword : TestConfig.defaultHavenod.walletDefaultPassword); + user2Wallet = await monerojs.connectToWalletRpc(TestConfig.startupHavenods[2].walletUrl, TestConfig.defaultHavenod.walletUsername, TestConfig.startupHavenods[2].accountPasswordRequired ? TestConfig.startupHavenods[2].accountPassword : TestConfig.defaultHavenod.walletDefaultPassword); // create test data directory if it doesn't exist if (!fs.existsSync(TestConfig.testDataDir)) fs.mkdirSync(TestConfig.testDataDir); @@ -247,108 +247,108 @@ test("Can get the version", async () => { }); test("Can manage an account", async () => { - let charlie: HavenoClient | undefined; + let user3: HavenoClient | undefined; let err: any; try { - // start charlie without opening account - charlie = await initHaveno({autoLogin: false}); - assert(!await charlie.accountExists()); + // start user3 without opening account + user3 = await initHaveno({autoLogin: false}); + assert(!await user3.accountExists()); // test errors when account not open - await testAccountNotOpen(charlie); + await testAccountNotOpen(user3); // create account let password = "testPassword"; - await charlie.createAccount(password); - if (await charlie.isConnectedToMonero()) await charlie.getBalances(); // only connected if local node running - assert(await charlie.accountExists()); - assert(await charlie.isAccountOpen()); + await user3.createAccount(password); + if (await user3.isConnectedToMonero()) await user3.getBalances(); // only connected if local node running + assert(await user3.accountExists()); + assert(await user3.isAccountOpen()); // create payment account - const paymentAccount = await charlie.createCryptoPaymentAccount("My ETH account", TestConfig.cryptoAddresses[0].currencyCode, TestConfig.cryptoAddresses[0].address); + const paymentAccount = await user3.createCryptoPaymentAccount("My ETH account", TestConfig.cryptoAddresses[0].currencyCode, TestConfig.cryptoAddresses[0].address); // close account - await charlie.closeAccount(); - assert(await charlie.accountExists()); - assert(!await charlie.isAccountOpen()); - await testAccountNotOpen(charlie); + await user3.closeAccount(); + assert(await user3.accountExists()); + assert(!await user3.isAccountOpen()); + await testAccountNotOpen(user3); // open account with wrong password try { - await charlie.openAccount("wrongPassword"); + await user3.openAccount("wrongPassword"); throw new Error("Should have failed opening account with wrong password"); } catch (err: any) { assert.equal(err.message, "Incorrect password"); } // open account - await charlie.openAccount(password); - assert(await charlie.accountExists()); - assert(await charlie.isAccountOpen()); + await user3.openAccount(password); + assert(await user3.accountExists()); + assert(await user3.isAccountOpen()); - // restart charlie - const charlieConfig = {appName: charlie.getAppName(), autoLogin: false} - await releaseHavenoProcess(charlie); - charlie = await initHaveno(charlieConfig); - assert(await charlie.accountExists()); - assert(!await charlie.isAccountOpen()); + // restart user3 + const user3Config = {appName: user3.getAppName(), autoLogin: false} + await releaseHavenoProcess(user3); + user3 = await initHaveno(user3Config); + assert(await user3.accountExists()); + assert(!await user3.isAccountOpen()); // open account - await charlie.openAccount(password); - assert(await charlie.accountExists()); - assert(await charlie.isAccountOpen()); + await user3.openAccount(password); + assert(await user3.accountExists()); + assert(await user3.isAccountOpen()); // change password password = "newPassword"; - await charlie.changePassword(password); - assert(await charlie.accountExists()); - assert(await charlie.isAccountOpen()); + await user3.changePassword(password); + assert(await user3.accountExists()); + assert(await user3.isAccountOpen()); - // restart charlie - await releaseHavenoProcess(charlie); - charlie = await initHaveno(charlieConfig); - await testAccountNotOpen(charlie); + // restart user3 + await releaseHavenoProcess(user3); + user3 = await initHaveno(user3Config); + await testAccountNotOpen(user3); // open account - await charlie.openAccount(password); - assert(await charlie.accountExists()); - assert(await charlie.isAccountOpen()); + await user3.openAccount(password); + assert(await user3.accountExists()); + assert(await user3.isAccountOpen()); // backup account to zip file const zipFile = TestConfig.testDataDir + "/backup.zip"; const stream = fs.createWriteStream(zipFile); - const size = await charlie.backupAccount(stream); + const size = await user3.backupAccount(stream); stream.end(); assert(size > 0); // delete account which shuts down server - await charlie.deleteAccount(); // TODO: support deleting and restoring account without shutting down server, #310 - assert(!await charlie.isConnectedToDaemon()); - await releaseHavenoProcess(charlie); + await user3.deleteAccount(); // TODO: support deleting and restoring account without shutting down server, #310 + assert(!await user3.isConnectedToDaemon()); + await releaseHavenoProcess(user3); // restore account which shuts down server - charlie = await initHaveno(charlieConfig); + user3 = await initHaveno(user3Config); const zipBytes: Uint8Array = new Uint8Array(fs.readFileSync(zipFile)); - await charlie.restoreAccount(zipBytes); - assert(!await charlie.isConnectedToDaemon()); - await releaseHavenoProcess(charlie); + await user3.restoreAccount(zipBytes); + assert(!await user3.isConnectedToDaemon()); + await releaseHavenoProcess(user3); // open restored account - charlie = await initHaveno(charlieConfig); - assert(await charlie.accountExists()); - await charlie.openAccount(password); - assert(await charlie.isAccountOpen()); + user3 = await initHaveno(user3Config); + assert(await user3.accountExists()); + await user3.openAccount(password); + assert(await user3.isAccountOpen()); // check the persisted payment account - const paymentAccount2 = await charlie.getPaymentAccount(paymentAccount.getId()); + const paymentAccount2 = await user3.getPaymentAccount(paymentAccount.getId()); testCryptoPaymentAccountsEqual(paymentAccount, paymentAccount2); } catch (err2) { err = err2; } // stop and delete instances - if (charlie) await releaseHavenoProcess(charlie, true); + if (user3) await releaseHavenoProcess(user3, true); if (err) throw err; async function testAccountNotOpen(havenod: HavenoClient): Promise { // TODO: generalize this? @@ -365,40 +365,40 @@ test("Can manage an account", async () => { test("Can manage Monero daemon connections", async () => { let monerod2: any; - let charlie: HavenoClient | undefined; + let user3: HavenoClient | undefined; let err: any; try { - // start charlie - charlie = await initHaveno(); + // start user3 + user3 = await initHaveno(); // test default connections const monerodUrl1 = "http://127.0.0.1:" + getNetworkStartPort() + "8081"; // TODO: (woodser): move to config - let connections: UrlConnection[] = await charlie.getMoneroConnections(); + let connections: UrlConnection[] = await user3.getMoneroConnections(); testConnection(getConnection(connections, monerodUrl1)!, monerodUrl1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1); // test default connection - let connection: UrlConnection | undefined = await charlie.getMoneroConnection(); - assert(await charlie.isConnectedToMonero()); + let connection: UrlConnection | undefined = await user3.getMoneroConnection(); + assert(await user3.isConnectedToMonero()); testConnection(connection!, monerodUrl1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1); // TODO: should be no authentication? // add a new connection const fooBarUrl = "http://foo.bar"; - await charlie.addMoneroConnection(fooBarUrl); - connections = await charlie.getMoneroConnections(); + await user3.addMoneroConnection(fooBarUrl); + connections = await user3.getMoneroConnections(); connection = getConnection(connections, fooBarUrl); testConnection(connection!, fooBarUrl, OnlineStatus.UNKNOWN, AuthenticationStatus.NO_AUTHENTICATION, 0); // set prioritized connection without credentials - await charlie.setMoneroConnection(new UrlConnection() + await user3.setMoneroConnection(new UrlConnection() .setUrl(TestConfig.monerod2.url) .setPriority(1)); - connection = await charlie.getMoneroConnection(); + connection = await user3.getMoneroConnection(); testConnection(connection!, TestConfig.monerod2.url, undefined, undefined, 1); // status may or may not be known due to periodic connection checking // connection is offline - connection = await charlie.checkMoneroConnection(); - assert(!await charlie.isConnectedToMonero()); + connection = await user3.checkMoneroConnection(); + assert(!await user3.isConnectedToMonero()); testConnection(connection!, TestConfig.monerod2.url, OnlineStatus.OFFLINE, AuthenticationStatus.NO_AUTHENTICATION, 1); // start monerod2 @@ -416,100 +416,100 @@ test("Can manage Monero daemon connections", async () => { monerod2 = await monerojs.connectToDaemonRpc(cmd); // connection is online and not authenticated - connection = await charlie.checkMoneroConnection(); - assert(!await charlie.isConnectedToMonero()); + connection = await user3.checkMoneroConnection(); + assert(!await user3.isConnectedToMonero()); testConnection(connection!, TestConfig.monerod2.url, OnlineStatus.ONLINE, AuthenticationStatus.NOT_AUTHENTICATED, 1); // set connection credentials - await charlie.setMoneroConnection(new UrlConnection() + await user3.setMoneroConnection(new UrlConnection() .setUrl(TestConfig.monerod2.url) .setUsername(TestConfig.monerod2.username) .setPassword(TestConfig.monerod2.password) .setPriority(1)); - connection = await charlie.getMoneroConnection(); + connection = await user3.getMoneroConnection(); testConnection(connection!, TestConfig.monerod2.url, undefined, undefined, 1); // connection is online and authenticated - connection = await charlie.checkMoneroConnection(); - assert(await charlie.isConnectedToMonero()); + connection = await user3.checkMoneroConnection(); + assert(await user3.isConnectedToMonero()); testConnection(connection!, TestConfig.monerod2.url, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1); // change account password const password = "newPassword"; - await charlie.changePassword("newPassword"); + await user3.changePassword("newPassword"); - // restart charlie - const appName = charlie.getAppName(); - await releaseHavenoProcess(charlie); - charlie = await initHaveno({appName: appName, accountPassword: password}); + // restart user3 + const appName = user3.getAppName(); + await releaseHavenoProcess(user3); + user3 = await initHaveno({appName: appName, accountPassword: password}); // connection is restored, online, and authenticated - connection = await charlie.getMoneroConnection(); + connection = await user3.getMoneroConnection(); testConnection(connection!, TestConfig.monerod2.url, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1); - connections = await charlie.getMoneroConnections(); + connections = await user3.getMoneroConnections(); testConnection(getConnection(connections, monerodUrl1)!, monerodUrl1, OnlineStatus.UNKNOWN, AuthenticationStatus.NO_AUTHENTICATION, 1); // enable auto switch - await charlie.setAutoSwitch(true); + await user3.setAutoSwitch(true); // stop monerod await monerod2.stopProcess(); // test auto switch after periodic connection check await wait(TestConfig.daemonPollPeriodMs * 2); - connection = await charlie.getMoneroConnection(); + connection = await user3.getMoneroConnection(); testConnection(connection!, monerodUrl1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1); // stop auto switch and checking connection periodically - await charlie.setAutoSwitch(false); - await charlie.stopCheckingConnection(); + await user3.setAutoSwitch(false); + await user3.stopCheckingConnection(); // remove current connection - await charlie.removeMoneroConnection(monerodUrl1); + await user3.removeMoneroConnection(monerodUrl1); // check current connection - connection = await charlie.checkMoneroConnection(); + connection = await user3.checkMoneroConnection(); assert.equal(connection, undefined); // check all connections - await charlie.checkMoneroConnections(); - connections = await charlie.getMoneroConnections(); + await user3.checkMoneroConnections(); + connections = await user3.getMoneroConnections(); testConnection(getConnection(connections, fooBarUrl)!, fooBarUrl, OnlineStatus.OFFLINE, AuthenticationStatus.NO_AUTHENTICATION, 0); // set connection to previous url - await charlie.setMoneroConnection(fooBarUrl); - connection = await charlie.getMoneroConnection(); + await user3.setMoneroConnection(fooBarUrl); + connection = await user3.getMoneroConnection(); testConnection(connection!, fooBarUrl, OnlineStatus.OFFLINE, AuthenticationStatus.NO_AUTHENTICATION, 0); // set connection to new url const fooBarUrl2 = "http://foo.bar.xyz"; - await charlie.setMoneroConnection(fooBarUrl2); - connections = await charlie.getMoneroConnections(); + await user3.setMoneroConnection(fooBarUrl2); + connections = await user3.getMoneroConnections(); connection = getConnection(connections, fooBarUrl2); testConnection(connection!, fooBarUrl2, OnlineStatus.UNKNOWN, AuthenticationStatus.NO_AUTHENTICATION, 0); // reset connection - await charlie.setMoneroConnection(); - assert.equal(await charlie.getMoneroConnection(), undefined); + await user3.setMoneroConnection(); + assert.equal(await user3.getMoneroConnection(), undefined); // test auto switch after start checking connection - await charlie.setAutoSwitch(false); - await charlie.startCheckingConnection(5000); // checks the connection - await charlie.setAutoSwitch(true); - await charlie.addMoneroConnection(new UrlConnection() + await user3.setAutoSwitch(false); + await user3.startCheckingConnection(5000); // checks the connection + await user3.setAutoSwitch(true); + await user3.addMoneroConnection(new UrlConnection() .setUrl(TestConfig.monerod.url) .setUsername(TestConfig.monerod.username) .setPassword(TestConfig.monerod.password) .setPriority(2)); await wait(10000); - connection = await charlie.getMoneroConnection(); + connection = await user3.getMoneroConnection(); testConnection(connection!, TestConfig.monerod.url, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 2); } catch (err2) { err = err2; } // stop processes - if (charlie) await releaseHavenoProcess(charlie, true); + if (user3) await releaseHavenoProcess(user3, true); if (monerod2) await monerod2.stopProcess(); if (err) throw err; }); @@ -518,9 +518,9 @@ test("Can start and stop a local Monero node", async() => { // expect error stopping local node try { - await alice.stopMoneroNode(); + await user1.stopMoneroNode(); HavenoUtils.log(1, "Running local Monero node stopped"); - await alice.stopMoneroNode(); // stop 2nd time to force error + await user1.stopMoneroNode(); // stop 2nd time to force error throw new Error("should have thrown"); } catch (err: any) { if (err.message !== "Local Monero node is not running" && @@ -529,14 +529,14 @@ test("Can start and stop a local Monero node", async() => { } } - let isMoneroNodeRunning = await alice.isMoneroNodeRunning(); + let isMoneroNodeRunning = await user1.isMoneroNodeRunning(); if (isMoneroNodeRunning) { HavenoUtils.log(0, "Warning: local Monero node is already running, skipping start and stop local Monero node tests"); // expect error due to existing running node const newSettings = new MoneroNodeSettings(); try { - await alice.startMoneroNode(newSettings); + await user1.startMoneroNode(newSettings); throw new Error("should have thrown"); } catch (err: any) { if (err.message !== "Local Monero node already running") throw new Error("Unexpected error: " + err.message); @@ -548,14 +548,14 @@ test("Can start and stop a local Monero node", async() => { const badSettings = new MoneroNodeSettings(); badSettings.setStartupFlagsList(["--invalid-flag"]); try { - await alice.startMoneroNode(badSettings); + await user1.startMoneroNode(badSettings); throw new Error("should have thrown"); } catch (err: any) { if (!err.message.startsWith("Failed to start monerod:")) throw new Error("Unexpected error: "); } // expect successful start with custom settings - const connectionsBefore = await alice.getMoneroConnections(); + const connectionsBefore = await user1.getMoneroConnections(); const settings: MoneroNodeSettings = new MoneroNodeSettings(); const dataDir = TestConfig.moneroBinsDir + "/" + TestConfig.baseCurrencyNetwork + "/node1"; const logFile = dataDir + "/test.log"; @@ -563,16 +563,16 @@ test("Can start and stop a local Monero node", async() => { const rpcPort = 38081; settings.setBlockchainPath(dataDir); settings.setStartupFlagsList(["--log-file", logFile, "--p2p-bind-port", p2pPort.toString(), "--rpc-bind-port", rpcPort.toString(), "--no-zmq"]); - await alice.startMoneroNode(settings); - isMoneroNodeRunning = await alice.isMoneroNodeRunning(); + await user1.startMoneroNode(settings); + isMoneroNodeRunning = await user1.isMoneroNodeRunning(); assert(isMoneroNodeRunning); // expect settings are updated - const settingsAfter = await alice.getMoneroNodeSettings(); + const settingsAfter = await user1.getMoneroNodeSettings(); testMoneroNodeSettingsEqual(settings, settingsAfter!); // expect connections to be unmodified by local node - const connectionsAfter = await alice.getMoneroConnections(); + const connectionsAfter = await user1.getMoneroConnections(); assert(connectionsBefore.length === connectionsAfter.length); // expect connection to local monero node to succeed @@ -584,15 +584,15 @@ test("Can start and stop a local Monero node", async() => { // expect error due to existing running node const newSettings = new MoneroNodeSettings(); try { - await alice.startMoneroNode(newSettings); + await user1.startMoneroNode(newSettings); throw new Error("should have thrown"); } catch (err: any) { if (err.message !== "Local Monero node already running") throw new Error("Unexpected error: " + err.message); } // expect stopped node - await alice.stopMoneroNode(); - isMoneroNodeRunning = await alice.isMoneroNodeRunning(); + await user1.stopMoneroNode(); + isMoneroNodeRunning = await user1.isMoneroNodeRunning(); assert(!isMoneroNodeRunning); try { daemon = await monerojs.connectToDaemonRpc(rpcUrl); @@ -608,24 +608,24 @@ test("Can start and stop a local Monero node", async() => { test("Has a Monero wallet", async () => { // get seed phrase - const seed = await alice.getXmrSeed(); + const seed = await user1.getXmrSeed(); await MoneroUtils.validateMnemonic(seed); // get primary address - const primaryAddress = await alice.getXmrPrimaryAddress(); + const primaryAddress = await user1.getXmrPrimaryAddress(); await MoneroUtils.validateAddress(primaryAddress, TestConfig.networkType); - // wait for alice to have unlocked balance + // wait for user1 to have unlocked balance const tradeAmount = BigInt("250000000000"); - await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice); + await waitForUnlockedBalance(tradeAmount * BigInt("2"), user1); // test balances - const balancesBefore: XmrBalanceInfo = await alice.getBalances(); // TODO: rename to getXmrBalances() for consistency? + const balancesBefore: XmrBalanceInfo = await user1.getBalances(); // TODO: rename to getXmrBalances() for consistency? expect(BigInt(balancesBefore.getUnlockedBalance())).toBeGreaterThan(BigInt("0")); expect(BigInt(balancesBefore.getBalance())).toBeGreaterThanOrEqual(BigInt(balancesBefore.getUnlockedBalance())); // get transactions - const txs: XmrTx[]= await alice.getXmrTxs(); + const txs: XmrTx[]= await user1.getXmrTxs(); assert(txs.length > 0); for (const tx of txs) { testTx(tx, {isCreatedTx: false}); @@ -633,31 +633,31 @@ test("Has a Monero wallet", async () => { // get new subaddresses for (let i = 0; i < 0; i++) { - const address = await alice.getXmrNewSubaddress(); + const address = await user1.getXmrNewSubaddress(); await MoneroUtils.validateAddress(address, TestConfig.networkType); } // create withdraw tx - const destination = new XmrDestination().setAddress(await alice.getXmrNewSubaddress()).setAmount("100000000000"); - let tx = await alice.createXmrTx([destination]); + const destination = new XmrDestination().setAddress(await user1.getXmrNewSubaddress()).setAmount("100000000000"); + let tx = await user1.createXmrTx([destination]); testTx(tx, {isCreatedTx: true}); // relay withdraw tx - const txHash = await alice.relayXmrTx(tx.getMetadata()); + const txHash = await user1.relayXmrTx(tx.getMetadata()); expect(txHash.length).toEqual(64); // balances decreased - const balancesAfter = await alice.getBalances(); + const balancesAfter = await user1.getBalances(); expect(BigInt(balancesAfter.getBalance())).toBeLessThan(BigInt(balancesBefore.getBalance())); expect(BigInt(balancesAfter.getUnlockedBalance())).toBeLessThan(BigInt(balancesBefore.getUnlockedBalance())); // get relayed tx - tx = await alice.getXmrTx(txHash); + tx = await user1.getXmrTx(txHash); testTx(tx, {isCreatedTx: false}); // relay invalid tx try { - await alice.relayXmrTx("invalid tx metadata"); + await user1.relayXmrTx("invalid tx metadata"); throw new Error("Cannot relay invalid tx metadata"); } catch (err: any) { if (err.message !== "Failed to parse hex.") throw new Error("Unexpected error: " + err.message); @@ -665,7 +665,7 @@ test("Has a Monero wallet", async () => { }); test("Can get balances", async () => { - const balances: XmrBalanceInfo = await alice.getBalances(); + const balances: XmrBalanceInfo = await user1.getBalances(); expect(BigInt(balances.getUnlockedBalance())).toBeGreaterThanOrEqual(0); expect(BigInt(balances.getLockedBalance())).toBeGreaterThanOrEqual(0); expect(BigInt(balances.getReservedOfferBalance())).toBeGreaterThanOrEqual(0); @@ -676,13 +676,13 @@ test("Can receive push notifications", async () => { // add notification listener const notifications: NotificationMessage[] = []; - await alice.addNotificationListener(notification => { + await user1.addNotificationListener(notification => { notifications.push(notification); }); // send test notification for (let i = 0; i < 3; i++) { - await alice._sendNotification(new NotificationMessage() + await user1._sendNotification(new NotificationMessage() .setTimestamp(Date.now()) .setTitle("Test title " + i) .setMessage("Test message " + i)); @@ -699,9 +699,9 @@ test("Can receive push notifications", async () => { }); test("Can get supported assets and their payment methods", async() => { - const assetCodes = await alice.getSupportedAssetCodes(); // TODO: replace with getSupportedAssets(): TradeCurrency[] + const assetCodes = await user1.getSupportedAssetCodes(); // TODO: replace with getSupportedAssets(): TradeCurrency[] for (const assetCode of assetCodes) { - const paymentMethods = await alice.getPaymentMethods(assetCode); + const paymentMethods = await user1.getPaymentMethods(assetCode); expect(paymentMethods.length).toBeGreaterThanOrEqual(0); } }); @@ -709,7 +709,7 @@ test("Can get supported assets and their payment methods", async() => { test("Can get market prices", async () => { // get all market prices - const prices: MarketPriceInfo[] = await alice.getPrices(); + const prices: MarketPriceInfo[] = await user1.getPrices(); expect(prices.length).toBeGreaterThan(1); for (const price of prices) { expect(price.getCurrencyCode().length).toBeGreaterThan(0); @@ -718,23 +718,23 @@ test("Can get market prices", async () => { // get market prices of primary assets for (const assetCode of TestConfig.assetCodes) { - const price = await alice.getPrice(assetCode); + const price = await user1.getPrice(assetCode); expect(price).toBeGreaterThan(0); } // test that prices are reasonable - const usd = await alice.getPrice("USD"); + const usd = await user1.getPrice("USD"); expect(usd).toBeGreaterThan(50); expect(usd).toBeLessThan(5000); - const doge = await alice.getPrice("DOGE"); + const doge = await user1.getPrice("DOGE"); expect(doge).toBeGreaterThan(200) expect(doge).toBeLessThan(20000); - const btc = await alice.getPrice("BTC"); + const btc = await user1.getPrice("BTC"); expect(btc).toBeGreaterThan(0.0004) expect(btc).toBeLessThan(0.4); // test invalid currency - await expect(async () => { await alice.getPrice("INVALID_CURRENCY") }) + await expect(async () => { await user1.getPrice("INVALID_CURRENCY") }) .rejects .toThrow('Currency not found: INVALID_CURRENCY'); }); @@ -743,8 +743,8 @@ test("Can get market depth", async () => { const assetCode = "eth"; // clear offers - await clearOffers(alice, assetCode); - await clearOffers(bob, assetCode); + await clearOffers(user1, assetCode); + await clearOffers(user2, assetCode); async function clearOffers(havenod: HavenoClient, assetCode: string) { for (const offer of await havenod.getMyOffers(assetCode)) { if (offer.getBaseCurrencyCode().toLowerCase() === assetCode.toLowerCase()) { // TODO (woodser): offer base currency and counter currency are switched for cryptos @@ -755,24 +755,24 @@ test("Can get market depth", async () => { // market depth has no data await wait(TestConfig.maxTimePeerNoticeMs); - let marketDepth = await alice.getMarketDepth(assetCode); + let marketDepth = await user1.getMarketDepth(assetCode); expect(marketDepth.getBuyPricesList().length).toEqual(0); expect(marketDepth.getBuyDepthList().length).toEqual(0); expect(marketDepth.getSellPricesList().length).toEqual(0); expect(marketDepth.getSellDepthList().length).toEqual(0); // post offers to buy and sell - await postOffer(alice, {direction: "buy", amount: BigInt("150000000000"), assetCode: assetCode, priceMargin: 0.00, awaitUnlockedBalance: true, price: 17.0}); // TODO: offer price is reversed. fix everywhere - await postOffer(alice, {direction: "buy", amount: BigInt("150000000000"), assetCode: assetCode, priceMargin: 0.02, awaitUnlockedBalance: true, price: 17.2}); - await postOffer(alice, {direction: "buy", amount: BigInt("200000000000"), assetCode: assetCode, priceMargin: 0.05, awaitUnlockedBalance: true, price: 17.3}); - await postOffer(alice, {direction: "buy", amount: BigInt("150000000000"), assetCode: assetCode, priceMargin: 0.02, awaitUnlockedBalance: true, price: 17.3}); - await postOffer(alice, {direction: "sell", amount: BigInt("300000000000"), assetCode: assetCode, priceMargin: 0.00, awaitUnlockedBalance: true}); - await postOffer(alice, {direction: "sell", amount: BigInt("300000000000"), assetCode: assetCode, priceMargin: 0.02, awaitUnlockedBalance: true}); - await postOffer(alice, {direction: "sell", amount: BigInt("400000000000"), assetCode: assetCode, priceMargin: 0.05, awaitUnlockedBalance: true}); + await postOffer(user1, {direction: "buy", amount: BigInt("150000000000"), assetCode: assetCode, priceMargin: 0.00, awaitUnlockedBalance: true, price: 17.0}); // TODO: offer price is reversed. fix everywhere + await postOffer(user1, {direction: "buy", amount: BigInt("150000000000"), assetCode: assetCode, priceMargin: 0.02, awaitUnlockedBalance: true, price: 17.2}); + await postOffer(user1, {direction: "buy", amount: BigInt("200000000000"), assetCode: assetCode, priceMargin: 0.05, awaitUnlockedBalance: true, price: 17.3}); + await postOffer(user1, {direction: "buy", amount: BigInt("150000000000"), assetCode: assetCode, priceMargin: 0.02, awaitUnlockedBalance: true, price: 17.3}); + await postOffer(user1, {direction: "sell", amount: BigInt("300000000000"), assetCode: assetCode, priceMargin: 0.00, awaitUnlockedBalance: true}); + await postOffer(user1, {direction: "sell", amount: BigInt("300000000000"), assetCode: assetCode, priceMargin: 0.02, awaitUnlockedBalance: true}); + await postOffer(user1, {direction: "sell", amount: BigInt("400000000000"), assetCode: assetCode, priceMargin: 0.05, awaitUnlockedBalance: true}); - // get bob's market depth + // get user2's market depth await wait(TestConfig.maxTimePeerNoticeMs); - marketDepth = await alice.getMarketDepth(assetCode); + marketDepth = await user1.getMarketDepth(assetCode); // each unique price has a depth expect(marketDepth.getBuyPricesList().length).toEqual(3); @@ -781,7 +781,7 @@ test("Can get market depth", async () => { expect(marketDepth.getSellPricesList().length).toEqual(marketDepth.getSellDepthList().length); // test buy prices and depths - const buyOffers = (await alice.getOffers(assetCode, "buy")).concat(await alice.getMyOffers(assetCode, "buy")).sort(function(a, b) { return parseFloat(a.getPrice()) - parseFloat(b.getPrice()) }); + const buyOffers = (await user1.getOffers(assetCode, "buy")).concat(await user1.getMyOffers(assetCode, "buy")).sort(function(a, b) { return parseFloat(a.getPrice()) - parseFloat(b.getPrice()) }); expect(marketDepth.getBuyPricesList()[0]).toEqual(1 / parseFloat(buyOffers[0].getPrice())); // TODO: price when posting offer is reversed. this assumes crypto counter currency expect(marketDepth.getBuyPricesList()[1]).toEqual(1 / parseFloat(buyOffers[1].getPrice())); expect(marketDepth.getBuyPricesList()[2]).toEqual(1 / parseFloat(buyOffers[2].getPrice())); @@ -790,7 +790,7 @@ test("Can get market depth", async () => { expect(marketDepth.getBuyDepthList()[2]).toEqual(0.65); // test sell prices and depths - const sellOffers = (await alice.getOffers(assetCode, "sell")).concat(await alice.getMyOffers(assetCode, "sell")).sort(function(a, b) { return parseFloat(b.getPrice()) - parseFloat(a.getPrice()) }); + const sellOffers = (await user1.getOffers(assetCode, "sell")).concat(await user1.getMyOffers(assetCode, "sell")).sort(function(a, b) { return parseFloat(b.getPrice()) - parseFloat(a.getPrice()) }); expect(marketDepth.getSellPricesList()[0]).toEqual(1 / parseFloat(sellOffers[0].getPrice())); expect(marketDepth.getSellPricesList()[1]).toEqual(1 / parseFloat(sellOffers[1].getPrice())); expect(marketDepth.getSellPricesList()[2]).toEqual(1 / parseFloat(sellOffers[2].getPrice())); @@ -799,11 +799,11 @@ test("Can get market depth", async () => { expect(marketDepth.getSellDepthList()[2]).toEqual(1); // clear offers - await clearOffers(alice, assetCode); - await clearOffers(bob, assetCode); + await clearOffers(user1, assetCode); + await clearOffers(user2, assetCode); // test invalid currency - await expect(async () => {await alice.getMarketDepth("INVALID_CURRENCY")}) + await expect(async () => {await user1.getMarketDepth("INVALID_CURRENCY")}) .rejects .toThrow('Currency not found: INVALID_CURRENCY'); }); @@ -832,20 +832,20 @@ test("Can register as an arbitrator", async () => { test("Can get offers", async () => { for (const assetCode of TestConfig.assetCodes) { - const offers: OfferInfo[] = await alice.getOffers(assetCode); + const offers: OfferInfo[] = await user1.getOffers(assetCode); for (const offer of offers) testOffer(offer); } }); test("Can get my offers", async () => { for (const assetCode of TestConfig.assetCodes) { - const offers: OfferInfo[] = await alice.getMyOffers(assetCode); + const offers: OfferInfo[] = await user1.getMyOffers(assetCode); for (const offer of offers) testOffer(offer); } }); test("Can get payment methods", async () => { - const paymentMethods: PaymentMethod[] = await alice.getPaymentMethods(); + const paymentMethods: PaymentMethod[] = await user1.getPaymentMethods(); expect(paymentMethods.length).toBeGreaterThan(0); for (const paymentMethod of paymentMethods) { expect(paymentMethod.getId().length).toBeGreaterThan(0); @@ -856,7 +856,7 @@ test("Can get payment methods", async () => { }); test("Can get payment accounts", async () => { - const paymentAccounts: PaymentAccount[] = await alice.getPaymentAccounts(); + const paymentAccounts: PaymentAccount[] = await user1.getPaymentAccounts(); for (const paymentAccount of paymentAccounts) { if (paymentAccount.getPaymentAccountPayload()!.getCryptoCurrencyAccountPayload()) { // TODO (woodser): test non-crypto testCryptoPaymentAccount(paymentAccount); @@ -872,7 +872,7 @@ test("Can validate payment account forms", async () => { const expectedPaymentMethods = ["REVOLUT", "SEPA", "SEPA_INSTANT", "TRANSFERWISE", "CLEAR_X_CHANGE", "SWIFT", "F2F", "STRIKE", "MONEY_GRAM", "FASTER_PAYMENTS", "UPHOLD", "PAXUM"]; // get payment methods - const paymentMethods = await alice.getPaymentMethods(); + const paymentMethods = await user1.getPaymentMethods(); expect(paymentMethods.length).toEqual(expectedPaymentMethods.length); for (const paymentMethod of paymentMethods) { assert(GenUtils.arrayContains(expectedPaymentMethods, paymentMethod.getId()), "Payment method is not expected: " + paymentMethod.getId()); @@ -882,7 +882,7 @@ test("Can validate payment account forms", async () => { for (const paymentMethod of paymentMethods) { // generate form - const accountForm = await alice.getPaymentAccountForm(paymentMethod.getId()); + const accountForm = await user1.getPaymentAccountForm(paymentMethod.getId()); // complete form, validating each field for (const field of accountForm.getFieldsList()) { @@ -890,7 +890,7 @@ test("Can validate payment account forms", async () => { // validate invalid form field try { const invalidInput = getInvalidFormInput(accountForm, field.getId()); - await alice.validateFormField(accountForm, field.getId(), invalidInput); + await user1.validateFormField(accountForm, field.getId(), invalidInput); throw new Error("Should have thrown error validating form field '" + field.getId() + "' with invalid value '" + invalidInput + "'"); } catch (err: any) { if (err.message.indexOf("Not implemented") >= 0) throw err; @@ -899,16 +899,16 @@ test("Can validate payment account forms", async () => { // validate valid form field const validInput = getValidFormInput(field.getId(), accountForm); - await alice.validateFormField(accountForm, field.getId(), validInput); + await user1.validateFormField(accountForm, field.getId(), validInput); field.setValue(validInput); } // create payment account - const fiatAccount = await alice.createPaymentAccount(accountForm); + const fiatAccount = await user1.createPaymentAccount(accountForm); // payment account added let found = false; - for (const paymentAccount of await alice.getPaymentAccounts()) { + for (const paymentAccount of await user1.getPaymentAccounts()) { if (paymentAccount.getId() === fiatAccount.getId()) { found = true; break; @@ -926,7 +926,7 @@ test("Can create fiat payment accounts", async () => { // get payment account form const paymentMethodId = 'REVOLUT'; - const accountForm = await alice.getPaymentAccountForm(paymentMethodId); + const accountForm = await user1.getPaymentAccountForm(paymentMethodId); // edit form HavenoUtils.setFormValue(PaymentAccountFormField.FieldId.ACCOUNT_NAME, "Revolut account " + GenUtils.getUUID(), accountForm); @@ -934,7 +934,7 @@ test("Can create fiat payment accounts", async () => { HavenoUtils.setFormValue(PaymentAccountFormField.FieldId.TRADE_CURRENCIES, "gbp,eur,usd", accountForm); // create payment account - const fiatAccount = await alice.createPaymentAccount(accountForm); + const fiatAccount = await user1.createPaymentAccount(accountForm); expect(fiatAccount.getAccountName()).toEqual(HavenoUtils.getFormValue(PaymentAccountFormField.FieldId.ACCOUNT_NAME, accountForm)); expect(fiatAccount.getSelectedTradeCurrency().getCode()).toEqual("USD"); expect(fiatAccount.getTradeCurrenciesList().length).toBeGreaterThan(0); @@ -944,7 +944,7 @@ test("Can create fiat payment accounts", async () => { // payment account added let found = false; - for (const paymentAccount of await alice.getPaymentAccounts()) { + for (const paymentAccount of await user1.getPaymentAccounts()) { if (paymentAccount.getId() === fiatAccount.getId()) { found = true; break; @@ -960,13 +960,13 @@ test("Can create crypto payment accounts", async () => { // create payment account const name = testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(); - const paymentAccount: PaymentAccount = await alice.createCryptoPaymentAccount(name, testAccount.currencyCode, testAccount.address); + const paymentAccount: PaymentAccount = await user1.createCryptoPaymentAccount(name, testAccount.currencyCode, testAccount.address); testCryptoPaymentAccount(paymentAccount); testCryptoPaymentAccountEquals(paymentAccount, testAccount, name); // fetch and test payment account let fetchedAccount: PaymentAccount | undefined; - for (const account of await alice.getPaymentAccounts()) { + for (const account of await user1.getPaymentAccounts()) { if (paymentAccount.getId() === account.getId()) { fetchedAccount = account; break; @@ -980,12 +980,12 @@ test("Can create crypto payment accounts", async () => { // TODO (woodser): update from latest Bisq CorePaymentAccountService.java for currency and address validation /* // test invalid currency code - await expect(async () => { await alice.createCryptoPaymentAccount("My account", "ABC", "123"); }) + await expect(async () => { await user1.createCryptoPaymentAccount("My account", "ABC", "123"); }) .rejects .toThrow('Unsupported cryptocurrency code: ABC'); // test invalid address - await expect(async () => { await alice.createCryptoPaymentAccount("My account", "ETH", "123"); }) + await expect(async () => { await user1.createCryptoPaymentAccount("My account", "ETH", "123"); }) .rejects .toThrow('Invalid address');*/ @@ -1001,16 +1001,16 @@ test("Can create crypto payment accounts", async () => { test("Can prepare for trading", async () => { // create payment accounts - if (!await hasPaymentAccount(alice, "eth")) await createPaymentAccount(alice, "eth"); - if (!await hasPaymentAccount(alice, "bch")) await createPaymentAccount(alice, "bch"); - if (!await hasPaymentAccount(alice, "usd")) await createPaymentAccount(alice, "usd"); - if (!await hasPaymentAccount(bob, "eth")) await createPaymentAccount(bob, "eth"); - if (!await hasPaymentAccount(bob, "bch")) await createPaymentAccount(bob, "bch"); - if (!await hasPaymentAccount(bob, "usd")) await createPaymentAccount(bob, "usd"); + if (!await hasPaymentAccount(user1, "eth")) await createPaymentAccount(user1, "eth"); + if (!await hasPaymentAccount(user1, "bch")) await createPaymentAccount(user1, "bch"); + if (!await hasPaymentAccount(user1, "usd")) await createPaymentAccount(user1, "usd"); + if (!await hasPaymentAccount(user2, "eth")) await createPaymentAccount(user2, "eth"); + if (!await hasPaymentAccount(user2, "bch")) await createPaymentAccount(user2, "bch"); + if (!await hasPaymentAccount(user2, "usd")) await createPaymentAccount(user2, "usd"); // fund wallets const tradeAmount = BigInt("250000000000"); - await fundOutputs([aliceWallet, bobWallet], tradeAmount * BigInt("2"), 4); + await fundOutputs([user1Wallet, user2Wallet], tradeAmount * BigInt("2"), 4); // wait for havenod to observe funds await wait(TestConfig.walletSyncPeriodMs); @@ -1018,321 +1018,321 @@ test("Can prepare for trading", async () => { test("Can post and remove offers", async () => { - // wait for alice to have unlocked balance to post offer - await waitForUnlockedBalance(BigInt("250000000000") * BigInt("2"), alice); + // wait for user1 to have unlocked balance to post offer + await waitForUnlockedBalance(BigInt("250000000000") * BigInt("2"), user1); // get unlocked balance before reserving funds for offer - const unlockedBalanceBefore = BigInt((await alice.getBalances()).getUnlockedBalance()); + const unlockedBalanceBefore = BigInt((await user1.getBalances()).getUnlockedBalance()); // post crypto offer let assetCode = "BCH"; let price = 1 / 17; price = 1 / price; // TODO: price in crypto offer is inverted - let offer: OfferInfo = await postOffer(alice, {assetCode: assetCode, price: price}); + let offer: OfferInfo = await postOffer(user1, {assetCode: assetCode, price: price}); assert.equal(offer.getState(), "AVAILABLE"); assert.equal(offer.getBaseCurrencyCode(), assetCode); // TODO: base and counter currencies inverted in crypto offer assert.equal(offer.getCounterCurrencyCode(), "XMR"); assert.equal(parseFloat(offer.getPrice()), price); // has offer - offer = await alice.getMyOffer(offer.getId()); + offer = await user1.getMyOffer(offer.getId()); assert.equal(offer.getState(), "AVAILABLE"); // peer sees offer await wait(TestConfig.maxTimePeerNoticeMs); - if (!getOffer(await bob.getOffers(assetCode, TestConfig.postOffer.direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posted"); + if (!getOffer(await user2.getOffers(assetCode, TestConfig.postOffer.direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posted"); // cancel offer - await alice.removeOffer(offer.getId()); + await user1.removeOffer(offer.getId()); // offer is removed from my offers - if (getOffer(await alice.getMyOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after removal"); + if (getOffer(await user1.getMyOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after removal"); // reserved balance released - expect(BigInt((await alice.getBalances()).getUnlockedBalance())).toEqual(unlockedBalanceBefore); + expect(BigInt((await user1.getBalances()).getUnlockedBalance())).toEqual(unlockedBalanceBefore); // post fiat offer assetCode = "USD"; price = 180.0; - offer = await postOffer(alice, {assetCode: assetCode, price: price}); + offer = await postOffer(user1, {assetCode: assetCode, price: price}); assert.equal(offer.getState(), "AVAILABLE"); assert.equal(offer.getBaseCurrencyCode(), "XMR"); assert.equal(offer.getCounterCurrencyCode(), "USD"); assert.equal(parseFloat(offer.getPrice()), price); // has offer - offer = await alice.getMyOffer(offer.getId()); + offer = await user1.getMyOffer(offer.getId()); assert.equal(offer.getState(), "AVAILABLE"); // cancel offer - await alice.removeOffer(offer.getId()); + await user1.removeOffer(offer.getId()); // offer is removed from my offers - if (getOffer(await alice.getMyOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after removal"); + if (getOffer(await user1.getMyOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after removal"); // reserved balance released - expect(BigInt((await alice.getBalances()).getUnlockedBalance())).toEqual(unlockedBalanceBefore); + expect(BigInt((await user1.getBalances()).getUnlockedBalance())).toEqual(unlockedBalanceBefore); }); // TODO: support splitting outputs // TODO: provide number of confirmations in offer status test("Can schedule offers with locked funds", async () => { - let charlie: HavenoClient | undefined; + let user3: HavenoClient | undefined; let err: any; try { - // start charlie - charlie = await initHaveno(); - const charlieWallet = await monerojs.connectToWalletRpc("http://127.0.0.1:" + charlie.getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword); + // start user3 + user3 = await initHaveno(); + const user3Wallet = await monerojs.connectToWalletRpc("http://127.0.0.1:" + user3.getWalletRpcPort(), TestConfig.defaultHavenod.walletUsername, TestConfig.defaultHavenod.accountPassword); - // fund charlie with 2 outputs of 0.5 XMR + // fund user3 with 2 outputs of 0.5 XMR const outputAmt = BigInt("500000000000"); - await fundOutputs([charlieWallet], outputAmt, 2, false); + await fundOutputs([user3Wallet], outputAmt, 2, false); // schedule offer const assetCode = "BCH"; const direction = "BUY"; - let offer: OfferInfo = await postOffer(charlie, {assetCode: assetCode, direction: direction, awaitUnlockedBalance: false}); + let offer: OfferInfo = await postOffer(user3, {assetCode: assetCode, direction: direction, awaitUnlockedBalance: false}); assert.equal(offer.getState(), "SCHEDULED"); // has offer - offer = await charlie.getMyOffer(offer.getId()); + offer = await user3.getMyOffer(offer.getId()); assert.equal(offer.getState(), "SCHEDULED"); // balances unchanged - expect(BigInt((await charlie.getBalances()).getLockedBalance())).toEqual(outputAmt * BigInt(2)); - expect(BigInt((await charlie.getBalances()).getReservedOfferBalance())).toEqual(BigInt(0)); + expect(BigInt((await user3.getBalances()).getLockedBalance())).toEqual(outputAmt * BigInt(2)); + expect(BigInt((await user3.getBalances()).getReservedOfferBalance())).toEqual(BigInt(0)); // peer does not see offer because it's scheduled await wait(TestConfig.maxTimePeerNoticeMs); - if (getOffer(await alice.getOffers(assetCode, direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in peer's offers before posted"); + if (getOffer(await user1.getOffers(assetCode, direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in peer's offers before posted"); // cancel offer - await charlie.removeOffer(offer.getId()); - if (getOffer(await charlie.getOffers(assetCode, direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was found after canceling offer"); + await user3.removeOffer(offer.getId()); + if (getOffer(await user3.getOffers(assetCode, direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was found after canceling offer"); // balances unchanged - expect(BigInt((await charlie.getBalances()).getLockedBalance())).toEqual(outputAmt * BigInt(2)); - expect(BigInt((await charlie.getBalances()).getReservedOfferBalance())).toEqual(BigInt(0)); + expect(BigInt((await user3.getBalances()).getLockedBalance())).toEqual(outputAmt * BigInt(2)); + expect(BigInt((await user3.getBalances()).getReservedOfferBalance())).toEqual(BigInt(0)); // schedule offer - offer = await postOffer(charlie, {assetCode: assetCode, direction: direction, awaitUnlockedBalance: false}); + offer = await postOffer(user3, {assetCode: assetCode, direction: direction, awaitUnlockedBalance: false}); assert.equal(offer.getState(), "SCHEDULED"); - // restart charlie - const charlieConfig = {appName: charlie.getAppName()}; - await releaseHavenoProcess(charlie); - charlie = await initHaveno(charlieConfig); + // restart user3 + const user3Config = {appName: user3.getAppName()}; + await releaseHavenoProcess(user3); + user3 = await initHaveno(user3Config); // has offer - offer = await charlie.getMyOffer(offer.getId()); + offer = await user3.getMyOffer(offer.getId()); assert.equal(offer.getState(), "SCHEDULED"); // peer does not see offer because it's scheduled await wait(TestConfig.maxTimePeerNoticeMs); - if (getOffer(await alice.getOffers(assetCode, direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in peer's offers before posted"); + if (getOffer(await user1.getOffers(assetCode, direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in peer's offers before posted"); // wait for deposit txs to unlock - await waitForUnlockedBalance(outputAmt, charlie); + await waitForUnlockedBalance(outputAmt, user3); // one output is reserved, one is unlocked - expect(BigInt((await charlie.getBalances()).getUnlockedBalance())).toEqual(outputAmt); - expect(BigInt((await charlie.getBalances()).getLockedBalance())).toEqual(BigInt(0)); - expect(BigInt((await charlie.getBalances()).getReservedOfferBalance())).toEqual(outputAmt); + expect(BigInt((await user3.getBalances()).getUnlockedBalance())).toEqual(outputAmt); + expect(BigInt((await user3.getBalances()).getLockedBalance())).toEqual(BigInt(0)); + expect(BigInt((await user3.getBalances()).getReservedOfferBalance())).toEqual(outputAmt); // peer sees offer await wait(TestConfig.maxTimePeerNoticeMs); - if (!getOffer(await alice.getOffers(assetCode, direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posted"); + if (!getOffer(await user1.getOffers(assetCode, direction), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posted"); // cancel offer - await charlie.removeOffer(offer.getId()); + await user3.removeOffer(offer.getId()); // offer is removed from my offers - if (getOffer(await charlie.getMyOffers(assetCode), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after removal"); + if (getOffer(await user3.getMyOffers(assetCode), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after removal"); // reserved balance becomes unlocked - expect(BigInt((await charlie.getBalances()).getUnlockedBalance())).toEqual(outputAmt * BigInt(2)); - expect(BigInt((await charlie.getBalances()).getLockedBalance())).toEqual(BigInt(0)); - expect(BigInt((await charlie.getBalances()).getReservedOfferBalance())).toEqual(BigInt(0)); + expect(BigInt((await user3.getBalances()).getUnlockedBalance())).toEqual(outputAmt * BigInt(2)); + expect(BigInt((await user3.getBalances()).getLockedBalance())).toEqual(BigInt(0)); + expect(BigInt((await user3.getBalances()).getReservedOfferBalance())).toEqual(BigInt(0)); } catch (err2) { err = err2; } // stop and delete instances - if (charlie) await releaseHavenoProcess(charlie, true); + if (user3) await releaseHavenoProcess(user3, true); if (err) throw err; }); // TODO (woodser): test grpc notifications test("Can complete a trade", async () => { - // wait for alice and bob to have unlocked balance for trade + // wait for user1 and user2 to have unlocked balance for trade const tradeAmount = BigInt("250000000000"); - await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice, bob); - const aliceBalancesBefore = await alice.getBalances(); - const bobBalancesBefore: XmrBalanceInfo = await bob.getBalances(); + await waitForUnlockedBalance(tradeAmount * BigInt("2"), user1, user2); + const user1BalancesBefore = await user1.getBalances(); + const user2BalancesBefore: XmrBalanceInfo = await user2.getBalances(); // register to receive notifications - const aliceNotifications: NotificationMessage[] = []; - const bobNotifications: NotificationMessage[] = []; - await alice.addNotificationListener(notification => { aliceNotifications.push(notification); }); - await bob.addNotificationListener(notification => { bobNotifications.push(notification); }); + const user1Notifications: NotificationMessage[] = []; + const user2Notifications: NotificationMessage[] = []; + await user1.addNotificationListener(notification => { user1Notifications.push(notification); }); + await user2.addNotificationListener(notification => { user2Notifications.push(notification); }); - // alice posts offer + // user1 posts offer const assetCode = getRandomAssetCode(); const direction = "buy"; - HavenoUtils.log(1, "Alice posting offer to " + direction + " XMR for " + assetCode); - const offer: OfferInfo = await postOffer(alice, {direction: direction, amount: tradeAmount, assetCode: assetCode}); + HavenoUtils.log(1, "user1 posting offer to " + direction + " XMR for " + assetCode); + const offer: OfferInfo = await postOffer(user1, {direction: direction, amount: tradeAmount, assetCode: assetCode}); expect(offer.getState()).toEqual("AVAILABLE"); - HavenoUtils.log(1, "Alice done posting offer " + offer.getId()); + HavenoUtils.log(1, "user1 done posting offer " + offer.getId()); // TODO (woodser): test error message taking offer before posted - // bob sees offer + // user2 sees offer await wait(TestConfig.walletSyncPeriodMs * 2); - const offerBob = getOffer(await bob.getOffers(assetCode, direction), offer.getId()); - if (!offerBob) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posting"); - expect(offerBob.getState()).toEqual("UNKNOWN"); // TODO: offer state is not known? + const offeruser2 = getOffer(await user2.getOffers(assetCode, direction), offer.getId()); + if (!offeruser2) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posting"); + expect(offeruser2.getState()).toEqual("UNKNOWN"); // TODO: offer state is not known? // cannot take offer with invalid payment id - const aliceTradesBefore = await alice.getTrades(); - const bobTradesBefore = await bob.getTrades(); + const user1TradesBefore = await user1.getTrades(); + const user2TradesBefore = await user2.getTrades(); try { - await bob.takeOffer(offer.getId(), "abc"); + await user2.takeOffer(offer.getId(), "abc"); throw new Error("taking offer with invalid payment account id should fail"); } catch (err: any) { assert.equal(err.message, "payment account with id 'abc' not found"); - assert.equal((await alice.getTrades()).length, aliceTradesBefore.length, "alice should not have new trades"); - assert.equal((await bob.getTrades()).length, bobTradesBefore.length, "bob should not have new trades"); // TODO (woodser): also test balance unreserved + assert.equal((await user1.getTrades()).length, user1TradesBefore.length, "user1 should not have new trades"); + assert.equal((await user2.getTrades()).length, user2TradesBefore.length, "user2 should not have new trades"); // TODO (woodser): also test balance unreserved } - // bob creates random payment account - const paymentAccount = await createPaymentAccount(bob, assetCode); + // user2 creates random payment account + const paymentAccount = await createPaymentAccount(user2, assetCode); - // bob takes offer + // user2 takes offer const startTime = Date.now(); - HavenoUtils.log(1, "Bob taking offer"); - const trade: TradeInfo = await bob.takeOffer(offer.getId(), paymentAccount.getId()); + HavenoUtils.log(1, "user2 taking offer"); + const trade: TradeInfo = await user2.takeOffer(offer.getId(), paymentAccount.getId()); expect(trade.getPhase()).toEqual("DEPOSIT_PUBLISHED"); - HavenoUtils.log(1, "Bob done taking offer in " + (Date.now() - startTime) + " ms"); + HavenoUtils.log(1, "user2 done taking offer in " + (Date.now() - startTime) + " ms"); - // alice is notified that offer is taken + // user1 is notified that offer is taken await wait(TestConfig.maxTimePeerNoticeMs); - const tradeNotifications = getNotifications(aliceNotifications, NotificationMessage.NotificationType.TRADE_UPDATE); + const tradeNotifications = getNotifications(user1Notifications, NotificationMessage.NotificationType.TRADE_UPDATE); expect(tradeNotifications.length).toBe(1); expect(tradeNotifications[0].getTrade()!.getPhase()).toEqual("DEPOSIT_PUBLISHED"); expect(tradeNotifications[0].getTitle()).toEqual("Offer Taken"); expect(tradeNotifications[0].getMessage()).toEqual("Your offer " + offer.getId() + " has been accepted"); - // alice is notified of balance change + // user1 is notified of balance change - // bob can get trade - let fetchedTrade: TradeInfo = await bob.getTrade(trade.getTradeId()); + // user2 can get trade + let fetchedTrade: TradeInfo = await user2.getTrade(trade.getTradeId()); expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_PUBLISHED"); // TODO: test fetched trade - // test bob's balances after taking trade - let bobBalancesAfter: XmrBalanceInfo = await bob.getBalances(); - expect(BigInt(bobBalancesAfter.getUnlockedBalance())).toBeLessThan(BigInt(bobBalancesBefore.getUnlockedBalance())); - expect(BigInt(bobBalancesAfter.getReservedOfferBalance()) + BigInt(bobBalancesAfter.getReservedTradeBalance())).toBeGreaterThan(BigInt(bobBalancesBefore.getReservedOfferBalance()) + BigInt(bobBalancesBefore.getReservedTradeBalance())); + // test user2's balances after taking trade + let user2BalancesAfter: XmrBalanceInfo = await user2.getBalances(); + expect(BigInt(user2BalancesAfter.getUnlockedBalance())).toBeLessThan(BigInt(user2BalancesBefore.getUnlockedBalance())); + expect(BigInt(user2BalancesAfter.getReservedOfferBalance()) + BigInt(user2BalancesAfter.getReservedTradeBalance())).toBeGreaterThan(BigInt(user2BalancesBefore.getReservedOfferBalance()) + BigInt(user2BalancesBefore.getReservedTradeBalance())); - // bob is notified of balance change + // user2 is notified of balance change - // alice can get trade - fetchedTrade = await alice.getTrade(trade.getTradeId()); + // user1 can get trade + fetchedTrade = await user1.getTrade(trade.getTradeId()); expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_PUBLISHED"); // test trader chat - await testTradeChat(trade.getTradeId(), alice, bob); + await testTradeChat(trade.getTradeId(), user1, user2); // mine until deposit txs unlock await waitForUnlockedTxs(fetchedTrade.getMakerDepositTxId(), fetchedTrade.getTakerDepositTxId()); - // alice notified to send payment + // user1 notified to send payment await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs * 2); - fetchedTrade = await alice.getTrade(trade.getTradeId()); + fetchedTrade = await user1.getTrade(trade.getTradeId()); expect(fetchedTrade.getIsDepositUnlocked()).toBe(true); expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_UNLOCKED"); - fetchedTrade = await bob.getTrade(trade.getTradeId()); + fetchedTrade = await user2.getTrade(trade.getTradeId()); expect(fetchedTrade.getIsDepositUnlocked()).toBe(true); expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_UNLOCKED"); - // alice indicates payment is sent - HavenoUtils.log(1, "Alice confirming payment sent"); - await alice.confirmPaymentStarted(trade.getTradeId()); - fetchedTrade = await alice.getTrade(trade.getTradeId()); + // user1 indicates payment is sent + HavenoUtils.log(1, "user1 confirming payment sent"); + await user1.confirmPaymentStarted(trade.getTradeId()); + fetchedTrade = await user1.getTrade(trade.getTradeId()); expect(fetchedTrade.getPhase()).toEqual("PAYMENT_SENT"); - // bob notified payment is sent + // user2 notified payment is sent await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs); - fetchedTrade = await bob.getTrade(trade.getTradeId()); + fetchedTrade = await user2.getTrade(trade.getTradeId()); expect(fetchedTrade.getPhase()).toEqual("PAYMENT_SENT"); - // bob confirms payment is received - HavenoUtils.log(1, "Bob confirming payment received"); - await bob.confirmPaymentReceived(trade.getTradeId()); - fetchedTrade = await bob.getTrade(trade.getTradeId()); + // user2 confirms payment is received + HavenoUtils.log(1, "user2 confirming payment received"); + await user2.confirmPaymentReceived(trade.getTradeId()); + fetchedTrade = await user2.getTrade(trade.getTradeId()); expect(fetchedTrade.getPhase()).toEqual("PAYMENT_RECEIVED"); // TODO (woodser): may be PAYOUT_PUBLISHED if seller sends multisig info after confirmation - // alice notified trade is complete and of balance changes + // user1 notified trade is complete and of balance changes await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs * 2); - fetchedTrade = await alice.getTrade(trade.getTradeId()); + fetchedTrade = await user1.getTrade(trade.getTradeId()); expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED"); // test balances after payout tx - const aliceBalancesAfter = await alice.getBalances(); - bobBalancesAfter = await bob.getBalances(); - const aliceFee = BigInt(aliceBalancesBefore.getBalance()) + tradeAmount - BigInt(aliceBalancesAfter.getBalance()); - const bobFee = BigInt(bobBalancesBefore.getBalance()) - tradeAmount - BigInt(bobBalancesAfter.getBalance()); - expect(aliceFee).toBeLessThanOrEqual(TestConfig.maxFee); - expect(aliceFee).toBeGreaterThan(BigInt("0")); - expect(bobFee).toBeLessThanOrEqual(TestConfig.maxFee); - expect(bobFee).toBeGreaterThan(BigInt("0")); + const user1BalancesAfter = await user1.getBalances(); + user2BalancesAfter = await user2.getBalances(); + const user1Fee = BigInt(user1BalancesBefore.getBalance()) + tradeAmount - BigInt(user1BalancesAfter.getBalance()); + const user2Fee = BigInt(user2BalancesBefore.getBalance()) - tradeAmount - BigInt(user2BalancesAfter.getBalance()); + expect(user1Fee).toBeLessThanOrEqual(TestConfig.maxFee); + expect(user1Fee).toBeGreaterThan(BigInt("0")); + expect(user2Fee).toBeLessThanOrEqual(TestConfig.maxFee); + expect(user2Fee).toBeGreaterThan(BigInt("0")); }); test("Can resolve disputes", async () => { - // wait for alice and bob to have unlocked balance for trade + // wait for user1 and user2 to have unlocked balance for trade const tradeAmount = BigInt("250000000000"); - await fundOutputs([aliceWallet, bobWallet], tradeAmount * BigInt("2"), 4); + await fundOutputs([user1Wallet, user2Wallet], tradeAmount * BigInt("2"), 4); // register to receive notifications - const aliceNotifications: NotificationMessage[] = []; - const bobNotifications: NotificationMessage[] = []; + const user1Notifications: NotificationMessage[] = []; + const user2Notifications: NotificationMessage[] = []; const arbitratorNotifications: NotificationMessage[] = []; - await alice.addNotificationListener(notification => { HavenoUtils.log(3, "Alice received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); aliceNotifications.push(notification); }); - await bob.addNotificationListener(notification => { HavenoUtils.log(3, "Bob received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); bobNotifications.push(notification); }); + await user1.addNotificationListener(notification => { HavenoUtils.log(3, "user1 received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); user1Notifications.push(notification); }); + await user2.addNotificationListener(notification => { HavenoUtils.log(3, "user2 received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); user2Notifications.push(notification); }); await arbitrator.addNotificationListener(notification => { HavenoUtils.log(3, "Arbitrator received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); arbitratorNotifications.push(notification); }); // TODO: notification collector with logging - // alice posts offers to buy xmr + // user1 posts offers to buy xmr const numOffers = 4; - HavenoUtils.log(1, "Alice posting offers"); + HavenoUtils.log(1, "user1 posting offers"); const direction = "buy"; let offers = []; - for (let i = 0; i < numOffers; i++) offers.push(postOffer(alice, {direction: direction, amount: tradeAmount, awaitUnlockedBalance: true})); + for (let i = 0; i < numOffers; i++) offers.push(postOffer(user1, {direction: direction, amount: tradeAmount, awaitUnlockedBalance: true})); offers = await Promise.all(offers); - HavenoUtils.log(1, "Alice done posting offers"); - for (let i = 0; i < offers.length; i++) HavenoUtils.log(2, "Offer " + i + ": " + (await alice.getMyOffer(offers[i].getId())).getId()); + HavenoUtils.log(1, "user1 done posting offers"); + for (let i = 0; i < offers.length; i++) HavenoUtils.log(2, "Offer " + i + ": " + (await user1.getMyOffer(offers[i].getId())).getId()); // wait for offers to post await wait(TestConfig.walletSyncPeriodMs * 2); - // bob takes offers - const paymentAccount = await createPaymentAccount(bob, "eth"); - HavenoUtils.log(1, "Bob taking offers"); + // user2 takes offers + const paymentAccount = await createPaymentAccount(user2, "eth"); + HavenoUtils.log(1, "user2 taking offers"); let trades = []; - for (let i = 0; i < numOffers; i++) trades.push(bob.takeOffer(offers[i].getId(), paymentAccount.getId())); + for (let i = 0; i < numOffers; i++) trades.push(user2.takeOffer(offers[i].getId(), paymentAccount.getId())); trades = await Promise.all(trades); - HavenoUtils.log(1, "Bob done taking offers"); + HavenoUtils.log(1, "user2 done taking offers"); // test trades const depositTxIds: string[] = []; for (const trade of trades) { if (trade.getPhase() !== "DEPOSIT_PUBLISHED") throw new Error("Trade phase expected to be DEPOSIT_PUBLISHED but was " + trade.getPhase() + " for trade " + trade.getTradeId()); expect(trade.getPhase()).toEqual("DEPOSIT_PUBLISHED"); - const fetchedTrade: TradeInfo = await bob.getTrade(trade.getTradeId()); + const fetchedTrade: TradeInfo = await user2.getTrade(trade.getTradeId()); if (fetchedTrade.getPhase() !== "DEPOSIT_PUBLISHED") throw new Error("Fetched phase expected to be DEPOSIT_PUBLISHED but was " + fetchedTrade.getPhase() + " for trade " + fetchedTrade.getTradeId()); expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_PUBLISHED"); depositTxIds.push(fetchedTrade.getMakerDepositTxId()); @@ -1345,173 +1345,173 @@ test("Can resolve disputes", async () => { // open disputes HavenoUtils.log(1, "Opening disputes"); await Promise.all([ - bob.openDispute(trades[0].getTradeId()), - alice.openDispute(trades[1].getTradeId()), - bob.openDispute(trades[2].getTradeId()), - alice.openDispute(trades[3].getTradeId()) + user2.openDispute(trades[0].getTradeId()), + user1.openDispute(trades[1].getTradeId()), + user2.openDispute(trades[2].getTradeId()), + user1.openDispute(trades[3].getTradeId()) ]); HavenoUtils.log(1, "Done opening disputes"); // test dispute state - const bobDispute = await bob.getDispute(trades[0].getTradeId()); - expect(bobDispute.getTradeId()).toEqual(trades[0].getTradeId()); - expect(bobDispute.getIsOpener()).toBe(true); - expect(bobDispute.getDisputeOpenerIsBuyer()).toBe(false); + const user2Dispute = await user2.getDispute(trades[0].getTradeId()); + expect(user2Dispute.getTradeId()).toEqual(trades[0].getTradeId()); + expect(user2Dispute.getIsOpener()).toBe(true); + expect(user2Dispute.getDisputeOpenerIsBuyer()).toBe(false); // get non-existing dispute should fail try { - await bob.getDispute("invalid"); + await user2.getDispute("invalid"); throw new Error("get dispute with invalid id should fail"); } catch (err: any) { assert.equal(err.message, "dispute for trade id 'invalid' not found"); } - // alice sees the dispute + // user1 sees the dispute await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs); - const aliceDispute = await alice.getDispute(trades[0].getTradeId()); - expect(aliceDispute.getTradeId()).toEqual(trades[0].getTradeId()); - expect(aliceDispute.getIsOpener()).toBe(false); + const user1Dispute = await user1.getDispute(trades[0].getTradeId()); + expect(user1Dispute.getTradeId()).toEqual(trades[0].getTradeId()); + expect(user1Dispute.getIsOpener()).toBe(false); // arbitrator sees both disputes const disputes = await arbitrator.getDisputes(); expect(disputes.length).toBeGreaterThanOrEqual(2); - const arbAliceDispute = disputes.find(d => d.getId() === aliceDispute.getId()); - assert(arbAliceDispute); - const arbBobDispute = disputes.find(d => d.getId() === bobDispute.getId()); - assert(arbBobDispute); + const arbuser1Dispute = disputes.find(d => d.getId() === user1Dispute.getId()); + assert(arbuser1Dispute); + const arbuser2Dispute = disputes.find(d => d.getId() === user2Dispute.getId()); + assert(arbuser2Dispute); - // arbitrator sends chat messages to alice and bob + // arbitrator sends chat messages to user1 and user2 HavenoUtils.log(1, "Testing chat messages"); - await arbitrator.sendDisputeChatMessage(arbBobDispute!.getId(), "Arbitrator chat message to Bob", []); - await arbitrator.sendDisputeChatMessage(arbAliceDispute!.getId(), "Arbitrator chat message to Alice", []); + await arbitrator.sendDisputeChatMessage(arbuser2Dispute!.getId(), "Arbitrator chat message to user2", []); + await arbitrator.sendDisputeChatMessage(arbuser1Dispute!.getId(), "Arbitrator chat message to user1", []); - // alice and bob reply to arbitrator chat messages + // user1 and user2 reply to arbitrator chat messages await wait(TestConfig.maxTimePeerNoticeMs); // wait for arbitrator's message to arrive const attachment = new Attachment(); - const bytes = new Uint8Array(Buffer.from("Proof Bob was scammed", "utf8")); + const bytes = new Uint8Array(Buffer.from("Proof user2 was scammed", "utf8")); attachment.setBytes(bytes); attachment.setFileName("proof.txt"); const attachment2 = new Attachment(); const bytes2 = new Uint8Array(Buffer.from("picture bytes", "utf8")); attachment2.setBytes(bytes2); attachment2.setFileName("proof.png"); - HavenoUtils.log(2, "Bob sending chat message to arbitrator. tradeId=" + trades[0].getTradeId() + ", disputeId=" + bobDispute.getId()); - await bob.sendDisputeChatMessage(bobDispute.getId(), "Bob chat message", [attachment, attachment2]); - await wait(TestConfig.maxTimePeerNoticeMs); // wait for bob's message to arrive - HavenoUtils.log(2, "Alice sending chat message to arbitrator. tradeId=" + trades[0].getTradeId() + ", disputeId=" + aliceDispute.getId()); - await alice.sendDisputeChatMessage(aliceDispute.getId(), "Alice chat message", []); + HavenoUtils.log(2, "user2 sending chat message to arbitrator. tradeId=" + trades[0].getTradeId() + ", disputeId=" + user2Dispute.getId()); + await user2.sendDisputeChatMessage(user2Dispute.getId(), "user2 chat message", [attachment, attachment2]); + await wait(TestConfig.maxTimePeerNoticeMs); // wait for user2's message to arrive + HavenoUtils.log(2, "user1 sending chat message to arbitrator. tradeId=" + trades[0].getTradeId() + ", disputeId=" + user1Dispute.getId()); + await user1.sendDisputeChatMessage(user1Dispute.getId(), "user1 chat message", []); - // test alice and bob's chat messages + // test user1 and user2's chat messages await wait(TestConfig.maxTimePeerNoticeMs); - let updatedDispute = await bob.getDispute(trades[0].getTradeId()); + let updatedDispute = await user2.getDispute(trades[0].getTradeId()); let messages = updatedDispute.getChatMessageList(); expect(messages.length).toEqual(3); // 1st message is the system message - expect(messages[1].getMessage()).toEqual("Arbitrator chat message to Bob"); - expect(messages[2].getMessage()).toEqual("Bob chat message"); + expect(messages[1].getMessage()).toEqual("Arbitrator chat message to user2"); + expect(messages[2].getMessage()).toEqual("user2 chat message"); let attachments = messages[2].getAttachmentsList(); expect(attachments.length).toEqual(2); expect(attachments[0].getFileName()).toEqual("proof.txt"); expect(attachments[0].getBytes()).toEqual(bytes); expect(attachments[1].getFileName()).toEqual("proof.png"); expect(attachments[1].getBytes()).toEqual(bytes2); - updatedDispute = await alice.getDispute(trades[0].getTradeId()); + updatedDispute = await user1.getDispute(trades[0].getTradeId()); messages = updatedDispute.getChatMessageList(); expect(messages.length).toEqual(3); - expect(messages[1].getMessage()).toEqual("Arbitrator chat message to Alice"); - expect(messages[2].getMessage()).toEqual("Alice chat message"); + expect(messages[1].getMessage()).toEqual("Arbitrator chat message to user1"); + expect(messages[2].getMessage()).toEqual("user1 chat message"); // test notifications of chat messages - let chatNotifications = getNotifications(aliceNotifications, NotificationMessage.NotificationType.CHAT_MESSAGE); + let chatNotifications = getNotifications(user1Notifications, NotificationMessage.NotificationType.CHAT_MESSAGE); expect(chatNotifications.length).toBe(1); - expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual("Arbitrator chat message to Alice"); - chatNotifications = getNotifications(bobNotifications, NotificationMessage.NotificationType.CHAT_MESSAGE); + expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual("Arbitrator chat message to user1"); + chatNotifications = getNotifications(user2Notifications, NotificationMessage.NotificationType.CHAT_MESSAGE); expect(chatNotifications.length).toBe(1); - expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual("Arbitrator chat message to Bob"); + expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual("Arbitrator chat message to user2"); // arbitrator has 2 chat messages, one with attachments chatNotifications = getNotifications(arbitratorNotifications, NotificationMessage.NotificationType.CHAT_MESSAGE); expect(chatNotifications.length).toBe(2); - expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual("Bob chat message"); + expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual("user2 chat message"); assert(chatNotifications[0].getChatMessage()?.getAttachmentsList()); attachments = chatNotifications[0].getChatMessage()?.getAttachmentsList()!; expect(attachments[0].getFileName()).toEqual("proof.txt"); expect(attachments[0].getBytes()).toEqual(bytes); expect(attachments[1].getFileName()).toEqual("proof.png"); expect(attachments[1].getBytes()).toEqual(bytes2); - expect(chatNotifications[1].getChatMessage()?.getMessage()).toEqual("Alice chat message"); + expect(chatNotifications[1].getChatMessage()?.getMessage()).toEqual("user1 chat message"); // award trade amount to seller HavenoUtils.log(1, "Awarding trade amount to seller, trade " + trades[0].getTradeId()); - let bobBalancesBefore = await bob.getBalances(); - let aliceBalancesBefore = await alice.getBalances(); + let user2BalancesBefore = await user2.getBalances(); + let user1BalancesBefore = await user1.getBalances(); await arbitrator.resolveDispute(trades[0].getTradeId(), DisputeResult.Winner.SELLER, DisputeResult.Reason.PEER_WAS_LATE, "Seller is winner"); HavenoUtils.log(1, "Done resolving dispute"); // dispute is resolved await wait(TestConfig.maxWalletStartupMs); - updatedDispute = await alice.getDispute(trades[0].getTradeId()); + updatedDispute = await user1.getDispute(trades[0].getTradeId()); expect(updatedDispute.getIsClosed()).toBe(true); - updatedDispute = await bob.getDispute(trades[0].getTradeId()); + updatedDispute = await user2.getDispute(trades[0].getTradeId()); expect(updatedDispute.getIsClosed()).toBe(true); // check balances after payout tx await wait(TestConfig.walletSyncPeriodMs * 2); - let aliceBalancesAfter = await alice.getBalances(); - let bobBalancesAfter = await bob.getBalances(); - let aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance()); - let bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance()); + let user1BalancesAfter = await user1.getBalances(); + let user2BalancesAfter = await user2.getBalances(); + let user1Difference = BigInt(user1BalancesAfter.getBalance()) - BigInt(user1BalancesBefore.getBalance()); + let user2Difference = BigInt(user2BalancesAfter.getBalance()) - BigInt(user2BalancesBefore.getBalance()); let winnerPayout = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[0].getSellerSecurityDeposit()); let loserPayout = HavenoUtils.centinerosToAtomicUnits(offers[0].getBuyerSecurityDeposit()); - expect(loserPayout - aliceDifference).toBeLessThan(TestConfig.maxFee); - expect(bobDifference).toEqual(winnerPayout); + expect(loserPayout - user1Difference).toBeLessThan(TestConfig.maxFee); + expect(user2Difference).toEqual(winnerPayout); // award trade amount to buyer HavenoUtils.log(1, "Awarding trade amount to buyer, trade " + trades[1].getTradeId()); - aliceBalancesBefore = await alice.getBalances(); - bobBalancesBefore = await bob.getBalances(); + user1BalancesBefore = await user1.getBalances(); + user2BalancesBefore = await user2.getBalances(); await arbitrator.resolveDispute(trades[1].getTradeId(), DisputeResult.Winner.BUYER, DisputeResult.Reason.SELLER_NOT_RESPONDING, "Buyer is winner"); HavenoUtils.log(1, "Done resolving dispute"); await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs * 2 + TestConfig.walletSyncPeriodMs); // TODO (woodser): arbitrator sends mailbox message to trader -> trader opens and syncs multisig wallet and sends updated multisig hex to arbitrator -> arbitrator opens and syncs multisig wallet, signs payout tx and sends to trader -> trader finishes signing payout tx and broadcasts. more efficient way? traders can verify payout tx without syncing multisig wallet again - aliceBalancesAfter = await alice.getBalances(); - bobBalancesAfter = await bob.getBalances(); - aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance()); - bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance()); + user1BalancesAfter = await user1.getBalances(); + user2BalancesAfter = await user2.getBalances(); + user1Difference = BigInt(user1BalancesAfter.getBalance()) - BigInt(user1BalancesBefore.getBalance()); + user2Difference = BigInt(user2BalancesAfter.getBalance()) - BigInt(user2BalancesBefore.getBalance()); winnerPayout = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[1].getBuyerSecurityDeposit()); loserPayout = HavenoUtils.centinerosToAtomicUnits(offers[1].getSellerSecurityDeposit()); - if (aliceDifference !== winnerPayout || loserPayout - bobDifference > TestConfig.maxFee) { + if (user1Difference !== winnerPayout || loserPayout - user2Difference > TestConfig.maxFee) { HavenoUtils.log(0, "WARNING: payout not observed. waiting longer"); // TODO (woodser): refactor dispute resolution await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs); - aliceBalancesAfter = await alice.getBalances(); - bobBalancesAfter = await bob.getBalances(); - aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance()); - bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance()); + user1BalancesAfter = await user1.getBalances(); + user2BalancesAfter = await user2.getBalances(); + user1Difference = BigInt(user1BalancesAfter.getBalance()) - BigInt(user1BalancesBefore.getBalance()); + user2Difference = BigInt(user2BalancesAfter.getBalance()) - BigInt(user2BalancesBefore.getBalance()); } - expect(aliceDifference).toEqual(winnerPayout); - expect(loserPayout - bobDifference).toBeLessThan(TestConfig.maxFee); + expect(user1Difference).toEqual(winnerPayout); + expect(loserPayout - user2Difference).toBeLessThan(TestConfig.maxFee); // award half of trade amount to buyer HavenoUtils.log(1, "Awarding half of trade amount to buyer, trade " + trades[2].getTradeId()); let customWinnerAmount = tradeAmount / BigInt(2) + HavenoUtils.centinerosToAtomicUnits(offers[2].getBuyerSecurityDeposit()); - aliceBalancesBefore = await alice.getBalances(); - bobBalancesBefore = await bob.getBalances(); + user1BalancesBefore = await user1.getBalances(); + user2BalancesBefore = await user2.getBalances(); await arbitrator.resolveDispute(trades[2].getTradeId(), DisputeResult.Winner.BUYER, DisputeResult.Reason.WRONG_SENDER_ACCOUNT, "Split trade amount", customWinnerAmount); HavenoUtils.log(1, "Done resolving dispute"); await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs * 2 + TestConfig.walletSyncPeriodMs); - aliceBalancesAfter = await alice.getBalances(); - bobBalancesAfter = await bob.getBalances(); - aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance()); - bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance()); + user1BalancesAfter = await user1.getBalances(); + user2BalancesAfter = await user2.getBalances(); + user1Difference = BigInt(user1BalancesAfter.getBalance()) - BigInt(user1BalancesBefore.getBalance()); + user2Difference = BigInt(user2BalancesAfter.getBalance()) - BigInt(user2BalancesBefore.getBalance()); loserPayout = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[2].getBuyerSecurityDeposit()) + HavenoUtils.centinerosToAtomicUnits(offers[2].getSellerSecurityDeposit()) - customWinnerAmount; - if (aliceDifference !== customWinnerAmount || loserPayout - bobDifference > TestConfig.maxFee) { + if (user1Difference !== customWinnerAmount || loserPayout - user2Difference > TestConfig.maxFee) { HavenoUtils.log(0, "WARNING: payout not observed. waiting longer"); await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs); - aliceBalancesAfter = await alice.getBalances(); - bobBalancesAfter = await bob.getBalances(); - aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance()); - bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance()); + user1BalancesAfter = await user1.getBalances(); + user2BalancesAfter = await user2.getBalances(); + user1Difference = BigInt(user1BalancesAfter.getBalance()) - BigInt(user1BalancesBefore.getBalance()); + user2Difference = BigInt(user2BalancesAfter.getBalance()) - BigInt(user2BalancesBefore.getBalance()); } - expect(aliceDifference).toEqual(customWinnerAmount); - expect(loserPayout - bobDifference).toBeLessThanOrEqual(TestConfig.maxFee); + expect(user1Difference).toEqual(customWinnerAmount); + expect(loserPayout - user2Difference).toBeLessThanOrEqual(TestConfig.maxFee); // award too little to loser customWinnerAmount = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[3].getBuyerSecurityDeposit()) + HavenoUtils.centinerosToAtomicUnits(offers[3].getSellerSecurityDeposit()) - BigInt("10000"); @@ -1525,40 +1525,40 @@ test("Can resolve disputes", async () => { // award full amount to seller HavenoUtils.log(1, "Awarding full amount to seller, trade " + trades[3].getTradeId()); customWinnerAmount = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[3].getBuyerSecurityDeposit()) + HavenoUtils.centinerosToAtomicUnits(offers[3].getSellerSecurityDeposit()); - aliceBalancesBefore = await alice.getBalances(); - bobBalancesBefore = await bob.getBalances(); + user1BalancesBefore = await user1.getBalances(); + user2BalancesBefore = await user2.getBalances(); await arbitrator.resolveDispute(trades[3].getTradeId(), DisputeResult.Winner.SELLER, DisputeResult.Reason.TRADE_ALREADY_SETTLED, "Seller gets everything", customWinnerAmount); await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs * 2 + TestConfig.walletSyncPeriodMs); - aliceBalancesAfter = await alice.getBalances(); - bobBalancesAfter = await bob.getBalances(); - aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance()); - bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance()); - expect(aliceDifference).toEqual(BigInt(0)); - if (customWinnerAmount - bobDifference > TestConfig.maxFee) { + user1BalancesAfter = await user1.getBalances(); + user2BalancesAfter = await user2.getBalances(); + user1Difference = BigInt(user1BalancesAfter.getBalance()) - BigInt(user1BalancesBefore.getBalance()); + user2Difference = BigInt(user2BalancesAfter.getBalance()) - BigInt(user2BalancesBefore.getBalance()); + expect(user1Difference).toEqual(BigInt(0)); + if (customWinnerAmount - user2Difference > TestConfig.maxFee) { HavenoUtils.log(0, "WARNING: payout not observed. waiting longer"); await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs); - aliceBalancesAfter = await alice.getBalances(); - bobBalancesAfter = await bob.getBalances(); - aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance()); - bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance()); + user1BalancesAfter = await user1.getBalances(); + user2BalancesAfter = await user2.getBalances(); + user1Difference = BigInt(user1BalancesAfter.getBalance()) - BigInt(user1BalancesBefore.getBalance()); + user2Difference = BigInt(user2BalancesAfter.getBalance()) - BigInt(user2BalancesBefore.getBalance()); } - expect(customWinnerAmount - bobDifference).toBeLessThanOrEqual(TestConfig.maxFee); + expect(customWinnerAmount - user2Difference).toBeLessThanOrEqual(TestConfig.maxFee); }); test("Cannot make or take offer with insufficient unlocked funds", async () => { - let charlie: HavenoClient | undefined; + let user3: HavenoClient | undefined; let err: any; try { - // start charlie - charlie = await initHaveno(); + // start user3 + user3 = await initHaveno(); - // charlie creates ethereum payment account - const paymentAccount = await createCryptoPaymentAccount(charlie); + // user3 creates ethereum payment account + const paymentAccount = await createCryptoPaymentAccount(user3); - // charlie cannot make offer with insufficient funds + // user3 cannot make offer with insufficient funds try { - await postOffer(charlie, {paymentAccountId: paymentAccount.getId()}); + await postOffer(user3, {paymentAccountId: paymentAccount.getId()}); throw new Error("Should have failed making offer with insufficient funds") } catch (err: any) { const errTyped = err as HavenoError; @@ -1566,21 +1566,21 @@ test("Cannot make or take offer with insufficient unlocked funds", async () => { assert(err.message.includes("not enough money"), "Unexpected error: " + err.message); } - // alice posts offer - const offers: OfferInfo[] = await alice.getMyOffers("ETH"); + // user1 posts offer + const offers: OfferInfo[] = await user1.getMyOffers("ETH"); let offer: OfferInfo; if (offers.length) offer = offers[0]; else { const tradeAmount = BigInt("250000000000"); - await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice); - offer = await postOffer(alice, {amount: tradeAmount}); + await waitForUnlockedBalance(tradeAmount * BigInt("2"), user1); + offer = await postOffer(user1, {amount: tradeAmount}); assert.equal(offer.getState(), "AVAILABLE"); await wait(TestConfig.walletSyncPeriodMs * 2); } - // charlie cannot take offer with insufficient funds + // user3 cannot take offer with insufficient funds try { - await charlie.takeOffer(offer.getId(), paymentAccount.getId()); + await user3.takeOffer(offer.getId(), paymentAccount.getId()); throw new Error("Should have failed taking offer with insufficient funds") } catch (err: any) { const errTyped = err as HavenoError; @@ -1588,9 +1588,9 @@ test("Cannot make or take offer with insufficient unlocked funds", async () => { assert.equal(errTyped.code, 2); } - // charlie does not have trade + // user3 does not have trade try { - await charlie.getTrade(offer.getId()); + await user3.getTrade(offer.getId()); } catch (err: any) { const errTyped = err as HavenoError; assert.equal(errTyped.code, 3); @@ -1600,8 +1600,8 @@ test("Cannot make or take offer with insufficient unlocked funds", async () => { err = err2; } - // stop charlie - if (charlie) await releaseHavenoProcess(charlie, true); + // stop user3 + if (user3) await releaseHavenoProcess(user3, true); if (err) throw err; }); @@ -1609,49 +1609,49 @@ test("Invalidates offers when reserved funds are spent", async () => { let err; let tx; try { - // wait for alice to have unlocked balance for trade + // wait for user1 to have unlocked balance for trade const tradeAmount = BigInt("250000000000"); - await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice); + await waitForUnlockedBalance(tradeAmount * BigInt("2"), user1); // get frozen key images before posting offer const frozenKeyImagesBefore = []; - for (const frozenOutput of await aliceWallet.getOutputs({isFrozen: true})) frozenKeyImagesBefore.push(frozenOutput.getKeyImage().getHex()); + for (const frozenOutput of await user1Wallet.getOutputs({isFrozen: true})) frozenKeyImagesBefore.push(frozenOutput.getKeyImage().getHex()); // post offer await wait(1000); const assetCode = getRandomAssetCode(); - const offer: OfferInfo = await postOffer(alice, {assetCode: assetCode, amount: tradeAmount}); + const offer: OfferInfo = await postOffer(user1, {assetCode: assetCode, amount: tradeAmount}); // get key images reserved by offer const reservedKeyImages = []; const frozenKeyImagesAfter = []; - for (const frozenOutput of await aliceWallet.getOutputs({isFrozen: true})) frozenKeyImagesAfter.push(frozenOutput.getKeyImage().getHex()); + for (const frozenOutput of await user1Wallet.getOutputs({isFrozen: true})) frozenKeyImagesAfter.push(frozenOutput.getKeyImage().getHex()); for (const frozenKeyImageAfter of frozenKeyImagesAfter) { if (!frozenKeyImagesBefore.includes(frozenKeyImageAfter)) reservedKeyImages.push(frozenKeyImageAfter); } // offer is available to peers await wait(TestConfig.walletSyncPeriodMs * 2); - if (!getOffer(await bob.getOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posting"); + if (!getOffer(await user2.getOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posting"); // spend one of offer's reserved outputs if (!reservedKeyImages.length) throw new Error("No reserved key images detected"); - await aliceWallet.thawOutput(reservedKeyImages[0]); - tx = await aliceWallet.sweepOutput({keyImage: reservedKeyImages[0], address: await aliceWallet.getPrimaryAddress(), relay: false}); + await user1Wallet.thawOutput(reservedKeyImages[0]); + tx = await user1Wallet.sweepOutput({keyImage: reservedKeyImages[0], address: await user1Wallet.getPrimaryAddress(), relay: false}); await monerod.submitTxHex(tx.getFullHex(), true); // wait for spend to be seen await wait(TestConfig.walletSyncPeriodMs * 2); // TODO (woodser): need place for common test utilities // offer is removed from peer offers - if (getOffer(await bob.getOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in peer's offers after reserved funds spent"); + if (getOffer(await user2.getOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in peer's offers after reserved funds spent"); // offer is removed from my offers - if (getOffer(await alice.getMyOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after reserved funds spent"); + if (getOffer(await user1.getMyOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after reserved funds spent"); // offer is automatically cancelled try { - await alice.removeOffer(offer.getId()); + await user1.removeOffer(offer.getId()); throw new Error("cannot remove invalidated offer"); } catch (err: any) { if (err.message === "cannot remove invalidated offer") throw new Error("Unexpected error: " + err.message); @@ -1810,7 +1810,7 @@ async function initHaveno(config?: any): Promise { if (!config.appName) config.appName = "haveno-" + TestConfig.baseCurrencyNetwork + "_instance_" + GenUtils.getUUID(); // connect to existing server or start new process - let havenod; + let havenod: HavenoClient; try { // try to connect to existing server @@ -1823,7 +1823,7 @@ async function initHaveno(config?: any): Promise { if (config.url) proxyPort = new URL(config.url).port else { for (const port of Array.from(TestConfig.proxyPorts.keys())) { - if (port === "8079" || port === "8080" || port === "8081") continue; // reserved for arbitrator, alice, and bob + if (port === "8079" || port === "8080" || port === "8081") continue; // reserved for arbitrator, user1, and user2 if (!GenUtils.arrayContains(HAVENO_PROCESS_PORTS, port)) { HAVENO_PROCESS_PORTS.push(port); proxyPort = port; @@ -2336,76 +2336,76 @@ function testOffer(offer: OfferInfo, config?: any) { /** * Tests trade chat functionality. Must be called during an open trade. */ -async function testTradeChat(tradeId: string, alice: HavenoClient, bob: HavenoClient) { +async function testTradeChat(tradeId: string, user1: HavenoClient, user2: HavenoClient) { HavenoUtils.log(1, "Testing trade chat"); // invalid trade should throw error try { - await alice.getChatMessages("invalid"); + await user1.getChatMessages("invalid"); throw new Error("get chat messages with invalid id should fail"); } catch (err: any) { assert.equal(err.message, "trade with id 'invalid' not found"); } // trade chat should be in initial state - let messages = await alice.getChatMessages(tradeId); + let messages = await user1.getChatMessages(tradeId); assert(messages.length === 0); - messages = await bob.getChatMessages(tradeId); + messages = await user2.getChatMessages(tradeId); assert(messages.length === 0); // add notification handlers and send some messages - const aliceNotifications: NotificationMessage[] = []; - const bobNotifications: NotificationMessage[] = []; - await alice.addNotificationListener(notification => { aliceNotifications.push(notification); }); - await bob.addNotificationListener(notification => { bobNotifications.push(notification); }); + const user1Notifications: NotificationMessage[] = []; + const user2Notifications: NotificationMessage[] = []; + await user1.addNotificationListener(notification => { user1Notifications.push(notification); }); + await user2.addNotificationListener(notification => { user2Notifications.push(notification); }); // send simple conversation and verify the list of messages - const aliceMsg = "Hi I'm Alice"; - await alice.sendChatMessage(tradeId, aliceMsg); + const user1Msg = "Hi I'm user1"; + await user1.sendChatMessage(tradeId, user1Msg); await wait(TestConfig.maxTimePeerNoticeMs); - messages = await bob.getChatMessages(tradeId); + messages = await user2.getChatMessages(tradeId); expect(messages.length).toEqual(2); expect(messages[0].getIsSystemMessage()).toEqual(true); // first message is system - expect(messages[1].getMessage()).toEqual(aliceMsg); + expect(messages[1].getMessage()).toEqual(user1Msg); - const bobMsg = "Hello I'm Bob"; - await bob.sendChatMessage(tradeId, bobMsg); + const user2Msg = "Hello I'm user2"; + await user2.sendChatMessage(tradeId, user2Msg); await wait(TestConfig.maxTimePeerNoticeMs); - messages = await alice.getChatMessages(tradeId); + messages = await user1.getChatMessages(tradeId); expect(messages.length).toEqual(3); expect(messages[0].getIsSystemMessage()).toEqual(true); - expect(messages[1].getMessage()).toEqual(aliceMsg); - expect(messages[2].getMessage()).toEqual(bobMsg); + expect(messages[1].getMessage()).toEqual(user1Msg); + expect(messages[2].getMessage()).toEqual(user2Msg); // verify notifications - let chatNotifications = getNotifications(aliceNotifications, NotificationMessage.NotificationType.CHAT_MESSAGE); + let chatNotifications = getNotifications(user1Notifications, NotificationMessage.NotificationType.CHAT_MESSAGE); expect(chatNotifications.length).toBe(1); - expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual(bobMsg); - chatNotifications = getNotifications(bobNotifications, NotificationMessage.NotificationType.CHAT_MESSAGE); + expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual(user2Msg); + chatNotifications = getNotifications(user2Notifications, NotificationMessage.NotificationType.CHAT_MESSAGE); expect(chatNotifications.length).toBe(1); - expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual(aliceMsg); + expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual(user1Msg); // additional msgs const msgs = ["", " ", "", "さようなら"]; for(const msg of msgs) { - await alice.sendChatMessage(tradeId, msg); + await user1.sendChatMessage(tradeId, msg); await wait(1000); // the async operation can result in out of order messages } await wait(TestConfig.maxTimePeerNoticeMs); - messages = await bob.getChatMessages(tradeId); + messages = await user2.getChatMessages(tradeId); let offset = 3; // 3 existing messages expect(messages.length).toEqual(offset + msgs.length); expect(messages[0].getIsSystemMessage()).toEqual(true); - expect(messages[1].getMessage()).toEqual(aliceMsg); - expect(messages[2].getMessage()).toEqual(bobMsg); + expect(messages[1].getMessage()).toEqual(user1Msg); + expect(messages[2].getMessage()).toEqual(user2Msg); for (let i = 0; i < msgs.length; i++) { expect(messages[i + offset].getMessage()).toEqual(msgs[i]); } - chatNotifications = getNotifications(bobNotifications, NotificationMessage.NotificationType.CHAT_MESSAGE); + chatNotifications = getNotifications(user2Notifications, NotificationMessage.NotificationType.CHAT_MESSAGE); offset = 1; // 1 existing notification expect(chatNotifications.length).toBe(offset + msgs.length); - expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual(aliceMsg); + expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual(user1Msg); for (let i = 0; i < msgs.length; i++) { expect(chatNotifications[i + offset].getChatMessage()?.getMessage()).toEqual(msgs[i]); } @@ -2495,7 +2495,7 @@ function getValidFormInput(fieldId: PaymentAccountFormField.FieldId, form: Payme case PaymentAccountFormField.FieldId.HOLDER_EMAIL: throw new Error("Not implemented"); case PaymentAccountFormField.FieldId.HOLDER_NAME: - return "Alice Doe"; + return "user1 Doe"; case PaymentAccountFormField.FieldId.HOLDER_TAX_ID: throw new Error("Not implemented"); case PaymentAccountFormField.FieldId.IBAN: