@originals/sdk 1.5.0 → 1.6.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/dist/adapters/FeeOracleMock.js +2 -2
- package/dist/bitcoin/OrdinalsClient.d.ts +1 -1
- package/dist/bitcoin/OrdinalsClient.js +10 -8
- package/dist/bitcoin/PSBTBuilder.js +1 -1
- package/dist/bitcoin/utxo-selection.js +2 -2
- package/dist/cel/ExternalReferenceManager.d.ts +57 -0
- package/dist/cel/ExternalReferenceManager.js +73 -0
- package/dist/cel/OriginalsCel.d.ts +245 -0
- package/dist/cel/OriginalsCel.js +349 -0
- package/dist/cel/algorithms/createEventLog.d.ts +32 -0
- package/dist/cel/algorithms/createEventLog.js +56 -0
- package/dist/cel/algorithms/deactivateEventLog.d.ts +35 -0
- package/dist/cel/algorithms/deactivateEventLog.js +91 -0
- package/dist/cel/algorithms/index.d.ts +10 -0
- package/dist/cel/algorithms/index.js +10 -0
- package/dist/cel/algorithms/updateEventLog.d.ts +34 -0
- package/dist/cel/algorithms/updateEventLog.js +82 -0
- package/dist/cel/algorithms/verifyEventLog.d.ts +45 -0
- package/dist/cel/algorithms/verifyEventLog.js +255 -0
- package/dist/cel/algorithms/witnessEvent.d.ts +29 -0
- package/dist/cel/algorithms/witnessEvent.js +75 -0
- package/dist/cel/cli/create.d.ts +36 -0
- package/dist/cel/cli/create.js +282 -0
- package/dist/cel/cli/index.d.ts +11 -0
- package/dist/cel/cli/index.js +351 -0
- package/dist/cel/cli/inspect.d.ts +30 -0
- package/dist/cel/cli/inspect.js +475 -0
- package/dist/cel/cli/migrate.d.ts +41 -0
- package/dist/cel/cli/migrate.js +405 -0
- package/dist/cel/cli/verify.d.ts +31 -0
- package/dist/cel/cli/verify.js +205 -0
- package/dist/cel/hash.d.ts +46 -0
- package/dist/cel/hash.js +66 -0
- package/dist/cel/index.d.ts +15 -0
- package/dist/cel/index.js +15 -0
- package/dist/cel/layers/BtcoCelManager.d.ts +121 -0
- package/dist/cel/layers/BtcoCelManager.js +329 -0
- package/dist/cel/layers/PeerCelManager.d.ts +151 -0
- package/dist/cel/layers/PeerCelManager.js +299 -0
- package/dist/cel/layers/WebVHCelManager.d.ts +122 -0
- package/dist/cel/layers/WebVHCelManager.js +291 -0
- package/dist/cel/layers/index.d.ts +13 -0
- package/dist/cel/layers/index.js +16 -0
- package/dist/cel/serialization/cbor.d.ts +42 -0
- package/dist/cel/serialization/cbor.js +163 -0
- package/dist/cel/serialization/index.d.ts +9 -0
- package/dist/cel/serialization/index.js +9 -0
- package/dist/cel/serialization/json.d.ts +41 -0
- package/dist/cel/serialization/json.js +180 -0
- package/dist/cel/types.d.ts +149 -0
- package/dist/cel/types.js +7 -0
- package/dist/cel/witnesses/BitcoinWitness.d.ts +83 -0
- package/dist/cel/witnesses/BitcoinWitness.js +116 -0
- package/dist/cel/witnesses/HttpWitness.d.ts +79 -0
- package/dist/cel/witnesses/HttpWitness.js +163 -0
- package/dist/cel/witnesses/WitnessService.d.ts +49 -0
- package/dist/cel/witnesses/WitnessService.js +10 -0
- package/dist/cel/witnesses/index.d.ts +10 -0
- package/dist/cel/witnesses/index.js +7 -0
- package/dist/core/OriginalsSDK.js +5 -1
- package/dist/crypto/Signer.js +14 -6
- package/dist/crypto/noble-init.js +20 -1
- package/dist/did/BtcoDidResolver.d.ts +2 -2
- package/dist/did/BtcoDidResolver.js +12 -8
- package/dist/did/DIDManager.js +6 -4
- package/dist/did/KeyManager.d.ts +1 -1
- package/dist/did/KeyManager.js +7 -4
- package/dist/did/WebVHManager.js +1 -1
- package/dist/did/createBtcoDidDocument.js +2 -1
- package/dist/events/types.d.ts +4 -1
- package/dist/examples/create-module-original.js +1 -1
- package/dist/examples/full-lifecycle-flow.js +2 -2
- package/dist/index.d.ts +13 -0
- package/dist/index.js +12 -0
- package/dist/kinds/KindRegistry.js +59 -29
- package/dist/lifecycle/BatchOperations.d.ts +5 -3
- package/dist/lifecycle/BatchOperations.js +11 -5
- package/dist/lifecycle/LifecycleManager.d.ts +1 -1
- package/dist/lifecycle/LifecycleManager.js +42 -33
- package/dist/lifecycle/OriginalsAsset.js +2 -2
- package/dist/migration/MigrationManager.js +67 -3
- package/dist/storage/LocalStorageAdapter.js +4 -1
- package/dist/storage/MemoryStorageAdapter.js +7 -7
- package/dist/types/network.js +6 -3
- package/dist/utils/Logger.d.ts +6 -6
- package/dist/utils/Logger.js +5 -3
- package/dist/utils/MetricsCollector.js +1 -1
- package/dist/utils/bitcoin-address.js +4 -2
- package/dist/utils/cbor.js +16 -3
- package/dist/utils/encoding.d.ts +4 -4
- package/dist/utils/encoding.js +7 -6
- package/dist/utils/hash.js +6 -1
- package/dist/utils/serialization.d.ts +2 -2
- package/dist/utils/serialization.js +7 -5
- package/dist/utils/telemetry.js +6 -2
- package/dist/utils/validation.js +8 -4
- package/dist/vc/CredentialManager.d.ts +8 -8
- package/dist/vc/CredentialManager.js +46 -33
- package/dist/vc/Issuer.d.ts +2 -2
- package/dist/vc/Issuer.js +5 -1
- package/dist/vc/Verifier.d.ts +2 -2
- package/dist/vc/Verifier.js +12 -6
- package/dist/vc/documentLoader.d.ts +5 -3
- package/dist/vc/documentLoader.js +5 -4
- package/package.json +4 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PeerCelManager - CEL Manager for did:peer Layer
|
|
3
|
+
*
|
|
4
|
+
* Creates and manages Originals assets in the did:peer layer (Layer 0).
|
|
5
|
+
* This is the initial layer for creating new assets with local control.
|
|
6
|
+
* No witnesses are required at this layer.
|
|
7
|
+
*
|
|
8
|
+
* @see https://identity.foundation/peer-did-method-spec/
|
|
9
|
+
*/
|
|
10
|
+
import type { EventLog, ExternalReference, DataIntegrityProof, AssetState } from '../types';
|
|
11
|
+
/**
|
|
12
|
+
* Configuration options for PeerCelManager
|
|
13
|
+
*/
|
|
14
|
+
export interface PeerCelConfig {
|
|
15
|
+
/** The DID URL of the verification method for signing */
|
|
16
|
+
verificationMethod?: string;
|
|
17
|
+
/** The purpose of proofs (defaults to 'assertionMethod') */
|
|
18
|
+
proofPurpose?: string;
|
|
19
|
+
/** Key type to use for DID generation (defaults to 'Ed25519') */
|
|
20
|
+
keyType?: 'Ed25519' | 'ES256K' | 'ES256';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Asset data stored in the create event
|
|
24
|
+
*/
|
|
25
|
+
export interface PeerAssetData {
|
|
26
|
+
/** Asset name */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Asset DID (did:peer) */
|
|
29
|
+
did: string;
|
|
30
|
+
/** Current layer */
|
|
31
|
+
layer: 'peer';
|
|
32
|
+
/** External resources associated with the asset */
|
|
33
|
+
resources: ExternalReference[];
|
|
34
|
+
/** Creator DID (same as asset DID for peer layer) */
|
|
35
|
+
creator: string;
|
|
36
|
+
/** ISO 8601 creation timestamp */
|
|
37
|
+
createdAt: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Signer function type that produces a DataIntegrityProof
|
|
41
|
+
*/
|
|
42
|
+
export type CelSigner = (data: unknown) => Promise<DataIntegrityProof>;
|
|
43
|
+
/**
|
|
44
|
+
* PeerCelManager - Manages CEL-based assets in the did:peer layer
|
|
45
|
+
*
|
|
46
|
+
* The peer layer is the initial layer for creating new Originals assets.
|
|
47
|
+
* Assets at this layer:
|
|
48
|
+
* - Have a did:peer identifier
|
|
49
|
+
* - Are controlled by the creator's key pair
|
|
50
|
+
* - Do not require witnesses (empty witness array)
|
|
51
|
+
* - Can be migrated to did:webvh or did:btco layers
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const manager = new PeerCelManager(async (data) => {
|
|
56
|
+
* // Sign with your private key
|
|
57
|
+
* return createEdDsaProof(data, privateKey);
|
|
58
|
+
* });
|
|
59
|
+
*
|
|
60
|
+
* const log = await manager.create('My Asset', [
|
|
61
|
+
* { digestMultibase: 'uXYZ...', mediaType: 'image/png' }
|
|
62
|
+
* ]);
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare class PeerCelManager {
|
|
66
|
+
private signer;
|
|
67
|
+
private config;
|
|
68
|
+
/**
|
|
69
|
+
* Creates a new PeerCelManager instance
|
|
70
|
+
*
|
|
71
|
+
* @param signer - Function that signs data and returns a DataIntegrityProof
|
|
72
|
+
* @param config - Optional configuration options
|
|
73
|
+
*/
|
|
74
|
+
constructor(signer: CelSigner, config?: PeerCelConfig);
|
|
75
|
+
/**
|
|
76
|
+
* Creates a new asset with a did:peer identifier and CEL event log
|
|
77
|
+
*
|
|
78
|
+
* This method:
|
|
79
|
+
* 1. Generates a new did:peer DID using the verification method from config
|
|
80
|
+
* 2. Creates a "create" event with the asset data
|
|
81
|
+
* 3. Signs the event using the provided signer
|
|
82
|
+
* 4. Returns an EventLog containing the initial create event
|
|
83
|
+
*
|
|
84
|
+
* @param name - Human-readable name for the asset
|
|
85
|
+
* @param resources - External resources associated with the asset
|
|
86
|
+
* @returns Promise resolving to an EventLog with the create event
|
|
87
|
+
*
|
|
88
|
+
* @throws Error if signer produces invalid proof
|
|
89
|
+
* @throws Error if DID generation fails
|
|
90
|
+
*/
|
|
91
|
+
create(name: string, resources: ExternalReference[]): Promise<EventLog>;
|
|
92
|
+
/**
|
|
93
|
+
* Generates a new did:peer DID (numalgo 4 - long form)
|
|
94
|
+
*
|
|
95
|
+
* Uses @aviarytech/did-peer library to create a numalgo 4 DID
|
|
96
|
+
* which embeds the full DID document for self-contained resolution.
|
|
97
|
+
*
|
|
98
|
+
* @returns Promise resolving to a did:peer string
|
|
99
|
+
*/
|
|
100
|
+
private generatePeerDid;
|
|
101
|
+
/**
|
|
102
|
+
* Generates a random Ed25519 public key for DID creation
|
|
103
|
+
*
|
|
104
|
+
* @returns Promise resolving to a multibase-encoded public key
|
|
105
|
+
*/
|
|
106
|
+
private generateRandomPublicKey;
|
|
107
|
+
/**
|
|
108
|
+
* Updates an existing event log by appending an update event.
|
|
109
|
+
*
|
|
110
|
+
* The new event is cryptographically linked to the previous event
|
|
111
|
+
* via a hash chain (previousEvent field).
|
|
112
|
+
*
|
|
113
|
+
* @param log - The existing event log to update
|
|
114
|
+
* @param data - The update data (new metadata, resources, etc.)
|
|
115
|
+
* @returns Promise resolving to a new EventLog with the update event appended
|
|
116
|
+
*
|
|
117
|
+
* @throws Error if the log is empty or deactivated
|
|
118
|
+
* @throws Error if signer produces invalid proof
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const updatedLog = await manager.update(log, {
|
|
123
|
+
* name: 'Renamed Asset',
|
|
124
|
+
* description: 'Updated description'
|
|
125
|
+
* });
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
update(log: EventLog, data: unknown): Promise<EventLog>;
|
|
129
|
+
/**
|
|
130
|
+
* Derives the current asset state by replaying all events in the log.
|
|
131
|
+
*
|
|
132
|
+
* This method:
|
|
133
|
+
* 1. Starts with the initial state from the create event
|
|
134
|
+
* 2. Applies each update event sequentially
|
|
135
|
+
* 3. Marks as deactivated if a deactivate event is present
|
|
136
|
+
*
|
|
137
|
+
* @param log - The event log to derive state from
|
|
138
|
+
* @returns The current AssetState derived from replaying events
|
|
139
|
+
*
|
|
140
|
+
* @throws Error if the log is empty
|
|
141
|
+
* @throws Error if the first event is not a create event
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const state = manager.getCurrentState(log);
|
|
146
|
+
* console.log(state.name); // Current asset name
|
|
147
|
+
* console.log(state.deactivated); // Whether asset is deactivated
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
getCurrentState(log: EventLog): AssetState;
|
|
151
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PeerCelManager - CEL Manager for did:peer Layer
|
|
3
|
+
*
|
|
4
|
+
* Creates and manages Originals assets in the did:peer layer (Layer 0).
|
|
5
|
+
* This is the initial layer for creating new assets with local control.
|
|
6
|
+
* No witnesses are required at this layer.
|
|
7
|
+
*
|
|
8
|
+
* @see https://identity.foundation/peer-did-method-spec/
|
|
9
|
+
*/
|
|
10
|
+
import { createEventLog } from '../algorithms/createEventLog';
|
|
11
|
+
import { updateEventLog } from '../algorithms/updateEventLog';
|
|
12
|
+
/**
|
|
13
|
+
* PeerCelManager - Manages CEL-based assets in the did:peer layer
|
|
14
|
+
*
|
|
15
|
+
* The peer layer is the initial layer for creating new Originals assets.
|
|
16
|
+
* Assets at this layer:
|
|
17
|
+
* - Have a did:peer identifier
|
|
18
|
+
* - Are controlled by the creator's key pair
|
|
19
|
+
* - Do not require witnesses (empty witness array)
|
|
20
|
+
* - Can be migrated to did:webvh or did:btco layers
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const manager = new PeerCelManager(async (data) => {
|
|
25
|
+
* // Sign with your private key
|
|
26
|
+
* return createEdDsaProof(data, privateKey);
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* const log = await manager.create('My Asset', [
|
|
30
|
+
* { digestMultibase: 'uXYZ...', mediaType: 'image/png' }
|
|
31
|
+
* ]);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class PeerCelManager {
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new PeerCelManager instance
|
|
37
|
+
*
|
|
38
|
+
* @param signer - Function that signs data and returns a DataIntegrityProof
|
|
39
|
+
* @param config - Optional configuration options
|
|
40
|
+
*/
|
|
41
|
+
constructor(signer, config = {}) {
|
|
42
|
+
if (typeof signer !== 'function') {
|
|
43
|
+
throw new Error('PeerCelManager requires a signer function');
|
|
44
|
+
}
|
|
45
|
+
this.signer = signer;
|
|
46
|
+
this.config = {
|
|
47
|
+
proofPurpose: 'assertionMethod',
|
|
48
|
+
keyType: 'Ed25519',
|
|
49
|
+
...config,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new asset with a did:peer identifier and CEL event log
|
|
54
|
+
*
|
|
55
|
+
* This method:
|
|
56
|
+
* 1. Generates a new did:peer DID using the verification method from config
|
|
57
|
+
* 2. Creates a "create" event with the asset data
|
|
58
|
+
* 3. Signs the event using the provided signer
|
|
59
|
+
* 4. Returns an EventLog containing the initial create event
|
|
60
|
+
*
|
|
61
|
+
* @param name - Human-readable name for the asset
|
|
62
|
+
* @param resources - External resources associated with the asset
|
|
63
|
+
* @returns Promise resolving to an EventLog with the create event
|
|
64
|
+
*
|
|
65
|
+
* @throws Error if signer produces invalid proof
|
|
66
|
+
* @throws Error if DID generation fails
|
|
67
|
+
*/
|
|
68
|
+
async create(name, resources) {
|
|
69
|
+
// Validate inputs
|
|
70
|
+
if (!name || typeof name !== 'string') {
|
|
71
|
+
throw new Error('Asset name is required and must be a string');
|
|
72
|
+
}
|
|
73
|
+
if (!Array.isArray(resources)) {
|
|
74
|
+
throw new Error('Resources must be an array');
|
|
75
|
+
}
|
|
76
|
+
// Generate did:peer DID for this asset
|
|
77
|
+
const did = await this.generatePeerDid();
|
|
78
|
+
// Prepare asset data for the create event
|
|
79
|
+
const assetData = {
|
|
80
|
+
name,
|
|
81
|
+
did,
|
|
82
|
+
layer: 'peer',
|
|
83
|
+
resources,
|
|
84
|
+
creator: did, // Creator is the same as asset DID for peer layer
|
|
85
|
+
createdAt: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
|
+
// Build create options
|
|
88
|
+
const createOptions = {
|
|
89
|
+
signer: this.signer,
|
|
90
|
+
verificationMethod: this.config.verificationMethod || `${did}#key-0`,
|
|
91
|
+
proofPurpose: this.config.proofPurpose,
|
|
92
|
+
};
|
|
93
|
+
// Create the event log with a create event
|
|
94
|
+
const eventLog = await createEventLog(assetData, createOptions);
|
|
95
|
+
return eventLog;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Generates a new did:peer DID (numalgo 4 - long form)
|
|
99
|
+
*
|
|
100
|
+
* Uses @aviarytech/did-peer library to create a numalgo 4 DID
|
|
101
|
+
* which embeds the full DID document for self-contained resolution.
|
|
102
|
+
*
|
|
103
|
+
* @returns Promise resolving to a did:peer string
|
|
104
|
+
*/
|
|
105
|
+
async generatePeerDid() {
|
|
106
|
+
// Dynamically import did-peer library
|
|
107
|
+
let didPeerMod;
|
|
108
|
+
try {
|
|
109
|
+
didPeerMod = await import('@aviarytech/did-peer');
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
throw new Error('Failed to import @aviarytech/did-peer. Make sure it is installed: npm install @aviarytech/did-peer');
|
|
113
|
+
}
|
|
114
|
+
// If we have a verification method with a public key, use it
|
|
115
|
+
// Otherwise, generate a placeholder DID using a random key
|
|
116
|
+
// In production, the signer's verification method should be used
|
|
117
|
+
// For did:peer numalgo 4, we need a verification method
|
|
118
|
+
// The verification method should come from the config or be derived from the signer
|
|
119
|
+
// Generate a DID using numalgo 4 (long-form with embedded DID document)
|
|
120
|
+
// We'll use a simple verification method structure
|
|
121
|
+
let publicKeyMultibase;
|
|
122
|
+
// If verificationMethod is provided and contains a key reference, extract it
|
|
123
|
+
if (this.config.verificationMethod && this.config.verificationMethod.includes('did:key:')) {
|
|
124
|
+
// Extract the public key from did:key format
|
|
125
|
+
const keyMatch = this.config.verificationMethod.match(/did:key:(z[a-zA-Z0-9]+)/);
|
|
126
|
+
publicKeyMultibase = keyMatch ? keyMatch[1] : await this.generateRandomPublicKey();
|
|
127
|
+
}
|
|
128
|
+
else if (this.config.verificationMethod && this.config.verificationMethod.includes('#')) {
|
|
129
|
+
// If it's a fragment reference, we need to generate a key
|
|
130
|
+
publicKeyMultibase = await this.generateRandomPublicKey();
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Generate a random public key for the DID
|
|
134
|
+
publicKeyMultibase = await this.generateRandomPublicKey();
|
|
135
|
+
}
|
|
136
|
+
// Create did:peer using numalgo 4
|
|
137
|
+
const did = await didPeerMod.createNumAlgo4([
|
|
138
|
+
{
|
|
139
|
+
type: 'Multikey',
|
|
140
|
+
publicKeyMultibase,
|
|
141
|
+
}
|
|
142
|
+
], undefined, // services
|
|
143
|
+
undefined // alsoKnownAs
|
|
144
|
+
);
|
|
145
|
+
return did;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Generates a random Ed25519 public key for DID creation
|
|
149
|
+
*
|
|
150
|
+
* @returns Promise resolving to a multibase-encoded public key
|
|
151
|
+
*/
|
|
152
|
+
async generateRandomPublicKey() {
|
|
153
|
+
// Use @noble/ed25519 for key generation
|
|
154
|
+
const ed25519 = await import('@noble/ed25519');
|
|
155
|
+
const privateKeyBytes = ed25519.utils.randomPrivateKey();
|
|
156
|
+
const publicKeyBytes = await ed25519.getPublicKeyAsync(privateKeyBytes);
|
|
157
|
+
// Import multikey encoder
|
|
158
|
+
const { multikey } = await import('../../crypto/Multikey');
|
|
159
|
+
return multikey.encodePublicKey(publicKeyBytes, 'Ed25519');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Updates an existing event log by appending an update event.
|
|
163
|
+
*
|
|
164
|
+
* The new event is cryptographically linked to the previous event
|
|
165
|
+
* via a hash chain (previousEvent field).
|
|
166
|
+
*
|
|
167
|
+
* @param log - The existing event log to update
|
|
168
|
+
* @param data - The update data (new metadata, resources, etc.)
|
|
169
|
+
* @returns Promise resolving to a new EventLog with the update event appended
|
|
170
|
+
*
|
|
171
|
+
* @throws Error if the log is empty or deactivated
|
|
172
|
+
* @throws Error if signer produces invalid proof
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* const updatedLog = await manager.update(log, {
|
|
177
|
+
* name: 'Renamed Asset',
|
|
178
|
+
* description: 'Updated description'
|
|
179
|
+
* });
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
async update(log, data) {
|
|
183
|
+
// Validate input log
|
|
184
|
+
if (!log || !log.events || log.events.length === 0) {
|
|
185
|
+
throw new Error('Cannot update an empty event log');
|
|
186
|
+
}
|
|
187
|
+
// Check if log is already deactivated
|
|
188
|
+
const lastEvent = log.events[log.events.length - 1];
|
|
189
|
+
if (lastEvent.type === 'deactivate') {
|
|
190
|
+
throw new Error('Cannot update a deactivated event log');
|
|
191
|
+
}
|
|
192
|
+
// Get the DID from the create event for verification method construction
|
|
193
|
+
const createData = log.events[0].data;
|
|
194
|
+
const did = createData.did;
|
|
195
|
+
// Build update options using the same signer
|
|
196
|
+
const updateOptions = {
|
|
197
|
+
signer: this.signer,
|
|
198
|
+
verificationMethod: this.config.verificationMethod || `${did}#key-0`,
|
|
199
|
+
proofPurpose: this.config.proofPurpose,
|
|
200
|
+
};
|
|
201
|
+
// Add timestamp to update data
|
|
202
|
+
const updateData = {
|
|
203
|
+
...((typeof data === 'object' && data !== null) ? data : { value: data }),
|
|
204
|
+
updatedAt: new Date().toISOString(),
|
|
205
|
+
};
|
|
206
|
+
// Delegate to updateEventLog algorithm
|
|
207
|
+
return updateEventLog(log, updateData, updateOptions);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Derives the current asset state by replaying all events in the log.
|
|
211
|
+
*
|
|
212
|
+
* This method:
|
|
213
|
+
* 1. Starts with the initial state from the create event
|
|
214
|
+
* 2. Applies each update event sequentially
|
|
215
|
+
* 3. Marks as deactivated if a deactivate event is present
|
|
216
|
+
*
|
|
217
|
+
* @param log - The event log to derive state from
|
|
218
|
+
* @returns The current AssetState derived from replaying events
|
|
219
|
+
*
|
|
220
|
+
* @throws Error if the log is empty
|
|
221
|
+
* @throws Error if the first event is not a create event
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```typescript
|
|
225
|
+
* const state = manager.getCurrentState(log);
|
|
226
|
+
* console.log(state.name); // Current asset name
|
|
227
|
+
* console.log(state.deactivated); // Whether asset is deactivated
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
getCurrentState(log) {
|
|
231
|
+
// Validate input log
|
|
232
|
+
if (!log || !log.events || log.events.length === 0) {
|
|
233
|
+
throw new Error('Cannot get state from an empty event log');
|
|
234
|
+
}
|
|
235
|
+
// First event must be a create event
|
|
236
|
+
const createEvent = log.events[0];
|
|
237
|
+
if (createEvent.type !== 'create') {
|
|
238
|
+
throw new Error('First event must be a create event');
|
|
239
|
+
}
|
|
240
|
+
// Extract initial state from create event
|
|
241
|
+
const createData = createEvent.data;
|
|
242
|
+
// Initialize state from create event
|
|
243
|
+
const state = {
|
|
244
|
+
did: createData.did,
|
|
245
|
+
name: createData.name,
|
|
246
|
+
layer: createData.layer,
|
|
247
|
+
resources: [...createData.resources],
|
|
248
|
+
creator: createData.creator,
|
|
249
|
+
createdAt: createData.createdAt,
|
|
250
|
+
updatedAt: undefined,
|
|
251
|
+
deactivated: false,
|
|
252
|
+
metadata: {},
|
|
253
|
+
};
|
|
254
|
+
// Apply subsequent events
|
|
255
|
+
for (let i = 1; i < log.events.length; i++) {
|
|
256
|
+
const event = log.events[i];
|
|
257
|
+
if (event.type === 'update') {
|
|
258
|
+
// Merge update data into state
|
|
259
|
+
const updateData = event.data;
|
|
260
|
+
// Update specific known fields
|
|
261
|
+
if (updateData.name !== undefined) {
|
|
262
|
+
state.name = updateData.name;
|
|
263
|
+
}
|
|
264
|
+
if (updateData.resources !== undefined) {
|
|
265
|
+
state.resources = updateData.resources;
|
|
266
|
+
}
|
|
267
|
+
if (updateData.updatedAt !== undefined) {
|
|
268
|
+
state.updatedAt = updateData.updatedAt;
|
|
269
|
+
}
|
|
270
|
+
if (updateData.did !== undefined) {
|
|
271
|
+
state.did = updateData.did;
|
|
272
|
+
}
|
|
273
|
+
if (updateData.layer !== undefined) {
|
|
274
|
+
state.layer = updateData.layer;
|
|
275
|
+
}
|
|
276
|
+
// Store other fields in metadata
|
|
277
|
+
for (const [key, value] of Object.entries(updateData)) {
|
|
278
|
+
if (!['name', 'resources', 'updatedAt', 'did', 'layer', 'creator', 'createdAt'].includes(key)) {
|
|
279
|
+
state.metadata = state.metadata || {};
|
|
280
|
+
state.metadata[key] = value;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else if (event.type === 'deactivate') {
|
|
285
|
+
state.deactivated = true;
|
|
286
|
+
// Extract deactivation details
|
|
287
|
+
const deactivateData = event.data;
|
|
288
|
+
if (deactivateData.deactivatedAt !== undefined) {
|
|
289
|
+
state.updatedAt = deactivateData.deactivatedAt;
|
|
290
|
+
}
|
|
291
|
+
if (deactivateData.reason !== undefined) {
|
|
292
|
+
state.metadata = state.metadata || {};
|
|
293
|
+
state.metadata.deactivationReason = deactivateData.reason;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return state;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebVHCelManager - CEL Manager for did:webvh Layer
|
|
3
|
+
*
|
|
4
|
+
* Manages migration of Originals assets to the did:webvh layer (Layer 1).
|
|
5
|
+
* This layer provides HTTP-based witnessing for verifiable history.
|
|
6
|
+
*
|
|
7
|
+
* The did:webvh (Web Verifiable History) method combines domain-based
|
|
8
|
+
* resolution with cryptographic event logging for discoverable assets.
|
|
9
|
+
*
|
|
10
|
+
* @see https://identity.foundation/didwebvh/
|
|
11
|
+
*/
|
|
12
|
+
import type { EventLog, AssetState } from '../types';
|
|
13
|
+
import type { WitnessService } from '../witnesses/WitnessService';
|
|
14
|
+
import type { CelSigner } from './PeerCelManager';
|
|
15
|
+
/**
|
|
16
|
+
* Configuration options for WebVHCelManager
|
|
17
|
+
*/
|
|
18
|
+
export interface WebVHCelConfig {
|
|
19
|
+
/** The DID URL of the verification method for signing */
|
|
20
|
+
verificationMethod?: string;
|
|
21
|
+
/** The purpose of proofs (defaults to 'assertionMethod') */
|
|
22
|
+
proofPurpose?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Migration data stored in the update event when migrating to webvh
|
|
26
|
+
*/
|
|
27
|
+
export interface WebVHMigrationData {
|
|
28
|
+
/** The source DID from the previous layer */
|
|
29
|
+
sourceDid: string;
|
|
30
|
+
/** The new did:webvh DID */
|
|
31
|
+
targetDid: string;
|
|
32
|
+
/** The target layer */
|
|
33
|
+
layer: 'webvh';
|
|
34
|
+
/** The domain hosting the did:webvh */
|
|
35
|
+
domain: string;
|
|
36
|
+
/** ISO 8601 timestamp of migration */
|
|
37
|
+
migratedAt: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* WebVHCelManager - Manages CEL-based asset migration to did:webvh layer
|
|
41
|
+
*
|
|
42
|
+
* The webvh layer is the first publication layer for Originals assets.
|
|
43
|
+
* Assets at this layer:
|
|
44
|
+
* - Have a did:webvh identifier based on a domain
|
|
45
|
+
* - Can have HTTP-based witness attestations
|
|
46
|
+
* - Are discoverable via web-based DID resolution
|
|
47
|
+
* - Can be further migrated to did:btco layer
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const httpWitness = new HttpWitness('https://witness.example.com/api/attest');
|
|
52
|
+
* const manager = new WebVHCelManager(
|
|
53
|
+
* async (data) => createEdDsaProof(data, privateKey),
|
|
54
|
+
* 'example.com',
|
|
55
|
+
* [httpWitness]
|
|
56
|
+
* );
|
|
57
|
+
*
|
|
58
|
+
* const webvhLog = await manager.migrate(peerLog);
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare class WebVHCelManager {
|
|
62
|
+
private signer;
|
|
63
|
+
private domain;
|
|
64
|
+
private witnesses;
|
|
65
|
+
private config;
|
|
66
|
+
/**
|
|
67
|
+
* Creates a new WebVHCelManager instance
|
|
68
|
+
*
|
|
69
|
+
* @param signer - Function that signs data and returns a DataIntegrityProof
|
|
70
|
+
* @param domain - The domain for the did:webvh DID (e.g., 'example.com')
|
|
71
|
+
* @param witnesses - Optional array of witness services for attestations
|
|
72
|
+
* @param config - Optional configuration options
|
|
73
|
+
*/
|
|
74
|
+
constructor(signer: CelSigner, domain: string, witnesses?: WitnessService[], config?: WebVHCelConfig);
|
|
75
|
+
/**
|
|
76
|
+
* Migrates a peer layer event log to the did:webvh layer
|
|
77
|
+
*
|
|
78
|
+
* This method:
|
|
79
|
+
* 1. Validates the input log (must have a create event with peer layer data)
|
|
80
|
+
* 2. Generates a new did:webvh DID based on the domain
|
|
81
|
+
* 3. Creates an update event with migration data
|
|
82
|
+
* 4. Optionally adds witness proofs from configured witnesses
|
|
83
|
+
* 5. Returns the updated EventLog
|
|
84
|
+
*
|
|
85
|
+
* @param peerLog - The event log from the peer layer to migrate
|
|
86
|
+
* @returns Promise resolving to an EventLog with the migration event appended
|
|
87
|
+
*
|
|
88
|
+
* @throws Error if the log is empty, deactivated, or not from peer layer
|
|
89
|
+
* @throws Error if signer produces invalid proof
|
|
90
|
+
* @throws Error if witness service fails (if witnesses configured)
|
|
91
|
+
*/
|
|
92
|
+
migrate(peerLog: EventLog): Promise<EventLog>;
|
|
93
|
+
/**
|
|
94
|
+
* Generates a did:webvh DID for this domain
|
|
95
|
+
*
|
|
96
|
+
* The did:webvh format is: did:webvh:{domain}:{id}
|
|
97
|
+
* where {id} is derived from the source DID to maintain linkage.
|
|
98
|
+
*
|
|
99
|
+
* @param sourceDid - The source DID to derive the webvh DID from
|
|
100
|
+
* @returns A did:webvh string
|
|
101
|
+
*/
|
|
102
|
+
private generateWebVHDid;
|
|
103
|
+
/**
|
|
104
|
+
* Creates a URL-safe hash-based identifier from a string
|
|
105
|
+
*/
|
|
106
|
+
private hashIdentifier;
|
|
107
|
+
/**
|
|
108
|
+
* Derives the current asset state by replaying all events in the log.
|
|
109
|
+
*
|
|
110
|
+
* @param log - The event log to derive state from
|
|
111
|
+
* @returns The current AssetState derived from replaying events
|
|
112
|
+
*/
|
|
113
|
+
getCurrentState(log: EventLog): AssetState;
|
|
114
|
+
/**
|
|
115
|
+
* Gets the domain this manager is configured for
|
|
116
|
+
*/
|
|
117
|
+
get domainName(): string;
|
|
118
|
+
/**
|
|
119
|
+
* Gets the number of configured witnesses
|
|
120
|
+
*/
|
|
121
|
+
get witnessCount(): number;
|
|
122
|
+
}
|