@snaha/swarm-id 0.0.1
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 +431 -0
- package/dist/chunk/bmt.d.ts +17 -0
- package/dist/chunk/bmt.d.ts.map +1 -0
- package/dist/chunk/cac.d.ts +18 -0
- package/dist/chunk/cac.d.ts.map +1 -0
- package/dist/chunk/constants.d.ts +10 -0
- package/dist/chunk/constants.d.ts.map +1 -0
- package/dist/chunk/encrypted-cac.d.ts +48 -0
- package/dist/chunk/encrypted-cac.d.ts.map +1 -0
- package/dist/chunk/encryption.d.ts +86 -0
- package/dist/chunk/encryption.d.ts.map +1 -0
- package/dist/chunk/index.d.ts +6 -0
- package/dist/chunk/index.d.ts.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/proxy/act/act.d.ts +78 -0
- package/dist/proxy/act/act.d.ts.map +1 -0
- package/dist/proxy/act/crypto.d.ts +44 -0
- package/dist/proxy/act/crypto.d.ts.map +1 -0
- package/dist/proxy/act/grantee-list.d.ts +82 -0
- package/dist/proxy/act/grantee-list.d.ts.map +1 -0
- package/dist/proxy/act/history.d.ts +183 -0
- package/dist/proxy/act/history.d.ts.map +1 -0
- package/dist/proxy/act/index.d.ts +104 -0
- package/dist/proxy/act/index.d.ts.map +1 -0
- package/dist/proxy/chunking-encrypted.d.ts +14 -0
- package/dist/proxy/chunking-encrypted.d.ts.map +1 -0
- package/dist/proxy/chunking.d.ts +15 -0
- package/dist/proxy/chunking.d.ts.map +1 -0
- package/dist/proxy/download-data.d.ts +16 -0
- package/dist/proxy/download-data.d.ts.map +1 -0
- package/dist/proxy/feed-manifest.d.ts +62 -0
- package/dist/proxy/feed-manifest.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts +77 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts +88 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/finder.d.ts +67 -0
- package/dist/proxy/feeds/epochs/finder.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/index.d.ts +35 -0
- package/dist/proxy/feeds/epochs/index.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/test-utils.d.ts +93 -0
- package/dist/proxy/feeds/epochs/test-utils.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/types.d.ts +109 -0
- package/dist/proxy/feeds/epochs/types.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/updater.d.ts +68 -0
- package/dist/proxy/feeds/epochs/updater.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/utils.d.ts +22 -0
- package/dist/proxy/feeds/epochs/utils.d.ts.map +1 -0
- package/dist/proxy/feeds/index.d.ts +5 -0
- package/dist/proxy/feeds/index.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts +14 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/finder.d.ts +17 -0
- package/dist/proxy/feeds/sequence/finder.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/index.d.ts +23 -0
- package/dist/proxy/feeds/sequence/index.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/types.d.ts +80 -0
- package/dist/proxy/feeds/sequence/types.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/updater.d.ts +26 -0
- package/dist/proxy/feeds/sequence/updater.d.ts.map +1 -0
- package/dist/proxy/index.d.ts +6 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/manifest-builder.d.ts +183 -0
- package/dist/proxy/manifest-builder.d.ts.map +1 -0
- package/dist/proxy/mantaray-encrypted.d.ts +27 -0
- package/dist/proxy/mantaray-encrypted.d.ts.map +1 -0
- package/dist/proxy/mantaray.d.ts +26 -0
- package/dist/proxy/mantaray.d.ts.map +1 -0
- package/dist/proxy/types.d.ts +29 -0
- package/dist/proxy/types.d.ts.map +1 -0
- package/dist/proxy/upload-data.d.ts +17 -0
- package/dist/proxy/upload-data.d.ts.map +1 -0
- package/dist/proxy/upload-encrypted-data.d.ts +103 -0
- package/dist/proxy/upload-encrypted-data.d.ts.map +1 -0
- package/dist/schemas.d.ts +240 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/storage/debounced-uploader.d.ts +62 -0
- package/dist/storage/debounced-uploader.d.ts.map +1 -0
- package/dist/storage/utilization-store.d.ts +108 -0
- package/dist/storage/utilization-store.d.ts.map +1 -0
- package/dist/swarm-id-auth.d.ts +74 -0
- package/dist/swarm-id-auth.d.ts.map +1 -0
- package/dist/swarm-id-auth.js +2 -0
- package/dist/swarm-id-auth.js.map +1 -0
- package/dist/swarm-id-client.d.ts +878 -0
- package/dist/swarm-id-client.d.ts.map +1 -0
- package/dist/swarm-id-client.js +2 -0
- package/dist/swarm-id-client.js.map +1 -0
- package/dist/swarm-id-proxy.d.ts +236 -0
- package/dist/swarm-id-proxy.d.ts.map +1 -0
- package/dist/swarm-id-proxy.js +2 -0
- package/dist/swarm-id-proxy.js.map +1 -0
- package/dist/swarm-id.esm.js +2 -0
- package/dist/swarm-id.esm.js.map +1 -0
- package/dist/swarm-id.umd.js +2 -0
- package/dist/swarm-id.umd.js.map +1 -0
- package/dist/sync/index.d.ts +9 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/key-derivation.d.ts +25 -0
- package/dist/sync/key-derivation.d.ts.map +1 -0
- package/dist/sync/restore-account.d.ts +28 -0
- package/dist/sync/restore-account.d.ts.map +1 -0
- package/dist/sync/serialization.d.ts +16 -0
- package/dist/sync/serialization.d.ts.map +1 -0
- package/dist/sync/store-interfaces.d.ts +53 -0
- package/dist/sync/store-interfaces.d.ts.map +1 -0
- package/dist/sync/sync-account.d.ts +44 -0
- package/dist/sync/sync-account.d.ts.map +1 -0
- package/dist/sync/types.d.ts +13 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/test-fixtures.d.ts +17 -0
- package/dist/test-fixtures.d.ts.map +1 -0
- package/dist/types-BD_VkNn0.js +2 -0
- package/dist/types-BD_VkNn0.js.map +1 -0
- package/dist/types-lJCaT-50.js +2 -0
- package/dist/types-lJCaT-50.js.map +1 -0
- package/dist/types.d.ts +2157 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/account-payload.d.ts +94 -0
- package/dist/utils/account-payload.d.ts.map +1 -0
- package/dist/utils/account-state-snapshot.d.ts +38 -0
- package/dist/utils/account-state-snapshot.d.ts.map +1 -0
- package/dist/utils/backup-encryption.d.ts +127 -0
- package/dist/utils/backup-encryption.d.ts.map +1 -0
- package/dist/utils/batch-utilization.d.ts +432 -0
- package/dist/utils/batch-utilization.d.ts.map +1 -0
- package/dist/utils/constants.d.ts +11 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/hex.d.ts +17 -0
- package/dist/utils/hex.d.ts.map +1 -0
- package/dist/utils/key-derivation.d.ts +92 -0
- package/dist/utils/key-derivation.d.ts.map +1 -0
- package/dist/utils/storage-managers.d.ts +65 -0
- package/dist/utils/storage-managers.d.ts.map +1 -0
- package/dist/utils/swarm-id-export.d.ts +24 -0
- package/dist/utils/swarm-id-export.d.ts.map +1 -0
- package/dist/utils/ttl.d.ts +49 -0
- package/dist/utils/ttl.d.ts.map +1 -0
- package/dist/utils/url.d.ts +41 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/versioned-storage.d.ts +131 -0
- package/dist/utils/versioned-storage.d.ts.map +1 -0
- package/package.json +78 -0
- package/src/chunk/bmt.test.ts +217 -0
- package/src/chunk/bmt.ts +57 -0
- package/src/chunk/cac.test.ts +214 -0
- package/src/chunk/cac.ts +65 -0
- package/src/chunk/constants.ts +18 -0
- package/src/chunk/encrypted-cac.test.ts +385 -0
- package/src/chunk/encrypted-cac.ts +131 -0
- package/src/chunk/encryption.test.ts +352 -0
- package/src/chunk/encryption.ts +300 -0
- package/src/chunk/index.ts +47 -0
- package/src/index.ts +430 -0
- package/src/proxy/act/act.test.ts +278 -0
- package/src/proxy/act/act.ts +158 -0
- package/src/proxy/act/bee-compat.test.ts +948 -0
- package/src/proxy/act/crypto.test.ts +436 -0
- package/src/proxy/act/crypto.ts +376 -0
- package/src/proxy/act/grantee-list.test.ts +393 -0
- package/src/proxy/act/grantee-list.ts +239 -0
- package/src/proxy/act/history.test.ts +360 -0
- package/src/proxy/act/history.ts +413 -0
- package/src/proxy/act/index.test.ts +748 -0
- package/src/proxy/act/index.ts +853 -0
- package/src/proxy/chunking-encrypted.ts +95 -0
- package/src/proxy/chunking.ts +65 -0
- package/src/proxy/download-data.ts +448 -0
- package/src/proxy/feed-manifest.ts +174 -0
- package/src/proxy/feeds/epochs/async-finder.ts +372 -0
- package/src/proxy/feeds/epochs/epoch.test.ts +249 -0
- package/src/proxy/feeds/epochs/epoch.ts +181 -0
- package/src/proxy/feeds/epochs/finder.ts +282 -0
- package/src/proxy/feeds/epochs/index.ts +73 -0
- package/src/proxy/feeds/epochs/integration.test.ts +1336 -0
- package/src/proxy/feeds/epochs/test-utils.ts +274 -0
- package/src/proxy/feeds/epochs/types.ts +128 -0
- package/src/proxy/feeds/epochs/updater.ts +192 -0
- package/src/proxy/feeds/epochs/utils.ts +62 -0
- package/src/proxy/feeds/index.ts +5 -0
- package/src/proxy/feeds/sequence/async-finder.ts +31 -0
- package/src/proxy/feeds/sequence/finder.ts +73 -0
- package/src/proxy/feeds/sequence/index.ts +54 -0
- package/src/proxy/feeds/sequence/integration.test.ts +966 -0
- package/src/proxy/feeds/sequence/types.ts +103 -0
- package/src/proxy/feeds/sequence/updater.ts +71 -0
- package/src/proxy/index.ts +5 -0
- package/src/proxy/manifest-builder.test.ts +427 -0
- package/src/proxy/manifest-builder.ts +679 -0
- package/src/proxy/mantaray-encrypted.ts +78 -0
- package/src/proxy/mantaray.ts +104 -0
- package/src/proxy/types.ts +32 -0
- package/src/proxy/upload-data.ts +189 -0
- package/src/proxy/upload-encrypted-data.ts +658 -0
- package/src/schemas.ts +299 -0
- package/src/storage/debounced-uploader.ts +192 -0
- package/src/storage/utilization-store.ts +397 -0
- package/src/swarm-id-client.test.ts +99 -0
- package/src/swarm-id-client.ts +3095 -0
- package/src/swarm-id-proxy.ts +3891 -0
- package/src/sync/index.ts +28 -0
- package/src/sync/restore-account.ts +90 -0
- package/src/sync/serialization.ts +39 -0
- package/src/sync/store-interfaces.ts +62 -0
- package/src/sync/sync-account.test.ts +302 -0
- package/src/sync/sync-account.ts +396 -0
- package/src/sync/types.ts +11 -0
- package/src/test-fixtures.ts +109 -0
- package/src/types.ts +1651 -0
- package/src/utils/account-state-snapshot.test.ts +595 -0
- package/src/utils/account-state-snapshot.ts +94 -0
- package/src/utils/backup-encryption.test.ts +442 -0
- package/src/utils/backup-encryption.ts +352 -0
- package/src/utils/batch-utilization.ts +1309 -0
- package/src/utils/constants.ts +20 -0
- package/src/utils/hex.ts +27 -0
- package/src/utils/key-derivation.ts +197 -0
- package/src/utils/storage-managers.ts +365 -0
- package/src/utils/ttl.ts +129 -0
- package/src/utils/url.test.ts +136 -0
- package/src/utils/url.ts +71 -0
- package/src/utils/versioned-storage.ts +323 -0
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACT (Access Control Tries) - Bee-Compatible Implementation
|
|
3
|
+
*
|
|
4
|
+
* This module provides client-side ACT operations with Bee-compatible
|
|
5
|
+
* Simple Manifest (JSON) format:
|
|
6
|
+
* - ACT manifest (JSON with lookup key -> encrypted access key mappings)
|
|
7
|
+
* - Encrypted grantee list (stored separately)
|
|
8
|
+
* - History manifest (tracks ACT versions over time)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Bee, BeeRequestOptions, UploadOptions } from "@ethersphere/bee-js"
|
|
12
|
+
import type { UploadContext, UploadProgress } from "../types"
|
|
13
|
+
import { uploadEncryptedDataWithSigning } from "../upload-encrypted-data"
|
|
14
|
+
import { uploadDataWithSigning } from "../upload-data"
|
|
15
|
+
import { downloadDataWithChunkAPI } from "../download-data"
|
|
16
|
+
import { hexToUint8Array, uint8ArrayToHex } from "../../utils/hex"
|
|
17
|
+
import {
|
|
18
|
+
deriveKeys,
|
|
19
|
+
counterModeEncrypt,
|
|
20
|
+
counterModeDecrypt,
|
|
21
|
+
publicKeyFromPrivate,
|
|
22
|
+
generateRandomKey,
|
|
23
|
+
publicKeyFromCompressed,
|
|
24
|
+
compressPublicKey,
|
|
25
|
+
} from "./crypto"
|
|
26
|
+
|
|
27
|
+
type ActUploadOptions = UploadOptions & { beeCompatible?: boolean }
|
|
28
|
+
import {
|
|
29
|
+
serializeAct,
|
|
30
|
+
deserializeAct,
|
|
31
|
+
findEntryByLookupKey,
|
|
32
|
+
publicKeysEqual,
|
|
33
|
+
type ActEntry,
|
|
34
|
+
} from "./act"
|
|
35
|
+
import {
|
|
36
|
+
serializeAndEncryptGranteeList,
|
|
37
|
+
decryptAndDeserializeGranteeList,
|
|
38
|
+
type UncompressedPublicKey,
|
|
39
|
+
} from "./grantee-list"
|
|
40
|
+
import {
|
|
41
|
+
createHistoryManifest,
|
|
42
|
+
addHistoryEntry,
|
|
43
|
+
getLatestEntry,
|
|
44
|
+
getEntryAtTimestamp,
|
|
45
|
+
saveHistoryTreeRecursively,
|
|
46
|
+
deserializeHistory,
|
|
47
|
+
loadHistoryEntries,
|
|
48
|
+
getCurrentTimestamp,
|
|
49
|
+
} from "./history"
|
|
50
|
+
|
|
51
|
+
// Reference size constants
|
|
52
|
+
const REFERENCE_SIZE = 32
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Result of ACT upload operation
|
|
56
|
+
*/
|
|
57
|
+
export interface ActUploadResult {
|
|
58
|
+
encryptedReference: string // Encrypted content reference (NOT stored in ACT)
|
|
59
|
+
historyReference: string // History manifest reference (root of ACT versions)
|
|
60
|
+
granteeListReference: string // Encrypted grantee list reference
|
|
61
|
+
publisherPubKey: string // Compressed public key for sharing with grantees
|
|
62
|
+
actReference: string // Latest ACT reference (for convenience)
|
|
63
|
+
tagUid?: number
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Result of ACT grantee modification
|
|
68
|
+
*/
|
|
69
|
+
export interface ActGranteeModifyResult {
|
|
70
|
+
historyReference: string
|
|
71
|
+
granteeListReference: string
|
|
72
|
+
actReference: string
|
|
73
|
+
tagUid?: number
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Result of ACT revocation (includes new encrypted reference due to key rotation)
|
|
78
|
+
*/
|
|
79
|
+
export interface ActRevocationResult extends ActGranteeModifyResult {
|
|
80
|
+
encryptedReference: string // New encrypted reference after key rotation
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Format decrypted reference - trim to 32 bytes if second half is all zeros
|
|
85
|
+
*/
|
|
86
|
+
function formatDecryptedReference(decryptedRef: Uint8Array): string {
|
|
87
|
+
let isShortRef = true
|
|
88
|
+
for (let i = REFERENCE_SIZE; i < decryptedRef.length; i++) {
|
|
89
|
+
if (decryptedRef[i] !== 0) {
|
|
90
|
+
isShortRef = false
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (isShortRef) {
|
|
96
|
+
return uint8ArrayToHex(decryptedRef.slice(0, REFERENCE_SIZE))
|
|
97
|
+
}
|
|
98
|
+
return uint8ArrayToHex(decryptedRef)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create an ACT-protected upload
|
|
103
|
+
*
|
|
104
|
+
* This creates:
|
|
105
|
+
* 1. ACT manifest (JSON Simple Manifest with lookup key -> encrypted access key mappings)
|
|
106
|
+
* 2. Encrypted grantee list (for publisher management)
|
|
107
|
+
* 3. History manifest (tracks ACT versions over time)
|
|
108
|
+
*
|
|
109
|
+
* @param context - Upload context with bee and stamper
|
|
110
|
+
* @param contentReference - The reference to protect (32 or 64 bytes)
|
|
111
|
+
* @param publisherPrivateKey - Publisher's private key (32 bytes)
|
|
112
|
+
* @param granteePublicKeys - Array of grantee public keys
|
|
113
|
+
* @param options - Upload options
|
|
114
|
+
* @param requestOptions - Bee request options
|
|
115
|
+
* @param onProgress - Progress callback
|
|
116
|
+
* @returns Multiple references for ACT
|
|
117
|
+
*/
|
|
118
|
+
export async function createActForContent(
|
|
119
|
+
context: UploadContext,
|
|
120
|
+
contentReference: Uint8Array,
|
|
121
|
+
publisherPrivateKey: Uint8Array,
|
|
122
|
+
granteePublicKeys: Array<{ x: Uint8Array; y: Uint8Array }>,
|
|
123
|
+
options?: ActUploadOptions,
|
|
124
|
+
requestOptions?: BeeRequestOptions,
|
|
125
|
+
onProgress?: (progress: UploadProgress) => void,
|
|
126
|
+
): Promise<ActUploadResult> {
|
|
127
|
+
// Generate random access key
|
|
128
|
+
const accessKey = generateRandomKey()
|
|
129
|
+
|
|
130
|
+
// Encrypt the content reference with the access key
|
|
131
|
+
// CTR mode preserves input length - 32-byte ref → 32-byte encrypted
|
|
132
|
+
const encryptedRef = counterModeEncrypt(contentReference, accessKey)
|
|
133
|
+
|
|
134
|
+
// Get publisher's public key
|
|
135
|
+
const publisherPubKey = publicKeyFromPrivate(publisherPrivateKey)
|
|
136
|
+
|
|
137
|
+
// Create entries for publisher and all grantees
|
|
138
|
+
const entries: ActEntry[] = []
|
|
139
|
+
|
|
140
|
+
// Entry for publisher (so they can decrypt their own content)
|
|
141
|
+
const publisherKeys = deriveKeys(
|
|
142
|
+
publisherPrivateKey,
|
|
143
|
+
publisherPubKey.x,
|
|
144
|
+
publisherPubKey.y,
|
|
145
|
+
)
|
|
146
|
+
entries.push({
|
|
147
|
+
lookupKey: publisherKeys.lookupKey,
|
|
148
|
+
encryptedAccessKey: counterModeEncrypt(
|
|
149
|
+
accessKey,
|
|
150
|
+
publisherKeys.accessKeyDecryptionKey,
|
|
151
|
+
),
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// Entry for each grantee
|
|
155
|
+
for (const granteePubKey of granteePublicKeys) {
|
|
156
|
+
const granteeKeys = deriveKeys(
|
|
157
|
+
publisherPrivateKey,
|
|
158
|
+
granteePubKey.x,
|
|
159
|
+
granteePubKey.y,
|
|
160
|
+
)
|
|
161
|
+
entries.push({
|
|
162
|
+
lookupKey: granteeKeys.lookupKey,
|
|
163
|
+
encryptedAccessKey: counterModeEncrypt(
|
|
164
|
+
accessKey,
|
|
165
|
+
granteeKeys.accessKeyDecryptionKey,
|
|
166
|
+
),
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 1. Serialize and upload ACT manifest (JSON Simple Manifest format)
|
|
171
|
+
const actJson = serializeAct(entries)
|
|
172
|
+
|
|
173
|
+
const beeCompatible = options?.beeCompatible === true
|
|
174
|
+
|
|
175
|
+
const actResult = beeCompatible
|
|
176
|
+
? await uploadDataWithSigning(
|
|
177
|
+
context,
|
|
178
|
+
actJson,
|
|
179
|
+
options,
|
|
180
|
+
undefined,
|
|
181
|
+
requestOptions,
|
|
182
|
+
)
|
|
183
|
+
: await uploadEncryptedDataWithSigning(
|
|
184
|
+
context,
|
|
185
|
+
actJson,
|
|
186
|
+
undefined,
|
|
187
|
+
options,
|
|
188
|
+
undefined,
|
|
189
|
+
requestOptions,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
// 2. Serialize and upload encrypted grantee list
|
|
193
|
+
const encryptedGranteeList = serializeAndEncryptGranteeList(
|
|
194
|
+
granteePublicKeys,
|
|
195
|
+
publisherPrivateKey,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
const granteeListResult = await uploadEncryptedDataWithSigning(
|
|
199
|
+
context,
|
|
200
|
+
encryptedGranteeList,
|
|
201
|
+
undefined,
|
|
202
|
+
options,
|
|
203
|
+
undefined,
|
|
204
|
+
requestOptions,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
// 3. Create and upload history manifest
|
|
208
|
+
const timestamp = getCurrentTimestamp()
|
|
209
|
+
const historyManifest = createHistoryManifest()
|
|
210
|
+
addHistoryEntry(
|
|
211
|
+
historyManifest,
|
|
212
|
+
timestamp,
|
|
213
|
+
actResult.reference,
|
|
214
|
+
granteeListResult.reference,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
// Save history tree bottom-up using Bee's actual returned references
|
|
218
|
+
// This ensures parent nodes reference children by their actual storage addresses
|
|
219
|
+
const historyResult = await saveHistoryTreeRecursively(
|
|
220
|
+
historyManifest,
|
|
221
|
+
async (data, isRoot) => {
|
|
222
|
+
const result = beeCompatible
|
|
223
|
+
? await uploadDataWithSigning(
|
|
224
|
+
context,
|
|
225
|
+
data,
|
|
226
|
+
options,
|
|
227
|
+
isRoot ? onProgress : undefined,
|
|
228
|
+
requestOptions,
|
|
229
|
+
)
|
|
230
|
+
: await uploadEncryptedDataWithSigning(
|
|
231
|
+
context,
|
|
232
|
+
data,
|
|
233
|
+
undefined,
|
|
234
|
+
options,
|
|
235
|
+
isRoot ? onProgress : undefined,
|
|
236
|
+
requestOptions,
|
|
237
|
+
)
|
|
238
|
+
return result
|
|
239
|
+
},
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
const historyReference = historyResult.rootReference
|
|
243
|
+
const historyTagUid = historyResult.tagUid
|
|
244
|
+
|
|
245
|
+
// Compress publisher public key for API response
|
|
246
|
+
const compressedPubKey = compressPublicKey(
|
|
247
|
+
publisherPubKey.x,
|
|
248
|
+
publisherPubKey.y,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
encryptedReference: uint8ArrayToHex(encryptedRef),
|
|
253
|
+
historyReference,
|
|
254
|
+
granteeListReference: granteeListResult.reference,
|
|
255
|
+
publisherPubKey: uint8ArrayToHex(compressedPubKey),
|
|
256
|
+
actReference: actResult.reference,
|
|
257
|
+
tagUid: historyTagUid,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Decrypt an ACT-protected reference
|
|
263
|
+
*
|
|
264
|
+
* @param bee - Bee instance
|
|
265
|
+
* @param encryptedReference - The encrypted reference (hex string)
|
|
266
|
+
* @param historyReference - History manifest reference
|
|
267
|
+
* @param publisherPubKeyHex - Publisher's compressed public key (hex)
|
|
268
|
+
* @param readerPrivateKey - Reader's private key (32 bytes)
|
|
269
|
+
* @param timestamp - Optional timestamp to look up specific ACT version
|
|
270
|
+
* @param requestOptions - Bee request options
|
|
271
|
+
* @returns Decrypted content reference (hex string)
|
|
272
|
+
*/
|
|
273
|
+
export async function decryptActReference(
|
|
274
|
+
bee: Bee,
|
|
275
|
+
encryptedReference: string,
|
|
276
|
+
historyReference: string,
|
|
277
|
+
publisherPubKeyHex: string,
|
|
278
|
+
readerPrivateKey: Uint8Array,
|
|
279
|
+
timestamp?: number,
|
|
280
|
+
requestOptions?: BeeRequestOptions,
|
|
281
|
+
): Promise<string> {
|
|
282
|
+
// Parse publisher public key
|
|
283
|
+
const compressedPubKey = hexToUint8Array(publisherPubKeyHex)
|
|
284
|
+
const publisherPubKey = publicKeyFromCompressed(compressedPubKey)
|
|
285
|
+
|
|
286
|
+
// Download history manifest
|
|
287
|
+
const historyData = await downloadDataWithChunkAPI(
|
|
288
|
+
bee,
|
|
289
|
+
historyReference,
|
|
290
|
+
undefined,
|
|
291
|
+
undefined,
|
|
292
|
+
requestOptions,
|
|
293
|
+
)
|
|
294
|
+
const historyManifest = deserializeHistory(
|
|
295
|
+
historyData,
|
|
296
|
+
hexToUint8Array(historyReference),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
// Load child nodes to populate entry data
|
|
300
|
+
await loadHistoryEntries(historyManifest, async (ref) => {
|
|
301
|
+
return downloadDataWithChunkAPI(
|
|
302
|
+
bee,
|
|
303
|
+
ref,
|
|
304
|
+
undefined,
|
|
305
|
+
undefined,
|
|
306
|
+
requestOptions,
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
// Get the appropriate entry
|
|
311
|
+
const entry = timestamp
|
|
312
|
+
? getEntryAtTimestamp(historyManifest, timestamp)
|
|
313
|
+
: getLatestEntry(historyManifest)
|
|
314
|
+
|
|
315
|
+
if (!entry) {
|
|
316
|
+
throw new Error("No ACT entry found in history")
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const actReference = entry.metadata.actReference
|
|
320
|
+
|
|
321
|
+
// Download ACT manifest (JSON Simple Manifest)
|
|
322
|
+
const actData = await downloadDataWithChunkAPI(
|
|
323
|
+
bee,
|
|
324
|
+
actReference,
|
|
325
|
+
undefined,
|
|
326
|
+
undefined,
|
|
327
|
+
requestOptions,
|
|
328
|
+
)
|
|
329
|
+
const entries = deserializeAct(actData)
|
|
330
|
+
|
|
331
|
+
// Derive keys using reader's private key and publisher's public key
|
|
332
|
+
const derivedKeys = deriveKeys(
|
|
333
|
+
readerPrivateKey,
|
|
334
|
+
publisherPubKey.x,
|
|
335
|
+
publisherPubKey.y,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
// Find entry matching the lookup key
|
|
339
|
+
let foundEntry = findEntryByLookupKey(entries, derivedKeys.lookupKey)
|
|
340
|
+
|
|
341
|
+
if (!foundEntry) {
|
|
342
|
+
// Also try self-lookup (if reader is publisher)
|
|
343
|
+
const readerPubKey = publicKeyFromPrivate(readerPrivateKey)
|
|
344
|
+
const selfKeys = deriveKeys(
|
|
345
|
+
readerPrivateKey,
|
|
346
|
+
readerPubKey.x,
|
|
347
|
+
readerPubKey.y,
|
|
348
|
+
)
|
|
349
|
+
foundEntry = findEntryByLookupKey(entries, selfKeys.lookupKey)
|
|
350
|
+
|
|
351
|
+
if (!foundEntry) {
|
|
352
|
+
throw new Error("Access denied: no ACT entry found for this key")
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Use self keys for decryption
|
|
356
|
+
const accessKey = counterModeDecrypt(
|
|
357
|
+
foundEntry.encryptedAccessKey,
|
|
358
|
+
selfKeys.accessKeyDecryptionKey,
|
|
359
|
+
)
|
|
360
|
+
const encryptedRef = hexToUint8Array(encryptedReference)
|
|
361
|
+
const decryptedRef = counterModeDecrypt(encryptedRef, accessKey)
|
|
362
|
+
return formatDecryptedReference(decryptedRef)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Decrypt access key
|
|
366
|
+
const accessKey = counterModeDecrypt(
|
|
367
|
+
foundEntry.encryptedAccessKey,
|
|
368
|
+
derivedKeys.accessKeyDecryptionKey,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
// Decrypt the content reference
|
|
372
|
+
const encryptedRef = hexToUint8Array(encryptedReference)
|
|
373
|
+
const decryptedRef = counterModeDecrypt(encryptedRef, accessKey)
|
|
374
|
+
|
|
375
|
+
return formatDecryptedReference(decryptedRef)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Add grantees to an existing ACT
|
|
380
|
+
*/
|
|
381
|
+
export async function addGranteesToAct(
|
|
382
|
+
context: UploadContext,
|
|
383
|
+
historyReference: string,
|
|
384
|
+
publisherPrivateKey: Uint8Array,
|
|
385
|
+
newGranteePublicKeys: Array<{ x: Uint8Array; y: Uint8Array }>,
|
|
386
|
+
options?: ActUploadOptions,
|
|
387
|
+
requestOptions?: BeeRequestOptions,
|
|
388
|
+
onProgress?: (progress: UploadProgress) => void,
|
|
389
|
+
): Promise<ActGranteeModifyResult> {
|
|
390
|
+
const { bee } = context
|
|
391
|
+
const beeCompatible = historyReference.length === 64
|
|
392
|
+
|
|
393
|
+
// Download history manifest
|
|
394
|
+
const historyData = await downloadDataWithChunkAPI(
|
|
395
|
+
bee,
|
|
396
|
+
historyReference,
|
|
397
|
+
undefined,
|
|
398
|
+
undefined,
|
|
399
|
+
requestOptions,
|
|
400
|
+
)
|
|
401
|
+
const historyManifest = deserializeHistory(
|
|
402
|
+
historyData,
|
|
403
|
+
hexToUint8Array(historyReference),
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
// Load child nodes to populate entry data
|
|
407
|
+
await loadHistoryEntries(historyManifest, async (ref) => {
|
|
408
|
+
return downloadDataWithChunkAPI(
|
|
409
|
+
bee,
|
|
410
|
+
ref,
|
|
411
|
+
undefined,
|
|
412
|
+
undefined,
|
|
413
|
+
requestOptions,
|
|
414
|
+
)
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
// Get latest entry
|
|
418
|
+
const latestEntry = getLatestEntry(historyManifest)
|
|
419
|
+
if (!latestEntry) {
|
|
420
|
+
throw new Error("History manifest is empty")
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Download current ACT (JSON Simple Manifest)
|
|
424
|
+
const actData = await downloadDataWithChunkAPI(
|
|
425
|
+
bee,
|
|
426
|
+
latestEntry.metadata.actReference,
|
|
427
|
+
undefined,
|
|
428
|
+
undefined,
|
|
429
|
+
requestOptions,
|
|
430
|
+
)
|
|
431
|
+
const entries = deserializeAct(actData)
|
|
432
|
+
|
|
433
|
+
// Get publisher's public key and recover access key
|
|
434
|
+
const publisherPubKey = publicKeyFromPrivate(publisherPrivateKey)
|
|
435
|
+
const publisherKeys = deriveKeys(
|
|
436
|
+
publisherPrivateKey,
|
|
437
|
+
publisherPubKey.x,
|
|
438
|
+
publisherPubKey.y,
|
|
439
|
+
)
|
|
440
|
+
const publisherEntry = findEntryByLookupKey(entries, publisherKeys.lookupKey)
|
|
441
|
+
|
|
442
|
+
if (!publisherEntry) {
|
|
443
|
+
throw new Error("Cannot find publisher entry in ACT")
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const accessKey = counterModeDecrypt(
|
|
447
|
+
publisherEntry.encryptedAccessKey,
|
|
448
|
+
publisherKeys.accessKeyDecryptionKey,
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
// Create new ACT manifest with existing entries plus new grantees
|
|
452
|
+
const newEntries: ActEntry[] = [...entries]
|
|
453
|
+
for (const granteePubKey of newGranteePublicKeys) {
|
|
454
|
+
const granteeKeys = deriveKeys(
|
|
455
|
+
publisherPrivateKey,
|
|
456
|
+
granteePubKey.x,
|
|
457
|
+
granteePubKey.y,
|
|
458
|
+
)
|
|
459
|
+
newEntries.push({
|
|
460
|
+
lookupKey: granteeKeys.lookupKey,
|
|
461
|
+
encryptedAccessKey: counterModeEncrypt(
|
|
462
|
+
accessKey,
|
|
463
|
+
granteeKeys.accessKeyDecryptionKey,
|
|
464
|
+
),
|
|
465
|
+
})
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Download and update grantee list
|
|
469
|
+
let existingGrantees: UncompressedPublicKey[] = []
|
|
470
|
+
if (latestEntry.metadata.encryptedGranteeListRef) {
|
|
471
|
+
const encryptedList = await downloadDataWithChunkAPI(
|
|
472
|
+
bee,
|
|
473
|
+
latestEntry.metadata.encryptedGranteeListRef,
|
|
474
|
+
undefined,
|
|
475
|
+
undefined,
|
|
476
|
+
requestOptions,
|
|
477
|
+
)
|
|
478
|
+
existingGrantees = decryptAndDeserializeGranteeList(
|
|
479
|
+
encryptedList,
|
|
480
|
+
publisherPrivateKey,
|
|
481
|
+
)
|
|
482
|
+
}
|
|
483
|
+
const updatedGrantees = [...existingGrantees, ...newGranteePublicKeys]
|
|
484
|
+
|
|
485
|
+
// Upload new ACT manifest (JSON Simple Manifest format)
|
|
486
|
+
const newActJson = serializeAct(newEntries)
|
|
487
|
+
const actResult = beeCompatible
|
|
488
|
+
? await uploadDataWithSigning(
|
|
489
|
+
context,
|
|
490
|
+
newActJson,
|
|
491
|
+
options,
|
|
492
|
+
undefined,
|
|
493
|
+
requestOptions,
|
|
494
|
+
)
|
|
495
|
+
: await uploadEncryptedDataWithSigning(
|
|
496
|
+
context,
|
|
497
|
+
newActJson,
|
|
498
|
+
undefined,
|
|
499
|
+
options,
|
|
500
|
+
undefined,
|
|
501
|
+
requestOptions,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
// Upload updated grantee list
|
|
505
|
+
const encryptedGranteeList = serializeAndEncryptGranteeList(
|
|
506
|
+
updatedGrantees,
|
|
507
|
+
publisherPrivateKey,
|
|
508
|
+
)
|
|
509
|
+
const granteeListResult = await uploadEncryptedDataWithSigning(
|
|
510
|
+
context,
|
|
511
|
+
encryptedGranteeList,
|
|
512
|
+
undefined,
|
|
513
|
+
options,
|
|
514
|
+
undefined,
|
|
515
|
+
requestOptions,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
// Add new history entry
|
|
519
|
+
const timestamp = getCurrentTimestamp()
|
|
520
|
+
addHistoryEntry(
|
|
521
|
+
historyManifest,
|
|
522
|
+
timestamp,
|
|
523
|
+
actResult.reference,
|
|
524
|
+
granteeListResult.reference,
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
// Save history tree bottom-up using Bee's actual returned references
|
|
528
|
+
const historyResult = await saveHistoryTreeRecursively(
|
|
529
|
+
historyManifest,
|
|
530
|
+
async (data, isRoot) => {
|
|
531
|
+
const result = beeCompatible
|
|
532
|
+
? await uploadDataWithSigning(
|
|
533
|
+
context,
|
|
534
|
+
data,
|
|
535
|
+
options,
|
|
536
|
+
isRoot ? onProgress : undefined,
|
|
537
|
+
requestOptions,
|
|
538
|
+
)
|
|
539
|
+
: await uploadEncryptedDataWithSigning(
|
|
540
|
+
context,
|
|
541
|
+
data,
|
|
542
|
+
undefined,
|
|
543
|
+
options,
|
|
544
|
+
isRoot ? onProgress : undefined,
|
|
545
|
+
requestOptions,
|
|
546
|
+
)
|
|
547
|
+
return result
|
|
548
|
+
},
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
const newHistoryReference = historyResult.rootReference
|
|
552
|
+
const historyTagUid = historyResult.tagUid
|
|
553
|
+
|
|
554
|
+
return {
|
|
555
|
+
historyReference: newHistoryReference,
|
|
556
|
+
granteeListReference: granteeListResult.reference,
|
|
557
|
+
actReference: actResult.reference,
|
|
558
|
+
tagUid: historyTagUid,
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Revoke grantees from an ACT (performs key rotation)
|
|
564
|
+
*/
|
|
565
|
+
export async function revokeGranteesFromAct(
|
|
566
|
+
context: UploadContext,
|
|
567
|
+
historyReference: string,
|
|
568
|
+
encryptedReference: string,
|
|
569
|
+
publisherPrivateKey: Uint8Array,
|
|
570
|
+
revokePublicKeys: Array<{ x: Uint8Array; y: Uint8Array }>,
|
|
571
|
+
options?: ActUploadOptions,
|
|
572
|
+
requestOptions?: BeeRequestOptions,
|
|
573
|
+
onProgress?: (progress: UploadProgress) => void,
|
|
574
|
+
): Promise<ActRevocationResult> {
|
|
575
|
+
const { bee } = context
|
|
576
|
+
const beeCompatible = historyReference.length === 64
|
|
577
|
+
|
|
578
|
+
// Download history manifest
|
|
579
|
+
const historyData = await downloadDataWithChunkAPI(
|
|
580
|
+
bee,
|
|
581
|
+
historyReference,
|
|
582
|
+
undefined,
|
|
583
|
+
undefined,
|
|
584
|
+
requestOptions,
|
|
585
|
+
)
|
|
586
|
+
const historyManifest = deserializeHistory(
|
|
587
|
+
historyData,
|
|
588
|
+
hexToUint8Array(historyReference),
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
// Load child nodes to populate entry data
|
|
592
|
+
await loadHistoryEntries(historyManifest, async (ref) => {
|
|
593
|
+
return downloadDataWithChunkAPI(
|
|
594
|
+
bee,
|
|
595
|
+
ref,
|
|
596
|
+
undefined,
|
|
597
|
+
undefined,
|
|
598
|
+
requestOptions,
|
|
599
|
+
)
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
// Get latest entry
|
|
603
|
+
const latestEntry = getLatestEntry(historyManifest)
|
|
604
|
+
if (!latestEntry) {
|
|
605
|
+
throw new Error("History manifest is empty")
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Download current ACT (JSON Simple Manifest)
|
|
609
|
+
const actData = await downloadDataWithChunkAPI(
|
|
610
|
+
bee,
|
|
611
|
+
latestEntry.metadata.actReference,
|
|
612
|
+
undefined,
|
|
613
|
+
undefined,
|
|
614
|
+
requestOptions,
|
|
615
|
+
)
|
|
616
|
+
const entries = deserializeAct(actData)
|
|
617
|
+
|
|
618
|
+
// Get publisher's public key and recover old access key
|
|
619
|
+
const publisherPubKey = publicKeyFromPrivate(publisherPrivateKey)
|
|
620
|
+
const publisherKeys = deriveKeys(
|
|
621
|
+
publisherPrivateKey,
|
|
622
|
+
publisherPubKey.x,
|
|
623
|
+
publisherPubKey.y,
|
|
624
|
+
)
|
|
625
|
+
const publisherEntry = findEntryByLookupKey(entries, publisherKeys.lookupKey)
|
|
626
|
+
|
|
627
|
+
if (!publisherEntry) {
|
|
628
|
+
throw new Error("Cannot find publisher entry in ACT")
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const oldAccessKey = counterModeDecrypt(
|
|
632
|
+
publisherEntry.encryptedAccessKey,
|
|
633
|
+
publisherKeys.accessKeyDecryptionKey,
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
// Decrypt the content reference
|
|
637
|
+
const encryptedRef = hexToUint8Array(encryptedReference)
|
|
638
|
+
const decryptedRef = counterModeDecrypt(encryptedRef, oldAccessKey)
|
|
639
|
+
|
|
640
|
+
// Generate NEW access key for key rotation
|
|
641
|
+
const newAccessKey = generateRandomKey()
|
|
642
|
+
|
|
643
|
+
// Encrypt content reference with new access key
|
|
644
|
+
const newEncryptedRef = counterModeEncrypt(decryptedRef, newAccessKey)
|
|
645
|
+
|
|
646
|
+
// Get current grantee list and filter out revoked
|
|
647
|
+
let currentGrantees: UncompressedPublicKey[] = []
|
|
648
|
+
if (latestEntry.metadata.encryptedGranteeListRef) {
|
|
649
|
+
const encryptedList = await downloadDataWithChunkAPI(
|
|
650
|
+
bee,
|
|
651
|
+
latestEntry.metadata.encryptedGranteeListRef,
|
|
652
|
+
undefined,
|
|
653
|
+
undefined,
|
|
654
|
+
requestOptions,
|
|
655
|
+
)
|
|
656
|
+
currentGrantees = decryptAndDeserializeGranteeList(
|
|
657
|
+
encryptedList,
|
|
658
|
+
publisherPrivateKey,
|
|
659
|
+
)
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const remainingGrantees = currentGrantees.filter((grantee) => {
|
|
663
|
+
return !revokePublicKeys.some((revoked) =>
|
|
664
|
+
publicKeysEqual(grantee, revoked),
|
|
665
|
+
)
|
|
666
|
+
})
|
|
667
|
+
|
|
668
|
+
// Rebuild ALL entries with new access key
|
|
669
|
+
const newEntries: ActEntry[] = []
|
|
670
|
+
|
|
671
|
+
// Entry for publisher
|
|
672
|
+
newEntries.push({
|
|
673
|
+
lookupKey: publisherKeys.lookupKey,
|
|
674
|
+
encryptedAccessKey: counterModeEncrypt(
|
|
675
|
+
newAccessKey,
|
|
676
|
+
publisherKeys.accessKeyDecryptionKey,
|
|
677
|
+
),
|
|
678
|
+
})
|
|
679
|
+
|
|
680
|
+
// Entry for each remaining grantee
|
|
681
|
+
for (const granteePubKey of remainingGrantees) {
|
|
682
|
+
const granteeKeys = deriveKeys(
|
|
683
|
+
publisherPrivateKey,
|
|
684
|
+
granteePubKey.x,
|
|
685
|
+
granteePubKey.y,
|
|
686
|
+
)
|
|
687
|
+
newEntries.push({
|
|
688
|
+
lookupKey: granteeKeys.lookupKey,
|
|
689
|
+
encryptedAccessKey: counterModeEncrypt(
|
|
690
|
+
newAccessKey,
|
|
691
|
+
granteeKeys.accessKeyDecryptionKey,
|
|
692
|
+
),
|
|
693
|
+
})
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Upload new ACT manifest (JSON Simple Manifest format)
|
|
697
|
+
const newActJson = serializeAct(newEntries)
|
|
698
|
+
const actResult = beeCompatible
|
|
699
|
+
? await uploadDataWithSigning(
|
|
700
|
+
context,
|
|
701
|
+
newActJson,
|
|
702
|
+
options,
|
|
703
|
+
undefined,
|
|
704
|
+
requestOptions,
|
|
705
|
+
)
|
|
706
|
+
: await uploadEncryptedDataWithSigning(
|
|
707
|
+
context,
|
|
708
|
+
newActJson,
|
|
709
|
+
undefined,
|
|
710
|
+
options,
|
|
711
|
+
undefined,
|
|
712
|
+
requestOptions,
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
// Upload updated grantee list
|
|
716
|
+
const encryptedGranteeList = serializeAndEncryptGranteeList(
|
|
717
|
+
remainingGrantees,
|
|
718
|
+
publisherPrivateKey,
|
|
719
|
+
)
|
|
720
|
+
const granteeListResult = await uploadEncryptedDataWithSigning(
|
|
721
|
+
context,
|
|
722
|
+
encryptedGranteeList,
|
|
723
|
+
undefined,
|
|
724
|
+
options,
|
|
725
|
+
undefined,
|
|
726
|
+
requestOptions,
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
// Add new history entry
|
|
730
|
+
const timestamp = getCurrentTimestamp()
|
|
731
|
+
addHistoryEntry(
|
|
732
|
+
historyManifest,
|
|
733
|
+
timestamp,
|
|
734
|
+
actResult.reference,
|
|
735
|
+
granteeListResult.reference,
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
// Save history tree bottom-up using Bee's actual returned references
|
|
739
|
+
const historyResult = await saveHistoryTreeRecursively(
|
|
740
|
+
historyManifest,
|
|
741
|
+
async (data, isRoot) => {
|
|
742
|
+
const result = beeCompatible
|
|
743
|
+
? await uploadDataWithSigning(
|
|
744
|
+
context,
|
|
745
|
+
data,
|
|
746
|
+
options,
|
|
747
|
+
isRoot ? onProgress : undefined,
|
|
748
|
+
requestOptions,
|
|
749
|
+
)
|
|
750
|
+
: await uploadEncryptedDataWithSigning(
|
|
751
|
+
context,
|
|
752
|
+
data,
|
|
753
|
+
undefined,
|
|
754
|
+
options,
|
|
755
|
+
isRoot ? onProgress : undefined,
|
|
756
|
+
requestOptions,
|
|
757
|
+
)
|
|
758
|
+
return result
|
|
759
|
+
},
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
const newHistoryReference = historyResult.rootReference
|
|
763
|
+
const historyTagUid = historyResult.tagUid
|
|
764
|
+
|
|
765
|
+
return {
|
|
766
|
+
encryptedReference: uint8ArrayToHex(newEncryptedRef),
|
|
767
|
+
historyReference: newHistoryReference,
|
|
768
|
+
granteeListReference: granteeListResult.reference,
|
|
769
|
+
actReference: actResult.reference,
|
|
770
|
+
tagUid: historyTagUid,
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Get grantees from an ACT
|
|
776
|
+
*/
|
|
777
|
+
export async function getGranteesFromAct(
|
|
778
|
+
bee: Bee,
|
|
779
|
+
historyReference: string,
|
|
780
|
+
publisherPrivateKey: Uint8Array,
|
|
781
|
+
requestOptions?: BeeRequestOptions,
|
|
782
|
+
): Promise<string[]> {
|
|
783
|
+
// Download history manifest
|
|
784
|
+
const historyData = await downloadDataWithChunkAPI(
|
|
785
|
+
bee,
|
|
786
|
+
historyReference,
|
|
787
|
+
undefined,
|
|
788
|
+
undefined,
|
|
789
|
+
requestOptions,
|
|
790
|
+
)
|
|
791
|
+
const historyManifest = deserializeHistory(
|
|
792
|
+
historyData,
|
|
793
|
+
hexToUint8Array(historyReference),
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
// Load child nodes to populate entry data
|
|
797
|
+
await loadHistoryEntries(historyManifest, async (ref) => {
|
|
798
|
+
return downloadDataWithChunkAPI(
|
|
799
|
+
bee,
|
|
800
|
+
ref,
|
|
801
|
+
undefined,
|
|
802
|
+
undefined,
|
|
803
|
+
requestOptions,
|
|
804
|
+
)
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
// Get latest entry
|
|
808
|
+
const latestEntry = getLatestEntry(historyManifest)
|
|
809
|
+
if (!latestEntry || !latestEntry.metadata.encryptedGranteeListRef) {
|
|
810
|
+
return []
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Download and decrypt grantee list
|
|
814
|
+
const encryptedList = await downloadDataWithChunkAPI(
|
|
815
|
+
bee,
|
|
816
|
+
latestEntry.metadata.encryptedGranteeListRef,
|
|
817
|
+
undefined,
|
|
818
|
+
undefined,
|
|
819
|
+
requestOptions,
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
const grantees = decryptAndDeserializeGranteeList(
|
|
823
|
+
encryptedList,
|
|
824
|
+
publisherPrivateKey,
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
// Return as compressed hex strings
|
|
828
|
+
return grantees.map((grantee) => {
|
|
829
|
+
const compressed = compressPublicKey(grantee.x, grantee.y)
|
|
830
|
+
return uint8ArrayToHex(compressed)
|
|
831
|
+
})
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Parse a compressed public key from hex string
|
|
836
|
+
*/
|
|
837
|
+
export function parseCompressedPublicKey(hex: string): {
|
|
838
|
+
x: Uint8Array
|
|
839
|
+
y: Uint8Array
|
|
840
|
+
} {
|
|
841
|
+
const compressed = hexToUint8Array(hex)
|
|
842
|
+
return publicKeyFromCompressed(compressed)
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Re-export types and utilities
|
|
846
|
+
export { type ActEntry } from "./act"
|
|
847
|
+
export { type UncompressedPublicKey } from "./grantee-list"
|
|
848
|
+
export { type HistoryEntry, type HistoryEntryMetadata } from "./history"
|
|
849
|
+
export {
|
|
850
|
+
publicKeyFromPrivate,
|
|
851
|
+
compressPublicKey,
|
|
852
|
+
publicKeyFromCompressed,
|
|
853
|
+
} from "./crypto"
|