1 /// \file recorderchain.d
2 module tagion.tools.recorderchain;
3 
4 /**
5  * @brief tool replay whole recorder chain in DART database
6  */
7 
8 import std.array;
9 import std.file : copy, exists;
10 import std.format;
11 import std.getopt;
12 import std.path;
13 import std.stdio;
14 import tagion.basic.Types : FileExtension;
15 import tagion.basic.tagionexceptions;
16 import tagion.crypto.SecureInterfaceNet : SecureNet;
17 import tagion.crypto.SecureNet;
18 import tagion.dart.BlockFile;
19 import tagion.dart.DART;
20 import tagion.dart.DARTFile;
21 import tagion.dart.Recorder;
22 // import tagion.prior_services.RecorderService;
23 import tagion.recorderchain.RecorderChain;
24 import tagion.recorderchain.RecorderChainBlock : RecorderChainBlock;
25 import tagion.tools.Basic;
26 
27 auto logo = import("logo.txt");
28 
29 mixin Main!(_main, "tagionrecorderchain");
30 
31 int _main(string[] args) {
32     immutable program = args[0];
33 
34     if (args.length == 1) {
35         writeln("Error: No arguments provided for ", baseName(args[0]), "!");
36         return 1;
37     }
38 
39     /** Net for DART database creation */
40     SecureNet secure_net = new StdSecureNet;
41     /** Net for recorder factory creation */
42     const hash_net = new StdHashNet;
43     /** Used for create recorder */
44     auto factory = RecordFactory(hash_net);
45     /** Passphrase for generate key pair for secure net */
46     string passphrase = "verysecret";
47     secure_net.generateKeyPair(passphrase);
48     /** Directory for recorder block chain */
49     string chain_directory;
50     /** Directory for DART database */
51     string dart_file;
52     /** Directory for genesis DART */
53     string gen_dart_file;
54 
55     bool interactive_mode;
56 
57     
58     GetoptResult main_args;
59 
60     try {
61         main_args = getopt(args,
62                 std.getopt.config.caseSensitive,
63                 std.getopt.config.bundling,
64                 "chaindirectory|c", "Path to recorder chain directory", &chain_directory,
65                 "dartfile|d", "Path to dart file", &dart_file,
66                 "genesisdart|g", "Path to genesis dart file", &gen_dart_file,
67                 "interactive|i", "Go through the recorder in interactive mode", &interactive_mode,
68         );
69 
70         if (main_args.helpWanted) {
71             writeln(logo);
72             defaultGetoptPrinter(
73                     [
74                 // format("%s version %s", program, REVNO),
75                 "Documentation: https://tagion.org/",
76                 "",
77                 "Usage:",
78                 format("%s [<option>...]", program),
79                 "",
80                 "Examples:",
81                 "# To run recorer chain specify 3 required parameters",
82                 format("%s -с chain_directory -d new_dart.drt -g genesis_dart.drt", program),
83                 "",
84                 "<option>:",
85 
86             ].join("\n"),
87                     main_args.options);
88             return 0;
89         }
90     }
91     catch (Exception e) {
92         stderr.writefln(e.msg);
93         return 1;
94     }
95 
96     // Check genesis DART file 
97     if (!gen_dart_file.exists || gen_dart_file.extension != FileExtension.dart) {
98         writefln("Incorrect genesis DART file '%s'", gen_dart_file);
99         return 1;
100     }
101 
102     // Copy genesis DART as base for DART that is being recovered
103     gen_dart_file.copy(dart_file);
104 
105     // Open new DART file
106     DART dart;
107     try {
108         dart = new DART(secure_net, dart_file);
109     }
110     catch (Exception e) {
111         writefln("Invalid format of genesis DART file '%s'", gen_dart_file);
112         return 1;
113     }
114 
115     // Check existence of recorder chain directory
116     if (!chain_directory.exists) {
117         writefln("Recorder chain directory '%s' does not exist", chain_directory);
118         return 1;
119     }
120 
121     RecorderChainStorage storage = new RecorderChainFileStorage(chain_directory, hash_net);
122     auto recorder_chain = new RecorderChain(storage);
123 
124     // Check validity of recorder chain
125     if (!recorder_chain.isValidChain) {
126         writeln("Recorder block chain is not valid!\nAbort");
127         return 1;
128     }
129 
130     // Collect info from chain directory
131     auto blocks_count = recorder_chain.storage.getHashes.length;
132     if (blocks_count == 0) {
133         writeln("No recorder chain files");
134         return 1;
135     }
136     import core.stdc.stdio;
137 
138 
139     import tagion.hibon.HiBONJSON;
140     try {
141         recorder_chain.replay((RecorderChainBlock block) {
142             writefln("current bullseye<%(%02x%)>", dart.bullseye);
143             if (interactive_mode) {
144                 writefln("recorder that will be added:\n%s", block.recorder_doc.toPretty);
145                 const input = getchar;
146             }
147 
148             auto recorder = factory.recorder(block.recorder_doc);
149             dart.modify(recorder);
150             if (block.bullseye != dart.bullseye) {
151                 stderr.writeln(format("ERROR: expected bullseye: %(%02x%) \ngot %(%02x%)", 
152                     block.bullseye, 
153                     dart.bullseye));
154             }
155         });
156     }
157     catch(TagionException e) {
158         writefln("%s. Abort", e.msg);
159         return 1;
160     }    
161 
162     return 0;
163 }