decoders.spec.ts 7.34 KB
Newer Older
YJ's avatar
YJ committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.

17
18
19
import {
	createType,
	GenericExtrinsicPayload,
20
	Metadata,
Thibaut Sardan's avatar
Thibaut Sardan committed
21
	TypeRegistry
22
} from '@polkadot/types';
23
import { ExtrinsicPayload } from '@polkadot/types/interfaces';
24
import Call from '@polkadot/types/generic/Call';
YJ's avatar
YJ committed
25
26
import { u8aConcat } from '@polkadot/util';
import { checkAddress, decodeAddress } from '@polkadot/util-crypto';
27
import 'jest';
28

Hanwen Cheng's avatar
Hanwen Cheng committed
29
30
31
import {
	SUBSTRATE_NETWORK_LIST,
	SubstrateNetworkKeys
32
33
} from 'constants/networkSpecs';
import { SubstrateCompletedParsedData } from 'types/scannerTypes';
34
35
36
37
38
39
40
import {
	constructDataFromBytes,
	rawDataToU8A,
	asciiToHex,
	hexToAscii,
	decodeToString,
	isJsonString
41
42
} from 'utils/decoders';
import { isAscii } from 'utils/strings';
43
import { kusamaMetadata } from 'constants/networkMetadata';
YJ's avatar
YJ committed
44
45
46

const SUBSTRATE_ID = new Uint8Array([0x53]);
const CRYPTO_SR25519 = new Uint8Array([0x01]);
YJ's avatar
YJ committed
47
const CMD_SIGN_MORTAL = new Uint8Array([0]);
YJ's avatar
YJ committed
48
const CMD_SIGN_MSG = new Uint8Array([3]);
Thibaut Sardan's avatar
Thibaut Sardan committed
49
const registry = new TypeRegistry();
50
registry.setMetadata(new Metadata(registry, kusamaMetadata));
YJ's avatar
YJ committed
51
52
53
54

const KUSAMA_ADDRESS = 'FF42iLDmp7JLeySMjwWWtYQqfycJvsJFBYrySoMvtGfvAGs';
const TEST_MESSAGE = 'THIS IS SPARTA!';

55
56
57
58
59
60
61
62
63
64
65
66
67
68
const RN_TX_REQUEST_RAW_DATA =
	'4' + // indicates data is binary encoded
	'37' + // byte length of data
	'00' + // is it multipart?
	'0001' + // how many parts in total?
	'0000' + // which frame are we on?
	'53' + // S for Substrate
	'01' + // sr25519
	'03' + // sign message
	'7602e6fd489d61eb35c652376a8f71b0fccb72189874df4abefa88e89ea40776' + // key
	'5448495320495320535041525441210' + // THIS IS SPARTA!
	'ec11ec11ec11ec';

/* eslint-disable prettier/prettier */
YJ's avatar
YJ committed
69
70
71
72
73
74
75
76
const SIGN_MSG_TEST = new Uint8Array([
    0,  0,  1,  0,  0,
    83,   1,   3, 118,   2, 230, 253,  72, 157,  97,
    235,  53, 198,  82,  55, 106, 143, 113, 176, 252,
    203, 114,  24, 152, 116, 223,  74, 190, 250, 136,
    232, 158, 164,   7, 118,  84,  72,  73,  83,  32,
    73,  83,  32,  83,  80,  65,  82,  84,  65,  33
  ]);
77
/* eslint-enable prettier/prettier */
YJ's avatar
YJ committed
78
79

const SIGNER_PAYLOAD_TEST = {
YJ's avatar
YJ committed
80
	address: KUSAMA_ADDRESS,
81
82
	blockHash:
		'0xde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7',
YJ's avatar
YJ committed
83
	blockNumber: '0x231d30',
Thibaut Sardan's avatar
Thibaut Sardan committed
84
	era: createType(registry, 'ExtrinsicEra', { current: 2301232, period: 200 }),
YJ's avatar
YJ committed
85
	genesisHash: SubstrateNetworkKeys.KUSAMA,
86
	method:
YJ's avatar
YJ committed
87
88
89
90
		'0x0600ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c',
	nonce: 0x1234,
	specVersion: 123,
	tip: 0x5678
YJ's avatar
YJ committed
91
92
93
};

const SIGN_TX_TEST = u8aConcat(
94
95
96
97
98
	new Uint8Array([0, 0, 1, 0, 0]),
	SUBSTRATE_ID,
	CRYPTO_SR25519,
	CMD_SIGN_MORTAL,
	decodeAddress(KUSAMA_ADDRESS),
Thibaut Sardan's avatar
Thibaut Sardan committed
99
100
101
	new GenericExtrinsicPayload(registry, SIGNER_PAYLOAD_TEST, {
		version: 4
	}).toU8a()
YJ's avatar
YJ committed
102
103
);

Hanwen Cheng's avatar
Hanwen Cheng committed
104
/* eslint-disable-next-line jest/no-disabled-tests */
YJ's avatar
YJ committed
105
describe.skip('sanity check', () => {
106
107
108
109
110
	it('sanity check address is kusama', () => {
		expect(checkAddress(KUSAMA_ADDRESS, 2)).toEqual([true, null]);
	});

	it('sanity check payload encodes as expected', () => {
Thibaut Sardan's avatar
Thibaut Sardan committed
111
		const payload = new GenericExtrinsicPayload(registry, SIGNER_PAYLOAD_TEST, {
YJ's avatar
YJ committed
112
113
			version: 4
		});
Thibaut Sardan's avatar
Thibaut Sardan committed
114
		const fromBytes = new GenericExtrinsicPayload(registry, payload.toU8a(), {
YJ's avatar
YJ committed
115
			version: 4
116
117
		});

YJ's avatar
YJ committed
118
119
		expect(payload).toMatchObject(fromBytes);
		expect(payload.genesisHash.toHex()).toEqual(SubstrateNetworkKeys.KUSAMA);
120
	});
YJ's avatar
YJ committed
121
122
123
});

describe('decoders', () => {
124
125
126
127
	describe('strings', () => {
		it('check if string is ascii', () => {
			const message = 'hello world';
			const numbers = 123;
YJ's avatar
YJ committed
128

129
130
131
			expect(isAscii(message)).toBe(true);
			expect(isAscii(numbers)).toBe(true);
		});
YJ's avatar
YJ committed
132

133
134
135
		it('converts ascii to hex', () => {
			const message = 'hello world';
			const messageHex = asciiToHex(message);
YJ's avatar
YJ committed
136

137
138
			expect(hexToAscii(messageHex)).toBe(message);
		});
YJ's avatar
YJ committed
139

140
141
		it('converts bytes to ascii', () => {
			/* eslint-disable-next-line prettier/prettier */
YJ's avatar
YJ committed
142
      const messageBytes = new Uint8Array([84,  72,  73,  83,  32, 73,  83,  32,  83,  80,  65,  82,  84,  65,  33]);
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
			const message = decodeToString(messageBytes);

			expect(message).toBeDefined();
			expect(message).toBe(TEST_MESSAGE);
		});

		it('checks if string is JSON parseable', () => {
			const jsonString = JSON.stringify({ a: 1, b: 2 });
			const notJsonString = '0x90u23jaiof';
			const validExample = isJsonString(jsonString);
			const inValidExample = isJsonString(notJsonString);

			expect(validExample).toBe(true);
			expect(inValidExample).toBe(false);
		});
	});

	describe('rawDataToU8a', () => {
		it('should properly extract only UOS relevant data from RNCamera txRequest.rawData', () => {
			const strippedU8a = rawDataToU8A(RN_TX_REQUEST_RAW_DATA);
163
164
165
			expect(strippedU8a).not.toBeNull();
			const frameInfo = strippedU8a!.slice(0, 5);
			const uos = strippedU8a!.slice(5);
166
167
168
169
170
171
172
173
174
175
176
177
178
179

			expect(frameInfo).toEqual(new Uint8Array([0, 0, 1, 0, 0]));
			expect(uos[0]).toEqual(SUBSTRATE_ID[0]);
			expect(uos[1]).toEqual(CRYPTO_SR25519[0]);
			expect(uos[2]).toEqual(CMD_SIGN_MSG[0]);
		});
	});

	describe('UOS parsing', () => {
		// after stripping
		it('from Substrate UOS message', async () => {
			const unsignedData = await constructDataFromBytes(SIGN_MSG_TEST);

			expect(unsignedData).toBeDefined();
180
181
182
183
184
185
186
187
188
			expect(
				(unsignedData as SubstrateCompletedParsedData).data.crypto
			).toEqual('sr25519');
			expect((unsignedData as SubstrateCompletedParsedData).data.data).toEqual(
				'THIS IS SPARTA!'
			);
			expect(
				(unsignedData as SubstrateCompletedParsedData).data.account
			).toEqual(KUSAMA_ADDRESS);
189
190
191
192
		});

		it('from Substrate UOS Payload Mortal', async () => {
			const unsignedData = await constructDataFromBytes(SIGN_TX_TEST);
193
194
195
196
197
198
199
200
			const payload = (unsignedData as SubstrateCompletedParsedData).data
				.data as ExtrinsicPayload;

			expect(payload.era.toHex()).toEqual(SIGNER_PAYLOAD_TEST.era.toHex());
			expect(payload.method.toHex()).toEqual(SIGNER_PAYLOAD_TEST.method);
			expect(payload.blockHash.toHex()).toEqual(SIGNER_PAYLOAD_TEST.blockHash);
			expect(payload.nonce.eq(SIGNER_PAYLOAD_TEST.nonce)).toBe(true);
			expect(payload.specVersion.eq(SIGNER_PAYLOAD_TEST.specVersion)).toBe(
201
202
				true
			);
203
			expect(payload.tip.eq(SIGNER_PAYLOAD_TEST.tip)).toBe(true);
204
205
206
207
		});
	});

	describe('Type injection from metadata', () => {
YJ's avatar
YJ committed
208
		it('can fetch the prefix matching to a hash', () => {
209
210
211
212
213
214
215
216
217
			const kusamaPrefix =
				SUBSTRATE_NETWORK_LIST[SubstrateNetworkKeys.KUSAMA].prefix;
			// const substratePrefix = SUBSTRATE_NETWORK_LIST[SubstrateNetworkKeys.SUBSTRATE_DEV].prefix;

			expect(kusamaPrefix).toBe(2);
			// expect(substrate).toBe(42);
		});

		it('decodes Payload Method to something human readable with Kusama metadata', () => {
Thibaut Sardan's avatar
Thibaut Sardan committed
218
219
220
221
222
223
224
			const payload = new GenericExtrinsicPayload(
				registry,
				SIGNER_PAYLOAD_TEST,
				{
					version: 4
				}
			);
225

Thibaut Sardan's avatar
Thibaut Sardan committed
226
			const call = new Call(registry, payload.method);
227
228
229
230
231
232

			expect(call).toBeDefined();
			expect(call.args).toBeDefined();
			expect(call.meta).toBeDefined();
		});
	});
YJ's avatar
YJ committed
233
});