PIP: PIP-0027 Title: E-PASA: Infinite Address-Space (via Layer-2) Type: Protocol, Front-End Impact: Hard-Fork Author: Herman Schoenfeld## 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 immediately by existing infrastructure such as wallets and exchanges and in future Layer-2 dapps. ## Motivation PascalCoin currently allows users to send/receive operations between accounts using their account numbers (PASA). 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 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. This PIP provides one such scheme. Additionally, this scheme can also be immediately employed at the presention-layer to greatly simplify exchange integrations and payload-based payments. ## Specification Layer-2 addresses will be herein referred to as Extended PASA, or E-PASA for short. An E-PASA has the following unique characteristics: * Each E-PASA is a **unique** identifier. * An E-PASA is encoded into the raw network payload of operations. * There is a 1-1 mapping between E-PASA and their raw payload form. * They are deterministically dehydrated to into raw payloads. * They are deterministically hydrated from raw payloads (**note**: requires V5 protocol). * No two fully checksummed E-PASA's can refer to the same logical address. ### Extended PASA format (E-PASA) An Extended PASA is defined by the below EBNF grammar: ``` EPASA = PASA, [ ExtendedAddress ], [ ':', ExtendedChecksum ] ; PASA = ( AccountName | AccountNumber ) ; AccountName = Pascal64String ; AccountNumber = Integer, "-", Checksum ; Checksum = Digit, Digit ; ExtendedChecksum = HexByte, HexByte ; ExtendedAddress = ( PublicPayload | ReceiverEncPayload | SenderEncPayload | PasswordEncPayload ) ; PublicPayload = "[", [ Payload ], "]" ; ReceiverEncPayload = "(", [ Payload ], ")" ; SenderEncPayload = "<", [ Payload ], ">" ; PasswordEncPayload = "{", [ Payload ], ":", [ Password ], "}" ; Payload = ( """, PascalAsciiString, """ | "0", "x", HexString | Base58String ) ; Password = PascalAsciiString PascalAsciiString = PascalAsciiChar, { PascalAsciiChar } ; PascalAsciiChar = (" " | "!" | 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 = Pascal64StartChar, { Pascal64Char } ; Pascal64Char = (Digit | Pascal64StartChar) Pascal64StartChar = ( "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, "<" | 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 *) Integer = NaturalDigit, { Digit } ; Digit = ( "0" | NaturalDigit ) ; NaturalDigit = ( "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ) ; EscapeChar = "\" ; ``` **NOTES**: * Text payload and passwords are restricted to ANSI charset subset range 32..126 * The following characters are escaped in **Pascal64** encoding: **(** **(** **)** **{** **}** **[** **]** **:** **"** **<** **>** * The following characters are escaped in **PascalAscii** encoding: **"** **(** **)** **:** **<** **>** **[** **\\** **]** **{** **}** * 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) | | Checksum | This is the standard layer-1 address checksum | | ExtendedAddress | The optional extra text that forms part of layer-2 address (payload specification) | | PublicPayload | A payload which is not encrypted and publically visible | | ReceiverEncPayload | A payload which is ECIES encrypted using receivers public key (the PASA portion specifies receiver) | | 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 | | 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 PascalAsciiString (chars 32..126) | | Pascal64String | An ANSI string involving a limited subset used for account names (cannot start with a digit) | | PascalAsciiString | An ANSI string involvolving subset characters 32..126 | | Base58String | A Base58-encoded string. This is used for specifying public keys, and hashes of public keys | | HexString | A hexadecimal-encoded string prefixed with a 0x. Every byte specified by two hexdigits, lower-case | #### Validation Rules #### AccountNumber Checksum Layer-1 account checkum must be the following number: ``` Checksum = ((AccountNumber*101) MOD 89) + 10 ``` **NOTE** AccountNumber above denotes the integer portion of the string. **IMPORTANT**: Whilst the E-PASA grammar allows optional PASA checksum, this is purely for convenience. Implementations are expected to automatically fill-in the checksum if not specified in the input. #### Pascal64String These strings are used to denote an account names and conform to the following rules. - By definition, they must **not** start with a digit. - String length must between 3..64 inclusive. #### Extended Checksum 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: ``` PayloadChecksum = ToHexStringLE ( CastToUINT16( MurMur3( ToAsciiBytes ( PASA ++ ExtendedAddress ) ) MOD 65536 ) ) where ToAsciiBytes = converts ASCII string argument into raw byte form, character by character (no endianness concerns here) MurMur3 = performs 32bit MurMur3 hash of the byte array argument CastToUINT16 = casts the integer argument into to a 16bit unsigned integer (should never overflow due to modulo 65536) ToHexStringLE = converts the 16bit unsigned integer argument into 4 hexadecimal characters in little-endian ``` **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 The following validation rules must be applied to Payload lengths | Payload Type | Encryption Mode | Byte-form Length | E-PASA string-form length | | :------------------- | :--------------- | :------------------ | :------------------------- | | ASCII | None (Public) | 255 | 255 | | ASCII | ECIES | 144 | 144 | | ASCII | AES | 223 | 223 | | Hexadecimal | None (Public) | 255 | 510+2 | | Hexadecimal | ECIES | 144 | 288+2 | | Hexadecimal | AES | 223 | 446+2 | | Base58 | None (Public) | 255 | 348 | | Base58 | ECIES | 144 | 196 | | Base58 | AES | 223 | 304 | **NOTE:** +2 accounts for "0x" prefix for hexadecimal strings #### Payload Type In order for a recipient of an operation to **automatically** and **deterministically** decode an E-PASA from a raw network payload, the PascalCoin protocol needs additional data to describe the Payload. This PIP proposes pre-fixing all Payloads in operations with a single-byte value called the **PayloadType**. The PayloadType describes the encryption and encoding of the Payload. PayloadType will allow E-PASA's to be deterministically decoded by the recipient and prevent ambiguous decodings. This capability is fundamental for using E-PASA as an *address-space* since uniqueness requires a 1-1 mapping. With this feature, Layer-2 apps can safely use E-PASA as Layer-2 addresses. #### Payload Type Specification: ```csharp [Flags] public enum PayloadType { ///Comments-URI: https://discord.gg/sJqcgtD (channel #pip-0027) Status: Active Created: 2019-02-11
| E-PASA | Description |
|---|---|
| 123456-77 | Account 123456-77 (backwards compatible with current addresses) |
| pascalcoin-foundation | Account with name 'pascalcoin-foundation' no payload |
| my-favorite-exchange("herman@email.com") | An account called "my-favorite-exchange" with a recipient-encrypted payload (e.g. an exchange deposit address where only exchange can see payload, used as a user ID) |
| E-PASA | Description |
|---|---|
| 123456-77 | Account 123456-77 (backwards compatible) |
| 123456-77["Hello World!"] | With public ASCII payload "Hello World!" without checksum protection |
| 123456-77["Hello World!"]:10cb | Checksum protected |
| 123456-77("Hello World!"):7ba2 | ECIES encrypted using recipients public key |
| 123456-77<"Hello World!">:b51f | ECIES encrypted using senders public key |
| 123456-77{"Hello World!":!43lp|-d|a%@#!} | AES256 encrypted payload using password !43lp|-d|a%@#! |
| E-PASA | Description |
|---|---|
| 77-44[0x416c70686124] | Account 77-44 with unencrypted (public) hexadecimal payload without protection |
| 77-44[0x416c70686124]:10cb | Checksum protected |
| 77-44(0x416c70686124):7ba2 | ECIES encrypted using recipients public key (and checksum protected) |
| 77-44<0x416c70686124>:b51f | ECIES encrypted using senders public key (and checksum protected) |
| 77-44{0x416c70686124:!43lp-da%@#!} | AES encrypted using password !43lp-da%@#! |
| E-PASA | Description |
|---|---|
| 77-44[1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2] | Account 77-44 with unencrypted (public) Base58 payload (bitcoin address) without checksum protection |
| 77-44[1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2]:10cb | Checksum protected |
| 77-44(1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2):7ba2 | ECIES encrypted using recipients public key |
| 77-44<1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2>:b51f | ECIES encrypted using senders public key |
| 77-44{1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2:!43lp-da%@#!} | AES encrypted using password !43lp-da%@#! |
| E-PASA | Description |
|---|---|
| 999-72["Message with all escaped chars \\\"\} here"] | Public ANSI string Message with all escaped chars \"} here |
| 999-72 | Non-deterministic, raw network-level payload bytes ignored |
| 999-72[] | Empty Public payload, raw network-level payload bytes ignored |
| 999-72() | Empty ECIES payload, raw network-level payload bytes encrypted with receivers key but ignored |
| 999-72<> | Empty ECIES payload, raw network-level payload bytes encrypted with senders key but ignored |
| 999-72{:} | Empty AES payload, raw network-level payload bytes AES encryped but ignored |
| 999-72{:pwd} | Empty AES payload, raw network-level payload bytes AES encryped with password pwd but ignoredpwd but ignored |
| my-account-name | Non-deterministic, raw network-level payload bytes ignored |
| my-account-name[] | Empty public payload, raw network-level payload bytes ignored |
| my-account-name() | Empty ECIES payload, raw network-level payload bytes encrypted with receivers key but ignored |
| my-account-name<> | Empty ECIES payload, raw network-level payload bytes encrypted with senders key but ignored |
| my-account-name{:} | Empty AES payload, raw network-level payload bytes encrypted with empty password but ignored |
| my-account-name{:pwd} | Empty AES payload, raw network-level payload bytes encrypted with password pwd but ignored |
| 999-72{"Hello":Funny\"Pwd} | AES encrypted using escaped password Funny"Pwd |
| 999-72{"Hello":\\\"\}} | AES encrypted using escaped password \"} |