Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
LocalRecipient.cs
1using Paiwise;
2using System;
3using System.Collections.Generic;
4using System.Threading.Tasks;
5using Waher.Events;
13
15{
20 {
21 private Wallet wallet;
22 private AccountEvent @event;
23 private decimal amount = 0;
24 private bool transferred = false;
25 private DateTime prevTP = DateTime.MinValue;
26 private ICurrencyConverterQuote quote = null;
27 private Transaction transaction;
28 private readonly bool addSecondaryTransactionObject;
29
34 public LocalRecipient(EDalerUri Uri, bool AddSecondaryTransactionObject)
35 : base(Uri)
36 {
37 this.addSecondaryTransactionObject = AddSecondaryTransactionObject;
38 }
39
44 protected override async Task<bool> DoPrepare()
45 {
46 this.wallet = await this.GetWallet(this.Uri.To, this.Uri.ToType);
47
48 if (this.wallet is null)
49 {
50 this.Uri.State.Error(Uris.States.EDalerUriErrorType.ServiceUnavailable, "Recipient wallet not found.", false);
51 return false;
52 }
53
54 this.amount = this.Uri.TotalAmount;
55
56 if (this.Uri.Currency != this.wallet.Currency)
57 {
59 new CurrencyPair(this.Uri.Currency, this.wallet.Currency));
60
61 if (Converter is null)
62 {
63 this.Uri.State.Error(Uris.States.EDalerUriErrorType.Forbidden, "Recipient cannot receive eDaler in " + this.Uri.Currency + ". No currency conversion service available.", false);
64 return false;
65 }
66
67 try
68 {
69 this.quote = await Converter.GetCurrencyConversionQuote(this.wallet.Currency, this.Uri.Currency);
70 if (this.quote is null)
71 {
72 this.Uri.State.Error(Uris.States.EDalerUriErrorType.Forbidden, "Recipient cannot receive eDaler in " + this.Uri.Currency + ". Conversion not supported by service.", false);
73 return false;
74 }
75 }
76 catch (Exception ex)
77 {
78 this.Uri.State.Error(Uris.States.EDalerUriErrorType.Forbidden, "Recipient cannot receive eDaler in " + this.Uri.Currency + ". Currency converter reports: " + ex.Message, false);
79 return false;
80 }
81
82 this.amount *= this.quote.Rate;
83
84 // TODO: Commission
85 }
86
87 if (this.addSecondaryTransactionObject)
88 {
89 this.transaction = await Database.TryLoadObject<Transaction>(this.Uri.Id);
90 if (!(this.transaction is null))
91 {
92 this.Uri.State.Error(Uris.States.EDalerUriErrorType.ResourceConstraint, "eDaler transaction already processed.", false);
93 return false;
94 }
95 }
96 else
97 this.transaction = null;
98
99 return true;
100 }
101
106 protected override async Task<bool> DoExecute()
107 {
108 using (Semaphore Semaphore = await Semaphores.BeginWrite("wallet:" + this.wallet.Account))
109 {
110 if (this.addSecondaryTransactionObject &&
111 (!string.IsNullOrEmpty(IoTGateway.Gateway.Domain) ||
112 (!CaseInsensitiveString.IsNullOrEmpty(this.Uri.State.Sender) &&
113 this.Uri.State.Sender != "example.com" &&
114 this.Uri.State.Sender != "example2.com" &&
115 this.Uri.State.Sender != "example3.com")))
116 {
118 {
119 ObjectId = this.Uri.Id,
120 Created = this.Uri.Created,
121 Processed = DateTime.UtcNow,
122 From = this.Uri.From.Address,
123 FromType = this.Uri.FromType,
124 To = this.Uri.To.Address,
125 ToType = this.Uri.ToType,
126 Sender = this.Uri.State.Sender,
127 Amount = this.Uri.TotalAmount,
128 Currency = this.Uri.Currency,
129 Uri = this.Uri.UriString
130 };
131
132 try
133 {
135 }
137 {
138 this.Uri.State.Error(Uris.States.EDalerUriErrorType.ResourceConstraint, "eDaler transaction already processed.", false);
139 return false;
140 }
141 catch (Exception ex)
142 {
143 Log.Exception(ex);
144
145 this.Uri.State.Error(Uris.States.EDalerUriErrorType.ServiceUnavailable, "eDaler transaction could not be processed due to internal failure.", false);
146 return false;
147 }
148
149 this.transaction = Transaction;
150 }
151
152 AccountEvent Ref = null;
153 DateTime BakTP = this.wallet.BalanceTimestamp;
154 decimal Bak = this.wallet.Balance;
155 bool RefAdded = false;
156
157 try
158 {
159 Ref = new AccountEvent()
160 {
161 TransactionId = this.Uri.Id,
162 Timestamp = DateTime.UtcNow,
163 Account = this.wallet.Account,
164 Remote = this.Uri.From.Address,
165 Change = this.amount,
166 Balance = this.wallet.Balance + this.amount,
167 Reserved = this.wallet.Reserved,
168 EncryptedMessage = this.Uri.EncryptedMessage,
169 EncryptionPublicKey = this.Uri.EncryptionPublicKey
170 };
171
172 await Database.Insert(Ref);
173 RefAdded = true;
174
175 this.prevTP = this.wallet.BalanceTimestamp;
176
177 this.wallet.Balance += this.amount;
178 this.wallet.BalanceTimestamp = Ref.Timestamp;
179
180 await Database.Update(this.wallet);
181
182 this.transferred = true;
183 }
184 catch (Exception ex)
185 {
186 Log.Exception(ex);
187
188 this.Uri.State.Error(Uris.States.EDalerUriErrorType.ServiceUnavailable, "eDaler transaction could not be processed due to internal failure.", false);
189
190 this.wallet.Balance = Bak;
191 this.wallet.BalanceTimestamp = BakTP;
192 this.transferred = false;
193
194 if (RefAdded)
195 {
196 try
197 {
198 await Database.Delete(Ref);
199 }
200 catch (Exception ex2)
201 {
202 Log.Exception(ex2);
203 }
204 }
205
206 return false;
207 }
208
209 this.@event = Ref;
210 }
211
212 return true;
213 }
214
219 protected override async Task<bool> DoCommit()
220 {
221 CaseInsensitiveString Domain = this.wallet.Domain ?? this.Uri.EDaler.Server.Domain;
222 XmppAddress To = new XmppAddress(this.wallet.Account + "@" + Domain);
223
224 try
225 {
226 await StateMachineProcessor.EDalerReceived(To, this.Uri, this.Uri.State.OriginalSender);
227 }
228 catch (Exception ex)
229 {
230 Log.Exception(ex);
231 }
232
233 string Xml = await this.Uri.EDaler.GetWalletBalanceXml(this.wallet.Account, Domain, this.@event);
234
235 await this.Uri.EDaler.Server.SendMessage(string.Empty, string.Empty,
236 new XmppAddress(this.Uri.EDaler.Subdomain + "." + Domain), To, string.Empty, Xml);
237
238 KeyValuePair<string, object>[] Tags = new KeyValuePair<string, object>[]
239 {
240 new KeyValuePair<string, object>("Amount", this.Uri.TotalAmount),
241 new KeyValuePair<string, object>("Currency", this.Uri.Currency),
242 new KeyValuePair<string, object>("RefId", this.Uri.Id.ToString()),
243 new KeyValuePair<string, object>("Sender", this.Uri.State.Sender.Value),
244 new KeyValuePair<string, object>("From", this.Uri.From.Address.Value),
245 new KeyValuePair<string, object>("To", this.Uri.To.Address.Value),
246 new KeyValuePair<string, object>("Uri", this.Uri.UriString)
247 };
248
249 LocalSender.Append(ref Tags, this.quote);
250
251 Log.Notice(this.Uri.TotalAmount.ToString() + " " + this.Uri.Currency +
252 " eDaler received, and added to wallet.", this.wallet.Account,
253 this.Uri.From.Address, "eDalerReceived", EventLevel.Major, Tags);
254
255 return true;
256 }
257
262 protected override async Task<bool> DoRollback()
263 {
264 bool Result = true;
265
266 using (Semaphore Semaphore = await Semaphores.BeginWrite("wallet:" + this.wallet.Account))
267 {
268 if (this.amount != 0 && this.transferred)
269 {
270 DateTime BakTP = this.wallet.BalanceTimestamp;
271 decimal Bak = this.wallet.Balance;
272
273 try
274 {
275 this.wallet.Balance -= this.amount;
276
277 if (this.wallet.BalanceTimestamp == this.@event.Timestamp)
278 this.wallet.BalanceTimestamp = this.prevTP;
279
280 await Database.Update(this.wallet);
281 this.amount = 0;
282 this.transferred = false;
283 }
284 catch (Exception ex)
285 {
286 Result = false;
287
288 this.wallet.Balance = Bak;
289 this.wallet.BalanceTimestamp = BakTP;
290
291 KeyValuePair<string, object>[] Tags = new KeyValuePair<string, object>[]
292 {
293 new KeyValuePair<string, object>("Amount", this.Uri.TotalAmount),
294 new KeyValuePair<string, object>("Currency", this.Uri.Currency),
295 new KeyValuePair<string, object>("RefId", this.Uri.Id.ToString()),
296 new KeyValuePair<string, object>("Sender", this.Uri.State.Sender.Value),
297 new KeyValuePair<string, object>("From", this.Uri.From.Address.Value),
298 new KeyValuePair<string, object>("To", this.Uri.To.Address.Value),
299 new KeyValuePair<string, object>("Uri", this.Uri.UriString)
300 };
301
302 LocalSender.Append(ref Tags, this.quote);
303
304 Log.Error("Unable to rollback wallet change of " + this.amount.ToString() +
305 " for " + this.wallet.Account + ". Error reported:\r\n\r\n" + ex.Message,
306 this.wallet.Account, string.Empty, "eDalerError", Tags);
307 }
308 }
309
310 if (!(this.@event is null))
311 {
312 try
313 {
314 await Database.Delete(this.@event);
315 this.@event = null;
316 }
317 catch (Exception ex)
318 {
319 Result = false;
320
321 KeyValuePair<string, object>[] Tags = new KeyValuePair<string, object>[]
322 {
323 new KeyValuePair<string, object>("Amount", this.Uri.TotalAmount),
324 new KeyValuePair<string, object>("Currency", this.Uri.Currency),
325 new KeyValuePair<string, object>("RefId", this.Uri.Id.ToString()),
326 new KeyValuePair<string, object>("Sender", this.Uri.State.Sender.Value),
327 new KeyValuePair<string, object>("From", this.Uri.From.Address.Value),
328 new KeyValuePair<string, object>("To", this.Uri.To.Address.Value),
329 new KeyValuePair<string, object>("Uri", this.Uri.UriString)
330 };
331
332 LocalSender.Append(ref Tags, this.quote);
333
334 Log.Error("Unable to rollback wallet event of " + this.amount.ToString() +
335 " for " + this.wallet.Account + ". Error reported:\r\n\r\n" + ex.Message,
336 this.wallet.Account, string.Empty, "eDalerError", Tags);
337 }
338 }
339 }
340
341 if (!(this.transaction is null))
342 {
343 await Database.Delete(this.transaction);
344 this.transaction = null;
345 }
346
347 return Result;
348 }
349 }
350}
Contains a pair of currencies, for currency conversion.
Definition: CurrencyPair.cs:9
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:682
static void Notice(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a notice event.
Definition: Log.cs:450
CaseInsensitiveString Subdomain
Subdomain name.
Definition: Component.cs:76
XmppServer Server
XMPP Server.
Definition: Component.cs:96
Contains information about one XMPP address.
Definition: XmppAddress.cs:9
CaseInsensitiveString Address
XMPP Address
Definition: XmppAddress.cs:37
static readonly XmppAddress Empty
Empty address.
Definition: XmppAddress.cs:31
Task< bool > SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
Definition: XmppServer.cs:3412
CaseInsensitiveString Domain
Domain name.
Definition: XmppServer.cs:882
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
static Task< object > TryLoadObject(string CollectionName, object ObjectId)
Tries to load an object given its Object ID ObjectId and its collection name CollectionName .
Definition: Database.cs:1079
An attempt to insert a key was done, but the key was already there.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Definition: Semaphore.cs:19
Static class of application-wide semaphores that can be used to order access to editable objects.
Definition: Semaphores.cs:16
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Definition: Semaphores.cs:90
Transaction()
Abstract base class for transactions.
Definition: Transaction.cs:26
Abstract base class for eDaler transactions.
async Task< Wallet > GetWallet(XmppAddress For, EntityType ForType)
Gets the wallet of an entity in an eDaler transaction.
override async Task< bool > DoPrepare()
Performs actual preparation.
override async Task< bool > DoRollback()
Performs actual rollback.
LocalRecipient(EDalerUri Uri, bool AddSecondaryTransactionObject)
Handles the local recipient of eDaler.
override async Task< bool > DoCommit()
Performs actual commit.
override async Task< bool > DoExecute()
Performs actual execution.
Abstract base class for eDaler URIs
Definition: EDalerUri.cs:20
EntityType ToType
Type of recipient.
Definition: EDalerUri.cs:191
string UriString
Original URI String.
Definition: EDalerUri.cs:101
EDalerUriState State
URI State object.
Definition: EDalerUri.cs:173
EDalerComponent EDaler
eDaler component reference
Definition: EDalerUri.cs:167
DateTime Created
When URI was created
Definition: EDalerUri.cs:136
decimal TotalAmount
Total amount: Amount+AmountExtra
Definition: EDalerUri.cs:121
byte[] EncryptedMessage
Encrypted message for recipient. If EncryptionPublicKey is null, the message is just UTF-8 encoded.
Definition: EDalerUri.cs:197
Retains the current balance of an account.
Definition: Wallet.cs:16
Interface for currency conversion quotes.
Interface for currency converter services
Task< ICurrencyConverterQuote > GetCurrencyConversionQuote(CaseInsensitiveString FromCurrency, CaseInsensitiveString ToCurrency)
Gets a Currency Exchange Rate from one currency to another.
EventLevel
Event level.
Definition: EventLevel.cs:7