Multisig Contracts in Ethereum
Multi-signature (multisig) refers to needing multiple keys/participants to authorize a transaction, rather than a single signature from one key. It has numerous applications in real world, where proper key management is required, and multiple people hold ownership of a fund.
As the name indicates, a multisig contract requires one or more signatures before the transaction can be submitted for execution. For example, consider a locker that has two locks and two keys. Consider one key is held by Alice and another by say Bob. The only way to open the box is by using both the keys together. As a result, Alice and Bob cannot open the locker without each other’s consent and whatever is in there is managed by both equally.
This document describes multisig contracts, implementation challenges in Ethereum, the possible approaches for implementing multisig contracts and gives an overview of some popular multisig contracts in Ethereum.
Why use multisig contracts?
- Splitting responsibility of ownership: Using multisig contracts allows you to split the ownership between multiple partners. Consider a situation where a wallet contains the money owned by a big company. In such a scenario, company’s decisions about spending the money are not taken by a single person, there is a group of people or board which decides it together usually by taking a vote. Multisig contracts can be used to model this concept of decision making in an organization. Using multisig contracts in this situation can be handled considering the number of votes as the number of signatures required. The transaction could be considered valid if it has signatures from majority of the key holders (2-of-3 multisig address), or all the key holders.
- Lost key: A single key is used to sign the transactions, that means if you lose the key it renders the account unusable or a person with an access to this key can use the funds in that account. Multisig contracts reduces this risk, for example consider a multisig contract that requires majority of the key holders to sign the transaction, that is out of n signers it requires n/2 + 1 to sign the transaction. In this scenario even if you lose n/2 -1 key ,the lost keys won’t allow the thief to use funds or render it incompatible for use as the minimum number of users required to sign still have the key available.
- Two factor authentications: By creating a multisig contract that requires two or more keys, the user can create a two-factor authentication mechanism to access her funds. For instance, she could have one private key stored in her laptop and the other one in her mobile device. This would ensure that only someone who has access to both the keys is able to make a transaction. But it also adds an additional risk of losing the keys if all the keys are required to sign the transaction.
- Escrow contracts: Creating a 2-of-3 multisig contract can allow for an escrow transaction between two parties. To see what an escrow transaction is, consider Alice and Bob and a third-party Charlie, as a mutually trusted arbiter in case anything goes wrong. In such a scenario, Alice would first deposit the funds, which would be locked up (neither user being able to access them on their own). Then, if Bob provides the goods or services as agreed, they can both use their keys to sign and complete the transaction. Charlie, the arbiter, would only need to step in if there was a dispute, at which point he could use his key with either Alice or Bob to create a signature, and take decision according to his judgment.
Multi-sig contracts are more suitable for a group of users that need to manage shared funds but can also be used by a single user who stores the multiple keys on multiple devices to manage their personal funds. Even if a single user owns two devices, the risk of incurring unauthorized transactions is reduced since an attacker would need to gain access to two private keys. But just as it could be the case with single-signature wallets, devices and private keys can still be lost or destroyed.
Multisig in Ethereum
Ethereum blockchain does not support multi-sig out of the box. Ethereum is based on accounts (which can be a public key hash or a smart contract), every transaction has only one source address. Hence, to emulate multi-sig logic you deploy a smart contract. That smart contract implements the logic to satisfy multi-sig requirement. There is no common API nor implementation for multisig in Ethereum.
Multisig contracts are a widely used class of Ethereum smart contracts. Ethereum has a very capable virtual machine and is designed for complex computing, this provides the ability to have multiple different implementations of multisig contracts and hence it’s not clear which one among these to trust.
Since multisig concept in Ethereum, is implemented as smart contract code, they must be audited very carefully, bugs in their code could result in lost or frozen funds.
Because of this many multisig contract creators opt for vault bug bounty programs. In these programs they open source their code and anyone who discovers a bug in the contract, can report them and earn a bug bounty.
Two approaches to implement multisig contracts in Ethereum
A multisig contract with off-chain signature collection
The idea behind this approach is `security over simplicity`, i.e. more complex the contract more space it leaves for vulnerability. This contract pushes most logic off-chain, where each multisig owner is responsible for creating the signature that authorizes transactions and then a single function is used to present all the signatures to the contract to be verified.
In terms of user interface, the idea is that each keyholder would have a UI where they enter in the details of the transaction they wish to send (ideally on an offline computer). Then on an online machine an “operator” would gather up all the signatures from the keyholder and send off the actual transaction containing all the signatures. The operator would not need to have any actual control of funds, the multisig key holders are ultimately the ones that have the authority to execute transactions.
The contract mentioned above declares the following variables:
- nonce: A simple counter which increments for each new transaction. Helps prevent replay attacks
- threshold: The number of required confirmations to execute a transaction
- isOwner: A mapping which is used to set and find if an address is an owner.
- ownersArr: An array listing the owner’s addresses
The constructor initializes a new wallet contract. It first verifies that there are not more than 10 owners, and a minimum of one confirmation is required to execute the transaction. Next it iterates over the owners_ array and adds each address to the contract’s owner list. And finally set the threshold.
When a transaction to transfer Y ether to X is created, it is signed using the private keys of the owners. That creates a signature per each transaction signing which is made up of three values (V, R and S), these points are part of the underlying elliptic curve cryptography used by Ethereum.
The initial checks make sure that the signatures are complete.
Then it creates the signed data following the ERC-191 standard proposal. The function then iterates for each number of required confirmations (threshold) and uses the ecrecover() function to recover the public keys using the signatures. That public key is then compared against the owner’s public key(address) and if it matches then you know that the owner has signed the transaction.
This way of verification allows you to verify that something has been sent/signed by a specific person without the need of knowing their private key.
The nonce counter is incremented for the next transaction to avoid replay attacks. Finally calling the call () function to transfer the funds in a generic way.
GitHub link: https://github.com/christianlundkvist/simple-multisig
A multisig contract with contract driven signature collection
Unlike the previous contract , that pushes the logic of signing transactions off-chain , this contract provides you with the function to confirm the transaction individually without having to collect all the signatures and then send it to the contract at once. The contract maintains the records of transactions and signatures/confirmations sent by parties in its contract state.
The mentioned contract declares the following variables:
- Transactions: It maintains the list of transactions received by the contract that did not receive the required number of confirmations yet.
- Confirmations: Confirmations maintains a mapping of transactions and confirmations received by each transaction.
- isOwner: Mapping that is used to set or find if an address is the owner.
- owners: It is an array listing owner address.
- required: This field specifies the number of confirmations required for a single transaction to execute.
- transactionCount: It the count of number of transactions executed from this account.
The constructor initializes a new wallet contract. First it checks if the length of the owners array is greater than the owners required to sign it. In a loop the constructor marks the value of isOwner Boolean array to true.
The function submitTransaction() is used to create/propose a new transaction that needs to be signed by others. It adds the transaction to the list of transaction and marks confirmation as true for this transaction to the address that invoked submit method.
The function confirmTransaction() accepts the transactionId of the transaction you want to approve/sign, before making any confirmation the method checks if the owner and transaction exists in the mapping and has not already submitted the confirmation.
At the end, the above method also calls another method executeTransaction() to submit transaction for execution.
The function executeTransaction() when called checks if the transaction with the provided transactionId has received the required number of confirmations or not. If it has received the required number of confirmations it submits the transaction for execution.
The two contracts above are just basic samples of how multisig contract can be implemented in Ethereum. Other methods to the smart contract or check to the existing methods can be added to these contracts. Some examples of additional features that can be provided are, to add Owner, remove Owner, replace an existing owner, change requirements for signing transaction.
You could also add a daily limit, amount in wei, which can be withdrawn without confirmations daily and store it in a variable in the smart contract. This will allow you to withdraw the amount specified without requiring signatures from required number of signers every day.
GitHub Link: https://github.com/ConsenSys/MultiSigWallet/blob/master/MultiSigWalletWithDailyLimit.sol
Well known implementations of Multisig contracts
Parity’s wallet:
The Parity wallet is designed to integrate seamlessly with all standard tokens as well as manage Ether transfers. The vast array of options offered by Parity wallet made it extremely popular in the crypto community.
The Parity wallet now deprecated, was attacked twice in 2017. The first attack occurred because of a vulnerability in the code that did not restrict access to a method in its smart contract. What happened as a result of it is that imagine if you could walk into a bank and request that they transfer ownership of a stranger’s account into your name. The bank happily does so, and you withdraw all the account’s money. This is what happened with the Parity multi-sig. The cause of this was that, in Solidity, a method that has no modifier is considered public. This means that the method can be accessed from any source, including external transaction calls made to the contract. In the Parity multi-sig, a critical mistake was made in not specifying the modifier for the method that could initialize the wallet.
Following this hack, Parity issued a fix for the exploit, deploying a new library contract that was meant to resolve the issue. But the new code contained another flaw which enabled the library contract in the Parity Wallet to be converted into a regular multi-sig wallet. Consequently, an individual was able to use the initWallet function to take ownership of the wallet.
Consensys wallet:
The Consensys multisig wallet is a simple multisig wallet. Its code is easy to understand and use and contains all the common functions a multisig contract would need. However, this wallet is not regularly maintained, and development has since continued under the Gnosis multisig wallet.
Source Code: https://github.com/ConsenSys/MultiSigWallet
Gnosis wallet:
Gnosis’ multisig wallet is vastly considered to be the go-to Ethereum multisig wallet by many and is used by many organizations in the Ethereum space. It’s based on the ConsenSys multisig wallet but has had many improvements and continued active development. Most wallets only allow multisig transactions for transactions sending ether from one address to another. Any other transaction like sending tokens or triggering any smart contract function can be done by any of the owners without additional confirmation. Gnosis wallet implements that.
Source Code: https://github.com/gnosis/MultiSigWallet
Unchained’s 2/3 multisig wallet:
It was built by Unchained Capital to help holders worldwide safely and collaboratively store their ETH using multisig cold storage provided by hardware wallets. To authorize a spend, two signatures must be provided by 2 of the 3 owners. To generate the message to be signed, provide the destination address and spend amount. The signatures must be provided as the (v, r, s) hex-encoded coordinates.
Source Code: https://github.com/unchained-capital/ethereum-multisig
Conclusion
Multisigs are a great solution for sharing ownership, control over funds, as well as a way of executing transactions that require multiple signatures in Ethereum. But as multisig is implemented as a smart contract in Ethereum, it can be said that the more logic in the smart contract the bigger the attack surface and the more likely that bugs are introduced that risk undermining the security features and hence need to be audited carefully before deploying.
References:
- https://medium.com/mycrypto/introduction-to-multisig-contracts-33d5b25134b2
- https://academy.binance.com/security/what-is-a-multisig-wallet
- https://medium.com/@ChrisLundkvist/exploring-simpler-ethereum-multisig-contracts-b71020c19037
- https://unchained-capital.com/blog/multisig-ethereum-contract-for-hardware-wallets/