1 module tagion.wallet.AccountDetails;
2 import std.format;
3 import tagion.basic.Types;
4 import tagion.crypto.Types;
5 
6 //import tagion.script.prior.StandardRecords;
7 import tagion.dart.DARTBasic;
8 import tagion.hibon.Document;
9 import tagion.hibon.HiBONRecord;
10 import tagion.script.TagionCurrency;
11 import tagion.script.common;
12 import tagion.script.standardnames;
13 
14 @safe
15 struct AccountDetails {
16     @optional string name;
17     @label(StdNames.owner) @optional Pubkey owner;
18     @label("$derivers") Buffer[Pubkey] derivers;
19     @label("$bills") TagionBill[] bills;
20     @label("$used") TagionBill[] used_bills;
21     @label("$state") Buffer derive_state;
22     @label("$locked") bool[Pubkey] activated; /// locked bills
23     @label("$requested") TagionBill[Pubkey] requested; /// Requested bills
24     @label("$requested_invoices") Invoice[] requested_invoices;
25     @label("$hirpc") Document[] hirpcs; /// HiRPC request    
26     import std.algorithm : any, each, filter, map, sum;
27 
28     bool remove_bill(Pubkey pk) {
29         import std.algorithm : countUntil, remove;
30 
31         const index = countUntil!"a.owner == b"(bills, pk);
32         if (index > 0) {
33             bills = bills.remove(index);
34             return true;
35         }
36         return false;
37     }
38 
39     void remove_bill_by_hash(const(DARTIndex) billHash) {
40         import std.algorithm : countUntil, remove;
41         import tagion.crypto.SecureNet : StdHashNet;
42 
43         const net = new StdHashNet;
44 
45         auto billsHashes = bills.map!(b => cast(Buffer) net.calcHash(b.toDoc.serialize)).array;
46         const index = billsHashes.countUntil(billHash);
47         bills = bills.remove(index);
48     }
49 
50     void unlock_bill_by_hash(const(DARTIndex) billHash) {
51         import std.algorithm : countUntil, remove;
52         import tagion.crypto.SecureNet : StdHashNet;
53 
54         const net = new StdHashNet;
55 
56         auto billsHashes = bills.map!(b => cast(Buffer) net.calcHash(b.toDoc.serialize)).array;
57         const index = billsHashes.countUntil(billHash);
58 
59         activated.remove(bills[index].owner);
60     }
61 
62     pragma(msg, "Don't think this function fits in AccountDetails");
63     int check_contract_payment(const(DARTIndex)[] inputs, const(Document[]) outputs) {
64         import std.algorithm : countUntil;
65         import tagion.crypto.SecureNet : StdHashNet;
66 
67         const net = new StdHashNet;
68 
69         auto billsHashes = bills.map!(b => cast(Buffer) net.calcHash(b.toDoc.serialize)).array;
70 
71         // Look for input matches. Return 0 from func if found.
72         foreach (inputHash; inputs) {
73             const index = countUntil!"a == b"(billsHashes, inputHash);
74             if (index >= 0) {
75                 return 0;
76             }
77         }
78         // Proceed if inputs are not matched.
79         // Look for outputs matches. Return 1 from func if found or 2 if not.
80         foreach (outputPubkey; outputs.map!(output => output[StdNames.owner].get!Pubkey)) {
81             const index = countUntil!"a.owner == b"(bills, outputPubkey);
82             if (index >= 0) {
83                 return 1;
84             }
85         }
86 
87         if (bills.length == 0) {
88             return 1;
89         }
90 
91         return 2;
92     }
93 
94     bool check_invoice_payment(Pubkey invoicePubkey, ref TagionCurrency amount) {
95         import std.algorithm : countUntil;
96 
97         const index = countUntil!"a.owner == b"(bills, invoicePubkey);
98         if (index >= 0) {
99             amount = bills[index].value;
100             return true;
101         }
102         return false;
103     }
104 
105     bool add_bill(TagionBill bill) {
106         if (bill.owner in requested) {
107             bills ~= requested[bill.owner];
108             requested.remove(bill.owner);
109             return true;
110         }
111         return false;
112     }
113 
114     TagionBill add_bill(const Document doc) {
115         auto bill = TagionBill(doc);
116         const added=add_bill(bill);
117         if (added) {
118         return bill;
119     }
120         return TagionBill.init;
121     }
122 
123     void requestBill(TagionBill bill, Buffer derive) {
124         check((bill.owner in derivers) is null, format("Bill %(%x%) already exists", bill.owner));
125         derivers[bill.owner] = derive;
126         requested[bill.owner] = bill;
127     }
128     /++
129          Clear up the Account
130          Remove used bills
131          +/
132     void clearup() pure {
133         bills
134             .filter!(b => b.owner in derivers)
135             .each!(b => derivers.remove(b.owner));
136         bills
137             .filter!(b => b.owner in activated)
138             .each!(b => activated.remove(b.owner));
139     }
140 
141     const pure {
142         /++
143          Returns:
144          true if the all transaction has been registered as processed
145          +/
146         bool processed() nothrow {
147             return bills
148                 .any!(b => (b.owner in activated));
149         }
150         /++
151          Returns:
152          The available balance
153          +/
154         TagionCurrency available() {
155             return bills
156                 .filter!(b => !(b.owner in activated))
157                 .map!(b => b.value)
158                 .sum;
159         }
160         /++
161          Returns:
162          The total locked amount
163          +/
164         TagionCurrency locked() {
165             return bills
166                 .filter!(b => b.owner in activated)
167                 .map!(b => b.value)
168                 .sum;
169         }
170         /++
171          Returns:
172          The total balance including the locked bills
173          +/
174         TagionCurrency total() {
175             return bills
176                 .map!(b => b.value)
177                 .sum;
178         }
179     }
180     mixin HiBONRecord;
181 }
182 
183 @safe
184 @recordType("Invoice")
185 struct Invoice {
186     string name; /// Name of the invoice
187     TagionCurrency amount; /// Amount to be payed
188     @label(StdNames.owner) Pubkey pkey; /// Key to the payee
189     @optional Document info; /// Information about the invoice
190     mixin HiBONRecord;
191 }
192 
193 @safe
194 struct Invoices {
195     Invoice[] list; /// List of invoice (store in the wallet)
196     mixin HiBONRecord;
197 }