diff --git a/test/src/concrete/Flow.construction.t.sol b/test/src/concrete/Flow.construction.t.sol index 03206941..85fdaefa 100644 --- a/test/src/concrete/Flow.construction.t.sol +++ b/test/src/concrete/Flow.construction.t.sol @@ -6,7 +6,7 @@ import {Vm} from "forge-std/Test.sol"; import {EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {FlowTest} from "test/abstract/FlowTest.sol"; -import {EmptyFlowConfig} from "src/error/ErrFlow.sol"; +import {EmptyFlowConfig, UnsupportedFlowInputs} from "src/error/ErrFlow.sol"; contract FlowConstructionTest is FlowTest { function testFlowConstructionEmptyConfigReverts() external { @@ -16,6 +16,30 @@ contract FlowConstructionTest is FlowTest { I_CLONE_FACTORY.clone(impl, abi.encode(emptyConfig)); } + /// Reverts with `UnsupportedFlowInputs` when the deployer reports any + /// non-zero `flowInputs` byte in the IO string. Pinning this protects + /// against a future deployer behaviour change leaking non-zero inputs + /// through the guard at `Flow.flowInit`. + /// forge-config: default.fuzz.runs = 100 + function testFlowConstructionRevertsOnNonZeroFlowInputs( + address expression, + bytes memory bytecode, + uint256[] memory constants, + uint8 flowInputs + ) external { + vm.assume(flowInputs != 0); + // io: byte0 = flowInputs (non-zero), byte1 = 7 (>= MIN_FLOW_SENTINELS). + bytes memory io = abi.encodePacked(flowInputs, uint8(7)); + expressionDeployerDeployExpression2MockCall(expression, io); + + EvaluableConfigV3[] memory flowConfig = new EvaluableConfigV3[](1); + flowConfig[0] = EvaluableConfigV3(DEPLOYER, bytecode, constants); + + address impl = deployFlowImplementation(); + vm.expectRevert(UnsupportedFlowInputs.selector); + I_CLONE_FACTORY.clone(impl, abi.encode(flowConfig)); + } + function testFlowConstructionInitialize(address expression, bytes memory bytecode, uint256[] memory constants) external {