diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 6f7ebae..979a461 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -45,8 +45,6 @@ jobs:
- name: Run Forge build
run: |
forge install
- forge --version
- forge build --sizes
id: build
- name: Run Forge tests
diff --git a/README.md b/README.md
index 4df53b8..4269c9c 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,21 @@
-# Add signers to a safe and keep them secret
+# 🥷🏽 Dark Safe 🏦
+
+## Deep Dive Video 📺
+
+
+
+
-Project Structure
+Repo Structure
```plaintext
├── circuits # contains the noir code
@@ -18,17 +32,92 @@
-## Setup
+## What
+
+A zodiac-compatible [Safe](https://app.safe.global) module that shields the ethereum addresses of up to 8 authorized "dark signers".
+
+## Background
+
+The idea for this project started back at devcon 6 - where I saw Noir had efficient keccak256 and secp256k1 signature verification circuits.
+
+I spent the plane ride home wondering if Noir could enable Gnosis Safes with a shielded set of signers, removing the attack vector of publically stored signer addresses.
+
+This would allow for anonymous onchain orgs, where signers could coordinate to execute transactions.
+
+### Problem
+
+Using this project as a research playground, I wanted to find an _~ elegant ~_ data structure that represented **_the set of valid sigers_** and **_the signing threshold_** in a **single hash**.
+
+- I knew I did not want to store all the leaves of some `n` depth merkle tree, nor do hash path and leaf index computation off-chain. Also... Merkle trees are boring 🚫🌳
+
+### Solution
+
+Thanks to some great help from @autoparallel and @0x_jepsen, I ended up representing valid signer sets (including signing threshold) into a polynomial.
+
+This polynomial is [emitted in an event onchain](contracts/DarkSafe.sol#L48) as a _reverse_ encoded array, of 32 byte coefficiencts, with the array index representing the degree of the `x` value's exponent. For example:
+
+```plaintext
+# Polynomial in array form: index represents exponent degree.
+[42, 1, 9, 145, 1]
+
+# Polynomial in standard algebraic form
+Polynomial = x^4 + 145x^3 + 9x^2 + x + 42
+ 👆🏼 👆🏼 👆🏼
+# (implied) (1x^4) (1x^1) (42x^0)
+```
+
+This polynomial represents all the valid combinations of the signers.
+
+Just like Safe, it protects against:
+
+- double signing
+- under signing
+- non-signer signatures
+
+## A TLDR Of How It works
+
+### 👷🏽♂️ Setup
+
+1. An admin selects up to 8 Ethereum EOA `addresses` as signers on the safe and a signing `threshold`
+ - (Note: to prevent brute-force attacks decoding who the signers are, add at least **one** fresh EOA as a signer).
+2. [`K choose N`](scripts/build.ts#L53) over the signer set and the threshold to find all possible _additive_ combinations of the Ethereum addresses (remember, an eth address is just a number, so you can use addition to express a combination of multiple addresses).
+3. Consider those combinations as ["roots"](scripts/build.ts#L60) and [Lagrange Interpolate](scripts/build.ts#L68) a polynomial that passes through all those points where `y=0`.
+4. Take the [Pedersen hash](scripts/build.ts#L137) of the polynomial as a suscinct commitment to the polynomial.
+5. Deploy a new instance of [DarkSafe](contracts/DarkSafe.sol#L31), via the [ModuleProxyFactory](lib/zodiac/contracts/factory/ModuleProxyFactory.sol#L40) passing the `polynomialHash` and the `polynomial` as initialization params.
+ - The contract will emit the `polynomial` and `polynomialHash` in a `SignersRotated()` event as decentralized, succinct data stucture to represent the signer set and threshold.
+
+### ✍️ Signing
+
+1. Sign over a [SafeMessageHash](lib/safe-contracts/contracts/Safe.sol#L427) with an EOA Private Key (via `eth_personalSign`).
+2. Pass your signature to the next signer.
+3. Repeat steps 1+2 until a valid signer until a valid proof can be generated via the Noir circuit.
+ - This keeps other signers anonymous on a "need-to-know" basis. In other words, not all signers need to be known at proof generation.
+4. Have some relayer call the [\_execute](contracts/DarkSafe.sol#L55) function, passing only the safe TX data, and the valid noir proof.
+5. 🍃 Transaction is executed 🍃
+
+## See it in action
```bash
-yarn && yarn build
+yarn && yarn build --debug
```
## Run tests
+
```bash
yarn && yarn build
cd circuits/ && nargo prove
forge test
-```
\ No newline at end of file
+```
+
+## Notes
+
+- This project is just for fun, demonstrating a relatively efficient and elegant usecase for Noir and shouldn't be used in production unless we work together on this and get it audited
+
+- Interpolating a polynomial over the K choose N of the signer set is _not_ secure enough for me to be comfortable. It is not impossible to brute force k choose n up to 8 over all the Ethereum addresses and compute f(x) to try and brute-force find out who's on the safe.
+
+Some possible solutions are:
+
+- Always spin up a fresh EOA to add as a signer, it's important this account has never made an Ethereum transaction on any chain.
+- Refactor the code to accept a bit of randomness: an `r` value to hash together with each `root`. This makes it impossible to brute force. The `r` value can be as simple as a known `password` has to at least be known by the prover.
diff --git a/contracts/test/E2E.t copy.sol b/contracts/test/E2E.t copy.sol
deleted file mode 100644
index c4a568e..0000000
--- a/contracts/test/E2E.t copy.sol
+++ /dev/null
@@ -1,49 +0,0 @@
-// // SPDX-License-Identifier: MIT
-// pragma solidity 0.8.27;
-
-// import "safe-tools/SafeTestTools.sol";
-// import "forge-std/Test.sol";
-
-// import {DeployScript} from "../script/Deploy.s.sol";
-// import {DarkSafeVerifier} from "../Verifier.sol";
-// import {DarkSafe} from "../DarkSafe.sol";
-// import {DarkSafeFactory} from "../utils/DarkSafeFactory.sol";
-
-// contract DarkSafeTest is Test, SafeTestTools {
-// using SafeTestLib for SafeInstance;
-
-// address private alice = address(0xA11c3);
-// DarkSafeFactory private darkSafeFactory;
-// DarkSafeVerifier private verifier;
-
-// function setUp() public {
-// (darkSafeFactory, verifier) = (new DeployScript()).run();
-// }
-
-// function test() public {
-// uint256[] memory ownerPKs = new uint256[](1);
-// ownerPKs[0] = 12345;
-
-// SafeInstance memory safeInstance = _setupSafe({
-// ownerPKs: ownerPKs,
-// threshold: 1,
-// initialBalance: 1 ether,
-// advancedParams: AdvancedSafeInitParams({
-// includeFallbackHandler: true,
-// initData: "",
-// saltNonce: 0,
-// setupModulesCall_to: address(0),
-// setupModulesCall_data: "",
-// refundAmount: 0,
-// refundToken: address(0),
-// refundReceiver: payable(address(0))
-// })
-// });
-
-// DarkSafe darkSafeMo
-
-// safeInstance.execTransaction({to: alice, value: 0.5 ether, data: ""});
-
-// assertEq(alice.balance, 0.5 ether);
-// }
-// }
diff --git a/contracts/test/E2E.t.sol b/contracts/test/E2E.t.sol
index 1c788e8..16eefc9 100644
--- a/contracts/test/E2E.t.sol
+++ b/contracts/test/E2E.t.sol
@@ -71,7 +71,7 @@ contract DarkSafeTest is Test, SafeTestTools {
);
// expect the signers rotated event to be emitted
- vm.expectEmit(true, true, true, true);
+ vm.expectEmit();
emit DarkSafe.SignersRotated(polynomialInput.polynomial_hash, polynomialInput.polynomial);
// deploy a new module proxy off the master copy
diff --git a/scripts/utils/inquirer.ts b/scripts/utils/inquirer.ts
index b16a795..8cca8ed 100644
--- a/scripts/utils/inquirer.ts
+++ b/scripts/utils/inquirer.ts
@@ -8,6 +8,7 @@ function parseCliArgs() {
options: {
signers: { type: "string" },
threshold: { type: "string" },
+ debug: { type: "boolean" },
},
});