@com-chain/jsc3l 2.0.1-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +45 -0
- package/build/bcRead.d.ts +15 -0
- package/build/bcRead.js +123 -0
- package/build/bcRead.js.map +1 -0
- package/build/bcTransaction.d.ts +10 -0
- package/build/bcTransaction.js +135 -0
- package/build/bcTransaction.js.map +1 -0
- package/build/blockies.d.ts +1 -0
- package/build/blockies.js +91 -0
- package/build/blockies.js.map +1 -0
- package/build/config.d.ts +6 -0
- package/build/config.js +25 -0
- package/build/config.js.map +1 -0
- package/build/connection.d.ts +58 -0
- package/build/connection.js +204 -0
- package/build/connection.js.map +1 -0
- package/build/customization.d.ts +37 -0
- package/build/customization.js +129 -0
- package/build/customization.js.map +1 -0
- package/build/ethereum/cipher.d.ts +3 -0
- package/build/ethereum/cipher.js +94 -0
- package/build/ethereum/cipher.js.map +1 -0
- package/build/ethereum/ethFuncs.d.ts +12 -0
- package/build/ethereum/ethFuncs.js +70 -0
- package/build/ethereum/ethFuncs.js.map +1 -0
- package/build/ethereum/etherUnits.d.ts +5 -0
- package/build/ethereum/etherUnits.js +60 -0
- package/build/ethereum/etherUnits.js.map +1 -0
- package/build/ethereum/myetherwallet.d.ts +47 -0
- package/build/ethereum/myetherwallet.js +294 -0
- package/build/ethereum/myetherwallet.js.map +1 -0
- package/build/ethereum/uiFuncs.d.ts +3 -0
- package/build/ethereum/uiFuncs.js +51 -0
- package/build/ethereum/uiFuncs.js.map +1 -0
- package/build/index.d.ts +111 -0
- package/build/index.js +310 -0
- package/build/index.js.map +1 -0
- package/build/memo.d.ts +6 -0
- package/build/memo.js +24 -0
- package/build/memo.js.map +1 -0
- package/build/qr.d.ts +8 -0
- package/build/qr.js +35 -0
- package/build/qr.js.map +1 -0
- package/build/rest/ajaxReq.d.ts +31 -0
- package/build/rest/ajaxReq.js +127 -0
- package/build/rest/ajaxReq.js.map +1 -0
- package/build/rest/endpoint.d.ts +18 -0
- package/build/rest/endpoint.js +26 -0
- package/build/rest/endpoint.js.map +1 -0
- package/build/rest/http.d.ts +10 -0
- package/build/rest/http.js +60 -0
- package/build/rest/http.js.map +1 -0
- package/build/rest/serializer.d.ts +1 -0
- package/build/rest/serializer.js +94 -0
- package/build/rest/serializer.js.map +1 -0
- package/build/type.d.ts +24 -0
- package/build/type.js +9 -0
- package/build/type.js.map +1 -0
- package/build/wallet.d.ts +35 -0
- package/build/wallet.js +162 -0
- package/build/wallet.js.map +1 -0
- package/package.json +41 -0
- package/src/bcRead.ts +184 -0
- package/src/bcTransaction.ts +157 -0
- package/src/blockies.ts +113 -0
- package/src/config.ts +33 -0
- package/src/connection.ts +243 -0
- package/src/customization.ts +156 -0
- package/src/ethereum/cipher.ts +118 -0
- package/src/ethereum/ethFuncs.ts +73 -0
- package/src/ethereum/etherUnits.ts +66 -0
- package/src/ethereum/myetherwallet.ts +354 -0
- package/src/ethereum/uiFuncs.ts +55 -0
- package/src/index.ts +366 -0
- package/src/memo.ts +37 -0
- package/src/qr.ts +34 -0
- package/src/rest/ajaxReq.ts +160 -0
- package/src/rest/endpoint.ts +31 -0
- package/src/rest/http.ts +69 -0
- package/src/rest/serializer.ts +99 -0
- package/src/type.ts +37 -0
- package/src/wallet.ts +200 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import crypto from 'crypto'
|
|
2
|
+
import { ec as EC } from 'elliptic'
|
|
3
|
+
|
|
4
|
+
/// Code adapted from https://github.com/LimelabsTech/eth-ecies
|
|
5
|
+
|
|
6
|
+
const ec = new EC('secp256k1')
|
|
7
|
+
|
|
8
|
+
function AES256CbcEncrypt (iv, key, plaintext) {
|
|
9
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv)
|
|
10
|
+
const firstChunk = cipher.update(plaintext)
|
|
11
|
+
const secondChunk = cipher.final()
|
|
12
|
+
|
|
13
|
+
return Buffer.concat([firstChunk, secondChunk])
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function AES256CbcDecrypt (iv, key, ciphertext) {
|
|
17
|
+
const cipher = crypto.createDecipheriv('aes-256-cbc', key, iv)
|
|
18
|
+
const firstChunk = cipher.update(ciphertext)
|
|
19
|
+
const secondChunk = cipher.final()
|
|
20
|
+
|
|
21
|
+
return Buffer.concat([firstChunk, secondChunk])
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function BufferEqual (b1, b2) {
|
|
25
|
+
if (b1.length !== b2.length) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let res = 0
|
|
30
|
+
for (let i = 0; i < b1.length; i++) {
|
|
31
|
+
res |= b1[i] ^ b2[i]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return res === 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Encrypt (publicKey, plaintext) {
|
|
38
|
+
/* DEBUG */
|
|
39
|
+
|
|
40
|
+
const pubKeyTo = Buffer.from(publicKey)
|
|
41
|
+
const ephemPrivKey = ec.keyFromPrivate(crypto.randomBytes(32))
|
|
42
|
+
const ephemPubKey = ephemPrivKey.getPublic()
|
|
43
|
+
const ephemPubKeyEncoded = Buffer.from(ephemPubKey.encode())
|
|
44
|
+
|
|
45
|
+
// Every EC public key begins with the 0x04 prefix before giving
|
|
46
|
+
// the location of the two point on the curve
|
|
47
|
+
const concatenated = Buffer.concat([Buffer.from([0x04]), pubKeyTo])
|
|
48
|
+
const keys = ec.keyFromPublic(concatenated)
|
|
49
|
+
const pub = keys.getPublic()
|
|
50
|
+
const px = ephemPrivKey.derive(pub)
|
|
51
|
+
const hash = crypto.createHash('sha512')
|
|
52
|
+
.update(Buffer.from(px.toArray())).digest()
|
|
53
|
+
const iv = crypto.randomBytes(16)
|
|
54
|
+
const encryptionKey = hash.slice(0, 32)
|
|
55
|
+
const macKey = hash.slice(32)
|
|
56
|
+
const ciphertext = AES256CbcEncrypt(iv, encryptionKey, plaintext)
|
|
57
|
+
const dataToMac = Buffer.concat([iv, ephemPubKeyEncoded, ciphertext])
|
|
58
|
+
const mac = crypto.createHmac('sha256', macKey).update(dataToMac).digest()
|
|
59
|
+
|
|
60
|
+
const serializedCiphertext = Buffer.concat([
|
|
61
|
+
iv, // 16 bytes
|
|
62
|
+
ephemPubKeyEncoded, // 65 bytes
|
|
63
|
+
mac, // 32 bytes
|
|
64
|
+
ciphertext
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
return serializedCiphertext.toString('hex')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function Decrypt (privateKey, encrypted) {
|
|
71
|
+
const encryptedBuff = Buffer.from(encrypted, 'hex')
|
|
72
|
+
const privateKeyBuff = Buffer.from(privateKey)
|
|
73
|
+
|
|
74
|
+
// Read iv, ephemPubKey, mac, ciphertext from encrypted message
|
|
75
|
+
|
|
76
|
+
const iv = encryptedBuff.slice(0, 16)
|
|
77
|
+
const ephemPubKeyEncoded = encryptedBuff.slice(16, 81)
|
|
78
|
+
const mac = encryptedBuff.slice(81, 113)
|
|
79
|
+
const ciphertext = encryptedBuff.slice(113)
|
|
80
|
+
const ephemPubKey = ec.keyFromPublic(ephemPubKeyEncoded).getPublic()
|
|
81
|
+
|
|
82
|
+
const px = ec.keyFromPrivate(privateKeyBuff).derive(ephemPubKey)
|
|
83
|
+
const hash = crypto.createHash('sha512')
|
|
84
|
+
.update(Buffer.from(px.toArray())).digest()
|
|
85
|
+
const encryptionKey = hash.slice(0, 32)
|
|
86
|
+
const macKey = hash.slice(32)
|
|
87
|
+
const dataToMac = Buffer.concat([iv, ephemPubKeyEncoded, ciphertext])
|
|
88
|
+
const computedMac = crypto.createHmac('sha256', macKey)
|
|
89
|
+
.update(dataToMac).digest()
|
|
90
|
+
|
|
91
|
+
// Verify mac
|
|
92
|
+
if (!BufferEqual(computedMac, mac)) {
|
|
93
|
+
throw new Error('MAC mismatch')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const plaintext = AES256CbcDecrypt(iv, encryptionKey, ciphertext)
|
|
97
|
+
|
|
98
|
+
return plaintext.toString()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
export function shortenAddress (address) {
|
|
103
|
+
if (address.toLowerCase().substring(0, 2) === '0x') {
|
|
104
|
+
address = address.substr(2)
|
|
105
|
+
}
|
|
106
|
+
return address
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function cipherMsg (publicKey, message) {
|
|
110
|
+
const msgBuff = Buffer.from(message)
|
|
111
|
+
const key = Buffer.from(shortenAddress(publicKey), 'hex')
|
|
112
|
+
return Encrypt(key, msgBuff)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function decipherMsg (privateKey, ciphered) {
|
|
116
|
+
const key = Buffer.from(shortenAddress(privateKey), 'hex')
|
|
117
|
+
return Decrypt(key, ciphered)
|
|
118
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js'
|
|
2
|
+
import ethUtil from 'ethereumjs-util'
|
|
3
|
+
|
|
4
|
+
import * as etherUnits from './etherUnits'
|
|
5
|
+
|
|
6
|
+
function isChecksumAddress (address) {
|
|
7
|
+
return address === ethUtil.toChecksumAddress(address)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function validateEtherAddress (address) {
|
|
11
|
+
if (address.substring(0, 2) !== '0x') return false
|
|
12
|
+
else if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) return false
|
|
13
|
+
else if (/^(0x)?[0-9a-f]{40}$/.test(address) ||
|
|
14
|
+
/^(0x)?[0-9A-F]{40}$/.test(address)) return true
|
|
15
|
+
else { return isChecksumAddress(address) }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function validateHexString (str) {
|
|
19
|
+
if (str === '') return true
|
|
20
|
+
str = str.substring(0, 2) === '0x'
|
|
21
|
+
? str.substring(2).toUpperCase()
|
|
22
|
+
: str.toUpperCase()
|
|
23
|
+
const re = /^[0-9A-F]+$/g
|
|
24
|
+
return re.test(str)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function sanitizeHex (hex) {
|
|
28
|
+
hex = hex.substring(0, 2) === '0x' ? hex.substring(2) : hex
|
|
29
|
+
if (hex === '') return ''
|
|
30
|
+
return '0x' + padLeftEven(hex)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function padLeftEven (hex) {
|
|
34
|
+
hex = hex.length % 2 !== 0 ? '0' + hex : hex
|
|
35
|
+
return hex
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function addTinyMoreToGas (hex) {
|
|
39
|
+
hex = this.sanitizeHex(hex)
|
|
40
|
+
return new BigNumber(hex).plus(etherUnits.getValueOfUnit('gwei'))
|
|
41
|
+
.toDigits(2).toString(16)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function decimalToHex (dec) {
|
|
45
|
+
return new BigNumber(dec).toString(16)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getNakedAddress (address) {
|
|
49
|
+
return address.toLowerCase().replace('0x', '')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function padLeft (n, width, z?) {
|
|
53
|
+
z = z || '0'
|
|
54
|
+
n = n + ''
|
|
55
|
+
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getDataObj (to, func, arrVals) {
|
|
59
|
+
let val = ''
|
|
60
|
+
for (let i = 0; i < arrVals.length; i++) val += padLeft(arrVals[i], 64)
|
|
61
|
+
return { to: to, data: func + val }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function encodeNumber (number) {
|
|
65
|
+
let valueHex
|
|
66
|
+
if (number < 0) {
|
|
67
|
+
valueHex = padLeft(new BigNumber(16).pow(64).plus(number).toString(16), 64)
|
|
68
|
+
} else {
|
|
69
|
+
valueHex = padLeft(new BigNumber(number).toString(16), 64)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return valueHex
|
|
73
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js'
|
|
2
|
+
|
|
3
|
+
const unitMap = {
|
|
4
|
+
wei: '1',
|
|
5
|
+
kwei: '1000',
|
|
6
|
+
ada: '1000',
|
|
7
|
+
femtoether: '1000',
|
|
8
|
+
mwei: '1000000',
|
|
9
|
+
babbage: '1000000',
|
|
10
|
+
picoether: '1000000',
|
|
11
|
+
gwei: '1000000000',
|
|
12
|
+
shannon: '1000000000',
|
|
13
|
+
nanoether: '1000000000',
|
|
14
|
+
nano: '1000000000',
|
|
15
|
+
szabo: '1000000000000',
|
|
16
|
+
microether: '1000000000000',
|
|
17
|
+
micro: '1000000000000',
|
|
18
|
+
finney: '1000000000000000',
|
|
19
|
+
milliether: '1000000000000000',
|
|
20
|
+
milli: '1000000000000000',
|
|
21
|
+
ether: '1000000000000000000',
|
|
22
|
+
kether: '1000000000000000000000',
|
|
23
|
+
grand: '1000000000000000000000',
|
|
24
|
+
einstein: '1000000000000000000000',
|
|
25
|
+
mether: '1000000000000000000000000',
|
|
26
|
+
gether: '1000000000000000000000000000',
|
|
27
|
+
tether: '1000000000000000000000000000000'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getValueOfUnit (unit) {
|
|
31
|
+
unit = unit ? unit.toLowerCase() : 'ether'
|
|
32
|
+
const unitValue = unitMap[unit]
|
|
33
|
+
if (unitValue === undefined) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"This unit doesn't exists, please use the " +
|
|
36
|
+
'one of the following units ' + JSON.stringify(unitMap, null, 2))
|
|
37
|
+
}
|
|
38
|
+
return new BigNumber(unitValue, 10)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function fiatToWei (number, pricePerEther) {
|
|
42
|
+
return new BigNumber(String(number))
|
|
43
|
+
.div(pricePerEther)
|
|
44
|
+
.times(this.getValueOfUnit('ether'))
|
|
45
|
+
.round(0)
|
|
46
|
+
.toString(10)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function toFiat (number, unit, multi) {
|
|
50
|
+
return new BigNumber(this.toEther(number, unit))
|
|
51
|
+
.times(multi)
|
|
52
|
+
.round(5)
|
|
53
|
+
.toString(10)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function toEther (number, unit) {
|
|
57
|
+
return new BigNumber(this.toWei(number, unit))
|
|
58
|
+
.div(this.getValueOfUnit('ether'))
|
|
59
|
+
.toString(10)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function toWei (number, unit) {
|
|
63
|
+
return new BigNumber(String(number))
|
|
64
|
+
.times(this.getValueOfUnit(unit))
|
|
65
|
+
.toString(10)
|
|
66
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import crypto from 'crypto'
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
3
|
+
import ethUtil from 'ethereumjs-util'
|
|
4
|
+
import scrypt from 'scryptsy'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
function decipherBuffer (decipher, data) {
|
|
8
|
+
return Buffer.concat([decipher.update(data), decipher.final()])
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
function decodeCryptojsSalt (input) {
|
|
13
|
+
const ciphertext = Buffer.from(input, 'base64')
|
|
14
|
+
if (ciphertext.slice(0, 8).toString() === 'Salted__') {
|
|
15
|
+
return {
|
|
16
|
+
salt: ciphertext.slice(8, 16),
|
|
17
|
+
ciphertext: ciphertext.slice(16)
|
|
18
|
+
}
|
|
19
|
+
} else {
|
|
20
|
+
return {
|
|
21
|
+
ciphertext: ciphertext
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function evpKdf (data, salt, opts) {
|
|
27
|
+
// A single EVP iteration, returns `D_i`, where block equlas to `D_(i-1)`
|
|
28
|
+
|
|
29
|
+
function iter (block) {
|
|
30
|
+
let hash = crypto.createHash(opts.digest || 'md5')
|
|
31
|
+
hash.update(block)
|
|
32
|
+
hash.update(data)
|
|
33
|
+
hash.update(salt)
|
|
34
|
+
block = hash.digest()
|
|
35
|
+
for (let i = 1; i < (opts.count || 1); i++) {
|
|
36
|
+
hash = crypto.createHash(opts.digest || 'md5')
|
|
37
|
+
hash.update(block)
|
|
38
|
+
block = hash.digest()
|
|
39
|
+
}
|
|
40
|
+
return block
|
|
41
|
+
}
|
|
42
|
+
const keysize = opts.keysize || 16
|
|
43
|
+
const ivsize = opts.ivsize || 16
|
|
44
|
+
const ret = []
|
|
45
|
+
let i = 0
|
|
46
|
+
while (Buffer.concat(ret).length < (keysize + ivsize)) {
|
|
47
|
+
ret[i] = iter((i === 0) ? Buffer.alloc(0) : ret[i - 1])
|
|
48
|
+
i++
|
|
49
|
+
}
|
|
50
|
+
const tmp = Buffer.concat(ret)
|
|
51
|
+
return {
|
|
52
|
+
key: tmp.slice(0, keysize),
|
|
53
|
+
iv: tmp.slice(keysize, keysize + ivsize)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
export default class Wallet {
|
|
60
|
+
privKey
|
|
61
|
+
|
|
62
|
+
constructor (priv?) {
|
|
63
|
+
if (!priv) {
|
|
64
|
+
priv = crypto.randomBytes(32)
|
|
65
|
+
}
|
|
66
|
+
this.privKey = priv.length === 32 ? priv : Buffer.from(priv, 'hex')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static generate (icapDirect) {
|
|
70
|
+
if (!icapDirect) {
|
|
71
|
+
return new this()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
while (true) {
|
|
75
|
+
const privKey = crypto.randomBytes(32)
|
|
76
|
+
if (ethUtil.privateToAddress(privKey)[0] === 0) {
|
|
77
|
+
return new this(privKey)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static fromPrivateKey (priv) {
|
|
83
|
+
return new this(priv)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getPrivateKey () { return this.privKey }
|
|
87
|
+
getPrivateKeyString () { return this.getPrivateKey().toString('hex') }
|
|
88
|
+
getPublicKey () { return ethUtil.privateToPublic(this.privKey) }
|
|
89
|
+
getPublicKeyString () { return '0x' + this.getPublicKey().toString('hex') }
|
|
90
|
+
getAddress () { return ethUtil.privateToAddress(this.privKey) }
|
|
91
|
+
getAddressString () { return '0x' + this.getAddress().toString('hex') }
|
|
92
|
+
getChecksumAddressString () {
|
|
93
|
+
return ethUtil.toChecksumAddress(this.getAddressString())
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
toV3 (password, opts) {
|
|
97
|
+
opts = opts || {}
|
|
98
|
+
const salt = opts.salt || crypto.randomBytes(32)
|
|
99
|
+
const iv = opts.iv || crypto.randomBytes(16)
|
|
100
|
+
let derivedKey
|
|
101
|
+
const kdf = opts.kdf || 'scrypt'
|
|
102
|
+
const kdfparams: {[k: string]: any} = {
|
|
103
|
+
dklen: opts.dklen || 32,
|
|
104
|
+
salt: salt.toString('hex')
|
|
105
|
+
}
|
|
106
|
+
if (kdf === 'pbkdf2') {
|
|
107
|
+
kdfparams.c = opts.c || 262144
|
|
108
|
+
kdfparams.prf = 'hmac-sha256'
|
|
109
|
+
derivedKey = crypto.pbkdf2Sync(
|
|
110
|
+
Buffer.from(password), salt, kdfparams.c, kdfparams.dklen, 'sha256')
|
|
111
|
+
} else if (kdf === 'scrypt') {
|
|
112
|
+
// FIXME: support progress reporting callback
|
|
113
|
+
kdfparams.n = opts.n || 262144
|
|
114
|
+
kdfparams.r = opts.r || 8
|
|
115
|
+
kdfparams.p = opts.p || 1
|
|
116
|
+
derivedKey = scrypt(
|
|
117
|
+
Buffer.from(password), salt, kdfparams.n, kdfparams.r,
|
|
118
|
+
kdfparams.p, kdfparams.dklen)
|
|
119
|
+
} else {
|
|
120
|
+
throw new Error('Unsupported kdf')
|
|
121
|
+
}
|
|
122
|
+
const cipher = crypto.createCipheriv(opts.cipher || 'aes-128-ctr',
|
|
123
|
+
derivedKey.slice(0, 16), iv)
|
|
124
|
+
if (!cipher) {
|
|
125
|
+
throw new Error('Unsupported cipher')
|
|
126
|
+
}
|
|
127
|
+
const ciphertext = Buffer.concat(
|
|
128
|
+
[cipher.update(this.privKey), cipher.final()])
|
|
129
|
+
const mac = ethUtil.keccak(
|
|
130
|
+
Buffer.concat([derivedKey.slice(16, 32), ciphertext]))
|
|
131
|
+
|
|
132
|
+
const obj: {[k: string]: any} = {
|
|
133
|
+
version: 3,
|
|
134
|
+
id: uuidv4({
|
|
135
|
+
random: opts.uuid || crypto.randomBytes(16)
|
|
136
|
+
}),
|
|
137
|
+
address: this.getAddress().toString('hex'),
|
|
138
|
+
Crypto: {
|
|
139
|
+
ciphertext: ciphertext.toString('hex'),
|
|
140
|
+
cipherparams: {
|
|
141
|
+
iv: iv.toString('hex')
|
|
142
|
+
},
|
|
143
|
+
cipher: opts.cipher || 'aes-128-ctr',
|
|
144
|
+
kdf: kdf,
|
|
145
|
+
kdfparams: kdfparams,
|
|
146
|
+
mac: mac.toString('hex')
|
|
147
|
+
},
|
|
148
|
+
// ComChain addition:
|
|
149
|
+
server: { name: opts.server_name }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (typeof opts.message_key !== 'undefined') {
|
|
153
|
+
obj.message_key = opts.message_key
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return obj
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
cipher (password, data, kdfparams) {
|
|
161
|
+
if (!kdfparams) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
'Wallet.cipher(..) requires a 3rd parameter kdfparams.'
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
const iv = crypto.randomBytes(16)
|
|
167
|
+
const derivedKey = scrypt(Buffer.from(password),
|
|
168
|
+
Buffer.from(kdfparams.salt, 'hex'),
|
|
169
|
+
kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen)
|
|
170
|
+
|
|
171
|
+
const cipher = crypto.createCipheriv(
|
|
172
|
+
'aes-128-ctr', derivedKey.slice(0, 16), iv)
|
|
173
|
+
|
|
174
|
+
const ciphertext = Buffer.concat([cipher.update(data), cipher.final()])
|
|
175
|
+
const mac = ethUtil.keccak(
|
|
176
|
+
Buffer.concat([derivedKey.slice(16, 32), ciphertext]))
|
|
177
|
+
return {
|
|
178
|
+
crypto: {
|
|
179
|
+
ciphertext: ciphertext.toString('hex'),
|
|
180
|
+
cipherparams: {
|
|
181
|
+
iv: iv.toString('hex')
|
|
182
|
+
},
|
|
183
|
+
cipher: 'aes-128-ctr',
|
|
184
|
+
kdf: 'scrypt',
|
|
185
|
+
kdfparams: kdfparams,
|
|
186
|
+
mac: mac.toString('hex')
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
decifer (input, password) {
|
|
192
|
+
const json = (typeof input === 'object')
|
|
193
|
+
? input
|
|
194
|
+
: JSON.parse(input.toLowerCase())
|
|
195
|
+
|
|
196
|
+
const kdfparams = json.crypto.kdfparams
|
|
197
|
+
const derivedKey = scrypt(
|
|
198
|
+
Buffer.from(password),
|
|
199
|
+
Buffer.from(kdfparams.salt, 'hex'),
|
|
200
|
+
kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen)
|
|
201
|
+
|
|
202
|
+
const ciphertext = Buffer.from(json.crypto.ciphertext, 'hex')
|
|
203
|
+
const mac = ethUtil.keccak(
|
|
204
|
+
Buffer.concat([derivedKey.slice(16, 32), ciphertext]))
|
|
205
|
+
if (mac.toString('hex') !== json.crypto.mac) {
|
|
206
|
+
throw new Error('Key derivation failed - possibly wrong passphrase')
|
|
207
|
+
}
|
|
208
|
+
const decipher = crypto.createDecipheriv(
|
|
209
|
+
json.crypto.cipher, derivedKey.slice(0, 16),
|
|
210
|
+
Buffer.from(json.crypto.cipherparams.iv, 'hex'))
|
|
211
|
+
return decipherBuffer(decipher, ciphertext)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
toJSON () {
|
|
215
|
+
return {
|
|
216
|
+
address: this.getAddressString(),
|
|
217
|
+
checksumAddress: this.getChecksumAddressString(),
|
|
218
|
+
privKey: this.getPrivateKeyString(),
|
|
219
|
+
pubKey: this.getPublicKeyString(),
|
|
220
|
+
publisher: 'MyEtherWallet',
|
|
221
|
+
encrypted: false,
|
|
222
|
+
version: 2
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static fromMyEtherWallet (input, password) {
|
|
227
|
+
const json = (typeof input === 'object') ? input : JSON.parse(input)
|
|
228
|
+
let privKey
|
|
229
|
+
if (!json.locked) {
|
|
230
|
+
if (json.private.length !== 64) {
|
|
231
|
+
throw new Error('Invalid private key length')
|
|
232
|
+
}
|
|
233
|
+
privKey = Buffer.from(json.private, 'hex')
|
|
234
|
+
} else {
|
|
235
|
+
if (typeof password !== 'string') {
|
|
236
|
+
throw new Error('Password required')
|
|
237
|
+
}
|
|
238
|
+
if (password.length < 7) {
|
|
239
|
+
throw new Error('Password must be at least 7 characters')
|
|
240
|
+
}
|
|
241
|
+
let cipher = json.encrypted ? json.private.slice(0, 128) : json.private
|
|
242
|
+
cipher = decodeCryptojsSalt(cipher)
|
|
243
|
+
const evp = evpKdf(Buffer.from(password), cipher.salt, {
|
|
244
|
+
keysize: 32,
|
|
245
|
+
ivsize: 16
|
|
246
|
+
})
|
|
247
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', evp.key, evp.iv)
|
|
248
|
+
privKey = decipherBuffer(decipher, Buffer.from(cipher.ciphertext))
|
|
249
|
+
privKey = Buffer.from((privKey.toString()), 'hex')
|
|
250
|
+
}
|
|
251
|
+
const wallet = new this(privKey)
|
|
252
|
+
if (wallet.getAddressString() !== json.address) {
|
|
253
|
+
throw new Error('Invalid private key or address')
|
|
254
|
+
}
|
|
255
|
+
return wallet
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static fromMyEtherWalletV2 (input) {
|
|
259
|
+
const json = (typeof input === 'object') ? input : JSON.parse(input)
|
|
260
|
+
if (json.privKey.length !== 64) {
|
|
261
|
+
throw new Error('Invalid private key length')
|
|
262
|
+
};
|
|
263
|
+
const privKey = Buffer.from(json.privKey, 'hex')
|
|
264
|
+
return new this(privKey)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
static fromEthSale (input, password) {
|
|
268
|
+
const json = (typeof input === 'object') ? input : JSON.parse(input)
|
|
269
|
+
const encseed = Buffer.from(json.encseed, 'hex')
|
|
270
|
+
const derivedKey = crypto.pbkdf2Sync(
|
|
271
|
+
Buffer.from(password), Buffer.from(password), 2000, 32, 'sha256')
|
|
272
|
+
.slice(0, 16)
|
|
273
|
+
const decipher = crypto.createDecipheriv(
|
|
274
|
+
'aes-128-cbc', derivedKey, encseed.slice(0, 16))
|
|
275
|
+
const seed = decipherBuffer(decipher, encseed.slice(16))
|
|
276
|
+
const wallet = new this(ethUtil.keccak(seed))
|
|
277
|
+
if (wallet.getAddress().toString('hex') !== json.ethaddr) {
|
|
278
|
+
throw new Error('Decoded key mismatch - possibly wrong passphrase')
|
|
279
|
+
}
|
|
280
|
+
return wallet
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
static fromMyEtherWalletKey (input, password) {
|
|
284
|
+
let cipher = input.slice(0, 128)
|
|
285
|
+
cipher = decodeCryptojsSalt(cipher)
|
|
286
|
+
const evp = evpKdf(Buffer.from(password), cipher.salt, {
|
|
287
|
+
keysize: 32,
|
|
288
|
+
ivsize: 16
|
|
289
|
+
})
|
|
290
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', evp.key, evp.iv)
|
|
291
|
+
let privKey = decipherBuffer(decipher,
|
|
292
|
+
Buffer.from(cipher.ciphertext))
|
|
293
|
+
privKey = Buffer.from((privKey.toString()), 'hex')
|
|
294
|
+
return new this(privKey)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
static fromV3 (input, password, nonStrict) {
|
|
298
|
+
const json = (typeof input === 'object')
|
|
299
|
+
? input
|
|
300
|
+
: JSON.parse(nonStrict ? input.toLowerCase() : input)
|
|
301
|
+
if (json.version !== 3) {
|
|
302
|
+
throw new Error('Not a V3 wallet')
|
|
303
|
+
}
|
|
304
|
+
let derivedKey
|
|
305
|
+
let kdfparams
|
|
306
|
+
if (json.crypto.kdf === 'scrypt') {
|
|
307
|
+
kdfparams = json.crypto.kdfparams
|
|
308
|
+
derivedKey = scrypt(
|
|
309
|
+
Buffer.from(password),
|
|
310
|
+
Buffer.from(kdfparams.salt, 'hex'),
|
|
311
|
+
kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen)
|
|
312
|
+
} else if (json.crypto.kdf === 'pbkdf2') {
|
|
313
|
+
kdfparams = json.crypto.kdfparams
|
|
314
|
+
if (kdfparams.prf !== 'hmac-sha256') {
|
|
315
|
+
throw new Error('Unsupported parameters to PBKDF2')
|
|
316
|
+
}
|
|
317
|
+
derivedKey = crypto.pbkdf2Sync(
|
|
318
|
+
Buffer.from(password),
|
|
319
|
+
Buffer.from(kdfparams.salt, 'hex'),
|
|
320
|
+
kdfparams.c, kdfparams.dklen, 'sha256')
|
|
321
|
+
} else {
|
|
322
|
+
throw new Error('Unsupported key derivation scheme')
|
|
323
|
+
}
|
|
324
|
+
const ciphertext = Buffer.from(json.crypto.ciphertext, 'hex')
|
|
325
|
+
const mac = ethUtil.keccak(Buffer.concat(
|
|
326
|
+
[derivedKey.slice(16, 32), ciphertext]))
|
|
327
|
+
if (mac.toString('hex') !== json.crypto.mac) {
|
|
328
|
+
throw new Error('Key derivation failed - possibly wrong passphrase')
|
|
329
|
+
}
|
|
330
|
+
const decipher = crypto.createDecipheriv(
|
|
331
|
+
json.crypto.cipher, derivedKey.slice(0, 16),
|
|
332
|
+
Buffer.from(json.crypto.cipherparams.iv, 'hex'))
|
|
333
|
+
const seed = decipherBuffer(decipher, ciphertext)
|
|
334
|
+
return new this(seed)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
toV3String (password, opts) {
|
|
338
|
+
return JSON.stringify(this.toV3(password, opts))
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
getV3Filename (timestamp) {
|
|
342
|
+
const ts = timestamp ? new Date(timestamp) : new Date()
|
|
343
|
+
return `UTC--${ts.toJSON().replace(/:/g, '-')}--${this.getAddress().toString('hex')}`
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
signMessage (msg) {
|
|
347
|
+
const msgHash = ethUtil.hashPersonalMessage(ethUtil.toBuffer(msg))
|
|
348
|
+
const signature = ethUtil.ecsign(msgHash, this.privKey)
|
|
349
|
+
return ethUtil.bufferToHex(
|
|
350
|
+
Buffer.concat([signature.r, signature.s, ethUtil.toBuffer(signature.v)]))
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Tx from 'ethereumjs-tx'
|
|
2
|
+
import * as ethFuncs from './ethFuncs'
|
|
3
|
+
import * as etherUnits from './etherUnits'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
function isNumeric (n) {
|
|
7
|
+
return !isNaN(parseFloat(n)) && isFinite(n)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isTxDataValid (txData) {
|
|
11
|
+
if (txData.to !== '0xCONTRACT' &&
|
|
12
|
+
!ethFuncs.validateEtherAddress(txData.to)) {
|
|
13
|
+
throw new Error('ERROR_6')
|
|
14
|
+
} else if (!isNumeric(txData.value) ||
|
|
15
|
+
parseFloat(txData.value) < 0) {
|
|
16
|
+
throw new Error('ERROR_8')
|
|
17
|
+
} else if (!isNumeric(txData.gasLimit) ||
|
|
18
|
+
parseFloat(txData.gasLimit) <= 0) {
|
|
19
|
+
throw new Error('ERROR_9')
|
|
20
|
+
} else if (!ethFuncs.validateHexString(txData.data)) {
|
|
21
|
+
throw new Error('ERROR_10')
|
|
22
|
+
}
|
|
23
|
+
if (txData.to === '0xCONTRACT') txData.to = ''
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function generateTx (txData, data) {
|
|
27
|
+
try {
|
|
28
|
+
isTxDataValid(txData)
|
|
29
|
+
|
|
30
|
+
const rawTx: {[k: string]: any} = {
|
|
31
|
+
nonce: ethFuncs.sanitizeHex(data.nonce),
|
|
32
|
+
gasPrice: ethFuncs.sanitizeHex(
|
|
33
|
+
ethFuncs.addTinyMoreToGas(data.gasprice)),
|
|
34
|
+
gasLimit: ethFuncs.sanitizeHex(
|
|
35
|
+
ethFuncs.decimalToHex(txData.gasLimit)),
|
|
36
|
+
to: ethFuncs.sanitizeHex(txData.to),
|
|
37
|
+
value: ethFuncs.sanitizeHex(
|
|
38
|
+
ethFuncs.decimalToHex(etherUnits.toWei(txData.value, txData.unit))),
|
|
39
|
+
data: ethFuncs.sanitizeHex(txData.data)
|
|
40
|
+
}
|
|
41
|
+
const eTx = new Tx(rawTx)
|
|
42
|
+
|
|
43
|
+
eTx.sign(Buffer.from(txData.key, 'hex'))
|
|
44
|
+
rawTx.rawTx = JSON.stringify(rawTx)
|
|
45
|
+
rawTx.signedTx = '0x' + eTx.serialize().toString('hex')
|
|
46
|
+
rawTx.isError = false
|
|
47
|
+
return rawTx
|
|
48
|
+
} catch (e) {
|
|
49
|
+
return {
|
|
50
|
+
isError: true,
|
|
51
|
+
error: e
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|