diff --git a/src/WorldIDIdentityManagerImplV3.sol b/src/WorldIDIdentityManagerImplV3.sol index 27021d4..f207206 100644 --- a/src/WorldIDIdentityManagerImplV3.sol +++ b/src/WorldIDIdentityManagerImplV3.sol @@ -124,6 +124,14 @@ contract WorldIDIdentityManagerImplV3 is WorldIDIdentityManagerImplV2 { uint32 startIndex; } + /////////////////////////////////////////////////////////////////////////////// + /// ERRORS /// + /////////////////////////////////////////////////////////////////////////////// + + /// @notice Thrown when the point evaluation precompile returns failure. + /// This means KZG proof cannot be verified. + error KzgProofVerificationFailed(); + /////////////////////////////////////////////////////////////////// /// IDENTITY MANAGEMENT /// /////////////////////////////////////////////////////////////////// @@ -140,6 +148,7 @@ contract WorldIDIdentityManagerImplV3 is WorldIDIdentityManagerImplV2 { /// provided inputs. /// @custom:reverts VerifierLookupTable.NoSuchVerifier If the batch sizes doesn't match a known /// verifier. + /// @custom:reverts KzgProofVerificationFailed If KZG proof verification fails function registerIdentities( RegisterIdentities4844Params calldata params ) public virtual onlyProxy onlyInitialized onlyIdentityOperator { @@ -183,7 +192,9 @@ contract WorldIDIdentityManagerImplV3 is WorldIDIdentityManagerImplV2 { params.kzgCommitment, params.kzgProof ); - require(success, "Call to point evaluation precompiled contract failed"); + if (!success) { + revert KzgProofVerificationFailed(); + } emit TreeChanged(params.preRoot, TreeChange.Insertion, params.postRoot); } catch Error(string memory errString) { diff --git a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol index e68e7ff..56f5bdb 100644 --- a/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol +++ b/src/test/identity-manager/WorldIDIdentityManagerIdentityRegistration4844.t.sol @@ -635,4 +635,94 @@ contract WorldIDIdentityManagerIdentityRegistration4844 is WorldIDIdentityManage managerImplV3.registerIdentities(params); } + + /// @notice Checks that the transaction fails if KZG proof cannot be verified. + function testRegisterIdentitiesWithBadKzgProof() public { + // Setup + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([40])); + insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); + makeNewIdentityManager( + treeDepth, + insertionPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3.RegisterIdentities4844Params({ + insertionProof: insertionProof4844, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgCommitment, + kzgProof: kzgCommitment, // Intentionally pass something that's not the KZG proof + expectedEvaluation: insertionExpectedEvaluation, + preRoot: insertionPreRoot, + postRoot: insertionPostRoot4844, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitmentsSize), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + + bytes memory registerCallData = abi.encodeCall(ManagerImplV3.registerIdentities, params); + + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV3.KzgProofVerificationFailed.selector); + + // Test + assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); + } + + /// @notice Checks that the transaction fails if KZG commitment does not match the rest of KZG-related input. + function testRegisterIdentitiesWithBadKzgCommitment() public { + // Setup + ITreeVerifier actualVerifier = new TreeVerifier(); + ( + VerifierLookupTable insertVerifiers, + VerifierLookupTable deletionVerifiers, + VerifierLookupTable updateVerifiers + ) = makeVerifierLookupTables(TC.makeDynArray([40])); + insertVerifiers.addVerifier(identityCommitmentsSize, actualVerifier); + makeNewIdentityManager( + treeDepth, + insertionPreRoot, + insertVerifiers, + deletionVerifiers, + updateVerifiers, + semaphoreVerifier + ); + + ManagerImplV3.RegisterIdentities4844Params memory params = ManagerImplV3.RegisterIdentities4844Params({ + insertionProof: insertionProof4844, + commitments: commitments, + commitmentPok: commitmentsPok, + kzgCommitment: kzgProof, // Intentionally pass something that's not the KZG commitment + kzgProof: kzgProof, + expectedEvaluation: insertionExpectedEvaluation, + preRoot: insertionPreRoot, + postRoot: insertionPostRoot4844, + inputHash: insertionInputHash4844, + batchSize: uint32(identityCommitmentsSize), + startIndex: startIndex + }); + + // Mock blobhash. This is valid for the next call only. + prepareBlobhash(kzgToVersionedHash(kzgCommitment)); + + bytes memory registerCallData = abi.encodeCall(ManagerImplV3.registerIdentities, params); + + bytes memory expectedError = + abi.encodeWithSelector(ManagerImplV3.KzgProofVerificationFailed.selector); + + // Test + assertCallFailsOn(identityManagerAddress, registerCallData, expectedError); + } }