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 }