Skip to content

Commit

Permalink
Adds support for AsyncBacking SlotInfo in State Manipulator
Browse files Browse the repository at this point in the history
  • Loading branch information
crystalin committed Nov 23, 2024
1 parent 1936f4f commit bb21a7d
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 38 deletions.
43 changes: 43 additions & 0 deletions src/libs/helpers/state-manipulator/cumulus-manipulator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Debug from "debug";

import {
Action,
encodeStorageBlake128MapKey,
encodeStorageKey,
StateLine,
StateManipulator,
} from "./genesis-parser.ts";
import { nToHex } from "@polkadot/util";

const debug = Debug("helper:cumulus-manipulator");

export class CumulusManipulator implements StateManipulator {
private readonly newTimestamp: bigint;

private readonly slotInfoKey: string;

constructor(newTimestamp: bigint) {
this.newTimestamp = newTimestamp;
this.slotInfoKey = encodeStorageKey("AsyncBacking", "SlotInfo");
}

processRead = (_) => {};

prepareWrite = () => {
};

processWrite = ({ key, value }) => {
if (key.startsWith(this.slotInfoKey)) {
debug(`Found async backing SlotInfo: ${value}. Resetting to ${this.newTimestamp}`);
return {
action: "remove" as Action,
extraLines: [
{
key: key,
value: `0x${nToHex(this.newTimestamp, { isLe: true, bitLength: 64 }).slice(2)}01000000`,
},
],
};
}
};
}
4 changes: 3 additions & 1 deletion src/libs/helpers/state-manipulator/state-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { SpecManipulator } from "./spec-manipulator.ts";
import { SudoManipulator } from "./sudo-manipulator.ts";
import { ValidationManipulator } from "./validation-manipulator.ts";
import { XCMPManipulator } from "./xcmp-manipulator.ts";
import { CumulusManipulator } from "./cumulus-manipulator.ts";

const debug = Debug("helper:state-manager");

Expand Down Expand Up @@ -115,7 +116,7 @@ export async function downloadExportedState(
return { stateFile, stateInfo };
}
}
const client = new Client(`http://states.kaki.dev`);
const client = new Client(`https://export.kaki.dev`);
const downloadedStateInfo: StateInfo = (await (
await client.request({
path: `/${network}-state${stateDate ? `-${stateDate}` : ""}.info.json`,
Expand Down Expand Up @@ -215,6 +216,7 @@ export async function neutralizeExportedState(
return { current, first: 0, length: 100 };
}),
new AuthorFilteringManipulator(100),
new CumulusManipulator(0n),
new SudoManipulator(ALITH_ADDRESS),
new CollatorManipulator(ALITH_ADDRESS, ALITH_SESSION_ADDRESS),
new HRMPManipulator(),
Expand Down
108 changes: 108 additions & 0 deletions src/tools/benchmark-lazy-nodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import yargs from "yargs";
import chalk from "chalk";

import { getApiFor, NETWORK_YARGS_OPTIONS } from "../utils/networks.ts";
import { mapExtrinsics } from "src/utils/types.ts";
import { getBlockDetails } from "src/utils/monitoring.ts";

const argv = yargs(process.argv.slice(2))
.usage("Usage: $0")
.version("1.0.0")
.options({
...NETWORK_YARGS_OPTIONS,
at: {
type: "number",
description: "Block number to look into",
},
tx: {
type: "string",
description: "transaction to replay",
demandOption: true,
},
}).argv;

const main = async () => {
const api = await getApiFor(argv);

const blockHash = argv.at
? await api.rpc.chain.getBlockHash(argv.at)
: await api.rpc.chain.getBlockHash();

const block = await getBlockDetails(api, blockHash);

const tx = block.txWithEvents.find((tx) => {
return tx.extrinsic.hash.toHex().toLocaleLowerCase() == argv.tx.toLocaleLowerCase()
});

if (!tx) {
console.error(`Transaction ${argv.tx} not found`);
process.exit(1);
}
console.log(`Transaction ${argv.tx} found ${!tx.dispatchError ? "✅" : "🟥"} (ref: ${tx.dispatchInfo.weight.refTime.toString().padStart(12)}, pov: ${tx.dispatchInfo.weight.proofSize.toString().padStart(9)})`);

const extrinsicData = tx.extrinsic.toHex();

const servers = [{
"name": "original",
"color": "gray",
"url": "ws://127.0.0.1:9947",
}, {
"name": "pov refund",
"color": "yellow",
"url": "ws://127.0.0.1:9948",
}, {
"name": "pov refund + fix",
"color": "green",
"url": "ws://127.0.0.1:9949",
}];



await Promise.all(servers.map(async (server) => {
const api = await getApiFor({ url: server.url });
const serverName = chalk[server.color](server.name.padStart(20));
let valid = 0;

const unsubscribe = await api.rpc.chain.subscribeNewHeads(async (header) => {
// console.log(`[${serverName}] Chain is at block: #${header.number}`);
const block = await getBlockDetails(api, header.hash);

for (const tx of block.txWithEvents) {
if (tx.extrinsic.hash.toHex().toLocaleLowerCase() == argv.tx.toLocaleLowerCase()) {
console.log(`[${serverName}] Transaction ${argv.tx} found in block ${header.number} ${!tx.dispatchError ? "✅" : "🟥"} (ref: ${tx.dispatchInfo.weight.refTime.toString().padStart(12)}, pov: ${tx.dispatchInfo.weight.proofSize.toString().padStart(9)})`);
unsubscribe();
valid = 1;
break;
}
};
})

const extrinsic = api.tx(extrinsicData);

try {
const hash = await api.rpc.author.submitExtrinsic(extrinsic);
console.log(`[${serverName}] Submitted hash: ${hash}`);
} catch (error) {
console.error(`[${serverName}] Failed to submit:`, error);
}

while (valid == 0) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}

await api.disconnect();
}));

await api.disconnect();
};

async function start() {
try {
await main();
} catch (e) {
console.error(e);
process.exit(1);
}
}

start();
72 changes: 36 additions & 36 deletions src/utils/monitoring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ export const getAccountIdentities = async (
const superIdentityOpts =
validSuperOfs.length > 0
? await api.rpc.state.queryStorageAt<Option<PalletIdentityRegistration>[]>(
validSuperOfs.map(
(superOf) => api.query.identity.identityOf.key(superOf[0].toString()),
at,
),
)
validSuperOfs.map(
(superOf) => api.query.identity.identityOf.key(superOf[0].toString()),
at,
),
)
: [];
let index = 0;
return superOfs.map((superOf) => {
Expand Down Expand Up @@ -148,9 +148,8 @@ export const getAccountIdentities = async (
return account && identity
? u8aToString(identity.info.display.asRaw.toU8a(true))
: superOf && superOf.identity
? `${u8aToString(superOf.identity.info.display.asRaw.toU8a(true))} - Sub ${
(superOf.data && u8aToString(superOf.data.asRaw.toU8a(true))) || ""
}`
? `${u8aToString(superOf.identity.info.display.asRaw.toU8a(true))} - Sub ${(superOf.data && u8aToString(superOf.data.asRaw.toU8a(true))) || ""
}`
: account?.toString();
});
};
Expand All @@ -165,22 +164,22 @@ export const getAccountIdentity = async (
if (!identityCache[account] || identityCache[account].lastUpdate < Date.now() - 3600 * 1000) {
const [identity, superOfIdentity] = api.query.identity
? await Promise.all([
api.query.identity
.identityOf(account.toString())
.then((a) => (a.isSome ? a.unwrap() : null)),
api.query.identity.superOf(account.toString()).then(async (superOfOpt) => {
const superOf = (superOfOpt.isSome && superOfOpt.unwrap()) || null;
if (!superOf) {
return null;
}
const identityOpt = await api.query.identity.identityOf(superOf[0].toString());
const identity = (identityOpt.isSome && identityOpt.unwrap()) || null;
return {
identity,
data: superOf[1],
};
}),
])
api.query.identity
.identityOf(account.toString())
.then((a) => (a.isSome ? a.unwrap() : null)),
api.query.identity.superOf(account.toString()).then(async (superOfOpt) => {
const superOf = (superOfOpt.isSome && superOfOpt.unwrap()) || null;
if (!superOf) {
return null;
}
const identityOpt = await api.query.identity.identityOf(superOf[0].toString());
const identity = (identityOpt.isSome && identityOpt.unwrap()) || null;
return {
identity,
data: superOf[1],
};
}),
])
: [null, null];
identityCache[account] = {
lastUpdate: Date.now(),
Expand All @@ -194,16 +193,18 @@ export const getAccountIdentity = async (
return identity
? u8aToString(identity.info.display.asRaw.toU8a(true))
: superOf
? `${u8aToString(superOf.identity.info.display.asRaw.toU8a(true))} - Sub ${
(superOf.data && u8aToString(superOf.data.asRaw.toU8a(true))) || ""
}`
? `${u8aToString(superOf.identity.info.display.asRaw.toU8a(true))} - Sub ${(superOf.data && u8aToString(superOf.data.asRaw.toU8a(true))) || ""
}`
: account?.toString();
};

export const getAccountFromNimbusKey = async (
api: ApiPromise | ApiDecoration<"promise">,
nmbsKey: string,
): Promise<string> => {
if (!nmbsKey) {
return null
}
if (
!authorMappingCache[nmbsKey] ||
authorMappingCache[nmbsKey].lastUpdate < Date.now() - 3600 * 1000
Expand Down Expand Up @@ -541,7 +542,7 @@ export function generateBlockDetailsLog(
? payload.asEip2930?.gasPrice.toBigInt()
: payload.isEip1559
? // If gasPrice is not indicated, we should use the base fee defined in that block
payload.asEip1559?.maxFeePerGas.toBigInt() || 0n
payload.asEip1559?.maxFeePerGas.toBigInt() || 0n
: (payload as any as LegacyTransaction).gasPrice?.toBigInt();

const refTime = (dispatchInfo.weight as any).toBn
Expand Down Expand Up @@ -572,7 +573,7 @@ export function generateBlockDetailsLog(
? payload.asEip2930?.gasPrice.toBigInt()
: payload.isEip1559
? // If gasPrice is not indicated, we should use the base fee defined in that block
payload.asEip1559?.maxFeePerGas.toBigInt() || 0n
payload.asEip1559?.maxFeePerGas.toBigInt() || 0n
: (payload as any as LegacyTransaction).gasPrice?.toBigInt();
}
return tx.events.reduce((total, event) => {
Expand All @@ -597,8 +598,8 @@ export function generateBlockDetailsLog(
const authorId =
blockDetails.authorName.length > 24
? `${blockDetails.authorName.substring(0, 9)}..${blockDetails.authorName.substring(
blockDetails.authorName.length - 6,
)}`
blockDetails.authorName.length - 6,
)}`
: blockDetails.authorName;
const authorName = blockDetails.isAuthorOrbiter ? chalk.yellow(authorId) : authorId;

Expand All @@ -614,11 +615,10 @@ export function generateBlockDetailsLog(
.padEnd(
7,
" ",
)} [${weightText}%, ${storageText}B, ${feesText} fees, ${extText} Txs (${evmText} Eth)(<->${coloredTransferred})]${
txPoolText ? `[Pool:${txPoolText}${poolIncText ? `(+${poolIncText})` : ""}]` : ``
}${secondText ? `[${secondText}s]` : ""}(hash: ${hash.substring(0, 7)}..${hash.substring(
hash.length - 4,
)})${options?.suffix ? ` ${options.suffix}` : ""} by ${authorName}`;
)} [${weightText}%, ${storageText}B, ${feesText} fees, ${extText} Txs (${evmText} Eth)(<->${coloredTransferred})]${txPoolText ? `[Pool:${txPoolText}${poolIncText ? `(+${poolIncText})` : ""}]` : ``
}${secondText ? `[${secondText}s]` : ""}(hash: ${hash.substring(0, 7)}..${hash.substring(
hash.length - 4,
)})${options?.suffix ? ` ${options.suffix}` : ""} by ${authorName}`;
}

export function printBlockDetails(
Expand Down
2 changes: 1 addition & 1 deletion src/utils/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export const getWsProviderFor = (argv: Argv) => {
if (isKnownNetwork(argv.network)) {
return getWsProviderForNetwork(argv.network);
}
return new WsProvider(argv.url);
return new WsProvider(argv.url || process.env.MOONBEAM_TOOLS_WS_URL);
};

export const getHttpProviderForNetwork = (name: NETWORK_NAME) => {
Expand Down
8 changes: 8 additions & 0 deletions test/state-manipulation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
HEATH_ADDRESS,
JUDITH_ADDRESS,
} from "../src/utils/constants.ts";
import { CumulusManipulator } from "src/libs/helpers/state-manipulator/cumulus-manipulator.ts";

describe("State Manipulation", () => {
const inFile = path.join(__dirname, "sample-state.json");
Expand All @@ -35,6 +36,7 @@ describe("State Manipulation", () => {
}),
new SudoManipulator(JUDITH_ADDRESS),
new AuthorFilteringManipulator(100),
new CumulusManipulator(288730710n),
new AuthorizeUpgradeManipulator(
"0xfb9f16ba6b3433ba2a273974207260c7ace6aa629992d492bad0ba873b39762d",
),
Expand Down Expand Up @@ -216,4 +218,10 @@ describe("State Manipulation", () => {
finalState["0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b"],
).toEqual(JUDITH_ADDRESS);
});

it("Should have the slot info reset to 0", async () => {
expect(
finalState["0x8985dff79e6002d0deba9ddac46f32a5a70806914c906d747e668a21f9021729"],
).toEqual("0x56ae35110000000001000000");

Check failure on line 225 in test/state-manipulation.spec.ts

View workflow job for this annotation

GitHub Actions / build

test/state-manipulation.spec.ts > State Manipulation > Should have the slot info reset to 0

AssertionError: expected undefined to deeply equal '0x56ae35110000000001000000' - Expected: "0x56ae35110000000001000000" + Received: undefined ❯ test/state-manipulation.spec.ts:225:7
});
});

0 comments on commit bb21a7d

Please sign in to comment.