Skip to content

Commit 8c4475d

Browse files
committed
Support for v1 of the offchain message specification
1 parent de974db commit 8c4475d

19 files changed

+699
-45
lines changed

packages/offchain-messages/src/__tests__/envelope-codec-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ describe('getOffchainMessageEnvelopeDecoder()', () => {
260260
);
261261
});
262262
it.each(
263-
Array.from({ length: 255 - 0 /* highest supported version */ })
263+
Array.from({ length: 255 - 1 /* highest supported version */ })
264264
.map((_, ii) => 255 - ii)
265265
.reverse(),
266266
)('throws if the preamble is of version `%s`', putativeVersion => {
@@ -451,7 +451,7 @@ describe('getOffchainMessageEnvelopeEncoder()', () => {
451451
);
452452
});
453453
it.each(
454-
Array.from({ length: 255 - 0 /* highest supported version */ })
454+
Array.from({ length: 255 - 1 /* highest supported version */ })
455455
.map((_, ii) => 255 - ii)
456456
.reverse(),
457457
)('throws if the envelope is of version `%s`', putativeVersion => {

packages/offchain-messages/src/__tests__/message-codec-test.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import { getOffchainMessageDecoder, getOffchainMessageEncoder } from '../codecs/
55
import { getOffchainMessageV0Decoder, getOffchainMessageV0Encoder } from '../codecs/message-v0';
66
import { OffchainMessage } from '../message';
77
import { OffchainMessageV0 } from '../message-v0';
8+
import { getOffchainMessageV1Decoder, getOffchainMessageV1Encoder } from '../codecs/message-v1';
9+
import { OffchainMessageV1 } from '../message-v1';
810

911
jest.mock('../codecs/message-v0');
12+
jest.mock('../codecs/message-v1');
1013

1114
// The string `'\xffsolana offchain'`
1215
const OFFCHAIN_MESSAGE_SIGNING_DOMAIN_BYTES: ReadonlyUint8Array = new Uint8Array([
@@ -44,8 +47,34 @@ describe('getOffchainMessageDecoder', () => {
4447
expect(mockReader).toHaveBeenCalledWith(encodedMessage, 1);
4548
expect(result).toBe(expectedResult);
4649
});
50+
it('delegates to the version 1 message decoder when the preamble is of version 1', () => {
51+
const expectedResult = 123;
52+
const mockDecoder = jest.fn().mockReturnValue(expectedResult);
53+
const mockReader = jest.fn().mockReturnValue([
54+
expectedResult,
55+
OFFCHAIN_MESSAGE_SIGNING_DOMAIN_BYTES.byteLength +
56+
// Version
57+
1 +
58+
// Padding
59+
1,
60+
]);
61+
jest.mocked(getOffchainMessageV1Decoder).mockReturnValue({ decode: mockDecoder, read: mockReader });
62+
const encodedMessage =
63+
// prettier-ignore
64+
new Uint8Array([
65+
// Padding
66+
0xff,
67+
// Signing domain
68+
...OFFCHAIN_MESSAGE_SIGNING_DOMAIN_BYTES,
69+
// Version
70+
0x01,
71+
]);
72+
const result = decoder.decode(encodedMessage, 1);
73+
expect(mockReader).toHaveBeenCalledWith(encodedMessage, 1);
74+
expect(result).toBe(expectedResult);
75+
});
4776
it.each(
48-
Array.from({ length: 255 - 0 /* highest supported version */ })
77+
Array.from({ length: 255 - 1 /* highest supported version */ })
4978
.map((_, ii) => 255 - ii)
5079
.reverse(),
5180
)('throws if the preamble is of version `%s`', putativeVersion => {
@@ -86,8 +115,22 @@ describe('getOffchainMessageEncoder', () => {
86115
expect(mockWriter).toHaveBeenCalledWith(mockMessage, expect.any(Uint8Array), 0 /* offset */);
87116
expect(result).toStrictEqual(new Uint8Array([1, 2, 3]));
88117
});
118+
it('delegates to the version 1 message encoder when the message is of version 1', () => {
119+
const mockMessage = { version: 1 };
120+
const mockWriter = jest.fn().mockImplementation((_message, bytes, offset) => {
121+
bytes.set([1, 2, 3], offset);
122+
});
123+
jest.mocked(getOffchainMessageV1Encoder).mockReturnValue({
124+
encode: jest.fn(),
125+
getSizeFromValue: jest.fn().mockReturnValue(3),
126+
write: mockWriter,
127+
});
128+
const result = encoder.encode(mockMessage as OffchainMessageV1);
129+
expect(mockWriter).toHaveBeenCalledWith(mockMessage, expect.any(Uint8Array), 0 /* offset */);
130+
expect(result).toStrictEqual(new Uint8Array([1, 2, 3]));
131+
});
89132
it.each(
90-
Array.from({ length: 255 - 0 /* highest supported version */ })
133+
Array.from({ length: 255 - 1 /* highest supported version */ })
91134
.map((_, ii) => 255 - ii)
92135
.reverse(),
93136
)('throws if the preamble is of version `%s`', putativeVersion => {

packages/offchain-messages/src/__tests__/message-v0-codec-test.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
SOLANA_ERROR__OFFCHAIN_MESSAGE__MESSAGE_MUST_BE_NON_EMPTY,
1414
SOLANA_ERROR__OFFCHAIN_MESSAGE__NUM_REQUIRED_SIGNERS_CANNOT_BE_ZERO,
1515
SOLANA_ERROR__OFFCHAIN_MESSAGE__RESTRICTED_ASCII_BODY_CHARACTER_OUT_OF_RANGE,
16+
SOLANA_ERROR__OFFCHAIN_MESSAGE__UNEXPECTED_VERSION,
1617
SOLANA_ERROR__OFFCHAIN_MESSAGE__VERSION_NUMBER_NOT_SUPPORTED,
1718
SolanaError,
1819
} from '@solana/errors';
@@ -26,7 +27,6 @@ import {
2627
OffchainMessageContentUtf8Of65535BytesMax,
2728
} from '../content';
2829
import { OffchainMessageV0 } from '../message-v0';
29-
import { OffchainMessageVersion } from '../version';
3030

3131
// The string `'\xffsolana offchain'`
3232
const OFFCHAIN_MESSAGE_SIGNING_DOMAIN_BYTES: ReadonlyUint8Array = new Uint8Array([
@@ -199,8 +199,9 @@ describe('getOffchainMessageV0Decoder()', () => {
199199
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x0A, 0x77, 0x6f, 0x72, 0x6c, 0x64,
200200
]);
201201
expect(() => decoder.decode(encodedMessage)).toThrow(
202-
new SolanaError(SOLANA_ERROR__OFFCHAIN_MESSAGE__VERSION_NUMBER_NOT_SUPPORTED, {
203-
unsupportedVersion: 1,
202+
new SolanaError(SOLANA_ERROR__OFFCHAIN_MESSAGE__UNEXPECTED_VERSION, {
203+
actualVersion: 1,
204+
expectedVersion: 0,
204205
}),
205206
);
206207
});
@@ -636,32 +637,33 @@ describe('getOffchainMessageEncoder()', () => {
636637
},
637638
);
638639
it('throws when encoding an ASCII encoded message with a non-zero version', () => {
639-
const offchainMessage: OffchainMessageV0 = {
640+
const offchainMessage = {
640641
applicationDomain: APPLICATION_DOMAIN,
641642
content: {
642643
format: OffchainMessageContentFormat.RESTRICTED_ASCII_1232_BYTES_MAX,
643644
text: 'Hello\nworld',
644645
} as OffchainMessageContentRestrictedAsciiOf1232BytesMax,
645646
requiredSignatories: [{ address: SIGNER_A }, { address: SIGNER_B }],
646-
version: 1 as OffchainMessageVersion,
647+
version: 1,
647648
};
648-
expect(() => encoder.encode(offchainMessage)).toThrow(
649-
new SolanaError(SOLANA_ERROR__OFFCHAIN_MESSAGE__VERSION_NUMBER_NOT_SUPPORTED, {
650-
unsupportedVersion: 1,
649+
expect(() => encoder.encode(offchainMessage as unknown as OffchainMessageV0)).toThrow(
650+
new SolanaError(SOLANA_ERROR__OFFCHAIN_MESSAGE__UNEXPECTED_VERSION, {
651+
actualVersion: 1,
652+
expectedVersion: 0,
651653
}),
652654
);
653655
});
654656
it('throws when encoding an ASCII encoded message with an unsupported version', () => {
655-
const offchainMessage: OffchainMessageV0 = {
657+
const offchainMessage = {
656658
applicationDomain: APPLICATION_DOMAIN,
657659
content: {
658660
format: OffchainMessageContentFormat.RESTRICTED_ASCII_1232_BYTES_MAX,
659661
text: 'Hello\nworld',
660662
} as OffchainMessageContentRestrictedAsciiOf1232BytesMax,
661663
requiredSignatories: [{ address: SIGNER_A }, { address: SIGNER_B }],
662-
version: 255 as OffchainMessageVersion,
664+
version: 255,
663665
};
664-
expect(() => encoder.encode(offchainMessage)).toThrow(
666+
expect(() => encoder.encode(offchainMessage as unknown as OffchainMessageV0)).toThrow(
665667
new SolanaError(SOLANA_ERROR__OFFCHAIN_MESSAGE__VERSION_NUMBER_NOT_SUPPORTED, {
666668
unsupportedVersion: 255,
667669
}),

0 commit comments

Comments
 (0)