'use strict';
/**
* @module lib/operations/blockchains
* @summary Whiteflag API blockchains endpoints handler module
* @description Module with api blockchains endpoint handlers
* @tutorial modules
* @tutorial openapi
*/
module.exports = {
// Endpoint handler functions
getBlockchains,
getBlockchain,
transferFunds,
getAccounts,
getAccount,
createAccount,
updateAccount,
deleteAccount,
createSignature
};
// Whiteflag common functions and classes //
const response = require('../common/httpres');
const array = require('../common/arrays');
const { ProcessingError } = require('../common/errors');
// Whiteflag modules //
const wfApiBlockchains = require('../blockchains');
const wfAuthenticate = require('../protocol/authenticate');
const wfState = require('../protocol/state');
// MAIN MODULE FUNCTIONS //
/**
* Provides current state of all blockchains from state module
* @function getBlockchains
* @alias module:lib/operations/blockchains.getBlockchains
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function getBlockchains(req, res, operationId, callback) {
wfState.getBlockchains(function endpointGetBlockchainsCb(err, blockchainsState = null) {
// Create response body and preserve information before responding
let resBody = response.createBody(req, operationId);
let resData = [];
// Send response using common endpoint response function
if (!err && !blockchainsState) err = new Error('Could not retrieve blockchains');
if (blockchainsState) {
resBody.meta.info = array.addItem(resBody.meta.info, 'Current blockchains in state');
resData = Object.keys(blockchainsState);
}
return response.sendIndicative(res, err, resBody, resData, callback);
});
}
/**
* Provides current blockchain state from state module
* @function getBlockchain
* @alias module:lib/operations/blockchains.getBlockchain
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function getBlockchain(req, res, operationId, callback) {
const blockchain = req.params.blockchain;
wfState.getBlockchainData(blockchain, function endpointGetBlockchainCb(err, blockchainState = null) {
// Create response body and preserve information before responding
let resBody = response.createBody(req, operationId);
// Send response using common endpoint response function
if (!err && !blockchainState) err = new ProcessingError(`Blockchain ${blockchain} does not exist`, null, 'WF_API_NO_RESOURCE');
if (!err) {
resBody.meta.blockchain = blockchain;
resBody.meta.info = array.addItem(resBody.meta.info, `Current ${blockchain} state`);
}
return response.sendIndicative(res, err, resBody, blockchainState, callback);
});
}
/**
* Provides all blockchain accounts from state module
* @function getAccounts
* @alias module:lib/operations/blockchains.getAccounts
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function getAccounts(req, res, operationId, callback) {
const blockchain = req.params.blockchain;
wfState.getBlockchainData(blockchain, function endpointGetAccountsCb(err, blockchainState = null) {
// Create response body and preserve information before responding
let resData = [];
let resBody = response.createBody(req, operationId);
// Extract account data from blockchain state
if (!err && !blockchainState) err = new ProcessingError(`Blockchain ${blockchain} does not exist`, null, 'WF_API_NO_RESOURCE');
if (!err) resBody.meta.blockchain = blockchain;
if (!err && !blockchainState.accounts) err = new Error(`Could not retrieve accounts for ${blockchain}`);
if (!err) {
resData = array.pluck(blockchainState.accounts, 'address') || [];
resBody.meta.info = array.addItem(resBody.meta.info, `Blockchain accounts on ${blockchain}`);
}
// Send response using common endpoint response function
return response.sendIndicative(res, err, resBody, resData, callback);
});
}
/**
* Provides specific blockchain account from state module
* @function getAccount
* @alias module:lib/operations/blockchains.getAccount
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function getAccount(req, res, operationId, callback) {
const blockchain = req.params.blockchain;
const address = req.params.account;
wfState.getBlockchainData(blockchain, function endpointGetAccountCb(err, blockchainState = null) {
// Create response body and preserve information before responding
let resData = {};
let resBody = response.createBody(req, operationId);
// Get the requested account object
if (!err && !blockchainState) err = new ProcessingError(`Blockchain ${blockchain} does not exist`, null, 'WF_API_NO_RESOURCE');
if (!err) resBody.meta.blockchain = blockchain;
if (!err && !blockchainState.accounts) err = new Error(`Could not retrieve accounts for ${blockchain}`);
if (!err) {
const index = blockchainState.accounts.findIndex(
item => item.address.toLowerCase() === address.toLowerCase()
);
if (index < 0) {
err = new ProcessingError(`Blockchain account ${address} does not exist`, null, 'WF_API_NO_RESOURCE');
} else {
resBody.meta.account = address;
resBody.meta.info = array.addItem(resBody.meta.info, 'Blockchain account details');
resData = blockchainState.accounts[index];
}
}
// Send response using common endpoint response function
return response.sendIndicative(res, err, resBody, resData, callback);
});
}
/**
* Creates account on specified blockchain
* @function createAccount
* @alias module:lib/operations/blockchains.createAccount
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function createAccount(req, res, operationId, callback) {
const blockchain = req.params.blockchain;
let privateKey = req.body.privateKey || null;
wfApiBlockchains.createAccount(blockchain, privateKey, function endpointCreateAccountCb(err, result = {}) {
// Create response body and preserve information before responding
let resData = {};
let resBody = response.createBody(req, operationId);
resBody.meta.blockchain = blockchain;
// Check results
if (result.address) {
resBody.meta.resource = `/blockchains/${blockchain}/accounts/${result.address}`;
} else {
if (!err) err = new Error('Could not determine address of created blockchain account');
}
if (!err) {
resData = result;
resBody.meta.account = result.address;
resBody.meta.info = array.addItem(resBody.meta.info, 'Blockchain account created');
}
// Send response using common endpoint response function
return response.sendImperative(res, err, resBody, resData, callback);
});
// Hopefully the garbage collector will do its work
privateKey = undefined;
}
/**
* Updates account on specified blockchain
* @function updateAccount
* @alias module:lib/operations/blockchains.updateAccount
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function updateAccount(req, res, operationId, callback) {
const blockchain = req.params.blockchain;
const address = req.params.account;
const account = req.body;
wfApiBlockchains.updateAccount(account, address, blockchain, function endpointUpdateAccountCb(err, result) {
// Create response body and preserve information before responding
let resData = {};
let resBody = response.createBody(req, operationId);
// Send response using common endpoint response function
if (!err && !result) resBody.meta.warnings = array.addItem(resBody.meta.warnings, 'No errors occured when updating blockchain account, but did not receive result');
if (!err) resBody.meta.blockchain = blockchain;
if (!err && result) {
resData = result;
resBody.meta.account = address;
resBody.meta.info = array.addItem(resBody.meta.info, 'Blockchain account updated');
}
return response.sendImperative(res, err, resBody, resData, callback);
});
}
/**
* Deletes account on specified blockchain
* @function deleteAccount
* @alias module:lib/operations/blockchains.deleteAccount
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function deleteAccount(req, res, operationId, callback) {
const blockchain = req.params.blockchain;
const address = req.params.account;
wfApiBlockchains.deleteAccount(address, blockchain, function endpointDeleteAccountCb(err, result) {
// Create response body and preserve information before responding
let resData = {};
let resBody = response.createBody(req, operationId);
// Send response using common endpoint response function
if (!err && !result) resBody.meta.warnings = array.addItem(resBody.meta.warnings, 'No errors occured when deleting blockchain account, but did not receive result');
if (!err) resBody.meta.blockchain = blockchain;
if (!err && result) {
resData = result;
resBody.meta.account = address;
resBody.meta.info = array.addItem(resBody.meta.info, 'Blockchain account deleted');
}
return response.sendImperative(res, err, resBody, resData, callback);
});
}
/**
* Requests a Whiteflag authentication signature
* @function createSignature
* @alias module:lib/operations/blockchains.createSignature
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function createSignature(req, res, operationId, callback) {
const blockchain = req.params.blockchain;
const address = req.params.account;
const signPayload = req.body;
wfAuthenticate.sign(signPayload, address, blockchain, function endpointCreateSignatureCb(err, wfSignature, wfSignatureDecoded) {
// Create response body and preserve information before responding
let resBody = response.createBody(req, operationId);
// Send response using common endpoint response function
if (!err) resBody.meta.blockchain = blockchain;
if (!err && (!wfSignature || !wfSignatureDecoded)) err = new Error('Did not receive valid signature from blockchain');
if (wfSignatureDecoded) {
resBody.meta.account = address;
resBody.meta.decoded = wfSignatureDecoded;
}
return response.sendImperative(res, err, resBody, wfSignature, callback);
});
}
/**
* Transfers value from onse blockchain address to an other address
* @function transferFunds
* @alias module:lib/operations/blockchains.transferFunds
* @param {Object} req the http request
* @param {Object} res the http response
* @param {string} operationId the operation id as defined in the openapi definition
* @param {logEndpointEventCb} callback
*/
function transferFunds(req, res, operationId, callback) {
const blockchain = req.params.blockchain;
const address = req.params.account;
const transfer = req.body;
wfApiBlockchains.transferFunds(transfer, address, blockchain, function endpointTransferFundsCb(err, txHash) {
// Create response body and preserve information before responding
let resData = {};
let resBody = response.createBody(req, operationId);
resBody.meta.blockchain = blockchain;
resBody.meta.account = address;
// Send response using common endpoint response function
if (!err) resBody.meta.transfer = transfer;
if (!err && !txHash) err = new Error('Could not determine transaction hash after value transfer');
if (txHash) {
resData = { transactionHash: txHash };
resBody.meta.info = array.addItem(resBody.meta.info, 'Value transfered');
}
return response.sendImperative(res, err, resBody, resData, callback);
});
}