mirror of
https://github.com/pezkuwichain/pezkuwi-ui.git
synced 2026-04-22 04:28:00 +00:00
Basic QR tests (+ number encoding fix) (#168)
* Basic QR tests (+ number encoding fix) * skipEncoding for Address display * Fixup comments
This commit is contained in:
+2
-1
@@ -19,7 +19,8 @@
|
||||
"clean": "polkadot-dev-clean-build",
|
||||
"demo:identicon": "webpack-serve --config packages/ui-identicon/webpack.config.js --content packages/ui-identicon --port 3000",
|
||||
"postinstall": "polkadot-dev-yarn-only",
|
||||
"test": "jest --coverage"
|
||||
"test": "jest --coverage",
|
||||
"test:one": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.5",
|
||||
|
||||
@@ -7,16 +7,14 @@ import { BaseProps } from './types';
|
||||
import React from 'react';
|
||||
import qrcode from 'qrcode-generator';
|
||||
import styled from 'styled-components';
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { xxhashAsHex } from '@polkadot/util-crypto';
|
||||
|
||||
import { createSize } from './constants';
|
||||
import { encodeNumber, decodeString } from './util';
|
||||
import { createFrames, createImgSize, decodeString } from './util';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
size?: number;
|
||||
skipEncoding?: boolean;
|
||||
value: Uint8Array;
|
||||
withMulti?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -28,8 +26,6 @@ interface State {
|
||||
}
|
||||
|
||||
const FRAME_DELAY = 2100;
|
||||
const FRAME_SIZE = 1716;
|
||||
const MULTIPART = new Uint8Array([0]);
|
||||
|
||||
function getDataUrl (value: string): string {
|
||||
const qr = qrcode(0, 'M');
|
||||
@@ -40,26 +36,6 @@ function getDataUrl (value: string): string {
|
||||
return qr.createDataURL(16, 0);
|
||||
}
|
||||
|
||||
function createFrames (input: Uint8Array): string[] {
|
||||
const frames = [];
|
||||
let idx = 0;
|
||||
|
||||
while (idx < input.length) {
|
||||
frames.push(input.subarray(idx, idx + FRAME_SIZE));
|
||||
|
||||
idx += FRAME_SIZE;
|
||||
}
|
||||
|
||||
return frames.map((frame, index: number): string =>
|
||||
decodeString(u8aConcat(
|
||||
MULTIPART,
|
||||
encodeNumber(frames.length),
|
||||
encodeNumber(index),
|
||||
frame
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
class Display extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
frames: [],
|
||||
@@ -69,16 +45,16 @@ class Display extends React.PureComponent<Props, State> {
|
||||
valueHash: null
|
||||
};
|
||||
|
||||
public static getDerivedStateFromProps ({ value, withMulti = true }: Props, prevState: State): Pick<State, never> | null {
|
||||
public static getDerivedStateFromProps ({ value, skipEncoding = false }: Props, prevState: State): Pick<State, never> | null {
|
||||
const valueHash = xxhashAsHex(value);
|
||||
|
||||
if (valueHash === prevState.valueHash) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const frames: string[] = withMulti
|
||||
? createFrames(value)
|
||||
: [decodeString(value)];
|
||||
const frames: string[] = skipEncoding
|
||||
? [decodeString(value)]
|
||||
: createFrames(value);
|
||||
|
||||
return {
|
||||
frames,
|
||||
@@ -113,7 +89,7 @@ class Display extends React.PureComponent<Props, State> {
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={createSize(size)}
|
||||
style={createImgSize(size)}
|
||||
>
|
||||
<div
|
||||
className='ui--qr-Display'
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { xxhashAsHex } from '@polkadot/util-crypto';
|
||||
|
||||
import { ADDRESS_PREFIX } from './constants';
|
||||
import { encodeString } from './util';
|
||||
import { createAddressPayload } from './util';
|
||||
import QrDisplay from './Display';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
@@ -21,8 +19,6 @@ interface State {
|
||||
dataHash: string | null;
|
||||
}
|
||||
|
||||
const PREFIX = encodeString(ADDRESS_PREFIX);
|
||||
|
||||
export default class DisplayExtrinsic extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
data: null,
|
||||
@@ -30,10 +26,7 @@ export default class DisplayExtrinsic extends React.PureComponent<Props, State>
|
||||
};
|
||||
|
||||
public static getDerivedStateFromProps ({ address }: Props, prevState: State): State | null {
|
||||
const data = u8aConcat(
|
||||
PREFIX,
|
||||
encodeString(address)
|
||||
);
|
||||
const data = createAddressPayload(address);
|
||||
const dataHash = xxhashAsHex(data);
|
||||
|
||||
if (dataHash === prevState.dataHash) {
|
||||
@@ -54,6 +47,7 @@ export default class DisplayExtrinsic extends React.PureComponent<Props, State>
|
||||
return (
|
||||
<QrDisplay
|
||||
className={className}
|
||||
skipEncoding={true}
|
||||
style={style}
|
||||
value={data}
|
||||
/>
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { u8aConcat } from '@polkadot/util';
|
||||
import { decodeAddress, xxhashAsHex } from '@polkadot/util-crypto';
|
||||
import { xxhashAsHex } from '@polkadot/util-crypto';
|
||||
|
||||
import { createSignPayload } from './util';
|
||||
import QrDisplay from './Display';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
@@ -20,10 +20,6 @@ interface State {
|
||||
dataHash: string | null;
|
||||
}
|
||||
|
||||
const SUBSTRATE = new Uint8Array([0x53]);
|
||||
const CRYPTO_SR25519 = new Uint8Array([0x01]);
|
||||
const SIGN_TX = new Uint8Array([0x00]);
|
||||
|
||||
export default class DisplayPayload extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
data: null,
|
||||
@@ -31,13 +27,7 @@ export default class DisplayPayload extends React.PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
public static getDerivedStateFromProps ({ address, payload }: Props, prevState: State): State | null {
|
||||
const data = u8aConcat(
|
||||
SUBSTRATE,
|
||||
CRYPTO_SR25519,
|
||||
SIGN_TX,
|
||||
decodeAddress(address),
|
||||
payload
|
||||
);
|
||||
const data = createSignPayload(address, payload);
|
||||
const dataHash = xxhashAsHex(data);
|
||||
|
||||
if (dataHash === prevState.dataHash) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import React from 'react';
|
||||
import Reader from 'react-qr-reader';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { createSize } from './constants';
|
||||
import { createImgSize } from './util';
|
||||
|
||||
interface Props extends BaseProps {
|
||||
delay?: number;
|
||||
@@ -29,7 +29,7 @@ class Scan extends React.PureComponent<Props> {
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={createSize(size)}
|
||||
style={createImgSize(size)}
|
||||
>
|
||||
<Reader
|
||||
className='ui--qr-Scan'
|
||||
|
||||
@@ -2,20 +2,18 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
const DEFAULT_SIZE = 300;
|
||||
const DEFAULT_IMG_SIZE = 300;
|
||||
const ADDRESS_PREFIX = 'substrate:';
|
||||
|
||||
function createSize (size: number = DEFAULT_SIZE): Record<string, string> {
|
||||
const height = `${size}px`;
|
||||
|
||||
return {
|
||||
height,
|
||||
width: height
|
||||
};
|
||||
}
|
||||
const FRAME_SIZE = 1716;
|
||||
const SUBSTRATE_ID = new Uint8Array([0x53]);
|
||||
const CRYPTO_SR25519 = new Uint8Array([0x01]);
|
||||
const CMD_SIGN_TX = new Uint8Array([0x00]);
|
||||
|
||||
export {
|
||||
ADDRESS_PREFIX,
|
||||
DEFAULT_SIZE,
|
||||
createSize
|
||||
CMD_SIGN_TX,
|
||||
CRYPTO_SR25519,
|
||||
DEFAULT_IMG_SIZE,
|
||||
FRAME_SIZE,
|
||||
SUBSTRATE_ID
|
||||
};
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2017-2019 @polkadot/react-qr authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { u8aToHex, u8aToString } from '@polkadot/util';
|
||||
|
||||
import { createAddressPayload, createSignPayload, createFrames, encodeNumber, encodeString } from './util';
|
||||
|
||||
describe('util', (): void => {
|
||||
describe('encodeNumber', (): void => {
|
||||
it('encodes 1 correctly', (): void => {
|
||||
expect(
|
||||
encodeNumber(1)
|
||||
).toEqual(new Uint8Array([0, 1]));
|
||||
});
|
||||
|
||||
it('encodes 257 correctly', (): void => {
|
||||
expect(
|
||||
encodeNumber(257)
|
||||
).toEqual(new Uint8Array([1, 1]));
|
||||
});
|
||||
});
|
||||
describe('createAddressPayload', (): void => {
|
||||
it('encodes an address properly', (): void => {
|
||||
expect(
|
||||
u8aToString(
|
||||
createAddressPayload('5GKhfyctwmW5LQdGaHTyU9qq2yDtggdJo719bj5ZUxnVGtmX')
|
||||
)
|
||||
).toEqual('substrate:5GKhfyctwmW5LQdGaHTyU9qq2yDtggdJo719bj5ZUxnVGtmX');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSignPayload', (): void => {
|
||||
it('encodes a payload properly', (): void => {
|
||||
expect(
|
||||
u8aToHex(
|
||||
createSignPayload('5HbgaJEuVN5qGbkhgtuDQANivSWwHXWsC2erP1SQUXgciTVq', 'THIS IS SPARTA!')
|
||||
)
|
||||
).toEqual(
|
||||
'0x' + // prefix
|
||||
'53' + // substrate
|
||||
'01' + // sr25519
|
||||
'00' + // sign tx
|
||||
'f4cd755672a8f9542ca9da4fbf2182e79135d94304002e6a09ffc96fef6e6c4c' + // publickey
|
||||
'544849532049532053504152544121' // THIS IS SPARTA!
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createFrames', (): void => {
|
||||
it('encodes frames properly', (): void => {
|
||||
expect(
|
||||
createFrames(
|
||||
createSignPayload('5HbgaJEuVN5qGbkhgtuDQANivSWwHXWsC2erP1SQUXgciTVq', '0x12345678')
|
||||
).map((str): string => u8aToHex(encodeString(str)))
|
||||
).toEqual([
|
||||
'0x' +
|
||||
'00' + // multipart
|
||||
'0001' + // length
|
||||
'0000' + // index
|
||||
'530100' + // payload info, substrate + sr25519 + signtx
|
||||
'f4cd755672a8f9542ca9da4fbf2182e79135d94304002e6a09ffc96fef6e6c4c' + // publicKey
|
||||
'12345678' // data
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,8 +2,15 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { u8aConcat, u8aToU8a } from '@polkadot/util';
|
||||
import { decodeAddress } from '@polkadot/util-crypto';
|
||||
|
||||
import { ADDRESS_PREFIX, CMD_SIGN_TX, CRYPTO_SR25519, DEFAULT_IMG_SIZE, FRAME_SIZE, SUBSTRATE_ID } from './constants';
|
||||
|
||||
const MULTIPART = new Uint8Array([0]);
|
||||
|
||||
export function encodeNumber (value: number): Uint8Array {
|
||||
return new Uint8Array([value >> 8, value & 256]);
|
||||
return new Uint8Array([value >> 8, value & 0xff]);
|
||||
}
|
||||
|
||||
export function encodeString (value: string): Uint8Array {
|
||||
@@ -21,3 +28,49 @@ export function decodeString (value: Uint8Array): string {
|
||||
return str + String.fromCharCode(code);
|
||||
}, '');
|
||||
}
|
||||
|
||||
export function createAddressPayload (address: string): Uint8Array {
|
||||
return u8aConcat(
|
||||
encodeString(ADDRESS_PREFIX),
|
||||
encodeString(address)
|
||||
);
|
||||
}
|
||||
|
||||
export function createSignPayload (address: string, payload: string | Uint8Array): Uint8Array {
|
||||
return u8aConcat(
|
||||
SUBSTRATE_ID,
|
||||
CRYPTO_SR25519,
|
||||
CMD_SIGN_TX,
|
||||
decodeAddress(address),
|
||||
u8aToU8a(payload)
|
||||
);
|
||||
}
|
||||
|
||||
export function createFrames (input: Uint8Array): string[] {
|
||||
const frames = [];
|
||||
let idx = 0;
|
||||
|
||||
while (idx < input.length) {
|
||||
frames.push(input.subarray(idx, idx + FRAME_SIZE));
|
||||
|
||||
idx += FRAME_SIZE;
|
||||
}
|
||||
|
||||
return frames.map((frame, index: number): string =>
|
||||
decodeString(u8aConcat(
|
||||
MULTIPART,
|
||||
encodeNumber(frames.length),
|
||||
encodeNumber(index),
|
||||
frame
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
export function createImgSize (size: number = DEFAULT_IMG_SIZE): Record<string, string> {
|
||||
const height = `${size}px`;
|
||||
|
||||
return {
|
||||
height,
|
||||
width: height
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user