github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/server/scripts/run_sample_updates.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 _ = require('lodash')
    20  const request = require('request-promise-native')
    21  const protos = require('../blockchain/protos')
    22  const {
    23    awaitServerPubkey,
    24    getTxnCreator,
    25    submitTxns,
    26    encodeTimestampedPayload
    27  } = require('../system/submit_utils')
    28  
    29  const SERVER = process.env.SERVER || 'http://localhost:3000'
    30  const DATA = process.env.DATA
    31  if (DATA.indexOf('.json') === -1) {
    32    throw new Error('Use the "DATA" environment variable to specify a JSON file')
    33  }
    34  
    35  // How much random quantities can vary by
    36  const VARIANCE_FACTOR = 0.75
    37  
    38  // How many times to send each update per minute
    39  // If 0, will send LIMIT updates immediately, then exit
    40  const RATE = process.env.RATE ? Number(process.env.RATE) : 6
    41  
    42  // Maximum number of times to repeat each update
    43  const LIMIT = process.env.LIMIT ? Number(process.env.LIMIT) : 25
    44  
    45  const updateGroups = require(`./${DATA}`)
    46  let createTxn = null
    47  
    48  const createUpdate = (privateKey, recordId, property) => {
    49    return createTxn(privateKey, encodeTimestampedPayload({
    50      action: protos.SCPayload.Action.UPDATE_PROPERTIES,
    51      updateProperties: protos.UpdatePropertiesAction.create({
    52        recordId,
    53        properties: [protos.PropertyValue.create(property)]
    54      })
    55    }))
    56  }
    57  
    58  const getVariance = max => {
    59    if (typeof max === 'object') return _.mapValues(max, getVariance)
    60    const variance = max * VARIANCE_FACTOR * Math.pow(Math.random(), 2)
    61    return Math.random() < 0.5 ? -variance : variance
    62  }
    63  
    64  const updateValue = (update, oldValue) => {
    65    if (typeof update.value === 'object') {
    66      return _.mapValues(update.value, (value, key) => {
    67        return updateValue(_.assign({}, update, { value }), oldValue[key])
    68      })
    69    }
    70  
    71    let value = getVariance(update.value)
    72    if (update.isAlwaysPositive) value = Math.abs(value)
    73    if (update.isAverage) value = update.value + value
    74    if (update.isRelative) value = oldValue + value
    75    return value
    76  }
    77  
    78  const updateProperty = (update, oldValue) => {
    79    oldValue = oldValue || update.startValue || null
    80    const { NUMBER, LOCATION } = protos.PropertySchema.DataType
    81    const property = _.pick(update, 'name', 'dataType')
    82  
    83    if (property.dataType === NUMBER) {
    84      property.numberValue = parseInt(updateValue(update, oldValue || 0))
    85  
    86    } else if (property.dataType === LOCATION) {
    87      const defaultLoc = { latitude: 0, longitude: 0 }
    88      const newLoc = updateValue(update, oldValue || defaultLoc)
    89      const intLoc = _.mapValues(newLoc, parseInt)
    90  
    91      if (intLoc.latitude > 90000000) intLoc.latitude = -90000000
    92      else if (intLoc.latitude < -90000000) intLoc.latitude = 90000000
    93      if (intLoc.longitude > 180000000) intLoc.longitude = -180000000
    94      else if (intLoc.longitude < -180000000) intLoc.longitude = 180000000
    95  
    96      property.locationValue = protos.Location.create(intLoc)
    97  
    98    } else if (property.name === 'tilt') {
    99      oldValue = JSON.parse(oldValue)
   100  
   101      const defaultTilt = { x: 0, y: 0 }
   102      const newTilt = updateValue(update, oldValue || defaultTilt)
   103      const intTilt = _.mapValues(newTilt, parseInt)
   104  
   105      property.stringValue = JSON.stringify(intTilt)
   106  
   107    } else if (property.name === 'shock') {
   108      oldValue = JSON.parse(oldValue)
   109  
   110      const defaultShock = { accel: 0, duration: 0 }
   111      const newShock = updateValue(update, oldValue || defaultShock)
   112      const intShock = _.mapValues(newShock, parseInt)
   113  
   114      property.stringValue = JSON.stringify(intShock)
   115  
   116    } else {
   117      throw new Error(`Bad update in JSON: ${property.name}`)
   118    }
   119  
   120    return property
   121  }
   122  
   123  const makeUpdateSubmitter = (count = 0) => () => {
   124    if (count >= LIMIT) return
   125    console.log(`Starting update set ${count + 1} of ${LIMIT}`)
   126    // Get current property values
   127    return request(`${SERVER}/records`)
   128      .then(res => {
   129        return JSON.parse(res).reduce((oldValues, record) => {
   130          return _.assign({
   131            [record.recordId]: _.zipObject(
   132              _.map(record.properties, prop => prop.name),
   133              _.map(record.properties, prop => prop.value))
   134          }, oldValues)
   135        }, {})
   136      })
   137  
   138      // Build update transactions
   139      .then(oldValues => {
   140        console.log(`Building updates . . .`)
   141        return updateGroups.reduce((updateTxns, group) => {
   142          group.updates.forEach(update => {
   143            if (update.noOpChance && Math.random() < update.noOpChance) return
   144            const oldValue = oldValues[group.recordId][update.name]
   145            const prop = updateProperty(update, oldValue)
   146            updateTxns.push(createUpdate(group.privateKey, group.recordId, prop))
   147          })
   148          return updateTxns
   149        }, [])
   150      })
   151  
   152      // Send update transactions
   153      .then(updateTxns => {
   154        console.log(`Submitting ${updateTxns.length} update transactions . . .`)
   155        submitTxns(updateTxns)
   156      })
   157  
   158      // Set timeout to call self
   159      .then(() => {
   160        console.log('Updates committed.')
   161        const wait = RATE ? 60000 / RATE : 0
   162        setTimeout(makeUpdateSubmitter(count + 1), wait)
   163      })
   164  }
   165  
   166  // Compile protos, fetch batcher pubkey, then begin submitting updates
   167  protos.compile()
   168    .then(awaitServerPubkey)
   169    .then(batcherPublicKey => {
   170      const txnCreators = {}
   171  
   172      createTxn = (privateKey, payload) => {
   173        if (!txnCreators[privateKey]) {
   174          txnCreators[privateKey] = getTxnCreator(privateKey, batcherPublicKey)
   175        }
   176        return txnCreators[privateKey](payload)
   177      }
   178    })
   179    .then(() => makeUpdateSubmitter()())
   180    .catch(err => {
   181      console.error(err.toString())
   182      process.exit()
   183    })