Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize Personal Edition #25

Merged
merged 65 commits into from
Jul 5, 2018
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
c12c720
Improve and cleanup Gnosis Safe
rmeissner Apr 16, 2018
b14eb2c
Implement #15: Add version byte for hashing
rmeissner Apr 16, 2018
cd5f25c
Implement #13: EIP-897 interface on Proxy
rmeissner Apr 16, 2018
e72143b
Adjusted extension logic
rmeissner Apr 16, 2018
4d7342d
Fixed tests
rmeissner Apr 16, 2018
e7ca1b2
Implemented #14: Use signature with daily limit extension
rmeissner Apr 17, 2018
37d6a41
Implements #20: Optimize array access in setup functions
rmeissner Apr 17, 2018
60facf3
Added different payment options (#26)
rmeissner May 7, 2018
ce6acef
Use gas instead of not(0)
rmeissner May 7, 2018
1ffee77
replace assembly switch with if
rmeissner May 7, 2018
2b95bc5
Added test for different safe versions
rmeissner May 8, 2018
1a750ec
Added test for send exploit
rmeissner May 8, 2018
af6a41b
Use preset gasPrice, Use transfer instead of send
rmeissner May 9, 2018
cf3098e
Implemented PR comments
rmeissner May 9, 2018
910f6dd
Fix tests
rmeissner May 9, 2018
5281a66
Use correct names for tests
rmeissner May 9, 2018
2bcbf07
Implement linked lists for modulemanager and ownermanager
rmeissner May 12, 2018
e5f2fe3
Optimize gas usage
rmeissner May 12, 2018
0aadbb0
Add note for naming of execute functions
rmeissner May 12, 2018
43adf2b
Optimized method names
rmeissner May 13, 2018
392634d
Convert state channel edition to state channel module
rmeissner May 14, 2018
f3c1d3f
Use CreateAndAddModules everywhere
rmeissner May 14, 2018
5ac5aed
Use parameters to approce transaction in team edition
rmeissner May 14, 2018
2a8268a
Fail if module execution fails
rmeissner May 15, 2018
580e3cf
Add require to module manager setup
rmeissner May 15, 2018
9e022c4
Fix calculation of padding in assembly; Remove unused contracts
rmeissner May 16, 2018
34323d6
Use revert to prevent requiredTxGas from being called in transaction
rmeissner May 16, 2018
f340b5d
Update readme; comment out revert in requiredTxGas function
rmeissner May 16, 2018
72c86bb
Add possibility to pay with token
rmeissner May 16, 2018
f06a766
Downgrade solidity pragma for truffle
rmeissner May 16, 2018
e3a4e0e
fix test
rmeissner May 16, 2018
22b5f85
Fix create and add modules test
rmeissner May 16, 2018
73e5061
More test fixing
rmeissner May 16, 2018
917625d
It is late
rmeissner May 16, 2018
c647011
enable revert
rmeissner May 18, 2018
fa626f3
Adjust dependencies
rmeissner May 18, 2018
06703b1
Add possibility to pay safe creation with tokens
rmeissner May 18, 2018
0f2453e
Calculate method ids at compile time
rmeissner May 18, 2018
71f7559
Fix warnings
rmeissner May 18, 2018
92d1214
Use build in solidity abi methods
rmeissner May 18, 2018
d84819b
Audit improvements for team edition
rmeissner May 19, 2018
4d616d0
Require token transfer for payment to be successfull
rmeissner May 25, 2018
4ed1ece
Added test for auto confirm of sender
rmeissner May 25, 2018
be2c138
Added error message to all requires
rmeissner May 27, 2018
95ccf95
Adjusted revert messages
rmeissner May 27, 2018
b24f6fc
More error message adjustments
rmeissner May 27, 2018
b42b5f9
Add mnemonic logic for deployment
rmeissner May 28, 2018
76b70e8
Fix truffle dependency
rmeissner May 28, 2018
b22967a
Fix possibility of malicious replace/remove operations on owners and …
rmeissner Jun 3, 2018
0ef7ade
Added delegatecall for multi send
rmeissner Jun 3, 2018
51a0935
Add hash to failed event; Use ganache npm package
rmeissner Jun 13, 2018
ecf060b
Update package-lock
rmeissner Jun 13, 2018
4567ea1
Added error message for ether payment; Removed static costs and inclu…
rmeissner Jun 14, 2018
355c413
Change bool and uint8 to uint256
rmeissner Jun 14, 2018
942968d
Change r, s, v arrays to bytes
rmeissner Jun 14, 2018
2983553
Use uint256 instead of uint8
rmeissner Jun 22, 2018
9dd2a84
Adjust visibility of signature validation methods
rmeissner Jun 22, 2018
04d2785
Perform token transfer in assembly for correct return
rmeissner Jun 22, 2018
1007cd9
Remove naming check for team edition
rmeissner Jun 22, 2018
cb41a12
Remove unused erc20 interface
rmeissner Jun 22, 2018
7aec3c0
Delete .node-xmlhttprequest-sync-11990
rmeissner Jun 22, 2018
5a7a442
Add tests for failed payment of creation
rmeissner Jun 26, 2018
73acdc6
Fix safe creation payment
rmeissner Jun 26, 2018
898cc89
Added require messages for paying proxy
rmeissner Jun 26, 2018
68685cd
Added audit report
rmeissner Jul 4, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language: node_js
node_js:
- '9'
before_script:
- truffle compile
84 changes: 55 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Gnosis Safe Contracts
=====================

The Gnosis Safe is a multisignature wallet with support for confirmations using signed messages based on [ERC191](https://github.com/ethereum/EIPs/issues/191). It is the successor of the [Gnosis Multisig Wallet](https://github.com/gnosis/MultiSigWallet) and combines more functionality with reduced gas costs. The Gnosis Safe allows basic wallet configuration like adding and removing owners and more advanced features like extensions, which allow to do transactions with different requirements.
The Gnosis Safe is a multisignature wallet with support for confirmations using signed messages based on [ERC191](https://github.com/ethereum/EIPs/issues/191). It is the successor of the [Gnosis Multisig Wallet](https://github.com/gnosis/MultiSigWallet) and combines more functionality with reduced gas costs. The Gnosis Safe allows basic wallet configuration like adding and removing owners and more advanced features like modules, which allow to do transactions with different requirements.

Contracts
---------
Expand All @@ -15,71 +15,97 @@ The nonce prevents replay attacks and is increased with every successfully execu
### Contract Creations
As the creation of new contracts is a very gas consuming operation, Safe contracts use a proxy pattern where only one master copy of a contract is deployed once and all its copies are deployed as minimal proxy contracts pointing to the master copy contract. This pattern also allows to update the contract functionality later on by updating the address of the master copy in the proxy contract. As contract constructors can only be executed once at the time the master copy is deployed, constructor logic has to be moved into an additional persistent setup function, which can be called to setup all copies of the master copy. This setup function has to be implemented in a way it can only be executed once. It is important to note that the master copy contract has to be persistent and there should be no possibility to execute a `selfdestruct` call on the master copy contract.

Multiple contracts use the `authorized()` modifier. This modifier should be overwritten by contract to implemented the desired logic to check access to the protected methods.

#### SelfAuthorized.sol
The self authorized contract implements the `authorized()` so that only the contract itself is authorized to perform actions.

#### Proxy.sol
The proxy contract implements only two functions: The constructor setting the address of the master copy and the fallback function forwarding all transactions sent to the proxy via a `DELEGATECALL` to the master copy and returning all data returned by the `DELEGATECALL`.

#### DelegateConstructorProxy.sol
This is an extension to the proxy contract that allows further initialization logic to be passed to the constructor.

#### PayingProxy.sol
This is an extension to the delegate constructor proxy contract that pays a specific amount to a target address after initialization.

#### ProxyFactory.sol
The proxy factory allows to create new proxy contracts pointing to a master copy and executing a function in the newly deployed proxy in one transaction. This additional transaction can for example execute the setup function to initialize the state of the contract.

#### MasterCopy.sol
The master copy contract defines the master copy field and has simple logic to change it. The master copy class should always be defined first if inherited.

#### ModuleManager.sol
The module manager allows the management (add, remove) of modules. These modules can execute transactions via the module manager. The module manager implements logic to execute calls, delegatecalls and create operations.

#### OwnerManager.sol
The owner manager allows the management (add, remove, replace) of owners. It also specifies a threshold that can be used for all actions that require the confirmation of a specific amount of owners.

### Gnosis Safe
#### GnosisSafe.sol
The Gnosis Safe contract implements all basic multisignature functionality. It allows to execute Safe transactions and Safe extensions.
The Gnosis Safe contract implements all basic multisignature functionality. It allows to execute Safe transactions and Safe modules.

Safe transactions can be used to configure the wallet like managing owners, updating the master copy address or whitelisting of modules. All configuration functions can only be called via transactions sent from the Safe itself. This assures that configuration changes require owner confirmations.

Before a Safe transaction can be executed, the transaction has to be confirmed by the required number of owners.

Safe transactions can be used to configure the wallet like managing owners, updating the master copy address or whitelisting of extensions. All configuration functions can only be called via transactions sent from the Safe itself. This assures that configuration changes require owner confirmations.
There are multiple implementations of the Gnosis Safe contract with different methods to check if a transaction has been confirmed by the required owners.

Before a Safe transaction can be executed, the transaction has to be confirmed by the required number of owners. There are two ways to confirm transactions:
#### GnosisSafePersonalEdition.sol
This version is targeted at users that control all keys owning a safe. The transaction hash can be signed with the private keys that manage the safe.

1. Owners represented by private key controlled accounts can sign the transaction hash.
2. Owners represented by contract accounts (e.g. other Safe contracts) or private key controlled accounts can confirm a transaction by calling the `confirmTransaction` function.
Once the required number of confirmations is available `payAndExecuteTransaction` can be called with the sending confirmation signatures. This method will pay the submitter of the transaction for the transaction fees after the Safe transaction has been executed.

Once the required number of confirmations is available `executeTransaction` can be called by sending confirmation signatures and references to confirmations sent using `confirmTransaction`. In case the account calling `executeTransaction` is a wallet owner its call can be used as confirmation and the owner doesn't have to confirm with a signed message or `confirmTransaction`.
`payAndExecuteTransaction` expects all confirmations sorted by owner address. This is required to easily validate no confirmation duplicates exist.

`executeTransaction` expects all confirmations sorted by owner address. This is required to easily validate no confirmation duplicates exist.
#### GnosisSafeTeamEdition.sol
This version is targeted at teams where each owner is a different user. Each owner has to confirm a transaction by using `confirmTransaction`. Once the required number of owners has confirmed, the transaction can be executed via `executeTransaction`. Furthermore this version doesn't store the nonce in the contract but for each transaction a nonce needs to be specified.

##### Example execution
#### GnosisSafeStateChannelEdition.sol
This version is meant to be used with state channels. It is similar to the personal edition, but without the payment option (therefore the method is named `executeTransaction`). Furthermore this version doesn't store the nonce in the contract but for each transaction a nonce needs to be specified.

Assuming we have 4 owners in a 4 out of 4 multisig configuration:
##### Example execution for Personal Edition

Assuming we have 2 owners in a 2 out of 2 multisig configuration:

1. `0x1` (Private key)
2. `0x2` (Private key)
3. `0x3` (Safe contract)
4. `0x4` (Private key)

`0x1` and `0x2` are confirming by signing a message. `0x3` is confirming by sending a `confirmTransaction` transaction. `0x4` is calling the `executeTransaction` function and therefore also confirming the transaction.
`0x1` and `0x2` are confirming by signing a message.

The Safe transaction parameters used for `executeTransaction` have to be set like the following:
* `v = [v_0x1, v_0x2]`
* `r = [r_0x1, r_0x2]`
* `s = [s_0x1, s_0x2]`
* `owners = [0x3, 0x4]`
* `indices = [2, 3]`

`v`, `r` and `s` are the signature parameters for the signed confirmation messages. Position `0` in `v` represents `0x1`'s signature part and corresponds to position `0` in `r` and `s`. The `owners` array contains owner addresses confirming transaction by sending a `confirmTransaction` or calling `executeTransaction`. Their address position in the sorted array of all confirming owner addresses is set in the `indices` array starting from position 0:

`allConfirmingOwners = [0x1, 0x2, 0x3, 0x4]`
`v`, `r` and `s` are the signature parameters for the signed confirmation messages. Position `0` in `v` represents `0x1`'s signature part and corresponds to position `0` in `r` and `s`.

Position of `0x3` is `2` and position of `0x4` is `3` in `indices` array.
### Modules
Modules allow to execute transactions from the Safe without the requirement of multiple signatures. For this Modules that have been added to a Safe can use the `executeModule` function. Modules define their own requirements for execution. Modules need to implement their own replay protection.

### Extensions
Extensions allow to execute transactions from the Safe without the requirement of multiple signatures. Extensions define their own requirements for execution. Every extension has to implement the interface for extensions. This interface requires only one function `isExecutable` receiving all transaction parameters and evaluating if a transaction is allowed to be executed. Extension transactions don't require a nonce as they don't require replay protection.
#### DailyLimitModule.sol
The Daily Limit Modules allows an owner to withdraw specified amounts of specified ERC20 tokens on a daily basis without confirmation by other owners. The daily limit is reset at midnight UTC. Ether is represented with the token address 0. Daily limits can be set via Safe transactions.

#### DailyLimitExtension.sol
The Daily Limit Extensions allows an owner to withdraw specified amounts of specified ERC20 tokens on a daily basis without confirmation by other owners. The daily limit is reset at midnight UTC. Ether is represented with the token address 0. Daily limits can be set via Safe transactions.
#### DailyLimitModuleWithSignature.sol
This is an extension to the daily limit module that checks if the transaction was issued by an owner via a signatures that is sent to the contract. For this the contract also keeps track of a nounce against replay attacks. This is used if the transaction is submitted by another account then an owner.

#### SocialRecoveryExtension.sol
The Social Recovery Extensions allows to recover a Safe in case access to owner accounts was lost. This is done by defining a minimum of 3 friends’ addresses as trusted parties. If all required friends confirm that a Safe owner should be replaced with another address, the Safe owner is replaced and access to the Safe can be restored. Every owner address can be replaced only once.
#### SocialRecoveryModule.sol
The Social Recovery Modules allows to recover a Safe in case access to owner accounts was lost. This is done by defining a minimum of 3 friends’ addresses as trusted parties. If all required friends confirm that a Safe owner should be replaced with another address, the Safe owner is replaced and access to the Safe can be restored. Every owner address can be replaced only once.

#### WhitelistExtension.sol
The Whitelist Extensions allows an owner to execute arbitrary transactions to specific addresses without confirmation by other owners. The whitelist can be maintained via Safe transactions.
#### WhitelistModule.sol
The Whitelist Modules allows an owner to execute arbitrary transactions to specific addresses without confirmation by other owners. The whitelist can be maintained via Safe transactions.

### Libraries
Libraries can be called from the Safe via a `DELEGATECALL`. They should not implement their own storage as this storage won’t be accessible via a `DELEGATECALL`.

#### MultiSend.sol
This library allows to batch transactions and execute them at once. This is useful if user interactions require more than one transaction for one UI interaction like approving an amount of ERC20 tokens and calling a contract consuming those tokens. If one transaction fails all are reverted.

#### CreateAndAddExtension.sol
This library allows to create a new Safe extension and whitelist this extension for the Safe in one single transaction.
#### MultiSendStruct.sol
Experimental implementation of the multi send library that uses structs instead of a byte array to pass the combined transactions to the contract.

#### CreateAndAddModule.sol
This library allows to create a new Safe module and whitelist this module for the Safe in one single transaction.

Install
-------
Expand Down
62 changes: 62 additions & 0 deletions contracts/DelegateConstructorProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
pragma solidity 0.4.23;


/// @title Delegate Constructor Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract. It is possible to send along initialization data with the constructor.
/// @author Stefan George - <stefan@gnosis.pm>
/// @author Richard Meissner - <richard@gnosis.pm>
contract DelegateConstructorProxy {

// masterCopy always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
address masterCopy;

/// @dev Constructor function sets address of master copy contract.
/// @param _masterCopy Master copy address.
/// @param initializer Data used for a delegate call to initialize the contract.
constructor(address _masterCopy, bytes initializer)
public
{
require(_masterCopy != 0);
masterCopy = _masterCopy;
if (initializer.length > 0) {
delegate(initializer, false);
}
}

/// @dev Fallback function forwards all transactions and returns all received return data.
function ()
external
payable
{
delegate(msg.data, true);
}

function delegate(bytes _calldata, bool returnData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not always retrunData == true? I would remove this argument as the constructor case a single time use case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we copy the delegate call logic in the constructor the one time costs might increase not sure how much the per transaction gas increases because of that, will check

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm gas costs are exactly the same :D

internal
{
// solium-disable-next-line security/no-inline-assembly
assembly {
let masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
let success := delegatecall(sub(gas, 10000), masterCopy, add(_calldata, 0x20), mload(_calldata), 0, 0)
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize)
if eq(success, 0) { revert(ptr, returndatasize) }
if returnData { return(ptr, returndatasize) }
}
}

function implementation()
public
view
returns (address)
{
return masterCopy;
}

function proxyType()
public
pure
returns (uint256)
{
return 2;
}
}
12 changes: 12 additions & 0 deletions contracts/Enum.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity 0.4.23;


/// @title Enum - Collection of enums
/// @author Richard Meissner - <richard@gnosis.pm>
contract Enum {
enum Operation {
Call,
DelegateCall,
Create
}
}
18 changes: 0 additions & 18 deletions contracts/Extension.sol

This file was deleted.

Loading