Skip to content

Commit

Permalink
fix: optional fields not populated if wire type has additional fields (
Browse files Browse the repository at this point in the history
…#627)

* fix: optional fields not populated if wire type has additional fields

* update changelog

* improve test description

* Simplify field names in test
  • Loading branch information
hpeebles committed Sep 20, 2022
1 parent 513f9b3 commit d061f4e
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 12 deletions.
4 changes: 4 additions & 0 deletions docs/generated/changelog.html
Expand Up @@ -39,6 +39,10 @@ <h2>Version 0.13.3</h2>
This can be used to set proper Expiry times when a device has fallen out of sync with the
replica.
</li>
<li>
Fixes a candid bug where when decoding, optional fields could be skipped if the data on
the wire contains additional fields.
</li>
</ul>
<h2>Version 0.13.2</h2>
<ul>
Expand Down
17 changes: 17 additions & 0 deletions packages/candid/src/idl.test.ts
Expand Up @@ -660,3 +660,20 @@ test('should correctly decode expected optional fields with lower hash than requ
upgrade: [true],
});
});

test('should decode matching optional fields if wire type contains additional fields', () => {
const InputType = IDL.Record({
a: IDL.Text,
b: IDL.Opt(IDL.Text),
});
const OutputType = IDL.Record({
b: IDL.Opt(IDL.Text),
});

const encoded = IDL.encode([InputType], [{ a: 'abc', b: ['123'] }]);
const decoded = IDL.decode([OutputType], encoded)[0];

expect(decoded).toEqual({
b: ['123'],
});
});
25 changes: 13 additions & 12 deletions packages/candid/src/idl.ts
Expand Up @@ -991,25 +991,26 @@ export class RecordClass extends ConstructType<Record<string, any>> {
}

const [expectKey, expectType] = this._fields[expectedRecordIdx];
if (idlLabelToId(this._fields[expectedRecordIdx][0]) !== idlLabelToId(hash)) {
// the current field on the wire does not match the expected field

// skip expected optional fields that are not present on the wire
const expectedId = idlLabelToId(this._fields[expectedRecordIdx][0]);
const actualId = idlLabelToId(hash);
if (expectedId === actualId) {
// the current field on the wire matches the expected field
x[expectKey] = expectType.decodeValue(b, type);
expectedRecordIdx++;
actualRecordIdx++;
} else if (actualId > expectedId) {
// The expected field does not exist on the wire
if (expectType instanceof OptClass || expectType instanceof ReservedClass) {
x[expectKey] = [];
expectedRecordIdx++;
continue;
} else {
throw new Error('Cannot find required field ' + expectKey);
}

// skip unexpected interspersed fields present on the wire
} else {
// The field on the wire does not exist in the output type, so we can skip it
type.decodeValue(b, type);
actualRecordIdx++;
continue;
}

x[expectKey] = expectType.decodeValue(b, type);
expectedRecordIdx++;
actualRecordIdx++;
}

// initialize left over expected optional fields
Expand Down

0 comments on commit d061f4e

Please sign in to comment.