@taqueria/plugin-tezbox 0.61.4
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/LICENSE +201 -0
- package/README.md +15 -0
- package/docker.ts +8 -0
- package/index.d.mts +2 -0
- package/index.d.ts +2 -0
- package/index.js +720 -0
- package/index.js.map +1 -0
- package/index.mjs +703 -0
- package/index.mjs.map +1 -0
- package/index.ts +71 -0
- package/package.json +74 -0
- package/proxy.ts +1129 -0
- package/tsconfig.json +108 -0
- package/types.ts +34 -0
package/index.js
ADDED
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// index.ts
|
|
26
|
+
var import_node_sdk3 = require("@taqueria/node-sdk");
|
|
27
|
+
|
|
28
|
+
// proxy.ts
|
|
29
|
+
var import_node_sdk2 = require("@taqueria/node-sdk");
|
|
30
|
+
var import_signer = require("@taquito/signer");
|
|
31
|
+
var import_bignumber = __toESM(require("bignumber.js"));
|
|
32
|
+
var bip39 = __toESM(require("bip39"));
|
|
33
|
+
var import_crypto = require("crypto");
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var hjson = __toESM(require("hjson"));
|
|
36
|
+
var path = __toESM(require("path"));
|
|
37
|
+
|
|
38
|
+
// docker.ts
|
|
39
|
+
var import_node_sdk = require("@taqueria/node-sdk");
|
|
40
|
+
var getDefaultDockerImage = (opts) => `ghcr.io/tez-capital/tezbox:tezos-v20.3.20241003`;
|
|
41
|
+
|
|
42
|
+
// proxy.ts
|
|
43
|
+
var logger = {
|
|
44
|
+
info: (message) => console.log(message),
|
|
45
|
+
warn: (message) => console.warn(message),
|
|
46
|
+
error: (message) => console.error(message)
|
|
47
|
+
};
|
|
48
|
+
function getErrorMessage(prefix, error) {
|
|
49
|
+
if (prefix === "") {
|
|
50
|
+
return error instanceof Error ? error.message : String(error);
|
|
51
|
+
}
|
|
52
|
+
if (typeof error === "boolean") {
|
|
53
|
+
return `${prefix}:`;
|
|
54
|
+
}
|
|
55
|
+
const errorString = error instanceof Error ? error.message : String(error);
|
|
56
|
+
return `${prefix}: ${errorString}`;
|
|
57
|
+
}
|
|
58
|
+
async function runCommand(cmd, stderrHandler) {
|
|
59
|
+
try {
|
|
60
|
+
const { stdout, stderr } = await (0, import_node_sdk2.execCmd)(cmd);
|
|
61
|
+
if (stderr.trim()) {
|
|
62
|
+
if (stderrHandler) {
|
|
63
|
+
await stderrHandler(stderr.trim());
|
|
64
|
+
} else {
|
|
65
|
+
throw new Error(stderr.trim());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { stdout };
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw new Error(getErrorMessage(`Command failed`, error));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function isTezBoxEnvironment(taskArgs) {
|
|
74
|
+
const environment = taskArgs.config.environment[taskArgs.env];
|
|
75
|
+
if (!environment || typeof environment !== "object") return false;
|
|
76
|
+
const sandboxes = environment.sandboxes;
|
|
77
|
+
if (!Array.isArray(sandboxes) || sandboxes.length === 0) return false;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
async function instantiateDeclaredAccount(declaredAccountName, balanceInMutez) {
|
|
81
|
+
logger.warn(`instantiateDeclaredAccount is not implemented. Returning dummy data for ${declaredAccountName}.`);
|
|
82
|
+
return {
|
|
83
|
+
encryptedKey: "unencrypted:edpktdummykey123",
|
|
84
|
+
publicKeyHash: `tz1_dummy_public_key_hash_${declaredAccountName}`,
|
|
85
|
+
secretKey: "unencrypted:edskdummysecretkey123"
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function getInstantiatedAccounts(taskArgs) {
|
|
89
|
+
const sandboxConfig = getSandboxConfig(taskArgs);
|
|
90
|
+
if (!sandboxConfig.accounts) {
|
|
91
|
+
throw new Error("No instantiated accounts found in sandbox config.");
|
|
92
|
+
}
|
|
93
|
+
const accounts = sandboxConfig.accounts;
|
|
94
|
+
return Object.entries(accounts).filter(([key]) => key !== "default").reduce((acc, [key, value]) => {
|
|
95
|
+
acc[key] = value;
|
|
96
|
+
return acc;
|
|
97
|
+
}, {});
|
|
98
|
+
}
|
|
99
|
+
function getSandboxConfig(taskArgs) {
|
|
100
|
+
var _a, _b;
|
|
101
|
+
if (!isTezBoxEnvironment(taskArgs)) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`This configuration doesn't appear to be configured to use TezBox environments. Check the ${taskArgs.env} environment in your .taq/config.json.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
const environment = taskArgs.config.environment[taskArgs.env];
|
|
107
|
+
const sandboxName = (_a = environment.sandboxes) == null ? void 0 : _a[0];
|
|
108
|
+
if (sandboxName) {
|
|
109
|
+
const sandboxConfig = (_b = taskArgs.config.sandbox) == null ? void 0 : _b[sandboxName];
|
|
110
|
+
if (sandboxConfig) {
|
|
111
|
+
const retval = {
|
|
112
|
+
blockTime: 1,
|
|
113
|
+
baking: "enabled" /* ENABLED */,
|
|
114
|
+
...sandboxConfig,
|
|
115
|
+
...sandboxConfig.annotations
|
|
116
|
+
};
|
|
117
|
+
return retval;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw new Error(`No sandbox configuration found for ${taskArgs.env} environment.`);
|
|
121
|
+
}
|
|
122
|
+
async function getOrCreateInstantiatedAccounts(taskArgs) {
|
|
123
|
+
let instantiatedAccounts;
|
|
124
|
+
try {
|
|
125
|
+
instantiatedAccounts = await getInstantiatedAccounts(taskArgs);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
instantiatedAccounts = {};
|
|
128
|
+
const declaredAccounts = taskArgs.config.accounts;
|
|
129
|
+
for (const [accountName, balanceInMutez] of Object.entries(declaredAccounts)) {
|
|
130
|
+
const balanceInMutezBN = new import_bignumber.default(balanceInMutez.toString().replace(/_/g, ""));
|
|
131
|
+
const instantiatedAccount = await instantiateDeclaredAccount(accountName, balanceInMutezBN);
|
|
132
|
+
instantiatedAccounts[accountName] = instantiatedAccount;
|
|
133
|
+
}
|
|
134
|
+
await saveInstantiatedAccounts(instantiatedAccounts, taskArgs.projectDir, taskArgs.env);
|
|
135
|
+
}
|
|
136
|
+
return instantiatedAccounts;
|
|
137
|
+
}
|
|
138
|
+
async function saveInstantiatedAccounts(accounts, projectDir, env) {
|
|
139
|
+
const configPath = path.join(projectDir, `.taq/config.local.${env}.json`);
|
|
140
|
+
try {
|
|
141
|
+
let config = {};
|
|
142
|
+
try {
|
|
143
|
+
const configContent = await fs.promises.readFile(configPath, "utf8");
|
|
144
|
+
config = JSON.parse(configContent);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
}
|
|
147
|
+
const sandboxAccounts = {};
|
|
148
|
+
for (const [name, account] of Object.entries(accounts)) {
|
|
149
|
+
sandboxAccounts[name] = {
|
|
150
|
+
publicKeyHash: account.publicKeyHash,
|
|
151
|
+
secretKey: account.secretKey
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
config.accounts = sandboxAccounts;
|
|
155
|
+
await fs.promises.mkdir(path.dirname(configPath), { recursive: true });
|
|
156
|
+
await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2), "utf8");
|
|
157
|
+
} catch (error) {
|
|
158
|
+
throw new Error(getErrorMessage("Failed to save instantiated accounts", error));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function getPublicKeyFromSecretKey(secretKey) {
|
|
162
|
+
const signer = await import_signer.InMemorySigner.fromSecretKey(secretKey.replace("unencrypted:", ""));
|
|
163
|
+
const publicKey = await signer.publicKey();
|
|
164
|
+
return publicKey;
|
|
165
|
+
}
|
|
166
|
+
async function prepareTezBoxAccounts(instantiatedAccounts, declaredAccounts) {
|
|
167
|
+
const tezboxAccounts = {};
|
|
168
|
+
for (const [accountName, accountData] of Object.entries(instantiatedAccounts)) {
|
|
169
|
+
if (accountName === "default") continue;
|
|
170
|
+
const secretKey = accountData.secretKey;
|
|
171
|
+
tezboxAccounts[accountName] = {
|
|
172
|
+
pkh: accountData.publicKeyHash,
|
|
173
|
+
pk: await getPublicKeyFromSecretKey(secretKey),
|
|
174
|
+
sk: secretKey,
|
|
175
|
+
balance: accountName === "funder" ? new import_bignumber.default(1e14).toString() : new import_bignumber.default(declaredAccounts[accountName].toString()).toString()
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return tezboxAccounts;
|
|
179
|
+
}
|
|
180
|
+
async function writeAccountsHjson(tezboxAccounts, tezBoxConfigDir) {
|
|
181
|
+
await Promise.resolve();
|
|
182
|
+
const hjsonContent = hjson.stringify(tezboxAccounts, {
|
|
183
|
+
quotes: "min",
|
|
184
|
+
bracesSameLine: true,
|
|
185
|
+
separator: false
|
|
186
|
+
});
|
|
187
|
+
const fixedHjsonContent = hjsonContent.replaceAll('"', "");
|
|
188
|
+
const accountsHjsonPath = path.join(tezBoxConfigDir, "accounts.hjson");
|
|
189
|
+
await fs.promises.writeFile(accountsHjsonPath, fixedHjsonContent, "utf8");
|
|
190
|
+
}
|
|
191
|
+
function getDeclaredAccounts(taskArgs) {
|
|
192
|
+
const declaredAccounts = taskArgs.config.accounts;
|
|
193
|
+
return Object.entries(declaredAccounts).reduce((acc, [key, value]) => {
|
|
194
|
+
acc[key] = value.toString().replace(/_/g, "");
|
|
195
|
+
return acc;
|
|
196
|
+
}, {});
|
|
197
|
+
}
|
|
198
|
+
async function prepareAccountsHjson(taskArgs, tezBoxConfigDir) {
|
|
199
|
+
try {
|
|
200
|
+
const instantiatedAccounts = await getOrCreateInstantiatedAccounts(taskArgs);
|
|
201
|
+
const declaredAccounts = getDeclaredAccounts(taskArgs);
|
|
202
|
+
const tezboxAccounts = await prepareTezBoxAccounts(instantiatedAccounts, declaredAccounts);
|
|
203
|
+
await writeAccountsHjson(tezboxAccounts, tezBoxConfigDir);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
throw new Error(getErrorMessage(`Failed to prepare accounts`, error));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function prepareBakersHjson(taskArgs, tezBoxConfigDir) {
|
|
209
|
+
try {
|
|
210
|
+
const declaredAccounts = getDeclaredAccounts(taskArgs);
|
|
211
|
+
const totalBalance = Object.values(declaredAccounts).reduce(
|
|
212
|
+
(sum, balance) => import_bignumber.default.sum(sum, balance),
|
|
213
|
+
new import_bignumber.default(0)
|
|
214
|
+
).toString();
|
|
215
|
+
const bakers = {
|
|
216
|
+
baker1: {
|
|
217
|
+
pkh: "tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU",
|
|
218
|
+
pk: "edpkuTXkJDGcFd5nh6VvMz8phXxU3Bi7h6hqgywNFi1vZTfQNnS1RV",
|
|
219
|
+
sk: "unencrypted:edsk4ArLQgBTLWG5FJmnGnT689VKoqhXwmDPBuGx3z4cvwU9MmrPZZ",
|
|
220
|
+
balance: totalBalance
|
|
221
|
+
},
|
|
222
|
+
baker2: {
|
|
223
|
+
pkh: "tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN",
|
|
224
|
+
pk: "edpktzNbDAUjUk697W7gYg2CRuBQjyPxbEg8dLccYYwKSKvkPvjtV9",
|
|
225
|
+
sk: "unencrypted:edsk39qAm1fiMjgmPkw1EgQYkMzkJezLNewd7PLNHTkr6w9XA2zdfo",
|
|
226
|
+
balance: totalBalance
|
|
227
|
+
},
|
|
228
|
+
baker3: {
|
|
229
|
+
pkh: "tz1b7tUupMgCNw2cCLpKTkSD1NZzB5TkP2sv",
|
|
230
|
+
pk: "edpkuFrRoDSEbJYgxRtLx2ps82UdaYc1WwfS9sE11yhauZt5DgCHbU",
|
|
231
|
+
sk: "unencrypted:edsk2uqQB9AY4FvioK2YMdfmyMrer5R8mGFyuaLLFfSRo8EoyNdht3",
|
|
232
|
+
balance: totalBalance
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const hjsonContent = hjson.stringify(bakers, {
|
|
236
|
+
quotes: "min",
|
|
237
|
+
bracesSameLine: true,
|
|
238
|
+
separator: false
|
|
239
|
+
});
|
|
240
|
+
const fixedHjsonContent = hjsonContent.replaceAll('"', "");
|
|
241
|
+
const bakersHjsonPath = path.join(tezBoxConfigDir, "bakers.hjson");
|
|
242
|
+
await fs.promises.writeFile(bakersHjsonPath, fixedHjsonContent, "utf8");
|
|
243
|
+
} catch (error) {
|
|
244
|
+
throw new Error(getErrorMessage(`Failed to prepare bakers`, error));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function getProjectId(taskArgs) {
|
|
248
|
+
return (0, import_crypto.createHash)("sha256").update(taskArgs.projectDir).digest("hex");
|
|
249
|
+
}
|
|
250
|
+
function getDockerContainerName(taskArgs) {
|
|
251
|
+
const projectId = getProjectId(taskArgs);
|
|
252
|
+
return `taq-${taskArgs.env}-${projectId}`;
|
|
253
|
+
}
|
|
254
|
+
function getTezBoxConfigDir(taskArgs) {
|
|
255
|
+
const containerName = getDockerContainerName(taskArgs);
|
|
256
|
+
return path.join(taskArgs.projectDir, `.taq/.${containerName}/config`);
|
|
257
|
+
}
|
|
258
|
+
function getTezBoxDataDir(taskArgs) {
|
|
259
|
+
const containerName = getDockerContainerName(taskArgs);
|
|
260
|
+
return path.join(taskArgs.projectDir, `.taq/.${containerName}/data`);
|
|
261
|
+
}
|
|
262
|
+
function getImage(taskArgs) {
|
|
263
|
+
return (0, import_node_sdk2.getDockerImage)(getDefaultDockerImage(taskArgs), "TAQ_TEZBOX_IMAGE");
|
|
264
|
+
}
|
|
265
|
+
async function isSandboxRunning(taskArgs) {
|
|
266
|
+
const containerName = getDockerContainerName(taskArgs);
|
|
267
|
+
const cmd = `docker ps --filter "name=${containerName}" --format "{{.ID}}"`;
|
|
268
|
+
const { stdout } = await runCommand(cmd);
|
|
269
|
+
return stdout.trim() !== "";
|
|
270
|
+
}
|
|
271
|
+
async function checkSandboxRunning(taskArgs) {
|
|
272
|
+
const running = await isSandboxRunning(taskArgs);
|
|
273
|
+
if (running) {
|
|
274
|
+
await (0, import_node_sdk2.sendAsyncRes)("Sandbox is already running.");
|
|
275
|
+
}
|
|
276
|
+
return running;
|
|
277
|
+
}
|
|
278
|
+
function getPortNumber(taskArgs) {
|
|
279
|
+
const rpcUrl = getSandboxConfig(taskArgs).rpcUrl;
|
|
280
|
+
const match = rpcUrl.match(/:(\d+)/);
|
|
281
|
+
return match ? parseInt(match[1], 10) : 80;
|
|
282
|
+
}
|
|
283
|
+
function getContainerPort(taskArgs) {
|
|
284
|
+
return getPortNumber(taskArgs) + 1;
|
|
285
|
+
}
|
|
286
|
+
async function getDockerRunParams(taskArgs) {
|
|
287
|
+
const image = getImage(taskArgs);
|
|
288
|
+
const containerName = getDockerContainerName(taskArgs);
|
|
289
|
+
const platform = await (0, import_node_sdk2.getArch)();
|
|
290
|
+
const configDir = getTezBoxConfigDir(taskArgs);
|
|
291
|
+
const dataDir = getTezBoxDataDir(taskArgs);
|
|
292
|
+
const port = getContainerPort(taskArgs);
|
|
293
|
+
return { platform, image, containerName, configDir, dataDir, port };
|
|
294
|
+
}
|
|
295
|
+
async function ensureDirectoriesExist(directories) {
|
|
296
|
+
await Promise.all(
|
|
297
|
+
directories.map((dir) => fs.promises.mkdir(dir, { recursive: true }))
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
function constructDockerRunCommand(params) {
|
|
301
|
+
const { platform, image, containerName, configDir, port } = params;
|
|
302
|
+
const dockerOptions = [
|
|
303
|
+
"docker run",
|
|
304
|
+
"-d",
|
|
305
|
+
`--platform ${platform}`,
|
|
306
|
+
"-p 8732:8732",
|
|
307
|
+
`-p ${port}:20000`,
|
|
308
|
+
`--name ${containerName}`,
|
|
309
|
+
`-v "${configDir}:/tezbox/overrides"`,
|
|
310
|
+
image
|
|
311
|
+
// 'qenabox', // TODO: restore once working upstream
|
|
312
|
+
];
|
|
313
|
+
return dockerOptions.join(" ");
|
|
314
|
+
}
|
|
315
|
+
function validateBlockTime(taskArgs) {
|
|
316
|
+
const sandboxConfig = getSandboxConfig(taskArgs);
|
|
317
|
+
const blockTime = sandboxConfig.blockTime;
|
|
318
|
+
if (blockTime === void 0 || blockTime === null) {
|
|
319
|
+
logger.warn("Block time is not specified; skipping block_time override.");
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
return blockTime;
|
|
323
|
+
}
|
|
324
|
+
async function writeSandboxParameters(protocolId, parameters, tezBoxConfigDir) {
|
|
325
|
+
const protocolsDir = path.join(tezBoxConfigDir, "protocols", protocolId);
|
|
326
|
+
await fs.promises.mkdir(protocolsDir, { recursive: true });
|
|
327
|
+
const hjsonContent = hjson.stringify(parameters, {
|
|
328
|
+
quotes: "min",
|
|
329
|
+
bracesSameLine: true,
|
|
330
|
+
separator: false
|
|
331
|
+
});
|
|
332
|
+
const sandboxParamsPath = path.join(protocolsDir, "sandbox-parameters.hjson");
|
|
333
|
+
await fs.promises.writeFile(sandboxParamsPath, hjsonContent, "utf8");
|
|
334
|
+
try {
|
|
335
|
+
await fs.promises.chmod(sandboxParamsPath, 420);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
logger.warn(getErrorMessage(`Failed to set file permissions for ${sandboxParamsPath}`, error));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async function applyBlockTimeOverrideToProtocol(protocolId, blockTime, tezBoxConfigDir) {
|
|
341
|
+
const nonce_revelation_threshold = 16;
|
|
342
|
+
const minimal_block_delay = blockTime;
|
|
343
|
+
const parameters = {
|
|
344
|
+
minimal_block_delay: minimal_block_delay.toString()
|
|
345
|
+
};
|
|
346
|
+
await writeSandboxParameters(protocolId, parameters, tezBoxConfigDir);
|
|
347
|
+
}
|
|
348
|
+
async function applyBlockTimeOverrideToProtocols(protocolIds, blockTime, tezBoxConfigDir) {
|
|
349
|
+
await Promise.all(
|
|
350
|
+
protocolIds.map(async (protocolId) => {
|
|
351
|
+
if (/^alpha$/i.test(protocolId)) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
await applyBlockTimeOverrideToProtocol(protocolId, blockTime, tezBoxConfigDir);
|
|
355
|
+
})
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
async function getProtocolIds(taskArgs) {
|
|
359
|
+
const image = getImage(taskArgs);
|
|
360
|
+
const cmd = `docker run --rm --entrypoint ls ${image} /tezbox/configuration/protocols`;
|
|
361
|
+
const { stdout } = await runCommand(cmd);
|
|
362
|
+
const protocolIds = stdout.trim().split("\n").map((line) => line.trim()).filter((line) => line !== "");
|
|
363
|
+
return protocolIds;
|
|
364
|
+
}
|
|
365
|
+
async function readProtocolJson(image, protocolId) {
|
|
366
|
+
const cmd = `docker run --rm --entrypoint cat ${image} /tezbox/configuration/protocols/${protocolId}/protocol.hjson`;
|
|
367
|
+
try {
|
|
368
|
+
const { stdout } = await runCommand(cmd);
|
|
369
|
+
if (!stdout.trim()) {
|
|
370
|
+
logger.warn(`protocol.hjson not found or empty for protocolId ${protocolId}; skipping this protocol.`);
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
const protocolData = hjson.parse(stdout);
|
|
374
|
+
const protocolHash = protocolData.hash;
|
|
375
|
+
if (protocolHash) {
|
|
376
|
+
return { id: protocolId, hash: protocolHash };
|
|
377
|
+
} else {
|
|
378
|
+
logger.warn(`Protocol hash not found in protocol.hjson for protocolId ${protocolId}; skipping.`);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
} catch (error) {
|
|
382
|
+
logger.warn(getErrorMessage(`Failed to read protocol.hjson for protocolId ${protocolId}`, error));
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
async function getProtocolMappings(taskArgs) {
|
|
387
|
+
const image = getImage(taskArgs);
|
|
388
|
+
const protocolIds = await getProtocolIds(taskArgs);
|
|
389
|
+
const protocolMappingsPromises = protocolIds.map(async (protocolId) => {
|
|
390
|
+
const mapping = await readProtocolJson(image, protocolId);
|
|
391
|
+
return mapping;
|
|
392
|
+
});
|
|
393
|
+
const protocolMappings = await Promise.all(protocolMappingsPromises);
|
|
394
|
+
return protocolMappings.filter((mapping) => mapping !== null);
|
|
395
|
+
}
|
|
396
|
+
async function getOctezClientProtocols(taskArgs) {
|
|
397
|
+
const image = getImage(taskArgs);
|
|
398
|
+
const cmd = `docker run --rm --entrypoint octez-client ${image} -M mockup list mockup protocols`;
|
|
399
|
+
const { stdout } = await runCommand(cmd, (stderr) => {
|
|
400
|
+
const ignorableError = "Base directory /tezbox/data/.tezos-client does not exist.";
|
|
401
|
+
if (stderr.trim() !== "" && !stderr.includes(ignorableError)) {
|
|
402
|
+
throw new Error(`Failed to list protocols: ${stderr.trim()}`);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
const protocols = stdout.trim().split("\n").filter((line) => line.trim() !== "");
|
|
406
|
+
return protocols;
|
|
407
|
+
}
|
|
408
|
+
async function prepareSandboxParametersHjson(taskArgs, tezBoxConfigDir) {
|
|
409
|
+
try {
|
|
410
|
+
const blockTime = validateBlockTime(taskArgs);
|
|
411
|
+
if (blockTime === null) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const protocolMappings = await getProtocolMappings(taskArgs);
|
|
415
|
+
const hashToIdMap = {};
|
|
416
|
+
for (const mapping of protocolMappings) {
|
|
417
|
+
hashToIdMap[mapping.hash] = mapping.id;
|
|
418
|
+
}
|
|
419
|
+
const protocolHashes = await getOctezClientProtocols(taskArgs);
|
|
420
|
+
const protocolIdsToOverride = protocolHashes.map((hash) => hashToIdMap[hash]).filter((id) => id !== void 0);
|
|
421
|
+
if (protocolIdsToOverride.length === 0) {
|
|
422
|
+
logger.warn("No matching protocol IDs found; cannot set block_time override.");
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
await applyBlockTimeOverrideToProtocols(protocolIdsToOverride, blockTime, tezBoxConfigDir);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
const errorMessage = getErrorMessage(`Failed to prepare sandbox parameters:`, error);
|
|
428
|
+
throw new Error(errorMessage);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
async function prepareBakerHjson(tezBoxConfigDir) {
|
|
432
|
+
const servicesDir = path.join(tezBoxConfigDir, "services");
|
|
433
|
+
try {
|
|
434
|
+
await fs.promises.mkdir(servicesDir, { recursive: true });
|
|
435
|
+
const bakerConfig = {
|
|
436
|
+
autostart: false
|
|
437
|
+
};
|
|
438
|
+
const hjsonContent = hjson.stringify(bakerConfig, {
|
|
439
|
+
quotes: "all",
|
|
440
|
+
bracesSameLine: true,
|
|
441
|
+
separator: true
|
|
442
|
+
});
|
|
443
|
+
const bakerConfigPath = path.join(servicesDir, "baker.hjson");
|
|
444
|
+
await fs.promises.writeFile(bakerConfigPath, hjsonContent, "utf8");
|
|
445
|
+
} catch (error) {
|
|
446
|
+
throw new Error(getErrorMessage(`Failed to prepare baker.hjson`, error));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
async function prepareTezBoxOverrides(taskArgs) {
|
|
450
|
+
const tezBoxConfigDir = getTezBoxConfigDir(taskArgs);
|
|
451
|
+
try {
|
|
452
|
+
const sandboxConfig = getSandboxConfig(taskArgs);
|
|
453
|
+
await fs.promises.mkdir(tezBoxConfigDir, { recursive: true });
|
|
454
|
+
const tasks = [];
|
|
455
|
+
tasks.push(prepareBakersHjson(taskArgs, tezBoxConfigDir));
|
|
456
|
+
if (taskArgs.config.accounts) {
|
|
457
|
+
tasks.push(prepareAccountsHjson(taskArgs, tezBoxConfigDir));
|
|
458
|
+
}
|
|
459
|
+
if (sandboxConfig.blockTime) {
|
|
460
|
+
tasks.push(prepareSandboxParametersHjson(taskArgs, tezBoxConfigDir));
|
|
461
|
+
}
|
|
462
|
+
if (sandboxConfig.baking === "disabled" /* DISABLED */) {
|
|
463
|
+
tasks.push(prepareBakerHjson(tezBoxConfigDir));
|
|
464
|
+
}
|
|
465
|
+
await Promise.all(tasks);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
throw new Error(getErrorMessage(`Failed to prepare TezBox overrides`, error));
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function getProxyContainerName(taskArgs) {
|
|
471
|
+
return `${getDockerContainerName(taskArgs)}-proxy`;
|
|
472
|
+
}
|
|
473
|
+
async function startProxyServer(taskArgs) {
|
|
474
|
+
const containerPort = getContainerPort(taskArgs);
|
|
475
|
+
const proxyPort = getPortNumber(taskArgs);
|
|
476
|
+
const proxyContainerName = getProxyContainerName(taskArgs);
|
|
477
|
+
const proxyCmd = `docker run -d --name ${proxyContainerName} --network host caddy:2-alpine caddy reverse-proxy --from http://:${proxyPort} --to http://127.0.0.1:${containerPort} --access-log`;
|
|
478
|
+
try {
|
|
479
|
+
await runCommand(proxyCmd);
|
|
480
|
+
} catch (error) {
|
|
481
|
+
throw new Error(getErrorMessage(`Failed to start Caddy reverse proxy`, error));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function stopProxyServer(taskArgs) {
|
|
485
|
+
const proxyContainerName = getProxyContainerName(taskArgs);
|
|
486
|
+
const cmd = `docker rm -f ${proxyContainerName}`;
|
|
487
|
+
await runCommand(cmd);
|
|
488
|
+
}
|
|
489
|
+
async function startSandbox(taskArgs) {
|
|
490
|
+
try {
|
|
491
|
+
await checkDockerAvailability();
|
|
492
|
+
if (await checkSandboxRunning(taskArgs)) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const params = await getDockerRunParams(taskArgs);
|
|
496
|
+
await ensureDirectoriesExist([params.dataDir, params.configDir]);
|
|
497
|
+
await prepareTezBoxOverrides(taskArgs);
|
|
498
|
+
const cmd = constructDockerRunCommand(params);
|
|
499
|
+
await runCommand(cmd);
|
|
500
|
+
await startProxyServer(taskArgs);
|
|
501
|
+
await (0, import_node_sdk2.sendAsyncRes)("Sandbox started successfully.");
|
|
502
|
+
} catch (error) {
|
|
503
|
+
await (0, import_node_sdk2.sendAsyncErr)(getErrorMessage(`Failed to start sandbox`, error));
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async function checkDockerAvailability() {
|
|
507
|
+
try {
|
|
508
|
+
await runCommand("docker --version");
|
|
509
|
+
} catch (error) {
|
|
510
|
+
throw new Error("Docker is not installed or not running. Please install and start Docker.");
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function removeSandboxContainer(taskArgs) {
|
|
514
|
+
const containerName = getDockerContainerName(taskArgs);
|
|
515
|
+
const cmd = `docker rm -f ${containerName}`;
|
|
516
|
+
try {
|
|
517
|
+
await runCommand(cmd);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
const errorMessage = getErrorMessage("", error);
|
|
520
|
+
if (errorMessage.includes("No such container")) {
|
|
521
|
+
await (0, import_node_sdk2.sendAsyncRes)("Sandbox is not running or already stopped.");
|
|
522
|
+
} else {
|
|
523
|
+
throw new Error(errorMessage);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
await stopProxyServer(taskArgs);
|
|
527
|
+
}
|
|
528
|
+
async function stopSandbox(taskArgs) {
|
|
529
|
+
try {
|
|
530
|
+
await removeSandboxContainer(taskArgs);
|
|
531
|
+
const configDir = getTezBoxConfigDir(taskArgs);
|
|
532
|
+
await fs.promises.rm(configDir, { recursive: true, force: true });
|
|
533
|
+
await (0, import_node_sdk2.sendAsyncRes)("Sandbox stopped and cleaned up.");
|
|
534
|
+
} catch (error) {
|
|
535
|
+
await (0, import_node_sdk2.sendAsyncErr)(getErrorMessage(`Failed to stop sandbox`, error));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
async function restartSandbox(taskArgs) {
|
|
539
|
+
try {
|
|
540
|
+
await removeSandboxContainer(taskArgs);
|
|
541
|
+
await startSandbox(taskArgs);
|
|
542
|
+
await (0, import_node_sdk2.sendAsyncRes)("Sandbox restarted successfully.");
|
|
543
|
+
} catch (error) {
|
|
544
|
+
await (0, import_node_sdk2.sendAsyncErr)(getErrorMessage(`Failed to restart sandbox`, error));
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async function listProtocols(taskArgs) {
|
|
548
|
+
try {
|
|
549
|
+
const protocolHashes = await getOctezClientProtocols(taskArgs);
|
|
550
|
+
const protocolObjects = protocolHashes.map((protocol) => ({ protocol }));
|
|
551
|
+
await (0, import_node_sdk2.sendAsyncJsonRes)(protocolObjects);
|
|
552
|
+
} catch (error) {
|
|
553
|
+
await (0, import_node_sdk2.sendAsyncErr)(getErrorMessage(`Failed to list protocols`, error));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function listAccounts(taskArgs) {
|
|
557
|
+
try {
|
|
558
|
+
if (await isSandboxRunning(taskArgs)) {
|
|
559
|
+
const containerName = getDockerContainerName(taskArgs);
|
|
560
|
+
const cmd = `docker exec ${containerName} octez-client list known addresses`;
|
|
561
|
+
const { stdout } = await runCommand(cmd);
|
|
562
|
+
if (!stdout.trim()) {
|
|
563
|
+
await (0, import_node_sdk2.sendAsyncRes)("No accounts found.");
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const accounts = stdout.trim().split("\n").filter((line) => line.trim() !== "").map((line) => {
|
|
567
|
+
const [name, rest] = line.split(":");
|
|
568
|
+
const address = rest ? rest.trim().split(" ")[0] : "";
|
|
569
|
+
return { name: name.trim(), address };
|
|
570
|
+
});
|
|
571
|
+
await (0, import_node_sdk2.sendAsyncJsonRes)(accounts);
|
|
572
|
+
} else {
|
|
573
|
+
await (0, import_node_sdk2.sendAsyncErr)(`Sandbox is not running. Please start the sandbox before attempting to list accounts.`);
|
|
574
|
+
}
|
|
575
|
+
} catch (error) {
|
|
576
|
+
await (0, import_node_sdk2.sendAsyncErr)(getErrorMessage(`Failed to list accounts`, error));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
async function bakeBlock(taskArgs) {
|
|
580
|
+
try {
|
|
581
|
+
if (await isSandboxRunning(taskArgs)) {
|
|
582
|
+
const containerName = getDockerContainerName(taskArgs);
|
|
583
|
+
const cmd = `docker exec ${containerName} octez-client bake for baker1`;
|
|
584
|
+
if (taskArgs.watch) {
|
|
585
|
+
console.log("Baking on demand as operations are injected.");
|
|
586
|
+
console.log("Press CTRL-C to stop and exit.");
|
|
587
|
+
console.log();
|
|
588
|
+
while (true) {
|
|
589
|
+
console.log("Waiting for operations to be injected...");
|
|
590
|
+
while (true) {
|
|
591
|
+
const { stdout } = await runCommand(
|
|
592
|
+
`docker exec ${containerName} octez-client rpc get /chains/main/mempool/pending_operations`
|
|
593
|
+
);
|
|
594
|
+
const ops = JSON.parse(stdout);
|
|
595
|
+
if (Array.isArray(ops.applied) && ops.applied.length > 0) break;
|
|
596
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
597
|
+
}
|
|
598
|
+
await runCommand(cmd);
|
|
599
|
+
console.log("Block baked.");
|
|
600
|
+
}
|
|
601
|
+
} else {
|
|
602
|
+
await runCommand(cmd);
|
|
603
|
+
await (0, import_node_sdk2.sendAsyncRes)("Block baked successfully.");
|
|
604
|
+
}
|
|
605
|
+
} else {
|
|
606
|
+
try {
|
|
607
|
+
await (0, import_node_sdk2.sendAsyncErr)("Sandbox is not running. Please start the sandbox before attempting to bake a block.");
|
|
608
|
+
} catch {
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
} catch (error) {
|
|
612
|
+
await (0, import_node_sdk2.sendAsyncErr)(getErrorMessage(`Failed to bake block`, error));
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
var proxy = async (taskArgs) => {
|
|
616
|
+
var _a;
|
|
617
|
+
if (!isTezBoxEnvironment(taskArgs)) {
|
|
618
|
+
await (0, import_node_sdk2.sendAsyncErr)(
|
|
619
|
+
`This configuration doesn't appear to be configured to use TezBox environments. Check the ${taskArgs.env} environment in your .taq/config.json.`
|
|
620
|
+
);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
const taskName = (_a = taskArgs.task) == null ? void 0 : _a.toLowerCase().trim();
|
|
624
|
+
const taskHandlers = {
|
|
625
|
+
"start sandbox": startSandbox,
|
|
626
|
+
"stop sandbox": stopSandbox,
|
|
627
|
+
"restart sandbox": restartSandbox,
|
|
628
|
+
"list protocols": listProtocols,
|
|
629
|
+
"list-protocols": listProtocols,
|
|
630
|
+
"show protocols": listProtocols,
|
|
631
|
+
"show-protocols": listProtocols,
|
|
632
|
+
"list accounts": listAccounts,
|
|
633
|
+
"list-accounts": listAccounts,
|
|
634
|
+
"show accounts": listAccounts,
|
|
635
|
+
"show-accounts": listAccounts,
|
|
636
|
+
"bake": bakeBlock,
|
|
637
|
+
"bake block": bakeBlock
|
|
638
|
+
};
|
|
639
|
+
const handler = taskName ? taskHandlers[taskName] : void 0;
|
|
640
|
+
if (handler) {
|
|
641
|
+
try {
|
|
642
|
+
await handler(taskArgs);
|
|
643
|
+
} catch (error) {
|
|
644
|
+
await (0, import_node_sdk2.sendAsyncErr)(getErrorMessage(`Error executing task '${taskArgs.task}'`, error));
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
await (0, import_node_sdk2.sendAsyncErr)(taskArgs.task ? `Unknown task: ${taskArgs.task}` : "No task provided");
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
var proxy_default = proxy;
|
|
651
|
+
|
|
652
|
+
// index.ts
|
|
653
|
+
import_node_sdk3.Plugin.create((_i18n) => ({
|
|
654
|
+
alias: "tezbox",
|
|
655
|
+
schema: "1.0",
|
|
656
|
+
version: "0.1",
|
|
657
|
+
tasks: [
|
|
658
|
+
import_node_sdk3.Task.create({
|
|
659
|
+
task: "start sandbox",
|
|
660
|
+
command: "start sandbox",
|
|
661
|
+
aliases: ["start tezbox"],
|
|
662
|
+
description: "Starts a TezBox sandbox",
|
|
663
|
+
options: [],
|
|
664
|
+
handler: "proxy",
|
|
665
|
+
encoding: "none"
|
|
666
|
+
}),
|
|
667
|
+
import_node_sdk3.Task.create({
|
|
668
|
+
task: "stop sandbox",
|
|
669
|
+
command: "stop sandbox",
|
|
670
|
+
aliases: ["stop tezbox"],
|
|
671
|
+
description: "Stops a TezBox sandbox",
|
|
672
|
+
options: [],
|
|
673
|
+
handler: "proxy"
|
|
674
|
+
}),
|
|
675
|
+
import_node_sdk3.Task.create({
|
|
676
|
+
task: "restart sandbox",
|
|
677
|
+
command: "restart sandbox",
|
|
678
|
+
aliases: ["restart tezbox"],
|
|
679
|
+
description: "Restarts a TezBox sandbox",
|
|
680
|
+
options: [],
|
|
681
|
+
handler: "proxy"
|
|
682
|
+
}),
|
|
683
|
+
import_node_sdk3.Task.create({
|
|
684
|
+
task: "list accounts",
|
|
685
|
+
command: "list accounts",
|
|
686
|
+
aliases: [],
|
|
687
|
+
description: "List the balances of all sandbox accounts",
|
|
688
|
+
options: [],
|
|
689
|
+
handler: "proxy",
|
|
690
|
+
encoding: "json"
|
|
691
|
+
}),
|
|
692
|
+
import_node_sdk3.Task.create({
|
|
693
|
+
task: "bake",
|
|
694
|
+
command: "bake",
|
|
695
|
+
aliases: ["b"],
|
|
696
|
+
description: 'Manually bake a block. Use when the "baking" setting of a TezBox sandbox is set to "disabled".',
|
|
697
|
+
options: [
|
|
698
|
+
import_node_sdk3.Option.create({
|
|
699
|
+
flag: "watch",
|
|
700
|
+
shortFlag: "w",
|
|
701
|
+
description: "Watch for operations as they are injected into the mempool and bake them as immediate as possible.",
|
|
702
|
+
boolean: true
|
|
703
|
+
})
|
|
704
|
+
],
|
|
705
|
+
handler: "proxy",
|
|
706
|
+
encoding: "none"
|
|
707
|
+
}),
|
|
708
|
+
import_node_sdk3.Task.create({
|
|
709
|
+
task: "show protocols",
|
|
710
|
+
command: "show protocols",
|
|
711
|
+
aliases: ["list protocols"],
|
|
712
|
+
description: "List protocols understood by this version of TezBox",
|
|
713
|
+
options: [],
|
|
714
|
+
handler: "proxy",
|
|
715
|
+
encoding: "json"
|
|
716
|
+
})
|
|
717
|
+
],
|
|
718
|
+
proxy: proxy_default
|
|
719
|
+
}), process.argv);
|
|
720
|
+
//# sourceMappingURL=index.js.map
|