Skip to content

Commit f6743ac

Browse files
authored
Merge pull request #10 from dapperlabs/pivot-to-1271
Pivot to ERC-1271 from ERC-725
2 parents c64ff85 + f479374 commit f6743ac

File tree

9 files changed

+205
-266
lines changed

9 files changed

+205
-266
lines changed

Diff for: ABIs/ERC1271.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* eslint-disable */
2+
3+
module.exports = [
4+
{
5+
constant: true,
6+
inputs: [
7+
{
8+
name: 'hash',
9+
type: 'bytes32',
10+
},
11+
{
12+
name: '_signature',
13+
type: 'bytes',
14+
},
15+
],
16+
name: 'isValidSignature',
17+
outputs: [
18+
{
19+
name: 'magicValue',
20+
type: 'bytes4',
21+
},
22+
],
23+
payable: false,
24+
stateMutability: 'view',
25+
type: 'function',
26+
},
27+
];

Diff for: ABIs/ERC165.js

-23
This file was deleted.

Diff for: ABIs/ERC725Core.js

-27
This file was deleted.

Diff for: index.js

+8-42
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
const Web3 = require('web3');
22
const ethUtil = require('ethereumjs-util');
3-
const erc165 = require('./ABIs/ERC165');
4-
const erc725Core = require('./ABIs/ERC725Core');
3+
const ERC1271 = require('./ABIs/ERC1271');
54

6-
const ERC725_CORE_INTERFACE_ID = '0xd202158d';
7-
const ERC725_INTERFACE_ID = '0xdc3d2a7b';
8-
const ERC725_ACTION_PURPOSE = 2;
5+
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
6+
const ERC1271_MAGIC_VALUE = '0x1626ba7e';
97

108
module.exports = class DappAuth {
119
constructor(web3Provider) {
1210
this.web3 = new Web3(web3Provider);
1311
}
1412

15-
async isSignerActionableOnAddress(challenge, signature, address) {
13+
async isAuthorizedSigner(challenge, signature, address) {
1614
const challengeHash = ethUtil.hashPersonalMessage(
1715
ethUtil.toBuffer(challenge),
1816
);
@@ -31,44 +29,12 @@ module.exports = class DappAuth {
3129
}
3230

3331
// try smart-contract wallet
34-
const isSupportedContract = await this._isSupportedContract(address);
35-
if (!isSupportedContract) {
36-
return false;
37-
}
38-
return this._keyHasActionPurpose(address, recoveredKey);
39-
}
40-
41-
async _keyHasActionPurpose(contractAddr, key) {
42-
const erc725CoreContract = new this.web3.eth.Contract(
43-
erc725Core,
44-
contractAddr,
45-
);
46-
const keyHash = ethUtil.keccak(key);
47-
48-
return erc725CoreContract.methods
49-
.keyHasPurpose(keyHash, ERC725_ACTION_PURPOSE)
50-
.call();
51-
}
52-
53-
async _isSupportedContract(contractAddr) {
54-
const erc165Contract = new this.web3.eth.Contract(erc165, contractAddr);
32+
const erc1271CoreContract = new this.web3.eth.Contract(ERC1271, address);
5533

56-
const isSupportsERC725CoreInterface = await erc165Contract.methods
57-
.supportsInterface(ERC725_CORE_INTERFACE_ID)
34+
const magicValue = await erc1271CoreContract.methods
35+
.isValidSignature(ethUtil.keccak(challenge), signature) // we send just a regular hash, which then the smart contract hashes ontop to an erc191 hash
5836
.call();
5937

60-
if (isSupportsERC725CoreInterface) {
61-
return true;
62-
}
63-
64-
const isSupportsERC725Interface = await erc165Contract.methods
65-
.supportsInterface(ERC725_INTERFACE_ID)
66-
.call();
67-
68-
if (isSupportsERC725Interface) {
69-
return true;
70-
}
71-
72-
return false;
38+
return magicValue === ERC1271_MAGIC_VALUE;
7339
}
7440
};

Diff for: package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
"license": "MIT",
2121
"dependencies": {
2222
"ethereumjs-util": "^6.0.0",
23-
"web3": "^1.0.0-beta.36"
23+
"web3": "^1.0.0-beta.36",
24+
"ethereumjs-abi": "^0.6.6",
25+
"safe-buffer": "^5.1.2"
2426
},
2527
"devDependencies": {
2628
"babel-eslint": "^8.0.1",

Diff for: test/contract-mock.js

+33-35
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,61 @@
11
const ethUtil = require('ethereumjs-util');
2+
const ethAbi = require('ethereumjs-abi');
3+
const Buffer = require('safe-buffer').Buffer;
4+
const utils = require('./utils');
25

3-
const erc725CoreInterfaceID = '0xd202158d';
4-
const erc725InterfaceID = '0xdc3d2a7b';
6+
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
7+
const ERC1271_METHOD_SIG = '1626ba7e';
58

69
module.exports = class MockContract {
710
constructor(options) {
8-
this.isSupportsERC725CoreInterface = options.isSupportsERC725CoreInterface;
9-
this.isSupportsERC725Interface = options.isSupportsERC725Interface;
10-
this.actionableKey = options.actionableKey;
11-
this.errorOnIsSupportedContract = options.errorOnIsSupportedContract;
12-
this.errorOnKeyHasPurpose = options.errorOnKeyHasPurpose;
11+
this.authorizedKey = options.authorizedKey;
12+
this.address = options.address;
13+
this.errorIsValidSignature = options.errorIsValidSignature;
1314
}
1415

1516
static _true() {
16-
return '0x0000000000000000000000000000000000000000000000000000000000000001';
17+
return `0x${ERC1271_METHOD_SIG}00000000000000000000000000000000000000000000000000000000`; // a.k.a the "magic value".
1718
}
1819

1920
static _false(callback) {
2021
return '0x0000000000000000000000000000000000000000000000000000000000000000';
2122
}
2223

24+
// @param {String} methodCall
25+
// @param {String} methodParams
26+
// @return {String}
2327
run(methodCall, methodParams) {
2428
switch (methodCall) {
25-
case '01ffc9a7':
26-
return this._01ffc9a7(`0x${methodParams.substring(0, 4 * 2)}`);
27-
case 'd202158d':
28-
return this._d202158d(`0x${methodParams.substring(0, 32 * 2)}`);
29+
case ERC1271_METHOD_SIG:
30+
const [hash, signature] = ethAbi.rawDecode(
31+
['bytes32', 'bytes'],
32+
Buffer.from(methodParams, 'hex'),
33+
);
34+
35+
return this._1626ba7e(hash, signature);
2936
default:
3037
throw new Error(`Unexpected method ${methodCall}`);
3138
}
3239
}
3340

34-
// "isSupportedContract" method call
35-
_01ffc9a7(interfaceID) {
36-
if (this.errorOnIsSupportedContract) {
37-
throw new Error('isSupportedContract call returned an error');
38-
}
39-
40-
if (
41-
this.isSupportsERC725CoreInterface &&
42-
interfaceID === erc725CoreInterfaceID
43-
) {
44-
return MockContract._true();
45-
}
46-
47-
if (this.isSupportsERC725Interface && interfaceID === erc725InterfaceID) {
48-
return MockContract._true();
41+
// "isValidSignature" method call
42+
// @param {Buffer} hash
43+
// @param {Buffer} signature
44+
// @return {String}
45+
_1626ba7e(hash, signature) {
46+
if (this.errorIsValidSignature) {
47+
throw new Error('isValidSignature call returned an error');
4948
}
5049

51-
return MockContract._false();
52-
}
50+
// Get the address of whoever signed this message
51+
const { v, r, s } = ethUtil.fromRpcSig(signature);
52+
const erc191MessageHash = utils.erc191MessageHash(hash, this.address);
53+
const recoveredKey = ethUtil.ecrecover(erc191MessageHash, v, r, s);
54+
const recoveredAddress = ethUtil.publicToAddress(recoveredKey);
5355

54-
// "keyHasPurpose" method call
55-
_d202158d(key) {
56-
if (this.errorOnKeyHasPurpose) {
57-
throw new Error('keyHasPurpose call returned an error');
58-
}
56+
const expectedAddress = ethUtil.publicToAddress(this.authorizedKey);
5957

60-
if (key === ethUtil.bufferToHex(ethUtil.keccak(this.actionableKey))) {
58+
if (recoveredAddress.toString() === expectedAddress.toString()) {
6159
return MockContract._true();
6260
}
6361

0 commit comments

Comments
 (0)