更多的字符串处理方法可以看
addHexPrefix
Adds "0x" to a given String if it does not already start with "0x"
在没有"0x"前缀的十六进制字符串前面添加前缀
Parameters
-
strString
Returns String
baToJSON
Converts a Buffer or Array to JSON
将Buffer或Array转成JSON
Parameters
Returns (Array | String | null)
BN
Type: Function
大数
bufferToHex
Converts a Buffer into a hex String
将 Buffer转成16进制的String
Parameters
-
bufBuffer
Returns String
bufferToInt
Converts a Buffer to a Number
将Buffer转成Number
Parameters
-
bufBuffer -
Throws any If the input number exceeds 53 bits.
Returns Number
defineProperties
Defines properties on a Object. It make the assumption that underlying data is binary.
定义对象的属性。假设下面的数据都为二进制数
Parameters
-
selfObject theObjectto define properties on 定义属性的对象
-
fieldsArray an array fields to define. Fields can contain:-
name- the name of the properties
-
length- the number of bytes the field can have -
allowLess- if the field can be less than the length allowEmpty
-
-
dataany data to be validated against the definitions
ecrecover
ECDSA public key recovery from signature
从签名r,s,v和消息hash得到公钥
Parameters
Returns Buffer publicKey
ecsign
ECDSA sign签名,返回r,s,v
Parameters
-
msgHashBuffer -
privateKeyBuffer -
chainIdNumber根据chainId是否设置来决定v=v+2 * chainId+35(chainId不为0)还是v= v+27(为0或undefined)
Returns Object
fromRpcSig
Convert signature format of the eth_sign RPC method to signature parameters
将eth_sign RPC方法的签名格式转换为签名参数,即将65字节的格式转成分开的r,s,v
NOTE: all because of a bug in geth: https://github.com/ethereum/go-ethereum/issues/2053
即r,v,s签名中的v = v +27
Parameters
-
sigString
Returns Object
toRpcSig
Convert signature parameters into the format of eth_sign RPC method
将签名参数转成符合eth_sign RPC方法的格式,即将r,s,v转成65字节的格式
Parameters
Returns String sig
fromSigned
Interprets a Buffer as a signed integer and returns a BN. Assumes 256-bit numbers.
将Buffer转成一个签名整数并返回BN大数对象,假设为一个256位的数字
Parameters
-
numBuffer
Returns BN
generateAddress
Generates an address of a newly created contract
为新创建的合约生成一个address(合约即交易,它的address都可以通过合约部署者的账户address及其nonce生成)
Parameters
-
fromBuffer the address which is creating this new address合约部署者账户address -
nonceBuffer the nonce of the from account 账户的nonce(即下一笔交易数)
Returns Buffer
generateAddress2
Generates an address for a contract created using CREATE2
使用CREATE2为合约的创建生成一个address
Parameters
-
fromBuffer the address which is creating this new address -
saltBuffer a salt -
initCodeBuffer the init code of the contract being created 合约的encode
Returns Buffer
hashPersonalMessage
Returns the keccak-256 hash of message, prefixed with the header used by the eth_sign RPC call. The output of this function can be fed into ecsign to produce the same signature as the eth_sign call for a given message, or fed to ecrecover along with a signature to recover the public key used to produce the signature.
返回消息的keccak-256散列,以eth_sign RPC调用使用的头作为前缀。可以将此函数的输出输入ecsign,以生成与给定消息的eth_sign调用相同的签名,也可以将该函数的输出与签名一起输入ecrecovery,以恢复用于生成签名的公钥。
Parameters
message
Returns Buffer hash
importPublic
Converts a public key to the Ethereum format.
将公钥转成以太坊的格式
Parameters
-
publicKeyBuffer
Returns Buffer
isPrecompiled
Returns true if the supplied address belongs to a precompiled account (Byzantium)
如果提供的地址属于预编译帐户,则返回true
Parameters
Returns Boolean
isValidAddress
Checks if the address is a valid. Accepts checksummed addresses too
检查地址是否有效,接受校验和地址
Parameters
-
addressString
Returns Boolean
isValidChecksumAddress
Checks if the address is a valid checksummed address
查看地址是否是有效的校验和地址
Parameters
-
addressBuffer
Returns Boolean
isValidPrivate
Checks if the private key satisfies the rules of the curve secp256k1.
查看私钥是否满足曲线secp256k1的规则
Parameters
-
privateKeyBuffer
Returns Boolean
isValidPublic
Checks if the public key satisfies the rules of the curve secp256k1 and the requirements of Ethereum.
查看公钥是否满足曲线secp256k1的规则和以太坊的需求
Parameters
-
publicKeyBuffer The two points of an uncompressed key, unless sanitize is enabled -
sanitizeBoolean Accept public keys in other formats (optional, defaultfalse)
Returns Boolean
keccak
Creates Keccak hash of the input
生成输入的Keccak hash散列值
Parameters
-
a(Buffer | Array | String | Number) the input data -
bitsNumber the Keccak width (optional, default256)设置返回的hash值的长度
Returns Buffer
keccak256
Creates Keccak-256 hash of the input, alias for keccak(a, 256)
生成输入的256比特Keccak hash散列值,返回hash值的长度为256bits.
与keccak(a, 256)等价,因为keccak默认为256,所以keccak256(s)==keccak(s)
Parameters
Returns Buffer
privateToAddress
Returns the ethereum address of a given private key
根据私钥得到以太坊地址
Parameters
-
privateKeyBuffer A private key must be 256 bits wide
Returns Buffer
pubToAddress
Returns the ethereum address of a given public key. Accepts "Ethereum public keys" and SEC1 encoded keys.
根据公钥得到以太坊地址,接受以太坊公钥和SEC1加密密钥
Parameters
-
pubKeyBuffer The two points of an uncompressed key, unless sanitize is enabled -
sanitizeBoolean Accept public keys in other formats (optional, defaultfalse)
Returns Buffer
ripemd160
Creates RIPEMD160 hash of the input
生成输入的RIPEMD160 hash散列值
Parameters
-
a(Buffer | Array | String | Number) the input data -
paddedBoolean whether it should be padded to 256 bits or not设置为true,则当返回的hash值长度不为256时左填充0
Returns Buffer
rlp
Type: Function
rlphash
Creates SHA-3 hash of the RLP encoded version of the input
生成输入的RLP编码的SHA-3 hash散列值
Parameters
Returns Buffer
secp256k1
Type: Object
setLengthRight
Right Pads an Array or Buffer with leading zeros till it has length bytes. Or it truncates the beginning if it exceeds.
使用0右填充Array or Buffer直至长度满足length,或是当长度超过的时候进行截断。
⚠️setLengthRight其实就是调用了setLengthLeft(msg, length, true)
Parameters
setLengthLeft
Left Pads an Array or Buffer with leading zeros till it has length bytes. Or it truncates the beginning if it exceeds.
使用0左填充Array or Buffer直至长度满足length,或是当长度超过的时候进行截断
Parameters
-
msg(Buffer | Array) the value to pad -
lengthNumber the number of bytes the output should be -
rightBoolean whether to start padding form the left or right (optional, defaultfalse)right = true就相当于右填充
sha256
Creates SHA256 hash of the input
创建输入的SHA256 hash散列值
Parameters
Returns Buffer
toBuffer
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.
将值转成Buffer,值输入支持Buffer, String, Number, null/undefined, BN和具有toArray()方法的对象
Parameters
-
vany the value
toChecksumAddress
Returns a checksummed address
将address转成校验和地址
Parameters
-
addressString
Returns String
toUnsigned
Converts a BN to an unsigned integer and returns it as a Buffer. Assumes 256-bit numbers.
将BN转成无符号整数并将其返回成Buffer,是256比特的数字
Parameters
-
numBN
Returns Buffer
unpad
Trims leading zeros from a Buffer or an Array
将Buffer或Array前面的0删掉
Parameters
Returns (Buffer | Array | String)
isValidSignature
Validate ECDSA signature
是否是有效的签名
Parameters
Returns Boolean
isZeroAddress
Checks if a given address is a zero address
查看给定的address是否是0x0000...000地址
Parameters
-
addressString
Returns Boolean
KECCAK256_NULL
Keccak-256 hash of null (a Buffer)
Buffer类型的null的Keccak-256 hash散列值
Type: Buffer
KECCAK256_NULL_S
Keccak-256 hash of null (a String)
String类型的null的Keccak-256 hash散列值
Type: String
KECCAK256_RLP
Keccak-256 hash of the RLP of null (a Buffer)
Buffer类型的RLP编码格式的null的Keccak-256 hash散列值
Type: Buffer
KECCAK256_RLP_S
Keccak-256 hash of the RLP of null (a String)
Type: String
String类型的RLP编码格式的null的Keccak-256 hash散列值
KECCAK256_RLP_ARRAY
Keccak-256 of an RLP of an empty array (a Buffer)
Buffer类型的RLP编码格式的空数组的Keccak-256 hash散列值
Type: Buffer
KECCAK256_RLP_ARRAY_S
Keccak-256 of an RLP of an empty array (a String)
String类型的RLP编码格式的空数组的Keccak-256 hash散列值
Type: String
MAX_INTEGER
the max integer that this VM can handle (a BN)
设置虚拟机能够处理的最大整数
Type: BN
privateToPublic
Returns the ethereum public key of a given private key
根据私钥得到以太坊的公钥
Parameters
-
privateKeyBuffer A private key must be 256 bits wide
Returns Buffer
TWO_POW256
2^256 (a BN)
得到2的256次幂的值
Type: BN
zeroAddress
Returns a zero address
返回0x000...000地址
Returns String
zeros
Returns a buffer filled with 0s
返回布满0的buffer
Parameters
-
bytesNumber the number of bytes the buffer should be
Returns Buffer
实现代码:
const createKeccakHash = require('keccak') const secp256k1 = require('secp256k1') const assert = require('assert') const rlp = require('rlp') const BN = require('bn.js') const createHash = require('create-hash') const Buffer = require('safe-buffer').Buffer Object.assign(exports, require('ethjs-util')) /** * the max integer that this VM can handle (a ```BN```) * @var {BN} MAX_INTEGER */ exports.MAX_INTEGER = new BN('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16) /** * 2^256 (a ```BN```) * @var {BN} TWO_POW256 */ exports.TWO_POW256 = new BN('10000000000000000000000000000000000000000000000000000000000000000', 16) /** * Keccak-256 hash of null (a ```String```) * @var {String} KECCAK256_NULL_S */ exports.KECCAK256_NULL_S = 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' /** * Keccak-256 hash of null (a ```Buffer```) * @var {Buffer} KECCAK256_NULL */ exports.KECCAK256_NULL = Buffer.from(exports.KECCAK256_NULL_S, 'hex') /** * Keccak-256 of an RLP of an empty array (a ```String```) * @var {String} KECCAK256_RLP_ARRAY_S */ exports.KECCAK256_RLP_ARRAY_S = '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' /** * Keccak-256 of an RLP of an empty array (a ```Buffer```) * @var {Buffer} KECCAK256_RLP_ARRAY */ exports.KECCAK256_RLP_ARRAY = Buffer.from(exports.KECCAK256_RLP_ARRAY_S, 'hex') /** * Keccak-256 hash of the RLP of null (a ```String```) * @var {String} KECCAK256_RLP_S */ exports.KECCAK256_RLP_S = '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' /** * Keccak-256 hash of the RLP of null (a ```Buffer```) * @var {Buffer} KECCAK256_RLP */ exports.KECCAK256_RLP = Buffer.from(exports.KECCAK256_RLP_S, 'hex') /** * [`BN`](https://github.com/indutny/bn.js) * @var {Function} */ exports.BN = BN /** * [`rlp`](https://github.com/ethereumjs/rlp) * @var {Function} */ exports.rlp = rlp /** * [`secp256k1`](https://github.com/cryptocoinjs/secp256k1-node/) * @var {Object} */ exports.secp256k1 = secp256k1 /** * Returns a buffer filled with 0s * @method zeros * @param {Number} bytes the number of bytes the buffer should be * @return {Buffer} */ exports.zeros = function (bytes) { return Buffer.allocUnsafe(bytes).fill(0) } /** * Returns a zero address * @method zeroAddress * @return {String} */ exports.zeroAddress = function () { const addressLength = 20 const zeroAddress = exports.zeros(addressLength) return exports.bufferToHex(zeroAddress) } /** * Left Pads an `Array` or `Buffer` with leading zeros till it has `length` bytes. * Or it truncates the beginning if it exceeds. * @method setLengthLeft * @param {Buffer|Array} msg the value to pad * @param {Number} length the number of bytes the output should be * @param {Boolean} [right=false] whether to start padding form the left or right * @return {Buffer|Array} */ exports.setLengthLeft = exports.setLength = function (msg, length, right) { const buf = exports.zeros(length) msg = exports.toBuffer(msg) if (right) { if (msg.length < length) { msg.copy(buf)//将msg的所有数据复制到buf,其实就相当于右填充 return buf } return msg.slice(0, length) } else { if (msg.length < length) { msg.copy(buf, length - msg.length)//将msg的所有数据复制到buf,从buf的length - msg.length位置开始 return buf } return msg.slice(-length) } } /** * Right Pads an `Array` or `Buffer` with leading zeros till it has `length` bytes. * Or it truncates the beginning if it exceeds. * @param {Buffer|Array} msg the value to pad * @param {Number} length the number of bytes the output should be * @return {Buffer|Array} */ exports.setLengthRight = function (msg, length) { return exports.setLength(msg, length, true) } /** * Trims leading zeros from a `Buffer` or an `Array` * @param {Buffer|Array|String} a * @return {Buffer|Array|String} */ exports.unpad = exports.stripZeros = function (a) { a = exports.stripHexPrefix(a) let first = a[0] while (a.length > 0 && first.toString() === '0') { a = a.slice(1) first = a[0] } return a } /** * 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. * @param {*} v the value */ exports.toBuffer = function (v) { if (!Buffer.isBuffer(v)) { if (Array.isArray(v)) { v = Buffer.from(v) } else if (typeof v === 'string') { if (exports.isHexString(v)) { v = Buffer.from(exports.padToEven(exports.stripHexPrefix(v)), 'hex') } else { v = Buffer.from(v) } } else if (typeof v === 'number') { v = exports.intToBuffer(v) } else if (v === null || v === undefined) { v = Buffer.allocUnsafe(0) } else if (BN.isBN(v)) { v = v.toArrayLike(Buffer) } else if (v.toArray) { // converts a BN to a Buffer v = Buffer.from(v.toArray()) } else { throw new Error('invalid type') } } return v } /** * Converts a `Buffer` to a `Number` * @param {Buffer} buf * @return {Number} * @throws If the input number exceeds 53 bits. */ exports.bufferToInt = function (buf) { return new BN(exports.toBuffer(buf)).toNumber() } /** * Converts a `Buffer` into a hex `String` * @param {Buffer} buf * @return {String} */ exports.bufferToHex = function (buf) { buf = exports.toBuffer(buf) return '0x' + buf.toString('hex') } /** * Interprets a `Buffer` as a signed integer and returns a `BN`. Assumes 256-bit numbers. * @param {Buffer} num * @return {BN} */ exports.fromSigned = function (num) { return new BN(num).fromTwos(256) } /** * Converts a `BN` to an unsigned integer and returns it as a `Buffer`. Assumes 256-bit numbers. * @param {BN} num * @return {Buffer} */ exports.toUnsigned = function (num) { return Buffer.from(num.toTwos(256).toArray()) } /** * Creates Keccak hash of the input * @param {Buffer|Array|String|Number} a the input data * @param {Number} [bits=256] the Keccak width * @return {Buffer} */ exports.keccak = function (a, bits) { a = exports.toBuffer(a) if (!bits) bits = 256 return createKeccakHash('keccak' + bits).update(a).digest() } /** * Creates Keccak-256 hash of the input, alias for keccak(a, 256) * @param {Buffer|Array|String|Number} a the input data * @return {Buffer} */ exports.keccak256 = function (a) { return exports.keccak(a) } /** * Creates SHA256 hash of the input * @param {Buffer|Array|String|Number} a the input data * @return {Buffer} */ exports.sha256 = function (a) { a = exports.toBuffer(a) return createHash('sha256').update(a).digest() } /** * Creates RIPEMD160 hash of the input * @param {Buffer|Array|String|Number} a the input data * @param {Boolean} padded whether it should be padded to 256 bits or not * @return {Buffer} */ exports.ripemd160 = function (a, padded) { a = exports.toBuffer(a) const hash = createHash('rmd160').update(a).digest() if (padded === true) { return exports.setLength(hash, 32) } else { return hash } } /** * Creates SHA-3 hash of the RLP encoded version of the input * @param {Buffer|Array|String|Number} a the input data * @return {Buffer} */ exports.rlphash = function (a) { return exports.keccak(rlp.encode(a)) } /** * Checks if the private key satisfies the rules of the curve secp256k1. * @param {Buffer} privateKey * @return {Boolean} */ exports.isValidPrivate = function (privateKey) { return secp256k1.privateKeyVerify(privateKey) } /** * Checks if the public key satisfies the rules of the curve secp256k1 * and the requirements of Ethereum. * @param {Buffer} publicKey The two points of an uncompressed key, unless sanitize is enabled * @param {Boolean} [sanitize=false] Accept public keys in other formats * @return {Boolean} */ exports.isValidPublic = function (publicKey, sanitize) { if (publicKey.length === 64) { // Convert to SEC1 for secp256k1 return secp256k1.publicKeyVerify(Buffer.concat([ Buffer.from([4]), publicKey ])) } if (!sanitize) { return false } return secp256k1.publicKeyVerify(publicKey) } /** * Returns the ethereum address of a given public key. * Accepts "Ethereum public keys" and SEC1 encoded keys. * @param {Buffer} pubKey The two points of an uncompressed key, unless sanitize is enabled * @param {Boolean} [sanitize=false] Accept public keys in other formats * @return {Buffer} */ exports.pubToAddress = exports.publicToAddress = function (pubKey, sanitize) { pubKey = exports.toBuffer(pubKey) if (sanitize && (pubKey.length !== 64)) { pubKey = secp256k1.publicKeyConvert(pubKey, false).slice(1) } assert(pubKey.length === 64) // Only take the lower 160bits of the hash return exports.keccak(pubKey).slice(-20) } /** * Returns the ethereum public key of a given private key * @param {Buffer} privateKey A private key must be 256 bits wide * @return {Buffer} */ const privateToPublic = exports.privateToPublic = function (privateKey) { privateKey = exports.toBuffer(privateKey) // skip the type flag and use the X, Y points return secp256k1.publicKeyCreate(privateKey, false).slice(1) } /** * Converts a public key to the Ethereum format. * @param {Buffer} publicKey * @return {Buffer} */ exports.importPublic = function (publicKey) { publicKey = exports.toBuffer(publicKey) if (publicKey.length !== 64) { publicKey = secp256k1.publicKeyConvert(publicKey, false).slice(1) } return publicKey } /** * ECDSA sign * @param {Buffer} msgHash * @param {Buffer} privateKey * @param {Number} [chainId] * @return {Object} */ exports.ecsign = function (msgHash, privateKey, chainId) { const sig = secp256k1.sign(msgHash, privateKey) const ret = {} ret.r = sig.signature.slice(0, 32) ret.s = sig.signature.slice(32, 64) ret.v = chainId ? sig.recovery + (chainId * 2 + 35) : sig.recovery + 27 return ret } /** * Returns the keccak-256 hash of `message`, prefixed with the header used by the `eth_sign` RPC call. * The output of this function can be fed into `ecsign` to produce the same signature as the `eth_sign` * call for a given `message`, or fed to `ecrecover` along with a signature to recover the public key * used to produce the signature. * @param message * @returns {Buffer} hash */ exports.hashPersonalMessage = function (message) { const prefix = exports.toBuffer('\u0019Ethereum Signed Message:\n' + message.length.toString()) return exports.keccak(Buffer.concat([prefix, message])) } /** * ECDSA public key recovery from signature * @param {Buffer} msgHash * @param {Number} v * @param {Buffer} r * @param {Buffer} s * @param {Number} [chainId] * @return {Buffer} publicKey */ exports.ecrecover = function (msgHash, v, r, s, chainId) { const signature = Buffer.concat([exports.setLength(r, 32), exports.setLength(s, 32)], 64) const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') } const senderPubKey = secp256k1.recover(msgHash, signature, recovery) return secp256k1.publicKeyConvert(senderPubKey, false).slice(1) } /** * Convert signature parameters into the format of `eth_sign` RPC method * @param {Number} v * @param {Buffer} r * @param {Buffer} s * @param {Number} [chainId] * @return {String} sig */ exports.toRpcSig = function (v, r, s, chainId) { let recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') } // geth (and the RPC eth_sign method) uses the 65 byte format used by Bitcoin return exports.bufferToHex(Buffer.concat([ exports.setLengthLeft(r, 32), exports.setLengthLeft(s, 32), exports.toBuffer(v) ])) } /** * Convert signature format of the `eth_sign` RPC method to signature parameters * NOTE: all because of a bug in geth: https://github.com/ethereum/go-ethereum/issues/2053 * @param {String} sig * @return {Object} */ exports.fromRpcSig = function (sig) { sig = exports.toBuffer(sig) // NOTE: with potential introduction of chainId this might need to be updated if (sig.length !== 65) { throw new Error('Invalid signature length') } let v = sig[64] // support both versions of `eth_sign` responses if (v < 27) { v += 27 } return { v: v, r: sig.slice(0, 32), s: sig.slice(32, 64) } } /** * Returns the ethereum address of a given private key * @param {Buffer} privateKey A private key must be 256 bits wide * @return {Buffer} */ exports.privateToAddress = function (privateKey) { return exports.publicToAddress(privateToPublic(privateKey)) } /** * Checks if the address is a valid. Accepts checksummed addresses too * @param {String} address * @return {Boolean} */ exports.isValidAddress = function (address) { return /^0x[0-9a-fA-F]{40}$/.test(address) } /** * Checks if a given address is a zero address * @method isZeroAddress * @param {String} address * @return {Boolean} */ exports.isZeroAddress = function (address) { const zeroAddress = exports.zeroAddress() return zeroAddress === exports.addHexPrefix(address) } /** * Returns a checksummed address * @param {String} address * @return {String} */ exports.toChecksumAddress = function (address) { address = exports.stripHexPrefix(address).toLowerCase() const hash = exports.keccak(address).toString('hex') let ret = '0x' for (let i = 0; i < address.length; i++) { if (parseInt(hash[i], 16) >= 8) { ret += address[i].toUpperCase() } else { ret += address[i] } } return ret } /** * Checks if the address is a valid checksummed address * @param {Buffer} address * @return {Boolean} */ exports.isValidChecksumAddress = function (address) { return exports.isValidAddress(address) && (exports.toChecksumAddress(address) === address) } /** * Generates an address of a newly created contract * @param {Buffer} from the address which is creating this new address * @param {Buffer} nonce the nonce of the from account * @return {Buffer} */ exports.generateAddress = function (from, nonce) { from = exports.toBuffer(from) nonce = new BN(nonce) if (nonce.isZero()) { // in RLP we want to encode null in the case of zero nonce // read the RLP documentation for an answer if you dare nonce = null } else { nonce = Buffer.from(nonce.toArray()) } // Only take the lower 160bits of the hash return exports.rlphash([from, nonce]).slice(-20) } /** * Generates an address for a contract created using CREATE2 * @param {Buffer} from the address which is creating this new address * @param {Buffer} salt a salt * @param {Buffer} initCode the init code of the contract being created * @return {Buffer} */ exports.generateAddress2 = function (from, salt, initCode) { from = exports.toBuffer(from) salt = exports.toBuffer(salt) initCode = exports.toBuffer(initCode) assert(from.length === 20) assert(salt.length === 32) let address = exports.keccak256(Buffer.concat([ Buffer.from('ff', 'hex'), from, salt, exports.keccak256(initCode) ])) return address.slice(-20) } /** * Returns true if the supplied address belongs to a precompiled account (Byzantium) * @param {Buffer|String} address * @return {Boolean} */ exports.isPrecompiled = function (address) { const a = exports.unpad(address) return a.length === 1 && a[0] >= 1 && a[0] <= 8 } /** * Adds "0x" to a given `String` if it does not already start with "0x" * @param {String} str * @return {String} */ exports.addHexPrefix = function (str) { if (typeof str !== 'string') { return str } return exports.isHexPrefixed(str) ? str : '0x' + str } /** * Validate ECDSA signature * @method isValidSignature * @param {Buffer} v * @param {Buffer} r * @param {Buffer} s * @param {Boolean} [homestead=true] * @param {Number} [chainId] * @return {Boolean} */ exports.isValidSignature = function (v, r, s, homestead, chainId) { const SECP256K1_N_DIV_2 = new BN('7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16) const SECP256K1_N = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) if (r.length !== 32 || s.length !== 32) { return false } if (!isValidSigRecovery(calculateSigRecovery(v, chainId))) { return false } r = new BN(r) s = new BN(s) if (r.isZero() || r.gt(SECP256K1_N) || s.isZero() || s.gt(SECP256K1_N)) { return false } if ((homestead === false) && (new BN(s).cmp(SECP256K1_N_DIV_2) === 1)) { return false } return true } /** * Converts a `Buffer` or `Array` to JSON * @param {Buffer|Array} ba * @return {Array|String|null} */ exports.baToJSON = function (ba) { if (Buffer.isBuffer(ba)) { return '0x' + ba.toString('hex') } else if (ba instanceof Array) { const array = [] for (let i = 0; i < ba.length; i++) { array.push(exports.baToJSON(ba[i])) } return array } } /** * Defines properties on a `Object`. It make the assumption that underlying data is binary. * @param {Object} self the `Object` to define properties on * @param {Array} fields an array fields to define. Fields can contain: * * `name` - the name of the properties * * `length` - the number of bytes the field can have * * `allowLess` - if the field can be less than the length * * `allowEmpty` * @param {*} data data to be validated against the definitions */ exports.defineProperties = function (self, fields, data) { self.raw = [] self._fields = [] // attach the `toJSON` self.toJSON = function (label) { if (label) { const obj = {} self._fields.forEach((field) => { obj[field] = '0x' + self[field].toString('hex') }) return obj } return exports.baToJSON(this.raw) } self.serialize = function serialize () { return rlp.encode(self.raw) } fields.forEach((field, i) => { self._fields.push(field.name) function getter () { return self.raw[i] } function setter (v) { v = exports.toBuffer(v) if (v.toString('hex') === '00' && !field.allowZero) { v = Buffer.allocUnsafe(0) } if (field.allowLess && field.length) { v = exports.stripZeros(v) assert(field.length >= v.length, 'The field ' + field.name + ' must not have more ' + field.length + ' bytes') } else if (!(field.allowZero && v.length === 0) && field.length) { assert(field.length === v.length, 'The field ' + field.name + ' must have byte length of ' + field.length) } self.raw[i] = v } Object.defineProperty(self, field.name, { enumerable: true, configurable: true, get: getter, set: setter }) if (field.default) { self[field.name] = field.default } // attach alias if (field.alias) { Object.defineProperty(self, field.alias, { enumerable: false, configurable: true, set: setter, get: getter }) } }) // if the constuctor is passed data if (data) { if (typeof data === 'string') { data = Buffer.from(exports.stripHexPrefix(data), 'hex') } if (Buffer.isBuffer(data)) { data = rlp.decode(data) } if (Array.isArray(data)) { if (data.length > self._fields.length) { throw (new Error('wrong number of fields in data')) } // make sure all the items are buffers data.forEach((d, i) => { self[self._fields[i]] = exports.toBuffer(d) }) } else if (typeof data === 'object') { const keys = Object.keys(data) fields.forEach((field) => { if (keys.indexOf(field.name) !== -1) self[field.name] = data[field.name] if (keys.indexOf(field.alias) !== -1) self[field.alias] = data[field.alias] }) } else { throw new Error('invalid data') } } } function calculateSigRecovery (v, chainId) { return chainId ? v - (2 * chainId + 35) : v - 27 } function isValidSigRecovery (recovery) { return recovery === 0 || recovery === 1 }