provared 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +1062 -0
  2. package/LICENSE +202 -0
  3. package/LICENSE-CC-BY-4.0.txt +396 -0
  4. package/README.md +689 -0
  5. package/bin/provared-check.mjs +565 -0
  6. package/bin/provared-headers.mjs +110 -0
  7. package/docs/threat-model.md +997 -0
  8. package/net/headers.js +403 -0
  9. package/package.json +57 -0
  10. package/page/check-page.js +162 -0
  11. package/page/index.html +47 -0
  12. package/page/render.js +530 -0
  13. package/page/style.css +150 -0
  14. package/samples/office-supplies.expected.json +82 -0
  15. package/samples/office-supplies.jsonl +10 -0
  16. package/samples/office-supplies.show.json +59 -0
  17. package/spec/provared-format.md +1672 -0
  18. package/src/actions.js +90 -0
  19. package/src/approval.js +73 -0
  20. package/src/blockstamp.js +198 -0
  21. package/src/book.js +2232 -0
  22. package/src/check.d.ts +21 -0
  23. package/src/check.js +15 -0
  24. package/src/compare.js +276 -0
  25. package/src/cover.js +198 -0
  26. package/src/der.js +192 -0
  27. package/src/encoding.js +286 -0
  28. package/src/fields.js +94 -0
  29. package/src/guard.js +171 -0
  30. package/src/headers.js +380 -0
  31. package/src/index.d.ts +2 -0
  32. package/src/index.js +37 -0
  33. package/src/jws.js +235 -0
  34. package/src/keys.js +149 -0
  35. package/src/pass.js +67 -0
  36. package/src/passkey-browser.d.ts +20 -0
  37. package/src/passkey-browser.js +171 -0
  38. package/src/recorder.js +1115 -0
  39. package/src/seal.js +133 -0
  40. package/src/service.js +124 -0
  41. package/src/signatures.js +147 -0
  42. package/src/slip.js +401 -0
  43. package/src/standing.js +181 -0
  44. package/src/stub.js +140 -0
  45. package/src/timestamp.js +351 -0
  46. package/src/tools.js +192 -0
  47. package/src/tree.js +143 -0
  48. package/src/types.d.ts +628 -0
  49. package/src/webauthn.js +119 -0
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ // provared-headers: fetch the chain of Bitcoin block headers from nodes you
3
+ // name, check every header on this device, and keep the chain in a file.
4
+ // The checker then reads that file (provared-check --headers <file>) to
5
+ // know, without asking anyone, which blocks are part of the chain.
6
+ //
7
+ // provared-headers --node <host:port>... [--nodes-file <file>] [--chain <file>]
8
+ //
9
+ // Exit codes:
10
+ // 0 the chain is up to date, checked, and a second node on another
11
+ // network handed back the latest blocks itself
12
+ // 2 it could not be done, or what you gave on the command line could
13
+ // not be used. A chain file is never left half written, never made
14
+ // shorter, and keeps only headers that were checked
15
+ //
16
+ // This command, and only this one, makes network requests: to the nodes
17
+ // you name, and to no one else.
18
+
19
+ import { readFileSync } from 'node:fs';
20
+ import { nodeAddress, updateChainFile } from '../net/headers.js';
21
+
22
+ // Node.js marks some of its methods as experimental and says so; this
23
+ // command does not repeat that warning. Any other warning is shown.
24
+ process.removeAllListeners('warning');
25
+ process.on('warning', (w) => {
26
+ if (w.name !== 'ExperimentalWarning') console.error(`${w.name}: ${w.message}`);
27
+ });
28
+
29
+ const USAGE = `Usage: provared-headers --node <host:port>... [--nodes-file <file>] [--chain <file>]
30
+
31
+ Experimental: this command is new in version 0.1.0 and may change.
32
+
33
+ --node a Bitcoin node to fetch headers from: your own (127.0.0.1:8333), or any you choose; may be repeated
34
+ --nodes-file a file of nodes, one "host:port" to a line; anything after "#" on a line is set aside
35
+ --chain the file that holds the chain (default: block-headers.bin in this folder); it is checked
36
+ in full when it is read, and only newer headers are fetched
37
+
38
+ Only the nodes you name are asked, in the order given. At least two are needed: a second node, on another
39
+ network, must hand back the latest blocks itself. Each node sees this device's address; a node named by
40
+ its name is looked up by this device's own name service. Every header is checked here,
41
+ by the rules every Bitcoin node applies, from the chain's first block; a node can refuse, but cannot have a
42
+ false header kept.
43
+
44
+ Evidence, not a verdict.`;
45
+
46
+ function parseArguments(argv) {
47
+ const out = { nodes: [], chain: 'block-headers.bin', setAside: 0 };
48
+ let chainGiven = false;
49
+ for (let i = 0; i < argv.length; i++) {
50
+ const a = argv[i];
51
+ if (a === '--help' || a === '-h') return { help: true };
52
+ if (a === '--node' || a === '--nodes-file' || a === '--chain') {
53
+ if (i + 1 >= argv.length) return { error: `${a} needs a value.` };
54
+ const value = argv[++i];
55
+ if (a === '--node') {
56
+ const node = nodeAddress(value);
57
+ if (!node) return { error: `The value of --node is not the address of a node: host:port, [IPv6]:port, or a host alone.` };
58
+ out.nodes.push(node);
59
+ } else if (a === '--nodes-file') {
60
+ let text;
61
+ try {
62
+ text = readFileSync(value, 'utf8');
63
+ } catch {
64
+ return { error: `The file of nodes could not be read: ${value}` };
65
+ }
66
+ for (const line of text.split('\n')) {
67
+ if (line.split('#')[0].trim() === '') continue;
68
+ const node = nodeAddress(line);
69
+ if (node) out.nodes.push(node);
70
+ else out.setAside++;
71
+ }
72
+ } else {
73
+ if (chainGiven) return { error: '--chain may be given once.' };
74
+ chainGiven = true;
75
+ out.chain = value;
76
+ }
77
+ } else return { error: `Unknown option: ${a}` };
78
+ }
79
+ if (out.nodes.length < 2) return { error: 'Name at least two nodes: a second node, on another network, must hand back the latest blocks itself.' };
80
+ return out;
81
+ }
82
+
83
+ const args = parseArguments(process.argv.slice(2));
84
+ if (args.help) {
85
+ console.log(USAGE);
86
+ process.exit(0);
87
+ }
88
+ if (args.error) {
89
+ console.error(`${args.error}\n\n${USAGE}`);
90
+ process.exit(2);
91
+ }
92
+ if (args.setAside) console.log(`${args.setAside} line${args.setAside === 1 ? '' : 's'} of the file of nodes ${args.setAside === 1 ? 'is' : 'are'} not the address of a node, and ${args.setAside === 1 ? 'was' : 'were'} set aside.`);
93
+
94
+ console.log('Experimental: provared-headers is new in version 0.1.0 and may change.');
95
+
96
+ let done;
97
+ try {
98
+ done = await updateChainFile({ file: args.chain, nodes: args.nodes, log: (line) => console.log(line.startsWith('Checking') || line.startsWith('It holds') ? line : ` ${line}`) });
99
+ } catch (e) {
100
+ console.error(e.message);
101
+ process.exit(2);
102
+ }
103
+
104
+ const { chain, before } = done;
105
+ const tip = chain.height;
106
+ console.log(`\nThe chain in ${args.chain}: blocks 0 to ${tip}, every header checked on this device.`);
107
+ console.log(`The latest block, ${tip}: ${chain.fingerprintAt(tip)}, dated ${new Date(chain.timeAt(tip)).toISOString().slice(0, 19)}Z.`);
108
+ console.log(`${before >= 0 ? `${tip - before} new headers fetched. ` : ''}A second node, on another network, handed back the latest blocks itself.`);
109
+ console.log(`\nTo use it: provared-check <record> --headers ${args.chain} --issuer <thumbprint> ...`);
110
+ console.log('A block counts only if at least six blocks come after it in this chain.');