mirror of
https://github.com/getwax/wax.git
synced 2026-01-09 23:27:58 -05:00
complete test & natspec
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user