@argonprotocol/testing 1.1.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +662 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +106 -0
- package/lib/index.d.ts +106 -0
- package/lib/index.js +625 -0
- package/lib/index.js.map +1 -0
- package/package.json +54 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var getFilename = () => fileURLToPath(import.meta.url);
|
|
5
|
+
var getDirname = () => path.dirname(getFilename());
|
|
6
|
+
var __dirname = /* @__PURE__ */ getDirname();
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
import {
|
|
10
|
+
Keyring as Keyring3,
|
|
11
|
+
TxSubmitter as TxSubmitter2
|
|
12
|
+
} from "@argonprotocol/mainchain";
|
|
13
|
+
import { describe } from "vitest";
|
|
14
|
+
import * as process4 from "node:process";
|
|
15
|
+
import HttpProxy from "http-proxy";
|
|
16
|
+
import * as child_process4 from "node:child_process";
|
|
17
|
+
import * as http from "node:http";
|
|
18
|
+
import * as url from "node:url";
|
|
19
|
+
import * as Path5 from "node:path";
|
|
20
|
+
|
|
21
|
+
// src/TestNotary.ts
|
|
22
|
+
import { customAlphabet } from "nanoid";
|
|
23
|
+
import pg from "pg";
|
|
24
|
+
import * as child_process from "node:child_process";
|
|
25
|
+
import {
|
|
26
|
+
Keyring,
|
|
27
|
+
TxSubmitter
|
|
28
|
+
} from "@argonprotocol/mainchain";
|
|
29
|
+
import * as fs from "node:fs";
|
|
30
|
+
import * as readline from "node:readline";
|
|
31
|
+
import * as process2 from "node:process";
|
|
32
|
+
import * as Path from "node:path";
|
|
33
|
+
var { Client: PgClient } = pg;
|
|
34
|
+
var nanoid = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz", 4);
|
|
35
|
+
function createUid() {
|
|
36
|
+
return nanoid();
|
|
37
|
+
}
|
|
38
|
+
var TestNotary = class {
|
|
39
|
+
operator;
|
|
40
|
+
ip = "127.0.0.1";
|
|
41
|
+
registeredPublicKey;
|
|
42
|
+
port;
|
|
43
|
+
containerName;
|
|
44
|
+
proxy;
|
|
45
|
+
#dbName;
|
|
46
|
+
#dbConnectionString;
|
|
47
|
+
#childProcess;
|
|
48
|
+
#stdioInterface;
|
|
49
|
+
get address() {
|
|
50
|
+
if (this.proxy) {
|
|
51
|
+
const url2 = new URL(this.proxy);
|
|
52
|
+
url2.searchParams.set("target", `ws://${this.ip}:${this.port}`);
|
|
53
|
+
return url2.href;
|
|
54
|
+
}
|
|
55
|
+
return `ws://${this.ip}:${this.port}`;
|
|
56
|
+
}
|
|
57
|
+
constructor(dbConnectionString) {
|
|
58
|
+
this.#dbConnectionString = dbConnectionString ?? process2.env.NOTARY_DB_URL ?? "postgres://postgres:postgres@localhost:5432";
|
|
59
|
+
addTeardown(this);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Returns the localhost address of the notary (NOTE: not accessible from containers)
|
|
63
|
+
*/
|
|
64
|
+
async start(options) {
|
|
65
|
+
const { pathToNotaryBin, uuid, mainchainUrl } = options;
|
|
66
|
+
this.operator = new Keyring({ type: "sr25519" }).createFromUri("//Bob");
|
|
67
|
+
this.registeredPublicKey = new Keyring({ type: "ed25519" }).createFromUri(
|
|
68
|
+
"//Ferdie//notary"
|
|
69
|
+
).publicKey;
|
|
70
|
+
let notaryPath = pathToNotaryBin ?? Path.join(projectRoot(), "target/debug/argon-notary");
|
|
71
|
+
if (process2.env.ARGON_USE_DOCKER_BINS) {
|
|
72
|
+
this.containerName = "notary_" + uuid;
|
|
73
|
+
const addHost = process2.env.ADD_DOCKER_HOST ? ` --add-host=host.docker.internal:host-gateway` : "";
|
|
74
|
+
notaryPath = `docker run --rm -p=0:9925${addHost} --name=${this.containerName} -e RUST_LOG=warn ghcr.io/argonprotocol/argon-notary:dev`;
|
|
75
|
+
this.#dbConnectionString = cleanHostForDocker(this.#dbConnectionString);
|
|
76
|
+
} else if (!fs.existsSync(notaryPath)) {
|
|
77
|
+
throw new Error(`Notary binary not found at ${notaryPath}`);
|
|
78
|
+
}
|
|
79
|
+
const client = await this.connect();
|
|
80
|
+
let dbName = "";
|
|
81
|
+
try {
|
|
82
|
+
let tries = 10;
|
|
83
|
+
while (tries > 0) {
|
|
84
|
+
dbName = `notary_${uuid}`;
|
|
85
|
+
const result2 = await client.query(
|
|
86
|
+
"SELECT 1 FROM pg_database WHERE datname = $1",
|
|
87
|
+
[dbName]
|
|
88
|
+
);
|
|
89
|
+
if (result2.rowCount === 0) {
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
tries -= 1;
|
|
93
|
+
}
|
|
94
|
+
this.#dbName = dbName;
|
|
95
|
+
await client.query(`CREATE DATABASE "${dbName}"`);
|
|
96
|
+
} finally {
|
|
97
|
+
await client.end();
|
|
98
|
+
}
|
|
99
|
+
let result = child_process.execSync(
|
|
100
|
+
`${notaryPath} migrate --db-url ${this.#dbConnectionString}/${this.#dbName}`,
|
|
101
|
+
{
|
|
102
|
+
encoding: "utf-8"
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
if (result.trim().length) {
|
|
106
|
+
console.log(result.trim());
|
|
107
|
+
}
|
|
108
|
+
console.log(
|
|
109
|
+
"Notary >> connecting to mainchain '%s', db %s",
|
|
110
|
+
mainchainUrl,
|
|
111
|
+
`${this.#dbConnectionString}/${this.#dbName}`
|
|
112
|
+
);
|
|
113
|
+
const bucketName = `notary-${uuid}`;
|
|
114
|
+
const execArgs = [
|
|
115
|
+
"run",
|
|
116
|
+
`--db-url=${this.#dbConnectionString}/${this.#dbName}`,
|
|
117
|
+
`--dev`,
|
|
118
|
+
`-t ${mainchainUrl}`,
|
|
119
|
+
`--archive-bucket=${bucketName}`,
|
|
120
|
+
`--operator-address=${this.operator.address}`
|
|
121
|
+
];
|
|
122
|
+
if (process2.env.ARGON_USE_DOCKER_BINS) {
|
|
123
|
+
process2.env.AWS_S3_ENDPOINT = "http://host.docker.internal:9000";
|
|
124
|
+
execArgs.unshift(...notaryPath.replace("docker run", "run").split(" "));
|
|
125
|
+
execArgs.push("-b=0.0.0.0:9925");
|
|
126
|
+
notaryPath = "docker";
|
|
127
|
+
}
|
|
128
|
+
if (process2.env.AWS_S3_ENDPOINT) {
|
|
129
|
+
execArgs.push(`--archive-endpoint=${process2.env.AWS_S3_ENDPOINT}`);
|
|
130
|
+
}
|
|
131
|
+
this.#childProcess = child_process.spawn(notaryPath, execArgs, {
|
|
132
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
133
|
+
env: { ...process2.env, RUST_LOG: "warn" }
|
|
134
|
+
});
|
|
135
|
+
this.#childProcess.stdout.setEncoding("utf8");
|
|
136
|
+
this.#childProcess.stderr.setEncoding("utf8");
|
|
137
|
+
this.port = await new Promise((resolve3, reject) => {
|
|
138
|
+
const onProcessError = (err) => {
|
|
139
|
+
console.warn("Error running notary", err);
|
|
140
|
+
reject(err);
|
|
141
|
+
};
|
|
142
|
+
this.#childProcess.once("error", onProcessError);
|
|
143
|
+
this.#childProcess.stderr.on("data", (data) => {
|
|
144
|
+
console.warn("Notary >> %s", data);
|
|
145
|
+
if (data.startsWith("WARNING")) return;
|
|
146
|
+
this.#childProcess.off("error", onProcessError);
|
|
147
|
+
reject(data);
|
|
148
|
+
});
|
|
149
|
+
this.#stdioInterface = readline.createInterface({ input: this.#childProcess.stdout }).on("line", (line) => {
|
|
150
|
+
console.log("Notary >> %s", line);
|
|
151
|
+
let match = line.match(/Listening on ([ws:/\d.]+)/);
|
|
152
|
+
if (match?.length ?? 0 > 0) {
|
|
153
|
+
resolve3(match[1].split(":").pop());
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
this.#childProcess.on("error", (err) => {
|
|
158
|
+
throw err;
|
|
159
|
+
});
|
|
160
|
+
if (this.containerName) {
|
|
161
|
+
this.port = await getDockerPortMapping(this.containerName, 9925);
|
|
162
|
+
this.proxy = cleanHostForDocker(await getProxy());
|
|
163
|
+
}
|
|
164
|
+
return this.address;
|
|
165
|
+
}
|
|
166
|
+
async register(client) {
|
|
167
|
+
let address = new URL(this.address);
|
|
168
|
+
await new TxSubmitter(
|
|
169
|
+
client,
|
|
170
|
+
client.tx.notaries.propose({
|
|
171
|
+
public: this.registeredPublicKey,
|
|
172
|
+
hosts: [address.href],
|
|
173
|
+
name: "Test Notary"
|
|
174
|
+
}),
|
|
175
|
+
this.operator
|
|
176
|
+
).submit({ waitForBlock: true });
|
|
177
|
+
}
|
|
178
|
+
async teardown() {
|
|
179
|
+
this.#childProcess?.kill();
|
|
180
|
+
this.#stdioInterface?.close();
|
|
181
|
+
const client = await this.connect();
|
|
182
|
+
try {
|
|
183
|
+
await client.query(`DROP DATABASE "${this.#dbName}" WITH (FORCE)`);
|
|
184
|
+
} finally {
|
|
185
|
+
await client.end();
|
|
186
|
+
}
|
|
187
|
+
if (this.containerName) {
|
|
188
|
+
try {
|
|
189
|
+
child_process.execSync(`docker rm -f ${this.containerName}`);
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async connect() {
|
|
195
|
+
const client = new PgClient({ connectionString: this.#dbConnectionString });
|
|
196
|
+
try {
|
|
197
|
+
await client.connect();
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.error("ERROR connecting to postgres client", err);
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
return client;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// src/TestMainchain.ts
|
|
207
|
+
import * as fs2 from "node:fs";
|
|
208
|
+
import { execSync as execSync2, spawn as spawn2 } from "node:child_process";
|
|
209
|
+
import * as Path2 from "node:path";
|
|
210
|
+
import * as readline2 from "node:readline";
|
|
211
|
+
import { detectPort } from "detect-port";
|
|
212
|
+
import { customAlphabet as customAlphabet2 } from "nanoid";
|
|
213
|
+
import Client from "bitcoin-core";
|
|
214
|
+
import { getClient } from "@argonprotocol/mainchain";
|
|
215
|
+
var nanoid2 = customAlphabet2("0123456789abcdefghijklmnopqrstuvwxyz", 4);
|
|
216
|
+
var TestMainchain = class {
|
|
217
|
+
ip = "127.0.0.1";
|
|
218
|
+
port;
|
|
219
|
+
loglevel = "warn";
|
|
220
|
+
uuid;
|
|
221
|
+
#binPath;
|
|
222
|
+
#process;
|
|
223
|
+
#interfaces = [];
|
|
224
|
+
containerName;
|
|
225
|
+
proxy;
|
|
226
|
+
#bitcoind;
|
|
227
|
+
bitcoinPort;
|
|
228
|
+
get address() {
|
|
229
|
+
if (this.proxy) {
|
|
230
|
+
const url2 = new URL(this.proxy);
|
|
231
|
+
url2.searchParams.set("target", `ws://${this.ip}:${this.port}`);
|
|
232
|
+
return url2.href;
|
|
233
|
+
}
|
|
234
|
+
return `ws://${this.ip}:${this.port}`;
|
|
235
|
+
}
|
|
236
|
+
constructor(binPath) {
|
|
237
|
+
this.#binPath = binPath ?? Path2.join(projectRoot(), `target/debug/argon-node`);
|
|
238
|
+
this.#binPath = Path2.resolve(this.#binPath);
|
|
239
|
+
if (!process.env.ARGON_USE_DOCKER_BINS && !fs2.existsSync(this.#binPath)) {
|
|
240
|
+
throw new Error(`Mainchain binary not found at ${this.#binPath}`);
|
|
241
|
+
}
|
|
242
|
+
this.uuid = createUid();
|
|
243
|
+
addTeardown(this);
|
|
244
|
+
}
|
|
245
|
+
getBitcoinClient() {
|
|
246
|
+
return new Client({
|
|
247
|
+
username: "bitcoin",
|
|
248
|
+
password: "bitcoin",
|
|
249
|
+
host: `http://localhost:${this.bitcoinPort}`
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Launch and return the localhost url. NOTE: this url will not work cross-docker. You need to use the containerAddress property
|
|
254
|
+
* @param options
|
|
255
|
+
* @param options.miningThreads - number of threads to use for mining
|
|
256
|
+
* @param options.bootnodes - bootnodes to use for the mainchain
|
|
257
|
+
*/
|
|
258
|
+
async launch(options) {
|
|
259
|
+
const {
|
|
260
|
+
miningThreads = 2,
|
|
261
|
+
bootnodes,
|
|
262
|
+
author = "alice",
|
|
263
|
+
launchBitcoin = false
|
|
264
|
+
} = options ?? {};
|
|
265
|
+
let port = 0;
|
|
266
|
+
let rpcPort = 0;
|
|
267
|
+
let execArgs = [];
|
|
268
|
+
let containerName;
|
|
269
|
+
if (process.env.ARGON_USE_DOCKER_BINS) {
|
|
270
|
+
containerName = "miner_" + nanoid2();
|
|
271
|
+
this.containerName = containerName;
|
|
272
|
+
this.#binPath = "docker";
|
|
273
|
+
port = 33344;
|
|
274
|
+
rpcPort = 9944;
|
|
275
|
+
execArgs = [
|
|
276
|
+
"run",
|
|
277
|
+
"--rm",
|
|
278
|
+
`--name=${containerName}`,
|
|
279
|
+
`-p=0:${port}`,
|
|
280
|
+
`-p=0:${rpcPort}`,
|
|
281
|
+
"-e",
|
|
282
|
+
`RUST_LOG=${this.loglevel},sc_rpc_server=info`,
|
|
283
|
+
"ghcr.io/argonprotocol/argon-miner:dev"
|
|
284
|
+
];
|
|
285
|
+
if (process.env.ADD_DOCKER_HOST) {
|
|
286
|
+
execArgs.splice(2, 0, `--add-host=host.docker.internal:host-gateway`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const bitcoinRpcUrl = await this.startBitcoin(launchBitcoin);
|
|
290
|
+
execArgs.push(
|
|
291
|
+
"--dev",
|
|
292
|
+
"--validator",
|
|
293
|
+
`--${author}`,
|
|
294
|
+
`--compute-miners=${miningThreads}`,
|
|
295
|
+
`--port=${port}`,
|
|
296
|
+
`--rpc-port=${rpcPort}`,
|
|
297
|
+
"--rpc-external",
|
|
298
|
+
"--unsafe-rpc-external",
|
|
299
|
+
"--rpc-methods=unsafe",
|
|
300
|
+
`--bitcoin-rpc-url=${bitcoinRpcUrl}`,
|
|
301
|
+
`--notebook-archive-hosts=http://127.0.0.1:9000/${this.uuid}`
|
|
302
|
+
);
|
|
303
|
+
if (bootnodes) {
|
|
304
|
+
execArgs.push(`--bootnodes=${bootnodes}`);
|
|
305
|
+
}
|
|
306
|
+
this.#process = spawn2(this.#binPath, execArgs, {
|
|
307
|
+
stdio: ["ignore", "pipe", "pipe", "ignore"],
|
|
308
|
+
env: { ...process.env, RUST_LOG: `${this.loglevel},sc_rpc_server=info` }
|
|
309
|
+
});
|
|
310
|
+
this.#process.stderr.setEncoding("utf8");
|
|
311
|
+
this.#process.stdout.setEncoding("utf8");
|
|
312
|
+
this.#process.stdout.on("data", (data) => {
|
|
313
|
+
console.log("Main >> %s", data);
|
|
314
|
+
});
|
|
315
|
+
const int1 = readline2.createInterface({ input: this.#process.stdout }).on("line", (line) => {
|
|
316
|
+
if (line) console.log("Main >> %s", line);
|
|
317
|
+
});
|
|
318
|
+
this.#interfaces.push(int1);
|
|
319
|
+
this.port = await new Promise((resolve3, reject) => {
|
|
320
|
+
this.#process.on("error", (err) => {
|
|
321
|
+
console.warn("Error running mainchain", err);
|
|
322
|
+
reject(err);
|
|
323
|
+
});
|
|
324
|
+
const int2 = readline2.createInterface({ input: this.#process.stderr }).on("line", (line) => {
|
|
325
|
+
console.log("Main >> %s", line);
|
|
326
|
+
let match = line.match(/Running JSON-RPC server: addr=([\d.:]+)/);
|
|
327
|
+
if (match) {
|
|
328
|
+
let ipv4 = match[1].split(",").at(0);
|
|
329
|
+
resolve3(ipv4.split(":").pop());
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
this.#interfaces.push(int2);
|
|
333
|
+
});
|
|
334
|
+
if (this.containerName) {
|
|
335
|
+
this.port = await getDockerPortMapping(this.containerName, rpcPort);
|
|
336
|
+
this.proxy = cleanHostForDocker(await getProxy());
|
|
337
|
+
}
|
|
338
|
+
console.log(`argon Node listening at ${this.address}`);
|
|
339
|
+
return this.address;
|
|
340
|
+
}
|
|
341
|
+
async client() {
|
|
342
|
+
const client = await getClient(this.address);
|
|
343
|
+
disconnectOnTeardown(client);
|
|
344
|
+
return client;
|
|
345
|
+
}
|
|
346
|
+
async bootAddress() {
|
|
347
|
+
const client = await this.client();
|
|
348
|
+
const bootAddress = await client.rpc.system.localListenAddresses();
|
|
349
|
+
for (const address of bootAddress) {
|
|
350
|
+
const addr = address.toString();
|
|
351
|
+
if (addr.includes("127.0.0.1")) {
|
|
352
|
+
return addr;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return void 0;
|
|
356
|
+
}
|
|
357
|
+
async teardown() {
|
|
358
|
+
if (process.env.ARGON_USE_DOCKER_BINS) {
|
|
359
|
+
try {
|
|
360
|
+
execSync2(`docker rm -f ${this.containerName}`);
|
|
361
|
+
} catch {
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const launchedProcess = this.#process;
|
|
365
|
+
if (launchedProcess) {
|
|
366
|
+
launchedProcess?.kill();
|
|
367
|
+
try {
|
|
368
|
+
launchedProcess.stdio.forEach((io) => io?.destroy());
|
|
369
|
+
} catch {
|
|
370
|
+
}
|
|
371
|
+
launchedProcess.unref();
|
|
372
|
+
}
|
|
373
|
+
this.#process?.kill();
|
|
374
|
+
this.#process?.unref();
|
|
375
|
+
this.#bitcoind?.kill();
|
|
376
|
+
this.#bitcoind?.unref();
|
|
377
|
+
for (const i of this.#interfaces) {
|
|
378
|
+
i.close();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
async startBitcoin(launchBitcoin) {
|
|
382
|
+
let rpcPort = 14338;
|
|
383
|
+
if (launchBitcoin) {
|
|
384
|
+
rpcPort = await detectPort();
|
|
385
|
+
const path2 = execSync2(
|
|
386
|
+
Path2.join(projectRoot(), `target/debug/argon-testing-bitcoin`),
|
|
387
|
+
{
|
|
388
|
+
encoding: "utf8"
|
|
389
|
+
}
|
|
390
|
+
).trim();
|
|
391
|
+
const tmpDir = fs2.mkdtempSync("/tmp/argon-bitcoin-" + this.uuid);
|
|
392
|
+
this.#bitcoind = spawn2(
|
|
393
|
+
path2,
|
|
394
|
+
[
|
|
395
|
+
"-regtest",
|
|
396
|
+
"-fallbackfee=0.0001",
|
|
397
|
+
"-listen=0",
|
|
398
|
+
`-datadir=${tmpDir}`,
|
|
399
|
+
"-blockfilterindex",
|
|
400
|
+
"-txindex",
|
|
401
|
+
`-rpcport=${rpcPort}`,
|
|
402
|
+
"-rpcuser=bitcoin",
|
|
403
|
+
"-rpcpassword=bitcoin"
|
|
404
|
+
],
|
|
405
|
+
{
|
|
406
|
+
stdio: ["ignore", "inherit", "inherit", "ignore"]
|
|
407
|
+
}
|
|
408
|
+
);
|
|
409
|
+
addTeardown({
|
|
410
|
+
async teardown() {
|
|
411
|
+
await fs2.promises.rm(tmpDir, {
|
|
412
|
+
recursive: true,
|
|
413
|
+
force: true
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
this.bitcoinPort = rpcPort;
|
|
419
|
+
return cleanHostForDocker(`http://bitcoin:bitcoin@localhost:${rpcPort}`);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// src/TestBitcoinCli.ts
|
|
424
|
+
import * as child_process2 from "node:child_process";
|
|
425
|
+
import * as Path3 from "node:path";
|
|
426
|
+
var TestBitcoinCli = class {
|
|
427
|
+
/**
|
|
428
|
+
* Returns the localhost address of the notary (NOTE: not accessible from containers)
|
|
429
|
+
*/
|
|
430
|
+
static run(command) {
|
|
431
|
+
const binPath = Path3.join(
|
|
432
|
+
`${projectRoot()}`,
|
|
433
|
+
"target/debug/argon-bitcoin-cli"
|
|
434
|
+
);
|
|
435
|
+
try {
|
|
436
|
+
return child_process2.execSync(`${binPath} ${command}`, {
|
|
437
|
+
encoding: "utf8"
|
|
438
|
+
}).trim();
|
|
439
|
+
} catch (e) {
|
|
440
|
+
console.error(`Error running command: ${command}`);
|
|
441
|
+
console.error(e.stdout);
|
|
442
|
+
throw e;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// src/TestOracle.ts
|
|
448
|
+
import * as child_process3 from "node:child_process";
|
|
449
|
+
import { Keyring as Keyring2 } from "@argonprotocol/mainchain";
|
|
450
|
+
import * as fs3 from "node:fs";
|
|
451
|
+
import * as readline3 from "node:readline";
|
|
452
|
+
import * as process3 from "node:process";
|
|
453
|
+
import * as Path4 from "node:path";
|
|
454
|
+
var TestOracle = class _TestOracle {
|
|
455
|
+
static BitcoinOperator = "//Dave";
|
|
456
|
+
static PriceIndexOperator = "//Eve";
|
|
457
|
+
operator;
|
|
458
|
+
port;
|
|
459
|
+
#childProcess;
|
|
460
|
+
#stdioInterface;
|
|
461
|
+
constructor() {
|
|
462
|
+
addTeardown(this);
|
|
463
|
+
}
|
|
464
|
+
async start(service, options) {
|
|
465
|
+
const { pathToBin, mainchainUrl, bitcoinRpcUrl } = options;
|
|
466
|
+
const operatorSuri = service == "bitcoin" ? _TestOracle.BitcoinOperator : _TestOracle.PriceIndexOperator;
|
|
467
|
+
this.operator = new Keyring2({ type: "sr25519" }).createFromUri(
|
|
468
|
+
operatorSuri
|
|
469
|
+
);
|
|
470
|
+
const binPath = pathToBin ?? Path4.join(projectRoot(), "target/debug/argon-oracle");
|
|
471
|
+
if (!fs3.existsSync(binPath)) {
|
|
472
|
+
throw new Error(`Oracle binary not found at ${binPath}`);
|
|
473
|
+
}
|
|
474
|
+
console.log(`Starting ${service} oracle`);
|
|
475
|
+
const execArgs = ["--dev", "-t", mainchainUrl, service];
|
|
476
|
+
if (service == "bitcoin") {
|
|
477
|
+
if (!bitcoinRpcUrl) {
|
|
478
|
+
throw new Error("Bitcoin RPC URL is required for bitcoin oracle");
|
|
479
|
+
}
|
|
480
|
+
execArgs.push("--bitcoin-rpc-url", bitcoinRpcUrl);
|
|
481
|
+
} else {
|
|
482
|
+
execArgs.push("--simulate-prices");
|
|
483
|
+
}
|
|
484
|
+
this.#childProcess = child_process3.spawn(binPath, execArgs, {
|
|
485
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
486
|
+
env: { ...process3.env, RUST_LOG: "info", ...options.env }
|
|
487
|
+
});
|
|
488
|
+
this.#childProcess.stdout.setEncoding("utf8");
|
|
489
|
+
this.#childProcess.stderr.setEncoding("utf8");
|
|
490
|
+
this.#childProcess.stderr.on("data", (data) => {
|
|
491
|
+
console.warn("%sOracle >> %s", service, data);
|
|
492
|
+
});
|
|
493
|
+
this.#stdioInterface = readline3.createInterface({ input: this.#childProcess.stdout }).on("line", (line) => {
|
|
494
|
+
console.log("%sOracle >> %s", service, line);
|
|
495
|
+
});
|
|
496
|
+
this.#childProcess.on("error", (err) => {
|
|
497
|
+
throw err;
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
async teardown() {
|
|
501
|
+
this.#childProcess?.kill();
|
|
502
|
+
this.#stdioInterface?.close();
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// src/index.ts
|
|
507
|
+
var toTeardown = [];
|
|
508
|
+
var proxy = null;
|
|
509
|
+
var proxyServer = null;
|
|
510
|
+
var describeIntegration = describe;
|
|
511
|
+
if (process4.env.SKIP_E2E === "true" || process4.env.SKIP_E2E === "1") {
|
|
512
|
+
describeIntegration = describe.skip;
|
|
513
|
+
}
|
|
514
|
+
async function getProxy() {
|
|
515
|
+
if (!proxy) {
|
|
516
|
+
proxy = HttpProxy.createProxyServer({
|
|
517
|
+
changeOrigin: true,
|
|
518
|
+
ws: true,
|
|
519
|
+
autoRewrite: true
|
|
520
|
+
});
|
|
521
|
+
proxy.on("error", () => null);
|
|
522
|
+
proxyServer = http.createServer(function(req, res) {
|
|
523
|
+
const queryData = url.parse(req.url, true).query;
|
|
524
|
+
if (!queryData.target) {
|
|
525
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
526
|
+
res.end("Target parameter is required");
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
console.log("Proxying http request", queryData.target);
|
|
530
|
+
proxy?.web(req, res, { target: queryData.target });
|
|
531
|
+
});
|
|
532
|
+
proxyServer.on("upgrade", function(req, clientSocket, head) {
|
|
533
|
+
const queryData = url.parse(req.url, true).query;
|
|
534
|
+
const target = url.parse(queryData.target);
|
|
535
|
+
proxy?.ws(req, clientSocket, head, {
|
|
536
|
+
target: target.href,
|
|
537
|
+
ws: true
|
|
538
|
+
});
|
|
539
|
+
clientSocket.on("error", console.error);
|
|
540
|
+
});
|
|
541
|
+
await new Promise((resolve3) => proxyServer.listen(0, resolve3));
|
|
542
|
+
toTeardown.push({
|
|
543
|
+
teardown: () => new Promise((resolve3) => {
|
|
544
|
+
proxy?.close();
|
|
545
|
+
proxyServer?.close((_) => null);
|
|
546
|
+
proxy = null;
|
|
547
|
+
proxyServer = null;
|
|
548
|
+
resolve3();
|
|
549
|
+
})
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
const port = proxyServer.address().port;
|
|
553
|
+
return `ws://host.docker.internal:${port}`;
|
|
554
|
+
}
|
|
555
|
+
function projectRoot() {
|
|
556
|
+
if (process4.env.ARGON_PROJECT_ROOT) {
|
|
557
|
+
return Path5.join(process4.env.ARGON_PROJECT_ROOT);
|
|
558
|
+
}
|
|
559
|
+
return Path5.join(__dirname, `../../..`);
|
|
560
|
+
}
|
|
561
|
+
async function runTestScript(relativePath) {
|
|
562
|
+
const scriptPath = Path5.resolve(projectRoot(), relativePath);
|
|
563
|
+
return child_process4.execSync(scriptPath, { encoding: "utf8" }).trim();
|
|
564
|
+
}
|
|
565
|
+
async function getDockerPortMapping(containerName, port) {
|
|
566
|
+
return child_process4.execSync(`docker port ${containerName} ${port}`, { encoding: "utf8" }).trim().split(":").pop();
|
|
567
|
+
}
|
|
568
|
+
async function teardown() {
|
|
569
|
+
for (const t of toTeardown) {
|
|
570
|
+
try {
|
|
571
|
+
await t.teardown().catch(console.error);
|
|
572
|
+
} catch {
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
toTeardown.length = 0;
|
|
576
|
+
}
|
|
577
|
+
function cleanHostForDocker(host, replacer = "host.docker.internal") {
|
|
578
|
+
if (process4.env.ARGON_USE_DOCKER_BINS) {
|
|
579
|
+
return host.replace("localhost", replacer).replace("127.0.0.1", replacer).replace("0.0.0.0", replacer);
|
|
580
|
+
}
|
|
581
|
+
return host;
|
|
582
|
+
}
|
|
583
|
+
function addTeardown(teardownable) {
|
|
584
|
+
toTeardown.push(teardownable);
|
|
585
|
+
}
|
|
586
|
+
function closeOnTeardown(closeable) {
|
|
587
|
+
addTeardown({ teardown: () => closeable.close() });
|
|
588
|
+
return closeable;
|
|
589
|
+
}
|
|
590
|
+
function disconnectOnTeardown(closeable) {
|
|
591
|
+
addTeardown({ teardown: () => closeable.disconnect() });
|
|
592
|
+
return closeable;
|
|
593
|
+
}
|
|
594
|
+
function sudo() {
|
|
595
|
+
return new Keyring3({ type: "sr25519" }).createFromUri("//Alice");
|
|
596
|
+
}
|
|
597
|
+
async function activateNotary(sudo2, client, notary) {
|
|
598
|
+
await notary.register(client);
|
|
599
|
+
await new TxSubmitter2(
|
|
600
|
+
client,
|
|
601
|
+
client.tx.sudo.sudo(
|
|
602
|
+
client.tx.notaries.activate(notary.operator.publicKey)
|
|
603
|
+
),
|
|
604
|
+
sudo2
|
|
605
|
+
).submit({ waitForBlock: true });
|
|
606
|
+
}
|
|
607
|
+
export {
|
|
608
|
+
TestBitcoinCli,
|
|
609
|
+
TestMainchain,
|
|
610
|
+
TestNotary,
|
|
611
|
+
TestOracle,
|
|
612
|
+
activateNotary,
|
|
613
|
+
addTeardown,
|
|
614
|
+
cleanHostForDocker,
|
|
615
|
+
closeOnTeardown,
|
|
616
|
+
describeIntegration,
|
|
617
|
+
disconnectOnTeardown,
|
|
618
|
+
getDockerPortMapping,
|
|
619
|
+
getProxy,
|
|
620
|
+
projectRoot,
|
|
621
|
+
runTestScript,
|
|
622
|
+
sudo,
|
|
623
|
+
teardown
|
|
624
|
+
};
|
|
625
|
+
//# sourceMappingURL=index.js.map
|