diff --git a/test/abstract/FlowERC1155Test.sol b/test/abstract/FlowERC1155Test.sol new file mode 100644 index 00000000..79d6f96e --- /dev/null +++ b/test/abstract/FlowERC1155Test.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {Vm} from "forge-std/Test.sol"; +import {FlowUtilsAbstractTest} from "test/abstract/FlowUtilsAbstractTest.sol"; +import {InterpreterMockTest} from "test/abstract/InterpreterMockTest.sol"; +import {IFlowERC1155V5, FlowERC1155ConfigV3} from "src/interface/unstable/IFlowERC1155V5.sol"; +import {EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {STUB_EXPRESSION_BYTECODE} from "./TestConstants.sol"; +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {CloneFactory} from "rain.factory/src/concrete/CloneFactory.sol"; +import {FlowERC1155} from "../../src/concrete/erc1155/FlowERC1155.sol"; + +abstract contract FlowERC1155Test is FlowUtilsAbstractTest, InterpreterMockTest { + CloneFactory internal immutable iCloneFactory; + IFlowERC1155V5 internal immutable iFlowImplementation; + + constructor() { + vm.pauseGasMetering(); + iCloneFactory = new CloneFactory(); + iFlowImplementation = new FlowERC1155(); + vm.resumeGasMetering(); + } + + function deployIFlowERC1155V5(string memory uri) + internal + returns (IFlowERC1155V5 flowErc1155, EvaluableV2 memory evaluable) + { + expressionDeployerDeployExpression2MockCall(address(0), bytes(hex"0006")); + // Create the evaluableConfig + EvaluableConfigV3 memory evaluableConfig = + EvaluableConfigV3(iDeployer, STUB_EXPRESSION_BYTECODE, new uint256[](0)); + // Create the flowConfig array with one entry + EvaluableConfigV3[] memory flowConfigArray = new EvaluableConfigV3[](1); + flowConfigArray[0] = evaluableConfig; + // Initialize the FlowERC1155ConfigV3 struct + FlowERC1155ConfigV3 memory flowErc1155Config = FlowERC1155ConfigV3(uri, evaluableConfig, flowConfigArray); + vm.recordLogs(); + flowErc1155 = IFlowERC1155V5(iCloneFactory.clone(address(iFlowImplementation), abi.encode(flowErc1155Config))); + Vm.Log[] memory logs = vm.getRecordedLogs(); + Vm.Log memory concreteEvent = findEvent(logs, keccak256("FlowInitialized(address,(address,address,address))")); + (, evaluable) = abi.decode(concreteEvent.data, (address, EvaluableV2)); + } +} diff --git a/test/concrete/flowErc1155/FlowSignedContextTest.sol b/test/concrete/flowErc1155/FlowSignedContextTest.sol new file mode 100644 index 00000000..2c633840 --- /dev/null +++ b/test/concrete/flowErc1155/FlowSignedContextTest.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.19; + +import {Test, Vm} from "forge-std/Test.sol"; +import {FlowERC1155} from "../../../src/concrete/erc1155/FlowERC1155.sol"; +import { + FlowUtilsAbstractTest, + ERC20Transfer, + ERC721Transfer, + ERC1155Transfer, + ERC1155SupplyChange +} from "test/abstract/FlowUtilsAbstractTest.sol"; +import {IFlowERC1155V5} from "../../../src/interface/unstable/IFlowERC1155V5.sol"; +import {EvaluableV2, SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {InvalidSignature} from "rain.interpreter.interface/lib/caller/LibContext.sol"; +import {FlowERC1155Test} from "../../abstract/FlowERC1155Test.sol"; +import {SignContextLib} from "test/lib/SignContextLib.sol"; + +contract FlowSignedContextTest is FlowUtilsAbstractTest, FlowERC1155Test { + using SignContextLib for Vm; + + /// Should validate multiple signed contexts + function testValidateMultipleSignedContexts( + string memory uri, + uint256[] memory context0, + uint256[] memory context1, + uint256 fuzzedKeyAlice, + uint256 fuzzedKeyBob + ) public { + vm.assume(fuzzedKeyBob != fuzzedKeyAlice); + (IFlowERC1155V5 erc1155Flow, EvaluableV2 memory evaluable) = deployIFlowERC1155V5(uri); + + // Ensure the fuzzed key is within the valid range for secp256k1 + uint256 aliceKey = (fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1; + uint256 bobKey = (fuzzedKeyBob % (SECP256K1_ORDER - 1)) + 1; + + SignedContextV1[] memory signedContexts = new SignedContextV1[](2); + + signedContexts[0] = vm.signContext(aliceKey, aliceKey, context0); + signedContexts[1] = vm.signContext(aliceKey, aliceKey, context1); + + uint256[] memory stack = generateFlowERC1155Stack( + new ERC1155Transfer[](0), + new ERC721Transfer[](0), + new ERC20Transfer[](0), + new ERC1155SupplyChange[](0), + new ERC1155SupplyChange[](0) + ); + interpreterEval2MockCall(stack, new uint256[](0)); + erc1155Flow.flow(evaluable, new uint256[](0), signedContexts); + + // With bad signature in second signed context + SignedContextV1[] memory signedContexts1 = new SignedContextV1[](2); + signedContexts1[0] = vm.signContext(aliceKey, aliceKey, context0); + signedContexts1[1] = vm.signContext(aliceKey, bobKey, context1); + + uint256[] memory stack1 = generateFlowERC1155Stack( + new ERC1155Transfer[](0), + new ERC721Transfer[](0), + new ERC20Transfer[](0), + new ERC1155SupplyChange[](0), + new ERC1155SupplyChange[](0) + ); + interpreterEval2MockCall(stack1, new uint256[](0)); + + vm.expectRevert(abi.encodeWithSelector(InvalidSignature.selector, 1)); + erc1155Flow.flow(evaluable, new uint256[](0), signedContexts1); + } +} diff --git a/test/lib/SignContextLib.sol b/test/lib/SignContextLib.sol index fe516ba9..0a2e08fb 100644 --- a/test/lib/SignContextLib.sol +++ b/test/lib/SignContextLib.sol @@ -7,7 +7,7 @@ import {ECDSAUpgradeable as ECDSA} from "openzeppelin/utils/cryptography/ECDSAUp import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; library SignContextLib { - function signContext(Vm vm, uint256 privateKey, uint256[] memory context) + function signContext(Vm vm, uint256 signerPrivateKey, uint256 signaturePrivateKey, uint256[] memory context) internal pure returns (SignedContextV1 memory) @@ -15,7 +15,7 @@ library SignContextLib { SignedContextV1 memory signedContext; // Store the signer's address in the struct - signedContext.signer = vm.addr(privateKey); + signedContext.signer = vm.addr(signerPrivateKey); signedContext.context = context; // copy the context data into the struct // Create a digest of the context data @@ -23,7 +23,7 @@ library SignContextLib { bytes32 digest = ECDSA.toEthSignedMessageHash(contextHash); // Create the signature using the cheatCode 'sign' - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signaturePrivateKey, digest); signedContext.signature = abi.encodePacked(r, s, v); return signedContext;