# all files / ethereumjs-tx/ index.js

97.26% Statements 71/73
95.35% Branches 41/43
100% Functions 12/12
98.57% Lines 69/70

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    1×
    1×
    1×

    1×

    283×

    283×

    283×

    155×

    155×
    147×
    147×

    147×
    147×

    16×

    105×

    105×
    105×
    6×

    99×
    25×
    25×
    25×
    25×
    25×
    25×

    74×

    105×

    3×

    70×
    6×

    64×
    64×
    64×

    67×
    11×

    67×

    80×

    80×

    80×
    80×
    80×
    12×

    80×

    9×

    71×

    6×
    6×
    6×
    1×

    6×

    68×
    68×
    68×
    20975138×

    68×

    66×
    66×
    1×

    66×

    1×

    59×
    59×
    6×

    59×
    6×

    59×
    53×

    6×

    1×
     '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: '00',
     *   gasPrice: '09184e72a000',
     *   gasLimit: '2710',
     *   to: '0000000000000000000000000000000000000000',
     *   value: '00',
     *   data: '7f7465737432000000000000000000000000000000000000000000000000000000600057',
     *   v: '1c',
     *   r: '5e1d3a76fbf824220eafc8c79ad578ad2b67d01b0c2425eb1f1347e8f50882ab',
     *   s: '5bd428537f05f9830e93792f90ea6a3e2d1ee84952dd96edbae9f658f831ab13'
     * };
     * 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 signature parameter
     * @param {Buffer} data.r EC signature parameter
     * @param {Buffer} data.s EC recovery ID
     * @param {Number} data.chainId EIP 155 chainId - mainnet: 1, ropsten: 3
     * */

    class Transaction {
      constructor (data) {
        data = data || {}
        // Define Properties
        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
      }

      /**
       * 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

        // 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) {
          items = this.raw
        } else {
          if (this._chainId > 0) {
            const raw = this.raw.slice()
            this.v = this._chainId
            this.r = 0
            this.s = 0
            items = this.raw
            this.raw = raw
          } else {
            items = this.raw.slice(0, 6)
          }
        }

        // create hash
        return ethUtil.rlphash(items)
      }

      /**
       * returns the public key of the sender
       * @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) {
          Iif (!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.
        Iif (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
          }
          this._senderPubKey = ethUtil.ecrecover(msgHash, v, this.r, this.s)
        } catch (e) {
          return false
        }

        return !!this._senderPubKey
      }

      /**
       * sign a transaction with a given a 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)
        }
        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 dscription of why the validation failed or return a Bloolean
       * @return {Boolean|String}
       */
      validate (stringError) {
        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