github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/ledger_sync/subscriber/deltas.js (about)

     1  /**
     2   * Copyright 2018 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 blocks = require('../db/blocks')
    21  const state = require('../db/state')
    22  const protos = require('./protos')
    23  
    24  const deltaQueue = {
    25    _queue: [],
    26    _running: false,
    27  
    28    add (promisedFn) {
    29      this._queue.push(promisedFn)
    30      this._runUntilEmpty()
    31    },
    32  
    33    _runUntilEmpty () {
    34      if (this._running) return
    35      this._running = true
    36      this._runNext()
    37    },
    38  
    39    _runNext () {
    40      if (this._queue.length === 0) {
    41        this._running = false
    42      } else {
    43        const current = this._queue.shift()
    44        return current().then(() => this._runNext())
    45      }
    46    }
    47  }
    48  
    49  const getProtoName = address => {
    50    const typePrefix = address.slice(6, 8)
    51    if (typePrefix === 'ea') {
    52      if (address.slice(-4) === '0000') return 'Property'
    53      else return 'PropertyPage'
    54    }
    55  
    56    const names = {
    57      ae: 'Agent',
    58      aa: 'Proposal',
    59      ec: 'Record',
    60      ee: 'RecordType'
    61    }
    62    if (names[typePrefix]) return names[typePrefix]
    63  
    64    throw new Error(`Blockchain Error: No Protobuf for prefix "${typePrefix}"`)
    65  }
    66  
    67  const getObjectifier = address => {
    68    const name = getProtoName(address)
    69    return stateInstance => {
    70      const obj = protos[name].toObject(stateInstance, {
    71        enums: String,  // use string names for enums
    72        longs: Number,  // convert int64 to Number, limiting precision to 2^53
    73        defaults: true  // use default for falsey values
    74      })
    75      if (name === 'PropertyPage') {
    76        obj.pageNum = parseInt(address.slice(-4), 16)
    77      }
    78      return obj
    79    }
    80  }
    81  
    82  const stateAdder = address => {
    83    const addState = state[`add${getProtoName(address)}`]
    84    const toObject = getObjectifier(address)
    85    return (stateInstance, blockNum) => {
    86      addState(toObject(stateInstance), blockNum)
    87    }
    88  }
    89  
    90  const getEntries = ({ address, value }) => {
    91    return protos[`${getProtoName(address)}Container`]
    92      .decode(value)
    93      .entries
    94  }
    95  
    96  const entryAdder = block => change => {
    97    const addState = stateAdder(change.address)
    98    return Promise.all(getEntries(change).map(entry => {
    99      return addState(entry, block.blockNum)
   100    }))
   101  }
   102  
   103  const handle = (block, changes) => {
   104    deltaQueue.add(() => {
   105      const [ pageChanges, otherChanges ] = _.partition(changes, change => {
   106        return getProtoName(change.address) === 'PropertyPage'
   107      })
   108  
   109      return Promise.all(otherChanges.map(entryAdder(block)))
   110        .then(() => {
   111          // If there are page changes, give other changes a chance to propagate
   112          const wait = pageChanges.length === 0 ? 0 : 100
   113          return new Promise(resolve => setTimeout(resolve, wait))
   114        })
   115        .then(() =>  Promise.all(pageChanges.map(entryAdder(block))))
   116        .then(() => blocks.insert(block))
   117    })
   118  }
   119  
   120  module.exports = {
   121    handle
   122  }