@@ -138,33 +138,100 @@ contract multiowned {
138
138
uint ownerIndexBit = 2 ** ownerIndex;
139
139
return ! (pending.ownersDone & ownerIndexBit == 0 );
140
140
}
141
-
142
- // INTERNAL METHODS
143
141
142
+ // Gets the next available sequence ID for signing when using confirmAndCheckUsingECRecover
143
+ function getNextSequenceId () constant returns (uint ) {
144
+ uint highestSequenceId = 0 ;
145
+ for (uint 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
144
156
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 (uint 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 ;
149
215
150
216
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
152
218
if (pending.yetNeeded == 0 ) {
153
- // reset count of confirmations needed.
219
+ // Reset count of confirmations needed.
154
220
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.
156
222
pending.ownersDone = 0 ;
157
223
pending.index = m_pendingIndex.length ++ ;
158
224
m_pendingIndex[pending.index] = _operation;
159
225
}
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
161
228
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.
163
230
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 .
166
233
if (pending.yetNeeded <= 1 ) {
167
- // enough confirmations: reset and run interior.
234
+ // Enough confirmations: mark operation as passed and return true to continue execution
168
235
delete m_pendingIndex[m_pending[_operation].index];
169
236
delete m_pending[_operation];
170
237
return true ;
@@ -176,6 +243,8 @@ contract multiowned {
176
243
pending.ownersDone |= ownerIndexBit;
177
244
}
178
245
}
246
+
247
+ return false ;
179
248
}
180
249
181
250
function reorganizeOwners () private {
@@ -216,6 +285,15 @@ contract multiowned {
216
285
// the ongoing operations.
217
286
mapping (bytes32 => PendingState) m_pending;
218
287
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;
219
297
}
220
298
221
299
// 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 {
363
441
return true ;
364
442
}
365
443
}
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
+
367
475
// INTERNAL METHODS
368
-
369
476
function clearPending () internal {
370
477
uint length = m_pendingIndex.length ;
371
478
for (uint i = 0 ; i < length; ++ i)
0 commit comments