Commit 5d9f8247 authored by YJ's avatar YJ Committed by Thibaut Sardan
Browse files

feat: Extrinsic V4 (#434)

* chore: bump @polkadot-js deps

* fix: make tests pass

* fix: bump patch

* bump RN to 0.61.2

* correctly import native modules

* typo

* new dev node gen hash

* correct new dev node gen hash

* fix: prepend type

* fix: feed extrinsic signature v4 to qr

* fix: wrap it up

* fix: make tests pass
parent 3bc6327c
Pipeline #55086 failed with stage
in 3 minutes and 44 seconds
......@@ -5,6 +5,7 @@
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
......@@ -25,13 +26,13 @@
1F426F8F208D2CC500CA43DB /* EthkeyBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F426F8E208D2CC500CA43DB /* EthkeyBridge.m */; };
1F426F92208D32FA00CA43DB /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
1F426F93208D332A00CA43DB /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
1F426F94208D333700CA43DB /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
1F426F95208D334200CA43DB /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
1F426F9C208D358500CA43DB /* EthkeyBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F426F99208D358500CA43DB /* EthkeyBridge.swift */; };
1F426F9D208D358500CA43DB /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F426F9A208D358500CA43DB /* String.swift */; };
1F73EE2721777A1D00706E91 /* libsigner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F426F39208B7CC000CA43DB /* libsigner.a */; };
391AD32122F9BF6F005136A0 /* libRNCamera.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 39D36A8722F4A07200EDA4F1 /* libRNCamera.a */; };
391AD3F022F9D848005136A0 /* libRNCNetInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 39D36A0622F4588C00EDA4F1 /* libRNCNetInfo.a */; };
3946D2A52364A7E700BAC371 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
3972BABE230D88B200202B52 /* libRNSecureStorage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3972BA68230C4CBE00202B52 /* libRNSecureStorage.a */; };
399D1B0E22DDDE1B00A815EB /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 399D1B0D22DDDE1B00A815EB /* JavaScriptCore.framework */; };
39B6F6432315B550009C3C05 /* main.jsbundle in Resources */ = {isa = PBXBuildFile; fileRef = 39B6F6102315B550009C3C05 /* main.jsbundle */; };
......@@ -134,20 +135,6 @@
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React;
};
1FE167A02088EA11004A6284 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D001F3B181A0099AA32;
remoteInfo = fishhook;
};
1FE167A22088EA11004A6284 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32;
remoteInfo = "fishhook-tvOS";
};
1FE167B42088EA11004A6284 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
......@@ -532,6 +519,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3946D2A52364A7E700BAC371 /* libRCTActionSheet.a in Frameworks */,
3972BABE230D88B200202B52 /* libRNSecureStorage.a in Frameworks */,
391AD3F022F9D848005136A0 /* libRNCNetInfo.a in Frameworks */,
391AD32122F9BF6F005136A0 /* libRNCamera.a in Frameworks */,
......@@ -539,7 +527,6 @@
399D1B0E22DDDE1B00A815EB /* JavaScriptCore.framework in Frameworks */,
1F73EE2721777A1D00706E91 /* libsigner.a in Frameworks */,
1F426F95208D334200CA43DB /* libReact.a in Frameworks */,
1F426F94208D333700CA43DB /* libRCTActionSheet.a in Frameworks */,
1F426F93208D332A00CA43DB /* libRCTAnimation.a in Frameworks */,
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
......@@ -629,8 +616,6 @@
children = (
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */,
1FE167A12088EA11004A6284 /* libfishhook.a */,
1FE167A32088EA11004A6284 /* libfishhook-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
......@@ -1114,20 +1099,6 @@
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
1FE167A12088EA11004A6284 /* libfishhook.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libfishhook.a;
remoteRef = 1FE167A02088EA11004A6284 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
1FE167A32088EA11004A6284 /* libfishhook-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libfishhook-tvOS.a";
remoteRef = 1FE167A22088EA11004A6284 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
1FE167B52088EA11004A6284 /* libjsinspector.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
......
......@@ -47,6 +47,7 @@ RCT_EXTERN_METHOD(randomPhrase:(RCTPromiseResolveBlock)resolve reject:(RCTPromis
RCT_EXTERN_METHOD(encryptData:(NSString*)data password:(NSString*)password resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(decryptData:(NSString*)data password:(NSString*)password resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(qrCode:(NSString*)data resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(qrCodeHex:(NSString*)data resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateAddress:(NSString*)seed version:(NSUInteger*)version resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(substrateSign:(NSString*)seed message:(NSString*)message resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(blake2s:(NSString*)data resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
......
This diff is collapsed.
......@@ -16,7 +16,6 @@
// @flow
import extrinsicsFromMeta from '@polkadot/api-metadata/extrinsics/fromMetadata';
import { GenericCall, getTypeRegistry, Metadata } from '@polkadot/types';
import Call from '@polkadot/types/primitive/Generic/Call';
import { formatBalance } from '@polkadot/util';
......@@ -65,6 +64,7 @@ export default class PayloadDetailsCard extends React.PureComponent {
});
} else if (__DEV__ && isSubstrateDev) {
metadata = new Metadata(substrateDevMetadata);
formatBalance.setDefaults({
decimals:
SUBSTRATE_NETWORK_LIST[SubstrateNetworkKeys.SUBSTRATE_DEV].decimals,
......@@ -82,8 +82,7 @@ export default class PayloadDetailsCard extends React.PureComponent {
Keys: 'SessionKeysPolkadot'
});
const extrinsics = extrinsicsFromMeta(metadata);
GenericCall.injectMethods(extrinsics);
GenericCall.injectMetadata(metadata);
}
render() {
......
......@@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import { Dimensions, Image, StyleSheet, View } from 'react-native';
import { qrCode, qrHex } from '../util/native';
import { qrCode, qrCodeHex } from '../util/native';
QrView.propTypes = {
data: PropTypes.string.isRequired
......@@ -34,7 +34,7 @@ export default function QrView(props) {
async function displayQrCode(data) {
try {
const generatedQr = isHex(data)
? await qrHex(data)
? await qrCodeHex(data)
: await qrCode(data);
setQr(generatedQr);
} catch (e) {
......
......@@ -27,7 +27,7 @@ export const EthereumNetworkKeys = Object.freeze({
export const SubstrateNetworkKeys = Object.freeze({
KUSAMA: '0xe3777fa922cafbff200cadeaea1a76bd7898ad5b89f7848999058b50e715f636', // https://polkascan.io/pre/kusama-cc2/block/0
SUBSTRATE_DEV:
'0x4393a679e1830a487e8ae92733f089a80f3e24ba515b08dd8adb40fc6cedee8d' // substrate --dev commit ac6a2a783f0e1f4a814cf2add40275730cd41be1 hosted on wss://dev-node.substrate.dev .
'0xa9b086bbadc5cedd43e0f8a686de18f0bc010cd88d4b46d2f75ae5a540bf460d' // substrate --dev commit ac6a2a783f0e1f4a814cf2add40275730cd41be1 hosted on wss://dev-node.substrate.dev .
});
const unknownNetworkBase = {
......
......@@ -87,7 +87,9 @@ export default class Scanner extends React.PureComponent {
strippedData,
accountsStore
);
} else if (scannerStore.getErrorMsg()) {
}
if (scannerStore.getErrorMsg()) {
throw new Error(scannerStore.getErrorMsg());
}
......@@ -101,6 +103,7 @@ export default class Scanner extends React.PureComponent {
this.props.navigation.navigate('MessageDetails');
}
}
('');
} catch (e) {
return this.showErrorMessage(
scannerStore,
......
......@@ -16,7 +16,14 @@
// @flow
import { GenericExtrinsicPayload } from '@polkadot/types';
import { hexStripPrefix, isU8a, u8aToHex, u8aConcat } from '@polkadot/util';
import {
hexStripPrefix,
hexToU8a,
isU8a,
u8aToHex,
u8aConcat
} from '@polkadot/util';
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
import { Container } from 'unstated';
......@@ -93,6 +100,11 @@ const defaultState = {
const MULTIPART = new Uint8Array([0]); // always mark as multipart for simplicity's sake. Consistent with @polkadot/react-qr
// const SIG_TYPE_NONE = new Uint8Array();
// const SIG_TYPE_ED25519 = new Uint8Array([0]);
const SIG_TYPE_SR25519 = new Uint8Array([1]);
// const SIG_TYPE_ECDSA = new Uint8Array([2]);
export default class ScannerStore extends Container<ScannerState> {
state = defaultState;
......@@ -352,7 +364,14 @@ export default class ScannerStore extends Container<ScannerState> {
} else if (isAscii(dataToSign)) {
signable = hexStripPrefix(asciiToHex(dataToSign));
}
signedData = await substrateSign(seed, signable);
// TODO: tweak the first byte if and when sig type is not sr25519
const sig = u8aConcat(
SIG_TYPE_SR25519,
hexToU8a('0x' + (await substrateSign(seed, signable)))
);
signedData = u8aToHex(sig, -1, false); // the false doesn't add 0x
}
this.setState({ signedData });
......
......@@ -167,8 +167,9 @@ export async function constructDataFromBytes(bytes, multipartComplete = false) {
switch (secondByte) {
case '00': // sign mortal extrinsic
case '02': // sign immortal extrinsic
extrinsicPayload = new GenericExtrinsicPayload(rawPayload, {
version: 3
version: 4
});
data.action = isOversized ? 'signData' : 'signTransaction';
......@@ -201,32 +202,6 @@ export async function constructDataFromBytes(bytes, multipartComplete = false) {
publicKeyAsBytes,
defaultPrefix
); // default to Kusama
break;
case '02': // immortal
extrinsicPayload = new GenericExtrinsicPayload(rawPayload, {
version: 3
});
data.action = isOversized ? 'signData' : 'signTransaction';
data.oversized = isOversized;
data.isHash = isOversized;
data.data.data = isOversized
? await blake2s(u8aToHex(rawPayload))
: extrinsicPayload;
network = NETWORK_LIST[extrinsicPayload.genesisHash.toHex()];
if (!network) {
throw new Error(
`Signer does not currently support a chain with genesis hash: ${extrinsicPayload.genesisHash.toHex()}`
);
}
data.data.account = encodeAddress(
publicKeyAsBytes,
network.prefix
); // encode to the prefix;
break;
case '03': // Cold Signer should attempt to decode message to utf8
data.action = 'signData';
......
......@@ -17,7 +17,6 @@
'use strict';
import '@polkadot/types/injector';
import extrinsicsFromMeta from '@polkadot/api-metadata/extrinsics/fromMetadata';
import {
createType,
GenericExtrinsicPayload,
......@@ -42,12 +41,15 @@ import kusamaData from './static-kusama';
const SUBSTRATE_ID = new Uint8Array([0x53]);
const CRYPTO_SR25519 = new Uint8Array([0x01]);
const CMD_SIGN_MORTAL = new Uint8Array([2]);
const CMD_SIGN_MORTAL = new Uint8Array([0]);
const CMD_SIGN_MSG = new Uint8Array([3]);
const KUSAMA_ADDRESS = 'FF42iLDmp7JLeySMjwWWtYQqfycJvsJFBYrySoMvtGfvAGs';
const TEST_MESSAGE = 'THIS IS SPARTA!';
const metadata = new Metadata(kusamaData);
GenericCall.injectMetadata(metadata);
const RN_TX_REQUEST_RAW_DATA =
'4' + // indicates data is binary encoded
'37' + // byte length of data
......@@ -73,17 +75,17 @@ const SIGN_MSG_TEST = new Uint8Array([
/* eslint-enable prettier/prettier */
const SIGNER_PAYLOAD_TEST = {
address: '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE',
address: KUSAMA_ADDRESS,
blockHash:
'0xde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7',
era: '0x0703',
genesisHash:
'0x3fd7b9eb6a00376e5be61f01abb429ffb0b104be05eaff4d458da48fcd425baf',
blockNumber: '0x231d30',
era: createType('ExtrinsicEra', { current: 2301232, period: 200 }),
genesisHash: SubstrateNetworkKeys.KUSAMA,
method:
'0x0400ffee5a3c1f409c4ad69cd7a477419bf3fd1bc2e72f3c43ba5c4a9896de1d8bf94200',
nonce: '0x00001234',
specVersion: 3,
tip: '0x00000000000000000000000000005678'
'0x0600ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c',
nonce: 0x1234,
specVersion: 123,
tip: 0x5678
};
const SIGN_TX_TEST = u8aConcat(
......@@ -92,7 +94,7 @@ const SIGN_TX_TEST = u8aConcat(
CRYPTO_SR25519,
CMD_SIGN_MORTAL,
decodeAddress(KUSAMA_ADDRESS),
createType('ExtrinsicPayload', SIGNER_PAYLOAD_TEST, { version: 3 }).toU8a()
new GenericExtrinsicPayload(SIGNER_PAYLOAD_TEST, { version: 4 }).toU8a()
);
describe.skip('sanity check', () => {
......@@ -101,12 +103,15 @@ describe.skip('sanity check', () => {
});
it('sanity check payload encodes as expected', () => {
const payload = new GenericExtrinsicPayload(SIGN_TX_TEST, { version: 3 });
const payload = new GenericExtrinsicPayload(SIGNER_PAYLOAD_TEST, {
version: 4
});
const fromBytes = new GenericExtrinsicPayload(payload.toU8a(), {
version: 3
version: 4
});
expect(payload).toEqual(fromBytes);
expect(payload).toMatchObject(fromBytes);
expect(payload.genesisHash.toHex()).toEqual(SubstrateNetworkKeys.KUSAMA);
});
});
......@@ -175,7 +180,7 @@ describe('decoders', () => {
const unsignedData = await constructDataFromBytes(SIGN_TX_TEST);
expect(unsignedData.data.data.era.toHex()).toEqual(
SIGNER_PAYLOAD_TEST.era
SIGNER_PAYLOAD_TEST.era.toHex()
);
expect(unsignedData.data.data.method.toHex()).toEqual(
SIGNER_PAYLOAD_TEST.method
......@@ -194,15 +199,7 @@ describe('decoders', () => {
});
describe('Type injection from metadata', () => {
beforeAll(() => {
const metadata = new Metadata(kusamaData);
const extrinsics = extrinsicsFromMeta(metadata);
GenericCall.injectMethods(extrinsics);
});
it.only('can fetch the prefix matching to a hash', () => {
it('can fetch the prefix matching to a hash', () => {
const kusamaPrefix =
SUBSTRATE_NETWORK_LIST[SubstrateNetworkKeys.KUSAMA].prefix;
// const substratePrefix = SUBSTRATE_NETWORK_LIST[SubstrateNetworkKeys.SUBSTRATE_DEV].prefix;
......@@ -213,7 +210,7 @@ describe('decoders', () => {
it('decodes Payload Method to something human readable with Kusama metadata', () => {
const payload = new GenericExtrinsicPayload(SIGNER_PAYLOAD_TEST, {
version: 3
version: 4
});
const call = new Call(payload.method);
......
This diff is collapsed.
......@@ -18,7 +18,6 @@
import '@polkadot/types/injector';
import extrinsicsFromMeta from '@polkadot/api-metadata/extrinsics/fromMetadata';
import { GenericCall, Metadata } from '@polkadot/types';
import Call from '@polkadot/types/primitive/Generic/Call';
import { formatBalance } from '@polkadot/util';
......@@ -51,9 +50,7 @@ describe('units', () => {
beforeAll(() => {
const metadata = new Metadata(kusamaData);
const extrinsics = extrinsicsFromMeta(metadata);
GenericCall.injectMethods(extrinsics);
GenericCall.injectMetadata(metadata);
formatBalance.setDefaults({
decimals: 12,
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment