mirror of
https://github.com/haveno-dex/haveno-ts.git
synced 2025-07-23 15:00:43 -04:00
support auto starting and stopping arbitrator, alice, and bob
This commit is contained in:
parent
1220a86202
commit
9d2ec873c5
5 changed files with 286 additions and 140 deletions
|
@ -28,9 +28,9 @@ Running the [top-level API tests](./src/HavenoDaemon.test.ts) is a great way to
|
||||||
|
|
||||||
[`HavenoDaemon`](./src/HavenoDaemon.ts) provides the interface to the Haveno daemon's gRPC API.
|
[`HavenoDaemon`](./src/HavenoDaemon.ts) provides the interface to the Haveno daemon's gRPC API.
|
||||||
|
|
||||||
1. [Run a local Haveno test network](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md), running Alice and Bob as daemons with `make alice-daemon` and `make bob-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`.
|
||||||
2. Clone this project to the same parent directory as the haveno project: `git clone https://github.com/haveno-dex/haveno-ui-poc`
|
2. Clone this project to the same parent directory as the haveno project: `git clone https://github.com/haveno-dex/haveno-ui-poc`
|
||||||
3. In a new terminal, start envoy with the config in haveno-ui-poc/config/envoy.test.yaml (change absolute path for your system): `docker run --rm --add-host host.docker.internal:host-gateway -it -v ~/git/haveno-ui-poc/config/envoy.test.yaml:/envoy.test.yaml -p 8080:8080 -p 8081:8081 -p 8082:8082 -p 8083:8083 -p 8084:8084 -p 8085:8085 -p 8086:8086 envoyproxy/envoy-dev:8a2143613d43d17d1eb35a24b4a4a4c432215606 -c /envoy.test.yaml`
|
3. In a new terminal, start envoy with the config in haveno-ui-poc/config/envoy.test.yaml (change absolute path for your system): `docker run --rm --add-host host.docker.internal:host-gateway -it -v ~/git/haveno-ui-poc/config/envoy.test.yaml:/envoy.test.yaml -p 8079:8079 -p 8080:8080 -p 8081:8081 -p 8082:8082 -p 8083:8083 -p 8084:8084 -p 8085:8085 -p 8086:8086 envoyproxy/envoy-dev:8a2143613d43d17d1eb35a24b4a4a4c432215606 -c /envoy.test.yaml`
|
||||||
4. In a new terminal, start the funding wallet. This wallet will be automatically funded in order to fund Alice and Bob during the tests.<br>For example: `cd ~/git/haveno && make funding-wallet`.
|
4. In a new terminal, start the funding wallet. This wallet will be automatically funded in order to fund Alice and Bob during the tests.<br>For example: `cd ~/git/haveno && make funding-wallet`.
|
||||||
5. Install protobuf for your system:<br>
|
5. Install protobuf for your system:<br>
|
||||||
mac: `brew install protobuf`<br>
|
mac: `brew install protobuf`<br>
|
||||||
|
|
|
@ -7,6 +7,39 @@ admin:
|
||||||
|
|
||||||
static_resources:
|
static_resources:
|
||||||
listeners:
|
listeners:
|
||||||
|
- name: arbitrator_listener
|
||||||
|
address:
|
||||||
|
socket_address: { address: 0.0.0.0, port_value: 8079 }
|
||||||
|
filter_chains:
|
||||||
|
- filters:
|
||||||
|
- name: envoy.filters.network.http_connection_manager
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
|
||||||
|
codec_type: auto
|
||||||
|
stat_prefix: ingress_http
|
||||||
|
route_config:
|
||||||
|
name: local_route
|
||||||
|
virtual_hosts:
|
||||||
|
- name: local_service
|
||||||
|
domains: ["*"]
|
||||||
|
routes:
|
||||||
|
- match: { prefix: "/" }
|
||||||
|
route:
|
||||||
|
cluster: arbitrator_service
|
||||||
|
timeout: 0s
|
||||||
|
max_stream_duration:
|
||||||
|
grpc_timeout_header_max: 0s
|
||||||
|
cors:
|
||||||
|
allow_origin_string_match:
|
||||||
|
- prefix: "*"
|
||||||
|
allow_methods: GET, PUT, DELETE, POST, OPTIONS
|
||||||
|
allow_headers: password,keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
|
||||||
|
max_age: "1728000"
|
||||||
|
expose_headers: custom-header-1,grpc-status,grpc-message
|
||||||
|
http_filters:
|
||||||
|
- name: envoy.filters.http.grpc_web
|
||||||
|
- name: envoy.filters.http.cors
|
||||||
|
- name: envoy.filters.http.router
|
||||||
- name: alice_listener
|
- name: alice_listener
|
||||||
address:
|
address:
|
||||||
socket_address: { address: 0.0.0.0, port_value: 8080 }
|
socket_address: { address: 0.0.0.0, port_value: 8080 }
|
||||||
|
@ -91,7 +124,7 @@ static_resources:
|
||||||
routes:
|
routes:
|
||||||
- match: { prefix: "/" }
|
- match: { prefix: "/" }
|
||||||
route:
|
route:
|
||||||
cluster: haveno1_service
|
cluster: haveno_service1
|
||||||
timeout: 0s
|
timeout: 0s
|
||||||
max_stream_duration:
|
max_stream_duration:
|
||||||
grpc_timeout_header_max: 0s
|
grpc_timeout_header_max: 0s
|
||||||
|
@ -124,7 +157,7 @@ static_resources:
|
||||||
routes:
|
routes:
|
||||||
- match: { prefix: "/" }
|
- match: { prefix: "/" }
|
||||||
route:
|
route:
|
||||||
cluster: haveno2_service
|
cluster: haveno_service2
|
||||||
timeout: 0s
|
timeout: 0s
|
||||||
max_stream_duration:
|
max_stream_duration:
|
||||||
grpc_timeout_header_max: 0s
|
grpc_timeout_header_max: 0s
|
||||||
|
@ -157,7 +190,7 @@ static_resources:
|
||||||
routes:
|
routes:
|
||||||
- match: { prefix: "/" }
|
- match: { prefix: "/" }
|
||||||
route:
|
route:
|
||||||
cluster: haveno3_service
|
cluster: haveno_service3
|
||||||
timeout: 0s
|
timeout: 0s
|
||||||
max_stream_duration:
|
max_stream_duration:
|
||||||
grpc_timeout_header_max: 0s
|
grpc_timeout_header_max: 0s
|
||||||
|
@ -190,7 +223,7 @@ static_resources:
|
||||||
routes:
|
routes:
|
||||||
- match: { prefix: "/" }
|
- match: { prefix: "/" }
|
||||||
route:
|
route:
|
||||||
cluster: haveno4_service
|
cluster: haveno_service4
|
||||||
timeout: 0s
|
timeout: 0s
|
||||||
max_stream_duration:
|
max_stream_duration:
|
||||||
grpc_timeout_header_max: 0s
|
grpc_timeout_header_max: 0s
|
||||||
|
@ -223,7 +256,7 @@ static_resources:
|
||||||
routes:
|
routes:
|
||||||
- match: { prefix: "/" }
|
- match: { prefix: "/" }
|
||||||
route:
|
route:
|
||||||
cluster: haveno5_service
|
cluster: haveno_service5
|
||||||
timeout: 0s
|
timeout: 0s
|
||||||
max_stream_duration:
|
max_stream_duration:
|
||||||
grpc_timeout_header_max: 0s
|
grpc_timeout_header_max: 0s
|
||||||
|
@ -239,6 +272,20 @@ static_resources:
|
||||||
- name: envoy.filters.http.cors
|
- name: envoy.filters.http.cors
|
||||||
- name: envoy.filters.http.router
|
- name: envoy.filters.http.router
|
||||||
clusters:
|
clusters:
|
||||||
|
- name: arbitrator_service
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: logical_dns
|
||||||
|
http2_protocol_options: {}
|
||||||
|
lb_policy: round_robin
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: cluster_0
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: host.docker.internal
|
||||||
|
port_value: 9998
|
||||||
- name: alice_service
|
- name: alice_service
|
||||||
connect_timeout: 0.25s
|
connect_timeout: 0.25s
|
||||||
type: logical_dns
|
type: logical_dns
|
||||||
|
@ -267,7 +314,7 @@ static_resources:
|
||||||
socket_address:
|
socket_address:
|
||||||
address: host.docker.internal
|
address: host.docker.internal
|
||||||
port_value: 10000
|
port_value: 10000
|
||||||
- name: haveno1_service
|
- name: haveno_service1
|
||||||
connect_timeout: 0.25s
|
connect_timeout: 0.25s
|
||||||
type: logical_dns
|
type: logical_dns
|
||||||
http2_protocol_options: {}
|
http2_protocol_options: {}
|
||||||
|
@ -281,7 +328,7 @@ static_resources:
|
||||||
socket_address:
|
socket_address:
|
||||||
address: host.docker.internal
|
address: host.docker.internal
|
||||||
port_value: 10001
|
port_value: 10001
|
||||||
- name: haveno2_service
|
- name: haveno_service2
|
||||||
connect_timeout: 0.25s
|
connect_timeout: 0.25s
|
||||||
type: logical_dns
|
type: logical_dns
|
||||||
http2_protocol_options: {}
|
http2_protocol_options: {}
|
||||||
|
@ -295,7 +342,7 @@ static_resources:
|
||||||
socket_address:
|
socket_address:
|
||||||
address: host.docker.internal
|
address: host.docker.internal
|
||||||
port_value: 10002
|
port_value: 10002
|
||||||
- name: haveno3_service
|
- name: haveno_service3
|
||||||
connect_timeout: 0.25s
|
connect_timeout: 0.25s
|
||||||
type: logical_dns
|
type: logical_dns
|
||||||
http2_protocol_options: {}
|
http2_protocol_options: {}
|
||||||
|
@ -309,7 +356,7 @@ static_resources:
|
||||||
socket_address:
|
socket_address:
|
||||||
address: host.docker.internal
|
address: host.docker.internal
|
||||||
port_value: 10003
|
port_value: 10003
|
||||||
- name: haveno4_service
|
- name: haveno_service4
|
||||||
connect_timeout: 0.25s
|
connect_timeout: 0.25s
|
||||||
type: logical_dns
|
type: logical_dns
|
||||||
http2_protocol_options: {}
|
http2_protocol_options: {}
|
||||||
|
@ -323,7 +370,7 @@ static_resources:
|
||||||
socket_address:
|
socket_address:
|
||||||
address: host.docker.internal
|
address: host.docker.internal
|
||||||
port_value: 10004
|
port_value: 10004
|
||||||
- name: haveno5_service
|
- name: haveno_service5
|
||||||
connect_timeout: 0.25s
|
connect_timeout: 0.25s
|
||||||
type: logical_dns
|
type: logical_dns
|
||||||
http2_protocol_options: {}
|
http2_protocol_options: {}
|
||||||
|
|
|
@ -18,83 +18,111 @@ const console = require('console'); // import console because jest swallows mess
|
||||||
const assert = require("assert");
|
const assert = require("assert");
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
|
|
||||||
// --------------------------- TEST CONFIGURATION -----------------------------
|
// ------------------------------ TEST CONFIG ---------------------------------
|
||||||
|
|
||||||
// logging options
|
// test config
|
||||||
HavenoUtils.setLogLevel(1); // set log level (gets more verbose increasing from 0)
|
const TestConfig = {
|
||||||
const LOG_PROCESS_OUTPUT = false; // enable or disable logging process output
|
logging: {
|
||||||
|
level: 0, // set log level (gets more verbose increasing from 0)
|
||||||
// path to directory with haveno binaries
|
logProcessOutput: false // enable or disable logging process output
|
||||||
const HAVENO_PATH = "../haveno";
|
},
|
||||||
|
haveno: {
|
||||||
// wallet to fund alice and bob during tests
|
path: "../haveno",
|
||||||
const FUNDING_WALLET_URL = "http://localhost:38084";
|
version: "1.6.2"
|
||||||
const FUNDING_WALLET_USERNAME = "rpc_user";
|
},
|
||||||
const FUNDING_WALLET_PASSWORD = "abc123";
|
monerod: {
|
||||||
const DEFAULT_FUNDING_WALLET_PATH = "test_funding_wallet";
|
url: "http://localhost:38081",
|
||||||
const MINIMUM_FUNDING = BigInt("5000000000000");
|
username: "superuser",
|
||||||
let fundingWallet: any;
|
password: "abctesting123"
|
||||||
|
},
|
||||||
// alice config
|
fundingWallet: {
|
||||||
const HAVENO_VERSION = "1.6.2";
|
url: "http://localhost:38084",
|
||||||
const ALICE_DAEMON_URL = "http://localhost:8080";
|
username: "rpc_user",
|
||||||
const ALICE_DAEMON_PASSWORD = "apitest";
|
password: "abc123",
|
||||||
const ALICE_WALLET_URL = "http://127.0.0.1:38091"; // alice's internal haveno wallet for direct testing
|
defaultPath: "test_funding_wallet",
|
||||||
const ALICE_WALLET_USERNAME = "rpc_user";
|
minimumFunding: BigInt("5000000000000")
|
||||||
const ALICE_WALLET_PASSWORD = "abc123";
|
},
|
||||||
let alice: HavenoDaemon;
|
arbitrator: {
|
||||||
let aliceWallet: any;
|
logProcessOutput: false,
|
||||||
|
appName: "haveno-XMR_STAGENET_arbitrator",
|
||||||
// bob config
|
url: "http://localhost:8079",
|
||||||
const BOB_DAEMON_URL = "http://localhost:8081";
|
password: "apitest",
|
||||||
const BOB_DAEMON_PASSWORD = "apitest";
|
walletUsername: "rpc_user",
|
||||||
let bob: HavenoDaemon;
|
walletPassword: "abc123"
|
||||||
|
},
|
||||||
// monero daemon config
|
alice: {
|
||||||
const MONERO_DAEMON_URL = "http://localhost:38081"
|
logProcessOutput: false,
|
||||||
const MONERO_DAEMON_USERNAME = "superuser";
|
appName: "haveno-XMR_STAGENET_alice",
|
||||||
const MONERO_DAEMON_PASSWORD = "abctesting123";
|
url: "http://localhost:8080",
|
||||||
let monerod: any;
|
password: "apitest",
|
||||||
|
walletUrl: "http://127.0.0.1:38091",
|
||||||
// other test config
|
walletUsername: "rpc_user",
|
||||||
const MAX_FEE = BigInt("75000000000");
|
walletPassword: "abc123"
|
||||||
const WALLET_SYNC_PERIOD = 5000;
|
},
|
||||||
const MAX_TIME_PEER_NOTICE = 3000;
|
bob: {
|
||||||
const TEST_CRYPTO_ACCOUNTS = [ // TODO (woodser): test other cryptos, fiat
|
logProcessOutput: false,
|
||||||
{
|
appName: "haveno-XMR_STAGENET_bob",
|
||||||
|
url: "http://localhost:8081",
|
||||||
|
password: "apitest",
|
||||||
|
},
|
||||||
|
maxFee: BigInt("75000000000"),
|
||||||
|
walletSyncPeriodMs: 5000,
|
||||||
|
maxTimePeerNoticeMs: 3000,
|
||||||
|
cryptoAccounts: [{ // TODO (woodser): test other cryptos, fiat
|
||||||
currencyCode: "ETH",
|
currencyCode: "ETH",
|
||||||
address: "0xdBdAb835Acd6fC84cF5F9aDD3c0B5a1E25fbd99f"
|
address: "0xdBdAb835Acd6fC84cF5F9aDD3c0B5a1E25fbd99f"
|
||||||
},
|
}, {
|
||||||
{
|
|
||||||
currencyCode: "BTC",
|
currencyCode: "BTC",
|
||||||
address: "bcrt1q6j90vywv8x7eyevcnn2tn2wrlg3vsjlsvt46qz"
|
address: "bcrt1q6j90vywv8x7eyevcnn2tn2wrlg3vsjlsvt46qz"
|
||||||
}
|
}
|
||||||
];
|
],
|
||||||
|
proxyPorts: new Map<string, string[]>([ // map proxied ports to havenod api and p2p ports
|
||||||
// map proxied ports to havenod api and p2p ports
|
["8079", ["9998", "4444"]], // arbitrator
|
||||||
const PROXY_PORTS = new Map<string, string[]>([
|
["8080", ["9999", "5555"]], // alice
|
||||||
["8080", ["9999", "5555"]],
|
["8081", ["10000", "6666"]], // bob
|
||||||
["8081", ["10000", "6666"]],
|
|
||||||
["8082", ["10001", "7777"]],
|
["8082", ["10001", "7777"]],
|
||||||
["8083", ["10002", "7778"]],
|
["8083", ["10002", "7778"]],
|
||||||
["8084", ["10003", "7779"]],
|
["8084", ["10003", "7779"]],
|
||||||
["8085", ["10004", "7780"]],
|
["8085", ["10004", "7780"]],
|
||||||
["8086", ["10005", "7781"]],
|
["8086", ["10005", "7781"]],
|
||||||
]);
|
])
|
||||||
|
};
|
||||||
|
|
||||||
|
// clients
|
||||||
|
let arbitrator: HavenoDaemon;
|
||||||
|
let alice: HavenoDaemon;
|
||||||
|
let bob: HavenoDaemon;
|
||||||
|
let monerod: any;
|
||||||
|
let fundingWallet: any;
|
||||||
|
let aliceWallet: any;
|
||||||
|
|
||||||
// track started haveno processes
|
// track started haveno processes
|
||||||
const HAVENO_PROCESSES: HavenoDaemon[] = [];
|
const HAVENO_PROCESSES: HavenoDaemon[] = [];
|
||||||
const HAVENO_PROCESS_PORTS: string[] = [];
|
const HAVENO_PROCESS_PORTS: string[] = [];
|
||||||
|
|
||||||
// ----------------------------------- TESTS ----------------------------------
|
// -------------------------- BEFORE / AFTER TESTS ----------------------------
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
|
||||||
// initialize clients
|
// set log level for tests
|
||||||
alice = new HavenoDaemon(ALICE_DAEMON_URL, ALICE_DAEMON_PASSWORD);
|
HavenoUtils.setLogLevel(TestConfig.logging.level);
|
||||||
bob = new HavenoDaemon(BOB_DAEMON_URL, BOB_DAEMON_PASSWORD);
|
|
||||||
monerod = await monerojs.connectToDaemonRpc(MONERO_DAEMON_URL, MONERO_DAEMON_USERNAME, MONERO_DAEMON_PASSWORD);
|
// connect to arbitrator, alice, and bob or start as child processes
|
||||||
aliceWallet = await monerojs.connectToWalletRpc(ALICE_WALLET_URL, ALICE_WALLET_USERNAME, ALICE_WALLET_PASSWORD);
|
let daemonPromises = [];
|
||||||
|
daemonPromises.push(initHavenoDaemon(TestConfig.arbitrator));
|
||||||
|
daemonPromises.push(initHavenoDaemon(TestConfig.alice));
|
||||||
|
daemonPromises.push(initHavenoDaemon(TestConfig.bob));
|
||||||
|
let daemons = await Promise.allSettled(daemonPromises);
|
||||||
|
if (daemons[0].status === "fulfilled") arbitrator = (daemons[0] as PromiseFulfilledResult<HavenoDaemon>).value;
|
||||||
|
else throw new Error((daemons[0] as PromiseRejectedResult).reason);
|
||||||
|
if (daemons[1].status === "fulfilled") alice = (daemons[1] as PromiseFulfilledResult<HavenoDaemon>).value;
|
||||||
|
else throw new Error((daemons[1] as PromiseRejectedResult).reason);
|
||||||
|
if (daemons[2].status === "fulfilled") bob = (daemons[2] as PromiseFulfilledResult<HavenoDaemon>).value;
|
||||||
|
else throw new Error((daemons[2] as PromiseRejectedResult).reason);
|
||||||
|
|
||||||
|
// connect monero clients
|
||||||
|
monerod = await monerojs.connectToDaemonRpc(TestConfig.monerod.url, TestConfig.monerod.username, TestConfig.monerod.password);
|
||||||
|
aliceWallet = await monerojs.connectToWalletRpc(TestConfig.alice.walletUrl, TestConfig.alice.walletUsername, TestConfig.alice.walletPassword);
|
||||||
|
|
||||||
// initialize funding wallet
|
// initialize funding wallet
|
||||||
await initFundingWallet();
|
await initFundingWallet();
|
||||||
|
@ -106,10 +134,25 @@ beforeAll(async () => {
|
||||||
//console.log((await bob.getBalances()).getUnlockedBalance() + ", " + (await bob.getBalances()).getLockedBalance());
|
//console.log((await bob.getBalances()).getUnlockedBalance() + ", " + (await bob.getBalances()).getLockedBalance());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
beforeEach(async() => {
|
||||||
|
console.log("Before test \"" + expect.getState().currentTestName + "\"");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
let stopPromises = [];
|
||||||
|
if (arbitrator && arbitrator.getProcess()) stopPromises.push(stopHavenoProcess(arbitrator));
|
||||||
|
if (alice && alice.getProcess())stopPromises.push(stopHavenoProcess(alice));
|
||||||
|
if (bob && bob.getProcess())stopPromises.push(stopHavenoProcess(bob));
|
||||||
|
return Promise.all(stopPromises);
|
||||||
|
});
|
||||||
|
|
||||||
jest.setTimeout(400000);
|
jest.setTimeout(400000);
|
||||||
|
|
||||||
|
// ----------------------------------- TESTS ----------------------------------
|
||||||
|
|
||||||
test("Can get the version", async () => {
|
test("Can get the version", async () => {
|
||||||
let version = await alice.getVersion();
|
let version = await arbitrator.getVersion();
|
||||||
expect(version).toEqual(HAVENO_VERSION);
|
expect(version).toEqual(TestConfig.haveno.version);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Can get market prices", async () => {
|
test("Can get market prices", async () => {
|
||||||
|
@ -123,7 +166,7 @@ test("Can get market prices", async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get market prices of specific currencies
|
// get market prices of specific currencies
|
||||||
for (let testAccount of TEST_CRYPTO_ACCOUNTS) {
|
for (let testAccount of TestConfig.cryptoAccounts) {
|
||||||
let price = await alice.getPrice(testAccount.currencyCode);
|
let price = await alice.getPrice(testAccount.currencyCode);
|
||||||
expect(price).toBeGreaterThan(0);
|
expect(price).toBeGreaterThan(0);
|
||||||
}
|
}
|
||||||
|
@ -179,7 +222,7 @@ test("Can get payment accounts", async () => {
|
||||||
test("Can create crypto payment accounts", async () => {
|
test("Can create crypto payment accounts", async () => {
|
||||||
|
|
||||||
// test each stagenet crypto account
|
// test each stagenet crypto account
|
||||||
for (let testAccount of TEST_CRYPTO_ACCOUNTS) {
|
for (let testAccount of TestConfig.cryptoAccounts) {
|
||||||
|
|
||||||
// create payment account
|
// create payment account
|
||||||
let name = testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID();
|
let name = testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID();
|
||||||
|
@ -269,7 +312,7 @@ test("Invalidates offers when reserved funds are spent", async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// offer is available to peers
|
// offer is available to peers
|
||||||
await wait(WALLET_SYNC_PERIOD * 2);
|
await wait(TestConfig.walletSyncPeriodMs * 2);
|
||||||
if (!getOffer(await bob.getOffers("buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posting");
|
if (!getOffer(await bob.getOffers("buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posting");
|
||||||
|
|
||||||
// spend one of offer's reserved outputs
|
// spend one of offer's reserved outputs
|
||||||
|
@ -279,7 +322,7 @@ test("Invalidates offers when reserved funds are spent", async () => {
|
||||||
await monerod.submitTxHex(tx.getFullHex(), true);
|
await monerod.submitTxHex(tx.getFullHex(), true);
|
||||||
|
|
||||||
// wait for spend to be seen
|
// wait for spend to be seen
|
||||||
await wait(WALLET_SYNC_PERIOD * 2); // TODO (woodser): need place for common test utilities
|
await wait(TestConfig.walletSyncPeriodMs * 2); // TODO (woodser): need place for common test utilities
|
||||||
|
|
||||||
// offer is removed from peer offers
|
// offer is removed from peer offers
|
||||||
if (getOffer(await bob.getOffers("buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in peer's offers after reserved funds spent");
|
if (getOffer(await bob.getOffers("buy"), offer.getId())) throw new Error("Offer " + offer.getId() + " was found in peer's offers after reserved funds spent");
|
||||||
|
@ -312,7 +355,7 @@ test("Handles unexpected errors during trade initialization", async () => {
|
||||||
|
|
||||||
// start and fund 3 trader processes
|
// start and fund 3 trader processes
|
||||||
console.log("Starting trader processes");
|
console.log("Starting trader processes");
|
||||||
traders = await startTraderProcesses(3, LOG_PROCESS_OUTPUT);
|
traders = await startHavenoProcesses(3, TestConfig.logging.logProcessOutput);
|
||||||
console.log("Funding traders");
|
console.log("Funding traders");
|
||||||
let tradeAmount: bigint = BigInt("250000000000");
|
let tradeAmount: bigint = BigInt("250000000000");
|
||||||
await waitForUnlockedBalance(tradeAmount * BigInt("2"), traders[0], traders[1], traders[2]);
|
await waitForUnlockedBalance(tradeAmount * BigInt("2"), traders[0], traders[1], traders[2]);
|
||||||
|
@ -324,7 +367,7 @@ test("Handles unexpected errors during trade initialization", async () => {
|
||||||
assert.equal(offer.getState(), "AVAILABLE");
|
assert.equal(offer.getState(), "AVAILABLE");
|
||||||
|
|
||||||
// wait for offer for offer to be seen
|
// wait for offer for offer to be seen
|
||||||
await wait(WALLET_SYNC_PERIOD * 2);
|
await wait(TestConfig.walletSyncPeriodMs * 2);
|
||||||
|
|
||||||
// trader 1 spends trade funds after initializing trade
|
// trader 1 spends trade funds after initializing trade
|
||||||
let paymentAccount = await createCryptoPaymentAccount(traders[1]);
|
let paymentAccount = await createCryptoPaymentAccount(traders[1]);
|
||||||
|
@ -404,7 +447,7 @@ test("Cannot make or take offer with insufficient unlocked funds", async () => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// start charlie
|
// start charlie
|
||||||
charlie = await startTraderProcess(LOG_PROCESS_OUTPUT);
|
charlie = await startHavenoProcess(undefined, TestConfig.logging.logProcessOutput);
|
||||||
|
|
||||||
// charlie creates ethereum payment account
|
// charlie creates ethereum payment account
|
||||||
let paymentAccount = await createCryptoPaymentAccount(charlie);
|
let paymentAccount = await createCryptoPaymentAccount(charlie);
|
||||||
|
@ -428,7 +471,7 @@ test("Cannot make or take offer with insufficient unlocked funds", async () => {
|
||||||
await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice);
|
await waitForUnlockedBalance(tradeAmount * BigInt("2"), alice);
|
||||||
offer = await postOffer(alice, "buy", tradeAmount, undefined);
|
offer = await postOffer(alice, "buy", tradeAmount, undefined);
|
||||||
assert.equal(offer.getState(), "AVAILABLE");
|
assert.equal(offer.getState(), "AVAILABLE");
|
||||||
await wait(WALLET_SYNC_PERIOD * 2);
|
await wait(TestConfig.walletSyncPeriodMs * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// charlie cannot take offer with insufficient funds
|
// charlie cannot take offer with insufficient funds
|
||||||
|
@ -478,7 +521,7 @@ test("Can complete a trade", async () => {
|
||||||
// TODO (woodser): test error message taking offer before posted
|
// TODO (woodser): test error message taking offer before posted
|
||||||
|
|
||||||
// bob sees offer
|
// bob sees offer
|
||||||
await wait(WALLET_SYNC_PERIOD * 2);
|
await wait(TestConfig.walletSyncPeriodMs * 2);
|
||||||
let offerBob = getOffer(await bob.getOffers(direction), offer.getId());
|
let offerBob = getOffer(await bob.getOffers(direction), offer.getId());
|
||||||
if (!offerBob) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posting");
|
if (!offerBob) throw new Error("Offer " + offer.getId() + " was not found in peer's offers after posting");
|
||||||
expect(offerBob.getState()).toEqual("UNKNOWN"); // TODO: offer state is not known?
|
expect(offerBob.getState()).toEqual("UNKNOWN"); // TODO: offer state is not known?
|
||||||
|
@ -496,7 +539,7 @@ test("Can complete a trade", async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// bob creates ethereum payment account
|
// bob creates ethereum payment account
|
||||||
let testAccount = TEST_CRYPTO_ACCOUNTS[0];
|
let testAccount = TestConfig.cryptoAccounts[0];
|
||||||
let ethPaymentAccount: PaymentAccount = await bob.createCryptoPaymentAccount(
|
let ethPaymentAccount: PaymentAccount = await bob.createCryptoPaymentAccount(
|
||||||
testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(),
|
testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(),
|
||||||
testAccount.currencyCode,
|
testAccount.currencyCode,
|
||||||
|
@ -522,7 +565,7 @@ test("Can complete a trade", async () => {
|
||||||
// bob is notified of balance change
|
// bob is notified of balance change
|
||||||
|
|
||||||
// alice notified of balance changes and that offer is taken
|
// alice notified of balance changes and that offer is taken
|
||||||
await wait(MAX_TIME_PEER_NOTICE);
|
await wait(TestConfig.maxTimePeerNoticeMs);
|
||||||
|
|
||||||
// alice can get trade
|
// alice can get trade
|
||||||
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
fetchedTrade = await alice.getTrade(trade.getTradeId());
|
||||||
|
@ -534,7 +577,7 @@ test("Can complete a trade", async () => {
|
||||||
console.log("Done mining to unlock deposit txs");
|
console.log("Done mining to unlock deposit txs");
|
||||||
|
|
||||||
// alice notified to send payment
|
// alice notified to send payment
|
||||||
await wait(WALLET_SYNC_PERIOD * 2);
|
await wait(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?
|
||||||
|
@ -548,7 +591,7 @@ test("Can complete a trade", async () => {
|
||||||
expect(fetchedTrade.getPhase()).toEqual("FIAT_SENT"); // TODO (woodser): rename to PAYMENT_SENT
|
expect(fetchedTrade.getPhase()).toEqual("FIAT_SENT"); // TODO (woodser): rename to PAYMENT_SENT
|
||||||
|
|
||||||
// bob notified payment is sent
|
// bob notified payment is sent
|
||||||
await wait(MAX_TIME_PEER_NOTICE);
|
await wait(TestConfig.maxTimePeerNoticeMs);
|
||||||
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("FIAT_SENT"); // TODO (woodser): rename to PAYMENT_SENT
|
||||||
|
|
||||||
|
@ -558,7 +601,7 @@ test("Can complete a trade", async () => {
|
||||||
expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED");
|
expect(fetchedTrade.getPhase()).toEqual("PAYOUT_PUBLISHED");
|
||||||
|
|
||||||
// alice notified trade is complete and of balance changes
|
// alice notified trade is complete and of balance changes
|
||||||
await wait(WALLET_SYNC_PERIOD * 2);
|
await wait(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");
|
||||||
|
|
||||||
|
@ -567,60 +610,87 @@ test("Can complete a trade", async () => {
|
||||||
bobBalancesAfter = await bob.getBalances();
|
bobBalancesAfter = await bob.getBalances();
|
||||||
let aliceFee = BigInt(aliceBalancesBefore.getBalance()) + tradeAmount - BigInt(aliceBalancesAfter.getBalance());
|
let aliceFee = BigInt(aliceBalancesBefore.getBalance()) + tradeAmount - BigInt(aliceBalancesAfter.getBalance());
|
||||||
let bobFee = BigInt(bobBalancesBefore.getBalance()) - tradeAmount - BigInt(bobBalancesAfter.getBalance());
|
let bobFee = BigInt(bobBalancesBefore.getBalance()) - tradeAmount - BigInt(bobBalancesAfter.getBalance());
|
||||||
expect(aliceFee).toBeLessThanOrEqual(MAX_FEE);
|
expect(aliceFee).toBeLessThanOrEqual(TestConfig.maxFee);
|
||||||
expect(aliceFee).toBeGreaterThan(BigInt("0"));
|
expect(aliceFee).toBeGreaterThan(BigInt("0"));
|
||||||
expect(bobFee).toBeLessThanOrEqual(MAX_FEE);
|
expect(bobFee).toBeLessThanOrEqual(TestConfig.maxFee);
|
||||||
expect(bobFee).toBeGreaterThan(BigInt("0"));
|
expect(bobFee).toBeGreaterThan(BigInt("0"));
|
||||||
});
|
});
|
||||||
|
|
||||||
// ------------------------------- HELPERS ------------------------------------
|
// ------------------------------- HELPERS ------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start Haveno trader daemons as processes.
|
* Initialize arbitrator, alice, or bob by their configuration.
|
||||||
|
*
|
||||||
|
* @param {object} config - for arbitrator, alice, or bob
|
||||||
|
* @return {HavenoDaemon} the created instance
|
||||||
|
*/
|
||||||
|
async function initHavenoDaemon(config: any): Promise<HavenoDaemon> {
|
||||||
|
try {
|
||||||
|
let havenod = new HavenoDaemon(config.url, config.password);
|
||||||
|
await havenod.getVersion();
|
||||||
|
return havenod;
|
||||||
|
} catch (err) {
|
||||||
|
return startHavenoProcess(config.appName, config.logProcessOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start Haveno daemons as processes.
|
||||||
*
|
*
|
||||||
* @param {number} numProcesses - number of trader processes to start
|
* @param {number} numProcesses - number of trader processes to start
|
||||||
* @param {boolean} enableLogging - specifies if process output should be logged
|
* @param {boolean} enableLogging - specifies if process output should be logged
|
||||||
* @return {HavenoDaemon[]} clients connected to the started Haveno processes
|
* @return {HavenoDaemon[]} clients connected to the started Haveno processes
|
||||||
*/
|
*/
|
||||||
async function startTraderProcesses(numProcesses: number, enableLogging: boolean): Promise<HavenoDaemon[]> {
|
async function startHavenoProcesses(numProcesses: number, enableLogging: boolean): Promise<HavenoDaemon[]> {
|
||||||
let traderPromises: Promise<HavenoDaemon>[] = [];
|
let traderPromises: Promise<HavenoDaemon>[] = [];
|
||||||
for (let i = 0; i < numProcesses; i++) traderPromises.push(startTraderProcess(enableLogging));
|
for (let i = 0; i < numProcesses; i++) traderPromises.push(startHavenoProcess(undefined, enableLogging));
|
||||||
return Promise.all(traderPromises);
|
return Promise.all(traderPromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a Haveno trader daemon as a process.
|
* Start a Haveno daemon as a process.
|
||||||
*
|
*
|
||||||
|
* If the appName belongs to the arbitrator, alice, or bob, the process is started using their configured ports.
|
||||||
|
*
|
||||||
|
* @param {string|undefined} appName - the app folder name (default to name with unique id)
|
||||||
* @param {boolean} enableLogging - specifies if process output should be logged
|
* @param {boolean} enableLogging - specifies if process output should be logged
|
||||||
* @return {HavenoDaemon} the client connected to the started Haveno process
|
* @return {HavenoDaemon} the client connected to the started Haveno process
|
||||||
*/
|
*/
|
||||||
async function startTraderProcess(enableLogging: boolean): Promise<HavenoDaemon> {
|
async function startHavenoProcess(appName: string|undefined, enableLogging: boolean): Promise<HavenoDaemon> {
|
||||||
|
if (!appName) appName = "haveno-XMR_STAGENET_instance_" + GenUtils.getUUID();
|
||||||
|
|
||||||
// iterate to find unused proxy port
|
// get proxy port for haveno process
|
||||||
for (let port of Array.from(PROXY_PORTS.keys())) {
|
let proxyPort;
|
||||||
if (port === "8080" || port === "8081") continue; // reserved for alice and bob
|
if (appName === TestConfig.arbitrator.appName) proxyPort = "8079";
|
||||||
|
else if (appName === TestConfig.alice.appName) proxyPort = "8080";
|
||||||
// start haveno process on unused port
|
else if (appName === TestConfig.bob.appName) proxyPort = "8081";
|
||||||
|
else {
|
||||||
|
for (let port of Array.from(TestConfig.proxyPorts.keys())) {
|
||||||
|
if (port === "8079" || port === "8080" || port === "8081") continue; // reserved for arbitrator, alice, and bob
|
||||||
if (!GenUtils.arrayContains(HAVENO_PROCESS_PORTS, port)) {
|
if (!GenUtils.arrayContains(HAVENO_PROCESS_PORTS, port)) {
|
||||||
HAVENO_PROCESS_PORTS.push(port);
|
HAVENO_PROCESS_PORTS.push(port);
|
||||||
let appName = "haveno-XMR_STAGENET_trader_" + GenUtils.getUUID();
|
proxyPort = port;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!proxyPort) throw new Error("No unused test ports available");
|
||||||
|
|
||||||
|
// start haveno process using configured ports if available
|
||||||
let cmd: string[] = [
|
let cmd: string[] = [
|
||||||
"./haveno-daemon",
|
"./haveno-daemon",
|
||||||
"--baseCurrencyNetwork", "XMR_STAGENET",
|
"--baseCurrencyNetwork", "XMR_STAGENET",
|
||||||
"--useLocalhostForP2P", "true",
|
"--useLocalhostForP2P", "true",
|
||||||
"--useDevPrivilegeKeys", "true",
|
"--useDevPrivilegeKeys", "true",
|
||||||
"--nodePort", PROXY_PORTS.get(port)![1],
|
"--nodePort", TestConfig.proxyPorts.get(proxyPort)![1],
|
||||||
"--appName", appName,
|
"--appName", appName,
|
||||||
"--apiPassword", "apitest",
|
"--apiPassword", "apitest",
|
||||||
"--apiPort", PROXY_PORTS.get(port)![0],
|
"--apiPort", TestConfig.proxyPorts.get(proxyPort)![0],
|
||||||
"--walletRpcBindPort", await getFreePort() + ""
|
"--walletRpcBindPort", (proxyPort === "8080" ? new URL(TestConfig.alice.walletUrl).port : await getFreePort()) + "" // use alice's configured wallet rpc port
|
||||||
];
|
];
|
||||||
let havenod = await HavenoDaemon.startProcess(HAVENO_PATH, cmd, "http://localhost:" + port, enableLogging);
|
let havenod = await HavenoDaemon.startProcess(TestConfig.haveno.path, cmd, "http://localhost:" + proxyPort, enableLogging);
|
||||||
HAVENO_PROCESSES.push(havenod);
|
HAVENO_PROCESSES.push(havenod);
|
||||||
return havenod;
|
return havenod;
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error("No unused test ports available");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -653,7 +723,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(FUNDING_WALLET_URL, FUNDING_WALLET_USERNAME, FUNDING_WALLET_PASSWORD);
|
fundingWallet = await monerojs.connectToWalletRpc(TestConfig.fundingWallet.url, TestConfig.fundingWallet.username, TestConfig.fundingWallet.password);
|
||||||
|
|
||||||
// check if wallet is open
|
// check if wallet is open
|
||||||
let walletIsOpen = false
|
let walletIsOpen = false
|
||||||
|
@ -667,7 +737,7 @@ async function initFundingWallet() {
|
||||||
|
|
||||||
// attempt to open funding wallet
|
// attempt to open funding wallet
|
||||||
try {
|
try {
|
||||||
await fundingWallet.openWallet({path: DEFAULT_FUNDING_WALLET_PATH, password: FUNDING_WALLET_PASSWORD});
|
await fundingWallet.openWallet({path: TestConfig.fundingWallet.defaultPath, password: TestConfig.fundingWallet.password});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!(e instanceof monerojs.MoneroRpcError)) throw e;
|
if (!(e instanceof monerojs.MoneroRpcError)) throw e;
|
||||||
|
|
||||||
|
@ -675,7 +745,7 @@ async function initFundingWallet() {
|
||||||
if (e.getCode() === -1) {
|
if (e.getCode() === -1) {
|
||||||
|
|
||||||
// create wallet
|
// create wallet
|
||||||
await fundingWallet.createWallet({path: DEFAULT_FUNDING_WALLET_PATH, password: FUNDING_WALLET_PASSWORD});
|
await fundingWallet.createWallet({path: TestConfig.fundingWallet.defaultPath, password: TestConfig.fundingWallet.password});
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -726,7 +796,7 @@ async function waitForUnlockedBalance(amount: bigint, ...wallets: any[]) {
|
||||||
if (depositNeeded > BigInt("0") && wallet._wallet !== fundingWallet) fundConfig.addDestination(await wallet.getDepositAddress(), depositNeeded * BigInt("10")); // deposit 10 times more than needed
|
if (depositNeeded > BigInt("0") && wallet._wallet !== fundingWallet) fundConfig.addDestination(await wallet.getDepositAddress(), depositNeeded * BigInt("10")); // deposit 10 times more than needed
|
||||||
}
|
}
|
||||||
if (fundConfig.getDestinations()) {
|
if (fundConfig.getDestinations()) {
|
||||||
await waitForUnlockedBalance(MINIMUM_FUNDING, fundingWallet); // TODO (woodser): wait for enough to cover tx amount + fee
|
await waitForUnlockedBalance(TestConfig.fundingWallet.minimumFunding, fundingWallet); // TODO (woodser): wait for enough to cover tx amount + fee
|
||||||
try { await fundingWallet.createTx(fundConfig); }
|
try { await fundingWallet.createTx(fundConfig); }
|
||||||
catch (err) { throw new Error("Error funding wallets: " + err.message); }
|
catch (err) { throw new Error("Error funding wallets: " + err.message); }
|
||||||
}
|
}
|
||||||
|
@ -786,7 +856,7 @@ async function wait(durationMs: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createCryptoPaymentAccount(trader: HavenoDaemon): Promise<PaymentAccount> {
|
async function createCryptoPaymentAccount(trader: HavenoDaemon): Promise<PaymentAccount> {
|
||||||
let testAccount = TEST_CRYPTO_ACCOUNTS[0];
|
let testAccount = TestConfig.cryptoAccounts[0];
|
||||||
let paymentAccount: PaymentAccount = await trader.createCryptoPaymentAccount(
|
let paymentAccount: PaymentAccount = await trader.createCryptoPaymentAccount(
|
||||||
testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(),
|
testAccount.currencyCode + " " + testAccount.address.substr(0, 8) + "... " + GenUtils.getUUID(),
|
||||||
testAccount.currencyCode,
|
testAccount.currencyCode,
|
||||||
|
|
|
@ -98,6 +98,11 @@ class HavenoDaemon {
|
||||||
isResolved = true;
|
isResolved = true;
|
||||||
resolve(daemon);
|
resolve(daemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read error message
|
||||||
|
if (line.indexOf("[HavenoDaemonMain] ERROR") >= 0) {
|
||||||
|
if (!isResolved) await rejectProcess(new Error(line));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle stderr
|
// handle stderr
|
||||||
|
@ -106,23 +111,28 @@ class HavenoDaemon {
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle exit
|
// handle exit
|
||||||
childProcess.on("exit", function(code: any) {
|
childProcess.on("exit", async function(code: any) {
|
||||||
if (!isResolved) reject(new Error("Haveno process terminated with exit code " + code + (output ? ":\n\n" + output : "")));
|
if (!isResolved) await rejectProcess(new Error("Haveno process terminated with exit code " + code + (output ? ":\n\n" + output : "")));
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle error
|
// handle error
|
||||||
childProcess.on("error", function(err: any) {
|
childProcess.on("error", async function(err: any) {
|
||||||
if (err.message.indexOf("ENOENT") >= 0) reject(new Error("haveno-daemon does not exist at path '" + cmd[0] + "'"));
|
if (err.message.indexOf("ENOENT") >= 0) reject(new Error("haveno-daemon does not exist at path '" + cmd[0] + "'"));
|
||||||
if (!isResolved) reject(err);
|
if (!isResolved) await rejectProcess(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle uncaught exception
|
// handle uncaught exception
|
||||||
childProcess.on("uncaughtException", function(err: any, origin: any) {
|
childProcess.on("uncaughtException", async function(err: any, origin: any) {
|
||||||
console.error("Uncaught exception in Haveno process: " + err.message);
|
console.error("Uncaught exception in Haveno process: " + err.message);
|
||||||
console.error(origin);
|
console.error(origin);
|
||||||
reject(err);
|
await rejectProcess(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function rejectProcess(err: any) {
|
||||||
|
await HavenoUtils.kill(childProcess);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
function loggingEnabled(): boolean {
|
function loggingEnabled(): boolean {
|
||||||
return (daemon && daemon._processLogging) || (!daemon && enableLogging);
|
return (daemon && daemon._processLogging) || (!daemon && enableLogging);
|
||||||
}
|
}
|
||||||
|
@ -134,12 +144,16 @@ class HavenoDaemon {
|
||||||
*/
|
*/
|
||||||
async stopProcess(): Promise<void> {
|
async stopProcess(): Promise<void> {
|
||||||
if (this._process === undefined) throw new Error("HavenoDaemon instance not created from new process");
|
if (this._process === undefined) throw new Error("HavenoDaemon instance not created from new process");
|
||||||
let that = this;
|
return HavenoUtils.kill(this._process);
|
||||||
return new Promise(function(resolve, reject) {
|
}
|
||||||
that._process.on("exit", function() { resolve(); });
|
|
||||||
that._process.on("error", function(err: any) { reject(err); });
|
/**
|
||||||
that._process.kill("SIGINT");
|
* Return the process running the haveno daemon.
|
||||||
});
|
*
|
||||||
|
* @return the process running the haveno daemon
|
||||||
|
*/
|
||||||
|
getProcess() {
|
||||||
|
return this._process;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -37,6 +37,21 @@ class HavenoUtils {
|
||||||
static getLogLevel(): number {
|
static getLogLevel(): number {
|
||||||
return HavenoUtils.LOG_LEVEL;
|
return HavenoUtils.LOG_LEVEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kill the given process.
|
||||||
|
*
|
||||||
|
* TODO (woodser): move this to monero-javascript GenUtils.js as common utility
|
||||||
|
*
|
||||||
|
* @param process is the nodejs child process to child
|
||||||
|
*/
|
||||||
|
static async kill(process: any): Promise<void> {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
process.on("exit", function() { resolve(); });
|
||||||
|
process.on("error", function(err: any) { reject(err); });
|
||||||
|
process.kill("SIGINT");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {HavenoUtils};
|
export {HavenoUtils};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue