mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Updated wallet code.
This commit is contained in:
		
							parent
							
								
									cb02917380
								
							
						
					
					
						commit
						c5984302fc
					
				| @ -47,12 +47,18 @@ static char const* walletCode = R"DELIMITER( | ||||
| // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
 | ||||
| // interior is executed.
 | ||||
| contract multiowned { | ||||
| 
 | ||||
| 	// TYPES
 | ||||
| 
 | ||||
| 	// struct for the status of a pending operation.
 | ||||
| 	struct PendingState { | ||||
| 		uint yetNeeded; | ||||
| 		uint ownersDone; | ||||
| 		uint index; | ||||
| 	} | ||||
| 
 | ||||
| 	// EVENTS
 | ||||
| 
 | ||||
| 	// this contract only has five types of events: it can accept a confirmation, in which case
 | ||||
| 	// we record owner and operation (hash) alongside it.
 | ||||
| 	event Confirmation(address owner, bytes32 operation); | ||||
| @ -63,14 +69,9 @@ contract multiowned { | ||||
| 	event OwnerRemoved(address oldOwner); | ||||
| 	// the last one is emitted if the required signatures change
 | ||||
| 	event RequirementChanged(uint newRequirement); | ||||
| 	// constructor is given number of sigs required to do protected "onlymanyowners" transactions
 | ||||
| 	// as well as the selection of addresses capable of confirming them.
 | ||||
| 	function multiowned() { | ||||
| 		m_required = 1; | ||||
| 		m_numOwners = 1; | ||||
| 		m_owners[m_numOwners] = uint(msg.sender); | ||||
| 		m_ownerIndex[uint(msg.sender)] = m_numOwners; | ||||
| 	} | ||||
| 
 | ||||
| 	// MODIFIERS
 | ||||
| 
 | ||||
| 	// simple single-sig function modifier.
 | ||||
| 	modifier onlyowner { | ||||
| 		if (isOwner(msg.sender)) | ||||
| @ -80,9 +81,21 @@ contract multiowned { | ||||
| 	// that later attempts can be realised as the same underlying operation and
 | ||||
| 	// thus count as confirmations.
 | ||||
| 	modifier onlymanyowners(bytes32 _operation) { | ||||
| 		if (confirmed(_operation)) | ||||
| 		if (confirmAndCheck(_operation)) | ||||
| 			_ | ||||
| 	} | ||||
| 
 | ||||
| 	// METHODS
 | ||||
| 
 | ||||
| 	// constructor is given number of sigs required to do protected "onlymanyowners" transactions
 | ||||
| 	// as well as the selection of addresses capable of confirming them.
 | ||||
| 	function multiowned() { | ||||
| 		m_required = 1; | ||||
| 		m_numOwners = 1; | ||||
| 		m_owners[m_numOwners] = uint(msg.sender); | ||||
| 		m_ownerIndex[uint(msg.sender)] = m_numOwners; | ||||
| 	} | ||||
| 
 | ||||
| 	// Revokes a prior confirmation of the given operation
 | ||||
| 	function revoke(bytes32 _operation) external { | ||||
| 		uint ownerIndex = m_ownerIndex[uint(msg.sender)]; | ||||
| @ -96,7 +109,75 @@ contract multiowned { | ||||
| 			Revoke(msg.sender, _operation); | ||||
| 		} | ||||
| 	} | ||||
| 	function confirmed(bytes32 _operation) internal returns (bool) { | ||||
| 
 | ||||
| 	// Replaces an owner `_from` with another `_to`.
 | ||||
| 	function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data, block.number)) external { | ||||
| 		if (isOwner(_to)) return; | ||||
| 		uint ownerIndex = m_ownerIndex[uint(_from)]; | ||||
| 		if (ownerIndex == 0) return; | ||||
| 
 | ||||
| 		clearPending(); | ||||
| 		m_owners[ownerIndex] = uint(_to); | ||||
| 		m_ownerIndex[uint(_from)] = 0; | ||||
| 		m_ownerIndex[uint(_to)] = ownerIndex; | ||||
| 		OwnerChanged(_from, _to); | ||||
| 	} | ||||
| 	function addOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external { | ||||
| 		if (isOwner(_owner)) return; | ||||
| 
 | ||||
| 		clearPending(); | ||||
| 		if (m_numOwners >= c_maxOwners) | ||||
| 			reorganizeOwners(); | ||||
| 		if (m_numOwners >= c_maxOwners) | ||||
| 			return; | ||||
| 		m_numOwners++; | ||||
| 		m_owners[m_numOwners] = uint(_owner); | ||||
| 		m_ownerIndex[uint(_owner)] = m_numOwners; | ||||
| 		OwnerAdded(_owner); | ||||
| 	} | ||||
| 
 | ||||
| 	function removeOwner(address _owner) onlymanyowners(sha3(msg.data, block.number)) external { | ||||
| 		uint ownerIndex = m_ownerIndex[uint(_owner)]; | ||||
| 		if (ownerIndex == 0) return; | ||||
| 		if (m_required > m_numOwners - 1) return; | ||||
| 
 | ||||
| 		m_owners[ownerIndex] = 0; | ||||
| 		m_ownerIndex[uint(_owner)] = 0; | ||||
| 		clearPending(); | ||||
| 		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, block.number)) external { | ||||
| 		if (_newRequired > m_numOwners) return; | ||||
| 		m_required = _newRequired; | ||||
| 		clearPending(); | ||||
| 		RequirementChanged(_newRequired); | ||||
| 	} | ||||
| 
 | ||||
| 	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)]; | ||||
| 
 | ||||
| 		// make sure they're an owner
 | ||||
| 		if (ownerIndex == 0) return false; | ||||
| 
 | ||||
| 		// determine the bit to set for this owner.
 | ||||
| 		uint ownerIndexBit = 2**ownerIndex; | ||||
| 		if (pending.ownersDone & ownerIndexBit == 0) { | ||||
| 			return false; | ||||
| 		} else { | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// INTERNAL METHODS
 | ||||
| 
 | ||||
| 	function confirmAndCheck(bytes32 _operation) internal returns (bool) { | ||||
| 		// determine what index the present sender is:
 | ||||
| 		uint ownerIndex = m_ownerIndex[uint(msg.sender)]; | ||||
| 		// make sure they're an owner
 | ||||
| @ -132,42 +213,7 @@ contract multiowned { | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Replaces an owner `_from` with another `_to`.
 | ||||
| 	function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external { | ||||
| 		if (isOwner(_to)) return; | ||||
| 		uint ownerIndex = m_ownerIndex[uint(_from)]; | ||||
| 		if (ownerIndex == 0) return; | ||||
| 
 | ||||
| 		clearPending(); | ||||
| 		m_owners[ownerIndex] = uint(_to); | ||||
| 		m_ownerIndex[uint(_from)] = 0; | ||||
| 		m_ownerIndex[uint(_to)] = ownerIndex; | ||||
| 		OwnerChanged(_from, _to); | ||||
| 	} | ||||
| 	function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external { | ||||
| 		if (isOwner(_owner)) return; | ||||
| 
 | ||||
| 		clearPending(); | ||||
| 		if (m_numOwners >= c_maxOwners) | ||||
| 			reorganizeOwners(); | ||||
| 		if (m_numOwners >= c_maxOwners) | ||||
| 			return; | ||||
| 		m_numOwners++; | ||||
| 		m_owners[m_numOwners] = uint(_owner); | ||||
| 		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; | ||||
| 		if (m_required > m_numOwners - 1) return; | ||||
| 
 | ||||
| 		m_owners[ownerIndex] = 0; | ||||
| 		m_ownerIndex[uint(_owner)] = 0; | ||||
| 		clearPending(); | ||||
| 		reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
 | ||||
| 		OwnerRemoved(_owner); | ||||
| 	} | ||||
| 	function reorganizeOwners() private returns (bool) { | ||||
| 		uint free = 1; | ||||
| 		while (free < m_numOwners) | ||||
| @ -182,6 +228,7 @@ contract multiowned { | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	function clearPending() internal { | ||||
| 		uint length = m_pendingIndex.length; | ||||
| 		for (uint i = 0; i < length; ++i) | ||||
| @ -189,46 +236,54 @@ contract multiowned { | ||||
| 				delete m_pending[m_pendingIndex[i]]; | ||||
| 		delete m_pendingIndex; | ||||
| 	} | ||||
| 	function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external { | ||||
| 		if (_newRequired > m_numOwners) return; | ||||
| 		m_required = _newRequired; | ||||
| 		clearPending(); | ||||
| 		RequirementChanged(_newRequired); | ||||
| 	} | ||||
| 	function isOwner(address _addr) returns (bool) { | ||||
| 		return m_ownerIndex[uint(_addr)] > 0; | ||||
| 	} | ||||
| 
 | ||||
| 	// 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] public m_owners; | ||||
| 	uint[256] m_owners; | ||||
| 	uint constant c_maxOwners = 250; | ||||
| 	// index on the list of owners to allow reverse lookup
 | ||||
| 	mapping(uint => uint) public m_ownerIndex; | ||||
| 	mapping(uint => uint) m_ownerIndex; | ||||
| 	// the ongoing operations.
 | ||||
| 	mapping(bytes32 => PendingState) public m_pending; | ||||
| 	bytes32[] public m_pendingIndex; | ||||
| 	mapping(bytes32 => PendingState) m_pending; | ||||
| 	bytes32[] m_pendingIndex; | ||||
| } | ||||
| 
 | ||||
| // inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
 | ||||
| // on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method
 | ||||
| // uses is specified in the modifier.
 | ||||
| contract daylimit is multiowned { | ||||
| 
 | ||||
| 	// MODIFIERS
 | ||||
| 
 | ||||
| 	// simple modifier for daily limit.
 | ||||
| 	modifier limitedDaily(uint _value) { | ||||
| 		if (underLimit(_value)) | ||||
| 			_ | ||||
| 	} | ||||
| 
 | ||||
| 	// METHODS
 | ||||
| 
 | ||||
| 	// constructor - just records the present day's index.
 | ||||
| 	function daylimit() { | ||||
| 		m_lastDay = today(); | ||||
| 	} | ||||
| 	// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
 | ||||
| 	function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { | ||||
| 	function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data, block.number)) external { | ||||
| 		m_dailyLimit = _newLimit; | ||||
| 	} | ||||
| 	// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
 | ||||
| 	function resetSpentToday() onlymanyowners(sha3(msg.data)) external { | ||||
| 	function resetSpentToday() onlymanyowners(sha3(msg.data, block.number)) 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) { | ||||
| @ -244,60 +299,76 @@ contract daylimit is multiowned { | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
| 	// simple modifier for daily limit.
 | ||||
| 	modifier limitedDaily(uint _value) { | ||||
| 		if (underLimit(_value)) | ||||
| 			_ | ||||
| 	} | ||||
| 	// determines today's index.
 | ||||
| 	function today() private constant returns (uint) { return now / 1 days; } | ||||
| 	uint public m_spentToday; | ||||
| 
 | ||||
| 	// FIELDS
 | ||||
| 
 | ||||
| 	uint public m_dailyLimit; | ||||
| 	uint public m_lastDay; | ||||
| 	uint m_spentToday; | ||||
| 	uint m_lastDay; | ||||
| } | ||||
| 
 | ||||
| // interface contract for multisig proxy contracts; see below for docs.
 | ||||
| contract multisig { | ||||
| 	event Deposit(address from, uint value); | ||||
| 	event SingleTransact(address owner, uint value, address to, bytes data); | ||||
| 	event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data); | ||||
| 	event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data); | ||||
| 	function changeOwner(address _from, address _to) external; | ||||
| 	function execute(address _to, uint _value, bytes _data) external returns (bytes32); | ||||
| 	function confirm(bytes32 _h) returns (bool); | ||||
| } | ||||
| // usage:
 | ||||
| // bytes32 h = Wallet(w).from(oneOwner).transact(to, value, data);
 | ||||
| // Wallet(w).from(anotherOwner).confirm(h);
 | ||||
| contract Wallet is multisig, multiowned, daylimit { | ||||
| 	// Transaction structure to remember details of transaction lest it need be saved for a later call.
 | ||||
| 	struct Transaction { | ||||
| 		address to; | ||||
| 		uint value; | ||||
| 		bytes data; | ||||
| 	} | ||||
| 	/*
 | ||||
| 
 | ||||
| 	// EVENTS
 | ||||
| 
 | ||||
| 	// logged events:
 | ||||
| 	// Funds has arrived into the wallet (record how much).
 | ||||
| 	event Deposit(address from, uint value); | ||||
| 	// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
 | ||||
| 	event SingleTransact(address owner, uint value, address to, bytes data); | ||||
| 	// Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
 | ||||
| 	event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);*/ | ||||
| 	// constructor - just pass on the owner arra to the multiowned.
 | ||||
| 	event Created(); | ||||
| 	function Wallet() { | ||||
| 		Created(); | ||||
| 	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); | ||||
| 	function confirm(bytes32 _h) returns (bool); | ||||
| } | ||||
| 
 | ||||
| // usage:
 | ||||
| // bytes32 h = Wallet(w).from(oneOwner).transact(to, value, data);
 | ||||
| // Wallet(w).from(anotherOwner).confirm(h);
 | ||||
| contract Wallet is multisig, multiowned, daylimit { | ||||
| 
 | ||||
| 	// TYPES
 | ||||
| 
 | ||||
| 	// Transaction structure to remember details of transaction lest it need be saved for a later call.
 | ||||
| 	struct Transaction { | ||||
| 		address to; | ||||
| 		uint value; | ||||
| 		bytes data; | ||||
| 	} | ||||
| 
 | ||||
| 	// EVENTS
 | ||||
| 
 | ||||
| 	event Created(bytes32 indexed identifier); | ||||
| 
 | ||||
| 	// METHODS
 | ||||
| 
 | ||||
| 	// constructor - just pass on the owner arra to the multiowned.
 | ||||
| 	function Wallet(bytes32 identifier) { | ||||
| 		Created(identifier); | ||||
| 	} | ||||
| 
 | ||||
| 	// kills the contract sending everything to `_to`.
 | ||||
| 	function kill(address _to) onlymanyowners(sha3(msg.data)) external { | ||||
| 	function kill(address _to) onlymanyowners(sha3(msg.data, block.number)) 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 transacion immediately if below daily spend limit.
 | ||||
| 	// 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
 | ||||
| @ -311,7 +382,7 @@ contract Wallet is multisig, multiowned, daylimit { | ||||
| 			return 0; | ||||
| 		} | ||||
| 		// determine our operation hash.
 | ||||
| 		_r = sha3(msg.data); | ||||
| 		_r = sha3(msg.data, block.number); | ||||
| 		if (!confirm(_r) && m_txs[_r].to == 0) { | ||||
| 			m_txs[_r].to = _to; | ||||
| 			m_txs[_r].value = _value; | ||||
| @ -319,6 +390,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) { | ||||
| @ -329,20 +401,20 @@ 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) | ||||
| 			delete m_txs[m_pendingIndex[i]]; | ||||
| 		super.clearPending(); | ||||
| 	} | ||||
| 	// // internally confirm transaction with all of the info. returns true iff confirmed good and executed.
 | ||||
| 	// function confirmVerbose(bytes32 _h, address _to, uint _value, bytes _data) private onlymanyowners(_h) returns (bool) {
 | ||||
| 	//     _to.call.value(_value)(_data);
 | ||||
| 	//     MultiTransact("out", msg.sender, _h, _value, _to);
 | ||||
| 	//     return true;
 | ||||
| 	// }
 | ||||
| 
 | ||||
| 	// FIELDS
 | ||||
| 
 | ||||
| 	// pending transactions we have at present.
 | ||||
| 	mapping (bytes32 => Transaction) public m_txs; | ||||
| 	mapping (bytes32 => Transaction) m_txs; | ||||
| } | ||||
| )DELIMITER"; | ||||
| 
 | ||||
| @ -443,7 +515,7 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer) | ||||
| 	// 4 owners, set required to 3
 | ||||
| 	BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); | ||||
| 	// check that balance is and stays zero at destination address
 | ||||
| 	h256 opHash("f916231db11c12e0142dc51f23632bc655de87c63f83fc928c443e90f7aa364a"); | ||||
| 	h256 opHash("8f27f478ebcfaf28b0c354f4809ace8087000d668b89c8bc3b1b608bfdbe6654"); | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0); | ||||
| 	m_sender = Address(0x12); | ||||
| 	BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash)); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user