Skip to content

Commit cda6877

Browse files
committed
experimental
1 parent e44f235 commit cda6877

File tree

3 files changed

+699
-3
lines changed

3 files changed

+699
-3
lines changed

contracts/account/Signer.sol

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,22 @@ pragma solidity >=0.5.0 <0.6.0;
66
contract Signer {
77

88
//bytes4(keccak256("isValidSignature(bytes,bytes)")
9-
bytes4 constant internal MAGICVALUE = 0x20c13b0b;
9+
bytes4 constant internal ERC1271_TRUE = 0x20c13b0b;
10+
bytes4 constant internal ERC1271_FALSE = 0xffffffff;
11+
12+
// Allowed signature types.
13+
enum SignatureType {
14+
Illegal, // 0x00, default value
15+
Invalid, // 0x01
16+
EIP712, // 0x02
17+
EthSign, // 0x03
18+
Caller, // 0x04
19+
Wallet, // 0x05
20+
Validator, // 0x06
21+
PreSigned, // 0x07
22+
Trezor, // 0x08
23+
NSignatureTypes // 0x09, number of signature types. Always leave at end.
24+
}
1025

1126
/**
1227
* @dev Should return whether the signature provided is valid for the provided data
@@ -24,4 +39,22 @@ contract Signer {
2439
public
2540
view
2641
returns (bytes4 magicValue);
27-
}
42+
43+
/**
44+
* @dev Verifies that a hash has been signed by the given signer.
45+
* @param hash Any 32 byte hash.
46+
* @param signerAddress Address that should have signed the given hash.
47+
* @param signature Proof that the hash has been signed by signer.
48+
* @return True if the address recovered from the provided signature matches the input signer address
49+
*/
50+
function isValidSignature(
51+
bytes32 hash,
52+
address signerAddress,
53+
bytes memory signature
54+
)
55+
public
56+
view
57+
returns (bool isValid);
58+
59+
60+
}

contracts/account/UserAccount.sol

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ pragma solidity >=0.5.0 <0.6.0;
33
import "./UserAccountInterface.sol";
44
import "./AccountGasAbstract.sol";
55
import "../cryptography/ECDSA.sol";
6+
import "../common/LibBytes.sol";
67

78
/**
89
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
910
* @notice Defines an account which can be setup by a owner address (multisig contract), recovered by a recover address (a sort of secret multisig contract), and execute actions from a list of addresses (authorized contracts, extensions, etc)
1011
*/
1112
contract UserAccount is UserAccountInterface, AccountGasAbstract {
13+
using LibBytes for bytes
14+
;
1215
string internal constant ERR_BAD_PARAMETER = "Bad parameter";
1316
string internal constant ERR_UNAUTHORIZED = "Unauthorized";
1417
string internal constant ERR_CREATE_FAILED = "Contract creation failed";
@@ -239,7 +242,107 @@ contract UserAccount is UserAccountInterface, AccountGasAbstract {
239242
if(isContract(owner)){
240243
return Signer(owner).isValidSignature(_data, _signature);
241244
} else {
242-
return owner == ECDSA.recover(ECDSA.toERC191SignedMessage(_data), _signature) ? MAGICVALUE : bytes4(0xffffffff);
245+
return owner == ECDSA.recover(ECDSA.toERC191SignedMessage(_data), _signature) ? ERC1271_TRUE : ERC1271_FALSE;
243246
}
244247
}
248+
249+
function isValidSignature(
250+
bytes32 hash,
251+
address signerAddress,
252+
bytes memory signature
253+
)
254+
public
255+
view
256+
returns (bool isValid)
257+
{
258+
require(
259+
signature.length > 0,
260+
"LENGTH_GREATER_THAN_0_REQUIRED"
261+
);
262+
263+
// Ensure signature is supported
264+
uint8 signatureTypeRaw = uint8(signature.popLastByte());
265+
require(
266+
signatureTypeRaw < uint8(SignatureType.NSignatureTypes),
267+
"SIGNATURE_UNSUPPORTED"
268+
);
269+
270+
// Pop last byte off of signature byte array.
271+
SignatureType signatureType = SignatureType(signatureTypeRaw);
272+
273+
// Variables are not scoped in Solidity.
274+
uint8 v;
275+
bytes32 r;
276+
bytes32 s;
277+
address recovered;
278+
279+
if (signatureType == SignatureType.Illegal) {
280+
revert("SIGNATURE_ILLEGAL");
281+
} else if (signatureType == SignatureType.Invalid) {
282+
require(
283+
signature.length == 0,
284+
"LENGTH_0_REQUIRED"
285+
);
286+
isValid = false;
287+
return isValid;
288+
} else if (signatureType == SignatureType.Validator) {
289+
address validatorAddress = signature.popLast20Bytes();
290+
if(validatorAddress == owner || validatorAddress == actor) {
291+
return false;
292+
}
293+
isValid = Signer(validatorAddress).isValidSignature(
294+
hash,
295+
signerAddress,
296+
signature
297+
);
298+
return isValid;
299+
}
300+
301+
if(signerAddress != owner && signerAddress != actor) {
302+
return false;
303+
}
304+
305+
if (signatureType == SignatureType.EIP712) {
306+
require(
307+
signature.length == 65,
308+
"LENGTH_65_REQUIRED"
309+
);
310+
v = uint8(signature[0]);
311+
r = signature.readBytes32(1);
312+
s = signature.readBytes32(33);
313+
recovered = ecrecover(hash, v, r, s);
314+
isValid = signerAddress == recovered;
315+
return isValid;
316+
} else if (signatureType == SignatureType.EthSign || signatureType == SignatureType.Trezor) {
317+
require(
318+
signature.length == 65,
319+
"LENGTH_65_REQUIRED"
320+
);
321+
v = uint8(signature[0]);
322+
r = signature.readBytes32(1);
323+
s = signature.readBytes32(33);
324+
recovered = ecrecover(
325+
ECDSA.toEthSignedMessageHash(hash),
326+
v,
327+
r,
328+
s
329+
);
330+
isValid = signerAddress == recovered;
331+
return isValid;
332+
333+
} else if (signatureType == SignatureType.Caller) {
334+
require(
335+
signature.length == 0,
336+
"LENGTH_0_REQUIRED"
337+
);
338+
isValid = signerAddress == msg.sender;
339+
340+
return isValid;
341+
} else if (signatureType == SignatureType.Wallet) {
342+
isValid = Signer(signerAddress).isValidSignature(abi.encode(hash), signature);
343+
return isValid;
344+
}
345+
revert("SIGNATURE_UNSUPPORTED");
346+
}
347+
245348
}

0 commit comments

Comments
 (0)