@@ -138,33 +138,100 @@ contract multiowned {
138138 uint ownerIndexBit = 2 ** ownerIndex;
139139 return ! (pending.ownersDone & ownerIndexBit == 0 );
140140 }
141-
142- // INTERNAL METHODS
143141
142+ // Gets the next available sequence ID for signing when using confirmAndCheckUsingECRecover
143+ function getNextSequenceId () returns (uint ) {
144+ uint highestSequenceId = 0 ;
145+ for (var i = 0 ; i < c_maxSequenceIdWindowSize; i++ ) {
146+ if (m_sequenceIdsUsed[i] > highestSequenceId) {
147+ highestSequenceId = m_sequenceIdsUsed[i];
148+ }
149+ }
150+ return highestSequenceId + 1 ;
151+ }
152+
153+ // INTERNAL METHODS
154+ // Called within the onlymanyowners modifier.
155+ // Records a confirmation by msg.sender and returns true if the operation has the required number of confirmations
144156 function confirmAndCheck (bytes32 _operation ) internal returns (bool ) {
145- // determine what index the present sender is:
146- uint ownerIndex = m_ownerIndex[uint (msg .sender )];
147- // make sure they're an owner
148- if (ownerIndex == 0 ) return ;
157+ return confirmAndCheckOperationForOwner (_operation, msg .sender );
158+ }
159+
160+ // This operation will look for 2 confirmations
161+ // The first confirmation will be verified using ecrecover
162+ // The second confirmation will be verified using msg.sender
163+ function confirmWithSenderAndECRecover (bytes32 _operation , uint _sequenceId , bytes _signature ) internal returns (bool ) {
164+ // We expect confirmAndCheckUsingECRecover to run and return false, but mark the pending operation as having 1 confirm
165+ return confirmAndCheckUsingECRecover (_operation, _sequenceId, _signature) || confirmAndCheck (_operation);
166+ }
167+
168+ // Gets an owner using ecrecover, records their confirmation and
169+ // returns true if the operation has the required number of confirmations
170+ function confirmAndCheckUsingECRecover (bytes32 _operation , uint _sequenceId , bytes _signature ) internal returns (bool ) {
171+ // Verify that the sequence id has not been used before
172+ // Create mapping of the sequence ids being used
173+ uint lowestValueIndex = 0 ;
174+ for (var i = 0 ; i < c_maxSequenceIdWindowSize; i++ ) {
175+ if (m_sequenceIdsUsed[i] == _sequenceId) {
176+ // This sequence ID has been used before. Disallow!
177+ throw ;
178+ }
179+ if (m_sequenceIdsUsed[i] < m_sequenceIdsUsed[lowestValueIndex]) {
180+ lowestValueIndex = i;
181+ }
182+ }
183+ if (_sequenceId < m_sequenceIdsUsed[lowestValueIndex]) {
184+ // The sequence ID being used is lower than the lowest value in the window
185+ // so we cannot accept it as it may have been used before
186+ throw ;
187+ }
188+ m_sequenceIdsUsed[lowestValueIndex] = _sequenceId;
189+
190+ // We need to unpack the signature, which is given as an array of 65 bytes (from eth.sign)
191+ bytes32 r;
192+ bytes32 s;
193+ uint8 v;
194+
195+ if (_signature.length != 65 )
196+ throw ;
197+
198+ assembly {
199+ r := mload (add (_signature, 32 ))
200+ s := mload (add (_signature, 64 ))
201+ v := and (mload (add (_signature, 65 )), 255 )
202+ }
203+
204+ var ownerAddress = ecrecover (_operation, v, r, s);
205+ return confirmAndCheckOperationForOwner (_operation, ownerAddress);
206+ }
207+
208+ // Records confirmations for an operation by the given owner and
209+ // returns true if the operation has the required number of confirmations
210+ function confirmAndCheckOperationForOwner (bytes32 _operation , address _owner ) private returns (bool ) {
211+ // Determine what index the present sender is
212+ uint ownerIndex = m_ownerIndex[uint (_owner)];
213+ // Make sure they're an owner
214+ if (ownerIndex == 0 ) return false ;
149215
150216 var pending = m_pending[_operation];
151- // if we're not yet working on this operation, switch over and reset the confirmation status.
217+ // If we're not yet working on this operation, add it
152218 if (pending.yetNeeded == 0 ) {
153- // reset count of confirmations needed.
219+ // Reset count of confirmations needed.
154220 pending.yetNeeded = m_required;
155- // reset which owners have confirmed (none) - set our bitmap to 0.
221+ // Reset which owners have confirmed (none) - set our bitmap to 0.
156222 pending.ownersDone = 0 ;
157223 pending.index = m_pendingIndex.length ++ ;
158224 m_pendingIndex[pending.index] = _operation;
159225 }
160- // determine the bit to set for this owner.
226+
227+ // Determine the bit to set for this owner on the pending state for the operation
161228 uint ownerIndexBit = 2 ** ownerIndex;
162- // make sure we ( the message sender) haven't confirmed this operation previously.
229+ // Make sure the owner has not confirmed this operation previously.
163230 if (pending.ownersDone & ownerIndexBit == 0 ) {
164- Confirmation (msg . sender , _operation);
165- // ok - check if count is enough to go ahead .
231+ Confirmation (_owner , _operation);
232+ // Check if this confirmation puts us at the required number of needed confirmations .
166233 if (pending.yetNeeded <= 1 ) {
167- // enough confirmations: reset and run interior.
234+ // Enough confirmations: mark operation as passed and return true to continue execution
168235 delete m_pendingIndex[m_pending[_operation].index];
169236 delete m_pending[_operation];
170237 return true ;
@@ -176,6 +243,8 @@ contract multiowned {
176243 pending.ownersDone |= ownerIndexBit;
177244 }
178245 }
246+
247+ return false ;
179248 }
180249
181250 function reorganizeOwners () private {
@@ -216,6 +285,15 @@ contract multiowned {
216285 // the ongoing operations.
217286 mapping (bytes32 => PendingState) m_pending;
218287 bytes32 [] m_pendingIndex;
288+
289+ // When we use ecrecover to verify signatures (in addition to msg.sender), an array window of sequence ids is used.
290+ // This prevents from replay attacks by the first signer.
291+ //
292+ // Sequence IDs may not be repeated and should start from 1 onwards. Stores the last 10 largest sequence ids in a window
293+ // New sequence ids being added must replace the smallest of those numbers and must be larger than the smallest value stored.
294+ // This allows some degree of flexibility for submission of multiple transactions in a block.
295+ uint constant c_maxSequenceIdWindowSize = 10 ;
296+ uint [10 ] m_sequenceIdsUsed;
219297}
220298
221299// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
@@ -363,9 +441,38 @@ contract Wallet is multisig, multiowned, daylimit {
363441 return true ;
364442 }
365443 }
366-
444+
445+ // Execute and confirm a transaction with 2 signatures - one using the msg.sender and another using ecrecover
446+ // The signature is a signed form (using eth.sign) of tightly packed to, value, data, expiretime and sequenceId
447+ // Sequence IDs are numbers starting from 1. They used to prevent replay attacks and may not be repeated.
448+ function executeAndConfirm (address _to , uint _value , bytes _data , uint _expireTime , uint _sequenceId , bytes _signature )
449+ external onlyowner
450+ returns (bytes32 )
451+ {
452+ if (_expireTime < block .timestamp ) {
453+ throw ;
454+ }
455+
456+ // The unique hash is the combination of all arguments except the signature
457+ var operationHash = sha3 (_to, _value, _data, _expireTime, _sequenceId);
458+
459+ // Confirm the operation
460+ if (confirmWithSenderAndECRecover (operationHash, _sequenceId, _signature)) {
461+ if (! (_to.call.value (_value)(_data))) {
462+ throw ;
463+ }
464+ MultiTransact (msg .sender , operationHash, _value, _to, _data);
465+ return 0 ;
466+ }
467+
468+ m_txs[operationHash].to = _to;
469+ m_txs[operationHash].value = _value;
470+ m_txs[operationHash].data = _data;
471+ ConfirmationNeeded (operationHash, msg .sender , _value, _to, _data);
472+ return operationHash;
473+ }
474+
367475 // INTERNAL METHODS
368-
369476 function clearPending () internal {
370477 uint length = m_pendingIndex.length ;
371478 for (uint i = 0 ; i < length; ++ i)
0 commit comments