fix: enforce optional manifest fields
This commit is contained in:
@@ -116,6 +116,7 @@ abstract contract DemoScript is Script {
|
|||||||
actors = new Actor[](actorCount);
|
actors = new Actor[](actorCount);
|
||||||
for (uint256 i; i < actorCount; ++i) {
|
for (uint256 i; i < actorCount; ++i) {
|
||||||
string memory index = vm.toString(i);
|
string memory index = vm.toString(i);
|
||||||
|
_assertExactActorSchema(json, index);
|
||||||
actors[i].label = vm.parseJsonString(json, string.concat(".actors[", index, "].label"));
|
actors[i].label = vm.parseJsonString(json, string.concat(".actors[", index, "].label"));
|
||||||
actors[i].address_ = vm.parseJsonAddress(json, string.concat(".actors[", index, "].address"));
|
actors[i].address_ = vm.parseJsonAddress(json, string.concat(".actors[", index, "].address"));
|
||||||
}
|
}
|
||||||
@@ -124,6 +125,19 @@ abstract contract DemoScript is Script {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _assertExactActorSchema(string memory json, string memory index) private view {
|
||||||
|
string[] memory keys = vm.parseJsonKeys(json, string.concat(".actors[", index, "]"));
|
||||||
|
bool hasLabel;
|
||||||
|
bool hasAddress;
|
||||||
|
if (keys.length != 2) revert InvalidManifestSchema(0);
|
||||||
|
for (uint256 i; i < keys.length; ++i) {
|
||||||
|
if (_equals(keys[i], "label")) hasLabel = true;
|
||||||
|
else if (_equals(keys[i], "address")) hasAddress = true;
|
||||||
|
else revert InvalidManifestSchema(0);
|
||||||
|
}
|
||||||
|
if (!hasLabel || !hasAddress) revert InvalidManifestSchema(0);
|
||||||
|
}
|
||||||
|
|
||||||
function _validateActorConfiguration(Manifest memory manifest) internal pure {
|
function _validateActorConfiguration(Manifest memory manifest) internal pure {
|
||||||
if (manifest.chainId == ANVIL_CHAIN_ID) {
|
if (manifest.chainId == ANVIL_CHAIN_ID) {
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -225,6 +225,20 @@ contract ScriptPreflightTest is Test {
|
|||||||
harness.readManifest(path, true);
|
harness.readManifest(path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testActorObjectWithExtraKeyIsRejected() public {
|
||||||
|
string memory path = _writeAnvilManifestWithExtraActorKey("note", "public");
|
||||||
|
_etchManifestContracts();
|
||||||
|
vm.expectRevert(abi.encodeWithSelector(DemoScript.InvalidManifestSchema.selector, uint256(0)));
|
||||||
|
harness.readManifest(path, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testActorObjectWithSecretBearingKeyIsRejected() public {
|
||||||
|
string memory path = _writeAnvilManifestWithExtraActorKey("privateKey", "not-a-key");
|
||||||
|
_etchManifestContracts();
|
||||||
|
vm.expectRevert(abi.encodeWithSelector(DemoScript.InvalidManifestSchema.selector, uint256(0)));
|
||||||
|
harness.readManifest(path, true);
|
||||||
|
}
|
||||||
|
|
||||||
function testBaseSepoliaManifestUsesCamelCaseAndOmitsUnavailableUrls() public {
|
function testBaseSepoliaManifestUsesCamelCaseAndOmitsUnavailableUrls() public {
|
||||||
string memory path = _writePublicBaseManifest();
|
string memory path = _writePublicBaseManifest();
|
||||||
vm.chainId(84532);
|
vm.chainId(84532);
|
||||||
@@ -404,6 +418,35 @@ contract ScriptPreflightTest is Test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _writeAnvilManifestWithExtraActorKey(string memory key, string memory value)
|
||||||
|
internal
|
||||||
|
returns (string memory path)
|
||||||
|
{
|
||||||
|
path = string.concat(fixtureDir, "/extra-actor-key.json");
|
||||||
|
vm.writeFile(
|
||||||
|
path,
|
||||||
|
string.concat(
|
||||||
|
'{"schemaVersion":1,"network":"anvil","chainId":31337,"deploymentBlock":1,"rpcUrl":"http://127.0.0.1:8545","token":"',
|
||||||
|
vm.toString(TOKEN),
|
||||||
|
'","proxy":"',
|
||||||
|
vm.toString(PROXY),
|
||||||
|
'","implementation":"',
|
||||||
|
vm.toString(IMPLEMENTATION),
|
||||||
|
'","owner":"',
|
||||||
|
vm.toString(OWNER),
|
||||||
|
'","actors":[{"label":"owner","address":"',
|
||||||
|
vm.toString(OWNER),
|
||||||
|
'","',
|
||||||
|
key,
|
||||||
|
'":"',
|
||||||
|
value,
|
||||||
|
'"},{"label":"Alice","address":"',
|
||||||
|
vm.toString(ALICE),
|
||||||
|
'"},{"label":"Bob","address":"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"}]}'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function _writePublicBaseManifest() internal returns (string memory path) {
|
function _writePublicBaseManifest() internal returns (string memory path) {
|
||||||
path = string.concat(fixtureDir, "/public-base-manifest.json");
|
path = string.concat(fixtureDir, "/public-base-manifest.json");
|
||||||
vm.writeFile(
|
vm.writeFile(
|
||||||
|
|||||||
@@ -159,7 +159,9 @@ function assertExactSchema(manifest) {
|
|||||||
|
|
||||||
function assertManifestUrls(manifest, spec) {
|
function assertManifestUrls(manifest, spec) {
|
||||||
if (manifest.network === "anvil") {
|
if (manifest.network === "anvil") {
|
||||||
if (manifest.rpcUrl !== spec.rpcUrl) throw new Error("manifest anvil rpcUrl must use the local public endpoint");
|
if (Object.hasOwn(manifest, "rpcUrl") && manifest.rpcUrl !== spec.rpcUrl) {
|
||||||
|
throw new Error("manifest anvil rpcUrl must use the local public endpoint");
|
||||||
|
}
|
||||||
if (Object.hasOwn(manifest, "explorerBaseUrl")) throw new Error("manifest anvil must omit explorerBaseUrl");
|
if (Object.hasOwn(manifest, "explorerBaseUrl")) throw new Error("manifest anvil must omit explorerBaseUrl");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,19 @@ test("finalizer confirms a nested public actor manifest and preserves omitted Ba
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("finalizer accepts an Anvil manifest with omitted optional rpcUrl and preserves its absence", async () => {
|
||||||
|
await withFixture(async (root) => {
|
||||||
|
const pending = manifest({ deploymentBlock: 0 });
|
||||||
|
delete pending.rpcUrl;
|
||||||
|
await writeJson(join(root, "deployments", "pending.json"), pending);
|
||||||
|
await writeBroadcast(root, pending);
|
||||||
|
|
||||||
|
const output = await finalizeDeployment({ root, rpc: fakeRpc() });
|
||||||
|
assert.equal(Object.hasOwn(output.manifest, "rpcUrl"), false);
|
||||||
|
assert.equal(Object.hasOwn(await readJson(output.path), "rpcUrl"), false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test("validator rejects the legacy parallel actor and explorer schema", () => {
|
test("validator rejects the legacy parallel actor and explorer schema", () => {
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => validateManifest(legacyManifest()),
|
() => validateManifest(legacyManifest()),
|
||||||
|
|||||||
Reference in New Issue
Block a user