diff --git a/wallet/wallet.sol b/wallet/wallet.sol index c0a3a28..d5fb3c9 100644 --- a/wallet/wallet.sol +++ b/wallet/wallet.sol @@ -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 -// 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 @@ -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)]; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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)]; @@ -143,7 +153,7 @@ contract multiowned { uint ownerIndexBit = 2**ownerIndex; return !(pending.ownersDone & ownerIndexBit == 0); } - + // INTERNAL METHODS function confirmAndCheck(bytes32 _operation) internal returns (bool) { @@ -197,7 +207,7 @@ contract multiowned { } } } - + function clearPending() internal { uint length = m_pendingIndex.length; for (uint i = 0; i < length; ++i) @@ -205,14 +215,14 @@ contract multiowned { 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; @@ -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) { @@ -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; @@ -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); @@ -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)) { @@ -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) { @@ -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)