complete test & natspec

This commit is contained in:
Porco
2024-04-21 03:31:15 +04:00
parent 723b56386e
commit 9fb3175f2d
6 changed files with 56 additions and 24 deletions

View File

@@ -10,6 +10,10 @@ import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";
import {SafeAnonAadhaarPlugin} from "./SafeAnonAadhaarPlugin.sol";
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
//////////////////////////////////////////////////////////////////////////*/
contract SafeAnonAadhaarFactory {
function create(
Safe safeSingleton,

View File

@@ -22,6 +22,10 @@ struct AnonAadhaarOwnerStorage {
address owner;
}
/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
//////////////////////////////////////////////////////////////////////////*/
contract SafeAnonAadhaarPlugin is Safe4337Base {
mapping(address => AnonAadhaarOwnerStorage) public anonAadhaarOwnerStorage;
@@ -30,8 +34,17 @@ contract SafeAnonAadhaarPlugin is Safe4337Base {
address internal constant _SENTINEL_MODULES = address(0x1);
address public immutable anonAadhaarAddr; // external contract managed by AnonAadhaar. it has verifyAnonAadhaarProof() method
uint public immutable userDataHash; // the hash of unique and private user data extracted from Aadhaar QR code
// Note: the following state variables `anonAadhaarAddr` and `userDataHash` are set to immutable
// to immutable to bypass invalid storage access error and make it accessible via delegatecall.
// And `signalNullifiers` is currently unused as it can't be immutable.
// external contract managed by Anon Aadhaar with verifyAnonAadhaarProof() method
address public immutable anonAadhaarAddr;
// the hash of unique and private user data extracted from Aadhaar QR code
uint public immutable userDataHash;
// nullifier for each signal(userOpHash) to prevent on-chain front-running
mapping(uint => bool) public signalNullifiers;
event OWNER_UPDATED(
@@ -105,6 +118,7 @@ contract SafeAnonAadhaarPlugin is Safe4337Base {
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal view override returns (uint256 validationData) {
// decode proof verification params
(
uint nullifierSeed,
uint timestamp,
@@ -113,15 +127,14 @@ contract SafeAnonAadhaarPlugin is Safe4337Base {
uint[8] memory groth16Proof
) = abi.decode(userOp.signature, (uint, uint, uint, uint[4], uint[8]));
// check the signal hasn't already been nullified
// WIP: can't be access due to delegate-call from Safe
// Check if the signal value has already been nullified
// require(!signalNullifiers[signal], "DUPLICATED_NULLIFIER");
// make sure userOpHash == signal
require(uint(userOpHash) == signal, "INVALID_SIGNAL");
// see if the proof is fresh enough
// commented-out for the sake of local test w/ old proof
// not called to avoid invalid opcode: the use of block.timestamp.
// require(isLessThan3HoursAgo(timestamp), "INVALID_TIMESTAMP");
// verify proof throuugh AnonAadhaar and AnonAadhaarGroth16Verifier contracts
@@ -138,8 +151,7 @@ contract SafeAnonAadhaarPlugin is Safe4337Base {
return SIG_VALIDATION_FAILED;
}
// store nullifier
// signalNullifiers[signal] = true;
// signalNullifiers[signal] = true; // store nullifier
return 0;
}
}

View File

@@ -4,9 +4,13 @@ pragma solidity ^0.8.19;
import "./interfaces/IAnonAadhaarGroth16Verifier.sol";
import "./interfaces/IAnonAadhaar.sol";
// https://github.com/anon-aadhaar/anon-aadhaar/blob/main/packages/contracts/src/AnonAadhaar.sol
// Note: This is a AnonAadhaar contract with modifications, where `verifier` and `storedPublicKeyHash` are not mutable
// so that verification doesn't fail due to invalid storage access
contract AnonAadhaar is IAnonAadhaar {
address public verifier;
uint256 public storedPublicKeyHash;
address public immutable verifier;
uint256 public immutable storedPublicKeyHash;
constructor(address _verifier, uint256 _pubkeyHash) {
verifier = _verifier;

View File

@@ -18,6 +18,11 @@
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
*/
// https://github.com/anon-aadhaar/anon-aadhaar/blob/main/packages/contracts/src/Verifier.sol
// Note: This Verifier contract has slight modifications that replace
// `sub(gas(), 2000)` with `gas()` in each elliptic curve precompiles
// so that it doesn't fail due to invalid opcode: the use of GAS
pragma solidity >=0.7.0 <0.9.0;
contract Verifier {
@@ -137,7 +142,7 @@ contract Verifier {
mstore(add(mIn, 64), s)
// success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
success := staticcall(not(0), 7, mIn, 96, mIn, 64)
success := staticcall(gas(), 7, mIn, 96, mIn, 64)
if iszero(success) {
mstore(0, 0)
@@ -148,7 +153,7 @@ contract Verifier {
mstore(add(mIn, 96), mload(add(pR, 32)))
// success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
success := staticcall(not(0), 6, mIn, 128, pR, 64)
success := staticcall(gas(), 6, mIn, 128, pR, 64)
if iszero(success) {
mstore(0, 0)
@@ -235,8 +240,7 @@ contract Verifier {
// 0x20
// )
let success := staticcall(
// sub(gas(), 2000),
// 100000,
gas(),
not(0),
8,
_pPairing,

View File

@@ -13,35 +13,28 @@ import {
} from "@anon-aadhaar/core";
import { buildPoseidon } from "circomlibjs";
// Method to extract a nullifier specific to each Aadhaar ID owner from Aadhaar QR code
// https://github.com/anon-aadhaar/anon-aadhaar/blob/main/packages/circuits/test/aadhaar-verifier.test.ts#L140
export async function copmuteUserNullifier(
nullifierSeed: number,
qrData: string
): Promise<bigint> {
const QRDataBytes = convertBigIntToByteArray(BigInt(qrData));
console.log("QRDataBytes: ", QRDataBytes);
const QRDataDecode = decompressByteArray(QRDataBytes);
console.log("QRDataDecode: ", QRDataDecode);
const signedData = QRDataDecode.slice(0, QRDataDecode.length - 256);
console.log("signedData: ", signedData);
const { bytes: photoBytes } = extractPhoto(Array.from(signedData));
console.log("photoBytes: ", photoBytes);
const photoBytesPacked = padArrayWithZeros(
bytesToIntChunks(new Uint8Array(photoBytes), 31),
32
);
console.log("photoBytesPacked: ", photoBytesPacked);
const poseidon = await buildPoseidon();
const first16 = poseidon([...photoBytesPacked.slice(0, 16)]);
console.log("first16: ", first16);
const last16 = poseidon([...photoBytesPacked.slice(16, 32)]);
console.log("last16: ", last16);
const nullifier = poseidon([nullifierSeed, first16, last16]);
console.log("nullifier: ", nullifier);
console.log("nullifier str: ", nullifier.toString());
return BigInt(poseidon.F.toString(nullifier));
}

View File

@@ -192,16 +192,31 @@ export const createAnonAadhaarOperation = async (
const { maxFeePerGas, maxPriorityFeePerGas } = await getFeeData(provider);
// Note that gas amount params, such as callGasLimit, verificationGasLimit, preVerificationGas, are constant.
// This is because it's challenging to have dynamic gas values for userOp due to the following dependency loop issue.
// Doing gas estimation requires a valid signature in the userOp. Otherwise, the estimation fails.
// The signautre is an encoded value of zk-proof and signal (userOp Hash), which means signature needs a complete userOp including gas values.
// If gas values are changed after creating signature, on-chain verification fails as the new userOp hash doesn't match the proof anymore.
// solution 1: re-create proof but this takes too much time
// solution 2: have fixed gas values
// solution 3:
// - gas estimation with dummy sig to get at least dynamic callGasLimit. ( pimlico offers such an API call )
// - keep verificationGasLimit & preVerificationGas fixed as they are more or less constant
// this issue is also problematic when simulation call with valid sig is necessary, e.g. userOp w/ paymaster.
const unsignedUserOperation = {
sender: accountAddress,
nonce: nonceHex,
factory: undefined,
factoryData: undefined,
callData: userOpCallData,
callGasLimit: ethers.toBeHex(150000n),
verificationGasLimit: ethers.toBeHex(1000000n),
preVerificationGas: ethers.toBeHex(200000n),
maxFeePerGas,
maxPriorityFeePerGas,
paymasterData: "0x",
signature: "0x",
} satisfies UserOperation;