Skip to content
This repository was archived by the owner on Jun 17, 2021. It is now read-only.

Commit 339c287

Browse files
committed
Mv fns to account, buffer, hash, signature and object files
1 parent ca4eef1 commit 339c287

File tree

6 files changed

+676
-634
lines changed

6 files changed

+676
-634
lines changed

src/account.ts

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
const assert = require('assert')
2+
const ethjsUtil = require('ethjs-util')
3+
const secp256k1 = require('secp256k1')
4+
import BN = require('bn.js')
5+
import { toBuffer, addHexPrefix, zeros, bufferToHex, unpad } from './buffer'
6+
import { keccak, keccak256, rlphash } from './hash'
7+
8+
/**
9+
* Returns a zero address.
10+
*/
11+
export const zeroAddress = function(): string {
12+
const addressLength = 20
13+
const addr = zeros(addressLength)
14+
return bufferToHex(addr)
15+
}
16+
17+
/**
18+
* Checks if the address is a valid. Accepts checksummed addresses too.
19+
*/
20+
export const isValidAddress = function(address: string): boolean {
21+
return /^0x[0-9a-fA-F]{40}$/.test(address)
22+
}
23+
24+
/**
25+
* Checks if a given address is a zero address.
26+
*/
27+
export const isZeroAddress = function(address: string): boolean {
28+
const zeroAddr = zeroAddress()
29+
return zeroAddr === addHexPrefix(address)
30+
}
31+
32+
/**
33+
* Returns a checksummed address.
34+
*/
35+
export const toChecksumAddress = function(address: string): string {
36+
address = ethjsUtil.stripHexPrefix(address).toLowerCase()
37+
const hash = keccak(address).toString('hex')
38+
let ret = '0x'
39+
40+
for (let i = 0; i < address.length; i++) {
41+
if (parseInt(hash[i], 16) >= 8) {
42+
ret += address[i].toUpperCase()
43+
} else {
44+
ret += address[i]
45+
}
46+
}
47+
48+
return ret
49+
}
50+
51+
/**
52+
* Checks if the address is a valid checksummed address.
53+
*/
54+
export const isValidChecksumAddress = function(address: string): boolean {
55+
return isValidAddress(address) && toChecksumAddress(address) === address
56+
}
57+
58+
/**
59+
* Generates an address of a newly created contract.
60+
* @param from The address which is creating this new address
61+
* @param nonce The nonce of the from account
62+
*/
63+
export const generateAddress = function(from: Buffer, nonce: Buffer): Buffer {
64+
from = toBuffer(from)
65+
const nonceBN = new BN(nonce)
66+
67+
if (nonceBN.isZero()) {
68+
// in RLP we want to encode null in the case of zero nonce
69+
// read the RLP documentation for an answer if you dare
70+
return rlphash([from, null]).slice(-20)
71+
}
72+
73+
// Only take the lower 160bits of the hash
74+
return rlphash([from, Buffer.from(nonceBN.toArray())]).slice(-20)
75+
}
76+
77+
/**
78+
* Generates an address for a contract created using CREATE2.
79+
* @param from The address which is creating this new address
80+
* @param salt A salt
81+
* @param initCode The init code of the contract being created
82+
*/
83+
export const generateAddress2 = function(
84+
from: Buffer | string,
85+
salt: Buffer | string,
86+
initCode: Buffer | string,
87+
): Buffer {
88+
const fromBuf = toBuffer(from)
89+
const saltBuf = toBuffer(salt)
90+
const initCodeBuf = toBuffer(initCode)
91+
92+
assert(fromBuf.length === 20)
93+
assert(saltBuf.length === 32)
94+
95+
const address = keccak256(
96+
Buffer.concat([Buffer.from('ff', 'hex'), fromBuf, saltBuf, keccak256(initCodeBuf)]),
97+
)
98+
99+
return address.slice(-20)
100+
}
101+
102+
/**
103+
* Returns true if the supplied address belongs to a precompiled account (Byzantium).
104+
*/
105+
export const isPrecompiled = function(address: Buffer | string): boolean {
106+
const a = unpad(address)
107+
return a.length === 1 && a[0] >= 1 && a[0] <= 8
108+
}
109+
110+
/**
111+
* Checks if the private key satisfies the rules of the curve secp256k1.
112+
*/
113+
export const isValidPrivate = function(privateKey: Buffer): boolean {
114+
return secp256k1.privateKeyVerify(privateKey)
115+
}
116+
117+
/**
118+
* Checks if the public key satisfies the rules of the curve secp256k1
119+
* and the requirements of Ethereum.
120+
* @param publicKey The two points of an uncompressed key, unless sanitize is enabled
121+
* @param sanitize Accept public keys in other formats
122+
*/
123+
export const isValidPublic = function(publicKey: Buffer, sanitize: boolean = false): boolean {
124+
if (publicKey.length === 64) {
125+
// Convert to SEC1 for secp256k1
126+
return secp256k1.publicKeyVerify(Buffer.concat([Buffer.from([4]), publicKey]))
127+
}
128+
129+
if (!sanitize) {
130+
return false
131+
}
132+
133+
return secp256k1.publicKeyVerify(publicKey)
134+
}
135+
136+
/**
137+
* Returns the ethereum address of a given public key.
138+
* Accepts "Ethereum public keys" and SEC1 encoded keys.
139+
* @param pubKey The two points of an uncompressed key, unless sanitize is enabled
140+
* @param sanitize Accept public keys in other formats
141+
*/
142+
export const pubToAddress = function(pubKey: Buffer, sanitize: boolean = false): Buffer {
143+
pubKey = toBuffer(pubKey)
144+
if (sanitize && pubKey.length !== 64) {
145+
pubKey = secp256k1.publicKeyConvert(pubKey, false).slice(1)
146+
}
147+
assert(pubKey.length === 64)
148+
// Only take the lower 160bits of the hash
149+
return keccak(pubKey).slice(-20)
150+
}
151+
export const publicToAddress = pubToAddress
152+
153+
/**
154+
* Returns the ethereum address of a given private key.
155+
* @param privateKey A private key must be 256 bits wide
156+
*/
157+
export const privateToAddress = function(privateKey: Buffer): Buffer {
158+
return publicToAddress(privateToPublic(privateKey))
159+
}
160+
161+
/**
162+
* Returns the ethereum public key of a given private key.
163+
* @param privateKey A private key must be 256 bits wide
164+
*/
165+
export const privateToPublic = function(privateKey: Buffer): Buffer {
166+
privateKey = toBuffer(privateKey)
167+
// skip the type flag and use the X, Y points
168+
return secp256k1.publicKeyCreate(privateKey, false).slice(1)
169+
}
170+
171+
/**
172+
* Converts a public key to the Ethereum format.
173+
*/
174+
export const importPublic = function(publicKey: Buffer): Buffer {
175+
publicKey = toBuffer(publicKey)
176+
if (publicKey.length !== 64) {
177+
publicKey = secp256k1.publicKeyConvert(publicKey, false).slice(1)
178+
}
179+
return publicKey
180+
}

src/buffer.ts

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
const ethjsUtil = require('ethjs-util')
2+
import BN = require('bn.js')
3+
4+
/**
5+
* Returns a buffer filled with 0s.
6+
* @param bytes the number of bytes the buffer should be
7+
*/
8+
export const zeros = function(bytes: number): Buffer {
9+
return Buffer.allocUnsafe(bytes).fill(0)
10+
}
11+
12+
/**
13+
* Left Pads an `Array` or `Buffer` with leading zeros till it has `length` bytes.
14+
* Or it truncates the beginning if it exceeds.
15+
* @param msg the value to pad (Buffer|Array)
16+
* @param length the number of bytes the output should be
17+
* @param right whether to start padding form the left or right
18+
* @return (Buffer|Array)
19+
*/
20+
export const setLengthLeft = function(msg: any, length: number, right: boolean = false) {
21+
const buf = zeros(length)
22+
msg = toBuffer(msg)
23+
if (right) {
24+
if (msg.length < length) {
25+
msg.copy(buf)
26+
return buf
27+
}
28+
return msg.slice(0, length)
29+
} else {
30+
if (msg.length < length) {
31+
msg.copy(buf, length - msg.length)
32+
return buf
33+
}
34+
return msg.slice(-length)
35+
}
36+
}
37+
export const setLength = setLengthLeft
38+
39+
/**
40+
* Right Pads an `Array` or `Buffer` with leading zeros till it has `length` bytes.
41+
* Or it truncates the beginning if it exceeds.
42+
* @param msg the value to pad (Buffer|Array)
43+
* @param length the number of bytes the output should be
44+
* @return (Buffer|Array)
45+
*/
46+
export const setLengthRight = function(msg: any, length: number) {
47+
return setLength(msg, length, true)
48+
}
49+
50+
/**
51+
* Trims leading zeros from a `Buffer` or an `Array`.
52+
* @param a (Buffer|Array|String)
53+
* @return (Buffer|Array|String)
54+
*/
55+
export const unpad = function(a: any) {
56+
a = ethjsUtil.stripHexPrefix(a)
57+
let first = a[0]
58+
while (a.length > 0 && first.toString() === '0') {
59+
a = a.slice(1)
60+
first = a[0]
61+
}
62+
return a
63+
}
64+
export const stripZeros = unpad
65+
66+
/**
67+
* 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.
68+
* @param v the value
69+
*/
70+
export const toBuffer = function(v: any): Buffer {
71+
if (!Buffer.isBuffer(v)) {
72+
if (Array.isArray(v)) {
73+
v = Buffer.from(v)
74+
} else if (typeof v === 'string') {
75+
if (ethjsUtil.isHexString(v)) {
76+
v = Buffer.from(ethjsUtil.padToEven(ethjsUtil.stripHexPrefix(v)), 'hex')
77+
} else {
78+
v = Buffer.from(v)
79+
}
80+
} else if (typeof v === 'number') {
81+
v = ethjsUtil.intToBuffer(v)
82+
} else if (v === null || v === undefined) {
83+
v = Buffer.allocUnsafe(0)
84+
} else if (BN.isBN(v)) {
85+
v = v.toArrayLike(Buffer)
86+
} else if (v.toArray) {
87+
// converts a BN to a Buffer
88+
v = Buffer.from(v.toArray())
89+
} else {
90+
throw new Error('invalid type')
91+
}
92+
}
93+
return v
94+
}
95+
96+
/**
97+
* Converts a `Buffer` to a `Number`.
98+
* @param buf `Buffer` object to convert
99+
* @throws If the input number exceeds 53 bits.
100+
*/
101+
export const bufferToInt = function(buf: Buffer): number {
102+
return new BN(toBuffer(buf)).toNumber()
103+
}
104+
105+
/**
106+
* Converts a `Buffer` into a hex `String`.
107+
* @param buf `Buffer` object to convert
108+
*/
109+
export const bufferToHex = function(buf: Buffer): string {
110+
buf = toBuffer(buf)
111+
return '0x' + buf.toString('hex')
112+
}
113+
114+
/**
115+
* Interprets a `Buffer` as a signed integer and returns a `BN`. Assumes 256-bit numbers.
116+
* @param num Signed integer value
117+
*/
118+
export const fromSigned = function(num: Buffer): BN {
119+
return new BN(num).fromTwos(256)
120+
}
121+
122+
/**
123+
* Converts a `BN` to an unsigned integer and returns it as a `Buffer`. Assumes 256-bit numbers.
124+
* @param num
125+
*/
126+
export const toUnsigned = function(num: BN): Buffer {
127+
return Buffer.from(num.toTwos(256).toArray())
128+
}
129+
130+
/**
131+
* Adds "0x" to a given `String` if it does not already start with "0x".
132+
*/
133+
export const addHexPrefix = function(str: string): string {
134+
if (typeof str !== 'string') {
135+
return str
136+
}
137+
138+
return ethjsUtil.isHexPrefixed(str) ? str : '0x' + str
139+
}
140+
141+
/**
142+
* Converts a `Buffer` or `Array` to JSON.
143+
* @param ba (Buffer|Array)
144+
* @return (Array|String|null)
145+
*/
146+
export const baToJSON = function(ba: any): any {
147+
if (Buffer.isBuffer(ba)) {
148+
return `0x${ba.toString('hex')}`
149+
} else if (ba instanceof Array) {
150+
const array = []
151+
for (let i = 0; i < ba.length; i++) {
152+
array.push(baToJSON(ba[i]))
153+
}
154+
return array
155+
}
156+
}

0 commit comments

Comments
 (0)