@requestnetwork/data-access
Version:
Main package for the Request Network data access layer.
196 lines • 6.72 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const MultiFormat = tslib_1.__importStar(require("@requestnetwork/multi-format"));
const utils_1 = require("@requestnetwork/utils");
/**
* Module to manage a block in the data access-layer
* a block is a sorted list of transactions with topics
*/
exports.default = {
createEmptyBlock,
getAllChannelIds,
getAllTopics,
getAllTransactions,
getTransactionFromPosition,
getTransactionPositionFromChannelId,
getTransactionPositionsByChannelIds,
getTransactionsByPositions,
parseBlock,
pushTransaction,
};
/**
* Function to create an empty block
*
* @returns IBlock an empty block
*/
function createEmptyBlock() {
return {
header: {
channelIds: {},
topics: {},
version: '0.1.0',
},
transactions: [],
};
}
/**
* Parses a string to a block checking if the standard is followed
*
* @param data the data to parse
* @returns the block or throw en exception if the standard is not followed
*/
function parseBlock(data) {
let maybeBlock;
try {
maybeBlock = JSON.parse(data);
}
catch (e) {
throw new Error(`Impossible to JSON parse the data: "${e}"`);
}
if (!maybeBlock.header || !maybeBlock.transactions) {
throw new Error(`Data do not follow the block standard`);
}
if (!maybeBlock.header.topics || !maybeBlock.header.channelIds || !maybeBlock.header.version) {
throw new Error(`Header do not follow the block standard`);
}
if (maybeBlock.header.version !== '0.1.0') {
throw new Error(`Version not supported`);
}
// check the transactions format
if (!maybeBlock.transactions.every((tx) => tx.data || tx.encryptedData)) {
throw new Error(`Transactions do not follow the block standard`);
}
// check if channelIds are well formatted
// check that all the channel ids are hashes
if (Object.keys(maybeBlock.header.channelIds).some((channelId) => !MultiFormat.hashFormat.isDeserializableString(channelId))) {
throw new Error(`Channel ids in header.channelIds must be formatted keccak256 hashes`);
}
// check if channel ids refer to actual transactions in the block
const transactionCount = maybeBlock.transactions.length;
if (Object.values(maybeBlock.header.channelIds).some((listOfTransactionPosition) => listOfTransactionPosition.some((position) => position < 0 || position >= transactionCount))) {
throw new Error(`transactions in channel ids must refer to transaction in the blocks`);
}
// check if topics are well formatted
// check that all the channel ids are hashes
if (Object.keys(maybeBlock.header.topics).some((channelId) => !MultiFormat.hashFormat.isDeserializableString(channelId))) {
throw new Error(`Channel ids in header.topics must be formatted keccak256 hashes`);
}
// check if topics are hashes
if (Object.values(maybeBlock.header.topics).some((listOfTopics) => listOfTopics.some((topic) => !MultiFormat.hashFormat.isDeserializableString(topic)))) {
throw new Error(`topics in header.topics must be formatted keccak256 hashes`);
}
return maybeBlock;
}
/**
* Function to add a transaction and topics in the block
*
* @param block previous state will not be modified
* @param transaction transaction to push
* @param channelId id of the channel to add the transaction in
* @param topics strings to topic the channel
*
* @returns the new state
*/
function pushTransaction(block, transaction, channelId, topics = []) {
if (transaction.data === undefined && !transaction.encryptedData) {
throw new Error('The transaction is missing the data property or encryptedData property');
}
// we don't want to modify the original block state
const copiedBlock = (0, utils_1.deepCopy)(block);
const newTransactionPosition = copiedBlock.transactions.length;
copiedBlock.transactions.push(transaction);
// index the transaction with the channel id
copiedBlock.header.channelIds[channelId] = (copiedBlock.header.channelIds[channelId] || []).concat([newTransactionPosition]);
// add topics in the header
for (const topic of topics) {
copiedBlock.header.topics[channelId] = (copiedBlock.header.topics[channelId] || []).concat([
topic,
]);
}
return copiedBlock;
}
/**
* Returns a transaction from its position
*
* @param block current block state
* @param position position of the transaction in the block
*
* @returns the transaction
*/
function getTransactionFromPosition(block, position) {
return block.transactions[position];
}
/**
* Returns several transactions from their positions
*
* @param block current block state
* @param positions list of positions of the transactions
*
* @returns the transactions
*/
function getTransactionsByPositions(block, positions) {
// remove duplicates and sort
const sortedPositions = Array.from(new Set(positions)).sort();
return sortedPositions
.map((position) => block.transactions[position])
.filter((position) => position !== undefined);
}
/**
* Returns all transactions of a block
*
* @param block current block state
*
* @returns all the transactions with topics
*/
function getAllTransactions(block) {
return block.transactions;
}
/**
* Returns a list of the positions of the transactions with given channel id
*
* @param block current block state
* @param channelId the channel id
*
* @returns list of transaction positions
*/
function getTransactionPositionFromChannelId(block, channelId) {
return block.header.channelIds[channelId] || [];
}
/**
* Returns a list of transactions position from a list of channel ids
*
* @param block current block state
* @param ids the channel ids
*
* @returns list of transaction positions
*/
function getTransactionPositionsByChannelIds(block, channelIds) {
const result = channelIds
.map((id) => block.header.channelIds[id])
.filter((value) => value !== undefined)
.reduce((accumulator, current) => accumulator.concat(current), []);
// remove duplicates and sort
return Array.from(new Set(result)).sort();
}
/**
* Returns all the topics of the block
*
* @param block current block state
*
* @returns all the topics
*/
function getAllTopics(block) {
return block.header.topics;
}
/**
* Returns all the channel ids of the block
*
* @param block current block state
*
* @returns all the channel ids
*/
function getAllChannelIds(block) {
return block.header.channelIds;
}
//# sourceMappingURL=block.js.map