Haskell library and client for OpenTimestamps

This is a Haskell library and -client for working with OpenTimestamps. OpenTimestamps is a protocol for creating and verifying timestamps for digital files, which can be used to prove that a file existed at a certain point in time.

Library

The library provides functionality for:

  • Stamping: Creating new timestamps for files by submitting their hashes to a network of calendar servers.
  • Information: Extracting information from timestamp files.
  • Pruning: Pruning existing timestamps.
  • Upgrading: Upgrade remote calendar timestamps to be locally verifiable.
  • Verification: Verifying the integrity and validity of existing timestamps.

The project has a number of dependencies, including cryptonite for cryptographic operations, http-client for making requests to calendar servers and haskoin-node and bitcoin-rpc to make RPC-calls to a locally running 'blocksonly' pruned Bitcoin node, which is required for Verification.

Client

This is a Haskell client for OpenTimestamps, providing a command-line utility to interact with the OpenTimestamps network.

The client uses the haskell-opentimestamps library for core OpenTimestamps stuff.

Stamp

1$ ../bin/haskell-opentimestamps-client -v stamp Makefile
2Reading content of: Makefile
3Stamping: Makefile
4Timestamp saved to Makefile.ots

Info

  1$ ../bin/haskell-opentimestamps-client info Makefile.ots
  2File sha256 hash: ecafaca3c4165df39633cc3bff8843ac6a7e52b68fe2a7403599ee99cfd316d9
  3Timestamp:
  4append af426c8366c10cdfcde9d2163a44e658
  5sha256
  6 -> append 54fc2a2a2040527f
  7    sha256
  8    append 103f7068035dd6a276fc8c2c00e0c0be
  9    sha256
 10    prepend 68bdcb18
 11    append 6ee6d6b320e0156b
 12    verify PendingAttestation("https://bob.btc.calendar.opentimestamps.org"
 13    sha256
 14    prepend a5d1d94c62badd8091f73d1e6bd81716322477f5bff60a96767f1cd5a1755ad6
 15    sha256
 16    append 8ea9e02de7bd80ec54bfed1e67955757ba855fe7d2279c9032e199354c774f21
 17    sha256
 18    append da3ff3de1b5f8305171d85729516f02a9d89f75f20351d7d0e99ddf4e75dc8f7
 19    sha256
 20    append 2e0d8b7d6bf6a0306cb2536c78d00feb87311681e449173cf50f8fec3924fbab
 21    sha256
 22    prepend 6faac7c3e45a5289d27f6bf9e2e307dfe2747e867ca909653ada5c77c35f40a5
 23    sha256
 24    append c054d868ed656aca3807096f3235e1a187234b1c653ff28d8043da044b595a33
 25    sha256
 26    append 1c5d5775448e3a7416f797820ee02e0ceb9766bdbe113cace817261b3472f504
 27    sha256
 28    prepend ef705909ea904e2a30468848727770227d94cbd4b067ab0474757922a006fbb9
 29    sha256
 30    prepend c3c790d8c1fe57d8c2f897ede7f9c772856171924f780b8f72a300d01c1516fa
 31    sha256
 32    append 782aa9d90d1ba98c3c2e088b1ea111c1cb59eb6787745b1490bbc78cd8aa9732
 33    sha256
 34    append 093234f4321a488f7ead9d3a9f1a40c5040931c5c9fffe6aa90ae5e0a7e2b801
 35    sha256
 36    prepend 0100000001b405bea1c96ecc7ba9c3339481a9773fe8e86b7db4eb2d8408c164e1f3f325e30000000000feffffff02dbdd030000000000160014e507a9ab62f3b696551647e96413a6df04b30ed00000000000000000226a20
 37    append d3f00d00
 38    # Transaction id 55a42e1dabbc155adb95806f934cf5e6ffeef53193b74e922e0bb1c547892545
 39    sha256
 40    sha256
 41    prepend 89d663da44e8ad5fef9a23d040d300be232199942a281311c2dd2170920c899d
 42    sha256
 43    sha256
 44    append 5a256977909e094752c03b36bda2409dea49be658997313432ebd4006478261a
 45    sha256
 46    sha256
 47    prepend 7325f376677ffabbb2bc3f1f972304d668494763da6e1bcc902d2a3a5a793005
 48    sha256
 49    sha256
 50    append cc9d5eebf5865d96c21fb3bcdf8dcd3279e9bd8bb870e644d4dfcb22b7e99c4d
 51    sha256
 52    sha256
 53    append 97e636fdd31eb06e6a0f67e1ffcd285873aafb3e144a4f3da7cc80d476bd76e5
 54    sha256
 55    sha256
 56    prepend d4c1e9032e42dc562fdd26f7c98f19e4af83655337074ed5f5507a23534ad1be
 57    sha256
 58    sha256
 59    prepend 4249fefe5218538378aad20809d3d2f05acaa27b27988794eb1aa327a3cc1efa
 60    sha256
 61    sha256
 62    append 5f2f318b1c5cb055b5c6c5654a852d6e7ec4b830296fff2d5f841efad9bd717a
 63    sha256
 64    sha256
 65    prepend 675b20adcd5b3b46352c59bee1d95fec9ad766a2383596d2f4528d7dba7e55e1
 66    sha256
 67    sha256
 68    append 164ac2d6ca21468cf688dddb84df1ceb45db9513157e477b05097ef573e2a029
 69    sha256
 70    sha256
 71    prepend 0155b96ab0d3adef207c522a0fe88d4a1a816f6ee3823207d298d9b2672b81fb
 72    sha256
 73    sha256
 74    append 4983aca4e67f3efe85b0a4db871444f8422410a0ab6cec8e913ddb8e87bbfe37
 75    sha256
 76    sha256
 77    append f8889592780f4c499beaef2b802c6a34b4f2cd13bd4da6e62a018a749cd227e1
 78    sha256
 79    sha256
 80    verify BitcoinBlockHeaderAttestation(913621)
 81    # Bitcoin block merkle root 1322d07e597f95297a6694c562c7e958ba98bcfe0657410ec8a00251f6d5dbf5
 82 -> append 706757a56da8443bb4c4e346f1d9a593
 83    sha256
 84    prepend ec53d58856dd36ae4fe47745dc53703d5e8e66b6e6b8837a41f3584bed79b35e
 85    sha256
 86    prepend 68bdcb1a
 87    append 23eb1c8332cb6165
 88    verify PendingAttestation("https://btc.calendar.catallaxy.com"
 89    sha256
 90    append f6a4b1d80098db7f9ec80080d947efafee468653cc34b408da96ed71416cea9b
 91    sha256
 92    prepend f79c01fa0cbe4944b0419d8f595e191e092b3418d7df60576254ded54698e1c6
 93    sha256
 94    prepend 65afe0b0c3bbbde179d22b03bdde2d5fa94bc479b8e6ef9d58de15f70ad64250
 95    sha256
 96    append e9f3b9454082b27e79ba7ab6a277eba5bf376e55932956a4754a594bc2453cae
 97    sha256
 98    prepend bbe2d718b2a9ba2919ce3eca4f6a1141b75107ce4ece96541d1512fcfc5c3126
 99    sha256
100    prepend d9776bf666e0762fde4465a29fb28de12449e47860a461494909a375000082fc
101    sha256
102    append d63d043677f2536f8a0af1e20370c32f5c99d20a692dbf612887b65b6b877f90
103    sha256
104    append cfbf7fbb89962887914955f6e38b1ae68d1fa9e829d3a2f91f15c9bb395c4d99
105    sha256
106    append be26d3dfd3ce41ac7d2bf3bd962437806748f3446d30138fcc467b309a470513
107    sha256
108    append fdccc2e5314ec36c448cb028a83cd34a49e72cd5a3e6a06eb352405ee9a9a45a
109    sha256
110    prepend 0f92ac848ed28ad2bc561b33ddbcd41c4697b67c086c3628abc5ffbb3204ff99
111    sha256
112    append 3d23909473211238377af8876819fd89358624be536bf7c5e99e1cfd049cddf5
113    sha256
114    append 3f5ef4f2de2640dc02bed7d6c9be34b4402dc2ad16ee8df84d2f2ebcb5d7ab53
115    sha256
116    append 36b3beeec8907e4f6c305e577b864972f6b3ea3e6726905576a637010c7ee979
117    sha256
118    prepend 0100000001e173d60a067bc607095350385954284799f313cbb1907e5ea0551c7ebf8475730000000000fdffffff027d88000000000000160014ad0aece6ff4e6a9334507bbcb161075284e60a6b0000000000000000226a20
119    append 03f10d00
120    # Transaction id 516050fcdb2349c4ab53109603747a0c1a767abbac37dd73e5b4e6f75c73ece0
121    sha256
122    sha256
123    append 34806b03711d1cb283354f1ad4908e5fd942b27d4e04c24fdcb4d7b2584bc6ac
124    sha256
125    sha256
126    append 6cf539958c8558c2f35a73154b3117b457d8b8b5284893f50e5a7917222130c5
127    sha256
128    sha256
129    prepend 769aa74127039654844eb432ab91e485aca2b3ce75d516732c8d7d792b698412
130    sha256
131    sha256
132    append 6a1fe4895690cb8d846876335b8a4b84a365c8433204aef86863dd6bcaf021b8
133    sha256
134    sha256
135    prepend 293368cfdb6ca39ddd91175394128c033e2ba7439ee9897877d9def1c6ab4d03
136    sha256
137    sha256
138    append e19ac587c14816cdb8a6d313450267995b6c0c4268622b5dbf1be22b8448e21a
139    sha256
140    sha256
141    prepend 51414b2b116993080a970944b26fdb3bd38c1eac47731317d8e554b42e7ea41f
142    sha256
143    sha256
144    append 9f91a8b6a82be808220e15a7f1c4de954cb2c21f1bc05574ff9f3cf9610613e9
145    sha256
146    sha256
147    append 910335379f79888acb4335c7b2e84d40f8fa3ff95e371a00de0b8c25250c8746
148    sha256
149    sha256
150    prepend 1abf7013d8e9e5c33f610d25984495e2c54be302394cf896f30e3de32d6db044
151    sha256
152    sha256
153    prepend 80689941c183c651653d406843cd5ecb9d1026eea2ca59b3b2289cd804430ce2
154    sha256
155    sha256
156    append c8d77ac84f9417896bf5f01e78537827e1d514f1994601fe0133b9a5037175b0
157    sha256
158    sha256
159    verify BitcoinBlockHeaderAttestation(913668)
160    # Bitcoin block merkle root 2eb995fab1fed57b772fe3903bfdf051b091b6dfefffc0681c5280bb9f19acc1
161 -> append 79b2133d063baeb2229546bc4db919d8
162    sha256
163    prepend 9930fbe92ad9ad0fa5433f7986cebc0a0306d2bd5ebe02f904e025387cf168f5
164    sha256
165    append d163d6f43f9be85bb365edf0f53e60285b4b05d831cf41cfe8dd37415fc27bb6
166    sha256
167    prepend 68bdcb19
168    append fda88883c8969c6e
169    verify PendingAttestation("https://finney.calendar.eternitywall.com"
170 -> append e1c7963b50915276
171    sha256
172    append 60aedf982c198a56722c4e7d20e7cc71
173    sha256
174    prepend 629ac7f7cb97bcaefa5fb61e1a0cae487bfeb544d91a8682e46a9289b11b8f0c
175    sha256
176    prepend 68bdcb18
177    append 91759bf2c1e900a1
178    verify PendingAttestation("https://alice.btc.calendar.opentimestamps.org"
179    sha256
180    prepend c68560d8de5973df049919978d5db2f12fa98a2208525fb768acba8d761bb59e
181    sha256
182    prepend 1d7b7f58004cff7966ef54f0849fd40fe086df93c6d9e8cc35bcb2fe272e9221
183    sha256
184    append 9eee6568170001acbc94729924f2832c3462ff945a0c24e4ca8d77ed9bc7242d
185    sha256
186    append 4324107ac567b8d1784571123c07fc43c555cbae59db51d4cf76c821113b25af
187    sha256
188    prepend 7932f979fca0efc7caa21648422a43b5ab8086fc389659240a677b61948d56e0
189    sha256
190    prepend bff85ff298ce09483026531ff6cc4602fa75f3da46639df120a7d0c681009001
191    sha256
192    append 2be867c9379b9688e35dcdf0ca327da27cb512e64c2345ef71d9461f8d2b6642
193    sha256
194    prepend c06727f69b1b5b1d50743f5fb3ddf1e3415c5d02e3482ef086b6a21b07b26974
195    sha256
196    prepend 70d1e9d5e26b3a0f750f5b819cfa000b880bc534b0516758eabd6092792a5b5a
197    sha256
198    prepend b687d71a7415c2054489548c2743cd52d111e64d55376375c3afad5648d5a178
199    sha256
200    prepend 0100000001ae53387ccdf44c700820ce0630b1d5303ce186b2ade6c8aa609eeaa8c47cf4710000000000feffffff02535f0400000000001600141f56764a6d531d209b0c839101a352c7fc1a36310000000000000000226a20
201    append cff00d00
202    # Transaction id a6aeeb9c40fe82d42987d9a69f1fa71ee1712a63f66cb74e8f1045b67d9e07ef
203    sha256
204    sha256
205    append 390c03975dc0f7b9b376ce8c2a1787e78345bd0bf3328d4accdc32c7151185f6
206    sha256
207    sha256
208    append f70a32e350380846f6e7fb8f0a2f922b5da9ebdb5f87dbbf9dd096bbdcccd003
209    sha256
210    sha256
211    append 3934534bc19f317d9d4f47faa9c8c4ba9aeb34d552fbd3285d0463f1f0792136
212    sha256
213    sha256
214    append 716b4fe63ff39474816b1007edb7e5d943548bdbc129fbde592584a9c936798e
215    sha256
216    sha256
217    prepend b28ce2ee6ed19119b65812d10437f35360901a937dea0a151c86f1eb57baf77d
218    sha256
219    sha256
220    append 8a060eea9368c18656ecaf29db7d5bebae38e40db6abb4f40836564bae0fba51
221    sha256
222    sha256
223    append 90273a8f005fbd2c4751761ba382741bb6c990f35d803b956cf8749bbba4d260
224    sha256
225    sha256
226    append 8425e8f774eeabb7719eb1e4adf2b09c60a81d35b11df0f98df1f410079665c5
227    sha256
228    sha256
229    append 315e96cd2fb7ddbdd36cf2af8b5e8eeb14e1b062b256066d3f410d0bab15c242
230    sha256
231    sha256
232    append 924477916389635d8d2ee1cd8934e6b848c85b57f4c3395e6ffe43cb03c50cc8
233    sha256
234    sha256
235    append 132b559f3d3fddbd831b7a1107ad4bea44be201cc3e18eadb32728ff776fff90
236    sha256
237    sha256
238    append 6353e68815428d780bba37f3f636b5dde7f79806c6e9b4df601a8db5ad66a209
239    sha256
240    sha256
241    append 276c31015cb406cb1acd74f583247750f7c4a161d79379442632366026640802
242    sha256
243    sha256
244    verify BitcoinBlockHeaderAttestation(913617)
245    # Bitcoin block merkle root 7580ebb22fa1cb26bfd38a685ead151592bd26bf80dc1b4a0cdcbe7b927773a8

Prune

1$ ../bin/haskell-opentimestamps-client -v prune hello-world-haskell.txt.ots
2Reading timestamp file: hello-world-haskell.txt.ots
3Pruning timestamp: hello-world-haskell.txt.ots
4Timestamp pruned and saved to hello-world-haskell.txt.ots
5
6$ ll
7.rw------- 4.6k mdo  8 Sep 12:43 hello-world-haskell.txt.ots.bak
8.rw------- 2.5k mdo  8 Sep 12:45 hello-world-haskell.txt.ots

Upgrade

 1$ cp ../haskell-opentimestamps/examples/incomplete.txt* . -i
 2
 3$ ../bin/haskell-opentimestamps-client -v upgrade incomplete.txt.ots 
 4Reading timestamp file: incomplete.txt.ots
 5Upgrading timestamp: incomplete.txt.ots
 6Upgrade iteration: 1
 7Found 1 pending attestations.
 8  Attempting to fetch for message "57cfa5c46716df9bd9e83595bce439c58108d8fcc1678f30d4c6731c3f1fa6c79ed712c66fb1ac8d4e4eb0e7" from ["https://alice.btc.calendar.opentimestamps.org"]
 9Fetching from: https://alice.btc.calendar.opentimestamps.org/timestamp/57cfa5c46716df9bd9e83595bce439c58108d8fcc1678f30d4c6731c3f1fa6c79ed712c66fb1ac8d4e4eb0e7
10Response status: 200
11Successfully deserialized timestamp.
12  Successfully fetched 1 upgrades.
13New attestations found, continuing upgrade.
14Upgrade complete after 1 iterations.
15Timestamp upgraded and saved to incomplete.txt.ots
16
17$ ls -l incomplete.txt.ots ../haskell-opentimestamps/examples/incomplete.txt.ots 
18-rw-rw-r-- 1 mdo mdo 175 Sep  4 19:33 ../haskell-opentimestamps/examples/incomplete.txt.ots
19-rw------- 1 mdo mdo 875 Sep  7 20:16 incomplete.txt.ots

Verify

 1$ ../bin/haskell-opentimestamps-client -v verify incomplete.txt.ots 
 2"Cookiefile path: /home/mdo/.bitcoin/.cookie"
 3Reading timestamp file: incomplete.txt.ots
 4Verifying timestamp: incomplete.txt.ots
 5Upgrade complete after 0 iterations.
 6"eBlockHeader: Right (BlockHeader {blockHeaderHash = \"000000000000000000ca478eb185560e1f1178746ee05e3d9bc9a31765f6f4a3\",
 7blockHeaderConfs = 375677, blockHeaderHeight = 428648,
 8blockHeaderMerkleRoot = \"078cdde9c89f2e3c58c96b1658627fd9298c63c6618954ea24ac3b5a13fe18da\",
 9blockHeaderTime = 2016-09-07 05:56:43 UTC,
10blockHeaderMedianTime = 2016-09-07 04:47:47 UTC,
11blockHeaderNonce = 2211912663,
12blockHeaderDifficulty = 2.207559083303723e11,
13blockHeaderTxCount = 1014,
14blockHeaderPrevHash = Just \"0000000000000000004f9b43c138ad0e2b048483738dc7dbab3bfda5d18e4d8d\",
15blockHeaderNextHash = Just \"0000000000000000003ac0b3d2a2d91f549bc655cd4a43801607d96323270e1d\"})"
16Success! Bitcoin block 428648 attests existence as of 2016-09-07 01:56:43 CEST

and:

 1"Cookiefile path: /home/mdo/.bitcoin/.cookie"
 2Upgrade iteration: 1
 3Found 4 pending attestations.
 4  Attempting to fetch for message "68caa16de703cf7a5ee55d2255e37375388866e1864bb513aa1db348cd3b89cac0b0591333532bdc68188d79" from ["https://btc.calendar.catallaxy.com"]
 5Fetching from: https://btc.calendar.catallaxy.com/timestamp/68caa16de703cf7a5ee55d2255e37375388866e1864bb513aa1db348cd3b89cac0b0591333532bdc68188d79
 6Response status: 200
 7Successfully deserialized timestamp.
 8  Successfully fetched 1 upgrades.
 9  Attempting to fetch for message "68caa16c4d0a2c40148c0089d8885c49cc1bcd5b36372739223c9487a665131fe6b6bfc60472741b3b95a41f" from ["https://finney.calendar.eternitywall.com"]
10Fetching from: https://finney.calendar.eternitywall.com/timestamp/68caa16c4d0a2c40148c0089d8885c49cc1bcd5b36372739223c9487a665131fe6b6bfc60472741b3b95a41f
11Response status: 200
12Successfully deserialized timestamp.
13  Successfully fetched 1 upgrades.
14  Attempting to fetch for message "68caa16b26d2ea12a85f708d8ae79706daf0fe2f431b4c1cd02a2e78bc25057d4478b633aada7f2e67229bd1" from ["https://alice.btc.calendar.opentimestamps.org"]
15Fetching from: https://alice.btc.calendar.opentimestamps.org/timestamp/68caa16b26d2ea12a85f708d8ae79706daf0fe2f431b4c1cd02a2e78bc25057d4478b633aada7f2e67229bd1
16Response status: 200
17Successfully deserialized timestamp.
18  Successfully fetched 1 upgrades.
19  Attempting to fetch for message "68caa16b59f449da8ad653d6ea930a2fca2e40701bbf0a29fbfab461251fa43b580a63c16e1da967dad2ba6b" from ["https://bob.btc.calendar.opentimestamps.org"]
20Fetching from: https://bob.btc.calendar.opentimestamps.org/timestamp/68caa16b59f449da8ad653d6ea930a2fca2e40701bbf0a29fbfab461251fa43b580a63c16e1da967dad2ba6b
21Response status: 200
22Successfully deserialized timestamp.
23  Successfully fetched 1 upgrades.
24New attestations found, continuing upgrade.
25Upgrade complete after 1 iterations.
26"eBlockHeader: Right (BlockHeader {blockHeaderHash = \"00000000000000000002055b4d853890d231ad2ac28db2cbc60442d9b176473a\", blockHeaderConfs = 664, blockHeaderHeight = 915128, blockHeaderMerkleRoot = \"435d70b6a47b687026cc81dc49a84288e942b16ce36dd7bc18bd227a5fd6715d\", blockHeaderTime = 2025-09-17 16:30:12 UTC, blockHeaderMedianTime = 2025-09-17 15:15:11 UTC, blockHeaderNonce = 2547123219, blockHeaderDifficulty = 1.360398728482613e14, blockHeaderTxCount = 3446, blockHeaderPrevHash = Just \"000000000000000000005ff263ed71d33ae3dd519b512b98bb9a17b662f3240f\", blockHeaderNextHash = Just \"000000000000000000016243f6155663ea56aeb617e220dfe372214395a7d65b\"})"
27Success! Bitcoin block 915128 attests existence as of 2025-09-17 12:30:12 CEST

Work In Progress

This software is under development. Although 'roundtrip' tested (stamp->info->upgrade->verify locally) there are remaining debug messages in the output and e.g. prune functionality not yet has all parameters implemented.

Setup and Build

libsecp256k1

Beware: you will need to have a libsecp256k1 package (Debian: libsecp256k1-dev) installed on your Linux distribution (beware: it needs to be < 0.7.x because of deprecated functions in 0.7.x). Otherwise dependencies will not be fullfilled and build will fail with a cryptic message.

Git

The client and the library are two seperate projects.

Build of the client

Clone the git repository and run the build command, as follows:

1git clone https://codeberg.org/photonsphere/haskell-opentimestamps-client
2cd haskell-opentimestamps-client
3make release

When successful it prints the path to the client executable.

The library

Clone the git repository and run the test command, as follows:

1git clone https://codeberg.org/photonsphere/haskell-opentimestamps
2cd haskell-opentimestamps
3make test

Beware: a local (pruned) Bitcoin node is required for some verify tests (if it's not running you'll get at least one failed test).

Codeberg

The Codeberg repositories: