|
@@ -11,7 +11,7 @@
|
|
|
|
|
|
## Summary
|
|
|
|
|
|
-This PIP proposes a backwards compatible addressing scheme that enables an infinite address-space within PascalCoin. The usage of these extension addresses can be employed within existing infrastructure such as wallets and exchanges now as well as future Layer-2 dapps.
|
|
|
+This PIP proposes a backwards compatible addressing scheme that enables an infinite address-space within PascalCoin. The usage of these extension addresses can be employed immediately by existing infrastructure such as wallets and exchanges and in future Layer-2 dapps.
|
|
|
|
|
|
## Motivation
|
|
|
|
|
@@ -19,7 +19,7 @@ PascalCoin currently allows users to send/receive operations between accounts us
|
|
|
|
|
|
These account numbers are a limited (and commoditized) resource which form a finite address-space (note: this is fundamental to SafeBox design and it's infinite-scaling capability).
|
|
|
|
|
|
-PascalCoin will provide an infinite address-space (similar toother other crypto-currencies) via "decentralized custodial accounts" which are Layer-2 dapps governed via a Layer-2 Proof-of-Stake overlay network.
|
|
|
+PascalCoin will provide an infinite address-space (similar to other other crypto-currencies) via "decentralized custodial accounts" which are Layer-2 dapps governed via a Layer-2 Proof-of-Stake overlay network.
|
|
|
|
|
|
Before rolling out this Layer-2 infrastructure, PascalCoin first needs to establish an addressing-scheme for this infinite address-space.
|
|
|
|
|
@@ -42,12 +42,12 @@ An E-PASA has the following unique characteristics:
|
|
|
An Extended PASA is defined by the below EBNF grammar:
|
|
|
|
|
|
```
|
|
|
- EPASA = PASA, [ ExtendedAddress ], [ ':', EPASAChecksum ] ;
|
|
|
+ EPASA = PASA, [ ExtendedAddress ], [ ':', ExtendedChecksum ] ;
|
|
|
PASA = ( AccountName | AccountNumber ) ;
|
|
|
AccountName = Pascal64String ;
|
|
|
AccountNumber = Integer, "-", Checksum ;
|
|
|
Checksum = Digit, Digit ;
|
|
|
- EPASAChecksum = HexByte, HexByte ;
|
|
|
+ ExtendedChecksum = HexByte, HexByte ;
|
|
|
ExtendedAddress = ( PublicPayload | ReceiverEncPayload | SenderEncPayload | PasswordEncPayload ) ;
|
|
|
PublicPayload = "[", [ Payload ], "]" ;
|
|
|
ReceiverEncPayload = "(", [ Payload ], ")" ;
|
|
@@ -56,24 +56,31 @@ An Extended PASA is defined by the below EBNF grammar:
|
|
|
Payload = ( """, SafeAnsiString, """ | "0", "x", HexString | Base58String ) ;
|
|
|
Password = SafeAnsiString
|
|
|
SafeAnsiString = SafeAnsiChar, { SafeAnsiChar } ;
|
|
|
- SafeAnsiChar = (" " | "!" | EscapeChar """ | "#" | "$" | "%" | "&" | "'" | "(" | ")" | "*" | "+" | "," | "-" | "." | "/" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | ":" | ";" | "<" | "=" | ">" | "?" | "@" | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "[" | EscapeChar "\" | "]" | "^" | "_" | "`" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "{" | "|" | EscapeChar "}" | "~") ;
|
|
|
+ SafeAnsiChar = (" " | "!" | EscapeChar, """ | "#" | "$" | "%" | "&" | "'" | EscapeChar, "(" | EscapeChar, ")" | "*" | "+" | "," | "-" | "." | "/" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | EscapeChar, ":" | ";" | EscapeChar, "<" | "=" | EscapeChar, ">" | "?" | "@" | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | EscapeChar, "[" | EscapeChar, "\" | EscapeChar, "]" | "^" | "_" | "`" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | EscapeChar, "{" | "|" | EscapeChar, "}" | "~") ;
|
|
|
Pascal64String = SafePascal64Char, { Pascal64Char } ;
|
|
|
Pascal64Char = (Digit | SafePascal64Char)
|
|
|
- SafePascal64Char = ( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "+", "{", "}", "[", "]", "_", ":", "`", "|", "<", ">", ",", ".", "?", "/", "~", ")" "-", "+", "{", "}", "[", "]", "_", ":", "`", "|", "<", ">", ",", ".", "?", "/", "~" ) ;
|
|
|
+ SafePascal64Char = ( "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "!" | "@" | "#" | "$" | "%" | "^" | "&" | "*" | EscapeChar, "(" | EscapeChar, ")" | "-" | "+" | EscapeChar, "{" | EscapeChar, "}" | EscapeChar, "[" | EscapeChar, "]" | "_" | EscapeChar, ":" | "`" | "|" | EscapeChar, "<" | EscapeChar, ">" | "," | "." | "?" | "/" | "~" ) ;
|
|
|
HexString = HexByte { HexByte } ;
|
|
|
HexByte = HexNibble, HexNibble ;
|
|
|
HexNibble = ( Digit | "a" | "b" | "c" | "d" | "e" | "f" ) ; (* no uppercase hex allowed *)
|
|
|
Base58String = Base58Char, { Base58Char } ;
|
|
|
Base58Char = ( NaturalDigit | Base58UpperChar | Base58LowerChar ) ;
|
|
|
- Base58UpperChar = ( "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ) ; (* missing I, O *)
|
|
|
- Base58LowerChar = ( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" ) ; (* missing l *)
|
|
|
+ Base58UpperChar = ( "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "J" | "K" | "L" | "M" | "N" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" ) ; (* missing I, O *)
|
|
|
+ Base58LowerChar = ( "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" ) ; (* missing l *)
|
|
|
Integer = NaturalDigit, { Digit } ;
|
|
|
Digit = ( "0" | NaturalDigit ) ;
|
|
|
NaturalDigit = ( "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ) ;
|
|
|
EscapeChar = "\" ;
|
|
|
-```
|
|
|
|
|
|
-| Rule | Explanation |
|
|
|
+**NOTES**:
|
|
|
+ * Text payload and passwords are restricted to ANSI charset subset range 32..126
|
|
|
+ * The following characters are escaped in Pascal64 and SafeAnsi strings): **:**, **\\**, **"**, **[**, **]**, **(**, **), **<**, **>**,**{**, **}**
|
|
|
+ * Escape character is always: **\\**
|
|
|
+
|
|
|
+The above rules can be interpreted as follows:
|
|
|
+
|
|
|
+```
|
|
|
+| Rule | Interpretation |
|
|
|
| -----------------: | :------------------------------------------------------------------------------------------------------------ |
|
|
|
| EPASA | This is a layer-2 address, fully backwards compatible as Layer-1 address |
|
|
|
| PASA | This is the standard layer-1 address of the receiver account (account number or account name) |
|
|
@@ -84,7 +91,7 @@ An Extended PASA is defined by the below EBNF grammar:
|
|
|
| SenderEncPayload | A payload which is ECIES encrypted using the senders public key (only sender can decrypt EPASA) |
|
|
|
| PasswordEncPayload | A payload which is AES256 encrypted using the specified password |
|
|
|
| Payload | The actual payload data, specified it an well-defined encoding |
|
|
|
-| PayloadChecksum | An UINT16 specified by two hexbytes (4 hexnibbles) that denotes a checksum of payload, used to ensure payload consistency (prevents typo/copy-paste errors) |
|
|
|
+| ExtendedChecksum | A checksum of all the preceding text in the E-PASA (necessary to prevent typo-errors) |
|
|
|
| Password | The password used in PasswordEndPayload. Must be specified as a SafeAnsiString (chars 32..126) |
|
|
|
| Pascal64String | An ANSI string involving a limited subset used for account names (cannot start with a digit) |
|
|
|
| SafeAnsiString | An ANSI string involvolving subset characters 32..126 |
|
|
@@ -92,11 +99,6 @@ An Extended PASA is defined by the below EBNF grammar:
|
|
|
| HexString | A hexadecimal-encoded string prefixed with a 0x. Every byte specified by two hexdigits, lower-case |
|
|
|
|
|
|
|
|
|
-**NOTES**:
|
|
|
- * Text payload and passwords are restricted to ANSI charset subset range 32..126
|
|
|
- * The following characters **\\**, **"**, and **}** must be escaped in ASCII payloads/AES passwords via preceding **\\**
|
|
|
- * Payload content is
|
|
|
-
|
|
|
#### Validation Rules
|
|
|
|
|
|
#### AccountNumber Checksum
|
|
@@ -117,9 +119,9 @@ These strings are used to denote an account names and conform to the following r
|
|
|
- By definition, they must **not** start with a digit.
|
|
|
- String length must between 3..64 inclusive.
|
|
|
|
|
|
-#### E-PASA Checksum
|
|
|
+#### Extended Checksum
|
|
|
|
|
|
-In order to avoid data entry errors, the EPASA is checksummed. In short, the E-PASA checksum is simply the 16-bit MurMur3 hash of all preceding text.
|
|
|
+In order to avoid data entry errors, the EPASA is checksummed via an Extended Checksum. In short, the Extended Checksum is simply the 16-bit MurMur3 hash of all preceding text.
|
|
|
|
|
|
Formally, the EPASA checksum is defined as follows:
|
|
|
|
|
@@ -133,7 +135,7 @@ Formally, the EPASA checksum is defined as follows:
|
|
|
ToHexStringLE = converts the 16bit unsigned integer argument into 4 hexadecimal characters in little-endian
|
|
|
```
|
|
|
|
|
|
-**IMPORTANT**: Whilst the E-PASA grammar allows optional E-PASA checksums, this is purely for convenience. Implementations are expected to automatically fill-in the checksum if not specified in the input.
|
|
|
+**IMPORTANT**: Whilst the E-PASA grammar allows optional Extended Checksums, this is purely for convenience. Implementations are expected to automatically fill-in the checksum if not specified in the input.
|
|
|
|
|
|
#### Payload Lengths
|
|
|
|
|
@@ -165,6 +167,61 @@ This capability is fundamental for using E-PASA as an *address-space* in Layer-2
|
|
|
|
|
|
#### Payload Type Specification:
|
|
|
|
|
|
+```csharp
|
|
|
+ [Flags]
|
|
|
+ public enum PayloadType {
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Payload encoding method not specified.
|
|
|
+ /// </summary>
|
|
|
+ NonDeterministic = 0x00000000,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Not encrypted public payload.
|
|
|
+ /// </summary>
|
|
|
+ Public = 0x00000001,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Encrypted using recipient accounts public key.
|
|
|
+ /// </summary>
|
|
|
+ RecipientKeyEncrypted = 0x00000010,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Encrypted using sender accounts public key.
|
|
|
+ /// </summary>
|
|
|
+ SenderKeyEncrypted = 0x00000100,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Encrypted data using pwd param
|
|
|
+ /// </summary>
|
|
|
+ AESEncrypted = 0x00001000,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Payload data is ASCII
|
|
|
+ /// </summary>
|
|
|
+ AsciiFormatted = 0x0001000,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Payload data is HEX
|
|
|
+ /// </summary>
|
|
|
+ HexFormatted = 0x0010000,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Payload data is Base58
|
|
|
+ /// </summary>
|
|
|
+ Base58Formatted = 0x0100000,
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// E-Pasa encoding uses account name
|
|
|
+ /// </summary>
|
|
|
+ AddressedByName = 0x1000000,
|
|
|
+
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+The values are interpreted as follows:
|
|
|
+
|
|
|
+
|
|
|
| Value | Interpretation |
|
|
|
| -------: | :------------------------------------------------------------------------------ |
|
|
|
| 00000000 | Non-deterministic (requires manual decoding by receiver and **not** an E-PASA) |
|
|
@@ -315,8 +372,172 @@ For Layer-2 applications the ability for a receiver to auto-decode the E-PASA vi
|
|
|
|
|
|
## Reference Implementation
|
|
|
|
|
|
-WIP
|
|
|
+The following regex parses an e-pasa:
|
|
|
+```
|
|
|
+((?<AccountNumber>[1-9]\d+)(?:(?<ChecksumDelim>-)(?<Checksum>\d{2}))?|(?<AccountName>(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|`|\||\\<|\\>|,|\.|\?|/|~)(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|`|\||\\<|\\>|,|\.|\?|/|~){2,63}))(?:(?<PayloadStartChar>[\[\(<\{])(?<PayloadContent>"( |!|\\"|#|\$|%|&|'|\\\(|\\\)|\*|\+|,|-|\.|/|0|1|2|3|4|5|6|7|8|9|\\:|;|\\<|=|\\>|\?|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|\\\[|\\\\|\\]|\^|_|`|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|\\\{|\||\\\}|~)+"|0x(?:[0-9a-f]{2})+|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)?(?:(?<PayloadPasswordDelim>:){1}(?<PayloadPassword>( |!|\\"|#|\$|%|&|'|\\\(|\\\)|\*|\+|,|-|\.|/|0|1|2|3|4|5|6|7|8|9|\\:|;|\\<|=|\\>|\?|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|\\\[|\\\\|\\]|\^|_|`|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|\\\{|\||\\\}|~)+)?)?(?<PayloadEndChar>[]\)>\}]))?(?:(?<ExtendedChecksumDelim>:)(?<ExtendedChecksum>[0-9a-f]{2}[0-9a-f]{2}))?
|
|
|
+```
|
|
|
+
|
|
|
+After matching with the above regex, the named groups need to be extracted and validated as the below snippet shows
|
|
|
+
|
|
|
+```csharp
|
|
|
+
|
|
|
+ public bool TryParse(string epasaText, out EPasa epasa, out EPasaErrorCode errorCode) {
|
|
|
+ errorCode = EPasaErrorCode.Success;
|
|
|
+ epasa = new EPasa();
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(epasaText)) {
|
|
|
+ errorCode = EPasaErrorCode.BadFormat;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ var match = _epasaRegex.Match(epasaText);
|
|
|
+ var checksumDelim = match.Groups["ChecksumDelim"].Success ? match.Groups["ChecksumDelim"].Value : null;
|
|
|
+ var accountNumber = match.Groups["AccountNumber"].Success ? match.Groups["AccountNumber"].Value : null;
|
|
|
+ var accountChecksum = match.Groups["AccountChecksum"].Success ? match.Groups["AccountChecksum"].Value : null;
|
|
|
+ var accountName = match.Groups["AccountName"].Success ? match.Groups["AccountName"].Value : null;
|
|
|
+ var payloadStartChar = match.Groups["PayloadStartChar"].Success ? match.Groups["PayloadStartChar"].Value : null;
|
|
|
+ var payloadEndChar = match.Groups["PayloadEndChar"].Success ? match.Groups["PayloadEndChar"].Value : null;
|
|
|
+ var payloadContent = match.Groups["PayloadContent"].Success ? match.Groups["PayloadContent"].Value : null;
|
|
|
+ var payloadPasswordDelim = match.Groups["PayloadPasswordDelim"].Success ? match.Groups["PayloadPasswordDelim"].Value : null;
|
|
|
+ var payloadPassword = match.Groups["PayloadPassword"].Success ? match.Groups["PayloadPassword"].Value : null;
|
|
|
+ var extendedChecksumDelim = match.Groups["ExtendedChecksumDelim"].Success ? match.Groups["ExtendedChecksumDelim"].Value : null;
|
|
|
+ var extendedChecksum = match.Groups["ExtendedChecksum"].Success ? match.Groups["ExtendedChecksum"].Value : null;
|
|
|
+
|
|
|
+ // Check parsed completely
|
|
|
+ if (epasaText != match.Value) {
|
|
|
+ errorCode = EPasaErrorCode.BadFormat;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (accountName != null) {
|
|
|
+ // Account Name
|
|
|
+ if (string.IsNullOrEmpty(accountName)) {
|
|
|
+ errorCode = EPasaErrorCode.BadFormat;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.PayloadType = epasa.PayloadType | PayloadType.AddressedByName;
|
|
|
+ epasa.AccountName = accountName;
|
|
|
+ } else {
|
|
|
+ // Account Number
|
|
|
+ if (!uint.TryParse(accountNumber, out var accNo)) {
|
|
|
+ errorCode = EPasaErrorCode.AccountNumberTooLong;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.PayloadType = epasa.PayloadType ^ PayloadType.AddressedByName;
|
|
|
+ epasa.Account = accNo;
|
|
|
+
|
|
|
+ if (checksumDelim != null) {
|
|
|
+ if (!uint.TryParse(accountChecksum, out var accChecksum)) {
|
|
|
+ errorCode = EPasaErrorCode.AccountChecksumInvalid;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!AccountHelper.IsValidAccountChecksum(epasa.Account.Value, accChecksum)) {
|
|
|
+ errorCode = EPasaErrorCode.BadChecksum;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.AccountChecksum = accChecksum;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Encryption type
|
|
|
+ switch (payloadStartChar) {
|
|
|
+ case null:
|
|
|
+ break;
|
|
|
+ case "[":
|
|
|
+ if (payloadEndChar != "]") {
|
|
|
+ errorCode = EPasaErrorCode.MismatchedPayloadEncoding;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.PayloadType = epasa.PayloadType | PayloadType.Public;
|
|
|
+ break;
|
|
|
+ case "(":
|
|
|
+ if (payloadEndChar != ")") {
|
|
|
+ errorCode = EPasaErrorCode.MismatchedPayloadEncoding;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.PayloadType = epasa.PayloadType | PayloadType.RecipientKeyEncrypted;
|
|
|
+ break;
|
|
|
+ case "<":
|
|
|
+ if (payloadEndChar != ">") {
|
|
|
+ errorCode = EPasaErrorCode.MismatchedPayloadEncoding;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.PayloadType = epasa.PayloadType | PayloadType.SenderKeyEncrypted;
|
|
|
+ break;
|
|
|
+ case "{":
|
|
|
+ if (payloadEndChar != "}") {
|
|
|
+ errorCode = EPasaErrorCode.MismatchedPayloadEncoding;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.PayloadType = epasa.PayloadType | PayloadType.AESEncrypted;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new NotSupportedException($"Unrecognized start character '{payloadStartChar}'");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // Password
|
|
|
+ if (epasa.PayloadType.HasFlag(PayloadType.AESEncrypted)) {
|
|
|
+ if (payloadPasswordDelim == null) {
|
|
|
+ errorCode = EPasaErrorCode.MissingPassword;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.Password = payloadPassword ?? "";
|
|
|
+ } else if (payloadPasswordDelim != null) {
|
|
|
+ errorCode = EPasaErrorCode.UnusedPassword;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Payload
|
|
|
+ if (payloadStartChar != null) {
|
|
|
+ if (payloadContent == null) {
|
|
|
+ epasa.Payload = string.Empty;
|
|
|
+ } else if (payloadContent.StartsWith("\"")) {
|
|
|
+ epasa.PayloadType = epasa.PayloadType | PayloadType.AsciiFormatted;
|
|
|
+ epasa.Payload = payloadContent.Trim('"');
|
|
|
+ } else if (payloadContent.StartsWith("0x")) {
|
|
|
+ epasa.PayloadType = epasa.PayloadType | PayloadType.HexFormatted;
|
|
|
+ epasa.Payload = payloadContent.Substring(2);
|
|
|
+ } else {
|
|
|
+ epasa.PayloadType = epasa.PayloadType | PayloadType.Base58Formatted;
|
|
|
+ epasa.Payload = payloadContent;
|
|
|
+ }
|
|
|
+ epasa.Payload = payloadContent;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Payload Lengths
|
|
|
+ if (!EPasaHelper.IsValidPayloadLength(epasa.PayloadType, epasa.Payload)) {
|
|
|
+ errorCode = EPasaErrorCode.PayloadTooLarge;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Extended Checksum
|
|
|
+ if (extendedChecksumDelim != null) {
|
|
|
+ if (checksumDelim == null) {
|
|
|
+ errorCode = EPasaErrorCode.MissingAccountChecksum;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!EPasaHelper.IsValidExtendedChecksum(epasa.ToString(true), epasa.ExtendedChecksum)) {
|
|
|
+ errorCode = EPasaErrorCode.BadExtendedChecksum;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ epasa.ExtendedChecksum = extendedChecksum;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+Full source-code for the above is available [here][1].
|
|
|
+
|
|
|
+A recursive-descent implementation can be found [here][2].
|
|
|
+
|
|
|
|
|
|
## Links
|
|
|
|
|
|
-None
|
|
|
+1. [C# Regex Parser][1]
|
|
|
+2. [C# Recursive-Descent Parser][2]
|
|
|
+
|
|
|
+[1]: https://github.com/Sphere10/NPascalCoin/blob/master/src/NPascalCoin/Common/Parsing/RegexEPasaParser.cs
|
|
|
+[2]: https://github.com/Sphere10/NPascalCoin/blob/master/src/NPascalCoin/Common/Parsing/RecursiveDescentEPasaParser.cs
|
|
|
+
|