1 module tagion.testbench.services.sendcontract;
2 // Default import list for bdd
3 import core.thread;
4 import core.time;
5 import nngd;
6 import std.algorithm;
7 import std.array;
8 import std.format;
9 import std.stdio;
10 import std.typecons : Tuple;
11 import tagion.behaviour;
12 import tagion.communication.HiRPC;
13 import tagion.crypto.SecureInterfaceNet;
14 import tagion.crypto.SecureNet : StdSecureNet;
15 import tagion.dart.DARTBasic;
16 import tagion.dart.DARTcrud;
17 import tagion.hashgraph.Refinement;
18 import tagion.hibon.Document;
19 import tagion.hibon.Document;
20 import tagion.hibon.HiBONJSON;
21 import tagion.hibon.HiBONRecord;
22 import tagion.logger.LogRecords : LogInfo;
23 import tagion.logger.Logger;
24 import tagion.script.TagionCurrency;
25 import tagion.script.common;
26 import tagion.script.execute;
27 import tagion.services.options;
28 import tagion.testbench.services.helper_functions;
29 import tagion.testbench.tools.Environment;
30 import tagion.tools.wallet.WalletInterface;
31 import tagion.utils.pretend_safe_concurrency;
32 import tagion.wallet.SecureWallet : SecureWallet;
33 
34 enum CONTRACT_TIMEOUT = 40;
35 
36 alias StdSecureWallet = SecureWallet!StdSecureNet;
37 enum feature = Feature(
38             "send a contract to the network.",
39             []);
40 
41 alias FeatureContext = Tuple!(
42         SendASingleTransactionFromAWalletToAnotherWallet, "SendASingleTransactionFromAWalletToAnotherWallet",
43         FeatureGroup*, "result"
44 );
45 
46 @safe @Scenario("send a single transaction from a wallet to another wallet.",
47         [])
48 class SendASingleTransactionFromAWalletToAnotherWallet {
49 
50     Options opts;
51     StdSecureWallet[] wallets;
52     string dart_interface_sock_addr;
53     string inputvalidator_sock_addr;
54     TagionCurrency fee;
55     TagionCurrency amount;
56     TagionCurrency start_amount;
57 
58     StdSecureWallet wallet1;
59     StdSecureWallet wallet2;
60 
61     this(Options opts, StdSecureWallet[] wallets, string dart_interface_sock_addr, string inputvalidator_sock_addr, TagionCurrency start_amount) {
62         this.opts = opts;
63         this.wallets = wallets;
64         this.dart_interface_sock_addr = dart_interface_sock_addr;
65         this.start_amount = start_amount;
66         this.inputvalidator_sock_addr = inputvalidator_sock_addr;
67 
68     }
69 
70     bool epoch_on_startup;
71 
72     @Given("i have a dart database with already existing bills linked to wallet1.")
73     Document _wallet1() @trusted {
74         // check that we are actually creating epochs;
75         submask.subscribe(StdRefinement.epoch_created);
76         writeln("waiting for epoch");
77         auto received = receiveTimeout(30.seconds, (LogInfo _, const(Document) __) {});
78 
79         epoch_on_startup = received;
80         check(epoch_on_startup, "No epoch on startup");
81 
82         // create the hirpc request for checking if the bills are already in the system.
83 
84         foreach (ref wallet; wallets) {
85             check(wallet.isLoggedin, "the wallet must be logged in!!!");
86             const hirpc = HiRPC(wallet.net);
87             auto amount = getWalletUpdateAmount(wallet, dart_interface_sock_addr, hirpc);
88             check(wallet.calcTotal(wallet.account.bills) > 0.TGN, "did not receive money");
89             check(wallet.calcTotal(wallet.account.bills) == start_amount, "money not correct");
90         }
91 
92         return result_ok;
93     }
94 
95     @Given("i make a payment request from wallet2.")
96     Document _wallet2() @trusted {
97         check(epoch_on_startup, "No epoch on startup");
98         wallet1 = wallets[1];
99         wallet2 = wallets[2];
100         amount = 1500.TGN;
101         auto payment_request = wallet2.requestBill(amount);
102 
103         import tagion.hibon.HiBONtoText;
104 
105         wallet1.account.bills
106             .each!(b => writefln("WALLET1 %s %s", wallet1.net.calcHash(b).encodeBase64, b.toPretty));
107         SignedContract signed_contract;
108         check(wallet1.createPayment([payment_request], signed_contract, fee).value, "Error creating wallet payment");
109         check(signed_contract !is SignedContract.init, "contract not updated");
110         check(signed_contract.contract.inputs.uniq.array.length == signed_contract.contract.inputs.length, "signed contract inputs invalid");
111 
112         writefln("WALLET1 created contract: %s", signed_contract.toPretty);
113 
114         auto wallet1_hirpc = HiRPC(wallet1.net);
115         auto hirpc_submit = wallet1_hirpc.submit(signed_contract);
116 
117         auto result = sendSubmitHiRPC(inputvalidator_sock_addr, hirpc_submit, wallet1.net);
118         writefln("SUBMIT hirpc result: %s", result.toDoc.toPretty);
119 
120         return result_ok;
121     }
122 
123     @When("wallet1 pays contract to wallet2 and sends it to the network.")
124     Document network() @trusted {
125         check(epoch_on_startup, "No epoch on startup");
126         writeln("WAITING FOR TIMEOUT");
127         Thread.sleep(CONTRACT_TIMEOUT.seconds);
128 
129         writeln("WALLET 1 request");
130 
131         const hirpc = HiRPC(wallet1.net);
132         auto wallet1_amount = getWalletUpdateAmount(wallet1, dart_interface_sock_addr, hirpc);
133         check(wallet1_amount < start_amount, format("no money withdrawn had %s", wallet1_amount));
134 
135         auto wallet1_expected = start_amount - amount - fee;
136         writefln("Wallet 1 total %s", wallet1_amount);
137         check(wallet1_amount == wallet1_expected, format("Wallet1 amount not correct had: %s expected: %s", wallet1_amount, wallet1_expected));
138         return result_ok;
139 
140     }
141 
142     @Then("wallet2 should receive the payment.")
143     Document payment() @trusted {
144         check(epoch_on_startup, "No epoch on startup");
145         const hirpc = HiRPC(wallet2.net);
146         auto wallet2_amount = getWalletUpdateAmount(wallet2, dart_interface_sock_addr, hirpc);
147         check(wallet2_amount > 0.TGN, "did not receive money");
148         check(wallet2_amount == start_amount + amount, "did not receive correct amount of tagion");
149         writefln("Wallet 2 total %s", wallet2.calcTotal(wallet2.account.bills));
150         return result_ok;
151     }
152 
153 }