zoukankan      html  css  js  c++  java
  • ethereumjs/ethereumjs-tx

    https://github.com/ethereumjs/ethereumjs-tx

    A simple module for creating, manipulating and signing ethereum transactions

    安装:

    npm install ethereumjs-tx --save

    example:

    const EthereumTx = require('ethereumjs-tx')
    const privateKey = Buffer.from('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', 'hex')
    
    const txParams = {
      nonce: '0x00',
      gasPrice: '0x09184e72a000', 
      gasLimit: '0x2710',
      to: '0x0000000000000000000000000000000000000000', 
      value: '0x00', 
      data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
      // EIP 155 chainId - mainnet: 1, ropsten: 3
      chainId: 3
    }
    
    const tx = new EthereumTx(txParams)
    tx.sign(privateKey)
    const serializedTx = tx.serialize()

    Note: this package expects ECMAScript 6 (ES6) as a minimum environment. From browsers lacking ES6 support, please use a shim (like es6-shim) before including any of the builds from this repo.

    支持ES6及以上,如果你的浏览器不支持ES6,则使用shim以兼容

    BROWSER

    For a browser build please see https://github.com/ethereumjs/browser-builds.

    API

    ./docs/

    Transaction

    index.js:46-293

    Creates a new transaction object.

    Parameters

    • data Buffer or Array or Object a transaction can be initiailized with either a buffer containing the RLP serialized transaction or an array of buffers relating to each of the tx Properties, listed in order below in the exmple.Or lastly an Object containing the Properties of the transaction like in the Usage example.For Object and Arrays each of the elements can either be a Buffer, a hex-prefixed (0x) String , Number, or an object with a toBuffer method such as Bignum
      • data.chainId Number EIP 155 chainId - mainnet: 1, ropsten: 3
      • data.gasLimit Buffer transaction gas limit
      • data.gasPrice Buffer transaction gas price
      • data.to Buffer to the to address
      • data.nonce Buffer nonce number
      • data.data Buffer this will contain the data of the message or the init of a contract
      • data.v Buffer EC recovery ID
      • data.r Buffer EC signature parameter
      • data.s Buffer EC signature parameter
      • data.value Buffer the amount of ether sent

    Properties

    • raw Buffer The raw rlp encoded transaction
    var rawTx = {
      nonce: '0x00',
      gasPrice: '0x09184e72a000',
      gasLimit: '0x2710',
      to: '0x0000000000000000000000000000000000000000',
      value: '0x00',
      data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
      v: '0x1c',
      r: '0x5e1d3a76fbf824220eafc8c79ad578ad2b67d01b0c2425eb1f1347e8f50882ab',
      s: '0x5bd428537f05f9830e93792f90ea6a3e2d1ee84952dd96edbae9f658f831ab13'
    };
    var tx = new Transaction(rawTx);

     

    getBaseFee

    index.js:254-260

    the minimum amount of gas the tx must have (DataFee + TxFee + Creation Fee)

    Returns BN

    该笔交易最少要花费的gas

    getChainId

    index.js:172-174

    返回chain id,即说明连接的network

    Returns Buffer

    getDataFee

    index.js:241-248

    The amount of gas paid for the data in this tx为了传递交易中的data需要花费的gas

    Returns BN

    getSenderAddress

    index.js:180-187

    returns the sender's address返回交易发送者的address

    Returns Buffer

    getSenderPublicKey

    index.js:193-198

    returns the public key of the sender返回交易发送者的公钥

    Returns Buffer

    getUpfrontCost

    index.js:266-270

    the up front amount that an account must have for this transaction to be valid为使该交易有效,帐户必须拥有的预备金额

    Returns BN

    hash

    index.js:140-166

    Computes a sha3-256 hash of the serialized tx计算序列化交易的hash值

    Parameters

    • includeSignature [Boolean] whether or not to inculde the signature (optional, default true)是否包含签名,默认为true

    Returns Buffer

    sign

    index.js:228-235

    sign a transaction with a given a private key使用privateKey去对交易进行签名

    Parameters

    • privateKey Buffer

    toCreationAddress

    index.js:131-133

    If the tx's to is to the creation address如果交易的to的地址是0x00000000...,返回true

    Returns Boolean

    validate

    index.js:277-292

    validates the signature and checks to see if it has enough gas确认签名并检查是否有足够的gas

    Parameters

    • stringError [Boolean] whether to return a string with a dscription of why the validation failed or return a Bloolean (optional, default false)是返回一个带有验证失败原因标记的字符串,还是返回一个布尔值

    Returns Boolean or String

    verifySignature

    index.js:204-222

    Determines if the signature is valid确认签名是否有效

    Returns Boolean

    from

    index.js:111-115

    Properties

    • from Buffer (read only) sender address of this transaction, mathematically derived from other parameters.属性from即交易的发送者的address

    serialize

    index.js:104-104

    Returns the rlp encoding of the transaction返回交易的rlp编码格式

    Returns Buffer

    实现代码:

    'use strict'
    const ethUtil = require('ethereumjs-util')
    const fees = require('ethereum-common/params.json')
    const BN = ethUtil.BN
    
    // secp256k1n/2
    const N_DIV_2 = new BN('7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16)
    
    /**
     * Creates a new transaction object.
     *
     * @example
     * var rawTx = {
     *   nonce: '0x00',
     *   gasPrice: '0x09184e72a000',
     *   gasLimit: '0x2710',
     *   to: '0x0000000000000000000000000000000000000000',
     *   value: '0x00',
     *   data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
     *   v: '0x1c',
     *   r: '0x5e1d3a76fbf824220eafc8c79ad578ad2b67d01b0c2425eb1f1347e8f50882ab',
     *   s: '0x5bd428537f05f9830e93792f90ea6a3e2d1ee84952dd96edbae9f658f831ab13'
     * };
     * var tx = new Transaction(rawTx);
     *
     * @class
     * @param {Buffer | Array | Object} data a transaction can be initiailized with either a buffer containing the RLP serialized transaction or an array of buffers relating to each of the tx Properties, listed in order below in the exmple.
     *
     * Or lastly an Object containing the Properties of the transaction like in the Usage example.
     *
     * For Object and Arrays each of the elements can either be a Buffer, a hex-prefixed (0x) String , Number, or an object with a toBuffer method such as Bignum
     *
     * @property {Buffer} raw The raw rlp encoded transaction
     * @param {Buffer} data.nonce nonce number
     * @param {Buffer} data.gasLimit transaction gas limit
     * @param {Buffer} data.gasPrice transaction gas price
     * @param {Buffer} data.to to the to address
     * @param {Buffer} data.value the amount of ether sent
     * @param {Buffer} data.data this will contain the data of the message or the init of a contract
     * @param {Buffer} data.v EC recovery ID
     * @param {Buffer} data.r EC signature parameter
     * @param {Buffer} data.s EC signature parameter
     * @param {Number} data.chainId EIP 155 chainId - mainnet: 1, ropsten: 3
     * */
    
    class Transaction {
      constructor (data) {
        data = data || {}
        // Define Properties,定义生成的raw transaction的属性
        const fields = [{
          name: 'nonce',
          length: 32,
          allowLess: true,
          default: new Buffer([])
        }, {
          name: 'gasPrice',
          length: 32,
          allowLess: true,
          default: new Buffer([])
        }, {
          name: 'gasLimit',
          alias: 'gas',
          length: 32,
          allowLess: true,
          default: new Buffer([])
        }, {
          name: 'to',
          allowZero: true,
          length: 20,
          default: new Buffer([])
        }, {
          name: 'value',
          length: 32,
          allowLess: true,
          default: new Buffer([])
        }, {
          name: 'data',
          alias: 'input',
          allowZero: true,
          default: new Buffer([])
        }, {
          name: 'v',
          allowZero: true,
          default: new Buffer([0x1c])
        }, {
          name: 'r',
          length: 32,
          allowZero: true,
          allowLess: true,
          default: new Buffer([])
        }, {
          name: 's',
          length: 32,
          allowZero: true,
          allowLess: true,
          default: new Buffer([])
        }]
    
        /**
         * Returns the rlp encoding of the transaction
         * @method serialize
         * @return {Buffer}
         * @memberof Transaction
         * @name serialize
         */
        // attached serialize
        ethUtil.defineProperties(this, fields, data)
    
        /**
         * @property {Buffer} from (read only) sender address of this transaction, mathematically derived from other parameters.
         * @name from
         * @memberof Transaction
         */
        Object.defineProperty(this, 'from', {
          enumerable: true,
          configurable: true,
          get: this.getSenderAddress.bind(this)
        })
    
        // calculate chainId from signature
        let sigV = ethUtil.bufferToInt(this.v)
        let chainId = Math.floor((sigV - 35) / 2)
        if (chainId < 0) chainId = 0
    
        // set chainId
        this._chainId = chainId || data.chainId || 0
        this._homestead = true //即该签名的版本为homestead
      }
    
      /**
       * If the tx's `to` is to the creation address
       * @return {Boolean}
       */
      toCreationAddress () {
        return this.to.toString('hex') === ''
      }
    
      /**
       * Computes a sha3-256 hash of the serialized tx
       * @param {Boolean} [includeSignature=true] whether or not to inculde the signature
       * @return {Buffer}
       */
      hash (includeSignature) {
        if (includeSignature === undefined) includeSignature = true //是否包含签名,默认为true
    
        // EIP155 spec:
        // when computing the hash of a transaction for purposes of signing or recovering,
        // instead of hashing only the first six elements (ie. nonce, gasprice, startgas, to, value, data),
        // hash nine elements, with v replaced by CHAIN_ID, r = 0 and s = 0
    
        let items
        if (includeSignature) {//包含签名v,r,s
          items = this.raw
        } else {
          if (this._chainId > 0) {//如果chainId存在,则只是将r,s设为0
            const raw = this.raw.slice()
            this.v = this._chainId
            this.r = 0
            this.s = 0
            items = this.raw
            this.raw = raw
          } else {//如果chainId不存在,则直接将v,r,s去掉
            items = this.raw.slice(0, 6)
          }
        }
    
        // create hash
        return ethUtil.rlphash(items)//然后进行hash
      }
    
      /**
       * returns chain ID
       * @return {Buffer}
       */
      getChainId () {
        return this._chainId
      }
    
      /**
       * returns the sender's address
       * @return {Buffer}
       */
      getSenderAddress () {
        if (this._from) {
          return this._from
        }
        const pubkey = this.getSenderPublicKey()
        this._from = ethUtil.publicToAddress(pubkey)
        return this._from
      }
    
      /**
       * returns the public key of the sender
       * @return {Buffer}
       */
      getSenderPublicKey () {
        if (!this._senderPubKey || !this._senderPubKey.length) {
          if (!this.verifySignature()) throw new Error('Invalid Signature')
        }
        return this._senderPubKey
      }
    
      /**
       * Determines if the signature is valid,确认签名是否有效
       * @return {Boolean}
       */
      verifySignature () {
        const msgHash = this.hash(false)
        // All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.签名的s大于secp256k1n/2则无效
        if (this._homestead && new BN(this.s).cmp(N_DIV_2) === 1) {
          return false
        }
    
        try {
          let v = ethUtil.bufferToInt(this.v)
          if (this._chainId > 0) {
            v -= this._chainId * 2 + 8 //将v值转回正常的chain id值
          }
          this._senderPubKey = ethUtil.ecrecover(msgHash, v, this.r, this.s) //根据签名得到发送者的公钥
        } catch (e) {
          return false
        }
    
        return !!this._senderPubKey //有公钥则返回true,否则为false
      }
    
      /**
       * sign a transaction with a given private key
       * @param {Buffer} privateKey
       */
      sign (privateKey) {
        const msgHash = this.hash(false)
        const sig = ethUtil.ecsign(msgHash, privateKey)
        if (this._chainId > 0) {
          sig.v += this._chainId * 2 + 8
        }
        Object.assign(this, sig)
      }
    
      /**
       * The amount of gas paid for the data in this tx
       * @return {BN}
       */
      getDataFee () {
        const data = this.raw[5]
        const cost = new BN(0)
        for (let i = 0; i < data.length; i++) {
          data[i] === 0 ? cost.iaddn(fees.txDataZeroGas.v) : cost.iaddn(fees.txDataNonZeroGas.v)//根据data是否有值来决定怎么加
        }
        return cost
      }
    
      /**
       * the minimum amount of gas the tx must have (DataFee + TxFee + Creation Fee)
       * @return {BN}
       */
      getBaseFee () {
        const fee = this.getDataFee().iaddn(fees.txGas.v)
        if (this._homestead && this.toCreationAddress()) {
          fee.iaddn(fees.txCreation.v)
        }
        return fee
      }
    
      /**
       * the up front amount that an account must have for this transaction to be valid
       * @return {BN}
       */
      getUpfrontCost () {//为使该交易有效,帐户必须拥有的预备金额
        return new BN(this.gasLimit)
          .imul(new BN(this.gasPrice))
          .iadd(new BN(this.value))
      }
    
      /**
       * validates the signature and checks to see if it has enough gas
       * @param {Boolean} [stringError=false] whether to return a string with a description of why the validation failed or return a Boolean
       * @return {Boolean|String}
       */
      validate (stringError) {//确认签名并检查是否有足够的gas
        const errors = []
        if (!this.verifySignature()) {
          errors.push('Invalid Signature')
        }
    
        if (this.getBaseFee().cmp(new BN(this.gasLimit)) > 0) {
          errors.push([`gas limit is too low. Need at least ${this.getBaseFee()}`])
        }
    
        if (stringError === undefined || stringError === false) {
          return errors.length === 0
        } else {
          return errors.join(' ')
        }
      }
    }
    
    module.exports = Transaction

    实例:

    // see full article here https://wanderer.github.io/ethereum/2014/06/14/creating-and-verifying-transaction-with-node/
    
    var Transaction = require('ethereumjs-tx')
    
    // create a blank transaction
    var tx = new Transaction(null, 1) // mainnet Tx EIP155
    
    // So now we have created a blank transaction but Its not quiet valid yet. We
    // need to add some things to it. Lets start:
    // notice we don't set the `to` field because we are creating a new contract.
    tx.nonce = 0
    tx.gasPrice = 100
    tx.gasLimit = 1000
    tx.value = 0
    tx.data = '0x7f4e616d65526567000000000000000000000000000000000000000000000000003057307f4e616d6552656700000000000000000000000000000000000000000000000000573360455760415160566000396000f20036602259604556330e0f600f5933ff33560f601e5960003356576000335700604158600035560f602b590033560f60365960003356573360003557600035335700'
    
    var privateKey = new Buffer.from('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', 'hex')
    tx.sign(privateKey)
    // We have a signed transaction, Now for it to be fully fundable the account that we signed
    // it with needs to have a certain amount of wei in to. To see how much this
    // account needs we can use the getUpfrontCost() method.
    var feeCost = tx.getUpfrontCost()
    tx.gas = feeCost
    console.log('Total Amount of wei needed:' + feeCost.toString())
    
    // if your wondering how that is caculated it is
    // bytes(data length) * 5
    // + 500 Default transaction fee
    // + gasAmount * gasPrice
    
    // lets serialize the transaction
    
    console.log('---Serialized TX----')
    console.log(tx.serialize().toString('hex'))
    console.log('--------------------')
    
    // Now that we have the serialized transaction we can get AlethZero to except by
    // selecting debug>inject transaction and pasting the transaction serialization and
    // it should show up in pending transaction.
    
    // Parsing & Validating transactions
    // If you have a transaction that you want to verify you can parse it. If you got
    // it directly from the network it will be rlp encoded. You can decode you the rlp
    // module. After that you should have something like
    var rawTx = [
      '0x00',
      '0x09184e72a000',
      '0x2710',
      '0x0000000000000000000000000000000000000000',
      '0x00',
      '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
      '0x1c',
      '0x5e1d3a76fbf824220eafc8c79ad578ad2b67d01b0c2425eb1f1347e8f50882ab',
      '0x5bd428537f05f9830e93792f90ea6a3e2d1ee84952dd96edbae9f658f831ab13'
    ]
    
    var tx2 = new Transaction(rawTx)
    
    // Note rlp.decode will actully produce an array of buffers `new Transaction` will
    // take either an array of buffers or an array of hex strings.
    // So assuming that you were able to parse the tranaction, we will now get the sender's
    // address
    
    console.log('Senders Address: ' + tx2.getSenderAddress().toString('hex'))
    
    // Cool now we know who sent the tx! Lets verfy the signature to make sure it was not
    // some poser.
    
    if (tx2.verifySignature()) {
      console.log('Signature Checks out!')
    }
    
    // And hopefully its verified. For the transaction to be totally valid we would
    // also need to check the account of the sender and see if they have at least
    // `TotalFee`.

    返回:

    userdeMacBook-Pro:test-hd-wallet user$ node transaction.js 
    Total Amount of wei needed:100000
    ---Serialized TX----
    f8e48064830186a08080b8977f4e616d65526567000000000000000000000000000000000000000000000000003057307f4e616d6552656700000000000000000000000000000000000000000000000000573360455760415160566000396000f20036602259604556330e0f600f5933ff33560f601e5960003356576000335700604158600035560f602b590033560f603659600033565733600035576000353357001ca0b8b9fedc076110cd002224a942e9d7099e4a626ebf66cd9301fc18e2c1181806a04e270be511d42189baf14599eb8d6eb5037ab105032dd3e0fa05b43dad4cb4c2
    --------------------
    Senders Address: 1f36f546477cda21bf2296c50976f2740247906f
    Signature Checks out!
  • 相关阅读:
    WPF开发进阶
    WPF开发进阶
    java 架构好文章
    Logback 输出 JPA SQL日志 到文件
    Linux Bash Shell j简单入门
    java内存空间简述
    PowerDesigner 16.5 反向PostgreSQL9.01 中 Unable to list the columns. SQLSTATE = 22003不良的类型值 short : t 解决方法
    Java代码自动部署
    Oracle优化技巧
    string类型与ASCII byte[]转换
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/9990431.html
Copyright © 2011-2022 走看看