Skip to content

Commit

Permalink
Add ismp to e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Szegoo committed May 27, 2024
1 parent fea7066 commit 53c31cd
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 13 deletions.
117 changes: 105 additions & 12 deletions e2e_tests/xc-transfer/region-transfer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ApiPromise, Keyring, WsProvider } from '@polkadot/api';
import { KeyringPair } from '@polkadot/keyring/types';
import { getEncodedRegionId, Id, RegionId } from 'coretime-utils';
import { ISubmittableResult } from '@polkadot/types/types';
import { getEncodedRegionId, RegionId } from 'coretime-utils';
import assert from 'node:assert';
import { setupRelayAsset, sleep, submitExtrinsic, transferRelayAssetToPara } from '../common';
import { CONFIG, CORE_COUNT, INITIAL_PRICE, UNIT } from '../consts';
import { Get, IsmpRequest, REGIONX_API_TYPES, REGIONX_CUSTOM_RPC } from './types';

const REGIONX_SOVEREIGN_ACCOUNT = '5Eg2fntJ27qsari4FGrGhrMqKFDRnkNSR6UshkZYBGXmSuC8';

Expand All @@ -12,15 +14,13 @@ async function run(_nodeName: any, networkInfo: any, _jsArgs: any) {
const { wsUri: coretimeUri } = networkInfo.nodesByName['coretime-collator01'];
const { wsUri: rococoUri } = networkInfo.nodesByName['rococo-validator01'];

const regionXApi = await ApiPromise.create({ provider: new WsProvider(regionXUri) });
const rococoApi = await ApiPromise.create({
provider: new WsProvider(rococoUri),
types: { Id },
});
const coretimeApi = await ApiPromise.create({
provider: new WsProvider(coretimeUri),
types: { Id },
const regionXApi = await ApiPromise.create({
provider: new WsProvider(regionXUri),
types: { ...REGIONX_API_TYPES },
rpc: REGIONX_CUSTOM_RPC,
});
const rococoApi = await ApiPromise.create({ provider: new WsProvider(rococoUri) });
const coretimeApi = await ApiPromise.create({ provider: new WsProvider(coretimeUri) });

// account to submit tx
const keyring = new Keyring({ type: 'sr25519' });
Expand Down Expand Up @@ -109,15 +109,32 @@ async function run(_nodeName: any, networkInfo: any, _jsArgs: any) {
let regions = await regionXApi.query.regions.regions.entries();
assert.equal(regions.length, 1);
assert.deepStrictEqual(regions[0][0].toHuman(), [regionId]);
// record is unavailable because we did not setup ismp.
assert((regions[0][1].toHuman() as any).owner == alice.address);
assert(typeof (regions[0][1].toHuman() as any).record.Pending === 'string');

let region = regions[0][1].toHuman() as any;
assert(region.owner == alice.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, alice.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 == alice.address);
assert.deepStrictEqual(region.record.Available, {
end: '66',
owner: '5C6cBwdHw3agsBKzjGABaMq1kgXnmKyaBPN8J6c8MkHBnKu5',
paid: null,
});

// Transfer the region back to the Coretime chain:
const reserveTransferToCoretime = regionXApi.tx.polkadotXcm.limitedReserveTransferAssets(
{ V3: { parents: 1, interior: { X1: { Parachain: 1005 } } } }, // dest
{
Expand Down Expand Up @@ -244,4 +261,80 @@ async function getRegionId(coretimeApi: ApiPromise): Promise<RegionId | null> {
return null;
}

async function queryRequest(regionxApi: ApiPromise, commitment: string): Promise<IsmpRequest> {
const leafIndex = regionxApi.createType('LeafIndexQuery', { commitment });
const requests = await (regionxApi as any).rpc.ismp.queryRequests([leafIndex]);
// We only requested a single request so we only get one in the response.
return requests.toJSON()[0] as IsmpRequest;
}

async function makeIsmpResponse(
regionXApi: ApiPromise,
coretimeApi: ApiPromise,
request: IsmpRequest,
responderAddress: string
): Promise<void> {
if (isGetRequest(request)) {
const hashAt = (
await coretimeApi.query.system.blockHash(Number(request.get.height))
).toString();
const proofData = await coretimeApi.rpc.state.getReadProof([request.get.keys[0]], hashAt);

const stateMachineProof = regionXApi.createType('StateMachineProof', {
hasher: 'Blake2',
storage_proof: proofData.proof,
});

const substrateStateProof = regionXApi.createType('SubstrateStateProof', {
StateProof: stateMachineProof,
});
const response = regionXApi.tx.ismp.handleUnsigned([
{
Response: {
datagram: {
Request: [request],
},
proof: {
height: {
id: {
stateId: {
Kusama: 1005,
},
consensusStateId: 'PARA',
},
height: request.get.height.toString(),
},
proof: substrateStateProof.toHex(),
},
signer: responderAddress,
},
},
]);

return new Promise((resolve, reject) => {
const unsub = response.send((result: ISubmittableResult) => {
const { status, isError } = result;
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();
}
});
});
} else {
new Error('Expected a Get request');
}
}

const isGetRequest = (request: IsmpRequest): request is { get: Get } => {
return (request as { get: Get }).get !== undefined;
};

export { run };
83 changes: 83 additions & 0 deletions e2e_tests/xc-transfer/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
export type StateMachine = { Polkadot: number } | { Kusama: number };

export interface Get {
source: StateMachine;
dest: StateMachine;
nonce: bigint;
from: string;
keys: Array<string>;
height: bigint;
timeout_timestamp: bigint;
}

export type IsmpRequest = { post: any } | { get: Get };

export const REGIONX_API_TYPES = {
CoreIndex: 'u32',
CoreMask: 'Vec<u8>',
Timeslice: 'u32',
RegionId: {
begin: 'Timeslice',
core: 'CoreIndex',
mask: 'CoreMask',
},
RegionRecord: {
end: 'Timeslice',
owner: 'AccountId',
paid: 'Option<Balance>',
},
HashAlgorithm: {
_enum: ['Keccak', 'Blake2'],
},
StateMachineProof: {
hasher: 'HashAlgorithm',
storage_proof: 'Vec<Vec<u8>>',
},
SubstrateStateProof: {
_enum: {
OverlayProof: 'StateMachineProof',
StateProof: 'StateMachineProof',
},
},
LeafIndexQuery: {
commitment: 'H256',
},
StateMachine: {
_enum: {
Ethereum: 'Vec<u8>',
Polkadot: 'u32',
Kusama: 'u32',
},
},
Post: {},
Get: {
source: 'StateMachine',
dest: 'StateMachine',
nonce: 'u64',
from: 'Vec<u8>',
keys: 'Vec<Vec<u8>>',
height: 'u64',
timeout_timestamp: 'u64',
},
Request: {
_enum: {
Post: 'Post',
Get: 'Get',
},
},
};

export const REGIONX_CUSTOM_RPC = {
ismp: {
queryRequests: {
description: '',
params: [
{
name: 'query',
type: 'Vec<LeafIndexQuery>',
},
],
type: 'Vec<Request>',
},
},
};
2 changes: 1 addition & 1 deletion zombienet_tests/xc-transfer/region-transfer.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ addToGenesis = false
[parachains.collator]
name = "regionx-collator01"
command = "regionx-node"
args = [ "--log=xcm=trace,regions=trace" ]
args = [ "--enable-offchain-indexing true --log=xcm=trace,regions=trace" ]

0 comments on commit 53c31cd

Please sign in to comment.