diff options
author | Ian Coleman <ian@iancoleman.io> | 2019-09-12 15:29:10 +1000 |
---|---|---|
committer | Ian Coleman <ian@iancoleman.io> | 2019-09-12 15:29:10 +1000 |
commit | dedb054fd81ca42aec2ed8caf20ba999ef1c4b44 (patch) | |
tree | c5fa4f342def3073c5088471103216e1fd61419b | |
parent | 079635cba1587d1b68ebe02bc0a1af38f09d16be (diff) | |
download | BIP39-dedb054fd81ca42aec2ed8caf20ba999ef1c4b44.tar.gz BIP39-dedb054fd81ca42aec2ed8caf20ba999ef1c4b44.tar.zst BIP39-dedb054fd81ca42aec2ed8caf20ba999ef1c4b44.zip |
Add ethereumjs-util to libs directory
-rw-r--r-- | libs/ethereumjs-util/index.js | 736 | ||||
-rw-r--r-- | libs/ethereumjs-util/package.json | 125 | ||||
-rw-r--r-- | libs/ethereumjs-util/readme.md | 4 |
3 files changed, 865 insertions, 0 deletions
diff --git a/libs/ethereumjs-util/index.js b/libs/ethereumjs-util/index.js new file mode 100644 index 0000000..b17b7ae --- /dev/null +++ b/libs/ethereumjs-util/index.js | |||
@@ -0,0 +1,736 @@ | |||
1 | const createKeccakHash = require('keccak') | ||
2 | const secp256k1 = require('secp256k1') | ||
3 | const assert = require('assert') | ||
4 | const rlp = require('rlp') | ||
5 | const BN = require('bn.js') | ||
6 | const createHash = require('create-hash') | ||
7 | const Buffer = require('safe-buffer').Buffer | ||
8 | Object.assign(exports, require('ethjs-util')) | ||
9 | |||
10 | /** | ||
11 | * the max integer that this VM can handle (a ```BN```) | ||
12 | * @var {BN} MAX_INTEGER | ||
13 | */ | ||
14 | exports.MAX_INTEGER = new BN('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16) | ||
15 | |||
16 | /** | ||
17 | * 2^256 (a ```BN```) | ||
18 | * @var {BN} TWO_POW256 | ||
19 | */ | ||
20 | exports.TWO_POW256 = new BN('10000000000000000000000000000000000000000000000000000000000000000', 16) | ||
21 | |||
22 | /** | ||
23 | * Keccak-256 hash of null (a ```String```) | ||
24 | * @var {String} KECCAK256_NULL_S | ||
25 | */ | ||
26 | exports.KECCAK256_NULL_S = 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' | ||
27 | |||
28 | /** | ||
29 | * Keccak-256 hash of null (a ```Buffer```) | ||
30 | * @var {Buffer} KECCAK256_NULL | ||
31 | */ | ||
32 | exports.KECCAK256_NULL = Buffer.from(exports.KECCAK256_NULL_S, 'hex') | ||
33 | |||
34 | /** | ||
35 | * Keccak-256 of an RLP of an empty array (a ```String```) | ||
36 | * @var {String} KECCAK256_RLP_ARRAY_S | ||
37 | */ | ||
38 | exports.KECCAK256_RLP_ARRAY_S = '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' | ||
39 | |||
40 | /** | ||
41 | * Keccak-256 of an RLP of an empty array (a ```Buffer```) | ||
42 | * @var {Buffer} KECCAK256_RLP_ARRAY | ||
43 | */ | ||
44 | exports.KECCAK256_RLP_ARRAY = Buffer.from(exports.KECCAK256_RLP_ARRAY_S, 'hex') | ||
45 | |||
46 | /** | ||
47 | * Keccak-256 hash of the RLP of null (a ```String```) | ||
48 | * @var {String} KECCAK256_RLP_S | ||
49 | */ | ||
50 | exports.KECCAK256_RLP_S = '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' | ||
51 | |||
52 | /** | ||
53 | * Keccak-256 hash of the RLP of null (a ```Buffer```) | ||
54 | * @var {Buffer} KECCAK256_RLP | ||
55 | */ | ||
56 | exports.KECCAK256_RLP = Buffer.from(exports.KECCAK256_RLP_S, 'hex') | ||
57 | |||
58 | /** | ||
59 | * [`BN`](https://github.com/indutny/bn.js) | ||
60 | * @var {Function} | ||
61 | */ | ||
62 | exports.BN = BN | ||
63 | |||
64 | /** | ||
65 | * [`rlp`](https://github.com/ethereumjs/rlp) | ||
66 | * @var {Function} | ||
67 | */ | ||
68 | exports.rlp = rlp | ||
69 | |||
70 | /** | ||
71 | * [`secp256k1`](https://github.com/cryptocoinjs/secp256k1-node/) | ||
72 | * @var {Object} | ||
73 | */ | ||
74 | exports.secp256k1 = secp256k1 | ||
75 | |||
76 | /** | ||
77 | * Returns a buffer filled with 0s | ||
78 | * @method zeros | ||
79 | * @param {Number} bytes the number of bytes the buffer should be | ||
80 | * @return {Buffer} | ||
81 | */ | ||
82 | exports.zeros = function (bytes) { | ||
83 | return Buffer.allocUnsafe(bytes).fill(0) | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Returns a zero address | ||
88 | * @method zeroAddress | ||
89 | * @return {String} | ||
90 | */ | ||
91 | exports.zeroAddress = function () { | ||
92 | const addressLength = 20 | ||
93 | const zeroAddress = exports.zeros(addressLength) | ||
94 | return exports.bufferToHex(zeroAddress) | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Left Pads an `Array` or `Buffer` with leading zeros till it has `length` bytes. | ||
99 | * Or it truncates the beginning if it exceeds. | ||
100 | * @method lsetLength | ||
101 | * @param {Buffer|Array} msg the value to pad | ||
102 | * @param {Number} length the number of bytes the output should be | ||
103 | * @param {Boolean} [right=false] whether to start padding form the left or right | ||
104 | * @return {Buffer|Array} | ||
105 | */ | ||
106 | exports.setLengthLeft = exports.setLength = function (msg, length, right) { | ||
107 | const buf = exports.zeros(length) | ||
108 | msg = exports.toBuffer(msg) | ||
109 | if (right) { | ||
110 | if (msg.length < length) { | ||
111 | msg.copy(buf) | ||
112 | return buf | ||
113 | } | ||
114 | return msg.slice(0, length) | ||
115 | } else { | ||
116 | if (msg.length < length) { | ||
117 | msg.copy(buf, length - msg.length) | ||
118 | return buf | ||
119 | } | ||
120 | return msg.slice(-length) | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Right Pads an `Array` or `Buffer` with leading zeros till it has `length` bytes. | ||
126 | * Or it truncates the beginning if it exceeds. | ||
127 | * @param {Buffer|Array} msg the value to pad | ||
128 | * @param {Number} length the number of bytes the output should be | ||
129 | * @return {Buffer|Array} | ||
130 | */ | ||
131 | exports.setLengthRight = function (msg, length) { | ||
132 | return exports.setLength(msg, length, true) | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * Trims leading zeros from a `Buffer` or an `Array` | ||
137 | * @param {Buffer|Array|String} a | ||
138 | * @return {Buffer|Array|String} | ||
139 | */ | ||
140 | exports.unpad = exports.stripZeros = function (a) { | ||
141 | a = exports.stripHexPrefix(a) | ||
142 | let first = a[0] | ||
143 | while (a.length > 0 && first.toString() === '0') { | ||
144 | a = a.slice(1) | ||
145 | first = a[0] | ||
146 | } | ||
147 | return a | ||
148 | } | ||
149 | /** | ||
150 | * Attempts to turn a value into a `Buffer`. As input it supports `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` method. | ||
151 | * @param {*} v the value | ||
152 | */ | ||
153 | exports.toBuffer = function (v) { | ||
154 | if (!Buffer.isBuffer(v)) { | ||
155 | if (Array.isArray(v)) { | ||
156 | v = Buffer.from(v) | ||
157 | } else if (typeof v === 'string') { | ||
158 | if (exports.isHexString(v)) { | ||
159 | v = Buffer.from(exports.padToEven(exports.stripHexPrefix(v)), 'hex') | ||
160 | } else { | ||
161 | v = Buffer.from(v) | ||
162 | } | ||
163 | } else if (typeof v === 'number') { | ||
164 | v = exports.intToBuffer(v) | ||
165 | } else if (v === null || v === undefined) { | ||
166 | v = Buffer.allocUnsafe(0) | ||
167 | } else if (BN.isBN(v)) { | ||
168 | v = v.toArrayLike(Buffer) | ||
169 | } else if (v.toArray) { | ||
170 | // converts a BN to a Buffer | ||
171 | v = Buffer.from(v.toArray()) | ||
172 | } else { | ||
173 | throw new Error('invalid type') | ||
174 | } | ||
175 | } | ||
176 | return v | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Converts a `Buffer` to a `Number` | ||
181 | * @param {Buffer} buf | ||
182 | * @return {Number} | ||
183 | * @throws If the input number exceeds 53 bits. | ||
184 | */ | ||
185 | exports.bufferToInt = function (buf) { | ||
186 | return new BN(exports.toBuffer(buf)).toNumber() | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * Converts a `Buffer` into a hex `String` | ||
191 | * @param {Buffer} buf | ||
192 | * @return {String} | ||
193 | */ | ||
194 | exports.bufferToHex = function (buf) { | ||
195 | buf = exports.toBuffer(buf) | ||
196 | return '0x' + buf.toString('hex') | ||
197 | } | ||
198 | |||
199 | /** | ||
200 | * Interprets a `Buffer` as a signed integer and returns a `BN`. Assumes 256-bit numbers. | ||
201 | * @param {Buffer} num | ||
202 | * @return {BN} | ||
203 | */ | ||
204 | exports.fromSigned = function (num) { | ||
205 | return new BN(num).fromTwos(256) | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * Converts a `BN` to an unsigned integer and returns it as a `Buffer`. Assumes 256-bit numbers. | ||
210 | * @param {BN} num | ||
211 | * @return {Buffer} | ||
212 | */ | ||
213 | exports.toUnsigned = function (num) { | ||
214 | return Buffer.from(num.toTwos(256).toArray()) | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * Creates Keccak hash of the input | ||
219 | * @param {Buffer|Array|String|Number} a the input data | ||
220 | * @param {Number} [bits=256] the Keccak width | ||
221 | * @return {Buffer} | ||
222 | */ | ||
223 | exports.keccak = function (a, bits) { | ||
224 | a = exports.toBuffer(a) | ||
225 | if (!bits) bits = 256 | ||
226 | |||
227 | return createKeccakHash('keccak' + bits).update(a).digest() | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * Creates Keccak-256 hash of the input, alias for keccak(a, 256) | ||
232 | * @param {Buffer|Array|String|Number} a the input data | ||
233 | * @return {Buffer} | ||
234 | */ | ||
235 | exports.keccak256 = function (a) { | ||
236 | return exports.keccak(a) | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * Creates SHA256 hash of the input | ||
241 | * @param {Buffer|Array|String|Number} a the input data | ||
242 | * @return {Buffer} | ||
243 | */ | ||
244 | exports.sha256 = function (a) { | ||
245 | a = exports.toBuffer(a) | ||
246 | return createHash('sha256').update(a).digest() | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * Creates RIPEMD160 hash of the input | ||
251 | * @param {Buffer|Array|String|Number} a the input data | ||
252 | * @param {Boolean} padded whether it should be padded to 256 bits or not | ||
253 | * @return {Buffer} | ||
254 | */ | ||
255 | exports.ripemd160 = function (a, padded) { | ||
256 | a = exports.toBuffer(a) | ||
257 | const hash = createHash('rmd160').update(a).digest() | ||
258 | if (padded === true) { | ||
259 | return exports.setLength(hash, 32) | ||
260 | } else { | ||
261 | return hash | ||
262 | } | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Creates SHA-3 hash of the RLP encoded version of the input | ||
267 | * @param {Buffer|Array|String|Number} a the input data | ||
268 | * @return {Buffer} | ||
269 | */ | ||
270 | exports.rlphash = function (a) { | ||
271 | return exports.keccak(rlp.encode(a)) | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * Checks if the private key satisfies the rules of the curve secp256k1. | ||
276 | * @param {Buffer} privateKey | ||
277 | * @return {Boolean} | ||
278 | */ | ||
279 | exports.isValidPrivate = function (privateKey) { | ||
280 | return secp256k1.privateKeyVerify(privateKey) | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * Checks if the public key satisfies the rules of the curve secp256k1 | ||
285 | * and the requirements of Ethereum. | ||
286 | * @param {Buffer} publicKey The two points of an uncompressed key, unless sanitize is enabled | ||
287 | * @param {Boolean} [sanitize=false] Accept public keys in other formats | ||
288 | * @return {Boolean} | ||
289 | */ | ||
290 | exports.isValidPublic = function (publicKey, sanitize) { | ||
291 | if (publicKey.length === 64) { | ||
292 | // Convert to SEC1 for secp256k1 | ||
293 | return secp256k1.publicKeyVerify(Buffer.concat([ Buffer.from([4]), publicKey ])) | ||
294 | } | ||
295 | |||
296 | if (!sanitize) { | ||
297 | return false | ||
298 | } | ||
299 | |||
300 | return secp256k1.publicKeyVerify(publicKey) | ||
301 | } | ||
302 | |||
303 | /** | ||
304 | * Returns the ethereum address of a given public key. | ||
305 | * Accepts "Ethereum public keys" and SEC1 encoded keys. | ||
306 | * @param {Buffer} pubKey The two points of an uncompressed key, unless sanitize is enabled | ||
307 | * @param {Boolean} [sanitize=false] Accept public keys in other formats | ||
308 | * @return {Buffer} | ||
309 | */ | ||
310 | exports.pubToAddress = exports.publicToAddress = function (pubKey, sanitize) { | ||
311 | pubKey = exports.toBuffer(pubKey) | ||
312 | if (sanitize && (pubKey.length !== 64)) { | ||
313 | pubKey = secp256k1.publicKeyConvert(pubKey, false).slice(1) | ||
314 | } | ||
315 | assert(pubKey.length === 64) | ||
316 | // Only take the lower 160bits of the hash | ||
317 | return exports.keccak(pubKey).slice(-20) | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * Returns the ethereum public key of a given private key | ||
322 | * @param {Buffer} privateKey A private key must be 256 bits wide | ||
323 | * @return {Buffer} | ||
324 | */ | ||
325 | const privateToPublic = exports.privateToPublic = function (privateKey) { | ||
326 | privateKey = exports.toBuffer(privateKey) | ||
327 | // skip the type flag and use the X, Y points | ||
328 | return secp256k1.publicKeyCreate(privateKey, false).slice(1) | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * Converts a public key to the Ethereum format. | ||
333 | * @param {Buffer} publicKey | ||
334 | * @return {Buffer} | ||
335 | */ | ||
336 | exports.importPublic = function (publicKey) { | ||
337 | publicKey = exports.toBuffer(publicKey) | ||
338 | if (publicKey.length !== 64) { | ||
339 | publicKey = secp256k1.publicKeyConvert(publicKey, false).slice(1) | ||
340 | } | ||
341 | return publicKey | ||
342 | } | ||
343 | |||
344 | /** | ||
345 | * ECDSA sign | ||
346 | * @param {Buffer} msgHash | ||
347 | * @param {Buffer} privateKey | ||
348 | * @param {Number} [chainId] | ||
349 | * @return {Object} | ||
350 | */ | ||
351 | exports.ecsign = function (msgHash, privateKey, chainId) { | ||
352 | const sig = secp256k1.sign(msgHash, privateKey) | ||
353 | |||
354 | const ret = {} | ||
355 | ret.r = sig.signature.slice(0, 32) | ||
356 | ret.s = sig.signature.slice(32, 64) | ||
357 | ret.v = chainId ? sig.recovery + (chainId * 2 + 35) : sig.recovery + 27 | ||
358 | return ret | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * Returns the keccak-256 hash of `message`, prefixed with the header used by the `eth_sign` RPC call. | ||
363 | * The output of this function can be fed into `ecsign` to produce the same signature as the `eth_sign` | ||
364 | * call for a given `message`, or fed to `ecrecover` along with a signature to recover the public key | ||
365 | * used to produce the signature. | ||
366 | * @param message | ||
367 | * @returns {Buffer} hash | ||
368 | */ | ||
369 | exports.hashPersonalMessage = function (message) { | ||
370 | const prefix = exports.toBuffer('\u0019Ethereum Signed Message:\n' + message.length.toString()) | ||
371 | return exports.keccak(Buffer.concat([prefix, message])) | ||
372 | } | ||
373 | |||
374 | /** | ||
375 | * ECDSA public key recovery from signature | ||
376 | * @param {Buffer} msgHash | ||
377 | * @param {Number} v | ||
378 | * @param {Buffer} r | ||
379 | * @param {Buffer} s | ||
380 | * @param {Number} [chainId] | ||
381 | * @return {Buffer} publicKey | ||
382 | */ | ||
383 | exports.ecrecover = function (msgHash, v, r, s, chainId) { | ||
384 | const signature = Buffer.concat([exports.setLength(r, 32), exports.setLength(s, 32)], 64) | ||
385 | const recovery = calculateSigRecovery(v, chainId) | ||
386 | if (!isValidSigRecovery(recovery)) { | ||
387 | throw new Error('Invalid signature v value') | ||
388 | } | ||
389 | const senderPubKey = secp256k1.recover(msgHash, signature, recovery) | ||
390 | return secp256k1.publicKeyConvert(senderPubKey, false).slice(1) | ||
391 | } | ||
392 | |||
393 | /** | ||
394 | * Convert signature parameters into the format of `eth_sign` RPC method | ||
395 | * @param {Number} v | ||
396 | * @param {Buffer} r | ||
397 | * @param {Buffer} s | ||
398 | * @param {Number} [chainId] | ||
399 | * @return {String} sig | ||
400 | */ | ||
401 | exports.toRpcSig = function (v, r, s, chainId) { | ||
402 | let recovery = calculateSigRecovery(v, chainId) | ||
403 | if (!isValidSigRecovery(recovery)) { | ||
404 | throw new Error('Invalid signature v value') | ||
405 | } | ||
406 | |||
407 | // geth (and the RPC eth_sign method) uses the 65 byte format used by Bitcoin | ||
408 | return exports.bufferToHex(Buffer.concat([ | ||
409 | exports.setLengthLeft(r, 32), | ||
410 | exports.setLengthLeft(s, 32), | ||
411 | exports.toBuffer(v) | ||
412 | ])) | ||
413 | } | ||
414 | |||
415 | /** | ||
416 | * Convert signature format of the `eth_sign` RPC method to signature parameters | ||
417 | * NOTE: all because of a bug in geth: https://github.com/ethereum/go-ethereum/issues/2053 | ||
418 | * @param {String} sig | ||
419 | * @return {Object} | ||
420 | */ | ||
421 | exports.fromRpcSig = function (sig) { | ||
422 | sig = exports.toBuffer(sig) | ||
423 | |||
424 | // NOTE: with potential introduction of chainId this might need to be updated | ||
425 | if (sig.length !== 65) { | ||
426 | throw new Error('Invalid signature length') | ||
427 | } | ||
428 | |||
429 | let v = sig[64] | ||
430 | // support both versions of `eth_sign` responses | ||
431 | if (v < 27) { | ||
432 | v += 27 | ||
433 | } | ||
434 | |||
435 | return { | ||
436 | v: v, | ||
437 | r: sig.slice(0, 32), | ||
438 | s: sig.slice(32, 64) | ||
439 | } | ||
440 | } | ||
441 | |||
442 | /** | ||
443 | * Returns the ethereum address of a given private key | ||
444 | * @param {Buffer} privateKey A private key must be 256 bits wide | ||
445 | * @return {Buffer} | ||
446 | */ | ||
447 | exports.privateToAddress = function (privateKey) { | ||
448 | return exports.publicToAddress(privateToPublic(privateKey)) | ||
449 | } | ||
450 | |||
451 | /** | ||
452 | * Checks if the address is a valid. Accepts checksummed addresses too | ||
453 | * @param {String} address | ||
454 | * @return {Boolean} | ||
455 | */ | ||
456 | exports.isValidAddress = function (address) { | ||
457 | return /^0x[0-9a-fA-F]{40}$/.test(address) | ||
458 | } | ||
459 | |||
460 | /** | ||
461 | * Checks if a given address is a zero address | ||
462 | * @method isZeroAddress | ||
463 | * @param {String} address | ||
464 | * @return {Boolean} | ||
465 | */ | ||
466 | exports.isZeroAddress = function (address) { | ||
467 | const zeroAddress = exports.zeroAddress() | ||
468 | return zeroAddress === exports.addHexPrefix(address) | ||
469 | } | ||
470 | |||
471 | /** | ||
472 | * Returns a checksummed address | ||
473 | * @param {String} address | ||
474 | * @return {String} | ||
475 | */ | ||
476 | exports.toChecksumAddress = function (address) { | ||
477 | address = exports.stripHexPrefix(address).toLowerCase() | ||
478 | const hash = exports.keccak(address).toString('hex') | ||
479 | let ret = '0x' | ||
480 | |||
481 | for (let i = 0; i < address.length; i++) { | ||
482 | if (parseInt(hash[i], 16) >= 8) { | ||
483 | ret += address[i].toUpperCase() | ||
484 | } else { | ||
485 | ret += address[i] | ||
486 | } | ||
487 | } | ||
488 | |||
489 | return ret | ||
490 | } | ||
491 | |||
492 | /** | ||
493 | * Checks if the address is a valid checksummed address | ||
494 | * @param {Buffer} address | ||
495 | * @return {Boolean} | ||
496 | */ | ||
497 | exports.isValidChecksumAddress = function (address) { | ||
498 | return exports.isValidAddress(address) && (exports.toChecksumAddress(address) === address) | ||
499 | } | ||
500 | |||
501 | /** | ||
502 | * Generates an address of a newly created contract | ||
503 | * @param {Buffer} from the address which is creating this new address | ||
504 | * @param {Buffer} nonce the nonce of the from account | ||
505 | * @return {Buffer} | ||
506 | */ | ||
507 | exports.generateAddress = function (from, nonce) { | ||
508 | from = exports.toBuffer(from) | ||
509 | nonce = new BN(nonce) | ||
510 | |||
511 | if (nonce.isZero()) { | ||
512 | // in RLP we want to encode null in the case of zero nonce | ||
513 | // read the RLP documentation for an answer if you dare | ||
514 | nonce = null | ||
515 | } else { | ||
516 | nonce = Buffer.from(nonce.toArray()) | ||
517 | } | ||
518 | |||
519 | // Only take the lower 160bits of the hash | ||
520 | return exports.rlphash([from, nonce]).slice(-20) | ||
521 | } | ||
522 | |||
523 | /** | ||
524 | * Generates an address for a contract created using CREATE2 | ||
525 | * @param {Buffer} from the address which is creating this new address | ||
526 | * @param {Buffer} salt a salt | ||
527 | * @param {Buffer} initCode the init code of the contract being created | ||
528 | * @return {Buffer} | ||
529 | */ | ||
530 | exports.generateAddress2 = function (from, salt, initCode) { | ||
531 | from = exports.toBuffer(from) | ||
532 | salt = exports.toBuffer(salt) | ||
533 | initCode = exports.toBuffer(initCode) | ||
534 | |||
535 | assert(from.length === 20) | ||
536 | assert(salt.length === 32) | ||
537 | |||
538 | let address = exports.keccak256(Buffer.concat([ | ||
539 | Buffer.from('ff', 'hex'), | ||
540 | from, | ||
541 | salt, | ||
542 | exports.keccak256(initCode) | ||
543 | ])) | ||
544 | |||
545 | return address.slice(-20) | ||
546 | } | ||
547 | |||
548 | /** | ||
549 | * Returns true if the supplied address belongs to a precompiled account (Byzantium) | ||
550 | * @param {Buffer|String} address | ||
551 | * @return {Boolean} | ||
552 | */ | ||
553 | exports.isPrecompiled = function (address) { | ||
554 | const a = exports.unpad(address) | ||
555 | return a.length === 1 && a[0] >= 1 && a[0] <= 8 | ||
556 | } | ||
557 | |||
558 | /** | ||
559 | * Adds "0x" to a given `String` if it does not already start with "0x" | ||
560 | * @param {String} str | ||
561 | * @return {String} | ||
562 | */ | ||
563 | exports.addHexPrefix = function (str) { | ||
564 | if (typeof str !== 'string') { | ||
565 | return str | ||
566 | } | ||
567 | |||
568 | return exports.isHexPrefixed(str) ? str : '0x' + str | ||
569 | } | ||
570 | |||
571 | /** | ||
572 | * Validate ECDSA signature | ||
573 | * @method isValidSignature | ||
574 | * @param {Buffer} v | ||
575 | * @param {Buffer} r | ||
576 | * @param {Buffer} s | ||
577 | * @param {Boolean} [homestead=true] | ||
578 | * @param {Number} [chainId] | ||
579 | * @return {Boolean} | ||
580 | */ | ||
581 | |||
582 | exports.isValidSignature = function (v, r, s, homestead, chainId) { | ||
583 | const SECP256K1_N_DIV_2 = new BN('7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16) | ||
584 | const SECP256K1_N = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) | ||
585 | |||
586 | if (r.length !== 32 || s.length !== 32) { | ||
587 | return false | ||
588 | } | ||
589 | |||
590 | if (!isValidSigRecovery(calculateSigRecovery(v, chainId))) { | ||
591 | return false | ||
592 | } | ||
593 | |||
594 | r = new BN(r) | ||
595 | s = new BN(s) | ||
596 | |||
597 | if (r.isZero() || r.gt(SECP256K1_N) || s.isZero() || s.gt(SECP256K1_N)) { | ||
598 | return false | ||
599 | } | ||
600 | |||
601 | if ((homestead === false) && (new BN(s).cmp(SECP256K1_N_DIV_2) === 1)) { | ||
602 | return false | ||
603 | } | ||
604 | |||
605 | return true | ||
606 | } | ||
607 | |||
608 | /** | ||
609 | * Converts a `Buffer` or `Array` to JSON | ||
610 | * @param {Buffer|Array} ba | ||
611 | * @return {Array|String|null} | ||
612 | */ | ||
613 | exports.baToJSON = function (ba) { | ||
614 | if (Buffer.isBuffer(ba)) { | ||
615 | return '0x' + ba.toString('hex') | ||
616 | } else if (ba instanceof Array) { | ||
617 | const array = [] | ||
618 | for (let i = 0; i < ba.length; i++) { | ||
619 | array.push(exports.baToJSON(ba[i])) | ||
620 | } | ||
621 | return array | ||
622 | } | ||
623 | } | ||
624 | |||
625 | /** | ||
626 | * Defines properties on a `Object`. It make the assumption that underlying data is binary. | ||
627 | * @param {Object} self the `Object` to define properties on | ||
628 | * @param {Array} fields an array fields to define. Fields can contain: | ||
629 | * * `name` - the name of the properties | ||
630 | * * `length` - the number of bytes the field can have | ||
631 | * * `allowLess` - if the field can be less than the length | ||
632 | * * `allowEmpty` | ||
633 | * @param {*} data data to be validated against the definitions | ||
634 | */ | ||
635 | exports.defineProperties = function (self, fields, data) { | ||
636 | self.raw = [] | ||
637 | self._fields = [] | ||
638 | |||
639 | // attach the `toJSON` | ||
640 | self.toJSON = function (label) { | ||
641 | if (label) { | ||
642 | const obj = {} | ||
643 | self._fields.forEach((field) => { | ||
644 | obj[field] = '0x' + self[field].toString('hex') | ||
645 | }) | ||
646 | return obj | ||
647 | } | ||
648 | return exports.baToJSON(this.raw) | ||
649 | } | ||
650 | |||
651 | self.serialize = function serialize () { | ||
652 | return rlp.encode(self.raw) | ||
653 | } | ||
654 | |||
655 | fields.forEach((field, i) => { | ||
656 | self._fields.push(field.name) | ||
657 | function getter () { | ||
658 | return self.raw[i] | ||
659 | } | ||
660 | function setter (v) { | ||
661 | v = exports.toBuffer(v) | ||
662 | |||
663 | if (v.toString('hex') === '00' && !field.allowZero) { | ||
664 | v = Buffer.allocUnsafe(0) | ||
665 | } | ||
666 | |||
667 | if (field.allowLess && field.length) { | ||
668 | v = exports.stripZeros(v) | ||
669 | assert(field.length >= v.length, 'The field ' + field.name + ' must not have more ' + field.length + ' bytes') | ||
670 | } else if (!(field.allowZero && v.length === 0) && field.length) { | ||
671 | assert(field.length === v.length, 'The field ' + field.name + ' must have byte length of ' + field.length) | ||
672 | } | ||
673 | |||
674 | self.raw[i] = v | ||
675 | } | ||
676 | |||
677 | Object.defineProperty(self, field.name, { | ||
678 | enumerable: true, | ||
679 | configurable: true, | ||
680 | get: getter, | ||
681 | set: setter | ||
682 | }) | ||
683 | |||
684 | if (field.default) { | ||
685 | self[field.name] = field.default | ||
686 | } | ||
687 | |||
688 | // attach alias | ||
689 | if (field.alias) { | ||
690 | Object.defineProperty(self, field.alias, { | ||
691 | enumerable: false, | ||
692 | configurable: true, | ||
693 | set: setter, | ||
694 | get: getter | ||
695 | }) | ||
696 | } | ||
697 | }) | ||
698 | |||
699 | // if the constuctor is passed data | ||
700 | if (data) { | ||
701 | if (typeof data === 'string') { | ||
702 | data = Buffer.from(exports.stripHexPrefix(data), 'hex') | ||
703 | } | ||
704 | |||
705 | if (Buffer.isBuffer(data)) { | ||
706 | data = rlp.decode(data) | ||
707 | } | ||
708 | |||
709 | if (Array.isArray(data)) { | ||
710 | if (data.length > self._fields.length) { | ||
711 | throw (new Error('wrong number of fields in data')) | ||
712 | } | ||
713 | |||
714 | // make sure all the items are buffers | ||
715 | data.forEach((d, i) => { | ||
716 | self[self._fields[i]] = exports.toBuffer(d) | ||
717 | }) | ||
718 | } else if (typeof data === 'object') { | ||
719 | const keys = Object.keys(data) | ||
720 | fields.forEach((field) => { | ||
721 | if (keys.indexOf(field.name) !== -1) self[field.name] = data[field.name] | ||
722 | if (keys.indexOf(field.alias) !== -1) self[field.alias] = data[field.alias] | ||
723 | }) | ||
724 | } else { | ||
725 | throw new Error('invalid data') | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | |||
730 | function calculateSigRecovery (v, chainId) { | ||
731 | return chainId ? v - (2 * chainId + 35) : v - 27 | ||
732 | } | ||
733 | |||
734 | function isValidSigRecovery (recovery) { | ||
735 | return recovery === 0 || recovery === 1 | ||
736 | } | ||
diff --git a/libs/ethereumjs-util/package.json b/libs/ethereumjs-util/package.json new file mode 100644 index 0000000..a49746f --- /dev/null +++ b/libs/ethereumjs-util/package.json | |||
@@ -0,0 +1,125 @@ | |||
1 | { | ||
2 | "name": "ethereumjs-util", | ||
3 | "version": "6.0.0", | ||
4 | "description": "a collection of utility functions for Ethereum", | ||
5 | "main": "dist/index.js", | ||
6 | "files": [ | ||
7 | "dist" | ||
8 | ], | ||
9 | "scripts": { | ||
10 | "build": "browserify index.js -s ethUtil -o /tmp/ethereumjs-util.js", | ||
11 | "coverage": "npm run build:dist && istanbul cover _mocha", | ||
12 | "coveralls": "npm run coverage && coveralls <coverage/lcov.info", | ||
13 | "lint": "standard", | ||
14 | "prepublishOnly": "npm run test && npm run build:dist", | ||
15 | "test": "npm run lint && npm run test:node && npm run test:browser", | ||
16 | "test:browser": "npm run build:dist && karma start karma.conf.js", | ||
17 | "test:node": "npm run build:dist && istanbul test mocha -- --reporter spec", | ||
18 | "build:dist": "babel index.js --source-root ./ -d ./dist", | ||
19 | "build:docs": "documentation build ./index.js --github --sort-order='alpha' -f md > ./docs/index.md" | ||
20 | }, | ||
21 | "repository": { | ||
22 | "type": "git", | ||
23 | "url": "https://github.com/ethereumjs/ethereumjs-util.git" | ||
24 | }, | ||
25 | "keywords": [ | ||
26 | "ethereum", | ||
27 | "utilties" | ||
28 | ], | ||
29 | "author": "mjbecze <mjbecze@gmail.com>", | ||
30 | "contributors": [ | ||
31 | { | ||
32 | "name": "Tim Coulter", | ||
33 | "email": "tim@timothyjcoulter.com", | ||
34 | "url": "https://github.com/tcoulter", | ||
35 | "contributions": 1, | ||
36 | "additions": 2, | ||
37 | "deletions": 2 | ||
38 | }, | ||
39 | { | ||
40 | "name": "Nick Dodson", | ||
41 | "url": "https://github.com/SilentCicero", | ||
42 | "contributions": 2, | ||
43 | "additions": 26, | ||
44 | "deletions": 2 | ||
45 | }, | ||
46 | { | ||
47 | "name": "Mr. Chico", | ||
48 | "url": "https://github.com/MrChico", | ||
49 | "contributions": 1, | ||
50 | "additions": 11, | ||
51 | "deletions": 1 | ||
52 | }, | ||
53 | { | ||
54 | "name": "Dũng Trần", | ||
55 | "email": "tad88.dev@gmail.com", | ||
56 | "url": "https://github.com/tad88dev", | ||
57 | "contributions": 2, | ||
58 | "additions": 5, | ||
59 | "deletions": 5 | ||
60 | }, | ||
61 | { | ||
62 | "name": "Alex Beregszaszi", | ||
63 | "email": "alex@rtfs.hu", | ||
64 | "url": "https://github.com/axic", | ||
65 | "contributions": 77, | ||
66 | "additions": 1796, | ||
67 | "deletions": 642 | ||
68 | }, | ||
69 | { | ||
70 | "name": "Taylor Gerring", | ||
71 | "url": "https://github.com/tgerring", | ||
72 | "contributions": 1, | ||
73 | "additions": 1, | ||
74 | "deletions": 1 | ||
75 | }, | ||
76 | { | ||
77 | "name": "Kirill Fomichev", | ||
78 | "email": "fanatid@ya.ru", | ||
79 | "url": "https://github.com/fanatid", | ||
80 | "contributions": 8, | ||
81 | "additions": 32, | ||
82 | "deletions": 16 | ||
83 | }, | ||
84 | { | ||
85 | "name": "kumavis", | ||
86 | "email": "aaron@kumavis.me", | ||
87 | "url": "https://github.com/kumavis", | ||
88 | "contributions": 2, | ||
89 | "additions": 2, | ||
90 | "deletions": 2 | ||
91 | }, | ||
92 | { | ||
93 | "name": "Alexander Sinyagin", | ||
94 | "email": "sinyagin.alexander@gmail.com", | ||
95 | "url": "https://github.com/asinyagin", | ||
96 | "contributions": 1, | ||
97 | "additions": 3, | ||
98 | "deletions": 1 | ||
99 | } | ||
100 | ], | ||
101 | "license": "MPL-2.0", | ||
102 | "bugs": { | ||
103 | "url": "https://github.com/ethereumjs/ethereumjs-util/issues" | ||
104 | }, | ||
105 | "homepage": "https://github.com/ethereumjs/ethereumjs-util", | ||
106 | "dependencies": { | ||
107 | "bn.js": "^4.11.0", | ||
108 | "create-hash": "^1.1.2", | ||
109 | "ethjs-util": "^0.1.6", | ||
110 | "keccak": "^1.0.2", | ||
111 | "rlp": "^2.0.0", | ||
112 | "safe-buffer": "^5.1.1", | ||
113 | "secp256k1": "^3.0.1" | ||
114 | }, | ||
115 | "devDependencies": {}, | ||
116 | "standard": { | ||
117 | "globals": [ | ||
118 | "describe", | ||
119 | "it" | ||
120 | ], | ||
121 | "ignore": [ | ||
122 | "dist/**" | ||
123 | ] | ||
124 | } | ||
125 | } | ||
diff --git a/libs/ethereumjs-util/readme.md b/libs/ethereumjs-util/readme.md new file mode 100644 index 0000000..2cc717a --- /dev/null +++ b/libs/ethereumjs-util/readme.md | |||
@@ -0,0 +1,4 @@ | |||
1 | Build (will create a bundle and copy it to /tmp/ethereumjs-util.js): | ||
2 | |||
3 | npm install | ||
4 | npm run build | ||