Browse Source

PIP-0041: PayToKey partial implementation

Herman Schoenfeld 4 years ago
parent
commit
3d3cd534bf
5 changed files with 170 additions and 30 deletions
  1. 70 2
      src/core/UAccounts.pas
  2. 4 0
      src/core/UConst.pas
  3. 54 8
      src/core/UEPasa.pas
  4. 1 1
      src/core/UPCRPCSend.pas
  5. 41 19
      src/core/URPC.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';

+ 54 - 8
src/core/UEPasa.pas

@@ -27,7 +27,6 @@ uses
   SysUtils,
   TypInfo,
   uregexpr,
-  UAccounts,
   UCommon,
   UCrypto,
   UEncoding;
@@ -78,6 +77,7 @@ type
 
   { TEPasa }
 
+
   TEPasa = record
     strict private
       var
@@ -99,13 +99,13 @@ type
       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;
@@ -113,6 +113,16 @@ type
       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
@@ -202,6 +214,9 @@ uses
   HlpConverters,
   UMemory;
 
+var
+  EmptyEPasa : TEPasa;
+
 { TPayloadTraitHelper }
 
 function TPayloadTraitHelper.ProtocolValue: Byte;
@@ -305,7 +320,17 @@ begin
   FPayloadType := AValue;
 end;
 
-function TEPasa.IsPayToKey: Boolean;
+function TEPasa.GetIsAddressedByNumber : Boolean;
+begin
+  Result := NOT PayloadType.HasTrait(ptAddressedByName);
+end;
+
+function TEPasa.GetIsAddressedByName : Boolean;
+begin
+  Result := (NOT IsPayToKey) AND PayloadType.HasTrait(ptAddressedByName);
+end;
+
+function TEPasa.GetIsPayToKey: Boolean;
 begin
   Result :=
     (AccountName = '@') and
@@ -314,6 +339,17 @@ begin
     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 (PayloadType.HasTrait(ptAsciiFormatted)) then
@@ -398,6 +434,11 @@ begin
 end;
 
 
+class function TEPasa.GetEmptyValue : TEPasa;
+begin
+  Result := EmptyEPasa;
+end;
+
 { TEPasaParser }
 
 class constructor TEPasaParser.CreateRegexEPasaParser;
@@ -700,4 +741,9 @@ begin
   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;