feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,122 @@
import { Chain, Observe } from "../src/test-case";
import { logger, nullifySigned, nullifyUnsigned, type ApiDeclarations } from "../src/utils";
// An unsigned solution scenario:
//
// When no staking-miner is running (and for simplicity the signed phase is also set to zero). We
// expect an unsigned solution to successfullly proceed and submit a solution with `minerPages` out
// of the total `pages`.
export function commonUnsignedSteps(
expectedValidatorSetCount: number,
minerPages: number,
pages: number,
doNullifySigned: boolean,
apis: ApiDeclarations
): Observe[] {
return [
// first relay session change at block 11
Observe.on(Chain.Relay, "Session", "NewSession").byBlock(11),
// by block 10 we will plan a new era
Observe.on(Chain.Teyrchain, "Staking", "SessionRotated")
.withDataCheck((x: any) => x.active_era == 0 && x.planned_era == 1)
.onPass(() => {
if (doNullifySigned) {
nullifySigned(apis.paraApi).then((ok) => {
logger.verbose("Nullified signed phase:", ok);
});
}
}),
// eventually we will verify all pages
...Array.from({ length: minerPages }, (_, __) => {
return Observe.on(Chain.Teyrchain, "MultiBlockElectionVerifier", "Verified");
}),
// eventually it will be queued
Observe.on(Chain.Teyrchain, "MultiBlockElectionVerifier", "Queued"),
// eventually multiblock election will transition to `Done`
Observe.on(Chain.Teyrchain, "MultiBlockElection", "PhaseTransitioned").withDataCheck(
(x: any) => x.to.type === "Done"
),
// eventually we will export all 4 pages to staking
// export events.
...Array.from({ length: pages }, (_, __) => {
return Observe.on(Chain.Teyrchain, "Staking", "PagedElectionProceeded");
}),
// eventually multiblock goes back to `Off`
Observe.on(Chain.Teyrchain, "MultiBlockElection", "PhaseTransitioned").withDataCheck(
(x: any) => x.to.type === "Off"
),
// eventually we will send it back to RC
Observe.on(Chain.Relay, "StakingAhClient", "ValidatorSetReceived").withDataCheck(
(x: any) => x.id === 1 && x.new_validator_set_count === expectedValidatorSetCount
),
Observe.on(Chain.Relay, "Session", "NewQueued"),
// eventually we will receive a session report back in AH with activation timestamp
Observe.on(Chain.Teyrchain, "StakingRcClient", "SessionReportReceived").withDataCheck(
(x) => x.activation_timestamp !== undefined
),
// eventually we will have era paid (inflation)
Observe.on(Chain.Teyrchain, "Staking", "EraPaid"),
].map((s) => s.build());
}
// A signed solution scenario.
//
// This test expect you to call `spawnMiner` in the final test code. A full solution of `pages` is
// expected to be submitted.
export function commonSignedSteps(
pages: number,
expectedValidatorSetCount: number,
apis: ApiDeclarations
): Observe[] {
return [
// first relay session change at block 11
Observe.on(Chain.Relay, "Session", "NewSession").byBlock(11),
// by block 10 we will plan a new era
Observe.on(Chain.Teyrchain, "Staking", "SessionRotated")
.withDataCheck((x: any) => x.active_era == 0 && x.planned_era == 1)
.onPass(() => {
nullifyUnsigned(apis.paraApi).then((ok) => {
logger.verbose("Nullified unsigned phase:", ok);
});
}),
// Eventually a signed submission is registered...
Observe.on(Chain.Teyrchain, "MultiBlockElectionSigned", "Registered"),
// ... and exact number of pages are generated
...Array.from({ length: pages }, () =>
Observe.on(Chain.Teyrchain, "MultiBlockElectionSigned", "Stored")
),
// ... and exact number of pages are verified
...Array.from({ length: pages }, () =>
Observe.on(Chain.Teyrchain, "MultiBlockElectionVerifier", "Verified")
),
// eventually it will be queued
Observe.on(Chain.Teyrchain, "MultiBlockElectionVerifier", "Queued"),
// eventually the signed submitter is rewarded.
// TODO: check rewarded account is Bob
Observe.on(Chain.Teyrchain, "MultiBlockElectionSigned", "Rewarded"),
// eventually multiblock election will transition to `Done`
Observe.on(Chain.Teyrchain, "MultiBlockElection", "PhaseTransitioned").withDataCheck(
(x: any) => x.to.type === "Done"
),
// eventually we will export all pages.
...Array.from({ length: pages }, () =>
Observe.on(Chain.Teyrchain, "Staking", "PagedElectionProceeded")
),
// eventually multiblock goes back to `Off`
Observe.on(Chain.Teyrchain, "MultiBlockElection", "PhaseTransitioned").withDataCheck(
(x: any) => x.to.type === "Off"
),
// eventually we will send it back to RC
Observe.on(Chain.Relay, "StakingAhClient", "ValidatorSetReceived").withDataCheck(
(x: any) => x.id === 1 && x.new_validator_set_count === expectedValidatorSetCount
),
Observe.on(Chain.Relay, "Session", "NewQueued"),
// eventually we will receive a session report back in AH with activation timestamp
Observe.on(Chain.Teyrchain, "StakingRcClient", "SessionReportReceived").withDataCheck(
(x) => x.activation_timestamp !== undefined
),
// eventually we will have era paid (inflation)
Observe.on(Chain.Teyrchain, "Staking", "EraPaid"),
].map((s) => s.build());
}
@@ -0,0 +1,67 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched } from "../src/cmd";
import { Chain, EventOutcome, Observe, runTest, TestCase } from "../src/test-case";
import { getApis, GlobalTimeout, logger, nullifySigned } from "../src/utils";
/// This is the preset against which your test will run. See the README or `PResets` for more info.
const PRESET: Presets = Presets.FakeKsm;
test(
`example test with preset ${PRESET}`,
async () => {
/// We run the test with our defined preset.
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
/// Grab PAPI Apis to both relay and teyrchain instance of the ZN.
const apis = await getApis();
// Our test is defined here. We expect a sequence of events to be observed in RC or
// Teyrchain. The events that we can observe are defined in `test-case.ts`'s `runTest`. In
// short, they are all of the events related to staking.
const testCase = new TestCase(
[
Observe.on(Chain.Relay, "Session", "NewSession")
// An event can be expected to happen by a certain block
.byBlock(11)
// And it can execute a callback when it passes.
.onPass(() => {
logger.verbose("New session observed on relay chain");
})
// and we can check the data of the event.
.withDataCheck((x: any) => {
logger.verbose("shall we check the data? maybe", x);
return true
}),
// add more `Observe`s here
].map((s) => s.build()),
// Passing this to true will allow events to be _interleaved_. If set to `false`, the
// above sequence of events are expected to happen in a strict order. If `true`, the
// events of each `Chain` must happen in a strict order, but intra-chain events can come
// in any order. For example, assume we have the following 4 observes in our test case:
// 1. Observe.on(Chain.Relay, "Module", "Event1")
// 2. Observe.on(Chain.Relay, "Module", "Event2")
// 3. Observe.on(Chain.Para, "Module", "Event3")
// 4. Observe.on(Chain.Para, "Module", "Event4")
//
// Without interleaving, 1 -> 4 has to be observed as-is.
//
// With interleaving, at any point in time, the first unobserved event of each chain
// type is acceptable. For example, the following is valid:
//
// 3 -> 1 -> 4 -> 2
//
// In some sense, with `interleave = true`, we break apart the test case into two stacks
// that need to be popped in order, while in interleave = false, it is one stack.
true,
// Something to happen when the test is over. Always kill ZN, and any other processes
// you might spawn.
() => {
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,52 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched, spawnMiner } from "../src/cmd";
import { Chain, EventOutcome, Observe, runTest, TestCase } from "../src/test-case";
import { getApis, GlobalTimeout, logger, nullifyUnsigned } from "../src/utils";
import { commonSignedSteps } from "./common";
const PRESET: Presets = Presets.FakeDot;
test(
`pruning era with signed (full solution) on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
const killMiner = await spawnMiner();
// This test has no real assertions. Change the `HistoryDepth` to 1 in the runtime, run it,
// and observe the logs and PoV sizes.
const steps = [
// first relay session change at block 11
Observe.on(Chain.Relay, "Session", "NewSession").byBlock(11)
.onPass(() => {
nullifyUnsigned(apis.paraApi).then((ok) => {
logger.verbose("Nullified signed phase:", ok);
});
}),
Observe.on(Chain.Teyrchain, "Staking", "EraPruned")
.withDataCheck((x) => x.index == 0),
Observe.on(Chain.Teyrchain, "Staking", "EraPruned")
.withDataCheck((x) => x.index == 1),
// Observe.on(Chain.Teyrchain, "Staking", "EraPruned")
// .withDataCheck((x) => x.index == 2),
// Observe.on(Chain.Teyrchain, "Staking", "EraPruned")
// .withDataCheck((x) => x.index == 3),
// Observe.on(Chain.Teyrchain, "Staking", "EraPruned")
// .withDataCheck((x) => x.index == 4),
].map((s) => s.build())
const testCase = new TestCase(
steps,
true,
() => {
killMiner();
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout * 10 }
);
@@ -0,0 +1,32 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched, spawnMiner } from "../src/cmd";
import { EventOutcome, runTest, TestCase } from "../src/test-case";
import {
getApis,
GlobalTimeout,
} from "../src/utils";
import { commonSignedSteps } from "./common";
const PRESET: Presets = Presets.FakeDev;
test(
`signed solution on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
const killMiner = await spawnMiner();
const testCase = new TestCase(
commonSignedSteps(4, 10, apis),
true,
() => {
killMiner();
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,30 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched, spawnMiner } from "../src/cmd";
import { EventOutcome, runTest, TestCase } from "../src/test-case";
import { getApis, GlobalTimeout} from "../src/utils";
import { commonSignedSteps } from "./common";
const PRESET: Presets = Presets.FakeDot;
test(
`signed solution on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
const killMiner = await spawnMiner();
const testCase = new TestCase(
commonSignedSteps(32, 500, apis),
true,
() => {
killMiner();
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,30 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched, spawnMiner } from "../src/cmd";
import { EventOutcome, runTest, TestCase } from "../src/test-case";
import { getApis, GlobalTimeout } from "../src/utils";
import { commonSignedSteps } from "./common";
const PRESET: Presets = Presets.FakeKsm;
test(
`signed solution on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
const killMiner = await spawnMiner();
const testCase = new TestCase(
commonSignedSteps(16, 1000, apis),
true,
() => {
killMiner();
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,103 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched } from "../src/cmd";
import { Chain, EventOutcome, Observe, runTest, TestCase } from "../src/test-case";
import { alice, getApis, GlobalTimeout, logger, nullifySigned, aliceStash } from "../src/utils";
// Note the `RealM` preset as this test MUST run with enough validators such that we can disable one.
const PRESET: Presets = Presets.RealM;
test(
`slashing with disabling on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
let aliceExposedNominators = 0;
let pages = 0;
const steps = [
// first relay session change at block 11
Observe.on(Chain.Relay, "Session", "NewSession").byBlock(11),
// eventually AH will will be instructed to plan a new session.
Observe.on(Chain.Teyrchain, "Staking", "SessionRotated")
.withDataCheck((x: any) => x.active_era == 0 && x.planned_era == 1)
.onPass(() => {
nullifySigned(apis.paraApi);
}),
// Eventually we will receive an activation timestamp in AH, meaning the first era was complete.
Observe.on(Chain.Teyrchain, "StakingRcClient", "SessionReportReceived")
.withDataCheck((x) => x.activation_timestamp !== undefined)
.onPass(() => {
// upon completion, submit a slash to rc
logger.info("Submitting slash to RC");
const call = apis.rcApi.tx.RootOffences.create_offence({
offenders: [
// alice//Stash, 50%, which will cause any disabling. See `DisablingStrategy` in `./runtimes/teyrchain`.
[aliceStash, 500000000],
],
maybe_identifications: undefined,
maybe_session_index: undefined,
}).decodedCall;
apis.rcApi.tx.Sudo.sudo({ call })
.signAndSubmit(alice)
.then((res: any) => {
logger.verbose("Slash submission result:", res.ok);
});
}),
// we will receive the root offence event
Observe.on(Chain.Relay, "RootOffences", "OffenceCreated"),
// Session pallet has insta-disabled this validator.
Observe.on(Chain.Relay, "Session", "ValidatorDisabled").withDataCheck(
(x: any) => x.validator === aliceStash
),
// Rest of the events are the same as in the non-disabling test.
// eventually we will receive an offence in the teyrchain, first the rc-client
Observe.on(Chain.Teyrchain, "StakingRcClient", "OffenceReceived").withDataCheck(
(x: any) => x.offences_count === 1
),
// then staking
Observe.on(Chain.Teyrchain, "Staking", "OffenceReported")
.withDataCheck((x: any) => x.offence_era === 1 && x.fraction === 500000000)
.onPass(async () => {
// let's calculate how many pages of exposure alice has -- this will impact the number of next events.
const overview = await apis.paraApi.query.Staking.ErasStakersOverview.getValue(
1,
aliceStash
);
pages = overview?.page_count || 0;
aliceExposedNominators = overview?.nominator_count || 0;
// TODO: lazily create the `Slashed` and `SlashComputed` based on this
logger.verbose(
`Alice has ${aliceExposedNominators} exposed nominators (${pages}) whom we expect to slash later`
);
}),
// then staking will calculate the slashes, we only check 1 page
Observe.on(Chain.Teyrchain, "Staking", "SlashComputed").withDataCheck(
(x: any) => x.page === 0
),
// staking will eventually bump to active era 2, where slashes will be applied.
Observe.on(Chain.Teyrchain, "Staking", "EraPaid"),
Observe.on(Chain.Teyrchain, "Staking", "SessionRotated").withDataCheck(
(x: any) => x.active_era === 2
),
// staking will apply slashes, we only check one slash.
Observe.on(Chain.Teyrchain, "Staking", "Slashed"),
];
const testCase = new TestCase(
steps.map((s) => s.build()),
true,
() => {
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,96 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched } from "../src/cmd";
import { Chain, EventOutcome, Observe, runTest, TestCase } from "../src/test-case";
import { alice, getApis, GlobalTimeout, logger, nullifySigned, aliceStash } from "../src/utils";
const PRESET: Presets = Presets.RealS;
test(
`slashing without disabling on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
let aliceExposedNominators = 0;
let pages = 0;
const steps = [
// first relay session change at block 11
Observe.on(Chain.Relay, "Session", "NewSession").byBlock(11),
// eventually AH will will be instructed to plan a new session.
Observe.on(Chain.Teyrchain, "Staking", "SessionRotated")
.withDataCheck((x: any) => x.active_era == 0 && x.planned_era == 1)
.onPass(() => {
nullifySigned(apis.paraApi);
}),
// Eventually we will receive an activation timestamp in AH, meaning the first era was complete.
Observe.on(Chain.Teyrchain, "StakingRcClient", "SessionReportReceived")
.withDataCheck((x) => x.activation_timestamp !== undefined)
.onPass(() => {
// upon completion, submit a slash to rc
logger.info("Submitting slash to RC");
const call = apis.rcApi.tx.RootOffences.create_offence({
offenders: [
// alice//Stash, 10%, which will NOT cause any disabling. See `DisablingStrategy` in `./runtimes/teyrchain`.
[aliceStash, 100000000],
],
maybe_identifications: undefined,
maybe_session_index: undefined,
}).decodedCall;
apis.rcApi.tx.Sudo.sudo({ call })
.signAndSubmit(alice)
.then((res: any) => {
logger.verbose("Slash submission result:", res.ok);
});
}),
// we will receive the root offence event
Observe.on(Chain.Relay, "RootOffences", "OffenceCreated"),
// eventually we will receive an offence in the teyrchain, first the rc-client
Observe.on(Chain.Teyrchain, "StakingRcClient", "OffenceReceived").withDataCheck(
(x: any) => x.offences_count === 1
),
// then staking
Observe.on(Chain.Teyrchain, "Staking", "OffenceReported")
.withDataCheck((x: any) => x.offence_era === 1 && x.fraction === 100000000)
.onPass(async () => {
// let's calculate how many pages of exposure alice has -- this will impact the number of next events.
const overview = await apis.paraApi.query.Staking.ErasStakersOverview.getValue(
1,
aliceStash
);
pages = overview?.page_count || 0;
aliceExposedNominators = overview?.nominator_count || 0;
// TODO: lazily create the `Slashed` and `SlashComputed` based on this
logger.verbose(
`Alice has ${aliceExposedNominators} exposed nominators (${pages}) whom we expect to slash later`
);
}),
// then staking will calculate the slasheh, we only check 1 page
Observe.on(Chain.Teyrchain, "Staking", "SlashComputed").withDataCheck(
(x: any) => x.page === 0
),
// staking will eventually bump to active era 2, where slashes will be applied.
Observe.on(Chain.Teyrchain, "Staking", "EraPaid"),
Observe.on(Chain.Teyrchain, "Staking", "SessionRotated").withDataCheck(
(x: any) => x.active_era === 2
),
// staking will apply slashes, we only check one slash.
Observe.on(Chain.Teyrchain, "Staking", "Slashed"),
];
const testCase = new TestCase(
steps.map((s) => s.build()),
true,
() => {
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,97 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched } from "../src/cmd";
import { Chain, EventOutcome, Observe, runTest, TestCase } from "../src/test-case";
import { alice, getApis, GlobalTimeout, logger, nullifySigned, aliceStash, derivePubkeyFrom, ss58 } from "../src/utils";
const PRESET: Presets = Presets.RealS;
test(
`slashing spam test on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
// total number of offences to send.
const target = 1000;
// the size of each batch.
const batchSize = 100;
const numBatches = Math.ceil(target / batchSize);
let sent = 0;
let received = 0;
// onchain-page-size for offence queueing in RC is 50, so we expect 20 pages for 1000 offences.
const steps = [
// first relay session change at block 11, just a sanity check
Observe.on(Chain.Relay, "Session", "NewSession")
.byBlock(11)
.onPass(async () => {
logger.info(`Submitting ${target} offences in batches of ${batchSize}`);
// Calculate number of batches needed
let nonce = await apis.rcApi.apis.AccountNonceApi.account_nonce(ss58(alice.publicKey));
logger.info(`Alice nonce at start: ${nonce}`);
for (let batchIndex = 0; batchIndex < numBatches; batchIndex++) {
const start = batchIndex * batchSize;
const end = Math.min(start + batchSize, target);
const currentBatchSize = end - start;
logger.info(`Processing batch ${batchIndex + 1}/${numBatches}: offences ${start} to ${end - 1}`);
// Create batch of offence calls
const offenceCalls = Array.from({ length: currentBatchSize }, (_, i) => {
const offenceIndex = start + i;
logger.debug(`Preparing offence ${offenceIndex}: ${derivePubkeyFrom(`//${offenceIndex}`)}`);
return apis.rcApi.tx.RootOffences.report_offence({
offences: [[
[derivePubkeyFrom(`//${offenceIndex}`), { total: BigInt(0), own: BigInt(0), others: [] }],
0, // session index
BigInt(offenceIndex), // time slot, each being unique
100000000 // slash ppm
]]
}).decodedCall;
});
// Submit this batch as a single transaction
try {
const batchCall = apis.rcApi.tx.Utility.force_batch({ calls: offenceCalls }).decodedCall;
const result = apis.rcApi.tx.Sudo.sudo({ call: batchCall })
.signAndSubmit(alice, { at: "best", nonce: nonce });
logger.info(`Batch ${batchIndex + 1} submitted`);
nonce += 1;
sent += currentBatchSize;
} catch (error) {
logger.error(`Batch ${batchIndex + 1} failed:`, error);
// Continue with next batch even if this one fails
}
}
}),
// in the meantime, we expect to see on the AH side:
...Array.from({ length: 20 }, (_, __) =>
Observe.on(Chain.Teyrchain, "StakingRcClient", "OffenceReceived").withDataCheck((x) => {
received += x.offences_count;
return true
}),
),
];
const testCase = new TestCase(
steps.map((s) => s.build()),
true,
() => {
logger.info(`Test completed. Created ${sent} offences, processed ${received} in teyrchain`);
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
expect(sent).toEqual(received);
},
{ timeout: GlobalTimeout * 2 } // Double timeout for this complex test
);
@@ -0,0 +1,26 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched } from "../src/cmd";
import { EventOutcome, runTest, TestCase } from "../src/test-case";
import { getApis, GlobalTimeout} from "../src/utils";
import { commonUnsignedSteps } from "./common";
const PRESET: Presets = Presets.FakeDev;
test(
`unsigned solution on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
const steps = commonUnsignedSteps(10, 4, 4, false, apis);
const testCase = new TestCase(steps, true, () => {
killZn();
});
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,33 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched } from "../src/cmd";
import { EventOutcome, runTest, TestCase } from "../src/test-case";
import { getApis, GlobalTimeout } from "../src/utils";
import { commonUnsignedSteps } from "./common";
const PRESET: Presets = Presets.FakeDot;
test(
`unsigned solution on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
const expectedValidatorSetCount = await apis.paraApi.query.Staking.ValidatorCount.getValue();
const pages = 32;
const minerPages = 4;
const steps = commonUnsignedSteps(expectedValidatorSetCount, minerPages, pages, true, apis);
const testCase = new TestCase(
steps,
true,
() => {
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,33 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched } from "../src/cmd";
import { EventOutcome, runTest, TestCase } from "../src/test-case";
import { getApis, GlobalTimeout } from "../src/utils";
import { commonUnsignedSteps } from "./common";
const PRESET: Presets = Presets.FakeKsm;
test(
`unsigned solution on ${PRESET}`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
const expectedValidatorSetCount = await apis.paraApi.query.Staking.ValidatorCount.getValue();
const pages = 16;
const minerPages = 4;
const steps = commonUnsignedSteps(expectedValidatorSetCount, minerPages, pages, true, apis);
const testCase = new TestCase(
steps,
true,
() => {
killZn();
}
);
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);
@@ -0,0 +1,243 @@
import { test, expect } from "bun:test";
import { Presets } from "../src";
import { runPresetUntilLaunched } from "../src/cmd";
import { Chain, EventOutcome, Observe, runTest, TestCase } from "../src/test-case";
import { alice, aliceStash, deriveFrom, getApis, GlobalTimeout, logger, safeJsonStringify, ss58, type ApiDeclarations } from "../src/utils";
import { DEV_PHRASE } from "@polkadot-labs/hdkd-helpers";
import { FixedSizeBinary, type PolkadotSigner, type TxCall, type TxCallData, type TypedApi } from "polkadot-api";
import { teyrchain, rc } from "@polkadot-api/descriptors";
const PRESET: Presets = Presets.FakeDev;
async function sendUp(api: TypedApi<typeof teyrchain>, count: number) {
const calls: TxCallData[] = [];
const ed = await api.constants.Balances.ExistentialDeposit();
for (let i = 0; i < count; i++) {
const account = deriveFrom(DEV_PHRASE, `//up_${i}`);
const endowment = BigInt(1000) * ed;
const teleport = endowment / BigInt(10);
const forceSetBalance = api.tx.Balances.force_set_balance({
new_free: endowment,
who: { type: "Id", value: ss58(account.publicKey) }
});
const xcm = api.tx.PolkadotXcm.teleport_assets({
dest: {
type: "V5",
value: {
parents: 1,
interior: {
type: "Here",
value: undefined
}
}
},
beneficiary: {
type: "V5",
value: {
parents: 0,
interior: {
type: "X1",
value: {
type: "AccountId32",
value: { id: new FixedSizeBinary(account.publicKey) }
}
}
}
},
assets: {
type: "V5",
value: [
{
id: {
parents: 1,
interior: {
type: "Here",
value: undefined
}
},
fun: {
type: "Fungible",
"value": teleport
}
}
]
},
fee_asset_id: {
type: "V5",
value: {
parents: 1,
interior: {
type: "Here",
value: undefined
}
},
},
})
const dispatchAs = api.tx.Utility.dispatch_as({
as_origin: { type: "system", value: { type: "Signed", value: ss58(account.publicKey) } },
call: xcm.decodedCall
});
calls.push(forceSetBalance.decodedCall);
calls.push(dispatchAs.decodedCall);
}
const finalBatch = api.tx.Utility.batch_all({ calls });
const finalSudo = api.tx.Sudo.sudo({ call: finalBatch.decodedCall });
try {
const res = await finalSudo.signAndSubmit(alice, { at: "best" });
let success = 0;
let failure = 0;
res.events.forEach((e) => {
logger.debug(safeJsonStringify(e.value));
if (e.value.type === "DispatchedAs") {
// @ts-ignore
if (e.value.value.result.success) {
success += 1
} else {
failure += 1
}
}
});
logger.info(`Sent ${count} upward messages, intercepted ${success + failure} events, ${success} succeeded, ${failure} failed`);
} catch(e) {
logger.warn(`Error sending upward messages: ${e}`);
}
}
async function sendDown(api: TypedApi<typeof rc>, count: number) {
const calls: TxCallData[] = [];
const ed = await api.constants.Balances.ExistentialDeposit();
for (let i = 0; i < count; i++) {
const account = deriveFrom(DEV_PHRASE, `//down_${i}`)
const endowment = BigInt(1000) * ed;
const teleport = endowment / BigInt(10);
const forceSetBalance = api.tx.Balances.force_set_balance({
new_free: endowment,
who: { type: "Id", value: ss58(account.publicKey) }
})
const xcm = api.tx.XcmPallet.teleport_assets({
dest: {
type: "V5",
value: {
parents: 0,
interior: {
type: "X1",
value: { type: "Teyrchain", value: 1100 }
}
}
},
beneficiary: {
type: "V5",
value: {
parents: 0,
interior: {
type: "X1",
value: {
type: "AccountId32",
value: { id: new FixedSizeBinary(account.publicKey) }
}
}
}
},
assets: {
type: "V5",
value: [
{
id: {
parents: 0,
interior: {
type: "Here",
value: undefined
}
},
fun: {
type: "Fungible",
value: teleport
}
}
]
},
fee_asset_id: {
type: "V5",
value: {
parents: 0,
interior: {
type: "Here",
value: undefined
}
},
},
})
const dispatchAs = api.tx.Utility.dispatch_as({
as_origin: { type: "system", value: { type: "Signed", value: ss58(account.publicKey) } },
call: xcm.decodedCall
});
calls.push(forceSetBalance.decodedCall);
calls.push(dispatchAs.decodedCall);
}
const finalBatch = api.tx.Utility.batch_all({ calls });
const finalSudo = api.tx.Sudo.sudo({ call: finalBatch.decodedCall });
try {
const res = await finalSudo.signAndSubmit(alice, { at: "best" });
let success = 0;
let failure = 0;
res.events.forEach((e) => {
logger.verbose(safeJsonStringify(e.value));
if (e.value.type === "DispatchedAs") {
// @ts-ignore
if (e.value.value.result.success) {
success += 1
} else {
failure += 1
}
}
});
logger.info(`Sent ${count} downward messages, intercepted ${success + failure} events, ${success} succeeded, ${failure} failed`);
} catch(e) {
logger.warn(`Error sending downward messages: ${e}`);
}
}
test(
`${PRESET} preset with vmp queues being spammed af`,
async () => {
const { killZn, paraLog } = await runPresetUntilLaunched(PRESET);
const apis = await getApis();
// This test is meant to not run automatically, so most things are commented out.
// const downSub = apis.rcClient.blocks$.subscribe((block) => {
// if (block.number > 10) {
// logger.verbose(`spammer::down spamming at height ${block.number}`);
// sendDown(apis.rcApi, (block.number * 10) + 50);
// }
// });
// const upSub = apis.paraClient.blocks$.subscribe((block) => {
// if (block.number > 0) {
// logger.verbose(`spammer::up spamming at height ${block.number}`);
// sendUp(apis.paraApi, 40);
// }
// });
const steps: Observe[] = [
Observe.on(Chain.Relay, "Session", "NewSession")
.byBlock(11),
// Observe.on(Chain.Relay, "WontReach", "WontReach")
].map((s) => s.build());
const testCase = new TestCase(steps, true, () => {
killZn();
// downSub.unsubscribe();
// upSub.unsubscribe()
});
const outcome = await runTest(testCase, apis, paraLog);
expect(outcome).toEqual(EventOutcome.Done);
},
{ timeout: GlobalTimeout }
);