fix: harden V1 deployment manifests

This commit is contained in:
golem
2026-08-21 02:36:17 -06:00
parent 52935acf5e
commit be8f01ea4e
8 changed files with 252 additions and 50 deletions
+1
View File
@@ -13,6 +13,7 @@ contract CheckState is DemoScript {
function run() external view {
_requireSupportedChain(block.chainid);
_printEducationalWarning();
string memory stage = vm.envOr("DEMO_EXPECTED_STAGE", string("v1"));
bool deployedStage = keccak256(bytes(stage)) == keccak256("deployed");
Manifest memory manifest = _readManifest(_manifestPath(ACTIVE_MANIFEST_PATH), !deployedStage);
+2 -1
View File
@@ -9,6 +9,7 @@ import {DemoScript} from "./lib/DemoScript.sol";
contract DeployV1 is DemoScript {
function run() external returns (address tokenAddress, address proxy, address implementation) {
_requireSupportedChain(block.chainid);
_printEducationalWarning();
address sender = vm.envAddress("SCRIPT_SENDER");
if (block.chainid == ANVIL_CHAIN_ID) {
@@ -50,7 +51,7 @@ contract DeployV1 is DemoScript {
_writeManifest(_manifestPath(PENDING_MANIFEST_PATH), manifest);
}
function _actors(address sender) private returns (string[] memory labels, address[] memory actors) {
function _actors(address sender) private view returns (string[] memory labels, address[] memory actors) {
if (block.chainid == ANVIL_CHAIN_ID) {
labels = new string[](3);
actors = new address[](3);
+1 -1
View File
@@ -8,8 +8,8 @@ import {DemoScript} from "./lib/DemoScript.sol";
contract SeedV1Demo is DemoScript {
function run() external {
if (block.chainid != ANVIL_CHAIN_ID) revert UnsupportedChain(block.chainid);
_printEducationalWarning();
Manifest memory manifest = _readManifest(_manifestPath(ACTIVE_MANIFEST_PATH), true);
if (manifest.actors.length != 3 || manifest.actorLabels.length != 3) revert InvalidManifestSchema(1);
(uint256 ownerKey, address owner) = _deriveLocalActor(block.chainid, 0);
(uint256 aliceKey, address alice) = _deriveLocalActor(block.chainid, 1);
+47 -1
View File
@@ -2,6 +2,7 @@
pragma solidity 0.8.35;
import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {BankV1} from "../../src/BankV1.sol";
import {MockUSDC} from "../../src/MockUSDC.sol";
@@ -11,12 +12,14 @@ abstract contract DemoScript is Script {
string internal constant ANVIL_TEST_PHRASE = "test test test test test test test test test test test junk";
string internal constant PENDING_MANIFEST_PATH = "deployments/pending.json";
string internal constant ACTIVE_MANIFEST_PATH = "deployments/active.json";
string internal constant EDUCATIONAL_WARNING = unicode"Educational demo — mock token — never use real funds.";
error UnsupportedChain(uint256 chainId);
error ManifestChainMismatch(uint256 expected, uint256 actual);
error MissingCode(string label, address target);
error InvalidDeploymentBlock();
error InvalidManifestSchema(uint256 schemaVersion);
error InvalidActorConfiguration();
error UnexpectedState(string label, uint256 expected, uint256 actual);
error UnexpectedAddress(string label, address expected, address actual);
@@ -39,7 +42,11 @@ abstract contract DemoScript is Script {
if (chainId != ANVIL_CHAIN_ID && chainId != BASE_SEPOLIA_CHAIN_ID) revert UnsupportedChain(chainId);
}
function _deriveLocalActor(uint256 chainId, uint32 index) internal returns (uint256 privateKey, address actor) {
function _deriveLocalActor(uint256 chainId, uint32 index)
internal
pure
returns (uint256 privateKey, address actor)
{
if (chainId != ANVIL_CHAIN_ID) revert UnsupportedChain(chainId);
privateKey = vm.deriveKey(ANVIL_TEST_PHRASE, index);
actor = vm.addr(privateKey);
@@ -67,6 +74,7 @@ abstract contract DemoScript is Script {
if (manifest.schemaVersion != 1) revert InvalidManifestSchema(manifest.schemaVersion);
if (manifest.chainId != block.chainid) revert ManifestChainMismatch(block.chainid, manifest.chainId);
if (active && manifest.deploymentBlock == 0) revert InvalidDeploymentBlock();
_validateActorConfiguration(manifest);
_requireCode("token", manifest.token);
_requireCode("proxy", manifest.proxy);
_requireCode("implementation", manifest.implementation);
@@ -76,6 +84,44 @@ abstract contract DemoScript is Script {
if (target == address(0) || target.code.length == 0) revert MissingCode(label, target);
}
function _validateActorConfiguration(Manifest memory manifest) internal pure {
if (manifest.actorLabels.length != manifest.actors.length) revert InvalidActorConfiguration();
if (manifest.chainId == ANVIL_CHAIN_ID) {
if (
manifest.actorLabels.length != 3 || !_equals(manifest.network, "anvil")
|| !_equals(manifest.actorLabels[0], "owner") || !_equals(manifest.actorLabels[1], "Alice")
|| !_equals(manifest.actorLabels[2], "Bob")
) revert InvalidActorConfiguration();
(, address owner) = _deriveLocalActor(ANVIL_CHAIN_ID, 0);
(, address alice) = _deriveLocalActor(ANVIL_CHAIN_ID, 1);
(, address bob) = _deriveLocalActor(ANVIL_CHAIN_ID, 2);
if (
manifest.actors[0] != manifest.owner || manifest.actors[0] != owner || manifest.actors[1] != alice
|| manifest.actors[2] != bob
) revert InvalidActorConfiguration();
return;
}
if (
manifest.chainId != BASE_SEPOLIA_CHAIN_ID || manifest.actorLabels.length != 1
|| !_equals(manifest.network, "base-sepolia") || !_equals(manifest.actorLabels[0], "owner")
|| manifest.actors[0] != manifest.owner
) revert InvalidActorConfiguration();
}
function _educationalWarning() internal pure returns (string memory) {
return EDUCATIONAL_WARNING;
}
function _printEducationalWarning() internal pure {
console2.log(EDUCATIONAL_WARNING);
}
function _equals(string memory left, string memory right) private pure returns (bool) {
return keccak256(bytes(left)) == keccak256(bytes(right));
}
function _serializeManifest(Manifest memory manifest) internal returns (string memory json) {
string memory objectKey = "demo-manifest";
vm.serializeUint(objectKey, "schemaVersion", manifest.schemaVersion);