Skip to content

Commit

Permalink
Read state according to new allocation logic (#843)
Browse files Browse the repository at this point in the history
  • Loading branch information
osipov-mit authored Jul 18, 2022
1 parent 56d556b commit ba0f70e
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 39 deletions.
36 changes: 29 additions & 7 deletions api/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
{
"root": true,
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"plugins": [
"@typescript-eslint"
],
"rules": {
"indent": ["warn", 2, { "SwitchCase": 1, "flatTernaryExpressions": false }],
"linebreak-style": ["error", "unix"],
"quotes": ["warn", "single"],
"semi": ["error", "always"],
"indent": [
"warn",
2,
{
"SwitchCase": 1,
"flatTernaryExpressions": false
}
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"warn",
"single"
],
"semi": [
"error",
"always"
],
"@typescript-eslint/no-empty-function": 0,
"no-case-declarations": 0
}
}
}
11 changes: 11 additions & 0 deletions api/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 0.23.5

_07/18/2022_

https://github.com/gear-tech/gear-js/pull/843

### Changes

- Fix getting program pages according to https://github.com/gear-tech/gear/pull/1193

---
## 0.23.4

_07/18/2022_
Expand Down
4 changes: 2 additions & 2 deletions api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gear-js/api",
"version": "0.23.4",
"version": "0.23.5",
"description": "A JavaScript library that provides functionality to connect GEAR Component APIs.",
"main": "cjs/index.js",
"module": "index.js",
Expand Down
27 changes: 26 additions & 1 deletion api/src/Code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Bytes, Option } from '@polkadot/types';

import { GearTransaction } from './Transaction';
import { generateCodeHash, validateCodeId } from './utils';
import { CodeMetadata, Hex } from './types';
import { CodeMetadata, CodeStorage, Hex } from './types';
export class GearCode extends GearTransaction {
/**
* Submit code without initialization
Expand All @@ -20,8 +20,33 @@ export class GearCode extends GearTransaction {
return { codeHash, submitted: this.submitted };
}

/**
* Check that codeId exists on chain
* @param codeId
* @returns
*/
async exists(codeId: string) {
const codeMetadata = await this.api.query.gearProgram.metadataStorage(codeId) as Option<CodeMetadata>;
return codeMetadata.isSome;
}


/**
* Get code storage
* @param codeId
* @returns
*/
async storage(codeId: Hex): Promise<CodeStorage> {
return this.api.query.gearProgram.codeStorage(codeId) as unknown as CodeStorage;
}

/**
* Get static pages of code
* @param codeId
* @returns
*/
async staticPages(codeId: Hex): Promise<number | null> {
const storage = await this.storage(codeId);
return storage.isSome ? storage.unwrap().staticPages.toNumber() : null;
}
}
13 changes: 13 additions & 0 deletions api/src/Program.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AnyJson, ISubmittableResult } from '@polkadot/types/types';
import { SubmittableExtrinsic } from '@polkadot/api/types';
import { randomAsHex } from '@polkadot/util-crypto';
import { u8aToHex } from '@polkadot/util';
import { Bytes } from '@polkadot/types';

import { createPayload, generateProgramId, GPROG, GPROG_HEX, validateGasLimit, validateValue } from './utils';
Expand Down Expand Up @@ -85,4 +86,16 @@ export class GearProgram extends GearTransaction {
const program = progs.find((prog) => prog.eq(`0x${GPROG_HEX}${id.slice(2)}`));
return Boolean(program);
}



/**
* Get codeHash of program on-chain
* @param programId
* @returns codeHash in hex format
*/
async codeHash(programId: Hex): Promise<Hex> {
const program = await this.api.storage.gProg(programId);
return u8aToHex(program.code_hash);
}
}
13 changes: 9 additions & 4 deletions api/src/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,18 @@ export class GearProgramState extends GearStorage {
* @returns decoded state
*/
async read(programId: Hex, metaWasm: Buffer, inputValue?: AnyJson): Promise<Codec> {
const codeHash = await this.api.program.codeHash(programId);
let initialSize = await this.api.code.staticPages(codeHash);

const program = await this.gProg(programId);
if (!program) {
throw new ReadStateError('Program is terminated');
}

program.allocations.forEach((value) => {
if (value.gtn(initialSize - 1)) {
initialSize = value.toNumber();
}
});

const pages = await this.gPages(programId, program);
const initialSize = program.allocations.size;
const block = await this.api.blocks.getFinalizedHead();
const blockTimestamp = await this.api.blocks.getBlockTimestamp(block.toHex());

Expand Down
24 changes: 8 additions & 16 deletions api/src/Storage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Option, Raw } from '@polkadot/types';
import { Codec } from '@polkadot/types/types';
import { u8aToHex } from '@polkadot/util';

import { IActiveProgram, IGearPages, IProgram, Hex } from './types';
import { ActiveProgram, IGearPages, IProgram, Hex } from './types';
import { GPAGES_HEX, GPROG_HEX, SEPARATOR } from './utils';
import { CreateType } from './create-type';
import { ReadStateError } from './errors';
import { ProgramTerminatedError, ReadStateError } from './errors';
import { GearApi } from './GearApi';

export class GearStorage {
Expand All @@ -21,13 +20,16 @@ export class GearStorage {
* @param programId
* @returns
*/
async gProg(programId: Hex): Promise<IActiveProgram> {
async gProg(programId: Hex): Promise<ActiveProgram> {
const storage = (await this.api.rpc.state.getStorage(`0x${GPROG_HEX}${programId.slice(2)}`)) as Option<Raw>;
if (storage.isNone) {
throw new ReadStateError(`Program with id ${programId} was not found in the storage`);
}
const program = this.api.createType('Program', storage.unwrap()) as IProgram;
return program.isActive ? program.asActive : program.asTerminated;

if (program.isTerminated) throw new ProgramTerminatedError();

return program.asActive;
}

/**
Expand All @@ -36,7 +38,7 @@ export class GearStorage {
* @param gProg
* @returns
*/
async gPages(programId: Hex, gProg: IActiveProgram): Promise<IGearPages> {
async gPages(programId: Hex, gProg: ActiveProgram): Promise<IGearPages> {
const keys = {};
gProg.pages_with_data.forEach((value) => {
keys[value.toNumber()] = `0x${GPAGES_HEX}${programId.slice(2)}${SEPARATOR}${this.api
Expand All @@ -51,14 +53,4 @@ export class GearStorage {
}
return pages;
}

/**
* Get codeHash of program on-chain
* @param programId
* @returns codeHash in hex format
*/
async getCodeHash(programId: Hex): Promise<Hex> {
const program = await this.gProg(programId);
return u8aToHex(program.code_hash);
}
}
16 changes: 16 additions & 0 deletions api/src/errors/program.errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,26 @@ export class SubmitProgramError extends Error {
}
}

export class ProgramDoesNotExistError extends Error {
name = 'ProgramDoesNotExist';

constructor() {
super('Program does not exist');
}
}

export class GetGasSpentError extends Error {
name = 'GetGasSpentError';

constructor(message?: string) {
super(`Unable to get gasSpent. ${message}` || 'Unable to get gasSpent. Params are invalid');
}
}

export class ProgramTerminatedError extends Error {
name = 'ProgramTerminated';

constructor() {
super('Program terminated');
}
}
2 changes: 2 additions & 0 deletions api/src/types/interfaces/message/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ export interface Entry extends Enum {
asHandle: Null;
asReply: MessageId;
}

export type DispatchKind = Entry
7 changes: 4 additions & 3 deletions api/src/types/interfaces/program/base.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Enum, u32, u64, Map, BTreeSet } from '@polkadot/types';

import { MessageId } from '../ids';
import { WasmPageNumber } from './pages';

export interface IProgram extends Enum {
isActive: boolean;
asActive: IActiveProgram;
asActive: ActiveProgram;
isTerminated: boolean;
asTerminated: null;
}

export interface IActiveProgram extends Map {
allocations: BTreeSet<u32>;
export interface ActiveProgram extends Map {
allocations: BTreeSet<WasmPageNumber>;
pages_with_data: BTreeSet<u32>;
code_hash: Uint8Array;
nonce: u64;
Expand Down
17 changes: 14 additions & 3 deletions api/src/types/interfaces/program/code.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { Codec } from '@polkadot/types-codec/types';
import { H256 } from '@polkadot/types/interfaces';
import { u32 } from '@polkadot/types';
import { H256, } from '@polkadot/types/interfaces';
import { u8, u32, Option, BTreeSet, Vec } from '@polkadot/types';
import { WasmPageNumber } from './pages';
import { DispatchKind } from '../message';

export interface CodeMetadata extends Codec {
author: H256,
blockNumber: u32
}
}

export interface InstrumentedCode extends Codec {
code: Vec<u8>,
exports: BTreeSet<DispatchKind>,
staticPages: WasmPageNumber,
version: u32
}

export type CodeStorage = Option<InstrumentedCode>;
4 changes: 4 additions & 0 deletions api/src/types/interfaces/program/pages.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { u32 } from '@polkadot/types';

export interface IGearPages {
[key: string]: Uint8Array;
}

export type WasmPageNumber = u32;
19 changes: 18 additions & 1 deletion api/test/State.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const demo_meta_test = {
meta: readFileSync(join(GEAR_EXAMPLES_WASM_DIR, 'demo_meta.meta.wasm')),
id: '0x' as Hex,
uploadBlock: '0x',
codeHash: '0x' as Hex,
};
const timestamp_test = {
code: readFileSync(join(TEST_WASM_DIR, 'timestamp.opt.wasm')),
Expand All @@ -28,7 +29,7 @@ beforeAll(async () => {
gasLimit: 2_000_000_000,
}).programId;
let initStatus = checkInit(api, timestamp_test.id);
api.program.signAndSend(alice, () => {});
api.program.signAndSend(alice, () => { });
expect(await initStatus()).toBe('success');

demo_meta_test.id = api.program.submit(
Expand Down Expand Up @@ -69,6 +70,22 @@ describe('Read State', () => {
expect(gPages).toBeDefined();
});

test('Get codeHash', async () => {
demo_meta_test.codeHash = await api.program.codeHash(demo_meta_test.id);
expect(demo_meta_test.codeHash).toBeDefined();
expect(demo_meta_test.codeHash.startsWith('0x')).toBeTruthy();
});

test('Get code storage', async () => {
const codeStorage = await api.code.storage(demo_meta_test.codeHash);
expect(codeStorage.isSome).toBeTruthy();
const unwrappedCodeStorage = codeStorage.unwrap().toHuman();
expect(unwrappedCodeStorage).toHaveProperty('code');
expect(unwrappedCodeStorage).toHaveProperty('exports');
expect(unwrappedCodeStorage).toHaveProperty('staticPages');
expect(unwrappedCodeStorage).toHaveProperty('version');
});

test('Get nonexistent program from storage', async () => {
await expect(
api.storage.gProg('0x0000000000000000000000000000000000000000000000000000000000000000'),
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"website/common"
],
"lint-staged": {
"*.{js,css,ts,tsx,scss}": "eslint --fix"
"*.{js,css,ts,tsx,scss}": "eslint --fix",
"./api/*.{js,ts}": "cd api && npm run lint:fix"
},
"resolutions": {
"@types/react": "17.0.44"
Expand Down

0 comments on commit ba0f70e

Please sign in to comment.