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

Stateful storage signed extension #1288

Closed
wants to merge 2 commits into from
Closed
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 Cargo.lock

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

2 changes: 1 addition & 1 deletion integration-tests/package-lock.json

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

124 changes: 61 additions & 63 deletions integration-tests/scaffolding/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import {
PaginatedUpsertSignaturePayload
} from "./extrinsicHelpers";
import { EXISTENTIAL_DEPOSIT } from "./rootHooks";
import {MessageSourceId, PageHash} from "@frequency-chain/api-augment/interfaces";
import { MessageSourceId, PageHash } from "@frequency-chain/api-augment/interfaces";

export interface DevAccount {
uri: string,
keys: KeyringPair,
uri: string,
keys: KeyringPair,
}

export let devAccounts: DevAccount[] = [];
Expand All @@ -26,35 +26,35 @@ export let devAccounts: DevAccount[] = [];
export type Sr25519Signature = { Sr25519: `0x${string}` }

export function signPayloadSr25519(keys: KeyringPair, data: Codec): Sr25519Signature {
return { Sr25519: u8aToHex(keys.sign(u8aWrapBytes(data.toU8a()))) }
return { Sr25519: u8aToHex(keys.sign(u8aWrapBytes(data.toU8a()))) }
}

export async function generateDelegationPayload(payloadInputs: AddProviderPayload, expirationOffset?: number): Promise<AddProviderPayload> {
let { expiration, ...payload } = payloadInputs;
if (!expiration) {
expiration = (await getBlockNumber()) + (expirationOffset || 5);
}
let { expiration, ...payload } = payloadInputs;
if (!expiration) {
expiration = (await getBlockNumber()) + (expirationOffset || 5);
}

return {
expiration,
...payload,
}
return {
expiration,
...payload,
}
}

export async function getBlockNumber(): Promise<number> {
return (await ExtrinsicHelper.getLastBlock()).block.header.number.toNumber()
return (await ExtrinsicHelper.getLastBlock()).block.header.number.toNumber()
}

export async function generateAddKeyPayload(payloadInputs: AddKeyData, expirationOffset: number = 5, blockNumber?: number): Promise<AddKeyData> {
let { expiration, ...payload } = payloadInputs;
if (!expiration) {
expiration = (blockNumber || (await getBlockNumber())) + expirationOffset;
}
let { expiration, ...payload } = payloadInputs;
if (!expiration) {
expiration = (blockNumber || (await getBlockNumber())) + expirationOffset;
}

return {
expiration,
...payload,
}
return {
expiration,
...payload,
}
}

export async function generateItemizedSignaturePayload(payloadInputs: ItemizedSignaturePayload, expirationOffset?: number): Promise<ItemizedSignaturePayload> {
Expand Down Expand Up @@ -94,77 +94,75 @@ export async function generatePaginatedDeleteSignaturePayload(payloadInputs: Pag
}

export function createKeys(name: string = 'first pair'): KeyringPair {
const mnemonic = mnemonicGenerate();
// create & add the pair to the keyring with the type and some additional
// metadata specified
const keyring = new Keyring({ type: 'sr25519' });
const keypair = keyring.addFromUri(mnemonic, { name }, 'sr25519');
const mnemonic = mnemonicGenerate();
// create & add the pair to the keyring with the type and some additional
// metadata specified
const keyring = new Keyring({ type: 'sr25519' });
const keypair = keyring.addFromUri(mnemonic, { name }, 'sr25519');

return keypair;
return keypair;
}

export async function fundKeypair(source: KeyringPair, dest: KeyringPair, amount: bigint, nonce?: number): Promise<void> {
await ExtrinsicHelper.transferFunds(source, dest, amount).signAndSend(nonce);
await ExtrinsicHelper.transferFunds(source, dest, amount).signAndSend(nonce);
}

export async function createAndFundKeypair(amount = EXISTENTIAL_DEPOSIT, keyName?: string, devAccount?: KeyringPair, nonce?: number): Promise<KeyringPair> {
const keypair = createKeys(keyName);
const keypair = createKeys(keyName);

// Transfer funds from source (usually pre-funded dev account) to new account
await fundKeypair((devAccount || devAccounts[0].keys), keypair, amount, nonce);
// Transfer funds from source (usually pre-funded dev account) to new account
await fundKeypair((devAccount || devAccounts[0].keys), keypair, amount, nonce);

return keypair;
return keypair;
}

export function log(...args: any[]) {
if (env.verbose) {
console.log(...args);
}
if (env.verbose) {
console.log(...args);
}
}

export async function createProviderKeysAndId(): Promise<[KeyringPair, u64]> {
let providerKeys = await createAndFundKeypair();
let createProviderMsaOp = ExtrinsicHelper.createMsa(providerKeys);
let providerId = new u64(ExtrinsicHelper.api.registry, 0)
await createProviderMsaOp.fundAndSend();
let createProviderOp = ExtrinsicHelper.createProvider(providerKeys, "PrivateProvider");
let [providerEvent] = await createProviderOp.fundAndSend();
if (providerEvent && ExtrinsicHelper.api.events.msa.ProviderCreated.is(providerEvent)) {
providerId = providerEvent.data.providerId;
}
return [providerKeys, providerId];
}

export async function createDelegator(): Promise<[KeyringPair, u64]> {
let providerId = new u64(ExtrinsicHelper.api.registry, 0);
let [providerKeys, _] = await createMsa();
let createProviderOp = ExtrinsicHelper.createProvider(providerKeys, "PrivateProvider");
let [providerEvent] = await createProviderOp.fundAndSend();
if (providerEvent && ExtrinsicHelper.api.events.msa.ProviderCreated.is(providerEvent)) {
providerId = providerEvent.data.providerId;
}
return [providerKeys, providerId];
}

export async function createMsa(): Promise<[KeyringPair, u64]> {
let keys = await createAndFundKeypair();
let delegator_msa_id = new u64(ExtrinsicHelper.api.registry, 0);
let msa_id = new u64(ExtrinsicHelper.api.registry, 0);
const createMsa = ExtrinsicHelper.createMsa(keys);
await createMsa.fundOperation();
const [msaCreatedEvent, _] = await createMsa.signAndSend();

if (msaCreatedEvent && ExtrinsicHelper.api.events.msa.MsaCreated.is(msaCreatedEvent)) {
delegator_msa_id = msaCreatedEvent.data.msaId;
msa_id = msaCreatedEvent.data.msaId;
}

return [keys, delegator_msa_id];
return [keys, msa_id];
}

export async function createDelegatorAndDelegation(schemaId: u16, providerId: u64, providerKeys: KeyringPair): Promise<[KeyringPair, u64]> {
// Create a delegator msa
const [keys, delegator_msa_id] = await createDelegator();
// Create a delegator msa
const [keys, delegator_msa_id] = await createMsa();

// Grant delegation to the provider
const payload = await generateDelegationPayload({
authorizedMsaId: providerId,
schemaIds: [schemaId],
});
const addProviderData = ExtrinsicHelper.api.registry.createType("PalletMsaAddProvider", payload);
// Grant delegation to the provider
const payload = await generateDelegationPayload({
authorizedMsaId: providerId,
schemaIds: [schemaId],
});
const addProviderData = ExtrinsicHelper.api.registry.createType("PalletMsaAddProvider", payload);

const grantDelegationOp = ExtrinsicHelper.grantDelegation(keys, providerKeys, signPayloadSr25519(keys, addProviderData), payload);
await grantDelegationOp.fundOperation();
await grantDelegationOp.signAndSend();
const grantDelegationOp = ExtrinsicHelper.grantDelegation(keys, providerKeys, signPayloadSr25519(keys, addProviderData), payload);
await grantDelegationOp.fundOperation();
await grantDelegationOp.signAndSend();

return [keys, delegator_msa_id];
return [keys, delegator_msa_id];
}

export async function getCurrentItemizedHash(msa_id: MessageSourceId, schemaId: u16): Promise<PageHash> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,69 +10,69 @@ import { KeyringPair } from "@polkadot/keyring/types";
import { ExtrinsicHelper } from "../scaffolding/extrinsicHelpers";
import { AVRO_CHAT_MESSAGE } from "./fixtures/itemizedSchemaType";
import { MessageSourceId, SchemaId } from "@frequency-chain/api-augment/interfaces";
import {Bytes, u16} from "@polkadot/types";
import { Bytes, u16 } from "@polkadot/types";

describe("📗 Stateful Pallet Storage AppendOnly Schemas", () => {
let itemizedSchemaId: SchemaId;
let msa_id: MessageSourceId;
let providerId: MessageSourceId;
let providerKeys: KeyringPair;
let sudoKey: KeyringPair;
let itemizedSchemaId: SchemaId;
let msa_id: MessageSourceId;
let providerId: MessageSourceId;
let providerKeys: KeyringPair;
let sudoKey: KeyringPair;

before(async function () {
// Using Alice as sudoKey
sudoKey = devAccounts[0].keys;
before(async function () {
// Using Alice as sudoKey
sudoKey = devAccounts[0].keys;

// Create a provider for the MSA, the provider will be used to grant delegation
[providerKeys, providerId] = await createProviderKeysAndId();
assert.notEqual(providerId, undefined, "setup should populate providerId");
assert.notEqual(providerKeys, undefined, "setup should populate providerKeys");
// Create a provider for the MSA, the provider will be used to grant delegation
[providerKeys, providerId] = await createProviderKeysAndId();
assert.notEqual(providerId, undefined, "setup should populate providerId");
assert.notEqual(providerKeys, undefined, "setup should populate providerKeys");

// Create a schema for Itemized PayloadLocation
const createSchema = ExtrinsicHelper.createSchemaWithSettingsGov(providerKeys, sudoKey, AVRO_CHAT_MESSAGE, "AvroBinary", "Itemized", "AppendOnly");
const [event] = await createSchema.sudoSignAndSend();
if (event && createSchema.api.events.schemas.SchemaCreated.is(event)) {
itemizedSchemaId = event.data.schemaId;
}
assert.notEqual(itemizedSchemaId, undefined, "setup should populate schemaId");
// Create a schema for Itemized PayloadLocation
const createSchema = ExtrinsicHelper.createSchemaWithSettingsGov(providerKeys, sudoKey, AVRO_CHAT_MESSAGE, "AvroBinary", "Itemized", "AppendOnly");
const [event] = await createSchema.sudoSignAndSend();
if (event && createSchema.api.events.schemas.SchemaCreated.is(event)) {
itemizedSchemaId = event.data.schemaId;
}
assert.notEqual(itemizedSchemaId, undefined, "setup should populate schemaId");

// Create a MSA for the delegator and delegate to the provider for the itemized schema
[, msa_id] = await createDelegatorAndDelegation(itemizedSchemaId, providerId, providerKeys);
assert.notEqual(msa_id, undefined, "setup should populate msa_id");
});
// Create a MSA for the delegator and delegate to the provider for the itemized schema
[, msa_id] = await createDelegatorAndDelegation(itemizedSchemaId, providerId, providerKeys);
assert.notEqual(msa_id, undefined, "setup should populate msa_id");
});

describe("Itemized With AppendOnly Storage Tests", () => {
describe("Itemized With AppendOnly Storage Tests", () => {

it("should not be able to call delete action", async function () {
it("should not be able to call delete action", async function () {

// Add and update actions
let payload_1 = new Bytes(ExtrinsicHelper.api.registry, "Hello World From Frequency");
// Add and update actions
let payload_1 = new Bytes(ExtrinsicHelper.api.registry, "Hello World From Frequency");

const add_action = {
"Add": payload_1
}
const add_action = {
"Add": payload_1
}

let payload_2 = new Bytes(ExtrinsicHelper.api.registry, "Hello World Again From Frequency");
let payload_2 = new Bytes(ExtrinsicHelper.api.registry, "Hello World Again From Frequency");

const update_action = {
"Add": payload_2
}
const update_action = {
"Add": payload_2
}

const idx_1: u16 = new u16(ExtrinsicHelper.api.registry, 1)
const idx_1: u16 = new u16(ExtrinsicHelper.api.registry, 1)

const delete_action = {
"Delete": idx_1
}
const target_hash = await getCurrentItemizedHash(msa_id, itemizedSchemaId);
const delete_action = {
"Delete": idx_1
}
const target_hash = await getCurrentItemizedHash(msa_id, itemizedSchemaId);

let add_actions = [add_action, update_action, delete_action];
let add_actions = [add_action, update_action, delete_action];

let itemized_add_result_1 = ExtrinsicHelper.applyItemActions(providerKeys, itemizedSchemaId, msa_id, add_actions, target_hash);
await assert.rejects(async () => {
await itemized_add_result_1.fundAndSend();
}, {
name: 'UnsupportedOperationForSchema',
section: 'statefulStorage',
let itemized_add_result_1 = ExtrinsicHelper.applyItemActions(providerKeys, itemizedSchemaId, msa_id, add_actions, target_hash);
await assert.rejects(async () => {
await itemized_add_result_1.fundAndSend();
}, {
name: 'RpcError',
message: /Custom error: 3/, // UnsupportedOperationForSchema
});
});
});
Expand Down
28 changes: 17 additions & 11 deletions integration-tests/stateful-pallet-storage/handleItemized.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Integration tests for pallets/stateful-pallet-storage/handleItemized.ts
import "@frequency-chain/api-augment";
import assert from "assert";
import {createDelegatorAndDelegation, createProviderKeysAndId, getCurrentItemizedHash} from "../scaffolding/helpers";
import { createDelegatorAndDelegation, createProviderKeysAndId, getCurrentItemizedHash } from "../scaffolding/helpers";
import { KeyringPair } from "@polkadot/keyring/types";
import { ExtrinsicHelper } from "../scaffolding/extrinsicHelpers";
import { AVRO_CHAT_MESSAGE } from "../stateful-pallet-storage/fixtures/itemizedSchemaType";
Expand Down Expand Up @@ -83,8 +83,8 @@ describe("📗 Stateful Pallet Storage", () => {
await assert.rejects(async () => {
await itemized_add_result_1.fundAndSend();
}, {
name: 'InvalidSchemaId',
section: 'statefulStorage',
name: 'RpcError',
message: /Custom error: 2/, // InvalidSchemaId
});
}).timeout(10000);

Expand All @@ -99,8 +99,8 @@ describe("📗 Stateful Pallet Storage", () => {
await assert.rejects(async () => {
await itemized_add_result_1.fundAndSend();
}, {
name: 'SchemaPayloadLocationMismatch',
section: 'statefulStorage',
name: 'RpcError',
message: /Custom error: 4/, // SchemaPayloadLocationMismatch
});
}).timeout(10000);

Expand Down Expand Up @@ -139,7 +139,10 @@ describe("📗 Stateful Pallet Storage", () => {

let add_actions = [add_action, update_action];
let itemized_add_result_1 = ExtrinsicHelper.applyItemActions(providerKeys, schemaId_deletable, msa_id, add_actions, 0);
await assert.rejects(itemized_add_result_1.fundAndSend(), { name: 'StalePageState' });
await assert.rejects(itemized_add_result_1.fundAndSend(), {
name: 'RpcError',
message: /Custom error: 9/, // StalePageState
});
}).timeout(10000);
});

Expand Down Expand Up @@ -175,8 +178,8 @@ describe("📗 Stateful Pallet Storage", () => {
await assert.rejects(async () => {
await itemized_remove_result_1.fundAndSend();
}, {
name: 'InvalidSchemaId',
section: 'statefulStorage',
name: 'RpcError',
message: /Custom error: 2/, // InvalidSchemaId
});
}).timeout(10000);

Expand All @@ -190,8 +193,8 @@ describe("📗 Stateful Pallet Storage", () => {
await assert.rejects(async () => {
await itemized_remove_result_1.fundAndSend();
}, {
name: 'SchemaPayloadLocationMismatch',
section: 'statefulStorage',
name: 'RpcError',
message: /Custom error: 4/, // SchemaPayloadLocationMismatch
});
}).timeout(10000);

Expand All @@ -218,7 +221,10 @@ describe("📗 Stateful Pallet Storage", () => {
}
let remove_actions = [remove_action];
let op = ExtrinsicHelper.applyItemActions(providerKeys, schemaId_deletable, msa_id, remove_actions, 0);
await assert.rejects(op.fundAndSend(), { name: 'StalePageState' })
await assert.rejects(op.fundAndSend(), {
name: 'RpcError',
message: /Custom error: 9/, // StalePageState
})
}).timeout(10000);
});

Expand Down
Loading