@steve02081504/fount-p2p 0.0.22 → 0.0.24
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 +8 -6
- package/core/entity_id.mjs +5 -5
- package/core/logical_entity.mjs +1 -1
- package/core/object.mjs +9 -0
- package/core/partpath.mjs +10 -0
- package/core/random_id.mjs +4 -2
- package/crypto/crypto.mjs +26 -93
- package/dag/storage.mjs +0 -5
- package/dag/strip_extensions.mjs +1 -3
- package/{wire/part_query_cache.mjs → federation/part_query/cache.mjs} +7 -7
- package/{wire/part_query.mjs → federation/part_query/runtime.mjs} +36 -77
- package/files/assemble.mjs +5 -5
- package/files/assemble_stream.mjs +2 -2
- package/files/chunk/fetch.mjs +101 -0
- package/files/chunk/pending.mjs +59 -0
- package/files/{chunk_provider_registry.mjs → chunk/provider_registry.mjs} +4 -5
- package/files/chunk/responder.mjs +104 -0
- package/files/{chunk_store.mjs → chunk/store.mjs} +2 -2
- package/files/{chunk_fetch_verify.mjs → chunk/verify.mjs} +2 -2
- package/files/evfs.mjs +18 -19
- package/files/fed/fetch_shared.mjs +39 -0
- package/files/fed/responder.mjs +47 -0
- package/files/{acl.mjs → manifest/acl.mjs} +4 -4
- package/files/{manifest_acl_registry.mjs → manifest/acl_registry.mjs} +14 -17
- package/files/{manifest_fetch.mjs → manifest/fetch.mjs} +47 -52
- package/files/{manifest.mjs → manifest/normalize.mjs} +2 -2
- package/files/manifest/pending.mjs +53 -0
- package/files/{public_manifest.mjs → manifest/public.mjs} +12 -12
- package/files/transfer_key.mjs +2 -2
- package/files/transfer_key_registry.mjs +7 -7
- package/index.mjs +4 -4
- package/infra/service.mjs +1 -1
- package/link/handshake.mjs +0 -3
- package/mailbox/deliver_or_store.mjs +0 -2
- package/mailbox/parse.mjs +1 -1
- package/mailbox/store.mjs +0 -9
- package/mailbox/wire.mjs +24 -16
- package/node/reputation_sync.mjs +33 -29
- package/package.json +9 -8
- package/registries/inbound.mjs +19 -9
- package/registries/part_path.mjs +3 -8
- package/schemas/discovery.mjs +1 -1
- package/schemas/federation_pull.mjs +1 -1
- package/schemas/part_query.mjs +6 -5
- package/transport/group_link_set.mjs +2 -2
- package/transport/link_registry.mjs +2 -2
- package/transport/node_scope/features.mjs +166 -0
- package/transport/node_scope/wire.mjs +124 -0
- package/transport/remote_user_room.mjs +0 -1
- package/{rooms → transport}/scoped_link.mjs +1 -1
- package/transport/user_room.mjs +15 -28
- package/utils/fetch_wait.mjs +98 -0
- package/wire/adapter.mjs +13 -0
- package/wire/ingress.mjs +1 -8
- package/wire/part/fanout.mjs +99 -0
- package/wire/part/group.mjs +45 -0
- package/wire/{part_ingress.mjs → part/ingress.mjs} +58 -67
- package/wire/{part_invoke.mjs → part/invoke.mjs} +1 -22
- package/wire/part/pending.mjs +32 -0
- package/wire/part/query.mjs +91 -0
- package/wire/subscribe.mjs +13 -0
- package/federation/chunk_fetch_pending.mjs +0 -124
- package/federation/manifest_fetch_pending.mjs +0 -88
- package/files/chunk_fetch.mjs +0 -99
- package/files/chunk_responder.mjs +0 -142
- package/transport/advert_ingest.mjs +0 -17
- package/transport/node_scope.mjs +0 -289
- package/wire/group_part.mjs +0 -43
- package/wire/part_common.mjs +0 -55
- package/wire/part_fanout.mjs +0 -52
- /package/{wire → schemas}/part_query.tunables.json +0 -0
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { base64ToBytes } from '../core/bytes_codec.mjs'
|
|
2
|
-
import { verifiedChunkBytes } from '../files/chunk_fetch_verify.mjs'
|
|
3
|
-
|
|
4
|
-
/** @type {Map<string, { expectedHash: string, timer: ReturnType<typeof setTimeout>, resolve: (v: Uint8Array | null) => void, reject?: (e: Error) => void }>} */
|
|
5
|
-
export const pendingChunkFetches = new Map()
|
|
6
|
-
|
|
7
|
-
/** 并发 pending chunk fetch 上限。 */
|
|
8
|
-
export const MAX_PENDING_CHUNK_FETCHES = 2048
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 注册 chunk 拉取等待槽(requestId 或 compositeKey)。
|
|
12
|
-
* @param {string} key 唯一等待键
|
|
13
|
-
* @param {string} expectedHash 期望 64 hex 密文哈希
|
|
14
|
-
* @param {number} timeoutMs 超时毫秒
|
|
15
|
-
* @param {{ rejectOnTimeout?: boolean }} [options] rejectOnTimeout 时 Promise 以 Error 拒绝
|
|
16
|
-
* @returns {{ done: Promise<Uint8Array | null>, cancel: () => void }} 等待 Promise 与取消函数
|
|
17
|
-
*/
|
|
18
|
-
export function registerChunkFetchWait(key, expectedHash, timeoutMs, options = {}) {
|
|
19
|
-
if (!key || pendingChunkFetches.size >= MAX_PENDING_CHUNK_FETCHES)
|
|
20
|
-
return {
|
|
21
|
-
done: Promise.resolve(null),
|
|
22
|
-
/**
|
|
23
|
-
* 槽位已满时的空操作取消。
|
|
24
|
-
*/
|
|
25
|
-
cancel: () => { },
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** @type {(value: Uint8Array | null | Error) => void} */
|
|
29
|
-
let settle
|
|
30
|
-
const done = new Promise((resolve, reject) => {
|
|
31
|
-
/**
|
|
32
|
-
* @param {Uint8Array | null | Error} value 块数据或错误
|
|
33
|
-
* @returns {void}
|
|
34
|
-
*/
|
|
35
|
-
function settleWait(value) {
|
|
36
|
-
if (value instanceof Error) reject(value)
|
|
37
|
-
else resolve(value)
|
|
38
|
-
}
|
|
39
|
-
settle = settleWait
|
|
40
|
-
})
|
|
41
|
-
const timer = setTimeout(() => {
|
|
42
|
-
pendingChunkFetches.delete(key)
|
|
43
|
-
if (options.rejectOnTimeout)
|
|
44
|
-
settle(new Error('chunk fetch timeout'))
|
|
45
|
-
else
|
|
46
|
-
settle(null)
|
|
47
|
-
}, timeoutMs)
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @param {Uint8Array | null} data 块数据
|
|
51
|
-
* @returns {void}
|
|
52
|
-
*/
|
|
53
|
-
function finish(data) {
|
|
54
|
-
clearTimeout(timer)
|
|
55
|
-
pendingChunkFetches.delete(key)
|
|
56
|
-
settle(data)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param {Error | unknown} error 拒绝原因
|
|
61
|
-
* @returns {void}
|
|
62
|
-
*/
|
|
63
|
-
function rejectWait(error) {
|
|
64
|
-
finish(error instanceof Error ? error : new Error(String(error)))
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
pendingChunkFetches.set(key, {
|
|
68
|
-
expectedHash,
|
|
69
|
-
timer,
|
|
70
|
-
resolve: finish,
|
|
71
|
-
reject: rejectWait,
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
done,
|
|
76
|
-
/** 取消等待并以 null settle。 */
|
|
77
|
-
cancel: () => {
|
|
78
|
-
clearTimeout(timer)
|
|
79
|
-
pendingChunkFetches.delete(key)
|
|
80
|
-
settle(null)
|
|
81
|
-
},
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 按等待键解析入站 chunk 响应(校验哈希后 resolve)。
|
|
87
|
-
* @param {string} key 等待键
|
|
88
|
-
* @param {string} expectedHash 期望哈希
|
|
89
|
-
* @param {Uint8Array | null} bytes 密文块
|
|
90
|
-
* @returns {boolean} 是否命中并完成等待
|
|
91
|
-
*/
|
|
92
|
-
export function resolveChunkFetchWait(key, expectedHash, bytes) {
|
|
93
|
-
const entry = pendingChunkFetches.get(key)
|
|
94
|
-
if (!entry || entry.expectedHash !== expectedHash) return false
|
|
95
|
-
const verified = bytes ? verifiedChunkBytes(expectedHash, bytes) : null
|
|
96
|
-
if (bytes && !verified) return false
|
|
97
|
-
clearTimeout(entry.timer)
|
|
98
|
-
pendingChunkFetches.delete(key)
|
|
99
|
-
entry.resolve(verified)
|
|
100
|
-
return true
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* 处理 fed_chunk_data / 带 requestId 的响应载荷。
|
|
105
|
-
* @param {object} payload 入站载荷
|
|
106
|
-
* @returns {boolean} 是否命中 pending
|
|
107
|
-
*/
|
|
108
|
-
export function resolvePendingChunkFetch(payload) {
|
|
109
|
-
const requestId = typeof payload?.requestId === 'string' ? payload.requestId : ''
|
|
110
|
-
if (!requestId) return false
|
|
111
|
-
const entry = pendingChunkFetches.get(requestId)
|
|
112
|
-
if (!entry) return false
|
|
113
|
-
if (typeof payload?.dataBase64 === 'string') {
|
|
114
|
-
try {
|
|
115
|
-
return resolveChunkFetchWait(requestId, entry.expectedHash, base64ToBytes(payload.dataBase64))
|
|
116
|
-
}
|
|
117
|
-
catch { /* keep waiting */ }
|
|
118
|
-
return false
|
|
119
|
-
}
|
|
120
|
-
clearTimeout(entry.timer)
|
|
121
|
-
pendingChunkFetches.delete(requestId)
|
|
122
|
-
entry.resolve(null)
|
|
123
|
-
return true
|
|
124
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { verifySignedPublicManifest } from '../files/public_manifest.mjs'
|
|
2
|
-
|
|
3
|
-
/** @type {Map<string, { expectedKey: string, timer: ReturnType<typeof setTimeout>, resolve: (v: object | null) => void }>} */
|
|
4
|
-
export const pendingManifestFetches = new Map()
|
|
5
|
-
|
|
6
|
-
/** 并发 pending manifest fetch 上限。 */
|
|
7
|
-
export const MAX_PENDING_MANIFEST_FETCHES = 512
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {string} ownerEntityHash owner
|
|
11
|
-
* @param {string} logicalPath 路径
|
|
12
|
-
* @returns {string} 期望键
|
|
13
|
-
*/
|
|
14
|
-
export function manifestFetchExpectedKey(ownerEntityHash, logicalPath) {
|
|
15
|
-
return `${String(ownerEntityHash || '').trim().toLowerCase()}\0${String(logicalPath || '').trim().replace(/^\/+/, '')}`
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @param {string} key requestId
|
|
20
|
-
* @param {string} expectedKey owner+path 复合键
|
|
21
|
-
* @param {number} timeoutMs 超时毫秒
|
|
22
|
-
* @returns {{ done: Promise<object | null>, cancel: () => void }} 等待 Promise 与取消
|
|
23
|
-
*/
|
|
24
|
-
export function registerManifestFetchWait(key, expectedKey, timeoutMs) {
|
|
25
|
-
if (!key || pendingManifestFetches.size >= MAX_PENDING_MANIFEST_FETCHES)
|
|
26
|
-
return {
|
|
27
|
-
done: Promise.resolve(null),
|
|
28
|
-
/**
|
|
29
|
-
* 槽位已满时的空操作取消。
|
|
30
|
-
*/
|
|
31
|
-
cancel: () => { },
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** @type {(value: object | null) => void} */
|
|
35
|
-
let settle
|
|
36
|
-
const done = new Promise(resolve => {
|
|
37
|
-
settle = resolve
|
|
38
|
-
})
|
|
39
|
-
const timer = setTimeout(() => {
|
|
40
|
-
pendingManifestFetches.delete(key)
|
|
41
|
-
settle(null)
|
|
42
|
-
}, timeoutMs)
|
|
43
|
-
|
|
44
|
-
pendingManifestFetches.set(key, {
|
|
45
|
-
expectedKey,
|
|
46
|
-
timer,
|
|
47
|
-
/**
|
|
48
|
-
* @param {object | null} value 验签后的 manifest,超时/取消为 null
|
|
49
|
-
*/
|
|
50
|
-
resolve: value => {
|
|
51
|
-
clearTimeout(timer)
|
|
52
|
-
pendingManifestFetches.delete(key)
|
|
53
|
-
settle(value)
|
|
54
|
-
},
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
done,
|
|
59
|
-
/**
|
|
60
|
-
* 取消等待并以 null 结算。
|
|
61
|
-
*/
|
|
62
|
-
cancel: () => {
|
|
63
|
-
clearTimeout(timer)
|
|
64
|
-
pendingManifestFetches.delete(key)
|
|
65
|
-
settle(null)
|
|
66
|
-
},
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 处理 fed_manifest_data:验签通过后 resolve pending。
|
|
72
|
-
* @param {object} payload 入站载荷
|
|
73
|
-
* @returns {Promise<boolean>} 是否命中并完成等待
|
|
74
|
-
*/
|
|
75
|
-
export async function resolvePendingManifestFetch(payload) {
|
|
76
|
-
const requestId = String(payload?.requestId || '')
|
|
77
|
-
if (!requestId) return false
|
|
78
|
-
const entry = pendingManifestFetches.get(requestId)
|
|
79
|
-
if (!entry) return false
|
|
80
|
-
|
|
81
|
-
const verified = await verifySignedPublicManifest(payload?.manifest)
|
|
82
|
-
if (!verified) return false
|
|
83
|
-
const key = manifestFetchExpectedKey(verified.ownerEntityHash, verified.logicalPath)
|
|
84
|
-
if (key !== entry.expectedKey) return false
|
|
85
|
-
|
|
86
|
-
entry.resolve(verified)
|
|
87
|
-
return true
|
|
88
|
-
}
|
package/files/chunk_fetch.mjs
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto'
|
|
2
|
-
|
|
3
|
-
import { bytesToBase64 } from '../core/bytes_codec.mjs'
|
|
4
|
-
import {
|
|
5
|
-
MAX_PENDING_CHUNK_FETCHES,
|
|
6
|
-
registerChunkFetchWait,
|
|
7
|
-
} from '../federation/chunk_fetch_pending.mjs'
|
|
8
|
-
import { ms } from '../utils/duration.mjs'
|
|
9
|
-
import { createInflightTable } from '../utils/inflight_table.mjs'
|
|
10
|
-
|
|
11
|
-
import { verifiedChunkBytes } from './chunk_fetch_verify.mjs'
|
|
12
|
-
import { fetchFederationChunk, resolveNodeHash } from './chunk_provider_registry.mjs'
|
|
13
|
-
import { getChunk, hasChunk, putChunk } from './chunk_store.mjs'
|
|
14
|
-
import { fanoutFedFetch } from './fetch_fanout.mjs'
|
|
15
|
-
|
|
16
|
-
const DEFAULT_CHUNK_FETCH_TIMEOUT_MS = ms('8s')
|
|
17
|
-
|
|
18
|
-
/** 同 username+hash 共享一次 fanout;队满只丢已超基础超时的队首。 */
|
|
19
|
-
const chunkInflight = createInflightTable({
|
|
20
|
-
maxSize: MAX_PENDING_CHUNK_FETCHES,
|
|
21
|
-
baseTimeoutMs: DEFAULT_CHUNK_FETCH_TIMEOUT_MS,
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @typedef {{
|
|
26
|
-
* username: string,
|
|
27
|
-
* ciphertextHash: string,
|
|
28
|
-
* ownerEntityHash?: string,
|
|
29
|
-
* groupId?: string,
|
|
30
|
-
* }} FetchChunkContext
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @param {FetchChunkContext} context 上下文
|
|
35
|
-
* @returns {Promise<Uint8Array | null>} 密文块
|
|
36
|
-
*/
|
|
37
|
-
export async function fetchChunk(context) {
|
|
38
|
-
const hash = String(context.ciphertextHash || '').trim().toLowerCase()
|
|
39
|
-
const { username } = context
|
|
40
|
-
if (!hash || !username) return null
|
|
41
|
-
|
|
42
|
-
if (await hasChunk(hash))
|
|
43
|
-
return new Uint8Array(await getChunk(hash))
|
|
44
|
-
|
|
45
|
-
if (context.groupId) {
|
|
46
|
-
const u8 = await fetchFederationChunk(username, context.groupId, hash)
|
|
47
|
-
const verified = verifiedChunkBytes(hash, u8)
|
|
48
|
-
if (verified) {
|
|
49
|
-
await putChunk(hash, verified)
|
|
50
|
-
return verified
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const inflightKey = `${username}\0${hash}`
|
|
55
|
-
const shared = chunkInflight.acquire(inflightKey, () => {
|
|
56
|
-
const requestId = randomUUID()
|
|
57
|
-
const wait = registerChunkFetchWait(requestId, hash, DEFAULT_CHUNK_FETCH_TIMEOUT_MS)
|
|
58
|
-
void (async () => {
|
|
59
|
-
try {
|
|
60
|
-
const { nodeHash } = await resolveNodeHash(username)
|
|
61
|
-
await fanoutFedFetch(username, 'fed_chunk_get', {
|
|
62
|
-
requestId,
|
|
63
|
-
nodeHash,
|
|
64
|
-
chunkHash: hash,
|
|
65
|
-
ownerEntityHash: context.ownerEntityHash,
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
catch { /* pending wait 超时/cancel 负责 settle */ }
|
|
69
|
-
})()
|
|
70
|
-
return { done: wait.done, cancel: wait.cancel }
|
|
71
|
-
})
|
|
72
|
-
if (!shared) return null
|
|
73
|
-
|
|
74
|
-
const result = await shared
|
|
75
|
-
const verified = verifiedChunkBytes(hash, result)
|
|
76
|
-
if (verified) {
|
|
77
|
-
await putChunk(hash, verified)
|
|
78
|
-
return verified
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return null
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* 若本机有 chunk 则响应 fed_chunk_get。
|
|
86
|
-
* @param {string} username 用户
|
|
87
|
-
* @param {object} payload 请求
|
|
88
|
-
* @param {(response: object, peerId: string) => void} sendResponse 发送
|
|
89
|
-
* @param {string} peerId 对端
|
|
90
|
-
* @returns {Promise<void>}
|
|
91
|
-
*/
|
|
92
|
-
export async function handleIncomingChunkGet(username, payload, sendResponse, peerId) {
|
|
93
|
-
const hash = String(payload?.chunkHash || '').trim().toLowerCase()
|
|
94
|
-
if (!hash) return
|
|
95
|
-
if (!await hasChunk(hash)) return
|
|
96
|
-
const chunkBytes = await getChunk(hash)
|
|
97
|
-
if (!chunkBytes?.length) return
|
|
98
|
-
sendResponse({ requestId: payload.requestId, dataBase64: bytesToBase64(chunkBytes) }, peerId)
|
|
99
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { resolvePendingChunkFetch } from '../federation/chunk_fetch_pending.mjs'
|
|
2
|
-
import { resolvePendingManifestFetch } from '../federation/manifest_fetch_pending.mjs'
|
|
3
|
-
|
|
4
|
-
import { handleIncomingChunkGet } from './chunk_fetch.mjs'
|
|
5
|
-
import { handleIncomingManifestGet } from './manifest_fetch.mjs'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @param {string} username 副本用户名 用户名
|
|
9
|
-
* @param {object} data 入站 fed_chunk_get
|
|
10
|
-
* @param {string} peerId 对端 id
|
|
11
|
-
* @param {(resp: object, peerId: string) => void} sendChunkData 发送 fed_chunk_data
|
|
12
|
-
* @returns {Promise<void>}
|
|
13
|
-
*/
|
|
14
|
-
export async function handleFedChunkGetIngress(username, data, peerId, sendChunkData) {
|
|
15
|
-
await handleIncomingChunkGet(username, data, sendChunkData, peerId)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @param {object} data 入站 fed_chunk_data(含 requestId 时 resolve pending fetch)
|
|
20
|
-
* @returns {void}
|
|
21
|
-
*/
|
|
22
|
-
export function handleFedChunkDataIngress(data) {
|
|
23
|
-
resolvePendingChunkFetch(data)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @param {string} username 副本用户名
|
|
28
|
-
* @param {object} data 入站 fed_manifest_get
|
|
29
|
-
* @param {string} peerId 对端 id
|
|
30
|
-
* @param {(resp: object, peerId: string) => void} sendManifestData 发送 fed_manifest_data
|
|
31
|
-
* @returns {Promise<void>}
|
|
32
|
-
*/
|
|
33
|
-
export async function handleFedManifestGetIngress(username, data, peerId, sendManifestData) {
|
|
34
|
-
await handleIncomingManifestGet(username, data, sendManifestData, peerId)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @param {object} data 入站 fed_manifest_data
|
|
39
|
-
* @returns {Promise<void>}
|
|
40
|
-
*/
|
|
41
|
-
export async function handleFedManifestDataIngress(data) {
|
|
42
|
-
await resolvePendingManifestFetch(data)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* node scope user-room wire:注册 fed_chunk_* + fed_manifest_*。
|
|
47
|
-
* @param {string | (() => string)} usernameOrGetter 副本用户名或 live getter
|
|
48
|
-
* @param {{ on: (name: string, handler: (payload: unknown, peerId: string) => void) => (() => void) | void, send: (name: string, payload: unknown, peerId: string | null) => void }} wire action 表
|
|
49
|
-
* @returns {() => void} 取消挂载的 dispose
|
|
50
|
-
*/
|
|
51
|
-
export function attachNodeScopeFedChunkResponder(usernameOrGetter, wire) {
|
|
52
|
-
const resolveUsername = typeof usernameOrGetter === 'function'
|
|
53
|
-
? usernameOrGetter
|
|
54
|
-
: () => usernameOrGetter
|
|
55
|
-
const offs = [
|
|
56
|
-
wire.on('fed_chunk_get', (data, peerId) => {
|
|
57
|
-
void handleFedChunkGetIngress(resolveUsername(), data, peerId, (resp, pid) => {
|
|
58
|
-
try { wire.send('fed_chunk_data', resp, pid) }
|
|
59
|
-
catch { /* disconnected */ }
|
|
60
|
-
})
|
|
61
|
-
}),
|
|
62
|
-
wire.on('fed_chunk_data', handleFedChunkDataIngress),
|
|
63
|
-
wire.on('fed_manifest_get', (data, peerId) => {
|
|
64
|
-
void handleFedManifestGetIngress(resolveUsername(), data, peerId, (resp, pid) => {
|
|
65
|
-
try { wire.send('fed_manifest_data', resp, pid) }
|
|
66
|
-
catch { /* disconnected */ }
|
|
67
|
-
})
|
|
68
|
-
}),
|
|
69
|
-
wire.on('fed_manifest_data', data => {
|
|
70
|
-
void handleFedManifestDataIngress(data)
|
|
71
|
-
}),
|
|
72
|
-
]
|
|
73
|
-
return () => {
|
|
74
|
-
for (const off of offs)
|
|
75
|
-
try { off?.() } catch { /* ignore */ }
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Trystero room:注册带 requestId 的 fed_chunk_* + fed_manifest_*(TrustGraph 全局 miss)。
|
|
81
|
-
* @param {string} username 用户
|
|
82
|
-
* @param {object} room Trystero room
|
|
83
|
-
* @param {{ enqueue: (prio: number, cleanup: () => void) => void }} [fedOut] 出站队列
|
|
84
|
-
* @param {(roomKey: string, action: string, rtcLimits: object) => boolean} [guardGet] RTC 负载守卫
|
|
85
|
-
* @param {object} [rtcLimits] RTC 限额
|
|
86
|
-
* @param {string} [roomKey] 房间键
|
|
87
|
-
* @returns {void}
|
|
88
|
-
*/
|
|
89
|
-
export function attachTrustGraphFedChunkResponder(username, room, fedOut, guardGet, rtcLimits = {}, roomKey = '') {
|
|
90
|
-
const [sendChunkData, getChunkData] = room.makeAction('fed_chunk_data')
|
|
91
|
-
const [, getChunkGet] = room.makeAction('fed_chunk_get')
|
|
92
|
-
const [sendManifestData, getManifestData] = room.makeAction('fed_manifest_data')
|
|
93
|
-
const [, getManifestGet] = room.makeAction('fed_manifest_get')
|
|
94
|
-
|
|
95
|
-
getChunkGet((data, peerId) => {
|
|
96
|
-
if (guardGet && !guardGet(roomKey, 'fed_chunk_get', rtcLimits)) return
|
|
97
|
-
void (async () => {
|
|
98
|
-
if (!data || typeof data !== 'object') return
|
|
99
|
-
if (!String(data.requestId || '')) return
|
|
100
|
-
await handleFedChunkGetIngress(username, data, peerId, (resp, pid) => {
|
|
101
|
-
/**
|
|
102
|
-
* 发送 fed_chunk_data 响应(可经 fedOut 限速队列)。
|
|
103
|
-
*/
|
|
104
|
-
const send = () => {
|
|
105
|
-
try { sendChunkData(resp, pid) }
|
|
106
|
-
catch { /* peer gone — normal */ }
|
|
107
|
-
}
|
|
108
|
-
if (fedOut) fedOut.enqueue(6, send)
|
|
109
|
-
else send()
|
|
110
|
-
})
|
|
111
|
-
})().catch(() => { /* peer/handler race — normal degrade */ })
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
getChunkData(data => {
|
|
115
|
-
if (!data || typeof data !== 'object' || !data.requestId) return
|
|
116
|
-
handleFedChunkDataIngress(data)
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
getManifestGet((data, peerId) => {
|
|
120
|
-
if (guardGet && !guardGet(roomKey, 'fed_manifest_get', rtcLimits)) return
|
|
121
|
-
void (async () => {
|
|
122
|
-
if (!data || typeof data !== 'object') return
|
|
123
|
-
if (!String(data.requestId || '')) return
|
|
124
|
-
await handleFedManifestGetIngress(username, data, peerId, (resp, pid) => {
|
|
125
|
-
/**
|
|
126
|
-
* 发送 fed_manifest_data 响应(可经 fedOut 限速队列)。
|
|
127
|
-
*/
|
|
128
|
-
const send = () => {
|
|
129
|
-
try { sendManifestData(resp, pid) }
|
|
130
|
-
catch { /* peer gone — normal */ }
|
|
131
|
-
}
|
|
132
|
-
if (fedOut) fedOut.enqueue(6, send)
|
|
133
|
-
else send()
|
|
134
|
-
})
|
|
135
|
-
})().catch(() => { /* peer/handler race — normal degrade */ })
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
getManifestData(data => {
|
|
139
|
-
if (!data || typeof data !== 'object' || !data.requestId) return
|
|
140
|
-
void handleFedManifestDataIngress(data)
|
|
141
|
-
})
|
|
142
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { noteAdvertPeerHints } from '../discovery/advert_peer_hints.mjs'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 解密并验签 advert(`ingestEncryptedAdvert` 别名)。不写 peer hints;写入需 `applyAdvertPeerHints`。Untrusted ingress。
|
|
5
|
-
*/
|
|
6
|
-
export { ingestEncryptedAdvert as ingestSignedAdvert } from '../discovery/adverts.mjs'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 将已验签 advert 的 peer hints 写入本地(apply 侧;与 `ingestSignedAdvert` 分离)。
|
|
10
|
-
* @param {string} verifiedNodeHash 已验签 nodeHash
|
|
11
|
-
* @param {object} body advert body
|
|
12
|
-
* @param {object} [meta] 元数据
|
|
13
|
-
* @returns {void}
|
|
14
|
-
*/
|
|
15
|
-
export function applyAdvertPeerHints(verifiedNodeHash, body, meta) {
|
|
16
|
-
noteAdvertPeerHints(verifiedNodeHash, body, meta)
|
|
17
|
-
}
|