Add features across agents, collab, trust, indexes, and hyperbee.
Manual pass: task cancelTask, workflow topologicalOrder/fail/reset, collab kick/replay, trust trustedPeers and multisig TTL sweep, graph shortestPath, inverted topTerms, dedup filterNew, bee notifyBatch/seekVersion — all with tests. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -16,7 +16,7 @@ class HyperP2PMultisigThreshold extends EventEmitter {
|
||||
this._peerMsgs = null
|
||||
}
|
||||
|
||||
createProposal (id, signers, threshold) {
|
||||
createProposal (id, signers, threshold, opts = {}) {
|
||||
assertNonEmpty(id, 'id')
|
||||
if (!Array.isArray(signers) || signers.length === 0) {
|
||||
throw new Error('signers must be a non-empty array')
|
||||
@@ -24,12 +24,14 @@ class HyperP2PMultisigThreshold extends EventEmitter {
|
||||
if (threshold < 1 || threshold > signers.length) {
|
||||
throw new Error('threshold must be between 1 and signers.length')
|
||||
}
|
||||
const expiresAt = opts.expiresAt ?? (opts.ttlMs ? Date.now() + opts.ttlMs : null)
|
||||
const proposal = {
|
||||
id,
|
||||
signers: [...signers],
|
||||
threshold,
|
||||
signatures: new Set(),
|
||||
createdAt: Date.now(),
|
||||
expiresAt,
|
||||
approved: false
|
||||
}
|
||||
this._proposals.set(id, proposal)
|
||||
@@ -66,9 +68,47 @@ class HyperP2PMultisigThreshold extends EventEmitter {
|
||||
isApproved (id) {
|
||||
assertNonEmpty(id, 'id')
|
||||
const p = this._proposals.get(id)
|
||||
if (p && p.expiresAt && Date.now() > p.expiresAt) return false
|
||||
return !!(p && p.approved)
|
||||
}
|
||||
|
||||
signatureCount (id) {
|
||||
const p = this._proposals.get(id)
|
||||
return p ? p.signatures.size : 0
|
||||
}
|
||||
|
||||
listProposals () {
|
||||
return [...this._proposals.values()].map((p) => ({
|
||||
id: p.id,
|
||||
signers: p.signers,
|
||||
threshold: p.threshold,
|
||||
signatures: p.signatures.size,
|
||||
approved: p.approved,
|
||||
expiresAt: p.expiresAt,
|
||||
createdAt: p.createdAt
|
||||
}))
|
||||
}
|
||||
|
||||
expireProposal (id) {
|
||||
assertNonEmpty(id, 'id')
|
||||
const ok = this._proposals.delete(id)
|
||||
if (ok) this.emit('expired', { id })
|
||||
return ok
|
||||
}
|
||||
|
||||
sweepExpired () {
|
||||
const now = Date.now()
|
||||
let n = 0
|
||||
for (const [id, p] of this._proposals) {
|
||||
if (p.expiresAt && now > p.expiresAt && !p.approved) {
|
||||
this._proposals.delete(id)
|
||||
n++
|
||||
}
|
||||
}
|
||||
if (n) this.emit('sweep', { removed: n })
|
||||
return n
|
||||
}
|
||||
|
||||
_gossip (data) {
|
||||
if (!this._peerMsgs) return
|
||||
gossipSend(this, data)
|
||||
|
||||
@@ -36,3 +36,12 @@ test('getStats', async (t) => {
|
||||
t.is(m.getStats().created, 1)
|
||||
await m.close()
|
||||
})
|
||||
|
||||
test('ttl sweep and listProposals', async (t) => {
|
||||
const m = new HyperP2PMultisigThreshold()
|
||||
m.createProposal('exp', ['a', 'b'], 2, { expiresAt: Date.now() - 1 })
|
||||
t.is(m.listProposals().length, 1)
|
||||
t.is(m.sweepExpired(), 1)
|
||||
t.is(m.listProposals().length, 0)
|
||||
await m.close()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user