Skip to content

Commit

Permalink
feat(ref-imp): #781 - add anchor file mode
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacJChen committed Sep 23, 2020
1 parent 896a808 commit d964c92
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 22 deletions.
44 changes: 28 additions & 16 deletions lib/core/versions/latest/CreateOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,27 +102,30 @@ export default class CreateOperation implements OperationModel {
// TODO: SIP 2 #781 deprecates this. Should be deleted when fully switched over
createOperation = await CreateOperation.parseObject(operationObject, operationBuffer, false);
} else {
createOperation = CreateOperation.parseJcsObject(operationObject, operationBuffer);
createOperation = CreateOperation.parseJcsObject(operationObject, operationBuffer, false);
}
return createOperation;
}

/**
* Parse the given operation object as a CreateOperation
* Parses the given operation object as a `CreateOperation`.
* The `operationBuffer` given is assumed to be valid and is assigned to the `operationBuffer` directly.
* NOTE: This method is purely intended to be used as an optimization method over the `parse` method in that
* JSON parsing is not required to be performed more than once when an operation buffer of an unknown operation type is given.
* @param operationObject The operationObject is a json object with no encoding
* @param operationBuffer The buffer format of the operationObject
* @param anchorFileMode If set to true, then `delta` and `type` properties are expected to be absent.
*/
public static parseJcsObject (operationObject: any, operationBuffer: Buffer): CreateOperation {
public static parseJcsObject (operationObject: any, operationBuffer: Buffer, anchorFileMode: boolean): CreateOperation {
let expectedPropertyCount = 3;
if (anchorFileMode) {
expectedPropertyCount = 1;
}
const properties = Object.keys(operationObject);
if (properties.length !== expectedPropertyCount) {
throw new SidetreeError(ErrorCode.CreateOperationMissingOrUnknownProperty);
}

if (operationObject.type !== OperationType.Create) {
throw new SidetreeError(ErrorCode.CreateOperationTypeIncorrect);
}

CreateOperation.validateSuffixData(operationObject.suffix_data);
const suffixData: SuffixDataModel = {
deltaHash: operationObject.suffix_data.delta_hash,
Expand All @@ -137,20 +140,29 @@ export default class CreateOperation implements OperationModel {
// thus an operation with invalid `delta` needs to be processed as an operation with unavailable `delta`,
// so here we let `delta` be `undefined`.
let delta;
try {
Operation.validateDelta(operationObject.delta);
delta = {
patches: operationObject.delta.patches,
updateCommitment: operationObject.delta.update_commitment
};
} catch {
delta = undefined;
let encodedDelta;
if (!anchorFileMode) {
if (operationObject.type !== OperationType.Create) {
throw new SidetreeError(ErrorCode.CreateOperationTypeIncorrect);
}

try {
Operation.validateDelta(operationObject.delta);
delta = {
patches: operationObject.delta.patches,
updateCommitment: operationObject.delta.update_commitment
};
} catch {
// For compatibility with data pruning, we have to assume that `delta` may be unavailable,
// thus an operation with invalid `delta` needs to be processed as an operation with unavailable `delta`,
// so here we let `delta` be `undefined`.
}
encodedDelta = Encoder.encode(JsonCanonicalizer.canonicalizeAsBuffer(operationObject.delta));
}

const didUniqueSuffix = CreateOperation.computeJcsDidUniqueSuffix(operationObject.suffix_data);

const encodedSuffixData = Encoder.encode(JsonCanonicalizer.canonicalizeAsBuffer(operationObject.suffix_data));
const encodedDelta = Encoder.encode(JsonCanonicalizer.canonicalizeAsBuffer(operationObject.delta));
return new CreateOperation(operationBuffer, didUniqueSuffix, encodedSuffixData, suffixData, encodedDelta, delta);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/core/versions/latest/Did.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export default class Did {
delta: initialStateObject.delta
};
const createOperationBuffer = Buffer.from(JSON.stringify(createOperationRequest));
const createOperation = CreateOperation.parseJcsObject(createOperationRequest, createOperationBuffer);
const createOperation = CreateOperation.parseJcsObject(createOperationRequest, createOperationBuffer, false);
return createOperation;
}

Expand Down
58 changes: 53 additions & 5 deletions tests/core/CreateOperation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,38 +27,86 @@ describe('CreateOperation', async () => {
// do nothing
});

const result = CreateOperation.parseJcsObject(operationObject, Buffer.from('something'));
const result = CreateOperation.parseJcsObject(operationObject, Buffer.from('something'), false);
expect(result.delta).toBeUndefined();
});

it('should process as anchor file mode when anchorFileMode is true', () => {
const operationObject = {
suffix_data: {
delta_hash: 'something',
recovery_commitment: 'something',
type: 'type'
}
};

spyOn(CreateOperation as any, 'validateSuffixData').and.callFake(() => {
// do nothing
});

const result = CreateOperation.parseJcsObject(operationObject, Buffer.from('something'), true);
expect(result.delta).toBeUndefined();
expect(result.suffixData).toBeDefined();
});

it('should throw sidetree error if object contains more or less than 3 properties', () => {
const twoProperties = { one: 1, two: 2 };
const fourProperties = { one: 1, two: 2, three: 3, four: 4 };

try {
CreateOperation.parseJcsObject(twoProperties, Buffer.from(JSON.stringify(twoProperties)));
CreateOperation.parseJcsObject(twoProperties, Buffer.from(JSON.stringify(twoProperties)), false);
fail('expect to throw sidetree error but did not');
} catch (e) {
expect(e).toEqual(new SidetreeError(ErrorCode.CreateOperationMissingOrUnknownProperty));
}

try {
CreateOperation.parseJcsObject(fourProperties, Buffer.from(JSON.stringify(fourProperties)));
CreateOperation.parseJcsObject(fourProperties, Buffer.from(JSON.stringify(fourProperties)), false);
fail('expect to throw sidetree error but did not');
} catch (e) {
expect(e).toEqual(new SidetreeError(ErrorCode.CreateOperationMissingOrUnknownProperty));
}
});

it('should throw sidetree error if type is not create', () => {
const testObject = { type: 'notCreate', two: 2, three: 3 };
const testObject = {
type: 'notCreate',
suffix_data: {
delta_hash: 'something',
recovery_commitment: 'something',
type: 'type'
},
delta: 'something'
};

spyOn(CreateOperation as any, 'validateSuffixData').and.callFake(() => {
// do nothing
});

try {
CreateOperation.parseJcsObject(testObject, Buffer.from(JSON.stringify(testObject)));
CreateOperation.parseJcsObject(testObject, Buffer.from(JSON.stringify(testObject)), false);
fail('expect to throw sidetree error but did not');
} catch (e) {
expect(e).toEqual(new SidetreeError(ErrorCode.CreateOperationTypeIncorrect));
}
});

it('should throw sidetree error if has more or less than 1 property when in anchor file mode', () => {
const testObject = {
type: 'this should not exist',
suffix_data: {
delta_hash: 'something',
recovery_commitment: 'something',
type: 'type'
}
};
try {
CreateOperation.parseJcsObject(testObject, Buffer.from(JSON.stringify(testObject)), true);
fail('expect to throw sidetree error but did not');
} catch (e) {
expect(e).toEqual(new SidetreeError(ErrorCode.CreateOperationMissingOrUnknownProperty));
}
})
});

describe('computeJcsDidUniqueSuffix', () => {
Expand Down

0 comments on commit d964c92

Please sign in to comment.