trac-msb 0.2.26 → 0.2.28
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 +34 -0
- package/package.json +11 -4
- package/rpc/handlers.js +9 -0
- package/rpc/routes/v1.js +2 -0
- package/scripts/check-hyperdht-bogon.cjs +50 -0
- package/scripts/hypercore-stallfix/apply.mjs +199 -0
- package/scripts/hypercore-stallfix/hypercore-11.18.3.mjs +434 -0
- package/scripts/hypercore-stallfix/hypercore-11.8.3.mjs +348 -0
- package/src/config/config.js +5 -0
- package/src/config/env.js +3 -0
- package/src/core/network/services/ReplicationStallGuard.js +177 -0
- package/src/index.js +28 -0
- package/src/utils/hypercoreStallfix.js +53 -0
package/README.md
CHANGED
|
@@ -37,6 +37,40 @@ cd main_settlement_bus
|
|
|
37
37
|
npm install
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
## HyperDHT LAN compatibility (0.2.28)
|
|
41
|
+
|
|
42
|
+
HyperDHT 6.29.x calls `bogon.isReserved` when connecting peers through a relay on
|
|
43
|
+
one host or LAN. Bogon 1.1.0 lacks that export; 1.2.0 first supplies it. This
|
|
44
|
+
release requires `bogon ^1.2.0` and checks the Bogon actually resolved by every
|
|
45
|
+
reachable HyperDHT copy during `postinstall`. This applies to regular peers as
|
|
46
|
+
well as validators.
|
|
47
|
+
|
|
48
|
+
A package lockfile or `overrides` in a dependency does not protect an application's
|
|
49
|
+
installation. Applications should update their own lockfiles and run
|
|
50
|
+
`node node_modules/trac-msb/scripts/check-hyperdht-bogon.cjs` after installation.
|
|
51
|
+
If lifecycle scripts are disabled, also run
|
|
52
|
+
`node node_modules/trac-msb/scripts/hypercore-stallfix/apply.mjs` and then repeat
|
|
53
|
+
it with `--check`. Both checks must succeed before starting the application.
|
|
54
|
+
The check reports an incompatible nested Bogon instead of modifying dependencies.
|
|
55
|
+
|
|
56
|
+
From this repository, run `npm run check:dependencies` and
|
|
57
|
+
`npm run test:hyperdht-bogon`. The regression uses only a local bootstrap and
|
|
58
|
+
throwaway keys, binds to loopback ports 59100–59199, and does not join a Trac
|
|
59
|
+
network. Start the application with `npm start` (there is no `prod` script).
|
|
60
|
+
|
|
61
|
+
## Upgrading from releases started with `npm run prod`
|
|
62
|
+
|
|
63
|
+
Early 0.2.x releases started with `npm run prod --store=<name>` (through `pear run`). Current releases have no
|
|
64
|
+
`prod` script; start a mainnet node with:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
npm run start -- --stores-directory stores/<name> --network mainnet
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The node reads its key from `stores/<name>/db/keypair.json`. A store created by an older release may live in a
|
|
71
|
+
different location, in which case the key file is not found. Copy that store's `db/keypair.json` to the new path
|
|
72
|
+
before the first start, or restore the wallet from its mnemonic.
|
|
73
|
+
|
|
40
74
|
## Post-install checklist
|
|
41
75
|
|
|
42
76
|
Before running tests, install bare globally:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trac-msb",
|
|
3
3
|
"main": "msb.mjs",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.28",
|
|
5
5
|
"pear": {
|
|
6
6
|
"name": "trac-msb",
|
|
7
7
|
"type": "terminal"
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
"NOTICE",
|
|
14
14
|
"rpc/",
|
|
15
15
|
"SECURITY.md",
|
|
16
|
+
"scripts/check-hyperdht-bogon.cjs",
|
|
17
|
+
"scripts/hypercore-stallfix/",
|
|
16
18
|
"scripts/pear-runner.js",
|
|
17
19
|
"src/"
|
|
18
20
|
],
|
|
19
21
|
"scripts": {
|
|
22
|
+
"postinstall": "node scripts/check-hyperdht-bogon.cjs && node scripts/hypercore-stallfix/apply.mjs",
|
|
20
23
|
"start": "NODE_OPTIONS='--max-old-space-size=4096' node scripts/pear-runner.js",
|
|
21
24
|
"rpc": "NODE_OPTIONS='--max-old-space-size=4096' node scripts/pear-runner.js --rpc --host ${npm_config_host} --port ${npm_config_port}",
|
|
22
25
|
"env": "if [ -f .env ]; then set -a; . ./.env; set +a; fi; NODE_OPTIONS='--max-old-space-size=4096' node scripts/pear-runner.js --stores-directory ${STORES_DIRECTORY:-stores} --network ${NETWORK:-mainnet}",
|
|
@@ -28,12 +31,15 @@
|
|
|
28
31
|
"docs:check": "node scripts/docs/docs-checker.js",
|
|
29
32
|
"test:acceptance": "node --experimental-vm-modules node_modules/jest/bin/jest.js --testTimeout=200000 tests/acceptance/v1/rpc.test.mjs --runInBand",
|
|
30
33
|
"test:integration": "brittle-node -t 1200000 tests/integration/integration.test.js",
|
|
34
|
+
"test:unit:prepare": "node scripts/hypercore-stallfix/apply.mjs && node scripts/hypercore-stallfix/apply.mjs --check",
|
|
31
35
|
"test:unit:node": "brittle-node -t 60000 tests/unit/unit.test.js",
|
|
32
36
|
"test:unit:bare": "brittle-bare -t 60000 tests/unit/unit.test.js",
|
|
33
37
|
"test:unit:pear": "BRITTLE='-t 60000' brittle-pear tests/unit/unit.test.js",
|
|
34
38
|
"test:pear-runner": "node --test tests/runner/pear-runner.test.js",
|
|
35
|
-
"test:unit:all": "(npm run test:unit:node && npm run test:unit:bare && npm run test:pear-runner) || exit 1",
|
|
36
|
-
"test:unit:node:cov": "brittle-node -c -t 60000 tests/unit/unit.test.js"
|
|
39
|
+
"test:unit:all": "(npm run test:unit:prepare && npm run test:hyperdht-bogon && npm run test:unit:node && npm run test:unit:bare && npm run test:pear-runner) || exit 1",
|
|
40
|
+
"test:unit:node:cov": "brittle-node -c -t 60000 tests/unit/unit.test.js",
|
|
41
|
+
"check:dependencies": "node scripts/check-hyperdht-bogon.cjs",
|
|
42
|
+
"test:hyperdht-bogon": "node --test tests/hyperdht-bogon-check.test.cjs && node tests/hyperdht-lan-repro.cjs"
|
|
37
43
|
},
|
|
38
44
|
"dependencies": {
|
|
39
45
|
"autobase": "7.20.1",
|
|
@@ -45,6 +51,7 @@
|
|
|
45
51
|
"bare-tty": "5.0.2",
|
|
46
52
|
"bare-utils": "1.5.1",
|
|
47
53
|
"bech32": "2.0.0",
|
|
54
|
+
"bogon": "^1.2.0",
|
|
48
55
|
"compact-encoding": "2.18.0",
|
|
49
56
|
"corestore": "7.5.0",
|
|
50
57
|
"crypto": "npm:bare-node-crypto",
|
|
@@ -55,8 +62,8 @@
|
|
|
55
62
|
"hypercore": "11.18.3",
|
|
56
63
|
"hypercore-crypto": "3.6.1",
|
|
57
64
|
"hyperdht": "6.29.6",
|
|
58
|
-
"hyperswarm": "4.14.2",
|
|
59
65
|
"hyperschema": "1.17.1",
|
|
66
|
+
"hyperswarm": "4.14.2",
|
|
60
67
|
"lodash": "^4.18.1",
|
|
61
68
|
"p-queue": "^9.1.0",
|
|
62
69
|
"pear-runtime": "1.3.1",
|
package/rpc/handlers.js
CHANGED
|
@@ -24,6 +24,15 @@ export async function handleHealth({ msbInstance, respond }) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
export async function handleReplication({ msbInstance, respond }) {
|
|
28
|
+
try {
|
|
29
|
+
if (!msbInstance?.opened || typeof msbInstance.replicationStatus !== 'function') throw new Error("RPC_OFFLINE");
|
|
30
|
+
respond(200, msbInstance.replicationStatus());
|
|
31
|
+
} catch {
|
|
32
|
+
respond(503, { error: "Could not connect to RPC server" });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
export async function handleBalance({ req, respond, msbInstance }) {
|
|
28
37
|
const url = buildRequestUrl(req);
|
|
29
38
|
const parts = url.pathname.split("/").filter(Boolean);
|
package/rpc/routes/v1.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleHealth,
|
|
3
|
+
handleReplication,
|
|
3
4
|
handleBalance,
|
|
4
5
|
handleTxv,
|
|
5
6
|
handleFee,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
|
|
16
17
|
export const v1Routes = [
|
|
17
18
|
{ method: 'GET', path: '/health', handler: handleHealth },
|
|
19
|
+
{ method: 'GET', path: '/replication', handler: handleReplication },
|
|
18
20
|
{ method: 'GET', path: '/balance', handler: handleBalance },
|
|
19
21
|
{ method: 'GET', path: '/txv', handler: handleTxv },
|
|
20
22
|
{ method: 'GET', path: '/fee', handler: handleFee },
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Check the actual Bogon seen by each reachable HyperDHT, including nested copies.
|
|
3
|
+
// A dependency's overrides and package-lock.json do not constrain its consumers.
|
|
4
|
+
const fs = require('node:fs')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
const { createRequire } = require('node:module')
|
|
7
|
+
|
|
8
|
+
function check(root = path.resolve(__dirname, '..')) {
|
|
9
|
+
const pending = [path.join(root, 'package.json')]
|
|
10
|
+
const seen = new Set()
|
|
11
|
+
let checked = 0
|
|
12
|
+
while (pending.length) {
|
|
13
|
+
const manifest = pending.pop()
|
|
14
|
+
if (seen.has(manifest)) continue
|
|
15
|
+
seen.add(manifest)
|
|
16
|
+
const pkg = JSON.parse(fs.readFileSync(manifest, 'utf8'))
|
|
17
|
+
const requireFrom = createRequire(manifest)
|
|
18
|
+
if (pkg.name === 'hyperdht') {
|
|
19
|
+
checked++
|
|
20
|
+
const [major, minor] = pkg.version.split('.').map(Number)
|
|
21
|
+
if (major < 6 || (major === 6 && minor < 29)) continue
|
|
22
|
+
const bogon = requireFrom('bogon')
|
|
23
|
+
if (typeof bogon.isReserved !== 'function' || typeof bogon.isBogon !== 'function') {
|
|
24
|
+
throw new Error(`HyperDHT ${pkg.version} resolves incompatible Bogon at ${requireFrom.resolve('bogon')}. Update the consumer lockfile to bogon >=1.2.0 and reinstall; same-host or LAN peer connections can otherwise crash.`)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
for (const name of Object.keys({ ...pkg.dependencies, ...pkg.optionalDependencies })) {
|
|
28
|
+
// Resolve physical manifests even when package exports hide package.json.
|
|
29
|
+
const paths = requireFrom.resolve.paths(`${name}/package.json`) || []
|
|
30
|
+
const resolved = paths.map(dir => path.join(dir, name, 'package.json')).find(file => fs.existsSync(file))
|
|
31
|
+
if (!resolved) {
|
|
32
|
+
if (pkg.optionalDependencies?.[name]) continue
|
|
33
|
+
throw new Error(`Missing dependency ${name} required by ${pkg.name}`)
|
|
34
|
+
}
|
|
35
|
+
pending.push(fs.realpathSync(resolved))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (checked === 0) throw new Error('No HyperDHT found; dependency installation is incomplete.')
|
|
39
|
+
return checked
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { check }
|
|
43
|
+
if (require.main === module) {
|
|
44
|
+
try {
|
|
45
|
+
console.log(`HyperDHT/Bogon compatibility: ${check()} copies checked`)
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(error.message)
|
|
48
|
+
process.exitCode = 1
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// OPENMAYHEM-STALLFIX: install-time Hypercore patch (identical copy in trac-msb and trac-peer, run from postinstall).
|
|
3
|
+
//
|
|
4
|
+
// Why: Hypercore block requests have no age limit, and a responder that has paused a peer drops that peer's block
|
|
5
|
+
// requests silently. A node whose needed request sits on such a peer never advances until the connection closes.
|
|
6
|
+
// The patch (one exact-text patch set per supported Hypercore version, see hypercore-<version>.mjs here):
|
|
7
|
+
// - cancels an in-flight request after 30 s on the wire, re-queues it for another peer and gives the silent peer a
|
|
8
|
+
// cool-down (30 s doubling to 4 min); one unref'd sweep per replicator, bounded work per sweep;
|
|
9
|
+
// - backports upstream 8cf8ad9 (#896) fix 2 (a stale block-queue entry no longer consumes a peer update);
|
|
10
|
+
// - answers NOT_AVAILABLE (max 16 per minute per peer, silent beyond) to a peer we paused, instead of silence.
|
|
11
|
+
// - backports upstream c24f37a (#911): never attach a stream that closed while the core was opening (v2).
|
|
12
|
+
// No wire-format change: patched and unpatched nodes interoperate.
|
|
13
|
+
//
|
|
14
|
+
// What this script does: finds every Hypercore copy this package loads (its own resolution plus the copies that
|
|
15
|
+
// autobase, corestore, trac-msb and trac-peer resolve from here, hoisted or nested), and for each copy:
|
|
16
|
+
// - supported version, file hash = original -> applies the patch set (each original text must occur exactly once),
|
|
17
|
+
// checks the result hash, writes atomically (tmp + rename);
|
|
18
|
+
// - supported version, file hash = patched -> "already patched" (idempotent; safe when both packages run it);
|
|
19
|
+
// - supported version, file hash = released v1 -> upgrades only the marker and #911 guard, checks the result hash;
|
|
20
|
+
// - supported version, any other content -> exit 1 with a clear message (nothing is written);
|
|
21
|
+
// - unsupported version -> loud warning, no change.
|
|
22
|
+
// Usage: node scripts/hypercore-stallfix/apply.mjs [--check] [--quiet]
|
|
23
|
+
// --check only report; exit 1 if a supported copy is not patched.
|
|
24
|
+
import fs from 'node:fs'
|
|
25
|
+
import path from 'node:path'
|
|
26
|
+
import crypto from 'node:crypto'
|
|
27
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
28
|
+
|
|
29
|
+
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
30
|
+
const PATCH_SETS = ['hypercore-11.18.3.mjs', 'hypercore-11.8.3.mjs']
|
|
31
|
+
const FOLLOW = ['autobase', 'corestore', 'trac-msb', 'trac-peer']
|
|
32
|
+
const MAX_DEPTH = 3
|
|
33
|
+
|
|
34
|
+
export async function loadPatchSets() {
|
|
35
|
+
const sets = new Map()
|
|
36
|
+
for (const name of PATCH_SETS) {
|
|
37
|
+
const mod = await import(pathToFileURL(path.join(here, name)).href)
|
|
38
|
+
sets.set(mod.default.version, mod.default)
|
|
39
|
+
}
|
|
40
|
+
return sets
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function sha256(text) {
|
|
44
|
+
return crypto.createHash('sha256').update(text).digest('hex')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function findPackageDir(name, fromDir) {
|
|
48
|
+
let dir = fromDir
|
|
49
|
+
for (;;) {
|
|
50
|
+
const candidate = path.join(dir, 'node_modules', name)
|
|
51
|
+
if (fs.existsSync(path.join(candidate, 'package.json'))) return fs.realpathSync(candidate)
|
|
52
|
+
const parent = path.dirname(dir)
|
|
53
|
+
if (parent === dir) return null
|
|
54
|
+
dir = parent
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Every Hypercore copy loaded from packageDir: bounded walk over FOLLOW packages, deduplicated by real path.
|
|
59
|
+
export function findHypercoreCopies(packageDir) {
|
|
60
|
+
const copies = new Map()
|
|
61
|
+
const seen = new Set()
|
|
62
|
+
let level = [fs.realpathSync(packageDir)]
|
|
63
|
+
for (let depth = 0; depth <= MAX_DEPTH && level.length > 0; depth++) {
|
|
64
|
+
const next = []
|
|
65
|
+
for (const dir of level) {
|
|
66
|
+
if (seen.has(dir)) continue
|
|
67
|
+
seen.add(dir)
|
|
68
|
+
const hc = findPackageDir('hypercore', dir)
|
|
69
|
+
if (hc !== null && !copies.has(hc)) copies.set(hc, path.relative(packageDir, dir) || '.')
|
|
70
|
+
for (const name of FOLLOW) {
|
|
71
|
+
const d = findPackageDir(name, dir)
|
|
72
|
+
if (d !== null && !seen.has(d)) next.push(d)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
level = next
|
|
76
|
+
}
|
|
77
|
+
return [...copies.entries()].map(([dir, from]) => ({ dir, from }))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function applyPatchSet(text, set) {
|
|
81
|
+
let out = text
|
|
82
|
+
for (const [original, replacement] of set.edits) {
|
|
83
|
+
const parts = out.split(original)
|
|
84
|
+
if (parts.length !== 2) {
|
|
85
|
+
throw new Error('expected original text exactly once, found ' + (parts.length - 1) + ': ' + JSON.stringify(original.slice(0, 80)))
|
|
86
|
+
}
|
|
87
|
+
out = parts[0] + replacement + parts[1]
|
|
88
|
+
}
|
|
89
|
+
return out
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Reverse of applyPatchSet (tests and forensics): restores the original text from a patched file.
|
|
93
|
+
export function unapplyPatchSet(text, set) {
|
|
94
|
+
let out = text
|
|
95
|
+
for (let i = set.edits.length - 1; i >= 0; i--) {
|
|
96
|
+
const [original, replacement] = set.edits[i]
|
|
97
|
+
const parts = out.split(replacement)
|
|
98
|
+
if (parts.length !== 2) throw new Error('expected patched text exactly once, found ' + (parts.length - 1))
|
|
99
|
+
out = parts[0] + original + parts[1]
|
|
100
|
+
}
|
|
101
|
+
return out
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function sha256Hex(text) {
|
|
105
|
+
return sha256(text)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function inspectCopy(copy, sets) {
|
|
109
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(copy.dir, 'package.json'), 'utf8'))
|
|
110
|
+
const set = sets.get(pkg.version) || null
|
|
111
|
+
const result = { dir: copy.dir, from: copy.from, version: pkg.version, supported: set !== null, state: null, sha256: null }
|
|
112
|
+
if (set === null) {
|
|
113
|
+
result.state = 'unsupported-version'
|
|
114
|
+
return result
|
|
115
|
+
}
|
|
116
|
+
const file = path.join(copy.dir, set.file)
|
|
117
|
+
const text = fs.readFileSync(file, 'utf8')
|
|
118
|
+
// keep the inspected text: patching must not re-read the file, a parallel run may have replaced it meanwhile
|
|
119
|
+
Object.defineProperty(result, 'text', { value: text, enumerable: false })
|
|
120
|
+
result.sha256 = sha256(text)
|
|
121
|
+
if (result.sha256 === set.patchedSha256) result.state = 'patched'
|
|
122
|
+
else if (result.sha256 === set.originalSha256) result.state = 'original'
|
|
123
|
+
else if (result.sha256 === set.previousPatchedSha256) result.state = 'previous-patched'
|
|
124
|
+
else if (text.includes('OPENMAYHEM-STALLFIX')) result.state = 'other-stallfix-revision'
|
|
125
|
+
else result.state = 'mismatch'
|
|
126
|
+
return result
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function run({ packageDir, check = false, quiet = false, log = console.log, warn = console.error } = {}) {
|
|
130
|
+
const sets = await loadPatchSets()
|
|
131
|
+
const copies = findHypercoreCopies(packageDir)
|
|
132
|
+
const name = JSON.parse(fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8')).name
|
|
133
|
+
const tag = '[hypercore-stallfix ' + name + ']'
|
|
134
|
+
let failed = false
|
|
135
|
+
|
|
136
|
+
if (copies.length === 0) warn(tag + ' WARNING: no hypercore copy found from ' + packageDir + ' (nothing patched)')
|
|
137
|
+
|
|
138
|
+
const results = []
|
|
139
|
+
for (const copy of copies) {
|
|
140
|
+
const r = inspectCopy(copy, sets)
|
|
141
|
+
const where = r.dir + ' (via ' + r.from + ')'
|
|
142
|
+
if (r.state === 'unsupported-version') {
|
|
143
|
+
warn(tag + ' WARNING: hypercore ' + r.version + ' at ' + where + ' is not a supported version (' + [...sets.keys()].join(', ') + '); NOT patched')
|
|
144
|
+
} else if (r.state === 'patched') {
|
|
145
|
+
if (!quiet) log(tag + ' hypercore ' + r.version + ' at ' + where + ': already patched (sha256 ' + r.sha256 + ')')
|
|
146
|
+
} else if ((r.state === 'original' || r.state === 'previous-patched') && check) {
|
|
147
|
+
warn(tag + ' hypercore ' + r.version + ' at ' + where + ': NOT patched to v2' + (r.state === 'previous-patched' ? ' (released v1 needs upgrade)' : ''))
|
|
148
|
+
failed = true
|
|
149
|
+
} else if (r.state === 'original' || r.state === 'previous-patched') {
|
|
150
|
+
const set = sets.get(r.version)
|
|
151
|
+
const file = path.join(r.dir, set.file)
|
|
152
|
+
const upgrading = r.state === 'previous-patched'
|
|
153
|
+
let patched
|
|
154
|
+
try {
|
|
155
|
+
patched = applyPatchSet(r.text, upgrading ? { edits: set.upgradeEdits } : set)
|
|
156
|
+
} catch (err) {
|
|
157
|
+
warn(tag + ' ERROR: hypercore ' + r.version + ' at ' + where + ': ' + err.message + '; nothing written')
|
|
158
|
+
failed = true
|
|
159
|
+
results.push(r)
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
if (sha256(patched) !== set.patchedSha256) {
|
|
163
|
+
warn(tag + ' ERROR: hypercore ' + r.version + ' at ' + where + ': patched result has an unexpected hash; nothing written')
|
|
164
|
+
failed = true
|
|
165
|
+
results.push(r)
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
// atomic: a parallel run (trac-msb and trac-peer postinstall) writes the same bytes, rename keeps one
|
|
169
|
+
const tmp = file + '.stallfix-' + process.pid + '.tmp'
|
|
170
|
+
fs.writeFileSync(tmp, patched)
|
|
171
|
+
fs.renameSync(tmp, file)
|
|
172
|
+
r.state = 'patched'
|
|
173
|
+
r.sha256 = set.patchedSha256
|
|
174
|
+
log(tag + ' hypercore ' + r.version + ' at ' + where + ': ' + (upgrading ? 'UPGRADED v1 -> v2' : 'PATCHED v2') + ' (sha256 ' + r.sha256 + ')')
|
|
175
|
+
} else {
|
|
176
|
+
const set = sets.get(r.version)
|
|
177
|
+
warn(tag + ' ERROR: hypercore ' + r.version + ' at ' + where + ' has unexpected ' + set.file + ' content (sha256 ' + r.sha256 +
|
|
178
|
+
(r.state === 'other-stallfix-revision' ? ', patched by a different stallfix revision' : '') +
|
|
179
|
+
'; expected original ' + set.originalSha256 + ', released v1 ' + set.previousPatchedSha256 + ' or patched v2 ' + set.patchedSha256 + '). Refusing to patch.')
|
|
180
|
+
failed = true
|
|
181
|
+
}
|
|
182
|
+
results.push(r)
|
|
183
|
+
}
|
|
184
|
+
return { ok: !failed, results }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (process.argv[1] && fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
188
|
+
const args = process.argv.slice(2)
|
|
189
|
+
const packageDir = path.resolve(here, '..', '..')
|
|
190
|
+
run({ packageDir, check: args.includes('--check'), quiet: args.includes('--quiet') }).then(({ ok }) => {
|
|
191
|
+
if (!ok) {
|
|
192
|
+
console.error('[hypercore-stallfix] FAILED: see the messages above. The Hypercore replication stall fix is not in place.')
|
|
193
|
+
process.exit(1)
|
|
194
|
+
}
|
|
195
|
+
}, (err) => {
|
|
196
|
+
console.error('[hypercore-stallfix] FAILED:', err && err.stack ? err.stack : err)
|
|
197
|
+
process.exit(1)
|
|
198
|
+
})
|
|
199
|
+
}
|