@argonprotocol/testing 1.4.3-dev.1f0d7a33 → 1.4.3-dev.25eec055
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/ethereum-contracts/ArgonToken.json +671 -0
- package/lib/ethereum-contracts/ArgonotToken.json +671 -0
- package/lib/ethereum-contracts/MintingGateway.json +1691 -0
- package/lib/ethereum-contracts/ProxyAdmin.json +135 -0
- package/lib/ethereum-contracts/TransparentUpgradeableProxy.json +131 -0
- package/lib/index.cjs +543 -17
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +96 -3
- package/lib/index.d.ts +96 -3
- package/lib/index.js +544 -17
- package/lib/index.js.map +1 -1
- package/package.json +5 -6
package/lib/index.js
CHANGED
|
@@ -10,9 +10,9 @@ import { Keyring as Keyring3, TxSubmitter as TxSubmitter2 } from "@argonprotocol
|
|
|
10
10
|
import * as process4 from "process";
|
|
11
11
|
import HttpProxy from "http-proxy";
|
|
12
12
|
import * as child_process4 from "child_process";
|
|
13
|
-
import * as
|
|
13
|
+
import * as http2 from "http";
|
|
14
14
|
import * as url from "url";
|
|
15
|
-
import * as
|
|
15
|
+
import * as Path7 from "path";
|
|
16
16
|
|
|
17
17
|
// src/TestNotary.ts
|
|
18
18
|
import { customAlphabet } from "nanoid";
|
|
@@ -201,7 +201,6 @@ import * as Path2 from "path";
|
|
|
201
201
|
import * as readline2 from "readline";
|
|
202
202
|
import { detectPort } from "detect-port";
|
|
203
203
|
import { customAlphabet as customAlphabet2 } from "nanoid";
|
|
204
|
-
import Client from "bitcoin-core";
|
|
205
204
|
import * as lockfile from "proper-lockfile";
|
|
206
205
|
import { getClient } from "@argonprotocol/mainchain";
|
|
207
206
|
var nanoid2 = customAlphabet2("0123456789abcdefghijklmnopqrstuvwxyz", 4);
|
|
@@ -237,11 +236,7 @@ var TestMainchain = class {
|
|
|
237
236
|
addTeardown(this);
|
|
238
237
|
}
|
|
239
238
|
getBitcoinClient() {
|
|
240
|
-
return new
|
|
241
|
-
username: "bitcoin",
|
|
242
|
-
password: "bitcoin",
|
|
243
|
-
host: `http://localhost:${this.bitcoinPort}`
|
|
244
|
-
});
|
|
239
|
+
return new BitcoinRpcClient(`http://localhost:${this.bitcoinPort}`, "bitcoin", "bitcoin");
|
|
245
240
|
}
|
|
246
241
|
/**
|
|
247
242
|
* Launch and return the localhost url. NOTE: this url will not work cross-docker. You need to use the containerAddress property
|
|
@@ -331,9 +326,20 @@ var TestMainchain = class {
|
|
|
331
326
|
return this.address;
|
|
332
327
|
}
|
|
333
328
|
async client() {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
329
|
+
let lastError;
|
|
330
|
+
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
331
|
+
try {
|
|
332
|
+
const client = await getClient(this.address);
|
|
333
|
+
disconnectOnTeardown(client);
|
|
334
|
+
return client;
|
|
335
|
+
} catch (error) {
|
|
336
|
+
lastError = error;
|
|
337
|
+
await new Promise((resolve3) => setTimeout(resolve3, 250));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
throw new Error(`Unable to connect to mainchain client at ${this.address}`, {
|
|
341
|
+
cause: lastError instanceof Error ? lastError : void 0
|
|
342
|
+
});
|
|
337
343
|
}
|
|
338
344
|
async bootAddress() {
|
|
339
345
|
const client = await this.client();
|
|
@@ -424,6 +430,51 @@ var TestMainchain = class {
|
|
|
424
430
|
return cleanHostForDocker(`http://bitcoin:bitcoin@localhost:${rpcPort}`);
|
|
425
431
|
}
|
|
426
432
|
};
|
|
433
|
+
var BitcoinRpcClient = class {
|
|
434
|
+
#rpcUrl;
|
|
435
|
+
#authorization;
|
|
436
|
+
constructor(rpcUrl, username, password) {
|
|
437
|
+
this.#rpcUrl = rpcUrl;
|
|
438
|
+
this.#authorization = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
|
|
439
|
+
}
|
|
440
|
+
async command(method, ...params) {
|
|
441
|
+
const response = await fetch(this.#rpcUrl, {
|
|
442
|
+
method: "POST",
|
|
443
|
+
headers: {
|
|
444
|
+
authorization: this.#authorization,
|
|
445
|
+
"content-type": "application/json"
|
|
446
|
+
},
|
|
447
|
+
body: JSON.stringify({
|
|
448
|
+
jsonrpc: "1.0",
|
|
449
|
+
id: `${method}-${Date.now()}`,
|
|
450
|
+
method,
|
|
451
|
+
params
|
|
452
|
+
})
|
|
453
|
+
});
|
|
454
|
+
const body = await response.text();
|
|
455
|
+
let payload;
|
|
456
|
+
if (body) {
|
|
457
|
+
try {
|
|
458
|
+
payload = JSON.parse(body);
|
|
459
|
+
} catch {
|
|
460
|
+
payload = void 0;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (payload?.error) {
|
|
464
|
+
const httpStatus = response.ok ? "" : ` with HTTP ${response.status}`;
|
|
465
|
+
throw new Error(
|
|
466
|
+
`Bitcoin RPC ${method} failed${httpStatus} (${payload.error.code}): ${payload.error.message}`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
if (!response.ok) {
|
|
470
|
+
throw new Error(`Bitcoin RPC ${method} failed with HTTP ${response.status}`);
|
|
471
|
+
}
|
|
472
|
+
if (!payload) {
|
|
473
|
+
throw new Error(`Bitcoin RPC ${method} returned an invalid JSON response`);
|
|
474
|
+
}
|
|
475
|
+
return payload.result;
|
|
476
|
+
}
|
|
477
|
+
};
|
|
427
478
|
|
|
428
479
|
// src/TestBitcoinCli.ts
|
|
429
480
|
import * as child_process2 from "child_process";
|
|
@@ -501,11 +552,484 @@ var TestOracle = class _TestOracle {
|
|
|
501
552
|
}
|
|
502
553
|
};
|
|
503
554
|
|
|
555
|
+
// src/TestEthereum.ts
|
|
556
|
+
import * as fs4 from "fs/promises";
|
|
557
|
+
import * as os from "os";
|
|
558
|
+
import * as Path5 from "path";
|
|
559
|
+
import { spawn as spawn4, spawnSync } from "child_process";
|
|
560
|
+
import { detectPort as detectPort2 } from "detect-port";
|
|
561
|
+
|
|
562
|
+
// src/ethereumContracts.ts
|
|
563
|
+
import { readFileSync } from "fs";
|
|
564
|
+
function loadArtifact(fileName) {
|
|
565
|
+
return JSON.parse(
|
|
566
|
+
readFileSync(new URL(`./ethereum-contracts/${fileName}`, import.meta.url), "utf8")
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
var argonTokenArtifact = loadArtifact("ArgonToken.json");
|
|
570
|
+
var argonotTokenArtifact = loadArtifact("ArgonotToken.json");
|
|
571
|
+
var mintingGatewayArtifact = loadArtifact("MintingGateway.json");
|
|
572
|
+
var proxyAdminArtifact = loadArtifact("ProxyAdmin.json");
|
|
573
|
+
var transparentUpgradeableProxyArtifact = loadArtifact("TransparentUpgradeableProxy.json");
|
|
574
|
+
|
|
575
|
+
// src/TestEthereum.ts
|
|
576
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
577
|
+
import {
|
|
578
|
+
encodeAbiParameters,
|
|
579
|
+
createPublicClient,
|
|
580
|
+
createWalletClient,
|
|
581
|
+
defineChain,
|
|
582
|
+
encodeFunctionData,
|
|
583
|
+
getAddress,
|
|
584
|
+
http,
|
|
585
|
+
keccak256,
|
|
586
|
+
zeroAddress
|
|
587
|
+
} from "viem";
|
|
588
|
+
var DEFAULT_KURTOSIS_BIN = "kurtosis";
|
|
589
|
+
var DEFAULT_ETHEREUM_PACKAGE = "github.com/ethpandaops/ethereum-package";
|
|
590
|
+
var DEFAULT_EL_PORT_START = 32e3;
|
|
591
|
+
var DEFAULT_CL_PORT_START = 33e3;
|
|
592
|
+
var PORT_RANGE_SIZE = 32;
|
|
593
|
+
var ENCLAVE_NAME_PREFIX = "argon-eth-";
|
|
594
|
+
var PROBE_INTERVAL_MS = 1e3;
|
|
595
|
+
var PROBE_TIMEOUT_MS = 6e4;
|
|
596
|
+
var LIGHT_CLIENT_READY_TIMEOUT_MS = 5 * 6e4;
|
|
597
|
+
var KURTOSIS_RUN_TIMEOUT_MS = 20 * 6e4;
|
|
598
|
+
var DEFAULT_SEED_ARGON_AMOUNT_BASE_UNITS = 1000000000n;
|
|
599
|
+
var DEFAULT_INITIAL_MICROGONS_PER_ARGONOT = 1000000n;
|
|
600
|
+
var ERC1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";
|
|
601
|
+
var TestEthereum = class {
|
|
602
|
+
enclaveName;
|
|
603
|
+
kurtosisBin;
|
|
604
|
+
packageRef;
|
|
605
|
+
executionRpcUrl;
|
|
606
|
+
beaconApiUrl;
|
|
607
|
+
chainId;
|
|
608
|
+
#argsDir;
|
|
609
|
+
constructor(enclaveName = `${ENCLAVE_NAME_PREFIX}${Math.random().toString(36).slice(2, 8)}`, kurtosisBin = DEFAULT_KURTOSIS_BIN, packageRef = DEFAULT_ETHEREUM_PACKAGE) {
|
|
610
|
+
this.enclaveName = enclaveName;
|
|
611
|
+
this.kurtosisBin = kurtosisBin;
|
|
612
|
+
this.packageRef = packageRef;
|
|
613
|
+
addTeardown(this);
|
|
614
|
+
}
|
|
615
|
+
static isInstalled(kurtosisBin = DEFAULT_KURTOSIS_BIN) {
|
|
616
|
+
return spawnSync(kurtosisBin, ["version"], { stdio: "ignore" }).status === 0;
|
|
617
|
+
}
|
|
618
|
+
async launch(options) {
|
|
619
|
+
const {
|
|
620
|
+
consensusClient = "lighthouse",
|
|
621
|
+
preset = "mainnet",
|
|
622
|
+
secondsPerSlot,
|
|
623
|
+
waitForFinalization = true,
|
|
624
|
+
prefundedAccounts
|
|
625
|
+
} = options ?? {};
|
|
626
|
+
const elPublicPortStart = await findFreePortRange(DEFAULT_EL_PORT_START, PORT_RANGE_SIZE);
|
|
627
|
+
const clPublicPortStart = await findFreePortRange(DEFAULT_CL_PORT_START, PORT_RANGE_SIZE);
|
|
628
|
+
this.#argsDir = await fs4.mkdtemp(Path5.join(os.tmpdir(), "argon-ethereum-devnet-"));
|
|
629
|
+
const argsFile = Path5.join(this.#argsDir, "network-params.yaml");
|
|
630
|
+
await fs4.writeFile(
|
|
631
|
+
argsFile,
|
|
632
|
+
renderEthereumArgs(
|
|
633
|
+
elPublicPortStart,
|
|
634
|
+
clPublicPortStart,
|
|
635
|
+
consensusClient,
|
|
636
|
+
preset,
|
|
637
|
+
secondsPerSlot,
|
|
638
|
+
waitForFinalization,
|
|
639
|
+
prefundedAccounts
|
|
640
|
+
)
|
|
641
|
+
);
|
|
642
|
+
await runCommand(
|
|
643
|
+
this.kurtosisBin,
|
|
644
|
+
["run", "--enclave", this.enclaveName, this.packageRef, "--args-file", argsFile],
|
|
645
|
+
KURTOSIS_RUN_TIMEOUT_MS
|
|
646
|
+
);
|
|
647
|
+
const executionRpc = await waitForProbe(
|
|
648
|
+
() => findExecutionRpcUrl(elPublicPortStart, PORT_RANGE_SIZE),
|
|
649
|
+
PROBE_TIMEOUT_MS
|
|
650
|
+
);
|
|
651
|
+
const beaconApi = await waitForProbe(
|
|
652
|
+
() => findBeaconApiUrl(clPublicPortStart, PORT_RANGE_SIZE),
|
|
653
|
+
PROBE_TIMEOUT_MS
|
|
654
|
+
);
|
|
655
|
+
this.executionRpcUrl = executionRpc.url;
|
|
656
|
+
this.beaconApiUrl = beaconApi.url;
|
|
657
|
+
this.chainId = executionRpc.chainId;
|
|
658
|
+
await waitForProbe(
|
|
659
|
+
() => this.getBeacon("/eth/v1/beacon/genesis"),
|
|
660
|
+
LIGHT_CLIENT_READY_TIMEOUT_MS
|
|
661
|
+
);
|
|
662
|
+
return {
|
|
663
|
+
executionRpcUrl: this.executionRpcUrl,
|
|
664
|
+
beaconApiUrl: this.beaconApiUrl,
|
|
665
|
+
chainId: this.chainId
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
async callExecution(method, params = []) {
|
|
669
|
+
const executionRpcUrl = this.executionRpcUrl;
|
|
670
|
+
if (!executionRpcUrl) {
|
|
671
|
+
throw new Error("Execution RPC URL is not available before launch");
|
|
672
|
+
}
|
|
673
|
+
const response = await fetch(executionRpcUrl, {
|
|
674
|
+
method: "POST",
|
|
675
|
+
headers: { "content-type": "application/json" },
|
|
676
|
+
body: JSON.stringify({
|
|
677
|
+
id: 1,
|
|
678
|
+
jsonrpc: "2.0",
|
|
679
|
+
method,
|
|
680
|
+
params
|
|
681
|
+
}),
|
|
682
|
+
signal: AbortSignal.timeout(1e4)
|
|
683
|
+
});
|
|
684
|
+
if (!response.ok) {
|
|
685
|
+
throw new Error(`Execution RPC request failed for ${method}: ${response.status}`);
|
|
686
|
+
}
|
|
687
|
+
const body = await response.json();
|
|
688
|
+
if (body.error) {
|
|
689
|
+
throw new Error(
|
|
690
|
+
`Execution RPC ${method} failed (${body.error.code ?? "unknown"}): ${body.error.message ?? "unknown error"}`
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
return body.result;
|
|
694
|
+
}
|
|
695
|
+
async getBeacon(path2) {
|
|
696
|
+
const beaconApiUrl = this.beaconApiUrl;
|
|
697
|
+
if (!beaconApiUrl) {
|
|
698
|
+
throw new Error("Beacon API URL is not available before launch");
|
|
699
|
+
}
|
|
700
|
+
const response = await fetch(new URL(path2, `${beaconApiUrl}/`), {
|
|
701
|
+
signal: AbortSignal.timeout(1e4)
|
|
702
|
+
});
|
|
703
|
+
if (!response.ok) {
|
|
704
|
+
throw new Error(`Beacon API request failed for ${path2}: ${response.status}`);
|
|
705
|
+
}
|
|
706
|
+
return await response.json();
|
|
707
|
+
}
|
|
708
|
+
async deployMintingGatewayFixture(options) {
|
|
709
|
+
const { executionRpcUrl, chainId } = this;
|
|
710
|
+
if (!executionRpcUrl || !chainId) {
|
|
711
|
+
throw new Error("Ethereum devnet must be launched before deploying MintingGateway fixtures");
|
|
712
|
+
}
|
|
713
|
+
const account = privateKeyToAccount(options.deployerPrivateKey);
|
|
714
|
+
const adminSafe = options.adminSafe ?? account.address;
|
|
715
|
+
const guardianSafe = options.guardianSafe ?? adminSafe;
|
|
716
|
+
const chain = createExecutionChain(chainId, executionRpcUrl);
|
|
717
|
+
const publicClient = createPublicClient({
|
|
718
|
+
chain,
|
|
719
|
+
transport: http(executionRpcUrl)
|
|
720
|
+
});
|
|
721
|
+
const walletClient = createWalletClient({
|
|
722
|
+
account,
|
|
723
|
+
chain,
|
|
724
|
+
transport: http(executionRpcUrl)
|
|
725
|
+
});
|
|
726
|
+
const bootstrapCouncil = {
|
|
727
|
+
signers: [adminSafe],
|
|
728
|
+
weights: [1n]
|
|
729
|
+
};
|
|
730
|
+
const bootstrapCouncilHash = keccak256(
|
|
731
|
+
encodeAbiParameters(
|
|
732
|
+
[{ type: "address[]" }, { type: "uint256[]" }],
|
|
733
|
+
[bootstrapCouncil.signers, bootstrapCouncil.weights]
|
|
734
|
+
)
|
|
735
|
+
);
|
|
736
|
+
const bootstrapImplementationAddress = await deployContract(walletClient, publicClient, {
|
|
737
|
+
abi: mintingGatewayArtifact.abi,
|
|
738
|
+
bytecode: mintingGatewayArtifact.bytecode,
|
|
739
|
+
args: [zeroAddress, zeroAddress]
|
|
740
|
+
});
|
|
741
|
+
const initializeData = encodeFunctionData({
|
|
742
|
+
abi: mintingGatewayArtifact.abi,
|
|
743
|
+
functionName: "initialize",
|
|
744
|
+
args: [
|
|
745
|
+
adminSafe,
|
|
746
|
+
guardianSafe,
|
|
747
|
+
bootstrapCouncilHash,
|
|
748
|
+
BigInt(bootstrapCouncil.signers.length),
|
|
749
|
+
1n,
|
|
750
|
+
DEFAULT_INITIAL_MICROGONS_PER_ARGONOT
|
|
751
|
+
]
|
|
752
|
+
});
|
|
753
|
+
const gatewayAddress = await deployContract(walletClient, publicClient, {
|
|
754
|
+
abi: transparentUpgradeableProxyArtifact.abi,
|
|
755
|
+
bytecode: transparentUpgradeableProxyArtifact.bytecode,
|
|
756
|
+
args: [bootstrapImplementationAddress, adminSafe, initializeData]
|
|
757
|
+
});
|
|
758
|
+
const proxyAdminAddress = getAddressFromStorage(
|
|
759
|
+
await publicClient.getStorageAt({
|
|
760
|
+
address: gatewayAddress,
|
|
761
|
+
slot: ERC1967_ADMIN_SLOT
|
|
762
|
+
})
|
|
763
|
+
);
|
|
764
|
+
const argonTokenAddress = await deployContract(walletClient, publicClient, {
|
|
765
|
+
abi: argonTokenArtifact.abi,
|
|
766
|
+
bytecode: argonTokenArtifact.bytecode,
|
|
767
|
+
args: [gatewayAddress]
|
|
768
|
+
});
|
|
769
|
+
const argonotTokenAddress = await deployContract(walletClient, publicClient, {
|
|
770
|
+
abi: argonotTokenArtifact.abi,
|
|
771
|
+
bytecode: argonotTokenArtifact.bytecode,
|
|
772
|
+
args: [gatewayAddress]
|
|
773
|
+
});
|
|
774
|
+
const finalImplementationAddress = await deployContract(walletClient, publicClient, {
|
|
775
|
+
abi: mintingGatewayArtifact.abi,
|
|
776
|
+
bytecode: mintingGatewayArtifact.bytecode,
|
|
777
|
+
args: [argonTokenAddress, argonotTokenAddress]
|
|
778
|
+
});
|
|
779
|
+
const upgradeHash = await walletClient.sendTransaction({
|
|
780
|
+
to: proxyAdminAddress,
|
|
781
|
+
data: encodeFunctionData({
|
|
782
|
+
abi: proxyAdminArtifact.abi,
|
|
783
|
+
functionName: "upgradeAndCall",
|
|
784
|
+
args: [gatewayAddress, finalImplementationAddress, "0x"]
|
|
785
|
+
})
|
|
786
|
+
});
|
|
787
|
+
const upgradeReceipt = await waitForExecutionReceipt(publicClient, upgradeHash);
|
|
788
|
+
if (upgradeReceipt.status !== "success") {
|
|
789
|
+
throw new Error("MintingGateway proxy upgrade failed");
|
|
790
|
+
}
|
|
791
|
+
if (options.seedArgonRecipient) {
|
|
792
|
+
const mintHash = await walletClient.sendTransaction({
|
|
793
|
+
to: gatewayAddress,
|
|
794
|
+
data: encodeFunctionData({
|
|
795
|
+
abi: mintingGatewayArtifact.abi,
|
|
796
|
+
functionName: "migrate",
|
|
797
|
+
args: [
|
|
798
|
+
{
|
|
799
|
+
recipients: [options.seedArgonRecipient],
|
|
800
|
+
amounts: [options.seedArgonAmountBaseUnits ?? DEFAULT_SEED_ARGON_AMOUNT_BASE_UNITS]
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
recipients: [],
|
|
804
|
+
amounts: []
|
|
805
|
+
}
|
|
806
|
+
]
|
|
807
|
+
})
|
|
808
|
+
});
|
|
809
|
+
const mintReceipt = await waitForExecutionReceipt(publicClient, mintHash);
|
|
810
|
+
if (mintReceipt.status !== "success") {
|
|
811
|
+
throw new Error("MintingGateway migrate failed");
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return {
|
|
815
|
+
argonTokenAddress,
|
|
816
|
+
argonotTokenAddress,
|
|
817
|
+
gatewayAddress
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
async teardown() {
|
|
821
|
+
if (this.#argsDir) {
|
|
822
|
+
await fs4.rm(this.#argsDir, { recursive: true, force: true });
|
|
823
|
+
this.#argsDir = void 0;
|
|
824
|
+
}
|
|
825
|
+
await runCommand(this.kurtosisBin, ["enclave", "rm", "-f", this.enclaveName], 6e4, true);
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
async function deployContract(walletClient, publicClient, request) {
|
|
829
|
+
const hash = await walletClient.deployContract({
|
|
830
|
+
...request,
|
|
831
|
+
account: walletClient.account,
|
|
832
|
+
chain: walletClient.chain
|
|
833
|
+
});
|
|
834
|
+
const receipt = await waitForExecutionReceipt(publicClient, hash);
|
|
835
|
+
if (receipt.status !== "success" || !receipt.contractAddress) {
|
|
836
|
+
throw new Error(`Contract deployment failed for ${request.bytecode.slice(0, 10)}`);
|
|
837
|
+
}
|
|
838
|
+
return receipt.contractAddress;
|
|
839
|
+
}
|
|
840
|
+
function getAddressFromStorage(value) {
|
|
841
|
+
if (!value || value === "0x") {
|
|
842
|
+
throw new Error("Missing proxy admin address in ERC1967 admin slot");
|
|
843
|
+
}
|
|
844
|
+
return getAddress(`0x${value.slice(-40)}`);
|
|
845
|
+
}
|
|
846
|
+
function renderEthereumArgs(elPublicPortStart, clPublicPortStart, consensusClient, preset, secondsPerSlot, waitForFinalization, prefundedAccounts) {
|
|
847
|
+
const lines = [
|
|
848
|
+
"participants:",
|
|
849
|
+
" - el_type: geth",
|
|
850
|
+
` cl_type: ${consensusClient}`,
|
|
851
|
+
"network_params:",
|
|
852
|
+
" network: kurtosis",
|
|
853
|
+
` preset: ${preset}`,
|
|
854
|
+
...secondsPerSlot ? [` seconds_per_slot: ${secondsPerSlot}`] : [],
|
|
855
|
+
...prefundedAccounts && Object.keys(prefundedAccounts).length > 0 ? [` prefunded_accounts: '${JSON.stringify(prefundedAccounts)}'`] : [],
|
|
856
|
+
"additional_services: []",
|
|
857
|
+
`wait_for_finalization: ${waitForFinalization ? "true" : "false"}`,
|
|
858
|
+
"global_log_level: warn",
|
|
859
|
+
"port_publisher:",
|
|
860
|
+
" el:",
|
|
861
|
+
" enabled: true",
|
|
862
|
+
` public_port_start: ${elPublicPortStart}`,
|
|
863
|
+
" cl:",
|
|
864
|
+
" enabled: true",
|
|
865
|
+
` public_port_start: ${clPublicPortStart}`
|
|
866
|
+
];
|
|
867
|
+
lines.push("");
|
|
868
|
+
return lines.join("\n");
|
|
869
|
+
}
|
|
870
|
+
async function findExecutionRpcUrl(portStart, rangeSize) {
|
|
871
|
+
for (let port2 = portStart; port2 < portStart + rangeSize; port2 += 1) {
|
|
872
|
+
const url2 = `http://127.0.0.1:${port2}`;
|
|
873
|
+
const response = await fetchJsonRpc(url2, "eth_chainId");
|
|
874
|
+
if (typeof response === "string") {
|
|
875
|
+
return { url: url2, chainId: response };
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
throw new Error(
|
|
879
|
+
`Unable to find an execution RPC endpoint in ${portStart}-${portStart + rangeSize - 1}`
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
async function findBeaconApiUrl(portStart, rangeSize) {
|
|
883
|
+
for (let port2 = portStart; port2 < portStart + rangeSize; port2 += 1) {
|
|
884
|
+
const url2 = `http://127.0.0.1:${port2}`;
|
|
885
|
+
try {
|
|
886
|
+
const response = await fetch(new URL("/eth/v1/node/version", `${url2}/`), {
|
|
887
|
+
signal: AbortSignal.timeout(2e3)
|
|
888
|
+
});
|
|
889
|
+
if (!response.ok) {
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
const body = await response.json();
|
|
893
|
+
if (body.data?.version) {
|
|
894
|
+
return { url: url2 };
|
|
895
|
+
}
|
|
896
|
+
} catch {
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
throw new Error(
|
|
900
|
+
`Unable to find a Beacon API endpoint in ${portStart}-${portStart + rangeSize - 1}`
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
async function fetchJsonRpc(url2, method) {
|
|
904
|
+
try {
|
|
905
|
+
const response = await fetch(url2, {
|
|
906
|
+
method: "POST",
|
|
907
|
+
headers: { "content-type": "application/json" },
|
|
908
|
+
body: JSON.stringify({
|
|
909
|
+
id: 1,
|
|
910
|
+
jsonrpc: "2.0",
|
|
911
|
+
method,
|
|
912
|
+
params: []
|
|
913
|
+
}),
|
|
914
|
+
signal: AbortSignal.timeout(2e3)
|
|
915
|
+
});
|
|
916
|
+
if (!response.ok) {
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
const body = await response.json();
|
|
920
|
+
return body.result ?? null;
|
|
921
|
+
} catch {
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function createExecutionChain(chainId, executionRpcUrl) {
|
|
926
|
+
return defineChain({
|
|
927
|
+
id: Number.parseInt(chainId, 16),
|
|
928
|
+
name: "argon-test-ethereum",
|
|
929
|
+
nativeCurrency: {
|
|
930
|
+
name: "Ether",
|
|
931
|
+
symbol: "ETH",
|
|
932
|
+
decimals: 18
|
|
933
|
+
},
|
|
934
|
+
rpcUrls: {
|
|
935
|
+
default: {
|
|
936
|
+
http: [executionRpcUrl]
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
async function findFreePortRange(start, size) {
|
|
942
|
+
for (let candidate = start; candidate < start + 1e3; candidate += size) {
|
|
943
|
+
const ports = Array.from({ length: size }, (_, index) => candidate + index);
|
|
944
|
+
const results = await Promise.all(ports.map((port2) => detectPort2(port2)));
|
|
945
|
+
if (results.every((resolvedPort, index) => resolvedPort === ports[index])) {
|
|
946
|
+
return candidate;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
throw new Error(`Unable to find a free port range starting near ${start}`);
|
|
950
|
+
}
|
|
951
|
+
async function waitForProbe(probe, timeoutMs) {
|
|
952
|
+
const start = Date.now();
|
|
953
|
+
let lastError;
|
|
954
|
+
while (Date.now() - start < timeoutMs) {
|
|
955
|
+
try {
|
|
956
|
+
return await probe();
|
|
957
|
+
} catch (error) {
|
|
958
|
+
lastError = error;
|
|
959
|
+
await delay(PROBE_INTERVAL_MS);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
throw lastError instanceof Error ? lastError : new Error("Timed out waiting for probe");
|
|
963
|
+
}
|
|
964
|
+
async function waitForExecutionReceipt(publicClient, hash) {
|
|
965
|
+
const start = Date.now();
|
|
966
|
+
let lastError;
|
|
967
|
+
while (Date.now() - start < 12e4) {
|
|
968
|
+
try {
|
|
969
|
+
const receipt = await publicClient.getTransactionReceipt({ hash });
|
|
970
|
+
if (receipt) {
|
|
971
|
+
return receipt;
|
|
972
|
+
}
|
|
973
|
+
} catch (error) {
|
|
974
|
+
const errorText = error instanceof Error ? [
|
|
975
|
+
error.message,
|
|
976
|
+
"details" in error && typeof error.details === "string" ? error.details : void 0
|
|
977
|
+
].filter(Boolean).join(" ") : String(error);
|
|
978
|
+
if (!errorText.includes("indexing is in progress") && !errorText.includes("Transaction receipt with hash") && !errorText.includes("could not be found")) {
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
981
|
+
lastError = error instanceof Error ? error : new Error(errorText);
|
|
982
|
+
}
|
|
983
|
+
await delay(500);
|
|
984
|
+
}
|
|
985
|
+
throw lastError ?? new Error(`Timed out waiting for execution receipt ${hash}`);
|
|
986
|
+
}
|
|
987
|
+
async function runCommand(command, args, timeoutMs, allowFailure = false) {
|
|
988
|
+
await new Promise((resolve3, reject) => {
|
|
989
|
+
const child = spawn4(command, args, {
|
|
990
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
991
|
+
});
|
|
992
|
+
let stdout = "";
|
|
993
|
+
let stderr = "";
|
|
994
|
+
const timeout = setTimeout(() => {
|
|
995
|
+
child.kill("SIGTERM");
|
|
996
|
+
reject(new Error(`Command timed out: ${command} ${args.join(" ")}`));
|
|
997
|
+
}, timeoutMs);
|
|
998
|
+
child.stdout?.setEncoding("utf8");
|
|
999
|
+
child.stderr?.setEncoding("utf8");
|
|
1000
|
+
child.stdout?.on("data", (chunk) => {
|
|
1001
|
+
stdout += chunk;
|
|
1002
|
+
});
|
|
1003
|
+
child.stderr?.on("data", (chunk) => {
|
|
1004
|
+
stderr += chunk;
|
|
1005
|
+
});
|
|
1006
|
+
child.on("error", (error) => {
|
|
1007
|
+
clearTimeout(timeout);
|
|
1008
|
+
reject(error);
|
|
1009
|
+
});
|
|
1010
|
+
child.on("exit", (code) => {
|
|
1011
|
+
clearTimeout(timeout);
|
|
1012
|
+
if (code === 0 || allowFailure) {
|
|
1013
|
+
resolve3();
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
reject(
|
|
1017
|
+
new Error(
|
|
1018
|
+
[`Command failed: ${command} ${args.join(" ")}`, stdout.trim(), stderr.trim()].filter(Boolean).join("\n")
|
|
1019
|
+
)
|
|
1020
|
+
);
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
async function delay(ms) {
|
|
1025
|
+
await new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1026
|
+
}
|
|
1027
|
+
|
|
504
1028
|
// src/TestNetwork.ts
|
|
505
1029
|
import * as docker from "docker-compose";
|
|
506
|
-
import * as
|
|
1030
|
+
import * as Path6 from "path";
|
|
507
1031
|
async function startNetwork(testName, options) {
|
|
508
|
-
const config =
|
|
1032
|
+
const config = Path6.join(__dirname, `dev.docker-compose.yml`);
|
|
509
1033
|
const env4 = {
|
|
510
1034
|
VERSION: "dev",
|
|
511
1035
|
ARGON_CHAIN: "dev-docker",
|
|
@@ -550,7 +1074,7 @@ async function getProxy() {
|
|
|
550
1074
|
autoRewrite: true
|
|
551
1075
|
});
|
|
552
1076
|
proxy.on("error", () => null);
|
|
553
|
-
proxyServer =
|
|
1077
|
+
proxyServer = http2.createServer(function(req, res) {
|
|
554
1078
|
const queryData = url.parse(req.url, true).query;
|
|
555
1079
|
if (!queryData.target) {
|
|
556
1080
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
@@ -600,12 +1124,12 @@ function stringifyExt(obj) {
|
|
|
600
1124
|
}
|
|
601
1125
|
function projectRoot() {
|
|
602
1126
|
if (process4.env.ARGON_PROJECT_ROOT) {
|
|
603
|
-
return
|
|
1127
|
+
return Path7.join(process4.env.ARGON_PROJECT_ROOT);
|
|
604
1128
|
}
|
|
605
|
-
return
|
|
1129
|
+
return Path7.join(__dirname, `../../..`);
|
|
606
1130
|
}
|
|
607
1131
|
async function runTestScript(relativePath) {
|
|
608
|
-
const scriptPath =
|
|
1132
|
+
const scriptPath = Path7.resolve(projectRoot(), relativePath);
|
|
609
1133
|
return child_process4.execSync(scriptPath, { encoding: "utf8" }).trim();
|
|
610
1134
|
}
|
|
611
1135
|
async function getDockerPortMapping(containerName, port2) {
|
|
@@ -655,16 +1179,19 @@ async function activateNotary(sudo2, client, notary) {
|
|
|
655
1179
|
export {
|
|
656
1180
|
SKIP_E2E,
|
|
657
1181
|
TestBitcoinCli,
|
|
1182
|
+
TestEthereum,
|
|
658
1183
|
TestMainchain,
|
|
659
1184
|
TestNotary,
|
|
660
1185
|
TestOracle,
|
|
661
1186
|
activateNotary,
|
|
662
1187
|
addTeardown,
|
|
1188
|
+
argonTokenArtifact,
|
|
663
1189
|
cleanHostForDocker,
|
|
664
1190
|
closeOnTeardown,
|
|
665
1191
|
disconnectOnTeardown,
|
|
666
1192
|
getDockerPortMapping,
|
|
667
1193
|
getProxy,
|
|
1194
|
+
mintingGatewayArtifact,
|
|
668
1195
|
projectRoot,
|
|
669
1196
|
runOnTeardown,
|
|
670
1197
|
runTestScript,
|