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

Allow usage of full PoV size #3198

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ jobs:
- name: Cargo build
uses: ./.github/workflow-templates/cargo-build
with:
features: metadata-hash
features: metadata-hash,full-pov-size
- name: Upload runtimes
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -627,7 +627,7 @@ jobs:

typescript-tracing-tests:
if: >
(github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork) ||
(github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork) ||
(github.event_name == 'push' && github.ref == 'refs/heads/master')
runs-on:
labels: bare-metal
Expand Down
36 changes: 18 additions & 18 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ metadata-hash = ["moonbeam-service/metadata-hash"]

lazy-loading = ["moonbeam-service/lazy-loading", "moonbeam-cli/lazy-loading"]

full-pov-size = ["moonbeam-service/full-pov-size"]

test-spec = []

runtime-benchmarks = [
Expand Down
2 changes: 2 additions & 0 deletions node/service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ default = [

lazy-loading = ["sc-service/test-helpers", "parking_lot/send_guard"]

full-pov-size = ["nimbus-consensus/full-pov-size"]

rococo-native = ["polkadot-cli/rococo-native", "polkadot-service/rococo-native"]
westend-native = [
"polkadot-cli/westend-native",
Expand Down
44 changes: 44 additions & 0 deletions test/contracts/src/StorageFiller.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
pragma solidity ^0.8.0;

contract StorageFiller {
// Mapping to store large byte arrays
mapping(uint256 => bytes) public largeStorage;

// Fill a single storage slot with a large value
function fillStorage(uint256 slot, uint256 size) public {
bytes memory data = new bytes(size);
// Fill with non-zero data to ensure it's stored
for (uint i = 0; i < size; i++) {
data[i] = bytes1(uint8((slot + i) % 256));
}
largeStorage[slot] = data;
}

// Fill multiple storage slots in one transaction
function fillStorageBatch(
uint256 startSlot,
uint256 count,
uint256 size
) public {
for (uint256 i = 0; i < count; i++) {
fillStorage(startSlot + i, size);
}
}

// Read a single storage slot
function readStorage(uint256 slot) public view returns (bytes memory) {
return largeStorage[slot];
}

// Read multiple storage slots in one transaction
function readStorageBatch(
uint256 startSlot,
uint256 count
) public view returns (uint256) {
uint256 totalSize = 0;
for (uint256 i = 0; i < count; i++) {
totalSize += readStorage(startSlot + i).length;
}
return totalSize;
}
}
132 changes: 132 additions & 0 deletions test/suites/dev/moonbase/test-pov/test-max-pov.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import "@moonbeam-network/api-augment";
import { beforeAll, deployCreateCompiledContract, describeSuite, expect } from "@moonwall/cli";
import { ALITH_ADDRESS, createEthersTransaction } from "@moonwall/util";
import { type Abi, encodeFunctionData } from "viem";

describeSuite({
id: "D012703",
title: "PoV size test - approaching maximum limit",
foundationMethods: "dev",
testCases: ({ context, it, log }) => {
let storageFillerAddress: `0x${string}`;
let storageFillerAbi: Abi;

// Target a PoV size of approximately 7.5MB
const TARGET_POV_MB = 7.5;
const TARGET_POV_BYTES = Math.floor(TARGET_POV_MB * 1024 * 1024);

// We'll create storage slots with large values
const SLOT_SIZE = 24 * 1024; // 24KB per slot
const NUM_SLOTS = 350; // Should give us ~8.4MB of raw storage data

beforeAll(async () => {
// Deploy a contract specifically designed to fill storage
const { contractAddress, abi } = await deployCreateCompiledContract(context, "StorageFiller");
storageFillerAddress = contractAddress;
storageFillerAbi = abi;

// Fill storage with large values in separate transactions
// This creates many storage slots with large values
log(`Filling ${NUM_SLOTS} storage slots with ${SLOT_SIZE} bytes each...`);

// Fill in batches to avoid transaction size limits
const BATCH_SIZE = 10;
for (let i = 0; i < NUM_SLOTS; i += BATCH_SIZE) {
const batchSize = Math.min(BATCH_SIZE, NUM_SLOTS - i);
const fillData = encodeFunctionData({
abi: storageFillerAbi,
functionName: "fillStorageBatch",
args: [i, batchSize, SLOT_SIZE],
});

const tx = await createEthersTransaction(context, {
to: storageFillerAddress,
data: fillData,
txnType: "eip1559",
gasLimit: 15000000n,
});

await context.createBlock(tx);
log(`Filled slots ${i} to ${i + batchSize - 1}`);
}
});

it({
id: "T01",
title: "should generate a large PoV by accessing many storage slots",
test: async function () {
// Now create a transaction that reads all these storage slots
// This will force the inclusion of all storage proofs in the PoV
const readData = encodeFunctionData({
abi: storageFillerAbi,
functionName: "readStorageBatch",
args: [0, NUM_SLOTS],
});

const gasEstimate = await context.viem().estimateGas({
account: ALITH_ADDRESS,
to: storageFillerAddress,
data: readData,
});

log(`Estimated gas for reading all slots: ${gasEstimate}`);

const rawSigned = await createEthersTransaction(context, {
to: storageFillerAddress,
data: readData,
txnType: "eip1559",
gasLimit: gasEstimate * 120n / 100n, // Add 20% buffer
});

const { result, block } = await context.createBlock(rawSigned);
const proofSize = block.proofSize ?? 0;

log(`Block proof size: ${proofSize} bytes (${(proofSize / (1024 * 1024)).toFixed(2)} MB)`);
log(`Transaction successful: ${result?.successful} `);

// Check if PoV size is in the expected range
expect(block.proofSize).toBeGreaterThanOrEqual(TARGET_POV_BYTES * 0.7);
expect(result?.successful).to.equal(true);
},
});

it({
id: "T02",
title: "should measure PoV size with incremental storage access",
test: async function () {
// Test with different numbers of slots to see how PoV size scales
const slotCounts = [50, 100, 200, 300, NUM_SLOTS];

for (const count of slotCounts) {
try {
const readData = encodeFunctionData({
abi: storageFillerAbi,
functionName: "readStorageBatch",
args: [0, count],
});

const gasEstimate = await context.viem().estimateGas({
account: ALITH_ADDRESS,
to: storageFillerAddress,
data: readData,
});

const rawSigned = await createEthersTransaction(context, {
to: storageFillerAddress,
data: readData,
txnType: "eip1559",
gasLimit: gasEstimate * 120n / 100n,
});

const { result, block } = await context.createBlock(rawSigned);
const proofSize = block.proofSize ?? 0;

log(`Slots: ${count}, PoV size: ${proofSize} bytes(${(proofSize / (1024 * 1024)).toFixed(2)} MB), Success: ${result?.successful} `);
} catch (error) {
log(`Slots: ${count}, Error: ${error.message} `);
}
}
},
});
},
});
Loading