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

Enhance multisig documentation #73

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
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
68 changes: 41 additions & 27 deletions wallet/wallet.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
//sol Wallet
// Multi-sig, daily-limited account proxy/wallet.
// Multi-sig, daily-limited account proxy/wallet. Any owner can issue transactions with value under
// the daily limit (including transactions to other contracts). For transactions with higher value,
// an approval is needed from a prespecified number of other owners.
//
// @authors:
// Gav Wood <[email protected]>
// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
// single, or, crucially, each of a number of, designated owners.
//
// usage:
// create a Wallet contract. When an owner wants to initiate a transaction, call
// execute(). when another owner wants to approve that transaction, call confirm().
//
// dev notes:
// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
// interior is executed.


// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
// single, or, crucially, each of a number of, designated owners.
contract multiowned {

// TYPES
Expand Down Expand Up @@ -62,7 +72,7 @@ contract multiowned {
}
m_required = _required;
}

// Revokes a prior confirmation of the given operation
function revoke(bytes32 _operation) external {
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
Expand All @@ -76,7 +86,7 @@ contract multiowned {
Revoke(msg.sender, _operation);
}
}

// Replaces an owner `_from` with another `_to`.
function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external {
if (isOwner(_to)) return;
Expand All @@ -89,7 +99,7 @@ contract multiowned {
m_ownerIndex[uint(_to)] = ownerIndex;
OwnerChanged(_from, _to);
}

function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
if (isOwner(_owner)) return;

Expand All @@ -103,7 +113,7 @@ contract multiowned {
m_ownerIndex[uint(_owner)] = m_numOwners;
OwnerAdded(_owner);
}

function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external {
uint ownerIndex = m_ownerIndex[uint(_owner)];
if (ownerIndex == 0) return;
Expand All @@ -115,7 +125,7 @@ contract multiowned {
reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
OwnerRemoved(_owner);
}

function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
if (_newRequired > m_numOwners) return;
m_required = _newRequired;
Expand All @@ -131,7 +141,7 @@ contract multiowned {
function isOwner(address _addr) returns (bool) {
return m_ownerIndex[uint(_addr)] > 0;
}

function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
var pending = m_pending[_operation];
uint ownerIndex = m_ownerIndex[uint(_owner)];
Expand All @@ -143,7 +153,7 @@ contract multiowned {
uint ownerIndexBit = 2**ownerIndex;
return !(pending.ownersDone & ownerIndexBit == 0);
}

// INTERNAL METHODS

function confirmAndCheck(bytes32 _operation) internal returns (bool) {
Expand Down Expand Up @@ -197,22 +207,22 @@ contract multiowned {
}
}
}

function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i)
if (m_pendingIndex[i] != 0)
delete m_pending[m_pendingIndex[i]];
delete m_pendingIndex;
}

// FIELDS

// the number of owners that must confirm the same operation before it is run.
uint public m_required;
// pointer used to find a free slot in m_owners
uint public m_numOwners;

// list of owners
uint[256] m_owners;
uint constant c_maxOwners = 250;
Expand Down Expand Up @@ -247,13 +257,13 @@ contract daylimit is multiowned {
function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external {
m_dailyLimit = _newLimit;
}
// resets the amount already spent today. needs many of the owners to confirm.
// resets the amount already spent today. needs many of the owners to confirm.
function resetSpentToday() onlymanyowners(sha3(msg.data)) external {
m_spentToday = 0;
}

// INTERNAL METHODS

// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
// returns true. otherwise just returns false.
function underLimit(uint _value) internal onlyowner returns (bool) {
Expand All @@ -263,7 +273,7 @@ contract daylimit is multiowned {
m_lastDay = today();
}
// check to see if there's enough left - if so, subtract and return true.
// overflow protection // dailyLimit check
// overflow protection // dailyLimit check
if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
m_spentToday += _value;
return true;
Expand Down Expand Up @@ -294,9 +304,9 @@ contract multisig {
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
// Confirmation still needed for a transaction.
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);

// FUNCTIONS

// TODO: document
function changeOwner(address _from, address _to) external;
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
Expand All @@ -320,27 +330,31 @@ contract Wallet is multisig, multiowned, daylimit {
// METHODS

// constructor - just pass on the owner array to the multiowned and
// the limit to daylimit
// the limit to daylimit. the contract creator is automatically added
// as an owner.
function Wallet(address[] _owners, uint _required, uint _daylimit)
multiowned(_owners, _required) daylimit(_daylimit) {
}

// kills the contract sending everything to `_to`.
function kill(address _to) onlymanyowners(sha3(msg.data)) external {
suicide(_to);
}

// gets called when no other function matches
function() {
// just being sent some cash?
if (msg.value > 0)
Deposit(msg.sender, msg.value);
}

// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.

// Outside-visible transact entry point. Executes transaction immediately if below daily spend limit,
// including transactions to other contracts.
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
// and _data arguments). They still get the option of using them if they want, anyways.
// If _to equals the zero address, it will go through the regular confirmation process but
// result in a no-op.
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) {
// first, take the opportunity to check that we're under the daily limit.
if (underLimit(_value)) {
Expand All @@ -358,7 +372,7 @@ contract Wallet is multisig, multiowned, daylimit {
ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
}
}

// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
// to determine the body of the transaction from the hash provided.
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
Expand All @@ -369,9 +383,9 @@ contract Wallet is multisig, multiowned, daylimit {
return true;
}
}

// INTERNAL METHODS

function clearPending() internal {
uint length = m_pendingIndex.length;
for (uint i = 0; i < length; ++i)
Expand Down