1 module tagion.betterC.communication.HiRPC; 2 3 import std.format; 4 import std.traits : EnumMembers; 5 import tagion.basic.Types : Buffer; 6 import tagion.betterC.hibon.Document; 7 import tagion.betterC.hibon.HiBON; 8 import tagion.betterC.utils.Memory; 9 import tagion.betterC.wallet.Net; 10 import tagion.betterC.wallet.WalletRecords : GetLabel, Label; 11 import tagion.crypto.Types : Pubkey, Signature; 12 13 struct HiRPC { 14 // import tagion.hibon.HiBONRecord; 15 16 struct Method { 17 @Label("*", true) uint id; 18 @Label("*", true) Document params; 19 @Label("method") string name; 20 21 this(Document doc) { 22 enum id_name = GetLabel!(id).name; 23 enum params_name = GetLabel!(params).name; 24 enum method_name = GetLabel!(name).name; 25 26 id = doc[id_name].get!uint; 27 params = doc[params_name].get!Document; 28 name = doc[method_name].get!string; 29 } 30 31 inout(HiBONT) toHiBON() inout { 32 auto hibon = HiBON(); 33 enum id_name = GetLabel!(id).name; 34 enum params_name = GetLabel!(params).name; 35 enum method_name = GetLabel!(name).name; 36 37 hibon[id_name] = id; 38 hibon[params_name] = params; 39 hibon[method_name] = name; 40 41 return cast(inout) hibon; 42 } 43 44 const(Document) toDoc() { 45 return Document(toHiBON.serialize); 46 } 47 } 48 49 struct Response { 50 @Label("*", true) uint id; 51 Document result; 52 53 this(Document doc) { 54 enum id_name = GetLabel!(id).name; 55 enum result_name = GetLabel!(result).name; 56 57 id = doc[id_name].get!uint; 58 result = doc[result_name].get!Document; 59 } 60 61 inout(HiBONT) toHiBON() inout { 62 auto hibon = HiBON(); 63 enum id_name = GetLabel!(id).name; 64 enum result_name = GetLabel!(result).name; 65 66 hibon[id_name] = id; 67 hibon[result_name] = result; 68 69 return cast(inout) hibon; 70 } 71 72 const(Document) toDoc() { 73 return Document(toHiBON.serialize); 74 } 75 } 76 77 struct Error { 78 @Label("*", true) uint id; 79 @Label("*", true) Document data; 80 @Label("*", true) string message; 81 @Label("*", true) int code; 82 83 this(Document doc) { 84 enum id_name = GetLabel!(id).name; 85 enum data_name = GetLabel!(data).name; 86 enum message_name = GetLabel!(message).name; 87 enum code_name = GetLabel!(code).name; 88 89 id = doc[id_name].get!uint; 90 data = doc[data_name].get!Document; 91 message = doc[message_name].get!string; 92 code = doc[code_name].get!int; 93 } 94 95 inout(HiBONT) toHiBON() inout { 96 auto hibon = HiBON(); 97 98 enum id_name = GetLabel!(id).name; 99 enum data_name = GetLabel!(data).name; 100 enum message_name = GetLabel!(message).name; 101 enum code_name = GetLabel!(code).name; 102 103 hibon[id_name] = id; 104 hibon[data_name] = data; 105 hibon[message_name] = message; 106 hibon[code_name] = code; 107 108 return cast(inout) hibon; 109 } 110 111 const(Document) toDoc() { 112 return Document(toHiBON.serialize); 113 } 114 115 static bool valid(const Document doc) { 116 enum codeName = GetLabel!(code).name; 117 enum messageName = GetLabel!(message).name; 118 enum dataName = GetLabel!(data).name; 119 return doc.hasMember(codeName) || doc.hasMember(messageName) || doc.hasMember(dataName); 120 } 121 122 } 123 124 enum SignedState { 125 INVALID = -1, 126 NOSIGN = 0, 127 VALID = 1 128 } 129 130 enum Type : uint { 131 none, /// No valid Type 132 method, /// Action method 133 result, /// Respose 134 error 135 } 136 137 enum Direction { 138 SEND, 139 RECEIVE 140 } 141 142 struct Post(Direction DIRECTION) { 143 union Message { 144 Method method; 145 Response response; 146 Error error; 147 } 148 149 @disable this(); 150 @Label("$sign", true) immutable(ubyte)[] signature; 151 @Label("$pkey", true) immutable(ubyte)[] pubkey; 152 @Label("$msg") Document message; 153 @Label("") immutable Type type; 154 155 this(Document doc) { 156 enum signature_name = GetLabel!(signature).name; 157 enum pubkey_name = GetLabel!(pubkey).name; 158 enum message_name = GetLabel!(message).name; 159 enum type_name = GetLabel!(type).name; 160 161 auto received_sign = doc[signature_name].get!Buffer; 162 signature.create(received_sign.length); 163 signature = received_sign; 164 165 auto received_pubkey = doc[pubkey_name].get!Buffer; 166 pubkey.create(received_pubkey.length); 167 pubkey = received_pubkey; 168 169 message = doc[message_name].get!Document; 170 type = cast(Type) doc[type_name].get!uint; 171 } 172 173 inout(HiBONT) toHiBON() inout { 174 auto hibon = HiBON(); 175 176 enum signature_name = GetLabel!(signature).name; 177 enum pubkey_name = GetLabel!(pubkey).name; 178 enum message_name = GetLabel!(message).name; 179 enum type_name = GetLabel!(type).name; 180 hibon[signature_name] = signature; 181 hibon[pubkey_name] = pubkey; 182 hibon[message_name] = message; 183 hibon[type_name] = cast(uint) type; 184 185 return cast(inout) hibon; 186 } 187 188 const(Document) toDoc() { 189 return Document(toHiBON.serialize); 190 } 191 192 bool supports(T)() const { 193 import std.algorithm.searching : canFind; 194 import std.traits : isCallable; 195 196 return (type is Type.method) && 197 Callers!T.canFind(method.name); 198 } 199 200 bool verify(const Document doc) { 201 return true; 202 } 203 204 static if (DIRECTION is Direction.RECEIVE) { 205 @Label("") protected Message _message; 206 @Label("") immutable SignedState signed; 207 enum signName = GetLabel!(signature).name; 208 enum pubkeyName = GetLabel!(pubkey).name; 209 enum messageName = GetLabel!(message).name; 210 // this(const Document doc) { 211 // this(null, doc); 212 // } 213 214 this(const SecureNet net, const Document doc) { 215 enum type_name = GetLabel!(type).name; 216 type = cast(Type) doc[type_name].get!uint; 217 message = doc[messageName].get!Document; 218 219 enum signature_name = GetLabel!(signature).name; 220 enum pubkey_name = GetLabel!(pubkey).name; 221 auto received_sign = doc[signature_name].get!Buffer; 222 signature.create(received_sign.length); 223 signature = received_sign[0 .. $]; 224 225 auto received_pubkey = doc[pubkey_name].get!Buffer; 226 pubkey.create(received_pubkey.length); 227 pubkey = received_pubkey[0 .. $]; 228 static SignedState verifySignature(const SecureNet net, const Document doc, const( 229 ubyte[]) sgn, const(ubyte[]) pkey) { 230 if (sgn.length) { 231 // //immutable fingerprint=net.hashOf(msg); 232 if (net.verify(doc.serialize, sgn, pkey)) { 233 return SignedState.VALID; 234 } 235 else { 236 return SignedState.INVALID; 237 } 238 } 239 return SignedState.NOSIGN; 240 } 241 242 void set_message() @trusted { 243 with (Type) { 244 final switch (type) { 245 case none: 246 break; 247 case method: 248 _message.method = Method(message); 249 break; 250 case result: 251 _message.response = Response(message); 252 break; 253 case error: 254 _message.error = Error(message); 255 } 256 } 257 } 258 259 set_message; 260 signed = verifySignature(net, message, signature, pubkey); 261 } 262 263 // this(T)(const SecureNet net, T pack) if (isHiBONRecord!T) { 264 // this(net, pack.toDoc); 265 // } 266 267 @trusted const(Error) error() const pure { 268 return _message.error; 269 } 270 271 @trusted const(Response) response() const pure { 272 return _message.response; 273 } 274 275 @trusted const(Method) method() const pure { 276 return _message.method; 277 } 278 279 const(T) params(T, Args...)(Args args) { 280 return T(args, method.params); 281 } 282 283 const(T) result(T, Args...)(Args args) { 284 return T(response.result); 285 } 286 287 // @trusted 288 // bool isRecord(T)() const { 289 // with (Type) { 290 // final switch (type) { 291 // case none, error: 292 // return false; 293 // case method: 294 // return T.isRecord(_message.method.params); 295 // case result: 296 // return T.isRecord(_message.response.result); 297 // } 298 // } 299 // assert(0); 300 // } 301 } 302 else { 303 this(T)(const SecureNet net, const T post) { 304 message = post; 305 // type = getType(post); 306 auto doc = post; 307 enum type_name = GetLabel!(type).name; 308 type = cast(Type) doc[type_name].get!uint; 309 // immutable signed=net.sign(message); 310 // fingerprint=signed.message; 311 312 // signature = net.sign(message.serialize).signature; 313 314 pubkey.create(net.pubkey.length); 315 // pubkey = net.pubkey; 316 } 317 318 Error error() const { 319 return Error(message); 320 } 321 322 Response response() const { 323 return Response(message); 324 } 325 326 Method method() const { 327 return Method(message); 328 } 329 330 /++ 331 Checks if the message has been signed 332 NOTE!! This does not mean that the signature is correct 333 Returns: 334 True if the message has been signed 335 +/ 336 bool isSigned() const pure { 337 return (signature.length !is 0); 338 } 339 } 340 341 } 342 343 alias Sender = Post!(Direction.SEND); 344 alias Receiver = Post!(Direction.RECEIVE); 345 const SecureNet net; 346 347 const(uint) generateId() const { 348 uint id = 0; 349 // import tagion.utils.Random; 350 // import stdrnd = std.random; 351 352 // auto rnd = Random!uint(stdrnd.unpredictableSeed); 353 do { 354 // id = rnd.value(); 355 } 356 while (id is 0 || id is uint.max); 357 return id; 358 } 359 360 const(Sender) action(string method, const Document params, const uint id = uint.max) const { 361 Method message; 362 message.id = (id is uint.max) ? generateId : id; 363 if (!params.empty) { 364 message.params = params; 365 } 366 message.name = method; 367 message.params = params; 368 auto sender = Sender(net, message.toDoc); 369 return sender; 370 } 371 372 const(Sender) action(string method, HiBONT params, const uint id = uint.max) const { 373 const doc = Document(params); 374 return action(method, doc, id); 375 } 376 377 final const(Receiver) receive(Document doc) const { 378 auto receiver = Receiver(net, doc); 379 return receiver; 380 } 381 382 }