Commit 1135161b authored by Amaury Martiny's avatar Amaury Martiny
Browse files

feat: decodeTx

parent 4637b153
......@@ -2,6 +2,8 @@ import Metadata from '@polkadot/metadata';
import { createType, TypeRegistry } from '@polkadot/types';
import { SignerPayloadJSON } from '@polkadot/types/types';
import { EXTRINSIC_VERSION } from './util/constants';
/**
* JSON format for an unsigned transaction
*/
......@@ -57,7 +59,8 @@ const ONE_MINUTE = 60 / BLOCKTIME;
const DEFAULT_MORTAL_LENGTH = 240 * ONE_MINUTE;
/**
* Construct a balance transfer transaction offline
* Construct a balance transfer transaction offline. Transactions can be
* constructed in such a way that it is valid for at least 240 minutes
*
* @param info - Information required to construct the transaction
* @param amount -
......@@ -81,6 +84,6 @@ export function balanceTransfer(info: TxInfo): UnsignedTransaction {
nonce: createType(registry, 'Compact<Index>', info.nonce).toHex(),
specVersion: createType(registry, 'u32', info.specVersion).toHex(),
tip: createType(registry, 'Compact<Balance>', info.tip).toHex(),
version: 4
version: EXTRINSIC_VERSION
};
}
import { Keyring } from '@polkadot/api';
import { createType, TypeRegistry } from '@polkadot/types';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { balanceTransfer } from './balanceTransfer';
import { createSignedTx } from './createSignedTx';
import { createSigningPayload } from './createSigningPayload';
import { TEST_TX_INFO } from './util/testUtil';
import { balanceTransfer } from './balanceTransfer';
import { metadataRpc, signWithAlice, TEST_TX_INFO } from './util/testUtil';
describe('createSignedTx', () => {
it('should work', async done => {
// We're creating an Alice account that will sign the payload
// Wait for the promise to resolve async WASM
await cryptoWaitReady();
const registry = new TypeRegistry();
// Use ed25519 because it has deterministic signatures
const keyring = new Keyring({ type: 'ed25519' });
const alice = keyring.addFromUri('//Alice', { name: 'Alice default' });
const unsigned = balanceTransfer(TEST_TX_INFO);
const signingPayload = createSigningPayload(unsigned);
const signature = await signWithAlice(signingPayload);
const { signature } = createType(
registry,
'ExtrinsicPayload',
signingPayload,
{
version: unsigned.version
}
).sign(alice);
expect(signature).toEqual(
'0x003d0fdf8b55e6712b2766d80e9a4f527c3deb3d728a815db77df52d766643cdab3170e25ccd867a4d16c0a8f648b753cd95fed0b46ace6cc4e6e5942712409908'
const tx = createSignedTx(unsigned, signature, metadataRpc);
expect(tx).toBe(
'0x2d0284ffd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003d0fdf8b55e6712b2766d80e9a4f527c3deb3d728a815db77df52d766643cdab3170e25ccd867a4d16c0a8f648b753cd95fed0b46ace6cc4e6e5942712409908eb5808000600ff96074594cccf1cd185fa8a72ceaeefd86648f8d45514f3ce33c31bdd07e4655d30'
);
const tx = createSignedTx(unsigned, signature);
expect(tx).toBe('1324');
done();
});
});
import { createType, TypeRegistry } from '@polkadot/types';
import { createType, Metadata, TypeRegistry } from '@polkadot/types';
import { UnsignedTransaction } from './balanceTransfer';
/**
* Create the signing payload (i.e. the payload that needs to be signed) from
* an unsigned transaction.
* Serialize a signed transaction in a format that can be submitted over the
* Node RPC Interface from the signing payload and signature produced by the
* remote signer
*
* @param unsigned - The JSON representing the unsigned transaction
* @param signature - Signature of the signing payload produced by the remote signer
* @param signature - Signature of the signing payload produced by the remote
* signer
*/
export function createSignedTx(
unsigned: UnsignedTransaction,
signature: string
signature: string,
metadataRpc: string
): string {
const registry = new TypeRegistry();
registry.setMetadata(new Metadata(registry, metadataRpc));
const extrinsic = createType(
registry,
......
import { createSigningPayload } from './createSigningPayload';
import { balanceTransfer } from './balanceTransfer';
import { createSigningPayload } from './createSigningPayload';
import { TEST_TX_INFO } from './util/testUtil';
describe('createSigningPayload', () => {
......
......@@ -3,8 +3,8 @@ import { createType, TypeRegistry } from '@polkadot/types';
import { UnsignedTransaction } from './balanceTransfer';
/**
* Create the signing payload (i.e. the payload that needs to be signed) from
* an unsigned transaction.
* Construct the signing payload from an unsigned transaction and export it to
* a remote signer (this is often called "detached signing")
*
* @param unsigned - The JSON representing the unsigned transaction
*/
......
import { Keyring } from '@polkadot/api';
import { createType, TypeRegistry } from '@polkadot/types';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { balanceTransfer } from './balanceTransfer';
import { createSignedTx } from './createSignedTx';
import { createSigningPayload } from './createSigningPayload';
import { TEST_TX_INFO } from './util/testUtil';
import { balanceTransfer } from './balanceTransfer';
import { decodeTx } from './decodeTx';
import { metadataRpc, signWithAlice, TEST_TX_INFO } from './util/testUtil';
describe('parseTx', () => {
describe('decodeTx', () => {
it('should work', async done => {
// We're creating an Alice account that will sign the payload
// Wait for the promise to resolve async WASM
await cryptoWaitReady();
const registry = new TypeRegistry();
// Use ed25519 because it has deterministic signatures
const keyring = new Keyring({ type: 'ed25519' });
const alice = keyring.addFromUri('//Alice', { name: 'Alice default' });
const unsigned = balanceTransfer(TEST_TX_INFO);
const signingPayload = createSigningPayload(unsigned);
const signature = await signWithAlice(signingPayload);
const { signature } = createType(
registry,
'ExtrinsicPayload',
signingPayload,
{
version: unsigned.version
}
).sign(alice);
const tx = createSignedTx(unsigned, signature, metadataRpc);
const tx = createSignedTx(unsigned, signature);
const txInfo = decodeTx(tx, metadataRpc);
const txInfo = decodeTx(tx);
(['address', 'amount', 'nonce', 'tip', 'to'] as const).forEach(key =>
expect(txInfo[key]).toBe(TEST_TX_INFO[key])
);
expect(txInfo).toEqual(TEST_TX_INFO);
done();
});
});
import { createType, TypeRegistry } from '@polkadot/types';
import { Compact, createType, Metadata, TypeRegistry } from '@polkadot/types';
import { Balance } from '@polkadot/types/interfaces';
import { hexToU8a } from '@polkadot/util';
import { setSS58Format } from '@polkadot/util-crypto';
import { TxInfo } from './balanceTransfer';
import { TxInfo } from './balanceTransfer';
import { KUSAMA_SS58_FORMAT } from './util/constants';
/**
* Parse a signed transaction, and extract information about the transaction
* Parse the transaction information from an unsigned and signed transaction
* offline
*
* @param unsigned - The JSON representing the unsigned transaction
* @param metadataRpc - The SCALE-encoded metadata, as a hex string. Can be
* retrieved via the RPC call `state_getMetadata`
*/
export function decodeTx(signedTx: string): TxInfo {
export function decodeTx(
signedTx: string,
metadataRpc: string
): Partial<TxInfo> {
const registry = new TypeRegistry();
registry.setMetadata(new Metadata(registry, metadataRpc));
const tx = createType(registry, 'Extrinsic', hexToU8a(signedTx), {
isSigned: true
});
console.log('AAA', tx.method.argsDef);
setSS58Format(KUSAMA_SS58_FORMAT);
return {
address: tx.nonce.
address: tx.signer.toString(),
amount: (tx.method.args[1] as Compact<Balance>).toNumber(),
metadataRpc,
nonce: tx.nonce.toNumber(),
tip: tx.tip.toNumber(),
to: tx.method.args[0].toString()
};
}
import { encodeAddress } from '@polkadot/keyring';
const KUSAMA_SS58_FORMAT = 2;
import { KUSAMA_SS58_FORMAT } from './util/constants';
/**
* Derive an address from a cryptographic public key offline
......
export const KUSAMA_SS58_FORMAT = 2;
export const EXTRINSIC_VERSION = 4;
import { Keyring } from '@polkadot/api';
import metadataRpc from '@polkadot/metadata/Metadata/v9/static';
import { createType, TypeRegistry } from '@polkadot/types';
import { TRANSACTION_VERSION } from '@polkadot/types/primitive/Extrinsic/v4/Extrinsic';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { TxInfo } from '../balanceTransfer';
export { metadataRpc };
/**
* @ignore
*/
export const TEST_TX_INFO: TxInfo = {
address: 'HgWWnAXFGikrPVD2FrZ6CRk7KnYdVDn7zVyye8hqFPMc5g1',
address: 'HNZata7iMYWmk5RvZRTiAsSDhV8366zq2YGb3tLH5Upf74F', // seed "//Alice"
amount: 12,
blockHash:
'0x1fc7493f3c1e9ac758a183839906475f8363aafb1b1d3e910fe16fab4ae1b582',
......@@ -16,3 +25,28 @@ export const TEST_TX_INFO: TxInfo = {
tip: 0,
to: 'Fy2rsYCoowQBtuFXqLE65ehAY9T6KWcGiNCQAyPDCkfpm4s'
};
/**
* @ignore
*/
export async function signWithAlice(signingPayload: string): Promise<string> {
// We're creating an Alice account that will sign the payload
// Wait for the promise to resolve async WASM
await cryptoWaitReady();
const registry = new TypeRegistry();
// Use ed25519 because it has deterministic signatures
const keyring = new Keyring({ type: 'ed25519' });
const alice = keyring.addFromUri('//Alice', { name: 'Alice default' });
const { signature } = createType(
registry,
'ExtrinsicPayload',
signingPayload,
{
version: TRANSACTION_VERSION
}
).sign(alice);
return signature;
}
Supports Markdown
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