1 /// \file kette.d 2 module tagion.tools.kette; 3 4 /** 5 * @brief tool for replaying and undoing the dart database 6 */ 7 8 import tagion.tools.Basic; 9 import std.getopt; 10 import tagion.tools.revision; 11 import tagion.dart.DART; 12 import tagion.dart.Recorder; 13 import tagion.basic.Types : FileExtension; 14 import std.path : extension; 15 import tagion.crypto.SecureInterfaceNet : SecureNet; 16 import tagion.crypto.SecureNet; 17 import std.file : copy, exists; 18 import std.range; 19 import tagion.dart.DARTException; 20 import tagion.hibon.HiBONRecord: isRecord; 21 import tagion.hibon.HiBONJSON : toPretty; 22 import tagion.hibon.HiBONtoText; 23 import std.format; 24 25 26 27 import tagion.recorderchain.RecorderChainBlock : RecorderBlock; 28 29 30 mixin Main!(_main, "tagionkette"); 31 32 import std.stdio; 33 34 int _main(string[] args) { 35 immutable program = args[0]; 36 bool version_switch; 37 38 SecureNet secure_net = new StdSecureNet; 39 const hash_net = new StdHashNet; 40 auto factory = RecordFactory(hash_net); 41 string genesis_dart; 42 string dart_file; 43 44 string passphrase = "verysecret"; 45 secure_net.generateKeyPair(passphrase); 46 47 48 bool replay; 49 bool replay_and_find; 50 51 GetoptResult main_args; 52 try { 53 main_args = getopt(args, 54 std.getopt.config.caseSensitive, 55 std.getopt.config.bundling, 56 "version", "display the version", &version_switch, 57 "v|verbose", "Prints more debug information", &__verbose_switch, 58 "genesisdart|g", "Path to genesis dart file", &genesis_dart, 59 "dartfile|d", "Path to dart file", &dart_file, 60 "replay|r", "Replay the recorder", &replay, 61 "findreplay|f", "Find the block and replay from there", &replay_and_find, 62 ); 63 64 if (version_switch) { 65 revision_text.writeln; 66 return 0; 67 } 68 if (main_args.helpWanted) { 69 defaultGetoptPrinter( 70 [ 71 // format("%s version %s", program, REVNO), 72 "Documentation: https://tagion.org/", 73 "", 74 "Usage:", 75 "", 76 "<option>:", 77 78 ].join("\n"), 79 main_args.options); 80 return 0; 81 } 82 83 if (!genesis_dart.exists || genesis_dart.extension != FileExtension.dart) { 84 error("incorrect genesis dart file"); 85 return 1; 86 } 87 88 if (replay) { 89 // Copy genesis DART as base for DART that is being recovered 90 genesis_dart.copy(dart_file); 91 } 92 93 DART dart; 94 scope(exit) { 95 dart.close; 96 } 97 try { 98 dart = new DART(secure_net, dart_file); 99 } 100 catch (DARTException e) { 101 error(e); 102 return 1; 103 } 104 105 106 107 import tagion.hibon.HiBONFile : HiBONRange; 108 109 110 if (replay) { 111 foreach(inputfilename; args[1 ..$]) { 112 writefln("going through the blocks of %s", inputfilename); 113 if (inputfilename.extension != FileExtension.hibon) { 114 error("File %s not valid (only %(.%s %))", 115 inputfilename, only(FileExtension.hibon)); 116 return 1; 117 118 } 119 auto fin = File(inputfilename, "r"); 120 scope(exit) { 121 fin.close; 122 } 123 124 RecorderBlock prev_block; 125 foreach(no, doc; HiBONRange(fin).enumerate) { 126 // add the blocks 127 if (!doc.isRecord!RecorderBlock) { 128 error("The document in the range was not of type RecorderBlock"); 129 return 1; 130 } 131 const _block = RecorderBlock(doc); 132 133 134 verbose("block %s", _block.toPretty); 135 136 if (prev_block !is RecorderBlock.init) { 137 const hash_of_prev = hash_net.calcHash(prev_block.toDoc); 138 if (hash_of_prev != _block.previous) { 139 error("The chain is not valid. fingerprint of previous %s=%s block %s expected %s", prev_block.epoch_number, hash_of_prev.encodeBase64, _block.epoch_number, _block.previous.encodeBase64); 140 141 } 142 } 143 144 const _recorder = factory.recorder(_block.recorder_doc); 145 verbose("epoch: %s, eye %(%02x%), recorder length %s", _block.epoch_number, _block.bullseye, _recorder.length); 146 const new_bullseye = dart.modify(_recorder); 147 if (_block.bullseye != new_bullseye) { 148 error(format("ERROR: expected bullseye: %(%02x%) \ngot %(%02x%)", 149 _block.bullseye, 150 new_bullseye)); 151 return 1; 152 } 153 verbose("succesfully added block"); 154 prev_block = _block; 155 } 156 157 } 158 159 } 160 161 if (replay_and_find) { 162 bool match; 163 164 const init_eye = dart.bullseye; 165 verbose("searching for eye %(%02x%)", init_eye); 166 foreach(inputfilename; args[1 ..$]) { 167 writefln("going through the blocks of %s", inputfilename); 168 if (inputfilename.extension != FileExtension.hibon) { 169 error("File %s not valid (only %(.%s %))", 170 inputfilename, only(FileExtension.hibon)); 171 return 1; 172 } 173 auto fin = File(inputfilename, "r"); 174 scope(exit) { 175 fin.close; 176 } 177 178 RecorderBlock prev_block; 179 Blocks: foreach(no, doc; HiBONRange(fin).enumerate) { 180 // add the blocks 181 if (!doc.isRecord!RecorderBlock) { 182 error("The document in the range was not of type RecorderBlock"); 183 return 1; 184 } 185 const _block = RecorderBlock(doc); 186 scope(success) { 187 prev_block = _block; 188 } 189 if (prev_block !is RecorderBlock.init) { 190 const hash_of_prev = hash_net.calcHash(prev_block.toDoc); 191 if (hash_of_prev != _block.previous) { 192 error("The chain is not valid. fingerprint of previous %s=%s block %s expected %s", prev_block.epoch_number, hash_of_prev.encodeBase64, _block.epoch_number, _block.previous.encodeBase64); 193 194 } 195 } 196 197 if (!match && _block.bullseye == init_eye) { 198 verbose("found a MATCH on epoch %d", _block.epoch_number); 199 match = true; 200 continue Blocks; 201 } 202 203 if (!match) { 204 verbose("Bullseye did not match searching next epoch %s", _block.epoch_number); 205 continue Blocks; 206 } 207 verbose("block %s", _block.toPretty); 208 209 210 const _recorder = factory.recorder(_block.recorder_doc); 211 verbose("epoch: %s, eye %(%02x%), recorder length %s", _block.epoch_number, _block.bullseye, _recorder.length); 212 const new_bullseye = dart.modify(_recorder); 213 if (_block.bullseye != new_bullseye) { 214 error(format("ERROR: expected bullseye: %(%02x%) \ngot %(%02x%)", 215 _block.bullseye, 216 new_bullseye)); 217 return 1; 218 } 219 verbose("succesfully added block"); 220 } 221 222 } 223 224 } 225 226 } catch (Exception e) { 227 error(e); 228 } 229 230 231 232 233 return 0; 234 235 236 237 } 238 239 240 241