From 9413ad462ce3707bd7083f8396818377d3c68290 Mon Sep 17 00:00:00 2001 From: bigq Date: Fri, 28 Jul 2023 17:57:41 +0200 Subject: [PATCH] feat: add tests but encountered stack to deep issue while trying to create SismoConnectVerifierMock contract --- test/BaseTest.t.sol | 60 +++++++++ test/fork/SismoConnectE2E.t.sol | 2 +- test/fork/proofs/Proofs.sol | 2 +- test/harness/SismoConnectHarness.sol | 2 +- test/mocks/AddressesProviderMock.sol | 28 +++++ test/mocks/SismoConnectVerifierMock.sol | 158 ++++++++++++++++++++++++ test/unit/SismoConnect.t.sol | 67 ++++++++++ 7 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 test/BaseTest.t.sol create mode 100644 test/mocks/AddressesProviderMock.sol create mode 100644 test/mocks/SismoConnectVerifierMock.sol create mode 100644 test/unit/SismoConnect.t.sol diff --git a/test/BaseTest.t.sol b/test/BaseTest.t.sol new file mode 100644 index 0000000..2501af3 --- /dev/null +++ b/test/BaseTest.t.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {Test} from "forge-std/Test.sol"; +import {IAddressesProvider} from "src/interfaces/IAddressesProvider.sol"; +import {AddressesProviderMock} from "test/mocks/AddressesProviderMock.sol"; +// import {SismoConnectVerifierMock} from "test/mocks/SismoConnectVerifierMock.sol"; +import {AuthRequestBuilder} from "src/utils/AuthRequestBuilder.sol"; +import {ClaimRequestBuilder} from "src/utils/ClaimRequestBuilder.sol"; +import {SignatureBuilder} from "src/utils/SignatureBuilder.sol"; +import {RequestBuilder} from "src/utils/RequestBuilder.sol"; + +contract BaseTest is Test { + address immutable user1 = vm.addr(1); + address immutable user2 = vm.addr(2); + address immutable owner = vm.addr(3); + address immutable sismoAddressProviderV2 = 0x3Cd5334eB64ebBd4003b72022CC25465f1BFcEe6; + + //SismoConnectVerifierMock sismoConnectVerifier; + + // external libraries + AuthRequestBuilder authRequestBuilder; + ClaimRequestBuilder claimRequestBuilder; + SignatureBuilder signatureBuilder; + RequestBuilder requestBuilder; + + function setUp() public virtual { + AddressesProviderMock addressesProviderMock = new AddressesProviderMock(); + // sismoConnectVerifier = new SismoConnectVerifierMock(); + + // external libraries + authRequestBuilder = new AuthRequestBuilder(); + claimRequestBuilder = new ClaimRequestBuilder(); + signatureBuilder = new SignatureBuilder(); + requestBuilder = new RequestBuilder(); + + vm.etch(sismoAddressProviderV2, address(addressesProviderMock).code); + + // IAddressesProvider(sismoAddressProviderV2).set( + // address(sismoConnectVerifier), + // string("sismoConnectVerifier-v1.2") + // ); + IAddressesProvider(sismoAddressProviderV2).set( + address(authRequestBuilder), + string("authRequestBuilder-v1.1") + ); + IAddressesProvider(sismoAddressProviderV2).set( + address(claimRequestBuilder), + string("claimRequestBuilder-v1.1") + ); + IAddressesProvider(sismoAddressProviderV2).set( + address(signatureBuilder), + string("signatureBuilder-v1.1") + ); + IAddressesProvider(sismoAddressProviderV2).set( + address(requestBuilder), + string("requestBuilder-v1.1") + ); + } +} diff --git a/test/fork/SismoConnectE2E.t.sol b/test/fork/SismoConnectE2E.t.sol index 5862218..b7d141a 100644 --- a/test/fork/SismoConnectE2E.t.sol +++ b/test/fork/SismoConnectE2E.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "forge-std/console.sol"; diff --git a/test/fork/proofs/Proofs.sol b/test/fork/proofs/Proofs.sol index ceb8eda..b88334a 100644 --- a/test/fork/proofs/Proofs.sol +++ b/test/fork/proofs/Proofs.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "forge-std/console.sol"; diff --git a/test/harness/SismoConnectHarness.sol b/test/harness/SismoConnectHarness.sol index b506288..b0067cc 100644 --- a/test/harness/SismoConnectHarness.sol +++ b/test/harness/SismoConnectHarness.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "src/SismoConnectLib.sol"; diff --git a/test/mocks/AddressesProviderMock.sol b/test/mocks/AddressesProviderMock.sol new file mode 100644 index 0000000..975ffd9 --- /dev/null +++ b/test/mocks/AddressesProviderMock.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract AddressesProviderMock { + mapping(bytes32 => address) private _contractAddresses; + string[] private _contractNames; + + /** + * @dev Sets the address of a contract. + * @param contractAddress Address of the contract. + * @param contractName Name of the contract. + */ + function set(address contractAddress, string memory contractName) public { + bytes32 contractNameHash = keccak256(abi.encodePacked(contractName)); + + if (_contractAddresses[contractNameHash] == address(0)) { + _contractNames.push(contractName); + } + + _contractAddresses[contractNameHash] = contractAddress; + } + + function get(string memory contractName) public view returns (address) { + bytes32 contractNameHash = keccak256(abi.encodePacked(contractName)); + + return _contractAddresses[contractNameHash]; + } +} diff --git a/test/mocks/SismoConnectVerifierMock.sol b/test/mocks/SismoConnectVerifierMock.sol new file mode 100644 index 0000000..5635a1c --- /dev/null +++ b/test/mocks/SismoConnectVerifierMock.sol @@ -0,0 +1,158 @@ +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.17; + +// import "../../src/interfaces/ISismoConnectVerifier.sol"; +// import "../../src/utils/Structs.sol"; + +// // this contract is supposed to implement the interface from SismoConnectVerifier contract +// // we dont specify the interface here because for some reason we encounter a stack too deep error up to a local variable +// // when implementing the `verify` function +// // TO replicate the error, try adding the `config` and `request` argument to the verify function +// // it compiles with one of them (+ response) but not with the three arguments +// contract SismoConnectVerifierMock is ISismoConnectVerifier { +// // struct to store informations about the number of verified auths and claims returned +// // indexes of the first available slot in the arrays of auths and claims are also stored +// // this struct is used to avoid stack to deep errors without using via_ir in foundry +// struct VerifiedArraysInfos { +// uint256 nbOfAuths; // number of verified auths +// uint256 nbOfClaims; // number of verified claims +// uint256 authsIndex; // index of the first available slot in the array of verified auths +// uint256 claimsIndex; // index of the first available slot in the array of verified claims +// VerifiedAuth[] auths; +// VerifiedClaim[] claims; +// } + +// bytes32 public immutable SISMO_CONNECT_VERSION = "sismo-connect-v1.1"; + +// function _check(SismoConnectRequest memory request) internal pure { +// if (request.auths.length == 0 && request.claims.length == 0) { +// revert("SismoConnectVerifierMock: no auths or claims"); +// } +// } + +// function verify( +// SismoConnectResponse memory response, +// SismoConnectRequest memory, // request, +// SismoConnectConfig memory // config +// ) external pure returns (SismoConnectVerifiedResult memory) { +// VerifiedArraysInfos memory infos = VerifiedArraysInfos({ +// nbOfAuths: 0, +// nbOfClaims: 0, +// authsIndex: 0, +// claimsIndex: 0, +// auths: new VerifiedAuth[](0), +// claims: new VerifiedClaim[](0) +// }); + +// infos = _getVerifiedArrayInfos(infos, response); + +// infos = _saveVerifiedAuthAndClaim(infos, response); + +// return +// SismoConnectVerifiedResult({ +// appId: response.appId, +// namespace: response.namespace, +// version: response.version, +// auths: infos.auths, +// claims: infos.claims, +// signedMessage: response.signedMessage +// }); +// } + +// function _getVerifiedArrayInfos( +// VerifiedArraysInfos memory infos, +// SismoConnectResponse memory response +// ) private pure returns (VerifiedArraysInfos memory) { +// // Count the number of auths and claims in the response +// for (uint256 i = 0; i < response.proofs.length; i++) { +// infos.nbOfAuths += response.proofs[i].auths.length; +// infos.nbOfClaims += response.proofs[i].claims.length; +// } + +// return +// VerifiedArraysInfos({ +// nbOfAuths: infos.nbOfAuths, +// nbOfClaims: infos.nbOfClaims, +// authsIndex: infos.authsIndex, +// claimsIndex: infos.claimsIndex, +// auths: new VerifiedAuth[](infos.nbOfAuths), +// claims: new VerifiedClaim[](infos.nbOfClaims) +// }); +// } + +// function _saveVerifiedAuthAndClaim( +// VerifiedArraysInfos memory infos, +// SismoConnectResponse memory response +// ) internal pure returns (VerifiedArraysInfos memory) { +// for (uint256 i = 0; i < response.proofs.length; i++) { +// ( +// VerifiedAuth memory verifiedAuth, +// VerifiedClaim memory verifiedClaim +// ) = _getVerifiedAuthAndClaim(response); +// if (verifiedAuth.proofData.length != 0) { +// infos.auths[infos.authsIndex] = verifiedAuth; +// infos.authsIndex++; +// } +// if (verifiedClaim.proofData.length != 0) { +// infos.claims[infos.claimsIndex] = verifiedClaim; +// infos.claimsIndex++; +// } +// } + +// return infos; +// } + +// function _getVerifiedAuthAndClaim( +// SismoConnectResponse memory response +// ) private pure returns (VerifiedAuth memory, VerifiedClaim memory) { +// VerifiedAuth memory verifiedAuth; +// VerifiedClaim memory verifiedClaim; + +// if (response.proofs[0].auths.length != 0) { +// verifiedAuth = VerifiedAuth({ +// authType: response.proofs[0].auths[0].authType, +// isAnon: response.proofs[0].auths[0].isAnon, +// userId: response.proofs[0].auths[0].userId, +// proofData: response.proofs[0].proofData, +// extraData: response.proofs[0].auths[0].extraData +// }); +// // verifiedAuth = VerifiedAuth({ +// // authType: AuthType.VAULT, +// // isAnon: false, +// // userId: 0, +// // proofData: "123", +// // extraData: "" +// // }); +// } +// if (response.proofs[0].claims.length != 0) { +// verifiedClaim = VerifiedClaim({ +// claimType: response.proofs[0].claims[0].claimType, +// groupId: response.proofs[0].claims[0].groupId, +// groupTimestamp: response.proofs[0].claims[0].groupTimestamp, +// value: response.proofs[0].claims[0].value, +// proofId: uint256( +// keccak256( +// abi.encodePacked( +// response.proofs[0].claims[0].groupId, +// response.proofs[0].claims[0].groupTimestamp, +// response.appId, +// response.namespace +// ) +// ) +// ), +// proofData: response.proofs[0].proofData, +// extraData: response.proofs[0].claims[0].extraData +// }); +// // verifiedClaim = VerifiedClaim({ +// // claimType: ClaimType.GTE, +// // groupId: bytes16("groupId"), +// // groupTimestamp: bytes16("latest"), +// // value: 1, +// // proofId: 1, +// // proofData: "123", +// // extraData: "" +// // }); +// } +// return (verifiedAuth, verifiedClaim); +// } +// } diff --git a/test/unit/SismoConnect.t.sol b/test/unit/SismoConnect.t.sol new file mode 100644 index 0000000..d2332d5 --- /dev/null +++ b/test/unit/SismoConnect.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {BaseTest} from "test/BaseTest.t.sol"; +import {SismoConnectHarness} from "test/harness/SismoConnectHarness.sol"; +import "src/utils/Structs.sol"; + +contract SismoConnectTest is BaseTest { + SismoConnectHarness sismoConnect; + + bytes16 public constant DEFAULT_APP_ID = bytes16("default-app-id"); + bool public constant IS_IMPERSONATION_MODE = false; + + function setUp() public virtual override { + BaseTest.setUp(); + + sismoConnect = new SismoConnectHarness({ + appId: DEFAULT_APP_ID, + isImpersonationMode: IS_IMPERSONATION_MODE + }); + } + + function test_RevertWith_EmptyMessageIfSismoConnectResponseIsEmpty() public { + bytes memory responseBytes = hex""; + ClaimRequest memory claimRequest = claimRequestBuilder.build({groupId: bytes16("group-id")}); + // we just expect a revert with an empty responseBytes as far as the decoding will not be successful + vm.expectRevert(); + sismoConnect.exposed_verify({responseBytes: responseBytes, claim: claimRequest}); + } + + function test_RevertWith_InvalidUserIdAndIsSelectableByUserAuthType() public { + // When `userId` is 0, it means the app does not require a specific auth account and the user needs + // to choose the account they want to use for the app. + // When `isSelectableByUser` is true, the user can select the account they want to use. + // The combination of `userId = 0` and `isSelectableByUser = false` does not make sense and should not be used. + + // Here we do expect the revert since we set isSelectableByUser to false + // and we keep the default value for userId which is 0 + // effectivelly triggering the revert + // Note: we use an AuthType different from VAULT to not trigger another revert + vm.expectRevert(abi.encodeWithSignature("InvalidUserIdAndIsSelectableByUserAuthType()")); + sismoConnect.exposed_buildAuth({ + authType: AuthType.GITHUB, + isOptional: false, + isSelectableByUser: false + }); + } + + function test_RevertWith_InvalidUserIdAndAuthType() public { + // When `userId` is 0, it means the app does not require a specific auth account and the user needs + // to choose the account they want to use for the app. + // When `isSelectableByUser` is true, the user can select the account they want to use. + // The combination of `userId = 0` and `isSelectableByUser = false` does not make sense and should not be used. + + // Here we set isSelectableByUser to false but we add a userId different from zero + // while choosing The AuthType VAULT, which does NOT make sense since it states that we allow the user to choose a vault account in his vault + // but in the case of the AuthType VAULT, the account is the vault itself and therefore there is no choice to make + // we should definitely revert based on this reasoning + vm.expectRevert(abi.encodeWithSignature("InvalidUserIdAndAuthType()")); + sismoConnect.exposed_buildAuth({ + authType: AuthType.VAULT, + isOptional: false, + isSelectableByUser: false, + userId: uint256(bytes32("wrong-id")) + }); + } +}