Skip to content

Commit

Permalink
Final touches
Browse files Browse the repository at this point in the history
  • Loading branch information
brickpop committed Nov 14, 2024
1 parent f12b40e commit 601efd2
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 2 deletions.
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ The governance settings need to be defined when the plugin is installed but the

It allows the Security Council members to create and approve proposals. After a certain minimum of approvals is met, proposals can be relayed to the [Optimistic Token Voting plugin](#optimistic-token-voting-plugin) only.

The list of signers for this plugin is taken from SignerList contract. Any changes on it will effect both plugin instances.

The ability to relay proposals to the [Optimistic Token Voting plugin](#optimistic-token-voting-plugin) is restricted by a [permission condition](src/conditions/StandardProposalCondition.sol), which ensures that a minimum veto period is defined as part of the parameters.

![Standard proposal flow](./img/std-proposal-flow.png)
Expand All @@ -52,7 +54,7 @@ The ability to relay proposals to the [Optimistic Token Voting plugin](#optimist

Like before, this plugin allows Security Council members to create and approve proposals. If a super majority approves, proposals can be relayed to the [Optimistic Token Voting plugin](#optimistic-token-voting-plugin) with a delay period of potentially 0. This is, being executed immediately.

The address list of this plugin is taken from the standard Multisig plugin. Any changes on the former will effect both plugin instances.
The list of signers for this plugin is taken from SignerList contract. Any changes on it will effect both plugin instances.

There are two key differences with the standard Multisig:
1. The proposal's metadata and the actions to execute are encrypted, only the Security Council members have the means to decrypt them
Expand All @@ -69,9 +71,37 @@ The Emergency Multisig settings are the same as for the standard Multisig.
- The plugin can only create proposals on the [Optimistic Token Voting plugin](#optimistic-token-voting-plugin) provided that the `duration` is equal or greater than the minimum defined
- The DAO can update the plugin settings

## Signer List

Both multisigs relate to this contract to determine if an address was listed at a certain block. It allows to read the state and manage the address list given that the appropriate permissions are granted.

It also plays an important role regarding encryption, this is why it is coupled with the Encryption Registry (see below).

It offers convenience methods to determine 3 potential states for a given address:
- An address was a listed signer at a given past block (owner)
- An address is appointed by another address, listed at a past block (appointed)
- An address not listed or appointed

### The encryption challenge

Smart wallets cannot possibly generate a private key, which means that encryption and decryption is unviable. To this end, the [EncryptionRegistry](#encryption-registry) (see below) allows listed signers to **appoint** an EOA to act on behalf of them.

This means that the Security Council could include a member who was an organization, and such organiation could have a smart wallet. This smart wallet would then appoint one of its members' EOA, so that emergency proposals could be reviewed, approved and eventually executed.

If at any point, the member's EOA became compromised or the member left the team, the smart wallet could then appoint a new EOA and continue without impacting the rest of the Security Council.

What it means:
- Owners (listed signers)
- Can always create emergency multisig proposals
- Can only approve if they are not appointing another address
- Addresses appointed by a listed signer
- Can create emergency proposals
- Can approve
- Can execute (they can decrypt the actions and the metadata)

## Encryption Registry

This is a helper contract that allows Security Council members to register the public key of their deterministic ephemeral wallet. The available public keys will be used to encrypt the proposal metadata and actions.
This is a helper contract that allows Security Council members ([SignerList](#signer-list) addresses) to register the public key of their deterministic ephemeral wallet. The available public keys will be used to encrypt the proposal metadata and actions.

Given that smart contracts cannot possibly sign or decrypt data, the encryption registry allows to appoint an EOA as the end target for encryption purposes. This is useful for organizations not wanting to rely on just a single wallet.

Expand Down
22 changes: 22 additions & 0 deletions test/EmergencyMultisigTree.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,28 @@ contract EmergencyMultisigTest is AragonTest {
assertEq(address(destinationPlugin), address(0), "Incorrect destinationPlugin");
}

function testFuzz_GetProposalReturnsEmptyValuesForNonExistingOnes(uint256 randomProposalId) public view {
(
bool executed,
uint16 approvals,
EmergencyMultisig.ProposalParameters memory parameters,
bytes memory encryptedPayloadURI,
bytes32 publicMetadataUriHash,
bytes32 destinationActionsHash,
OptimisticTokenVotingPlugin destinationPlugin
) = eMultisig.getProposal(randomProposalId);

assertEq(executed, false, "The proposal should not be executed");
assertEq(approvals, 0, "The tally should be zero");
assertEq(encryptedPayloadURI, "", "Incorrect encryptedPayloadURI");
assertEq(parameters.expirationDate, 0, "Incorrect expirationDate");
assertEq(parameters.snapshotBlock, 0, "Incorrect snapshotBlock");
assertEq(parameters.minApprovals, 0, "Incorrect minApprovals");
assertEq(publicMetadataUriHash, 0, "Metadata URI hash should have no items");
assertEq(destinationActionsHash, 0, "Actions hash should have no items");
assertEq(address(destinationPlugin), address(0), "Incorrect destination plugin");
}

function test_WhenCallingCanApproveOrApproveBeingUncreated() external givenTheProposalIsNotCreated {
uint256 randomProposalId = 1234;
bool canApprove;
Expand Down
20 changes: 20 additions & 0 deletions test/MultisigTree.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,26 @@ contract MultisigTest is AragonTest {
// David should approve and trigger auto execution
vm.startPrank(david);
assertEq(multisig.canApprove(0, david), true, "David should be able to approve");

// It execute recreates the proposal on the destination plugin
uint256 targetPid = (block.timestamp << 128) | ((block.timestamp + DESTINATION_PROPOSAL_DURATION) << 64);
IDAO.Action[] memory actions = new IDAO.Action[](2);
actions[0].value = 0.25 ether;
actions[0].to = address(alice);
actions[0].data = hex"";
actions[1].value = 0.75 ether;
actions[1].to = address(dao);
actions[1].data = abi.encodeCall(DAO.setMetadata, "ipfs://new-metadata");
vm.expectEmit();
emit ProposalCreated(
targetPid,
address(multisig),
uint64(block.timestamp),
uint64(block.timestamp + DESTINATION_PROPOSAL_DURATION),
"ipfs://pub-metadata",
actions,
0
);
multisig.approve(0, true);

(executed, approvals,,,,) = multisig.getProposal(0);
Expand Down

0 comments on commit 601efd2

Please sign in to comment.