6using System.Text.RegularExpressions;
7using System.Threading.Tasks;
57 #region Smart Contract interface
71 internal static async Task ContractSigned(
Contract Contract,
bool ContractIsLocked,
72 Dictionary<CaseInsensitiveString, Parameter> TransientParameters,
78 if ((TransientParameters?.Count ?? 0) > 0)
80 await RejectContract(
Contract, ContractIsLocked,
81 "Transient parameters not permitted for transparency reasons.",
86 switch (
Contract.ForMachinesLocalName)
89 if (!OnlyMissingTrustProvider(
Contract, out
string TrustProviderRole, out XmlDocument ForMachines))
92 await CreateTokens(ForMachines.DocumentElement,
Contract, ContractIsLocked, TrustProviderRole, Legal,
EDaler);
96 if (!OnlyMissingTrustProvider(
Contract, out TrustProviderRole, out ForMachines))
99 await DestroyTokens(ForMachines.DocumentElement,
Contract, ContractIsLocked, TrustProviderRole, Legal,
EDaler);
103 if (!OnlyMissingTrustProvider(
Contract, out TrustProviderRole, out ForMachines))
106 await TransferTokens(ForMachines.DocumentElement,
Contract, ContractIsLocked, TrustProviderRole, Legal,
EDaler);
110 if (!OnlyMissingTrustProvider(
Contract, out TrustProviderRole, out ForMachines))
113 await DonateTokens(ForMachines.DocumentElement,
Contract, ContractIsLocked, TrustProviderRole, Legal,
EDaler);
117 await RejectContract(
Contract, ContractIsLocked,
"Rejected Neuro-Feature contract: Unrecognized element: " +
Contract.ForMachinesLocalName,
true, Legal,
EDaler);
125 return RejectContract(
Contract, ContractIsLocked, ex.Message, FailContract, Legal,
EDaler,
string.Empty);
130 return RejectContract(
Contract, ContractIsLocked, Reason, FailContract, Legal,
EDaler,
string.Empty);
133 internal static async Task RejectContract(
Contract Contract,
bool ContractIsLocked,
string Reason,
bool FailContract,
142 if (ContractIsLocked)
159 await Legal.SendContractUpdatedEvent(
Contract,
false);
161 if (!(Legal.
Geo is
null))
170 if (IsNeuroFeaturesCreationContract(
Contract, out
string LocalName, out
string Namespace))
172 bool HasStateMachine = LocalName == StateMachineProcessor.StateMachineDefinition &&
178 await TokensDeleted(
EDaler, Deleted,
true,
true,
true, HasStateMachine,
true);
182 Dictionary<CaseInsensitiveString, bool> SendTo =
new Dictionary<CaseInsensitiveString, bool>();
189 await
EDaler.Server.SendMessage(
"chat",
string.Empty,
EDaler.MainDomain.Address, Jid,
"en",
190 "<body>" +
XML.
Encode(Reason) +
"</body>");
202 out
string LocalName, out
string Namespace)
208 private static bool OnlyMissingTrustProvider(
Contract Contract, out
string TrustProviderRole, out XmlDocument ForMachines)
210 TrustProviderRole =
null;
216 if ((
Contract.Roles?.Length ?? 0) < 2)
219 string CreatorRole =
null;
220 string OwnerRole =
null;
223 ForMachines =
Contract.ForMachinesParsed;
224 foreach (XmlElement E
in Contract.ForMachinesParsed.DocumentElement.ChildNodes)
226 if (E.NamespaceURI !=
Contract.ForMachinesNamespace)
231 case "TrustProvider":
247 if (
string.IsNullOrEmpty(TrustProviderRole) || !
string.IsNullOrEmpty(TrustProviderLegalId))
250 if (
string.IsNullOrEmpty(OwnerRole))
251 OwnerRole = CreatorRole;
253 Dictionary<string, SignatureStat> Signatures =
new Dictionary<string, SignatureStat>();
273 Signatures[
Role.
Name] =
new SignatureStat()
287 if (Signatures.TryGetValue(
Signature.Role, out SignatureStat Stat))
293 foreach (SignatureStat Stat
in Signatures.Values)
295 if (Stat.Count < Stat.Min || Stat.Count > Stat.Max)
314 private class SignatureStat
321 private class TokenIDReference
327 public int ShortIdLength;
328 public string ShortIdAlphabet;
333 int ShortIdLength =
XML.
Attribute(Value,
"shortIdLength", 0);
334 string ShortIdAlphabet =
XML.
Attribute(Value,
"shortIdAlphabet",
"0123456789");
336 foreach (XmlNode N
in Value.ChildNodes)
338 if (!(N is XmlElement E))
344 return new TokenIDReference()
346 TokenId = Guid.NewGuid(),
348 ShortIdLength = ShortIdLength,
349 ShortIdAlphabet = ShortIdAlphabet
353 return new TokenIDReference()
355 TokenId = Guid.Empty,
357 ShortIdLength = ShortIdLength,
358 ShortIdAlphabet = ShortIdAlphabet
364 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid Token ID.",
true, Legal,
EDaler);
368 if (s != s.ToLower())
370 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Token IDs must be lower-case.",
true, Legal,
EDaler);
374 if (!Guid.TryParse(s, out Guid TokenGuid))
376 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Token IDs must be GUIDs.",
true, Legal,
EDaler);
380 return new TokenIDReference()
384 ShortIdLength = ShortIdLength,
385 ShortIdAlphabet = ShortIdAlphabet
390 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Undefined Token ID.",
true, Legal,
EDaler);
399 if (tokenCache.TryGetValue(TokenId, out
Token Result))
416 StringBuilder Xml =
new StringBuilder();
418 Xml.Append(
"<token id='");
420 Xml.Append(
"' xmlns='");
430 if (!(Result is
null))
431 tokenCache[TokenId] = Result;
441 (
int NrTokens,
int NrEvents,
int NrTags) = await TokensDeleted(
EDaler, Deleted,
true,
true,
true,
true,
true);
443 return (NrTokens, NrEvents, NrTags);
447 IEnumerable<Token> Deleted,
bool RemoveCache,
bool DeleteEvents,
bool DeleteTags,
448 bool StopMachines,
bool SendNotifications)
480 if (SendNotifications && !
string.IsNullOrEmpty(
Token.
OwnerJid))
482 StringBuilder Xml =
new StringBuilder();
484 Xml.Append(
"<tokenRemoved xmlns='");
488 Xml.Append(
"</tokenRemoved>");
496 return (NrTokens, NrEvents, NrTags);
501 #region Create Contract
506 private enum CommissionPaidBy
519 private static async Task<bool> CreateTokens(XmlElement Create,
Contract Contract,
bool ContractIsLocked,
string TrustProviderRole,
LegalComponent Legal,
522 Dictionary<Guid, TokenIDReference> ProposedTokenIDs =
new Dictionary<Guid, TokenIDReference>();
523 List<TokenIDReference> AllTokenIDs =
new List<TokenIDReference>();
524 SortedDictionary<CaseInsensitiveString, TokenTagReference> TagReferences =
null;
532 string Currency =
null;
533 string Reference =
null;
534 string FriendlyName =
null;
535 string Category =
null;
536 string Description =
null;
537 string GlyphContentType =
null;
539 XmlElement Definition =
null;
540 decimal? Value =
null;
541 decimal? CommissionPercent =
null;
542 CommissionPaidBy CommissionPaidBy = CommissionPaidBy.Owner;
543 int? GlyphWidth =
null;
544 int? GlyphHeight =
null;
545 DateTime Expires = DateTime.MaxValue;
546 bool CreatorCanDestroy =
false;
547 bool OwnerCanDestroyBatch =
false;
548 bool OwnerCanDestroyIndividual =
false;
549 bool CertifierCanDestroy =
false;
551 foreach (XmlElement E
in Create.ChildNodes)
553 if (E.NamespaceURI ==
Contract.ForMachinesNamespace)
558 TokenIDReference TokenRef = await GetTokenId(E,
Contract, ContractIsLocked, Legal,
EDaler);
559 if (TokenRef is
null)
562 AllTokenIDs.Add(TokenRef);
566 if (ProposedTokenIDs.ContainsKey(TokenRef.TokenId))
568 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Duplicate Token IDs used.",
true, Legal,
EDaler);
572 ProposedTokenIDs[TokenRef.TokenId] = TokenRef;
600 case "TrustProvider":
607 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid value.",
true, Legal,
EDaler);
617 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid currency.",
true, Legal,
EDaler);
624 case "CommissionPercent":
627 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid commission (%).",
true, Legal,
EDaler);
631 CommissionPercent = d2;
634 case "CommissionPaidBy":
635 if (!Enum.TryParse(E.InnerText, out CommissionPaidBy CommissionPaidBy2))
637 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid commission payer.",
true, Legal,
EDaler);
641 CommissionPaidBy = CommissionPaidBy2;
646 (Expires = TP.Date) <= DateTime.UtcNow.Date)
648 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid expiry date.",
true, Legal,
EDaler);
653 case "CreatorCanDestroy":
656 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid CreatorCanDestroy property value.",
true, Legal,
EDaler);
660 CreatorCanDestroy = b;
663 case "OwnerCanDestroyBatch":
666 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid OwnerCanDestroyBatch property value.",
true, Legal,
EDaler);
670 OwnerCanDestroyBatch = b2;
673 case "OwnerCanDestroyIndividual":
676 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid OwnerCanDestroyIndividual property value.",
true, Legal,
EDaler);
680 OwnerCanDestroyIndividual = b4;
683 case "CertifierCanDestroy":
686 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid CertifierCanDestroy property value.",
true, Legal,
EDaler);
690 CertifierCanDestroy = b3;
696 if (Ref is
string s3)
702 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid reference.",
true, Legal,
EDaler);
708 foreach (XmlNode N
in E.ChildNodes)
710 if (N is XmlElement E2)
712 if (Definition is
null)
716 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Definition can only be one element.",
true, Legal,
EDaler);
724 TagReferences ??=
new SortedDictionary<CaseInsensitiveString, TokenTagReference>();
728 if (TagReferences.ContainsKey(TagName))
730 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Duplicated tag name.",
true, Legal,
EDaler);
736 if (TagValue is decimal d3)
738 else if (TagValue is
string s)
742 else if (TagValue is
bool b5)
744 else if (TagValue is DateTime TP2)
746 else if (TagValue is TimeSpan TS)
750 else if (TagValue is
byte[] Bin)
756 DateTime AttachmentExpires = Expires;
761 AttachmentExpires.ToUniversalTime());
767 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Unable to get attachment when evaluating tag " + TagName +
".",
true, Legal,
EDaler);
775 else if (TagValue is
null)
777 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Unable to evaluate tag " + TagName +
".",
true, Legal,
EDaler);
782 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Unrecognized value for tag " + TagName +
".",
true, Legal,
EDaler);
790 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid friendly name.",
true, Legal,
EDaler);
794 FriendlyName = Category = Description = s4;
800 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid category.",
true, Legal,
EDaler);
810 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid description.",
true, Legal,
EDaler);
821 if (!GlyphContentType.StartsWith(
"image/"))
823 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid glyph content type.",
true, Legal,
EDaler);
827 Glyph = Convert.FromBase64String(E.InnerText);
832 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid glyph.",
true, Legal,
EDaler);
836 if (!(Decoded.
Decoded is SKImage Image))
840 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Glyph not an image.",
true, Legal,
EDaler);
844 GlyphWidth = Image.Width;
845 GlyphHeight = Image.Height;
851 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid glyph.",
true, Legal,
EDaler);
859 if (AllTokenIDs.Count == 0)
861 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: No Token IDs.",
true, Legal,
EDaler);
865 if (Definition is
null)
867 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Definition missing.",
true, Legal,
EDaler);
873 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Definition schema not found.",
true, Legal,
EDaler);
877 if (
string.IsNullOrEmpty(CreatorId))
879 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Missing Creator ID.",
true, Legal,
EDaler);
883 if (!Value.HasValue || Value.Value <= 0)
885 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Value must be positive.",
true, Legal,
EDaler);
889 if (
string.IsNullOrEmpty(Currency) || Currency.Length != 3)
891 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid currency.",
true, Legal,
EDaler);
895 if (
string.IsNullOrEmpty(TrustProviderRole))
897 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Trust Provider role not defined.",
true, Legal,
EDaler);
901 if (!
string.IsNullOrEmpty(TrustProviderLegalId) &&
904 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Trust Provider ID does not represent the current server.",
true, Legal,
EDaler);
908 if (!CommissionPercent.HasValue)
910 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Commission missing.",
true, Legal,
EDaler);
916 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Commission too low.",
true, Legal,
EDaler);
920 if (Expires <= DateTime.UtcNow.Date)
922 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Must expire on a future date, if to expire.",
true, Legal,
EDaler);
927 DateTime.UtcNow.Date +
Contract.Duration.Value < Expires.AddDays(1))
929 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Duration must cover expiry date.",
true, Legal,
EDaler);
933 if (
string.IsNullOrEmpty(FriendlyName))
935 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: No friendly name provided.",
true, Legal,
EDaler);
939 if (
string.IsNullOrEmpty(Category))
941 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: No category provided.",
true, Legal,
EDaler);
945 if (
string.IsNullOrEmpty(Description))
947 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: No description provided.",
true, Legal,
EDaler);
951 if (Glyph is
null ||
string.IsNullOrEmpty(GlyphContentType) ||
952 !GlyphWidth.HasValue || !GlyphHeight.HasValue || GlyphWidth <= 0 || GlyphHeight <= 0)
954 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: A valid glyph has not been provided.",
true, Legal,
EDaler);
958 OwnerId ??= CreatorId;
972 if (!(CertifierIds is
null))
974 int i, c = CertifierIds.
Length;
976 for (i = 0; i < c; i++)
978 if (
Signature.LegalId == CertifierIds[i])
986 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Owner JID not accessible.",
true, Legal,
EDaler);
992 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Creator JID not accessible.",
true, Legal,
EDaler);
996 if (!(CertifierIds is
null))
998 int i, c = CertifierIds.
Length;
1000 for (i = 0; i < c; i++)
1004 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Certifier JID not accessible.",
true, Legal,
EDaler);
1010 List<Token> Tokens =
new List<Token>();
1011 Dictionary<StateMachine, Token> StateMachines =
null;
1013 DateTime
Created = DateTime.UtcNow;
1014 StringBuilder sb =
new StringBuilder();
1018 string NormalizedDefinition = sb.ToString();
1020 Contract SingletonStateMachineContract =
null;
1023 foreach (TokenIDReference TokenID
in AllTokenIDs)
1027 if (TagReferences is
null)
1028 TagReferences2 =
null;
1032 TagReferences.Values.CopyTo(TagReferences2, 0);
1038 TokenId = TokenID.TokenId.
ToString() +
"@" +
EDaler.MainDomain.Address,
1039 Ordinal = ++Ordinal,
1040 BatchSize = AllTokenIDs.Count,
1042 Creator = CreatorId,
1043 CreatorJid = CreatorJid,
1045 OwnerJid = OwnerJid,
1048 Definition = NormalizedDefinition,
1049 DefinitionNamespace = Definition.NamespaceURI,
1050 DefinitionSchemaDigest = DefinitionSchema.Digest,
1051 DefinitionSchemaHashFunction = DefinitionSchema.Algorithm,
1052 Tags = TagReferences2,
1054 CreatorCanDestroy = CreatorCanDestroy,
1055 OwnerCanDestroyBatch = OwnerCanDestroyBatch,
1056 OwnerCanDestroyIndividual = OwnerCanDestroyIndividual,
1057 CertifierCanDestroy = CertifierCanDestroy,
1058 ArchiveRequired =
Contract.ArchiveRequired,
1059 ArchiveOptional =
Contract.ArchiveOptional,
1060 Value = Value.Value,
1061 Currency = Currency,
1063 CreationContractTemplate =
Contract.TemplateId,
1065 Reference = Reference,
1066 Valuator = ValuatorIds,
1067 Assessor = AssessorIds,
1068 Certifier = CertifierIds,
1069 CertifierJids = CertifierJids,
1070 Witness = WitnessIds,
1073 FriendlyName = FriendlyName,
1074 Category = Category,
1075 Description = Description,
1077 GlyphContentType = GlyphContentType,
1078 GlyphWidth = GlyphWidth.Value,
1079 GlyphHeight = GlyphHeight.Value
1082 TokenID.Token =
Token;
1086 if (!(TagReferences2 is
null))
1088 Dictionary<string, bool> TagNames =
new Dictionary<string, bool>();
1091 TagNames[Ref.
Name] =
true;
1093 LinkedList<XmlElement> ToCheck =
new LinkedList<XmlElement>();
1095 ToCheck.AddLast(Definition);
1097 while (!(ToCheck.First is
null))
1099 XmlElement E = ToCheck.First.Value;
1100 ToCheck.RemoveFirst();
1102 foreach (XmlAttribute Attr
in E.Attributes)
1103 TagNames.Remove(Attr.Value);
1105 TagNames.Remove(E.InnerText);
1107 foreach (XmlNode N
in E.ChildNodes)
1109 if (N is XmlElement E2)
1110 ToCheck.AddLast(E2);
1114 if (TagNames.Count > 0)
1116 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Uniqueness requires all tags to be referenced by the machine-readable XML definition of the token, either in attributes or element values.",
true, Legal,
EDaler);
1124 Token.ShortId = CalcShortId(
Token.
TokenId, TokenID.ShortIdLength, TokenID.ShortIdAlphabet);
1136 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Not able to parse State-Machine definition.",
true, Legal,
EDaler);
1142 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: " + ex.Message,
true, Legal,
EDaler);
1145 catch (UnauthorizedAccessException ex)
1147 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: " + ex.Message,
true, Legal,
EDaler);
1150 catch (Exception ex)
1153 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Exception occurred when parsing state-machine definition: " + ex.Message,
true, Legal,
EDaler);
1178 while (!(Temp is
null));
1180 StateMachine.StateMachineId = Id;
1190 if (SingletonStateMachine is
null)
1196 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Attempted to change the definition of a singleton State-Machine.",
true, Legal,
EDaler);
1202 if (SingletonStateMachineContract is
null)
1204 KeyValuePair<Contract, IqResultEventArgs> P = await legal.GetContract(SingletonStateMachineContract.
ContractId);
1205 SingletonStateMachineContract = P.Key;
1207 if (SingletonStateMachineContract is
null)
1209 if (
string.IsNullOrEmpty(P.Value?.ErrorText))
1210 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Unable to access original creation contract.",
true, Legal,
EDaler);
1212 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Unable to access original creation contract: " + P.Value.ErrorText,
true, Legal,
EDaler);
1217 if (SingletonStateMachineContract.State !=
ContractState.Signed)
1219 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Original creation contract not in a signed state.",
true, Legal,
EDaler);
1223 Dictionary<string, string> Parts =
new Dictionary<string, string>();
1225 if (!(SingletonStateMachineContract.ClientSignatures is
null))
1231 if (!(
Contract.ClientSignatures is
null))
1240 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Role mismatch compared to original creation contract, for part " +
Signature.LegalId,
true, Legal,
EDaler);
1248 if (Parts.Count > 0)
1250 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: All parts of the original creation contract need to sign the new contract.",
true, Legal,
EDaler);
1260 StateMachines ??=
new Dictionary<StateMachine, Token>();
1278 foreach (TokenIDReference TokenID
in AllTokenIDs)
1280 Token Prev = await GetToken(TokenID.Token.TokenId,
false);
1281 Token PrevShort = await GetToken(TokenID.Token.ShortId,
false);
1283 if (!(Prev is
null) || !(PrevShort is
null))
1289 TokenID.TokenId = Guid.NewGuid();
1290 TokenID.Token.TokenId = TokenID.TokenId.ToString() +
"@" +
EDaler.MainDomain.Address;
1291 TokenID.Token.ShortId = CalcShortId(TokenID.Token.TokenId, TokenID.ShortIdLength, TokenID.ShortIdAlphabet);
1293 Prev = await GetToken(TokenID.Token.TokenId,
false);
1294 PrevShort = await GetToken(TokenID.Token.ShortId,
false);
1296 while (!(Prev is
null) || !(PrevShort is
null));
1300 if (!(Prev is
null))
1301 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Token with same ID already exists.",
true, Legal,
EDaler);
1303 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Token with same short ID already exists.",
true, Legal,
EDaler);
1315 TransactionId = Guid.NewGuid();
1317 decimal Commission = Value.Value * AllTokenIDs.Count * CommissionPercent.Value * 0.01m;
1322 string PaymentUri =
PaiwiseProcessor.GenerateContractualPaymentUri(TransactionId,
1323 CommissionPaidBy == CommissionPaidBy.Creator ? CreatorId : OwnerId,
true,
1329 await RejectContract(
Contract, ContractIsLocked,
"Unable to process payment for token creation. Tokens not created.",
1330 true, Legal,
EDaler,
"TokenPayment",
1331 new KeyValuePair<string, object>(
"Creator", CreatorId),
1332 new KeyValuePair<string, object>(
"Owner", OwnerId),
1333 new KeyValuePair<string, object>(
"Value", Value),
1334 new KeyValuePair<string, object>(
"Commission", Commission),
1335 new KeyValuePair<string, object>(
"Currency", Currency));
1343 if (!(StateMachines is
null))
1346 if (!(TagReferences is
null) && TagReferences.Count > 0)
1350 List<TokenTag> Tags =
new List<TokenTag>();
1360 catch (Exception ex)
1368 List<TokenEvent> Events =
new List<TokenEvent>();
1383 Timestamp = DateTime.UtcNow,
1390 catch (Exception ex)
1403 if (!(StateMachines is
null))
1405 foreach (KeyValuePair<StateMachine, Token> P
in StateMachines)
1411 StringBuilder Xml =
new StringBuilder();
1417 Xml.Append(
"<tokenAdded xmlns='");
1421 Xml.Append(
"</tokenAdded>");
1423 if (await SendTokenMessage(
EDaler, Xml.ToString(), OwnerJid,
true,
true))
1424 await TokenAdded(
Token, OwnerJid);
1441 internal static Task<bool> SendTokenMessage(
EDalerComponent EDaler,
string Xml,
string RecipientJid,
bool ToClient,
bool ToBroker)
1443 return SendTokenMessage(
EDaler, Xml,
new XmppAddress(RecipientJid), ToClient, ToBroker);
1460 await
EDaler.Server.SendMessage(
string.Empty,
string.Empty,
EDaler.MainDomain, Recipient,
1464 if (
EDaler.Server.IsServerDomain(Recipient.
Domain,
true))
1479 private static string CalcShortId(
string TokenId,
int ShortIdLength,
string ShortIdAlphabet)
1481 if (ShortIdLength <= 0)
1482 return string.Empty;
1485 int i, j, c = Bin.Length;
1487 for (i = 4, j = 0; i < c; i++, j = (j + 1) & 3)
1490 int Seed = BitConverter.ToInt32(Bin, 0);
1491 Random Rnd =
new Random(Seed);
1492 char[] ch =
new char[ShortIdLength];
1494 c = ShortIdAlphabet.Length;
1496 return string.Empty;
1498 for (i = 0; i < ShortIdLength; i++)
1499 ch[i] = ShortIdAlphabet[Rnd.Next(c)];
1501 return new string(ch);
1510 Dictionary<string, object> Counts =
new Dictionary<string, object>();
1512 StringBuilder Xml =
new StringBuilder();
1518 Xml.Append(
"<tokenAdded xmlns='");
1522 Xml.Append(
"</tokenAdded>");
1526 if (await SendTokenMessage(eDaler, Xml.ToString(), Owner,
false,
true))
1532 Inc(Counts, Owner.
Domain);
1544 Dictionary<string, object> Counts =
new Dictionary<string, object>();
1550 Inc(Counts, Owner.
Domain);
1556 private static void Inc(Dictionary<string, object> Counts,
string Key)
1558 if (Counts.TryGetValue(Key, out
object Obj) && Obj is
int Count)
1559 Counts[Key] = Count + 1;
1566 #region Destroy Contract
1568 private static async Task<bool> DestroyTokens(XmlElement Destroy,
Contract Contract,
bool ContractIsLocked,
string TrustProviderRole,
LegalComponent Legal,
1571 List<string> TokenIDs =
new List<string>();
1573 foreach (XmlElement E
in Destroy.ChildNodes)
1575 if (E.NamespaceURI ==
Contract.ForMachinesNamespace)
1577 switch (E.LocalName)
1583 TokenIDs.Add(TokenId);
1589 if (TokenIDs.Count == 0)
1591 await RejectContract(
Contract, ContractIsLocked,
"Rejected token destruction contract: No Token IDs.",
true, Legal,
EDaler);
1595 Dictionary<string, KeyValuePair<string, Token>> AuthorizedBy =
new Dictionary<string, KeyValuePair<string, Token>>();
1596 Dictionary<string, KeyValuePair<string, List<Token>>> TokensPerBatch =
new Dictionary<string, KeyValuePair<string, List<Token>>>();
1597 List<Token> Tokens =
new List<Token>();
1602 foreach (
string TokenID
in TokenIDs)
1604 Token Token = await GetToken(TokenID,
false);
1608 await RejectContract(
Contract, ContractIsLocked,
"Rejected token destruction contract: Token referenced that does not exist.",
true, Legal,
EDaler);
1611 else if (!
Contract.Duration.HasValue ||
1614 await RejectContract(
Contract, ContractIsLocked,
"Rejected token destruction contract: Duration must cover token expiry date.",
true, Legal,
EDaler);
1619 if (!(
Contract.ClientSignatures is
null))
1628 AuthorizedBy[TokenID] =
new KeyValuePair<string, Token>(
Signature.LegalId,
Token);
1638 AuthorizedBy[TokenID] =
new KeyValuePair<string, Token>(
Signature.LegalId,
Token);
1643 if (!TokensPerBatch.TryGetValue(
Token.
CreationContract, out KeyValuePair<
string, List<Token>> TokensInBatch))
1645 TokensInBatch =
new KeyValuePair<string, List<Token>>(
Signature.LegalId,
new List<Token>());
1649 TokensInBatch.Value.Add(
Token);
1657 for (i = 0; i < c; i++)
1664 AuthorizedBy[TokenID] =
new KeyValuePair<string, Token>(
Signature.LegalId,
Token);
1675 if (TokensPerBatch.Count > 0)
1677 foreach (KeyValuePair<
string, List<Token>> TokensInBatch
in TokensPerBatch.Values)
1679 if (TokensInBatch.Value[0].BatchSize == TokensInBatch.Value.Count)
1682 AuthorizedBy[
Token.
TokenId] =
new KeyValuePair<string, Token>(TokensInBatch.Key,
Token);
1687 if (AuthorizedBy.Count != TokenIDs.Count)
1689 await RejectContract(
Contract, ContractIsLocked,
"Rejected token destruction contract: Not authorized to destroy token referenced in contract.",
true, Legal,
EDaler);
1696 List<TokenEvent> Events =
new List<TokenEvent>();
1698 foreach (KeyValuePair<string, Token> P
in AuthorizedBy.Values)
1702 ArchiveOptional = P.
Value.ArchiveOptional,
1703 ArchiveRequired = P.Value.ArchiveRequired,
1704 Expires = P.Value.Expires,
1707 TokenId = P.
Value.TokenId,
1709 Currency =
string.Empty,
1710 Timestamp = DateTime.UtcNow,
1714 Tokens.Add(P.Value);
1721 await DeletePersonalEvents(TokenIDs);
1728 catch (Exception ex)
1748 foreach (KeyValuePair<string, Token> P
in AuthorizedBy.Values)
1752 StringBuilder Xml =
new StringBuilder();
1758 if (!
string.IsNullOrEmpty(PrevOwner))
1760 Token.Owner =
string.
Empty;
1761 Token.OwnerJid =
string.
Empty;
1767 Xml.Append(
"<tokenRemoved xmlns='");
1771 Xml.Append(
"</tokenRemoved>");
1773 if (await SendTokenMessage(
EDaler, Xml.ToString(), PrevOwner,
true,
true))
1774 await TokenRemoved(
Token, PrevOwner);
1783 await DeletePersonalEvents(
new string[] { Token.TokenId });
1795 Currency =
string.
Empty,
1796 Timestamp = DateTime.UtcNow,
1811 if (!
string.IsNullOrEmpty(PrevOwner))
1813 StringBuilder Xml =
new StringBuilder();
1815 Token.Owner =
string.Empty;
1816 Token.OwnerJid =
string.Empty;
1817 Token.OwnershipContract =
string.Empty;
1822 Xml.Append(
"<tokenRemoved xmlns='");
1826 Xml.Append(
"</tokenRemoved>");
1828 if (await SendTokenMessage(
EDaler, Xml.ToString(), PrevOwner,
true,
true))
1829 await TokenRemoved(
Token, PrevOwner);
1833 internal static async Task DeletePersonalEvents(IEnumerable<string> TokenIds)
1835 foreach (
string TokenId
in TokenIds)
1843 catch (Exception ex)
1852 #region Transfer Contract
1854 private static async Task<bool> TransferTokens(XmlElement Transfer,
Contract Contract,
bool ContractIsLocked,
string TrustProviderRole,
LegalComponent Legal,
1857 List<string> TokenIDs =
new List<string>();
1858 string Currency =
null;
1859 string OwnershipContract =
null;
1860 decimal? Value =
null;
1861 decimal? CommissionPercent =
null;
1863 foreach (XmlElement E
in Transfer.ChildNodes)
1865 if (E.NamespaceURI ==
Contract.ForMachinesNamespace)
1867 switch (E.LocalName)
1873 TokenIDs.Add(TokenId);
1879 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid value.",
true, Legal,
EDaler);
1889 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid currency.",
true, Legal,
EDaler);
1896 case "CommissionPercent":
1899 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid commission (%).",
true, Legal,
EDaler);
1903 CommissionPercent = d2;
1906 case "OwnershipContract":
1909 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid Ownership contract reference.",
true, Legal,
EDaler);
1913 OwnershipContract = s2;
1919 if (TokenIDs.Count == 0)
1921 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: No Token IDs.",
true, Legal,
EDaler);
1925 if (!Value.HasValue || Value.Value <= 0)
1927 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Value must be positive.",
true, Legal,
EDaler);
1931 if (
string.IsNullOrEmpty(Currency) || Currency.Length != 3)
1933 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Invalid currency.",
true, Legal,
EDaler);
1937 if (!CommissionPercent.HasValue)
1939 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Commission missing.",
true, Legal,
EDaler);
1945 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Commission too low.",
true, Legal,
EDaler);
1949 List<Token> Tokens =
new List<Token>();
1958 foreach (
string TokenID
in TokenIDs)
1960 Token Token = await GetToken(TokenID,
false);
1964 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Token referenced that does not exist.",
true, Legal,
EDaler);
1969 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Incorrect ownership contract reference.",
true, Legal,
EDaler);
1972 else if (!
Contract.Duration.HasValue ||
1975 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Duration must cover token expiry date.",
true, Legal,
EDaler);
1980 if (!(
Contract.ClientSignatures is
null))
1984 bool Identified =
false;
1995 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Multiple sellers in one contract not permitted.",
true, Legal,
EDaler);
2009 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Token not hosted by this trust provider.",
true, Legal,
EDaler);
2015 else if (TrustProviderId !=
Signature.LegalId)
2017 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Multiple trust providers in one contract not permitted.",
true, Legal,
EDaler);
2033 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Only one buyer allowed (buyer=not current owner or trust provider).",
true, Legal,
EDaler);
2041 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: No seller identified for token.",
true, Legal,
EDaler);
2047 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: No buyer identified for token.",
true, Legal,
EDaler);
2053 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Trust provider already signed.",
true, Legal,
EDaler);
2062 if (Tokens.Count != TokenIDs.Count)
2064 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Not authorized to transfer token referenced in contract.",
true, Legal,
EDaler);
2068 if (SellerJid == BuyerJid)
2070 await RejectContract(
Contract, ContractIsLocked,
"Rejected token transfer contract: Buyer and Seller cannot be the same.",
true, Legal,
EDaler);
2079 TransactionId = Guid.NewGuid();
2081 decimal Total = Value.Value * TokenIDs.Count;
2082 decimal Commission = Total * CommissionPercent.Value * 0.01m;
2085 string PaymentUri1 =
PaiwiseProcessor.GenerateContractualPaymentUri(TransactionId, BuyerId,
true,
2086 SellerId,
true, Currency, Total,
null, Ref,
Contract.
ContractId,
null, 1, out
_, out
_);
2090 await RejectContract(
Contract, ContractIsLocked,
"Unable to process payment for token transfer. Tokens not created.",
2091 true, Legal,
EDaler,
"TokenPayment",
2092 new KeyValuePair<string, object>(
"Buyer", BuyerId.
Value),
2093 new KeyValuePair<string, object>(
"Seller", SellerId.
Value),
2094 new KeyValuePair<string, object>(
"Value", Value),
2095 new KeyValuePair<string, object>(
"Commission", Commission),
2096 new KeyValuePair<string, object>(
"Currency", Currency));
2103 string PaymentUri2 =
PaiwiseProcessor.GenerateContractualPaymentUri(Guid.NewGuid(), SellerId,
true,
2109 await RejectContract(
Contract, ContractIsLocked,
"Unable to process commission payment for token transfer.",
2110 false, Legal,
EDaler,
"TokenPayment",
2111 new KeyValuePair<string, object>(
"Buyer", BuyerId.
Value),
2112 new KeyValuePair<string, object>(
"Seller", SellerId.
Value),
2113 new KeyValuePair<string, object>(
"Value", Value),
2114 new KeyValuePair<string, object>(
"Commission", Commission),
2115 new KeyValuePair<string, object>(
"Currency", Currency));
2119 List<TokenEvent> Events =
new List<TokenEvent>();
2123 Token.Value = Value.Value;
2124 Token.Currency = Currency;
2125 Token.Owner = BuyerId;
2126 Token.OwnerJid = BuyerJid;
2138 Value = Value.
Value,
2139 Commission = Commission,
2140 Currency = Currency,
2142 Timestamp = DateTime.UtcNow,
2150 await DeletePersonalEvents(TokenIDs);
2157 catch (Exception ex)
2169 int c = Tokens.Count;
2171 for (i = 0; i < c; i++)
2177 catch (Exception ex)
2184 StringBuilder Xml =
new StringBuilder();
2190 Xml.Append(
"<tokenRemoved xmlns='");
2194 Xml.Append(
"</tokenRemoved>");
2196 if (await SendTokenMessage(
EDaler, Xml.ToString(), SellerJid,
true,
true))
2197 await TokenRemoved(
Token, SellerJid);
2201 Xml.Append(
"<tokenAdded xmlns='");
2205 Xml.Append(
"</tokenAdded>");
2207 if (await SendTokenMessage(
EDaler, Xml.ToString(), BuyerJid,
true,
true))
2208 await TokenAdded(
Token, BuyerJid);
2216 #region Donate Contract
2218 private static async Task<bool> DonateTokens(XmlElement Donate,
Contract Contract,
bool ContractIsLocked,
string TrustProviderRole,
LegalComponent Legal,
2221 List<string> TokenIDs =
new List<string>();
2222 string OwnershipContract =
null;
2224 foreach (XmlElement E
in Donate.ChildNodes)
2226 if (E.NamespaceURI ==
Contract.ForMachinesNamespace)
2228 switch (E.LocalName)
2234 TokenIDs.Add(TokenId);
2237 case "OwnershipContract":
2240 await RejectContract(
Contract, ContractIsLocked,
"Rejected token creation contract: Invalid Ownership contract reference.",
true, Legal,
EDaler);
2244 OwnershipContract = s2;
2250 if (TokenIDs.Count == 0)
2252 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: No Token IDs.",
true, Legal,
EDaler);
2256 List<Token> Tokens =
new List<Token>();
2265 foreach (
string TokenID
in TokenIDs)
2267 Token Token = await GetToken(TokenID,
false);
2271 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Token referenced that does not exist.",
true, Legal,
EDaler);
2276 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Incorrect ownership contract reference.",
true, Legal,
EDaler);
2279 else if (!
Contract.Duration.HasValue ||
2282 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Duration must cover token expiry date.",
true, Legal,
EDaler);
2287 if (!(
Contract.ClientSignatures is
null))
2291 bool Identified =
false;
2302 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Multiple donors in one contract not permitted.",
true, Legal,
EDaler);
2316 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Token not hosted by this trust provider.",
true, Legal,
EDaler);
2322 else if (TrustProviderId !=
Signature.LegalId)
2324 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Multiple trust providers in one contract not permitted.",
true, Legal,
EDaler);
2338 else if (RecipientId !=
Signature.LegalId)
2340 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Only one recipient allowed (recipient=not current owner or trust provider).",
true, Legal,
EDaler);
2348 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: No donor identified for token.",
true, Legal,
EDaler);
2354 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: No recipient identified for token.",
true, Legal,
EDaler);
2360 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Trust provider already signed.",
true, Legal,
EDaler);
2369 if (Tokens.Count != TokenIDs.Count)
2371 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Not authorized to donate token referenced in contract.",
true, Legal,
EDaler);
2375 if (DonorJid == RecipientJid)
2377 await RejectContract(
Contract, ContractIsLocked,
"Rejected token donation contract: Donor and Recipient cannot be the same.",
true, Legal,
EDaler);
2386 TransactionId = Guid.NewGuid();
2388 List<TokenEvent> Events =
new List<TokenEvent>();
2392 Token.Owner = RecipientId;
2393 Token.OwnerJid = RecipientJid;
2403 Owner = RecipientId,
2408 Timestamp = DateTime.UtcNow,
2416 await DeletePersonalEvents(TokenIDs);
2423 catch (Exception ex)
2435 int c = Tokens.Count;
2437 for (i = 0; i < c; i++)
2443 catch (Exception ex)
2450 StringBuilder Xml =
new StringBuilder();
2456 Xml.Append(
"<tokenRemoved xmlns='");
2460 Xml.Append(
"</tokenRemoved>");
2462 if (await SendTokenMessage(
EDaler, Xml.ToString(), DonorJid,
true,
true))
2463 await TokenRemoved(
Token, DonorJid);
2467 Xml.Append(
"<tokenAdded xmlns='");
2471 Xml.Append(
"</tokenAdded>");
2473 if (await SendTokenMessage(
EDaler, Xml.ToString(), RecipientJid,
true,
true))
2474 await TokenAdded(
Token, RecipientJid);
2485 #region XMPP interface
2548 private static async Task GetTokenHandler(
object Sender,
IqEventArgs e)
2554 StringBuilder Xml =
new StringBuilder();
2560 private static async Task<Token> GetToken(
IqEventArgs e)
2564 Token Token = await GetToken(TokenId,
false);
2571 if (!await IsAuthorizedAccess(
Token, e))
2602 if (
string.IsNullOrEmpty(P.Value?.ErrorText))
2623 private static async Task GetTokensHandler(
object Sender,
IqEventArgs e)
2634 Xml = SerializeTokenReferences(TokenReferences);
2637 List<Token> Tokens =
new List<Token>();
2642 if (!(
Token is
null))
2646 Xml = await SerializeTokenReferences(Tokens);
2652 private static async Task GetContractTokensHandler(
object Sender,
IqEventArgs e)
2666 KeyValuePair<Contract, IqResultEventArgs> P = await legal.GetContract(ContractId);
2671 if (
string.IsNullOrEmpty(P.Value?.ErrorText))
2691 List<TokenReference> TokenReferences =
new List<TokenReference>();
2697 TokenId = Token.TokenId
2701 Xml = SerializeTokenReferences(TokenReferences);
2704 Xml = await SerializeTokenReferences(Tokens);
2709 internal static string SerializeTokenReferences(IEnumerable<TokenReference> Tokens)
2711 StringBuilder Xml =
new StringBuilder();
2713 Xml.Append(
"<tokenReferences xmlns='");
2719 Xml.Append(
"<ref id='");
2724 Xml.Append(
"</tokenReferences>");
2726 return Xml.ToString();
2729 internal static async Task<string> SerializeTokenReferences(IEnumerable<Token> Tokens)
2731 StringBuilder Xml =
new StringBuilder();
2733 Xml.Append(
"<tokens xmlns='");
2740 Xml.Append(
"</tokens>");
2742 return Xml.ToString();
2749 private static async Task GetTotalsHandler(
object Sender,
IqEventArgs e)
2751 StringBuilder Xml =
new StringBuilder();
2752 string Currency =
null;
2756 Xml.Append(
"<totals xmlns='");
2762 if (Currency is
null || Currency != TokenRef.
Currency)
2766 Xml.Append(
"<total nr='");
2767 Xml.Append(Nr.ToString());
2768 Xml.Append(
"' total='");
2770 Xml.Append(
"' currency='");
2776 Total = TokenRef.
Value;
2782 Total += TokenRef.
Value;
2788 Xml.Append(
"<total nr='");
2789 Xml.Append(Nr.ToString());
2790 Xml.Append(
"' total='");
2792 Xml.Append(
"' currency='");
2797 Xml.Append(
"</totals>");
2806 private static async Task SetNoteTextHandler(
object Sender,
IqEventArgs e)
2811 Token Token = await GetToken(TokenId,
false);
2827 Note = e.
Query.InnerText,
2828 Personal = Personal,
2829 Timestamp = DateTime.UtcNow,
2830 TokenId = Token.TokenId
2835 if (!await IsApprovedExternalSource(
Token, e))
2843 Note = e.
Query.InnerText,
2844 Personal = Personal,
2845 Timestamp = DateTime.UtcNow,
2847 Source = e.From.BareJid
2854 await Task.Run(async () =>
2860 catch (Exception ex)
2875 if (Machine is
null)
2881 if (Machine.CurrentState is
null)
2887 if (Machine.CurrentState.HasEnded)
2893 if (!Machine.CurrentState.ContainsSource(e.
From.
BareJid))
2895 await e.
IqErrorForbidden(e.
To,
"You are not an approved external part for this token.",
"en");
2901 await e.
IqErrorForbidden(e.
To,
"You are not an approved external part for this token.",
"en");
2912 private static async Task SetNoteXmlHandler(
object Sender,
IqEventArgs e)
2917 Token Token = await GetToken(TokenId,
false);
2927 string LocalName =
null;
2928 string Namespace =
null;
2930 foreach (XmlNode N
in e.
Query.ChildNodes)
2932 if (N is XmlElement E)
2936 Namespace = E.NamespaceURI;
2949 if (!(legal is
null))
2953 (string, Dictionary<string, ValidationSchema>) P = await legal.ValidateContent(Doc,
null);
2954 if (!
string.IsNullOrEmpty(P.Item1))
2968 Personal = Personal,
2969 Timestamp = DateTime.UtcNow,
2972 LocalName = LocalName,
2973 Namespace = Namespace
2978 if (!await IsApprovedExternalSource(
Token, e))
2986 Personal = Personal,
2987 Timestamp = DateTime.UtcNow,
2990 LocalName = LocalName,
2991 Namespace = Namespace,
2992 Source = e.From.BareJid
2999 await Task.Run(async () =>
3005 catch (Exception ex)
3016 private static async Task GetEventsHandler(
object Sender,
IqEventArgs e)
3020 Token Token = await GetToken(TokenId,
false);
3027 if (!await IsAuthorizedAccess(
Token, e))
3033 IEnumerable<TokenEvent> Events = await GetTokenEvents(
Token, Offset, MaxCount);
3034 StringBuilder Xml =
new StringBuilder();
3036 Xml.Append(
"<events xmlns='");
3041 Event.Serialize(Xml);
3043 Xml.Append(
"</events>");
3048 internal static async Task<IEnumerable<TokenEvent>> GetTokenEvents(
Token Token,
int Offset,
int MaxCount)
3059 using IEnumerator<
TokenEvent> e1 = Events.GetEnumerator();
3060 using IEnumerator<
TokenEvent> e2 = Events2.GetEnumerator();
3062 LinkedList<TokenEvent> Merged =
new LinkedList<TokenEvent>();
3063 bool Has1 = e1.MoveNext();
3064 bool Has2 = e2.MoveNext();
3066 while (Has1 && Has2)
3068 if (e1.Current.Timestamp > e2.Current.
Timestamp)
3070 Merged.AddLast(e1.Current);
3071 Has1 = e1.MoveNext();
3075 Merged.AddLast(e2.Current);
3076 Has2 = e2.MoveNext();
3082 Merged.AddLast(e1.Current);
3083 Has1 = e1.MoveNext();
3088 Merged.AddLast(e2.Current);
3089 Has2 = e2.MoveNext();
3100 #region GetCreationAttributes
3102 private static async Task GetCreationAttributesHandler(
object Sender,
IqEventArgs e)
3104 StringBuilder Xml =
new StringBuilder();
3106 Xml.Append(
"<creationAttributes xmlns='");
3108 Xml.Append(
"' currency='");
3110 if (!(eDaler is
null))
3113 Xml.Append(
"' commission='");
3115 Xml.Append(
"' trustProvider='");
3127 #region GetDescription
3129 private static async Task GetDescriptionHandler(
object Sender,
IqEventArgs e)
3136 StringBuilder Xml =
new StringBuilder();
3138 Xml.Append(
"<report xmlns='");
3142 Xml.Append(
"</report>");
3160 internal static async Task<string> FormatReport(
string Markdown,
3165 AllowScriptTag =
false,
3166 AudioAutoplay =
false,
3167 AudioControls =
false,
3169 ParseMetaData =
false,
3171 VideoAutoplay =
false,
3172 VideoControls =
false,
3173 EmbedEmojis =
false,
3179 return Task.FromResult(Prohibited);
3198 return FixLocalFileLinks(await Doc.GenerateXAML());
3201 return FixLocalFileLinks(await Doc.GenerateXamarinForms());
3204 return FixLocalFileLinks(await Doc.GenerateSmartContractXml());
3207 throw new ArgumentException(
"Unrecognized report format: " + Format.ToString(), nameof(Format));
3211 private static string FixLocalFileLinks(
string Report)
3213 if (localFileReference is
null)
3216 if (
string.IsNullOrEmpty(s))
3219 if (!
string.IsNullOrEmpty(s) && s[^1] == Path.DirectorySeparatorChar)
3222 localFileReference =
new Regex(
"[\"']" + s.Replace(
"\\",
"\\\\") +
3223 "(?'LocalUrl'[^\"']*)[\"']", RegexOptions.Compiled | RegexOptions.Multiline);
3226 StringBuilder sb =
new StringBuilder();
3228 int c = Report.Length;
3232 Match M = localFileReference.Match(Report, i);
3235 sb.Append(Report[i..c]);
3240 sb.Append(Report.Substring(i, M.Index));
3241 i = M.Index + M.Length;
3244 sb.Append(
Gateway.
GetUrl(M.Groups[
"LocalUrl"].Value.Replace(Path.DirectorySeparatorChar,
'/')));
3249 return sb.ToString();
3252 private static Regex localFileReference =
null;
3258 private static async Task TokenAddedIqHandler(
object Sender,
IqEventArgs e)
3260 string Error =
null;
3263 foreach (XmlNode N
in e.
Query.ChildNodes)
3265 if (N is XmlElement E)
3269 Token Token = IsTokenMessageElement(E, e.
From, out
string Message);
3270 if (!(
Token is
null))
3272 else if (!
string.IsNullOrEmpty(Message))
3277 if (!
string.IsNullOrEmpty(Error))
3285 private static async Task TokenAddedMessageHandler(
object Sender,
MessageEventArgs e)
3287 foreach (XmlNode N
in e.
Content.ChildNodes)
3289 if (N is XmlElement E)
3292 if (!(
Token is
null))
3311 OwnerJid = OwnerJid,
3315 Currency = Token.Currency
3326 Ref.OwnerJid = OwnerJid;
3335 private static Token IsTokenMessageElement(XmlElement E,
XmppAddress From, out
string Message)
3339 Message =
"Non-token embedded element.";
3345 Message =
"Unable to parse embedded token.";
3351 Message =
"Embedded token discarded. Owner not on broker.";
3357 Message =
"Embedded token discarded. Sender not host of token.";
3362 return EmbeddedToken;
3367 #region Token Removed
3369 private static async Task TokenRemovedIqHandler(
object Sender,
IqEventArgs e)
3371 string Error =
null;
3374 foreach (XmlNode N
in e.
Query.ChildNodes)
3376 if (N is XmlElement E)
3380 Token Token = IsTokenMessageElement(E, e.
From, out
string Message);
3381 if (!(
Token is
null))
3383 else if (!
string.IsNullOrEmpty(Message))
3388 if (!
string.IsNullOrEmpty(Error))
3396 private static async Task TokenRemovedMessageHandler(
object Sender,
MessageEventArgs e)
3398 foreach (XmlNode N
in e.
Content.ChildNodes)
3400 if (N is XmlElement E)
3403 if (!(
Token is
null))
Helps with parsing of commong data types.
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Contains information about a response to a content request.
async Task DisposeAsync()
IDisposableAsync.DisposeAsync
bool HasError
If an error occurred.
object Decoded
Decoded object.
static string GetBody(string Html)
Extracts the contents of the BODY element in a HTML string.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > DecodeAsync(string ContentType, byte[] Data, Encoding Encoding, KeyValuePair< string, string >[] Fields, Uri BaseUri)
Decodes an object.
Contains a markdown document. This markdown document class supports original markdown,...
Task< string > GenerateMarkdown()
Generates Markdown from the markdown text.
async Task< string > GeneratePlainText()
Generates Plain Text from the markdown text.
async Task< string > GenerateHTML()
Generates HTML from the markdown text.
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
Contains settings that the Markdown parser uses to customize its behavior.
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
static string Encode(string s)
Encodes a string for use in XML.
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Class representing an event.
Static class managing the application event log. Applications and services log events on this static ...
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.
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.
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Static class managing the runtime environment of the IoT Gateway.
static string GetUrl(string LocalResource)
Gets a URL for a resource.
static ContractsClient ContractsClient
XMPP Contracts Client, if such a compoent is available on the XMPP broker.
static string RootFolder
Web root folder.
Configures legal identity for the gateway.
static bool IsMeApproved(CaseInsensitiveString LegalId)
Checks if a Legal Identity refers to an approved ID of the gateway.
static bool HasApprovedLegalIdentities
If there are approved legal identities configured.
static string LatestApprovedLegalIdentityId
Latest approved Legal Identity ID.
Implements an HTTP server.
static SessionVariables CreateSessionVariables()
Creates a new collection of variables, that contains access to the global set of variables.
Base class for components.
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
XmppAddress MainDomain
Main/principal domain address
bool IsComponentDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the component domain, or optionally, an alternative component domain.
XmppServer Server
XMPP Server.
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
Event arguments for IQ queries.
XmppAddress From
From address attribute
Task IqResult(string Xml, string From)
Returns a response to the current request.
Task IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
XmlElement Query
Query element, if found, null otherwise.
XmppAddress To
To address attribute
Task IqErrorServiceUnavailable(XmppAddress From, string ErrorText, string Language)
Returns a service-unavailable error.
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Event arguments for responses to IQ queries.
XmlElement FirstElement
First child element of the Response element.
bool Ok
If the response is an OK result response (true), or an error response (false).
Event arguments for Messages.
XmppAddress From
From address attribute
XmlElement Content
Content element, if found, null otherwise.
Contains information about one XMPP address.
CaseInsensitiveString Domain
Domain
CaseInsensitiveString Address
XMPP Address
CaseInsensitiveString BareJid
Bare JID
Task< IqResultEventArgs > IqRequest(string Type, string From, string To, string Language, string ContentXml)
Sends an IQ stanza to a recipient.
bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the server domain, or optionally, an alternative domain.
CaseInsensitiveString Domain
Domain name.
static bool CheckExpressionSafe(Expression Expression, out ScriptNode Prohibited)
Checks if an expression is safe to execute (if it comes from an external source).
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
int Length
Gets the number of characters in the current CaseInsensitiveString object.
string LowerCase
Lower-case representation of the case-insensitive string.
int IndexOf(CaseInsensitiveString value, StringComparison comparisonType)
Reports the zero-based index of the first occurrence of the specified string in the current System....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
override string ToString()
CaseInsensitiveString Substring(int startIndex, int length)
Retrieves a substring from this instance. The substring starts at a specified character position and ...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
static Task EndBulk()
Ends bulk-processing of data. Must be called once for every call to StartBulk.
static Task StartBulk()
Starts bulk-proccessing of data. Must be followed by a call to EndBulk.
static async Task Update(object Object)
Updates an object in the database.
static async Task Delete(object Object)
Deletes an object in the database.
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
This filter selects objects that conform to all child-filters provided.
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field lesser than a given value.
Implements an in-memory cache.
Static class managing persistent counters.
static Task< long > IncrementCounter(CaseInsensitiveString Key)
Increments a counter.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
async Task DisposeAsync()
Disposes of the object, asynchronously.
Static class of application-wide semaphores that can be used to order access to editable objects.
static async Task< Semaphore > BeginRead(string Key)
Waits until the semaphore identified by Key is ready for reading. Each call to BeginRead must be fol...
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...
Base class for script exceptions.
Base class for all nodes in a parsed script tree.
Contains methods for simple hash calculations.
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Manages eDaler on accounts connected to the broker.
async Task< string > GetDefaultCurrency()
Gets the default currency
Represents an attachment to a document.
DateTime Timestamp
Timestamp of signature. If no signature, value is DateTime.MinValue
CaseInsensitiveString Id
Attachment ID
byte[] Signature
Binary signature of the attachment, generated by an approved legal identity of the account-holder....
string ContentType
Internet Content Type of binary attachment.
Represents an attachment to a document, with a corresponding URL.
Represents a digital signature on a contract.
Contains the definition of a contract
static void NormalizeXml(XmlElement Xml, StringBuilder Output, string CurrentNamespace)
Normalizes an XML element.
DateTime Expires
When contract expires.
CaseInsensitiveString ContractId
Contract Identity
Task< bool > CanRead(XmppAddress Jid, XmppServer Server, LegalComponent LegalComponent)
Checks if a client with a given Jid is allowed to read the contract.
Class defining a part in a contract
CaseInsensitiveString Role
Role of the part in the contract
CaseInsensitiveString LegalId
Legal identity of part
int MinCount
Smallest amount of signatures of this role required for a legally binding contract.
bool CanRevoke
If parts having this role, can revoke their signature, once signed.
int MaxCount
Largest amount of signatures of this role required for a legally binding contract.
CaseInsensitiveString Name
Name of the role.
Contains an XML schema reference, used to validate a contract.
Abstract base class of signatures
Legal (digital identities, smart contracts) service component.
GeoSpatialComponent Geo
Geo-spatial component
Marketplace processor, brokering sales of items via tenders and offers defined in smart contracts.
Event raised when a token has been created.
Event raised when a token has been destroyed.
Event raised when a token has been donated.
A text note logged on the token from an external source.
An xml note logged on the token from an external source.
A text note logged on the token.
An xml note logged on the token.
string LocalName
Local name of root-element in XML
Abstract base class for token events.
DateTime Timestamp
When event was recorded.
Abstract base class for token events containing notes made by the owner.
decimal Value
Latest value of token
Event raised when a token has been transferred.
Marketplace processor, brokering sales of items via tenders and offers defined in smart contracts.
static async Task< Dictionary< string, object > > TokenOwnerStatistics()
Reindexes token references, and sends relevant token messages to remote neurons.
const string NeuroFeaturesNamespace
https://paiwise.tagroot.io/Schema/NeuroFeatures.xsd
static async Task< Dictionary< string, object > > ReindexTokenReferences()
Reindexes token references, and sends relevant token messages to remote neurons.
Attachment-valued token tag.
Boolean-valued token tag.
DateTime-valued token tag.
Duration-valued token tag.
TimeSpan-valued token tag.
Abstract base class for token tag references.
abstract object Value
Object value.
CaseInsensitiveString Name
Tag Name
abstract TokenTag CreateTag(Token Token)
Creates a Tag object.
CaseInsensitiveString CreatorJid
JID of Creator of token
bool HasStateMachine
If the token has an associated state-machine.
async Task Serialize(StringBuilder Xml, bool IncludeNamespace, bool IncludeServerSignature)
Serializes the Token, in normalized form.
Duration? ArchiveOptional
Duration after which token expires, and the required archiving time, the token can optionally be arch...
string Definition
M2M definition of token, in XML, from the original creation contract.
bool OwnerCanDestroyBatch
If the current owner is allowed to destroy the token.
CaseInsensitiveString OwnershipContract
ID of contract that details the claims of the current owner
ContractVisibility Visibility
Visibility of token
string DefinitionNamespace
Namespace of M2M definition of token.
CaseInsensitiveString Owner
Current owner of token
CaseInsensitiveString OwnerJid
JID of Current owner of token
CaseInsensitiveString TrustProviderJid
JID of Trust Provider, asserting claims in the token.
CaseInsensitiveString CreationContract
ID of contract that details the creation of the token.
async Task< Guid > ComputeUniqueGuid()
Computes a GUID from the defining contents of the token.
bool IsExternalPart(CaseInsensitiveString BareJid)
Checks if a JID is an external part in the creation of the contract.
bool CertifierCanDestroy
If a certifier is allowed to destroy the token.
CaseInsensitiveString ShortId
Short ID
CaseInsensitiveString[] CertifierJids
Optional Bare JIDs of parties used to certify claims in the legal document used to create the token.
TokenTagReference[] Tags
Optional tags and their values, attached on created.
DateTime Updated
When token was updated.
string Description
Description Markdown for the token.
Duration? ArchiveRequired
Duration after which token expires, the token is required to be archived.
static bool TryParse(XmlElement Xml, out Token Token)
Serializes the Token, in normalized form.
DateTime Created
When token was created.
bool CreatorCanDestroy
If the creator is allowed to destroy the token.
decimal Value
Latest value of token
CaseInsensitiveString[] Certifier
Optional Legal IDs of parties used to certify claims in the legal document used to create the token.
CaseInsensitiveString MachineId
State Machine ID, if any
string Currency
Currency used to represent latest value.
CaseInsensitiveString TrustProvider
Trust Provider, asserting claims in the token.
DateTime Expires
Expiry date of token.
bool OwnerCanDestroyIndividual
If the current owner is allowed to destroy the token.
CaseInsensitiveString TokenId
Token ID
CaseInsensitiveString Creator
Creator of token
Contains a reference to a token, possibly on another neuron.
CaseInsensitiveString TokenId
Token ID
decimal Value
Latest value of token
string Currency
Currency used to represent latest value.
CaseInsensitiveString OwnerJid
JID of Current owner of token
DateTime Updated
When token was updated.
Paiwise processor, processing payment instructions defined in smart contracts.
Class representing a state machine.
string XmlDefinition
XML Definition.
CaseInsensitiveString StateMachineId
ID of State Machine.
CaseInsensitiveString DefinitionContractId
ID of Definition Contract
static Task< StateMachine > Parse(Token Token)
Parses the XML representation of a State Machine.
const string StateMachineNamespace
https://paiwise.tagroot.io/Schema/StateMachines.xsd
Component that synchronizes content in the federated network, by sending synchronization messages and...
static async Task SendMessage(CaseInsensitiveString Sender, CaseInsensitiveString Recipient, CaseInsensitiveString RecipientServer, string Xml)
Sends a synchronization message
delegate Task< ScriptNode > AuthorizeExpression(Expression Expression)
Delegate for expression authorization methods.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
SecondaryNameType
What type of secondary name is begin used.
ContractState
Recognized contract states
ContractVisibility
Visibility types for contracts.
ReportFormat
Desired report format
TokenIdMethod
By which mechanism the Token ID was created
Represents a duration value, as defined by the xsd:duration data type: http://www....