UNPKG

amazon-qldb-kvs-nodejs

Version:

A helper module, simplifying basic interactions with Amazon Quantum Ledger Database for Node.js through a simple key-value store interface.

215 lines (212 loc) 12.7 kB
"use strict"; /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * 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. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDocumentLedgerMetadataByDocIdAndTxId = exports.getDocumentLedgerMetadata = exports.lookupBlockAddressAndDocIdForKey = void 0; const ion_js_1 = require("ion-js"); const GetDigest_1 = require("./GetDigest"); const BlockAddress_1 = require("./BlockAddress"); const Logging_1 = require("./Logging"); const logger = Logging_1.log.getLogger("qldb-helper"); const Util_1 = require("./Util"); const GetRevision_1 = require("./GetRevision"); /** * Query the table metadata for a document with a particular key for verification. * @param txn The {@linkcode TransactionExecutor} for lambda execute. * @param tableName The table name to query. * @param keyAttributeName A keyAttributeName to query. * @param keyAttributeValue The key of the given keyAttributeName. * @returns Promise which fulfills with a list of Ion values that contains the results of the query. */ function lookupBlockAddressAndDocIdForKey(txn, tableName, keyAttributeName, keyAttributeValue) { return __awaiter(this, void 0, void 0, function* () { const fcnName = "[GetMetadata lookupBlockAddressAndDocIdForKey]"; try { logger.debug(`${fcnName} Querying the '${tableName}' table for key ${keyAttributeName}: ${keyAttributeValue}...`); (0, Util_1.validateTableNameConstrains)(tableName); (0, Util_1.validateAttributeNameConstrains)(keyAttributeName); const query = `SELECT blockAddress, metadata.id FROM _ql_committed_${tableName} WHERE data.${keyAttributeName} = ?`; logger.debug(`${fcnName} Constructed query: ${query}`); const result = yield txn.execute(query, keyAttributeValue); const resultList = result.getResultList(); return resultList; } catch (err) { logger.debug(`${fcnName} ${err} `); throw `${fcnName} ${err} `; } }); } exports.lookupBlockAddressAndDocIdForKey = lookupBlockAddressAndDocIdForKey; /** * Retrieve full ledger metadata of the most recent revision of the document for the given Key. * @param txn The {@linkcode TransactionExecutor} for lambda execute. * @param ledgerName The ledger to get the digest from. * @param tableName The table name to query. * @param keyAttributeName A keyAttributeName to query. * @param keyAttributeValue The key of the given keyAttributeName. * @param qldbClient The QLDB control plane client to use. * @returns Promise which fulfills with void. * @throws Error: When verification fails. */ function getDocumentLedgerMetadata(txn, ledgerName, tableName, keyAttributeName, keyAttributeValue, qldbClient, ledgerDigest) { return __awaiter(this, void 0, void 0, function* () { const fcnName = "[GetMetadata getDocumentLedgerMetadata]"; try { logger.debug(`${fcnName} Getting metadata for document with "${keyAttributeName}" = ${keyAttributeValue}, in ledger = ${ledgerName}.`); // Getting Block Address and Document Id for the document logger.debug(`${fcnName} Getting Block Address and Document Id for the document`); const blockAddressAndIdList = yield lookupBlockAddressAndDocIdForKey(txn, tableName, keyAttributeName, keyAttributeValue); logger.debug(`${fcnName} Received ${blockAddressAndIdList.length} Block Address and Document Id combination.`); if (!blockAddressAndIdList.length) { throw `Unable to find block address and document id associated with "${keyAttributeName}" = ${keyAttributeValue}`; } const blockAddressAndId = blockAddressAndIdList[blockAddressAndIdList.length - 1]; const blockAddress = (0, BlockAddress_1.blockAddressToValueHolder)(blockAddressAndId); logger.debug(`${fcnName} Getting a proof for the document.`); let digest = ledgerDigest; if (!ledgerDigest) { // Requesting ledger digest logger.debug(`${fcnName} Requesting ledger digest`); let ledgerDigest = yield (0, GetDigest_1.getLedgerDigest)(ledgerName, qldbClient); // Checking if digest sequenceNo has caught up with block sequenceNo const digestTipAddressSeqNo = ion_js_1.dom.load(ledgerDigest.DigestTipAddress.IonText).get("sequenceNo"); const blockAddressSeqNo = ion_js_1.dom.load(blockAddress.IonText).get("sequenceNo"); if (digestTipAddressSeqNo < blockAddressSeqNo) { logger.debug(`${fcnName} The ledger digest sequenceNo is behind the block sequenceNo, so retrying after 100 ms`); yield (0, Util_1.sleep)(100); ledgerDigest = yield (0, GetDigest_1.getLedgerDigest)(ledgerName, qldbClient); } const digestBase64 = (0, ion_js_1.toBase64)(ledgerDigest.Digest); digest = { Digest: digestBase64, DigestTipAddress: ledgerDigest.DigestTipAddress }; logger.debug(`${fcnName} Got Ledger Digest: ${JSON.stringify(digest)} `); } // Converting digest from default buffer array to base64 format const digestTipAddress = digest.DigestTipAddress; logger.debug(`${fcnName} Got a ledger digest: digest tip address = ${(0, Util_1.valueHolderToString)(digestTipAddress)}, \n digest = ${digest.Digest}.`); // Getting revision const documentId = (0, BlockAddress_1.getMetadataId)(blockAddressAndId); logger.debug(`${fcnName} Getting document revision with the following parameters: ${JSON.stringify({ ledgerName: ledgerName, documentId: documentId, blockAddress: blockAddress, digestTipAddress: digestTipAddress })}`); const revisionResponse = yield (0, GetRevision_1.getRevision)(ledgerName, documentId, blockAddress, digestTipAddress, qldbClient); const revision = ion_js_1.dom.load(revisionResponse.Revision.IonText); const revisionHash = (0, ion_js_1.toBase64)((0, Util_1.getBlobValue)(revision, "hash")); const proof = revisionResponse.Proof; logger.debug(`${fcnName} Got back a proof: ${(0, Util_1.valueHolderToString)(proof)}.`); return { LedgerName: ledgerName, TableName: tableName, BlockAddress: blockAddress, DocumentId: documentId, RevisionHash: revisionHash, Proof: proof, LedgerDigest: digest }; } catch (err) { throw `${fcnName} ${err} `; } }); } exports.getDocumentLedgerMetadata = getDocumentLedgerMetadata; /** * Retrieve full ledger metadata of the most recent revision of the document for the given Key. * @param txn The {@linkcode TransactionExecutor} for lambda execute. * @param ledgerName The ledger to get the digest from. * @param tableName The table name to query. * @param keyAttributeName A keyAttributeName to query. * @param keyAttributeValue The key of the given keyAttributeName. * @param qldbClient The QLDB control plane client to use. * @returns Promise which fulfills with void. * @throws Error: When verification fails. */ function getDocumentLedgerMetadataByDocIdAndTxId(txn, ledgerName, tableName, documentId, transactionId, qldbClient) { return __awaiter(this, void 0, void 0, function* () { const fcnName = "[GetMetadata getDocumentLedgerMetadataByDocIdAndTxId]"; try { logger.debug(`${fcnName} Getting metadata for document with documentId = ${documentId} and transactionId = ${transactionId}, in ledger = ${ledgerName} and table = ${tableName}.`); // Getting Block Address and Document Id for the document logger.debug(`${fcnName} Getting revision metadata for the document`); const revisionMetadata = yield (0, GetRevision_1.getRevisionMetadataByDocIdAndTxId)(txn, tableName, documentId, transactionId); logger.debug(`${fcnName} Received metadata ${revisionMetadata}`); if (!revisionMetadata) { throw `Unable to find revision metadata for documentId = ${documentId} and transactionId = ${transactionId}`; } //const blockAddressAndId = blockAddressAndIdList[blockAddressAndIdList.length - 1] const blockAddress = (0, BlockAddress_1.blockAddressToValueHolder)(revisionMetadata); logger.debug(`${fcnName} Getting a proof for the document.`); // Requesting ledger digest logger.debug(`${fcnName} Requesting ledger digest`); let ledgerDigest = yield (0, GetDigest_1.getLedgerDigest)(ledgerName, qldbClient); // Checking if digest sequenceNo has caught up with block sequenceNo const digestTipAddressSeqNo = ion_js_1.dom.load(ledgerDigest.DigestTipAddress.IonText).get("sequenceNo"); const blockAddressSeqNo = ion_js_1.dom.load(blockAddress.IonText).get("sequenceNo"); if (digestTipAddressSeqNo < blockAddressSeqNo) { logger.debug(`${fcnName} The ledger digest sequenceNo is behind the block sequenceNo, so retrying after 100 ms`); yield (0, Util_1.sleep)(100); ledgerDigest = yield (0, GetDigest_1.getLedgerDigest)(ledgerName, qldbClient); } const digestBase64 = (0, ion_js_1.toBase64)(ledgerDigest.Digest); const digest = { Digest: digestBase64, DigestTipAddress: ledgerDigest.DigestTipAddress }; logger.debug(`${fcnName} Got Ledger Digest: ${JSON.stringify(digest)} `); // Converting digest from default buffer array to base64 format const digestTipAddress = digest.DigestTipAddress; logger.debug(`${fcnName} Got a ledger digest: digest tip address = ${(0, Util_1.valueHolderToString)(digestTipAddress)}, \n digest = ${digest.Digest}.`); // Getting revision logger.debug(`${fcnName} Getting document revision with the following parameters: ${JSON.stringify({ ledgerName: ledgerName, documentId: documentId, blockAddress: blockAddress, digestTipAddress: digestTipAddress })}`); const revisionResponse = yield (0, GetRevision_1.getRevision)(ledgerName, documentId, blockAddress, digestTipAddress, qldbClient); const revision = ion_js_1.dom.load(revisionResponse.Revision.IonText); const revisionHash = (0, ion_js_1.toBase64)((0, Util_1.getBlobValue)(revision, "hash")); const proof = revisionResponse.Proof; logger.debug(`${fcnName} Got back a proof: ${(0, Util_1.valueHolderToString)(proof)}.`); return { LedgerName: ledgerName, TableName: tableName, BlockAddress: blockAddress, DocumentId: documentId, RevisionHash: revisionHash, Proof: proof, LedgerDigest: digest }; } catch (err) { throw `${fcnName} ${err} `; } }); } exports.getDocumentLedgerMetadataByDocIdAndTxId = getDocumentLedgerMetadataByDocIdAndTxId;