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
 uses
   Classes, SysUtils, UConst, UCrypto, SyncObjs, UThread, UBaseTypes,
   Classes, SysUtils, UConst, UCrypto, SyncObjs, UThread, UBaseTypes,
-  UPCOrderedLists, UPCDataTypes, UPCSafeBoxRootHash,
+  UPCOrderedLists, UPCDataTypes, UPCSafeBoxRootHash, UEPasa,
   UPCHardcodedRandomHashTable, UJSONFunctions,
   UPCHardcodedRandomHashTable, UJSONFunctions,
   {$IFDEF USE_ABSTRACTMEM}
   {$IFDEF USE_ABSTRACTMEM}
   UPCAbstractMem, UPCAbstractMemAccountKeys,
   UPCAbstractMem, UPCAbstractMemAccountKeys,
@@ -295,7 +295,9 @@ Type
     Function FindAccountByName(const aName : String) : Integer; overload;
     Function FindAccountByName(const aName : String) : Integer; overload;
     Function FindAccountByName(const aName : TRawBytes) : Integer; overload;
     Function FindAccountByName(const aName : TRawBytes) : Integer; overload;
     Function FindAccountsStartingByName(const AStartName : TRawBytes; const ARawList : TOrderedRawList; const AMax : Integer = 0) : Integer;
     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;
     Procedure Clear;
     Function Account(account_number : Cardinal) : TAccount;
     Function Account(account_number : Cardinal) : TAccount;
     Function GetBlock(block_number : Cardinal) : TBlockAccount;
     Function GetBlock(block_number : Cardinal) : TBlockAccount;
@@ -4700,6 +4702,72 @@ begin
   end;
   end;
 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);
 procedure TPCSafeBox.SearchBlockWhenOnSeparatedChain(blockNumber: Cardinal; out blockAccount: TBlockAccount);
   Function WasUpdatedBeforeOrigin : Boolean;
   Function WasUpdatedBeforeOrigin : Boolean;
   var j, maxUB : Integer;
   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};
   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
   // App Params
   CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';
   CT_PARAM_GridAccountsStream = 'GridAccountsStreamV2';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';
   CT_PARAM_GridAccountsPos = 'GridAccountsPos';

+ 54 - 8
src/core/UEPasa.pas

@@ -27,7 +27,6 @@ uses
   SysUtils,
   SysUtils,
   TypInfo,
   TypInfo,
   uregexpr,
   uregexpr,
-  UAccounts,
   UCommon,
   UCommon,
   UCrypto,
   UCrypto,
   UEncoding;
   UEncoding;
@@ -78,6 +77,7 @@ type
 
 
   { TEPasa }
   { TEPasa }
 
 
+
   TEPasa = record
   TEPasa = record
     strict private
     strict private
       var
       var
@@ -99,13 +99,13 @@ type
       procedure SetPassword(const AValue: String); inline;
       procedure SetPassword(const AValue: String); inline;
       function GetPayload: String; inline;
       function GetPayload: String; inline;
       procedure SetPayload(const AValue: 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
     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 Account: TNullable<UInt32> read GetAccount write SetAccount;
       property AccountChecksum: TNullable<UInt32> read GetAccountChecksum write SetAccountChecksum;
       property AccountChecksum: TNullable<UInt32> read GetAccountChecksum write SetAccountChecksum;
       property AccountName: String read GetAccountName write SetAccountName;
       property AccountName: String read GetAccountName write SetAccountName;
@@ -113,6 +113,16 @@ type
       property Payload: String read GetPayload write SetPayload;
       property Payload: String read GetPayload write SetPayload;
       property Password: String read GetPassword write SetPassword;
       property Password: String read GetPassword write SetPassword;
       property ExtendedChecksum: String read GetExtendedChecksum write SetExtendedChecksum;
       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 TryParse(const AEPasaText: String; out AEPasa: TEPasa) : Boolean; static;
       class function Parse(const AEPasaText: String): TEPasa; static;
       class function Parse(const AEPasaText: String): TEPasa; static;
@@ -121,6 +131,8 @@ type
 
 
   end;
   end;
 
 
+
+
   { TEPasaParser }
   { TEPasaParser }
 
 
   TEPasaParser = class
   TEPasaParser = class
@@ -202,6 +214,9 @@ uses
   HlpConverters,
   HlpConverters,
   UMemory;
   UMemory;
 
 
+var
+  EmptyEPasa : TEPasa;
+
 { TPayloadTraitHelper }
 { TPayloadTraitHelper }
 
 
 function TPayloadTraitHelper.ProtocolValue: Byte;
 function TPayloadTraitHelper.ProtocolValue: Byte;
@@ -305,7 +320,17 @@ begin
   FPayloadType := AValue;
   FPayloadType := AValue;
 end;
 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
 begin
   Result :=
   Result :=
     (AccountName = '@') and
     (AccountName = '@') and
@@ -314,6 +339,17 @@ begin
     PayloadType.HasTrait(ptBase58Formatted));
     PayloadType.HasTrait(ptBase58Formatted));
 end;
 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>;
 function TEPasa.GetRawPayloadBytes: TArray<Byte>;
 begin
 begin
   if (PayloadType.HasTrait(ptAsciiFormatted)) then
   if (PayloadType.HasTrait(ptAsciiFormatted)) then
@@ -398,6 +434,11 @@ begin
 end;
 end;
 
 
 
 
+class function TEPasa.GetEmptyValue : TEPasa;
+begin
+  Result := EmptyEPasa;
+end;
+
 { TEPasaParser }
 { TEPasaParser }
 
 
 class constructor TEPasaParser.CreateRegexEPasaParser;
 class constructor TEPasaParser.CreateRegexEPasaParser;
@@ -700,4 +741,9 @@ begin
   end;
   end;
 end;
 end;
 
 
+initialization
+
+EmptyEPasa := Default(TEPasa);
+
+
 end.
 end.

+ 1 - 1
src/core/UPCRPCSend.pas

@@ -59,7 +59,7 @@ Var LOpPayload : TOperationPayload;
 Begin
 Begin
   Result := Nil;
   Result := Nil;
   if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(ASenderAccounKey,LPrivateKey,AErrorNum,AErrorDesc) then Exit(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);
   Result := TOpTransaction.CreateTransaction(ACurrent_protocol, ASender,ASender_last_n_operation+1, ATarget, LPrivateKey, AAmount, AFee, LOpPayload);
   if Not Result.HasValidSignature then begin
   if Not Result.HasValidSignature then begin
     FreeAndNil(Result);
     FreeAndNil(Result);

+ 41 - 19
src/core/URPC.pas

@@ -27,7 +27,7 @@ interface
 Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
 Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
   UNetProtocol, UOpTransaction, UWallet, UTime, UPCEncryption, UTxMultiOperation,
   UNetProtocol, UOpTransaction, UWallet, UTime, UPCEncryption, UTxMultiOperation,
   UJSONFunctions, classes, blcksock, synsock,
   UJSONFunctions, classes, blcksock, synsock,
-  IniFiles, Variants, math, UBaseTypes,
+  IniFiles, Variants, math, UBaseTypes, UEPasa,
   {$IFDEF Use_OpenSSL}
   {$IFDEF Use_OpenSSL}
   UOpenSSL,
   UOpenSSL,
   {$ENDIF}
   {$ENDIF}
@@ -72,7 +72,8 @@ Type
     class Function HexaStringToOperationsHashTree(Const AHexaStringOperationsHashTree : String; ACurrentProtocol : Word; out AOperationsHashTree : TOperationsHashTree; var AErrors : String) : Boolean;
     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 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 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;
   end;
 
 
   TRPCServerThread = Class;
   TRPCServerThread = Class;
@@ -154,7 +155,8 @@ Type
 
 
 implementation
 implementation
 
 
-Uses  {$IFNDEF FPC}windows,{$ENDIF}
+Uses
+  {$IFNDEF FPC}windows,{$ENDIF}
   SysUtils, Synautil,
   SysUtils, Synautil,
   UPCRPCSend,
   UPCRPCSend,
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
@@ -347,25 +349,41 @@ Begin
   end;
   end;
 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;
 var LParamValue : String;
 Begin
 Begin
+  if not Assigned(ABank) then raise EArgumentNilException.Create('ABank');
   LParamValue := AInputParams.AsString(AParamName,'');
   LParamValue := AInputParams.AsString(AParamName,'');
   if Length(LParamValue)>0 then begin
   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
   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]);
     AErrorParam := Format('Param "%s" not provided or null',[AParamName]);
+    Exit(False);
   end;
   end;
 end;
 end;
 
 
@@ -405,16 +423,20 @@ begin
 end;
 end;
 
 
 class function TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
 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;
   const ASenderAccounKey, ATargetAccountKey: TAccountKey;
-  out AOperationPayload : TOperationPayload; var AErrorNum: Integer;
+  out AOperationPayload : TOperationPayload;
+  var AErrorNum: Integer;
   var AErrorDesc: String): Boolean;
   var AErrorDesc: String): Boolean;
 begin
 begin
   AOperationPayload := CT_TOperationPayload_NUL;
   AOperationPayload := CT_TOperationPayload_NUL;
-  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+   // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   // TODO:
   // TODO:
   // Needs to assign AOperationPayload.payload_type based on PIP-0027
   // Needs to assign AOperationPayload.payload_type based on PIP-0027
   // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
   // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+  //AOperationPayload.payload_type := APayloadType.ProtocolValue;
   if (Length(ARawPayload)>0) then begin
   if (Length(ARawPayload)>0) then begin
     if (APayload_method='none') then begin
     if (APayload_method='none') then begin
       AOperationPayload.payload_raw:=ARawPayload;
       AOperationPayload.payload_raw:=ARawPayload;