Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support upload_code for vouchers #1472

Merged
merged 2 commits into from
Jan 22, 2024
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
1 change: 1 addition & 0 deletions api/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ test/wasm/examples/

.rollup.cache
.yarnrc.yaml
temp/
3 changes: 3 additions & 0 deletions api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ https://github.com/gear-tech/gear-js/pull/1467
- Support new vouchers according to https://github.com/gear-tech/gear/pull/3606
- Bump polkadot dependencies to `10.11.2`

https://github.com/gear-tech/gear-js/pull/1472
- Support `uploadCode` call for vouchers.

## 0.35.2

_10/31/2023_
Expand Down
14 changes: 13 additions & 1 deletion api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,9 @@ const programs = ['0x1234...', '0x5678...'];
const spenderAddress = '0x...';
const validForOneHour = (60 * 60) / 3; // number of blocks in one hour

const { voucherId, extrinsic } = await api.voucher.issue(spenderAddress, 100 * 10 ** 12, validForOneHour, programs);
const { voucherId, extrinsic } = await api.voucher.issue(spenderAddress, 100 * 10 ** 12, validForOneHour, programs, true);

// To allow the voucher to be used for code uploading, set the last argument of the `.issue` method to true

extrinsic.signAndSend(account, (events) => {
const voucherIssuedEvent = events.events.filter(({event: {method}}) => method === 'VoucherIssued') as VoucherIssued;
Expand Down Expand Up @@ -404,6 +406,16 @@ await voucherTx.signAndSend(account, (events) => {
});
```

#### Upload code with issued voucher
```javascript
const { extrinsic } = await api.code.upload(code);

const tx = api.voucher.call(voucherId, { UploadCode: extrinsic })
await tx.signAndSend(account, (events) => {
console.log(events.toHuman());
});
```

#### Update voucher
The `api.voucher.update` can be used to update the voucher. All parameters in the 3rd argument are optional, but at least one parameter must be specified.
```javascript
Expand Down
7 changes: 3 additions & 4 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@
"test": "jest --runInBand",
"test:only": "jest --testPathPattern",
"build": "rm -rf lib && rollup --config rollup.config.js",
"generate:defs": "ts-node --skip-project node_modules/.bin/polkadot-types-from-defs --package sample-polkadotjs-typegen/interfaces --input ./src/interfaces --endpoint ./gear.json",
"generate:meta": "ts-node --skip-project node_modules/.bin/polkadot-types-from-chain --package sample-polkadotjs-typegen/interfaces --endpoint ./gear.json --output ./src/interfaces",
"generate:defs": "ts-node --skip-project node_modules/.bin/polkadot-types-from-defs --package sample-polkadotjs-typegen/interfaces --input ./temp --endpoint ws://127.0.0.1:9944",
"generate:meta": "ts-node --skip-project node_modules/.bin/polkadot-types-from-chain --package sample-polkadotjs-typegen/interfaces --endpoint ws://127.0.0.1:9944 --output ./temp",
"lint": "eslint . --ext .ts --ignore-pattern lib/ --ignore-pattern node_modules/",
"lint:fix": "eslint --fix . --ext .ts --ignore-pattern lib/ --ignore-pattern node_modules/",
"get:metadata": "curl -H \"Content-Type: application/json\" -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9944 > gear.json"
"lint:fix": "eslint --fix . --ext .ts --ignore-pattern lib/ --ignore-pattern node_modules/"
},
"exports": {
"./cjs/package.json": "./cjs/package.json",
Expand Down
8 changes: 8 additions & 0 deletions api/programs/Cargo.lock

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

1 change: 1 addition & 0 deletions api/programs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"test-gas",
"test-waitlist",
"test-meta",
"empty"
]

[profile.release]
Expand Down
11 changes: 11 additions & 0 deletions api/programs/empty/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "empty"
version = "0.1.0"
authors = ["Gear Technologies"]
edition = "2021"

[dependencies]
gstd = { git = "https://github.com/gear-tech/gear.git" }

[build-dependencies]
gear-wasm-builder = { git = "https://github.com/gear-tech/gear.git" }
3 changes: 3 additions & 0 deletions api/programs/empty/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
gear_wasm_builder::build();
}
8 changes: 8 additions & 0 deletions api/programs/empty/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![no_std]

use gstd::msg;

#[no_mangle]
unsafe extern "C" fn init() {
msg::reply("ok", 0).unwrap();
}
10 changes: 3 additions & 7 deletions api/src/Code.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { Bytes, Option } from '@polkadot/types';
import { GearCommonCodeMetadata, GearCoreCodeInstrumentedCode } from './types';
import { HexString } from '@polkadot/util/types';
import { ISubmittableResult } from '@polkadot/types/types';
import { SubmittableExtrinsic } from '@polkadot/api/types';
import { u8aToHex } from '@polkadot/util';

import { CodeUploadResult, GearCommonCodeMetadata, GearCoreCodeInstrumentedCode } from './types';
import { generateCodeHash, getIdsFromKeys, validateCodeId } from './utils';
import { CodeDoesNotExistError } from './errors';
import { GearTransaction } from './Transaction';
Expand All @@ -16,15 +14,13 @@ export class GearCode extends GearTransaction {
* @param code
* @returns Code hash
*/
async upload(
code: Buffer | Uint8Array,
): Promise<{ codeHash: HexString; submitted: SubmittableExtrinsic<'promise', ISubmittableResult> }> {
async upload(code: Buffer | Uint8Array): Promise<CodeUploadResult> {
const codeHash = generateCodeHash(code);
await validateCodeId(codeHash, this._api);

const codeBytes = this._api.createType('Bytes', Array.from(code)) as Bytes;
this.extrinsic = this._api.tx.gear.uploadCode(codeBytes);
return { codeHash, submitted: this.extrinsic };
return { codeHash, submitted: this.extrinsic, extrinsic: this.extrinsic };
}

/**
Expand Down
4 changes: 2 additions & 2 deletions api/src/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export class GearMessage extends GearTransaction {
* @param args Message parameters
* @param metaOrHexRegistry (optional) Registry in hex format or ProgramMetadata
* @param typeName payload type (one of the default rust types if metadata or registry don't specified)
* @returns Submitable result
* @returns Submitable extrinsic
*
* _Note that parameter `prepaid` is not supported starting from `1010` runtime version._
* @example
Expand Down Expand Up @@ -231,7 +231,7 @@ export class GearMessage extends GearTransaction {
* @param args Message parameters
* @param metaOrHexRegistry Metadata
* @param typeIndexOrTypeName type index in registry or type name
* @returns Submitted result
* @returns Submittable result
*/

async sendReply(
Expand Down
35 changes: 30 additions & 5 deletions api/src/Voucher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ export class GearVoucher extends GearTransaction {
* ### Issue a new voucher for a `user` to be used to pay for sending messages to `program_id` program.
* @param spender The voucher holder account id.
* @param value The voucher amount.
* @param validity The number of the block until which the voucher is valid.
* @param duration (optional) The number of the block until which the voucher is valid. If not specified, the voucher is valid in `api.voucher.minDuration` blocks.
* @param programs (optional) The list of programs that the voucher can be used for. If not specified, the voucher can be used for any program.
* @param codeUploading (optional) Whether the voucher can be used for uploading code.
* @returns The voucher id and the extrinsic to submit.
*
* @example
Expand All @@ -32,15 +33,22 @@ export class GearVoucher extends GearTransaction {
async issue(
spender: HexString,
value: number | bigint | BalanceOf,
validity: number,
duration?: number,
programs?: HexString[],
codeUploading = false,
): Promise<{ extrinsic: SubmittableExtrinsic<'promise', ISubmittableResult>; voucherId: HexString }> {
const nonce = await this._api.query.gearVoucher.issued();

const nextNonce = nonce.unwrapOrDefault().addn(1).toArray('le', 8);
const voucherId = generateVoucherId(nextNonce);

this.extrinsic = this._api.tx.gearVoucher.issue(spender, value, programs || null, validity);
this.extrinsic = this._api.tx.gearVoucher.issue(
spender,
value,
programs || null,
codeUploading,
duration || this.minDuration,
);
return { extrinsic: this.extrinsic, voucherId };
}

Expand Down Expand Up @@ -101,6 +109,16 @@ export class GearVoucher extends GearTransaction {
return this._api.tx.gearVoucher.call(voucherId, {
SendReply: { replyToId, payload, gasLimit, value, keepAlive },
});
} else if ('UploadCode' in params) {
if (params.UploadCode.method.method !== 'uploadCode') {
throw new Error(`Invalid method name. Expected 'UploadCode' but actual is ${params.UploadCode.method.method}`);
}

const [code] = params.UploadCode.args;

return this._api.tx.gearVoucher.call(voucherId, {
UploadCode: { code },
});
}

throw new Error('Invalid call params');
Expand Down Expand Up @@ -173,7 +191,13 @@ export class GearVoucher extends GearTransaction {
voucherId: string,
params: IUpdateVoucherParams,
): SubmittableExtrinsic<'promise', ISubmittableResult> {
if (!params.moveOwnership && !params.balanceTopUp && !params.appendPrograms && !params.prolongValidity) {
if (
!params.moveOwnership &&
!params.balanceTopUp &&
!params.appendPrograms &&
!params.prolongDuration &&
(params.codeUploading === undefined || params.codeUploading === null)
) {
throw new Error('At least one of the parameters must be specified');
}
return this._api.tx.gearVoucher.update(
Expand All @@ -182,7 +206,8 @@ export class GearVoucher extends GearTransaction {
params.moveOwnership || null,
params.balanceTopUp || null,
params.appendPrograms || null,
params.prolongValidity || null,
params.codeUploading || null,
params.prolongDuration || null,
);
}

Expand Down
8 changes: 8 additions & 0 deletions api/src/types/augment/augment-api-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ declare module '@polkadot/api-base/types/errors' {
* Error trying transfer balance to/from voucher account.
**/
BalanceTransfer: AugmentedError<ApiType>;
/**
* Voucher is disabled for code uploading, but requested.
**/
CodeUploadingDisabled: AugmentedError<ApiType>;
/**
* Voucher update function tries to cut voucher ability of code upload.
**/
CodeUploadingEnabled: AugmentedError<ApiType>;
/**
* Voucher issue/prolongation duration out of [min; max] constants.
**/
Expand Down
54 changes: 42 additions & 12 deletions api/src/types/augment/augment-api-tx.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '@polkadot/api-base/types/submittable';

import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
import type { AnyNumber, Codec, ITuple } from '@polkadot/types-codec/types';
import type { ApiTypes, AugmentedSubmittable } from '@polkadot/api-base/types';
import type { BTreeSet, Bytes, Option, Vec, bool, u128, u32, u64 } from '@polkadot/types-codec';
import {
Expand Down Expand Up @@ -270,7 +270,33 @@ declare module '@polkadot/api-base/types/submittable' {
call: AugmentedSubmittable<
(
voucherId: PalletGearVoucherInternalVoucherId | string | Uint8Array,
call: PalletGearVoucherInternalPrepaidCall | { SendMessage: any } | { SendReply: any } | string | Uint8Array,
call:
| PalletGearVoucherInternalPrepaidCall
| {
SendMessage: {
destination: string | Codec;
payload: string | Uint8Array | Codec;
gasLimit: number | bigint | Uint8Array | Codec;
value: number | bigint | Uint8Array | Codec;
keepAlive: boolean | Uint8Array | Codec;
};
}
| {
SendReply: {
replyToId: string | Codec;
payload: string | Uint8Array | Codec;
gasLimit: number | bigint | Uint8Array | Codec;
value: number | bigint | Uint8Array | Codec;
keepAlive: boolean | Uint8Array | Codec;
};
}
| {
UploadCode: {
code: string | Uint8Array | Codec;
};
}
| string
| Uint8Array,
) => SubmittableExtrinsic<ApiType>,
[PalletGearVoucherInternalVoucherId, PalletGearVoucherInternalPrepaidCall]
>;
Expand All @@ -288,14 +314,15 @@ declare module '@polkadot/api-base/types/submittable' {
spender: AccountId32 | string | Uint8Array,
balance: u128 | AnyNumber | Uint8Array,
programs:
| Option<Vec<GearCoreIdsProgramId>>
| Option<BTreeSet<GearCoreIdsProgramId>>
| null
| Uint8Array
| Vec<GearCoreIdsProgramId>
| (GearCoreIdsProgramId | string | Uint8Array)[],
validity: u32 | AnyNumber | Uint8Array,
| BTreeSet<GearCoreIdsProgramId>
| string[],
codeUploading: bool | boolean | Uint8Array,
duration: u32 | AnyNumber | Uint8Array,
) => SubmittableExtrinsic<ApiType>,
[AccountId32, u128, Option<Vec<GearCoreIdsProgramId>>, u32]
[AccountId32, u128, Option<BTreeSet<GearCoreIdsProgramId>>, bool, u32]
>;
revoke: AugmentedSubmittable<
(
Expand All @@ -311,19 +338,22 @@ declare module '@polkadot/api-base/types/submittable' {
moveOwnership: Option<AccountId32> | null | Uint8Array | AccountId32 | string,
balanceTopUp: Option<u128> | null | Uint8Array | u128 | AnyNumber,
appendPrograms:
| Option<Vec<GearCoreIdsProgramId>>
| Option<Option<BTreeSet<GearCoreIdsProgramId>>>
| null
| Uint8Array
| Vec<GearCoreIdsProgramId>
| (GearCoreIdsProgramId | string | Uint8Array)[],
prolongValidity: Option<u32> | null | Uint8Array | u32 | AnyNumber,
| Option<BTreeSet<GearCoreIdsProgramId>>
| BTreeSet<GearCoreIdsProgramId>
| (string | Uint8Array)[],
codeUploading: Option<bool> | null | Uint8Array | bool | boolean,
prolongDuration: Option<u32> | null | Uint8Array | u32 | AnyNumber,
) => SubmittableExtrinsic<ApiType>,
[
AccountId32,
PalletGearVoucherInternalVoucherId,
Option<AccountId32>,
Option<u128>,
Option<Vec<GearCoreIdsProgramId>>,
Option<Option<BTreeSet<GearCoreIdsProgramId>>>,
Option<bool>,
Option<u32>,
]
>;
Expand Down
19 changes: 19 additions & 0 deletions api/src/types/interfaces/program/code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ISubmittableResult } from '@polkadot/types/types';
import { SubmittableExtrinsic } from '@polkadot/api/types';

import { HexString } from '../../index';

export interface CodeUploadResult {
/**
* Code hash
*/
codeHash: HexString;
/**
* Submittable extrinsic
*/
extrinsic: SubmittableExtrinsic<'promise', ISubmittableResult>;
/**
* @deprecated will be removed in next major version
*/
submitted: SubmittableExtrinsic<'promise', ISubmittableResult>;
}
1 change: 1 addition & 0 deletions api/src/types/interfaces/program/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './pages';
export * from './extrinsic';
export * from './storage';
export * from './state';
export * from './code';
11 changes: 8 additions & 3 deletions api/src/types/interfaces/voucher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { SubmittableExtrinsic } from '@polkadot/api/types';

export type ICallOptions =
| { SendMessage: SubmittableExtrinsic<'promise', ISubmittableResult> }
| { SendReply: SubmittableExtrinsic<'promise', ISubmittableResult> };
| { SendReply: SubmittableExtrinsic<'promise', ISubmittableResult> }
| { UploadCode: SubmittableExtrinsic<'promise', ISubmittableResult> };

export interface IUpdateVoucherParams {
/**
Expand All @@ -21,9 +22,13 @@ export interface IUpdateVoucherParams {
*/
appendPrograms?: string[];
/**
* Prolong the voucher validity.
* Enable or disable code uploading.
*/
prolongValidity?: number;
codeUploading?: boolean;
/**
* Prolong the duration of th voucher validity.
*/
prolongDuration?: number;
}

export interface IVoucherDetails {
Expand Down
Loading
Loading