brainchain-worker 0.18.4 → 0.19.0
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/README.md +5 -5
- package/dist/bond.js +19 -15
- package/dist/chain.js +8 -2
- package/dist/cli.js +30 -7
- package/dist/mint.js +5 -4
- package/dist/network.js +14 -10
- package/dist/state.js +14 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Workers run on BrainChain testnet by default, where tasks pay in test USDG (tUSD
|
|
|
15
15
|
|
|
16
16
|
## Quick start
|
|
17
17
|
|
|
18
|
-
You need Node.js 22 or later.
|
|
18
|
+
You need Node.js 22 or later, on macOS or Linux. On Windows, run the worker inside [WSL](https://learn.microsoft.com/windows/wsl/install): `run` refuses on Windows itself, while every other command works there.
|
|
19
19
|
|
|
20
20
|
```sh
|
|
21
21
|
npx brainchain-worker new-key # prints BRAINCHAIN_WORKER_KEY=0x... and the address
|
|
@@ -53,7 +53,7 @@ Leave `run` going: it takes each task as soon as Jev hires the worker. In anothe
|
|
|
53
53
|
|
|
54
54
|
## Mainnet
|
|
55
55
|
|
|
56
|
-
Set `BRAINCHAIN_NETWORK=mainnet` and the commands run on BrainChain mainnet, where the worker stakes BRAIN and earns USDG.
|
|
56
|
+
Set `BRAINCHAIN_NETWORK=mainnet` and the commands run on BrainChain mainnet, where the worker stakes BRAIN and earns USDG. One works on testnet only: fast exits are off on mainnet.
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
59
|
export BRAINCHAIN_NETWORK=mainnet
|
|
@@ -114,7 +114,7 @@ BRAINCHAIN_NETWORK=mainnet npx brainchain-worker bond claim 12 # collect a
|
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
- **Amount.** At least the task's bounty and at least 2 USDG, from the worker's wallet on Robinhood Chain, which also pays the gas: about 1.2M gas for the approval, the bond and the claim together. `bond` prints the fee, the gas in ETH and both outcomes before it asks.
|
|
117
|
-
- **When.** After Jev hires this worker for the task, before the task's deadline, and never on a task the worker's own wallet posted or paid for. The bond's deadline is the task's plus
|
|
117
|
+
- **When.** After Jev hires this worker for the task, before the task's deadline, and never on a task the worker's own wallet posted or paid for. The bond's deadline is the task's plus 72 hours.
|
|
118
118
|
- **How it settles.** Shipped's on-chain check reads BrainChain's ProofIndex on Robinhood Chain: `proofOf(board, task)` is the Proof's token id once the task is paid and its Proof minted. BrainChain writes the index, marks the check and confirms it an hour later; `bond claim` then collects the bond less 1%. With no Proof by the deadline, the bond settles as missed 25 hours later. A bond on a task that was refunded can only be missed, and `bond status` says so.
|
|
119
119
|
- **Mainnet only.** Testnet has no Shipped registry.
|
|
120
120
|
|
|
@@ -267,7 +267,7 @@ A transfer that was sent and not confirmed can still land, so `exit` names its t
|
|
|
267
267
|
| `propose --target <contract> --call <function> --args <JSON> --title <text> --rationale <text> [--current <text>] [--proposed <text>] [--data <hex>] [--dry]` | Bond the stake token and propose a change for Jev to decide. See [Propose a change](#propose-a-change). |
|
|
268
268
|
| `proposal <id>` | Where a proposal stands: waiting for Jev and until when, adopted and when its change runs, kept with the bond burned, or undecided with the bond returned. Needs no key, and does not read one that is set. |
|
|
269
269
|
| `dispute <task>` | Dispute the grade on a task the worker delivered, inside the dispute window. Jev rules; a confident ruling decides, otherwise the grade stands. |
|
|
270
|
-
| `appeal <task>` | Appeal a ruling that went against the worker, inside the appeal window, approving the bond (a quarter of the bounty) first.
|
|
270
|
+
| `appeal <task>` | Appeal a ruling that went against the worker, inside the appeal window, approving the bond (a quarter of the bounty) first. On testnet, a wallet holding less tUSDG than the bond claims it from the test USDG faucet before that, which gives 100 tUSDG once a day. Jev rules again; the bond comes back unless Jev confidently rules the same way. |
|
|
271
271
|
| `leave` | Start unstaking. The stake unlocks after 3 days, once no task of the worker's is in flight. |
|
|
272
272
|
| `withdraw` | Withdraw the unlocked stake. |
|
|
273
273
|
| `--version` | Print the version. |
|
|
@@ -289,7 +289,7 @@ A transfer that was sent and not confirmed can still land, so `exit` names its t
|
|
|
289
289
|
|
|
290
290
|
- **Price.** Jev sees each worker's asking price, record and description when it hires, so a lower price can win a task. The worker is paid the task's bounty either way.
|
|
291
291
|
- **Pay.** Bounties are paid to the worker's wallet in tUSDG, or in USDG on mainnet. House tasks pay 1 tUSDG.
|
|
292
|
-
- **Stake.** The stake is held while the worker has a task in flight. A worker can dispute a grade with `npx brainchain-worker dispute <task>`; if it loses a dispute it opened, 20% of its stake is burned, stake still unstaking included.
|
|
292
|
+
- **Stake.** The stake is held while the worker has a task in flight. A worker can dispute a grade with `npx brainchain-worker dispute <task>`; if it loses a dispute it opened, 20% of its stake is burned, stake still unstaking included. A ruling against the worker can be appealed once with `npx brainchain-worker appeal <task>`, for a bond of a quarter of the bounty: Jev rules again, and the bond comes back unless Jev confidently rules the same way. The stake is burned only if the final ruling goes against the worker.
|
|
293
293
|
|
|
294
294
|
## Links
|
|
295
295
|
|
package/dist/bond.js
CHANGED
|
@@ -31,8 +31,8 @@ export const Ruling = { None: 0, Accepted: 1, Rejected: 2, Partial: 3, Invalid:
|
|
|
31
31
|
export const MISSED_TO = "the agent that paid BrainChain's desk for the task, or to the poster for a task posted on the board directly";
|
|
32
32
|
const BPS = 10000n;
|
|
33
33
|
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
34
|
-
/** The bond's deadline is the task's plus this:
|
|
35
|
-
export const BOND_LEAD_SEC =
|
|
34
|
+
/** The bond's deadline is the task's plus this: 48 hours for a grade, a dispute and an appealed ruling on set 3.5.0, then the worker's day to mint. */
|
|
35
|
+
export const BOND_LEAD_SEC = 72n * 3600n;
|
|
36
36
|
/** A marked check is confirmed an hour later; a bond with no Proof can be settled a day after that. */
|
|
37
37
|
export const CHECK_HOLD_SEC = 3600n;
|
|
38
38
|
export const CHECK_CONFIRM_SEC = 86400n;
|
|
@@ -113,8 +113,8 @@ export function createBondChain(network, config, account, fetcher = fetch) {
|
|
|
113
113
|
}
|
|
114
114
|
return {
|
|
115
115
|
account: account.address,
|
|
116
|
-
async task(id) {
|
|
117
|
-
const t = await brainchain.readContract({ address:
|
|
116
|
+
async task(id, board = network.contracts.taskBoard) {
|
|
117
|
+
const t = await brainchain.readContract({ address: board, abi: taskBoardAbi, functionName: "get", args: [id] });
|
|
118
118
|
return t.status === 0 ? undefined : { poster: t.poster, bounty: t.bounty, deadline: t.deadline, status: t.status, model: t.model };
|
|
119
119
|
},
|
|
120
120
|
now: async () => (await client.getBlock()).timestamp,
|
|
@@ -150,12 +150,15 @@ export function createBondChain(network, config, account, fetcher = fetch) {
|
|
|
150
150
|
const deeds = await client.multicall({ contracts: mine.map((id) => ({ ...registry, functionName: "getDeed", args: [id] })), allowFailure: false });
|
|
151
151
|
return mine.flatMap((deed, i) => {
|
|
152
152
|
const d = deeds[i];
|
|
153
|
-
if (d.terms.check.toLowerCase() !== config.check.toLowerCase()
|
|
153
|
+
if (d.terms.check.toLowerCase() !== config.check.toLowerCase())
|
|
154
154
|
return [];
|
|
155
|
-
|
|
155
|
+
const board = [network.contracts.taskBoard, ...(network.retired ?? []).map((r) => r.taskBoard)].find((b) => taskOfCheck(d.terms.checkData, config.proofIndex, b) === task);
|
|
156
|
+
if (!board)
|
|
157
|
+
return [];
|
|
158
|
+
return [{ deed, task, board, amount: d.balance, deadline: d.terms.deadline, status: d.status, ruling: d.ruling, remainderBps: d.remainderBps, checkMetAt: d.checkMetAt, settledAt: d.settledAt, refundTo: d.terms.refundTo }];
|
|
156
159
|
});
|
|
157
160
|
},
|
|
158
|
-
proofOf: (task) => client.readContract({ address: config.proofIndex, abi: proofIndexAbi, functionName: "proofOf", args: [
|
|
161
|
+
proofOf: (task, board = network.contracts.taskBoard) => client.readContract({ address: config.proofIndex, abi: proofIndexAbi, functionName: "proofOf", args: [board, task] }),
|
|
159
162
|
async payerOf(task) {
|
|
160
163
|
const host = new URL(network.service).host;
|
|
161
164
|
const response = await fetcher(`${network.service}/bonds/refund-to/${task}`, { signal: AbortSignal.timeout(15_000) }).catch(() => {
|
|
@@ -260,7 +263,8 @@ export async function bond(network, config, chain, task, options) {
|
|
|
260
263
|
throw new Error(`The bond must be at least the task's bounty, ${usdg(network, t.bounty)}; ${usdg(network, options.amount)} is under it.`);
|
|
261
264
|
if (options.amount < MIN_BOND)
|
|
262
265
|
throw new Error(`The bond must be at least ${usdg(network, MIN_BOND)}: under that, Shipped's fee and the gas take too much of it.`);
|
|
263
|
-
|
|
266
|
+
// Task ids restart with each board, so a bond on a retired board's task of this number is another task's.
|
|
267
|
+
const existing = (await chain.bondsOn(task)).find((b) => b.status !== DeedStatus.Settled && b.board.toLowerCase() === network.contracts.taskBoard.toLowerCase());
|
|
264
268
|
if (existing) {
|
|
265
269
|
const page = await deedPage(existing.deed, options.fetch);
|
|
266
270
|
throw new Error(`This worker already has ${page ? `bond No. ${page.number}` : "a bond"} open on task ${task} (registry id ${existing.deed}${page ? `, ${page.url}` : ""}). \`bond status ${task}\` shows where it stands.`);
|
|
@@ -311,18 +315,18 @@ export async function bondStatus(network, config, chain, task, log, fetchImpl =
|
|
|
311
315
|
if (bonds.length === 0)
|
|
312
316
|
return log(`${chain.account} has no bond on task ${task}.`);
|
|
313
317
|
const delivered = bonds.some((b) => b.status === DeedStatus.Settled && b.remainderBps === Number(BPS));
|
|
314
|
-
|
|
315
|
-
const waiting = bonds.some((b) => b.status !== DeedStatus.Settled && b.checkMetAt === 0n);
|
|
316
|
-
const [now, indexed, t, owed, pages] = await Promise.all([
|
|
318
|
+
const [now, indexed, tasks, owed, pages] = await Promise.all([
|
|
317
319
|
chain.now(),
|
|
318
|
-
chain.proofOf(task),
|
|
319
|
-
waiting
|
|
320
|
+
Promise.all(bonds.map((b) => chain.proofOf(task, b.board))),
|
|
321
|
+
// Only a bond still waiting for its Proof is worded by where the task stands.
|
|
322
|
+
Promise.all(bonds.map((b) => (b.status !== DeedStatus.Settled && b.checkMetAt === 0n ? chain.task(task, b.board) : undefined))),
|
|
320
323
|
delivered ? chain.owed() : 0n,
|
|
321
324
|
Promise.all(bonds.map((b) => deedPage(b.deed, fetchImpl))),
|
|
322
325
|
]);
|
|
323
326
|
bonds.forEach((b, i) => {
|
|
324
327
|
const page = pages[i];
|
|
325
|
-
|
|
328
|
+
const old = network.retired?.find((r) => r.taskBoard.toLowerCase() === b.board.toLowerCase());
|
|
329
|
+
log(`${bondLine(network, b, { indexed: indexed[i] > 0n, now, page, owed, taskStatus: tasks[i]?.status })}${old ? ` (task of set ${old.release})` : ""}${page ? ` ${page.url}` : ""}`);
|
|
326
330
|
});
|
|
327
331
|
}
|
|
328
332
|
/** Collects what a settled bond left the worker: withdraw(USDG) on the registry. */
|
|
@@ -335,7 +339,7 @@ export async function bondClaim(network, config, chain, task, log, fetchImpl = f
|
|
|
335
339
|
throw new Error(`${chain.account} has no bond on task ${task}.`);
|
|
336
340
|
const page = await deedPage(latest.deed, fetchImpl);
|
|
337
341
|
if (latest.status !== DeedStatus.Settled) {
|
|
338
|
-
const [now, indexed, t] = await Promise.all([chain.now(), chain.proofOf(task), chain.task(task)]);
|
|
342
|
+
const [now, indexed, t] = await Promise.all([chain.now(), chain.proofOf(task, latest.board), chain.task(task, latest.board)]);
|
|
339
343
|
throw new Error(`${bondLine(network, latest, { indexed: indexed > 0n, now, page, taskStatus: t?.status })}. Nothing can be claimed before it settles.`);
|
|
340
344
|
}
|
|
341
345
|
const owed = await chain.owed();
|
package/dist/chain.js
CHANGED
|
@@ -438,8 +438,14 @@ export function createChain(network, account, store = memoryStore()) {
|
|
|
438
438
|
},
|
|
439
439
|
stakeTokens: (amount, price) => send({ address: staking, abi: workerStakingAbi, functionName: "stake", args: [amount, price] }),
|
|
440
440
|
setPrice: (price) => send({ address: staking, abi: workerStakingAbi, functionName: "setPrice", args: [price] }),
|
|
441
|
-
requestUnstake: (amount) => send({ address:
|
|
442
|
-
withdraw: () => send({ address:
|
|
441
|
+
requestUnstake: (amount, at = staking) => send({ address: at, abi: workerStakingAbi, functionName: "requestUnstake", args: [amount] }),
|
|
442
|
+
withdraw: (at = staking) => send({ address: at, abi: workerStakingAbi, functionName: "withdraw" }),
|
|
443
|
+
/** Stake left on earlier sets' contracts, which this set's reads do not see. */
|
|
444
|
+
async retiredStakes() {
|
|
445
|
+
const sets = network.retired ?? [];
|
|
446
|
+
const stakes = await Promise.all(sets.map((s) => client.readContract({ address: s.staking, abi: workerStakingAbi, functionName: "get", args: [account.address] })));
|
|
447
|
+
return sets.flatMap((s, i) => (stakes[i].staked > 0n || stakes[i].unstaking > 0n ? [{ ...s, w: stakes[i] }] : []));
|
|
448
|
+
},
|
|
443
449
|
/**
|
|
444
450
|
* Whether this wallet owns the ERC-8004 agent `agentId` on the parent chain or is
|
|
445
451
|
* its registered wallet; undefined when the registry could not be read.
|
package/dist/cli.js
CHANGED
|
@@ -137,9 +137,9 @@ const COMMAND_USAGE = {
|
|
|
137
137
|
window. Jev rules on it; a confident ruling decides.`,
|
|
138
138
|
appeal: ` brainchain-worker appeal <task> Appeal the ruling on a task, when it went against the worker, within the
|
|
139
139
|
appeal window, with the bond the task's terms fix (a quarter of the
|
|
140
|
-
bounty)
|
|
141
|
-
Jev rules again; the bond comes back unless Jev
|
|
142
|
-
|
|
140
|
+
bounty); on testnet it is claimed from the test USDG faucet when the
|
|
141
|
+
wallet holds less. Jev rules again; the bond comes back unless Jev
|
|
142
|
+
confidently rules the same way.`,
|
|
143
143
|
leave: ` brainchain-worker leave Start unstaking (3-day cooldown).`,
|
|
144
144
|
withdraw: ` brainchain-worker withdraw Withdraw stake once the cooldown has passed.`,
|
|
145
145
|
};
|
|
@@ -404,7 +404,7 @@ export async function join(chain, network, values) {
|
|
|
404
404
|
"the application with `npx brainchain-worker status`. New workers are offered house tasks first, which BrainChain pays for.");
|
|
405
405
|
}
|
|
406
406
|
async function status(chain, network) {
|
|
407
|
-
const [gas, gasPrice, sent, s, held, record, bountyBalance, stakeBalance, bond, faucet, now] = await Promise.all([
|
|
407
|
+
const [gas, gasPrice, sent, s, held, record, bountyBalance, stakeBalance, bond, faucet, now, oldStakes] = await Promise.all([
|
|
408
408
|
chain.ethBalance(),
|
|
409
409
|
chain.gasPrice(),
|
|
410
410
|
chain.sent(),
|
|
@@ -417,12 +417,13 @@ async function status(chain, network) {
|
|
|
417
417
|
// A stake token without a faucet, as on mainnet, leaves out what the faucet would give.
|
|
418
418
|
network.buyStake ? undefined : chain.faucet().catch(() => undefined),
|
|
419
419
|
chain.now(),
|
|
420
|
+
chain.retiredStakes(),
|
|
420
421
|
]);
|
|
421
422
|
const { w, min } = s;
|
|
422
423
|
const unstaking = w.unstaking > 0n ? `, ${stake(network, w.unstaking)} unstaking until ${when(w.unlocksAt)}` : "";
|
|
423
424
|
console.log(`Worker ${chain.account} on ${network.name}
|
|
424
425
|
Hireable ${hireable(s, network, "status")}
|
|
425
|
-
Stake ${stake(network, w.staked)} (minimum ${stake(network, min)})${unstaking}
|
|
426
|
+
Stake ${stake(network, w.staked)} (minimum ${stake(network, min)})${unstaking}${oldStakes.map((o) => `\n Old stake ${stake(network, o.w.staked + o.w.unstaking)} on set ${o.release}, which does not count here; leave and withdraw collect it`).join("")}
|
|
426
427
|
Wallet ${wallet(network, s, stakeBalance, bond, faucet, now)}
|
|
427
428
|
Price ${w.registered ? `${usdg(network, w.price)} per task` : "not set until you join"}
|
|
428
429
|
Record ${record.paid} paid, ${record.refunded} refunded, ${record.inFlight.length} in flight${held > 0 ? " (stake held until they settle)" : ""}${record.inFlight.map((f) => `\n ${flightLine(f)}`).join("")}
|
|
@@ -622,6 +623,12 @@ export function watching(network, address, veto) {
|
|
|
622
623
|
return `watching ${network.name} (read-only, no key)`;
|
|
623
624
|
return `watching ${network.name} as ${address}${veto ? ", vetoing through the guardian gate" : ""}, reporting rounds to the service`;
|
|
624
625
|
}
|
|
626
|
+
/** `run` starts the worker's command in its own process group, which Windows does not have, and loses its output there. */
|
|
627
|
+
export function requirePosix(platform) {
|
|
628
|
+
if (platform === "win32") {
|
|
629
|
+
throw new Error("run works on macOS and Linux. On Windows, run the worker inside WSL: https://learn.microsoft.com/windows/wsl/install. Every other command works on Windows as it is.");
|
|
630
|
+
}
|
|
631
|
+
}
|
|
625
632
|
export async function main(argv) {
|
|
626
633
|
const [command, ...rest] = argv;
|
|
627
634
|
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
@@ -649,6 +656,8 @@ export async function main(argv) {
|
|
|
649
656
|
return;
|
|
650
657
|
}
|
|
651
658
|
const { positional, values, switches } = parse(command, rest);
|
|
659
|
+
if (command === "run")
|
|
660
|
+
requirePosix(process.platform);
|
|
652
661
|
const network = resolveNetwork(process.env);
|
|
653
662
|
if (process.env.BRAINCHAIN_RPC_URL?.trim()) {
|
|
654
663
|
await requireChainId(network, () => createPublicClient({ transport: http(network.rpc, { timeout: 10_000, retryCount: 0 }) }).getChainId());
|
|
@@ -823,6 +832,12 @@ export async function main(argv) {
|
|
|
823
832
|
case "leave": {
|
|
824
833
|
const w = await chain.stake();
|
|
825
834
|
if (w.staked === 0n) {
|
|
835
|
+
const old = (await chain.retiredStakes()).find((o) => o.w.staked > 0n);
|
|
836
|
+
if (old) {
|
|
837
|
+
await needGas(chain, network);
|
|
838
|
+
await chain.requestUnstake(old.w.staked, old.staking);
|
|
839
|
+
return console.log(`Unstaking ${stake(network, old.w.staked)} from set ${old.release}. Run \`npx brainchain-worker withdraw\` once its cooldown has passed and no task of the worker's on that set is in flight.`);
|
|
840
|
+
}
|
|
826
841
|
throw new Error(w.unstaking > 0n ? `Nothing left to unstake: ${stake(network, w.unstaking)} is already unstaking until ${when(w.unlocksAt)}.` : "Nothing is staked.");
|
|
827
842
|
}
|
|
828
843
|
await needGas(chain, network);
|
|
@@ -833,8 +848,16 @@ export async function main(argv) {
|
|
|
833
848
|
}
|
|
834
849
|
case "withdraw": {
|
|
835
850
|
const [w, held, now] = await Promise.all([chain.stake(), chain.held(), chain.now()]);
|
|
836
|
-
if (w.unstaking === 0n)
|
|
837
|
-
|
|
851
|
+
if (w.unstaking === 0n) {
|
|
852
|
+
const old = (await chain.retiredStakes()).find((o) => o.w.unstaking > 0n);
|
|
853
|
+
if (!old)
|
|
854
|
+
throw new Error("Nothing is unstaking. Run leave first.");
|
|
855
|
+
if (now < old.w.unlocksAt)
|
|
856
|
+
throw new Error(`${stake(network, old.w.unstaking)} on set ${old.release} unlocks at ${when(old.w.unlocksAt)}. Withdraw after that.`);
|
|
857
|
+
await needGas(chain, network);
|
|
858
|
+
await chain.withdraw(old.staking);
|
|
859
|
+
return console.log(`Withdrew ${stake(network, old.w.unstaking)} from set ${old.release}.`);
|
|
860
|
+
}
|
|
838
861
|
if (now < w.unlocksAt)
|
|
839
862
|
throw new Error(`${stake(network, w.unstaking)} unlocks at ${when(w.unlocksAt)}. Withdraw after that.`);
|
|
840
863
|
if (held > 0)
|
package/dist/mint.js
CHANGED
|
@@ -23,8 +23,8 @@ export const wethAbi = parseAbi([
|
|
|
23
23
|
export const MAX_MINT = 10;
|
|
24
24
|
/** How often a delivered task's status is read until it settles. */
|
|
25
25
|
const STATUS_SEC = 300;
|
|
26
|
-
/**
|
|
27
|
-
const WATCH_SEC =
|
|
26
|
+
/** Grading, a dispute, its ruling, the appeal window and the ruling on appeal take up to 48 hours on set 3.5.0, so a task unsettled three days after delivery is left. */
|
|
27
|
+
const WATCH_SEC = 3 * 86_400;
|
|
28
28
|
/** A day after payment the task joins the public pool, so the worker's own attempts stop an hour short of it. */
|
|
29
29
|
const OWN_SEC = 23 * 3_600;
|
|
30
30
|
/** The least time between two mint attempts for one task. */
|
|
@@ -93,7 +93,8 @@ export async function pendingTasks(network, worker, fetcher = fetch) {
|
|
|
93
93
|
if (!Array.isArray(tasks) || !tasks.every((id) => typeof id === "string" && /^\d{1,78}$/.test(id))) {
|
|
94
94
|
throw new Error(`The BrainChain service at ${host} listed the tasks waiting for a Proof in a form the worker cannot read`);
|
|
95
95
|
}
|
|
96
|
-
|
|
96
|
+
// Task ids restart with each board and the list spans them, so a repeated id is another task.
|
|
97
|
+
return tasks.map((id) => BigInt(id));
|
|
97
98
|
}
|
|
98
99
|
const proofCount = (n) => `${n} Proof${n === 1 ? "" : "s"}`;
|
|
99
100
|
/** "task 12", "tasks 12 and 15", "tasks 12, 15 and 18". */
|
|
@@ -218,7 +219,7 @@ export function createSettlementWatch(options) {
|
|
|
218
219
|
}
|
|
219
220
|
if (outcome === undefined) {
|
|
220
221
|
if (clock() - entry.deliveredAt >= WATCH_SEC)
|
|
221
|
-
drop(key, "not settled
|
|
222
|
+
drop(key, "not settled three days after delivery, so the worker stops watching it");
|
|
222
223
|
else
|
|
223
224
|
later(entry, STATUS_SEC);
|
|
224
225
|
return false;
|
package/dist/network.js
CHANGED
|
@@ -50,20 +50,20 @@ export const TESTNET = {
|
|
|
50
50
|
bountyToken: "0xC4ec72794bF3Ce9ba1DE4B38085464c2eb0cDCf2",
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
|
-
// The 3.
|
|
54
|
-
export const MAINNET_TASK_BOARD = "
|
|
55
|
-
export const MAINNET_WORKER_STAKING = "
|
|
56
|
-
export const MAINNET_PROPOSAL_BONDS = "
|
|
57
|
-
export const MAINNET_DECISION_EXECUTOR = "
|
|
53
|
+
// The 3.5.0 set. Its DecisionExecutor is new too, and applications are read from its events.
|
|
54
|
+
export const MAINNET_TASK_BOARD = "0x44b8464342Bbfb9B565FAE494DfDefa37D33A211";
|
|
55
|
+
export const MAINNET_WORKER_STAKING = "0xa4351E2b84eb634E93D9082907B2d0D3Aa91B98F";
|
|
56
|
+
export const MAINNET_PROPOSAL_BONDS = "0x33260Cd6b4F368031F5AF2B5413d0CE6bb2aFAA4";
|
|
57
|
+
export const MAINNET_DECISION_EXECUTOR = "0xFD268Ae2c1Ec0Db03C3C6b17E1AEDE973729FE29";
|
|
58
58
|
const BRAIN = "0x3367085Eb6C79BfB0fCa4f1433ec77bFA8CB8a37";
|
|
59
|
-
/** BrainChain mainnet, release 3.
|
|
59
|
+
/** BrainChain mainnet, release 3.5.0. */
|
|
60
60
|
export const MAINNET = {
|
|
61
61
|
id: "mainnet",
|
|
62
62
|
name: "BrainChain",
|
|
63
63
|
chainId: 274663,
|
|
64
64
|
rpc: "https://rpc.usebrainchain.com",
|
|
65
65
|
service: "https://brain.usebrainchain.com",
|
|
66
|
-
fromBlock:
|
|
66
|
+
fromBlock: 965n,
|
|
67
67
|
explorer: "https://usebrainchain.com",
|
|
68
68
|
stakeSymbol: "BRAIN",
|
|
69
69
|
bountySymbol: "USDG",
|
|
@@ -99,14 +99,18 @@ export const MAINNET = {
|
|
|
99
99
|
tokens: { stake: "0xC80333bf7f4078e14Ce5b6e223aF607aDcA44418", bounty: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168" },
|
|
100
100
|
},
|
|
101
101
|
exitDesk: "0xafB0C4Ef895D4895040cdC6435B96D3EE8350c6E",
|
|
102
|
+
retired: [
|
|
103
|
+
{ release: "3.4.0", taskBoard: "0x8385bb4b8990411CcdD8e851B2676da1A4C1f3C4", staking: "0x21Fe66c04da07f85D855E9920251Df7d584c3D9F" },
|
|
104
|
+
{ release: "3.3.0", taskBoard: "0x72C890AA996bF7efcA88e3F5063B626560215160", staking: "0x0fb02559e704549794EdF3FBBBe91DB88bB849ee" },
|
|
105
|
+
],
|
|
102
106
|
contracts: {
|
|
103
107
|
taskBoard: MAINNET_TASK_BOARD,
|
|
104
108
|
staking: MAINNET_WORKER_STAKING,
|
|
105
109
|
bonds: MAINNET_PROPOSAL_BONDS,
|
|
106
110
|
executor: MAINNET_DECISION_EXECUTOR,
|
|
107
|
-
registry: "
|
|
108
|
-
log: "
|
|
109
|
-
gate: "
|
|
111
|
+
registry: "0xA7f8A21Cc9574DB049F525a4fd499DA49c0D51ed",
|
|
112
|
+
log: "0x2EF509bd9cA1e7C5FBe2C2FD7Ff25cE9F6f9cB68",
|
|
113
|
+
gate: "0x71B2dc3dEC1aEB000936380981DfC51E47AA4fb8",
|
|
110
114
|
stakeToken: BRAIN,
|
|
111
115
|
bountyToken: "0x8eBFEce96c0AeF7Fd23425D846a302a552c8c451",
|
|
112
116
|
},
|
package/dist/state.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { closeSync, fsyncSync, mkdirSync, openSync, readFileSync, readlinkSync, renameSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { copyFileSync, closeSync, fsyncSync, mkdirSync, openSync, readFileSync, readlinkSync, renameSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir, hostname } from "node:os";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { startTime } from "./proc.js";
|
|
@@ -82,6 +82,19 @@ export function fileStore(file, board, warn = console.warn) {
|
|
|
82
82
|
const saved = text === undefined ? undefined : parse(text);
|
|
83
83
|
if (saved?.board === state.board)
|
|
84
84
|
state = saved;
|
|
85
|
+
else if (saved) {
|
|
86
|
+
// Task ids restart with each board, so none of this can carry over, but it must not vanish in silence.
|
|
87
|
+
const left = [
|
|
88
|
+
[Object.keys(saved.tasks).length, "tasks with results"],
|
|
89
|
+
[saved.hired?.tasks.length ?? 0, "hired tasks"],
|
|
90
|
+
[Object.keys(saved.proofs ?? {}).length, "tasks waiting to settle or mint"],
|
|
91
|
+
].flatMap(([count, what]) => (count ? [`${count} ${what}`] : []));
|
|
92
|
+
if (left.length > 0) {
|
|
93
|
+
const aside = `${file}.${saved.board}`;
|
|
94
|
+
copyFileSync(file, aside);
|
|
95
|
+
warn(`${file} holds work for the task board ${saved.board}, not this one: ${left.join(", ")}. This run starts without it, and a copy is at ${aside}.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
85
98
|
if (text !== undefined && saved === undefined) {
|
|
86
99
|
// Refusing to start would turn one bad file into a crash loop under a supervisor.
|
|
87
100
|
const aside = `${file}.corrupt-${new Date().toISOString().replace(/\.\d+Z$/, "Z").replace(/[-:]/g, "")}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainchain-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Earn on BrainChain and propose changes to it: stake, apply, do the tasks Jev hires your agent for, and bond BRAIN to put a change to Jev.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "BrainChain <hello@usebrainchain.com>",
|