mirror of
https://github.com/haveno-dex/haveno-ts.git
synced 2025-06-29 00:57:08 -04:00
Add monero connections manager
This commit is contained in:
parent
be86aafeff
commit
a27fa770ec
2 changed files with 429 additions and 73 deletions
|
@ -4,9 +4,11 @@
|
||||||
import {HavenoDaemon} from "./HavenoDaemon";
|
import {HavenoDaemon} from "./HavenoDaemon";
|
||||||
import {HavenoUtils} from "./utils/HavenoUtils";
|
import {HavenoUtils} from "./utils/HavenoUtils";
|
||||||
import * as grpcWeb from 'grpc-web';
|
import * as grpcWeb from 'grpc-web';
|
||||||
import {MarketPriceInfo, NotificationMessage, OfferInfo, TradeInfo, XmrBalanceInfo} from './protobuf/grpc_pb'; // TODO (woodser): better names; haveno_grpc_pb, haveno_pb
|
import {MarketPriceInfo, NotificationMessage, OfferInfo, TradeInfo, UriConnection, XmrBalanceInfo} from './protobuf/grpc_pb'; // TODO (woodser): better names; haveno_grpc_pb, haveno_pb
|
||||||
import {PaymentAccount} from './protobuf/pb_pb';
|
import {PaymentAccount} from './protobuf/pb_pb';
|
||||||
import {XmrDestination, XmrTx, XmrIncomingTransfer, XmrOutgoingTransfer} from './protobuf/grpc_pb';
|
import {XmrDestination, XmrTx, XmrIncomingTransfer, XmrOutgoingTransfer} from './protobuf/grpc_pb';
|
||||||
|
import AuthenticationStatus = UriConnection.AuthenticationStatus;
|
||||||
|
import OnlineStatus = UriConnection.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
|
||||||
|
@ -29,17 +31,24 @@ const TestConfig = {
|
||||||
level: 0, // set log level (gets more verbose increasing from 0)
|
level: 0, // set log level (gets more verbose increasing from 0)
|
||||||
logProcessOutput: false // enable or disable logging process output
|
logProcessOutput: false // enable or disable logging process output
|
||||||
},
|
},
|
||||||
|
moneroBinsDir: "../haveno/.localnet",
|
||||||
|
networkType: monerojs.MoneroNetworkType.STAGENET,
|
||||||
haveno: {
|
haveno: {
|
||||||
path: "../haveno",
|
path: "../haveno",
|
||||||
version: "1.6.2"
|
version: "1.6.2"
|
||||||
},
|
},
|
||||||
monerod: {
|
monerod: {
|
||||||
url: "http://localhost:38081",
|
uri: "http://localhost:38081",
|
||||||
|
username: "superuser",
|
||||||
|
password: "abctesting123"
|
||||||
|
},
|
||||||
|
monerod2: {
|
||||||
|
uri: "http://localhost:58081",
|
||||||
username: "superuser",
|
username: "superuser",
|
||||||
password: "abctesting123"
|
password: "abctesting123"
|
||||||
},
|
},
|
||||||
fundingWallet: {
|
fundingWallet: {
|
||||||
url: "http://localhost:38084",
|
uri: "http://localhost:38084",
|
||||||
username: "rpc_user",
|
username: "rpc_user",
|
||||||
password: "abc123",
|
password: "abc123",
|
||||||
defaultPath: "test_funding_wallet",
|
defaultPath: "test_funding_wallet",
|
||||||
|
@ -48,7 +57,7 @@ const TestConfig = {
|
||||||
arbitrator: {
|
arbitrator: {
|
||||||
logProcessOutput: false,
|
logProcessOutput: false,
|
||||||
appName: "haveno-XMR_STAGENET_arbitrator",
|
appName: "haveno-XMR_STAGENET_arbitrator",
|
||||||
url: "http://localhost:8079",
|
uri: "http://localhost:8079",
|
||||||
password: "apitest",
|
password: "apitest",
|
||||||
walletUsername: "rpc_user",
|
walletUsername: "rpc_user",
|
||||||
walletPassword: "abc123"
|
walletPassword: "abc123"
|
||||||
|
@ -56,20 +65,21 @@ const TestConfig = {
|
||||||
alice: {
|
alice: {
|
||||||
logProcessOutput: false,
|
logProcessOutput: false,
|
||||||
appName: "haveno-XMR_STAGENET_alice",
|
appName: "haveno-XMR_STAGENET_alice",
|
||||||
url: "http://localhost:8080",
|
uri: "http://localhost:8080",
|
||||||
password: "apitest",
|
password: "apitest",
|
||||||
walletUrl: "http://127.0.0.1:38091",
|
walletUri: "http://127.0.0.1:38091",
|
||||||
walletUsername: "rpc_user",
|
walletUsername: "rpc_user",
|
||||||
walletPassword: "abc123"
|
walletPassword: "abc123"
|
||||||
},
|
},
|
||||||
bob: {
|
bob: {
|
||||||
logProcessOutput: false,
|
logProcessOutput: false,
|
||||||
appName: "haveno-XMR_STAGENET_bob",
|
appName: "haveno-XMR_STAGENET_bob",
|
||||||
url: "http://localhost:8081",
|
uri: "http://localhost:8081",
|
||||||
password: "apitest",
|
password: "apitest",
|
||||||
},
|
},
|
||||||
maxFee: BigInt("75000000000"),
|
maxFee: BigInt("75000000000"),
|
||||||
walletSyncPeriodMs: 5000,
|
walletSyncPeriodMs: 5000, // TODO (woodser): auto adjust higher if using remote connection
|
||||||
|
daemonPollPeriodMs: 15000,
|
||||||
maxTimePeerNoticeMs: 3000,
|
maxTimePeerNoticeMs: 3000,
|
||||||
cryptoAccounts: [{ // TODO (woodser): test other cryptos, fiat
|
cryptoAccounts: [{ // TODO (woodser): test other cryptos, fiat
|
||||||
currencyCode: "ETH",
|
currencyCode: "ETH",
|
||||||
|
@ -133,8 +143,8 @@ beforeAll(async () => {
|
||||||
await arbitrator.registerDisputeAgent("refundagent", TestConfig.devPrivilegePrivKey);
|
await arbitrator.registerDisputeAgent("refundagent", TestConfig.devPrivilegePrivKey);
|
||||||
|
|
||||||
// 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.uri, TestConfig.monerod.username, TestConfig.monerod.password);
|
||||||
aliceWallet = await monerojs.connectToWalletRpc(TestConfig.alice.walletUrl, TestConfig.alice.walletUsername, TestConfig.alice.walletPassword);
|
aliceWallet = await monerojs.connectToWalletRpc(TestConfig.alice.walletUri, TestConfig.alice.walletUsername, TestConfig.alice.walletPassword);
|
||||||
|
|
||||||
// initialize funding wallet
|
// initialize funding wallet
|
||||||
await initFundingWallet();
|
await initFundingWallet();
|
||||||
|
@ -158,7 +168,7 @@ afterAll(async () => {
|
||||||
return Promise.all(stopPromises);
|
return Promise.all(stopPromises);
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.setTimeout(400000);
|
jest.setTimeout(500000);
|
||||||
|
|
||||||
// ----------------------------------- TESTS ----------------------------------
|
// ----------------------------------- TESTS ----------------------------------
|
||||||
|
|
||||||
|
@ -189,13 +199,13 @@ test("Can register as dispute agents", async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Can receive push notifications", async () => {
|
test("Can receive push notifications", async () => {
|
||||||
|
|
||||||
// add notification listener
|
// add notification listener
|
||||||
let notifications: NotificationMessage[] = [];
|
let notifications: NotificationMessage[] = [];
|
||||||
await alice.addNotificationListener(notification => {
|
await alice.addNotificationListener(notification => {
|
||||||
notifications.push(notification);
|
notifications.push(notification);
|
||||||
});
|
});
|
||||||
|
|
||||||
// send test notification
|
// send test notification
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
await alice._sendNotification(new NotificationMessage()
|
await alice._sendNotification(new NotificationMessage()
|
||||||
|
@ -203,7 +213,7 @@ test("Can receive push notifications", async () => {
|
||||||
.setTitle("Test title")
|
.setTitle("Test title")
|
||||||
.setMessage("Test message"));
|
.setMessage("Test message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// test notification
|
// test notification
|
||||||
await wait(1000);
|
await wait(1000);
|
||||||
assert.equal(3, notifications.length);
|
assert.equal(3, notifications.length);
|
||||||
|
@ -214,37 +224,152 @@ test("Can receive push notifications", async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Can get market prices", async () => {
|
test("Can manage Monero daemon connections", async () => {
|
||||||
|
let monerod2: any;
|
||||||
// get all market prices
|
let charlie: HavenoDaemon | undefined;
|
||||||
let prices: MarketPriceInfo[] = await alice.getPrices();
|
let err: any;
|
||||||
expect(prices.length).toBeGreaterThan(1);
|
try {
|
||||||
for (let price of prices) {
|
|
||||||
expect(price.getCurrencyCode().length).toBeGreaterThan(0);
|
|
||||||
expect(price.getPrice()).toBeGreaterThanOrEqual(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get market prices of specific currencies
|
|
||||||
for (let testAccount of TestConfig.cryptoAccounts) {
|
|
||||||
let price = await alice.getPrice(testAccount.currencyCode);
|
|
||||||
expect(price).toBeGreaterThan(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// test that prices are reasonable
|
|
||||||
let usd = await alice.getPrice("USD");
|
|
||||||
expect(usd).toBeGreaterThan(50);
|
|
||||||
expect(usd).toBeLessThan(5000);
|
|
||||||
let doge = await alice.getPrice("DOGE");
|
|
||||||
expect(doge).toBeGreaterThan(200)
|
|
||||||
expect(doge).toBeLessThan(20000);
|
|
||||||
let btc = await alice.getPrice("BTC");
|
|
||||||
expect(btc).toBeGreaterThan(0.0004)
|
|
||||||
expect(btc).toBeLessThan(0.4);
|
|
||||||
|
|
||||||
// test invalid currency
|
// start charlie
|
||||||
await expect(async () => {await alice.getPrice("INVALID_CURRENCY")})
|
charlie = await startHavenoProcess(undefined, TestConfig.logging.logProcessOutput);
|
||||||
.rejects
|
|
||||||
.toThrow('Currency not found: INVALID_CURRENCY');
|
// test default connections
|
||||||
|
let monerodUri1 = "http://localhost:38081"; // TODO: (woodser): move to config
|
||||||
|
let monerodUri2 = "http://haveno.exchange:38081";
|
||||||
|
let connections: UriConnection[] = await charlie.getMoneroConnections();
|
||||||
|
testConnection(getConnection(connections, monerodUri1)!, monerodUri1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1);
|
||||||
|
testConnection(getConnection(connections, monerodUri2)!, monerodUri2, OnlineStatus.UNKNOWN, AuthenticationStatus.NO_AUTHENTICATION, 2);
|
||||||
|
|
||||||
|
// test default connection
|
||||||
|
let connection: UriConnection | undefined = await charlie.getMoneroConnection();
|
||||||
|
testConnection(connection!, monerodUri1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1);
|
||||||
|
//assert(await charlie.isMoneroConnected()); // TODO (woodser): support havenod.isConnected()?
|
||||||
|
|
||||||
|
// add a new connection
|
||||||
|
let fooBarUri = "http://foo.bar";
|
||||||
|
await charlie.addMoneroConnection(fooBarUri);
|
||||||
|
connections = await charlie.getMoneroConnections();
|
||||||
|
connection = getConnection(connections, fooBarUri);
|
||||||
|
testConnection(connection!, fooBarUri, OnlineStatus.UNKNOWN, AuthenticationStatus.NO_AUTHENTICATION, 0);
|
||||||
|
//connection = await charlie.getMoneroConnection(uri); TODO (woodser): allow getting connection by uri?
|
||||||
|
|
||||||
|
// set prioritized connection without credentials
|
||||||
|
await charlie.setMoneroConnection(new UriConnection()
|
||||||
|
.setUri(TestConfig.monerod2.uri)
|
||||||
|
.setPriority(1));
|
||||||
|
connection = await charlie.getMoneroConnection();
|
||||||
|
testConnection(connection!, TestConfig.monerod2.uri, undefined, undefined, 1); // status may or may not be known due to periodic connection checking
|
||||||
|
|
||||||
|
// connection is offline
|
||||||
|
connection = await charlie.checkMoneroConnection();
|
||||||
|
testConnection(connection!, TestConfig.monerod2.uri, OnlineStatus.OFFLINE, AuthenticationStatus.NO_AUTHENTICATION, 1);
|
||||||
|
|
||||||
|
// start monerod2
|
||||||
|
let cmd = [
|
||||||
|
TestConfig.moneroBinsDir + "/monerod",
|
||||||
|
"--" + monerojs.MoneroNetworkType.toString(TestConfig.networkType).toLowerCase(),
|
||||||
|
"--no-igd",
|
||||||
|
"--hide-my-port",
|
||||||
|
"--data-dir", TestConfig.moneroBinsDir + "/node1",
|
||||||
|
"--p2p-bind-port", "58080",
|
||||||
|
"--rpc-bind-port", "58081",
|
||||||
|
"--rpc-login", "superuser:abctesting123",
|
||||||
|
"--zmq-rpc-bind-port", "58082"
|
||||||
|
];
|
||||||
|
monerod2 = await monerojs.connectToDaemonRpc(cmd);
|
||||||
|
|
||||||
|
// connection is online and not authenticated
|
||||||
|
connection = await charlie.checkMoneroConnection();
|
||||||
|
testConnection(connection!, TestConfig.monerod2.uri, OnlineStatus.ONLINE, AuthenticationStatus.NOT_AUTHENTICATED, 1);
|
||||||
|
|
||||||
|
// set connection credentials
|
||||||
|
await charlie.setMoneroConnection(new UriConnection()
|
||||||
|
.setUri(TestConfig.monerod2.uri)
|
||||||
|
.setUsername(TestConfig.monerod2.username)
|
||||||
|
.setPassword(TestConfig.monerod2.password)
|
||||||
|
.setPriority(1));
|
||||||
|
connection = await charlie.getMoneroConnection();
|
||||||
|
testConnection(connection!, TestConfig.monerod2.uri, undefined, undefined, 1);
|
||||||
|
|
||||||
|
// connection is online and authenticated
|
||||||
|
connection = await charlie.checkMoneroConnection();
|
||||||
|
testConnection(connection!, TestConfig.monerod2.uri, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1);
|
||||||
|
|
||||||
|
// restart charlie
|
||||||
|
let appName = charlie.getAppName();
|
||||||
|
await stopHavenoProcess(charlie);
|
||||||
|
charlie = await startHavenoProcess(appName, TestConfig.logging.logProcessOutput);
|
||||||
|
|
||||||
|
// connection is restored, online, and authenticated
|
||||||
|
connection = await charlie.getMoneroConnection();
|
||||||
|
testConnection(connection!, TestConfig.monerod2.uri, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1);
|
||||||
|
connections = await charlie.getMoneroConnections();
|
||||||
|
testConnection(getConnection(connections, monerodUri1)!, monerodUri1, OnlineStatus.UNKNOWN, AuthenticationStatus.NO_AUTHENTICATION, 1);
|
||||||
|
|
||||||
|
// enable auto switch
|
||||||
|
await charlie.setAutoSwitch(true);
|
||||||
|
|
||||||
|
// stop monerod
|
||||||
|
await monerod2.stopProcess();
|
||||||
|
|
||||||
|
// test auto switch after periodic connection check
|
||||||
|
await wait(TestConfig.daemonPollPeriodMs);
|
||||||
|
connection = await charlie.getMoneroConnection();
|
||||||
|
testConnection(connection!, monerodUri1, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 1);
|
||||||
|
|
||||||
|
// stop checking connection periodically
|
||||||
|
await charlie.stopCheckingConnection();
|
||||||
|
|
||||||
|
// remove current connection
|
||||||
|
await charlie.removeMoneroConnection(monerodUri1);
|
||||||
|
|
||||||
|
// check current connection
|
||||||
|
connection = await charlie.checkMoneroConnection();
|
||||||
|
assert.equal(undefined, connection);
|
||||||
|
|
||||||
|
// check all connections
|
||||||
|
await charlie.checkMoneroConnections();
|
||||||
|
connections = await charlie.getMoneroConnections();
|
||||||
|
testConnection(getConnection(connections, fooBarUri)!, fooBarUri, OnlineStatus.OFFLINE, AuthenticationStatus.NO_AUTHENTICATION, 0);
|
||||||
|
for (let connection of connections) testConnection(connection!, connection.getUri(), OnlineStatus.OFFLINE, AuthenticationStatus.NO_AUTHENTICATION);
|
||||||
|
|
||||||
|
// set connection to previous uri
|
||||||
|
await charlie.setMoneroConnection(fooBarUri);
|
||||||
|
connection = await charlie.getMoneroConnection();
|
||||||
|
testConnection(connection!, fooBarUri, OnlineStatus.OFFLINE, AuthenticationStatus.NO_AUTHENTICATION, 0);
|
||||||
|
|
||||||
|
// set connection to new uri
|
||||||
|
let fooBarUri2 = "http://foo.bar.xyz";
|
||||||
|
await charlie.setMoneroConnection(fooBarUri2);
|
||||||
|
connections = await charlie.getMoneroConnections();
|
||||||
|
connection = getConnection(connections, fooBarUri2);
|
||||||
|
testConnection(connection!, fooBarUri2, OnlineStatus.UNKNOWN, AuthenticationStatus.NO_AUTHENTICATION, 0);
|
||||||
|
|
||||||
|
// reset connection
|
||||||
|
await charlie.setMoneroConnection();
|
||||||
|
assert.equal(undefined, await charlie.getMoneroConnection());
|
||||||
|
|
||||||
|
// 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 UriConnection()
|
||||||
|
.setUri(TestConfig.monerod.uri)
|
||||||
|
.setUsername(TestConfig.monerod.username)
|
||||||
|
.setPassword(TestConfig.monerod.password)
|
||||||
|
.setPriority(2));
|
||||||
|
await wait(10000);
|
||||||
|
connection = await charlie.getMoneroConnection();
|
||||||
|
testConnection(connection!, TestConfig.monerod.uri, OnlineStatus.ONLINE, AuthenticationStatus.AUTHENTICATED, 2);
|
||||||
|
} catch (err2) {
|
||||||
|
err = err2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop processes
|
||||||
|
if (charlie) await stopHavenoProcess(charlie);
|
||||||
|
if (monerod2) await monerod2.stopProcess();
|
||||||
|
// TODO: how to delete trader app folder at end of test?
|
||||||
|
if (err) throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
// test wallet balances, transactions, deposit addresses, create and relay txs
|
// test wallet balances, transactions, deposit addresses, create and relay txs
|
||||||
|
@ -307,6 +432,39 @@ test("Can get balances", async () => {
|
||||||
expect(BigInt(balances.getReservedTradeBalance())).toBeGreaterThanOrEqual(0);
|
expect(BigInt(balances.getReservedTradeBalance())).toBeGreaterThanOrEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Can get market prices", async () => {
|
||||||
|
|
||||||
|
// get all market prices
|
||||||
|
let prices: MarketPriceInfo[] = await alice.getPrices();
|
||||||
|
expect(prices.length).toBeGreaterThan(1);
|
||||||
|
for (let price of prices) {
|
||||||
|
expect(price.getCurrencyCode().length).toBeGreaterThan(0);
|
||||||
|
expect(price.getPrice()).toBeGreaterThanOrEqual(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get market prices of specific currencies
|
||||||
|
for (let testAccount of TestConfig.cryptoAccounts) {
|
||||||
|
let price = await alice.getPrice(testAccount.currencyCode);
|
||||||
|
expect(price).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that prices are reasonable
|
||||||
|
let usd = await alice.getPrice("USD");
|
||||||
|
expect(usd).toBeGreaterThan(50);
|
||||||
|
expect(usd).toBeLessThan(5000);
|
||||||
|
let doge = await alice.getPrice("DOGE");
|
||||||
|
expect(doge).toBeGreaterThan(200)
|
||||||
|
expect(doge).toBeLessThan(20000);
|
||||||
|
let btc = await alice.getPrice("BTC");
|
||||||
|
expect(btc).toBeGreaterThan(0.0004)
|
||||||
|
expect(btc).toBeLessThan(0.4);
|
||||||
|
|
||||||
|
// test invalid currency
|
||||||
|
await expect(async () => {await alice.getPrice("INVALID_CURRENCY")})
|
||||||
|
.rejects
|
||||||
|
.toThrow('Currency not found: INVALID_CURRENCY');
|
||||||
|
});
|
||||||
|
|
||||||
test("Can get offers", async () => {
|
test("Can get offers", async () => {
|
||||||
let offers: OfferInfo[] = await alice.getOffers("BUY");
|
let offers: OfferInfo[] = await alice.getOffers("BUY");
|
||||||
for (let offer of offers) {
|
for (let offer of offers) {
|
||||||
|
@ -627,7 +785,7 @@ test("Can complete a trade", async () => {
|
||||||
let bobNotifications: NotificationMessage[] = [];
|
let bobNotifications: NotificationMessage[] = [];
|
||||||
await alice.addNotificationListener(notification => { aliceNotifications.push(notification); });
|
await alice.addNotificationListener(notification => { aliceNotifications.push(notification); });
|
||||||
await bob.addNotificationListener(notification => { bobNotifications.push(notification); });
|
await bob.addNotificationListener(notification => { bobNotifications.push(notification); });
|
||||||
|
|
||||||
// alice posts offer to buy xmr
|
// alice posts offer to buy xmr
|
||||||
console.log("Alice posting offer");
|
console.log("Alice posting offer");
|
||||||
let direction = "buy";
|
let direction = "buy";
|
||||||
|
@ -675,7 +833,7 @@ test("Can complete a trade", async () => {
|
||||||
expect(tradeNotifications[0].getTrade()!.getPhase()).toEqual("DEPOSIT_PUBLISHED");
|
expect(tradeNotifications[0].getTrade()!.getPhase()).toEqual("DEPOSIT_PUBLISHED");
|
||||||
expect(tradeNotifications[0].getTitle()).toEqual("Offer Taken");
|
expect(tradeNotifications[0].getTitle()).toEqual("Offer Taken");
|
||||||
expect(tradeNotifications[0].getMessage()).toEqual("Your offer " + offer.getId() + " has been accepted");
|
expect(tradeNotifications[0].getMessage()).toEqual("Your offer " + offer.getId() + " has been accepted");
|
||||||
|
|
||||||
// alice is notified of balance change
|
// alice is notified of balance change
|
||||||
|
|
||||||
// bob can get trade
|
// bob can get trade
|
||||||
|
@ -689,7 +847,7 @@ test("Can complete a trade", async () => {
|
||||||
expect(BigInt(bobBalancesAfter.getReservedOfferBalance()) + BigInt(bobBalancesAfter.getReservedTradeBalance())).toBeGreaterThan(BigInt(bobBalancesBefore.getReservedOfferBalance()) + BigInt(bobBalancesBefore.getReservedTradeBalance()));
|
expect(BigInt(bobBalancesAfter.getReservedOfferBalance()) + BigInt(bobBalancesAfter.getReservedTradeBalance())).toBeGreaterThan(BigInt(bobBalancesBefore.getReservedOfferBalance()) + BigInt(bobBalancesBefore.getReservedTradeBalance()));
|
||||||
|
|
||||||
// bob is notified of balance change
|
// bob is notified of balance change
|
||||||
|
|
||||||
// alice can get trade
|
// alice can get trade
|
||||||
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
||||||
expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_PUBLISHED");
|
expect(fetchedTrade.getPhase()).toEqual("DEPOSIT_PUBLISHED");
|
||||||
|
@ -741,6 +899,20 @@ test("Can complete a trade", async () => {
|
||||||
|
|
||||||
// ------------------------------- HELPERS ------------------------------------
|
// ------------------------------- HELPERS ------------------------------------
|
||||||
|
|
||||||
|
function getConnection(connections: UriConnection[], uri: string): UriConnection | undefined {
|
||||||
|
for (let connection of connections) if (connection.getUri() === uri) return connection;
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testConnection(connection: UriConnection, uri?: string, onlineStatus?: OnlineStatus, authenticationStatus?: AuthenticationStatus, priority?: number) {
|
||||||
|
if (uri) assert.equal(connection.getUri(), uri);
|
||||||
|
assert.equal(connection.getPassword(), ""); // TODO (woodser): undefined instead of ""?
|
||||||
|
assert.equal(connection.getUsername(), "");
|
||||||
|
if (onlineStatus !== undefined) assert.equal(connection.getOnlineStatus(), onlineStatus);
|
||||||
|
if (authenticationStatus !== undefined) assert.equal(connection.getAuthenticationStatus(), authenticationStatus);
|
||||||
|
if (priority !== undefined) assert.equal(connection.getPriority(), priority);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize arbitrator, alice, or bob by their configuration.
|
* Initialize arbitrator, alice, or bob by their configuration.
|
||||||
*
|
*
|
||||||
|
@ -749,7 +921,7 @@ test("Can complete a trade", async () => {
|
||||||
*/
|
*/
|
||||||
async function initHavenoDaemon(config: any): Promise<HavenoDaemon> {
|
async function initHavenoDaemon(config: any): Promise<HavenoDaemon> {
|
||||||
try {
|
try {
|
||||||
let havenod = new HavenoDaemon(config.url, config.password);
|
let havenod = new HavenoDaemon(config.uri, config.password);
|
||||||
await havenod.getVersion();
|
await havenod.getVersion();
|
||||||
return havenod;
|
return havenod;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -809,7 +981,7 @@ async function startHavenoProcess(appName: string|undefined, enableLogging: bool
|
||||||
"--appName", appName,
|
"--appName", appName,
|
||||||
"--apiPassword", "apitest",
|
"--apiPassword", "apitest",
|
||||||
"--apiPort", TestConfig.proxyPorts.get(proxyPort)![0],
|
"--apiPort", TestConfig.proxyPorts.get(proxyPort)![0],
|
||||||
"--walletRpcBindPort", (proxyPort === "8080" ? new URL(TestConfig.alice.walletUrl).port : await getFreePort()) + "" // use alice's configured wallet rpc port
|
"--walletRpcBindPort", (proxyPort === "8080" ? new URL(TestConfig.alice.walletUri).port : await getFreePort()) + "" // use alice's configured wallet rpc port
|
||||||
];
|
];
|
||||||
let havenod = await HavenoDaemon.startProcess(TestConfig.haveno.path, cmd, "http://localhost:" + proxyPort, enableLogging);
|
let havenod = await HavenoDaemon.startProcess(TestConfig.haveno.path, cmd, "http://localhost:" + proxyPort, enableLogging);
|
||||||
HAVENO_PROCESSES.push(havenod);
|
HAVENO_PROCESSES.push(havenod);
|
||||||
|
@ -837,7 +1009,7 @@ async function getFreePort(): Promise<number> {
|
||||||
async function stopHavenoProcess(havenod: HavenoDaemon) {
|
async function stopHavenoProcess(havenod: HavenoDaemon) {
|
||||||
await havenod.stopProcess();
|
await havenod.stopProcess();
|
||||||
GenUtils.remove(HAVENO_PROCESSES, havenod);
|
GenUtils.remove(HAVENO_PROCESSES, havenod);
|
||||||
GenUtils.remove(HAVENO_PROCESS_PORTS, new URL(havenod.getUrl()).port);
|
GenUtils.remove(HAVENO_PROCESS_PORTS, new URL(havenod.getUrl()).port); // TODO (woodser): standardize to uri
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -846,7 +1018,7 @@ async function stopHavenoProcess(havenod: HavenoDaemon) {
|
||||||
async function initFundingWallet() {
|
async function initFundingWallet() {
|
||||||
|
|
||||||
// init client connected to monero-wallet-rpc
|
// init client connected to monero-wallet-rpc
|
||||||
fundingWallet = await monerojs.connectToWalletRpc(TestConfig.fundingWallet.url, TestConfig.fundingWallet.username, TestConfig.fundingWallet.password);
|
fundingWallet = await monerojs.connectToWalletRpc(TestConfig.fundingWallet.uri, TestConfig.fundingWallet.username, TestConfig.fundingWallet.password);
|
||||||
|
|
||||||
// check if wallet is open
|
// check if wallet is open
|
||||||
let walletIsOpen = false
|
let walletIsOpen = false
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {HavenoUtils} from "./utils/HavenoUtils";
|
import {HavenoUtils} from "./utils/HavenoUtils";
|
||||||
import {TaskLooper} from "./utils/TaskLooper";
|
import {TaskLooper} from "./utils/TaskLooper";
|
||||||
import * as grpcWeb from 'grpc-web';
|
import * as grpcWeb from 'grpc-web';
|
||||||
import {DisputeAgentsClient, GetVersionClient, NotificationsClient, PriceClient, WalletsClient, OffersClient, PaymentAccountsClient, TradesClient} from './protobuf/GrpcServiceClientPb';
|
import {DisputeAgentsClient, GetVersionClient, NotificationsClient, PriceClient, WalletsClient, OffersClient, PaymentAccountsClient, TradesClient, MoneroConnectionsClient} from './protobuf/GrpcServiceClientPb';
|
||||||
import {CancelOfferRequest, ConfirmPaymentReceivedRequest, ConfirmPaymentStartedRequest, CreateCryptoCurrencyPaymentAccountReply, CreateCryptoCurrencyPaymentAccountRequest, CreateOfferReply, CreateOfferRequest, CreateXmrTxReply, CreateXmrTxRequest, GetBalancesReply, GetBalancesRequest, GetNewDepositSubaddressReply, GetNewDepositSubaddressRequest, GetOffersReply, GetOffersRequest, GetPaymentAccountsReply, GetPaymentAccountsRequest, GetTradeReply, GetTradeRequest, GetTradesReply, GetTradesRequest, GetVersionReply, GetVersionRequest, GetXmrTxsReply, GetXmrTxsRequest, MarketPriceInfo, MarketPriceReply, MarketPriceRequest, MarketPricesReply, MarketPricesRequest, NotificationMessage, OfferInfo, RegisterDisputeAgentRequest, RegisterNotificationListenerRequest, RelayXmrTxReply, RelayXmrTxRequest, SendNotificationRequest, TakeOfferReply, TakeOfferRequest, TradeInfo, XmrBalanceInfo, XmrDestination, XmrTx} from './protobuf/grpc_pb';
|
import {CancelOfferRequest, ConfirmPaymentReceivedRequest, ConfirmPaymentStartedRequest, CreateCryptoCurrencyPaymentAccountReply, CreateCryptoCurrencyPaymentAccountRequest, CreateOfferReply, CreateOfferRequest, CreateXmrTxReply, CreateXmrTxRequest, GetBalancesReply, GetBalancesRequest, GetNewDepositSubaddressReply, GetNewDepositSubaddressRequest, GetOffersReply, GetOffersRequest, GetPaymentAccountsReply, GetPaymentAccountsRequest, GetTradeReply, GetTradeRequest, GetTradesReply, GetTradesRequest, GetVersionReply, GetVersionRequest, GetXmrTxsReply, GetXmrTxsRequest, MarketPriceInfo, MarketPriceReply, MarketPriceRequest, MarketPricesReply, MarketPricesRequest, NotificationMessage, OfferInfo, RegisterDisputeAgentRequest, RegisterNotificationListenerRequest, RelayXmrTxReply, RelayXmrTxRequest, SendNotificationRequest, TakeOfferReply, TakeOfferRequest, TradeInfo, XmrBalanceInfo, XmrDestination, XmrTx, UriConnection, AddConnectionRequest, RemoveConnectionRequest, GetConnectionRequest, GetConnectionsRequest, SetConnectionRequest, CheckConnectionRequest, CheckConnectionsReply, CheckConnectionsRequest, StartCheckingConnectionsRequest, StopCheckingConnectionsRequest, GetBestAvailableConnectionRequest, SetAutoSwitchRequest, CheckConnectionReply, GetConnectionsReply, GetConnectionReply, GetBestAvailableConnectionReply} from './protobuf/grpc_pb';
|
||||||
import {AvailabilityResult, PaymentAccount} from './protobuf/pb_pb';
|
import {AvailabilityResult, PaymentAccount} from './protobuf/pb_pb';
|
||||||
const console = require('console');
|
const console = require('console');
|
||||||
|
|
||||||
|
@ -15,8 +15,9 @@ class HavenoDaemon {
|
||||||
_getVersionClient: GetVersionClient;
|
_getVersionClient: GetVersionClient;
|
||||||
_disputeAgentsClient: DisputeAgentsClient;
|
_disputeAgentsClient: DisputeAgentsClient;
|
||||||
_notificationsClient: NotificationsClient;
|
_notificationsClient: NotificationsClient;
|
||||||
_priceClient: PriceClient;
|
_moneroConnectionsClient: MoneroConnectionsClient;
|
||||||
_walletsClient: WalletsClient;
|
_walletsClient: WalletsClient;
|
||||||
|
_priceClient: PriceClient;
|
||||||
_paymentAccountsClient: PaymentAccountsClient;
|
_paymentAccountsClient: PaymentAccountsClient;
|
||||||
_offersClient: OffersClient;
|
_offersClient: OffersClient;
|
||||||
_tradesClient: TradesClient;
|
_tradesClient: TradesClient;
|
||||||
|
@ -29,7 +30,8 @@ class HavenoDaemon {
|
||||||
_walletRpcPort: number|undefined;
|
_walletRpcPort: number|undefined;
|
||||||
_notificationListeners: ((notification: NotificationMessage) => void)[] = [];
|
_notificationListeners: ((notification: NotificationMessage) => void)[] = [];
|
||||||
_keepAlivePeriodMs: number = 60000;
|
_keepAlivePeriodMs: number = 60000;
|
||||||
|
_appName: string|undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a client connected to a Haveno daemon.
|
* Construct a client connected to a Haveno daemon.
|
||||||
*
|
*
|
||||||
|
@ -44,8 +46,9 @@ class HavenoDaemon {
|
||||||
this._password = password;
|
this._password = password;
|
||||||
this._getVersionClient = new GetVersionClient(this._url);
|
this._getVersionClient = new GetVersionClient(this._url);
|
||||||
this._disputeAgentsClient = new DisputeAgentsClient(this._url);
|
this._disputeAgentsClient = new DisputeAgentsClient(this._url);
|
||||||
this._priceClient = new PriceClient(this._url);
|
this._moneroConnectionsClient = new MoneroConnectionsClient(this._url)
|
||||||
this._walletsClient = new WalletsClient(this._url);
|
this._walletsClient = new WalletsClient(this._url);
|
||||||
|
this._priceClient = new PriceClient(this._url);
|
||||||
this._paymentAccountsClient = new PaymentAccountsClient(this._url);
|
this._paymentAccountsClient = new PaymentAccountsClient(this._url);
|
||||||
this._offersClient = new OffersClient(this._url);
|
this._offersClient = new OffersClient(this._url);
|
||||||
this._tradesClient = new TradesClient(this._url);
|
this._tradesClient = new TradesClient(this._url);
|
||||||
|
@ -98,7 +101,8 @@ class HavenoDaemon {
|
||||||
daemon = new HavenoDaemon(url, password);
|
daemon = new HavenoDaemon(url, password);
|
||||||
daemon._process = childProcess;
|
daemon._process = childProcess;
|
||||||
daemon._processLogging = enableLogging;
|
daemon._processLogging = enableLogging;
|
||||||
|
daemon._appName = cmd[cmd.indexOf("--appName") + 1];
|
||||||
|
|
||||||
// get wallet rpc port
|
// get wallet rpc port
|
||||||
let walletRpcPortIdx = cmd.indexOf("--walletRpcBindPort");
|
let walletRpcPortIdx = cmd.indexOf("--walletRpcBindPort");
|
||||||
if (walletRpcPortIdx >= 0) daemon._walletRpcPort = parseInt(cmd[walletRpcPortIdx + 1]);
|
if (walletRpcPortIdx >= 0) daemon._walletRpcPort = parseInt(cmd[walletRpcPortIdx + 1]);
|
||||||
|
@ -193,6 +197,13 @@ class HavenoDaemon {
|
||||||
return this._walletRpcPort;
|
return this._walletRpcPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the Haveno application folder.
|
||||||
|
*/
|
||||||
|
getAppName(): string|undefined {
|
||||||
|
return this._appName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Haveno version.
|
* Get the Haveno version.
|
||||||
*
|
*
|
||||||
|
@ -238,32 +249,174 @@ class HavenoDaemon {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current market price per 1 XMR in the given currency.
|
* Add a Monero daemon connection.
|
||||||
*
|
*
|
||||||
* @param {string} currencyCode - currency code (fiat or crypto) to get the price of
|
* @param {string | UriConnection} connection - daemon uri or connection to add
|
||||||
* @return {number} the current market price per 1 XMR in the given currency
|
|
||||||
*/
|
*/
|
||||||
async getPrice(currencyCode: string): Promise<number> {
|
async addMoneroConnection(connection: string | UriConnection): Promise<void> {
|
||||||
let that = this;
|
let that = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
that._priceClient.getMarketPrice(new MarketPriceRequest().setCurrencyCode(currencyCode), {password: that._password}, function(err: grpcWeb.RpcError, response: MarketPriceReply) {
|
that._moneroConnectionsClient.addConnection(new AddConnectionRequest().setConnection(typeof connection === "string" ? new UriConnection().setUri(connection) : connection), {password: that._password}, function(err: grpcWeb.RpcError) {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
else resolve(response.getPrice());
|
else resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current market prices of all the currencies.
|
* Remove a Monero daemon connection.
|
||||||
*
|
*
|
||||||
* @return {MarketPrice[]} price per 1 XMR in all supported currencies (fiat & crypto)
|
* @param {string} uri - uri of the daemon connection to remove
|
||||||
*/
|
*/
|
||||||
async getPrices(): Promise<MarketPriceInfo[]> {
|
async removeMoneroConnection(uri: string): Promise<void> {
|
||||||
let that = this;
|
let that = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
that._priceClient.getMarketPrices(new MarketPricesRequest(), {password: that._password}, function(err: grpcWeb.RpcError, response: MarketPricesReply) {
|
that._moneroConnectionsClient.removeConnection(new RemoveConnectionRequest().setUri(uri), {password: that._password}, function(err: grpcWeb.RpcError) {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
else resolve(response.getMarketPriceList());
|
else resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current Monero daemon connection.
|
||||||
|
*
|
||||||
|
* @return {UriConnection | undefined} the current daemon connection, undefined if no current connection
|
||||||
|
*/
|
||||||
|
async getMoneroConnection(): Promise<UriConnection | undefined> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.getConnection(new GetConnectionRequest(), {password: that._password}, function(err: grpcWeb.RpcError, response: GetConnectionReply) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve(response.getConnection());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all Monero daemon connections.
|
||||||
|
*
|
||||||
|
* @return {UriConnection[]} all daemon connections
|
||||||
|
*/
|
||||||
|
async getMoneroConnections(): Promise<UriConnection[]> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.getConnections(new GetConnectionsRequest(), {password: that._password}, function(err: grpcWeb.RpcError, response: GetConnectionsReply) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve(response.getConnectionsList());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current Monero daemon connection.
|
||||||
|
*
|
||||||
|
* Add the connection if not previously seen.
|
||||||
|
* If the connection is provided as string, connect to the URI with any previously set credentials and priority.
|
||||||
|
* If the connection is provided as UriConnection, overwrite any previously set credentials and priority.
|
||||||
|
* If undefined connection provided, disconnect the client.
|
||||||
|
*
|
||||||
|
* @param {string | UriConnection} connection - connection to set as current
|
||||||
|
*/
|
||||||
|
async setMoneroConnection(connection?: string | UriConnection): Promise<void> {
|
||||||
|
let that = this;
|
||||||
|
let request = new SetConnectionRequest();
|
||||||
|
if (typeof connection === "string") request.setUri(connection);
|
||||||
|
else request.setConnection(connection);
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.setConnection(request, {password: that._password}, function(err: grpcWeb.RpcError) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the current Monero daemon connection.
|
||||||
|
*
|
||||||
|
* If disconnected and auto switch enabled, switch to the best available connection and return its status.
|
||||||
|
*
|
||||||
|
* @return {UriConnection | undefined} the current daemon connection status, undefined if no current connection
|
||||||
|
*/
|
||||||
|
async checkMoneroConnection(): Promise<UriConnection | undefined> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.checkConnection(new CheckConnectionRequest(), {password: that._password}, function(err: grpcWeb.RpcError, response: CheckConnectionReply) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve(response.getConnection());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check all Monero daemon connections.
|
||||||
|
*
|
||||||
|
* @return {UriConnection[]} status of all managed connections.
|
||||||
|
*/
|
||||||
|
async checkMoneroConnections(): Promise<UriConnection[]> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.checkConnections(new CheckConnectionsRequest(), {password: that._password}, function(err: grpcWeb.RpcError, response: CheckConnectionsReply) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve(response.getConnectionsList());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the connection and start checking the connection periodically.
|
||||||
|
*
|
||||||
|
* @param {number} refreshPeriod - time between checks in milliseconds (default 15000 ms or 15 seconds)
|
||||||
|
*/
|
||||||
|
async startCheckingConnection(refreshPeriod: number): Promise<void> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.startCheckingConnections(new StartCheckingConnectionsRequest().setRefreshPeriod(refreshPeriod), {password: that._password}, function(err: grpcWeb.RpcError) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop checking the connection status periodically.
|
||||||
|
*/
|
||||||
|
async stopCheckingConnection(): Promise<void> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.stopCheckingConnections(new StopCheckingConnectionsRequest(), {password: that._password}, function(err: grpcWeb.RpcError) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the best available connection in order of priority then response time.
|
||||||
|
*
|
||||||
|
* @return {UriConnection | undefined} the best available connection in order of priority then response time, undefined if no connections available
|
||||||
|
*/
|
||||||
|
async getBestAvailableConnection(): Promise<UriConnection | undefined> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.getBestAvailableConnection(new GetBestAvailableConnectionRequest(), {password: that._password}, function(err: grpcWeb.RpcError, response: GetBestAvailableConnectionReply) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve(response.getConnection());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically switch to the best available connection if current connection is disconnected after being checked.
|
||||||
|
*
|
||||||
|
* @param {boolean} autoSwitch - whether auto switch is enabled or disabled
|
||||||
|
*/
|
||||||
|
async setAutoSwitch(autoSwitch: boolean): Promise<void> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._moneroConnectionsClient.setAutoSwitch(new SetAutoSwitchRequest().setAutoSwitch(autoSwitch), {password: that._password}, function(err: grpcWeb.RpcError) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -357,6 +510,37 @@ class HavenoDaemon {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current market price per 1 XMR in the given currency.
|
||||||
|
*
|
||||||
|
* @param {string} currencyCode - currency code (fiat or crypto) to get the price of
|
||||||
|
* @return {number} the current market price per 1 XMR in the given currency
|
||||||
|
*/
|
||||||
|
async getPrice(currencyCode: string): Promise<number> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._priceClient.getMarketPrice(new MarketPriceRequest().setCurrencyCode(currencyCode), {password: that._password}, function(err: grpcWeb.RpcError, response: MarketPriceReply) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve(response.getPrice());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current market prices of all the currencies.
|
||||||
|
*
|
||||||
|
* @return {MarketPrice[]} price per 1 XMR in all supported currencies (fiat & crypto)
|
||||||
|
*/
|
||||||
|
async getPrices(): Promise<MarketPriceInfo[]> {
|
||||||
|
let that = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that._priceClient.getMarketPrices(new MarketPricesRequest(), {password: that._password}, function(err: grpcWeb.RpcError, response: MarketPricesReply) {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve(response.getMarketPriceList());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get payment accounts.
|
* Get payment accounts.
|
||||||
*
|
*
|
||||||
|
@ -596,7 +780,7 @@ class HavenoDaemon {
|
||||||
async _registerNotificationListener(): Promise<void> {
|
async _registerNotificationListener(): Promise<void> {
|
||||||
let that = this;
|
let that = this;
|
||||||
return new Promise(function(resolve) {
|
return new Promise(function(resolve) {
|
||||||
|
|
||||||
// send request to register client listener
|
// send request to register client listener
|
||||||
that._notificationsClient.registerNotificationListener(new RegisterNotificationListenerRequest(), {password: that._password})
|
that._notificationsClient.registerNotificationListener(new RegisterNotificationListenerRequest(), {password: that._password})
|
||||||
.on("data", (data) => {
|
.on("data", (data) => {
|
||||||
|
@ -604,7 +788,7 @@ class HavenoDaemon {
|
||||||
for (let listener of that._notificationListeners) listener(data);
|
for (let listener of that._notificationListeners) listener(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// periodically send keep alive requests // TODO (woodser): better way to keep notification stream alive?
|
// periodically send keep alive requests // TODO (woodser): better way to keep notification stream alive?
|
||||||
let firstRequest = true;
|
let firstRequest = true;
|
||||||
let taskLooper = new TaskLooper(async function() {
|
let taskLooper = new TaskLooper(async function() {
|
||||||
|
@ -617,7 +801,7 @@ class HavenoDaemon {
|
||||||
.setTimestamp(Date.now()));
|
.setTimestamp(Date.now()));
|
||||||
});
|
});
|
||||||
taskLooper.start(that._keepAlivePeriodMs);
|
taskLooper.start(that._keepAlivePeriodMs);
|
||||||
|
|
||||||
// TODO: call returns before listener registered
|
// TODO: call returns before listener registered
|
||||||
setTimeout(function() { resolve(); }, 1000);
|
setTimeout(function() { resolve(); }, 1000);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue