1 /// Service for verifying contracts 2 /// [Documentation](https://docs.tagion.org/#/documents/architecture/HiRPCVerifier) 3 module tagion.services.hirpc_verifier; 4 5 import std.stdio; 6 import tagion.actor.actor; 7 import tagion.basic.Debug : __write; 8 import tagion.communication.HiRPC; 9 import tagion.crypto.SecureInterfaceNet; 10 import tagion.crypto.SecureNet; 11 import tagion.hibon.Document; 12 import tagion.hibon.HiBONJSON; 13 import tagion.hibon.HiBONRecord; 14 import tagion.logger.Logger; 15 import tagion.script.common : SignedContract; 16 import tagion.services.messages; 17 import tagion.utils.JSONCommon; 18 import tagion.utils.pretend_safe_concurrency; 19 20 struct HiRPCVerifierOptions { 21 /// Rejected documents won be discarded and instead sent to rejected_contracts_task 22 bool send_rejected_hirpcs = false; 23 /// Which task to send rejected document to; 24 string rejected_hirpcs = ""; 25 mixin JSONCommon; 26 } 27 28 /// HiRPC methods 29 enum ContractMethods { 30 submit = "submit", 31 } 32 33 /// used internally in combination with `send_rejected_contracts` optios for testing & tracing that contracts are correctly rejected 34 enum RejectReason { 35 notAHiRPC, // The Document received was not a vald HiRPC 36 invalidMethod, // The method was not one of the accepted methods 37 notSigned, // The rpc was not signed when it should have been 38 invalidType, // the rpc was not a method or fit the criteria for any of the available contracts 39 } 40 41 /** 42 * HiRPCVerifierService actor 43 * Examples: [tagion.testbench.services.hirpc_verifier] 44 * Receives: (inputDoc, Document) 45 * Sends: (inputHiRPC, HiRPC.Receiver) to collector_task, where Document is a correctly formatted HiRPC 46 **/ 47 @safe 48 struct HiRPCVerifierService { 49 import tagion.services.options : TaskNames; 50 51 void task(immutable(HiRPCVerifierOptions) opts, immutable(TaskNames) task_names) { 52 53 SecureNet net = new StdSecureNet; 54 const hirpc = HiRPC(net); 55 immutable collector_task = task_names.collector; 56 57 void reject(RejectReason reason, lazy Document doc) @safe { 58 if (opts.send_rejected_hirpcs) { 59 locate(opts.rejected_hirpcs).send(reason, doc); 60 } 61 } 62 63 void contract(inputDoc, Document doc) @safe { 64 if (!doc.isRecord!(HiRPC.Sender)) { 65 reject(RejectReason.notAHiRPC, doc); 66 return; 67 } 68 69 const receiver = hirpc.receive(doc); 70 if (!receiver.isMethod) { 71 reject(RejectReason.invalidType, doc); 72 return; 73 } 74 75 import tagion.dart.DART; 76 77 switch (receiver.method.name) { 78 case ContractMethods.submit: 79 if (!(Document(receiver.method.params).isRecord!SignedContract)) { 80 reject(RejectReason.invalidType, doc); 81 return; 82 } 83 if (receiver.signed is HiRPC.SignedState.VALID) { 84 log("sending contract to collector"); 85 locate(collector_task).send(inputHiRPC(), receiver); 86 } 87 else { 88 reject(RejectReason.notSigned, doc); 89 } 90 break; 91 default: 92 reject(RejectReason.invalidMethod, doc); 93 break; 94 } 95 } 96 97 run(&contract); 98 } 99 }