Skip to content
Open
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
32 changes: 32 additions & 0 deletions chopsticks/configs/coretime-kusama.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
endpoint: wss://sys.ibp.network/coretime-kusama
mock-signature-host: true
db: ./db.sqlite

import-storage:
System:
Account:
- - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
- providers: 1
data:
free: 1000000000000000
Broker:
Configuration:
advance_notice: 20
interlude_length: 10
leadin_length: 10
ideal_bulk_proportion: 0
limit_cores_offered: 50
region_length: 30
renewal_bump: 10
contribution_timeout: 5
SaleInfo:
saleStart: 2831089
leadinLength: 50400
endPrice: 93489341
regionBegin: 361368
regionEnd: 366408
idealCoresSold: 38
coresOffered: 94
firstCore: 6
selloutPrice: 962940212
coresSold: 42
66 changes: 66 additions & 0 deletions chopsticks/configs/regionx-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
endpoint: ws://127.0.0.1:9955
mock-signature-host: true
db: ./db.sqlite

import-storage:
System:
Account:
- - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
- providers: 1
data:
free: 1000000000000000
Regions:
Regions:
[
[
[{ core: 0, begin: 366408, mask: "0xffffffffffffffffffff" }],
{
owner: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
locked: true,
record:
{
Available:
{
end: 371448,
owner: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
paid: null,
},
},
},
],
[
[{ core: 1, begin: 366408, mask: "0xffffffffffffffffffff" }],
{
owner: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
locked: true,
record:
{
Available:
{
end: 371448,
owner: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
paid: null,
},
},
},
],
]
Market:
Listings: [
[
[{ core: 0, begin: 366408, mask: "0xffffffffffffffffffff" }],
{
seller: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
timeslice_price: 1000000000, # 0.001 KSM / timeslice
sale_recipient: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
},
],
[
[{ core: 1, begin: 366408, mask: "0xffffffffffffffffffff" }],
{
seller: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
timeslice_price: 15000000000, # 0.0015 KSM / timeslice
sale_recipient: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,
},
],
]
38 changes: 38 additions & 0 deletions chopsticks/tests/coretime.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ApiPromise } from "@polkadot/api";
import { KeyringPair } from "@polkadot/keyring/types";
import { RegionId } from "coretime-utils";

const UNIT = 10n ** 12n; // KSM has 12 decimals
const INITIAL_PRICE = 1n * UNIT;

export async function purchaseRegion(
coretimeApi: ApiPromise,
buyer: KeyringPair
): Promise<RegionId | null> {
const callTx = async (resolve: (regionId: RegionId | null) => void) => {
const purchase = coretimeApi.tx.broker.purchase(INITIAL_PRICE * 10n);
const unsub = await purchase.signAndSend(buyer, async (result: any) => {
if (result.status.isInBlock) {
const regionId = await getRegionId(coretimeApi);
unsub();
resolve(regionId);
}
});
};

return new Promise(callTx);
}

async function getRegionId(coretimeApi: ApiPromise): Promise<RegionId | null> {
const events: any = await coretimeApi.query.system.events();

for (const record of events) {
const { event } = record;
if (event.section === 'broker' && event.method === 'Purchased') {
const data = event.data[1].toJSON();
return data;
}
}

return null;
}
10 changes: 4 additions & 6 deletions e2e_tests/ismp.common.ts → chopsticks/tests/ismp.utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ApiPromise } from '@polkadot/api';
import { KeyringPair } from '@polkadot/keyring/types';
import { submitExtrinsic, submitUnsigned } from './common';
import { submitExtrinsic, submitUnsigned } from './utils';
import { Get, IsmpRequest } from './types';

async function ismpAddParachain(signer: KeyringPair, regionXApi: ApiPromise) {
const addParaCall = regionXApi.tx.ismpParachain.addParachain([1005]);
const addParaCall = regionXApi.tx.ismpParachain.addParachain([{ id: 1005, slotDuration: 6000 }]);
const sudoCall = regionXApi.tx.sudo.sudo(addParaCall);
return submitExtrinsic(signer, sudoCall, {});
}
Expand Down Expand Up @@ -45,10 +45,8 @@ async function makeIsmpResponse(
proof: {
height: {
id: {
stateId: {
Kusama: 1005,
},
consensusStateId: 'PARA',
stateId: 1005,
consensusStateId: 'PAS0',
},
height: request.get.height.toString(),
},
Expand Down
124 changes: 124 additions & 0 deletions chopsticks/tests/region-transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { ApiPromise, Keyring, WsProvider } from "@polkadot/api";
import { cryptoWaitReady } from "@polkadot/util-crypto";
import { sleep, submitExtrinsic, submitUnsigned } from "./utils";
import { KeyringPair } from "@polkadot/keyring/types";
import { getEncodedRegionId, RegionId } from "coretime-utils";
import { makeIsmpResponse, queryRequest } from "./ismp.utils";
import { purchaseRegion } from "./coretime.utils";

const RELAY_ENDPOINT = "ws://127.0.0.1:8002";
const CORETIME_ENDPOINT = "ws://127.0.0.1:8000";
const REGIONX_ENDPOINT = "ws://127.0.0.1:8001";

const REGIONX_PARA_ID = 4242;

export const keyring = new Keyring({ type: "sr25519" });

async function run() {
await cryptoWaitReady();

const coretimeWsProvider = new WsProvider(CORETIME_ENDPOINT);
const coretimeApi = await ApiPromise.create({ provider: coretimeWsProvider });

const regionxWsProvider = new WsProvider(REGIONX_ENDPOINT);
const regionxApi = await ApiPromise.create({ provider: regionxWsProvider });

const alice = keyring.addFromUri("//Alice");

const regionId = await purchaseRegion(coretimeApi, alice);
if (!regionId) throw new Error('RegionId not found');

console.log(regionId);
console.log(getEncodedRegionId(regionId, coretimeApi).toString());
await transferRegionToRegionX(coretimeApi, regionxApi, alice, regionId);
}

async function transferRegionToRegionX(
coretimeApi: ApiPromise,
regionXApi: ApiPromise,
sender: KeyringPair,
regionId: RegionId
) {
const receiverKeypair = new Keyring();
receiverKeypair.addFromAddress(sender.address);

const feeAssetItem = 0;
const weightLimit = 'Unlimited';
const reserveTransferToRegionX = coretimeApi.tx.polkadotXcm.limitedReserveTransferAssets(
{ V3: { parents: 1, interior: { X1: { Parachain: REGIONX_PARA_ID } } } }, //dest
{
V3: {
parents: 0,
interior: {
X1: {
AccountId32: {
chain: 'Any',
id: receiverKeypair.pairs[0].publicKey,
},
},
},
},
}, //beneficiary
{
V3: [
{
id: {
Concrete: {
parents: 1,
interior: 'Here',
},
},
fun: {
Fungible: 10n ** 10n,
},
}, // ^^ fee payment asset
{
id: {
Concrete: {
parents: 0,
interior: { X1: { PalletInstance: 50 } },
},
},
fun: {
NonFungible: {
Index: getEncodedRegionId(regionId, coretimeApi).toString(),
},
},
},
],
}, //asset
feeAssetItem,
weightLimit
);
await submitExtrinsic(sender, reserveTransferToRegionX, {});

await sleep(5000);

const requestRecord = regionXApi.tx.regions.requestRegionRecord(regionId);
await submitUnsigned(requestRecord);

let regions = await regionXApi.query.regions.regions.entries();
// assert.equal(regions.length, 1);
// assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]);

let region = regions[0][1].toHuman() as any;
// assert(region.owner == sender.address);
// assert(typeof region.record.Pending === 'string');

// Check the data on the Coretime chain:
regions = await coretimeApi.query.broker.regions.entries();
// assert.equal(regions.length, 1);
// assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]);
// assert.equal((regions[0][1].toHuman() as any).owner, REGIONX_SOVEREIGN_ACCOUNT);

// Respond to the ISMP get request:
const request = await queryRequest(regionXApi, region.record.Pending);
await makeIsmpResponse(regionXApi, coretimeApi, request, sender.address);

// The record should be set after ISMP response:
regions = await regionXApi.query.regions.regions.entries();
region = regions[0][1].toHuman() as any;
// assert(region.owner == sender.address);
}

run().then(() => process.exit(0));
File renamed without changes.
75 changes: 75 additions & 0 deletions chopsticks/tests/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { ApiPromise, Keyring } from "@polkadot/api";
import { SignerOptions, SubmittableExtrinsic } from "@polkadot/api/types";
import { KeyringPair } from "@polkadot/keyring/types";

export const keyring = new Keyring({ type: "sr25519" });

export function log(message: string) {
// Green log.
console.log("\x1b[32m%s\x1b[0m", message);
}

export async function setBalance(api: ApiPromise, who: string, balance: string) {
log(`Setting balance of ${who} to ${balance}`);

const setBalanceCall = api.tx.balances.forceSetBalance(who, balance);
return force(api, setBalanceCall);
}

export async function submitExtrinsic(
signer: KeyringPair,
call: SubmittableExtrinsic<"promise">,
options: Partial<SignerOptions>
): Promise<void> {
try {
return new Promise((resolve, _reject) => {
const unsub = call.signAndSend(signer, options, (result) => {
console.log(`Current status is ${result.status}`);
if (result.status.isInBlock) {
console.log(`Transaction included at blockHash ${result.status.asInBlock}`);
} else if (result.status.isFinalized) {
console.log(`Transaction finalized at blockHash ${result.status.asFinalized}`);
unsub.then();
return resolve();
} else if (result.isError) {
console.log("Transaction error");
unsub.then();
return resolve();
}
});
});
} catch (e) {
console.log(e);
}
}

export async function submitUnsigned(call: SubmittableExtrinsic<'promise'>): Promise<void> {
return new Promise((resolve, reject) => {
const unsub = call.send(({ status, isError }) => {
console.log(`Current status is ${status}`);
if (status.isInBlock) {
console.log(`Transaction included at blockHash ${status.asInBlock}`);
} else if (status.isFinalized) {
console.log(`Transaction finalized at blockHash ${status.asFinalized}`);
unsub.then();
return resolve();
} else if (isError) {
console.log('Transaction error');
unsub.then();
return reject();
}
});
});
}

export async function force(api: ApiPromise, call: SubmittableExtrinsic<"promise">): Promise<void> {
const sudoCall = api.tx.sudo.sudo(call);

const alice = keyring.addFromUri("//Alice");

await submitExtrinsic(alice, sudoCall, {});
}

export async function sleep(milliseconds: number) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
Loading
Loading