This repository hosts the javascript implementation of OpenTimestamps.
It is based on the python implementation at Opentimestamps-Python and Opentimestamps-Client
It has been developed as node modules for being used within Node environments along with Node Package Manager.
You can also use it in the browser, see the Installation section.
Jsdoc available
$ npm install -g javascript-opentimestamps
Take the js used by opentimestamps.org
From this repo, install the dev library
npm install --dev
then run the gulp process
gulp
include the result /dist/opentimestamps.min.js
in your page
<script src="opentimestamps.min.js"></script>
This library is tested on CI against version 6.0, 6.1 and 7. See Travis build
Download: NodeJS
The lib is compatible with ECMAScript6(ES6) and ECMAScript5(ES5).
Browser compatibility:
- Chrome version >= 40
- Chromium version >= 40
- Firefox version >= 40
- Internet Explorer version >= 9
- Safari version >= 8
The following example requires you installed globally the javascript-opentimestamps library, if you want to test locally just prepend node
to the command, like node ots-cli.js --help
Create timestamp README.md.ots
from this README.md
with the aid of a remote calendar.
$ ots-cli.js stamp README.md
Submitting to remote calendar https://alice.btc.calendar.opentimestamps.org
Submitting to remote calendar https://bob.btc.calendar.opentimestamps.org
Submitting to remote calendar https://finney.calendar.eternitywall.com
The timestamp proof 'README.md.ots' has been created!
If you already have the hash of some file, you don't need to rehash it:
$ ots-cli.js stamp -d 05c4f616a8e5310d19d938cfd769864d7f4ccdc2ca8b479b10af83564b097af9
Submitting to remote calendar https://alice.btc.calendar.opentimestamps.org
Submitting to remote calendar https://bob.btc.calendar.opentimestamps.org
Submitting to remote calendar https://finney.calendar.eternitywall.com
The timestamp proof '05c4f616a8e5310d19d938cfd769864d7f4ccdc2ca8b479b10af83564b097af9.ots' has been created!
Note that verify implicity requires the file must be called like the .ots receipt but without the ots, if you timestamp hashes and want to verify you need to rename files accordingly.
The default hash used sha256
but you can also specify other hashes like sha1
or ripemd
(eg ots-cli.js stamp -d 4f8d8389200583977943be86f0f74a964a670405 -a sha1
). Note that partially broken hash function like sha1
is ok for timestamping purpose because they are still resistant to preimage attacks (their use is discouraged anyway).
The stamp command supports multiple files or hashes as arguments.
$ ots-cli.js stamp README.md ots-cli.js
Submitting to remote calendar https://alice.btc.calendar.opentimestamps.org
Submitting to remote calendar https://bob.btc.calendar.opentimestamps.org
Submitting to remote calendar https://finney.calendar.eternitywall.com
The timestamp proof 'ots-cli.js.ots' has been created!
The timestamp proof 'README.md.ots' has been created!
Show information on a timestamp.
$ ots-cli.js info examples/incomplete.txt.ots
File sha256 hash: 05c4f616a8e5310d19d938cfd769864d7f4ccdc2ca8b479b10af83564b097af9
Timestamp:
append e754bf93806a7ebaa680ef7bd0114bf4
sha256
append b573e8850cfd9e63d1f043fbb6fc250e
sha256
prepend 57cfa5c4
append 6fb1ac8d4e4eb0e7
verify PendingAttestation('https://alice.btc.calendar.opentimestamps.org')
You can have verbose output which clarifies operations, arguments, results and also contains comments showing the involved transaction id and calculated merkle tree root.
$ node ots-cli.js info -v examples/hello-world.txt.ots
File sha256 hash: 03ba204e50d126e4674c005e04d82e84c21366780af1f43bd54a37816b6ab340
Timestamp:
ripemd160 == 1df8859e60bc679503d16dcb870e6ce91a57e9df
prepend 0100000001e482f9d32ecc3ba657b69d898010857b54457a90497982ff56f97c4ec58e6f98010000006b483045022100b253add1d1cf90844338a475a04ff13fc9e7bd242b07762dea07f5608b2de367022000b268ca9c3342b3769cdd062891317cdcef87aac310b6855e9d93898ebbe8ec0121020d8e4d107d2b339b0050efdd4b4a09245aa056048f125396374ea6a2ab0709c6ffffffff026533e605000000001976a9140bf057d40fbba6744862515f5b55a2310de5772f88aca0860100000000001976a914 == 0100000001e482f9d32ecc3ba657b69d898010857b54457a90497982ff56f97c4ec58e6f98010000006b483045022100b253add1d1cf90844338a475a04ff13fc9e7bd242b07762dea07f5608b2de367022000b268ca9c3342b3769cdd062891317cdcef87aac310b6855e9d93898ebbe8ec0121020d8e4d107d2b339b0050efdd4b4a09245aa056048f125396374ea6a2ab0709c6ffffffff026533e605000000001976a9140bf057d40fbba6744862515f5b55a2310de5772f88aca0860100000000001976a9141df8859e60bc679503d16dcb870e6ce91a57e9df
append 88ac00000000 == 0100000001e482f9d32ecc3ba657b69d898010857b54457a90497982ff56f97c4ec58e6f98010000006b483045022100b253add1d1cf90844338a475a04ff13fc9e7bd242b07762dea07f5608b2de367022000b268ca9c3342b3769cdd062891317cdcef87aac310b6855e9d93898ebbe8ec0121020d8e4d107d2b339b0050efdd4b4a09245aa056048f125396374ea6a2ab0709c6ffffffff026533e605000000001976a9140bf057d40fbba6744862515f5b55a2310de5772f88aca0860100000000001976a9141df8859e60bc679503d16dcb870e6ce91a57e9df88ac00000000
# Bitcoin transaction id 7e9f0f7d9daa2d9e51b2e22f4abe814c3f90539afa778a9bef88dc64627cb2ec
sha256 == 9c6aa9591003377455b6f27fc71b5acfa5fbb2fa49362fa87a25ef0d799dd462
&
...content omitted...
&
prepend 0be23709859913babd4460bbddf8ed213e7c8773a4b1face30f8acfdf093b705 == 0be23709859913babd4460bbddf8ed213e7c8773a4b1face30f8acfdf093b7053f10376d0aebb4647ff550b60d69ba5ad6b6809d51af6a6476e0312a9433a3bf
sha256 == faa6e88835c144ad73f48992bc05e691a52a9199df02450194f3a03b530dc2d7
sha256 == 007ee445d23ad061af4a36b809501fab1ac4f2d7e7a739817dd0cbb7ec661b8a
verify BitcoinBlockHeaderAttestation(358391)
# Bitcoin block merkle root 8a1b66ecb7cbd07d8139a7e7d7f2c41aab1f5009b8364aaf61d03ad245e47e00
Verify the timestamp attestations with the aid of remote block explorers.
$ ots-cli.js verify examples/hello-world.txt.ots
Assuming target filename is 'examples/hello-world.txt'
Success! Bitcoin block 358391 attests existence as of 2015-05-28 CEST
Note: The verification will always try to use the local bitcoin node first and fallback to asking block explorers if it isn't possible. The connection to the node is made by looking for the bitcoin.conf
file in the default places according to your OS.
When there is no running local node, or the verification happen in the browser (which cannot access to your file system looking for the bitcoin.conf
file) the verify ask information to the block explores. Verification using block explorers is convenient but not as secure as askinkg a local node.
Upgrade incomplete remote calendar timestamps to be independently verifiable. This command overwrites the file examples/incomplete.txt.ots
if needed and makes a backup of the old content at examples/incomplete.txt.ots.bak
.
$ ots-cli.js upgrade examples/incomplete.txt.ots
Timestamp has been successfully upgraded!
The file .bak was saved!
The file .ots was upgraded
Note that from version 0.1.x to 0.2.x basic method interface changed, an upgrade will require change in your code.
Create timestamp of a file with the aid of a remote calendar.
const OpenTimestamps = require('javascript-opentimestamps');
const file = Buffer.from('5468652074696d657374616d70206f6e20746869732066696c6520697320696e636f6d706c6574652c20616e642063616e2062652075706772616465642e0a','hex');
const detached = OpenTimestamps.DetachedTimestampFile.fromBytes(new OpenTimestamps.Ops.OpSHA256(), file);
OpenTimestamps.stamp(detached).then( ()=>{
const fileOts = detached.serializeToBytes();
});
Const file
created from the hex representation of the file examples/incomplete.txt
Sometimes you don't want to timestamp a file or you already have the sha256 hash of what you want to timestamp. For example, if you want to timestamp the file examples/incomplete.txt
and you already have its hash you can do this:
const hash = Buffer.from('05c4f616a8e5310d19d938cfd769864d7f4ccdc2ca8b479b10af83564b097af9');
detached = DetachedTimestampFile.fromHash(new Ops.OpSHA256(), hash);
If you have a lot of files to timestamp it is unconvenient to make a calendar request for every file. In this cases you can build a merkle tree by your own or call the stamp function with an array for request, behind the curtain the function will make a merkle tree and perform just one call to the calendars.
const files = []; /* init this array with the binary contents of the files you want to timestamp */
const detaches = [];
files.forEach(file => {
const detached = DetachedTimestampFile.fromBytes(new Ops.OpSHA256(), file);
detaches.push(detached);
});
OpenTimestamps.stamp(detaches).then(() => {
ots = [];
detaches.forEach((timestamp, i) => {
ots.push( timestamp.serializeToBytes() );
});
}).catch(err => {
console.error(err);
});
The ots
array will contain multiple proofs, one for each input.
If you prefer the async/await (require Node 7.6+) syntax the promise based API of the library works out of the box:
go_async();
async function go_async() {
const hash = Buffer.from('05c4f616a8e5310d19d938cfd769864d7f4ccdc2ca8b479b10af83564b097af9','hex');
const detached = OpenTimestamps.DetachedTimestampFile.fromHash(new OpenTimestamps.Ops.OpSHA256(), hash);
try {
await OpenTimestamps.stamp(detached);
const fileOts = detached.serializeToBytes();
} catch(err) {
console.log(err);
}
}
Show information on a timestamp.
const OpenTimestamps = require('javascript-opentimestamps');
const fileOts = Buffer.from('004f70656e54696d657374616d7073000050726f6f6600bf89e2e884e89294010805c4f616a8e5310d19d938cfd769864d7f4ccdc2ca8b479b10af83564b097af9f010e754bf93806a7ebaa680ef7bd0114bf408f010b573e8850cfd9e63d1f043fbb6fc250e08f10457cfa5c4f0086fb1ac8d4e4eb0e70083dfe30d2ef90c8e2e2d68747470733a2f2f616c6963652e6274632e63616c656e6461722e6f70656e74696d657374616d70732e6f7267','hex');
const detached = OpenTimestamps.DetachedTimestampFile.deserialize(fileOts);
const infoResult = OpenTimestamps.info(detached);
console.log(infoResult);
Const fileOts
created from the hex representation of the file examples/incomplete.txt.ots
. You can match the printed result with the file test/incomplete.txt.ots.info
.
Verify the timestamp attestations.
const OpenTimestamps = require('javascript-opentimestamps');
const file = Buffer.from('5468697320646f63756d656e742069732074696d657374616d706564206f6e20626f7468204c697465636f696e20616e6420426974636f696e20626c6f636b636861696e73','hex');
const fileOts = Buffer.from('004f70656e54696d657374616d7073000050726f6f6600bf89e2e884e89294010832bb24ab386bef01c0656944ecafa2dbb1e4162ced385754419467f9fb6f4d97f010c7c118043ce37d45f1ab81d3cd9dc9aa08fff0109b01031328e457c754a860bc5bc567ab08f02012dbcf25d46d7f01c4bd7c7ebdcd2080974b83a9198bc63cdb23f69c817f110508f0203c6274f7a67007de279fb68938e5549f462043570ccdbc17ba43e632a772d43208f1045ab0daf9f008ad9722b721af69e80083dfe30d2ef90c8e292868747470733a2f2f66696e6e65792e63616c656e6461722e657465726e69747977616c6c2e636f6df010dfd289ba718b4f30bb78191936c762a508f02026503e60c641473ec6f833953d04f7c8a65c5059a44a7e8c01c8cb9fed2ac2b308f1045ab0dafaf008c0c7948d8d5b64cf0083dfe30d2ef90c8e232268747470733a2f2f6c74632e63616c656e6461722e636174616c6c6178792e636f6d','hex');
const detached = OpenTimestamps.DetachedTimestampFile.fromBytes(new OpenTimestamps.Ops.OpSHA256(), file);
const detachedOts = OpenTimestamps.DetachedTimestampFile.deserialize(fileOts);
OpenTimestamps.verify(detachedOts,detached).then(verifyResult => {
// return an object containing timestamp and height for every attestation if verified, undefined otherwise.
console.log(verifyResult);
// prints:
// { bitcoin: { timestamp: 1521545768, height: 514371 },
// litecoin: { timestamp: 1521540398, height: 1388467 } }
});
Const file
created from the hex representation of the file examples/ltc-and-btc.txt
while fileOts
contains examples/ltc-and-btc.txt.ots
.
Upgrade incomplete remote calendar timestamps to be independently verifiable.
const OpenTimestamps = require('javascript-opentimestamps');
const fileOts = Buffer.from('004f70656e54696d657374616d7073000050726f6f6600bf89e2e884e89294010805c4f616a8e5310d19d938cfd769864d7f4ccdc2ca8b479b10af83564b097af9f010e754bf93806a7ebaa680ef7bd0114bf408f010b573e8850cfd9e63d1f043fbb6fc250e08f10457cfa5c4f0086fb1ac8d4e4eb0e70083dfe30d2ef90c8e2e2d68747470733a2f2f616c6963652e6274632e63616c656e6461722e6f70656e74696d657374616d70732e6f7267','hex');
const detached = OpenTimestamps.DetachedTimestampFile.deserialize(fileOts);
OpenTimestamps.upgrade(detached).then((changed) => {
if (changed) {
console.log('Timestamp upgraded');
const upgradedFileOts = detached.serializeToBytes();
} else {
console.log('Timestamp not upgraded');
}
}).catch(err => {
console.log(err);
});
PRs are welcome, before reviewing them we wait the travis build concludes successfully, so please check:
- All tests must pass. (Rarely test fails for networking issue, if you think it's your case ask the maintainers in the comments to relaunch the travis build)
- standard linter is launched in the build process, any warning found by the linker break the build, so be sure to check warnings by launching
standard
before the commit, most simple errors could be automatically fixed withstandard --fix
The OpenTimestamps Client is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
The OpenTimestamps Client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.