@twinsearth/agent-universe 2.3.6 → 2.5.7
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/LICENSE +21 -0
- package/README.md +411 -0
- package/index.d.ts +5 -208
- package/index.js +5 -69
- package/js/index.d.ts +209 -0
- package/js/index.js +71 -0
- package/js/lib/aca.js +259 -0
- package/js/lib/dht.js +92 -0
- package/js/lib/keychain.js +102 -0
- package/js/lib/market.js +240 -0
- package/js/lib/mcp.js +101 -0
- package/js/lib/models.js +124 -0
- package/js/package-lock.json +16 -0
- package/js/package.json +43 -0
- package/js/test/test.js +224 -0
- package/lib/aca.js +3 -259
- package/lib/dht.js +3 -92
- package/lib/keychain.js +3 -74
- package/lib/market.js +4 -240
- package/lib/mcp.js +3 -101
- package/lib/models.js +3 -124
- package/package.json +14 -10
package/js/test/test.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// test/test.js
|
|
2
|
+
// Agent Universe JS SDK 测试(12 项)
|
|
3
|
+
|
|
4
|
+
const assert = require('node:assert');
|
|
5
|
+
const {
|
|
6
|
+
version,
|
|
7
|
+
AgentUniverse,
|
|
8
|
+
Keypair,
|
|
9
|
+
AgentCard,
|
|
10
|
+
Task,
|
|
11
|
+
TaskStatus,
|
|
12
|
+
MemoryDHT,
|
|
13
|
+
ShardedIndex,
|
|
14
|
+
shardOf,
|
|
15
|
+
AgentMarket,
|
|
16
|
+
MIN_STAKE,
|
|
17
|
+
AipIdentity,
|
|
18
|
+
buildManifest,
|
|
19
|
+
buildEnvelope,
|
|
20
|
+
proposalMessage,
|
|
21
|
+
buildReceipt,
|
|
22
|
+
verifyManifest,
|
|
23
|
+
verifyMessage,
|
|
24
|
+
verifyReceipt,
|
|
25
|
+
verifyReceiptResult,
|
|
26
|
+
} = require('../index');
|
|
27
|
+
|
|
28
|
+
let passed = 0;
|
|
29
|
+
function test(name, fn) {
|
|
30
|
+
try {
|
|
31
|
+
fn();
|
|
32
|
+
passed++;
|
|
33
|
+
console.log(` ✓ ${name}`);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error(` ✗ ${name}`);
|
|
36
|
+
console.error(` ${e.message}`);
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('Agent Universe JS SDK 测试\n');
|
|
42
|
+
|
|
43
|
+
// 1. 版本号
|
|
44
|
+
test('版本号为 2.5.7', () => {
|
|
45
|
+
assert.strictEqual(version, '2.5.7');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 2. 密钥对 + DID + 签名验证
|
|
49
|
+
test('Keypair 生成 DID 且签名可验证', () => {
|
|
50
|
+
const kp = Keypair.generate();
|
|
51
|
+
assert.ok(kp.did.startsWith('did:nau:'), 'DID 前缀');
|
|
52
|
+
assert.strictEqual(kp.did.length, 'did:nau:'.length + 16);
|
|
53
|
+
const msg = 'hello agent universe';
|
|
54
|
+
const sig = kp.sign(msg);
|
|
55
|
+
assert.ok(kp.verify(msg, sig), '签名验证通过');
|
|
56
|
+
assert.ok(!kp.verify('tampered', sig), '篡改消息验证失败');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// 3. AgentCard
|
|
60
|
+
test('AgentCard 能力与技能', () => {
|
|
61
|
+
const card = AgentCard.new({ did: 'did:au:abc', name: 'writer' })
|
|
62
|
+
.withCapability('writing')
|
|
63
|
+
.withSkill('translation');
|
|
64
|
+
assert.ok(card.hasCapability('writing'));
|
|
65
|
+
assert.deepStrictEqual(card.toJSON().skills, ['translation']);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 4. Task 状态机
|
|
69
|
+
test('Task 状态机合法流转且拒绝非法转移', () => {
|
|
70
|
+
const t = new Task('t1', 'write doc');
|
|
71
|
+
t.transition(TaskStatus.OPEN);
|
|
72
|
+
t.assign('did:au:worker');
|
|
73
|
+
t.start();
|
|
74
|
+
t.complete();
|
|
75
|
+
t.verify(true);
|
|
76
|
+
t.settle();
|
|
77
|
+
assert.strictEqual(t.status, TaskStatus.SETTLED);
|
|
78
|
+
assert.ok(t.isTerminal);
|
|
79
|
+
|
|
80
|
+
const t2 = new Task('t2', 'bad');
|
|
81
|
+
assert.throws(() => t2.start(), /非法状态转移/);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// 5. MemoryDHT
|
|
85
|
+
test('MemoryDHT 存取与前缀查找', () => {
|
|
86
|
+
const dht = new MemoryDHT();
|
|
87
|
+
dht.put('/aip/agent/1', { name: 'a' });
|
|
88
|
+
dht.put('/aip/agent/2', { name: 'b' });
|
|
89
|
+
assert.strictEqual(dht.size, 2);
|
|
90
|
+
assert.strictEqual(dht.get('/aip/agent/1').name, 'a');
|
|
91
|
+
assert.strictEqual(dht.findByPrefix('/aip/agent').length, 2);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// 6. ShardedIndex 分片
|
|
95
|
+
test('ShardedIndex 分片落盘且分片号合法', () => {
|
|
96
|
+
const idx = new ShardedIndex(16);
|
|
97
|
+
for (let i = 0; i < 100; i++) {
|
|
98
|
+
const s = idx.put(`key-${i}`, i);
|
|
99
|
+
assert.ok(s >= 0 && s < 16);
|
|
100
|
+
}
|
|
101
|
+
assert.strictEqual(idx.size, 100);
|
|
102
|
+
assert.strictEqual(shardOf('key-0', 16), shardOf('key-0', 16));
|
|
103
|
+
// 分布:100 key 到 16 片,不应有大片为空(概率性,至少覆盖 8 片)
|
|
104
|
+
const nonEmpty = idx.distribution().filter((n) => n > 0).length;
|
|
105
|
+
assert.ok(nonEmpty >= 8, `非空分片 ${nonEmpty} >= 8`);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// 7. AgentMarket 端到端 + 守恒
|
|
109
|
+
test('AgentMarket 端到端:注册→发布→投标→匹配→完成→结算→守恒', () => {
|
|
110
|
+
const market = new AgentMarket();
|
|
111
|
+
const owner = 'did:au:owner';
|
|
112
|
+
const requester = 'did:au:requester';
|
|
113
|
+
|
|
114
|
+
// 充值(owner 质押 100,requester 预算 50)
|
|
115
|
+
market.deposit(owner, 100);
|
|
116
|
+
market.deposit(requester, 50);
|
|
117
|
+
const totalDeposits = 150;
|
|
118
|
+
|
|
119
|
+
const card = AgentCard.new({ did: owner, name: 'worker' })
|
|
120
|
+
.withSkill('writing');
|
|
121
|
+
market.registerAgent(card, MIN_STAKE);
|
|
122
|
+
|
|
123
|
+
market.publishTask('task-1', 'write an article', 50, requester);
|
|
124
|
+
market.submitBid('task-1', owner, 40);
|
|
125
|
+
const winner = market.matchTask('task-1');
|
|
126
|
+
assert.strictEqual(winner.agentId, owner);
|
|
127
|
+
|
|
128
|
+
market.completeTask('task-1', true);
|
|
129
|
+
const result = market.settle('task-1');
|
|
130
|
+
assert.strictEqual(result.paid, 40);
|
|
131
|
+
|
|
132
|
+
// 重复结算短路
|
|
133
|
+
const again = market.settle('task-1');
|
|
134
|
+
assert.strictEqual(again.reason, 'already_paid');
|
|
135
|
+
|
|
136
|
+
// 守恒:无罚没,总余额 = 总充值
|
|
137
|
+
const check = market.conservationCheck(totalDeposits);
|
|
138
|
+
assert.ok(check.conserved, `守恒: ${JSON.stringify(check)}`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// 8. 罚没守恒
|
|
142
|
+
test('罚没后守恒:总余额 = 总充值 − 罚没', () => {
|
|
143
|
+
const market = new AgentMarket();
|
|
144
|
+
const owner = 'did:au:bad';
|
|
145
|
+
market.deposit(owner, 200);
|
|
146
|
+
const card = AgentCard.new({ did: owner, name: 'bad' });
|
|
147
|
+
market.registerAgent(card, MIN_STAKE); // 质押 100
|
|
148
|
+
|
|
149
|
+
const slashResult = market.slash(owner, 60, 'fraud');
|
|
150
|
+
assert.strictEqual(slashResult.slashed, 60);
|
|
151
|
+
|
|
152
|
+
const check = market.conservationCheck(200);
|
|
153
|
+
assert.ok(check.conserved, `罚没守恒: ${JSON.stringify(check)}`);
|
|
154
|
+
assert.strictEqual(check.balanceSum, 140);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// 9. AipIdentity DID 与签名往返
|
|
158
|
+
test('AipIdentity 生成 did:aip 且签名可验证', () => {
|
|
159
|
+
const id = AipIdentity.generate();
|
|
160
|
+
assert.ok(id.did.startsWith('did:aip:'), 'DID 前缀');
|
|
161
|
+
assert.strictEqual(id.publicKey.length, 32, '原始公钥 32 字节');
|
|
162
|
+
const obj = { did: id.did, name: 't' };
|
|
163
|
+
id.signInto(obj);
|
|
164
|
+
assert.ok(AipIdentity.verifyObject(obj, id.publicKey), '验签通过');
|
|
165
|
+
const tampered = { ...obj, name: 'x' };
|
|
166
|
+
assert.ok(!AipIdentity.verifyObject(tampered, id.publicKey), '篡改拒绝');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// 10. 跨语言基准(与 Rust/Python 固定种子一致)
|
|
170
|
+
test('AipIdentity.fromSeed 复算 Rust/Python 基准', () => {
|
|
171
|
+
const id = AipIdentity.fromSeed(Buffer.alloc(32, 1));
|
|
172
|
+
assert.strictEqual(
|
|
173
|
+
id.publicKey.toString('hex'),
|
|
174
|
+
'8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c'
|
|
175
|
+
);
|
|
176
|
+
assert.strictEqual(id.did, 'did:aip:34750f98bd59fcfc');
|
|
177
|
+
const obj = {
|
|
178
|
+
did: id.did,
|
|
179
|
+
name: 'CrossLang',
|
|
180
|
+
capabilities: ['text-generation', 'mcp'],
|
|
181
|
+
stake: 100,
|
|
182
|
+
};
|
|
183
|
+
const sig = id.signObject(obj);
|
|
184
|
+
assert.strictEqual(
|
|
185
|
+
sig,
|
|
186
|
+
'e14d3f9e8204ea185ea4ba32a8117f262095ab9dac1352e1a7964ce36d3355c288dd6804ffa7daeb5f6eba9f30428f52702a75fd3efbb6a62555a7f24665da0e'
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// 11. ACA manifest/message 构造与验签
|
|
191
|
+
test('ACA manifest 与 proposal message 可验签、篡改拒绝', () => {
|
|
192
|
+
const owner = AipIdentity.generate();
|
|
193
|
+
const peer = AipIdentity.generate();
|
|
194
|
+
const manifest = buildManifest(owner, 'Agent1', ['text-generation'], {
|
|
195
|
+
stake: 100,
|
|
196
|
+
mcpToolCount: 3,
|
|
197
|
+
});
|
|
198
|
+
assert.ok(verifyManifest(manifest, owner.publicKey));
|
|
199
|
+
assert.ok(!verifyManifest({ ...manifest, name: 'Fake' }, owner.publicKey));
|
|
200
|
+
|
|
201
|
+
const envelope = buildEnvelope(owner.did, 'text-generation', 'out', {
|
|
202
|
+
budget: 10,
|
|
203
|
+
});
|
|
204
|
+
const message = proposalMessage(owner, peer.did, envelope);
|
|
205
|
+
assert.ok(verifyMessage(message, owner.publicKey));
|
|
206
|
+
assert.ok(!verifyMessage(message, peer.publicKey), '他人公钥拒绝');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// 12. Receipt 签名与结果哈希
|
|
210
|
+
test('ACA receipt 签名、结果哈希正反例', () => {
|
|
211
|
+
const owner = AipIdentity.generate();
|
|
212
|
+
const result = Buffer.from('hello result');
|
|
213
|
+
const receipt = buildReceipt(owner, 'task-1', result);
|
|
214
|
+
assert.ok(verifyReceipt(receipt, owner.publicKey));
|
|
215
|
+
assert.ok(verifyReceiptResult(receipt, result));
|
|
216
|
+
assert.ok(!verifyReceiptResult(receipt, Buffer.from('other')));
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
console.log(`\n${passed} 项测试通过`);
|
|
220
|
+
if (process.exitCode === 1) {
|
|
221
|
+
console.error('存在失败测试');
|
|
222
|
+
} else {
|
|
223
|
+
console.log('全部通过');
|
|
224
|
+
}
|
package/lib/aca.js
CHANGED
|
@@ -1,259 +1,3 @@
|
|
|
1
|
-
// lib
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
//
|
|
5
|
-
// 跨语言一致性:canonical 载荷为移除 signature 键后,紧凑、按 key 字典序、
|
|
6
|
-
// 不转义非 ASCII 的 JSON 字节,与 Rust serde_json(BTreeMap)、Python
|
|
7
|
-
// sort_keys 产出逐字节一致,因此三端可互验签名。
|
|
8
|
-
|
|
9
|
-
const crypto = require('node:crypto');
|
|
10
|
-
|
|
11
|
-
// Ed25519 PKCS#8 DER 固定前缀(16 字节),后接 32 字节种子
|
|
12
|
-
const PKCS8_PREFIX = Buffer.from('302e020100300506032b657004220420', 'hex');
|
|
13
|
-
|
|
14
|
-
// ── 枚举标签(与 Rust serde 变体名一致) ──
|
|
15
|
-
const MODE_SELF_REPORTED = 'SelfReported';
|
|
16
|
-
const LEVEL_L0 = 'L0Sample';
|
|
17
|
-
const PRIVACY_PUBLIC = 'Public';
|
|
18
|
-
const PRIORITY_NORMAL = 'Normal';
|
|
19
|
-
const STATUS_COMPLETED = 'Completed';
|
|
20
|
-
const MSG_HANDSHAKE = 'Handshake';
|
|
21
|
-
const MSG_TASK_PROPOSAL = 'TaskProposal';
|
|
22
|
-
const MSG_RECEIPT = 'Receipt';
|
|
23
|
-
|
|
24
|
-
function nowSec() {
|
|
25
|
-
return Math.floor(Date.now() / 1000);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** 稳定 JSON 序列化:紧凑、对象键按字典序、非 ASCII 原样输出 */
|
|
29
|
-
function stableStringify(value) {
|
|
30
|
-
if (value === null || typeof value !== 'object') {
|
|
31
|
-
return JSON.stringify(value);
|
|
32
|
-
}
|
|
33
|
-
if (Array.isArray(value)) {
|
|
34
|
-
return '[' + value.map(stableStringify).join(',') + ']';
|
|
35
|
-
}
|
|
36
|
-
const keys = Object.keys(value).sort();
|
|
37
|
-
return (
|
|
38
|
-
'{' +
|
|
39
|
-
keys
|
|
40
|
-
.map((k) => JSON.stringify(k) + ':' + stableStringify(value[k]))
|
|
41
|
-
.join(',') +
|
|
42
|
-
'}'
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** 规范待签名载荷:移除 signature 键,返回 Buffer */
|
|
47
|
-
function canonicalPayload(obj) {
|
|
48
|
-
const copy = { ...obj };
|
|
49
|
-
delete copy.signature;
|
|
50
|
-
return Buffer.from(stableStringify(copy), 'utf8');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function rawPublicFromKey(pubKey) {
|
|
54
|
-
return Buffer.from(pubKey.export({ format: 'jwk' }).x, 'base64url');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function pubKeyFromRaw(raw) {
|
|
58
|
-
return crypto.createPublicKey({
|
|
59
|
-
key: { kty: 'OKP', crv: 'Ed25519', x: raw.toString('base64url') },
|
|
60
|
-
format: 'jwk',
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* 跨语言对齐的节点身份:Ed25519 + did:aip(原始 32 字节公钥指纹)
|
|
66
|
-
*/
|
|
67
|
-
class AipIdentity {
|
|
68
|
-
constructor(privKey, rawPub) {
|
|
69
|
-
this._priv = privKey;
|
|
70
|
-
this._rawPub = rawPub;
|
|
71
|
-
const hash = crypto.createHash('sha256').update(rawPub).digest('hex');
|
|
72
|
-
this.did = 'did:aip:' + hash.slice(0, 16);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
static generate() {
|
|
76
|
-
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
|
|
77
|
-
return new AipIdentity(privateKey, rawPublicFromKey(publicKey));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
static fromSeed(seed) {
|
|
81
|
-
const buf = Buffer.isBuffer(seed) ? seed : Buffer.from(seed);
|
|
82
|
-
if (buf.length !== 32) throw new Error('seed 必须为 32 字节');
|
|
83
|
-
const priv = crypto.createPrivateKey({
|
|
84
|
-
key: Buffer.concat([PKCS8_PREFIX, buf]),
|
|
85
|
-
format: 'der',
|
|
86
|
-
type: 'pkcs8',
|
|
87
|
-
});
|
|
88
|
-
const rawPub = rawPublicFromKey(crypto.createPublicKey(priv));
|
|
89
|
-
return new AipIdentity(priv, rawPub);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** 32 字节原始公钥 */
|
|
93
|
-
get publicKey() {
|
|
94
|
-
return this._rawPub;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
signObject(obj) {
|
|
98
|
-
return crypto.sign(null, canonicalPayload(obj), this._priv).toString('hex');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
signInto(obj) {
|
|
102
|
-
obj.signature = this.signObject(obj);
|
|
103
|
-
return obj;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
static verifyObject(obj, rawPub) {
|
|
107
|
-
const sigHex = obj && obj.signature;
|
|
108
|
-
if (!sigHex) return false;
|
|
109
|
-
let sig;
|
|
110
|
-
try {
|
|
111
|
-
sig = Buffer.from(sigHex, 'hex');
|
|
112
|
-
} catch (e) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
return crypto.verify(
|
|
117
|
-
null,
|
|
118
|
-
canonicalPayload(obj),
|
|
119
|
-
pubKeyFromRaw(rawPub),
|
|
120
|
-
sig
|
|
121
|
-
);
|
|
122
|
-
} catch (e) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function buildManifest(identity, name, capabilities, opts = {}) {
|
|
129
|
-
const manifest = {
|
|
130
|
-
did: identity.did,
|
|
131
|
-
name,
|
|
132
|
-
version: opts.version || '2.3.6',
|
|
133
|
-
capabilities: [...capabilities],
|
|
134
|
-
endpoints: opts.endpoints || [],
|
|
135
|
-
hardware: {
|
|
136
|
-
cpu_cores: opts.cpuCores || 0,
|
|
137
|
-
memory_mb: opts.memoryMb || 0,
|
|
138
|
-
disk_free_gb: opts.diskFreeGb || 0,
|
|
139
|
-
gpu: null,
|
|
140
|
-
bandwidth_up_mbps: 0,
|
|
141
|
-
bandwidth_down_mbps: 0,
|
|
142
|
-
platform: opts.platform || 'desktop',
|
|
143
|
-
},
|
|
144
|
-
verification_modes: opts.verificationModes || [MODE_SELF_REPORTED],
|
|
145
|
-
stake: opts.stake || 0,
|
|
146
|
-
reputation_ref: null,
|
|
147
|
-
model_hash: null,
|
|
148
|
-
mcp_tool_count: opts.mcpToolCount || 0,
|
|
149
|
-
signature: '',
|
|
150
|
-
timestamp: nowSec(),
|
|
151
|
-
};
|
|
152
|
-
return identity.signInto(manifest);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function buildEnvelope(requesterDid, capability, outputSpec, opts = {}) {
|
|
156
|
-
return {
|
|
157
|
-
task_id: crypto.randomUUID(),
|
|
158
|
-
requester_did: requesterDid,
|
|
159
|
-
capability,
|
|
160
|
-
input_cid: opts.inputCid || '',
|
|
161
|
-
output_spec: outputSpec,
|
|
162
|
-
verification_level: opts.verificationLevel || LEVEL_L0,
|
|
163
|
-
privacy: opts.privacy || PRIVACY_PUBLIC,
|
|
164
|
-
budget: opts.budget || 0,
|
|
165
|
-
timeout_secs: opts.timeoutSecs || 300,
|
|
166
|
-
priority: opts.priority || PRIORITY_NORMAL,
|
|
167
|
-
mcp_tool: opts.mcpTool || null,
|
|
168
|
-
created_at: nowSec(),
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function buildMessage(identity, msgType, toDid, payload) {
|
|
173
|
-
const message = {
|
|
174
|
-
message_id: crypto.randomUUID(),
|
|
175
|
-
msg_type: msgType,
|
|
176
|
-
from_did: identity.did,
|
|
177
|
-
to_did: toDid,
|
|
178
|
-
payload,
|
|
179
|
-
timestamp: nowSec(),
|
|
180
|
-
signature: '',
|
|
181
|
-
};
|
|
182
|
-
return identity.signInto(message);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function handshakeMessage(identity, manifest) {
|
|
186
|
-
return buildMessage(identity, MSG_HANDSHAKE, '*', manifest);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function proposalMessage(identity, toDid, envelope) {
|
|
190
|
-
return buildMessage(identity, MSG_TASK_PROPOSAL, toDid, envelope);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function buildReceipt(identity, taskId, result, opts = {}) {
|
|
194
|
-
const hash = crypto.createHash('sha256').update(result).digest();
|
|
195
|
-
const metering = Object.assign(
|
|
196
|
-
{
|
|
197
|
-
compute_ms: 0,
|
|
198
|
-
memory_peak_mb: 0,
|
|
199
|
-
bandwidth_mb: 0,
|
|
200
|
-
storage_bytes: 0,
|
|
201
|
-
energy_joules: 0,
|
|
202
|
-
},
|
|
203
|
-
opts.metering || {}
|
|
204
|
-
);
|
|
205
|
-
const receipt = {
|
|
206
|
-
task_id: taskId,
|
|
207
|
-
executor_did: identity.did,
|
|
208
|
-
result_cid: 'cid:' + hash.toString('hex').slice(0, 16),
|
|
209
|
-
result_hash: [...hash],
|
|
210
|
-
status: opts.status || STATUS_COMPLETED,
|
|
211
|
-
metering,
|
|
212
|
-
tee_quote: opts.teeQuote || null,
|
|
213
|
-
zk_proof: opts.zkProof || null,
|
|
214
|
-
completed_at: nowSec(),
|
|
215
|
-
signature: '',
|
|
216
|
-
};
|
|
217
|
-
return identity.signInto(receipt);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function receiptMessage(identity, toDid, receipt) {
|
|
221
|
-
return buildMessage(identity, MSG_RECEIPT, toDid, receipt);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function verifyManifest(manifest, rawPub) {
|
|
225
|
-
return AipIdentity.verifyObject(manifest, rawPub);
|
|
226
|
-
}
|
|
227
|
-
function verifyMessage(message, rawPub) {
|
|
228
|
-
return AipIdentity.verifyObject(message, rawPub);
|
|
229
|
-
}
|
|
230
|
-
function verifyReceipt(receipt, rawPub) {
|
|
231
|
-
return AipIdentity.verifyObject(receipt, rawPub);
|
|
232
|
-
}
|
|
233
|
-
function verifyReceiptResult(receipt, result) {
|
|
234
|
-
const hash = crypto.createHash('sha256').update(result).digest();
|
|
235
|
-
return deepEqualArray(receipt.result_hash, [...hash]);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function deepEqualArray(a, b) {
|
|
239
|
-
if (!Array.isArray(a) || a.length !== b.length) return false;
|
|
240
|
-
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
module.exports = {
|
|
245
|
-
AipIdentity,
|
|
246
|
-
canonicalPayload,
|
|
247
|
-
stableStringify,
|
|
248
|
-
buildManifest,
|
|
249
|
-
buildEnvelope,
|
|
250
|
-
buildMessage,
|
|
251
|
-
buildReceipt,
|
|
252
|
-
handshakeMessage,
|
|
253
|
-
proposalMessage,
|
|
254
|
-
receiptMessage,
|
|
255
|
-
verifyManifest,
|
|
256
|
-
verifyMessage,
|
|
257
|
-
verifyReceipt,
|
|
258
|
-
verifyReceiptResult,
|
|
259
|
-
};
|
|
1
|
+
// 根包 /lib 子路径入口(v2.5.7 补):re-export js/lib,
|
|
2
|
+
// 使根发布包暴露 /lib/aca.js 子路径,供浏览器端按需引入(与 js 子包发布结构对齐)。
|
|
3
|
+
module.exports = require('../js/lib/aca.js');
|
package/lib/dht.js
CHANGED
|
@@ -1,92 +1,3 @@
|
|
|
1
|
-
// lib
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
const crypto = require('node:crypto');
|
|
5
|
-
|
|
6
|
-
/** 计算 key 所属分片:SHA-256(key) 前 8 hex 转整数,对 numShards 取模 */
|
|
7
|
-
function shardOf(key, numShards) {
|
|
8
|
-
const hash = crypto.createHash('sha256').update(String(key)).digest('hex');
|
|
9
|
-
const prefix = parseInt(hash.slice(0, 8), 16);
|
|
10
|
-
return prefix % numShards;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** 内存 DHT(键值存储) */
|
|
14
|
-
class MemoryDHT {
|
|
15
|
-
constructor() {
|
|
16
|
-
this.store = new Map();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
put(key, value) {
|
|
20
|
-
this.store.set(String(key), value);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get(key) {
|
|
24
|
-
return this.store.get(String(key));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
has(key) {
|
|
28
|
-
return this.store.has(String(key));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
remove(key) {
|
|
32
|
-
return this.store.delete(String(key));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
get size() {
|
|
36
|
-
return this.store.size;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** 按前缀查找 */
|
|
40
|
-
findByPrefix(prefix) {
|
|
41
|
-
const result = [];
|
|
42
|
-
for (const [k, v] of this.store) {
|
|
43
|
-
if (k.startsWith(prefix)) result.push({ key: k, value: v });
|
|
44
|
-
}
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** 分片索引:把 key 均匀分布到 numShards 个分片 */
|
|
50
|
-
class ShardedIndex {
|
|
51
|
-
constructor(numShards = 16) {
|
|
52
|
-
this.numShards = numShards;
|
|
53
|
-
this.shards = [];
|
|
54
|
-
for (let i = 0; i < numShards; i++) this.shards.push(new Map());
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
put(key, value) {
|
|
58
|
-
const s = shardOf(key, this.numShards);
|
|
59
|
-
this.shards[s].set(String(key), value);
|
|
60
|
-
return s;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
get(key) {
|
|
64
|
-
const s = shardOf(key, this.numShards);
|
|
65
|
-
return this.shards[s].get(String(key));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
has(key) {
|
|
69
|
-
const s = shardOf(key, this.numShards);
|
|
70
|
-
return this.shards[s].has(String(key));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
remove(key) {
|
|
74
|
-
const s = shardOf(key, this.numShards);
|
|
75
|
-
return this.shards[s].delete(String(key));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
shardSize(i) {
|
|
79
|
-
return this.shards[i].size;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
get size() {
|
|
83
|
-
return this.shards.reduce((sum, m) => sum + m.size, 0);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** 各分片条目数(用于验证分布均匀性) */
|
|
87
|
-
distribution() {
|
|
88
|
-
return this.shards.map((m) => m.size);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
module.exports = { MemoryDHT, ShardedIndex, shardOf };
|
|
1
|
+
// 根包 /lib 子路径入口(v2.5.7 补):re-export js/lib,
|
|
2
|
+
// 使根发布包暴露 /lib/dht.js 子路径(与 js 子包发布结构对齐)。
|
|
3
|
+
module.exports = require('../js/lib/dht.js');
|
package/lib/keychain.js
CHANGED
|
@@ -1,74 +1,3 @@
|
|
|
1
|
-
// lib
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
const crypto = require('node:crypto');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 密钥对与去中心化身份
|
|
8
|
-
*/
|
|
9
|
-
class Keypair {
|
|
10
|
-
constructor(publicKey, privateKey, did) {
|
|
11
|
-
this.publicKey = publicKey; // hex
|
|
12
|
-
this.privateKey = privateKey; // hex(node KeyObject 可导出的形式)
|
|
13
|
-
this.did = did; // did:au:<hash>
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** 生成新的 Ed25519 密钥对与 DID */
|
|
17
|
-
static generate() {
|
|
18
|
-
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
|
|
19
|
-
const pubHex = publicKey.export({ format: 'der', type: 'spki' })
|
|
20
|
-
.toString('hex');
|
|
21
|
-
// DID 由公钥的 SHA-256 哈希(取前 32 字符)派生
|
|
22
|
-
const didHash = crypto.createHash('sha256')
|
|
23
|
-
.update(publicKey.export({ format: 'der', type: 'spki' }))
|
|
24
|
-
.digest('hex')
|
|
25
|
-
.slice(0, 32);
|
|
26
|
-
const did = `did:au:${didHash}`;
|
|
27
|
-
|
|
28
|
-
// 私钥以 PKCS8 DER hex 保存,便于重建 KeyObject
|
|
29
|
-
const privHex = privateKey.export({ format: 'der', type: 'pkcs8' })
|
|
30
|
-
.toString('hex');
|
|
31
|
-
|
|
32
|
-
const kp = new Keypair(pubHex, privHex, did);
|
|
33
|
-
kp._pubKeyObj = publicKey;
|
|
34
|
-
kp._privKeyObj = privateKey;
|
|
35
|
-
return kp;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** 获取可用于签名的私钥 KeyObject */
|
|
39
|
-
_privateKeyObject() {
|
|
40
|
-
if (this._privKeyObj) return this._privKeyObj;
|
|
41
|
-
this._privKeyObj = crypto.createPrivateKey({
|
|
42
|
-
key: Buffer.from(this.privateKey, 'hex'),
|
|
43
|
-
format: 'der',
|
|
44
|
-
type: 'pkcs8',
|
|
45
|
-
});
|
|
46
|
-
return this._privKeyObj;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** 获取公钥 KeyObject */
|
|
50
|
-
_publicKeyObject() {
|
|
51
|
-
if (this._pubKeyObj) return this._pubKeyObj;
|
|
52
|
-
this._pubKeyObj = crypto.createPublicKey({
|
|
53
|
-
key: Buffer.from(this.publicKey, 'hex'),
|
|
54
|
-
format: 'der',
|
|
55
|
-
type: 'spki',
|
|
56
|
-
});
|
|
57
|
-
return this._pubKeyObj;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** 对消息签名,返回 hex */
|
|
61
|
-
sign(message) {
|
|
62
|
-
const data = Buffer.isBuffer(message) ? message : Buffer.from(message);
|
|
63
|
-
return crypto.sign(null, data, this._privateKeyObject()).toString('hex');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** 验证签名(默认用本密钥对的公钥) */
|
|
67
|
-
verify(message, signatureHex) {
|
|
68
|
-
const data = Buffer.isBuffer(message) ? message : Buffer.from(message);
|
|
69
|
-
const sig = Buffer.from(signatureHex, 'hex');
|
|
70
|
-
return crypto.verify(null, data, this._publicKeyObject(), sig);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
module.exports = { Keypair };
|
|
1
|
+
// 根包 /lib 子路径入口(v2.5.7 补):re-export js/lib,
|
|
2
|
+
// 使根发布包暴露 /lib/keychain.js 子路径(依赖 node:crypto,浏览器端勿直接引)。
|
|
3
|
+
module.exports = require('../js/lib/keychain.js');
|