1 module tagion.testbench.services.big_contract; 2 // Default import list for bdd 3 import core.thread; 4 import core.time; 5 import std.file; 6 import std.path : buildPath, setExtension; 7 import std.stdio; 8 import tagion.GlobalSignals; 9 import tagion.actor; 10 import tagion.basic.Types : FileExtension; 11 import tagion.behaviour.Behaviour; 12 import tagion.logger.Logger; 13 import tagion.services.options; 14 import tagion.testbench.services; 15 import tagion.testbench.tools.Environment; 16 import tagion.tools.Basic; 17 import tagion.utils.pretend_safe_concurrency; 18 import neuewelle = tagion.tools.neuewelle; 19 import tagion.communication.HiRPC; 20 import tagion.script.TagionCurrency; 21 import tagion.script.common; 22 import tagion.hibon.Document; 23 import tagion.testbench.services.sendcontract : StdSecureWallet; 24 import tagion.behaviour; 25 import std.typecons : Tuple; 26 import tagion.testbench.tools.Environment; 27 import tagion.behaviour; 28 import tagion.behaviour.BehaviourException : check; 29 import tagion.tools.wallet.WalletInterface; 30 import std.format; 31 import tagion.wallet.SecureWallet; 32 import tagion.testbench.services.helper_functions; 33 34 mixin Main!(_main); 35 36 void wrap_neuewelle(immutable(string)[] args) { 37 neuewelle._main(cast(string[]) args); 38 } 39 40 enum CONTRACT_TIMEOUT = 40; 41 int _main(string[] args) { 42 auto module_path = env.bdd_log.buildPath(__MODULE__); 43 44 if (module_path.exists) { 45 rmdirRecurse(module_path); 46 } 47 mkdirRecurse(module_path); 48 49 string config_file = buildPath(module_path, "tagionwave.json"); 50 51 scope Options local_options = Options.defaultOptions; 52 local_options.dart.folder_path = buildPath(module_path); 53 local_options.replicator.folder_path = buildPath(module_path, "recorders"); 54 local_options.wave.prefix_format = "BigContract_Node_%s_"; 55 local_options.subscription.address = contract_sock_addr("BIG_CONTRACT_SUBSCRIPTION"); 56 local_options.save(config_file); 57 58 import std.algorithm; 59 import std.array; 60 import std.format; 61 import std.range; 62 import std.stdio; 63 import tagion.crypto.SecureInterfaceNet; 64 import tagion.crypto.SecureNet : StdSecureNet; 65 import tagion.dart.DART; 66 import tagion.dart.DARTBasic; 67 import tagion.dart.DARTFile; 68 import tagion.dart.Recorder; 69 import tagion.hibon.HiBON; 70 71 StdSecureWallet[] wallets; 72 // create the wallets 73 foreach (i; 0 .. 2) { 74 StdSecureWallet secure_wallet; 75 secure_wallet = StdSecureWallet( 76 iota(0, 5).map!(n => format("%dquestion%d", i, n)).array, 77 iota(0, 5).map!(n => format("%danswer%d", i, n)).array, 78 4, 79 format("%04d", i), 80 ); 81 wallets ~= secure_wallet; 82 } 83 84 TagionBill requestAndForce(ref StdSecureWallet w, TagionCurrency amount) { 85 auto b = w.requestBill(amount); 86 w.addBill(b); 87 return b; 88 } 89 TagionBill[] bills; 90 auto bill = requestAndForce(wallets[0], 1000_000_000.TGN); 91 bills ~= bill; 92 93 SecureNet net = new StdSecureNet(); 94 net.generateKeyPair("very_secret"); 95 96 auto factory = RecordFactory(net); 97 auto recorder = factory.recorder; 98 recorder.insert(bills, Archive.Type.ADD); 99 100 foreach (i; 0 .. local_options.wave.number_of_nodes) { 101 immutable prefix = format(local_options.wave.prefix_format, i); 102 const path = buildPath(local_options.dart.folder_path, prefix ~ local_options.dart.dart_filename); 103 writeln(path); 104 DARTFile.create(path, net); 105 auto db = new DART(net, path); 106 db.modify(recorder); 107 } 108 109 immutable neuewelle_args = ["big_contract", config_file, "--nodeopts", module_path]; // ~ args; 110 auto tid = spawn(&wrap_neuewelle, neuewelle_args); 111 import tagion.utils.JSONCommon : load; 112 113 Options[] node_opts; 114 115 Thread.sleep(5.seconds); 116 foreach (i; 0 .. local_options.wave.number_of_nodes) { 117 const filename = buildPath(module_path, format(local_options.wave.prefix_format ~ "opts", i).setExtension(FileExtension 118 .json)); 119 writeln(filename); 120 Options node_opt = load!(Options)(filename); 121 node_opts ~= node_opt; 122 } 123 Thread.sleep(15.seconds); 124 auto name = "big_contract_testing"; 125 register(name, thisTid); 126 log.registerSubscriptionTask(name); 127 128 writefln("BEFORE RUNNING TESTS"); 129 auto feature = automation!(big_contract); 130 feature.SendASingleTransactionFromAWalletToAnotherWalletWithManyOutputs(node_opts[0], wallets[0], wallets[1]); 131 feature.run; 132 stopsignal.set; 133 Thread.sleep(6.seconds); 134 return 0; 135 136 } 137 138 enum feature = Feature( 139 "send a contract with many outputs to the network.", 140 []); 141 142 alias FeatureContext = Tuple!( 143 SendASingleTransactionFromAWalletToAnotherWalletWithManyOutputs, "SendASingleTransactionFromAWalletToAnotherWalletWithManyOutputs", 144 FeatureGroup*, "result" 145 ); 146 147 @safe @Scenario("send a single transaction from a wallet to another wallet with many outputs.", 148 []) 149 class SendASingleTransactionFromAWalletToAnotherWalletWithManyOutputs { 150 Options opts1; 151 StdSecureWallet wallet1; 152 StdSecureWallet wallet2; 153 // 154 SignedContract signed_contract1; 155 TagionCurrency amount; 156 TagionCurrency fee; 157 HiRPC wallet1_hirpc; 158 HiRPC wallet2_hirpc; 159 TagionCurrency start_amount1; 160 TagionCurrency start_amount2; 161 const req_amount = 1000.TGN; 162 TagionBill[] bills; 163 164 this(Options opts1, ref StdSecureWallet wallet1, ref StdSecureWallet wallet2) { 165 this.wallet1 = wallet1; 166 this.wallet2 = wallet2; 167 this.opts1 = opts1; 168 169 wallet1_hirpc = HiRPC(wallet1.net); 170 wallet2_hirpc = HiRPC(wallet2.net); 171 writefln("bills length: %s", wallet1.account.bills.length); 172 start_amount1 = wallet1.calcTotal(wallet1.account.bills); 173 start_amount2 = wallet2.calcTotal(wallet2.account.bills); 174 } 175 176 177 @Given("i have a dart database with already existing bills liked to wallet1") 178 Document _wallet1() { 179 writefln("wowo"); 180 return result_ok; 181 } 182 183 @Given("i make multiple payment requests in wallet2") 184 Document _wallet2() { 185 186 writeln("requesting bill"); 187 (() @trusted => stdout.flush)(); 188 189 foreach(i; 0..69) { 190 auto payment_request = wallet2.requestBill(req_amount); 191 bills ~= payment_request; 192 } 193 194 check(wallet1.createPayment(bills, signed_contract1, fee).value, "error creating payment"); 195 // writefln("signedcontract %s", signed_contract1.toPretty); 196 return result_ok; 197 } 198 199 @When("i pay all of the requests from wallet2 and send it to the network") 200 Document network() { 201 202 auto contract = wallet1_hirpc.submit(signed_contract1); 203 writefln("contract: %s", contract.toPretty); 204 writefln("contract: %(%02x%)", contract.toDoc.serialize); 205 writefln("CONTRACT size %d", contract.toDoc.full_size); 206 sendSubmitHiRPC(opts1.inputvalidator.sock_addr, contract, wallet1.net); 207 return result_ok; 208 } 209 210 @Then("the contract should go through") 211 Document through() { 212 (() @trusted => Thread.sleep(CONTRACT_TIMEOUT.seconds))(); 213 214 auto wallet1_amount = getWalletUpdateAmount(wallet1, opts1.dart_interface.sock_addr, wallet1_hirpc); 215 auto wallet2_amount = getWalletUpdateAmount(wallet2, opts1.dart_interface.sock_addr, wallet2_hirpc); 216 writefln("WALLET 1: %s, WALLET 2: %s", wallet1_amount, wallet2_amount); 217 const wallet1_expected = start_amount1 - wallet1.calcTotal(bills) - fee; 218 check(wallet1_amount == wallet1_expected, format("Wallet1 should have %s had %s", wallet1_expected, wallet1_amount)); 219 const wallet2_expected = start_amount2 + wallet1.calcTotal(bills); 220 check(wallet2_amount == wallet2_expected, format("Wallet2 should have %s had %s", wallet2_expected, wallet2_amount)); 221 return result_ok; 222 } 223 224 }