@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,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACT History Management
|
|
3
|
+
*
|
|
4
|
+
* This module provides timestamped versioning of ACT entries, matching Bee's
|
|
5
|
+
* approach to tracking ACT versions over time.
|
|
6
|
+
*
|
|
7
|
+
* Key concepts:
|
|
8
|
+
* - Each ACT update creates a new history entry
|
|
9
|
+
* - Entries are keyed by reversed timestamp (MaxInt64 - timestamp)
|
|
10
|
+
* - History enables looking up ACT state at any point in time
|
|
11
|
+
* - Encrypted grantee list reference is stored in metadata
|
|
12
|
+
*
|
|
13
|
+
* This implementation uses the MantarayNode class from bee-js to produce
|
|
14
|
+
* Bee-compatible binary manifests with the proper version hash header.
|
|
15
|
+
*
|
|
16
|
+
* IMPORTANT: Mantaray manifests are hierarchical - each child node must be
|
|
17
|
+
* uploaded separately to Swarm. The root node references children by their
|
|
18
|
+
* content addresses (selfAddress). To read entries, all child nodes must be
|
|
19
|
+
* loaded recursively.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { MantarayNode } from "@ethersphere/bee-js"
|
|
23
|
+
import { hexToUint8Array, uint8ArrayToHex } from "../../utils/hex"
|
|
24
|
+
|
|
25
|
+
// Constants
|
|
26
|
+
const MAX_INT64 = BigInt("9223372036854775807")
|
|
27
|
+
const ENCODER = new TextEncoder()
|
|
28
|
+
|
|
29
|
+
// Bee uses "encryptedglref" as the metadata key for encrypted grantee list reference
|
|
30
|
+
const ENCRYPTED_GRANTEE_LIST_METADATA_KEY = "encryptedglref"
|
|
31
|
+
|
|
32
|
+
// Ensure monotonic seconds so multiple ACT updates within the same second
|
|
33
|
+
// don't collide on the same history key (mantaray requires unique paths).
|
|
34
|
+
let lastTimestamp = 0
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Single history entry metadata
|
|
38
|
+
*/
|
|
39
|
+
export interface HistoryEntryMetadata {
|
|
40
|
+
actReference: string // Reference to the ACT manifest
|
|
41
|
+
encryptedGranteeListRef?: string // Reference to encrypted grantee list (publisher only)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* History entry with timestamp
|
|
46
|
+
*/
|
|
47
|
+
export interface HistoryEntry {
|
|
48
|
+
timestamp: number // Unix timestamp in seconds
|
|
49
|
+
metadata: HistoryEntryMetadata
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Result of serializing a history tree
|
|
54
|
+
*/
|
|
55
|
+
export interface SerializedHistoryTree {
|
|
56
|
+
blobs: Map<string, Uint8Array> // Content address -> serialized data
|
|
57
|
+
rootReference: string // Reference to the root node
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Result of saving a history tree
|
|
62
|
+
*/
|
|
63
|
+
export interface SaveHistoryTreeResult {
|
|
64
|
+
rootReference: string // Reference to the root node (from Bee)
|
|
65
|
+
tagUid?: number // Tag UID from root upload
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Calculate reversed timestamp key for history lookup
|
|
70
|
+
*
|
|
71
|
+
* Bee uses reversed timestamps so that the latest entry sorts first.
|
|
72
|
+
* Key = MaxInt64 - timestamp
|
|
73
|
+
*
|
|
74
|
+
* @param timestamp - Unix timestamp in seconds
|
|
75
|
+
* @returns Reversed timestamp as string (for use as path)
|
|
76
|
+
*/
|
|
77
|
+
export function calculateReversedTimestamp(timestamp: number): string {
|
|
78
|
+
const reversed = MAX_INT64 - BigInt(timestamp)
|
|
79
|
+
return reversed.toString()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Calculate original timestamp from reversed key
|
|
84
|
+
*
|
|
85
|
+
* @param reversedKey - Reversed timestamp string
|
|
86
|
+
* @returns Original Unix timestamp in seconds
|
|
87
|
+
*/
|
|
88
|
+
export function calculateOriginalTimestamp(reversedKey: string): number {
|
|
89
|
+
const reversed = BigInt(reversedKey)
|
|
90
|
+
const original = MAX_INT64 - reversed
|
|
91
|
+
return Number(original)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Create a new empty history manifest using MantarayNode
|
|
96
|
+
*/
|
|
97
|
+
export function createHistoryManifest(): MantarayNode {
|
|
98
|
+
return new MantarayNode()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Add an entry to the history manifest
|
|
103
|
+
*
|
|
104
|
+
* This mutates the manifest in place by adding a fork.
|
|
105
|
+
*
|
|
106
|
+
* @param manifest - Existing history manifest (MantarayNode)
|
|
107
|
+
* @param timestamp - Unix timestamp for this entry
|
|
108
|
+
* @param actReference - Reference to the ACT manifest (hex string)
|
|
109
|
+
* @param encryptedGranteeListRef - Optional reference to encrypted grantee list (hex string)
|
|
110
|
+
*/
|
|
111
|
+
export function addHistoryEntry(
|
|
112
|
+
manifest: MantarayNode,
|
|
113
|
+
timestamp: number,
|
|
114
|
+
actReference: string,
|
|
115
|
+
encryptedGranteeListRef?: string,
|
|
116
|
+
): void {
|
|
117
|
+
const path = calculateReversedTimestamp(timestamp)
|
|
118
|
+
const reference = hexToUint8Array(actReference)
|
|
119
|
+
const metadata = encryptedGranteeListRef
|
|
120
|
+
? { [ENCRYPTED_GRANTEE_LIST_METADATA_KEY]: encryptedGranteeListRef }
|
|
121
|
+
: undefined
|
|
122
|
+
manifest.addFork(ENCODER.encode(path), reference, metadata)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get the latest history entry (most recent timestamp)
|
|
127
|
+
*
|
|
128
|
+
* Since keys are reversed timestamps, the smallest key is the latest entry.
|
|
129
|
+
* NOTE: This requires the manifest to have been loaded with loadRecursively()
|
|
130
|
+
* or to have been populated locally with addHistoryEntry().
|
|
131
|
+
*
|
|
132
|
+
* @param manifest - History manifest (MantarayNode)
|
|
133
|
+
* @returns Latest entry with its timestamp, or undefined if empty
|
|
134
|
+
*/
|
|
135
|
+
export function getLatestEntry(
|
|
136
|
+
manifest: MantarayNode,
|
|
137
|
+
): HistoryEntry | undefined {
|
|
138
|
+
const nodes = manifest.collect()
|
|
139
|
+
if (nodes.length === 0) {
|
|
140
|
+
return undefined
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Sort by path ascending (smallest = latest timestamp due to reversal)
|
|
144
|
+
nodes.sort((a, b) => a.fullPathString.localeCompare(b.fullPathString))
|
|
145
|
+
|
|
146
|
+
const latest = nodes[0]
|
|
147
|
+
return {
|
|
148
|
+
timestamp: calculateOriginalTimestamp(latest.fullPathString),
|
|
149
|
+
metadata: {
|
|
150
|
+
actReference: uint8ArrayToHex(latest.targetAddress),
|
|
151
|
+
encryptedGranteeListRef:
|
|
152
|
+
latest.metadata?.[ENCRYPTED_GRANTEE_LIST_METADATA_KEY],
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get entry at or before a specific timestamp
|
|
159
|
+
*
|
|
160
|
+
* This finds the ACT state that was valid at the given timestamp.
|
|
161
|
+
* NOTE: This requires the manifest to have been loaded with loadRecursively().
|
|
162
|
+
*
|
|
163
|
+
* @param manifest - History manifest (MantarayNode)
|
|
164
|
+
* @param timestamp - Target timestamp
|
|
165
|
+
* @returns Entry at or before timestamp, or undefined if none exists
|
|
166
|
+
*/
|
|
167
|
+
export function getEntryAtTimestamp(
|
|
168
|
+
manifest: MantarayNode,
|
|
169
|
+
timestamp: number,
|
|
170
|
+
): HistoryEntry | undefined {
|
|
171
|
+
const targetPath = calculateReversedTimestamp(timestamp)
|
|
172
|
+
const nodes = manifest.collect()
|
|
173
|
+
|
|
174
|
+
if (nodes.length === 0) {
|
|
175
|
+
return undefined
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Sort by path ascending
|
|
179
|
+
nodes.sort((a, b) => a.fullPathString.localeCompare(b.fullPathString))
|
|
180
|
+
|
|
181
|
+
// Find the first node with path >= targetPath (which corresponds to timestamp <= target)
|
|
182
|
+
for (const node of nodes) {
|
|
183
|
+
if (node.fullPathString >= targetPath) {
|
|
184
|
+
return {
|
|
185
|
+
timestamp: calculateOriginalTimestamp(node.fullPathString),
|
|
186
|
+
metadata: {
|
|
187
|
+
actReference: uint8ArrayToHex(node.targetAddress),
|
|
188
|
+
encryptedGranteeListRef:
|
|
189
|
+
node.metadata?.[ENCRYPTED_GRANTEE_LIST_METADATA_KEY],
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// If no entry at or before timestamp, return the oldest entry
|
|
196
|
+
// (this is the "first" ACT state)
|
|
197
|
+
const oldest = nodes[nodes.length - 1]
|
|
198
|
+
return {
|
|
199
|
+
timestamp: calculateOriginalTimestamp(oldest.fullPathString),
|
|
200
|
+
metadata: {
|
|
201
|
+
actReference: uint8ArrayToHex(oldest.targetAddress),
|
|
202
|
+
encryptedGranteeListRef:
|
|
203
|
+
oldest.metadata?.[ENCRYPTED_GRANTEE_LIST_METADATA_KEY],
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Get all history entries sorted by timestamp (newest first)
|
|
210
|
+
*
|
|
211
|
+
* @param manifest - History manifest (MantarayNode)
|
|
212
|
+
* @returns Array of entries sorted newest first
|
|
213
|
+
*/
|
|
214
|
+
export function getAllEntries(manifest: MantarayNode): HistoryEntry[] {
|
|
215
|
+
const nodes = manifest.collect()
|
|
216
|
+
|
|
217
|
+
// Sort by path ascending (newest first due to reversed timestamps)
|
|
218
|
+
nodes.sort((a, b) => a.fullPathString.localeCompare(b.fullPathString))
|
|
219
|
+
|
|
220
|
+
return nodes.map((node) => ({
|
|
221
|
+
timestamp: calculateOriginalTimestamp(node.fullPathString),
|
|
222
|
+
metadata: {
|
|
223
|
+
actReference: uint8ArrayToHex(node.targetAddress),
|
|
224
|
+
encryptedGranteeListRef:
|
|
225
|
+
node.metadata?.[ENCRYPTED_GRANTEE_LIST_METADATA_KEY],
|
|
226
|
+
},
|
|
227
|
+
}))
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Serialize the entire history manifest tree to individual blobs
|
|
232
|
+
*
|
|
233
|
+
* Mantaray manifests are hierarchical - each node is stored at its content
|
|
234
|
+
* address. This function returns all blobs that need to be uploaded, keyed
|
|
235
|
+
* by their content addresses.
|
|
236
|
+
*
|
|
237
|
+
* @deprecated Use saveHistoryTreeRecursively instead which uploads bottom-up
|
|
238
|
+
* and uses Bee's actual returned references to avoid address mismatches.
|
|
239
|
+
*
|
|
240
|
+
* @param manifest - History manifest (MantarayNode)
|
|
241
|
+
* @returns Map of content address -> serialized data, plus root reference
|
|
242
|
+
*/
|
|
243
|
+
export async function serializeHistoryTree(
|
|
244
|
+
manifest: MantarayNode,
|
|
245
|
+
): Promise<SerializedHistoryTree> {
|
|
246
|
+
const blobs = new Map<string, Uint8Array>()
|
|
247
|
+
|
|
248
|
+
async function marshalRecursively(node: MantarayNode): Promise<void> {
|
|
249
|
+
// First, marshal all child nodes
|
|
250
|
+
for (const fork of node.forks.values()) {
|
|
251
|
+
await marshalRecursively(fork.node)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Now marshal this node (which will use children's selfAddresses)
|
|
255
|
+
const data = await node.marshal()
|
|
256
|
+
const selfAddress = await node.calculateSelfAddress()
|
|
257
|
+
node.selfAddress = selfAddress.toUint8Array()
|
|
258
|
+
blobs.set(selfAddress.toHex(), data)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
await marshalRecursively(manifest)
|
|
262
|
+
|
|
263
|
+
const rootRef = uint8ArrayToHex(manifest.selfAddress!)
|
|
264
|
+
return { blobs, rootReference: rootRef }
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Upload callback type for saveHistoryTreeRecursively
|
|
269
|
+
*/
|
|
270
|
+
export type UploadCallback = (
|
|
271
|
+
data: Uint8Array,
|
|
272
|
+
isRoot: boolean,
|
|
273
|
+
) => Promise<{ reference: string; tagUid?: number }>
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Save the entire history manifest tree by uploading bottom-up
|
|
277
|
+
*
|
|
278
|
+
* This function uploads nodes in the correct order (children before parents)
|
|
279
|
+
* and uses Bee's actual returned references to update selfAddress before
|
|
280
|
+
* marshaling parents. This avoids address mismatches between local hash
|
|
281
|
+
* computation and Bee's storage.
|
|
282
|
+
*
|
|
283
|
+
* The flow mirrors MantarayNode.saveRecursively() from bee-js:
|
|
284
|
+
* 1. Recursively save all child forks first
|
|
285
|
+
* 2. Marshal this node (which uses children's updated selfAddress)
|
|
286
|
+
* 3. Upload and set selfAddress from Bee's response
|
|
287
|
+
*
|
|
288
|
+
* @param manifest - History manifest (MantarayNode)
|
|
289
|
+
* @param uploadFn - Callback to upload data, returns reference from Bee
|
|
290
|
+
* @returns Root reference from Bee and optional tag UID
|
|
291
|
+
*/
|
|
292
|
+
export async function saveHistoryTreeRecursively(
|
|
293
|
+
manifest: MantarayNode,
|
|
294
|
+
uploadFn: UploadCallback,
|
|
295
|
+
): Promise<SaveHistoryTreeResult> {
|
|
296
|
+
async function saveRecursively(
|
|
297
|
+
node: MantarayNode,
|
|
298
|
+
isRoot: boolean,
|
|
299
|
+
): Promise<{ reference: string; tagUid?: number }> {
|
|
300
|
+
// First, save all child forks recursively
|
|
301
|
+
for (const fork of node.forks.values()) {
|
|
302
|
+
await saveRecursively(fork.node, false)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Now marshal this node - children's selfAddress should be set from their uploads
|
|
306
|
+
const data = await node.marshal()
|
|
307
|
+
|
|
308
|
+
// Upload and get Bee's actual reference
|
|
309
|
+
const result = await uploadFn(data, isRoot)
|
|
310
|
+
|
|
311
|
+
// Update selfAddress with Bee's reference (critical for parent marshaling)
|
|
312
|
+
node.selfAddress = hexToUint8Array(result.reference)
|
|
313
|
+
|
|
314
|
+
return result
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const result = await saveRecursively(manifest, true)
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
rootReference: result.reference,
|
|
321
|
+
tagUid: result.tagUid,
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Serialize history manifest root to Mantaray binary format
|
|
327
|
+
*
|
|
328
|
+
* @deprecated Use serializeHistoryTree for proper Mantaray serialization
|
|
329
|
+
* @param manifest - History manifest (MantarayNode)
|
|
330
|
+
* @returns Serialized root manifest as Uint8Array
|
|
331
|
+
*/
|
|
332
|
+
export async function serializeHistory(
|
|
333
|
+
manifest: MantarayNode,
|
|
334
|
+
): Promise<Uint8Array> {
|
|
335
|
+
return manifest.marshal()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Deserialize history manifest from Mantaray binary format
|
|
340
|
+
*
|
|
341
|
+
* NOTE: After deserialization, call loadRecursively() or manually load
|
|
342
|
+
* child nodes to populate targetAddress for entries.
|
|
343
|
+
*
|
|
344
|
+
* @param data - Serialized manifest
|
|
345
|
+
* @param selfAddress - The reference/address of the manifest (32 bytes as Uint8Array)
|
|
346
|
+
* @returns Parsed history manifest (MantarayNode)
|
|
347
|
+
*/
|
|
348
|
+
export function deserializeHistory(
|
|
349
|
+
data: Uint8Array,
|
|
350
|
+
selfAddress: Uint8Array,
|
|
351
|
+
): MantarayNode {
|
|
352
|
+
return MantarayNode.unmarshalFromData(data, selfAddress)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Load child node data into a deserialized manifest
|
|
357
|
+
*
|
|
358
|
+
* After deserializing the root node, this function loads all child node data
|
|
359
|
+
* so that targetAddress is available for each entry.
|
|
360
|
+
*
|
|
361
|
+
* @param manifest - Deserialized root manifest
|
|
362
|
+
* @param loadData - Callback to load data for a given reference
|
|
363
|
+
*/
|
|
364
|
+
export async function loadHistoryEntries(
|
|
365
|
+
manifest: MantarayNode,
|
|
366
|
+
loadData: (reference: string) => Promise<Uint8Array>,
|
|
367
|
+
): Promise<void> {
|
|
368
|
+
async function loadRecursively(node: MantarayNode): Promise<void> {
|
|
369
|
+
for (const fork of node.forks.values()) {
|
|
370
|
+
if (!fork.node.selfAddress) {
|
|
371
|
+
throw new Error("Fork node selfAddress is not set")
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Load the child node data
|
|
375
|
+
const childRef = uint8ArrayToHex(fork.node.selfAddress)
|
|
376
|
+
const childData = await loadData(childRef)
|
|
377
|
+
|
|
378
|
+
// Deserialize the child node
|
|
379
|
+
const childNode = MantarayNode.unmarshalFromData(
|
|
380
|
+
childData,
|
|
381
|
+
fork.node.selfAddress,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
// Copy the loaded data to the fork node
|
|
385
|
+
fork.node.targetAddress = childNode.targetAddress
|
|
386
|
+
fork.node.forks = childNode.forks
|
|
387
|
+
fork.node.obfuscationKey = childNode.obfuscationKey
|
|
388
|
+
|
|
389
|
+
// Fix parent pointers for nested forks
|
|
390
|
+
for (const nestedFork of fork.node.forks.values()) {
|
|
391
|
+
nestedFork.node.parent = fork.node
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Recursively load any nested forks
|
|
395
|
+
await loadRecursively(fork.node)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
await loadRecursively(manifest)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Get current Unix timestamp in seconds
|
|
404
|
+
*/
|
|
405
|
+
export function getCurrentTimestamp(): number {
|
|
406
|
+
const now = Math.floor(Date.now() / 1000)
|
|
407
|
+
if (now <= lastTimestamp) {
|
|
408
|
+
lastTimestamp += 1
|
|
409
|
+
return lastTimestamp
|
|
410
|
+
}
|
|
411
|
+
lastTimestamp = now
|
|
412
|
+
return now
|
|
413
|
+
}
|