Browse Source

Merge pull request #213 from PascalCoin/master

Integrate 5.4.Beta4
Albert Molina 4 years ago
parent
commit
794d997258

+ 2 - 1
CHANGELOG.md

@@ -1,11 +1,12 @@
 # Changelog
 # Changelog
 
 
 ## Build 5.4 - (PENDING RELEASE)
 ## Build 5.4 - (PENDING RELEASE)
-- CURRENT 5.4.Beta3
+- CURRENT 5.4.Beta4
 - Added usage of AbstractMem library to allow build a PascalCoin version using virtual memory and efficient caching mechanism
 - Added usage of AbstractMem library to allow build a PascalCoin version using virtual memory and efficient caching mechanism
   - Use AbstractMem library v1.2
   - Use AbstractMem library v1.2
   - Must activate {$DEFINE USE_ABSTRACTMEM} at config.inc file (Enabled by default)
   - Must activate {$DEFINE USE_ABSTRACTMEM} at config.inc file (Enabled by default)
 - Added "Ask for Account (PASA)" feature on GUI wallet
 - Added "Ask for Account (PASA)" feature on GUI wallet
+- Implementation of PIP-0027 (E-PASA: Infinite Address-Space Layer-2) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0027.md  
 - Changes to `pascalcoin_daemon.ini` file:
 - Changes to `pascalcoin_daemon.ini` file:
   - Added "DATAFOLDER" configuration option at pascalcoin_daemon.ini file (daemon/service) in order to allow customize data folder
   - Added "DATAFOLDER" configuration option at pascalcoin_daemon.ini file (daemon/service) in order to allow customize data folder
   - Added "ABSTRACTMEM_MAX_CACHE_MB" to customize Maximum megabytes in memory as a cache
   - Added "ABSTRACTMEM_MAX_CACHE_MB" to customize Maximum megabytes in memory as a cache

+ 1 - 1
PIP/PIP-0027.md

@@ -388,7 +388,7 @@ For Layer-2 applications the ability for a receiver to auto-decode the E-PASA vi
 
 
 The following regex parses an e-pasa:
 The following regex parses an e-pasa:
 ```
 ```
-((?<AccountNumber>(0|[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|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|\\"|`|\||\\<|\\>|,|\.|\?|/|~)*))(?:(?<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}))?
+((?<AccountNumber>(0|[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|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|\\"|`|\||\\<|\\>|,|\.|\?|/|~)*))(?:(?<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
 After matching with the above regex, the named groups need to be extracted and validated as the below snippet shows

+ 101 - 68
src/core/UAccounts.pas

@@ -118,6 +118,8 @@ Type
     Class procedure SaveTOperationBlockToStream(const stream : TStream; const operationBlock:TOperationBlock);
     Class procedure SaveTOperationBlockToStream(const stream : TStream; const operationBlock:TOperationBlock);
     Class Function LoadTOperationBlockFromStream(const stream : TStream; var operationBlock:TOperationBlock) : Boolean;
     Class Function LoadTOperationBlockFromStream(const stream : TStream; var operationBlock:TOperationBlock) : Boolean;
     Class Function AccountToTxt(const Account : TAccount) : String;
     Class Function AccountToTxt(const Account : TAccount) : String;
+    Class Function DecodeEPASAPartial(AAccount : Cardinal; const APayload : TBytes; APayloadType : Byte; ADefault : TEPasa) : TEPasa;
+    Class Function TryDecodeEPASAPartial(AAccount : Cardinal; const APayload : TBytes; APayloadType : Byte; out AEPasa : TEPasa) : Boolean;
   End;
   End;
 
 
   TPCSafeBox = Class;
   TPCSafeBox = Class;
@@ -295,8 +297,6 @@ 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;
@@ -1900,6 +1900,105 @@ begin
   end;
   end;
 end;
 end;
 
 
+class Function TAccountComp.DecodeEPASAPartial(AAccount : Cardinal; const APayload : TBytes; APayloadType : Byte; ADefault : TEPasa) : TEPasa;
+begin
+  if NOT TryDecodeEPASAPartial(AAccount, APayload, APayloadType, Result) then
+    Result := ADefault;
+end;
+
+class Function TAccountComp.TryDecodeEPASAPartial(AAccount : Cardinal; const APayload : TBytes; APayloadType : Byte; out AEPasa : TEPasa) : Boolean;
+var
+  LPayloadType : TPayloadType;
+  LUnencryptedPayloadBytes : TBytes;
+  LEPasaStr : String;
+  LPublic, LRecipientKeyEncrypted, LSenderKeyEncrypted,
+  LPasswordEncrypted, LAsciiFormatted, LHexFormatted, LBase58Formatted, LAddressedByName : Boolean;
+begin
+  LPayloadType := TEPasaComp.FromProtocolValue(APayloadType);
+  LPublic := LPayloadType.HasTrait(ptPublic);
+  LRecipientKeyEncrypted := LPayloadType.HasTrait(ptRecipientKeyEncrypted);
+  LSenderKeyEncrypted := LPayloadType.HasTrait(ptSenderKeyEncrypted);
+  LPasswordEncrypted := LPayloadType.HasTrait(ptPasswordEncrypted);
+  LAsciiFormatted := LPayloadType.HasTrait(ptAsciiFormatted);
+  LHexFormatted := LPayloadType.HasTrait(ptHexFormatted);
+  LBase58Formatted := LPayloadType.HasTrait(ptBase58Formatted);
+  LAddressedByName := LPayloadType.HasTrait(ptAddressedByName);
+
+  if LPayloadType.HasTrait(ptAddressedByName) then begin
+     (*LEPasaStr := Self.GetMempoolAccount(AAccount).name.ToPrintable;
+     if LEPasaStr.IsEmpty then
+       Exit(False); *)
+     // Note: since doing a name resolution for every encountered addressed-by-name EPASA,
+     // would overwhelm the SafeBox with lookups, names are not resolved.
+     // So in V5 addressed-by-name EPASA's auto-resolved as addressed-by-number
+     LEPasaStr := TAccountComp.AccountNumberToAccountTxtNumber(AAccount);
+  end else LEPasaStr := TAccountComp.AccountNumberToAccountTxtNumber(AAccount);
+
+
+  // payload opening char
+  if LPublic then begin
+     LEPasaStr := LEPasaStr + '[';
+  end;
+
+  if LSenderKeyEncrypted then begin
+     LEPasaStr := LEPasaStr + '<';
+  end;
+
+  if LRecipientKeyEncrypted then begin
+     LEPasaStr := LEPasaStr + '(';
+  end;
+
+  if LPasswordEncrypted then begin
+     LEPasaStr := LEPasaStr + '{';
+  end;
+
+  // payload data
+  if LPublic OR LSenderKeyEncrypted OR LRecipientKeyEncrypted OR LPasswordEncrypted then begin
+
+    if LPublic then
+      LUnencryptedPayloadBytes := APayload
+    else if LSenderKeyEncrypted then
+      Exit(False) // Todo: Partial decoding does not decrypt due to performance penalty if always decrypting. This needs to be implemented as a IFuture<TBytes> value with lazy evaluation.
+    else if LRecipientKeyEncrypted then
+      Exit(False) // Todo: Partial decoding does not decrypt due to performance penalty if always decrypting. This needs to be implemented as a IFuture<TBytes> value with lazy evaluation.
+    else if LPasswordEncrypted then
+      Exit(False) // Todo: Partial decoding does not decrypt due to performance penalty if always decrypting. This needs to be implemented as a IFuture<TBytes> value with lazy evaluation..
+    else raise Exception.Create('Internal Error a0805389-df1a-4b40-b12e-d22327a3d049');
+
+    // decrypt data
+     if LAsciiFormatted then
+       LEPasaStr := LEPasaStr + '"' + TEncoding.ASCII.GetString(LUnencryptedPayloadBytes) + '"'
+     else if LHexFormatted then
+       LEPasaStr := LEPasaStr + '0x' + THexEncoding.Encode(LUnencryptedPayloadBytes)
+     else if LBase58Formatted then
+       LEPasaStr := LEPasaStr + TPascalBase58Encoding.Encode(LUnencryptedPayloadBytes)
+     else raise Exception.Create('Internal Error 67a61d3e-eef2-40a9-8d92-45570f400c1e');
+  end;
+
+  // payload closing char
+  if LPublic then begin
+     LEPasaStr := LEPasaStr + ']';
+  end;
+
+  if LSenderKeyEncrypted then begin
+     LEPasaStr := LEPasaStr + '>';
+  end;
+
+  if LRecipientKeyEncrypted then begin
+     LEPasaStr := LEPasaStr + ')';
+  end;
+
+  if LPasswordEncrypted then begin
+     LEPasaStr := LEPasaStr + '}';
+  end;
+
+  // Parse as EPASA
+  if NOT TEPasa.TryParse(LEPasaStr, AEPasa) then
+    Exit(False);
+
+  Result := true;
+end;
+
 {$IFNDEF VER210}
 {$IFNDEF VER210}
 {$DEFINE DELPHIXE}
 {$DEFINE DELPHIXE}
 {$ENDIF}
 {$ENDIF}
@@ -4702,72 +4801,6 @@ 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 - 2
src/core/UBlockChain.pas

@@ -28,7 +28,7 @@ uses
   Classes, UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
   Classes, UCrypto, UAccounts, ULog, UThread, SyncObjs, UBaseTypes, SysUtils,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFDEF USE_ABSTRACTMEM}UPCAbstractMem,{$ENDIF}
   {$IFDEF USE_ABSTRACTMEM}UPCAbstractMem,{$ENDIF}
-  UPCDataTypes, UChunk;
+  UPCDataTypes, UChunk, UEPasa;
 
 
 {
 {
 
 
@@ -139,6 +139,7 @@ Type
 
 
   TMultiOpSender = Record
   TMultiOpSender = Record
     Account : Cardinal;
     Account : Cardinal;
+    AccountEPASA : TEPasa;
     Amount : Int64;
     Amount : Int64;
     N_Operation : Cardinal;
     N_Operation : Cardinal;
     OpData : TMultiOpData; // Filled only when Operation is TOpData type
     OpData : TMultiOpData; // Filled only when Operation is TOpData type
@@ -148,6 +149,7 @@ Type
   TMultiOpSenders = Array of TMultiOpSender;
   TMultiOpSenders = Array of TMultiOpSender;
   TMultiOpReceiver = Record
   TMultiOpReceiver = Record
     Account : Cardinal;
     Account : Cardinal;
+    AccountEPASA : TEPasa;
     Amount : Int64;
     Amount : Int64;
     Payload : TOperationPayload;
     Payload : TOperationPayload;
   end;
   end;
@@ -575,7 +577,7 @@ Type
 Const
 Const
   CT_TOperationPayload_NUL : TOperationPayload = (payload_type:0;payload_raw:Nil);
   CT_TOperationPayload_NUL : TOperationPayload = (payload_type:0;payload_raw:Nil);
   CT_TOperationResume_NUL : TOperationResume = (valid:false;Block:0;NOpInsideBlock:-1;OpType:0;OpSubtype:0;time:0;AffectedAccount:0;SignerAccount:-1;n_operation:0;DestAccount:-1;SellerAccount:-1;newKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:(payload_type:0;payload_raw:nil);PrintablePayload:'';OperationHash:Nil;OperationHash_OLD:Nil;errors:'';isMultiOperation:False;Senders:Nil;Receivers:Nil;changers:Nil);
   CT_TOperationResume_NUL : TOperationResume = (valid:false;Block:0;NOpInsideBlock:-1;OpType:0;OpSubtype:0;time:0;AffectedAccount:0;SignerAccount:-1;n_operation:0;DestAccount:-1;SellerAccount:-1;newKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);OperationTxt:'';Amount:0;Fee:0;Balance:0;OriginalPayload:(payload_type:0;payload_raw:nil);PrintablePayload:'';OperationHash:Nil;OperationHash_OLD:Nil;errors:'';isMultiOperation:False;Senders:Nil;Receivers:Nil;changers:Nil);
-  CT_TMultiOpSender_NUL : TMultiOpSender =  (Account:0;Amount:0;N_Operation:0;Payload:(payload_type:0;payload_raw:Nil);Signature:(r:Nil;s:Nil));
+  CT_TMultiOpSender_NUL : TMultiOpSender =  (Account:0;(*AccountEPASA:TEPasa.Empty;*)Amount:0;N_Operation:0;Payload:(payload_type:0;payload_raw:Nil);Signature:(r:Nil;s:Nil));
   CT_TMultiOpReceiver_NUL : TMultiOpReceiver = (Account:0;Amount:0;Payload:(payload_type:0;payload_raw:Nil));
   CT_TMultiOpReceiver_NUL : TMultiOpReceiver = (Account:0;Amount:0;Payload:(payload_type:0;payload_raw:Nil));
   CT_TMultiOpChangeInfo_NUL : TMultiOpChangeInfo = (Account:0;N_Operation:0;Changes_type:[];New_Accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);New_Name:Nil;New_Type:0;New_Data:Nil;Seller_Account:-1;Account_Price:-1;Locked_Until_Block:0;
   CT_TMultiOpChangeInfo_NUL : TMultiOpChangeInfo = (Account:0;N_Operation:0;Changes_type:[];New_Accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);New_Name:Nil;New_Type:0;New_Data:Nil;Seller_Account:-1;Account_Price:-1;Locked_Until_Block:0;
     Hashed_secret:Nil;
     Hashed_secret:Nil;

+ 3 - 1
src/core/UConst.pas

@@ -198,7 +198,7 @@ Const
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Receiver              = 104;
   CT_OpSubtype_Data_Receiver              = 104;
 
 
-  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'5.4.Beta3'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.4.Beta3'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'5.4.Beta4'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.4.Beta4'{$ELSE}{$ENDIF}{$ENDIF};
 
 
   CT_Discover_IPs = {$IFDEF PRODUCTION}'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.dynamic-dns.net;pascalcoin2.dynamic-dns.net;pascalcoin1.dns1.us;pascalcoin2.dns1.us;pascalcoin1.dns2.us;pascalcoin2.dns2.us'
   CT_Discover_IPs = {$IFDEF PRODUCTION}'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.dynamic-dns.net;pascalcoin2.dynamic-dns.net;pascalcoin1.dns1.us;pascalcoin2.dns1.us;pascalcoin1.dns2.us;pascalcoin2.dns2.us'
                     {$ELSE}'pascaltestnet1.dynamic-dns.net;pascaltestnet2.dynamic-dns.net;pascaltestnet1.dns1.us;pascaltestnet2.dns1.us'{$ENDIF};
                     {$ELSE}'pascaltestnet1.dynamic-dns.net;pascaltestnet2.dynamic-dns.net;pascaltestnet1.dns1.us;pascaltestnet2.dns1.us'{$ENDIF};
@@ -217,6 +217,8 @@ Const
   CT_MOLINA  = 1;
   CT_MOLINA  = 1;
   CT_MOLINA_DECIMAL = {$IFDEF FPC}Real(CT_MOLINA/1000.0);{$ELSE}0.0001;{$ENDIF}
   CT_MOLINA_DECIMAL = {$IFDEF FPC}Real(CT_MOLINA/1000.0);{$ELSE}0.0001;{$ENDIF}
 
 
+  CT_DEFAULT_PAY_TO_KEY_MAX_MOLINAS = 5000;
+
   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
   // Represents a non-existent account number

+ 74 - 39
src/core/UEPasa.pas

@@ -46,7 +46,7 @@ type
 
 
   TPayloadTrait = (
   TPayloadTrait = (
     ptNonDeterministic = 0,      // Payload encryption and encoding method not specified.
     ptNonDeterministic = 0,      // Payload encryption and encoding method not specified.
-    ptPublic = 1,               // Unencrypted, public payload.
+    ptPublic = 1,                // Unencrypted, public payload.
     ptRecipientKeyEncrypted = 2, // ECIES encrypted using recipient accounts public key.
     ptRecipientKeyEncrypted = 2, // ECIES encrypted using recipient accounts public key.
     ptSenderKeyEncrypted = 3,    // ECIES encrypted using sender accounts public key.
     ptSenderKeyEncrypted = 3,    // ECIES encrypted using sender accounts public key.
     ptPasswordEncrypted = 4,     // AES encrypted using pwd param
     ptPasswordEncrypted = 4,     // AES encrypted using pwd param
@@ -60,7 +60,7 @@ type
 
 
   TPayloadTraitHelper = record helper for TPayloadTrait
   TPayloadTraitHelper = record helper for TPayloadTrait
   public
   public
-    function ProtocolValue: byte;
+    function ToProtocolValue: byte;
   end;
   end;
 
 
   { TPayloadType }
   { TPayloadType }
@@ -72,18 +72,17 @@ type
   TPayloadTypeHelper = record helper for TPayloadType
   TPayloadTypeHelper = record helper for TPayloadType
   public
   public
     function HasTrait(APayloadTrait: TPayloadTrait): Boolean; inline;
     function HasTrait(APayloadTrait: TPayloadTrait): Boolean; inline;
-    function ProtocolValue : byte;
+    function ToProtocolValue : byte;
   end;
   end;
 
 
   { TEPasa }
   { TEPasa }
 
 
 
 
   TEPasa = record
   TEPasa = record
-    strict private
-      var
-        FAccount, FAccountChecksum: TNullable<UInt32>;
-        FAccountName, FPayload, FPassword, FExtendedChecksum: String;
-        FPayloadType: TPayloadType;
+    strict private var
+      FAccount, FAccountChecksum: TNullable<UInt32>;
+      FAccountName, FPayload, FPassword, FExtendedChecksum: String;
+      FPayloadType: TPayloadType;
 
 
       function GetAccount: TNullable<UInt32>; inline;
       function GetAccount: TNullable<UInt32>; inline;
       procedure SetAccount(const AValue: TNullable<UInt32>); inline;
       procedure SetAccount(const AValue: TNullable<UInt32>); inline;
@@ -116,11 +115,13 @@ type
       property IsAddressedByNumber: boolean read GetIsAddressedByNumber;
       property IsAddressedByNumber: boolean read GetIsAddressedByNumber;
       property IsAddressedByName: boolean read GetIsAddressedByName;
       property IsAddressedByName: boolean read GetIsAddressedByName;
       property IsPayToKey: boolean read GetIsPayToKey;
       property IsPayToKey: boolean read GetIsPayToKey;
-      property IsStandard: boolean read GetIsStandard;
+      property IsClassicPASA: boolean read GetIsStandard;
       property HasPayload: boolean read GetHasPayload;
       property HasPayload: boolean read GetHasPayload;
       class property Empty : TEPasa read GetEmptyValue;
       class property Empty : TEPasa read GetEmptyValue;
 
 
-      function GetRawPayloadBytes(): TArray<Byte>; inline;
+      function GetRawPayloadBytes(): TBytes; inline;
+
+      function ToClassicPASAString(): String; overload;
       function ToString(): String; overload;
       function ToString(): String; overload;
       function ToString(AOmitExtendedChecksum: Boolean): String; overload;
       function ToString(AOmitExtendedChecksum: Boolean): String; overload;
 
 
@@ -128,7 +129,7 @@ type
       class function Parse(const AEPasaText: String): TEPasa; static;
       class function Parse(const AEPasaText: String): TEPasa; static;
 
 
       class function CalculateAccountChecksum(AAccNo: UInt32): Byte; static; inline;
       class function CalculateAccountChecksum(AAccNo: UInt32): Byte; static; inline;
-
+      procedure Clear;
   end;
   end;
 
 
 
 
@@ -146,7 +147,7 @@ type
       // note: regex syntax escapes following chars [\^$.|?*+(){}
       // note: regex syntax escapes following chars [\^$.|?*+(){}
       // note: epasa syntax escapes following chars: :\"[]()<>(){}
       // note: epasa syntax escapes following chars: :\"[]()<>(){}
       // note: c-sharp syntax verbatim strings escape: " as ""
       // note: c-sharp syntax verbatim strings escape: " as ""
-      IntegerPattern = '(0|[1-9]\d+)';
+      IntegerPattern = '(0|[1-9]\d*)';
       AccountNamePattern = '(?P<AccountName>' + TPascal64Encoding.StringPattern + ')';
       AccountNamePattern = '(?P<AccountName>' + TPascal64Encoding.StringPattern + ')';
       AccountChecksumPattern = '(?:(?P<ChecksumDelim>-)(?P<Checksum>\d{2}))?';
       AccountChecksumPattern = '(?:(?P<ChecksumDelim>-)(?P<Checksum>\d{2}))?';
       AccountNumberPattern = '(?P<AccountNumber>' + IntegerPattern + ')' + AccountChecksumPattern;
       AccountNumberPattern = '(?P<AccountNumber>' + IntegerPattern + ')' + AccountChecksumPattern;
@@ -194,6 +195,7 @@ type
 
 
       class function GetPayloadTypeProtocolByte(const APayloadType : TPayloadType) : Byte;
       class function GetPayloadTypeProtocolByte(const APayloadType : TPayloadType) : Byte;
       class function GetPayloadTypeFromProtocolByte(AByte : Byte) : TPayloadType;
       class function GetPayloadTypeFromProtocolByte(AByte : Byte) : TPayloadType;
+      class function FromProtocolValue(AVal : Byte) : TPayloadType;
   end;
   end;
 
 
 resourcestring
 resourcestring
@@ -219,18 +221,18 @@ var
 
 
 { TPayloadTraitHelper }
 { TPayloadTraitHelper }
 
 
-function TPayloadTraitHelper.ProtocolValue: Byte;
+function TPayloadTraitHelper.ToProtocolValue: Byte;
 begin
 begin
   case Self of
   case Self of
-    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;
+    ptNonDeterministic: Exit(0);
+    ptPublic: Exit(BYTE_BIT_0);
+    ptRecipientKeyEncrypted: Exit(BYTE_BIT_1);
+    ptSenderKeyEncrypted: Exit(BYTE_BIT_2);
+    ptPasswordEncrypted: Exit(BYTE_BIT_3);
+    ptAsciiFormatted: Exit(BYTE_BIT_4);
+    ptHexFormatted: Exit(BYTE_BIT_5);
+    ptBase58Formatted: Exit(BYTE_BIT_6);
+    ptAddressedByName: Exit(BYTE_BIT_7);
   end;
   end;
   raise Exception.Create('Internal Error 2faed11a-1b0f-447a-87d1-2e1735ac4ca2');
   raise Exception.Create('Internal Error 2faed11a-1b0f-447a-87d1-2e1735ac4ca2');
 end;
 end;
@@ -242,14 +244,24 @@ begin
   Result := APayloadTrait in Self;
   Result := APayloadTrait in Self;
 end;
 end;
 
 
-function TPayloadTypeHelper.ProtocolValue : Byte;
+function TPayloadTypeHelper.ToProtocolValue : Byte;
 begin
 begin
   Result := TEPasaComp.GetPayloadTypeProtocolByte(Self);
   Result := TEPasaComp.GetPayloadTypeProtocolByte(Self);
 end;
 end;
 
 
-
 { TEPasa }
 { TEPasa }
 
 
+procedure TEPasa.Clear;
+begin
+  Self.FAccount.Clear;
+  Self.FAccountChecksum.Clear;
+  Self.FAccountName:='';
+  Self.FPayload:='';
+  Self.FPassword:='';
+  Self.FExtendedChecksum:='';
+  Self.FPayloadType:=[];
+end;
+
 function TEPasa.GetAccount: TNullable<UInt32>;
 function TEPasa.GetAccount: TNullable<UInt32>;
 begin
 begin
   Result := FAccount;
   Result := FAccount;
@@ -349,8 +361,7 @@ begin
   Result := PayloadType.HasTrait(ptPublic) OR PayloadType.HasTrait(ptRecipientKeyEncrypted) OR PayloadType.HasTrait(ptSenderKeyEncrypted);
   Result := PayloadType.HasTrait(ptPublic) OR PayloadType.HasTrait(ptRecipientKeyEncrypted) OR PayloadType.HasTrait(ptSenderKeyEncrypted);
 end;
 end;
 
 
-
-function TEPasa.GetRawPayloadBytes: TArray<Byte>;
+function TEPasa.GetRawPayloadBytes: TBytes;
 begin
 begin
   if (PayloadType.HasTrait(ptAsciiFormatted)) then
   if (PayloadType.HasTrait(ptAsciiFormatted)) then
     Exit(TEncoding.ASCII.GetBytes(Payload));
     Exit(TEncoding.ASCII.GetBytes(Payload));
@@ -364,6 +375,16 @@ begin
   raise EPascalCoinException.CreateRes(@SUnknownPayloadEncoding);
   raise EPascalCoinException.CreateRes(@SUnknownPayloadEncoding);
 end;
 end;
 
 
+function TEPasa.ToClassicPASAString : String;
+begin
+  Result := ToString(True);
+end;
+
+function TEPasa.ToString: String;
+begin
+  Result := ToString(False);
+end;
+
 function TEPasa.ToString(AOmitExtendedChecksum: Boolean): String;
 function TEPasa.ToString(AOmitExtendedChecksum: Boolean): String;
 var
 var
   LPayloadContent: String;
   LPayloadContent: String;
@@ -406,16 +427,12 @@ begin
   end;
   end;
 end;
 end;
 
 
-function TEPasa.ToString: String;
-begin
-  Result := ToString(False);
-end;
+
 
 
 class function TEPasa.TryParse(const AEPasaText: String; out AEPasa: TEPasa): Boolean;
 class function TEPasa.TryParse(const AEPasaText: String; out AEPasa: TEPasa): Boolean;
 var
 var
   LParser: TEPasaParser;
   LParser: TEPasaParser;
   LDisposables : TDisposables;
   LDisposables : TDisposables;
-
 begin
 begin
   LParser := LDisposables.AddObject( TEPasaParser.Create() ) as TEPasaParser;
   LParser := LDisposables.AddObject( TEPasaParser.Create() ) as TEPasaParser;
   Result := LParser.TryParse(AEPasaText, AEPasa);
   Result := LParser.TryParse(AEPasaText, AEPasa);
@@ -475,10 +492,9 @@ var
   LExtendedChecksumDelim, LExtendedChecksum, LActualChecksum: String;
   LExtendedChecksumDelim, LExtendedChecksum, LActualChecksum: String;
   LAccNo, LAccChecksum: UInt32;
   LAccNo, LAccChecksum: UInt32;
   LActualAccountChecksum: Byte;
   LActualAccountChecksum: Byte;
-  LEPasa : TEPasa;
 begin
 begin
   AErrorCode := EPasaErrorCode.Success;
   AErrorCode := EPasaErrorCode.Success;
-  AEPasa := LEPasa;
+  AEPasa := TEPasa.Empty;
   if (string.IsNullOrEmpty(AEPasaText)) then begin
   if (string.IsNullOrEmpty(AEPasaText)) then begin
     AErrorCode := EPasaErrorCode.BadFormat;
     AErrorCode := EPasaErrorCode.BadFormat;
     Exit(False);
     Exit(False);
@@ -515,8 +531,7 @@ begin
     // when multiple enums are OR'ed in C#, they are combined and
     // when multiple enums are OR'ed in C#, they are combined and
     // if any of the enums numeric value is zero, it is excluded.
     // 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.
     // in our case,"PayloadType.NonDeterministic" is always zero so we exclude it from our set.
-    AEPasa.PayloadType := AEPasa.PayloadType + [ptAddressedByName] -
-      [ptNonDeterministic];
+    AEPasa.PayloadType := AEPasa.PayloadType + [ptAddressedByName] -[ptNonDeterministic];
     AEPasa.AccountName := TPascal64Encoding.Unescape(LAccountName);
     AEPasa.AccountName := TPascal64Encoding.Unescape(LAccountName);
     AEPasa.Account := Nil;
     AEPasa.Account := Nil;
     AEPasa.AccountChecksum := Nil;
     AEPasa.AccountChecksum := Nil;
@@ -722,7 +737,7 @@ begin
   Result := 0; // NonDeterministic by default
   Result := 0; // NonDeterministic by default
   for LPayloadType := Low(TPayloadTrait) to High(TPayloadTrait) do
   for LPayloadType := Low(TPayloadTrait) to High(TPayloadTrait) do
     if APayloadType.HasTrait(LPayloadType) then
     if APayloadType.HasTrait(LPayloadType) then
-      Result := Result OR LPayloadType.ProtocolValue;
+      Result := Result OR LPayloadType.ToProtocolValue;
 end;
 end;
 
 
 class function TEPasaComp.GetPayloadTypeFromProtocolByte(AByte: Byte) : TPayloadType;
 class function TEPasaComp.GetPayloadTypeFromProtocolByte(AByte: Byte) : TPayloadType;
@@ -735,15 +750,35 @@ begin
 
 
   Result := [];
   Result := [];
   for LPayloadType := Low(TPayloadTrait) to High(TPayloadTrait) do begin
   for LPayloadType := Low(TPayloadTrait) to High(TPayloadTrait) do begin
-    LPayloadTypeByte := LPayloadType.ProtocolValue;
+    LPayloadTypeByte := LPayloadType.ToProtocolValue;
     if (AByte AND LPayloadTypeByte) = LPayloadTypeByte then
     if (AByte AND LPayloadTypeByte) = LPayloadTypeByte then
       Result := Result + [LPayloadType];
       Result := Result + [LPayloadType];
   end;
   end;
 end;
 end;
 
 
-initialization
+class function TEPasaComp.FromProtocolValue(AVal : Byte) : TPayloadType;
+begin
+  if AVal = 0 then begin
+    Exit([ptNonDeterministic]);
+  end;
+  Result := [];
+  if AVal AND BYTE_BIT_0 <> 0 then Result := Result + [ptPublic];
+  if AVal AND BYTE_BIT_1 <> 0 then Result := Result + [ptRecipientKeyEncrypted];
+  if AVal AND BYTE_BIT_2 <> 0 then Result := Result + [ptSenderKeyEncrypted];
+  if AVal AND BYTE_BIT_3 <> 0 then Result := Result + [ptPasswordEncrypted];
+  if AVal AND BYTE_BIT_4 <> 0 then Result := Result + [ptAsciiFormatted];
+  if AVal AND BYTE_BIT_5 <> 0 then Result := Result + [ptHexFormatted];
+  if AVal AND BYTE_BIT_6 <> 0 then Result := Result + [ptBase58Formatted];
+  if AVal AND BYTE_BIT_7 <> 0 then Result := Result + [ptAddressedByName];
+end;
 
 
-EmptyEPasa := Default(TEPasa);
 
 
 
 
+
+initialization
+{$IFDEF FPC}
+FillChar(EmptyEPasa, SizeOf(EmptyEPASA), 0);
+{$ELSE}
+EmptyEPasa := Default(TEPasa);
+{$ENDIF}
 end.
 end.

+ 1 - 1
src/core/UEncoding.pas

@@ -106,7 +106,7 @@ type
     public
     public
       const
       const
         CharSet = '0123456789abcdef';
         CharSet = '0123456789abcdef';
-        NibblePattern = '[0-9a-f]';
+        NibblePattern = '[0-9a-fA-F]';
         BytePattern = NibblePattern + '{2}';
         BytePattern = NibblePattern + '{2}';
         SubStringPattern = '(?:' + BytePattern + ')+';
         SubStringPattern = '(?:' + BytePattern + ')+';
         StringPattern = SubStringPattern + '$';
         StringPattern = SubStringPattern + '$';

+ 101 - 3
src/core/UNode.pas

@@ -35,8 +35,8 @@ interface
 
 
 uses
 uses
   Classes, SysUtils,
   Classes, SysUtils,
-  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UPCDataTypes,
-  UBlockChain, UNetProtocol, UAccounts, UCrypto, UThread, SyncObjs, ULog, UBaseTypes, UPCOrderedLists;
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, UPCDataTypes, UEncoding,
+  UBlockChain, UNetProtocol, UAccounts, UCrypto, UEPasa, UThread, SyncObjs, ULog, UBaseTypes, UPCOrderedLists;
 
 
 {$I ./../config.inc}
 {$I ./../config.inc}
 
 
@@ -131,6 +131,11 @@ Type
     //
     //
     function TryFindAccountByKey(const APubKey : TAccountKey; out AAccountNumber : Cardinal) : Boolean;
     function TryFindAccountByKey(const APubKey : TAccountKey; out AAccountNumber : Cardinal) : Boolean;
     function TryFindPublicSaleAccount(AMaximumPrice : Int64; APreventRaceCondition : Boolean; out AAccountNumber : Cardinal) : Boolean;
     function TryFindPublicSaleAccount(AMaximumPrice : Int64; APreventRaceCondition : Boolean; out AAccountNumber : Cardinal) : Boolean;
+    Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal): Boolean; overload;
+    Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AErrorMessage: String): Boolean; overload;
+    Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean): Boolean; overload;
+    Function TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean; out AErrorMessage: String): Boolean; overload;
+
   End;
   End;
 
 
   TThreadSafeNodeNotifyEvent = Class(TPCThread)
   TThreadSafeNodeNotifyEvent = Class(TPCThread)
@@ -212,7 +217,7 @@ Type
 implementation
 implementation
 
 
 Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator,
 Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator,
-  UFolderHelper;
+  UFolderHelper, USettings;
 
 
 var _Node : TNode;
 var _Node : TNode;
   _PascalCoinDataFolder : String;
   _PascalCoinDataFolder : String;
@@ -840,6 +845,99 @@ begin
   end;
   end;
 end;
 end;
 
 
+Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal): Boolean;
+var LErrMsg : String;
+begin
+  Result := TryResolveEPASA(AEPasa, AResolvedAccount, LErrMsg);
+end;
+
+Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AErrorMessage: String): Boolean;
+var
+  LAccountKey : TAccountKey;
+  LRequiresPurchase : Boolean;
+begin
+  Result := TryResolveEPASA(AEPasa, AResolvedAccount, LAccountKey, LRequiresPurchase, AErrorMessage);
+  if Result AND AEPasa.IsPayToKey then begin
+    Result := False;
+    AErrorMessage := 'EPASA was a pay-to-key style';
+  end;
+end;
+
+Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean): Boolean;
+var LErrMsg : String;
+begin
+  Result := TryResolveEPASA(AEPasa, AResolvedAccount, AResolvedKey, ARequiresPurchase, LErrMsg);
+end;
+
+Function TNode.TryResolveEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : boolean; out AErrorMessage: String): Boolean;
+var
+  LErrMsg : String;
+begin
+  AResolvedAccount := 0;
+  AResolvedKey.Clear;
+  ARequiresPurchase := False;
+  AErrorMessage := '';
+  if (AEPasa.IsPayToKey) then begin
+    // Parse account key in EPASA
+    if NOT TAccountComp.AccountPublicKeyImport(AEPasa.Payload, AResolvedKey, 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 TryFindAccountByKey(AResolvedKey, AResolvedAccount) then begin
+      // Key already exists in SafeBox, so send to that account
+      ARequiresPurchase := False;
+      Exit(True);
+    end;
+
+    // If no key found, find optimal public purchase account
+    if TryFindPublicSaleAccount(TSettings.MaxPayToKeyPurchasePrice, True, AResolvedAccount) then begin
+      // Account needs to be purchased
+      ARequiresPurchase := True;
+      Exit(True);
+    end;
+
+    // Account could not be resolved
+    AResolvedAccount := CT_AccountNo_NUL;
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+    ARequiresPurchase := False;
+    AErrorMessage := 'No account could be resolved for pay to key EPASA';
+    Exit(False);
+
+  end else if (AEPasa.IsAddressedByName) then begin
+    // Find account by name
+    AResolvedAccount := Bank.SafeBox.FindAccountByName(AEPasa.AccountName);
+    AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+    ARequiresPurchase := False;
+    if AResolvedAccount = CT_AccountNo_NUL then begin
+      // No account with name found
+      AResolvedAccount := CT_AccountNo_NUL;
+      AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+      ARequiresPurchase := False;
+      AErrorMessage := Format('No account with name "%s" was found', [AEPasa.AccountName]);
+      Exit(False);
+    end;
+    Exit(True);
+  end;
+  // 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');
+  AResolvedAccount := AEPasa.Account.Value;
+  if (AResolvedAccount < 0) or (AResolvedAccount >= Self.Bank.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;
+  Result := true;
+end;
+
+
 function TNode.TryLockNode(MaxWaitMilliseconds: Cardinal): Boolean;
 function TNode.TryLockNode(MaxWaitMilliseconds: Cardinal): Boolean;
 begin
 begin
   Result := TPCThread.TryProtectEnterCriticalSection(Self,MaxWaitMilliseconds,FLockMempool);
   Result := TPCThread.TryProtectEnterCriticalSection(Self,MaxWaitMilliseconds,FLockMempool);

+ 3 - 1
src/core/UOpTransaction.pas

@@ -27,7 +27,7 @@ interface
 
 
 Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
 Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  UPCDataTypes;
+  UPCDataTypes, UEPasa;
 
 
 Type
 Type
   // Operations Type
   // Operations Type
@@ -1189,6 +1189,7 @@ begin
   SetLength(OperationResume.Senders,1);
   SetLength(OperationResume.Senders,1);
   OperationResume.Senders[0] := CT_TMultiOpSender_NUL;
   OperationResume.Senders[0] := CT_TMultiOpSender_NUL;
   OperationResume.Senders[0].Account:=FData.sender;
   OperationResume.Senders[0].Account:=FData.sender;
+  OperationResume.Senders[0].AccountEPASA := TAccountComp.DecodeEPASAPartial(FData.sender, FData.payload.payload_raw, FData.payload.payload_type, TEPasa.Empty);
   OperationResume.Senders[0].Amount:=Int64(FData.amount + FData.fee);
   OperationResume.Senders[0].Amount:=Int64(FData.amount + FData.fee);
   OperationResume.Senders[0].N_Operation:=FData.n_operation;
   OperationResume.Senders[0].N_Operation:=FData.n_operation;
   OperationResume.Senders[0].Payload:=FData.payload;
   OperationResume.Senders[0].Payload:=FData.payload;
@@ -1198,6 +1199,7 @@ begin
       SetLength(OperationResume.Receivers,1);
       SetLength(OperationResume.Receivers,1);
       OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
       OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
       OperationResume.Receivers[0].Account:=FData.target;
       OperationResume.Receivers[0].Account:=FData.target;
+      OperationResume.Receivers[0].AccountEPASA:=TAccountComp.DecodeEPASAPartial(FData.target, FData.payload.payload_raw, FData.payload.payload_type, TEPasa.Empty);
       OperationResume.Receivers[0].Amount:=FData.amount;
       OperationResume.Receivers[0].Amount:=FData.amount;
       OperationResume.Receivers[0].Payload:=FData.payload;
       OperationResume.Receivers[0].Payload:=FData.payload;
     end;
     end;

+ 4 - 2
src/core/UPCAbstractMem.pas

@@ -6,6 +6,8 @@ interface
   {$MODE DELPHI}
   {$MODE DELPHI}
 {$ENDIF}
 {$ENDIF}
 
 
+{$I ./../config.inc}
+
 uses Classes, SysUtils, SyncObjs,
 uses Classes, SysUtils, SyncObjs,
   UAbstractMem, UFileMem, UAbstractMemTList, UCacheMem,
   UAbstractMem, UFileMem, UAbstractMemTList, UCacheMem,
   UAbstractBTree, UThread, UAbstractMemBTree,
   UAbstractBTree, UThread, UAbstractMemBTree,
@@ -457,10 +459,10 @@ begin
   end;
   end;
   FBufferBlocksHash := TPCAbstractMemBytesBuffer32Safebox.Create(FAbstractMem,LZoneBuffersBlockHash,FBlocks.Count);
   FBufferBlocksHash := TPCAbstractMemBytesBuffer32Safebox.Create(FAbstractMem,LZoneBuffersBlockHash,FBlocks.Count);
 
 
-  FAccountsOrderedByUpdatedBlock := TAccountsOrderedByUpdatedBlock.Create(FAbstractMem,LZoneAccountsOrderedByUpdatedBlock,DoGetAccount);
+  FAccountsOrderedByUpdatedBlock := TAccountsOrderedByUpdatedBlock.Create({$IFDEF USE_ABSTRACTMEM}FAbstractMem,LZoneAccountsOrderedByUpdatedBlock,{$ENDIF}DoGetAccount);
   FAccounts.AccountsOrderedByUpdatedBlock := FAccountsOrderedByUpdatedBlock;
   FAccounts.AccountsOrderedByUpdatedBlock := FAccountsOrderedByUpdatedBlock;
 
 
-  FAccountsOrderedBySalePrice := TAccountsOrderedBySalePrice.Create(FAbstractMem,LZoneAccountsOrderedBySalePrice,DoGetAccount);
+  FAccountsOrderedBySalePrice := TAccountsOrderedBySalePrice.Create({$IFDEF USE_ABSTRACTMEM}FAbstractMem,LZoneAccountsOrderedBySalePrice,{$ENDIF}DoGetAccount);
   FAccounts.AccountsOrderedBySalePrice := FAccountsOrderedBySalePrice;
   FAccounts.AccountsOrderedBySalePrice := FAccountsOrderedBySalePrice;
 
 
   FAccountCache.Clear;
   FAccountCache.Clear;

+ 3 - 1
src/core/UPCRPCOpData.pas

@@ -27,7 +27,7 @@ interface
 Uses classes, SysUtils,
 Uses classes, SysUtils,
   UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst, UPCDataTypes,
   UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst, UPCDataTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  URPC, UCrypto, UWallet, UBlockChain, ULog;
+  URPC, UCrypto, UEPasa, UWallet, UBlockChain, ULog;
 
 
 
 
 Type
 Type
@@ -335,6 +335,7 @@ begin
 
 
   if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
   if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
     TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
     TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+    [ptNonDeterministic],
     AInputParams.AsString('payload_method','none'),
     AInputParams.AsString('payload_method','none'),
     AInputParams.AsString('pwd',''),
     AInputParams.AsString('pwd',''),
     LSender.accountInfo.accountKey,
     LSender.accountInfo.accountKey,
@@ -409,6 +410,7 @@ begin
 
 
     if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
     if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
       TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
       TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+      [ptNonDeterministic],
       AInputParams.AsString('payload_method','dest'),
       AInputParams.AsString('payload_method','dest'),
       AInputParams.AsString('pwd',''),
       AInputParams.AsString('pwd',''),
       LPayloadPubkey,
       LPayloadPubkey,

+ 106 - 35
src/core/UPCRPCSend.pas

@@ -27,40 +27,55 @@ interface
 Uses classes, SysUtils,
 Uses classes, SysUtils,
   UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
   UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  URPC, UCrypto, UWallet, UBlockChain, ULog, UPCOrderedLists, UPCDataTypes;
+  URPC, UCrypto, UWallet, UBlockChain, UEPasa, ULog, UPCOrderedLists, UPCDataTypes;
 
 
 
 
 Type
 Type
   TRPCSend = Class
   TRPCSend = Class
   private
   private
   public
   public
-    class function CreateOperationTransaction(const ARPCProcess : TRPCProcess;
-      ACurrent_protocol : Word; ASender, ATarget, ASender_last_n_operation : Cardinal; AAmount, AFee : UInt64;
-      Const ASenderAccounKey, ATargetAccountKey : TAccountKey; Const ARawPayload : TRawBytes;
-      Const APayload_method, AEncodePwd : String; var AErrorNum: Integer; var AErrorDesc: String) : TOpTransaction;
-    //
+    class function CreateOperationTransaction(const ARPCProcess : TRPCProcess; ACurrentProtocol : Word; ASender, ATarget : TAccount; AAmount, AFee : UInt64; const ARawPayload : TRawBytes; const APayloadMethod, AEncodePwd : String; const APayloadType : TPayloadType; var AErrorNum: Integer; var AErrorDesc: String) : TOpTransaction;
+    class function CreatePayToKeyTransaction(const ARPCProcess : TRPCProcess; ACurrentProtocol: Word; ASender, APurchaseAccount : TAccount; const ANewKey : TAccountKey; AAmount, AFee: UInt64; const ARawPayload: TRawBytes;  const APayloadMethod, AEncodePwd: String; const APayloadType : TPayloadType; var AErrorNum: Integer; var AErrorDesc: String) : TOpTransaction;
     class function SendTo(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
     class function SendTo(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
     class function SignSendTo(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
     class function SignSendTo(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
   End;
   End;
 
 
 implementation
 implementation
 
 
-{ TRPCFindAccounts }
+{ TRPCSend }
 
 
-class function TRPCSend.CreateOperationTransaction(const ARPCProcess : TRPCProcess;
-  ACurrent_protocol: Word;
-  ASender, ATarget, ASender_last_n_operation: Cardinal; AAmount, AFee: UInt64;
-  const ASenderAccounKey, ATargetAccountKey: TAccountKey;
-  const ARawPayload: TRawBytes; const APayload_method,
-  AEncodePwd: String; var AErrorNum: Integer; var AErrorDesc: String): TOpTransaction;
+class function TRPCSend.CreateOperationTransaction(const ARPCProcess : TRPCProcess; ACurrentProtocol : Word; ASender, ATarget : TAccount; AAmount, AFee : UInt64; const ARawPayload : TRawBytes; const APayloadMethod, AEncodePwd : String; const APayloadType : TPayloadType; var AErrorNum: Integer; var AErrorDesc: String): TOpTransaction;
+var
+  LOpPayload : TOperationPayload;
+  LPrivateKey : TECPrivateKey;
+Begin
+  Result := Nil;
+  if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(ASender.accountInfo.accountKey, LPrivateKey, AErrorNum, AErrorDesc) then Exit(Nil);
+  if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(ARawPayload, APayloadType, APayloadMethod, AEncodePwd, ASender.accountInfo.accountKey, ATarget.accountInfo.accountKey, LOpPayload, AErrorNum, AErrorDesc) then Exit(Nil);
+  Result := TOpTransaction.CreateTransaction(ACurrentProtocol, ASender.account, ASender.n_operation+1, ATarget.account, LPrivateKey, AAmount, AFee, LOpPayload);
+  if Not Result.HasValidSignature then begin
+    FreeAndNil(Result);
+    AErrorNum:=CT_RPC_ErrNum_InternalError;
+    AErrorDesc:='Invalid signature';
+    exit;
+  end;
+end;
 
 
-Var LOpPayload : TOperationPayload;
+class function TRPCSend.CreatePayToKeyTransaction(const ARPCProcess : TRPCProcess; ACurrentProtocol: Word; ASender, APurchaseAccount : TAccount; const ANewKey : TAccountKey; AAmount, AFee: UInt64; const ARawPayload: TRawBytes;  const APayloadMethod, AEncodePwd: String; const APayloadType : TPayloadType; var AErrorNum: Integer; var AErrorDesc: String): TOpTransaction;
+Var
+  LOpPayload : TOperationPayload;
   LPrivateKey : TECPrivateKey;
   LPrivateKey : TECPrivateKey;
 Begin
 Begin
   Result := Nil;
   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);
-  Result := TOpTransaction.CreateTransaction(ACurrent_protocol, ASender,ASender_last_n_operation+1, ATarget, LPrivateKey, AAmount, AFee, LOpPayload);
+  if (AAmount < APurchaseAccount.accountInfo.price) then begin
+    AErrorNum := CT_RPC_ErrNum_InternalError;
+    AErrorDesc := 'Insufficient funds to purchase account for pay-to-key transaction';
+    Exit(Nil);
+  end;
+
+  if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(ASender.accountInfo.accountKey, LPrivateKey, AErrorNum, AErrorDesc) then Exit(Nil);
+  if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(ARawPayload, APayloadType, APayloadMethod, AEncodePwd, ASender.accountInfo.accountKey, ANewKey, LOpPayload, AErrorNum, AErrorDesc) then Exit(Nil);
+  Result := TOpBuyAccount.CreateBuy(ACurrentProtocol, ASender.account, ASender.n_operation + 1, APurchaseAccount.account, APurchaseAccount.accountInfo.account_to_pay, APurchaseAccount.accountInfo.price, AAmount, AFee, ANewKey, LPrivateKey, LOpPayload);
   if Not Result.HasValidSignature then begin
   if Not Result.HasValidSignature then begin
     FreeAndNil(Result);
     FreeAndNil(Result);
     AErrorNum:=CT_RPC_ErrNum_InternalError;
     AErrorNum:=CT_RPC_ErrNum_InternalError;
@@ -97,7 +112,11 @@ Otherwise, will return a JSON-RPC error code with description
 
 
 }
 }
 
 
-var LSender, LTarget : TAccount;
+var
+ LSender, LTarget : TAccount;
+ LTargetEPASA : TEPasa;
+ LTargetKey : TAccountKey;
+ LTargetRequiresPurchase : Boolean;
  LAmount, LFee : UInt64;
  LAmount, LFee : UInt64;
  LRawPayload : TRawBytes;
  LRawPayload : TRawBytes;
  LPayload_method, LEncodePwd, LErrors : String;
  LPayload_method, LEncodePwd, LErrors : String;
@@ -118,28 +137,49 @@ begin
     exit;
     exit;
   end;
   end;
 
 
-  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',ASender.Node.Bank,LSender.account,AErrorDesc) then begin
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',ASender.Node,LSender.account,AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
     Exit;
     Exit;
   end else LSender := ASender.Node.GetMempoolAccount(LSender.account);
   end else LSender := ASender.Node.GetMempoolAccount(LSender.account);
 
 
-  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'target',ASender.Node.Bank,LTarget.account,AErrorDesc) then begin
+  LTarget := CT_Account_NUL;
+  if Not TPascalCoinJSONComp.CaptureEPASA(AInputParams,'target',ASender.Node, LTargetEPASA, LTarget.account, LTargetKey, LTargetRequiresPurchase, AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
     Exit;
     Exit;
   end else LTarget := ASender.Node.GetMempoolAccount(LTarget.account);
   end else LTarget := ASender.Node.GetMempoolAccount(LTarget.account);
 
 
+  if Not TPascalCoinJSONComp.OverridePayloadParams(AInputParams, LTargetEPASA) then begin
+    AErrorNum := CT_RPC_ErrNum_AmbiguousPayload;
+    AErrorDesc := 'Target EPASA payload conflicts with argument payload.';
+    Exit;
+  end;
+
   LAmount := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('amount',0));
   LAmount := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('amount',0));
   LFee := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('fee',0));
   LFee := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('fee',0));
   LRawPayload := TCrypto.HexaToRaw(AInputParams.AsString('payload',''));
   LRawPayload := TCrypto.HexaToRaw(AInputParams.AsString('payload',''));
   LPayload_method := AInputParams.AsString('payload_method','dest');
   LPayload_method := AInputParams.AsString('payload_method','dest');
   LEncodePwd := AInputParams.AsString('pwd','');
   LEncodePwd := AInputParams.AsString('pwd','');
 
 
+  // Do new operation
   ASender.Node.OperationSequenceLock.Acquire;  // Use lock to prevent N_Operation race-condition on concurrent sends
   ASender.Node.OperationSequenceLock.Acquire;  // Use lock to prevent N_Operation race-condition on concurrent sends
   try
   try
-    LOpt := CreateOperationTransaction(ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
-      LSender.account, LTarget.account, LSender.n_operation, LAmount, LFee,
-      LSender.accountInfo.accountKey, LTarget.accountInfo.accountKey,
-      LRawPayload, LPayload_method, LEncodePwd, AErrorNum, AErrorDesc);
+    // Create operation
+    if LTargetRequiresPurchase then begin
+      // Buy Account
+      LOpt := CreatePayToKeyTransaction(
+          ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+          LSender, LTarget, LTargetKey, LAmount, LFee,
+          LRawPayload, LPayload_method, LEncodePwd, LTargetEPASA.PayloadType,
+          AErrorNum, AErrorDesc);
+    end else begin
+      // Transaction
+      LOpt := CreateOperationTransaction(
+        ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+        LSender, LTarget, LAmount, LFee,
+        LRawPayload, LPayload_method, LEncodePwd, LTargetEPASA.PayloadType,
+        AErrorNum, AErrorDesc);
+    end;
+    // Execute operation
     if Assigned(LOpt) then
     if Assigned(LOpt) then
     try
     try
       If not ASender.Node.AddOperation(Nil,LOpt,LErrors) then begin
       If not ASender.Node.AddOperation(Nil,LOpt,LErrors) then begin
@@ -185,8 +225,11 @@ No other checks are made (no checks for valid target, valid n_operation, valid a
 Returns a [Raw Operations Object](#raw-operations-object)
 Returns a [Raw Operations Object](#raw-operations-object)
 
 
 }
 }
-var LSender, LTarget : Cardinal;
- LSenderPubKey, LTargetPubKey : TAccountKey;
+var
+ LSender, LTarget : TAccount;
+ LTargetEPASA : TEPasa;
+ LTargetKey : TAccountKey;
+ LTargetRequiresPurchase : Boolean;
  LHexaStringOperationsHashTree, LErrors : String;
  LHexaStringOperationsHashTree, LErrors : String;
  LProtocol : Integer;
  LProtocol : Integer;
  LOperationsHashTree : TOperationsHashTree;
  LOperationsHashTree : TOperationsHashTree;
@@ -208,19 +251,31 @@ begin
     AErrorDesc := 'Wallet is password protected. Unlock first';
     AErrorDesc := 'Wallet is password protected. Unlock first';
     exit;
     exit;
   end;
   end;
-  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',Nil,LSender,AErrorDesc) then begin
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',Nil,LSender.account,AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
     Exit;
     Exit;
   end;
   end;
-  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'target',Nil,LTarget,AErrorDesc) then begin
+  if Not TPascalCoinJSONComp.CaptureNOperation(AInputParams,'last_n_operation',Nil,LSender.n_operation,AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
     Exit;
     Exit;
   end;
   end;
-  If Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'sender_',LSenderPubKey,AErrorDesc) then begin
+
+  if Not TPascalCoinJSONComp.CaptureEPASA(AInputParams,'target', nil, LTargetEPASA, LTarget.account, LTargetKey, LTargetRequiresPurchase, AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidEPASA;
+    Exit;
+  end;
+
+  if Not TPascalCoinJSONComp.OverridePayloadParams(AInputParams, LTargetEPASA) then begin
+    AErrorNum := CT_RPC_ErrNum_AmbiguousPayload;
+    Exit;
+  end;
+
+  If Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'sender_',LSender.accountInfo.accountKey,AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
     AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
     exit;
     exit;
   end;
   end;
-  If Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'target_',LTargetPubKey,AErrorDesc) then begin
+
+  If Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'target_',LTarget.accountInfo.accountKey,AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
     AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
     exit;
     exit;
   end;
   end;
@@ -239,12 +294,27 @@ begin
     AErrorDesc:= 'Error decoding param "rawoperations": '+LErrors;
     AErrorDesc:= 'Error decoding param "rawoperations": '+LErrors;
     Exit;
     Exit;
   end;
   end;
+
+
   Try
   Try
-    LOpt := CreateOperationTransaction(ASender,LProtocol,LSender,LTarget,
-      AInputParams.AsCardinal('last_n_operation',0),
-      LAmount, LFee,
-      LSenderPubKey, LTargetPubKey,
-      LRawPayload,LPayload_method,LEncodePwd, AErrorNum, AErrorDesc);
+    // Create operation
+    if LTargetRequiresPurchase then begin
+      // Buy Account
+      LOpt := CreatePayToKeyTransaction(
+          ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+          LSender, LTarget, LTargetKey, LAmount, LFee,
+          LRawPayload, LPayload_method, LEncodePwd, LTargetEPASA.PayloadType,
+          AErrorNum, AErrorDesc);
+    end else begin
+      // Transaction
+      LOpt := CreateOperationTransaction(
+        ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+        LSender, LTarget, LAmount, LFee,
+        LRawPayload, LPayload_method, LEncodePwd, LTargetEPASA.PayloadType,
+        AErrorNum, AErrorDesc);
+    end;
+
+    // Execute operation
     if Assigned(LOpt) then
     if Assigned(LOpt) then
     try
     try
       LOperationsHashTree.AddOperationToHashTree(LOpt);
       LOperationsHashTree.AddOperationToHashTree(LOpt);
@@ -253,6 +323,7 @@ begin
     finally
     finally
       LOpt.Free;
       LOpt.Free;
     end;
     end;
+
   Finally
   Finally
     LOperationsHashTree.Free;
     LOperationsHashTree.Free;
   End;
   End;

+ 55 - 22
src/core/UPCTNetDataExtraMessages.pas

@@ -33,7 +33,7 @@ interface
 {$ENDIF}
 {$ENDIF}
 
 
 Uses Classes, UThread, UAccounts, UBlockChain, UNetProtocol, SysUtils, UNode,
 Uses Classes, UThread, UAccounts, UBlockChain, UNetProtocol, SysUtils, UNode,
-  UWallet, UNetProtection, UPCDataTypes,
+  UWallet, UNetProtection, UPCDataTypes, UPCAccountsOrdenations, UOrderedList,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 type
 type
@@ -43,8 +43,8 @@ type
     FNetData : TNetData;
     FNetData : TNetData;
     FWalletKeys : TWalletKeysExt;
     FWalletKeys : TWalletKeysExt;
     function DoAskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String) : Integer;
     function DoAskForFreeAccount(const ANewPubliKey : TAccountKey; const AMessage : String) : Integer;
-    {$IFDEF TESTNET}
     procedure DoGiveMeAnAccount(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
     procedure DoGiveMeAnAccount(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
+    {$IFDEF TESTNET}
     procedure DoGiveMeMoney(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
     procedure DoGiveMeMoney(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
     {$ENDIF}
     {$ENDIF}
     procedure OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
     procedure OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
@@ -63,7 +63,7 @@ const
 
 
 implementation
 implementation
 
 
-Uses UOpTransaction, UBaseTypes, ULog;
+Uses UOpTransaction, UBaseTypes, ULog, UPCAbstractMemAccountKeys;
 
 
 var _PCTNetDataExtraMessages : TPCTNetDataExtraMessages = Nil;
 var _PCTNetDataExtraMessages : TPCTNetDataExtraMessages = Nil;
 
 
@@ -129,44 +129,76 @@ begin
   end;
   end;
 end;
 end;
 
 
-{$IFDEF TESTNET}
 procedure TPCTNetDataExtraMessages.DoGiveMeAnAccount(ANetData: TNetData;
 procedure TPCTNetDataExtraMessages.DoGiveMeAnAccount(ANetData: TNetData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   AReceivedData, AResponseData: TStream);
   AReceivedData, AResponseData: TStream);
 var LSenderPublicKey : TAccountKey;
 var LSenderPublicKey : TAccountKey;
-  LIndexKey : Integer;
+  LIndexKey,LOnSafebox,LOnMempool : Integer;
   LAccount : TAccount;
   LAccount : TAccount;
-  LOpChangeKey : TOpChangeKey;
+  LOpRecoverFounds : TOpRecoverFounds;
   LPayload : TOperationPayload;
   LPayload : TOperationPayload;
   LErrors, LSenderMessage : String;
   LErrors, LSenderMessage : String;
   LWord : Word;
   LWord : Word;
+  LAccOrd : TAccountsOrderedByUpdatedBlock;
+  LRaw : TRawBytes;
 begin
 begin
   if Not (AHeaderData.header_type in [ntp_request,ntp_autosend]) then Exit; // Nothing to do
   if Not (AHeaderData.header_type in [ntp_request,ntp_autosend]) then Exit; // Nothing to do
   // Protection to allow spam
   // Protection to allow spam
-  if ANetData.IpInfos.Update_And_ReachesLimits(ASenderConnection.Client.RemoteHost,'EXTRA','GIVE_ME_AN_ACCOUNT',AHeaderData.buffer_data_length,True,
+  if ANetData.IpInfos.Update_And_ReachesLimits(ASenderConnection.Client.RemoteHost,'EXTRA','GIVE_ME_AN_ACCOUNT',
+    AHeaderData.buffer_data_length,True,
     TArray<TLimitLifetime>.Create(TLimitLifetime.Create(300,2,20000))) then Exit;
     TArray<TLimitLifetime>.Create(TLimitLifetime.Create(300,2,20000))) then Exit;
   // Read info
   // Read info
   if TStreamOp.ReadAccountKey(AReceivedData,LSenderPublicKey)<=0 then Exit;
   if TStreamOp.ReadAccountKey(AReceivedData,LSenderPublicKey)<=0 then Exit;
   if TStreamOp.ReadString(AReceivedData,LSenderMessage)<0 then Exit;
   if TStreamOp.ReadString(AReceivedData,LSenderMessage)<0 then Exit;
 
 
-  if Not RandomGetWalletKeysAccount(FNode.Bank.SafeBox,FWalletKeys,0,10000,LIndexKey,LAccount) then Exit;
-  // Send
-  LPayload := CT_TOperationPayload_NUL;
-  LPayload.payload_raw.FromString('Free Account to '+ASenderConnection.Client.RemoteHost);
-  LOpChangeKey := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,
-    LAccount.account,FWalletKeys.Key[LIndexKey].PrivateKey,LSenderPublicKey,0,LPayload);
+  if FNode.GetAccountsAvailableByPublicKey(LSenderPublicKey,LOnSafebox,LOnMempool)>0 then begin
+    // Exit;
+    TLog.NewLog(ltdebug,ClassName,Format('Not Sending to %s because PublicKey %s is used %d and mempool %d',[ASenderConnection.Client.RemoteHost,
+      TAccountComp.AccountPublicKeyExport(LSenderPublicKey),LOnSafebox,LOnMempool]));
+    Lword := 0;
+    AResponseData.Write(Lword,2);
+    Exit;
+  end;
+
+  LAccOrd := FNode.Bank.SafeBox.AccountsOrderedByUpdatedBlock;
+  if Assigned(LAccOrd) then begin
+    LAccount := CT_Account_NUL;
+    if LAccOrd.First(LIndexKey) then begin
+      LAccount := FNode.GetMempoolAccount(LIndexKey);
+      while (Random(100)>0) or (LAccount.balance>0) or (Length(LAccount.name)>0) do begin
+        if Not LAccOrd.Next(LIndexKey) then Exit;
+        LAccount := FNode.GetMempoolAccount(LIndexKey);
+      end;
+    end;
+    //
+  end;
+
+  TLog.NewLog(ltdebug,ClassName,Format('Sending to %s Account %s PublicKey %s',
+    [ASenderConnection.Client.RemoteHost,
+     TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),
+     TAccountComp.AccountPublicKeyExport(LSenderPublicKey)]));
+
+  LOpRecoverFounds := TOpRecoverFounds.Create(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,0,LSenderPublicKey);
   try
   try
-    FNode.AddOperation(Nil,LOpChangeKey,LErrors);
+    if FNode.AddOperation(Nil,LOpRecoverFounds,LErrors) then begin
+      Lword := 1;
+      AResponseData.Write(Lword,2);
+      LRaw := LOpRecoverFounds.OperationHashValid(LOpRecoverFounds,0);
+      TStreamOp.WriteAnsiString(AResponseData,LRaw);
+    end else begin
+      Lword := 0;
+      AResponseData.Write(Lword,2);
+      TLog.NewLog(ltdebug,ClassName,Format('Error %s sending to %s Account %s PublicKey %s',
+        [LErrors, ASenderConnection.Client.RemoteHost,
+         TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),
+         TAccountComp.AccountPublicKeyExport(LSenderPublicKey)]));
+    end;
   finally
   finally
-    LOpChangeKey.Free;
+    LOpRecoverFounds.Free;
   end;
   end;
-  // Response
-  TStreamOp.WriteAccountKey(AResponseData,LSenderPublicKey);
-  LWord := 1;
-  AResponseData.Write(LWord,SizeOf(LWord));
-  AResponseData.Write(LAccount.account,SizeOf(LAccount.account));
 end;
 end;
 
 
+{$IFDEF TESTNET}
 procedure TPCTNetDataExtraMessages.DoGiveMeMoney(ANetData: TNetData;
 procedure TPCTNetDataExtraMessages.DoGiveMeMoney(ANetData: TNetData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   ASenderConnection: TNetConnection; const AHeaderData: TNetHeaderData;
   AReceivedData, AResponseData: TStream);
   AReceivedData, AResponseData: TStream);
@@ -212,6 +244,7 @@ class function TPCTNetDataExtraMessages.InitNetDataExtraMessages(ANode: TNode;
   ANetData: TNetData; AWalletKeys: TWalletKeysExt): TPCTNetDataExtraMessages;
   ANetData: TNetData; AWalletKeys: TWalletKeysExt): TPCTNetDataExtraMessages;
 begin
 begin
   if not Assigned(_PCTNetDataExtraMessages) then begin
   if not Assigned(_PCTNetDataExtraMessages) then begin
+    TLog.NewLog(ltinfo,ClassName,'InitNetDataExtraMessages');
     _PCTNetDataExtraMessages := TPCTNetDataExtraMessages.Create(ANode,ANetData,AWalletKeys);
     _PCTNetDataExtraMessages := TPCTNetDataExtraMessages.Create(ANode,ANetData,AWalletKeys);
   end;
   end;
   Result := _PCTNetDataExtraMessages;
   Result := _PCTNetDataExtraMessages;
@@ -220,12 +253,12 @@ end;
 procedure TPCTNetDataExtraMessages.OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
 procedure TPCTNetDataExtraMessages.OnTNetDataProcessReservedAreaMessage(ANetData : TNetData; ASenderConnection : TNetConnection; const AHeaderData : TNetHeaderData; AReceivedData : TStream; AResponseData : TStream);
 begin
 begin
   TLog.NewLog(ltdebug,ClassName,Format('Received extra message from %s Operation:%d',[ASenderConnection.ClientRemoteAddr,AHeaderData.operation]));
   TLog.NewLog(ltdebug,ClassName,Format('Received extra message from %s Operation:%d',[ASenderConnection.ClientRemoteAddr,AHeaderData.operation]));
-  {$IFDEF TESTNET}
   case AHeaderData.operation of
   case AHeaderData.operation of
     CT_NetProtocol_Extra_NetOp_GIVE_ME_AN_ACCOUNT : DoGiveMeAnAccount(ANetData,ASenderConnection,AHeaderData,AReceivedData,AResponseData);
     CT_NetProtocol_Extra_NetOp_GIVE_ME_AN_ACCOUNT : DoGiveMeAnAccount(ANetData,ASenderConnection,AHeaderData,AReceivedData,AResponseData);
+    {$IFDEF TESTNET}
     CT_NetProtocol_Extra_NetOp_GIVE_ME_MONEY : DoGiveMeMoney(ANetData,ASenderConnection,AHeaderData,AReceivedData,AResponseData);
     CT_NetProtocol_Extra_NetOp_GIVE_ME_MONEY : DoGiveMeMoney(ANetData,ASenderConnection,AHeaderData,AReceivedData,AResponseData);
+    {$ENDIF}
   end;
   end;
-  {$ENDIF}
 end;
 end;
 
 
 function TPCTNetDataExtraMessages.RandomGetWalletKeysAccount(
 function TPCTNetDataExtraMessages.RandomGetWalletKeysAccount(

+ 145 - 35
src/core/URPC.pas

@@ -45,12 +45,15 @@ Const
   CT_RPC_ErrNum_InvalidOperation = 1004;
   CT_RPC_ErrNum_InvalidOperation = 1004;
   CT_RPC_ErrNum_InvalidPubKey = 1005;
   CT_RPC_ErrNum_InvalidPubKey = 1005;
   CT_RPC_ErrNum_InvalidAccountName = 1006;
   CT_RPC_ErrNum_InvalidAccountName = 1006;
+  CT_RPC_ErrNum_InvalidEPASA = 1007;
   CT_RPC_ErrNum_NotFound = 1010;
   CT_RPC_ErrNum_NotFound = 1010;
   CT_RPC_ErrNum_WalletPasswordProtected = 1015;
   CT_RPC_ErrNum_WalletPasswordProtected = 1015;
   CT_RPC_ErrNum_InvalidData = 1016;
   CT_RPC_ErrNum_InvalidData = 1016;
+  CT_RPC_ErrNum_AmbiguousPayload = 1017;
   CT_RPC_ErrNum_InvalidSignature = 1020;
   CT_RPC_ErrNum_InvalidSignature = 1020;
   CT_RPC_ErrNum_NotAllowedCall = 1021;
   CT_RPC_ErrNum_NotAllowedCall = 1021;
 
 
+
 Type
 Type
 
 
   { TRPCServer }
   { TRPCServer }
@@ -60,6 +63,7 @@ Type
   TPascalCoinJSONComp = Class
   TPascalCoinJSONComp = Class
   private
   private
     class function OperationsHashTreeToHexaString(Const OperationsHashTree : TOperationsHashTree) : String;
     class function OperationsHashTreeToHexaString(Const OperationsHashTree : TOperationsHashTree) : String;
+    class function TryResolveOfflineEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AErrorMessage: String): Boolean;
   public
   public
     class procedure FillAccountObject(Const account : TAccount; jsonObj : TPCJSONObject);
     class procedure FillAccountObject(Const account : TAccount; jsonObj : TPCJSONObject);
     class procedure FillBlockObject(nBlock : Cardinal; ANode : TNode; jsonObject: TPCJSONObject);
     class procedure FillBlockObject(nBlock : Cardinal; ANode : TNode; jsonObject: TPCJSONObject);
@@ -71,9 +75,11 @@ 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 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;
+    class function CheckAndGetEncodedRAWPayload(Const ARawPayload : TRawBytes; const APayloadType : TPayloadType; Const APayload_method, AEncodePwdForAES : String; const ASenderAccounKey, ATargetAccountKey : TAccountKey; out AOperationPayload : TOperationPayload; Var AErrorNum : Integer; Var AErrorDesc : String) : Boolean;
+    class Function CaptureNOperation(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out ALastNOp: Cardinal; var AErrorParam : String) : Boolean;
+    class Function CaptureAccountNumber(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out AResolvedAccount: Cardinal; var AErrorParam : String) : Boolean;
+    class Function CaptureEPASA(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out AEPasa: TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : Boolean; var AErrorParam : String) : Boolean;
+    class Function OverridePayloadParams(const AInputParams : TPCJSONObject; const AEPASA : TEPasa) : Boolean;
   end;
   end;
 
 
   TRPCServerThread = Class;
   TRPCServerThread = Class;
@@ -152,7 +158,6 @@ Type
     class function FindRegisteredProcessMethod(Const AMethodName : String) : TRPCProcessMethod;
     class function FindRegisteredProcessMethod(Const AMethodName : String) : TRPCProcessMethod;
   end;
   end;
 
 
-
 implementation
 implementation
 
 
 Uses
 Uses
@@ -247,15 +252,17 @@ Begin
     jsonObject.GetAsVariant('signer_account').Value:=OPR.SignerAccount;
     jsonObject.GetAsVariant('signer_account').Value:=OPR.SignerAccount;
     if (OPR.n_operation>0) then jsonObject.GetAsVariant('n_operation').Value:=OPR.n_operation;
     if (OPR.n_operation>0) then jsonObject.GetAsVariant('n_operation').Value:=OPR.n_operation;
   end;
   end;
-  // New V3: Will include senders[], receivers[] and changers[]
+    // New V3: Will include senders[], receivers[] and changers[]
     jsonArr := jsonObject.GetAsArray('senders');
     jsonArr := jsonObject.GetAsArray('senders');
     for i:=Low(OPR.senders) to High(OPR.Senders) do begin
     for i:=Low(OPR.senders) to High(OPR.Senders) do begin
+      LString := TCrypto.ToHexaString(OPR.Senders[i].Payload.payload_raw);
       auxObj := jsonArr.GetAsObject(jsonArr.Count);
       auxObj := jsonArr.GetAsObject(jsonArr.Count);
       auxObj.GetAsVariant('account').Value := OPR.Senders[i].Account;
       auxObj.GetAsVariant('account').Value := OPR.Senders[i].Account;
+      auxObj.GetAsVariant('account_epasa').Value := OPR.Senders[i].AccountEPASA.ToString();
       if (OPR.Senders[i].N_Operation>0) then auxObj.GetAsVariant('n_operation').Value := OPR.Senders[i].N_Operation;
       if (OPR.Senders[i].N_Operation>0) then auxObj.GetAsVariant('n_operation').Value := OPR.Senders[i].N_Operation;
       auxObj.GetAsVariant('amount').Value := TAccountComp.FormatMoneyDecimal(OPR.Senders[i].Amount * (-1));
       auxObj.GetAsVariant('amount').Value := TAccountComp.FormatMoneyDecimal(OPR.Senders[i].Amount * (-1));
       auxObj.GetAsVariant('amount_s').Value := TAccountComp.FormatMoney (OPR.Senders[i].Amount * (-1));
       auxObj.GetAsVariant('amount_s').Value := TAccountComp.FormatMoney (OPR.Senders[i].Amount * (-1));
-      auxObj.GetAsVariant('payload').Value := TCrypto.ToHexaString(OPR.Senders[i].Payload.payload_raw);
+      auxObj.GetAsVariant('payload').Value := LString;
       auxObj.GetAsVariant('payload_type').Value := OPR.Senders[i].Payload.payload_type;
       auxObj.GetAsVariant('payload_type').Value := OPR.Senders[i].Payload.payload_type;
       if (OPR.OpType = CT_Op_Data) then begin
       if (OPR.OpType = CT_Op_Data) then begin
         FillOpDataObject(auxObj, OPR.senders[i].OpData);
         FillOpDataObject(auxObj, OPR.senders[i].OpData);
@@ -349,7 +356,19 @@ Begin
   end;
   end;
 end;
 end;
 
 
-class Function TPascalCoinJSONComp.CaptureAccountNumber(const AInputParams : TPCJSONObject; const AParamName : String; const ABank : TPCBank; out AResolvedAccount: Cardinal; var AErrorParam : String) : Boolean;
+class Function TPascalCoinJSONComp.CaptureNOperation(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out ALastNOp: Cardinal; var AErrorParam : String) : Boolean;
+var
+  LParamValue : String;
+begin
+  if NOT AInputParams.HasName(AParamName) then begin
+    AErrorParam := Format('Missing n-operation value for Param "%s"',[AParamName]);
+    Exit(False);
+  end;
+  // TODO: add type checking?
+  ALastNOp := AInputParams.AsCardinal(AParamName,0);
+end;
+
+class Function TPascalCoinJSONComp.CaptureAccountNumber(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out AResolvedAccount: Cardinal; var AErrorParam : String) : Boolean;
 var
 var
   LEPasa : TEPasa;
   LEPasa : TEPasa;
   LKey : TAccountKey;
   LKey : TAccountKey;
@@ -357,17 +376,21 @@ var
   LParamValue : String;
   LParamValue : String;
 begin
 begin
   LParamValue := AInputParams.AsString(AParamName,'');
   LParamValue := AInputParams.AsString(AParamName,'');
-  Result := CaptureEPASA(AInputParams, AParamName, ABank, LEPasa, AResolvedAccount, LKey, LPurchase, AErrorParam);
-  if Result AND (NOT LEPasa.IsStandard) then begin
+  Result := CaptureEPASA(AInputParams, AParamName, ANode, LEPasa, AResolvedAccount, LKey, LPurchase, AErrorParam);
+  if Result AND (NOT LEPasa.IsClassicPASA) then begin
       AErrorParam := Format('"%s" is not valid Account Number for Param "%s"',[LParamValue,AParamName]);
       AErrorParam := Format('"%s" is not valid Account Number for Param "%s"',[LParamValue,AParamName]);
       Exit(False);
       Exit(False);
   end;
   end;
 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;
+class function TPascalCoinJSONComp.CaptureEPASA(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; 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');
+  AEPasa.Clear;
+  AResolvedAccount := 0;
+  AResolvedKey.Clear;
+  ARequiresPurchase := False;
+  AErrorParam := '';
   LParamValue := AInputParams.AsString(AParamName,'');
   LParamValue := AInputParams.AsString(AParamName,'');
   if Length(LParamValue)>0 then begin
   if Length(LParamValue)>0 then begin
     if Not TEPasa.TryParse(LParamValue, AEPasa) then begin
     if Not TEPasa.TryParse(LParamValue, AEPasa) then begin
@@ -377,7 +400,14 @@ Begin
       AErrorParam := Format('"%s" is not valid Account EPASA for Param "%s"',[LParamValue,AParamName]);
       AErrorParam := Format('"%s" is not valid Account EPASA for Param "%s"',[LParamValue,AParamName]);
       Exit(False);
       Exit(False);
     end;
     end;
-    Result := ABank.SafeBox.TryResolveAccountByEPASA(AEPasa, AResolvedAccount, AResolvedKey, ARequiresPurchase, AErrorParam);
+    if Assigned(ANode) then begin
+      Result := ANode.TryResolveEPASA(AEPasa, AResolvedAccount, AResolvedKey, ARequiresPurchase, AErrorParam);
+    end else begin
+      // Offline EPASA
+      Result := TryResolveOfflineEPASA(AEPasa, AResolvedAccount, AErrorParam);
+      AResolvedKey := CT_Account_NUL.accountInfo.accountKey;
+      ARequiresPurchase := False;
+    end;
   end else begin
   end else begin
     AEPasa := TEPasa.Empty;
     AEPasa := TEPasa.Empty;
     AResolvedAccount := CT_AccountNo_NUL;
     AResolvedAccount := CT_AccountNo_NUL;
@@ -387,6 +417,30 @@ Begin
   end;
   end;
 end;
 end;
 
 
+class function TPascalCoinJSONComp.OverridePayloadParams(const AInputParams : TPCJSONObject; const AEPASA : TEPasa) : Boolean;
+begin
+   // none, dest, sender, aes, payload, pwd
+   if (NOT AEPASA.IsClassicPASA) AND (AInputParams.HasValue('payload') OR AInputParams.HasValue('payload_method') OR AInputParams.HasValue('pwd')) then
+     Exit(False);
+
+   if AEPASA.PayloadType.HasTrait(ptPublic) then begin
+     AInputParams.SetAs('payload_method', TPCJSONVariantValue.CreateFromVariant('none'));
+     AInputParams.SetAs('payload', TPCJSONVariantValue.CreateFromVariant(AEPASA.GetRawPayloadBytes.ToHexaString));
+   end else if AEPASA.PayloadType.HasTrait(ptSenderKeyEncrypted) then begin
+     AInputParams.SetAs('payload_method', TPCJSONVariantValue.CreateFromVariant('sender'));
+     AInputParams.SetAs('payload', TPCJSONVariantValue.CreateFromVariant(AEPASA.GetRawPayloadBytes().ToHexaString()));
+   end else if AEPASA.PayloadType.HasTrait(ptRecipientKeyEncrypted) then begin
+     AInputParams.SetAs('payload_method', TPCJSONVariantValue.CreateFromVariant('dest'));
+     AInputParams.SetAs('payload', TPCJSONVariantValue.CreateFromVariant(AEPASA.GetRawPayloadBytes().ToHexaString()));
+   end else if AEPASA.PayloadType.HasTrait(ptPasswordEncrypted) then begin
+     AInputParams.SetAs('payload_method', TPCJSONVariantValue.CreateFromVariant('aes'));
+     AInputParams.SetAs('payload', TPCJSONVariantValue.CreateFromVariant(AEPASA.GetRawPayloadBytes().ToHexaString()));
+     AInputParams.SetAs('pwd', TPCJSONVariantValue.CreateFromVariant(AEPASA.Password));
+     Result := True;
+   end;
+   Result := True;
+end;
+
 class function TPascalCoinJSONComp.CapturePubKey(
 class function TPascalCoinJSONComp.CapturePubKey(
   const AInputParams: TPCJSONObject; const APrefix: String;
   const AInputParams: TPCJSONObject; const APrefix: String;
   var APubKey: TAccountKey; var AErrortxt: String): Boolean;
   var APubKey: TAccountKey; var AErrortxt: String): Boolean;
@@ -424,7 +478,7 @@ end;
 
 
 class function TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
 class function TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
   const ARawPayload: TRawBytes;
   const ARawPayload: TRawBytes;
-//  const APayloadType : TPayloadType;
+  const APayloadType : TPayloadType;
   const APayload_method, AEncodePwdForAES: String;
   const APayload_method, AEncodePwdForAES: String;
   const ASenderAccounKey, ATargetAccountKey: TAccountKey;
   const ASenderAccounKey, ATargetAccountKey: TAccountKey;
   out AOperationPayload : TOperationPayload;
   out AOperationPayload : TOperationPayload;
@@ -432,11 +486,7 @@ class function TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
   var AErrorDesc: String): Boolean;
   var AErrorDesc: String): Boolean;
 begin
 begin
   AOperationPayload := CT_TOperationPayload_NUL;
   AOperationPayload := CT_TOperationPayload_NUL;
-   // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-  // TODO:
-  // Needs to assign AOperationPayload.payload_type based on PIP-0027
-  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-  //AOperationPayload.payload_type := APayloadType.ProtocolValue;
+  AOperationPayload.payload_type := APayloadType.ToProtocolValue;
   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;
@@ -519,6 +569,7 @@ Var i, nSigned, nNotSigned : Integer;
   opht : TOperationsHashTree;
   opht : TOperationsHashTree;
   jsonArr : TPCJSONArray;
   jsonArr : TPCJSONArray;
   auxObj : TPCJSONObject;
   auxObj : TPCJSONObject;
+  LStr : String;
 begin
 begin
   opht := TOperationsHashTree.Create;
   opht := TOperationsHashTree.Create;
   Try
   Try
@@ -539,18 +590,22 @@ begin
   //
   //
   jsonArr := jsonObject.GetAsArray('senders');
   jsonArr := jsonObject.GetAsArray('senders');
   for i:=Low(multiOperation.Data.txSenders) to High(multiOperation.Data.txSenders) do begin
   for i:=Low(multiOperation.Data.txSenders) to High(multiOperation.Data.txSenders) do begin
+    LStr := TCrypto.ToHexaString(multiOperation.Data.txSenders[i].Payload.payload_raw);
     auxObj := jsonArr.GetAsObject(jsonArr.Count);
     auxObj := jsonArr.GetAsObject(jsonArr.Count);
     auxObj.GetAsVariant('account').Value := multiOperation.Data.txSenders[i].Account;
     auxObj.GetAsVariant('account').Value := multiOperation.Data.txSenders[i].Account;
+    auxObj.GetAsVariant('account_epasa').Value := multiOperation.Data.txSenders[i].AccountEPASA.ToString();
     auxObj.GetAsVariant('n_operation').Value := multiOperation.Data.txSenders[i].N_Operation;
     auxObj.GetAsVariant('n_operation').Value := multiOperation.Data.txSenders[i].N_Operation;
     auxObj.GetAsVariant('amount').Value := TAccountComp.FormatMoneyDecimal(multiOperation.Data.txSenders[i].Amount * (-1));
     auxObj.GetAsVariant('amount').Value := TAccountComp.FormatMoneyDecimal(multiOperation.Data.txSenders[i].Amount * (-1));
-    auxObj.GetAsVariant('payload').Value := TCrypto.ToHexaString(multiOperation.Data.txSenders[i].Payload.payload_raw);
+    auxObj.GetAsVariant('payload').Value := LStr;
     auxObj.GetAsVariant('payload_type').Value := multiOperation.Data.txSenders[i].Payload.payload_type;
     auxObj.GetAsVariant('payload_type').Value := multiOperation.Data.txSenders[i].Payload.payload_type;
   end;
   end;
   //
   //
   jsonArr := jsonObject.GetAsArray('receivers');
   jsonArr := jsonObject.GetAsArray('receivers');
   for i:=Low(multiOperation.Data.txReceivers) to High(multiOperation.Data.txReceivers) do begin
   for i:=Low(multiOperation.Data.txReceivers) to High(multiOperation.Data.txReceivers) do begin
+    LStr := TCrypto.ToHexaString(multiOperation.Data.txSenders[i].Payload.payload_raw);
     auxObj := jsonArr.GetAsObject(jsonArr.Count);
     auxObj := jsonArr.GetAsObject(jsonArr.Count);
     auxObj.GetAsVariant('account').Value := multiOperation.Data.txReceivers[i].Account;
     auxObj.GetAsVariant('account').Value := multiOperation.Data.txReceivers[i].Account;
+    auxObj.GetAsVariant('account_epasa').Value := multiOperation.Data.txReceivers[i].AccountEPASA.ToString();
     auxObj.GetAsVariant('amount').Value := TAccountComp.FormatMoneyDecimal(multiOperation.Data.txReceivers[i].Amount);
     auxObj.GetAsVariant('amount').Value := TAccountComp.FormatMoneyDecimal(multiOperation.Data.txReceivers[i].Amount);
     auxObj.GetAsVariant('payload').Value := TCrypto.ToHexaString(multiOperation.Data.txReceivers[i].Payload.payload_raw);
     auxObj.GetAsVariant('payload').Value := TCrypto.ToHexaString(multiOperation.Data.txReceivers[i].Payload.payload_raw);
     auxObj.GetAsVariant('payload_type').Value := multiOperation.Data.txReceivers[i].Payload.payload_type;
     auxObj.GetAsVariant('payload_type').Value := multiOperation.Data.txReceivers[i].Payload.payload_type;
@@ -643,6 +698,27 @@ Begin
   End;
   End;
 end;
 end;
 
 
+
+class function TPascalCoinJSONComp.TryResolveOfflineEPASA(const AEPasa : TEPasa; out AResolvedAccount: Cardinal; out AErrorMessage: String): Boolean;
+begin
+  if (AEPasa.IsPayToKey) then begin
+    // PayToKey not supported in offline signing
+    AResolvedAccount := CT_AccountNo_NUL;
+    AErrorMessage := 'PayToKey not supported in offline signing';
+    Exit(False);
+ end else if (AEPasa.IsAddressedByName) then begin
+    // PayToKey not supported in offline signing
+    AResolvedAccount := CT_AccountNo_NUL;
+    AErrorMessage := 'Addressed-by-name EPASA not supported in offline signing';
+    Exit(False);
+ end;
+  // addressed by number
+  if NOT AEPasa.IsAddressedByNumber then raise Exception.Create('Internal Error 0293f104-fce6-46a5-853f-e91fb501b452');
+  if NOT AEPasa.Account.HasValue then raise Exception.Create('Internal Error b569cd90-8dd7-4fac-95c4-6508179dac03');
+  AResolvedAccount := AEPasa.Account.Value;
+  Result := true;
+end;
+
 class function TPascalCoinJSONComp.ToPascalCoins(jsonCurr: Real): Int64;
 class function TPascalCoinJSONComp.ToPascalCoins(jsonCurr: Real): Int64;
 begin
 begin
   Result := Round(jsonCurr * 10000);
   Result := Round(jsonCurr * 10000);
@@ -1208,7 +1284,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
   Begin
   Begin
     Result := Nil;
     Result := Nil;
     if Not RPCServer.CheckAndGetPrivateKeyInWallet(senderAccounKey,privateKey,ErrorNum,ErrorDesc) then Exit(Nil);
     if Not RPCServer.CheckAndGetPrivateKeyInWallet(senderAccounKey,privateKey,ErrorNum,ErrorDesc) then Exit(Nil);
-    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,Payload_method,EncodePwd,senderAccounKey,targetAccountKey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
+    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,[ptNonDeterministic],Payload_method,EncodePwd,senderAccounKey,targetAccountKey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
     Result := TOpTransaction.CreateTransaction(current_protocol, sender,sender_last_n_operation+1,target,privateKey,amount,fee,LOpPayload);
     Result := TOpTransaction.CreateTransaction(current_protocol, sender,sender_last_n_operation+1,target,privateKey,amount,fee,LOpPayload);
     if Not Result.HasValidSignature then begin
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
       FreeAndNil(Result);
@@ -1221,7 +1297,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
   Function CaptureAccountNumber(const AParamName : String; const ACheckAccountNumberExistsInSafebox : Boolean; var AAccountNumber : Cardinal; var AErrorParam : String) : Boolean;
   Function CaptureAccountNumber(const AParamName : String; const ACheckAccountNumberExistsInSafebox : Boolean; var AAccountNumber : Cardinal; var AErrorParam : String) : Boolean;
   var LParamValue : String;
   var LParamValue : String;
   Begin
   Begin
-    Result := TPascalCoinJSONComp.CaptureAccountNumber(params,AParamName,FNode.Bank,AAccountNumber,AErrorParam);
+    Result := TPascalCoinJSONComp.CaptureAccountNumber(params,AParamName,FNode,AAccountNumber,AErrorParam);
   End;
   End;
 
 
   // This function creates a TOpChangeKey without looking for private key of account
   // This function creates a TOpChangeKey without looking for private key of account
@@ -1236,7 +1312,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     Result := Nil;
     Result := Nil;
     LOpPayload := CT_TOperationPayload_NUL;
     LOpPayload := CT_TOperationPayload_NUL;
     if Not RPCServer.CheckAndGetPrivateKeyInWallet(account_pubkey,privateKey,ErrorNum,ErrorDesc) then Exit(Nil);
     if Not RPCServer.CheckAndGetPrivateKeyInWallet(account_pubkey,privateKey,ErrorNum,ErrorDesc) then Exit(Nil);
-    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,Payload_method,EncodePwd,account_pubkey,new_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
+    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,[ptNonDeterministic],Payload_method,EncodePwd,account_pubkey,new_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
     If account_signer=account_target then begin
     If account_signer=account_target then begin
       Result := TOpChangeKey.Create(current_protocol,account_signer,account_last_n_operation+1,account_target,privateKey,new_pubkey,fee,LOpPayload);
       Result := TOpChangeKey.Create(current_protocol,account_signer,account_last_n_operation+1,account_target,privateKey,new_pubkey,fee,LOpPayload);
     end else begin
     end else begin
@@ -1303,7 +1379,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         // If using 'dest', only will apply if there is a fixed new public key, otherwise will use current public key of account
         // If using 'dest', only will apply if there is a fixed new public key, otherwise will use current public key of account
        aux_target_pubkey := new_account_pubkey;
        aux_target_pubkey := new_account_pubkey;
     end else aux_target_pubkey := account_signer_pubkey;
     end else aux_target_pubkey := account_signer_pubkey;
-    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,Payload_method,EncodePwd,account_signer_pubkey,aux_target_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
+    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,[ptNonDeterministic],Payload_method,EncodePwd,account_signer_pubkey,aux_target_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
     Result := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(
     Result := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(
       current_protocol,
       current_protocol,
       ANewAccountState,
       ANewAccountState,
@@ -1338,7 +1414,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     Result := Nil;
     Result := Nil;
     LOpPayload := CT_TOperationPayload_NUL;
     LOpPayload := CT_TOperationPayload_NUL;
     if Not RPCServer.CheckAndGetPrivateKeyInWallet(account_signer_pubkey,privateKey,ErrorNum,ErrorDesc) then Exit(Nil);
     if Not RPCServer.CheckAndGetPrivateKeyInWallet(account_signer_pubkey,privateKey,ErrorNum,ErrorDesc) then Exit(Nil);
-    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,Payload_method,EncodePwd,account_signer_pubkey,account_signer_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
+    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload, [ptNonDeterministic],Payload_method,EncodePwd,account_signer_pubkey,account_signer_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
     Result := TOpDelistAccountForSale.CreateDelistAccountForSale(current_protocol,account_signer,account_last_n_operation+1,account_delisted,fee,privateKey,LOpPayload);
     Result := TOpDelistAccountForSale.CreateDelistAccountForSale(current_protocol,account_signer,account_last_n_operation+1,account_delisted,fee,privateKey,LOpPayload);
     if Not Result.HasValidSignature then begin
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
       FreeAndNil(Result);
@@ -1361,7 +1437,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
   Begin
   Begin
     Result := Nil;
     Result := Nil;
     if Not RPCServer.CheckAndGetPrivateKeyInWallet(account_pubkey,privateKey,ErrorNum,ErrorDesc) then Exit(Nil);
     if Not RPCServer.CheckAndGetPrivateKeyInWallet(account_pubkey,privateKey,ErrorNum,ErrorDesc) then Exit(Nil);
-    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,Payload_method,EncodePwd,account_pubkey,new_account_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
+    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,[ptNonDeterministic],Payload_method,EncodePwd,account_pubkey,new_account_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
     Result := TOpBuyAccount.CreateBuy(current_protocol,account_number,account_last_n_operation+1,account_to_buy,account_to_pay,account_price,amount,fee,new_account_pubkey,privateKey,LOpPayload);
     Result := TOpBuyAccount.CreateBuy(current_protocol,account_number,account_last_n_operation+1,account_to_buy,account_to_pay,account_price,amount,fee,new_account_pubkey,privateKey,LOpPayload);
     if Not Result.HasValidSignature then begin
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
       FreeAndNil(Result);
@@ -1866,7 +1942,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         // If using 'dest', only will apply if there is a fixed new public key, otherwise will use current public key of account
         // If using 'dest', only will apply if there is a fixed new public key, otherwise will use current public key of account
        aux_target_pubkey := new_account_pubkey;
        aux_target_pubkey := new_account_pubkey;
     end else aux_target_pubkey := account_signer_pubkey;
     end else aux_target_pubkey := account_signer_pubkey;
-    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,Payload_method,EncodePwd,account_signer_pubkey,aux_target_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
+    if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(RawPayload,[ptNonDeterministic],Payload_method,EncodePwd,account_signer_pubkey,aux_target_pubkey,LOpPayload,ErrorNum,ErrorDesc) then Exit(Nil);
     Result := TOpChangeAccountInfo.CreateChangeAccountInfo(current_protocol,
     Result := TOpChangeAccountInfo.CreateChangeAccountInfo(current_protocol,
       account_signer,account_last_n_operation+1,account_target,
       account_signer,account_last_n_operation+1,account_target,
       privateKey,
       privateKey,
@@ -2370,6 +2446,45 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       Result := FNode.GetMempoolAccount( nAccount );
       Result := FNode.GetMempoolAccount( nAccount );
     end;
     end;
 
 
+    Function TryCaptureEPASA(const AJSONObj : TPCJSONObject; out AAccount : Cardinal; out AErrorNum : Integer; out AErrorDesc : String) : Boolean;
+    var LEPasa : TEPasa;
+    begin
+      // Parse EPASA
+      if NOT TEPasa.TryParse(AJSONObj.AsString('account',''), LEPasa) then begin
+        AErrorNum := CT_RPC_ErrNum_InvalidData;
+        AErrorDesc := 'Field "account" missing or invalid EPASA format';
+        Exit(False);
+      end;
+
+      // Resolve EPASA (note: PayToKey returns error in this resolution method)
+      if NOT FNode.TryResolveEPASA(LEPasa, AAccount, AErrorDesc) then begin
+        AErrorNum := CT_RPC_ErrNum_InvalidEPASA;
+        Exit(False);
+      end;
+
+      // Payload override
+      if LEPasa.HasPayload then begin
+        // Only support public payloads for now
+        if NOT LEPasa.PayloadType.HasTrait(ptPublic) then begin
+          AErrorNum := CT_RPC_ErrNum_NotImplemented;
+          AErrorDesc := 'Encrypted payloads not currently supported in DATA operation';
+          Exit(false);
+        end;
+
+        // Ensure no ambiguity with payload arguments
+        if AJSONObj.HasValue('payload') OR AJSONObj.HasValue('payload_type') then begin
+          AErrorNum := CT_RPC_ErrNum_AmbiguousPayload;
+          AErrorDesc := 'Ambiguous Payload between EPASA and method arguments';
+          Exit(False);
+        end;
+        // Override the JSON args (processed later by caller)
+        AJSONObj.SetAs('payload', TPCJSONVariantValue.CreateFromVariant(LEPasa.GetRawPayloadBytes.ToHexaString));
+        AJSONObj.SetAs('payload_type', TPCJSONVariantValue.CreateFromVariant(LEPasa.PayloadType.ToProtocolValue));
+      end;
+      AAccount := LEPasa.Account.Value;
+      Result := True;
+    end;
+
   var errors : String;
   var errors : String;
     OperationsHashTree : TOperationsHashTree;
     OperationsHashTree : TOperationsHashTree;
     jsonArr : TPCJSONArray;
     jsonArr : TPCJSONArray;
@@ -2378,6 +2493,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     receiver : TMultiOpReceiver;
     receiver : TMultiOpReceiver;
     changeinfo : TMultiOpChangeInfo;
     changeinfo : TMultiOpChangeInfo;
     mop : TOpMultiOperation;
     mop : TOpMultiOperation;
+    LEPASA : TEPasa;
   begin
   begin
     { This will ADD or UPDATE a MultiOperation with NEW field/s
     { This will ADD or UPDATE a MultiOperation with NEW field/s
       - UPDATE: If LAST operation in HexaStringOperationsHashTree RAW value contains a MultiOperation
       - UPDATE: If LAST operation in HexaStringOperationsHashTree RAW value contains a MultiOperation
@@ -2405,7 +2521,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         }
         }
     Result := false;
     Result := false;
     if Not HexaStringToOperationsHashTreeAndGetMultioperation(
     if Not HexaStringToOperationsHashTreeAndGetMultioperation(
-      Self.FNode.Bank.SafeBox.CurrentProtocol, // HS: 2019-07-09: use current protocol since this API used to build new unpublished operations, not historical ones
+      Self.FNode.Bank.SafeBox.CurrentProtocol,
       HexaStringOperationsHashTree,True,OperationsHashTree,mop,errors) then begin
       HexaStringOperationsHashTree,True,OperationsHashTree,mop,errors) then begin
       ErrorNum:=CT_RPC_ErrNum_InvalidData;
       ErrorNum:=CT_RPC_ErrNum_InvalidData;
       ErrorDesc:= 'Error decoding param previous operations hash tree raw value: '+errors;
       ErrorDesc:= 'Error decoding param previous operations hash tree raw value: '+errors;
@@ -2416,11 +2532,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       jsonArr := params.GetAsArray('senders');
       jsonArr := params.GetAsArray('senders');
       for i:=0 to jsonArr.Count-1 do begin
       for i:=0 to jsonArr.Count-1 do begin
         sender := CT_TMultiOpSender_NUL;
         sender := CT_TMultiOpSender_NUL;
-        if not TAccountComp.AccountTxtNumberToAccountNumber(jsonArr.GetAsObject(i).AsString('account',''),sender.Account) then begin
-          ErrorNum := CT_RPC_ErrNum_InvalidData;
-          ErrorDesc := 'Field "account" for "senders" array not found at senders['+IntToStr(i)+']';
+        if NOT TryCaptureEPASA(jsonArr.GetAsObject(i), sender.Account, ErrorNum, ErrorDesc) then
           Exit;
           Exit;
-        end;
         sender.Amount:= ToPascalCoins(jsonArr.GetAsObject(i).AsDouble('amount',0));
         sender.Amount:= ToPascalCoins(jsonArr.GetAsObject(i).AsDouble('amount',0));
         sender.N_Operation:=jsonArr.GetAsObject(i).AsInteger('n_operation',0);
         sender.N_Operation:=jsonArr.GetAsObject(i).AsInteger('n_operation',0);
         // Update N_Operation with valid info
         // Update N_Operation with valid info
@@ -2438,11 +2551,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       jsonArr := params.GetAsArray('receivers');
       jsonArr := params.GetAsArray('receivers');
       for i:=0 to jsonArr.Count-1 do begin
       for i:=0 to jsonArr.Count-1 do begin
         receiver := CT_TMultiOpReceiver_NUL;
         receiver := CT_TMultiOpReceiver_NUL;
-        if not TAccountComp.AccountTxtNumberToAccountNumber(jsonArr.GetAsObject(i).AsString('account',''),receiver.Account) then begin
-          ErrorNum := CT_RPC_ErrNum_InvalidData;
-          ErrorDesc := 'Field "account" for "receivers" array not found at receivers['+IntToStr(i)+']';
+        if NOT TryCaptureEPASA(jsonArr.GetAsObject(i), receiver.Account, ErrorNum, ErrorDesc) then
           Exit;
           Exit;
-        end;
         receiver.Amount:= ToPascalCoins(jsonArr.GetAsObject(i).AsDouble('amount',0));
         receiver.Amount:= ToPascalCoins(jsonArr.GetAsObject(i).AsDouble('amount',0));
         receiver.Payload.payload_raw:=TCrypto.HexaToRaw(jsonArr.GetAsObject(i).AsString('payload',''));
         receiver.Payload.payload_raw:=TCrypto.HexaToRaw(jsonArr.GetAsObject(i).AsString('payload',''));
         receiver.Payload.payload_type := jsonArr.GetAsObject(i).AsInteger('payload_type',CT_TOperationPayload_NUL.payload_type);
         receiver.Payload.payload_type := jsonArr.GetAsObject(i).AsInteger('payload_type',CT_TOperationPayload_NUL.payload_type);

+ 107 - 26
src/core/USettings.pas

@@ -39,6 +39,7 @@ const
   CT_PARAM_SaveDebugLogs = 'SaveDebugLogs';
   CT_PARAM_SaveDebugLogs = 'SaveDebugLogs';
   CT_PARAM_ShowLogs = 'ShowLogs';
   CT_PARAM_ShowLogs = 'ShowLogs';
   CT_PARAM_MinerName = 'MinerName';
   CT_PARAM_MinerName = 'MinerName';
+  CT_PARAM_MaxPayToKeyPurchasePrice = 'MaxPayToKeyPurchasePrice';
   CT_PARAM_RunCount = 'RunCount';
   CT_PARAM_RunCount = 'RunCount';
   CT_PARAM_FirstTime = 'FirstTime';
   CT_PARAM_FirstTime = 'FirstTime';
   CT_PARAM_ShowModalMessages = 'ShowModalMessages';
   CT_PARAM_ShowModalMessages = 'ShowModalMessages';
@@ -61,6 +62,10 @@ type
 
 
   TMinerPrivateKeyType = (mpk_NewEachTime, mpk_Random, mpk_Selected);
   TMinerPrivateKeyType = (mpk_NewEachTime, mpk_Random, mpk_Selected);
 
 
+  { TShowHashRateAs }
+
+  TShowHashRateAs = (hr_Unit, hr_Kilo, hr_Mega, hr_Giga, hr_Tera, hr_Peta, hr_Exa);
+
   { TSettings }
   { TSettings }
 
 
   TSettings = class
   TSettings = class
@@ -72,19 +77,15 @@ type
       class function GetMinFutureBlocksToDownloadNewSafebox: Integer; static;
       class function GetMinFutureBlocksToDownloadNewSafebox: Integer; static;
       class procedure SetAllowDownloadNewCheckpointIfOlderThan(ABool: Boolean); static;
       class procedure SetAllowDownloadNewCheckpointIfOlderThan(ABool: Boolean); static;
       class procedure SetInternetServerPort(AInt:Integer); static;
       class procedure SetInternetServerPort(AInt:Integer); static;
-      class function GetRpcPortEnabled : boolean; static;
+      class function GetJsonRpcPortEnabled : boolean; static;
       class procedure SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer); static;
       class procedure SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer); static;
-      class procedure SetRpcPortEnabled(ABool: boolean); static;
+      class procedure SetJsonRpcPortEnabled(ABool: boolean); static;
       class function GetDefaultFee : Int64; static;
       class function GetDefaultFee : Int64; static;
       class procedure SetDefaultFee(AInt64: Int64); static;
       class procedure SetDefaultFee(AInt64: Int64); static;
       class function GetMinerPrivateKeyType : TMinerPrivateKeyType; static;
       class function GetMinerPrivateKeyType : TMinerPrivateKeyType; static;
       class procedure SetMinerPrivateKeyType(AType: TMinerPrivateKeyType); static;
       class procedure SetMinerPrivateKeyType(AType: TMinerPrivateKeyType); static;
-      class function GetMinerSelectedPrivateKey : TRawBytes; static;
-      class procedure SetMinerSelectedPrivateKey(AKey:TRawBytes); static;
-      class function GetMinerServerRpcActive : boolean; static;
-      class procedure SetMinerServerRpcActive(ABool: Boolean); static;
-      class function GetMinerServerRpcPort : Integer; static;
-      class procedure SetMinerServerRpcPort(APort: Integer); static;
+      class function GetMinerSelectedPublicKey : TRawBytes; static;
+      class procedure SetMinerSelectedPublicKey(AKey:TRawBytes); static;
       class function GetSaveLogFiles : boolean; static;
       class function GetSaveLogFiles : boolean; static;
       class procedure SetSaveLogFiles(ABool: boolean); static;
       class procedure SetSaveLogFiles(ABool: boolean); static;
       class function GetShowLogs : boolean; static;
       class function GetShowLogs : boolean; static;
@@ -95,14 +96,27 @@ type
       class procedure SetMinerName(AName: string); static;
       class procedure SetMinerName(AName: string); static;
       class function GetRunCount : Integer; static;
       class function GetRunCount : Integer; static;
       class procedure SetRunCount(AInt: Integer); static;
       class procedure SetRunCount(AInt: Integer); static;
+      class function GetFirstTime : Boolean; static;
+      class procedure SetFirstTime(ABool: Boolean); static;
+      class function GetMaxPayToKeyPurchasePrice : UInt64; static;
+      class procedure SetMaxPayToKeyPurchasePrice(AVal: UInt64); static;
       class function GetShowModalMessages : boolean; static;
       class function GetShowModalMessages : boolean; static;
       class procedure SetShowModalMessages(ABool: boolean); static;
       class procedure SetShowModalMessages(ABool: boolean); static;
-      class function GetRpcAllowedIPs : string; static;
-      class procedure SetRpcAllowedIPs(AString: string); static;
+      class function GetJsonRpcAllowedIPs : string; static;
+      class procedure SetJsonRpcAllowedIPs(AString: string); static;
+      class function GetJsonRpcMinerServerActive : boolean; static;
+      class procedure SetJsonRpcMinerServerActive(ABool: Boolean); static;
+      class function GetMinerServerRpcPort : Integer; static;
+      class procedure SetMinerServerRpcPort(APort: Integer); static;
+      class function GetHashRateAvgBlocksCount : Integer; static;
+      class procedure SetHashRateAvgBlocksCount(AInt: Integer); static;
+      class function GetShowHashRateAs : TShowHashRateAs; static;
+      class procedure SetShowHashRateAs(AVal: TShowHashRateAs); static;
       class function GetPeerCache : string; static;
       class function GetPeerCache : string; static;
       class procedure SetPeerCache(AString: string); static;
       class procedure SetPeerCache(AString: string); static;
       class function GetTryConnectOnlyWithThisFixedServers : string; static;
       class function GetTryConnectOnlyWithThisFixedServers : string; static;
       class procedure SetTryConnectOnlyWithThisFixedServers(AString: string); static;
       class procedure SetTryConnectOnlyWithThisFixedServers(AString: string); static;
+      class procedure CheckNotLoaded;
       class procedure CheckLoaded;
       class procedure CheckLoaded;
       class procedure NotifyOnChanged;
       class procedure NotifyOnChanged;
     public
     public
@@ -110,18 +124,22 @@ type
       class procedure Save;
       class procedure Save;
       class property OnChanged : TNotifyManyEvent read FOnChanged;
       class property OnChanged : TNotifyManyEvent read FOnChanged;
       class property InternetServerPort : Integer read GetInternetServerPort write SetInternetServerPort;
       class property InternetServerPort : Integer read GetInternetServerPort write SetInternetServerPort;
-      class property RpcPortEnabled : boolean read GetRpcPortEnabled write SetRpcPortEnabled;
-      class property RpcAllowedIPs : string read GetRpcAllowedIPs write SetRpcAllowedIPs;
+      class property JsonRpcPortEnabled : boolean read GetJsonRpcPortEnabled write SetJsonRpcPortEnabled;
+      class property JsonRpcAllowedIPs : string read GetJsonRpcAllowedIPs write SetJsonRpcAllowedIPs;
+      class property JsonRpcMinerServerActive : boolean read GetJsonRpcMinerServerActive write SetJsonRpcMinerServerActive;
+      class property JsonRpcMinerServerPort : Integer read GetMinerServerRpcPort write SetMinerServerRpcPort;
+      class property HashRateAvgBlocksCount : Integer read GetHashRateAvgBlocksCount write SetHashRateAvgBlocksCount;
+      class property ShowHashRateAs : TShowHashRateAs read GetShowHashRateAs write SetShowHashRateAs;
       class property DefaultFee : Int64 read GetDefaultFee write SetDefaultFee;
       class property DefaultFee : Int64 read GetDefaultFee write SetDefaultFee;
       class property MinerPrivateKeyType : TMinerPrivateKeyType read GetMinerPrivateKeyType write SetMinerPrivateKeyType;
       class property MinerPrivateKeyType : TMinerPrivateKeyType read GetMinerPrivateKeyType write SetMinerPrivateKeyType;
-      class property MinerSelectedPrivateKey : TRawBytes read GetMinerSelectedPrivateKey write SetMinerSelectedPrivateKey;
-      class property MinerServerRpcActive : boolean read GetMinerServerRpcActive write SetMinerServerRpcActive;
-      class property MinerServerRpcPort : Integer read GetMinerServerRpcPort write SetMinerServerRpcPort;
+      class property MinerSelectedPublicKey : TRawBytes read GetMinerSelectedPublicKey write SetMinerSelectedPublicKey;
       class property SaveLogFiles : boolean read GetSaveLogFiles write SetSaveLogFiles;
       class property SaveLogFiles : boolean read GetSaveLogFiles write SetSaveLogFiles;
       class property ShowLogs : boolean read GetShowLogs write SetShowLogs;
       class property ShowLogs : boolean read GetShowLogs write SetShowLogs;
       class property SaveDebugLogs : boolean read GetSaveDebugLogs write SetSaveDebugLogs;
       class property SaveDebugLogs : boolean read GetSaveDebugLogs write SetSaveDebugLogs;
       class property MinerName : string read GetMinerName write SetMinerName;
       class property MinerName : string read GetMinerName write SetMinerName;
       class property RunCount : Integer read GetRunCount write SetRunCount;
       class property RunCount : Integer read GetRunCount write SetRunCount;
+      class property FirstTime : Boolean read GetFirstTime write SetFirstTime;
+      class property MaxPayToKeyPurchasePrice : UInt64 read GetMaxPayToKeyPurchasePrice write SetMaxPayToKeyPurchasePrice;
       class property ShowModalMessages : boolean read GetShowModalMessages write SetShowModalMessages;
       class property ShowModalMessages : boolean read GetShowModalMessages write SetShowModalMessages;
       class property PeerCache : string read GetPeerCache write SetPeerCache;
       class property PeerCache : string read GetPeerCache write SetPeerCache;
       class property TryConnectOnlyWithThisFixedServers : string read GetTryConnectOnlyWithThisFixedServers write SetTryConnectOnlyWithThisFixedServers;
       class property TryConnectOnlyWithThisFixedServers : string read GetTryConnectOnlyWithThisFixedServers write SetTryConnectOnlyWithThisFixedServers;
@@ -140,13 +158,22 @@ uses
 
 
 class procedure TSettings.Load;
 class procedure TSettings.Load;
 begin
 begin
+  CheckNotLoaded;
   FAppParams := TAppParams.Create(nil);
   FAppParams := TAppParams.Create(nil);
   FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
   FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+
+  If FAppParams.FindParam(CT_PARAM_MinerName)=Nil then begin
+    // New configuration... assigning a new random value
+    FAppParams.ParamByName[CT_PARAM_MinerName].SetAsString('New Node '+DateTimeToStr(Now)+' - '+ CT_ClientAppVersion);
+  end;
+
 end;
 end;
 
 
 class procedure TSettings.Save;
 class procedure TSettings.Save;
 begin
 begin
   //TODO Update FAppParams to optionally save on set value, and make FApp.Save public and verify all AppParams updates in client code
   //TODO Update FAppParams to optionally save on set value, and make FApp.Save public and verify all AppParams updates in client code
+  CheckLoaded;
+  OnChanged.Invoke(Nil);
 end;
 end;
 
 
 class function TSettings.GetInternetServerPort : Integer;
 class function TSettings.GetInternetServerPort : Integer;
@@ -179,36 +206,60 @@ begin
   FAppParams.ParamByName[CT_PARAM_InternetServerPort].SetAsInteger(AInt);
   FAppParams.ParamByName[CT_PARAM_InternetServerPort].SetAsInteger(AInt);
 end;
 end;
 
 
-class function TSettings.GetRpcPortEnabled : boolean;
+class procedure TSettings.SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
-  Result := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
+  FAppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].SetAsInteger(AInt);
 end;
 end;
 
 
-class procedure TSettings.SetMinFutureBlocksToDownloadNewSafebox(AInt: Integer);
+class function TSettings.GetJsonRpcPortEnabled : boolean;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
-  FAppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].SetAsInteger(AInt);
+  Result := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
 end;
 end;
 
 
-class procedure TSettings.SetRpcPortEnabled(ABool: boolean);
+class procedure TSettings.SetJsonRpcPortEnabled(ABool: boolean);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].SetAsBoolean(ABool);
   FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].SetAsBoolean(ABool);
 end;
 end;
 
 
-class function TSettings.GetRpcAllowedIPs : string;
+class function TSettings.GetJsonRpcAllowedIPs : string;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1;');
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1;');
 end;
 end;
 
 
-class procedure TSettings.SetRpcAllowedIPs(AString: string);
+class procedure TSettings.SetJsonRpcAllowedIPs(AString: string);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].SetAsString(AString);
   FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].SetAsString(AString);
 end;
 end;
 
 
+class function TSettings.GetHashRateAvgBlocksCount : Integer;
+begin
+  CheckLoaded;
+  Result := FAppParams.ParamByName[CT_PARAM_HashRateAvgBlocksCount].GetAsInteger(50)
+end;
+
+class procedure TSettings.SetHashRateAvgBlocksCount(AInt: Integer);
+begin
+  CheckLoaded;
+  FAppParams.ParamByName[CT_PARAM_HashRateAvgBlocksCount].SetAsInteger(AInt);
+end;
+
+class function TSettings.GetShowHashRateAs : TShowHashRateAs;
+begin
+  CheckLoaded;
+  Result := TShowHashRateAs(FAppParams.ParamByName[CT_PARAM_ShowHashRateAs].GetAsInteger(Integer({$IFDEF TESTNET}hr_Mega{$ELSE}hr_Tera{$ENDIF})));
+end;
+
+class procedure TSettings.SetShowHashRateAs(AVal: TShowHashRateAs);
+begin
+  CheckLoaded;
+  FAppParams.ParamByName[CT_PARAM_ShowHashRateAs].SetAsInteger(Integer(AVal));
+end;
+
 class function TSettings.GetDefaultFee : Int64;
 class function TSettings.GetDefaultFee : Int64;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
@@ -221,13 +272,13 @@ begin
   FAppParams.ParamByName[CT_PARAM_DefaultFee].SetAsInt64(AInt64);
   FAppParams.ParamByName[CT_PARAM_DefaultFee].SetAsInt64(AInt64);
 end;
 end;
 
 
-class function TSettings.GetMinerServerRpcActive : boolean;
+class function TSettings.GetJsonRpcMinerServerActive : boolean;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
   Result := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
 end;
 end;
 
 
-class procedure TSettings.SetMinerServerRpcActive(ABool: Boolean);
+class procedure TSettings.SetJsonRpcMinerServerActive(ABool: Boolean);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].SetAsBoolean(ABool);
   FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].SetAsBoolean(ABool);
@@ -257,13 +308,13 @@ begin
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].SetAsInteger(Integer(AType));
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].SetAsInteger(Integer(AType));
 end;
 end;
 
 
-class function TSettings.GetMinerSelectedPrivateKey : TRawBytes;
+class function TSettings.GetMinerSelectedPublicKey : TRawBytes;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   Result := FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].GetAsTBytes(Nil);
   Result := FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].GetAsTBytes(Nil);
 end;
 end;
 
 
-class procedure TSettings.SetMinerSelectedPrivateKey(AKey:TRawBytes);
+class procedure TSettings.SetMinerSelectedPublicKey(AKey:TRawBytes);
 begin
 begin
   CheckLoaded;
   CheckLoaded;
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].SetAsTBytes(AKey);
   FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].SetAsTBytes(AKey);
@@ -329,6 +380,30 @@ begin
   FAppParams.ParamByName[CT_PARAM_RunCount].SetAsInteger(AInt)
   FAppParams.ParamByName[CT_PARAM_RunCount].SetAsInteger(AInt)
 end;
 end;
 
 
+class function TSettings.GetFirsttime : Boolean;
+begin
+  CheckLoaded;
+  Result := FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true);
+end;
+
+class procedure TSettings.SetFirstTime(ABool: Boolean);
+begin
+  CheckLoaded;
+  FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(ABool);
+end;
+
+class function TSettings.GetMaxPayToKeyPurchasePrice : UInt64;
+begin
+  CheckLoaded;
+  Result := FAppParams.ParamByName[CT_PARAM_MaxPayToKeyPurchasePrice].GetAsUInt64(CT_DEFAULT_PAY_TO_KEY_MAX_MOLINAS);
+end;
+
+class procedure TSettings.SetMaxPayToKeyPurchasePrice(AVal: UInt64);
+begin
+  CheckLoaded;
+  FAppParams.ParamByName[CT_PARAM_MaxPayToKeyPurchasePrice].SetAsUInt64(AVal);
+end;
+
 class function TSettings.GetShowModalMessages : boolean;
 class function TSettings.GetShowModalMessages : boolean;
 begin
 begin
   CheckLoaded;
   CheckLoaded;
@@ -365,6 +440,12 @@ begin
   FAppParams.ParamByName[CT_PARAM_TryToConnectOnlyWithThisFixedServers].SetAsString(Trim(AString));
   FAppParams.ParamByName[CT_PARAM_TryToConnectOnlyWithThisFixedServers].SetAsString(Trim(AString));
 end;
 end;
 
 
+class procedure TSettings.CheckNotLoaded;
+begin
+  if Assigned(FAppParams) then
+    raise Exception.Create('Application settings have already been loaded');
+end;
+
 class procedure TSettings.CheckLoaded;
 class procedure TSettings.CheckLoaded;
 begin
 begin
   if not Assigned(FAppParams) then
   if not Assigned(FAppParams) then

+ 2 - 2
src/core/UWallet.pas

@@ -592,7 +592,7 @@ begin
   Result := CT_TECDSA_Public_Nul;
   Result := CT_TECDSA_Public_Nul;
   case TSettings.MinerPrivateKeyType of
   case TSettings.MinerPrivateKeyType of
     mpk_NewEachTime: PublicK := CT_TECDSA_Public_Nul;
     mpk_NewEachTime: PublicK := CT_TECDSA_Public_Nul;
-    mpk_Selected: PublicK := TAccountComp.RawString2Accountkey(TSettings.MinerSelectedPrivateKey);
+    mpk_Selected: PublicK := TAccountComp.RawString2Accountkey(TSettings.MinerSelectedPublicKey);
     mpk_Random: begin
     mpk_Random: begin
       PublicK := CT_TECDSA_Public_Nul;
       PublicK := CT_TECDSA_Public_Nul;
       if FKeys.Count>0 then PublicK := FKeys.Key[Random(FKeys.Count)].AccountKey;
       if FKeys.Count>0 then PublicK := FKeys.Key[Random(FKeys.Count)].AccountKey;
@@ -612,7 +612,7 @@ begin
       // Set to Settings if not mpk_NewEachTime
       // Set to Settings if not mpk_NewEachTime
       if (TSettings.MinerPrivateKeyType<>mpk_NewEachTime) then begin
       if (TSettings.MinerPrivateKeyType<>mpk_NewEachTime) then begin
         TSettings.MinerPrivateKeyType:=mpk_Selected;
         TSettings.MinerPrivateKeyType:=mpk_Selected;
-        TSettings.MinerSelectedPrivateKey := TAccountComp.AccountKey2RawString(PublicK);
+        TSettings.MinerSelectedPublicKey := TAccountComp.AccountKey2RawString(PublicK);
       end;
       end;
     finally
     finally
       PK.Free;
       PK.Free;

+ 9 - 4
src/gui-classic/UFRMOperation.dfm

@@ -295,11 +295,14 @@ object FRMOperation: TFRMOperation
         Top = 7
         Top = 7
         Width = 524
         Width = 524
         Height = 204
         Height = 204
-        ActivePage = tsBuyAccount
+        ActivePage = tsTransaction
         TabOrder = 0
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
         object tsTransaction: TTabSheet
           Caption = 'Transaction'
           Caption = 'Transaction'
+          DesignSize = (
+            516
+            176)
           object lblDestAccount: TLabel
           object lblDestAccount: TLabel
             Left = 13
             Left = 13
             Top = 32
             Top = 32
@@ -335,10 +338,11 @@ object FRMOperation: TFRMOperation
             ParentFont = False
             ParentFont = False
           end
           end
           object sbSearchDestinationAccount: TSpeedButton
           object sbSearchDestinationAccount: TSpeedButton
-            Left = 208
+            Left = 490
             Top = 29
             Top = 29
             Width = 23
             Width = 23
             Height = 22
             Height = 22
+            Anchors = [akTop, akRight]
             Glyph.Data = {
             Glyph.Data = {
               36030000424D3603000000000000360000002800000010000000100000000100
               36030000424D3603000000000000360000002800000010000000100000000100
               18000000000000030000120B0000120B00000000000000000000FF00FF4A667C
               18000000000000030000120B0000120B00000000000000000000FF00FF4A667C
@@ -369,10 +373,11 @@ object FRMOperation: TFRMOperation
             OnClick = sbSearchDestinationAccountClick
             OnClick = sbSearchDestinationAccountClick
           end
           end
           object ebDestAccount: TEdit
           object ebDestAccount: TEdit
-            Left = 115
+            Left = 114
             Top = 29
             Top = 29
-            Width = 87
+            Width = 370
             Height = 21
             Height = 21
+            Anchors = [akLeft, akTop, akRight]
             TabOrder = 0
             TabOrder = 0
           end
           end
           object ebAmount: TEdit
           object ebAmount: TEdit

+ 283 - 223
src/gui-classic/UFRMOperation.pas

@@ -32,7 +32,7 @@ uses
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, StdCtrls, UNode, UWallet, UCrypto, Buttons, UBlockChain,
   Dialogs, StdCtrls, UNode, UWallet, UCrypto, Buttons, UBlockChain,
   UAccounts, UFRMAccountSelect, ActnList, ComCtrls, Types, UFRMMemoText,
   UAccounts, UFRMAccountSelect, ActnList, ComCtrls, Types, UFRMMemoText,
-  UPCEncryption, UBaseTypes, UPCDataTypes, UPCOrderedLists;
+  UPCEncryption, UBaseTypes, UPCDataTypes, UPCOrderedLists, UEPasa;
 
 
 Const
 Const
   CM_PC_WalletKeysChanged = WM_USER + 1;
   CM_PC_WalletKeysChanged = WM_USER + 1;
@@ -166,8 +166,8 @@ type
     Procedure UpdateAccountsInfo;
     Procedure UpdateAccountsInfo;
     Function UpdateFee(var Fee : Int64; errors : String) : Boolean;
     Function UpdateFee(var Fee : Int64; errors : String) : Boolean;
     Function UpdateOperationOptions(var errors : String) : Boolean;
     Function UpdateOperationOptions(var errors : String) : Boolean;
-    Function UpdatePayload(Const SenderAccount : TAccount; var errors : String) : Boolean;
-    Function UpdateOpTransaction(Const SenderAccount : TAccount; var DestAccount : TAccount; var amount : Int64; var errors : String) : Boolean;
+    Function UpdatePayload(Const ASenderAccount : TAccount; var AErrors : String) : Boolean;
+    Function UpdateOpTransaction(const ASenderAccount: TAccount; out LTargetEPASA : TEPasa;  out ATargetAccount : TAccount; out AResolvedTargetKey : TAccountKey; out ATargetRequiresPurchase : Boolean; out AAmount: Int64; out AErrors: String) : Boolean;
     Function UpdateOpChangeKey(Const TargetAccount : TAccount; var SignerAccount : TAccount; var NewPublicKey : TAccountKey; var errors : String) : Boolean;
     Function UpdateOpChangeKey(Const TargetAccount : TAccount; var SignerAccount : TAccount; var NewPublicKey : TAccountKey; var errors : String) : Boolean;
     Function UpdateOpListAccount(Const TargetAccount : TAccount; var SalePrice : Int64; var SellerAccount,SignerAccount : TAccount; var NewOwnerPublicKey : TAccountKey; var LockedUntilBlock : Cardinal; var HashLock : T32Bytes; var errors : String) : Boolean;
     Function UpdateOpListAccount(Const TargetAccount : TAccount; var SalePrice : Int64; var SellerAccount,SignerAccount : TAccount; var NewOwnerPublicKey : TAccountKey; var LockedUntilBlock : Cardinal; var HashLock : T32Bytes; var errors : String) : Boolean;
     Function UpdateOpDelist(Const TargetAccount : TAccount; var SignerAccount : TAccount; var errors : String) : Boolean;
     Function UpdateOpDelist(Const TargetAccount : TAccount; var SignerAccount : TAccount; var errors : String) : Boolean;
@@ -203,215 +203,224 @@ uses
 { TFRMOperation }
 { TFRMOperation }
 
 
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
 procedure TFRMOperation.actExecuteExecute(Sender: TObject);
-Var errors : String;
-  P : PAccount;
-  i,iAcc : Integer;
+var
+  LErrors : String;
+  LAccountPtr : PAccount;
+  i,LAccountNo : Integer;
   LKey : TWalletKey;
   LKey : TWalletKey;
-  ops : TOperationsHashTree;
-  op : TPCOperation;
-  account,signerAccount,destAccount,accountToBuy : TAccount;
-  operation_to_string, operationstxt, auxs : String;
-  _amount,_fee, _totalamount, _totalfee, _totalSignerFee, _salePrice : Int64;
-  _lockedUntil, _signer_n_ops : Cardinal;
-  dooperation : Boolean;
-  _newOwnerPublicKey : TECDSA_Public;
+  LOperations : TOperationsHashTree;
+  LOperation : TPCOperation;
+  LAccount,LSignerAccount,LTargetAccount,LPurchaseAccount : TAccount;
+  LTargetEPASA : TEPasa;
+  LOperationString, LOperationsTxt, LAuxStr : String;
+  LAmount, LFee, LTotalamount, LTotalFee, LTotalSignerFee, LSalePrice : Int64;
+  LLockedUntil, LSignerNOps : Cardinal;
+  LDoOperation : Boolean;
+  LNewOwnerPublicKey : TECDSA_Public;
   LHashLock : T32Bytes;
   LHashLock : T32Bytes;
-  _newName, LNewAccountData : TRawBytes;
-  _newType : Word;
-  _changeName, _changeType, LChangeAccountData, _V2, _executeSigner, LRecipientSigned : Boolean;
-  _senderAccounts : TCardinalsArray;
+  LNewName, LNewAccountData : TRawBytes;
+  LNewType : Word;
+  LChangeName, LChangeType, LChangeAccountData, LIsV2, LExecuteSigner, LRecipientSigned, LTargetRequiresPurchase : Boolean;
+  LSenderAccounts : TCardinalsArray;
 label loop_start;
 label loop_start;
 begin
 begin
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
   if Not Assigned(WalletKeys) then raise Exception.Create('No wallet keys');
-  If Not UpdateOperationOptions(errors) then raise Exception.Create(errors);
-  ops := TOperationsHashTree.Create;
+  If Not UpdateOperationOptions(LErrors) then raise Exception.Create(LErrors);
+  LOperations := TOperationsHashTree.Create;
   Try
   Try
-    _V2 := FNode.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
-    _totalamount := 0;
-    _totalfee := 0;
-    _totalSignerFee := 0;
-    _signer_n_ops := 0;
-    operationstxt := '';
-    operation_to_string := '';
+    LIsV2 := FNode.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
+    LTotalamount := 0;
+    LTotalFee := 0;
+    LTotalSignerFee := 0;
+    LSignerNOps := 0;
+    LOperationsTxt := '';
+    LOperationString := '';
 
 
     // Compile FSenderAccounts into a reorderable array
     // Compile FSenderAccounts into a reorderable array
-    _senderAccounts := FSenderAccounts.ToArray;
+    LSenderAccounts := FSenderAccounts.ToArray;
 
 
     // Loop through each sender account
     // Loop through each sender account
-    for iAcc := 0 to Length(_senderAccounts) - 1 do begin
+    for LAccountNo := 0 to Length(LSenderAccounts) - 1 do begin
 loop_start:
 loop_start:
-      op := Nil;
-      account := FNode.GetMempoolAccount(_senderAccounts[iAcc]);
-      If Not UpdatePayload(account, errors) then
-        raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
-      if NOT WalletKeys.TryGetKey(account.accountInfo.accountKey, LKey) then begin
+      LOperation := Nil;
+      LAccount := FNode.GetMempoolAccount(LSenderAccounts[LAccountNo]);
+      If Not UpdatePayload(LAccount, LErrors) then
+        raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account)+': '+LErrors);
+      if NOT WalletKeys.TryGetKey(LAccount.accountInfo.accountKey, LKey) then begin
 
 
         if  (
         if  (
-             (TAccountComp.IsAccountForPrivateSale(account.accountInfo)) or
-             (TAccountComp.IsAccountForAccountSwap(account.accountInfo))
+             (TAccountComp.IsAccountForPrivateSale(LAccount.accountInfo)) or
+             (TAccountComp.IsAccountForAccountSwap(LAccount.accountInfo))
              )
              )
-            and (Not TAccountComp.IsNullAccountKey(account.accountInfo.new_publicKey)) then begin
+            and (Not TAccountComp.IsNullAccountKey(LAccount.accountInfo.new_publicKey)) then begin
 
 
-          if NOT WalletKeys.TryGetKey(account.accountInfo.new_publicKey, LKey) then
+          if NOT WalletKeys.TryGetKey(LAccount.accountInfo.new_publicKey, LKey) then
             Raise Exception.Create('New sender account private key not found in Wallet');
             Raise Exception.Create('New sender account private key not found in Wallet');
         end else Raise Exception.Create('Sender account private key not found in Wallet');
         end else Raise Exception.Create('Sender account private key not found in Wallet');
       end;
       end;
-      dooperation := true;
+      LDoOperation := true;
       // Default fee
       // Default fee
-      if account.balance > uint64(DefaultFee) then _fee := DefaultFee else _fee := account.balance;
+      if LAccount.balance > uint64(DefaultFee) then LFee := DefaultFee else LFee := LAccount.balance;
       // Determine which operation type it is
       // Determine which operation type it is
       if PageControlOpType.ActivePage = tsTransaction then begin
       if PageControlOpType.ActivePage = tsTransaction then begin
         {%region Operation: Transaction}
         {%region Operation: Transaction}
-        if Not UpdateOpTransaction(account,destAccount,_amount,errors) then raise Exception.Create(errors);
-        if Length(_senderAccounts) > 1 then begin
-          if account.balance>0 then begin
-            if account.balance>DefaultFee then begin
-              _amount := account.balance - DefaultFee;
-              _fee := DefaultFee;
+        if Not UpdateOpTransaction(LAccount, LTargetEPASA, LTargetAccount,LNewOwnerPublicKey, LTargetRequiresPurchase, LAmount, LErrors) then
+          raise Exception.Create(LErrors);
+
+        if Length(LSenderAccounts) > 1 then begin
+          if LAccount.balance>0 then begin
+            if LAccount.balance>DefaultFee then begin
+              LAmount := LAccount.balance - DefaultFee;
+              LFee := DefaultFee;
             end else begin
             end else begin
-              _amount := account.balance;
-              _fee := 0;
+              LAmount := LAccount.balance;
+              LFee := 0;
             end;
             end;
-          end else dooperation := false;
-        end else begin
+          end else LDoOperation := false;
         end;
         end;
-        if dooperation then begin
-          op := TOpTransaction.CreateTransaction(FNode.Bank.Safebox.CurrentProtocol,account.account,account.n_operation+1,destAccount.account,LKey.PrivateKey,_amount,_fee,FEncodedPayload);
-          inc(_totalamount,_amount);
-          inc(_totalfee,_fee);
+
+        if LDoOperation then begin
+          if NOT LTargetRequiresPurchase then begin
+            LOperation := TOpTransaction.CreateTransaction(FNode.Bank.Safebox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,LTargetAccount.account,LKey.PrivateKey,LAmount,LFee,FEncodedPayload);
+            LOperationsTxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(LTargetAccount.account);
+          end else begin
+            // Pay-to-Key
+            LOperation := TOpBuyAccount.CreateBuy(FNode.Bank.SafeBox.CurrentProtocol, LAccount.account, LAccount.n_operation + 1, LTargetAccount.account, LTargetAccount.accountInfo.account_to_pay, LTargetAccount.accountInfo.price, LAmount, LFee, LNewOwnerPublicKey, LKey.PrivateKey, FEncodedPayload);
+          end;
+         inc(LTotalamount,LAmount);
+         inc(LTotalFee,LFee);
         end;
         end;
-        operationstxt := 'Transaction to '+TAccountComp.AccountNumberToAccountTxtNumber(destAccount.account);
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
       end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
         {%region Operation: Change Private Key}
         {%region Operation: Change Private Key}
-        if Not UpdateOpChangeKey(account,signerAccount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
-        if _V2 then begin
+        if Not UpdateOpChangeKey(LAccount,LSignerAccount,LNewOwnerPublicKey,LErrors) then raise Exception.Create(LErrors);
+        if LIsV2 then begin
           // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
           // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
-          if (iAcc < Length(_senderAccounts) - 1) AND (account.account = signerAccount.account) then begin
-            TArrayTool<Cardinal>.Swap(_senderAccounts, iAcc, Length(_senderAccounts) - 1); // ensure signer account processed last
+          if (LAccountNo < Length(LSenderAccounts) - 1) AND (LAccount.account = LSignerAccount.account) then begin
+            TArrayTool<Cardinal>.Swap(LSenderAccounts, LAccountNo, Length(LSenderAccounts) - 1); // ensure signer account processed last
             goto loop_start; // TODO: remove ugly hack with refactoring!
             goto loop_start; // TODO: remove ugly hack with refactoring!
           end;
           end;
 
 
           // Maintain correct signer fee distribution
           // Maintain correct signer fee distribution
-          if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
-          else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
-          else _fee := signerAccount.balance - uint64(_totalSignerFee);
-          op := TOpChangeKeySigned.Create(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+_signer_n_ops+1,account.account,LKey.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
-          inc(_signer_n_ops);
-          inc(_totalSignerFee, _fee);
+          if uint64(LTotalSignerFee) >= LSignerAccount.balance then LFee := 0
+          else if LSignerAccount.balance - uint64(LTotalSignerFee) > uint64(DefaultFee) then LFee := DefaultFee
+          else LFee := LSignerAccount.balance - uint64(LTotalSignerFee);
+          LOperation := TOpChangeKeySigned.Create(FNode.Bank.SafeBox.CurrentProtocol,LSignerAccount.account,LSignerAccount.n_operation+LSignerNOps+1,LAccount.account,LKey.PrivateKey,LNewOwnerPublicKey,LFee,FEncodedPayload);
+          inc(LSignerNOps);
+          inc(LTotalSignerFee, LFee);
         end else begin
         end else begin
-          op := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,account.account,account.n_operation+1,account.account,LKey.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
+          LOperation := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,LAccount.account,LKey.PrivateKey,LNewOwnerPublicKey,LFee,FEncodedPayload);
         end;
         end;
-        inc(_totalfee,_fee);
-        operationstxt := 'Change private key to '+TAccountComp.GetECInfoTxt(_newOwnerPublicKey.EC_OpenSSL_NID);
+        inc(LTotalFee,LFee);
+        LOperationsTxt := 'Change private key to '+TAccountComp.GetECInfoTxt(LNewOwnerPublicKey.EC_OpenSSL_NID);
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsListAccount) then begin
       end else if (PageControlOpType.ActivePage = tsListAccount) then begin
         {%region Operation: List For Sale}
         {%region Operation: List For Sale}
-        If Not UpdateOpListAccount(account,_salePrice,destAccount,signerAccount,_newOwnerPublicKey, _lockedUntil, LHashLock, errors) then raise Exception.Create(errors);
+        If Not UpdateOpListAccount(LAccount,LSalePrice,LTargetAccount,LSignerAccount,LNewOwnerPublicKey, LLockedUntil, LHashLock, LErrors) then raise Exception.Create(LErrors);
         // Special fee account:
         // Special fee account:
-        if signerAccount.balance>DefaultFee then _fee := DefaultFee
-        else _fee := signerAccount.balance;
+        if LSignerAccount.balance>DefaultFee then LFee := DefaultFee
+        else LFee := LSignerAccount.balance;
         if (rbListAccountForPublicSale.Checked) then begin
         if (rbListAccountForPublicSale.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
-            destAccount.account,CT_TECDSA_Public_Nul,0,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
+          LOperation := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo, LAccount.account,LSalePrice,LFee,
+            LTargetAccount.account,CT_TECDSA_Public_Nul,0,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
         end else if (rbListAccountForPrivateSale.Checked) then begin
         end else if (rbListAccountForPrivateSale.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
-            destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
+          LOperation := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForSale, LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo, LAccount.account,LSalePrice,LFee,
+            LTargetAccount.account,LNewOwnerPublicKey,LLockedUntil,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
         end  else if (rbListAccountForAccountSwap.Checked) then begin
         end  else if (rbListAccountForAccountSwap.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicAccountSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
-            destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
+          LOperation := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicAccountSwap, LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo, LAccount.account,LSalePrice,LFee,
+            LTargetAccount.account,LNewOwnerPublicKey,LLockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
         end  else if (rbListAccountForCoinSwap.Checked) then begin
         end  else if (rbListAccountForCoinSwap.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicCoinSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,
-            destAccount.account,CT_TECDSA_Public_Nul,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
+          LOperation := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, as_ForAtomicCoinSwap, LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo, LAccount.account,LSalePrice,LFee,
+            LTargetAccount.account,CT_TECDSA_Public_Nul,LLockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
         end else raise Exception.Create('Select Sale type');
         end else raise Exception.Create('Select Sale type');
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin
         {%region Operation: Delist For Sale}
         {%region Operation: Delist For Sale}
-        if Not UpdateOpDelist(account,signerAccount,errors) then raise Exception.Create(errors);
+        if Not UpdateOpDelist(LAccount,LSignerAccount,LErrors) then raise Exception.Create(LErrors);
         // Special fee account:
         // Special fee account:
-        if signerAccount.balance>DefaultFee then _fee := DefaultFee
-        else _fee := signerAccount.balance;
-        op := TOpDelistAccountForSale.CreateDelistAccountForSale(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1+iAcc,account.account,_fee,LKey.PrivateKey,FEncodedPayload);
+        if LSignerAccount.balance>DefaultFee then LFee := DefaultFee
+        else LFee := LSignerAccount.balance;
+        LOperation := TOpDelistAccountForSale.CreateDelistAccountForSale(FNode.Bank.SafeBox.CurrentProtocol,LSignerAccount.account,LSignerAccount.n_operation+1+LAccountNo,LAccount.account,LFee,LKey.PrivateKey,FEncodedPayload);
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
         {%region Operation: Buy Account}
         {%region Operation: Buy Account}
-        if Not UpdateOpBuyAccount(account,accountToBuy,_amount,_newOwnerPublicKey, LRecipientSigned, errors) then raise Exception.Create(errors);
-        if LRecipientSigned AND (NOT WalletKeys.TryGetKey(account.accountInfo.new_publicKey, LKey)) then
+        if Not UpdateOpBuyAccount(LAccount,LPurchaseAccount,LAmount,LNewOwnerPublicKey, LRecipientSigned, LErrors) then raise Exception.Create(LErrors);
+        if LRecipientSigned AND (NOT WalletKeys.TryGetKey(LAccount.accountInfo.new_publicKey, LKey)) then
           raise Exception.Create('Recipient-signed key not found in Wallet');
           raise Exception.Create('Recipient-signed key not found in Wallet');
-        op := TOpBuyAccount.CreateBuy(FNode.Bank.Safebox.CurrentProtocol,account.account,account.n_operation+1,accountToBuy.account,accountToBuy.accountInfo.account_to_pay,
-          accountToBuy.accountInfo.price,_amount,_fee,_newOwnerPublicKey,LKey.PrivateKey,FEncodedPayload);
+        LOperation := TOpBuyAccount.CreateBuy(FNode.Bank.Safebox.CurrentProtocol,LAccount.account,LAccount.n_operation+1,LPurchaseAccount.account,LPurchaseAccount.accountInfo.account_to_pay,
+          LPurchaseAccount.accountInfo.price,LAmount,LFee,LNewOwnerPublicKey,LKey.PrivateKey,FEncodedPayload);
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
         {%region Operation: Change Info}
         {%region Operation: Change Info}
-        if not UpdateOpChangeInfo(account,signerAccount,_changeName,_newName,_changeType,_newType,LChangeAccountData,LNewAccountData,errors) then begin
-          If Length(_senderAccounts)=1 then raise Exception.Create(errors);
+        if not UpdateOpChangeInfo(LAccount,LSignerAccount,LChangeName,LNewName,LChangeType,LNewType,LChangeAccountData,LNewAccountData,LErrors) then begin
+          If Length(LSenderAccounts)=1 then raise Exception.Create(LErrors);
         end else begin
         end else begin
-          if signerAccount.balance>DefaultFee then _fee := DefaultFee
-          else _fee := signerAccount.balance;
-          op := TOpChangeAccountInfo.CreateChangeAccountInfo(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1,account.account,LKey.PrivateKey,false,CT_TECDSA_Public_Nul,
-             _changeName,_newName,_changeType,_newType,
+          if LSignerAccount.balance>DefaultFee then LFee := DefaultFee
+          else LFee := LSignerAccount.balance;
+          LOperation := TOpChangeAccountInfo.CreateChangeAccountInfo(FNode.Bank.SafeBox.CurrentProtocol,LSignerAccount.account,LSignerAccount.n_operation+1,LAccount.account,LKey.PrivateKey,false,CT_TECDSA_Public_Nul,
+             LChangeName,LNewName,LChangeType,LNewType,
              LChangeAccountData,LNewAccountData,
              LChangeAccountData,LNewAccountData,
-             _fee,FEncodedPayload);
+             LFee,FEncodedPayload);
         end;
         end;
         {%endregion}
         {%endregion}
       end else begin
       end else begin
         raise Exception.Create('No operation selected');
         raise Exception.Create('No operation selected');
       end;
       end;
-      if Assigned(op) And (dooperation) then begin
-        ops.AddOperationToHashTree(op);
-        if operation_to_string<>'' then operation_to_string := operation_to_string + #10;
-        operation_to_string := operation_to_string + op.ToString;
+      if Assigned(LOperation) And (LDoOperation) then begin
+        LOperations.AddOperationToHashTree(LOperation);
+        if LOperationString<>'' then LOperationString := LOperationString + #10;
+        LOperationString := LOperationString + LOperation.ToString;
       end;
       end;
-      FreeAndNil(op);
+      FreeAndNil(LOperation);
     end;
     end;
 
 
-    if (ops.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
+    if (LOperations.OperationsCount=0) then raise Exception.Create('No valid operation to execute');
 
 
-    if (Length(_senderAccounts)>1) then begin
-      if PageControlOpType.ActivePage = tsTransaction then auxs := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(_totalamount)+#10
-      else auxs:='';
-      if Application.MessageBox(PChar('Execute '+Inttostr(Length(_senderAccounts))+' operations?'+#10+
-        'Operation: '+operationstxt+#10+
-        auxs+
-        'Total fee: '+TAccountComp.FormatMoney(_totalfee)+#10+#10+'Note: This operation will be transmitted to the network!'),
+    if (Length(LSenderAccounts)>1) then begin
+      if PageControlOpType.ActivePage = tsTransaction then LAuxStr := 'Total amount that dest will receive: '+TAccountComp.FormatMoney(LTotalamount)+#10
+      else LAuxStr:='';
+      if Application.MessageBox(PChar('Execute '+Inttostr(Length(LSenderAccounts))+' operations?'+#10+
+        'Operation: '+LOperationsTxt+#10+
+        LAuxStr+
+        'Total fee: '+TAccountComp.FormatMoney(LTotalFee)+#10+#10+'Note: This operation will be transmitted to the network!'),
         PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
         PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
     end else begin
     end else begin
-      if Application.MessageBox(PChar('Execute this operation:'+#10+#10+operation_to_string+#10+#10+'Note: This operation will be transmitted to the network!'),
+      if Application.MessageBox(PChar('Execute this operation:'+#10+#10+LOperationString+#10+#10+'Note: This operation will be transmitted to the network!'),
         PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
         PChar(Application.Title),MB_YESNO+MB_ICONINFORMATION+MB_DEFBUTTON2)<>IdYes then exit;
     end;
     end;
-    i := FNode.AddOperations(nil,ops,Nil,errors);
-    if (i=ops.OperationsCount) then begin
-      operationstxt := 'Successfully executed '+inttostr(i)+' operations!'+#10+#10+operation_to_string;
+    i := FNode.AddOperations(nil,LOperations,Nil,LErrors);
+    if (i=LOperations.OperationsCount) then begin
+      LOperationsTxt := 'Successfully executed '+inttostr(i)+' operations!'+#10+#10+LOperationString;
       If i>1 then begin
       If i>1 then begin
         With TFRMMemoText.Create(Self) do
         With TFRMMemoText.Create(Self) do
         Try
         Try
-          InitData(Application.Title,operationstxt);
+          InitData(Application.Title,LOperationsTxt);
           ShowModal;
           ShowModal;
         finally
         finally
           Free;
           Free;
         end;
         end;
       end else begin
       end else begin
-        Application.MessageBox(PChar('Successfully executed '+inttostr(i)+' operations!'+#10+#10+operation_to_string),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
+        Application.MessageBox(PChar('Successfully executed '+inttostr(i)+' operations!'+#10+#10+LOperationString),PChar(Application.Title),MB_OK+MB_ICONINFORMATION);
       end;
       end;
       ModalResult := MrOk;
       ModalResult := MrOk;
     end else if (i>0) then begin
     end else if (i>0) then begin
-      operationstxt := 'One or more of your operations has not been executed:'+#10+
+      LOperationsTxt := 'One or more of your operations has not been executed:'+#10+
         'Errors:'+#10+
         'Errors:'+#10+
-        errors+#10+#10+
+        LErrors+#10+#10+
         'Total successfully executed operations: '+inttostr(i);
         'Total successfully executed operations: '+inttostr(i);
       With TFRMMemoText.Create(Self) do
       With TFRMMemoText.Create(Self) do
       Try
       Try
-        InitData(Application.Title,operationstxt);
+        InitData(Application.Title,LOperationsTxt);
         ShowModal;
         ShowModal;
       finally
       finally
         Free;
         Free;
       end;
       end;
       ModalResult := MrOk;
       ModalResult := MrOk;
     end else begin
     end else begin
-      raise Exception.Create(errors);
+      raise Exception.Create(LErrors);
     end;
     end;
   Finally
   Finally
-    ops.Free;
+    LOperations.Free;
   End;
   End;
 end;
 end;
 
 
@@ -467,14 +476,17 @@ begin
 end;
 end;
 
 
 procedure TFRMOperation.ebAccountNumberExit(Sender: TObject);
 procedure TFRMOperation.ebAccountNumberExit(Sender: TObject);
-Var an : Cardinal;
+Var LEPasa : TEPASA;
   eb : TEdit;
   eb : TEdit;
 begin
 begin
   if (Not assigned(Sender)) then exit;
   if (Not assigned(Sender)) then exit;
   if (Not (Sender is TEdit)) then exit;
   if (Not (Sender is TEdit)) then exit;
   eb := TEdit(Sender);
   eb := TEdit(Sender);
-  If TAccountComp.AccountTxtNumberToAccountNumber(eb.Text,an) then begin
-    eb.Text := TAccountComp.AccountNumberToAccountTxtNumber(an);
+  If TEPasa.TryParse(eb.Text,LEPasa) then begin
+    if LEPasa.IsClassicPASA then
+      eb.Text := LEPasa.ToClassicPASAString()
+    else
+      eb.Text := LEPasa.ToString();
   end else begin
   end else begin
     eb.Text := '';
     eb.Text := '';
   end;
   end;
@@ -1147,6 +1159,8 @@ Var
   changeName,changeType, LRecipientSigned, LChangeAccountData : Boolean;
   changeName,changeType, LRecipientSigned, LChangeAccountData : Boolean;
   newName, LNewAccountData : TRawBytes;
   newName, LNewAccountData : TRawBytes;
   newType : Word;
   newType : Word;
+  LTargetEPASA : TEPasa;
+  LRequiresPurchase : Boolean;
 begin
 begin
   Result := false;
   Result := false;
   sender_account := CT_Account_NUL;
   sender_account := CT_Account_NUL;
@@ -1212,7 +1226,7 @@ begin
     end;
     end;
   End;
   End;
   if (PageControlOpType.ActivePage = tsTransaction) then begin
   if (PageControlOpType.ActivePage = tsTransaction) then begin
-    Result := UpdateOpTransaction(GetDefaultSenderAccount,dest_account,amount,errors);
+    Result := UpdateOpTransaction(GetDefaultSenderAccount,LTargetEPASA, dest_account, publicKey, LRequiresPurchase, amount,errors);
   end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
   end else if (PageControlOpType.ActivePage = tsChangePrivateKey) then begin
     Result := UpdateOpChangeKey(GetDefaultSenderAccount,signer_account,publicKey,errors);
     Result := UpdateOpChangeKey(GetDefaultSenderAccount,signer_account,publicKey,errors);
   end else if (PageControlOpType.ActivePage = tsListAccount) then begin
   end else if (PageControlOpType.ActivePage = tsListAccount) then begin
@@ -1508,177 +1522,223 @@ begin
   End;
   End;
 end;
 end;
 
 
-function TFRMOperation.UpdateOpTransaction(const SenderAccount: TAccount;  var DestAccount: TAccount; var amount: Int64;  var errors: String): Boolean;
-Var c : Cardinal;
+function TFRMOperation.UpdateOpTransaction(const ASenderAccount: TAccount; out LTargetEPASA : TEPasa; out ATargetAccount : TAccount; out AResolvedTargetKey : TECDSA_Public; out ATargetRequiresPurchase : Boolean; out AAmount: Int64; out AErrors: String): Boolean;
+Var
+  LResolvedAccountNo : Cardinal;
 begin
 begin
-  Result := False;
-  errors := '';
+  AErrors := '';
   lblTransactionErrors.Caption := '';
   lblTransactionErrors.Caption := '';
   if PageControlOpType.ActivePage<>tsTransaction then exit;
   if PageControlOpType.ActivePage<>tsTransaction then exit;
-  if not (TAccountComp.AccountTxtNumberToAccountNumber(ebDestAccount.Text,c)) then begin
-    errors := 'Invalid dest. account ('+ebDestAccount.Text+')';
-    lblTransactionErrors.Caption := errors;
-    exit;
+  if not TEPasa.TryParse(ebDestAccount.Text, LTargetEPASA) then begin
+    AErrors := 'Invalid dest. EPASA ('+ebDestAccount.Text+')';
+    lblTransactionErrors.Caption := AErrors;
+    Exit(False);
   end;
   end;
-  if (c<0) Or (c>=TNode.Node.Bank.AccountsCount) then begin
-    errors := 'Invalid dest. account ('+TAccountComp.AccountNumberToAccountTxtNumber(c)+')';
-    lblTransactionErrors.Caption := errors;
-    exit;
+
+  Result := TNode.Node.TryResolveEPASA(LTargetEPASA, LResolvedAccountNo, AResolvedTargetKey, ATargetRequiresPurchase, AErrors);
+  if NOT Result then begin
+    lblTransactionErrors.Caption := AErrors;
+    Exit(False);
   end;
   end;
-  DestAccount := TNode.Node.GetMempoolAccount(c);
-  if SenderAccounts.Count=1 then begin
-    if not TAccountComp.TxtToMoney(ebAmount.Text,amount) then begin
-      errors := 'Invalid amount ('+ebAmount.Text+')';
-      lblTransactionErrors.Caption := errors;
-      exit;
+
+  if LResolvedAccountNo <> CT_AccountNo_NUL then begin
+    ATargetAccount := TNode.Node.GetMempoolAccount(LResolvedAccountNo);
+    if ATargetAccount.account=ASenderAccount.account then begin
+      AErrors := 'Sender and dest account are the same';
+      lblTransactionErrors.Caption := AErrors;
+      Exit(False);
     end;
     end;
-  end else amount := 0; // ALL BALANCE
-  if DestAccount.account=SenderAccount.account then begin
-    errors := 'Sender and dest account are the same';
-    lblTransactionErrors.Caption := errors;
-    exit;
   end;
   end;
+
+  if SenderAccounts.Count=1 then begin
+    if not TAccountComp.TxtToMoney(ebAmount.Text,AAmount) then begin
+      AErrors := 'Invalid amount ('+ebAmount.Text+')';
+      lblTransactionErrors.Caption := AErrors;
+      Exit(False);
+    end;
+  end else AAmount := 0; // ALL BALANCE
+
+
   if (SenderAccounts.Count=1) then begin
   if (SenderAccounts.Count=1) then begin
-    if (SenderAccount.balance<(amount+FDefaultFee)) then begin
-       errors := 'Insufficient funds';
-       lblTransactionErrors.Caption := errors;
-       exit;
+    if (ASenderAccount.balance<(AAmount+FDefaultFee)) then begin
+       AErrors := 'Insufficient funds';
+       lblTransactionErrors.Caption := AErrors;
+       Exit(False);
     end;
     end;
   end;
   end;
-  Result := True;
 end;
 end;
 
 
-function TFRMOperation.UpdatePayload(const SenderAccount: TAccount;
-  var errors: String): Boolean;
-Var payload_u : AnsiString;
-  payload_encrypted : TRawBytes;
-  account : TAccount;
-  public_key : TAccountKey;
-  dest_account_number : Cardinal;
+function TFRMOperation.UpdatePayload(const ASenderAccount: TAccount; var AErrors: String): Boolean;
+Var
+  LUserPayloadString : AnsiString;
+  LEncryptedPayloadBytes : TRawBytes;
+  LAccount : TAccount;
+  LTargetEPASA : TEPasa;
+  LPublicKey : TAccountKey;
+  LAccountNumber : Cardinal;
   i : Integer;
   i : Integer;
-  valid : Boolean;
-  wk : TWalletKey;
+  LValid : Boolean;
+  LWalletKey : TWalletKey;
+  LPassword : String;
   LPayloadBytes : TRawBytes;
   LPayloadBytes : TRawBytes;
 begin
 begin
-  valid := false;
-  payload_encrypted := Nil;
+  LValid := false;
+  LEncryptedPayloadBytes := Nil;
   FEncodedPayload := CT_TOperationPayload_NUL;
   FEncodedPayload := CT_TOperationPayload_NUL;
-  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-  // TODO:
-  // Needs to assign FEncodedPayload.payload_type based on PIP-0027
-  // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-  errors := 'Unknown error';
-  payload_u := memoPayload.Lines.Text;
+  LUserPayloadString := memoPayload.Lines.Text;
+  FEncodedPayload.payload_type := 0; // [ptNonDeterministic]
+  AErrors := 'Unknown error';
   try
   try
-    if (payload_u='') then begin
-      valid := true;
+    LTargetEPASA := TEPasa.Empty;
+    If (PageControlOpType.ActivePage=tsTransaction) then begin
+      if NOT TEPasa.TryParse(ebDestAccount.Text, LTargetEPASA) then begin
+        AErrors := 'Indeterminable target';
+        Exit(False);
+      end;
+
+      if LTargetEPASA.IsPayToKey then begin
+        LValid := true;
+        Exit;
+      end;
+
+      if LTargetEPASA.HasPayload then begin
+        if LUserPayloadString <> '' then begin
+          AErrors := 'Ambiguous payload. Payload already specified by target EPASA.';
+          LValid := False;
+          Exit;
+        end;
+
+        if ebEncryptPassword.Text <> '' then begin
+          AErrors := 'Ambiguous payload. Password cannot be specified.';
+          LValid := False;
+          Exit;
+        end;
+      end;
+      FEncodedPayload.payload_type := LTargetEPASA.PayloadType.ToProtocolValue;
+    end;
+
+    if (LUserPayloadString='') AND (LTargetEPASA.Payload.IsEmpty) then begin
+      LValid := true;
       exit;
       exit;
     end;
     end;
-    if cbPayloadAsHex.Checked then begin
-      if NOT TCrypto.HexaToRaw(payload_u, LPayloadBytes) then begin
-        valid := false;
-        errors := 'Payload not hex-formatted';
+    if LTargetEPASA.HasPayload then begin
+      LPayloadBytes := LTargetEPASA.GetRawPayloadBytes;
+    end else if cbPayloadAsHex.Checked then begin
+      if NOT TCrypto.HexaToRaw(LUserPayloadString, LPayloadBytes) then begin
+        LValid := false;
+        AErrors := 'Payload not hex-formatted';
         exit;
         exit;
       end;
       end;
-    end else LPayloadBytes := TEncoding.ANSI.GetBytes(payload_u);
+    end else LPayloadBytes := TEncoding.ANSI.GetBytes(LUserPayloadString);
 
 
-    if (rbEncryptedWithOldEC.Checked) then begin
+    if (NOT LTargetEPasa.HasPayload AND rbEncryptedWithOldEC.Checked) OR LTargetEPASA.PayloadType.HasTrait(ptSenderKeyEncrypted) then begin
       // Use sender
       // Use sender
-      errors := 'Error encrypting';
-      account := FNode.GetMempoolAccount(SenderAccount.account);
-      TPCEncryption.DoPascalCoinECIESEncrypt(account.accountInfo.accountKey,LPayloadBytes,payload_encrypted);
-      valid := Length(payload_encrypted)>0;
-    end else if (rbEncryptedWithEC.Checked) then begin
-      errors := 'Error encrypting';
+      AErrors := 'Error encrypting';
+      LAccount := FNode.GetMempoolAccount(ASenderAccount.account);
+      TPCEncryption.DoPascalCoinECIESEncrypt(LAccount.accountInfo.accountKey,LPayloadBytes,LEncryptedPayloadBytes);
+      LValid := Length(LEncryptedPayloadBytes)>0;
+    end else if (NOT LTargetEPasa.HasPayload AND rbEncryptedWithEC.Checked) or LTargetEPASA.PayloadType.HasTrait(ptRecipientKeyEncrypted) then begin
+      AErrors := 'Error encrypting';
       if (PageControlOpType.ActivePage=tsTransaction) or (PageControlOpType.ActivePage=tsListAccount) or (PageControlOpType.ActivePage=tsDelistAccount)
       if (PageControlOpType.ActivePage=tsTransaction) or (PageControlOpType.ActivePage=tsListAccount) or (PageControlOpType.ActivePage=tsDelistAccount)
         or (PageControlOpType.ActivePage=tsBuyAccount) then begin
         or (PageControlOpType.ActivePage=tsBuyAccount) then begin
+
         // With dest public key
         // With dest public key
         If (PageControlOpType.ActivePage=tsTransaction) then begin
         If (PageControlOpType.ActivePage=tsTransaction) then begin
-          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebDestAccount.Text,dest_account_number) then begin
-            errors := 'Invalid dest account number';
+          If Not Self.FNode.TryResolveEPASA(LTargetEPASA, LAccountNumber) then begin
+            AErrors := 'Invalid dest account EPASA';
             exit;
             exit;
           end;
           end;
         end else if (PageControlOpType.ActivePage=tsListAccount) then begin
         end else if (PageControlOpType.ActivePage=tsListAccount) then begin
-          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebSignerAccount.Text,dest_account_number) then begin
-            errors := 'Invalid signer account number';
+          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebSignerAccount.Text,LAccountNumber) then begin
+            AErrors := 'Invalid signer account number';
             exit;
             exit;
           end;
           end;
         end else if (PageControlOpType.ActivePage=tsDelistAccount) then begin
         end else if (PageControlOpType.ActivePage=tsDelistAccount) then begin
-          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebSignerAccount.Text,dest_account_number) then begin
-            errors := 'Invalid signer account number';
+          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebSignerAccount.Text,LAccountNumber) then begin
+            AErrors := 'Invalid signer account number';
             exit;
             exit;
           end;
           end;
         end else if (PageControlOpType.ActivePage=tsBuyAccount) then begin
         end else if (PageControlOpType.ActivePage=tsBuyAccount) then begin
-          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebAccountToBuy.Text,dest_account_number) then begin
-            errors := 'Invalid account to buy number';
+          If Not TAccountComp.AccountTxtNumberToAccountNumber(ebAccountToBuy.Text,LAccountNumber) then begin
+            AErrors := 'Invalid account to buy number';
             exit;
             exit;
           end;
           end;
         end else begin
         end else begin
-          errors := 'ERROR DEV 20170512-1';
+          AErrors := 'ERROR DEV 20170512-1';
           exit;
           exit;
         end;
         end;
-        if (dest_account_number<0) or (dest_account_number>=FNode.Bank.AccountsCount) then begin
-          errors := 'Invalid payload encrypted account number: '+TAccountComp.AccountNumberToAccountTxtNumber(dest_account_number);
+        if (LAccountNumber<0) or (LAccountNumber>=FNode.Bank.AccountsCount) then begin
+          AErrors := 'Invalid payload encrypted account number: '+TAccountComp.AccountNumberToAccountTxtNumber(LAccountNumber);
           exit;
           exit;
         end;
         end;
-        account := FNode.GetMempoolAccount(dest_account_number);
-        TPCEncryption.DoPascalCoinECIESEncrypt(account.accountInfo.accountKey,LPayloadBytes,payload_encrypted);
-        valid := Length(payload_encrypted)>0;
+        LAccount := FNode.GetMempoolAccount(LAccountNumber);
+        TPCEncryption.DoPascalCoinECIESEncrypt(LAccount.accountInfo.accountKey,LPayloadBytes,LEncryptedPayloadBytes);
+        LValid := Length(LEncryptedPayloadBytes)>0;
       end else if (PageControlOpType.ActivePage=tsChangePrivateKey) then begin
       end else if (PageControlOpType.ActivePage=tsChangePrivateKey) then begin
         if (rbChangeKeyWithAnother.Checked) then begin
         if (rbChangeKeyWithAnother.Checked) then begin
           // With new key generated
           // With new key generated
           if (cbNewPrivateKey.ItemIndex>=0) then begin
           if (cbNewPrivateKey.ItemIndex>=0) then begin
             i := PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
             i := PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
-            if (i>=0) then public_key := WalletKeys.Key[i].AccountKey;
+            if (i>=0) then LPublicKey := WalletKeys.Key[i].AccountKey;
           end else begin
           end else begin
-            errors := 'Must select a private key';
+            AErrors := 'Must select a private key';
             exit;
             exit;
           end;
           end;
         end else if (rbChangeKeyTransferAccountToNewOwner.Checked) then begin
         end else if (rbChangeKeyTransferAccountToNewOwner.Checked) then begin
-          If Not TAccountComp.AccountKeyFromImport(ebNewPublicKey.Text,public_key,errors) then begin
-            errors := 'Public key: '+errors;
+          If Not TAccountComp.AccountKeyFromImport(ebNewPublicKey.Text,LPublicKey,AErrors) then begin
+            AErrors := 'Public key: '+AErrors;
             exit;
             exit;
           end;
           end;
         end else begin
         end else begin
-          errors := 'Must select change type';
+          AErrors := 'Must select change type';
           exit;
           exit;
         end;
         end;
-        if public_key.EC_OpenSSL_NID<>CT_Account_NUL.accountInfo.accountKey.EC_OpenSSL_NID then begin
-          TPCEncryption.DoPascalCoinECIESEncrypt(public_key,LPayloadBytes,payload_encrypted);
-          valid := Length(payload_encrypted)>0;
+        if LPublicKey.EC_OpenSSL_NID<>CT_Account_NUL.accountInfo.accountKey.EC_OpenSSL_NID then begin
+          TPCEncryption.DoPascalCoinECIESEncrypt(LPublicKey,LPayloadBytes,LEncryptedPayloadBytes);
+          LValid := Length(LEncryptedPayloadBytes)>0;
         end else begin
         end else begin
-          valid := false;
-          errors := 'Selected private key is not valid to encode';
+          LValid := false;
+          AErrors := 'Selected private key is not valid to encode';
           exit;
           exit;
         end;
         end;
       end else begin
       end else begin
-        errors := 'This operation does not allow this kind of payload';
+        AErrors := 'This operation does not allow this kind of payload';
       end;
       end;
-    end else if (rbEncrptedWithPassword.Checked) then begin
-      payload_encrypted := TPCEncryption.DoPascalCoinAESEncrypt(LPayloadBytes,TEncoding.ANSI.GetBytes(ebEncryptPassword.Text));
-      valid := Length(payload_encrypted)>0;
-    end else if (rbNotEncrypted.Checked) then begin
-      payload_encrypted := LPayloadBytes;
-      valid := true;
+    end else if (NOT LTargetEPasa.HasPayload AND rbEncrptedWithPassword.Checked) OR LTargetEPASA.PayloadType.HasTrait(ptPasswordEncrypted) then begin
+      if LTargetEPASA.PayloadType.HasTrait(ptPasswordEncrypted) then
+        LPassword := LTargetEPASA.Password
+      else
+        LPassword := ebEncryptPassword.Text;
+      LEncryptedPayloadBytes := TPCEncryption.DoPascalCoinAESEncrypt(LPayloadBytes,TEncoding.ANSI.GetBytes(LPassword));
+      LValid := Length(LEncryptedPayloadBytes)>0;
+    end else if (NOT LTargetEPasa.HasPayload AND rbNotEncrypted.Checked) or LTargetEPASA.PayloadType.HasTrait(ptPublic) then begin
+      LEncryptedPayloadBytes := LPayloadBytes;
+      LValid := true;
     end else begin
     end else begin
-      errors := 'Must select an encryption option for payload';
+      AErrors := 'Must select an encryption option for payload';
     end;
     end;
   finally
   finally
-    if valid then begin
-      if length(payload_encrypted)>CT_MaxPayloadSize then begin
-        valid := false;
-        errors := 'Payload size is bigger than '+inttostr(CT_MaxPayloadSize)+' ('+Inttostr(length(payload_encrypted))+')';
+    if LValid then begin
+      if length(LEncryptedPayloadBytes)>CT_MaxPayloadSize then begin
+        LValid := false;
+        AErrors := 'Payload size is bigger than '+inttostr(CT_MaxPayloadSize)+' ('+Inttostr(length(LEncryptedPayloadBytes))+')';
       end;
       end;
     end;
     end;
-    if valid then begin
+    if LValid then begin
       lblEncryptionErrors.Caption := '';
       lblEncryptionErrors.Caption := '';
-      lblPayloadLength.Caption := Format('(%db -> %db)',[length(payload_u),length(payload_encrypted)]);
+      if LTargetEPASA.HasPayload then
+        lblPayloadLength.Caption := Format('(%db -> %db)',[length(LTargetEPASA.Payload),length(LEncryptedPayloadBytes)])
+      else
+        lblPayloadLength.Caption := Format('(%db -> %db)',[length(LUserPayloadString),length(LEncryptedPayloadBytes)]);
     end else begin
     end else begin
-      lblEncryptionErrors.Caption := errors;
-      lblPayloadLength.Caption := Format('(%db -> ?)',[length(payload_u)]);
+      lblEncryptionErrors.Caption := AErrors;
+      if LTargetEPASA.HasPayload then
+        lblPayloadLength.Caption := Format('(%db -> ?)',[length(LTargetEPASA.Payload)])
+      else
+        lblPayloadLength.Caption := Format('(%db -> ?)',[length(LUserPayloadString)]);
     end;
     end;
-    FEncodedPayload.payload_raw := payload_encrypted;
-    Result := valid;
+    FEncodedPayload.payload_raw := LEncryptedPayloadBytes;
+    Result := LValid;
   end;
   end;
 end;
 end;
 
 

+ 4 - 5
src/gui-classic/UFRMPascalCoinWalletConfig.pas

@@ -30,11 +30,10 @@ uses
   LCLIntf, LCLType, LMessages,
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
 {$ENDIF}
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
-  Dialogs, StdCtrls, Buttons, ComCtrls, UAppParams, UWallet;
+  Dialogs, StdCtrls, Buttons, ComCtrls, UAppParams, USettings, UWallet;
 
 
 type
 type
 
 
-  TMinerPrivateKey = (mpk_NewEachTime, mpk_Random, mpk_Selected);
 
 
   { TFRMPascalCoinWalletConfig }
   { TFRMPascalCoinWalletConfig }
 
 
@@ -98,7 +97,7 @@ type
 implementation
 implementation
 
 
 uses
 uses
-  {$IFDEF USE_GNUGETTEXT}gnugettext, UFRMSelectLanguage, {$ENDIF}UConst, UAccounts, ULog, UCrypto, UNode, USettings, UGUIUtils, UNetProtocol;
+  {$IFDEF USE_GNUGETTEXT}gnugettext, UFRMSelectLanguage, {$ENDIF}UConst, UAccounts, ULog, UCrypto, UNode, UGUIUtils, UNetProtocol;
 
 
 {$IFnDEF FPC}
 {$IFnDEF FPC}
   {$R *.dfm}
   {$R *.dfm}
@@ -108,7 +107,7 @@ uses
 
 
 procedure TFRMPascalCoinWalletConfig.bbOkClick(Sender: TObject);
 procedure TFRMPascalCoinWalletConfig.bbOkClick(Sender: TObject);
 Var df : Int64;
 Var df : Int64;
-  mpk : TMinerPrivateKey;
+  mpk : TMinerPrivateKeyType;
   i : Integer;
   i : Integer;
 begin
 begin
   if udInternetServerPort.Position = udJSONRPCMinerServerPort.Position then raise Exception.Create('Server port and JSON-RPC Server miner port are equal!');
   if udInternetServerPort.Position = udJSONRPCMinerServerPort.Position then raise Exception.Create('Server port and JSON-RPC Server miner port are equal!');
@@ -241,7 +240,7 @@ begin
     udInternetServerPort.Position := AppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
     udInternetServerPort.Position := AppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
     ebDefaultFee.Text := TAccountComp.FormatMoney(AppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0));
     ebDefaultFee.Text := TAccountComp.FormatMoney(AppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0));
     cbJSONRPCMinerServerActive.Checked := AppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
     cbJSONRPCMinerServerActive.Checked := AppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
-    case TMinerPrivateKey(AppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].GetAsInteger(Integer(mpk_Random))) of
+    case TMinerPrivateKeyType(AppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].GetAsInteger(Integer(mpk_Random))) of
       mpk_NewEachTime : rbGenerateANewPrivateKeyEachBlock.Checked := true;
       mpk_NewEachTime : rbGenerateANewPrivateKeyEachBlock.Checked := true;
       mpk_Random : rbUseARandomKey.Checked := true;
       mpk_Random : rbUseARandomKey.Checked := true;
       mpk_Selected : rbMineAllwaysWithThisKey.Checked := true;
       mpk_Selected : rbMineAllwaysWithThisKey.Checked := true;

+ 58 - 100
src/gui-classic/UFRMWallet.pas

@@ -35,7 +35,7 @@ uses
   UNode, UGridUtils, UJSONFunctions, UAccounts, Menus, ImgList, UNetProtocol,
   UNode, UGridUtils, UJSONFunctions, UAccounts, Menus, ImgList, UNetProtocol,
   UCrypto, Buttons, UPoolMining, URPC, UFRMAccountSelect, UConst,
   UCrypto, Buttons, UPoolMining, URPC, UFRMAccountSelect, UConst,
   UAccountKeyStorage, UBaseTypes, UPCDataTypes, UOrderedList,
   UAccountKeyStorage, UBaseTypes, UPCDataTypes, UOrderedList,
-  UFRMRPCCalls, UTxMultiOperation,
+  UFRMRPCCalls, UTxMultiOperation, USettings,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 Const
 Const
@@ -43,7 +43,6 @@ Const
   CM_PC_NetConnectionUpdated = WM_USER + 2;
   CM_PC_NetConnectionUpdated = WM_USER + 2;
 
 
 type
 type
-  TMinerPrivateKey = (mpk_NewEachTime, mpk_Random, mpk_Selected);
 
 
   { TFRMWallet }
   { TFRMWallet }
 
 
@@ -259,7 +258,6 @@ type
     FIsActivated : Boolean;
     FIsActivated : Boolean;
     FWalletKeys : TWalletKeysExt;
     FWalletKeys : TWalletKeysExt;
     FLog : TLog;
     FLog : TLog;
-    FAppParams : TAppParams;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FNodeNotifyEvents : TNodeNotifyEvents;
     FAccountsGrid : TAccountsGrid;
     FAccountsGrid : TAccountsGrid;
     FSelectedAccountsGrid : TAccountsGrid;
     FSelectedAccountsGrid : TAccountsGrid;
@@ -267,7 +265,7 @@ type
     FPendingOperationsGrid : TOperationsGrid;
     FPendingOperationsGrid : TOperationsGrid;
     FOperationsExplorerGrid : TOperationsGrid;
     FOperationsExplorerGrid : TOperationsGrid;
     FBlockChainGrid : TBlockChainGrid;
     FBlockChainGrid : TBlockChainGrid;
-    FMinerPrivateKeyType : TMinerPrivateKey;
+    FMinerPrivateKeyType : TMinerPrivateKeyType;
     FUpdating : Boolean;
     FUpdating : Boolean;
     FMessagesUnreadCount : Integer;
     FMessagesUnreadCount : Integer;
     FMinAccountBalance : Int64;
     FMinAccountBalance : Int64;
@@ -295,9 +293,7 @@ type
     Procedure UpdateBlockChainState;
     Procedure UpdateBlockChainState;
     Procedure UpdatePrivateKeys;
     Procedure UpdatePrivateKeys;
     Procedure UpdateOperations;
     Procedure UpdateOperations;
-    Procedure LoadAppParams;
-    Procedure SaveAppParams;
-    Procedure UpdateConfigChanged;
+    Procedure UpdateConfigChanged(Sender:TObject);
     Procedure UpdateNodeStatus;
     Procedure UpdateNodeStatus;
     Procedure UpdateAvailableConnections;
     Procedure UpdateAvailableConnections;
     procedure Activate; override;
     procedure Activate; override;
@@ -339,7 +335,7 @@ Uses UFolderHelper,{$IFDEF USE_GNUGETTEXT}gnugettext,{$ENDIF}
   UFRMAskForAccount,
   UFRMAskForAccount,
   UAbstractBTree,
   UAbstractBTree,
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText,
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText,
-  USettings, UCommon, UPCOrderedLists;
+  UCommon, UPCOrderedLists;
 
 
 Type
 Type
 
 
@@ -439,19 +435,19 @@ begin
         Raise;
         Raise;
       end;
       end;
     End;
     End;
-    ips := FAppParams.ParamByName[CT_PARAM_TryToConnectOnlyWithThisFixedServers].GetAsString('');
+    ips := TSettings.TryConnectOnlyWithThisFixedServers;
     TNode.DecodeIpStringToNodeServerAddressArray(ips,nsarr);
     TNode.DecodeIpStringToNodeServerAddressArray(ips,nsarr);
     TNetData.NetData.DiscoverFixedServersOnly(nsarr);
     TNetData.NetData.DiscoverFixedServersOnly(nsarr);
     setlength(nsarr,0);
     setlength(nsarr,0);
     // Creating Node:
     // Creating Node:
     FNode := TNode.Node;
     FNode := TNode.Node;
-    FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
-    FNode.PeerCache := FAppParams.ParamByName[CT_PARAM_PeerCache].GetAsString('')+';'+CT_Discover_IPs;
+    FNode.NetServer.Port := TSettings.InternetServerPort;
+    FNode.PeerCache := TSettings.PeerCache+';'+CT_Discover_IPs;
     // Create RPC server
     // Create RPC server
     FRPCServer := TRPCServer.Create;
     FRPCServer := TRPCServer.Create;
     FRPCServer.WalletKeys := WalletKeys;
     FRPCServer.WalletKeys := WalletKeys;
-    FRPCServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
-    FRPCServer.ValidIPs := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1');
+    FRPCServer.Active := TSettings.JsonRpcPortEnabled;
+    FRPCServer.ValidIPs := TSettings.JsonRpcAllowedIPs;
     WalletKeys.SafeBox := FNode.Bank.SafeBox;
     WalletKeys.SafeBox := FNode.Bank.SafeBox;
     // Check Database
     // Check Database
     FNode.Bank.StorageClass := TFileStorage;
     FNode.Bank.StorageClass := TFileStorage;
@@ -462,15 +458,15 @@ begin
     FWalletKeys.OnChanged.Add( OnWalletChanged );
     FWalletKeys.OnChanged.Add( OnWalletChanged );
     FAccountsGrid.Node := FNode;
     FAccountsGrid.Node := FNode;
     FOperationsAccountGrid.Node := FNode;
     FOperationsAccountGrid.Node := FNode;
-    FBlockChainGrid.HashRateAverageBlocksCount := FAppParams.ParamByName[CT_PARAM_HashRateAvgBlocksCount].GetAsInteger(50);
-    i := FAppParams.ParamByName[CT_PARAM_ShowHashRateAs].GetAsInteger(Integer({$IFDEF TESTNET}hr_Mega{$ELSE}hr_Tera{$ENDIF}));
+    FBlockChainGrid.HashRateAverageBlocksCount := TSettings.HashRateAvgBlocksCount;
+    i := Integer(TSettings.ShowHashRateAs);
     if (i<Integer(Low(TShowHashRateAs))) Or (i>Integer(High(TShowHashRateAs))) then i := Integer({$IFDEF TESTNET}hr_Mega{$ELSE}hr_Tera{$ENDIF});
     if (i<Integer(Low(TShowHashRateAs))) Or (i>Integer(High(TShowHashRateAs))) then i := Integer({$IFDEF TESTNET}hr_Mega{$ELSE}hr_Tera{$ENDIF});
     FBlockChainGrid.HashRateAs := TShowHashRateAs(i);
     FBlockChainGrid.HashRateAs := TShowHashRateAs(i);
     // Reading database
     // Reading database
     FThreadActivate := TThreadActivate.Create(true);
     FThreadActivate := TThreadActivate.Create(true);
     TThreadActivate(FThreadActivate).FreeOnTerminate := true;
     TThreadActivate(FThreadActivate).FreeOnTerminate := true;
     TThreadActivate(FThreadActivate).Suspended := False;
     TThreadActivate(FThreadActivate).Suspended := False;
-    UpdateConfigChanged;
+    UpdateConfigChanged(Self);
     UpdateNodeStatus;
     UpdateNodeStatus;
     TPCTNetDataExtraMessages.InitNetDataExtraMessages(FNode,TNetData.NetData,FWalletKeys);
     TPCTNetDataExtraMessages.InitNetDataExtraMessages(FNode,TNetData.NetData,FWalletKeys);
   Except
   Except
@@ -482,8 +478,8 @@ begin
   end;
   end;
   UpdatePrivateKeys;
   UpdatePrivateKeys;
   UpdateAccounts(false);
   UpdateAccounts(false);
-  if FAppParams.ParamByName[CT_PARAM_FirstTime].GetAsBoolean(true) then begin
-    FAppParams.ParamByName[CT_PARAM_FirstTime].SetAsBoolean(false);
+  if TSettings.FirstTime then begin
+    TSettings.FirstTime := false;
     miAboutPascalCoinClick(Nil);
     miAboutPascalCoinClick(Nil);
   end;
   end;
 
 
@@ -521,7 +517,7 @@ begin
     finally
     finally
       FSelectedAccountsGrid.UnlockAccountsList;
       FSelectedAccountsGrid.UnlockAccountsList;
     end;
     end;
-    DefaultFee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    DefaultFee := TSettings.DefaultFee;
     WalletKeys := FWalletKeys;
     WalletKeys := FWalletKeys;
     ShowModal;
     ShowModal;
   Finally
   Finally
@@ -700,13 +696,13 @@ end;
 
 
 procedure TFRMWallet.dgAccountsColumnMoved(Sender: TObject; FromIndex, ToIndex: Integer);
 procedure TFRMWallet.dgAccountsColumnMoved(Sender: TObject; FromIndex, ToIndex: Integer);
 begin
 begin
-  SaveAppParams;
+  TSettings.Save;
 end;
 end;
 
 
 procedure TFRMWallet.dgAccountsFixedCellClick(Sender: TObject; ACol,
 procedure TFRMWallet.dgAccountsFixedCellClick(Sender: TObject; ACol,
   ARow: Integer);
   ARow: Integer);
 begin
 begin
-  SaveAppParams;
+  TSettings.Save;
 end;
 end;
 
 
 procedure TFRMWallet.DoUpdateAccounts;
 procedure TFRMWallet.DoUpdateAccounts;
@@ -866,16 +862,16 @@ begin
   TimerUpdateStatus.Enabled := true;
   TimerUpdateStatus.Enabled := true;
   //
   //
   FPoolMiningServer := TPoolMiningServer.Create;
   FPoolMiningServer := TPoolMiningServer.Create;
-  FPoolMiningServer.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
+  FPoolMiningServer.Port := TSettings.JsonRpcMinerServerPort;
   FPoolMiningServer.MinerAccountKey := GetAccountKeyForMiner;
   FPoolMiningServer.MinerAccountKey := GetAccountKeyForMiner;
-  FPoolMiningServer.MinerPayload := TEncoding.ANSI.GetBytes( FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString('') );
+  FPoolMiningServer.MinerPayload := TEncoding.ANSI.GetBytes(TSettings.MinerName);
   LLockedMempool := FNode.LockMempoolWrite;
   LLockedMempool := FNode.LockMempoolWrite;
   try
   try
     LLockedMempool.AccountKey := GetAccountKeyForMiner;
     LLockedMempool.AccountKey := GetAccountKeyForMiner;
   finally
   finally
     FNode.UnlockMempoolWrite;
     FNode.UnlockMempoolWrite;
   end;
   end;
-  FPoolMiningServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
+  FPoolMiningServer.Active := TSettings.JsonRpcMinerServerActive;
   FPoolMiningServer.OnMiningServerNewBlockFound := OnMiningServerNewBlockFound;
   FPoolMiningServer.OnMiningServerNewBlockFound := OnMiningServerNewBlockFound;
   FreeAndNil(FBackgroundLabel);
   FreeAndNil(FBackgroundLabel);
   FreeAndNil(FBackgroundPanel);
   FreeAndNil(FBackgroundPanel);
@@ -1091,7 +1087,9 @@ begin
   mi := TMenuItem.Create(MainMenu);
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Create a block';
   mi.Caption:='Create a block';
   mi.OnClick:=Test_CreateABlock;
   mi.OnClick:=Test_CreateABlock;
+  {$IFnDEF FPC}
   mi.ShortCut := TextToShortCut('CTRL+B');
   mi.ShortCut := TextToShortCut('CTRL+B');
+  {$ENDIF}
   miAbout.Add(mi);
   miAbout.Add(mi);
   {$ENDIF}
   {$ENDIF}
   mi := TMenuItem.Create(MainMenu);
   mi := TMenuItem.Create(MainMenu);
@@ -1332,8 +1330,8 @@ begin
   FLog.OnNewLog := OnNewLog;
   FLog.OnNewLog := OnNewLog;
   FLog.SaveTypes := [];
   FLog.SaveTypes := [];
   If Not ForceDirectories(TNode.GetPascalCoinDataFolder) then raise Exception.Create('Cannot create dir: '+TNode.GetPascalCoinDataFolder);
   If Not ForceDirectories(TNode.GetPascalCoinDataFolder) then raise Exception.Create('Cannot create dir: '+TNode.GetPascalCoinDataFolder);
-  FAppParams := TAppParams.Create(self);
-  FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+  TSettings.Load;
+  TSettings.OnChanged.Add(UpdateConfigChanged);
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents.OnBlocksChanged := OnNewAccount;
   FNodeNotifyEvents.OnBlocksChanged := OnNewAccount;
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
@@ -1360,8 +1358,8 @@ begin
   FOperationsExplorerGrid.PendingOperations := False;
   FOperationsExplorerGrid.PendingOperations := False;
   FBlockChainGrid := TBlockChainGrid.Create(Self);
   FBlockChainGrid := TBlockChainGrid.Create(Self);
   FBlockChainGrid.DrawGrid := dgBlockChainExplorer;
   FBlockChainGrid.DrawGrid := dgBlockChainExplorer;
+  FBlockChainGrid.ShowTimeAverageColumns:={$IFDEF SHOW_AVERAGE_TIME_STATS}True;{$ELSE}False;{$ENDIF}
   // FWalletKeys.OnChanged.Add( OnWalletChanged );
   // FWalletKeys.OnChanged.Add( OnWalletChanged );
-  LoadAppParams;
   {$IFDEF USE_GNUGETTEXT}
   {$IFDEF USE_GNUGETTEXT}
   // use language from the params and retranslate if needed
   // use language from the params and retranslate if needed
   // might be better to move this a bit earlier in the formcreate routine
   // might be better to move this a bit earlier in the formcreate routine
@@ -1427,7 +1425,7 @@ begin
   Try
   Try
     i := StrToIntDef(ebHashRateBackBlocks.Text,-1);
     i := StrToIntDef(ebHashRateBackBlocks.Text,-1);
     FBlockChainGrid.HashRateAverageBlocksCount:=i;
     FBlockChainGrid.HashRateAverageBlocksCount:=i;
-    FAppParams.ParamByName[CT_PARAM_HashRateAvgBlocksCount].Value := FBlockChainGrid.HashRateAverageBlocksCount;
+    TSettings.HashRateAvgBlocksCount := FBlockChainGrid.HashRateAverageBlocksCount;
   Finally
   Finally
     ebHashRateBackBlocks.Text := IntToStr(FBlockChainGrid.HashRateAverageBlocksCount);
     ebHashRateBackBlocks.Text := IntToStr(FBlockChainGrid.HashRateAverageBlocksCount);
     FUpdating := false;
     FUpdating := false;
@@ -1449,7 +1447,7 @@ begin
       6 : FBlockChainGrid.HashRateAs := hr_Exa;
       6 : FBlockChainGrid.HashRateAs := hr_Exa;
     else FBlockChainGrid.HashRateAs := hr_Mega;
     else FBlockChainGrid.HashRateAs := hr_Mega;
     end;
     end;
-    FAppParams.ParamByName[CT_PARAM_ShowHashRateAs].Value := Integer(FBlockChainGrid.HashRateAs);
+    TSettings.ShowHashRateAs := FBlockChainGrid.HashRateAs;
   Finally
   Finally
     FUpdating := false;
     FUpdating := false;
   End;
   End;
@@ -1469,8 +1467,7 @@ begin
   FreeAndNil(FRPCServer);
   FreeAndNil(FRPCServer);
   FreeAndNil(FPoolMiningServer);
   FreeAndNil(FPoolMiningServer);
   step := 'Saving params';
   step := 'Saving params';
-  SaveAppParams;
-  FreeAndNil(FAppParams);
+  TSettings.Save;
   //
   //
   step := 'Assigning nil events';
   step := 'Assigning nil events';
   FLog.OnNewLog :=Nil;
   FLog.OnNewLog :=Nil;
@@ -1570,11 +1567,10 @@ Var PK : TECPrivateKey;
 begin
 begin
   Result := CT_TECDSA_Public_Nul;
   Result := CT_TECDSA_Public_Nul;
   if Not Assigned(FWalletKeys) then exit;
   if Not Assigned(FWalletKeys) then exit;
-  if Not Assigned(FAppParams) then exit;
   case FMinerPrivateKeyType of
   case FMinerPrivateKeyType of
     mpk_NewEachTime: PublicK := CT_TECDSA_Public_Nul;
     mpk_NewEachTime: PublicK := CT_TECDSA_Public_Nul;
     mpk_Selected: begin
     mpk_Selected: begin
-      PublicK := TAccountComp.RawString2Accountkey(FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].GetAsTBytes(Nil));
+      PublicK := TAccountComp.RawString2Accountkey(TSettings.MinerSelectedPublicKey);
     end;
     end;
   else
   else
     // Random
     // Random
@@ -1601,9 +1597,9 @@ begin
       PublicK := PK.PublicKey;
       PublicK := PK.PublicKey;
       // Set to AppParams if not mpk_NewEachTime
       // Set to AppParams if not mpk_NewEachTime
       if (FMinerPrivateKeyType<>mpk_NewEachTime) then begin
       if (FMinerPrivateKeyType<>mpk_NewEachTime) then begin
-        FAppParams.ParamByName[CT_PARAM_MinerPrivateKeySelectedPublicKey].SetAsTBytes(TAccountComp.AccountKey2RawString(PublicK));
+        TSettings.MinerSelectedPublicKey := TAccountComp.AccountKey2RawString(PublicK);
         FMinerPrivateKeyType:=mpk_Selected;
         FMinerPrivateKeyType:=mpk_Selected;
-        FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].Value := Integer(mpk_Selected);
+        TSettings.MinerPrivateKeyType := mpk_Selected;
       end;
       end;
     finally
     finally
       PK.Free;
       PK.Free;
@@ -1617,7 +1613,7 @@ Var FRM : TFRMNodesIp;
 begin
 begin
   FRM := TFRMNodesIp.Create(Self);
   FRM := TFRMNodesIp.Create(Self);
   Try
   Try
-    FRM.AppParams := FAppParams;
+    FRM.AppParams := TSettings.AppParams;
     FRM.ShowModal;
     FRM.ShowModal;
   Finally
   Finally
     FRM.Free;
     FRM.Free;
@@ -1629,28 +1625,6 @@ begin
   PageControl.ActivePage := tsMessages;
   PageControl.ActivePage := tsMessages;
 end;
 end;
 
 
-procedure TFRMWallet.LoadAppParams;
-Var ms : TMemoryStream;
-  s : AnsiString;
-begin
-  ms := TMemoryStream.Create;
-  Try
-    s := FAppParams.ParamByName[CT_PARAM_GridAccountsStream].GetAsString('');
-    ms.WriteBuffer(s[1],length(s));
-    ms.Position := 0;
-    // Disabled on V2: FAccountsGrid.LoadFromStream(ms);
-  Finally
-    ms.Free;
-  End;
-  If FAppParams.FindParam(CT_PARAM_MinerName)=Nil then begin
-    // New configuration... assigning a new random value
-    FAppParams.ParamByName[CT_PARAM_MinerName].SetAsString('New Node '+DateTimeToStr(Now)+' - '+
-      CT_ClientAppVersion);
-  end;
-  FBlockChainGrid.ShowTimeAverageColumns:={$IFDEF SHOW_AVERAGE_TIME_STATS}True;{$ELSE}False;{$ENDIF}
-  UpdateConfigChanged;
-end;
-
 procedure TFRMWallet.miAboutPascalCoinClick(Sender: TObject);
 procedure TFRMWallet.miAboutPascalCoinClick(Sender: TObject);
 begin
 begin
   With TFRMAbout.Create(Self) do
   With TFRMAbout.Create(Self) do
@@ -1737,11 +1711,11 @@ end;
 procedure TFRMWallet.MiDecodePayloadClick(Sender: TObject);
 procedure TFRMWallet.MiDecodePayloadClick(Sender: TObject);
 begin
 begin
   if PageControl.ActivePage=tsOperations then begin
   if PageControl.ActivePage=tsOperations then begin
-    FOperationsExplorerGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+    FOperationsExplorerGrid.ShowModalDecoder(FWalletKeys, TSettings.AppParams);
   end else if PageControl.ActivePage=tsPendingOperations then begin
   end else if PageControl.ActivePage=tsPendingOperations then begin
-    FPendingOperationsGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+    FPendingOperationsGrid.ShowModalDecoder(FWalletKeys,TSettings.AppParams);
   end else if PageControl.ActivePage=tsMyAccounts then begin
   end else if PageControl.ActivePage=tsMyAccounts then begin
-    FOperationsAccountGrid.ShowModalDecoder(FWalletKeys,FAppParams);
+    FOperationsAccountGrid.ShowModalDecoder(FWalletKeys,TSettings.AppParams);
   end;
   end;
 end;
 end;
 
 
@@ -1987,7 +1961,7 @@ begin
   //
   //
   FRM := TFRMPayloadDecoder.Create(Self);
   FRM := TFRMPayloadDecoder.Create(Self);
   try
   try
-    FRM.Init(CT_TOperationResume_NUL,WalletKeys,FAppParams);
+    FRM.Init(CT_TOperationResume_NUL,WalletKeys,TSettings.AppParams);
     FRM.DoFind(oph);
     FRM.DoFind(oph);
     FRM.ShowModal;
     FRM.ShowModal;
   finally
   finally
@@ -2036,7 +2010,7 @@ begin
     finally
     finally
       l.Free;
       l.Free;
     end;
     end;
-    DefaultFee := FAppParams.ParamByName[CT_PARAM_DefaultFee].GetAsInt64(0);
+    DefaultFee := TSettings.DefaultFee;
     WalletKeys := FWalletKeys;
     WalletKeys := FWalletKeys;
     ShowModal;
     ShowModal;
   Finally
   Finally
@@ -2048,11 +2022,11 @@ procedure TFRMWallet.miOptionsClick(Sender: TObject);
 begin
 begin
   With TFRMPascalCoinWalletConfig.Create(Self) do
   With TFRMPascalCoinWalletConfig.Create(Self) do
   try
   try
-    AppParams := Self.FAppParams;
+    AppParams := TSettings.AppParams;
     WalletKeys := Self.FWalletKeys;
     WalletKeys := Self.FWalletKeys;
     if ShowModal=MrOk then begin
     if ShowModal=MrOk then begin
-      SaveAppParams;
-      UpdateConfigChanged;
+      TSettings.Save;
+      UpdateConfigChanged(Self);
       {$IFDEF USE_GNUGETTEXT}RetranslateComponent(self);{$ENDIF}
       {$IFDEF USE_GNUGETTEXT}RetranslateComponent(self);{$ENDIF}
     end;
     end;
   finally
   finally
@@ -2251,7 +2225,7 @@ begin
     s := DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr;
     s := DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr;
     memoMessages.Lines.Add(DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr+' Length '+inttostr(Length(MessageData))+' bytes');
     memoMessages.Lines.Add(DateTimeToStr(now)+' Message received from '+NetConnection.ClientRemoteAddr+' Length '+inttostr(Length(MessageData))+' bytes');
     memoMessages.Lines.Add('RECEIVED> '+MessageData);
     memoMessages.Lines.Add('RECEIVED> '+MessageData);
-    if FAppParams.ParamByName[CT_PARAM_ShowModalMessages].GetAsBoolean(false) then begin
+    if TSettings.ShowModalMessages then begin
       s := DateTimeToStr(now)+' Message from '+NetConnection.ClientRemoteAddr+#10+
       s := DateTimeToStr(now)+' Message from '+NetConnection.ClientRemoteAddr+#10+
          'Length '+inttostr(length(MessageData))+' bytes'+#10+#10;
          'Length '+inttostr(length(MessageData))+' bytes'+#10+#10;
       if TCrypto.IsHumanReadable(TEncoding.ANSI.GetBytes(MessageData)) then begin
       if TCrypto.IsHumanReadable(TEncoding.ANSI.GetBytes(MessageData)) then begin
@@ -2289,7 +2263,7 @@ begin
     if (s<>'') then s := s+';';
     if (s<>'') then s := s+';';
     s := s + nsarr[i].ip+':'+IntToStr( nsarr[i].port );
     s := s + nsarr[i].ip+':'+IntToStr( nsarr[i].port );
   end;
   end;
-  FAppParams.ParamByName[CT_PARAM_PeerCache].SetAsString(s);
+  TSettings.PeerCache := s;
   TNode.Node.PeerCache := s;
   TNode.Node.PeerCache := s;
 end;
 end;
 
 
@@ -2334,22 +2308,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TFRMWallet.SaveAppParams;
-Var ms : TMemoryStream;
-  s : AnsiString;
-begin
-  ms := TMemoryStream.Create;
-  Try
-    FAccountsGrid.SaveToStream(ms);
-    ms.Position := 0;
-    setlength(s,ms.Size);
-    ms.ReadBuffer(s[1],ms.Size);
-    FAppParams.ParamByName[CT_PARAM_GridAccountsStream].SetAsString(s);
-  Finally
-    ms.Free;
-  End;
-end;
-
 procedure TFRMWallet.sbSelectedAccountsAddAllClick(Sender: TObject);
 procedure TFRMWallet.sbSelectedAccountsAddAllClick(Sender: TObject);
 Var lsource,ltarget : TOrderedCardinalList;
 Var lsource,ltarget : TOrderedCardinalList;
   i : Integer;
   i : Integer;
@@ -2587,18 +2545,18 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TFRMWallet.UpdateConfigChanged;
+procedure TFRMWallet.UpdateConfigChanged(Sender:TObject);
 Var wa : Boolean;
 Var wa : Boolean;
   i : Integer;
   i : Integer;
   LLockedMempool : TPCOperationsComp;
   LLockedMempool : TPCOperationsComp;
 begin
 begin
-  tsLogs.TabVisible := FAppParams.ParamByName[CT_PARAM_ShowLogs].GetAsBoolean(false);
+  tsLogs.TabVisible := TSettings.ShowLogs;
   if (Not tsLogs.TabVisible) then begin
   if (Not tsLogs.TabVisible) then begin
     FLog.OnNewLog := Nil;
     FLog.OnNewLog := Nil;
     if PageControl.ActivePage = tsLogs then PageControl.ActivePage := tsMyAccounts;
     if PageControl.ActivePage = tsLogs then PageControl.ActivePage := tsMyAccounts;
   end else FLog.OnNewLog := OnNewLog;
   end else FLog.OnNewLog := OnNewLog;
-  if (FAppParams.ParamByName[CT_PARAM_SaveLogFiles].GetAsBoolean(false)) then begin
-    if FAppParams.ParamByName[CT_PARAM_SaveDebugLogs].GetAsBoolean(false) then FLog.SaveTypes := CT_TLogTypes_ALL
+  if TSettings.SaveLogFiles then begin
+    if TSettings.SaveDebugLogs then FLog.SaveTypes := CT_TLogTypes_ALL
     else FLog.SaveTypes := CT_TLogTypes_DEFAULT;
     else FLog.SaveTypes := CT_TLogTypes_DEFAULT;
     FLog.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
     FLog.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
   end else begin
   end else begin
@@ -2607,30 +2565,30 @@ begin
   end;
   end;
   if Assigned(FNode) then begin
   if Assigned(FNode) then begin
     wa := FNode.NetServer.Active;
     wa := FNode.NetServer.Active;
-    FNode.NetServer.Port := FAppParams.ParamByName[CT_PARAM_InternetServerPort].GetAsInteger(CT_NetServer_Port);
+    FNode.NetServer.Port := TSettings.InternetServerPort;
     FNode.NetServer.Active := wa;
     FNode.NetServer.Active := wa;
     LLockedMempool := FNode.LockMempoolWrite;
     LLockedMempool := FNode.LockMempoolWrite;
     try
     try
-      LLockedMempool.BlockPayload := TEncoding.ANSI.GetBytes(FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString(''));
+      LLockedMempool.BlockPayload := TEncoding.ANSI.GetBytes(TSettings.MinerName);
     finally
     finally
       FNode.UnlockMempoolWrite;
       FNode.UnlockMempoolWrite;
     end;
     end;
     FNode.NodeLogFilename := TNode.GetPascalCoinDataFolder+PathDelim+'blocks.log';
     FNode.NodeLogFilename := TNode.GetPascalCoinDataFolder+PathDelim+'blocks.log';
   end;
   end;
   if Assigned(FPoolMiningServer) then begin
   if Assigned(FPoolMiningServer) then begin
-    if FPoolMiningServer.Port<>FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port) then begin
+    if FPoolMiningServer.Port<>TSettings.JsonRpcMinerServerPort then begin
       FPoolMiningServer.Active := false;
       FPoolMiningServer.Active := false;
-      FPoolMiningServer.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
+      FPoolMiningServer.Port := TSettings.JsonRpcMinerServerPort;
     end;
     end;
-    FPoolMiningServer.Active :=FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerActive].GetAsBoolean(true);
-    FPoolMiningServer.UpdateAccountAndPayload(GetAccountKeyForMiner,TEncoding.ANSI.GetBytes(FAppParams.ParamByName[CT_PARAM_MinerName].GetAsString('')));
+    FPoolMiningServer.Active := TSettings.JsonRpcMinerServerActive;
+    FPoolMiningServer.UpdateAccountAndPayload(GetAccountKeyForMiner,TEncoding.ANSI.GetBytes(TSettings.MinerName));
   end;
   end;
   if Assigned(FRPCServer) then begin
   if Assigned(FRPCServer) then begin
-    FRPCServer.Active := FAppParams.ParamByName[CT_PARAM_JSONRPCEnabled].GetAsBoolean(false);
-    FRPCServer.ValidIPs := FAppParams.ParamByName[CT_PARAM_JSONRPCAllowedIPs].GetAsString('127.0.0.1');
+    FRPCServer.Active := TSettings.JsonRpcPortEnabled;
+    FRPCServer.ValidIPs := TSettings.JsonRpcAllowedIPs;
   end;
   end;
-  i := FAppParams.ParamByName[CT_PARAM_MinerPrivateKeyType].GetAsInteger(Integer(mpk_Random));
-  if (i>=Integer(Low(TMinerPrivatekey))) And (i<=Integer(High(TMinerPrivatekey))) then FMinerPrivateKeyType := TMinerPrivateKey(i)
+  i := Integer(TSettings.MinerPrivateKeyType);
+  if (i>=Integer(Low(TMinerPrivateKeyType))) And (i<=Integer(High(TMinerPrivateKeyType))) then FMinerPrivateKeyType := TMinerPrivateKeyType(i)
   else FMinerPrivateKeyType := mpk_Random;
   else FMinerPrivateKeyType := mpk_Random;
   ebHashRateBackBlocks.Text := IntToStr(FBlockChainGrid.HashRateAverageBlocksCount);
   ebHashRateBackBlocks.Text := IntToStr(FBlockChainGrid.HashRateAverageBlocksCount);
   Case FBlockChainGrid.HashRateAs of
   Case FBlockChainGrid.HashRateAs of
@@ -2644,8 +2602,8 @@ begin
   else cbHashRateUnits.ItemIndex:=-1;
   else cbHashRateUnits.ItemIndex:=-1;
   end;
   end;
   if TNetData.NetDataExists then begin
   if TNetData.NetDataExists then begin
-    if FAppParams.ParamByName[CT_PARAM_AllowDownloadNewCheckpointIfOlderThan].GetAsBoolean(TNetData.NetData.MinFutureBlocksToDownloadNewSafebox>200) then begin
-      TNetData.NetData.MinFutureBlocksToDownloadNewSafebox:=FAppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].GetAsInteger(TNetData.NetData.MinFutureBlocksToDownloadNewSafebox);
+    if TSettings.AppParams.ParamByName[CT_PARAM_AllowDownloadNewCheckpointIfOlderThan].GetAsBoolean(TNetData.NetData.MinFutureBlocksToDownloadNewSafebox>200) then begin
+      TNetData.NetData.MinFutureBlocksToDownloadNewSafebox:=TSettings.AppParams.ParamByName[CT_PARAM_MinFutureBlocksToDownloadNewSafebox].GetAsInteger(TNetData.NetData.MinFutureBlocksToDownloadNewSafebox);
     end else TNetData.NetData.MinFutureBlocksToDownloadNewSafebox:=0;
     end else TNetData.NetData.MinFutureBlocksToDownloadNewSafebox:=0;
   end;
   end;
 end;
 end;

+ 1 - 3
src/gui-classic/UGridUtils.pas

@@ -31,7 +31,7 @@ uses
   LCLIntf, LCLType, LMessages,
   LCLIntf, LCLType, LMessages,
 {$ENDIF}
 {$ENDIF}
   Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams, UThread, UPCDataTypes,
   Classes, Grids, UNode, UAccounts, UBlockChain, UAppParams, UThread, UPCDataTypes,
-  UWallet, UCrypto, UPoolMining, URPC, UBaseTypes, UPCOrderedLists,
+  UWallet, UCrypto, UPoolMining, URPC, UBaseTypes, UPCOrderedLists, USettings,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 Type
 Type
@@ -230,8 +230,6 @@ Type
 
 
   { TBlockChainGrid }
   { TBlockChainGrid }
 
 
-  TShowHashRateAs = (hr_Unit, hr_Kilo, hr_Mega, hr_Giga, hr_Tera, hr_Peta, hr_Exa);
-
   TBlockChainGrid = Class(TComponent)
   TBlockChainGrid = Class(TComponent)
   private
   private
     FBlockChainDataList : TList<TBlockChainData>;
     FBlockChainDataList : TList<TBlockChainData>;

+ 21 - 0
src/libraries/pascalcoin/UAppParams.pas

@@ -57,6 +57,7 @@ Type
     Procedure SetAsCardinal(CardValue : Cardinal);
     Procedure SetAsCardinal(CardValue : Cardinal);
     Procedure SetAsString(StringValue : String);
     Procedure SetAsString(StringValue : String);
     Procedure SetAsInt64(Int64Value : Int64);
     Procedure SetAsInt64(Int64Value : Int64);
+    Procedure SetAsUInt64(UInt64Value : UInt64);
     Procedure SetAsBoolean(BoolValue : Boolean);
     Procedure SetAsBoolean(BoolValue : Boolean);
     Procedure SetAsStream(Stream : TStream);
     Procedure SetAsStream(Stream : TStream);
     Procedure SetAsTBytes(Bytes : TBytes);
     Procedure SetAsTBytes(Bytes : TBytes);
@@ -65,6 +66,7 @@ Type
     function GetAsBoolean(Const DefValue : Boolean): Boolean;
     function GetAsBoolean(Const DefValue : Boolean): Boolean;
     function GetAsInteger(Const DefValue : Integer): Integer;
     function GetAsInteger(Const DefValue : Integer): Integer;
     function GetAsInt64(Const DefValue : Int64): Int64;
     function GetAsInt64(Const DefValue : Int64): Int64;
+    function GetAsUInt64(Const DefValue : UInt64): UInt64;
     function GetAsStream(Stream : TStream) : Integer;
     function GetAsStream(Stream : TStream) : Integer;
     function GetAsTBytes(Const DefValue : TBytes) : TBytes;
     function GetAsTBytes(Const DefValue : TBytes) : TBytes;
   End;
   End;
@@ -186,6 +188,18 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TAppParam.GetAsUInt64(const DefValue: UInt64): UInt64;
+begin
+  if IsNull then Result := DefValue
+  else begin
+    Try
+      Result := FValue;
+    Except
+      Result := DefValue;
+    End;
+  end;
+end;
+
 function TAppParam.GetAsInteger(const DefValue: Integer): Integer;
 function TAppParam.GetAsInteger(const DefValue: Integer): Integer;
 begin
 begin
   if IsNull then Result := DefValue
   if IsNull then Result := DefValue
@@ -357,6 +371,13 @@ begin
   If Assigned(FAppParams) then FAppParams.Save;
   If Assigned(FAppParams) then FAppParams.Save;
 end;
 end;
 
 
+procedure TAppParam.SetAsUInt64(UInt64Value: UInt64);
+begin
+  FParamType := ptInt64;
+  FValue := UInt64Value;
+  If Assigned(FAppParams) then FAppParams.Save;
+end;
+
 procedure TAppParam.SetAsInteger(IntValue: Integer);
 procedure TAppParam.SetAsInteger(IntValue: Integer);
 begin
 begin
   FParamType := ptInteger;
   FParamType := ptInteger;

+ 20 - 0
src/libraries/pascalcoin/UJSONFunctions.pas

@@ -76,6 +76,7 @@ Type
     Function ToJSONFormatted(pretty:Boolean;const prefix : String) : String; override;
     Function ToJSONFormatted(pretty:Boolean;const prefix : String) : String; override;
   public
   public
     Constructor Create; override;
     Constructor Create; override;
+    Constructor CreateFromVariant(const Value: Variant);
     Constructor CreateFromJSONValue(JSONValue : TJSONValue);
     Constructor CreateFromJSONValue(JSONValue : TJSONValue);
     Property Value : Variant read FValue write SetValue;
     Property Value : Variant read FValue write SetValue;
     Function AsString(DefValue : String) : String;
     Function AsString(DefValue : String) : String;
@@ -158,6 +159,8 @@ Type
     Destructor Destroy; override;
     Destructor Destroy; override;
     Function FindName(Name : String) : TPCJSONNameValue;
     Function FindName(Name : String) : TPCJSONNameValue;
     Function IndexOfName(Name : String) : Integer;
     Function IndexOfName(Name : String) : Integer;
+    Function HasName(Name: String): Boolean;
+    Function HasValue(const AParamName : String) : Boolean;
     Procedure DeleteName(Name : String);
     Procedure DeleteName(Name : String);
     Function GetAsVariant(Name : String) : TPCJSONVariantValue;
     Function GetAsVariant(Name : String) : TPCJSONVariantValue;
     Function GetAsObject(Name : String) : TPCJSONObject;
     Function GetAsObject(Name : String) : TPCJSONObject;
@@ -496,6 +499,12 @@ begin
   FWritable := False;
   FWritable := False;
 end;
 end;
 
 
+Constructor TPCJSONVariantValue.CreateFromVariant(const Value: Variant);
+begin
+  Create;
+  SetValue(Value);
+end;
+
 constructor TPCJSONVariantValue.CreateFromJSONValue(JSONValue: TJSONValue);
 constructor TPCJSONVariantValue.CreateFromJSONValue(JSONValue: TJSONValue);
 {$IFnDEF FPC}
 {$IFnDEF FPC}
 Var d : Double;
 Var d : Double;
@@ -878,6 +887,17 @@ begin
   Result := -1;
   Result := -1;
 end;
 end;
 
 
+function TPCJSONObject.HasName(Name: String): Boolean;
+begin
+  Result := IndexOfName(Name) >= 0;
+end;
+
+Function TPCJSONObject.HasValue(const AParamName : String) : Boolean;
+begin
+  Result := HasName(AParamName) AND (NOT AsString(AParamName, String.Empty).IsEmpty);
+end;
+
+
 function TPCJSONObject.LoadAsStream(ParamName: String; Stream: TStream): Integer;
 function TPCJSONObject.LoadAsStream(ParamName: String; Stream: TStream): Integer;
 Var s : RawByteString;
 Var s : RawByteString;
 begin
 begin

+ 51 - 32
src/pascalcoin_wallet_classic.dpr

@@ -15,25 +15,73 @@ uses
   Interfaces,
   Interfaces,
   {$ENDIF }
   {$ENDIF }
   Forms,
   Forms,
+  UFRMAbout in 'gui-classic\UFRMAbout.pas' {FRMAbout},
+  UFRMAccountSelect in 'gui-classic\UFRMAccountSelect.pas' {FRMAccountSelect},
+  UFRMMemoText in 'gui-classic\UFRMMemoText.pas' {FRMMemoText},
+  UFRMNewPrivateKeyType in 'gui-classic\UFRMNewPrivateKeyType.pas' {FRMNewPrivateKeyType},
+  UFRMNodesIp in 'gui-classic\UFRMNodesIp.pas' {FRMNodesIp},
+  UFRMOperation in 'gui-classic\UFRMOperation.pas' {FRMOperation},
+  UFRMOperationsExplorer in 'gui-classic\UFRMOperationsExplorer.pas' {FRMOperationsExplorer},
+  {$IFDEF USE_GNUGETTEXT}
+  UFRMSelectLanguage in 'gui-classic\UFRMSelectLanguage.pas' {FRMChangeLanguage},
+  {$ENDIF }
+  UFRMPascalCoinWalletConfig in 'gui-classic\UFRMPascalCoinWalletConfig.pas' {FRMPascalCoinWalletConfig},
+  UFRMPayloadDecoder in 'gui-classic\UFRMPayloadDecoder.pas' {FRMPayloadDecoder},
+  UFRMRandomOperations in 'gui-classic\UFRMRandomOperations.pas' {FRMRandomOperations},
+  UFRMRPCCalls in 'gui-classic\UFRMRPCCalls.pas' {FRMRPCCalls},
+  UFRMWallet in 'gui-classic\UFRMWallet.pas' {FRMWallet},
+  UFRMWalletKeys in 'gui-classic\UFRMWalletKeys.pas' {FRMWalletKeys},
+  UGridUtils in 'gui-classic\UGridUtils.pas',
+  UGUIUtils in 'gui-classic\UGUIUtils.pas',
+  UFRMHashLock in 'gui-classic\UFRMHashLock.pas' {FRMHashLock},
+  UFRMDiagnosticTool in 'gui-classic\UFRMDiagnosticTool.pas' {FRMDiagnosticTool},
+  UCommon in 'libraries\sphere10\UCommon.pas',
+  UMemory in 'libraries\sphere10\UMemory.pas',
   UAccountKeyStorage in 'core\UAccountKeyStorage.pas',
   UAccountKeyStorage in 'core\UAccountKeyStorage.pas',
   UAccounts in 'core\UAccounts.pas',
   UAccounts in 'core\UAccounts.pas',
-  {$IFDEF Use_OpenSSL}
   UAES in 'core\UAES.pas',
   UAES in 'core\UAES.pas',
-  {$ENDIF }
   UBaseTypes in 'core\UBaseTypes.pas',
   UBaseTypes in 'core\UBaseTypes.pas',
   UBlockChain in 'core\UBlockChain.pas',
   UBlockChain in 'core\UBlockChain.pas',
   UChunk in 'core\UChunk.pas',
   UChunk in 'core\UChunk.pas',
   UConst in 'core\UConst.pas',
   UConst in 'core\UConst.pas',
   UCrypto in 'core\UCrypto.pas',
   UCrypto in 'core\UCrypto.pas',
+  UECIES in 'core\UECIES.pas',
+  UEncoding in 'core\UEncoding.pas',
+  UEPasa in 'core\UEPasa.pas',
   UFileStorage in 'core\UFileStorage.pas',
   UFileStorage in 'core\UFileStorage.pas',
   ULog in 'core\ULog.pas',
   ULog in 'core\ULog.pas',
+  UMurMur3Fast in 'core\UMurMur3Fast.pas',
   UNetProtection in 'core\UNetProtection.pas',
   UNetProtection in 'core\UNetProtection.pas',
   UNetProtocol in 'core\UNetProtocol.pas',
   UNetProtocol in 'core\UNetProtocol.pas',
   UNode in 'core\UNode.pas',
   UNode in 'core\UNode.pas',
+  UOpenSSL in 'core\UOpenSSL.pas',
   UOpTransaction in 'core\UOpTransaction.pas',
   UOpTransaction in 'core\UOpTransaction.pas',
+  {$IFDEF USE_ABSTRACTMEM}
+  UPCAbstractMem in 'core\UPCAbstractMem.pas',
+  UPCAbstractMemAccountKeys in 'core\UPCAbstractMemAccountKeys.pas',
+  {$ENDIF}
+  UPCAbstractMemAccounts in 'core\UPCAbstractMemAccounts.pas',
+  UPCAccountsOrdenations in 'core\UPCAccountsOrdenations.pas',
+  UPCCryptoLib4Pascal in 'core\UPCCryptoLib4Pascal.pas',
+  UPCDataTypes in 'core\UPCDataTypes.pas',
+  UPCEncryption in 'core\UPCEncryption.pas',
+  UPCHardcodedRandomHashTable in 'core\UPCHardcodedRandomHashTable.pas',
+  UPCOperationsBlockValidator in 'core\UPCOperationsBlockValidator.pas',
+  UPCOperationsSignatureValidator in 'core\UPCOperationsSignatureValidator.pas',
+  UPCOrderedLists in 'core\UPCOrderedLists.pas',
+  UPCRPCFileUtils in 'core\UPCRPCFileUtils.pas',
+  UPCRPCFindAccounts in 'core\UPCRPCFindAccounts.pas',
+  UPCRPCFindBlocks in 'core\UPCRPCFindBlocks.pas',
+  UPCRPCOpData in 'core\UPCRPCOpData.pas',
+  UPCRPCSend in 'core\UPCRPCSend.pas',
+  UPCSafeBoxRootHash in 'core\UPCSafeBoxRootHash.pas',
+  UPCTemporalAbstractMem in 'core\UPCTemporalAbstractMem.pas',
+  UPCTemporalFileStream in 'core\UPCTemporalFileStream.pas',
+  UPCTNetDataExtraMessages in 'core\UPCTNetDataExtraMessages.pas',
   UPoolMinerThreads in 'core\UPoolMinerThreads.pas',
   UPoolMinerThreads in 'core\UPoolMinerThreads.pas',
   UPoolMining in 'core\UPoolMining.pas',
   UPoolMining in 'core\UPoolMining.pas',
   URandomHash in 'core\URandomHash.pas',
   URandomHash in 'core\URandomHash.pas',
+  URandomHash2 in 'core\URandomHash2.pas',
   URPC in 'core\URPC.pas',
   URPC in 'core\URPC.pas',
   USettings in 'core\USettings.pas',
   USettings in 'core\USettings.pas',
   USha256 in 'core\USha256.pas',
   USha256 in 'core\USha256.pas',
@@ -41,36 +89,7 @@ uses
   UThread in 'core\UThread.pas',
   UThread in 'core\UThread.pas',
   UTime in 'core\UTime.pas',
   UTime in 'core\UTime.pas',
   UTxMultiOperation in 'core\UTxMultiOperation.pas',
   UTxMultiOperation in 'core\UTxMultiOperation.pas',
-  UWallet in 'core\UWallet.pas',
-  UFRMAbout in 'gui-classic\UFRMAbout.pas' {FRMAbout},
-  UFRMAccountSelect in 'gui-classic\UFRMAccountSelect.pas' {FRMAccountSelect},
-  UFRMMemoText in 'gui-classic\UFRMMemoText.pas' {FRMMemoText},
-  UFRMNewPrivateKeyType in 'gui-classic\UFRMNewPrivateKeyType.pas' {FRMNewPrivateKeyType},
-  UFRMNodesIp in 'gui-classic\UFRMNodesIp.pas' {FRMNodesIp},
-  UFRMOperation in 'gui-classic\UFRMOperation.pas' {FRMOperation},
-  UFRMOperationsExplorer in 'gui-classic\UFRMOperationsExplorer.pas' {FRMOperationsExplorer},
-  {$IFDEF USE_GNUGETTEXT}
-  UFRMSelectLanguage in 'gui-classic\UFRMSelectLanguage.pas' {FRMChangeLanguage},
-  {$ENDIF }
-  UFRMPascalCoinWalletConfig in 'gui-classic\UFRMPascalCoinWalletConfig.pas' {FRMPascalCoinWalletConfig},
-  UFRMPayloadDecoder in 'gui-classic\UFRMPayloadDecoder.pas' {FRMPayloadDecoder},
-  UFRMRandomOperations in 'gui-classic\UFRMRandomOperations.pas' {FRMRandomOperations},
-  UFRMRPCCalls in 'gui-classic\UFRMRPCCalls.pas' {FRMRPCCalls},
-  UFRMWallet in 'gui-classic\UFRMWallet.pas' {FRMWallet},
-  UFRMWalletKeys in 'gui-classic\UFRMWalletKeys.pas' {FRMWalletKeys},
-  UGridUtils in 'gui-classic\UGridUtils.pas',
-  UGUIUtils in 'gui-classic\UGUIUtils.pas',
-  UPCDataTypes in 'core\UPCDataTypes.pas',
-  UPCOrderedLists in 'core\UPCOrderedLists.pas',
-  UPCOperationsSignatureValidator in 'core\UPCOperationsSignatureValidator.pas',
-  UPCTNetDataExtraMessages in 'core\UPCTNetDataExtraMessages.pas',
-  UFRMHashLock in 'gui-classic\UFRMHashLock.pas' {FRMHashLock},
-  URandomHash2 in 'core\URandomHash2.pas',
-  UFRMDiagnosticTool in 'gui-classic\UFRMDiagnosticTool.pas' {FRMDiagnosticTool},
-  UCommon in 'libraries\sphere10\UCommon.pas',
-  UMemory in 'libraries\sphere10\UMemory.pas',
-  UEPasa in 'core\UEPasa.pas',
-  UEncoding in 'core\UEncoding.pas';
+  UWallet in 'core\UWallet.pas';
 
 
 {$R *.res}
 {$R *.res}
 
 

+ 50 - 31
src/pascalcoin_wallet_classic.dproj

@@ -125,31 +125,6 @@
         <DelphiCompile Include="$(MainSource)">
         <DelphiCompile Include="$(MainSource)">
             <MainSource>MainSource</MainSource>
             <MainSource>MainSource</MainSource>
         </DelphiCompile>
         </DelphiCompile>
-        <DCCReference Include="core\UAccountKeyStorage.pas"/>
-        <DCCReference Include="core\UAccounts.pas"/>
-        <DCCReference Include="core\UAES.pas"/>
-        <DCCReference Include="core\UBaseTypes.pas"/>
-        <DCCReference Include="core\UBlockChain.pas"/>
-        <DCCReference Include="core\UChunk.pas"/>
-        <DCCReference Include="core\UConst.pas"/>
-        <DCCReference Include="core\UCrypto.pas"/>
-        <DCCReference Include="core\UFileStorage.pas"/>
-        <DCCReference Include="core\ULog.pas"/>
-        <DCCReference Include="core\UNetProtection.pas"/>
-        <DCCReference Include="core\UNetProtocol.pas"/>
-        <DCCReference Include="core\UNode.pas"/>
-        <DCCReference Include="core\UOpTransaction.pas"/>
-        <DCCReference Include="core\UPoolMinerThreads.pas"/>
-        <DCCReference Include="core\UPoolMining.pas"/>
-        <DCCReference Include="core\URandomHash.pas"/>
-        <DCCReference Include="core\URPC.pas"/>
-        <DCCReference Include="core\USettings.pas"/>
-        <DCCReference Include="core\USha256.pas"/>
-        <DCCReference Include="core\UTCPIP.pas"/>
-        <DCCReference Include="core\UThread.pas"/>
-        <DCCReference Include="core\UTime.pas"/>
-        <DCCReference Include="core\UTxMultiOperation.pas"/>
-        <DCCReference Include="core\UWallet.pas"/>
         <DCCReference Include="gui-classic\UFRMAbout.pas">
         <DCCReference Include="gui-classic\UFRMAbout.pas">
             <Form>FRMAbout</Form>
             <Form>FRMAbout</Form>
             <FormType>dfm</FormType>
             <FormType>dfm</FormType>
@@ -201,21 +176,65 @@
         </DCCReference>
         </DCCReference>
         <DCCReference Include="gui-classic\UGridUtils.pas"/>
         <DCCReference Include="gui-classic\UGridUtils.pas"/>
         <DCCReference Include="gui-classic\UGUIUtils.pas"/>
         <DCCReference Include="gui-classic\UGUIUtils.pas"/>
-        <DCCReference Include="core\UPCDataTypes.pas"/>
-        <DCCReference Include="core\UPCOrderedLists.pas"/>
-        <DCCReference Include="core\UPCOperationsSignatureValidator.pas"/>
-        <DCCReference Include="core\UPCTNetDataExtraMessages.pas"/>
         <DCCReference Include="gui-classic\UFRMHashLock.pas">
         <DCCReference Include="gui-classic\UFRMHashLock.pas">
             <Form>FRMHashLock</Form>
             <Form>FRMHashLock</Form>
         </DCCReference>
         </DCCReference>
-        <DCCReference Include="core\URandomHash2.pas"/>
         <DCCReference Include="gui-classic\UFRMDiagnosticTool.pas">
         <DCCReference Include="gui-classic\UFRMDiagnosticTool.pas">
             <Form>FRMDiagnosticTool</Form>
             <Form>FRMDiagnosticTool</Form>
         </DCCReference>
         </DCCReference>
         <DCCReference Include="libraries\sphere10\UCommon.pas"/>
         <DCCReference Include="libraries\sphere10\UCommon.pas"/>
         <DCCReference Include="libraries\sphere10\UMemory.pas"/>
         <DCCReference Include="libraries\sphere10\UMemory.pas"/>
-        <DCCReference Include="core\UEPasa.pas"/>
+        <DCCReference Include="core\UAccountKeyStorage.pas"/>
+        <DCCReference Include="core\UAccounts.pas"/>
+        <DCCReference Include="core\UAES.pas"/>
+        <DCCReference Include="core\UBaseTypes.pas"/>
+        <DCCReference Include="core\UBlockChain.pas"/>
+        <DCCReference Include="core\UChunk.pas"/>
+        <DCCReference Include="core\UConst.pas"/>
+        <DCCReference Include="core\UCrypto.pas"/>
+        <DCCReference Include="core\UECIES.pas"/>
         <DCCReference Include="core\UEncoding.pas"/>
         <DCCReference Include="core\UEncoding.pas"/>
+        <DCCReference Include="core\UEPasa.pas"/>
+        <DCCReference Include="core\UFileStorage.pas"/>
+        <DCCReference Include="core\ULog.pas"/>
+        <DCCReference Include="core\UMurMur3Fast.pas"/>
+        <DCCReference Include="core\UNetProtection.pas"/>
+        <DCCReference Include="core\UNetProtocol.pas"/>
+        <DCCReference Include="core\UNode.pas"/>
+        <DCCReference Include="core\UOpenSSL.pas"/>
+        <DCCReference Include="core\UOpTransaction.pas"/>
+        <DCCReference Include="core\UPCAbstractMem.pas"/>
+        <DCCReference Include="core\UPCAbstractMemAccountKeys.pas"/>
+        <DCCReference Include="core\UPCAbstractMemAccounts.pas"/>
+        <DCCReference Include="core\UPCAccountsOrdenations.pas"/>
+        <DCCReference Include="core\UPCCryptoLib4Pascal.pas"/>
+        <DCCReference Include="core\UPCDataTypes.pas"/>
+        <DCCReference Include="core\UPCEncryption.pas"/>
+        <DCCReference Include="core\UPCHardcodedRandomHashTable.pas"/>
+        <DCCReference Include="core\UPCOperationsBlockValidator.pas"/>
+        <DCCReference Include="core\UPCOperationsSignatureValidator.pas"/>
+        <DCCReference Include="core\UPCOrderedLists.pas"/>
+        <DCCReference Include="core\UPCRPCFileUtils.pas"/>
+        <DCCReference Include="core\UPCRPCFindAccounts.pas"/>
+        <DCCReference Include="core\UPCRPCFindBlocks.pas"/>
+        <DCCReference Include="core\UPCRPCOpData.pas"/>
+        <DCCReference Include="core\UPCRPCSend.pas"/>
+        <DCCReference Include="core\UPCSafeBoxRootHash.pas"/>
+        <DCCReference Include="core\UPCTemporalAbstractMem.pas"/>
+        <DCCReference Include="core\UPCTemporalFileStream.pas"/>
+        <DCCReference Include="core\UPCTNetDataExtraMessages.pas"/>
+        <DCCReference Include="core\UPoolMinerThreads.pas"/>
+        <DCCReference Include="core\UPoolMining.pas"/>
+        <DCCReference Include="core\URandomHash.pas"/>
+        <DCCReference Include="core\URandomHash2.pas"/>
+        <DCCReference Include="core\URPC.pas"/>
+        <DCCReference Include="core\USettings.pas"/>
+        <DCCReference Include="core\USha256.pas"/>
+        <DCCReference Include="core\UTCPIP.pas"/>
+        <DCCReference Include="core\UThread.pas"/>
+        <DCCReference Include="core\UTime.pas"/>
+        <DCCReference Include="core\UTxMultiOperation.pas"/>
+        <DCCReference Include="core\UWallet.pas"/>
         <BuildConfiguration Include="Debug">
         <BuildConfiguration Include="Debug">
             <Key>Cfg_2</Key>
             <Key>Cfg_2</Key>
             <CfgParent>Base</CfgParent>
             <CfgParent>Base</CfgParent>