• ethereumjs/ethereumjs-util


    /ethereumjs-util

    Most of the string manipulation methods are provided by ethjs-util

    更多的字符串处理方法可以看ethjs-util

    addHexPrefix

    index.js:563-569

    Adds "0x" to a given String if it does not already start with "0x"

    在没有"0x"前缀的十六进制字符串前面添加前缀

    Parameters

    Returns String

    baToJSON

    index.js:613-623

    Converts a Buffer or Array to JSON

    BufferArray转成JSON

    Parameters

    Returns (Array | String | null)

    BN

    index.js:62-62

    BN

    Type: Function

    大数

    bufferToHex

    index.js:194-197

    Converts a Buffer into a hex String

     Buffer转成16进制的String

    Parameters

    Returns String

    bufferToInt

    index.js:185-187

    Converts a Buffer to a Number

    Buffer转成Number

    Parameters

    • buf Buffer

    • Throws any If the input number exceeds 53 bits.

    Returns Number

    defineProperties

    index.js:635-728

    Defines properties on a Object. It make the assumption that underlying data is binary.

    定义对象的属性。假设下面的数据都为二进制数

    Parameters

    • self Object the Object 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 have
      • allowLess - if the field can be less than the length
      • allowEmpty
    • data any data to be validated against the definitions

    ecrecover

    index.js:383-391

    ECDSA public key recovery from signature

    从签名r,s,v和消息hash得到公钥

    Parameters

    Returns Buffer publicKey

    ecsign

    index.js:351-359

    ECDSA sign签名,返回r,s,v

    Parameters

    • msgHash Buffer
    • privateKey Buffer
    • chainId Number根据chainId是否设置来决定v=v+2 * chainId+35(chainId不为0)还是v= v+27(为0或undefined)

    Returns Object

    fromRpcSig

    index.js:421-440

    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

    Returns Object

    toRpcSig

    index.js:401-413

    Convert signature parameters into the format of eth_sign RPC method

    将签名参数转成符合eth_sign RPC方法的格式,即将r,s,v转成65字节的格式

    Parameters

    Returns String sig

    fromSigned

    index.js:204-206

    Interprets a Buffer as a signed integer and returns a BN. Assumes 256-bit numbers.

    将Buffer转成一个签名整数并返回BN大数对象,假设为一个256位的数字

    Parameters

    Returns BN

    generateAddress

    index.js:507-521

    Generates an address of a newly created contract

    为新创建的合约生成一个address(合约即交易,它的address都可以通过合约部署者的账户address及其nonce生成)

    Parameters

    • from Buffer the address which is creating this new address合约部署者账户address
    • nonce Buffer the nonce of the from account 账户的nonce(即下一笔交易数)

    Returns Buffer

    generateAddress2

    index.js:530-546

    Generates an address for a contract created using CREATE2

    使用CREATE2为合约的创建生成一个address

    Parameters

    • from Buffer the address which is creating this new address
    • salt Buffer a salt
    • initCode Buffer the init code of the contract being created 合约的encode

    Returns Buffer

    hashPersonalMessage

    index.js:369-372

    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

    index.js:336-342

    Converts a public key to the Ethereum format.

    将公钥转成以太坊的格式

    Parameters

    Returns Buffer

    isPrecompiled

    index.js:553-556

    Returns true if the supplied address belongs to a precompiled account (Byzantium)

    如果提供的地址属于预编译帐户,则返回true

    Parameters

    Returns Boolean

    isValidAddress

    index.js:456-458

    Checks if the address is a valid. Accepts checksummed addresses too

    检查地址是否有效,接受校验和地址

    Parameters

    Returns Boolean

    isValidChecksumAddress

    index.js:497-499

    Checks if the address is a valid checksummed address

    查看地址是否是有效的校验和地址

    Parameters

    Returns Boolean

    isValidPrivate

    index.js:279-281

    Checks if the private key satisfies the rules of the curve secp256k1.

    查看私钥是否满足曲线secp256k1的规则

    Parameters

    Returns Boolean

    isValidPublic

    index.js:290-301

    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 enabled
    • sanitize Boolean Accept public keys in other formats (optional, default false)

    Returns Boolean

    keccak

    index.js:223-228

    Creates Keccak hash of the input

    生成输入的Keccak hash散列值

    Parameters

    • a (Buffer | Array | String | Number) the input data
    • bits Number the Keccak width (optional, default 256)设置返回的hash值的长度

    Returns Buffer

    keccak256

    index.js:235-237

    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

    index.js:447-449

    Returns the ethereum address of a given private key

    根据私钥得到以太坊地址

    Parameters

    • privateKey Buffer A private key must be 256 bits wide

    Returns Buffer

    pubToAddress

    index.js:310-318

    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 enabled
    • sanitize Boolean Accept public keys in other formats (optional, default false)

    Returns Buffer

    ripemd160

    index.js:255-263

    Creates RIPEMD160 hash of the input

    生成输入的RIPEMD160 hash散列值

    Parameters

    • a (Buffer | Array | String | Number) the input data
    • padded Boolean whether it should be padded to 256 bits or not设置为true,则当返回的hash值长度不为256时左填充0

    Returns Buffer

    rlp

    index.js:68-68

    rlp编码

    Type: Function

    rlphash

    index.js:270-272

    Creates SHA-3 hash of the RLP encoded version of the input

    生成输入的RLP编码的SHA-3 hash散列值

    Parameters

    Returns Buffer

    secp256k1

    index.js:74-74

    secp256k1

    Type: Object

    setLengthRight

    index.js:131-133

    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

    • msg (Buffer | Array) the value to pad
    • length Number the number of bytes the output should be

    Returns (Buffer | Array)

    setLengthLeft

    index.js:106-122

    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
    • length Number the number of bytes the output should be
    • right Boolean whether to start padding form the left or right (optional, default false)right = true就相当于右填充

    Returns (Buffer | Array)

    sha256

    index.js:244-247

    Creates SHA256 hash of the input

    创建输入的SHA256 hash散列值

    Parameters

    Returns Buffer

    toBuffer

    index.js:153-177

    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

    index.js:476-490

    Returns a checksummed address

    将address转成校验和地址

    Parameters

    Returns String

    toUnsigned

    index.js:213-215

    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

    index.js:140-148

    Trims leading zeros from a Buffer or an Array

    BufferArray前面的0删掉

    Parameters

    Returns (Buffer | Array | String)

    isValidSignature

    index.js:582-606

    Validate ECDSA signature

    是否是有效的签名

    Parameters

    Returns Boolean

    isZeroAddress

    index.js:466-469

    Checks if a given address is a zero address

    查看给定的address是否是0x0000...000地址

    Parameters

    Returns Boolean

    KECCAK256_NULL

    index.js:32-32

    Keccak-256 hash of null (a Buffer)

    Buffer类型的null的Keccak-256 hash散列值

    Type: Buffer

    KECCAK256_NULL_S

    index.js:26-26

    Keccak-256 hash of null (a String)

    String类型的null的Keccak-256 hash散列值

    Type: String

    KECCAK256_RLP

    index.js:56-56

    Keccak-256 hash of the RLP of null (a Buffer)

    Buffer类型的RLP编码格式的null的Keccak-256 hash散列值

    Type: Buffer

    KECCAK256_RLP_S

    index.js:50-50

    Keccak-256 hash of the RLP of null (a String)

    Type: String

    String类型的RLP编码格式的null的Keccak-256 hash散列值

    KECCAK256_RLP_ARRAY

    index.js:44-44

    Keccak-256 of an RLP of an empty array (a Buffer)

    Buffer类型的RLP编码格式的空数组的Keccak-256 hash散列值

    Type: Buffer

    KECCAK256_RLP_ARRAY_S

    index.js:38-38

    Keccak-256 of an RLP of an empty array (a String)

    String类型的RLP编码格式的空数组的Keccak-256 hash散列值

    Type: String

    MAX_INTEGER

    index.js:14-14

    the max integer that this VM can handle (a BN)

    设置虚拟机能够处理的最大整数

    Type: BN

    privateToPublic

    index.js:325-329

    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

    index.js:20-20

    2^256 (a BN)

    得到2的256次幂的值

    Type: BN

    zeroAddress

    index.js:91-95

    Returns a zero address

    返回0x000...000地址

    Returns String

    zeros

    index.js:82-84

    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:
    ' + 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
    }
  • 相关阅读:
    day103 跨域请求 与频率访问限制.
    day 102 GIT 的使用方法.
    day 101 天
    day 100天 VUE 父子传值,单页面.
    JS 在元素后插入元素
    JS 网页加载后执行多个函数
    MySQL 一般操作语句
    PHP 通过设置表单元素name属性生成数组
    PHP SQL语气中value必须添加单引号
    PHP 单引号和双引号的区别
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/9924174.html
Copyright © 2020-2023  润新知