UNPKG

@cronstamp/clientlib

Version:

Client library for cronstamp, a blockchain-based document timestamping and verification service.

234 lines (181 loc) 13.9 kB
# cronStamp Client Library This is a TypeScript client library for [cronStamp](https://cronstamp.com/), the blockchain-based document timestamping and verification service. For an explanation of cronStamp check out [this page](https://cronstamp.com/explanation). [cronStamp website](https://cronstamp.com/) &mdash; [GitLab](https://gitlab.com/cronstamp/clientlib) &mdash; [API reference](https://clientlib.cronstamp.com) &mdash; [npm package](https://www.npmjs.com/package/@cronstamp/clientlib) ## Installation ### npm In a [Node.js](https://nodejs.org/en/download/package-manager) environment you can install the cronStamp client library with npm ```sh npm install @cronstamp/clientlib ``` Once installed, library functions can be imported like this in your typescript project ```typescript import { requestCertificateForString } from '@cronstamp/clientlib'; ... ``` ### CDN If you are in a browser environment instead, you can directly embed the library from a [CDN](https://esm.sh/) like so ```html <script type="module"> import { requestCertificateForString } from "https://esm.sh/@cronstamp/clientlib"; ... </script> ``` ## Usage The two main functionalities of the client library are document timestamping and document verification. Let's walk through each of the two steps with an example. ### Timestamp a document First comes document timestamping, where we provide the document and receive in turn the certificate. Under the hood, the client library hashes the document and submits the hash to the cronStamp [Web API](https://cronstamp.com/web_api). cronStamp inserts the hash into each supported blockchain and generates a preliminary certificate. From this, the client library assembles and returns the full certificate. Afterwards, the client library performs a full document verification to ensure correctness of the certificate. Let's dive into an example. ```typescript import { requestCertificateForString } from 'https://esm.sh/@cronstamp/clientlib'; let message = 'This is the data that the certificate will be created for.'; let requestResult = await requestCertificateForString(message, ['xrp']) .onMessage((data) => console.log(data.message)) .start(); ``` You can run the example in this [JSFdiddle](https://jsfiddle.net/45zmrugw/) (open the console in the bottom right to see the output). For simplicity, the example uses a string (the `message` variable) to represent the document. Calling [ `requestCertificateForString`](https://clientlib.cronstamp.com/functions/lifecycle_request.requestCertificateForString.html) and passing the message returns the timestamping process which is an object of type [ `RequestProcess`](https://clientlib.cronstamp.com/classes/lifecycle_request.RequestProcess.html). This request process object has several methods, such as [ `configure`](https://clientlib.cronstamp.com/classes/lifecycle_request.RequestProcess.html#configure), [ `onMessage`](https://clientlib.cronstamp.com/classes/lifecycle_request.RequestProcess.html#onMessage), [ `onError`](https://clientlib.cronstamp.com/classes/lifecycle_request.RequestProcess.html#onError), [ `start`](https://clientlib.cronstamp.com/classes/lifecycle_request.RequestProcess.html#start), and [ `cancel`](https://clientlib.cronstamp.com/classes/lifecycle_request.RequestProcess.html#cancel). For this example, we request only XRP to be included in the certificate by passing `'xrp'` as second parameter to `requestCertificateForString`. We use `onMessage` to define handlers for processing the result data of type [`ResultData`](https://clientlib.cronstamp.com/types/lifecycle_manager.ResultData.html) which is emitted after each step of the timestamping process (for all possible steps see the [`Step`](https://clientlib.cronstamp.com/enums/lifecycle_manager.Step.html) enum). In this case, we simply print the status message. Calling `start` starts the timestamping process and returns a promise, which has to be awaited and eventually resolves to the `requestResult` of type `ResultData`. Calling `onMessage` is optional and in case of it's absence the entire `ResultData` object is logged to console for each process step. After successful execution of th example, the `requestResult` object looks as follows with the final certificate in the `currentCert` field. The step is set to 7 corresponding to [ `Step.REQUEST_FULL_CERTIFICATE_SUCCEEDED`](https://clientlib.cronstamp.com/enums/lifecycle_manager.Step.html#REQUEST_FULLCERT_SUCCESS). ```typescript { step: 7, type: 0, currentCert: { blockchains: { xrp: { transaction: '9851AFC28E106CAC9228B597EADEF20985647BF57F937404BCC0C36C834D21AB', block_timestamp: 786491741, merkle_tree_splice: [ 'vUAUzj3hnMufac7DkXD4RYYC/nRngagSF6Gnmbjl8A0=', 'Y5NWzOO3tXA1lAjfF1LFb747lrqK1BNd/iEu0ADQqPw=', 'dsGP+cPO/twnKNamRIuVs88KF21QAzhMtRs5/CMXiFo=' ] } }, meta: { version: 1, blockchains: ['xrp'], salt: '670b53ed2eb453e946c197b6be157745' }, info: { hash_algorithm: 'SHA-256', salted_hash: '3XBMLhyb5dZedq4mx6HdcS9hWrt19isIigov9WHtLdE=' }, document_hash: 'R/IRPRqKv8VS4CGemNZDJubr78cc7WA8J4O2h1K7R6Y=' }, message: 'Certificate created and verified successfully!', additionalDataType: 0, inputData: { type: 0, data: 'This is the data that the certificate will be created for.', blockchainsToRequire: ['xrp'], certFileName: 'This is the data tha', displayName: 'This is the dat[...]' }, currentTime: '2024-11-08T20:34:27.327Z' } ``` In addition to [`requestCertificateForString`](https://clientlib.cronstamp.com/functions/lifecycle_request.requestCertificateForString.html), the client library also provides a method [`requestCertificateForFile`](https://clientlib.cronstamp.com/functions/lifecycle_request.requestCertificateForFile.html) which takes a file, and [`requestCertificateForHash`](https://clientlib.cronstamp.com/functions/lifecycle_request.requestCertificateForHash.html) which takes a hash, in case you want to hash the document on your own. There is also the generic method [`requestCertificate`](https://clientlib.cronstamp.com/functions/lifecycle_request.requestCertificate.html) which works for all three types of inputs. ### Verify a document Document verification requires as inputs the document and the certificate and returns whether the document has been modified since the timestamping or not. To this end, the client library hashes the document (together with the hashes in the merkletree splice) and compares the resulting Merkle tree root hash with the one stored on each blockchain. If the root hashes match for all blockchains, the document is verified, i.e., has not been modified. Document verification takes place on client-side only and requires no connection to the cronStamp servers. Let's see an example of how document verification with the client library works. You can also find the example in this [JSFiddle](https://jsfiddle.net/e253t6gf/). ```typescript import { Step, verifyCertificateForString } from 'https://esm.sh/@cronstamp/clientlib'; let message = 'This is the data that the certificate will be created for.'; let certificate = { blockchains: { xrp: { transaction: 'B0479FE372F06F1BBE5E0AD547D9A92A67A211E2A6FEC832EF9710CE42BA783F', block_timestamp: 786501102, merkle_tree_splice: [ 'aqQGaOTkjQ7sn2l1EhWypyY7pg/nwil+oKYBR+mrMvU=', '', 'rKpRM19PEdnKcgm2FuADP9dKptszNFBweCtgvsdICCM=' ] } }, meta: { version: 1, blockchains: ['xrp'], salt: '7174d145aa1f57d50b38cb94d70533ce' }, info: { hash_algorithm: 'SHA-256', salted_hash: 'GfGTduOe5dpC3JL4RY1NnnmZUFwREQWkE2Tzw45N6jk=' }, document_hash: '2xXmxEInHGanIixLeS28kOBomIYWuuFKWsFnceNwRQI=' }; let verificationResult = await verifyCertificateForString(certificate, message) .onMessage((data) => console.log(data.message)) .start(); ``` The document is again represented by the string in the `message` variable. The certificate is defined as instance of the [`Certificate`](https://clientlib.cronstamp.com/types/certificate.Certificate.html) type. Both message and certificate are passed to [`verifyCertificateForString`](https://clientlib.cronstamp.com/functions/lifecycle_verify.verifyCertificateForString.html), which returns a [`VerificationProcess`](https://clientlib.cronstamp.com/classes/lifecycle_verify.VerificationProcess.html) instance. The `VerificationProcess` has the same methods as the `RequestProcess`, i.e., [`configure`](https://clientlib.cronstamp.com/classes/lifecycle_verify.VerificationProcess.html#configure), [`onMessage`](https://clientlib.cronstamp.com/classes/lifecycle_verify.VerificationProcess.html#onMessage), [`onError`](https://clientlib.cronstamp.com/classes/lifecycle_verify.VerificationProcess.html#onError), [`start`](https://clientlib.cronstamp.com/classes/lifecycle_verify.VerificationProcess.html#start), and [`cancel`](https://clientlib.cronstamp.com/classes/lifecycle_verify.VerificationProcess.html#cancel). Here, we define again a handler for logging the status message and start the verification process by calling `start`. When executing the example the last two status messages in the console indicate that the document is verified successfully, i.e., has not been modified since timestamping. ```console ... The certificate is valid! The hash in the blockchain and certificate match. This means the document existed at time 2024-11-08T20:34:21.000Z Verified root hash successfully in 1 blockchains. ``` The `verificationResult` object looks like this with step 10 corresponding to [ `Step.VERIFICATION_ALL_BLOCKCHAINS_SUCCEEDED`](https://clientlib.cronstamp.com/enums/lifecycle_manager.Step.html#VERIFICATION_FULL_SUCCESS). ```typescript { step: 10, type: 0, currentCert: { ... }, message: 'Verified root hash successfully in 1 blockchains.', additionalDataType: 6, additionalData: 1731098061000, inputData: { type: 0, data: 'This is the data that the certificate will be created for.', certFileName: 'This is the data tha', displayName: 'This is the dat[...]' }, currentTime: '2024-11-08T20:36:59.643Z' } ``` Note the `additionalData` field that holds additional information specific to the step, in this case the unix timestamp of the blockchain block containing the document hash. What type of information is present in the `additionalData` field is indicated by the `additionalDataType` field that takes a value of the [`AdditionalDataType`](https://clientlib.cronstamp.com/enums/lifecycle_manager.AdditionalDataType.html) enum. Here, the value is 6 corresponding to [`AdditionalDataType.TIMESTAMP`](https://clientlib.cronstamp.com/enums/lifecycle_manager.AdditionalDataType.html#TIMESTAMP). You can use the value in the `step` field of the `verificationResult` object like this to determine whether verification was successful. ```typescript if (verificationResult.step == Step.VERIFICATION_ALL_BLOCKCHAINS_SUCCEEDED) { console.log(`Document verified successfully.`); } else { console.log(`Error during certificate verification: ${verificationResult.message}`); } ``` Check out this [JSFiddle](https://jsfiddle.net/8tw7xm1u/) to see what happens if you run verification on a modified document. The last line of console output is `"Error during certificate verification: Locally calculated data hash does not match hash in provided certificate: 9OI5T+j0wCi9Fp31xTBbkUkY3sguo4b69RBAngP0KYM= != 2xXmxEInHGanIixLeS28kOBomIYWuuFKWsFnceNwRQI="` indicating a mismatch of the calculated document hash and the hash stored in the blockchain. The client library correctly detects that the document has been modified. ## Customize error handling Similar to [`onMessage`](https://clientlib.cronstamp.com/classes/lifecycle_manager.CertificateProcess.html#onMessage), the [`RequestProcess`](https://clientlib.cronstamp.com/classes/lifecycle_request.RequestProcess.html) and [`VerificationProcess`](https://clientlib.cronstamp.com/classes/lifecycle_verify.VerificationProcess.html) objects returned by `requestCertificate` and `verifyCertificate` have an optional [`onError`](https://clientlib.cronstamp.com/classes/lifecycle_manager.CertificateProcess.html#onError) method. With this you can define a custom handler for processing the [`ResultData`](https://clientlib.cronstamp.com/types/lifecycle_manager.ResultData.html) object emitted in case of an error. If `onError` is not called, the handler defined by `onMessage` is used as a fallback in case of an error. ## Customize configuration You can customize the default settings of the client library by calling [`configure`](https://clientlib.cronstamp.com/classes/lifecycle_manager.CertificateProcess.html#configure) on the [`RequestProces`](https://clientlib.cronstamp.com/classes/lifecycle_request.RequestProcess.html) or [`VerificationProcess`](https://clientlib.cronstamp.com/classes/lifecycle_verify.VerificationProcess.html) with a configuration object as shown in the example below. Note that all values in the configuration object are optional, i.e., you could also provide a partial configuration to update only a subset of the settings. ```typescript import { requestCertificateForString, CronStampConfig, LOG_LEVELS } from '@cronstamp/clientlib'; export const customConfig: CronStampConfig = { LOG_LEVEL: LOG_LEVELS.WARN, BLOCKCHAIN_XRP_SERVERS: ['wss://testnet.xrpl-labs.com'], BLOCKCHAIN_SOLANA_SERVERS: ['https://api.devnet.solana.com'], API_URL: 'https://api.cronstamp.com', CERT_REQUEST_TIMEOUT: 100, CERT_REQUEST_INTERVAL: 3 }; let requestResult = await requestCertificateForString('some message').configure(customConfig).start(); ``` The default settings can be found [here](https://gitlab.com/cronstamp/clientlib/-/blob/master/src/util/config.ts?ref_type=heads#L15).