github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/asset_client/src/services/transactions.js (about)

     1  /**
     2   * Copyright 2017 Intel Corporation
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   * ----------------------------------------------------------------------------
    16   */
    17  'use strict'
    18  
    19  const m = require('mithril')
    20  const _ = require('lodash')
    21  const sjcl = require('sjcl')
    22  const { createHash } = require('crypto')
    23  const secp256k1 = require('sawtooth-sdk/signing/secp256k1')
    24  const {
    25    Transaction,
    26    TransactionHeader,
    27    TransactionList
    28  } = require('sawtooth-sdk/protobuf')
    29  const modals = require('../components/modals')
    30  const api = require('../services/api')
    31  
    32  const STORAGE_KEY = 'asset_track.encryptedKey'
    33  const FAMILY_NAME = 'supply_chain'
    34  const FAMILY_VERSION = '1.1'
    35  const NAMESPACE = '3400de'
    36  
    37  const context = new secp256k1.Secp256k1Context()
    38  let privateKey = null
    39  let signerPublicKey = null
    40  let batcherPublicKey = null
    41  
    42  const setBatcherPubkey = () => {
    43    return api.get('info')
    44      .then(({ pubkey }) => { batcherPublicKey = pubkey })
    45  }
    46  setBatcherPubkey()
    47  
    48  const requestPassword = () => {
    49    let password = null
    50  
    51    return modals.show(modals.BasicModal, {
    52      title: 'Enter Password',
    53      acceptText: 'Submit',
    54      body: m('.container', [
    55        m('.mb-4', 'Please confirm your password to unlock your signing key.'),
    56        m('input.form-control', {
    57          type: 'password',
    58          oninput: m.withAttr('value', value => { password = value })
    59        })
    60      ])
    61    })
    62      .then(() => password)
    63  }
    64  
    65  const createTxn = payload => {
    66    const header = TransactionHeader.encode({
    67      signerPublicKey,
    68      batcherPublicKey,
    69      familyName: FAMILY_NAME,
    70      familyVersion: FAMILY_VERSION,
    71      inputs: [NAMESPACE],
    72      outputs: [NAMESPACE],
    73      nonce: (Math.random() * 10 ** 18).toString(36),
    74      payloadSha512: createHash('sha512').update(payload).digest('hex'),
    75    }).finish()
    76  
    77    return Transaction.create({
    78      payload,
    79      header,
    80      headerSignature: context.sign(header, privateKey)
    81    })
    82  }
    83  
    84  const encodeTxns = transactions => {
    85    return TransactionList.encode({ transactions }).finish()
    86  }
    87  
    88  /**
    89   * Generates a new private key, saving it it to memory and storage (encrypted).
    90   * Returns both a public key and the encrypted private key.
    91   */
    92  const makePrivateKey = password => {
    93    privateKey = context.newRandomPrivateKey()
    94    signerPublicKey = context.getPublicKey(privateKey).asHex()
    95  
    96    const encryptedKey = sjcl.encrypt(password, privateKey.asHex())
    97    window.localStorage.setItem(STORAGE_KEY, encryptedKey)
    98  
    99    return { encryptedKey, publicKey: signerPublicKey }
   100  }
   101  
   102  /**
   103   * Saves an encrypted key to storage, and the decrypted version in memory.
   104   */
   105  const setPrivateKey = (password, encryptedKey) => {
   106    const privateKeyHex = sjcl.decrypt(password, encryptedKey)
   107  
   108    privateKey = secp256k1.Secp256k1PrivateKey.fromHex(privateKeyHex)
   109    signerPublicKey = context.getPublicKey(privateKey).asHex()
   110  
   111    window.localStorage.setItem(STORAGE_KEY, encryptedKey)
   112  
   113    return encryptedKey
   114  }
   115  
   116  /**
   117   * Clears the users private key from memory and storage.
   118   */
   119  const clearPrivateKey = () => {
   120    const encryptedKey = window.localStorage.getItem(STORAGE_KEY)
   121  
   122    window.localStorage.clear(STORAGE_KEY)
   123    privateKey = null
   124    signerPublicKey = null
   125  
   126    return encryptedKey
   127  }
   128  
   129  /**
   130   * Returns the user's private key as promised, requesting password as needed.
   131   */
   132  const getPrivateKey = () => {
   133    return Promise.resolve()
   134    .then(() => {
   135      if (privateKey) return privateKey.asHex()
   136      const encryptedKey = window.localStorage.getItem(STORAGE_KEY)
   137      return requestPassword()
   138        .then(password => sjcl.decrypt(password, encryptedKey))
   139    })
   140  }
   141  
   142  /**
   143   * Re-encrypts a private key with a new password.
   144   */
   145  const changePassword = password => {
   146    return getPrivateKey()
   147      .then(privateKey => {
   148        const encryptedKey = sjcl.encrypt(password, privateKey)
   149        window.localStorage.setItem(STORAGE_KEY, encryptedKey)
   150        return encryptedKey
   151      })
   152  }
   153  
   154  /**
   155   * Wraps a Protobuf payload in a TransactionList and submits it to the API.
   156   * Prompts user for their password if their private key is not in memory.
   157   */
   158  const submit = (payloads, wait = false) => {
   159    if (!_.isArray(payloads)) payloads = [payloads]
   160    return Promise.resolve()
   161      .then(() => {
   162        if (privateKey) return
   163  
   164        return requestPassword()
   165          .then(password => {
   166            const encryptedKey = window.localStorage.getItem(STORAGE_KEY)
   167            setPrivateKey(password, encryptedKey)
   168          })
   169      })
   170      .then(() => {
   171        if (batcherPublicKey) return
   172        return setBatcherPubkey()
   173      })
   174      .then(() => {
   175        const txns = payloads.map(payload => createTxn(payload))
   176        const txnList = encodeTxns(txns)
   177        return api.postBinary(`transactions${wait ? '?wait' : ''}`, txnList)
   178      })
   179  }
   180  
   181  module.exports = {
   182    makePrivateKey,
   183    setPrivateKey,
   184    clearPrivateKey,
   185    getPrivateKey,
   186    changePassword,
   187    submit
   188  }