rename to haveno.ts, haveno.test.ts

This commit is contained in:
woodser 2022-04-05 15:18:36 -04:00
parent 91710b2bcf
commit 1a08f3d15f
4 changed files with 57 additions and 57 deletions

View File

@ -25,9 +25,9 @@ This application is a lightly modified [create-react-app](https://github.com/fac
## Run Tests
Running the [top-level API tests](./src/HavenoDaemon.test.ts) is a great way to develop and test Haveno end-to-end.
Running the [API tests](./src/haveno.test.ts) is the best way to develop and test Haveno end-to-end.
[`HavenoDaemon`](./src/HavenoDaemon.ts) provides the interface to the Haveno daemon's gRPC API.
[`haveno.ts`](./src/haveno.ts) provides the interface to Haveno's backend daemon.
1. [Run a local Haveno test network](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md) and then shut down the arbitrator, Alice, and Bob or run them as daemons, e.g. `make alice-daemon`. You may omit the arbitrator registration steps since it is done automatically in the tests.
2. Clone this project to the same parent directory as the haveno project: `git clone https://github.com/haveno-dex/haveno-ui-poc`

View File

@ -1,19 +1,19 @@
import React from 'react';
import logo from './logo.png';
import './App.css';
import {HavenoDaemon} from './HavenoDaemon';
import {haveno} from './haveno';
const HAVENO_DAEMON_URL = "http://localhost:8080";
const HAVENO_DAEMON_PASSWORD = "apitest";
class App extends React.Component<{}, {daemonVersion: string}> {
daemon: HavenoDaemon;
daemon: haveno;
constructor(props: any) {
super(props);
this.state = {daemonVersion: ""};
this.daemon = new HavenoDaemon(HAVENO_DAEMON_URL, HAVENO_DAEMON_PASSWORD);
this.daemon = new haveno(HAVENO_DAEMON_URL, HAVENO_DAEMON_PASSWORD);
}
render() {

View File

@ -1,7 +1,7 @@
// --------------------------------- IMPORTS ----------------------------------
// import haveno types
import {HavenoDaemon} from "./HavenoDaemon";
import {haveno} from "./haveno";
import {HavenoUtils} from "./utils/HavenoUtils";
import * as grpcWeb from 'grpc-web';
import {MarketPriceInfo, NotificationMessage, OfferInfo, TradeInfo, UrlConnection, XmrBalanceInfo} from './protobuf/grpc_pb'; // TODO (woodser): better names; haveno_grpc_pb, haveno_pb
@ -140,17 +140,17 @@ interface TxContext {
}
// clients
let startupHavenods: HavenoDaemon[] = [];
let arbitrator: HavenoDaemon;
let alice: HavenoDaemon;
let bob: HavenoDaemon;
let startupHavenods: haveno[] = [];
let arbitrator: haveno;
let alice: haveno;
let bob: haveno;
let monerod: any;
let fundingWallet: any;
let aliceWallet: any;
let bobWallet: any;
// track started haveno processes
const HAVENO_PROCESSES: HavenoDaemon[] = [];
const HAVENO_PROCESSES: haveno[] = [];
const HAVENO_PROCESS_PORTS: string[] = [];
// other config
@ -167,10 +167,10 @@ beforeAll(async () => {
// start configured haveno daemons
let promises = [];
for (let config of TestConfig.startupHavenods) promises.push(initHavenoDaemon(config));
for (let config of TestConfig.startupHavenods) promises.push(initHaveno(config));
for (let settledPromise of await Promise.allSettled(promises)) {
if (settledPromise.status !== "fulfilled") throw new Error((settledPromise as PromiseRejectedResult).reason);
startupHavenods.push((settledPromise as PromiseFulfilledResult<HavenoDaemon>).value);
startupHavenods.push((settledPromise as PromiseFulfilledResult<haveno>).value);
}
// assign arbitrator alice, bob
@ -209,12 +209,12 @@ test("Can get the version", async () => {
});
test("Can manage an account", async () => {
let charlie: HavenoDaemon | undefined;
let charlie: haveno | undefined;
let err: any;
try {
// start charlie without opening account
charlie = await initHavenoDaemon({autoLogin: false});
charlie = await initHaveno({autoLogin: false});
assert(!await charlie.accountExists());
// test errors when account not open
@ -249,7 +249,7 @@ test("Can manage an account", async () => {
// restart charlie
let charlieConfig = {appName: charlie.getAppName(), autoLogin: false}
await releaseHavenoProcess(charlie);
charlie = await initHavenoDaemon(charlieConfig);
charlie = await initHaveno(charlieConfig);
assert(await charlie.accountExists());
assert(!await charlie.isAccountOpen());
@ -266,7 +266,7 @@ test("Can manage an account", async () => {
// restart charlie
await releaseHavenoProcess(charlie);
charlie = await initHavenoDaemon(charlieConfig);
charlie = await initHaveno(charlieConfig);
await testAccountNotOpen(charlie);
// open account
@ -287,14 +287,14 @@ test("Can manage an account", async () => {
await releaseHavenoProcess(charlie);
// restore account which shuts down server
charlie = await initHavenoDaemon(charlieConfig);
charlie = await initHaveno(charlieConfig);
let zipBytes: Uint8Array = new Uint8Array(fs.readFileSync(zipFile));
await charlie.restoreAccount(zipBytes);
assert(!await charlie.isConnectedToDaemon());
await releaseHavenoProcess(charlie);
// open restored account
charlie = await initHavenoDaemon(charlieConfig);
charlie = await initHaveno(charlieConfig);
assert(await charlie.accountExists());
await charlie.openAccount(password);
assert(await charlie.isAccountOpen());
@ -308,7 +308,7 @@ test("Can manage an account", async () => {
// TODO: how to delete trader app folder at end of test?
if (err) throw err;
async function testAccountNotOpen(havenod: HavenoDaemon): Promise<void> { // TODO: generalize this?
async function testAccountNotOpen(havenod: haveno): Promise<void> { // TODO: generalize this?
try { await havenod.getMoneroConnections(); throw new Error("Should have thrown"); }
catch (err) { assert.equal(err.message, "Account not open"); }
try { await havenod.getXmrTxs(); throw new Error("Should have thrown"); }
@ -320,12 +320,12 @@ test("Can manage an account", async () => {
test("Can manage Monero daemon connections", async () => {
let monerod2: any;
let charlie: HavenoDaemon | undefined;
let charlie: haveno | undefined;
let err: any;
try {
// start charlie
charlie = await initHavenoDaemon();
charlie = await initHaveno();
// test default connections
let monerodUrl1 = "http://127.0.0.1:38081"; // TODO: (woodser): move to config
@ -398,7 +398,7 @@ test("Can manage Monero daemon connections", async () => {
// restart charlie
let appName = charlie.getAppName();
await releaseHavenoProcess(charlie);
charlie = await initHavenoDaemon({appName: appName, accountPassword: password});
charlie = await initHaveno({appName: appName, accountPassword: password});
// connection is restored, online, and authenticated
connection = await charlie.getMoneroConnection();
@ -687,7 +687,7 @@ test("Can get market depth", async () => {
// clear offers
await clearOffers(alice, assetCode);
await clearOffers(bob, assetCode);
async function clearOffers(havenod: HavenoDaemon, assetCode: string) {
async function clearOffers(havenod: haveno, assetCode: string) {
for (let offer of await havenod.getMyOffers(assetCode)) {
if (offer.getBaseCurrencyCode().toLowerCase() === assetCode.toLowerCase()) { // TODO (woodser): offer base currency and counter currency are switched for cryptos
await havenod.removeOffer(offer.getId());
@ -1282,12 +1282,12 @@ test("Can resolve disputes", async () => {
});
test("Cannot make or take offer with insufficient unlocked funds", async () => {
let charlie: HavenoDaemon | undefined;
let charlie: haveno | undefined;
let err: any;
try {
// start charlie
charlie = await initHavenoDaemon();
charlie = await initHaveno();
// charlie creates ethereum payment account
let paymentAccount = await createCryptoPaymentAccount(charlie);
@ -1405,13 +1405,13 @@ test("Invalidates offers when reserved funds are spent", async () => {
// TODO (woodser): test arbitrator state too
// TODO (woodser): test breaking protocol after depositing to multisig (e.g. don't send payment account payload by deleting it)
test("Handles unexpected errors during trade initialization", async () => {
let traders: HavenoDaemon[] = [];
let traders: haveno[] = [];
let err: any;
try {
// start and fund 3 trader processes
HavenoUtils.log(1, "Starting trader processes");
traders = await initHavenoDaemons(3);
traders = await initHavenos(3);
HavenoUtils.log(1, "Funding traders");
let tradeAmount: bigint = BigInt("250000000000");
await waitForUnlockedBalance(tradeAmount * BigInt("2"), traders[0], traders[1], traders[2]);
@ -1498,13 +1498,13 @@ test("Handles unexpected errors during trade initialization", async () => {
// ------------------------------- HELPERS ------------------------------------
async function initHavenoDaemons(numDaemons: number, config?: any) {
let traderPromises: Promise<HavenoDaemon>[] = [];
for (let i = 0; i < numDaemons; i++) traderPromises.push(initHavenoDaemon(config));
async function initHavenos(numDaemons: number, config?: any) {
let traderPromises: Promise<haveno>[] = [];
for (let i = 0; i < numDaemons; i++) traderPromises.push(initHaveno(config));
return Promise.all(traderPromises);
}
async function initHavenoDaemon(config?: any): Promise<HavenoDaemon> {
async function initHaveno(config?: any): Promise<haveno> {
config = Object.assign({}, TestConfig.defaultHavenod, config);
if (!config.appName) config.appName = "haveno-XMR_STAGENET_instance_" + GenUtils.getUUID();
@ -1513,7 +1513,7 @@ async function initHavenoDaemon(config?: any): Promise<HavenoDaemon> {
try {
// try to connect to existing server
havenod = new HavenoDaemon(config.url, config.apiPassword);
havenod = new haveno(config.url, config.apiPassword);
await havenod.getVersion();
} catch (err) {
@ -1545,7 +1545,7 @@ async function initHavenoDaemon(config?: any): Promise<HavenoDaemon> {
"--walletRpcBindPort", config.walletUrl ? new URL(config.walletUrl).port : "" + await getAvailablePort(), // use configured port if given
"--passwordRequired", (config.accountPasswordRequired ? "true" : "false")
];
havenod = await HavenoDaemon.startProcess(TestConfig.haveno.path, cmd, "http://localhost:" + proxyPort, config.logProcessOutput);
havenod = await haveno.startProcess(TestConfig.haveno.path, cmd, "http://localhost:" + proxyPort, config.logProcessOutput);
HAVENO_PROCESSES.push(havenod);
}
@ -1570,7 +1570,7 @@ async function initHavenoDaemon(config?: any): Promise<HavenoDaemon> {
/**
* Release a Haveno process for reuse and try to shutdown.
*/
async function releaseHavenoProcess(havenod: HavenoDaemon) {
async function releaseHavenoProcess(havenod: haveno) {
GenUtils.remove(HAVENO_PROCESSES, havenod);
GenUtils.remove(HAVENO_PROCESS_PORTS, new URL(havenod.getUrl()).port); // TODO (woodser): standardize to url
try {
@ -1583,7 +1583,7 @@ async function releaseHavenoProcess(havenod: HavenoDaemon) {
/**
* Create or open an account with the given daemon and password.
*/
async function initHavenoAccount(havenod: HavenoDaemon, password: string) {
async function initHavenoAccount(havenod: haveno, password: string) {
if (await havenod.isAccountOpen()) return;
if (await havenod.accountExists()) return havenod.openAccount(password);
await havenod.createAccount(password);
@ -1649,17 +1649,17 @@ async function waitForUnlockedBalance(amount: bigint, ...wallets: any[]) {
}
async getUnlockedBalance(): Promise<bigint> {
if (this._wallet instanceof HavenoDaemon) return BigInt((await this._wallet.getBalances()).getUnlockedBalance());
if (this._wallet instanceof haveno) return BigInt((await this._wallet.getBalances()).getUnlockedBalance());
else return BigInt((await this._wallet.getUnlockedBalance()).toString());
}
async getLockedBalance(): Promise<bigint> {
if (this._wallet instanceof HavenoDaemon) return BigInt((await this._wallet.getBalances()).getLockedBalance());
if (this._wallet instanceof haveno) return BigInt((await this._wallet.getBalances()).getLockedBalance());
else return BigInt((await this._wallet.getBalance()).toString()) - await this.getUnlockedBalance();
}
async getDepositAddress(): Promise<string> {
if (this._wallet instanceof HavenoDaemon) return await this._wallet.getNewDepositSubaddress();
if (this._wallet instanceof haveno) return await this._wallet.getNewDepositSubaddress();
else return (await this._wallet.createSubaddress()).getAddress();
}
}
@ -1880,7 +1880,7 @@ function getRandomAssetCode() {
return TestConfig.assetCodes[GenUtils.getRandomInt(0, TestConfig.assetCodes.length - 1)];
}
async function createPaymentAccount(trader: HavenoDaemon, assetCode: string): Promise<PaymentAccount> {
async function createPaymentAccount(trader: haveno, assetCode: string): Promise<PaymentAccount> {
return isCrypto(assetCode) ? createCryptoPaymentAccount(trader, assetCode) : createRevolutPaymentAccount(trader);
}
@ -1894,14 +1894,14 @@ function getCryptoAddress(currencyCode: string): string | undefined {
}
}
async function createRevolutPaymentAccount(trader: HavenoDaemon): Promise<PaymentAccount> {
async function createRevolutPaymentAccount(trader: haveno): Promise<PaymentAccount> {
let accountForm = await trader.getPaymentAccountForm('REVOLUT');
accountForm.accountName = "Revolut account " + GenUtils.getUUID();
accountForm.userName = "user123";
return trader.createPaymentAccount(accountForm);
}
async function createCryptoPaymentAccount(trader: HavenoDaemon, currencyCode = "eth"): Promise<PaymentAccount> {
async function createCryptoPaymentAccount(trader: haveno, currencyCode = "eth"): Promise<PaymentAccount> {
for (let cryptoAddress of TestConfig.cryptoAddresses) {
if (cryptoAddress.currencyCode.toLowerCase() !== currencyCode.toLowerCase()) continue;
return trader.createCryptoPaymentAccount(
@ -1913,7 +1913,7 @@ async function createCryptoPaymentAccount(trader: HavenoDaemon, currencyCode = "
}
// TODO: specify counter currency code
async function postOffer(maker: HavenoDaemon, config?: any) {
async function postOffer(maker: haveno, config?: any) {
// assign default options
config = Object.assign({}, TestConfig.postOffer, config);
@ -1984,7 +1984,7 @@ function testOffer(offer: OfferInfo, config?: any) {
/**
* Tests trade chat functionality. Must be called during an open trade.
*/
async function testTradeChat(tradeId: string, alice: HavenoDaemon, bob: HavenoDaemon) {
async function testTradeChat(tradeId: string, alice: haveno, bob: haveno) {
HavenoUtils.log(1, "Testing trade chat");
// invalid trade should throw error

View File

@ -10,7 +10,7 @@ const console = require('console');
/**
* Haveno daemon client using gRPC.
*/
class HavenoDaemon {
class haveno {
// grpc clients
_appName: string|undefined;
@ -40,8 +40,8 @@ class HavenoDaemon {
_keepAlivePeriodMs: number = 60000;
// constants
static readonly _fullyInitializedMessage = "AppStartupState: Application fully initialized";
static readonly _loginRequiredMessage = "HavenoDaemonMain: Interactive login required";
static readonly _fullyInitializedMessage = "Application fully initialized";
static readonly _loginRequiredMessage = "Interactive login required";
/**
* Construct a client connected to a Haveno daemon.
@ -52,7 +52,7 @@ class HavenoDaemon {
constructor(url: string, password: string) {
if (!url) throw new Error("Must provide URL of Haveno daemon");
if (!password) throw new Error("Must provide password of Haveno daemon");
HavenoUtils.log(2, "Creating HavenoDaemon(" + url + ", " + password + ")");
HavenoUtils.log(2, "Creating Haveno client connected to " + url);
this._url = url;
this._password = password;
this._getVersionClient = new GetVersionClient(this._url);
@ -77,9 +77,9 @@ class HavenoDaemon {
* @param {string[]} cmd - command to start the process
* @param {string} url - Haveno daemon url (must proxy to api port)
* @param {boolean} enableLogging - specifies if logging is enabled or disabled at log level 3
* @return {HavenoDaemon} a client connected to the newly started Haveno process
* @return {haveno} a client connected to the newly started Haveno process
*/
static async startProcess(havenoPath: string, cmd: string[], url: string, enableLogging: boolean): Promise<HavenoDaemon> {
static async startProcess(havenoPath: string, cmd: string[], url: string, enableLogging: boolean): Promise<haveno> {
// return promise which resolves after starting havenod
return new Promise(function(resolve, reject) {
@ -88,7 +88,7 @@ class HavenoDaemon {
// state variables
let output = "";
let isStarted = false;
let daemon: HavenoDaemon|undefined = undefined;
let daemon: haveno|undefined = undefined;
// start process
let childProcess = require('child_process').spawn(cmd[0], cmd.slice(1), {cwd: havenoPath});
@ -102,7 +102,7 @@ class HavenoDaemon {
output += line + '\n'; // capture output in case of error
// initialize daemon on success or login required message
if (!daemon && (line.indexOf(HavenoDaemon._fullyInitializedMessage) >= 0 || line.indexOf(HavenoDaemon._loginRequiredMessage) >= 0)) {
if (!daemon && (line.indexOf(haveno._fullyInitializedMessage) >= 0 || line.indexOf(haveno._loginRequiredMessage) >= 0)) {
// get api password
let passwordIdx = cmd.indexOf("--apiPassword");
@ -113,7 +113,7 @@ class HavenoDaemon {
let password = cmd[passwordIdx + 1];
// create client connected to internal process
daemon = new HavenoDaemon(url, password);
daemon = new haveno(url, password);
daemon._process = childProcess;
daemon._processLogging = enableLogging;
daemon._appName = cmd[cmd.indexOf("--appName") + 1];
@ -182,7 +182,7 @@ class HavenoDaemon {
* @param {boolean} enabled - specifies if logging is enabled or disabled
*/
setProcessLogging(enabled: boolean) {
if (this._process === undefined) throw new Error("HavenoDaemon instance not created from new process");
if (this._process === undefined) throw new Error("haveno instance not created from new process");
this._processLogging = enabled;
}
@ -393,7 +393,7 @@ class HavenoDaemon {
/**
* Add a listener to receive notifications from the Haveno daemon.
*
* @param {HavenoDaemonListener} listener - the notification listener to add
* @param {(notification: NotificationMessage) => void} listener - the notification listener to add
*/
async addNotificationListener(listener: (notification: NotificationMessage) => void): Promise<void> {
this._notificationListeners.push(listener);
@ -403,7 +403,7 @@ class HavenoDaemon {
/**
* Remove a notification listener.
*
* @param {HavenoDaemonListener} listener - the notification listener to remove
* @param {(notification: NotificationMessage) => void} listener - the notification listener to remove
*/
async removeNotificationListener(listener: (notification: NotificationMessage) => void): Promise<void> {
let idx = this._notificationListeners.indexOf(listener);
@ -1339,4 +1339,4 @@ class HavenoDaemon {
}
}
export {HavenoDaemon};
export {haveno};