Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# 0.82.0-beta.x

- **Breaking change** The `ContractsAbi` type has been moved from `@polkadot/types` to `import { Abi } from '@polkadot/api-contract`. This paves the way for an enhanced contracts interface, instead of dealing with low-level API calls.
- **Breaking change** `usize` is now a blacklisted type that will throw on construction. Since it is platform-specific, it creates incompatibilities between native (generally u64) and WASM (always u32) code. Use one of the `u32` or `u64` types explicitly.
- **Breaking change** `api.derive.contract` is now `api.derive.contracts` to align with the substrate 2.x rename. (Feture detection is used so it supports both 1.x and 2.x chains)
- **Breaking change** The api now uses the module name instead of the prefix to generate the storage methods. The methods of the grandpa module changed from `api.query.grandpaFinality` to `api.query.grandpa`.
- Update with latest substrate 2.x types
- **Breaking Change** StorageFunction has been renamed to StorageEntry.
- **Breaking Change** `@polkadot/extrinsics` and `@polkadot/storage` have been moved to `@polkadot/api-metadata` and are now accessible as `@polkadot/api-metadata/extrinsics` and `@polkadot/api-metadata/storage`, respectively.
- **Breaking Change** Vote interface extends U8a instead of I8. Vote properties can be accessed via the `isAye`, `isNay`, and `conviction` getters. Votes can still be constructed as before with a raw JS boolean, a SCALE encoded Boolean, an i8 number, or a JS object with properties `aye` and `conviction` defined.
- Support latest substrate 2.x v6 metadata with module constants using `api.consts`.
- Support V2 Extrinsics in addition to V1
- Addition of `api.derive.elections`
- `usize` is now a blacklisted type that will throw on construction. Since it is platform-specific, it creates incompatibilities between native (generally u64) and WASM (always u32) code. Use one of the `u32` or `u64` types explicitly.

# 0.81.1

Expand Down
18 changes: 1 addition & 17 deletions packages/api-metadata/src/extrinsics/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,6 @@ import extrinsics from './static';
const keyring = testingPairs({ type: 'ed25519' }, false);

describe('extrinsics', (): void => {
it('encodes extrinsic correctly (nobody)', (): void => {
Copy link
Member Author

@jacogr jacogr Jul 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I whacked this, technically a backwards incompatible change, however we have not had the nobody account in the system since poc-2 - so actually the test is bullsh*t. (inherents are now just unsigned, not with a nobody sig)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just remove it, then? Are we still supporting POC-2?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which is why the test was removed. (It relied on an unsupported all-0 sig)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, right, monday morning comment...

expect(
new Extrinsic(
extrinsics.timestamp.set(10101)
).sign(
keyring.nobody,
{
blockHash: new Uint8Array(),
nonce: 1234
}
).toU8a(true)
).toEqual(new Uint8Array([
129, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 19, 0, 2, 0, 213, 157
]));
});

it('encodes an actual transfer (actual data)', (): void => {
expect(
new Extrinsic(
Expand All @@ -41,7 +25,7 @@ describe('extrinsics', (): void => {
'ffd172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f' + // who
'fa4c192f6960a3bcdbdee5bcd9c26a3f971b131081912abcc31eab6a0b7589ab' + // sig1
'7f99b81a01738cb5e2a911a19d5daa5c0b654d4b8dbc521a6b29090c6d205903' + // sig2
'0000' + // nonce
'0000' + // nonce & era
'0500' + // balances.transfer
'ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9' + // to
'e56c' // value
Expand Down
34 changes: 27 additions & 7 deletions packages/api/src/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import { Storage } from '@polkadot/api-metadata/storage/types';
import storageFromMeta from '@polkadot/api-metadata/storage/fromMetadata';
import RpcCore from '@polkadot/rpc-core';
import { WsProvider } from '@polkadot/rpc-provider';
import { Event, getTypeRegistry, Hash, Metadata, Method, RuntimeVersion, Null, U64 } from '@polkadot/types';
import { Event, getTypeRegistry, Hash, Metadata, Method, RuntimeVersion, SignedBlock, Null, U64 } from '@polkadot/types';
import Linkage, { LinkageResult } from '@polkadot/types/codec/Linkage';
import { DEFAULT_VERSION as EXTRINSIC_DEFAULT_VERSION } from '@polkadot/types/primitive/Extrinsic/constants';
import { MethodFunction, ModulesWithMethods } from '@polkadot/types/primitive/Method';
import * as srmlTypes from '@polkadot/types/srml/definitions';
import { StorageEntry } from '@polkadot/types/primitive/StorageKey';
Expand Down Expand Up @@ -72,6 +73,8 @@ export default abstract class ApiBase<ApiType> {

private _extrinsics?: SubmittableExtrinsics<ApiType>;

private _extrinsicType: number = EXTRINSIC_DEFAULT_VERSION;

private _genesisHash?: Hash;

protected _isConnected: BehaviorSubject<boolean>;
Expand Down Expand Up @@ -150,6 +153,13 @@ export default abstract class ApiBase<ApiType> {
this.init();
}

/**
* @description Returns th version of extrinsics in-use on this chain
*/
public get extrinsicVersion (): number {
return this._extrinsicType;
}

/**
* @description Contains the genesis Hash of the attached chain. Apart from being useful to determine the actual chain, it can also be used to sign immortal transactions.
*/
Expand Down Expand Up @@ -498,7 +508,9 @@ export default abstract class ApiBase<ApiType> {
this._rpcCore.chain.getBlockHash(0).toPromise(),
this._rpcCore.chain.getRuntimeVersion().toPromise()
]);

const metadataKey = `${this._genesisHash}-${(this._runtimeVersion as RuntimeVersion).specVersion}`;

if (metadataKey in metadata) {
this._runtimeMetadata = new Metadata(metadata[metadataKey]);
} else {
Expand All @@ -508,6 +520,7 @@ export default abstract class ApiBase<ApiType> {
// get unique types & validate
this.runtimeMetadata.getUniqTypes(false);
} else {
this._extrinsicType = this._options.source.extrinsicVersion;
this._runtimeMetadata = this._options.source.runtimeMetadata;
this._runtimeVersion = this._options.source.runtimeVersion;
this._genesisHash = this._options.source.genesisHash;
Expand All @@ -517,22 +530,29 @@ export default abstract class ApiBase<ApiType> {
const storage = storageFromMeta(this.runtimeMetadata);
const constants = constantsFromMeta(this.runtimeMetadata);

// only inject if we are not a clone (global init)
if (!this._options.source) {
Event.injectMetadata(this.runtimeMetadata);
Method.injectMethods(extrinsics);

// detect the extrinsic version in-use based on the last block
const lastBlock: SignedBlock = await this._rpcCore.chain.getBlock().toPromise();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now determine the extrinsic version from the chain - basically, based on the last block, we pull out the version from contained extrinsics and then use this in any future construction of this type.


this._extrinsicType = lastBlock.block.extrinsics[0].type;
}

this._extrinsics = this.decorateExtrinsics(extrinsics, this.decorateMethod);
this._query = this.decorateStorage(storage, this.decorateMethod);
this._consts = constants;

this._rx.extrinsicType = this._extrinsicType;
this._rx.genesisHash = this._genesisHash;
this._rx.runtimeVersion = this._runtimeVersion;
this._rx.tx = this.decorateExtrinsics(extrinsics, rxDecorateMethod);
this._rx.query = this.decorateStorage(storage, rxDecorateMethod);
this._rx.consts = constants;
this._derive = this.decorateDerive(this._rx as ApiInterfaceRx, this.decorateMethod);

// only inject if we are not a clone (global init)
if (!this._options.source) {
Event.injectMetadata(this.runtimeMetadata);
Method.injectMethods(extrinsics);
}
this._derive = this.decorateDerive(this._rx as ApiInterfaceRx, this.decorateMethod);

return true;
}
Expand Down
77 changes: 77 additions & 0 deletions packages/api/src/SignerPayload.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2017-2019 @polkadot/api 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 extrinsics from '@polkadot/api-metadata/extrinsics/static';
import { Method, ExtrinsicEra, SignaturePayload } from '@polkadot/types';

import SignerPayload from './SignerPayload';

describe('SignerPayload', (): void => {
const TEST = {
address: '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE',
blockHash: '0xde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7',
blockNumber: '0x0000000000231d30',
era: '0x0703',
genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b',
method: '0x0500ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c',
nonce: '0x0000000000001234',
tip: '0x00000000000000000000000000005678',
version: 2
};

beforeEach((): void => {
Method.injectMethods(extrinsics);
});

it('creates a valid JSON output', (): void => {
expect(
new SignerPayload({
address: '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE',
blockHash: '0xde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7',
blockNumber: '0x231d30',
era: new ExtrinsicEra({ current: 2301232, period: 200 }),
genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b',
method: new Method('0x0500ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c'),
nonce: 0x1234,
tip: 0x5678,
version: 2
}).toPayload()
).toEqual({
address: '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE',
blockHash: '0xde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7',
blockNumber: '0x0000000000231d30',
era: '0x0703',
genesisHash: '0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b',
method: '0x0500ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c',
nonce: '0x0000000000001234',
tip: '0x00000000000000000000000000005678',
version: 2
});
});

it('re-constructs from JSON', (): void => {
expect(
new SignerPayload(TEST).toPayload()
).toEqual(TEST);
});

it('re-constructs from itself', (): void => {
expect(
new SignerPayload(
new SignerPayload(TEST)
).toPayload()
).toEqual(TEST);
});

it('can be used as a feed to SignaturePayload', (): void => {
const signer = new SignerPayload(TEST).toPayload();
const payload = new SignaturePayload(signer, { version: signer.version });

expect(payload.era.toHex()).toEqual(TEST.era);
expect(payload.method.toHex()).toEqual(TEST.method);
expect(payload.blockHash.toHex()).toEqual(TEST.blockHash);
expect(payload.nonce.eq(TEST.nonce)).toBe(true);
expect(payload.tip.eq(TEST.tip)).toBe(true);
});
});
57 changes: 57 additions & 0 deletions packages/api/src/SignerPayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2017-2019 @polkadot/api 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 { SignerPayload as ISignerPayload } from './types';

import { Address, Balance, BlockNumber, Compact, ExtrinsicEra, Hash, Nonce, Struct, U8, Method } from '@polkadot/types';

export interface SignerPayloadType {
address: Address;
blockHash: Hash;
blockNumber: BlockNumber;
era: ExtrinsicEra;
genesisHash: Hash;
method: Method;
nonce: Compact<Nonce>;
tip: Compact<Balance>;
version: U8;
}

export default class SignerPayload extends Struct.with({
address: Address,
blockHash: Hash,
blockNumber: BlockNumber,
era: ExtrinsicEra,
genesisHash: Hash,
method: Method,
nonce: Compact.with(Nonce),
tip: Compact.with(Balance),
version: U8
}) {
/**
* @description Returns this as a SignerPayloadType. This works since the Struct.with injects all the getters automatically (just ensure the 2 definitiona are matching)
*/
public get self (): SignerPayloadType {
return this as any as SignerPayloadType;
}

/**
* @description Creates an representation of the structure as an ISignerPayload JSON
*/
public toPayload (): ISignerPayload {
const { address, blockHash, blockNumber, era, genesisHash, method, nonce, tip, version } = this.self;

return {
address: address.toString(),
blockHash: blockHash.toHex(),
blockNumber: blockNumber.toHex(),
era: era.toHex(),
genesisHash: genesisHash.toHex(),
method: method.toHex(),
nonce: nonce.toHex(),
tip: tip.toHex(),
version: version.toNumber()
};
}
}
50 changes: 40 additions & 10 deletions packages/api/src/SubmittableExtrinsic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

import { AccountId, Address, ExtrinsicStatus, EventRecord, getTypeRegistry, Hash, Header, Index, Method, SignedBlock, Vector, ExtrinsicEra } from '@polkadot/types';
import { AnyNumber, AnyU8a, Callback, Codec, IExtrinsic, IExtrinsicEra, IKeyringPair, SignatureOptions } from '@polkadot/types/types';
import { ApiInterfaceRx, ApiTypes } from './types';

import BN from 'bn.js';
import { Observable, combineLatest, of } from 'rxjs';
import { first, map, mergeMap, switchMap, tap } from 'rxjs/operators';
import { AccountId, Address, ExtrinsicStatus, EventRecord, getTypeRegistry, Hash, Header, Index, Method, SignedBlock, Vector, ExtrinsicEra } from '@polkadot/types';
import { isBn, isFunction, isNumber, isUndefined } from '@polkadot/util';

import ApiBase from './Base';
import filterEvents from './util/filterEvents';
import ApiBase from './Base';
import SignerPayload from './SignerPayload';

// eslint-disable-next-line @typescript-eslint/interface-name-prefix
export interface ISubmittableResult {
Expand Down Expand Up @@ -44,10 +45,14 @@ interface SignerOptions {
blockHash: AnyU8a;
era?: IExtrinsicEra | number;
nonce: AnyNumber;
tip?: AnyNumber;
}

// pick a default - in the case of 4s blocktimes, this translates to 60 seconds
const ONE_MINUTE = 15;
// The default for 6s allowing for 5min eras. When translating this to faster blocks -
// - 4s = (10 / 15) * 5 = 3.33m
// - 2s = (10 / 30) * 5 = 1.66m
const BLOCKTIME = 6;
const ONE_MINUTE = 60 / BLOCKTIME;
const DEFAULT_MORTAL_LENGTH = 5 * ONE_MINUTE;

function isKeyringPair (account: string | IKeyringPair | AccountId | Address): account is IKeyringPair {
Expand Down Expand Up @@ -107,7 +112,7 @@ export default function createSubmittableExtrinsic<ApiType> (
extrinsic: Method | Uint8Array | string,
trackingCb?: Callback<ISubmittableResult>
): SubmittableExtrinsic<ApiType> {
const _extrinsic = new (getTypeRegistry().getOrThrow('Extrinsic'))(extrinsic) as SubmittableExtrinsic<ApiType>;
const _extrinsic = new (getTypeRegistry().getOrThrow('Extrinsic'))(extrinsic, { version: api.extrinsicType }) as SubmittableExtrinsic<ApiType>;
const _noStatusCb = type === 'rxjs';

function updateSigner (updateId: number, status: Hash | ISubmittableResult): void {
Expand Down Expand Up @@ -257,14 +262,39 @@ export default function createSubmittableExtrinsic<ApiType> (
mergeMap(async ([nonce, header]): Promise<void> => {
const eraOptions = expandEraOptions(options, { header, nonce });

// FIXME This is becoming real messy with all the options - way past
// "a method should fit on a single screen" stage. (Probably want to
// clean this when we remove `api.signer.sign` in the next beta cycle)
if (isKeyringPair(account)) {
this.sign(account, eraOptions);
} else if (api.signer) {
updateId = await api.signer.sign(_extrinsic, address, {
...eraOptions,
blockNumber: header ? header.blockNumber : new BN(0),
genesisHash: api.genesisHash
});
if (api.signer.signPayload) {
const signPayload = new SignerPayload({
...eraOptions,
address,
method: _extrinsic.method,
blockNumber: header ? header.blockNumber : 0,
genesisHash: api.genesisHash,
version: api.extrinsicType
});
const result = await api.signer.signPayload(signPayload.toPayload());

// Here we explicitly call `toPayload()` again instead of working with an object
// (reference) as passed to the signer. This means that we are sure that the
// payload data is not modified from our inputs, but the signer
_extrinsic.addSignature(address, result.signature, signPayload.toPayload());
updateId = result.id;
} else if (api.signer.sign) {
console.warn('The Signer.sign interface is deprecated and will be removed in a future version, Swap to using the Signer.signPayload interface instead.');

updateId = await api.signer.sign(_extrinsic, address, {
...eraOptions,
blockNumber: header ? header.blockNumber : new BN(0),
genesisHash: api.genesisHash
});
} else {
throw new Error('Invalid signer interface');
}
} else {
throw new Error('no signer exists');
}
Expand Down
Loading