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 }