Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/makefile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ jobs:
export PATH="/home/runner/.config/.foundry/bin:$PATH";
make build;

- name: Verify storage layout
run: |
git fetch origin main || true
export PATH="/home/runner/.config/.foundry/bin:$PATH";
make verify-storage-layout;

- name: Run tests
run: |
export PATH="/home/runner/.config/.foundry/bin:$PATH";
Expand Down
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,16 @@ extract-abis:
.PHONY: contract-size-check
contract-size-check:
@echo "Checking contract sizes..."
bash tools/check-contract-size.sh
bash tools/check-contract-size.sh

# Generate storage layout
.PHONY: gen
gen:
@echo "Generating storage layout..."
bash tools/gen-storage-layout.sh

# Verify storage layout (for CI)
.PHONY: verify-storage-layout
verify-storage-layout:
@echo "Verifying storage layout..."
bash tools/verify-storage-layout.sh
2 changes: 1 addition & 1 deletion lib/pyth-sdk-solidity
5 changes: 2 additions & 3 deletions src/PDPVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -961,9 +961,8 @@ contract PDPVerifier is Initializable, UUPSUpgradeable, OwnableUpgradeable {

address listenerAddr = dataSetListener[setId];
if (listenerAddr != address(0)) {
PDPListener(listenerAddr).nextProvingPeriod(
setId, nextChallengeEpoch[setId], dataSetLeafCount[setId], extraData
);
PDPListener(listenerAddr)
.nextProvingPeriod(setId, nextChallengeEpoch[setId], dataSetLeafCount[setId], extraData);
}
emit NextProvingPeriod(setId, challengeEpoch, dataSetLeafCount[setId]);
}
Expand Down
31 changes: 31 additions & 0 deletions src/PDPVerifierServiceLayout.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
pragma solidity ^0.8.20;

/**
* @title PDPVerifierServiceLayout
* @notice This file defines the storage layout for PDPVerifier.
* @dev This file is auto-generated by `make gen`. DO NOT EDIT MANUALLY.
* This contract should NOT be deployed or used directly; it is only for storage slot tracking.
*/
abstract contract PDPVerifierServiceLayout {
/**
* @dev STORAGE LAYOUT (slot: type name)
* [slot: 0] uint256 challengeFinality
* [slot: 1] uint64 nextDataSetId
* [slot: 2] mapping(uint256 => mapping(uint256 => struct Cids.Cid)) pieceCids
* [slot: 3] mapping(uint256 => mapping(uint256 => uint256)) pieceLeafCounts
* [slot: 4] mapping(uint256 => mapping(uint256 => uint256)) sumTreeCounts
* [slot: 5] mapping(uint256 => uint256) nextPieceId
* [slot: 6] mapping(uint256 => uint256) dataSetLeafCount
* [slot: 7] mapping(uint256 => uint256) nextChallengeEpoch
* [slot: 8] mapping(uint256 => address) dataSetListener
* [slot: 9] mapping(uint256 => uint256) challengeRange
* [slot: 10] mapping(uint256 => uint256[]) scheduledRemovals
* [slot: 11] mapping(uint256 => mapping(uint256 => uint256)) scheduledRemovalsBitmap
* [slot: 12] mapping(uint256 => address) storageProvider
* [slot: 13] mapping(uint256 => address) dataSetProposedStorageProvider
* [slot: 14] mapping(uint256 => uint256) dataSetLastProvenEpoch
* [slot: 15] struct PDPVerifier.FeeStatus feeStatus
* [slot: 16] struct PDPVerifier.PlannedUpgrade nextUpgrade
*/
}
10 changes: 8 additions & 2 deletions src/SimplePDPService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,10 @@ contract SimplePDPService is PDPListener, IPDPProvingSchedule, Initializable, UU
uint256, /*challengedLeafCount*/
uint256, /*seed*/
uint256 challengeCount
) external onlyPDPVerifier {
)
external
onlyPDPVerifier
{
if (provenThisPeriod[dataSetId]) {
revert("Only one proof of possession allowed per proving period. Open a new proving period.");
}
Expand Down Expand Up @@ -253,7 +256,10 @@ contract SimplePDPService is PDPListener, IPDPProvingSchedule, Initializable, UU
uint256,
/*leafCount*/
bytes calldata
) external onlyPDPVerifier {
)
external
onlyPDPVerifier
{
// initialize state for new data set
if (provingDeadlines[dataSetId] == NO_PROVING_DEADLINE) {
uint256 firstDeadline = block.number + getMaxProvingPeriod();
Expand Down
3 changes: 1 addition & 2 deletions test/PDPVerifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1027,8 +1027,7 @@ contract PDPVerifierPaginationTest is MockFVMTest, PieceHelper {
assertEq(pdpVerifier.getActivePieceCount(setId), 5, "Should have 5 active pieces");

// Test offset beyond range
(Cids.Cid[] memory pieces1, /*uint256[] memory ids1*/, bool hasMore1) =
pdpVerifier.getActivePieces(setId, 10, 5);
(Cids.Cid[] memory pieces1,/*uint256[] memory ids1*/, bool hasMore1) = pdpVerifier.getActivePieces(setId, 10, 5);
assertEq(pieces1.length, 0, "Should return empty when offset beyond range");
assertEq(hasMore1, false, "Should not have more items");

Expand Down
8 changes: 6 additions & 2 deletions test/SimplePDPService.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,12 @@ contract SimplePDPServiceFaultsTest is Test {
}

function testGetPDPConfig() public view {
(uint64 maxProvingPeriod, uint256 challengeWindow, uint256 challengesPerProof, uint256 initChallengeWindowStart)
= pdpService.getPDPConfig();
(
uint64 maxProvingPeriod,
uint256 challengeWindow,
uint256 challengesPerProof,
uint256 initChallengeWindowStart
) = pdpService.getPDPConfig();

assertEq(maxProvingPeriod, 2880, "Max proving period should be 2880");
assertEq(challengeWindow, 60, "Challenge window should be 60");
Expand Down
35 changes: 35 additions & 0 deletions tools/gen-storage-layout.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail

# This script generates the storage layout for PDPVerifier
# It uses forge inspect to get the storage layout in a table format
# and then formats it into a Solidity file for tracking.

CONTRACT="PDPVerifier"
OUTPUT_FILE="src/${CONTRACT}ServiceLayout.sol"

echo "Generating storage layout for ${CONTRACT}..."

# Get storage layout from forge and parse the table format
LAYOUT_CONTENT=$(forge inspect ${CONTRACT} storage-layout | awk -F'|' '/src\/'${CONTRACT}'\.sol/ { gsub(/^[ \t]+|[ \t]+$/, "", $2); gsub(/^[ \t]+|[ \t]+$/, "", $3); gsub(/^[ \t]+|[ \t]+$/, "", $4); print " * [slot: " $4 "] " $3 " " $2 }')

# Generate Solidity file
cat << SOL_EOF > "${OUTPUT_FILE}"
// SPDX-License-Identifier: Apache-2.0 OR MIT
pragma solidity ^0.8.20;

/**
* @title ${CONTRACT}ServiceLayout
* @notice This file defines the storage layout for ${CONTRACT}.
* @dev This file is auto-generated by \`make gen\`. DO NOT EDIT MANUALLY.
* This contract should NOT be deployed or used directly; it is only for storage slot tracking.
*/
abstract contract ${CONTRACT}ServiceLayout {
/**
* @dev STORAGE LAYOUT (slot: type name)
${LAYOUT_CONTENT}
*/
}
SOL_EOF

echo "Storage layout generated at ${OUTPUT_FILE}"
82 changes: 82 additions & 0 deletions tools/verify-storage-layout.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
set -euo pipefail

# This script verifies that the checked-in storage layout matches the freshly generated one
# and ensures that only additions are made to the storage layout when compared to the base branch.

CONTRACT="PDPVerifier"
LAYOUT_FILE="src/${CONTRACT}ServiceLayout.sol"
TMP_LAYOUT_FILE="src/${CONTRACT}ServiceLayout.sol.tmp"

echo "Verifying storage layout for ${CONTRACT}..."

GEN_SCRIPT="tools/gen-storage-layout.sh"
if [ ! -f "$GEN_SCRIPT" ]; then
echo "Error: Gen script not found at $GEN_SCRIPT"
exit 1
fi

# Run gen script which outputs to the actual LAYOUT_FILE,
# but we first move the original aside so we can compare it!
if [ -f "$LAYOUT_FILE" ]; then
cp "$LAYOUT_FILE" "${LAYOUT_FILE}.bak"
else
# If the file didn't exist at all locally, just create an empty backup
touch "${LAYOUT_FILE}.bak"
fi

# Generate fresh layout
bash "$GEN_SCRIPT"
mv "$LAYOUT_FILE" "$TMP_LAYOUT_FILE"

# Restore original file for comparison
if [ -s "${LAYOUT_FILE}.bak" ]; then
mv "${LAYOUT_FILE}.bak" "$LAYOUT_FILE"
else
rm "${LAYOUT_FILE}.bak"
touch "$LAYOUT_FILE"
fi

# 1. Check if files match
# (Compare locally checked-in layout vs what make gen produces)
if [ ! -s "$LAYOUT_FILE" ]; then
echo "Error: Checked-in storage layout does not exist. Please run 'make gen' and commit."
rm -f "$TMP_LAYOUT_FILE"
exit 1
fi

if ! diff -q "$LAYOUT_FILE" "$TMP_LAYOUT_FILE" > /dev/null; then
echo "Error: Checked-in storage layout does not match freshly generated one!"
echo "Please run 'make gen' and commit the changes."
diff -u "$LAYOUT_FILE" "$TMP_LAYOUT_FILE" || true
rm "$TMP_LAYOUT_FILE"
exit 1
fi

# 2. Check for destructive changes (only additions allowed vs base branch)
BASE_BRANCH=${GITHUB_BASE_REF:-main}
BASE_LAYOUT_FILE="src/${CONTRACT}ServiceLayout.sol.base"

echo "Checking for destructive storage changes against branch: $BASE_BRANCH..."
if git show "origin/$BASE_BRANCH:$LAYOUT_FILE" > "$BASE_LAYOUT_FILE" 2>/dev/null || git show "$BASE_BRANCH:$LAYOUT_FILE" > "$BASE_LAYOUT_FILE" 2>/dev/null; then
OLD_SLOTS=$(grep "\[slot:" "$BASE_LAYOUT_FILE" | sed 's/^[[:space:]]*//')
NEW_SLOTS=$(grep "\[slot:" "$TMP_LAYOUT_FILE" | sed 's/^[[:space:]]*//')

while IFS= read -r old_line; do
if [ -z "$old_line" ]; then continue; fi
if ! echo "$NEW_SLOTS" | grep -Fqx "$old_line"; then
echo "Error: Destructive storage change detected!"
echo "Missing or modified slot from base branch ($BASE_BRANCH):"
echo " $old_line"
rm "$BASE_LAYOUT_FILE"
rm "$TMP_LAYOUT_FILE"
exit 1
fi
done <<< "$OLD_SLOTS"
rm "$BASE_LAYOUT_FILE"
else
echo "Base layout not found on $BASE_BRANCH. Skipping destructive change check."
fi

echo "Storage layout verification passed."
rm "$TMP_LAYOUT_FILE"
Loading