sawtooth-sdk
Version:
An SDK for interacting with the Hyperledger Sawtooth distributed ledger.
164 lines (140 loc) • 4.29 kB
JavaScript
/**
* Copyright 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ------------------------------------------------------------------------------
*/
'use strict'
const crypto = require('crypto')
const uuid = require('uuid/v4')
const zmq = require('zeromq')
const assert = require('assert')
const { Message } = require('../protobuf')
const Deferred = require('./deferred')
const { ValidatorConnectionError } = require('../processor/exceptions')
const _encodeMessage = (messageType, correlationId, content) => {
assert(
typeof messageType === 'number',
`messageType must be a number; was ${messageType}`
)
assert(
typeof correlationId === 'string',
`correlationId must be a string; was ${correlationId}`
)
assert(
content !== undefined || content !== null,
'content must not be null or undefined'
)
assert(
Buffer.isBuffer(content),
`content must be a buffer; was ${
content.constructor ? content.constructor.name : typeof content
}`
)
return Message.encode({
messageType,
correlationId,
content
}).finish()
}
const _generateId = () =>
crypto.createHash('sha256')
.update(uuid())
.digest('hex')
class Stream {
constructor (url) {
this._url = url
this._initial_connection = true
}
connect (onConnectCb) {
if (this._onConnectCb) {
console.log(`Attempting to reconnect to ${this._url}`)
}
this._onConnectCb = onConnectCb
this._futures = {}
this._socket = zmq.socket('dealer')
this._socket.setsockopt('identity', Buffer.from(uuid(), 'utf8'))
this._socket.on('connect', () => {
console.log(`Connected to ${this._url}`)
onConnectCb()
})
this._socket.on('disconnect', (fd, endpoint) => this._handleDisconnect())
this._socket.monitor(250, 0)
this._socket.connect(this._url)
this._initial_connection = false
}
close () {
this._socket.setsockopt(zmq.ZMQ_LINGER, 0)
this._socket.unmonitor()
this._socket.close()
this._socket = null
}
_handleDisconnect () {
console.log(`Disconnected from ${this._url}`)
this.close()
Object.keys(this._futures).forEach((correlationId) => {
this._futures[correlationId].reject(
new ValidatorConnectionError('The connection to the validator was lost')
)
})
this.connect(this._onConnectCb)
}
send (type, content) {
if (this._socket) {
const correlationId = _generateId()
let deferred = new Deferred()
this._futures[correlationId] = deferred
try {
this._socket.send(_encodeMessage(type, correlationId, content))
} catch (e) {
delete this._futures[correlationId]
return Promise.reject(e)
}
return deferred.promise
.then(result => {
delete this._futures[correlationId]
return result
})
.catch(err => {
delete this._futures[correlationId]
throw err
})
} else {
let err = null
if (this._initial_connection) {
err = new Error('Must call `connect` before calling `send`')
} else {
err = new ValidatorConnectionError(
'The connection to the validator was lost'
)
}
return Promise.reject(err)
}
}
sendBack (type, correlationId, content) {
if (this._socket) {
this._socket.send(_encodeMessage(type, correlationId, content))
}
}
onReceive (cb) {
this._socket.on('message', buffer => {
let message = Message.decode(buffer)
if (this._futures[message.correlationId]) {
this._futures[message.correlationId].resolve(message.content)
} else {
process.nextTick(() => cb(message))
}
})
}
}
module.exports = { Stream }