1 module tagion.betterC.wallet.WalletRecords; 2 3 import tagion.basic.Types : Buffer; 4 import tagion.basic.basic : basename; 5 import tagion.betterC.hibon.Document : Document; 6 import tagion.betterC.hibon.HiBON; 7 import tagion.betterC.utils.BinBuffer; 8 import tagion.betterC.utils.Memory; 9 import tagion.betterC.wallet.KeyRecover : KeyRecover; 10 import tagion.crypto.Types : Pubkey, Signature; 11 12 // import std.format; 13 import core.stdc.string : memcpy; 14 import std.conv : emplace; 15 import std.range.primitives : isInputRange; 16 import std.traits : ForeachType, ReturnType, Unqual, hasMember, isArray, isAssociativeArray, isIntegral, isUnsigned; 17 import std.typecons : Tuple, TypedefType; 18 import tagion.betterC.funnel.TagionCurrency; 19 20 // import tagion.script.StandardRecords : StandardBill; 21 22 template isSpecialKeyType(T) { 23 import std.traits : KeyType, isAssociativeArray, isUnsigned; 24 25 static if (isAssociativeArray!T) { 26 alias KeyT = KeyType!T; 27 enum isSpecialKeyType = !(isUnsigned!KeyT) && !is(KeyT : string); 28 } 29 else { 30 enum isSpecialKeyType = false; 31 } 32 } 33 34 static R toList(R)(const Document doc) { 35 alias MemberU = ForeachType!(R); 36 alias BaseU = TypedefType!MemberU; 37 static if (isArray!R) { 38 alias UnqualU = Unqual!MemberU; 39 UnqualU[] result; 40 result.length = doc.length; 41 enum do_foreach = true; 42 } 43 else static if (isSpecialKeyType!R) { 44 R result; 45 enum do_foreach = true; 46 } 47 else static if (isAssociativeArray!R) { 48 R result; 49 enum do_foreach = true; 50 } 51 else { 52 return R(doc); 53 enum do_foreach = false; 54 } 55 static if (do_foreach) { 56 foreach (elm; doc[]) { 57 static if (isSpecialKeyType!R) { 58 const value_doc = elm.get!Document; 59 alias KeyT = KeyType!R; 60 alias BaseKeyT = TypedefType!KeyT; 61 static if (Document.Value.hasType!BaseKeyT || is(BaseKeyT == enum)) { 62 const key = KeyT(value_doc[0].get!BaseKeyT); 63 } 64 else { 65 auto key = KeyT(value_doc[0].get!BaseKeyT); 66 } 67 const e = value_doc[1]; 68 } 69 else { 70 const e = elm; 71 } 72 static if (Document.Value.hasType!MemberU || is(BaseU == enum)) { 73 auto value = e.get!BaseU; 74 } 75 else static if (Document.Value.hasType!BaseU) { 76 // Special case for Typedef 77 auto value = MemberU(e.get!BaseU); 78 } 79 else { 80 const sub_doc = e.get!Document; 81 static if (is(BaseU == struct)) { 82 auto value = BaseU(sub_doc); 83 } 84 else { 85 auto value = toList!BaseU(sub_doc); 86 } 87 // else { 88 // static assert(0, 89 // format("Can not convert %s to Document", R.stringof)); 90 // } 91 } 92 static if (isAssociativeArray!R) { 93 static if (isSpecialKeyType!R) { 94 result[key] = value; 95 } 96 else { 97 result[e.key] = value; 98 } 99 } 100 else { 101 result[e.index] = value; 102 } 103 } 104 } 105 return cast(immutable) result; 106 } 107 108 struct RecordType { 109 string name; 110 string code; // This is is mixed after the Document constructor 111 } 112 113 struct Label { 114 string name; /// Name of the HiBON member 115 bool optional; /// This flag is set to true if this paramer is optional 116 } 117 118 enum VOID = "*"; 119 120 template GetLabel(alias member) { 121 import std.traits : getUDAs, hasUDA; 122 123 static if (hasUDA!(member, Label)) { 124 enum label = getUDAs!(member, Label)[0]; 125 static if (label.name == VOID) { 126 enum GetLabel = Label(basename!(member), label.optional); 127 } 128 else { 129 enum GetLabel = label; 130 } 131 } 132 else { 133 enum GetLabel = Label(basename!(member)); 134 } 135 } 136 137 @trusted { 138 import std.algorithm; 139 import std.array; 140 141 @RecordType("Quiz") 142 struct Quiz { 143 @Label("$Q") string[] questions; 144 this(Document doc) { 145 auto received_questions = doc["$Q"].get!Document; 146 questions.create(received_questions.length); 147 foreach (element; received_questions[]) { 148 questions[element.index] = element.get!string; 149 } 150 } 151 152 inout(HiBONT) toHiBON() inout { 153 auto hibon = HiBON(); 154 auto tmp_arr = HiBON(); 155 foreach (i, question; questions) { 156 tmp_arr[i] = question; 157 } 158 // GetLabel 159 hibon["$Q"] = tmp_arr; 160 return cast(inout) hibon; 161 } 162 163 const(Document) toDoc() { 164 return Document(toHiBON.serialize); 165 } 166 } 167 168 @RecordType("PIN") 169 struct DevicePIN { 170 Buffer D; /// Device number 171 Buffer U; /// Device random 172 Buffer S; /// Check sum value 173 void recover(ref scope ubyte[] R, scope const(ubyte[]) P) const { 174 import tagion.betterC.utils.Miscellaneous : xor; 175 176 xor(R, D, P); 177 } 178 179 this(Document doc) { 180 enum number_name = GetLabel!(D).name; 181 enum random_name = GetLabel!(U).name; 182 enum sum_name = GetLabel!(S).name; 183 184 D = doc[number_name].get!Buffer; 185 U = doc[random_name].get!Buffer; 186 S = doc[sum_name].get!Buffer; 187 } 188 189 inout(HiBONT) toHiBON() inout { 190 auto hibon = HiBON(); 191 enum number_name = GetLabel!(D).name; 192 enum random_name = GetLabel!(U).name; 193 enum sum_name = GetLabel!(S).name; 194 195 hibon[number_name] = D; 196 hibon[random_name] = U; 197 hibon[sum_name] = S; 198 return cast(inout) hibon; 199 } 200 201 const(Document) toDoc() { 202 return Document(toHiBON.serialize); 203 } 204 } 205 206 @RecordType("Wallet") 207 struct RecoverGenerator { 208 Buffer[] Y; /// Recorvery seed 209 Buffer S; /// Check value S=H(H(R)) 210 @Label("N") uint confidence; 211 import tagion.betterC.hibon.HiBON; 212 213 inout(HiBONT) toHiBON() inout { 214 auto hibon = HiBON(); 215 auto tmp_arr = HiBON(); 216 foreach (i, y; Y) { 217 tmp_arr[i] = y; 218 } 219 tmp_arr["S"] = S; 220 tmp_arr["N"] = confidence; 221 hibon = tmp_arr; 222 return cast(inout) hibon; 223 } 224 225 const(Document) toDoc() { 226 return Document(toHiBON.serialize); 227 } 228 229 this(Document doc) { 230 auto Y_data = doc["Y"].get!Document; 231 Y.create(Y_data.length); 232 foreach (element; Y_data[]) { 233 Y[element.index] = element.get!Buffer; 234 } 235 S = doc["S"].get!Buffer; 236 confidence = doc["N"].get!uint; 237 } 238 } 239 240 struct AccountDetails { 241 // @Label("$derives") Buffer[Pubkey] derives; 242 @Label("$derives") Document derives; 243 @Label("$bills") StandardBill[] bills; 244 @Label("$state") Buffer derive_state; 245 // boll[Pubkey] 246 @Label("$active") Document activated; /// Actived bills 247 import std.algorithm : any, each, filter, map, sum; 248 249 this(Document doc) { 250 enum derives_name = GetLabel!(derives).name; 251 enum bills_name = GetLabel!(bills).name; 252 enum ds_name = GetLabel!(derive_state).name; 253 enum active_name = GetLabel!(activated).name; 254 255 auto received_der = doc[derives_name].get!Document; 256 // auto list = toList!Buffer[Pubkey](received_der); 257 auto received_bills = doc[bills_name].get!Document; 258 bills.create(received_bills.length); 259 foreach (element; received_bills[]) { 260 enum value_name = GetLabel!(StandardBill.value).name; 261 bills[element.index].value = TagionCurrency( 262 received_bills[value_name].get!Document); 263 bills[element.index].epoch = element.get!uint; 264 // bills[element.index].owner = element; 265 bills[element.index].gene = element.get!Buffer; 266 } 267 derive_state = doc[ds_name].get!Buffer; 268 activated = doc[active_name].get!Document; 269 } 270 271 inout(HiBONT) toHiBON() inout { 272 auto hibon = HiBON(); 273 enum derives_name = GetLabel!(derives).name; 274 enum bills_name = GetLabel!(bills).name; 275 enum ds_name = GetLabel!(derive_state).name; 276 enum active_name = GetLabel!(activated).name; 277 278 hibon[derives_name] = derives; 279 // auto tmp_hibon = HiBON(); 280 // foreach(i, bill; bills) { 281 // tmp_hibon[i] = bill; 282 // } 283 // hibon[bills_name] = tmp_hibon; 284 hibon[ds_name] = derive_state; 285 hibon[active_name] = activated; 286 return cast(inout) hibon; 287 } 288 289 const(Document) toDoc() { 290 return Document(toHiBON.serialize); 291 } 292 293 bool remove_bill(Pubkey pk) { 294 import std.algorithm : countUntil, remove; 295 296 const index = countUntil!"a.owner == b"(bills, pk); 297 if (index > 0) { 298 bills = bills.remove(index); 299 return true; 300 } 301 return false; 302 } 303 304 void add_bill(StandardBill bill) { 305 bills.resize(bills.length + 1); 306 bills[$ - 1] = bill; 307 } 308 309 /++ 310 Clear up the Account 311 Remove used bills 312 +/ 313 void clearup() pure { 314 // bills 315 // .filter!(b => b.owner in derives) 316 // .each!(b => derives.remove(b.owner)); 317 // bills 318 // .filter!(b => b.owner in activated) 319 // .each!(b => activated.remove(b.owner)); 320 } 321 322 const { 323 /++ 324 Returns: 325 true if the all transaction has been registered as processed 326 +/ 327 bool processed() { 328 bool res = false; 329 foreach (bill; bills) { 330 foreach (active; activated[]) { 331 const active_data = active.get!Document; 332 if (active_data[0].get!Buffer == bill.owner) { 333 334 } 335 // const key = tmp[0].get!Buffer; 336 // const value = tmp[1].get!bool; 337 } 338 // auto tmp = toList(activated) 339 // if (bill.owner in activated) { 340 // res = true; 341 // } 342 } 343 return res; 344 } 345 /++ 346 Returns: 347 The available balance 348 +/ 349 TagionCurrency available() { 350 long result; 351 foreach (bill; bills) { 352 foreach (active; activated[]) { 353 const active_data = active.get!Document; 354 if (active_data[0].get!Buffer == bill.owner) { 355 result += active_data[1].get!uint; 356 } 357 } 358 } 359 return TagionCurrency(result); 360 } 361 /++ 362 // Returns: 363 // The total active amount 364 // +/ 365 TagionCurrency active() { 366 long result; 367 foreach (bill; bills) { 368 foreach (active; activated[]) { 369 const active_data = active.get!Document; 370 if (active_data[0].get!Buffer == bill.owner) { 371 result += active_data[1].get!uint; 372 } 373 } 374 } 375 return TagionCurrency(result); 376 } 377 // /++ 378 // Returns: 379 // The total balance including the active bills 380 // +/ 381 // TagionCurrency total() { 382 // return bills 383 // .map!(b => b.value) 384 // .sum; 385 // } 386 } 387 } 388 389 @RecordType("BIL") struct StandardBill { 390 @Label("$V") TagionCurrency value; // Bill type 391 @Label("$k") uint epoch; // Epoch number 392 // @Label("$T", true) string bill_type; // Bill type 393 @Label("$Y") Pubkey owner; // Double hashed owner key 394 @Label("$G") Buffer gene; // Bill gene 395 this(Document doc) { 396 // value = doc["Y"].get!Document; 397 epoch = doc["k"].get!uint; 398 Buffer tmp_buf = doc["Y"].get!Buffer; 399 Pubkey pkey; 400 pkey = tmp_buf; 401 // memcpy_wrapper(owner, pkey); 402 gene = doc["G"].get!Buffer; 403 } 404 405 inout(HiBONT) toHiBON() inout { 406 auto hibon = HiBON(); 407 // hibon["V"] = value; 408 hibon["k"] = epoch; 409 // hibon["Y"] = owner; 410 hibon["G"] = gene; 411 return cast(inout) hibon; 412 } 413 414 const(Document) toDoc() { 415 return Document(toHiBON.serialize); 416 } 417 } 418 419 @RecordType("Invoice") struct Invoice { 420 string name; 421 TagionCurrency amount; 422 Pubkey pkey; 423 @Label("*", true) Document info; 424 this(Document doc) { 425 } 426 427 inout(HiBONT) toHiBON() inout { 428 auto hibon = HiBON(); 429 return cast(inout) hibon; 430 } 431 432 const(Document) toDoc() { 433 return Document(toHiBON.serialize); 434 } 435 } 436 437 @RecordType("SMC") struct Contract { 438 @Label("$in") Buffer[] input; /// Hash pointer to input (DART) 439 @Label("$read", true) Buffer[] read; /// Hash pointer to read-only input (DART) 440 @Label("$out") Document[Pubkey] output; // pubkey of the output 441 @Label("$run") Script script; // TVM-links / Wasm binary 442 bool verify() { 443 return (input.length > 0); 444 } 445 } 446 447 @RecordType("SSC") struct SignedContract { 448 @Label("$signs") immutable(ubyte)[] signs; /// Signature of all inputs 449 @Label("$contract") Contract contract; /// The contract must signed by all inputs 450 @Label("$in", true) Document input; /// The actual inputs 451 this(Document doc) { 452 enum sign_name = GetLabel!(signs).name; 453 // enum contract_name = GetLabel!(contract).name; 454 enum input_name = GetLabel!(input).name; 455 456 auto received_sign = doc[sign_name].get!Buffer; 457 signs.create(received_sign.length); 458 signs = received_sign; 459 460 // contract = doc[contract_name].get!Contract; 461 input = doc[input_name].get!Document; 462 463 } 464 465 inout(HiBONT) toHiBON() inout { 466 auto hibon = HiBON(); 467 return cast(inout) hibon; 468 } 469 470 const(Document) toDoc() { 471 return Document(toHiBON.serialize); 472 } 473 } 474 475 struct Script { 476 @Label("$name") string name; 477 @Label("$env", true) Buffer link; // Hash pointer to smart contract object; 478 // mixin HiBONRecord!( 479 // q{ 480 // this(string name, Buffer link=null) { 481 // this.name = name; 482 // this.link = link; 483 // } 484 // }); 485 // bool verify() { 486 // return (wasm.length is 0) ^ (link.empty); 487 // } 488 489 } 490 }