@cambrianprotocol/stream-caip10-link-handler
Version:
Ceramic AccountLink stream handler
93 lines • 3.96 kB
JavaScript
import { validateLink } from '@ceramicnetwork/blockchain-utils-validation';
import { Caip10Link } from '@ceramicnetwork/stream-caip10-link';
import { AnchorStatus, CommitType, SignatureStatus, StreamUtils, toLegacyAccountId, } from '@cambrianprotocol/common';
import { applyAnchorCommit } from '@ceramicnetwork/stream-handler-common';
export class Caip10LinkHandler {
get type() {
return Caip10Link.STREAM_TYPE_ID;
}
get name() {
return Caip10Link.STREAM_TYPE_NAME;
}
get stream_constructor() {
return Caip10Link;
}
async applyCommit(commitData, context, state) {
if (state == null) {
return this._applyGenesis(commitData);
}
if (StreamUtils.isAnchorCommitData(commitData)) {
return this._applyAnchor(commitData, state);
}
return this._applySigned(commitData, state);
}
async _applyGenesis(commitData) {
const commit = commitData.commit;
if (commit.data) {
throw new Error('Caip10Link genesis commit cannot have data');
}
const metadata = commit.header;
if (!(metadata.controllers && metadata.controllers.length === 1)) {
throw new Error('Exactly one controller must be specified');
}
const state = {
type: Caip10Link.STREAM_TYPE_ID,
content: null,
next: {
content: null,
},
metadata,
signature: SignatureStatus.GENESIS,
anchorStatus: AnchorStatus.NOT_REQUESTED,
log: [{ cid: commitData.cid, type: CommitType.GENESIS }],
};
return state;
}
async _applySigned(commitData, state) {
const commit = commitData.commit;
StreamUtils.assertCommitLinksToState(state, commit);
let validProof = null;
try {
validProof = await validateLink(commit.data);
}
catch (e) {
throw new Error('Error while validating link proof for caip10-link signed commit: ' + e.toString());
}
if (!validProof) {
throw new Error('Invalid proof for signed commit');
}
if (state.signature !== SignatureStatus.GENESIS &&
((state.anchorStatus === AnchorStatus.ANCHORED &&
validProof.timestamp < StreamUtils.anchorTimestampFromState(state)) ||
(state.anchorStatus !== AnchorStatus.ANCHORED &&
validProof.timestamp < state.next.metadata.lastUpdate))) {
throw new Error('Invalid commit, proof timestamp too old');
}
const account = validProof.account || validProof.address;
const legacyAccountCaip10 = toLegacyAccountId(account);
const legacyControllerCaip10 = state.metadata.controllers[0];
if (!legacyControllerCaip10.includes('@')) {
throw new Error('Controller is not following the legacy CAIP10 format. Unexpected error.');
}
if (legacyAccountCaip10.toLowerCase() !== legacyControllerCaip10.toLowerCase()) {
throw new Error(`Address '${legacyAccountCaip10.toLowerCase()}' used to sign update to Caip10Link doesn't match stream controller '${legacyControllerCaip10.toLowerCase()}'`);
}
state.log.push({ cid: commitData.cid, type: CommitType.SIGNED });
return {
...state,
signature: SignatureStatus.SIGNED,
anchorStatus: AnchorStatus.NOT_REQUESTED,
next: {
content: validProof.did,
metadata: {
...state.metadata,
lastUpdate: validProof.timestamp,
},
},
};
}
async _applyAnchor(commitData, state) {
return applyAnchorCommit(commitData, state);
}
}
//# sourceMappingURL=caip10-link-handler.js.map