Skip to content

Commit

Permalink
fix: update scope computation for proof generation (#4)
Browse files Browse the repository at this point in the history
* chore: update remappings

Signed-off-by: 0xpanoramix <[email protected]>

* test: update UTs

Signed-off-by: 0xpanoramix <[email protected]>

* fix: update scope computation

Signed-off-by: 0xpanoramix <[email protected]>

* docs(README): add getting started with anvil fork section

Signed-off-by: 0xpanoramix <[email protected]>

* style: fix fmt errors

Signed-off-by: 0xpanoramix <[email protected]>

* ci: update testing step

Signed-off-by: 0xpanoramix <[email protected]>

* docs(README): update step description

Signed-off-by: 0xpanoramix <[email protected]>

---------

Signed-off-by: 0xpanoramix <[email protected]>
  • Loading branch information
0xpanoramix authored Jan 13, 2025
1 parent 0f60ce3 commit 0b51e73
Show file tree
Hide file tree
Showing 20 changed files with 1,102 additions and 184 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
run: |
npm install
npm i -g tsx
working-directory: test/utils
working-directory: test/ts-utils

- name: Show Forge version
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,5 @@ bin_output
logs

# Custom
broadcast/
cache/
170 changes: 170 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

- [Disclaimer](#disclaimer)
- [Introduction](#introduction)
- [Getting Started](#getting-started)
- [Anvil Fork](#anvil-fork)
- [Contributing](#contributing)
- [Authors](#authors)

Expand All @@ -23,6 +25,174 @@ This project is a prototype. It must not be used in a production environment.

Coming soon.

## Getting Started

### Anvil Fork

To deploy and use Obscurus on an Anvil fork, you'll need:

- [The foundry suite](https://book.getfoundry.sh/getting-started/installation)
- [Docker](https://docs.docker.com/engine/install/)

First, run the Anvil fork (for the following steps, it is assumed that the network forked is Holesky):
```shell
anvil -f <YOUR_RPC_URL> --auto-impersonate
```

Anvil starts and provides some pre-funded accounts. We will use the first one for the next steps.

Then, deploy a safe with the default parameters:
```shell
docker run -it safeglobal/safe-cli safe-creator http://host.docker.internal:8545 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
```

You should get an output similar to this one:
```shell
____ __ ____ _
/ ___| __ _ / _| ___ / ___| _ __ ___ __ _ | |_ ___ _ __
\___ \ / _` || |_ / _ \ | | | '__| / _ \ / _` || __| / _ \ | '__|
___) || (_| || _|| __/ | |___ | | | __/| (_| || |_ | (_) || |
|____/ \__,_||_| \___| \____||_| \___| \__,_| \__| \___/ |_|
Network HOLESKY - Sender 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - Balance: 10000.000000Ξ
Creating new Safe with owners=['0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'] threshold=1 salt-nonce=13432527610990583215445046268100408472893264083727515392743572596940086483582
Safe-master-copy=0x29fcB43b46531BcA003ddC8FCB67FFE91900C762 version=1.4.1
Fallback-handler=0xfd0732Dc9E303f09fCEf3a7388Ad10A83459Ec99
Proxy factory=0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67
Do you want to continue? [y/N]: y
Safe will be deployed on 0x43Ca5D81816AA33BfE354438007dCa68224A47AC, looks good? [y/N]: y
Sent tx with tx-hash=0xb2ae7557e52976415293eadb26e01dec1037af65774172ae7743a62b35ebe4a1 Safe=0x43Ca5D81816AA33BfE354438007dCa68224A47AC is being created
Tx parameters={'value': 0, 'gas': 262053, 'maxFeePerGas': 1000000056, 'maxPriorityFeePerGas': 1000000000, 'chainId': 17000, 'from': '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', 'to': '0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67', 'data': '0x1688f0b900000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c76200000000000000000000000000000000000000000000000000000000000000601db28a8c56f33a389a49c81ac21650ba57cede92737c8bab9acf4b5e50909a7e0000000000000000000000000000000000000000000000000000000000000164b63e800d0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000fd0732dc9e303f09fcef3a7388ad10a83459ec990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'nonce': 1010}
EthereumTxSent(tx_hash=HexBytes('0xb2ae7557e52976415293eadb26e01dec1037af65774172ae7743a62b35ebe4a1'), tx={'value': 0, 'gas': 262053, 'maxFeePerGas': 1000000056, 'maxPriorityFeePerGas': 1000000000, 'chainId': 17000, 'from': '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', 'to': '0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67', 'data': '0x1688f0b900000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c76200000000000000000000000000000000000000000000000000000000000000601db28a8c56f33a389a49c81ac21650ba57cede92737c8bab9acf4b5e50909a7e0000000000000000000000000000000000000000000000000000000000000164b63e800d0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000fd0732dc9e303f09fcef3a7388ad10a83459ec990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'nonce': 1010}, contract_address='0x43Ca5D81816AA33BfE354438007dCa68224A47AC')
```
Grab the Safe address for the next steps (here `0x43Ca5D81816AA33BfE354438007dCa68224A47AC`).
Next, set those environment variables in your shell. They will be used to create a 2/3 Obscurus Safe:
```shell
export RPC_URL=http://localhost:8545
export ODS_SAFE_ADDRESS=0x43Ca5D81816AA33BfE354438007dCa68224A47AC
export ODS_SAFE_OWNER_PK=77814517325470205911140941194401928579557062014761831930645393041380819009408
export ODS_THRESHOLD=2
export ODS_IDENTITY_COMMITMENT_0=16154315983106457604964519632507479861508345150261468393750536759701116467867
export ODS_IDENTITY_COMMITMENT_1=12241487482047535455927935140249225031445150086629846794755977920490270273737
export ODS_IDENTITY_COMMITMENT_2=15899159344494353576723282793888645985869881307160044049094584250085609856278
export ODS_NUM_IDENTITIES=3
```
Run the deployment script:
```shell
forge script ./script/Deploy.fork.holesky.s.sol --rpc-url $RPC_URL --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast -vvvvv
```
From the `broadcast/Deploy.fork.holesky.s.sol/17000/run-latest.json` file, grab the Obscurus deployment address.
Set the new environment variable in your shell:
```shell
export ODS_OBSCURUS_ADDRESS=0x9c5e1b46cf9d94ec55f8ca6d2075d9dfb988673e
```
This call should confirm that the module has been enabled on the Safe:
```shell
cast call $ODS_SAFE_ADDRESS "isModuleEnabled(address)(bool)" $ODS_OBSCURUS_ADDRESS
# Should return `true`
```
Then, generate the proofs for each one of the Semaphore identity (feel free to replace the `to`, `value`, `data` and `operation` fields to run another transaction. don't forget to reflect the changes in the very next step when executing the transaction using the generated proofs):
```shell
# Identity 0 is Alice.
npx tsx test/ts-utils/obscurus-cli.ts prove --prover "zBdAt1wUe6DAK1wEXuJqeSyBiTX+UPA4M95TI3F9mIM=" --identities $ODS_IDENTITY_COMMITMENT_0 $ODS_IDENTITY_COMMITMENT_1 $ODS_IDENTITY_COMMITMENT_2 --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0 > /tmp/proof-alice.json
# Identity 1 is Bob.
npx tsx test/ts-utils/obscurus-cli.ts prove --prover "QigfrBOzbDNxo9Y7aJQoY52OSwsdOuEtnFvKFaPaG4g=" --identities $ODS_IDENTITY_COMMITMENT_0 $ODS_IDENTITY_COMMITMENT_1 $ODS_IDENTITY_COMMITMENT_2 --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0 > /tmp/proof-bob.json
# Identity 2 is Pierre.
npx tsx test/ts-utils/obscurus-cli.ts prove --prover "h2QBqQRh1ecKCDWzQX1Pt8w2zAK/QwKANcMJA6nQeRs=" --identities $ODS_IDENTITY_COMMITMENT_0 $ODS_IDENTITY_COMMITMENT_1 $ODS_IDENTITY_COMMITMENT_2 --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0 > /tmp/proof-pierre.json
```
Finally, execute the transaction by giving the proofs that will be verified onchain:
```shell
npx tsx test/ts-utils/obscurus-cli.ts exec --proofs /tmp/proof-alice.json /tmp/proof-bob.json /tmp/proof-pierre.json --signer "0xAC0974BEC39A17E36BA4A6B4D238FF944BACB478CBED5EFCAE784D7BF4F2FF80" --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0
```
Nice! You should get an output similar to this:
```shell
{"hash":"0xe6e83eb6d5afa4df26c648b7a370b42d4fc48d5c4a662beeec70e61f23758d0d","result":[true,"0x"]}
```
You can regenerate the proofs, this time only for Alice and Bob, and execute the transaction again. This will work as the number of proofs is still greater than the threshold:
```
# Identity 0 is Alice.
npx tsx test/ts-utils/obscurus-cli.ts prove --prover "zBdAt1wUe6DAK1wEXuJqeSyBiTX+UPA4M95TI3F9mIM=" --identities $ODS_IDENTITY_COMMITMENT_0 $ODS_IDENTITY_COMMITMENT_1 $ODS_IDENTITY_COMMITMENT_2 --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0 > /tmp/proof-alice.json
# Identity 1 is Bob.
npx tsx test/ts-utils/obscurus-cli.ts prove --prover "QigfrBOzbDNxo9Y7aJQoY52OSwsdOuEtnFvKFaPaG4g=" --identities $ODS_IDENTITY_COMMITMENT_0 $ODS_IDENTITY_COMMITMENT_1 $ODS_IDENTITY_COMMITMENT_2 --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0 > /tmp/proof-bob.json
npx tsx test/ts-utils/obscurus-cli.ts exec --proofs /tmp/proof-alice.json /tmp/proof-bob.json --signer "0xAC0974BEC39A17E36BA4A6B4D238FF944BACB478CBED5EFCAE784D7BF4F2FF80" --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0
```
Again, we got a validation:
```shell
{"hash":"0xb79b94d52aa198be0fba3b9d913611d1dee22402209ce0a8c0f7c9b38def877f","result":[true,"0x"]}
```
But this time, if we try with only Alice, it will fail:
```shell
# Identity 0 is Alice.
npx tsx test/ts-utils/obscurus-cli.ts prove --prover "zBdAt1wUe6DAK1wEXuJqeSyBiTX+UPA4M95TI3F9mIM=" --identities $ODS_IDENTITY_COMMITMENT_0 $ODS_IDENTITY_COMMITMENT_1 $ODS_IDENTITY_COMMITMENT_2 --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0 > /tmp/proof-alice.json
npx tsx test/ts-utils/obscurus-cli.ts exec --proofs /tmp/proof-alice.json --signer "0xAC0974BEC39A17E36BA4A6B4D238FF944BACB478CBED5EFCAE784D7BF4F2FF80" --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0
```
You should get an error similar to this:
```shell
/Users/Developer/obscurus/test/ts-utils/node_modules/viem/utils/errors/getContractError.ts:78
return new ContractFunctionExecutionError(cause as BaseError, {
^
ContractFunctionExecutionError: The contract function "obscureExecAndReturnData" reverted.
Error: NotEnoughProofs()
```
Another error should occur if you temper the proofs. Going back to the example with 2 identities, Alice and Bob, edit the Alice's proof file before executing the transaction:
```shell
# Identity 0 is Alice.
npx tsx test/ts-utils/obscurus-cli.ts prove --prover "zBdAt1wUe6DAK1wEXuJqeSyBiTX+UPA4M95TI3F9mIM=" --identities $ODS_IDENTITY_COMMITMENT_0 $ODS_IDENTITY_COMMITMENT_1 $ODS_IDENTITY_COMMITMENT_2 --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0 > /tmp/proof-alice.json
# Identity 1 is Bob.
npx tsx test/ts-utils/obscurus-cli.ts prove --prover "QigfrBOzbDNxo9Y7aJQoY52OSwsdOuEtnFvKFaPaG4g=" --identities $ODS_IDENTITY_COMMITMENT_0 $ODS_IDENTITY_COMMITMENT_1 $ODS_IDENTITY_COMMITMENT_2 --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0 > /tmp/proof-bob.json
##
## Before running the command below, edit the /tmp/proof-alice.json.
## Try replacing one of the nullifier field for example.
##
npx tsx test/ts-utils/obscurus-cli.ts exec --proofs /tmp/proof-alice.json /tmp/proof-bob.json --signer "0xAC0974BEC39A17E36BA4A6B4D238FF944BACB478CBED5EFCAE784D7BF4F2FF80" --obscurus-address $ODS_OBSCURUS_ADDRESS --rpc-url $RPC_URL --to "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" --value 0 --data 0x --operation 0
```
You should get an error similar to the following one:
```shell
/Users/Developer/obscurus/test/ts-utils/node_modules/viem/utils/errors/getContractError.ts:78
return new ContractFunctionExecutionError(cause as BaseError, {
^
ContractFunctionExecutionError: The contract function "obscureExecAndReturnData" reverted with the following signature:
0x4aa6bc40
```
cast can help us decode the error:
```shell
cast decode-error 0x4aa6bc40
```
Which prints:
```shell
Semaphore__InvalidProof()
```
## Contributing
Coming soon.
Expand Down
2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ safe-tools/=lib/safe-tools/src/
solady/=lib/solady/src/
@semaphore-protocol/=lib/semaphore/packages/contracts/
@zk-kit/lean-imt.sol/=lib/zk-kit.solidity/packages/lean-imt/contracts/

@src/=src/
113 changes: 113 additions & 0 deletions script/Deploy.fork.holesky.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.27;

import {Script} from "forge-std/Script.sol";
import {ISemaphoreVerifier} from "@semaphore-protocol/contracts/interfaces/ISemaphoreVerifier.sol";
import {SemaphoreVerifier} from "@semaphore-protocol/contracts/base/SemaphoreVerifier.sol";
import {ISemaphore} from "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
import {Semaphore} from "@semaphore-protocol/contracts/Semaphore.sol";
import {Safe, Enum as SafeEnum} from "safe-contracts/Safe.sol";
import "safe-tools/SafeTestTools.sol";
import "forge-std/Test.sol";

import "./DeployConfig.holesky.sol";
import "./IDeployConfig.sol";
import "@src/Obscurus.sol";

contract DeployForkHolesky is Script, Test, SafeTestTools {
using SafeTestLib for SafeInstance;

IDeployConfig internal dc;
IDeployConfig.Configuration internal cfg;

function setUp() external {
dc = new DeployConfigHolesky();
cfg = dc.getConfiguration();
}

function run() external {
vm.startBroadcast();
ISemaphore semaphore = _deploySemaphore();
Obscurus obscurus = _deployObscurus(semaphore);
_deployModule(obscurus);
vm.stopBroadcast();
}

function _deploySemaphore() internal returns (ISemaphore semaphore) {
SemaphoreVerifier semaphoreVerifier = new SemaphoreVerifier();
semaphore = new Semaphore(ISemaphoreVerifier(address(semaphoreVerifier)));
}

function _deployObscurus(ISemaphore _semaphore) internal returns (Obscurus obscurus) {
obscurus = new Obscurus({
_safe: cfg.safe,
_semaphore: address(_semaphore),
_threshold: cfg.threshold,
_identities: cfg.identities
});
}

function _deployModule(Obscurus _obscurus) internal {
bytes32 txHash = Safe(payable(cfg.safe)).getTransactionHash(
address(cfg.safe),
0,
abi.encodeWithSelector(Safe(payable(cfg.safe)).enableModule.selector, address(_obscurus)),
Enum.Operation.Call,
0,
0,
0,
address(0),
address(0),
Safe(payable(cfg.safe)).nonce()
);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(vm.envUint("ODS_SAFE_OWNER_PK"), txHash);

bytes memory signatures;
signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));

address signer = ecrecover(txHash, v, r, s);
console.log("Signer: %s", signer);
console.log("V: %s", v);

(v, r, s) = signatureSplit(signatures, 0);
signer = ecrecover(txHash, v, r, s);
console.log("Signer: %s", signer);
console.log("V: %s", v);

address lastOwner = address(0);
require(signer > lastOwner && true, "GS026");

Safe(payable(cfg.safe)).execTransaction(
address(cfg.safe),
0,
abi.encodeWithSelector(Safe(payable(cfg.safe)).enableModule.selector, address(_obscurus)),
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
signatures
);
}

function signatureSplit(bytes memory signatures, uint256 pos)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
{
// solhint-disable-next-line no-inline-assembly
assembly {
let signaturePos := mul(0x41, pos)
r := mload(add(signatures, add(signaturePos, 0x20)))
s := mload(add(signatures, add(signaturePos, 0x40)))
/**
* Here we are loading the last 32 bytes, including 31 bytes
* of 's'. There is no 'mload8' to do this.
* 'byte' is not working due to the Solidity parser, so lets
* use the second best option, 'and'
*/
v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
}
}
}
37 changes: 0 additions & 37 deletions script/Deploy.s.sol

This file was deleted.

29 changes: 29 additions & 0 deletions script/DeployConfig.holesky.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.27;

import {Script, console} from "forge-std/Script.sol";
import "openzeppelin/contracts/utils/Strings.sol";

import "./IDeployConfig.sol";

contract DeployConfigHolesky is IDeployConfig, Script {
function getConfiguration() public view returns (Configuration memory cfg) {
cfg.safe = vm.envAddress("ODS_SAFE_ADDRESS");
uint256 numIdentities = vm.envUint("ODS_NUM_IDENTITIES");

if (numIdentities == 0) {
revert DeploymentConfigurationError("ODS: number of identities must be greater than 0");
}

cfg.threshold = vm.envUint("ODS_THRESHOLD");
if (cfg.threshold > numIdentities) {
revert DeploymentConfigurationError("ODS: threshold must be lower than number of identities");
}

cfg.identities = new uint256[](numIdentities);

for (uint256 i = 0; i < numIdentities; i++) {
cfg.identities[i] = vm.envUint(string.concat("ODS_IDENTITY_COMMITMENT_", Strings.toString(i)));
}
}
}
Loading

0 comments on commit 0b51e73

Please sign in to comment.