1 /// Basic fuinction and types used in the DART database 2 module tagion.dart.DARTBasic; 3 4 @safe: 5 import std.format; 6 import std.traits; 7 import std.typecons; 8 import std.array; 9 import tagion.basic.Types : Buffer; 10 import tagion.crypto.SecureInterfaceNet : HashNet; 11 import tagion.crypto.Types : BufferType, Fingerprint; 12 import tagion.dart.DARTFile : KEY_SPAN; 13 import tagion.hibon.Document; 14 import tagion.hibon.HiBONRecord : isHiBONRecord; 15 import tagion.hibon.HiBONRecord : HiBONPrefix, STUB; 16 17 /** 18 * This is the raw-hash value of a message and is used when message is signed. 19 */ 20 alias DARTIndex = Typedef!(Buffer, null, BufferType.HASHPOINTER.stringof); 21 22 /** 23 * Calculates the fingerprint used as an index for the DART 24 * Handles the hashkey '#' and stub used in the DART 25 * Params: 26 * net = Hash function interface 27 * doc = document to be hashed 28 * Returns: 29 * The DART fingerprint 30 */ 31 32 immutable(DARTIndex) dartIndex(const(HashNet) net, const(Document) doc) { 33 if (!doc.empty && (doc.keys.front[0] is HiBONPrefix.HASH)) { 34 if (doc.keys.front == STUB) { 35 return doc[STUB].get!DARTIndex; 36 } 37 auto first = doc[].front; 38 immutable value_data = first.data[0 .. first.size]; 39 return DARTIndex(net.rawCalcHash(value_data)); 40 } 41 return DARTIndex(cast(Buffer) net.calcHash(doc)); 42 } 43 44 /// Ditto 45 immutable(DARTIndex) dartIndex(T)(const(HashNet) net, T value) if (isHiBONRecord!T) { 46 return net.dartIndex(value.toDoc); 47 } 48 49 unittest { // Check the #key hash with types 50 import tagion.crypto.SecureInterfaceNet : HashNet; 51 import tagion.crypto.SecureNet : StdHashNet; 52 import tagion.hibon.HiBONRecord : HiBONRecord, label; 53 54 const(HashNet) net = new StdHashNet; 55 static struct HashU32 { 56 @label("#key") uint x; 57 string extra_name; 58 mixin HiBONRecord; 59 } 60 61 static struct HashU64 { 62 @label("#key") ulong x; 63 mixin HiBONRecord; 64 } 65 66 HashU32 hash_u32; 67 HashU64 hash_u64; 68 hash_u32.x = 42; 69 hash_u64.x = 42; 70 import std.stdio; 71 72 assert(net.dartIndex(hash_u32) != net.dartIndex(hash_u64)); 73 auto other_hash_u32 = hash_u32; 74 other_hash_u32.extra_name = "extra"; 75 assert(net.dartIndex(hash_u32) == net.dartIndex(other_hash_u32), 76 "Archives with the same #key should have the same dart-Index"); 77 assert(net.calcHash(hash_u32) != net.calcHash(other_hash_u32), 78 "Two archives with same #key and different data should have different fingerprints"); 79 } 80 81 DARTIndex dartKey(T)(const(HashNet) net, const(char[]) name, T val) { 82 import std.stdio; 83 import tagion.hibon.HiBON; 84 85 const key = (name[0] == HiBONPrefix.HASH) ? name.idup : (HiBONPrefix.HASH ~ name).idup; 86 auto h = new HiBON; 87 h[key] = val; 88 return net.dartIndex(Document(h)); 89 } 90 91 unittest { 92 import std.format; 93 import std.traits; 94 import std.typecons; 95 import tagion.crypto.SecureNet : StdHashNet; 96 import tagion.hibon.BigNumber; 97 import tagion.hibon.HiBONBase : Type; 98 import tagion.hibon.HiBONRecord : HiBONRecord, label; 99 import tagion.utils.StdTime; 100 101 const net = new StdHashNet; 102 103 static struct DARTKey(T) { 104 @label("#key") T key; 105 int x; 106 107 mixin HiBONRecord!(q{ 108 this(T key, int x) { 109 this.key=key; 110 this.x=x; 111 } 112 }); 113 } 114 115 auto dartKeyT(T)(T key, int x) { 116 return DARTKey!T(key, x); 117 } 118 119 alias Table = Tuple!( 120 BigNumber, Type.BIGINT.stringof, 121 bool, Type.BOOLEAN.stringof, 122 float, Type.FLOAT32.stringof, 123 double, Type.FLOAT64.stringof, 124 int, Type.INT32.stringof, 125 long, Type.INT64.stringof, 126 sdt_t, Type.TIME.stringof, 127 uint, Type.UINT32.stringof, 128 ulong, Type.UINT64.stringof, 129 immutable(ubyte)[], Type.BINARY.stringof, 130 string, Type.STRING.stringof, 131 132 ); 133 // dfmt on 134 135 Table test_table; 136 test_table.FLOAT32 = 1.23; 137 test_table.FLOAT64 = 1.23e200; 138 test_table.INT32 = -42; 139 test_table.INT64 = -0x0123_3456_789A_BCDF; 140 test_table.UINT32 = 42; 141 test_table.UINT64 = 0x0123_3456_789A_BCDF; 142 test_table.BIGINT = BigNumber("-1234_5678_9123_1234_5678_9123_1234_5678_9123"); 143 test_table.BOOLEAN = true; 144 test_table.TIME = 1001; 145 test_table.BINARY = [1, 2, 3]; 146 test_table.STRING = "Text"; 147 import std.stdio; 148 149 foreach (i, t; test_table) { 150 const dart_index = net.dartKey("#key", t); 151 const dart_key = dartKeyT(t, 42); 152 assert(dart_index == net.dartIndex(dart_key), format("%s dartKey failed", Fields!Table[i].stringof)); 153 assert(dart_index != net.calcHash(dart_key.toDoc), format("%s dart_index should not be equal to the fingerpint", Fields!Table[i] 154 .stringof)); 155 } 156 } 157 158 immutable(Buffer) binaryHash(const(HashNet) net, scope const(ubyte[]) h1, scope const(ubyte[]) h2) 159 in { 160 assert(h1.length is 0 || h1.length is net.hashSize, 161 format("h1 is not a valid hash (length=%d should be 0 or %d", h1.length, net.hashSize)); 162 assert(h2.length is 0 || h2.length is net.hashSize, 163 format("h2 is not a valid hash (length=%d should be 0 or %d", h2.length, net.hashSize)); 164 } 165 out (result) { 166 if (h1.length is 0) { 167 assert(h2 == result); 168 } 169 else if (h2.length is 0) { 170 assert(h1 == result); 171 } 172 } 173 do { 174 assert(h1.length is 0 || h1.length is net.hashSize, 175 format("h1 is not a valid hash (length=%d should be 0 or %d", h1.length, net.hashSize)); 176 assert(h2.length is 0 || h2.length is net.hashSize, 177 format("h2 is not a valid hash (length=%d should be 0 or %d", h2.length, net.hashSize)); 178 if (h1.length is 0) { 179 return h2.idup; 180 } 181 if (h2.length is 0) { 182 return h1.idup; 183 } 184 return net.rawCalcHash(h1 ~ h2); 185 } 186 187 Fingerprint binaryHash(const(HashNet) net, scope const(Fingerprint) h1, scope const(Fingerprint) h2) { 188 return Fingerprint(binaryHash(net, cast(Buffer) h1, cast(Buffer) h2)); 189 } 190 /** 191 192 * Calculates the sparsed Merkle root from the branch-table list 193 * The size of the table must be KEY_SPAN 194 * Leaves in the branch table which doen't exist should have the value null 195 * Params: 196 * net = The hash object/function used to calculate the hashs 197 * table = List if hash-value(fingerprint) in the branch 198 * Returns: 199 * The Merkle root 200 */ 201 Buffer sparsed_merkletree(const HashNet net, const(Buffer[]) table) 202 in (table.length == KEY_SPAN) 203 do { 204 immutable(Buffer) merkletree( 205 const(Buffer[]) left, 206 const(Buffer[]) right) { 207 Buffer _left_fingerprint; 208 Buffer _right_fingerprint; 209 if ((left.length == 1) && (right.length == 1)) { 210 _left_fingerprint = left[0]; 211 _right_fingerprint = right[0]; 212 } 213 else { 214 immutable left_mid = left.length >> 1; 215 immutable right_mid = right.length >> 1; 216 _left_fingerprint = merkletree(left[0 .. left_mid], left[left_mid .. $]); 217 _right_fingerprint = merkletree(right[0 .. right_mid], right[right_mid .. $]); 218 } 219 if (_left_fingerprint is null) { 220 return _right_fingerprint; 221 } 222 else if (_right_fingerprint is null) { 223 return _left_fingerprint; 224 } 225 else { 226 return net.binaryHash(_left_fingerprint, _right_fingerprint); 227 } 228 } 229 230 immutable mid = table.length >> 1; 231 return merkletree(table[0 .. mid], table[mid .. $]); 232 } 233 234 Fingerprint sparsed_merkletree(const HashNet net, const(Fingerprint[]) table, const Flag!"flat" flat = No.flat) @trusted { 235 if (flat) { 236 return net.calcHash((cast(Buffer[]) table).join); 237 } 238 return Fingerprint(sparsed_merkletree(net, cast(const(Buffer[])) table)); 239 } 240 241 unittest { // StdHashNet 242 //import tagion.utils.Miscellaneous : toHex=toHexString; 243 import core.exception : AssertError; 244 import std.exception : assertThrown; 245 import std.string : representation; 246 import tagion.hibon.HiBONRecord : hasHashKey, isStub; 247 248 // import std.stdio; 249 250 import tagion.crypto.SecureNet : StdHashNet; 251 import tagion.hibon.HiBON; 252 253 const net = new StdHashNet; 254 Document doc; // This is the data which is filed in the DART 255 { 256 auto hibon = new HiBON; 257 hibon["text"] = "Some text"; 258 doc = Document(hibon); 259 } 260 261 immutable doc_fingerprint = net.rawCalcHash(doc.serialize); 262 263 { 264 assert(net.binaryHash(null, null).length is 0); 265 assert(net.binaryHash(doc_fingerprint, null) == doc_fingerprint); 266 assert(net.binaryHash(null, doc_fingerprint) == doc_fingerprint); 267 } 268 269 }