All files / js-dfinity-message index.js

96.55% Statements 28/29
100% Branches 5/5
100% Functions 7/7
96.55% Lines 28/29
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 902x 2x 2x 2x 2x 2x   2x   4x                       4x       2x 2x 2x 2x 2x 2x       6x       6x         4x 4x 4x   4x 4x       4x       2x 2x 2x                       2x       4x                              
const Buffer = require('safe-buffer').Buffer
const Message = require('primea-message')
const Pipe = require('buffer-pipe')
const secp256k1 = require('secp256k1')
const leb128 = require('leb128').unsigned
const crypto = require('node-webcrypto-shim')
 
module.exports = class DfinityTx extends Message {
  serialize (inculdeSig = this.signature.length !== 0) {
    const args = [
      leb128.encode(this.version),
      this.to,
      leb128.encode(this.caps),
      leb128.encode(this.ticks),
      leb128.encode(this.ticksPrice),
      leb128.encode(this.nonce),
      leb128.encode(this.data.length),
      this.data,
      inculdeSig ? this.signature : Buffer.from([]),
      inculdeSig ? Buffer.from([this.recovery]) : Buffer.from([])
    ]
    return Buffer.concat(args)
  }
 
  async sign (secretKey) {
    const serialized = this.serialize(false)
    const hash = await DfinityTx.hash(serialized)
    const sig = secp256k1.sign(hash, secretKey)
    this.signature = sig.signature
    this.recovery = sig.recovery
    return Buffer.concat([serialized, sig.signature, Buffer.from([sig.recovery])])
  }
 
  static hash (serialized) {
    return crypto.subtle.digest({
      name: 'SHA-256'
    }, serialized).then(function (hash) {
      // returns the hash as an ArrayBuffer
      return new Uint8Array(hash)
    })
  }
 
  static async validateSignature (serialized) {
    const sig = serialized.slice(-65, -1)
    const recovery = serialized[serialized.length - 1]
    const hash = await DfinityTx.hash(serialized)
    let publicKey
    try {
      publicKey = secp256k1.recover(hash, sig, recovery)
    } catch (e) {
      publicKey = false
    }
    return publicKey
  }
 
  static async deserialize (raw) {
    const publicKey = await DfinityTx.validateSignature(raw)
    const p = new Pipe(raw)
    const json = {
      version: leb128.read(p),
      to: p.read(20),
      caps: leb128.read(p),
      ticks: leb128.read(p),
      ticksPrice: leb128.read(p),
      nonce: leb128.read(p),
      data: p.read(leb128.read(p)),
      signature: p.read(64),
      recovery: p.buffer[0],
      publicKey: publicKey
    }
    return new DfinityTx(json)
  }
 
  static get defaults () {
    return {
      version: 0,
      to: new Uint8Array(20),
      caps: 0,
      ticks: 0,
      ticksPrice: 0,
      nonce: 0,
      height: 0,
      data: new Uint8Array([]),
      publicKey: new Uint8Array(32),
      signature: new Uint8Array([]),
      recovery: 0
    }
  }
}