更多的字符串處理方法可以看
addHexPrefix
Adds "0x" to a given String
if it does not already start with "0x"
在沒有"0x"前綴的十六進制字符串前面添加前綴
Parameters
str
String
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
buf
Buffer
Returns String
bufferToInt
Converts a Buffer
to a Number
將Buffer
轉成Number
Parameters
-
buf
Buffer -
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
self
Object theObject
to define properties on 定義屬性的對象fields
Array an array fields to define. Fields can contain:-
name
- the name of the properties
length
- the number of bytes the field can haveallowLess
- if the field can be less than the lengthallowEmpty
-
data
any 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
msgHash
BufferprivateKey
BufferchainId
Number根據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
sig
String
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
num
Buffer
Returns BN
generateAddress
Generates an address of a newly created contract
為新創建的合約生成一個address(合約即交易,它的address都可以通過合約部署者的賬戶address及其nonce生成)
Parameters
from
Buffer the address which is creating this new address合約部署者賬戶addressnonce
Buffer the nonce of the from account 賬戶的nonce(即下一筆交易數)
Returns Buffer
generateAddress2
Generates an address for a contract created using CREATE2
使用CREATE2為合約的創建生成一個address
Parameters
from
Buffer the address which is creating this new addresssalt
Buffer a saltinitCode
Buffer 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
publicKey
Buffer
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
address
String
Returns Boolean
isValidChecksumAddress
Checks if the address is a valid checksummed address
查看地址是否是有效的校驗和地址
Parameters
address
Buffer
Returns Boolean
isValidPrivate
Checks if the private key satisfies the rules of the curve secp256k1.
查看私鑰是否滿足曲線secp256k1的規則
Parameters
privateKey
Buffer
Returns Boolean
isValidPublic
Checks if the public key satisfies the rules of the curve secp256k1 and the requirements of Ethereum.
查看公鑰是否滿足曲線secp256k1的規則和以太坊的需求
Parameters
publicKey
Buffer The two points of an uncompressed key, unless sanitize is enabledsanitize
Boolean 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 databits
Number 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
privateKey
Buffer 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
pubKey
Buffer The two points of an uncompressed key, unless sanitize is enabledsanitize
Boolean 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 datapadded
Boolean 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 padlength
Number the number of bytes the output should beright
Boolean 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
v
any the value
toChecksumAddress
Returns a checksummed address
將address轉成校驗和地址
Parameters
address
String
Returns String
toUnsigned
Converts a BN
to an unsigned integer and returns it as a Buffer
. Assumes 256-bit numbers.
將BN轉成無符號整數並將其返回成Buffer,是256比特的數字
Parameters
num
BN
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
address
String
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
privateKey
Buffer 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
bytes
Number 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 }