Browse Source

Merge pull request #212 from PascalCoin/master

Dev. update
Pascal Coin 4 years ago
parent
commit
da9409a07f
6 changed files with 306 additions and 122 deletions
  1. 70 2
      src/core/UAccounts.pas
  2. 4 0
      src/core/UConst.pas
  3. 181 100
      src/core/UEPasa.pas
  4. 1 1
      src/core/UPCRPCSend.pas
  5. 41 19
      src/core/URPC.pas
  6. 9 0
      src/libraries/sphere10/UCommon.pas

+ 70 - 2
src/core/UAccounts.pas

@@ -26,7 +26,7 @@ interface
 
 uses
   Classes, SysUtils, UConst, UCrypto, SyncObjs, UThread, UBaseTypes,
-  UPCOrderedLists, UPCDataTypes, UPCSafeBoxRootHash,
+  UPCOrderedLists, UPCDataTypes, UPCSafeBoxRootHash, UEPasa,
   UPCHardcodedRandomHashTable, UJSONFunctions,
   {$IFDEF USE_ABSTRACTMEM}
   UPCAbstractMem, UPCAbstractMemAccountKeys,
@@ -295,7 +295,9 @@ Type
     Function FindAccountByName(const aName : String) : Integer; overload;
     Function FindAccountByName(const aName : TRawBytes) : Integer; overload;
     Function FindAccountsStartingByName(const AStartName : TRawBytes; const ARawList : TOrderedRawList; const AMax : Integer = 0) : Integer;
-
+    Function TryResolveAccountByEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean): Boolean; overload;
+    Function TryResolveAccountByEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean; out AErrorMessage: String): Boolean; overload;
+    
     Procedure Clear;
     Function Account(account_number : Cardinal) : TAccount;
     Function GetBlock(block_number : Cardinal) : TBlockAccount;
@@ -4700,6 +4702,72 @@ begin
   end;
 end;
 
+Function TPCSafeBox.TryResolveAccountByEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean): Boolean;
+var LErrMsg : String;
+begin
+  Result := TryResolveAccountByEPASA(AEPasa, AResolvedAccount, AResolvedKey, ARequiresPurchase, LErrMsg);
+end;
+
+Function TPCSafeBox.TryResolveAccountByEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean; out AErrorMessage: String): Boolean;
+var
+  LKey : TAccountKey;
+  LErrMsg : String;
+begin
+  if (AEPasa.IsPayToKey) then begin
+    // Parse account key in EPASA
+    if NOT TAccountComp.AccountPublicKeyImport(AEPasa.Payload, LKey, LErrMsg) then begin
+      AResolvedAccount := CT_AccountNo_NUL;
+      AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+      ARequiresPurchase := False;
+      AErrorMessage := Format('Invalid key specified in PayToKey EPASA "%s". %s',[AEPasa.ToString(), LErrMsg]);
+      Exit(False);
+    end;
+    
+    // Try to find key in safebox 
+
+    // If key is found, then do not purchase new account and send to first account with key
+
+    // If no key found, find optimal public purchase account
+
+    // WIP (not implemented)
+    AResolvedAccount := CT_AccountNo_NUL;    
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+    ARequiresPurchase := False;
+    AErrorMessage := 'Not implemented';
+    Result := False;     
+  end else if (AEPasa.IsAddressedByName) then begin
+    // Find account by name
+
+    // WIP (not implemented)
+    AResolvedAccount := CT_AccountNo_NUL;    
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+    ARequiresPurchase := False;
+    AErrorMessage := 'Not implemented';
+    Result := False; 
+    
+  end else begin
+    // addressed by number
+    if NOT AEPasa.IsAddressedByNumber then
+      raise Exception.Create('Internal Error c8ecd69d-3621-4f5e-b4f1-9926ab2f5013');
+    if NOT AEPasa.Account.HasValue then raise Exception.Create('Internal Error 544c8cb9-b700-4b5f-93ca-4d045d0a06ae');
+
+    if (AEPasa.Account.Value < 0) or (AEPasa.Account.Value >= Self.AccountsCount) then begin
+      AResolvedAccount := CT_AccountNo_NUL;
+      AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+      ARequiresPurchase := False;
+      AErrorMessage := Format('Account number %d does not exist in safebox',[AEPasa.Account.Value]);
+      Exit(False);
+    end;
+    
+    AResolvedAccount := AEPasa.Account.Value;
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;      
+    ARequiresPurchase := False;    
+    Result := true;
+  end;
+
+
+end;
+
 procedure TPCSafeBox.SearchBlockWhenOnSeparatedChain(blockNumber: Cardinal; out blockAccount: TBlockAccount);
   Function WasUpdatedBeforeOrigin : Boolean;
   var j, maxUB : Integer;

+ 4 - 0
src/core/UConst.pas

@@ -219,6 +219,10 @@ Const
 
   CT_ACTIVATE_RANDOMHASH_V4 = {$IFDEF ACTIVATE_RANDOMHASH_V4}True{$ELSE}False{$ENDIF};
 
+  // Represents a non-existent account number
+  // (chosen as the last account in safebox, generated in year 6101)
+  CT_AccountNo_NUL = High(Cardinal);
+
   // App Params
   CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';

+ 181 - 100
src/core/UEPasa.pas

@@ -27,7 +27,6 @@ uses
   SysUtils,
   TypInfo,
   uregexpr,
-  UAccounts,
   UCommon,
   UCrypto,
   UEncoding;
@@ -43,76 +42,87 @@ type
     BadExtendedChecksum
   );
 
-  { PayloadType}
-
-  PayloadType = (
-    NonDeterministic = 0,      // Payload encryption and encoding method not specified.
-    &Public = 1,               // Unencrypted, public payload.
-    RecipientKeyEncrypted = 2, // ECIES encrypted using recipient accounts public key.
-    SenderKeyEncrypted = 3,    // ECIES encrypted using sender accounts public key.
-    PasswordEncrypted = 4,     // AES encrypted using pwd param
-    AsciiFormatted = 5,        // Payload data encoded in ASCII
-    HexFormatted = 6,          // Payload data encoded in HEX
-    Base58Formatted = 7,       // Payload data encoded in Base58
-    AddressedByName = 8        // E-PASA addressed by account name (not number).
+  { TPayloadTrait }
+
+  TPayloadTrait = (
+    ptNonDeterministic = 0,      // Payload encryption and encoding method not specified.
+    ptPublic = 1,               // Unencrypted, public payload.
+    ptRecipientKeyEncrypted = 2, // ECIES encrypted using recipient accounts public key.
+    ptSenderKeyEncrypted = 3,    // ECIES encrypted using sender accounts public key.
+    ptPasswordEncrypted = 4,     // AES encrypted using pwd param
+    ptAsciiFormatted = 5,        // Payload data encoded in ASCII
+    ptHexFormatted = 6,          // Payload data encoded in HEX
+    ptBase58Formatted = 7,       // Payload data encoded in Base58
+    ptAddressedByName = 8        // E-PASA addressed by account name (not number).
   );
 
-  { PayloadTypeHelper }
+  { TPayloadTraitHelper }
 
-  PayloadTypeHelper = record helper for PayloadType
+  TPayloadTraitHelper = record helper for TPayloadTrait
   public
-    function Value: Int32;
+    function ProtocolValue: byte;
   end;
 
-  { PayloadTypes }
+  { TPayloadType }
 
-  PayloadTypes = set of PayloadType;
+  TPayloadType = set of TPayloadTrait;
 
-  { PayloadTypesHelper }
+  { TPayloadTypesHelper }
 
-  PayloadTypesHelper = record helper for PayloadTypes
+  TPayloadTypeHelper = record helper for TPayloadType
   public
-    function IsPayloadTypeInSet(APayloadType: PayloadType): Boolean; inline;
+    function HasTrait(APayloadTrait: TPayloadTrait): Boolean; inline;
+    function ProtocolValue : byte;
   end;
 
-
   { TEPasa }
 
+
   TEPasa = record
     strict private
       var
         FAccount, FAccountChecksum: TNullable<UInt32>;
         FAccountName, FPayload, FPassword, FExtendedChecksum: String;
-        FPayloadTypes: PayloadTypes;
+        FPayloadType: TPayloadType;
 
       function GetAccount: TNullable<UInt32>; inline;
       procedure SetAccount(const AValue: TNullable<UInt32>); inline;
       function GetAccountChecksum: TNullable<UInt32>; inline;
       procedure SetAccountChecksum(const AValue: TNullable<UInt32>); inline;
-      function GetPayloadTypes: PayloadTypes; inline;
+      function GetPayloadType: TPayloadType; inline;
       function GetAccountName: String; inline;
       procedure SetAccountName(const AValue: String); inline;
-      procedure SetPayloadTypes(const AValue: PayloadTypes); inline;
+      procedure SetPayloadType(const AValue: TPayloadType); inline;
       function GetExtendedChecksum: String; inline;
       procedure SetExtendedChecksum(const AValue: String); inline;
       function GetPassword: String; inline;
       procedure SetPassword(const AValue: String); inline;
       function GetPayload: String; inline;
       procedure SetPayload(const AValue: String); inline;
-
+      function GetHasPayload: Boolean; inline;
+      function GetIsStandard: Boolean; inline;
+      function GetIsPayToKey: Boolean; inline;
+      function GetIsAddressedByName : Boolean; inline;
+      function GetIsAddressedByNumber : Boolean; inline;
+      class function GetEmptyValue : TEPasa; static;
     public
-      function IsPayToKey: Boolean; inline;
-      function GetRawPayloadBytes(): TArray<Byte>; inline;
-      function ToString(): String; overload;
-      function ToString(AOmitExtendedChecksum: Boolean): String; overload;
-
       property Account: TNullable<UInt32> read GetAccount write SetAccount;
       property AccountChecksum: TNullable<UInt32> read GetAccountChecksum write SetAccountChecksum;
       property AccountName: String read GetAccountName write SetAccountName;
-      property PayloadTypes: PayloadTypes read GetPayloadTypes write SetPayloadTypes;
+      property PayloadType: TPayloadType read GetPayloadType write SetPayloadType;
       property Payload: String read GetPayload write SetPayload;
       property Password: String read GetPassword write SetPassword;
       property ExtendedChecksum: String read GetExtendedChecksum write SetExtendedChecksum;
+      property IsAddressedByNumber: boolean read GetIsAddressedByNumber;
+      property IsAddressedByName: boolean read GetIsAddressedByName;
+      property IsPayToKey: boolean read GetIsPayToKey;
+      property IsStandard: boolean read GetIsStandard;
+      property HasPayload: boolean read GetHasPayload;
+      class property Empty : TEPasa read GetEmptyValue;
+
+      function GetRawPayloadBytes(): TArray<Byte>; inline;
+      function ToString(): String; overload;
+      function ToString(AOmitExtendedChecksum: Boolean): String; overload;
 
       class function TryParse(const AEPasaText: String; out AEPasa: TEPasa) : Boolean; static;
       class function Parse(const AEPasaText: String): TEPasa; static;
@@ -121,6 +131,8 @@ type
 
   end;
 
+
+
   { TEPasaParser }
 
   TEPasaParser = class
@@ -155,9 +167,9 @@ type
       function TryParse(const AEPasaText: String; out AEPasa: TEPasa; out AErrorCode: EPasaErrorCode): Boolean; overload;
   end;
 
-  { TEPasaHelper }
+  { TEPasaComp }
 
-  TEPasaHelper = class sealed(TObject)
+  TEPasaComp = class sealed(TObject)
 
     strict private
       class function ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>; static;
@@ -177,9 +189,11 @@ type
 
       class function ComputeExtendedChecksum(const AText: String): String; static;
       class function IsValidExtendedChecksum(const AText: String; const AChecksum: String): Boolean; static;
-      class function IsValidPayloadLength(APayloadType: PayloadTypes; const APayloadContent: String): Boolean; static;
+      class function IsValidPayloadLength(APayloadType: TPayloadType; const APayloadContent: String): Boolean; static;
       class function IsValidPasswordLength(const APasswordValue: String) : Boolean; static;
 
+      class function GetPayloadTypeProtocolByte(const APayloadType : TPayloadType) : Byte;
+      class function GetPayloadTypeFromProtocolByte(AByte : Byte) : TPayloadType;
   end;
 
 resourcestring
@@ -200,30 +214,40 @@ uses
   HlpConverters,
   UMemory;
 
-{ PayloadTypeHelper }
+var
+  EmptyEPasa : TEPasa;
+
+{ TPayloadTraitHelper }
 
-function PayloadTypeHelper.Value: Int32;
+function TPayloadTraitHelper.ProtocolValue: Byte;
 begin
   case Self of
-    NonDeterministic: Result := $00000000;
-    &Public: Result := $00000001;
-    RecipientKeyEncrypted: Result := $00000010;
-    SenderKeyEncrypted: Result := $00000100;
-    PasswordEncrypted: Result := $00001000;
-    AsciiFormatted: Result := $00010000;
-    HexFormatted: Result := $00100000;
-    Base58Formatted: Result := $01000000;
-    AddressedByName: Result := $10000000;
+    ptNonDeterministic: Result := 0;
+    ptPublic: Result := BYTE_BIT_0;
+    ptRecipientKeyEncrypted: Result := BYTE_BIT_1;
+    ptSenderKeyEncrypted: Result := BYTE_BIT_2;
+    ptPasswordEncrypted: Result := BYTE_BIT_3;
+    ptAsciiFormatted: Result := BYTE_BIT_4;
+    ptHexFormatted: Result := BYTE_BIT_5;
+    ptBase58Formatted: Result := BYTE_BIT_6;
+    ptAddressedByName: Result := BYTE_BIT_7;
   end;
+  raise Exception.Create('Internal Error 2faed11a-1b0f-447a-87d1-2e1735ac4ca2');
 end;
 
-{ PayloadTypesHelper }
+{ TPayloadTypeHelper }
+
+function TPayloadTypeHelper.HasTrait(APayloadTrait : TPayloadTrait) : Boolean;
+begin
+  Result := APayloadTrait in Self;
+end;
 
-function PayloadTypesHelper.IsPayloadTypeInSet(APayloadType : PayloadType) : Boolean;
+function TPayloadTypeHelper.ProtocolValue : Byte;
 begin
-  Result := APayloadType in Self;
+  Result := TEPasaComp.GetPayloadTypeProtocolByte(Self);
 end;
 
+
 { TEPasa }
 
 function TEPasa.GetAccount: TNullable<UInt32>;
@@ -256,9 +280,9 @@ begin
   Result := FPayload;
 end;
 
-function TEPasa.GetPayloadTypes: PayloadTypes;
+function TEPasa.GetPayloadType: TPayloadType;
 begin
-  Result := FPayloadTypes;
+  Result := FPayloadType;
 end;
 
 procedure TEPasa.SetAccount(const AValue: TNullable<UInt32>);
@@ -291,29 +315,50 @@ begin
   FPayload := AValue;
 end;
 
-procedure TEPasa.SetPayloadTypes(const AValue: PayloadTypes);
+procedure TEPasa.SetPayloadType(const AValue: TPayloadType);
+begin
+  FPayloadType := AValue;
+end;
+
+function TEPasa.GetIsAddressedByNumber : Boolean;
+begin
+  Result := NOT PayloadType.HasTrait(ptAddressedByName);
+end;
+
+function TEPasa.GetIsAddressedByName : Boolean;
 begin
-  FPayloadTypes := AValue;
+  Result := (NOT IsPayToKey) AND PayloadType.HasTrait(ptAddressedByName);
 end;
 
-function TEPasa.IsPayToKey: Boolean;
+function TEPasa.GetIsPayToKey: Boolean;
 begin
   Result :=
     (AccountName = '@') and
-    (PayloadTypes.IsPayloadTypeInSet(PayloadType.AddressedByName) and
-    PayloadTypes.IsPayloadTypeInSet(PayloadType.Public) and
-    PayloadTypes.IsPayloadTypeInSet(PayloadType.Base58Formatted));
+    (PayloadType.HasTrait(ptAddressedByName) and
+    PayloadType.HasTrait(ptPublic) and
+    PayloadType.HasTrait(ptBase58Formatted));
+end;
+
+function TEPasa.GetIsStandard: Boolean;
+begin
+  Result := (NOT PayloadType.HasTrait(ptAddressedByName)) AND (NOT HasPayload);
+end;
+
+function TEPasa.GetHasPayload: Boolean;
+begin
+  Result := PayloadType.HasTrait(ptPublic) OR PayloadType.HasTrait(ptRecipientKeyEncrypted) OR PayloadType.HasTrait(ptSenderKeyEncrypted);
 end;
 
+
 function TEPasa.GetRawPayloadBytes: TArray<Byte>;
 begin
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then
+  if (PayloadType.HasTrait(ptAsciiFormatted)) then
     Exit(TEncoding.ASCII.GetBytes(Payload));
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+  if (PayloadType.HasTrait(ptBase58Formatted)) then
     Exit(TPascalBase58Encoding.Decode(Payload));
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+  if (PayloadType.HasTrait(ptHexFormatted)) then
     Exit(THexEncoding.Decode(Payload));
 
   raise EPascalCoinException.CreateRes(@SUnknownPayloadEncoding);
@@ -324,7 +369,7 @@ var
   LPayloadContent: String;
 begin
   Result := string.Empty;
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.AddressedByName)) then begin
+  if (PayloadType.HasTrait(ptAddressedByName)) then begin
     Result := Result + TPascal64Encoding.Escape(AccountName);
   end else begin
     Result := Result + Account.Value.ToString();
@@ -333,24 +378,24 @@ begin
     end;
   end;
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then begin
+  if (PayloadType.HasTrait(ptAsciiFormatted)) then begin
     LPayloadContent := String.Format('"%s"', [TPascalAsciiEncoding.Escape(Payload)]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.HexFormatted)) then begin
+  end else if (PayloadType.HasTrait(ptHexFormatted)) then begin
     LPayloadContent := string.Format('0x%s', [Payload]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then begin
+  end else if (PayloadType.HasTrait(ptBase58Formatted)) then begin
     LPayloadContent := string.Format('%s', [Payload]);
   end else begin
     // it is non-deterministic, so payload content is ignored
     LPayloadContent := string.Empty;
   end;
 
-  if (PayloadTypes.IsPayloadTypeInSet(PayloadType.Public)) then begin
+  if (PayloadType.HasTrait(ptPublic)) then begin
     Result := Result + string.Format('[%s]', [LPayloadContent]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.RecipientKeyEncrypted)) then begin
+  end else if (PayloadType.HasTrait(ptRecipientKeyEncrypted)) then begin
     Result := Result + string.Format('(%s)', [LPayloadContent]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.SenderKeyEncrypted)) then begin
+  end else if (PayloadType.HasTrait(ptSenderKeyEncrypted)) then begin
     Result := Result + string.Format('<%s>', [LPayloadContent]);
-  end else if (PayloadTypes.IsPayloadTypeInSet(PayloadType.PasswordEncrypted)) then begin
+  end else if (PayloadType.HasTrait(ptPasswordEncrypted)) then begin
     Result := Result + string.Format('{%s:%s}', [LPayloadContent, TPascalAsciiEncoding.Escape(Password)]);
   end else begin
     // it is non-deterministic, so payload omitted entirely
@@ -389,6 +434,11 @@ begin
 end;
 
 
+class function TEPasa.GetEmptyValue : TEPasa;
+begin
+  Result := EmptyEPasa;
+end;
+
 { TEPasaParser }
 
 class constructor TEPasaParser.CreateRegexEPasaParser;
@@ -465,8 +515,8 @@ begin
     // when multiple enums are OR'ed in C#, they are combined and
     // if any of the enums numeric value is zero, it is excluded.
     // in our case,"PayloadType.NonDeterministic" is always zero so we exclude it from our set.
-    AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.AddressedByName] -
-      [PayloadType.NonDeterministic];
+    AEPasa.PayloadType := AEPasa.PayloadType + [ptAddressedByName] -
+      [ptNonDeterministic];
     AEPasa.AccountName := TPascal64Encoding.Unescape(LAccountName);
     AEPasa.Account := Nil;
     AEPasa.AccountChecksum := Nil;
@@ -504,23 +554,23 @@ begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.Public] -
-          [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptPublic] -
+          [ptNonDeterministic];
       end;
      '(': begin
         if (LPayloadEndChar <> ')') then begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.RecipientKeyEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptRecipientKeyEncrypted] - [ptNonDeterministic];
       end;
     '<': begin
         if (LPayloadEndChar <> '>') then begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes +
-          [PayloadType.SenderKeyEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType +
+          [ptSenderKeyEncrypted] - [ptNonDeterministic];
       end;
 
     '{': begin
@@ -528,13 +578,13 @@ begin
           AErrorCode := EPasaErrorCode.MismatchedPayloadEncoding;
           Exit(False);
         end;
-        AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.PasswordEncrypted] - [PayloadType.NonDeterministic];
+        AEPasa.PayloadType := AEPasa.PayloadType + [ptPasswordEncrypted] - [ptNonDeterministic];
       end
       else raise ENotSupportedException.CreateResFmt(@SUnRecognizedStartCharacter, [LPayloadStartChar]);
   end;
 
   // Password
-  if (AEPasa.PayloadTypes.IsPayloadTypeInSet(PayloadType.PasswordEncrypted)) then begin
+  if (AEPasa.PayloadType.HasTrait(ptPasswordEncrypted)) then begin
     if (LPayloadPasswordDelim = #0) then begin
       AErrorCode := EPasaErrorCode.MissingPassword;
       Exit(False);
@@ -551,25 +601,25 @@ begin
     if (LPayloadContent = #0) then begin
       AEPasa.Payload := string.Empty;
     end else if (LPayloadContent.StartsWith('"')) then begin
-      AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.AsciiFormatted] - [PayloadType.NonDeterministic];
+      AEPasa.PayloadType := AEPasa.PayloadType + [ptAsciiFormatted] - [ptNonDeterministic];
       AEPasa.Payload := TPascalAsciiEncoding.UnEscape(LPayloadContent.Trim(['"']));
     end else if (LPayloadContent.StartsWith('0x')) then begin
-      AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.HexFormatted] - [PayloadType.NonDeterministic];
+      AEPasa.PayloadType := AEPasa.PayloadType + [ptHexFormatted] - [ptNonDeterministic];
       AEPasa.Payload := System.Copy(LPayloadContent, 3, LPayloadContent.Length - 2);
     end else begin
-      AEPasa.PayloadTypes := AEPasa.PayloadTypes + [PayloadType.Base58Formatted] - [PayloadType.NonDeterministic];
+      AEPasa.PayloadType := AEPasa.PayloadType + [ptBase58Formatted] - [ptNonDeterministic];
       AEPasa.Payload := LPayloadContent;
     end;
   end;
 
   // Payload Lengths
-  if (not TEPasaHelper.IsValidPayloadLength(AEPasa.PayloadTypes, AEPasa.Payload)) then begin
+  if (not TEPasaComp.IsValidPayloadLength(AEPasa.PayloadType, AEPasa.Payload)) then begin
     AErrorCode := EPasaErrorCode.PayloadTooLarge;
     Exit(False);
   end;
 
   // Extended Checksum
-  LActualChecksum := TEPasaHelper.ComputeExtendedChecksum(AEPasa.ToString(True));
+  LActualChecksum := TEPasaComp.ComputeExtendedChecksum(AEPasa.ToString(True));
   if (LExtendedChecksumDelim <> #0) then begin
     if (LExtendedChecksum <> LActualChecksum) then begin
       AErrorCode := EPasaErrorCode.BadExtendedChecksum;
@@ -582,16 +632,16 @@ begin
 end;
 
 
-{ TEPasaHelper }
+{ TEPasaComp }
 
-class function TEPasaHelper.ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>;
+class function TEPasaComp.ReadUInt16AsBytesLE(AValue: UInt16): TArray<Byte>;
 begin
   System.SetLength(Result, System.SizeOf(UInt16));
   Result[0] := Byte(AValue);
   Result[1] := Byte(AValue shr 8);
 end;
 
-class function TEPasaHelper.ComputeExtendedChecksum(const AText: String): String;
+class function TEPasaComp.ComputeExtendedChecksum(const AText: String): String;
 var
   LHashInstance: IHashWithKey;
   LChecksum: UInt16;
@@ -602,53 +652,53 @@ begin
   Result := THexEncoding.Encode(ReadUInt16AsBytesLE(LChecksum), True);
 end;
 
-class function TEPasaHelper.IsValidExtendedChecksum(const AText, AChecksum: String): Boolean;
+class function TEPasaComp.IsValidExtendedChecksum(const AText, AChecksum: String): Boolean;
 begin
   Result := ComputeExtendedChecksum(AText) = AChecksum;
 end;
 
-class function TEPasaHelper.IsValidPayloadLength(APayloadType: PayloadTypes; const APayloadContent: String): Boolean;
+class function TEPasaComp.IsValidPayloadLength(APayloadType: TPayloadType; const APayloadContent: String): Boolean;
 begin
   if (string.IsNullOrEmpty(APayloadContent)) then
     Exit(True);
 
-  if (APayloadType.IsPayloadTypeInSet(PayloadType.Public)) then begin
+  if (APayloadType.HasTrait(ptPublic)) then begin
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then
+    if (APayloadType.HasTrait(ptAsciiFormatted)) then
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxPublicAsciiContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxPublicHexContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxPublicBase58ContentLength);
 
     // unknown encoding format
     Result := False;
   end;
 
-  if (APayloadType.IsPayloadTypeInSet(PayloadType.SenderKeyEncrypted) or APayloadType.IsPayloadTypeInSet(PayloadType.RecipientKeyEncrypted)) then begin
+  if (APayloadType.HasTrait(ptSenderKeyEncrypted) or APayloadType.HasTrait(ptRecipientKeyEncrypted)) then begin
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then
+    if (APayloadType.HasTrait(ptAsciiFormatted)) then
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxECIESAsciiContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxECIESHexContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxECIESBase58ContentLength);
     // unknown encoding format
     Result := False;
   end;
 
-  if (APayloadType.IsPayloadTypeInSet(PayloadType.PasswordEncrypted)) then begin
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.AsciiFormatted)) then
+  if (APayloadType.HasTrait(ptPasswordEncrypted)) then begin
+    if (APayloadType.HasTrait(ptAsciiFormatted)) then
       Exit(TPascalAsciiEncoding.UnEscape(APayloadContent).Length <= MaxAESAsciiContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.HexFormatted)) then
+    if (APayloadType.HasTrait(ptHexFormatted)) then
       Exit(APayloadContent.Length <= MaxAESHexContentLength);
 
-    if (APayloadType.IsPayloadTypeInSet(PayloadType.Base58Formatted)) then
+    if (APayloadType.HasTrait(ptBase58Formatted)) then
       Exit(APayloadContent.Length <= MaxAESBase58ContentLength);
 
     // unknown encoding format
@@ -659,10 +709,41 @@ begin
   Result := False;
 end;
 
-class function TEPasaHelper.IsValidPasswordLength(const APasswordValue : String): Boolean;
+class function TEPasaComp.IsValidPasswordLength(const APasswordValue : String): Boolean;
 begin
   // no password length policy established (only client-side concern)
   Result := True;
 end;
 
+class function TEPasaComp.GetPayloadTypeProtocolByte(const APayloadType : TPayloadType) : Byte;
+var
+ LPayloadType : TPayloadTrait;
+begin
+  Result := 0; // NonDeterministic by default
+  for LPayloadType := Low(TPayloadTrait) to High(TPayloadTrait) do
+    if APayloadType.HasTrait(LPayloadType) then
+      Result := Result OR LPayloadType.ProtocolValue;
+end;
+
+class function TEPasaComp.GetPayloadTypeFromProtocolByte(AByte: Byte) : TPayloadType;
+var
+ LPayloadType : TPayloadTrait;
+ LPayloadTypeByte : byte;
+begin
+  if AByte = 0 then
+    Exit([ptNonDeterministic]);
+
+  Result := [];
+  for LPayloadType := Low(TPayloadTrait) to High(TPayloadTrait) do begin
+    LPayloadTypeByte := LPayloadType.ProtocolValue;
+    if (AByte AND LPayloadTypeByte) = LPayloadTypeByte then
+      Result := Result + [LPayloadType];
+  end;
+end;
+
+initialization
+
+EmptyEPasa := Default(TEPasa);
+
+
 end.

+ 1 - 1
src/core/UPCRPCSend.pas

@@ -59,7 +59,7 @@ Var LOpPayload : TOperationPayload;
 Begin
   Result := Nil;
   if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(ASenderAccounKey,LPrivateKey,AErrorNum,AErrorDesc) then Exit(Nil);
-  if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(ARawPayload,APayload_method,AEncodePwd,ASenderAccounKey,ATargetAccountKey,LOpPayload,AErrorNum,AErrorDesc) then Exit(Nil);
+  if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(ARawPayload, APayload_method,AEncodePwd,ASenderAccounKey,ATargetAccountKey,LOpPayload,AErrorNum,AErrorDesc) then Exit(Nil);
   Result := TOpTransaction.CreateTransaction(ACurrent_protocol, ASender,ASender_last_n_operation+1, ATarget, LPrivateKey, AAmount, AFee, LOpPayload);
   if Not Result.HasValidSignature then begin
     FreeAndNil(Result);

+ 41 - 19
src/core/URPC.pas

@@ -27,7 +27,7 @@ interface
 Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
   UNetProtocol, UOpTransaction, UWallet, UTime, UPCEncryption, UTxMultiOperation,
   UJSONFunctions, classes, blcksock, synsock,
-  IniFiles, Variants, math, UBaseTypes,
+  IniFiles, Variants, math, UBaseTypes, UEPasa,
   {$IFDEF Use_OpenSSL}
   UOpenSSL,
   {$ENDIF}
@@ -72,7 +72,8 @@ Type
     class Function HexaStringToOperationsHashTree(Const AHexaStringOperationsHashTree : String; ACurrentProtocol : Word; out AOperationsHashTree : TOperationsHashTree; var AErrors : String) : Boolean;
     class Function CapturePubKey(const AInputParams : TPCJSONObject; const APrefix : String; var APubKey : TAccountKey; var AErrortxt : String) : Boolean;
     class function CheckAndGetEncodedRAWPayload(Const ARawPayload : TRawBytes; Const APayload_method, AEncodePwdForAES : String; const ASenderAccounKey, ATargetAccountKey : TAccountKey; out AOperationPayload : TOperationPayload; Var AErrorNum : Integer; Var AErrorDesc : String) : Boolean;
-    class Function CaptureAccountNumber(const AInputParams : TPCJSONObject; const AParamName : String; const ABank : TPCBank; var AAccountNumber : Cardinal; var AErrorParam : String) : Boolean;
+    class Function CaptureAccountNumber(const AInputParams : TPCJSONObject; const AParamName : String; const ABank : TPCBank; out AResolvedAccount: Cardinal; var AErrorParam : String) : Boolean;
+    class Function CaptureEPASA(const AInputParams : TPCJSONObject; const AParamName : String; const ABank : TPCBank; out AEPasa: TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : Boolean; var AErrorParam : String) : Boolean;
   end;
 
   TRPCServerThread = Class;
@@ -154,7 +155,8 @@ Type
 
 implementation
 
-Uses  {$IFNDEF FPC}windows,{$ENDIF}
+Uses
+  {$IFNDEF FPC}windows,{$ENDIF}
   SysUtils, Synautil,
   UPCRPCSend,
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
@@ -347,25 +349,41 @@ Begin
   end;
 end;
 
-class function TPascalCoinJSONComp.CaptureAccountNumber(const AInputParams : TPCJSONObject;
-  const AParamName: String; const ABank : TPCBank; var AAccountNumber: Cardinal;
-  var AErrorParam: String): Boolean;
+class Function TPascalCoinJSONComp.CaptureAccountNumber(const AInputParams : TPCJSONObject; const AParamName : String; const ABank : TPCBank; out AResolvedAccount: Cardinal; var AErrorParam : String) : Boolean;
+var
+  LEPasa : TEPasa;
+  LKey : TAccountKey;
+  LPurchase : Boolean;
+  LParamValue : String;
+begin
+  LParamValue := AInputParams.AsString(AParamName,'');
+  Result := CaptureEPASA(AInputParams, AParamName, ABank, LEPasa, AResolvedAccount, LKey, LPurchase, AErrorParam);
+  if Result AND (NOT LEPasa.IsStandard) then begin
+      AErrorParam := Format('"%s" is not valid Account Number for Param "%s"',[LParamValue,AParamName]);
+      Exit(False);
+  end;
+end;
+
+class function TPascalCoinJSONComp.CaptureEPASA(const AInputParams : TPCJSONObject; const AParamName : String; const ABank : TPCBank; out AEPasa: TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : Boolean; var AErrorParam : String): Boolean;
 var LParamValue : String;
 Begin
+  if not Assigned(ABank) then raise EArgumentNilException.Create('ABank');
   LParamValue := AInputParams.AsString(AParamName,'');
   if Length(LParamValue)>0 then begin
-    Result := TAccountComp.AccountTxtNumberToAccountNumber(LParamValue,AAccountNumber);
-    if Not Result then begin
-      AErrorParam := Format('"%s" is no valid Account number for Param "%s"',[LParamValue,AParamName]);
-    end else if Assigned(ABank) then begin
-      if (AAccountNumber<0) or (AAccountNumber>=ABank.AccountsCount) then begin
-        Result := False;
-        AErrorParam := Format('Account %d does not exist in safebox (param "%s")',[AAccountNumber,AParamName]);
-      end;
-    end;
+    if Not TEPasa.TryParse(LParamValue, AEPasa) then begin
+      AEPasa := TEPasa.Empty;
+      AResolvedAccount := CT_AccountNo_NUL;
+      AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+      AErrorParam := Format('"%s" is not valid Account EPASA for Param "%s"',[LParamValue,AParamName]);
+      Exit(False);
+    end;
+    Result := ABank.SafeBox.TryResolveAccountByEPASA(AEPasa, AResolvedAccount, AResolvedKey, ARequiresPurchase, AErrorParam);
   end else begin
-    Result := False;
+    AEPasa := TEPasa.Empty;
+    AResolvedAccount := CT_AccountNo_NUL;
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
     AErrorParam := Format('Param "%s" not provided or null',[AParamName]);
+    Exit(False);
   end;
 end;
 
@@ -405,16 +423,20 @@ begin
 end;
 
 class function TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
-  const ARawPayload: TRawBytes; const APayload_method, AEncodePwdForAES: String;
+  const ARawPayload: TRawBytes;
+//  const APayloadType : TPayloadType;
+  const APayload_method, AEncodePwdForAES: String;
   const ASenderAccounKey, ATargetAccountKey: TAccountKey;
-  out AOperationPayload : TOperationPayload; var AErrorNum: Integer;
+  out AOperationPayload : TOperationPayload;
+  var AErrorNum: Integer;
   var AErrorDesc: String): Boolean;
 begin
   AOperationPayload := CT_TOperationPayload_NUL;
-  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+   // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   // TODO:
   // Needs to assign AOperationPayload.payload_type based on PIP-0027
   // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+  //AOperationPayload.payload_type := APayloadType.ProtocolValue;
   if (Length(ARawPayload)>0) then begin
     if (APayload_method='none') then begin
       AOperationPayload.payload_raw:=ARawPayload;

+ 9 - 0
src/libraries/sphere10/UCommon.pas

@@ -43,6 +43,15 @@ const
   MaxSeconds = MaxMilliseconds div 60;
   MinSeconds = MinMilliseconds div 60;
 
+  BYTE_BIT_0 = byte(1);
+  BYTE_BIT_1 = byte(2);
+  BYTE_BIT_2 = byte(4);
+  BYTE_BIT_3 = byte(8);
+  BYTE_BIT_4 = byte(16);
+  BYTE_BIT_5 = byte(32);
+  BYTE_BIT_6 = byte(64);
+  BYTE_BIT_7 = byte(128);
+
 { GLOBAL HELPER FUNCTIONS }