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, 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, 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 } );