@steve02081504/fount-p2p 0.0.31 → 0.0.32
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/AGENTS.md +79 -0
- package/docs/evfs.md +32 -0
- package/docs/infra.md +25 -0
- package/docs/mesh.md +67 -0
- package/docs/reputation.md +22 -0
- package/docs/runtime.md +62 -0
- package/docs/signaling.md +23 -0
- package/docs/transports.md +92 -0
- package/docs/wire.md +21 -0
- package/package.json +2 -9
- package/sim/AGENTS.md +29 -0
- package/sim/admission.mjs +56 -0
- package/sim/anomaly.mjs +39 -0
- package/sim/apply.mjs +77 -0
- package/sim/attack_space.mjs +113 -0
- package/sim/attacks.mjs +614 -0
- package/sim/behavior.mjs +78 -0
- package/sim/cli.mjs +282 -0
- package/sim/coevolution.mjs +245 -0
- package/sim/cold_start.md +13 -0
- package/sim/constants.mjs +44 -0
- package/sim/discovery.mjs +259 -0
- package/sim/duration.mjs +64 -0
- package/sim/graph_adj.mjs +28 -0
- package/sim/integrity.mjs +58 -0
- package/sim/metrics.mjs +423 -0
- package/sim/model.mjs +1176 -0
- package/sim/optimizer.mjs +250 -0
- package/sim/propagation.mjs +57 -0
- package/sim/report.mjs +176 -0
- package/sim/reputation_social.tunables.json +6 -0
- package/sim/rng.mjs +62 -0
- package/sim/scenarios.mjs +345 -0
- package/sim/sim_pool.mjs +169 -0
- package/sim/sim_worker.mjs +31 -0
- package/sim/social_reputation.mjs +30 -0
- package/sim/space.mjs +312 -0
- package/sim/test/admission.test.mjs +25 -0
- package/sim/test/attack_space.test.mjs +39 -0
- package/sim/test/behavior_vector.test.mjs +37 -0
- package/sim/test/coevolution.test.mjs +32 -0
- package/sim/test/cold_start.test.mjs +46 -0
- package/sim/test/defense.test.mjs +320 -0
- package/sim/test/discovery.test.mjs +68 -0
- package/sim/test/duration.test.mjs +20 -0
- package/sim/test/fidelity.test.mjs +151 -0
- package/sim/test/integrity.test.mjs +56 -0
- package/sim/test/optimizer.test.mjs +24 -0
- package/sim/test/propagation.test.mjs +29 -0
- package/sim/test/sim_model_regressions.test.mjs +110 -0
- package/sim/test/slow_drip.test.mjs +28 -0
- package/sim/test/smoke.test.mjs +52 -0
- package/sim/test/transport.test.mjs +55 -0
- package/sim/test/tunables_apply.test.mjs +64 -0
- package/sim/test/tunables_param_space.test.mjs +74 -0
- package/sim/test/vulnerability.test.mjs +127 -0
- package/sim/transport.mjs +135 -0
- package/sim/tunables_bundle.mjs +71 -0
- package/sim/vulnerability.mjs +532 -0
- package/deno.json +0 -7
package/sim/apply.mjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 按模块写回 tunables JSON。
|
|
3
|
+
*/
|
|
4
|
+
import { writeFile } from 'node:fs/promises'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
|
|
8
|
+
import { normalizeBundle, sanitizeBundle } from './space.mjs'
|
|
9
|
+
|
|
10
|
+
/** @typedef {import('./tunables_bundle.mjs').TunablesBundle} TunablesBundle */
|
|
11
|
+
|
|
12
|
+
/** @type {Record<Exclude<keyof TunablesBundle, 'social'>, URL>} */
|
|
13
|
+
const PACKAGE_MODULE_URLS = {
|
|
14
|
+
reputation: new URL('../reputation/tunables.json', import.meta.url),
|
|
15
|
+
trustGraph: new URL('../trust_graph/tunables.json', import.meta.url),
|
|
16
|
+
mailbox: new URL('../mailbox/tunables.json', import.meta.url),
|
|
17
|
+
archive: new URL('../dag/tunables.json', import.meta.url),
|
|
18
|
+
admission: new URL('../governance/tunables.json', import.meta.url),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {TunablesBundle} bundle 完整 tunables
|
|
23
|
+
* @returns {TunablesBundle} 写盘前规整后的 bundle
|
|
24
|
+
*/
|
|
25
|
+
export function prepareBundleForApply(bundle) {
|
|
26
|
+
return sanitizeBundle(normalizeBundle(bundle))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} socialTunablesPath 外部 social shell 的 reputation_social.tunables.json 绝对或相对路径
|
|
31
|
+
* @returns {string} 规范化绝对路径
|
|
32
|
+
*/
|
|
33
|
+
export function resolveSocialTunablesPath(socialTunablesPath) {
|
|
34
|
+
const raw = String(socialTunablesPath ?? '').trim()
|
|
35
|
+
if (!raw) throw new Error('socialTunablesPath required to write social tunables')
|
|
36
|
+
return path.resolve(raw)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {keyof TunablesBundle} module 模块键
|
|
41
|
+
* @param {Record<string, unknown>} data JSON 内容
|
|
42
|
+
* @param {string} [socialTunablesPath] social 模块写入路径(module === 'social' 时必填)
|
|
43
|
+
* @returns {Promise<string>} 写入路径
|
|
44
|
+
*/
|
|
45
|
+
export async function writeModuleTunables(module, data, socialTunablesPath) {
|
|
46
|
+
const filePath = module === 'social'
|
|
47
|
+
? resolveSocialTunablesPath(socialTunablesPath)
|
|
48
|
+
: fileURLToPath(PACKAGE_MODULE_URLS[module])
|
|
49
|
+
await writeFile(filePath, `${JSON.stringify(data, null, '\t')}\n`, 'utf8')
|
|
50
|
+
return filePath
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {TunablesBundle} bundle 完整 tunables
|
|
55
|
+
* @param {{ socialTunablesPath: string }} paths 外部模块路径(social 写回 shell 侧 JSON)
|
|
56
|
+
* @returns {Promise<string[]>} 各模块 JSON 写入路径
|
|
57
|
+
*/
|
|
58
|
+
export async function applyTunablesBundle(bundle, paths) {
|
|
59
|
+
const socialPath = resolveSocialTunablesPath(paths?.socialTunablesPath)
|
|
60
|
+
const ready = prepareBundleForApply(bundle)
|
|
61
|
+
const written = []
|
|
62
|
+
written.push(await writeModuleTunables('reputation', ready.reputation))
|
|
63
|
+
written.push(await writeModuleTunables('trustGraph', ready.trustGraph))
|
|
64
|
+
written.push(await writeModuleTunables('social', ready.social, socialPath))
|
|
65
|
+
written.push(await writeModuleTunables('mailbox', ready.mailbox))
|
|
66
|
+
written.push(await writeModuleTunables('archive', ready.archive))
|
|
67
|
+
written.push(await writeModuleTunables('admission', ready.admission))
|
|
68
|
+
return written
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} [dir] sim 目录(默认本模块目录)
|
|
73
|
+
* @returns {string} results 子目录绝对路径
|
|
74
|
+
*/
|
|
75
|
+
export function resultsDirFromSim(dir = path.dirname(fileURLToPath(import.meta.url))) {
|
|
76
|
+
return path.join(dir, 'results')
|
|
77
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 攻击者可进化基因(与 space.mjs 采样器共用 latent 语义)。
|
|
3
|
+
*/
|
|
4
|
+
import { createRng } from './rng.mjs'
|
|
5
|
+
import { normalizeParam, sampleParam } from './space.mjs'
|
|
6
|
+
import { loadDefaultTunables } from './tunables_bundle.mjs'
|
|
7
|
+
|
|
8
|
+
/** @typedef {import('./attacks.mjs').AttackKind} AttackKind */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {{
|
|
12
|
+
* global: { activationRate: number, burstSize: number, targetBiasHighRep: number, eclipseFocus: number },
|
|
13
|
+
* byAttack: Partial<Record<AttackKind, { activationRate?: number, burstSize?: number, collusionAllyRate?: number, sleeperTurnFrac?: number, hintWeightMul?: number }>>,
|
|
14
|
+
* }} AttackGenome
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** @type {import('./space.mjs').ParamSpec[]} */
|
|
18
|
+
const GLOBAL_ATTACK_SPECS = [
|
|
19
|
+
{ module: 'reputation', key: 'relayRepBump', kind: 'unit' }, // reuse kind; values remapped below
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const DEFAULT_GENOME = Object.freeze({
|
|
23
|
+
global: {
|
|
24
|
+
activationRate: 0.55,
|
|
25
|
+
burstSize: 4,
|
|
26
|
+
targetBiasHighRep: 0.35,
|
|
27
|
+
eclipseFocus: 0.65,
|
|
28
|
+
},
|
|
29
|
+
byAttack: {},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {() => number} rng 随机源
|
|
34
|
+
* @returns {AttackGenome} 随机攻击基因组
|
|
35
|
+
*/
|
|
36
|
+
export function randomAttackGenome(rng) {
|
|
37
|
+
return {
|
|
38
|
+
global: {
|
|
39
|
+
activationRate: 0.2 + rng() * 0.75,
|
|
40
|
+
burstSize: 1 + Math.floor(rng() * 8),
|
|
41
|
+
targetBiasHighRep: rng() * 0.9,
|
|
42
|
+
eclipseFocus: 0.3 + rng() * 0.65,
|
|
43
|
+
},
|
|
44
|
+
byAttack: {},
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {AttackGenome} parent 父代
|
|
50
|
+
* @param {number} seed 种子
|
|
51
|
+
* @returns {AttackGenome} 变异子代
|
|
52
|
+
*/
|
|
53
|
+
export function mutateAttackGenome(parent, seed) {
|
|
54
|
+
const rng = createRng(seed)
|
|
55
|
+
const child = structuredClone(parent)
|
|
56
|
+
if (rng() < 0.4) child.global.activationRate = Math.max(0.05, Math.min(0.98, child.global.activationRate + (rng() - 0.5) * 0.2))
|
|
57
|
+
if (rng() < 0.35) child.global.burstSize = Math.max(1, Math.min(12, child.global.burstSize + Math.floor((rng() - 0.5) * 4)))
|
|
58
|
+
if (rng() < 0.3) child.global.targetBiasHighRep = Math.max(0, Math.min(1, child.global.targetBiasHighRep + (rng() - 0.5) * 0.25))
|
|
59
|
+
if (rng() < 0.3) child.global.eclipseFocus = Math.max(0.1, Math.min(1, child.global.eclipseFocus + (rng() - 0.5) * 0.25))
|
|
60
|
+
return child
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {AttackKind} attack 攻击类型
|
|
65
|
+
* @param {AttackGenome} genome 基因组
|
|
66
|
+
* @returns {{ activationRate: number, burstSize: number, targetBiasHighRep: number, eclipseFocus: number, collusionAllyRate: number, sleeperTurnFrac: number, hintWeightMul: number }} 有效参数
|
|
67
|
+
*/
|
|
68
|
+
export function resolveAttackParams(attack, genome) {
|
|
69
|
+
const g = genome.global
|
|
70
|
+
const row = genome.byAttack[attack] ?? {}
|
|
71
|
+
return {
|
|
72
|
+
activationRate: row.activationRate ?? g.activationRate,
|
|
73
|
+
burstSize: Math.max(1, Math.floor(row.burstSize ?? g.burstSize)),
|
|
74
|
+
targetBiasHighRep: g.targetBiasHighRep,
|
|
75
|
+
eclipseFocus: g.eclipseFocus,
|
|
76
|
+
collusionAllyRate: row.collusionAllyRate ?? 0.25,
|
|
77
|
+
sleeperTurnFrac: row.sleeperTurnFrac ?? 0.35,
|
|
78
|
+
hintWeightMul: row.hintWeightMul ?? 3,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {AttackGenome | undefined} genome 基因组
|
|
84
|
+
* @returns {AttackGenome} 默认补齐
|
|
85
|
+
*/
|
|
86
|
+
export function normalizeAttackGenome(genome) {
|
|
87
|
+
if (!genome) return structuredClone(DEFAULT_GENOME)
|
|
88
|
+
return {
|
|
89
|
+
global: { ...DEFAULT_GENOME.global, ...genome.global },
|
|
90
|
+
byAttack: { ...genome.byAttack },
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** 名人堂容量 */
|
|
95
|
+
export const ATTACK_HOF_SIZE = 6
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {Array<{ genome: AttackGenome, fitness: number }>} hall 名人堂
|
|
99
|
+
* @param {AttackGenome} genome 候选
|
|
100
|
+
* @param {number} fitness 适应度
|
|
101
|
+
* @returns {Array<{ genome: AttackGenome, fitness: number }>} 更新后名人堂
|
|
102
|
+
*/
|
|
103
|
+
export function updateAttackHallOfFame(hall, genome, fitness) {
|
|
104
|
+
const next = [...hall, { genome: structuredClone(genome), fitness }]
|
|
105
|
+
next.sort((a, b) => b.fitness - a.fitness)
|
|
106
|
+
return next.slice(0, ATTACK_HOF_SIZE)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// silence unused import warning from eslint if any
|
|
110
|
+
void GLOBAL_ATTACK_SPECS
|
|
111
|
+
void loadDefaultTunables
|
|
112
|
+
void normalizeParam
|
|
113
|
+
void sampleParam
|