Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: 🎲 randomize contract size smoke test #3028

Merged
merged 17 commits into from
Dec 19, 2024
96 changes: 96 additions & 0 deletions test/helpers/storageQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,99 @@ export async function processAllStorage(

await limiter.disconnect();
}

export async function processRandomStoragePrefixes(
api: ApiPromise,
storagePrefix: string,
blockHash: string,
processor: (batchResult: { key: `0x${string}`; value: string }[]) => void,
override = ""
) {
const maxKeys = 1000;
let total = 0;
const preFilteredPrefixes = splitPrefix(storagePrefix);
const chanceToSample = 0.05;
let prefixes = override
? [override]
: preFilteredPrefixes.filter(() => Math.random() < chanceToSample);
if (prefixes.length > 25) {
prefixes = prefixes.slice(0, 25);
}
console.log(`Processing ${prefixes.length} prefixes: ${prefixes.join(", ")}`);
const limiter = rateLimiter();
const stopReport = startReport(() => total);

try {
await Promise.all(
prefixes.map(async (prefix) =>
limiter.schedule(async () => {
let startKey: string | undefined = undefined;
while (true) {
// @ts-expect-error _rpcCore is not yet exposed
const keys: string = await api._rpcCore.provider.send("state_getKeysPaged", [
prefix,
maxKeys,
startKey,
blockHash,
]);

if (!keys.length) {
break;
}

// @ts-expect-error _rpcCore is not yet exposed
const response = await api._rpcCore.provider.send("state_queryStorageAt", [
keys,
blockHash,
]);

try {
processor(
response[0].changes.map((pair: [string, string]) => ({
key: pair[0],
value: pair[1],
}))
);
} catch (e) {
console.log(`Error processing ${prefix}: ${e}`);
console.log(`Replace the empty string in smoke/test-ethereum-contract-code.ts
with the prefix to reproduce`);
}

total += keys.length;

if (keys.length !== maxKeys) {
break;
}
startKey = keys[keys.length - 1];
}
})
)
);
} finally {
stopReport();
}

await limiter.disconnect();
}

export const extractStorageKeyComponents = (storageKey: string) => {
// The full storage key is composed of
// - The 0x prefix (2 characters)
// - The module prefix (32 characters)
// - The method name (32 characters)
// - The parameters (variable length)
const regex = /(?<moduleKey>0x[a-f0-9]{32})(?<fnKey>[a-f0-9]{32})(?<paramsKey>[a-f0-9]*)/i;
const match = regex.exec(storageKey);

if (!match) {
throw new Error("Invalid storage key format");
}

const { moduleKey, fnKey, paramsKey } = match.groups!;
return {
moduleKey,
fnKey,
paramsKey,
};
};
28 changes: 18 additions & 10 deletions test/suites/smoke/test-ethereum-contract-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ONE_HOURS } from "@moonwall/util";
import { compactStripLength, hexToU8a, u8aConcat, u8aToHex } from "@polkadot/util";
import { xxhashAsU8a } from "@polkadot/util-crypto";
import chalk from "chalk";
import { processAllStorage } from "../../helpers/storageQueries.js";
import { processRandomStoragePrefixes } from "../../helpers/storageQueries.js";

describeSuite({
id: "S08",
Expand Down Expand Up @@ -37,16 +37,24 @@ describeSuite({
u8aConcat(xxhashAsU8a("EVM", 128), xxhashAsU8a("AccountCodes", 128))
);
const t0 = performance.now();
await processAllStorage(paraApi, keyPrefix, blockHash, (items) => {
for (const item of items) {
const codesize = getBytecodeSize(hexToU8a(item.value));
if (codesize > MAX_CONTRACT_SIZE_BYTES) {
const accountId = "0x" + item.key.slice(-40);
failedContractCodes.push({ accountId, codesize });

await processRandomStoragePrefixes(
paraApi,
keyPrefix,
blockHash,
(items) => {
for (const item of items) {
const codesize = getBytecodeSize(hexToU8a(item.value));
if (codesize > MAX_CONTRACT_SIZE_BYTES) {
const accountId = "0x" + item.key.slice(-40);
failedContractCodes.push({ accountId, codesize });
}
}
}
totalContracts += BigInt(items.length);
});
totalContracts += BigInt(items.length);
},
// WHEN DEBUGGING REPLACE THE EMPTY STRING WITH A PREFIX TO FETCH
""
);

const t1 = performance.now();
const checkTime = (t1 - t0) / 1000;
Expand Down
22 changes: 1 addition & 21 deletions test/suites/smoke/test-polkadot-decoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import chalk from "chalk";
import { describeSuite, beforeAll } from "@moonwall/cli";
import { ONE_HOURS } from "@moonwall/util";
import type { ApiPromise } from "@polkadot/api";
import { extractStorageKeyComponents } from "../../helpers/storageQueries";
import { fail } from "node:assert";

// Change the following line to reproduce a particular case
Expand All @@ -12,27 +13,6 @@ const FN_NAME = "";

const pageSize = (process.env.PAGE_SIZE && Number.parseInt(process.env.PAGE_SIZE)) || 500;

const extractStorageKeyComponents = (storageKey: string) => {
// The full storage key is composed of
// - The 0x prefix (2 characters)
// - The module prefix (32 characters)
// - The method name (32 characters)
// - The parameters (variable length)
const regex = /(?<moduleKey>0x[a-f0-9]{32})(?<fnKey>[a-f0-9]{32})(?<paramsKey>[a-f0-9]*)/i;
const match = regex.exec(storageKey);

if (!match) {
throw new Error("Invalid storage key format");
}

const { moduleKey, fnKey, paramsKey } = match.groups!;
return {
moduleKey,
fnKey,
paramsKey,
};
};

const randomHex = (nBytes) =>
[...crypto.getRandomValues(new Uint8Array(nBytes))]
.map((m) => ("0" + m.toString(16)).slice(-2))
Expand Down
Loading