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 }