-
Notifications
You must be signed in to change notification settings - Fork 45
/
Copy pathaccount.js
76 lines (67 loc) · 2.56 KB
/
account.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
const Bytes = require("./bytes");
const Nat = require("./nat");
const elliptic = require("elliptic");
const rlp = require("./rlp");
const secp256k1 = new (elliptic.ec)("secp256k1"); // eslint-disable-line
const {keccak256, keccak256s} = require("./hash");
const create = entropy => {
const innerHex = keccak256(Bytes.concat(Bytes.random(32), entropy || Bytes.random(32)));
const middleHex = Bytes.concat(Bytes.concat(Bytes.random(32), innerHex), Bytes.random(32));
const outerHex = keccak256(middleHex);
return fromPrivate(outerHex);
}
const toChecksum = address => {
const addressHash = keccak256s(address.slice(2));
let checksumAddress = "0x";
for (let i = 0; i < 40; i++)
checksumAddress += parseInt(addressHash[i + 2], 16) > 7
? address[i + 2].toUpperCase()
: address[i + 2];
return checksumAddress;
}
const fromPrivate = privateKey => {
const buffer = new Buffer(privateKey.slice(2), "hex");
const ecKey = secp256k1.keyFromPrivate(buffer);
const publicKey = "0x" + ecKey.getPublic(false, 'hex').slice(2);
const publicHash = keccak256(publicKey);
const address = toChecksum("0x" + publicHash.slice(-40));
return {
address: address,
privateKey: privateKey
}
}
const encodeSignature = ([v, r, s]) =>
Bytes.flatten([r,s,v]);
const decodeSignature = (hex) => [
Bytes.slice(64, Bytes.length(hex), hex),
Bytes.slice(0, 32, hex),
Bytes.slice(32, 64, hex)];
const makeSigner = addToV => (hash, privateKey) => {
const signature = secp256k1
.keyFromPrivate(new Buffer(privateKey.slice(2), "hex"))
.sign(new Buffer(hash.slice(2), "hex"), {canonical: true});
return encodeSignature([
Nat.fromString(Bytes.fromNumber(addToV + signature.recoveryParam)),
Bytes.pad(32, Bytes.fromNat("0x" + signature.r.toString(16))),
Bytes.pad(32, Bytes.fromNat("0x" + signature.s.toString(16)))]);
}
const sign = makeSigner(27); // v=27|28 instead of 0|1...
const recover = (hash, signature) => {
const vals = decodeSignature(signature);
const vrs = {v: Bytes.toNumber(vals[0]), r:vals[1].slice(2), s:vals[2].slice(2)};
const ecPublicKey = secp256k1.recoverPubKey(new Buffer(hash.slice(2), "hex"), vrs, vrs.v < 2 ? vrs.v : 1 - (vrs.v % 2)); // because odd vals mean v=0... sadly that means v=0 means v=1... I hate that
const publicKey = "0x" + ecPublicKey.encode("hex", false).slice(2);
const publicHash = keccak256(publicKey);
const address = toChecksum("0x" + publicHash.slice(-40));
return address;
}
module.exports = {
create,
toChecksum,
fromPrivate,
sign,
makeSigner,
recover,
encodeSignature,
decodeSignature
}