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: