mirror of
https://github.com/haveno-dex/haveno-ts.git
synced 2025-01-26 06:26:03 -05:00
test posting offers, taking trades, and opening disputes in parallel (#78)
tests can run repeatedly since backend closes wallets when unused support funding a wallet with X outputs of amount support checking if wallet funded with X outputs of amount
This commit is contained in:
parent
ad26aae4e6
commit
91710b2bcf
@ -13,8 +13,10 @@ import OnlineStatus = UrlConnection.OnlineStatus;
|
|||||||
// import monero-javascript
|
// import monero-javascript
|
||||||
const monerojs = require("monero-javascript"); // TODO (woodser): support typescript and `npm install @types/monero-javascript` in monero-javascript
|
const monerojs = require("monero-javascript"); // TODO (woodser): support typescript and `npm install @types/monero-javascript` in monero-javascript
|
||||||
const GenUtils = monerojs.GenUtils;
|
const GenUtils = monerojs.GenUtils;
|
||||||
|
const BigInteger = monerojs.BigInteger;
|
||||||
const MoneroNetworkType = monerojs.MoneroNetworkType;
|
const MoneroNetworkType = monerojs.MoneroNetworkType;
|
||||||
const MoneroTxConfig = monerojs.MoneroTxConfig;
|
const MoneroTxConfig = monerojs.MoneroTxConfig;
|
||||||
|
const MoneroDestination = monerojs.MoneroDestination;
|
||||||
const MoneroUtils = monerojs.MoneroUtils;
|
const MoneroUtils = monerojs.MoneroUtils;
|
||||||
const TaskLooper = monerojs.TaskLooper;
|
const TaskLooper = monerojs.TaskLooper;
|
||||||
|
|
||||||
@ -80,11 +82,13 @@ const TestConfig = {
|
|||||||
url: "http://localhost:8081",
|
url: "http://localhost:8081",
|
||||||
accountPasswordRequired: false,
|
accountPasswordRequired: false,
|
||||||
accountPassword: "abctesting789",
|
accountPassword: "abctesting789",
|
||||||
|
walletUrl: "http://127.0.0.1:38092",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
maxFee: BigInt("75000000000"),
|
maxFee: BigInt("75000000000"),
|
||||||
walletSyncPeriodMs: 5000, // TODO (woodser): auto adjust higher if using remote connection
|
walletSyncPeriodMs: 5000, // TODO (woodser): auto adjust higher if using remote connection
|
||||||
daemonPollPeriodMs: 15000,
|
daemonPollPeriodMs: 15000,
|
||||||
|
maxWalletStartupMs: 10000, // TODO (woodser): make shorter by switching to jni
|
||||||
maxTimePeerNoticeMs: 3000,
|
maxTimePeerNoticeMs: 3000,
|
||||||
assetCodes: ["USD", "GBP", "EUR", "ETH", "BTC", "BCH", "LTC", "ZEC"], // primary asset codes
|
assetCodes: ["USD", "GBP", "EUR", "ETH", "BTC", "BCH", "LTC", "ZEC"], // primary asset codes
|
||||||
cryptoAddresses: [{
|
cryptoAddresses: [{
|
||||||
@ -95,7 +99,7 @@ const TestConfig = {
|
|||||||
address: "bcrt1q6j90vywv8x7eyevcnn2tn2wrlg3vsjlsvt46qz"
|
address: "bcrt1q6j90vywv8x7eyevcnn2tn2wrlg3vsjlsvt46qz"
|
||||||
}, {
|
}, {
|
||||||
currencyCode: "BCH",
|
currencyCode: "BCH",
|
||||||
address: "1JRjBNKi4ZgJpKPeoL4149Q7ZZD3VvVgk9" // TODO: support Cash Address format also
|
address: "1JRjBNKi4ZgJpKPeoL4149Q7ZZD3VvVgk9" // TODO: support CashAddr format only
|
||||||
}, {
|
}, {
|
||||||
currencyCode: "LTC",
|
currencyCode: "LTC",
|
||||||
address: "LXUTUN5mTPc2LsS7cEjkyjTRcfYyJGoUuQ"
|
address: "LXUTUN5mTPc2LsS7cEjkyjTRcfYyJGoUuQ"
|
||||||
@ -115,8 +119,9 @@ const TestConfig = {
|
|||||||
["8086", ["10005", "7781"]],
|
["8086", ["10005", "7781"]],
|
||||||
]),
|
]),
|
||||||
devPrivilegePrivKey: "6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a", // from DEV_PRIVILEGE_PRIV_KEY
|
devPrivilegePrivKey: "6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a", // from DEV_PRIVILEGE_PRIV_KEY
|
||||||
|
tradeInitTimeout: 60000,
|
||||||
timeout: 900000, // timeout in ms for all tests to complete (15 minutes)
|
timeout: 900000, // timeout in ms for all tests to complete (15 minutes)
|
||||||
postOffer: {
|
postOffer: { // TODO (woodser): use typed config
|
||||||
direction: "buy", // buy or sell xmr
|
direction: "buy", // buy or sell xmr
|
||||||
amount: BigInt("200000000000"), // amount of xmr to trade
|
amount: BigInt("200000000000"), // amount of xmr to trade
|
||||||
assetCode: "eth", // counter asset to trade
|
assetCode: "eth", // counter asset to trade
|
||||||
@ -142,6 +147,7 @@ let bob: HavenoDaemon;
|
|||||||
let monerod: any;
|
let monerod: any;
|
||||||
let fundingWallet: any;
|
let fundingWallet: any;
|
||||||
let aliceWallet: any;
|
let aliceWallet: any;
|
||||||
|
let bobWallet: any;
|
||||||
|
|
||||||
// track started haveno processes
|
// track started haveno processes
|
||||||
const HAVENO_PROCESSES: HavenoDaemon[] = [];
|
const HAVENO_PROCESSES: HavenoDaemon[] = [];
|
||||||
@ -179,13 +185,14 @@ beforeAll(async () => {
|
|||||||
// connect monero clients
|
// connect monero clients
|
||||||
monerod = await monerojs.connectToDaemonRpc(TestConfig.monerod.url, TestConfig.monerod.username, TestConfig.monerod.password);
|
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);
|
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);
|
||||||
|
|
||||||
// initialize funding wallet
|
// initialize funding wallet
|
||||||
await initFundingWallet();
|
await initFundingWallet();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async() => {
|
beforeEach(async() => {
|
||||||
console.log("Before test \"" + expect.getState().currentTestName + "\"");
|
HavenoUtils.log(0, "BEFORE TEST \"" + expect.getState().currentTestName + "\"");
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@ -889,7 +896,7 @@ test("Can post and remove an offer", async () => {
|
|||||||
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 alice.getMyOffers(assetCode, "buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in my offers after removal");
|
||||||
|
|
||||||
// reserved balance released
|
// reserved balance released
|
||||||
expect(unlockedBalanceBefore).toEqual(BigInt((await alice.getBalances()).getUnlockedBalance()));
|
expect(BigInt((await alice.getBalances()).getUnlockedBalance())).toEqual(unlockedBalanceBefore);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO (woodser): test grpc notifications
|
// TODO (woodser): test grpc notifications
|
||||||
@ -913,7 +920,7 @@ test("Can complete a trade", async () => {
|
|||||||
HavenoUtils.log(1, "Alice posting offer to " + direction + " XMR for " + assetCode);
|
HavenoUtils.log(1, "Alice posting offer to " + direction + " XMR for " + assetCode);
|
||||||
let offer: OfferInfo = await postOffer(alice, {direction: direction, amount: tradeAmount, assetCode: assetCode});
|
let offer: OfferInfo = await postOffer(alice, {direction: direction, amount: tradeAmount, assetCode: assetCode});
|
||||||
expect(offer.getState()).toEqual("AVAILABLE");
|
expect(offer.getState()).toEqual("AVAILABLE");
|
||||||
HavenoUtils.log(1, "Alice done posting offer");
|
HavenoUtils.log(1, "Alice done posting offer " + offer.getId());
|
||||||
|
|
||||||
// TODO (woodser): test error message taking offer before posted
|
// TODO (woodser): test error message taking offer before posted
|
||||||
|
|
||||||
@ -946,6 +953,7 @@ test("Can complete a trade", async () => {
|
|||||||
HavenoUtils.log(1, "Bob done taking offer in " + (Date.now() - startTime) + " ms");
|
HavenoUtils.log(1, "Bob done taking offer in " + (Date.now() - startTime) + " ms");
|
||||||
|
|
||||||
// alice is notified that offer is taken
|
// alice is notified that offer is taken
|
||||||
|
await wait(1000);
|
||||||
let tradeNotifications = getNotifications(aliceNotifications, NotificationMessage.NotificationType.TRADE_UPDATE);
|
let tradeNotifications = getNotifications(aliceNotifications, NotificationMessage.NotificationType.TRADE_UPDATE);
|
||||||
expect(tradeNotifications.length).toBe(1);
|
expect(tradeNotifications.length).toBe(1);
|
||||||
expect(tradeNotifications[0].getTrade()!.getPhase()).toEqual("DEPOSIT_PUBLISHED");
|
expect(tradeNotifications[0].getTrade()!.getPhase()).toEqual("DEPOSIT_PUBLISHED");
|
||||||
@ -974,12 +982,10 @@ test("Can complete a trade", async () => {
|
|||||||
await testTradeChat(trade.getTradeId(), alice, bob);
|
await testTradeChat(trade.getTradeId(), alice, bob);
|
||||||
|
|
||||||
// mine until deposit txs unlock
|
// mine until deposit txs unlock
|
||||||
HavenoUtils.log(1, "Mining to unlock deposit txs");
|
|
||||||
await waitForUnlockedTxs(fetchedTrade.getMakerDepositTxId(), fetchedTrade.getTakerDepositTxId());
|
await waitForUnlockedTxs(fetchedTrade.getMakerDepositTxId(), fetchedTrade.getTakerDepositTxId());
|
||||||
HavenoUtils.log(1, "Done mining to unlock deposit txs");
|
|
||||||
|
|
||||||
// alice notified to send payment
|
// alice notified to send payment
|
||||||
await wait(TestConfig.walletSyncPeriodMs * 2);
|
await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs * 2);
|
||||||
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
||||||
expect(fetchedTrade.getIsDepositConfirmed()).toBe(true);
|
expect(fetchedTrade.getIsDepositConfirmed()).toBe(true);
|
||||||
expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_CONFIRMED"); // TODO (woodser): rename to DEPOSIT_UNLOCKED, have phase for when deposit txs confirm?
|
expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_CONFIRMED"); // TODO (woodser): rename to DEPOSIT_UNLOCKED, have phase for when deposit txs confirm?
|
||||||
@ -988,22 +994,24 @@ test("Can complete a trade", async () => {
|
|||||||
expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_CONFIRMED");
|
expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_CONFIRMED");
|
||||||
|
|
||||||
// alice indicates payment is sent
|
// alice indicates payment is sent
|
||||||
|
HavenoUtils.log(1, "Alice confirming payment started");
|
||||||
await alice.confirmPaymentStarted(trade.getTradeId());
|
await alice.confirmPaymentStarted(trade.getTradeId());
|
||||||
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
||||||
expect(fetchedTrade.getPhase()).toEqual("FIAT_SENT"); // TODO (woodser): rename to PAYMENT_SENT
|
expect(fetchedTrade.getPhase()).toEqual("PAYMENT_SENT");
|
||||||
|
|
||||||
// bob notified payment is sent
|
// bob notified payment is sent
|
||||||
await wait(TestConfig.maxTimePeerNoticeMs);
|
await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs);
|
||||||
fetchedTrade = await bob.getTrade(trade.getTradeId());
|
fetchedTrade = await bob.getTrade(trade.getTradeId());
|
||||||
expect(fetchedTrade.getPhase()).toEqual("FIAT_SENT"); // TODO (woodser): rename to PAYMENT_SENT
|
expect(fetchedTrade.getPhase()).toEqual("PAYMENT_SENT");
|
||||||
|
|
||||||
// bob confirms payment is received
|
// bob confirms payment is received
|
||||||
|
HavenoUtils.log(1, "Bob confirming payment received");
|
||||||
await bob.confirmPaymentReceived(trade.getTradeId());
|
await bob.confirmPaymentReceived(trade.getTradeId());
|
||||||
fetchedTrade = await bob.getTrade(trade.getTradeId());
|
fetchedTrade = await bob.getTrade(trade.getTradeId());
|
||||||
expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED");
|
expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED"); // TODO (woodser): payout is not necessarily published, buyer might need to sign
|
||||||
|
|
||||||
// alice notified trade is complete and of balance changes
|
// alice notified trade is complete and of balance changes
|
||||||
await wait(TestConfig.walletSyncPeriodMs * 2);
|
await wait(TestConfig.maxWalletStartupMs + TestConfig.walletSyncPeriodMs * 2);
|
||||||
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
||||||
expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED");
|
expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED");
|
||||||
|
|
||||||
@ -1022,24 +1030,27 @@ test("Can resolve disputes", async () => {
|
|||||||
|
|
||||||
// wait for alice and bob to have unlocked balance for trade
|
// wait for alice and bob to have unlocked balance for trade
|
||||||
let tradeAmount: bigint = BigInt("250000000000");
|
let tradeAmount: bigint = BigInt("250000000000");
|
||||||
await waitForUnlockedBalance(tradeAmount * BigInt("6"), alice, bob);
|
await fundWallets([aliceWallet, bobWallet], tradeAmount * BigInt("6"), 4);
|
||||||
|
|
||||||
// register to receive notifications
|
// register to receive notifications
|
||||||
let aliceNotifications: NotificationMessage[] = [];
|
let aliceNotifications: NotificationMessage[] = [];
|
||||||
let bobNotifications: NotificationMessage[] = [];
|
let bobNotifications: NotificationMessage[] = [];
|
||||||
let arbitratorNotifications: NotificationMessage[] = [];
|
let arbitratorNotifications: NotificationMessage[] = [];
|
||||||
await alice.addNotificationListener(notification => { aliceNotifications.push(notification); });
|
await alice.addNotificationListener(notification => { HavenoUtils.log(3, "Alice received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); aliceNotifications.push(notification); });
|
||||||
await bob.addNotificationListener(notification => { bobNotifications.push(notification); });
|
await bob.addNotificationListener(notification => { HavenoUtils.log(3, "Bob received notification " + notification.getType() + " " + (notification.getChatMessage() ? notification.getChatMessage()?.getMessage() : "")); bobNotifications.push(notification); });
|
||||||
await arbitrator.addNotificationListener(notification => { arbitratorNotifications.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
|
// alice posts offers to buy xmr
|
||||||
let numOffers = 4;
|
let numOffers = 4;
|
||||||
HavenoUtils.log(1, "Alice posting offers");
|
HavenoUtils.log(1, "Alice posting offers");
|
||||||
let direction = "buy";
|
let direction = "buy";
|
||||||
let offers = [];
|
let offers = [];
|
||||||
for (let i = 0; i < numOffers; i++) offers.push(postOffer(alice, {direction: direction, amount: tradeAmount}));
|
for (let i = 0; i < numOffers; i++) offers.push(postOffer(alice, {direction: direction, amount: tradeAmount, awaitUnlockedBalance: true}));
|
||||||
offers = await Promise.all(offers);
|
offers = await Promise.all(offers);
|
||||||
HavenoUtils.log(1, "Alice done posting 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());
|
||||||
|
|
||||||
// wait for offers to post
|
// wait for offers to post
|
||||||
await wait(TestConfig.walletSyncPeriodMs * 2);
|
await wait(TestConfig.walletSyncPeriodMs * 2);
|
||||||
@ -1047,35 +1058,37 @@ test("Can resolve disputes", async () => {
|
|||||||
// bob takes offers
|
// bob takes offers
|
||||||
let paymentAccount = await createPaymentAccount(bob, "eth");
|
let paymentAccount = await createPaymentAccount(bob, "eth");
|
||||||
HavenoUtils.log(1, "Bob taking offers");
|
HavenoUtils.log(1, "Bob taking offers");
|
||||||
let startTime = Date.now();
|
|
||||||
let trades = [];
|
let trades = [];
|
||||||
for (let i = 0; i < numOffers; i++) trades.push(await bob.takeOffer(offers[i].getId(), paymentAccount.getId()));
|
for (let i = 0; i < numOffers; i++) trades.push(bob.takeOffer(offers[i].getId(), paymentAccount.getId()));
|
||||||
//trades = await Promise.all(trades); // TODO: take trades in parallel when they take less time
|
trades = await Promise.all(trades);
|
||||||
HavenoUtils.log(1, "Bob done taking offers in " + (Date.now() - startTime) + " ms")
|
HavenoUtils.log(1, "Bob done taking offers");
|
||||||
|
|
||||||
// test trades
|
// test trades
|
||||||
let depositTxIds: string[] = [];
|
let depositTxIds: string[] = [];
|
||||||
for (let trade of trades) {
|
for (let 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");
|
expect(trade.getPhase()).toEqual("DEPOSIT_PUBLISHED");
|
||||||
let fetchedTrade: TradeInfo = await bob.getTrade(trade.getTradeId());
|
let fetchedTrade: TradeInfo = await bob.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");
|
expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_PUBLISHED");
|
||||||
depositTxIds.push(fetchedTrade.getMakerDepositTxId());
|
depositTxIds.push(fetchedTrade.getMakerDepositTxId());
|
||||||
depositTxIds.push(fetchedTrade.getTakerDepositTxId());
|
depositTxIds.push(fetchedTrade.getTakerDepositTxId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// mine until deposit txs unlock
|
// mine until deposit txs unlock
|
||||||
HavenoUtils.log(1, "Mining to unlock deposit txs");
|
|
||||||
await waitForUnlockedTxs(...depositTxIds);
|
await waitForUnlockedTxs(...depositTxIds);
|
||||||
HavenoUtils.log(1, "Done mining to unlock deposit txs");
|
|
||||||
|
|
||||||
// open disputes
|
// open disputes
|
||||||
HavenoUtils.log(1, "Opening disputes");
|
HavenoUtils.log(1, "Opening disputes");
|
||||||
await bob.openDispute(trades[0].getTradeId());
|
await Promise.all([
|
||||||
await alice.openDispute(trades[1].getTradeId());
|
bob.openDispute(trades[0].getTradeId()),
|
||||||
await bob.openDispute(trades[2].getTradeId());
|
alice.openDispute(trades[1].getTradeId()),
|
||||||
await alice.openDispute(trades[3].getTradeId());
|
bob.openDispute(trades[2].getTradeId()),
|
||||||
|
alice.openDispute(trades[3].getTradeId())
|
||||||
|
]);
|
||||||
|
HavenoUtils.log(1, "Done opening disputes");
|
||||||
|
|
||||||
// test dispute
|
// test dispute state
|
||||||
let bobDispute = await bob.getDispute(trades[0].getTradeId());
|
let bobDispute = await bob.getDispute(trades[0].getTradeId());
|
||||||
expect(bobDispute.getTradeId()).toEqual(trades[0].getTradeId());
|
expect(bobDispute.getTradeId()).toEqual(trades[0].getTradeId());
|
||||||
expect(bobDispute.getIsOpener()).toBe(true);
|
expect(bobDispute.getIsOpener()).toBe(true);
|
||||||
@ -1090,7 +1103,7 @@ test("Can resolve disputes", async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// alice sees the dispute
|
// alice sees the dispute
|
||||||
await wait(TestConfig.maxTimePeerNoticeMs * 2);
|
await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs);
|
||||||
let aliceDispute = await alice.getDispute(trades[0].getTradeId());
|
let aliceDispute = await alice.getDispute(trades[0].getTradeId());
|
||||||
expect(aliceDispute.getTradeId()).toEqual(trades[0].getTradeId());
|
expect(aliceDispute.getTradeId()).toEqual(trades[0].getTradeId());
|
||||||
expect(aliceDispute.getIsOpener()).toBe(false);
|
expect(aliceDispute.getIsOpener()).toBe(false);
|
||||||
@ -1109,6 +1122,7 @@ test("Can resolve disputes", async () => {
|
|||||||
await arbitrator.sendDisputeChatMessage(arbAliceDispute!.getId(), "Arbitrator chat message to Alice", []);
|
await arbitrator.sendDisputeChatMessage(arbAliceDispute!.getId(), "Arbitrator chat message to Alice", []);
|
||||||
|
|
||||||
// alice and bob reply to arbitrator chat messages
|
// alice and bob reply to arbitrator chat messages
|
||||||
|
await wait(TestConfig.maxTimePeerNoticeMs); // wait for arbitrator's message to arrive
|
||||||
let attachment = new Attachment();
|
let attachment = new Attachment();
|
||||||
let bytes = new Uint8Array(Buffer.from("Proof Bob was scammed", "utf8"));
|
let bytes = new Uint8Array(Buffer.from("Proof Bob was scammed", "utf8"));
|
||||||
attachment.setBytes(bytes);
|
attachment.setBytes(bytes);
|
||||||
@ -1117,8 +1131,10 @@ test("Can resolve disputes", async () => {
|
|||||||
let bytes2 = new Uint8Array(Buffer.from("picture bytes", "utf8"));
|
let bytes2 = new Uint8Array(Buffer.from("picture bytes", "utf8"));
|
||||||
attachment2.setBytes(bytes2);
|
attachment2.setBytes(bytes2);
|
||||||
attachment2.setFileName("proof.png");
|
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 bob.sendDisputeChatMessage(bobDispute.getId(), "Bob chat message", [attachment, attachment2]);
|
||||||
await wait(1000); // make sure messages are sent in order
|
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", []);
|
await alice.sendDisputeChatMessage(aliceDispute.getId(), "Alice chat message", []);
|
||||||
|
|
||||||
// test alice and bob's chat messages
|
// test alice and bob's chat messages
|
||||||
@ -1161,13 +1177,14 @@ test("Can resolve disputes", async () => {
|
|||||||
expect(chatNotifications[1].getChatMessage()?.getMessage()).toEqual("Alice chat message");
|
expect(chatNotifications[1].getChatMessage()?.getMessage()).toEqual("Alice chat message");
|
||||||
|
|
||||||
// award trade amount to seller
|
// award trade amount to seller
|
||||||
HavenoUtils.log(1, "Awarding trade amount to seller");
|
HavenoUtils.log(1, "Awarding trade amount to seller, trade " + trades[0].getTradeId());
|
||||||
let bobBalancesBefore = await bob.getBalances();
|
let bobBalancesBefore = await bob.getBalances();
|
||||||
let aliceBalancesBefore = await alice.getBalances();
|
let aliceBalancesBefore = await alice.getBalances();
|
||||||
await arbitrator.resolveDispute(trades[0].getTradeId(), DisputeResult.Winner.SELLER, DisputeResult.Reason.PEER_WAS_LATE, "Seller is winner");
|
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
|
// dispute is resolved
|
||||||
await wait(TestConfig.maxTimePeerNoticeMs);
|
await wait(TestConfig.maxWalletStartupMs);
|
||||||
updatedDispute = await alice.getDispute(trades[0].getTradeId());
|
updatedDispute = await alice.getDispute(trades[0].getTradeId());
|
||||||
expect(updatedDispute.getIsClosed()).toBe(true);
|
expect(updatedDispute.getIsClosed()).toBe(true);
|
||||||
updatedDispute = await bob.getDispute(trades[0].getTradeId());
|
updatedDispute = await bob.getDispute(trades[0].getTradeId());
|
||||||
@ -1185,34 +1202,52 @@ test("Can resolve disputes", async () => {
|
|||||||
expect(bobDifference).toEqual(winnerPayout);
|
expect(bobDifference).toEqual(winnerPayout);
|
||||||
|
|
||||||
// award trade amount to buyer
|
// award trade amount to buyer
|
||||||
HavenoUtils.log(1, "Awarding trade amount to buyer");
|
HavenoUtils.log(1, "Awarding trade amount to buyer, trade " + trades[1].getTradeId());
|
||||||
aliceBalancesBefore = await alice.getBalances();
|
aliceBalancesBefore = await alice.getBalances();
|
||||||
bobBalancesBefore = await bob.getBalances();
|
bobBalancesBefore = await bob.getBalances();
|
||||||
await arbitrator.resolveDispute(trades[1].getTradeId(), DisputeResult.Winner.BUYER, DisputeResult.Reason.SELLER_NOT_RESPONDING, "Buyer is winner");
|
await arbitrator.resolveDispute(trades[1].getTradeId(), DisputeResult.Winner.BUYER, DisputeResult.Reason.SELLER_NOT_RESPONDING, "Buyer is winner");
|
||||||
await wait(TestConfig.walletSyncPeriodMs * 2);
|
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();
|
aliceBalancesAfter = await alice.getBalances();
|
||||||
bobBalancesAfter = await bob.getBalances();
|
bobBalancesAfter = await bob.getBalances();
|
||||||
aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance());
|
aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance());
|
||||||
bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance());
|
bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance());
|
||||||
winnerPayout = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[1].getBuyerSecurityDeposit());
|
winnerPayout = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[1].getBuyerSecurityDeposit());
|
||||||
loserPayout = HavenoUtils.centinerosToAtomicUnits(offers[1].getSellerSecurityDeposit());
|
loserPayout = HavenoUtils.centinerosToAtomicUnits(offers[1].getSellerSecurityDeposit());
|
||||||
|
if (aliceDifference !== winnerPayout || loserPayout - bobDifference > 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());
|
||||||
|
}
|
||||||
expect(aliceDifference).toEqual(winnerPayout);
|
expect(aliceDifference).toEqual(winnerPayout);
|
||||||
expect(loserPayout - bobDifference).toBeLessThan(TestConfig.maxFee);
|
expect(loserPayout - bobDifference).toBeLessThan(TestConfig.maxFee);
|
||||||
|
|
||||||
// award half of trade amount to buyer
|
// award half of trade amount to buyer
|
||||||
HavenoUtils.log(1, "Awarding 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());
|
let customWinnerAmount = tradeAmount / BigInt(2) + HavenoUtils.centinerosToAtomicUnits(offers[2].getBuyerSecurityDeposit());
|
||||||
aliceBalancesBefore = await alice.getBalances();
|
aliceBalancesBefore = await alice.getBalances();
|
||||||
bobBalancesBefore = await bob.getBalances();
|
bobBalancesBefore = await bob.getBalances();
|
||||||
await arbitrator.resolveDispute(trades[2].getTradeId(), DisputeResult.Winner.BUYER, DisputeResult.Reason.WRONG_SENDER_ACCOUNT, "Split trade amount", customWinnerAmount);
|
await arbitrator.resolveDispute(trades[2].getTradeId(), DisputeResult.Winner.BUYER, DisputeResult.Reason.WRONG_SENDER_ACCOUNT, "Split trade amount", customWinnerAmount);
|
||||||
await wait(TestConfig.walletSyncPeriodMs * 2);
|
HavenoUtils.log(1, "Done resolving dispute");
|
||||||
|
await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs * 2 + TestConfig.walletSyncPeriodMs);
|
||||||
aliceBalancesAfter = await alice.getBalances();
|
aliceBalancesAfter = await alice.getBalances();
|
||||||
bobBalancesAfter = await bob.getBalances();
|
bobBalancesAfter = await bob.getBalances();
|
||||||
aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance());
|
aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance());
|
||||||
bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance());
|
bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance());
|
||||||
loserPayout = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[2].getBuyerSecurityDeposit()) + HavenoUtils.centinerosToAtomicUnits(offers[2].getSellerSecurityDeposit()) - customWinnerAmount;
|
loserPayout = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[2].getBuyerSecurityDeposit()) + HavenoUtils.centinerosToAtomicUnits(offers[2].getSellerSecurityDeposit()) - customWinnerAmount;
|
||||||
|
if (aliceDifference !== customWinnerAmount || loserPayout - bobDifference > 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());
|
||||||
|
}
|
||||||
expect(aliceDifference).toEqual(customWinnerAmount);
|
expect(aliceDifference).toEqual(customWinnerAmount);
|
||||||
expect(loserPayout - bobDifference).toBeLessThan(TestConfig.maxFee);
|
expect(loserPayout - bobDifference).toBeLessThanOrEqual(TestConfig.maxFee);
|
||||||
|
|
||||||
// award too little to loser
|
// award too little to loser
|
||||||
customWinnerAmount = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[3].getBuyerSecurityDeposit()) + HavenoUtils.centinerosToAtomicUnits(offers[3].getSellerSecurityDeposit()) - BigInt("10000");
|
customWinnerAmount = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[3].getBuyerSecurityDeposit()) + HavenoUtils.centinerosToAtomicUnits(offers[3].getSellerSecurityDeposit()) - BigInt("10000");
|
||||||
@ -1224,18 +1259,26 @@ test("Can resolve disputes", async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// award full amount to seller
|
// award full amount to seller
|
||||||
HavenoUtils.log(1, "Awarding 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());
|
customWinnerAmount = tradeAmount + HavenoUtils.centinerosToAtomicUnits(offers[3].getBuyerSecurityDeposit()) + HavenoUtils.centinerosToAtomicUnits(offers[3].getSellerSecurityDeposit());
|
||||||
aliceBalancesBefore = await alice.getBalances();
|
aliceBalancesBefore = await alice.getBalances();
|
||||||
bobBalancesBefore = await bob.getBalances();
|
bobBalancesBefore = await bob.getBalances();
|
||||||
await arbitrator.resolveDispute(trades[3].getTradeId(), DisputeResult.Winner.SELLER, DisputeResult.Reason.TRADE_ALREADY_SETTLED, "Seller gets everything", customWinnerAmount);
|
await arbitrator.resolveDispute(trades[3].getTradeId(), DisputeResult.Winner.SELLER, DisputeResult.Reason.TRADE_ALREADY_SETTLED, "Seller gets everything", customWinnerAmount);
|
||||||
await wait(TestConfig.walletSyncPeriodMs * 2);
|
await wait(TestConfig.maxTimePeerNoticeMs + TestConfig.maxWalletStartupMs * 2 + TestConfig.walletSyncPeriodMs);
|
||||||
aliceBalancesAfter = await alice.getBalances();
|
aliceBalancesAfter = await alice.getBalances();
|
||||||
bobBalancesAfter = await bob.getBalances();
|
bobBalancesAfter = await bob.getBalances();
|
||||||
aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance());
|
aliceDifference = BigInt(aliceBalancesAfter.getBalance()) - BigInt(aliceBalancesBefore.getBalance());
|
||||||
bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance());
|
bobDifference = BigInt(bobBalancesAfter.getBalance()) - BigInt(bobBalancesBefore.getBalance());
|
||||||
expect(aliceDifference).toEqual(BigInt(0));
|
expect(aliceDifference).toEqual(BigInt(0));
|
||||||
expect(customWinnerAmount - bobDifference).toBeLessThan(TestConfig.maxFee);
|
if (customWinnerAmount - bobDifference > 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());
|
||||||
|
}
|
||||||
|
expect(customWinnerAmount - bobDifference).toBeLessThanOrEqual(TestConfig.maxFee);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Cannot make or take offer with insufficient unlocked funds", async () => {
|
test("Cannot make or take offer with insufficient unlocked funds", async () => {
|
||||||
@ -1398,7 +1441,7 @@ test("Handles unexpected errors during trade initialization", async () => {
|
|||||||
|
|
||||||
// trader 1 tries to take offer
|
// trader 1 tries to take offer
|
||||||
try {
|
try {
|
||||||
HavenoUtils.log(1, "Trader 1 taking offer");
|
HavenoUtils.log(1, "Trader 1 taking offer " + offer.getId());
|
||||||
await traders[1].takeOffer(offer.getId(), paymentAccount.getId());
|
await traders[1].takeOffer(offer.getId(), paymentAccount.getId());
|
||||||
throw new Error("Should have failed taking offer because taker trade funds spent")
|
throw new Error("Should have failed taking offer because taker trade funds spent")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -1411,8 +1454,8 @@ test("Handles unexpected errors during trade initialization", async () => {
|
|||||||
await wait(10000); // give time for trade initialization to fail and offer to become available
|
await wait(10000); // give time for trade initialization to fail and offer to become available
|
||||||
offer = await traders[0].getMyOffer(offer.getId());
|
offer = await traders[0].getMyOffer(offer.getId());
|
||||||
if (offer.getState() !== "AVAILABLE") {
|
if (offer.getState() !== "AVAILABLE") {
|
||||||
HavenoUtils.log(1, "Offer is not yet available, waiting to become available after timeout..."); // there is no error notice if peer stops responding
|
HavenoUtils.log(1, "Offer is not yet available, waiting to become available after timeout..."); // TODO (woodser): fail trade on nack during initialization to save a bunch of time
|
||||||
await wait(25000); // give another 25 seconds to become available
|
await wait(TestConfig.tradeInitTimeout - 10000); // wait remaining time for offer to become available after timeout
|
||||||
offer = await traders[0].getMyOffer(offer.getId());
|
offer = await traders[0].getMyOffer(offer.getId());
|
||||||
assert.equal(offer.getState(), "AVAILABLE");
|
assert.equal(offer.getState(), "AVAILABLE");
|
||||||
}
|
}
|
||||||
@ -1583,6 +1626,14 @@ async function initFundingWallet() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function startMining() {
|
||||||
|
try {
|
||||||
|
await monerod.startMining(await fundingWallet.getPrimaryAddress(), 3);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.message !== "Already mining") throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for unlocked balance in wallet or Haveno daemon.
|
* Wait for unlocked balance in wallet or Haveno daemon.
|
||||||
*/
|
*/
|
||||||
@ -1639,7 +1690,7 @@ async function waitForUnlockedBalance(amount: bigint, ...wallets: any[]) {
|
|||||||
if (!miningNeeded) return;
|
if (!miningNeeded) return;
|
||||||
|
|
||||||
// wait for funds to unlock
|
// wait for funds to unlock
|
||||||
console.log("Mining for unlocked balance of " + amount);
|
HavenoUtils.log(0, "Mining for unlocked balance of " + amount);
|
||||||
await startMining();
|
await startMining();
|
||||||
let promises: Promise<void>[] = [];
|
let promises: Promise<void>[] = [];
|
||||||
for (let wallet of wallets) {
|
for (let wallet of wallets) {
|
||||||
@ -1655,10 +1706,12 @@ async function waitForUnlockedBalance(amount: bigint, ...wallets: any[]) {
|
|||||||
}
|
}
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
await monerod.stopMining();
|
await monerod.stopMining();
|
||||||
console.log("Funds unlocked, done mining");
|
HavenoUtils.log(0, "Funds unlocked, done mining");
|
||||||
};
|
};
|
||||||
|
|
||||||
async function waitForUnlockedTxs(...txHashes: string[]) {
|
async function waitForUnlockedTxs(...txHashes: string[]) {
|
||||||
|
if (txHashes.length === 0) return;
|
||||||
|
HavenoUtils.log(1, "Mining to unlock txs");
|
||||||
await startMining();
|
await startMining();
|
||||||
let promises: Promise<void>[] = [];
|
let promises: Promise<void>[] = [];
|
||||||
for (let txHash of txHashes) {
|
for (let txHash of txHashes) {
|
||||||
@ -1675,15 +1728,68 @@ async function waitForUnlockedTxs(...txHashes: string[]) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
HavenoUtils.log(1, "Done mining to unlock txs");
|
||||||
await monerod.stopMining();
|
await monerod.stopMining();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startMining() {
|
/**
|
||||||
try {
|
* Indicates if the wallet has an unlocked amount.
|
||||||
await monerod.startMining(await fundingWallet.getPrimaryAddress(), 3);
|
*
|
||||||
} catch (err) {
|
* @param {MoneroWallet} wallet - wallet to check
|
||||||
if (err.message !== "Already mining") throw err;
|
* @param {BigInt} amt - amount to check
|
||||||
|
* @param {number?} numOutputs - number of outputs of the given amount (default 1)
|
||||||
|
*/
|
||||||
|
async function hasUnlockedOutputs(wallet: any, amt: BigInt, numOutputs?: number): Promise<boolean> {
|
||||||
|
if (numOutputs === undefined) numOutputs = 1;
|
||||||
|
let availableOutputs = await wallet.getOutputs({isSpent: false, isFrozen: false, minAmount: monerojs.BigInteger(amt.toString()), txQuery: {isLocked: false}});
|
||||||
|
return availableOutputs.length >= numOutputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fund the given wallets.
|
||||||
|
*
|
||||||
|
* @param {MoneroWallet} wallets - monerojs wallets
|
||||||
|
* @param {BigInt} amt - the amount to fund
|
||||||
|
* @param {number?} numOutputs - the number of outputs of the given amount (default 1)
|
||||||
|
*/
|
||||||
|
async function fundWallets(wallets: any[], amt: BigInt, numOutputs?: number): Promise<void> {
|
||||||
|
if (numOutputs === undefined) numOutputs = 1;
|
||||||
|
|
||||||
|
// collect destinations
|
||||||
|
let destinations = [];
|
||||||
|
for (let wallet of wallets) {
|
||||||
|
if (await hasUnlockedOutputs(wallet, amt, numOutputs)) continue;
|
||||||
|
for (let i = 0; i < numOutputs; i++) {
|
||||||
|
destinations.push(new MoneroDestination((await wallet.createSubaddress()).getAddress(), monerojs.BigInteger(amt.toString())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fund destinations
|
||||||
|
let txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(true);
|
||||||
|
let txHashes: string[] = [];
|
||||||
|
let sendAmt = BigInteger("0");
|
||||||
|
for (let i = 0; i < destinations.length; i++) {
|
||||||
|
txConfig.addDestination(destinations[i]);
|
||||||
|
sendAmt = sendAmt.add(destinations[i].getAmount());
|
||||||
|
if (i === destinations.length - 1 || (i > 0 && i % 15 === 0)) {
|
||||||
|
await waitForUnlockedBalance(toBigInt(sendAmt), fundingWallet);
|
||||||
|
txHashes.push((await fundingWallet.createTx(txConfig)).getHash());
|
||||||
|
txConfig = new MoneroTxConfig().setAccountIndex(0).setRelay(true);
|
||||||
|
sendAmt = BigInteger("0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for txs to unlock
|
||||||
|
if (txHashes.length > 0) {
|
||||||
|
await waitForUnlockedTxs(...txHashes);
|
||||||
|
await wait(1000);
|
||||||
|
for (let wallet of wallets) await wallet.sync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert monero-javascript BigInteger to typescript BigInt
|
||||||
|
function toBigInt(mjsBigInt: any) {
|
||||||
|
return BigInt(mjsBigInt.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function wait(durationMs: number) {
|
async function wait(durationMs: number) {
|
||||||
@ -1836,10 +1942,6 @@ async function postOffer(maker: HavenoDaemon, config?: any) {
|
|||||||
config.triggerPrice);
|
config.triggerPrice);
|
||||||
testOffer(offer, config);
|
testOffer(offer, config);
|
||||||
|
|
||||||
// unlocked balance has decreased
|
|
||||||
let unlockedBalanceAfter: bigint = BigInt((await maker.getBalances()).getUnlockedBalance());
|
|
||||||
if (unlockedBalanceAfter === unlockedBalanceBefore) throw new Error("unlocked balance did not change after posting offer");
|
|
||||||
|
|
||||||
// offer is included in my offers only
|
// offer is included in my offers only
|
||||||
if (!getOffer(await maker.getMyOffers(config.assetCode, config.direction), offer.getId())) {
|
if (!getOffer(await maker.getMyOffers(config.assetCode, config.direction), offer.getId())) {
|
||||||
await wait(10000);
|
await wait(10000);
|
||||||
@ -1847,6 +1949,10 @@ async function postOffer(maker: HavenoDaemon, config?: any) {
|
|||||||
}
|
}
|
||||||
if (getOffer(await maker.getOffers(config.assetCode, config.direction), offer.getId())) throw new Error("My offer " + offer.getId() + " should not appear in available offers");
|
if (getOffer(await maker.getOffers(config.assetCode, config.direction), offer.getId())) throw new Error("My offer " + offer.getId() + " should not appear in available offers");
|
||||||
|
|
||||||
|
// unlocked balance has decreased
|
||||||
|
let unlockedBalanceAfter: bigint = BigInt((await maker.getBalances()).getUnlockedBalance());
|
||||||
|
if (unlockedBalanceAfter === unlockedBalanceBefore) throw new Error("unlocked balance did not change after posting offer");
|
||||||
|
|
||||||
return offer;
|
return offer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1948,7 +2054,7 @@ async function testTradeChat(tradeId: string, alice: HavenoDaemon, bob: HavenoDa
|
|||||||
offset = 1; // 1 existing notification
|
offset = 1; // 1 existing notification
|
||||||
expect(chatNotifications.length).toBe(offset + msgs.length);
|
expect(chatNotifications.length).toBe(offset + msgs.length);
|
||||||
expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual(aliceMsg);
|
expect(chatNotifications[0].getChatMessage()?.getMessage()).toEqual(aliceMsg);
|
||||||
for (var i = 0; i < msgs.length; i++) {
|
for (let i = 0; i < msgs.length; i++) {
|
||||||
expect(chatNotifications[i + offset].getChatMessage()?.getMessage()).toEqual(msgs[i]);
|
expect(chatNotifications[i + offset].getChatMessage()?.getMessage()).toEqual(msgs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -679,7 +679,7 @@ class HavenoDaemon {
|
|||||||
async getBalances(): Promise<XmrBalanceInfo> {
|
async getBalances(): Promise<XmrBalanceInfo> {
|
||||||
let that = this;
|
let that = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
that._walletsClient.getBalances(new GetBalancesRequest(), {password: that._password}, function(err: grpcWeb.RpcError, response: GetBalancesReply) {
|
that._walletsClient.getBalances(new GetBalancesRequest().setCurrencyCode("XMR"), {password: that._password}, function(err: grpcWeb.RpcError, response: GetBalancesReply) {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
else resolve(response.getBalances()!.getXmr()!);
|
else resolve(response.getBalances()!.getXmr()!);
|
||||||
});
|
});
|
||||||
@ -1018,7 +1018,7 @@ class HavenoDaemon {
|
|||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
that._tradesClient.takeOffer(request, {password: that._password}, function(err: grpcWeb.RpcError, response: TakeOfferReply) {
|
that._tradesClient.takeOffer(request, {password: that._password}, function(err: grpcWeb.RpcError, response: TakeOfferReply) {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
else if (response.getFailureReason() && response.getFailureReason()!.getAvailabilityResult() !== AvailabilityResult.AVAILABLE) reject(response.getFailureReason()!.getDescription());
|
else if (response.getFailureReason() && response.getFailureReason()!.getAvailabilityResult() !== AvailabilityResult.AVAILABLE) reject(new Error(response.getFailureReason()!.getDescription())); // TODO: api should throw grpcWeb.RpcError
|
||||||
else resolve(response.getTrade()!);
|
else resolve(response.getTrade()!);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,19 +6,10 @@ const console = require('console');
|
|||||||
*/
|
*/
|
||||||
class HavenoUtils {
|
class HavenoUtils {
|
||||||
|
|
||||||
static LOG_LEVEL = 0;
|
static logLevel = 0;
|
||||||
static CENTINEROS_AU_MULTIPLIER = 10000;
|
static centinerosToAUMultiplier = 10000;
|
||||||
|
static months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
||||||
/**
|
static lastLogTimeMs = 0;
|
||||||
* Log a message.
|
|
||||||
*
|
|
||||||
* @param {int} level - log level of the message
|
|
||||||
* @param {string} msg - message to log
|
|
||||||
*/
|
|
||||||
static log(level: number, msg: string) {
|
|
||||||
assert(level === parseInt(level + "", 10) && level >= 0, "Log level must be an integer >= 0");
|
|
||||||
if (HavenoUtils.LOG_LEVEL >= level) console.log(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the log level with 0 being least verbose.
|
* Set the log level with 0 being least verbose.
|
||||||
@ -27,7 +18,7 @@ class HavenoUtils {
|
|||||||
*/
|
*/
|
||||||
static async setLogLevel(level: number) {
|
static async setLogLevel(level: number) {
|
||||||
assert(level === parseInt(level + "", 10) && level >= 0, "Log level must be an integer >= 0");
|
assert(level === parseInt(level + "", 10) && level >= 0, "Log level must be an integer >= 0");
|
||||||
HavenoUtils.LOG_LEVEL = level;
|
HavenoUtils.logLevel = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,7 +27,35 @@ class HavenoUtils {
|
|||||||
* @return {int} the current log level
|
* @return {int} the current log level
|
||||||
*/
|
*/
|
||||||
static getLogLevel(): number {
|
static getLogLevel(): number {
|
||||||
return HavenoUtils.LOG_LEVEL;
|
return HavenoUtils.logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message. // TODO (woodser): switch to log library?
|
||||||
|
*
|
||||||
|
* @param {int} level - log level of the message
|
||||||
|
* @param {string} msg - message to log
|
||||||
|
* @param {boolean?} warn - log the message as a warning if true
|
||||||
|
*/
|
||||||
|
static log(level: number, msg: string) {
|
||||||
|
assert(level === parseInt(level + "", 10) && level >= 0, "Log level must be an integer >= 0");
|
||||||
|
if (HavenoUtils.logLevel >= level) {
|
||||||
|
let now = Date.now();
|
||||||
|
let formattedTimeSinceLastLog = HavenoUtils.lastLogTimeMs ? " (+" + (now - HavenoUtils.lastLogTimeMs) + " ms)" : "\t";
|
||||||
|
HavenoUtils.lastLogTimeMs = now;
|
||||||
|
console.log(HavenoUtils.formatTimestamp(now) + formattedTimeSinceLastLog + "\t[L" + level + "] " + msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a timestamp as e.g. Jul-07 hh:mm:ss:ms. // TODO: move to GenUtils?
|
||||||
|
*
|
||||||
|
* @param {number} timestamp - the timestamp in milliseconds to format
|
||||||
|
* @return {string} the formatted timestamp
|
||||||
|
*/
|
||||||
|
static formatTimestamp(timestamp: number): string {
|
||||||
|
let date = new Date(timestamp);
|
||||||
|
return HavenoUtils.months[date.getMonth()] + "-" + date.getDate() + " " + date.getHours() + ':' + ("0" + date.getMinutes()).substr(-2) + ':' + ("0" + date.getSeconds()).substr(-2) + ':' + ("0" + date.getMilliseconds()).substr(-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,7 +81,7 @@ class HavenoUtils {
|
|||||||
* @return {BigInt} the amount denominated in atomic units
|
* @return {BigInt} the amount denominated in atomic units
|
||||||
*/
|
*/
|
||||||
static centinerosToAtomicUnits(centineros: number): bigint {
|
static centinerosToAtomicUnits(centineros: number): bigint {
|
||||||
return BigInt(centineros) * BigInt(HavenoUtils.CENTINEROS_AU_MULTIPLIER);
|
return BigInt(centineros) * BigInt(HavenoUtils.centinerosToAUMultiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user