Browse Source

Merge upstream.

Herman Schoenfeld 6 years ago
parent
commit
a2a34764ef

+ 48 - 5
README.md

@@ -36,16 +36,59 @@ Also, consider a donation at PascalCoin development account: "0-10"
 
 ### Current Build (Pending release date)
 - Upgrade to Protocol 5 (Hard fork)
-- Implementation of PIP-0030 -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0030.md
-- Implementation of PIP-0029 -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0029.md
-- New digest hash value for OP_DATA ( PIP-0016 ) on Protocol 5
+- Implementation of PIP-0032 (Atomic Swaps) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0032.md
+- Implementation of PIP-0030 (Safebox root) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0030.md
+- Implementation of PIP-0029 (Account Seals) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0029.md
+- Implementation of PIP-0024 (Account Data) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0024.md
+- Implementation of PIP-0033 (OpData JOSN-RPC calls) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0033.md
+- Updated "OP_DATA" operation: (PIP-0016)
+  - New digest hash value for OP_DATA ( PIP-0016 ) on Protocol 5
+  - Added "id" field (GUID/UUID type as described on PIP-0016), was missing on V4, added on V5
+- Updated "OP_CHANGE_ACCOUNT_INFO" and "OP_MULTIOPERATION" to allow Account.Data as described on PIP-0024
+  - Added "new_data" field allowing update Account.Data field (0..32 bytes)
+  - Updated digest hash value adding "new_data" field  
 - Hardcoded RandomHash digest/hash values for quick speed safebox check on fresh installation
+- JSON-RPC changes:  
+  - Updated "listaccountforsale" method to allow ATOMIC SWAPS (PIP-0032)
+    - Added "type" to discrimine between kind of listing. Available values are:
+      - "public_sale"
+      - "private_sale": Need to provide a valid "new_enc_pubkey" or "new_b58_pubkey"
+      - "atomic_account_swap": Need to provide a valid "new_enc_pubkey" or "new_b58_pubkey" and the unlocking param "enc_hash_lock"
+      - "atomic_coin_swap": Need to provide a valid "enc_hash_lock"
+      - If no "type" is defined, will automatically select between "public_sale" or "private_sale"
+    - Added "enc_hash_lock" (HEXASTRING) that must be exactly a 32 bytes value (stored as 64 bytes because is HexaString)
+  - Updated "changeaccountinfo" and "signchangeaccountinfo" calls to allow add "new_data" field for change Account.Data value (PIP-0024)
+    - New param "new_data" (HEXASTRING) if provided will change Account Data info. Limited from 0 to 32 bytes.
+  - New method "senddata" as described on PIP-0033 returning an "Operation Object"
+  - New method "signdata" as described on PIP-0033 returning a "Raw Operations Object"
+  - New method "finddataoperations" as described on PIP-0033 returning an ARRAY of "Raw Operations Object"
+    - News params added to original PIP
+      - "depth" (Integer, 1000 by default) : Will search backward in blocks a maximum of "depth" blocks
+      - "startblock" (Integer, optional) : If defined, will start at a specified block, otherwise will start at last "sender" or "target" updated block
+  - Updated "Account Object" return values:
+    - "state": Can return "normal", "listed", "account_swap", "coin_swap"
+    - "hashed_secret" : (HEXASTRING) will contain the SHA256( SECRET ) value that must match Payload received data for Atomic Swaps (only when "state" in "account_swap" or "coin_swap")
+    - "amount_to_swap" : (PASCURRENCY) amount that will be transferred to counterparty account on ATOMIC COIN SWAP ("receiver_swap_account")
+    - "receiver_swap_account": (Integer) Counterpaty account that will receive "amount_to_swap" on ATOMIC COIN SWAP
+    - "data" : (HEXASTRING) will return the Account Data stored with PIP-0024
+    - "seal" : (HEXASTRING) will return the Account Seal stored with PIP-0029
+  - Updated "Operation Object" return values:
+    - "senders" : ARRAY
+      - "data" : OBJECT will store OP_DATA information when operation is OP_DATA type as described on PIP-0016
+        - "id" : (String) String representation of GUID/UUID as "00000000-0000-0000-0000-000000000000" that stores 16 bytes
+        - "sequence" : (Integer)
+        - "type" : (Integer)
+    - "changers" : ARRAY
+      - "new_data" : (HEXASTRING) : If "data" is changed on "account"
+      - "changes" : (String) Description of changes type made
+  - Updated "Multi Operation Object" values:
+    - "changers" : ARRAY
+      - "new_data" : (HEXASTRING) : If "data" is changed on "account"
+
 TODO  
 - TODO: RPC calls for PIP-0029
-- TODO Implement Seal calculation
 - TODO: RPC calls for PIP-0030
 - TODO: RPC calls for PIP-0016
-- TODO: Save data for GUID on OPDATA operation and use GUID for calcs
 
 ### Build 4.0.3.1 - 2019-04-12
 - Fixed core bug #182 in RPC calls

+ 95 - 39
src/core/UAccounts.pas

@@ -25,7 +25,7 @@ interface
 uses
   Classes, SysUtils, UConst, UCrypto, SyncObjs, UThread, UBaseTypes,
   UPCOrderedLists, UPCDataTypes, UPCSafeBoxRootHash,
-  UPCHardcodedRandomHashTable,
+  UPCHardcodedRandomHashTable, UJSONFunctions,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 {$I config.inc}
@@ -44,6 +44,7 @@ Type
     price : UInt64;                // 0 = invalid price
     account_to_pay : Cardinal;     // <> itself
     new_publicKey : TAccountKey;
+    hashed_secret : TRawBytes;     // Hashed Secret for AtomicSwaps
   end;
 
   TOperationBlock = Record
@@ -138,7 +139,7 @@ Type
     Class function IsNullAccountKey(const AAccountInfo : TAccountKey) : Boolean;
     Class function IsValidNewAccountKey(const AAccountInfo : TAccountInfo; const ANewKey : TAccountKey; AProtocolVersion : Integer) : Boolean;
     Class Function IsValidAccountInfo(const AAccountInfo: TAccountInfo; var errors : String): Boolean;
-    Class Function IsValidAccountHashLockKey(const AAccount : TAccount; const AKey : TRawBytes) : Boolean;
+    Class Function IsValidAccountInfoHashLockKey(const AAccountInfo : TAccountInfo; const AKey : TRawBytes) : Boolean;
     Class Function IsValidHashLockKey(const AKey : TRawBytes; out AError : String) : Boolean;
     Class Function CalculateHashLock(const AKey : TRawBytes) : T32Bytes;
     Class Function IsAccountForSale(const AAccountInfo: TAccountInfo) : Boolean;
@@ -148,8 +149,8 @@ Type
     Class function IsAccountForCoinSwap(const AAccountInfo: TAccountInfo) : Boolean;
     Class function IsAccountForAccountSwap(const AAccountInfo: TAccountInfo) : Boolean;
     Class Function IsAccountForSaleOrSwap(const AAccountInfo: TAccountInfo) : Boolean;
-    Class Function IsAccountForSaleOrSwapAcceptingTransactions(const AAccount: TAccount; ACurrentBlock : Integer; const APayload : TRawBytes) : Boolean;
-    Class Function IsOperationRecipientSignable(const ASender, ATarget : TAccount; AIncomingFunds : UInt64; ACurrentBlock : Integer ) : Boolean;
+    Class Function IsAccountForSaleOrSwapAcceptingTransactions(const AAccount: TAccount; ACurrentBlock : Integer; ACurrentProtocol : Word; const APayload : TRawBytes) : Boolean;
+    Class Function IsOperationRecipientSignable(const ASender, ATarget : TAccount; AIncomingFunds : UInt64; ACurrentBlock : Integer; ACurrentProtocol : Word) : Boolean;
     Class Function GetECInfoTxt(Const EC_OpenSSL_NID: Word) : String;
     Class Procedure ValidsEC_OpenSSL_NID(list : TList<Word>);
     Class Function AccountKey2RawString(const account: TAccountKey): TRawBytes; overload;
@@ -500,6 +501,8 @@ Type
     class Function ReadAccountKey(Stream: TStream; var value : TAccountKey): Integer;
     class Function SaveStreamToRaw(Stream: TStream) : TRawBytes;
     class procedure LoadStreamFromRaw(Stream: TStream; const raw : TRawBytes);
+    class Function WriteGUID(AStream : TStream; const AGUID : TGUID) : Integer;
+    class Function ReadGUID(AStream : TStream; var AGUID : TGUID) : Integer;
   End;
 
 
@@ -507,7 +510,7 @@ Type
 Const
   CT_OperationBlock_NUL : TOperationBlock = (block:0;account_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);reward:0;fee:0;protocol_version:0;
     protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:Nil;initial_safe_box_hash:Nil;operations_hash:Nil;proof_of_work:Nil);
-  CT_AccountInfo_NUL : TAccountInfo = (state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));
+  CT_AccountInfo_NUL : TAccountInfo = (state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);hashed_secret:Nil);
   CT_Account_NUL : TAccount = (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_block:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0);
   CT_BlockAccount_NUL : TBlockAccount = (
     blockchainInfo:(block:0;account_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);reward:0;fee:0;protocol_version:0;
@@ -1087,6 +1090,22 @@ begin
 end;
 
 
+class function TStreamOp.ReadGUID(AStream: TStream; var AGUID: TGUID): Integer;
+var i : Integer;
+begin
+  if AStream.Size - AStream.Position < 16 then begin
+    Result := 0; // Not enough space!
+    Exit;
+  end;
+  AStream.Read(AGUID.D1,4);
+  AStream.Read(AGUID.D2,2);
+  AStream.Read(AGUID.D3,2);
+  for i := 0 to 7 do begin
+    AStream.Read(AGUID.D4[i],1);
+  end;
+  Result := 16; // GUID is 16 bytes
+end;
+
 class function TStreamOp.ReadString(Stream: TStream; var value: String): Integer;
 var raw : TRawBytes;
 begin
@@ -1123,6 +1142,18 @@ begin
 end;
 
 
+class function TStreamOp.WriteGUID(AStream: TStream; const AGUID: TGUID): Integer;
+var i : Integer;
+begin
+  AStream.Write(AGUID.D1,4);
+  AStream.Write(AGUID.D2,2);
+  AStream.Write(AGUID.D3,2);
+  for i := 0 to 7 do begin
+    AStream.Write(AGUID.D4[i],1);
+  end;
+  Result := 16; // GUID is 16 bytes
+end;
+
 { TAccountComp }
 Const CT_Base58 : String = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
 
@@ -1158,6 +1189,10 @@ begin
         ms.Write(AccountInfo.price,SizeOf(AccountInfo.price));
         ms.Write(AccountInfo.account_to_pay,SizeOf(AccountInfo.account_to_pay));
         TStreamOp.WriteAccountKey(ms,AccountInfo.new_publicKey);
+        // Adding Hashed_secret if Atomic Swap
+        if AccountInfo.state in [as_ForAtomicAccountSwap,as_ForAtomicCoinSwap] then begin
+          TStreamOp.WriteAnsiString(ms,AccountInfo.hashed_secret);
+        end;
         SetLength(dest,ms.Size);
         ms.Position := 0;
         ms.Read(dest[Low(dest)],ms.Size);
@@ -1424,7 +1459,7 @@ end;
 
 class function TAccountComp.FormatMoney(Money: Int64): String;
 begin
-  Result := FormatFloat('#,###0.0000',(Money/10000));
+  Result := FormatFloat('#,###0.0000',(Money/10000), TPCJSONData.JSONFormatSettings);
 end;
 
 class function TAccountComp.FormatMoneyDecimal(Money : Int64) : Currency;
@@ -1459,9 +1494,9 @@ begin
   end;
 end;
 
-Class Function TAccountComp.IsValidAccountHashLockKey(const AAccount : TAccount; const AKey : TRawBytes) : Boolean;
+Class Function TAccountComp.IsValidAccountInfoHashLockKey(const AAccountInfo : TAccountInfo; const AKey : TRawBytes) : Boolean;
 begin
-  Result := BytesEqual( TBaseType.ToRawBytes( CalculateHashLock( AKey ) ), AAccount.account_data);
+  Result := BytesEqual( TBaseType.ToRawBytes( CalculateHashLock( AKey ) ), AAccountInfo.hashed_secret);
 end;
 
 Class Function TAccountComp.IsValidHashLockKey(const AKey : TRawBytes; out AError : String) : Boolean;
@@ -1485,12 +1520,12 @@ end;
 
 class function TAccountComp.IsAccountForPrivateSale(const AAccountInfo: TAccountInfo): Boolean;
 begin
-  Result := (AAccountInfo.state in [as_ForSale]) AND (NOT IsNullAccountKey(AAccountInfo.accountKey));
+  Result := (AAccountInfo.state in [as_ForSale]) AND (NOT IsNullAccountKey(AAccountInfo.new_publicKey));
 end;
 
 class function TAccountComp.IsAccountForPublicSale(const AAccountInfo: TAccountInfo): Boolean;
 begin
-  Result := (AAccountInfo.state in [as_ForSale]) AND IsNullAccountKey(AAccountInfo.accountKey);
+  Result := (AAccountInfo.state in [as_ForSale]) AND IsNullAccountKey(AAccountInfo.new_publicKey);
 end;
 
 class function TAccountComp.IsAccountForSwap(const AAccountInfo: TAccountInfo): Boolean;
@@ -1513,27 +1548,33 @@ begin
   Result := IsAccountForSale(AAccountInfo) OR IsAccountForSwap(AAccountInfo);
 end;
 
-class function TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(const AAccount: TAccount; ACurrentBlock : Integer; const APayload : TRawBytes): Boolean;
+class function TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(const AAccount: TAccount; ACurrentBlock : Integer; ACurrentProtocol : Word; const APayload : TRawBytes): Boolean;
 var errors : String;
 begin
   Result := False;
   if Not IsAccountForSaleOrSwap(AAccount.accountInfo) then
     exit;
 
+  if (ACurrentProtocol<CT_PROTOCOL_5) then begin
+    // V4 and below only allows Private sales (No Swaps)
+    if Not (IsAccountForPrivateSale(AAccount.accountInfo)) then Exit;
+  end;
+
   if (AAccount.accountInfo.state in [as_ForSale, as_ForAtomicAccountSwap]) then
     if NOT IsValidAccountKey(AAccount.accountInfo.new_publicKey,errors) then
       exit;
 
    if (AAccount.accountInfo.state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) then
-     if NOT IsValidAccountHashLockKey(AAccount, APayload) then
+     if NOT IsValidAccountInfoHashLockKey(AAccount.accountInfo, APayload) then
        exit;
   Result := True;
 end;
 
-Class Function TAccountComp.IsOperationRecipientSignable(const ASender, ATarget : TAccount; AIncomingFunds : UInt64; ACurrentBlock : Integer ) : Boolean;
+Class Function TAccountComp.IsOperationRecipientSignable(const ASender, ATarget : TAccount; AIncomingFunds : UInt64; ACurrentBlock : Integer; ACurrentProtocol : Word) : Boolean;
 begin
   // V5 - Allow recipient-signed operations under following conditions:
   //  - Sender Account = Target Account
+  //  - Target Account is listed for SWAP (Atomic Swap)
   //  - Target Account is time-locked to new-owner-key R and time-lock is active
   //  - (Target.Balance + Operation.Quantity) >= Target.SalePrice
   //  - Signed by new-owner-key R
@@ -1541,8 +1582,10 @@ begin
   //  This allows following use-cases:
   //  - Private account sale where buyer does not have existing account to initiate transaction
   //  - Atomic account swap where counterparty does not have existing account to initiate transaction
-  Result := (ASender.account = ATarget.account) AND
+  Result := (ACurrentProtocol >= CT_PROTOCOL_5) AND
+            (ASender.account = ATarget.account) AND
             TAccountComp.IsAccountLocked(ATarget.accountInfo, ACurrentBlock) AND
+            TAccountComp.IsAccountForSwap(ATarget.accountInfo) AND
            ((ATarget.balance + AIncomingFunds) >= (ATarget.accountInfo.price));
 
  // Note: this does not validate recipient signature, only determines if
@@ -1702,6 +1745,7 @@ begin
         dest.price:=CT_AccountInfo_NUL.price;
         dest.account_to_pay:=CT_AccountInfo_NUL.account_to_pay;
         dest.new_publicKey:=CT_AccountInfo_NUL.new_publicKey;
+        dest.hashed_secret:=CT_AccountInfo_NUL.hashed_secret;
       End;
       CT_AccountInfo_ForSale, CT_AccountInfo_ForAccountSwap, CT_AccountInfo_ForCoinSwap : Begin
         TStreamOp.ReadAccountKey(ms,dest.accountKey);
@@ -1714,6 +1758,11 @@ begin
           CT_AccountInfo_ForAccountSwap: dest.state := as_ForAtomicAccountSwap;
           CT_AccountInfo_ForCoinSwap: dest.state := as_ForAtomicCoinSwap;
         end;
+        if dest.state in [as_ForAtomicAccountSwap,as_ForAtomicCoinSwap] then begin
+          TStreamOp.ReadAnsiString(ms,dest.hashed_secret);
+        end else begin
+          dest.hashed_secret:=CT_AccountInfo_NUL.hashed_secret;
+        end;
       End;
     else
       raise Exception.Create('DEVELOP ERROR 20170214-2');
@@ -4183,11 +4232,40 @@ begin
       TAccountComp.FormatMoney(LPAccountToBuy^.balance)+' + amount '+TAccountComp.FormatMoney(AAmount);
     Exit;
   end;
-  if TAccountComp.IsAccountForSwap(LPAccountToBuy^.accountInfo) AND (NOT TAccountComp.IsValidAccountHashLockKey(LPAccountToBuy^, AHashLockKey)) then begin
+  if TAccountComp.IsAccountForSwap(LPAccountToBuy^.accountInfo) AND (NOT TAccountComp.IsValidAccountInfoHashLockKey(LPAccountToBuy^.accountInfo, AHashLockKey)) then begin
     AErrors := 'Account is not unlocked by supplied hash lock key';
     Exit;
   end;
 
+
+  // Overflow checks:
+  if AAmount > (AAmount + AFee) then begin
+    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: 37E7343143614D4C8489FA9963CE8C3C';
+    exit;
+  end;
+  if LPBuyerAccount^.balance < (AAmount + AFee) then begin
+    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: F06169D9A209410AACB1AAD324B7A191';
+    exit;
+  end;
+
+  if LPAccountToBuy^.balance > (LPAccountToBuy^.balance + AAmount) then begin
+    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: 390BB1E7241B4A0BA5A2D934E67F39D3';
+    exit;
+  end;
+  if (LPAccountToBuy^.balance + AAmount) < (LPAccountToBuy^.accountInfo.price) then begin
+    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: 19D80241DA584D0B93ED30F27110B9A9';
+    exit;
+  end;
+
+  if LPSellerAccount^.balance > (LPSellerAccount^.balance + LPAccountToBuy^.accountInfo.price) then begin
+    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: 972CA85C6E4E4081ABB767F6D8019421';
+    exit;
+  end;
+
+
+  // NOTE:
+  // At this point, we have checked integrity, cannot check later!
+
   UpdateSeal(LPBuyerAccount_Sealed,AOpID);
   UpdateSeal(LPAccountToBuy_Sealed,AOpID);
   UpdateSeal(LPSellerAccount_Sealed,AOpID);
@@ -4213,32 +4291,10 @@ begin
 
   // Inc buyer n_operation
   LPBuyerAccount^.n_operation := ANOperation;
-  // Set new balance values (with overflow checks)
-  if AAmount > (AAmount + AFee) then begin
-    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: 37E7343143614D4C8489FA9963CE8C3C';
-    exit;
-  end;
-  if LPBuyerAccount^.balance < (AAmount + AFee) then begin
-    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: F06169D9A209410AACB1AAD324B7A191';
-    exit;
-  end;
+  // Set new balance values
   LPBuyerAccount^.balance := LPBuyerAccount^.balance - (AAmount + AFee);
-
-  if LPAccountToBuy^.balance > (LPAccountToBuy^.balance + AAmount) then begin
-    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: 390BB1E7241B4A0BA5A2D934E67F39D3';
-    exit;
-  end;
-  if (LPAccountToBuy^.balance + AAmount) < (LPAccountToBuy^.accountInfo.price) then begin
-    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: 19D80241DA584D0B93ED30F27110B9A9';
-    exit;
-  end;
   LPAccountToBuy^.balance := LPAccountToBuy^.balance + AAmount - LPAccountToBuy^.accountInfo.price;
-
-  if LPSellerAccount^.balance > (LPSellerAccount^.balance + LPAccountToBuy^.accountInfo.price) then begin
-    AErrors := 'Critical overflow error detected, aborting SafeBox update. Ref: 972CA85C6E4E4081ABB767F6D8019421';
-    exit;
-  end;
-    LPSellerAccount^.balance := LPSellerAccount^.balance + LPAccountToBuy^.accountInfo.price;
+  LPSellerAccount^.balance := LPSellerAccount^.balance + LPAccountToBuy^.accountInfo.price;
 
   // After buy, account will be unlocked and set to normal state and new account public key changed
   LPAccountToBuy^.accountInfo := CT_AccountInfo_NUL;

+ 57 - 4
src/core/UBlockChain.pas

@@ -126,7 +126,7 @@ Type
     Account : Cardinal;
     Amount : Int64;
     N_Operation : Cardinal;
-    Data : TMultiOpData;
+    OpData : TMultiOpData; // Filled only when Operation is TOpData type
     Payload : TRawBytes;
     Signature : TECDSA_SIG;
   end;
@@ -134,7 +134,6 @@ Type
   TMultiOpReceiver = Record
     Account : Cardinal;
     Amount : Int64;
-    Data : TMultiOpData;
     Payload : TRawBytes;
   end;
   TMultiOpReceivers = Array of TMultiOpReceiver;
@@ -149,7 +148,7 @@ Type
     Seller_Account : Int64;
     Account_Price : Int64;
     Locked_Until_Block : Cardinal;
-    Data : TMultiOpData;
+    Hashed_secret : TRawBytes;
     Fee: Int64;
     Signature: TECDSA_SIG;
   end;
@@ -560,7 +559,9 @@ Const
   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: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:Nil;Signature:(r:Nil;s:Nil));
   CT_TMultiOpReceiver_NUL : TMultiOpReceiver = (Account:0;Amount:0;Payload: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;Seller_Account:-1;Account_Price:-1;Locked_Until_Block:0;Fee:0;Signature:(r:Nil;s: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;
+    Hashed_secret:Nil;
+    Fee:0;Signature:(r:Nil;s:Nil));
   CT_TOpChangeAccountInfoType_Txt : Array[Low(TOpChangeAccountInfoType)..High(TOpChangeAccountInfoType)] of String = ('public_key','account_name','account_type','list_for_public_sale','list_for_private_sale', 'delist', 'account_data','list_for_account_swap','list_for_coin_swap');
 
 implementation
@@ -3190,6 +3191,7 @@ end;
 
 class function TPCOperation.OperationToOperationResume(Block : Cardinal; Operation: TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume): Boolean;
 Var s : String;
+  LOpToText : String;
 begin
   OperationResume := CT_TOperationResume_NUL;
   OperationResume.Block:=Block;
@@ -3229,6 +3231,53 @@ begin
           OperationResume.Fee := 0;
           Result := true;
         end else exit;
+      end else if (TOpTransaction(Operation).Data.opTransactionStyle = transaction_with_auto_atomic_swap) then begin
+        if TOpTransaction(Operation).Data.new_accountkey.EC_OpenSSL_NID=0 then begin
+          // COIN SWAP
+          LOpToText := Format('COIN SWAP %s PASC from %s to %s',[
+            TAccountComp.FormatMoney(TOpTransaction(Operation).Data.AccountPrice),
+            TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+            TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.SellerAccount)]);
+        end else begin
+          // ACCOUNT SWAP
+          LOpToText := Format('ACCOUNT SWAP %s to new PublicKey %s',[
+            TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+            TAccountComp.AccountPublicKeyExport(TOpTransaction(Operation).Data.new_accountkey)]);
+        end;
+        if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
+          // The sender of the transaction
+          OperationResume.OpSubtype := CT_OpSubtype_SwapTransactionSender;
+          OperationResume.OperationTxt := Format('Tx-Out %s PASC from %s to %s with %s',
+             [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+             LOpToText]);
+          If (TOpTransaction(Operation).Data.sender=TOpTransaction(Operation).Data.SellerAccount) then begin
+            // Valid calc when sender is the same than seller
+            OperationResume.Amount := (Int64(TOpTransaction(Operation).Data.amount) - (TOpTransaction(Operation).Data.AccountPrice)) * (-1);
+          end else OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) * (-1);
+          Result := true;
+        end else if TOpTransaction(Operation).Data.target=Affected_account_number then begin
+          OperationResume.OpSubtype := CT_OpSubtype_SwapTransactionTarget;
+          OperationResume.OperationTxt := Format('Tx-In %s PASC from %s to %s with %s',
+             [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.amount),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+             LOpToText]);
+          OperationResume.Amount := Int64(TOpTransaction(Operation).Data.amount) - Int64(TOpTransaction(Operation).Data.AccountPrice);
+          OperationResume.Fee := 0;
+          Result := true;
+        end else if TOpTransaction(Operation).Data.SellerAccount=Affected_account_number then begin
+          OperationResume.OpSubtype := CT_OpSubtype_BuyTransactionSeller;
+          OperationResume.OperationTxt := Format('Tx-In seller receiving %s PASC from Tx between %s to %s with %s',
+             [TAccountComp.FormatMoney(TOpTransaction(Operation).Data.AccountPrice),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.sender),
+             TAccountComp.AccountNumberToAccountTxtNumber(TOpTransaction(Operation).Data.target),
+             LOpToText]);
+          OperationResume.Amount := TOpTransaction(Operation).Data.AccountPrice;
+          OperationResume.Fee := 0;
+          Result := true;
+        end else exit;
       end else begin
         if TOpTransaction(Operation).Data.sender=Affected_account_number then begin
           OperationResume.OpSubtype := CT_OpSubtype_TransactionSender;
@@ -3339,6 +3388,10 @@ begin
         if s<>'' then s:=s+',';
         s := s + 'type';
       end;
+      if (account_data in TOpChangeAccountInfo(Operation).Data.changes_type) then begin
+        if s<>'' then s:=s+',';
+        s := s + 'data';
+      end;
       OperationResume.OperationTxt:= 'Changed '+s+' of account '+TAccountComp.AccountNumberToAccountTxtNumber(Operation.DestinationAccount);
       OperationResume.OpSubtype:=CT_OpSubtype_ChangeAccountInfo;
       Result := True;

+ 3 - 0
src/core/UConst.pas

@@ -167,6 +167,9 @@ Const
   CT_OpSubtype_BuyTransactionBuyer        = 13;
   CT_OpSubtype_BuyTransactionTarget       = 14;
   CT_OpSubtype_BuyTransactionSeller       = 15;
+  CT_OpSubtype_SwapTransactionSender      = 16;
+  CT_OpSubtype_SwapTransactionTarget      = 17;
+  CT_OpSubtype_SwapTransactionReceiver    = 18;
   CT_OpSubtype_ChangeKey                  = 21;
   CT_OpSubtype_Recover                    = 31;
   CT_OpSubtype_ListAccountForPublicSale   = 41;

+ 5 - 2
src/core/UFileStorage.pas

@@ -316,15 +316,18 @@ procedure TFileStorage.DoLoadPendingBufferOperations(OperationsHashTree : TOpera
 Var fs : TFileStream;
   errors : String;
   n : Integer;
+  LCurrentProtocol : Word;
 begin
   LockBlockChainStream;
   Try
     fs := GetPendingBufferOperationsStream;
     fs.Position:=0;
     if fs.Size>0 then begin
-      If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,CT_PROTOCOL_3,Nil,errors) then begin
+      if Assigned(Bank) then LCurrentProtocol := Bank.SafeBox.CurrentProtocol
+      else LCurrentProtocol := CT_BUILD_PROTOCOL;
+      If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,LCurrentProtocol,Nil,errors) then begin
         TLog.NewLog(ltInfo,ClassName,Format('DoLoadPendingBufferOperations loaded operations:%d',[OperationsHashTree.OperationsCount]));
-      end else TLog.NewLog(ltError,ClassName,Format('DoLoadPendingBufferOperations ERROR: loaded operations:%d errors:%s',[OperationsHashTree.OperationsCount,errors]));
+      end else TLog.NewLog(ltError,ClassName,Format('DoLoadPendingBufferOperations ERROR (Protocol %d): loaded operations:%d errors:%s',[LCurrentProtocol,OperationsHashTree.OperationsCount,errors]));
     end;
   finally
     UnlockBlockChainStream;

+ 216 - 100
src/core/UOpTransaction.pas

@@ -31,7 +31,12 @@ Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
 
 Type
   // Operations Type
-  TOpTransactionStyle = (transaction, transaction_with_auto_buy_account, buy_account, atomic_swap, transaction_with_auto_atomic_swap);
+  TOpTransactionStyle = (transaction, transaction_with_auto_buy_account, buy_account, transaction_with_auto_atomic_swap);
+    // transaction = Sinlge standard transaction
+    // transaction_with_auto_buy_account = Single transaction made over an account listed for private sale. For STORING purposes only
+    // buy_account = A Buy account operation
+    // transaction_with_auto_atomic_swap = Single transaction made over an account listed for atomic swap (coin swap or account swap)
+
   TOpTransactionData = Record
     sender: Cardinal;
     n_operation : Cardinal;
@@ -47,7 +52,6 @@ Type
     AccountPrice : UInt64;
     SellerAccount : Cardinal;
     new_accountkey : TAccountKey;
-
   End;
 
   TOpChangeKeyData = Record
@@ -201,18 +205,20 @@ Type
     fee: UInt64;
     payload: TRawBytes;
     public_key: TECDSA_Public;
-    changes_type : TOpChangeAccountInfoTypes; // bits mask. $0001 = New account key , $0002 = New name , $0004 = New type
+    changes_type : TOpChangeAccountInfoTypes; // bits mask. $0001 = New account key , $0002 = New name , $0004 = New type , $0008 = New Data
     new_accountkey: TAccountKey;  // If (changes_mask and $0001)=$0001 then change account key
     new_name: TRawBytes;          // If (changes_mask and $0002)=$0002 then change name
     new_type: Word;               // If (changes_mask and $0004)=$0004 then change type
+    new_data: TRawBytes;          // If (changes_mask and $0008)=$0008 then change type
     sign: TECDSA_SIG;
   End;
 
 
 Const
-  CT_TOpListAccountData_NUL : TOpListAccountData = (account_signer:0;account_target:0;operation_type:lat_Unknown;n_operation:0;account_price:0;account_to_pay:0;fee:0;payload:Nil;public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;sign:(r:Nil;s:Nil));
+  CT_TOpListAccountData_NUL : TOpListAccountData = (account_signer:0;account_target:0;operation_type:lat_Unknown;n_operation:0;account_state:as_Unknown;account_price:0;account_to_pay:0;fee:0;
+    hash_lock:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);payload:Nil;public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;sign:(r:Nil;s:Nil));
   CT_TOpChangeAccountInfoData_NUL : TOpChangeAccountInfoData = (account_signer:0;account_target:0;n_operation:0;fee:0;payload:Nil;public_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);changes_type:[];
-    new_accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_name:Nil;new_type:0;sign:(r:Nil;s:Nil));
+    new_accountkey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);new_name:Nil;new_type:0;new_data:Nil;sign:(r:Nil;s:Nil));
 
 Type
 
@@ -249,7 +255,7 @@ Type
     function GetOpSubType : Integer;
   public
     class function OpType : Byte; override;
-    Constructor CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; AListOpSubType : Integer; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal;  ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey; const AHashLock : T32Bytes; const APayload: TRawBytes);
+    Constructor CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; ANewAccountState : TAccountState; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal;  ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey; const AHashLock : T32Bytes; const APayload: TRawBytes);
     property OpSubType : Integer read GetOpSubType;
   End;
 
@@ -297,6 +303,7 @@ Type
       change_key : Boolean; const new_account_key : TAccountKey;
       change_name: Boolean; const new_name : TRawBytes;
       change_type: Boolean; const new_type : Word;
+      change_data: Boolean; const new_data : TRawBytes;
       fee: UInt64; payload: TRawBytes);
     Property Data : TOpChangeAccountInfoData read FData;
     Function toString : String; Override;
@@ -311,6 +318,7 @@ Type
     account_sender,              // The account sender. Public key must be EQUAL to account_signer public key
     account_target: Cardinal;    // The destination account. Will recive DATA and amount (if any)
     n_operation : Cardinal;      // Signer n_operation
+    guid: TGUID;                 // GUID value, added on Protocol V5
     dataType : Word;             // 2 byte data type
     dataSequence : Word;         // 2 byte data sequence
     amount: UInt64;              // Allow amount=0
@@ -342,7 +350,7 @@ Type
     function N_Operation : Cardinal; override;
     procedure AffectedAccounts(list : TList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
-    Constructor CreateOpData( ACurrentProtocol : word; account_signer, account_sender, account_target : Cardinal; signer_key:TECPrivateKey; n_operation : Cardinal; dataType, dataSequence : Word; amount, fee : UInt64; payload: TRawBytes);
+    Constructor CreateOpData( ACurrentProtocol : word; account_signer, account_sender, account_target : Cardinal; signer_key:TECPrivateKey; n_operation : Cardinal; dataType, dataSequence : Word; AGUID : TGUID; amount, fee : UInt64; payload: TRawBytes);
     Property Data : TOpDataData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
@@ -350,7 +358,7 @@ Type
   End;
 
 Const
-  CT_TOpDataData_NUL : TOpDataData = (account_signer:0;account_sender:0;account_target:0;n_operation:0;dataType:0;dataSequence:0;amount:0;fee:0;payload:Nil;sign:(r:Nil;s:Nil));
+  CT_TOpDataData_NUL : TOpDataData = (account_signer:0;account_sender:0;account_target:0;n_operation:0;guid:(D1:0;D2:0;D3:0;D4:(0,0,0,0,0,0,0,0));dataType:0;dataSequence:0;amount:0;fee:0;payload:Nil;sign:(r:Nil;s:Nil));
 
 Procedure RegisterOperationsClass;
 
@@ -401,10 +409,14 @@ begin
   if (public_key in FData.changes_type) then b:=b OR $01;
   if (account_name in FData.changes_type) then b:=b OR $02;
   if (account_type in FData.changes_type) then b:=b OR $04;
+  if (account_data in FData.changes_type) then b:=b OR $08;
   Stream.Write(b,Sizeof(b));
   TStreamOp.WriteAccountKey(Stream,FData.new_accountkey);
   TStreamOp.WriteAnsiString(Stream,FData.new_name);
   Stream.Write(FData.new_type,Sizeof(FData.new_type));
+  if FProtocolVersion>=CT_PROTOCOL_5 then begin
+    TStreamOp.WriteAnsiString(Stream,FData.new_data);
+  end;
   TStreamOp.WriteAnsiString(Stream,FData.sign.r);
   TStreamOp.WriteAnsiString(Stream,FData.sign.s);
   Result := true;
@@ -426,11 +438,15 @@ begin
   if (b AND $01)=$01 then FData.changes_type:=FData.changes_type + [public_key];
   if (b AND $02)=$02 then FData.changes_type:=FData.changes_type + [account_name];
   if (b AND $04)=$04 then FData.changes_type:=FData.changes_type + [account_type];
+  if (b AND $08)=$08 then FData.changes_type:=FData.changes_type + [account_data];
   // Check
-  if (b AND $F8)<>0 then Exit;
+  if (b AND $F0)<>0 then Exit;
   if TStreamOp.ReadAccountKey(Stream,FData.new_accountkey)<0 then Exit;
   if TStreamOp.ReadAnsiString(Stream,FData.new_name)<0 then Exit;
   Stream.Read(FData.new_type,Sizeof(FData.new_type));
+  if FProtocolVersion>=CT_PROTOCOL_5 then begin
+    if TStreamOp.ReadAnsiString(Stream,FData.new_data)<0 then Exit;
+  end else FData.new_data := Nil;
   if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then Exit;
   if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then Exit;
   Result := true;
@@ -446,6 +462,7 @@ begin
   OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
   OperationResume.Changers[0].New_Name := FData.new_name;
   OperationResume.Changers[0].New_Type := FData.new_type;
+  OperationResume.Changers[0].New_Data := FData.new_data;
   If (FData.account_signer=FData.account_target) then begin
     OperationResume.Changers[0].N_Operation := FData.n_operation;
     OperationResume.Changers[0].Signature := FData.sign;
@@ -503,7 +520,7 @@ begin
     Exit;
   end;
   if (account_signer.balance<FData.fee) then begin
-    errors := 'Insuficient founds';
+    errors := 'Insuficient funds';
     exit;
   end;
   if (length(FData.payload)>CT_MaxPayloadSize) then begin
@@ -536,6 +553,18 @@ begin
       Exit;
     end;
   end;
+  if (account_data in FData.changes_type) then begin
+    // TAccount.Data is a 0..32 bytes length
+    if (Length(FData.new_data)>CT_MaxAccountDataSize) then begin
+      errors := 'New data length ('+IntToStr(Length(FData.new_data))+') > '+IntToStr(CT_MaxAccountDataSize);
+      Exit;
+    end;
+  end else begin
+    if Length(FData.new_data)<>0 then begin
+      errors := 'New data must be null when no data change';
+      Exit;
+    end;
+  end;
   If (FData.changes_type=[]) then begin
     errors := 'No change';
     Exit;
@@ -576,6 +605,9 @@ begin
   If (account_type in FData.changes_type) then begin
     account_target.account_type := FData.new_type;
   end;
+  If (account_data in FData.changes_type) then begin
+    account_target.account_data := FData.new_data;
+  end;
   Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
          GetOpID,
          FData.account_signer,FData.n_operation,FData.account_target,
@@ -633,6 +665,7 @@ constructor TOpChangeAccountInfo.CreateChangeAccountInfo(ACurrentProtocol : word
   account_target: Cardinal; key: TECPrivateKey; change_key: Boolean;
   const new_account_key: TAccountKey; change_name: Boolean;
   const new_name: TRawBytes; change_type: Boolean; const new_type: Word;
+  change_data: Boolean; const new_data : TRawBytes;
   fee: UInt64; payload: TRawBytes);
 begin
   inherited Create(ACurrentProtocol);
@@ -656,6 +689,10 @@ begin
     FData.changes_type:=FData.changes_type + [account_type];
     FData.new_type:=new_type;
   end;
+  If change_data then begin
+    FData.changes_type:=FData.changes_type + [account_data];
+    FData.new_data:=new_data;
+  end;
 
   if Assigned(key) then begin
     FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(ACurrentProtocol));
@@ -680,6 +717,10 @@ begin
     if s<>'' then s:=s+', ';
     s := s + 'new type to '+IntToStr(FData.new_type);
   end;
+  If (account_data IN FData.changes_type)  then begin
+    if s<>'' then s:=s+', ';
+    s := s + 'new data to '+FData.new_data.ToHexaString;
+  end;
   Result := Format('Change account %s info: %s fee:%s (n_op:%d) payload size:%d',[
      TAccountComp.AccountNumberToAccountTxtNumber(FData.account_target),
      s,
@@ -702,10 +743,14 @@ begin
     if (public_key in FData.changes_type) then b:=b OR $01;
     if (account_name in FData.changes_type) then b:=b OR $02;
     if (account_type in FData.changes_type) then b:=b OR $04;
+    if (account_data in FData.changes_type) then b:=b OR $08;
     Stream.Write(b,Sizeof(b));
     TStreamOp.WriteAccountKey(Stream,FData.new_accountkey);
     TStreamOp.WriteAnsiString(Stream,FData.new_name);
     Stream.Write(FData.new_type,Sizeof(FData.new_type));
+    if (current_protocol>=CT_PROTOCOL_5) then begin
+      TStreamOp.WriteAnsiString(Stream,FData.new_data);
+    end;
     if (current_protocol<=CT_PROTOCOL_3) then begin
       Stream.Position := 0;
       setlength(Result,Stream.Size);
@@ -726,7 +771,7 @@ procedure TOpTransaction.AffectedAccounts(list: TList<Cardinal>);
 begin
   list.Add(FData.sender);
   list.Add(FData.target);
-  if (FData.opTransactionStyle in [transaction_with_auto_buy_account, buy_account, atomic_swap, transaction_with_auto_atomic_swap]) then begin
+  if (FData.opTransactionStyle in [transaction_with_auto_buy_account, buy_account, transaction_with_auto_atomic_swap]) then begin
     list.Add(FData.SellerAccount);
   end;
 end;
@@ -758,8 +803,9 @@ function TOpTransaction.DoOperation(APrevious : TAccountPreviousBlockInfo; ASafe
 Var s_new, t_new : Int64;
   LTotalAmount : Cardinal;
   LSender,LTarget,LSeller : TAccount;
-  LRecipientSignable, LIsSwap : Boolean;
+  LRecipientSignable, LIsCoinSwap : Boolean;
   LCurrentBlock, LCurrentProtocol : Integer;
+  LBuyAccountNewPubkey : TAccountKey;
 begin
   Result := false;
   AErrors := '';
@@ -772,6 +818,10 @@ begin
     AErrors := Format('Invalid sender %d',[FData.sender]);
     Exit;
   end;
+  if (FData.target>=ASafeBoxTransaction.FreezedSafeBox.AccountsCount) then begin
+    AErrors := Format('Invalid target %d',[FData.target]);
+    Exit;
+  end;
   if TAccountComp.IsAccountBlockedByProtocol(FData.sender,LCurrentBlock) then begin
     AErrors := Format('sender (%d) is blocked for protocol',[FData.sender]);
     Exit;
@@ -786,7 +836,7 @@ begin
   end;
   if (length(FData.payload)>CT_MaxPayloadSize) then begin
     AErrors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
-    If (ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) then begin
+    If (LCurrentProtocol>=CT_PROTOCOL_2) then begin
       Exit; // BUG from protocol 1
     end;
   end;
@@ -794,21 +844,16 @@ begin
   LSender := ASafeBoxTransaction.Account(FData.sender);
   LTarget := ASafeBoxTransaction.Account(FData.target);
 
-  if (FData.target>=ASafeBoxTransaction.FreezedSafeBox.AccountsCount) then begin
-    AErrors := Format('Invalid target %d',[FData.target]);
-    Exit;
-  end;
-
   // V5 - Allow recipient-signed transactions. This is defined as
   //  - Sender Account = Target Account
-  LRecipientSignable := TAccountComp.IsOperationRecipientSignable(LSender, LTarget, FData.Amount, LCurrentBlock);
-  LIsSwap := TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo);
+  LRecipientSignable := TAccountComp.IsOperationRecipientSignable(LSender, LTarget, FData.Amount, LCurrentBlock, LCurrentProtocol);
+  LIsCoinSwap := TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo);
 
   if (FData.sender=FData.target) AND (NOT LRecipientSignable) then begin
     AErrors := Format('Sender=Target and Target is not recipient-signable. Account: %d',[FData.sender]);
     Exit;
   end;
-  if (LRecipientSignable Or LIsSwap) then begin
+  if (LRecipientSignable Or LIsCoinSwap) then begin
     IF ((FData.amount < 0) or (FData.amount>CT_MaxTransactionAmount)) then begin
       AErrors := Format('Recipient-signed transaction had invalid amount %d. Must be within 0 or %d.)',[FData.amount,CT_MaxTransactionAmount]);
       Exit;
@@ -860,11 +905,16 @@ begin
   // Is buy account ?
   if (FData.opTransactionStyle = buy_Account ) then begin
     {$region 'Buy Account Validation'}
-    if (ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
+    if (LCurrentProtocol<CT_PROTOCOL_2) then begin
       AErrors := 'Buy account is not allowed on Protocol 1';
       exit;
     end;
 
+    if (TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo)) then begin
+      AErrors := 'Atomic coin swap cannot be made purchasing, use standard tx instead';
+      Exit;
+    end;
+
     if (TAccountComp.IsAccountForSwap(LTarget.accountInfo) AND (LCurrentProtocol<CT_PROTOCOL_5)) then begin
       AErrors := 'Atomic swaps are not allowed until Protocol 5';
       exit;
@@ -901,14 +951,16 @@ begin
     end;
 
     If Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,AErrors)) then exit; // BUG 20171511
+    LBuyAccountNewPubkey := FData.new_accountkey;
     {$endregion}
     FPrevious_Seller_updated_block := LSeller.updated_block;
   end else if // (is auto buy) OR (is transaction that can buy)
               (FData.opTransactionStyle = transaction_with_auto_buy_account) OR
+              (FData.opTransactionStyle = transaction_with_auto_atomic_swap) OR
               (
                 (FData.opTransactionStyle = transaction) AND
                 (LCurrentProtocol >= CT_PROTOCOL_2) AND
-                (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, FData.payload)) AND
+                (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, LCurrentProtocol, FData.payload)) AND
                 ((LTarget.balance + FData.amount >= LTarget.accountInfo.price))
               )  then begin
     {$region 'Transaction Auto Buy Validation'}
@@ -917,48 +969,68 @@ begin
       exit;
     end;
 
-    if (TAccountComp.IsAccountForSwap( LTarget.accountInfo ) AND (ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_5)) then begin
-      AErrors := 'Tx-Buy atomic swaps are not allowed until Protocol 5';
-      exit;
-    end else If (LCurrentProtocol<CT_PROTOCOL_5) then begin
-      // the below line was a bug fix that introduced a new bug, and is retained here for
-      // V2-V4 consistency
-      //------
-      if Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,AErrors)) then exit; // BUG 20171511
-      //------
-    end else if Not (TAccountComp.IsValidAccountKey(LTarget.accountInfo.new_publicKey,AErrors)) then exit;
+    If (LCurrentProtocol<CT_PROTOCOL_5) then begin
+      if (TAccountComp.IsAccountForSwap( LTarget.accountInfo )) then begin
+        AErrors := 'Tx-Buy atomic swaps are not allowed until Protocol 5';
+        exit;
+      end else begin
+        // the below line was a bug fix that introduced a new bug, and is retained here for
+        // V2-V4 consistency
+        //------
+        if Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,AErrors)) then exit; // BUG 20171511
+        //------
+      end;
+    end;
+
+    // Check that stored "new_publicKey" is valid (when not in coin swap)
+    if (Not TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo)) and
+       (Not (TAccountComp.IsValidAccountKey(LTarget.accountInfo.new_publicKey,AErrors))) then exit;
+
+    // NOTE: This is a Transaction opereation (not a buy account operation) that
+    // has some "added" effects (private sale, swap...)
+    // in order to Store at the blockchain file we will fill this fields not specified
+    // on a transaction (otherwise JSON-RPC calls will not be abble to know what
+    // happened in this transaction as extra effect):
+    //
+    //  FData.opTransactionStyle: TOpTransactionStyle;
+    //  FData.AccountPrice : UInt64;
+    //  FData.SellerAccount : Cardinal;
+    //  FData.new_accountkey : TAccountKey;
 
     // Fill the purchase data
-    FData.opTransactionStyle := transaction_with_auto_buy_account; // Set this data!
+    if TAccountComp.IsAccountForSale( LTarget.accountInfo ) then begin
+      FData.opTransactionStyle := transaction_with_auto_buy_account; // Set this data!
+    end else begin
+      FData.opTransactionStyle := transaction_with_auto_atomic_swap; // Set this data!
+    end;
     FData.AccountPrice := LTarget.accountInfo.price;
     FData.SellerAccount := LTarget.accountInfo.account_to_pay;
     LSeller := ASafeBoxTransaction.Account(LTarget.accountInfo.account_to_pay);
     FPrevious_Seller_updated_block := LSeller.updated_block;
-    FData.new_accountkey := LTarget.accountInfo.new_publicKey;
+    if TAccountComp.IsAccountForCoinSwap( LTarget.accountInfo ) then begin
+      // We will save extra info that account key has not changed
+      FData.new_accountkey := CT_TECDSA_Public_Nul;
+      // COIN SWAP: Ensure public key will not change and will be the same
+      LBuyAccountNewPubkey := LTarget.accountInfo.accountKey;
+    end else begin
+      FData.new_accountkey := LTarget.accountInfo.new_publicKey;
+      LBuyAccountNewPubkey := LTarget.accountInfo.new_publicKey;
+    end;
     {$endregion}
   end else if (FData.opTransactionStyle <> transaction) then begin
      AErrors := 'INTERNAL ERROR: 477C2A3C53C34E63A6B82C057741C44D';
      exit;
   end;
 
-  // final atomic coin swap checks for buy account flow
-  if (FData.opTransactionStyle in [buy_account, transaction_with_auto_buy_account]) AND (LTarget.accountInfo.state = as_ForAtomicCoinSwap) then begin
-    // Ensure the key for target doesn't change
-    if NOT TAccountComp.EqualAccountKeys(LTarget.accountInfo.accountKey, FData.new_accountkey) then begin
-      AErrors := 'Target key cannot be changed during atomic coin swap';
-      exit;
-    end;
-  end;
-
-  if (FData.opTransactionStyle in [buy_account, transaction_with_auto_buy_account]) then begin
+  if (FData.opTransactionStyle in [buy_account, transaction_with_auto_buy_account, transaction_with_auto_atomic_swap]) then begin
     // account purchase
-    if (ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
+    if (LCurrentProtocol<CT_PROTOCOL_2) then begin
       AErrors := 'NOT ALLOWED ON PROTOCOL 1';
       exit;
     end;
 
     if (LTarget.accountInfo.state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) AND
-       (ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_5) then begin
+       (LCurrentProtocol<CT_PROTOCOL_5) then begin
       AErrors := 'NOT ALLOWED UNTIL PROTOCOL 5';
       exit;
     end;
@@ -973,7 +1045,7 @@ begin
       FData.amount,
       LTarget.accountInfo.price,
       FData.fee,
-      FData.new_accountkey,
+      LBuyAccountNewPubkey,
       FData.payload,
       LRecipientSignable,
       AErrors
@@ -1063,10 +1135,16 @@ begin
       2 : Begin
         FData.opTransactionStyle := buy_account;
         if (Not (Self is TOpBuyAccount)) then exit;
-      End
+      End;
+      3 : FData.opTransactionStyle := transaction_with_auto_atomic_swap;
     else exit;
     end;
-    if (FData.opTransactionStyle in [transaction_with_auto_buy_account,buy_account]) then begin
+    if ((Self is TOpBuyAccount) and (FData.opTransactionStyle<>buy_account)) or
+       ((Not (Self is TOpBuyAccount)) and (FData.opTransactionStyle=buy_account)) then begin
+      // Protection invalid case added 20190705
+      Exit;
+    end;
+    if (FData.opTransactionStyle in [transaction_with_auto_buy_account,buy_account,transaction_with_auto_atomic_swap]) then begin
       Stream.Read(FData.AccountPrice,SizeOf(FData.AccountPrice));
       Stream.Read(FData.SellerAccount,SizeOf(FData.SellerAccount));
       if Stream.Read(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID))<0 then exit;
@@ -1097,7 +1175,7 @@ begin
       OperationResume.Receivers[0].Amount:=FData.amount;
       OperationResume.Receivers[0].Payload:=FData.payload;
     end;
-    buy_account,transaction_with_auto_buy_account : begin
+    buy_account, transaction_with_auto_buy_account, transaction_with_auto_atomic_swap : begin
       SetLength(OperationResume.Receivers,2);
       OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
       OperationResume.Receivers[0].Account:=FData.target;
@@ -1107,11 +1185,13 @@ begin
       OperationResume.Receivers[1].Account:=FData.SellerAccount;
       OperationResume.Receivers[1].Amount:= FData.AccountPrice;
       OperationResume.Receivers[1].Payload:=FData.payload;
-      SetLength(OperationResume.Changers,1);
-      OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
-      OperationResume.Changers[0].Account := FData.target;
-      OperationResume.Changers[0].Changes_type := [public_key];
-      OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
+      if (Not TAccountComp.IsNullAccountKey(FData.new_accountkey)) then begin
+        SetLength(OperationResume.Changers,1);
+        OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
+        OperationResume.Changers[0].Account := FData.target;
+        OperationResume.Changers[0].Changes_type := [public_key];
+        OperationResume.Changers[0].New_Accountkey := FData.new_accountkey;
+      end;
     end;
   end;
 end;
@@ -1153,10 +1233,11 @@ begin
       transaction : b:=0;
       transaction_with_auto_buy_account : b:=1;
       buy_account : b:=2;
+      transaction_with_auto_atomic_swap : b:=3;
     else raise Exception.Create('ERROR DEV 20170424-1');
     end;
     Stream.Write(b,1);
-    if (FData.opTransactionStyle in [transaction_with_auto_buy_account,buy_account]) then begin
+    if (FData.opTransactionStyle in [transaction_with_auto_buy_account,buy_account,transaction_with_auto_atomic_swap]) then begin
       Stream.Write(FData.AccountPrice,SizeOf(FData.AccountPrice));
       Stream.Write(FData.SellerAccount,SizeOf(FData.SellerAccount));
       Stream.Write(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID));
@@ -1182,7 +1263,7 @@ end;
 function TOpTransaction.SellerAccount: Int64;
 begin
   Case FData.opTransactionStyle of
-    transaction_with_auto_buy_account, buy_account : Result := FData.SellerAccount;
+    transaction_with_auto_buy_account, buy_account, transaction_with_auto_atomic_swap : Result := FData.SellerAccount;
   else Result:=inherited SellerAccount;
   end;
 end;
@@ -1793,6 +1874,7 @@ end;
 function TOpListAccount.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;Var
   account_signer, account_target : TAccount;
   LIsDelist, LIsSale, LIsPrivateSale, LIsPublicSale, LIsSwap, LIsAccountSwap, LIsCoinSwap : boolean;
+  LCurrentProtocol : Integer;
 begin
   Result := false;
   // Determine which flow this function will execute
@@ -1824,11 +1906,12 @@ begin
     LIsCoinSwap := false;
   end;
 
-  if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
+  LCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
+  if (LCurrentProtocol<CT_PROTOCOL_2) then begin
     errors := 'List/Delist Account is not allowed on Protocol 1';
     exit;
   end;
-  if LIsSwap AND (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_5) then begin
+  if LIsSwap AND (LCurrentProtocol<CT_PROTOCOL_5) then begin
     errors := 'Atomic Swaps are not allowed before Protocol 5';
     exit;
   end;
@@ -1848,6 +1931,7 @@ begin
     errors := 'Target account is blocked for protocol';
     Exit;
   end;
+
   if NOT LIsDelist then begin
     if (FData.account_to_pay>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
       errors := 'Invalid account to pay number';
@@ -1866,15 +1950,28 @@ begin
       errors := 'Account for sale price must be greater than 0';
       exit;
     end;
+
+    if (LIsAccountSwap) and (FData.account_price<>0) then begin
+      errors := 'Account for Account Swap must have 0 price';
+      Exit;
+    end;
+
+
     if (FData.locked_until_block > (AccountTransaction.FreezedSafeBox.BlocksCount + CT_MaxFutureBlocksLockedAccount)) then begin
       errors := 'Invalid locked block: Current block '+Inttostr(AccountTransaction.FreezedSafeBox.BlocksCount)+' cannot lock to block '+IntToStr(FData.locked_until_block);
       exit;
     end;
-    if LIsPrivateSale OR LIsAccountSwap OR LIsCoinSwap then begin
+    if LIsPrivateSale OR LIsAccountSwap then begin
       If Not TAccountComp.IsValidAccountKey( FData.new_public_key, errors ) then begin
         errors := 'Invalid new public key: '+errors;
         exit;
       end;
+    end else if (LCurrentProtocol>=CT_PROTOCOL_5) then begin
+      // COIN SWAP or PUBLIC SALE must set FData.new_public_key to NULL
+      if Not TAccountComp.IsNullAccountKey(FData.new_public_key) then begin
+        errors := 'Coin swap/Public sale needs a NULL new public key';
+        Exit;
+      end;
     end;
   end;
   if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
@@ -1936,7 +2033,7 @@ begin
   end;
 
   // Check signature
-  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
+  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,LCurrentProtocol,FData.sign) then begin
     errors := 'Invalid ECDSA signature';
     Exit;
   end;
@@ -1950,14 +2047,19 @@ begin
     account_target.accountInfo.price := CT_AccountInfo_NUL.price;
     account_target.accountInfo.account_to_pay := CT_AccountInfo_NUL.account_to_pay;
     account_target.accountInfo.new_publicKey := CT_AccountInfo_NUL.new_publicKey;
+    account_target.accountInfo.hashed_secret := CT_AccountInfo_NUL.hashed_secret;
   end else begin
     account_target.accountInfo.state := FData.account_state;
     account_target.accountInfo.locked_until_block := FData.locked_until_block;
     account_target.accountInfo.price := FData.account_price;
     account_target.accountInfo.account_to_pay := FData.account_to_pay;
     account_target.accountInfo.new_publicKey := FData.new_public_key;
-    if LIsSwap then
-      account_target.account_data := TBaseType.ToRawBytes( FData.hash_lock );
+    if LIsSwap then begin
+      account_target.accountInfo.hashed_secret := TBaseType.ToRawBytes( FData.hash_lock );
+    end else begin
+      // FData.hash_lock has no utility when no AtomicSwap
+      account_target.accountInfo.hashed_secret := CT_AccountInfo_NUL.hashed_secret;
+    end;
   end;
 
   Result := AccountTransaction.UpdateAccountInfo(
@@ -2041,8 +2143,6 @@ begin
 end;
 
 procedure TOpListAccount.FillOperationResume(Block: Cardinal; getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume);
-var
- LData : TMultiOpData;
 begin
   inherited FillOperationResume(Block, getInfoForAllAccounts, Affected_account_number, OperationResume);
   SetLength(OperationResume.Changers,1);
@@ -2050,13 +2150,13 @@ begin
   OperationResume.Changers[0].Account:=FData.account_target;
   case FData.operation_type of
     lat_ListAccount : begin
-        if (FData.new_public_key.EC_OpenSSL_NID=CT_TECDSA_Public_Nul.EC_OpenSSL_NID) then begin
+        if (FData.account_state = as_ForSale) And (FData.new_public_key.EC_OpenSSL_NID=CT_TECDSA_Public_Nul.EC_OpenSSL_NID) then begin
           OperationResume.Changers[0].Changes_type:=[list_for_public_sale];
         end else begin
           if FData.account_state = as_ForAtomicAccountSwap then
-            OperationResume.Changers[0].Changes_type:=[list_for_account_swap, account_data]
-          else if FData.account_state = as_ForAtomicAccountSwap then
-            OperationResume.Changers[0].Changes_type:=[list_for_coin_swap, account_data]
+            OperationResume.Changers[0].Changes_type:=[list_for_account_swap]
+          else if FData.account_state = as_ForAtomicCoinSwap then
+            OperationResume.Changers[0].Changes_type:=[list_for_coin_swap]
           else
             OperationResume.Changers[0].Changes_type:=[list_for_private_sale];
           OperationResume.Changers[0].New_Accountkey := FData.new_public_key;
@@ -2065,7 +2165,7 @@ begin
         OperationResume.Changers[0].Seller_Account:=FData.account_to_pay;
         OperationResume.Changers[0].Account_Price:=FData.account_price;
         if (FData.account_state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) then begin
-          OperationResume.Changers[0].New_Data := TBaseType.ToRawBytes( FData.hash_lock );
+          OperationResume.Changers[0].Hashed_secret := TBaseType.ToRawBytes( FData.hash_lock );
         end;
     end;
     lat_DelistAccount : begin
@@ -2258,36 +2358,43 @@ end;
 
 { TOpListAccountForSaleOrSwap }
 
-constructor TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; AListOpSubType : Integer; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal;  ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey;  const AHashLock : T32Bytes; const APayload: TRawBytes);
+constructor TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(ACurrentProtocol : Word; ANewAccountState : TAccountState; AAccountSigner, ANOperation, AAccountTarget: Cardinal; AAccountPrice, AFee: UInt64; AAccountToPay: Cardinal;  ANewPublicKey: TAccountKey; ALockedUntilBlock: Cardinal; AKey: TECPrivateKey;  const AHashLock : T32Bytes; const APayload: TRawBytes);
 begin
   inherited Create(ACurrentProtocol);
-  if NOT (AListOpSubType IN [CT_OpSubtype_ListAccountForPublicSale, CT_OpSubtype_ListAccountForPrivateSale, CT_OpSubtype_ListAccountForAccountSwap, CT_OpSubtype_ListAccountForCoinSwap]) then
-    raise EArgumentOutOfRangeException.Create('Invalid list operation sub type');
-
-  case AListOpSubType of
-     CT_OpSubtype_ListAccountForPublicSale: begin
-       if (FData.new_public_key.EC_OpenSSL_NID<>0) then
-         raise EArgumentOutOfRangeException.Create('Public sale must be to a null key');
-       FData.account_state := as_ForSale;
-     end;
-     CT_OpSubtype_ListAccountForPrivateSale: FData.account_state := as_ForSale;
-     CT_OpSubtype_ListAccountForAccountSwap: FData.account_state := as_ForAtomicAccountSwap;
-     CT_OpSubtype_ListAccountForCoinSwap: FData.account_state := as_ForAtomicCoinSwap;
+  case ANewAccountState of
+    as_Normal: raise EArgumentOutOfRangeException.Create('Listing to normal state is not a listing');
+    as_ForSale: ;
+    as_ForAtomicAccountSwap: ;
+    as_ForAtomicCoinSwap: ;
+  else
+    raise EArgumentOutOfRangeException.Create('Invalid new list account state');
   end;
+
   FData.account_signer := AAccountSigner;
   FData.account_target := AAccountTarget;
   FData.operation_type := lat_ListAccount;
   FData.n_operation := ANOperation;
+
+  FData.account_state := ANewAccountState;
+  if ANewAccountState in [as_ForAtomicAccountSwap,as_ForAtomicCoinSwap] then begin
+    // Hash lock is stored only if AtomicSwap
+    FData.hash_lock := AHashLock;
+  end;
+  FData.locked_until_block := ALockedUntilBlock;
   FData.account_price := AAccountPrice;
   FData.account_to_pay := AAccountToPay;
   FData.fee := AFee;
   FData.payload := APayload;
   // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
   // FData.public_key := key.PublicKey;
-  FData.new_public_key := ANewPublicKey;
-  FData.locked_until_block := ALockedUntilBlock;
-  if AListOpSubType in [CT_OpSubtype_ListAccountForAccountSwap, CT_OpSubtype_ListAccountForCoinSwap] then
-    FData.hash_lock := AHashLock;
+  if (ANewAccountState in [as_ForAtomicCoinSwap]) then begin
+    // Force NULL new_public_key
+    FData.new_public_key := CT_TECDSA_Public_Nul;
+  end else begin
+    FData.new_public_key := ANewPublicKey;
+  end;
+
+
 
   if Assigned(AKey) then begin
     FData.sign := TCrypto.ECDSASign(AKey.PrivateKey, GetDigestToSign(ACurrentProtocol));
@@ -2403,6 +2510,10 @@ begin
   Stream.Write(FData.account_sender,Sizeof(FData.account_sender));
   Stream.Write(FData.account_target,Sizeof(FData.account_target));
   Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
+  // VERSION 5: write the GUID
+  if FProtocolVersion >= CT_PROTOCOL_5 then begin
+    TStreamOp.writeGuid(Stream,FData.guid);
+  end;
   Stream.Write(FData.dataType,Sizeof(FData.dataType));
   Stream.Write(FData.dataSequence,Sizeof(FData.dataSequence));
   Stream.Write(FData.amount,Sizeof(FData.amount));
@@ -2416,11 +2527,16 @@ end;
 function TOpData.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
 begin
   Result := false;
-  if Stream.Size-Stream.Position < 36  then exit; // Invalid stream
+  if Stream.Size-Stream.Position < 16  then exit; // Invalid stream
   Stream.Read(FData.account_signer,Sizeof(FData.account_signer));
   Stream.Read(FData.account_sender,Sizeof(FData.account_sender));
   Stream.Read(FData.account_target,Sizeof(FData.account_target));
   Stream.Read(FData.n_operation,Sizeof(FData.n_operation));
+  // VERSION 5: write the GUID
+  if FProtocolVersion >= CT_PROTOCOL_5 then begin
+    if TStreamOp.ReadGUID(Stream,FData.guid)<16 then Exit;
+  end;
+  if Stream.Size-Stream.Position < 20  then exit; // Invalid stream
   Stream.Read(FData.dataType,Sizeof(FData.dataType));
   Stream.Read(FData.dataSequence,Sizeof(FData.dataSequence));
   Stream.Read(FData.amount,Sizeof(FData.amount));
@@ -2454,9 +2570,6 @@ begin
     OperationResume.Senders[0].N_Operation:=FData.n_operation;
     OperationResume.Senders[0].Signature:=FData.sign;
     OperationResume.Senders[0].Amount:=Int64(FData.amount + FData.fee)*(-1);
-    OperationResume.Senders[0].Data.ID := StringToGUID('{00000000-0000-0000-0000-000000000000}'); // NOTE: ID missing in V4, added to V5
-    OperationResume.Senders[0].Data.Sequence := FData.dataSequence;
-    OperationResume.Senders[0].Data.&Type := FData.dataType;
   end else begin
     OperationResume.Senders[0].Amount:=Int64(FData.amount)*(-1);
     SetLength(OperationResume.Changers,1);
@@ -2465,9 +2578,6 @@ begin
     OperationResume.Changers[0].Fee:=FData.fee;
     OperationResume.Changers[0].N_Operation:=FData.n_operation;
     OperationResume.Changers[0].Signature:=FData.sign;
-    OperationResume.Senders[0].Data.ID := StringToGUID('{00000000-0000-0000-0000-000000000000}'); // NOTE: ID missing in V4, added to V5
-    OperationResume.Changers[0].Data.Sequence := FData.dataSequence;
-    OperationResume.Changers[0].Data.&Type := FData.dataType;
   end;
   //
   SetLength(OperationResume.Receivers,1);
@@ -2475,9 +2585,10 @@ begin
   OperationResume.Receivers[0].Account:=FData.account_target;
   OperationResume.Receivers[0].Amount:=FData.amount;
   OperationResume.Receivers[0].Payload:=FData.payload;
-  OperationResume.Senders[0].Data.ID := StringToGUID('{00000000-0000-0000-0000-000000000000}'); // NOTE: ID missing in V4, added to V5
-  OperationResume.Receivers[0].Data.Sequence := FData.dataSequence;
-  OperationResume.Receivers[0].Data.&Type := FData.dataType;
+  // Add OpData missing in V4, added to V5
+  OperationResume.Senders[0].OpData.ID := FData.guid;
+  OperationResume.Senders[0].OpData.Sequence := FData.dataSequence;
+  OperationResume.Senders[0].OpData.&Type := FData.dataType;
 
   //
   OperationResume.n_operation:=FData.n_operation;
@@ -2657,7 +2768,7 @@ end;
 
 constructor TOpData.CreateOpData(ACurrentProtocol : word; account_signer, account_sender,
   account_target: Cardinal; signer_key: TECPrivateKey; n_operation: Cardinal;
-  dataType, dataSequence: Word; amount, fee: UInt64; payload: TRawBytes);
+  dataType, dataSequence: Word; AGUID : TGUID; amount, fee: UInt64; payload: TRawBytes);
 begin
   Inherited Create(ACurrentProtocol);
   FData.account_sender:=account_sender;
@@ -2669,8 +2780,9 @@ begin
   FData.payload:=payload;
   FData.dataSequence:=dataSequence;
   FData.dataType:=dataType;
+  FData.guid := AGUID;
   if Assigned(signer_key) then begin
-    FData.sign := TCrypto.ECDSASign(signer_key.PrivateKey, GetDigestToSign(CT_PROTOCOL_4));
+    FData.sign := TCrypto.ECDSASign(signer_key.PrivateKey, GetDigestToSign(ACurrentProtocol));
     FHasValidSignature := true;
     FUsedPubkeyForSignature := signer_key.PublicKey;
   end else begin
@@ -2696,6 +2808,10 @@ begin
     Stream.Write(FData.account_sender,Sizeof(FData.account_sender));
     Stream.Write(FData.account_target,Sizeof(FData.account_target));
     Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
+    // VERSION 5: write the GUID to the digest
+    if current_protocol >= CT_PROTOCOL_5 then begin
+      TStreamOp.WriteGUID(Stream,FData.guid);
+    end;
     Stream.Write(FData.dataType,Sizeof(FData.dataType));
     Stream.Write(FData.dataSequence,Sizeof(FData.dataSequence));
     Stream.Write(FData.amount,Sizeof(FData.amount));

+ 449 - 0
src/core/UPCRPCOpData.pas

@@ -0,0 +1,449 @@
+unit UPCRPCOpData;
+
+{ Copyright (c) 2019 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I config.inc}
+
+Uses classes, SysUtils,
+  UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  URPC, UCrypto, UWallet, UBlockChain, ULog;
+
+
+Type
+  TRPCOpData = Class
+  private
+    class Function CreateOpData(ARPCProcess : TRPCProcess; ACurrent_protocol : Word;
+       AInputParams : TPCJSONObject;
+       const AAccountSignerPublicKey : TAccountKey;
+       AAccountSigner, AAccountSender, AAccountTarget, ASender_last_n_operation : Cardinal;
+       Const ARawPayload : TRawBytes;
+       var AErrorNum: Integer; var AErrorDesc: String; var AOpData : TOpData): Boolean;
+  public
+    class function OpData_SendOpData(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function OpData_SignOpData(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function OpData_FindOpDataOperations(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+  End;
+
+implementation
+
+{ TRPCOpData }
+
+class function TRPCOpData.CreateOpData(ARPCProcess: TRPCProcess;
+  ACurrent_protocol: Word;
+  AInputParams : TPCJSONObject;
+  const AAccountSignerPublicKey : TAccountKey;
+  AAccountSigner, AAccountSender, AAccountTarget, ASender_last_n_operation: Cardinal;
+  const ARawPayload: TRawBytes;
+  var AErrorNum: Integer; var AErrorDesc: String; var AOpData : TOpData): Boolean;
+var LSignerKey : TECPrivateKey;
+  LGUID : TGUID;
+  LStringGuid : String;
+begin
+  Result := False;
+  AOpData := Nil;
+  if (Not ARPCProcess.RPCServer.AllowUsePrivateKeys) then begin
+    // Protection when server is locked to avoid private keys call
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit;
+  end;
+  If Not ARPCProcess.RPCServer.WalletKeys.IsValidPassword then begin
+    AErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+    AErrorDesc := 'Wallet is password protected. Unlock first';
+    Exit;
+  end;
+  if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(AAccountSignerPublicKey,LSignerKey,AErrorNum,AErrorDesc) then Exit;
+
+  LStringGuid := AInputParams.AsString('guid','');
+  if LStringGuid<>'' then begin
+    try
+      LGUID := StringToGUID(LStringGuid);
+    except
+      On E:Exception do begin
+        AErrorNum := CT_RPC_ErrNum_InvalidData;
+        AErrordesc := 'Invalid "guid" value '+E.Message;
+        Exit;
+      end;
+    end;
+  end else LGUID := CT_TOpDataData_NUL.guid;
+
+  AOpData := TOpData.CreateOpData(ACurrent_protocol,
+    AAccountSigner,
+    AAccountSender,
+    AAccountTarget,
+    LSignerKey,
+    ASender_last_n_operation+1,
+    AInputParams.AsInteger('data_type',0),
+    AInputParams.AsInteger('data_sequence',0),
+    LGUID,
+    TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('amount',0)),
+    TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('fee',0)),
+    ARawPayload);
+  Result := True;
+end;
+
+class function TRPCOpData.OpData_FindOpDataOperations(
+  const ASender: TRPCProcess; const AMethodName: String; AInputParams,
+  AJSONResponse: TPCJSONObject; var AErrorNum: Integer;
+  var AErrorDesc: String): Boolean;
+
+    Procedure DoFindFromBlock(ABlock_number : Integer;
+      ASearchedAccount_number: Cardinal;
+      AStartOperation, AEndOperation: Integer;
+      AAct_depth : Integer; AFirst_Block_Is_Unknown : Boolean;
+      ASearchBySender : Boolean; ASearchSender : Cardinal;
+      ASearchByTarget : Boolean; ASearchTarget : Cardinal;
+      ASearchByGUID : Boolean; ASearchGUID : TGUID;
+      ASearchByDataSequence : Boolean; ASearchDataSequence : Word;
+      ASearchByDataType : Boolean; ASearchDataType : Word;
+      AOperationsResumeList : TOperationsResumeList
+      );
+
+      Function EqualGUIDs(const AGuid_A, AGuid_B : TGUID) : Boolean;
+      var i : Integer;
+      begin
+        Result := (AGuid_A.D1 = AGuid_B.D1)
+          and (AGuid_A.D2 = AGuid_B.D2)
+          and (AGuid_A.D3 = AGuid_B.D3);
+        i := Low(AGuid_A.D4);
+        while (Result) and (i<=High(AGuid_A.D4)) do begin
+          Result := (AGuid_A.D4[i] = AGuid_B.D4[i]);
+          inc(i);
+        end;
+      end;
+
+    var LOpComp : TPCOperationsComp;
+      LOperation : TPCOperation;
+      LOpData : TOpData;
+      LOperationResume : TOperationResume;
+      LList : TList<Cardinal>;
+      i : Integer;
+      LLast_block_number : Integer;
+      LFound_in_block : Boolean;
+      LFoundCounter : Integer;
+    begin
+      if (AAct_depth<=0) then exit;
+      LFoundCounter := 0;
+      LOpComp := TPCOperationsComp.Create(Nil);
+      Try
+        LList := TList<Cardinal>.Create;
+        try
+          LLast_block_number := ABlock_number+1;
+          while (LLast_block_number>ABlock_number) And (AAct_depth>0)
+            And (ABlock_number >= (ASearchedAccount_number DIV CT_AccountsPerBlock))
+            And (LFoundCounter <= AEndOperation) do begin
+
+            if Assigned(ASender) then begin
+              // Threading protection
+              if ASender.Terminated then Exit;
+            end;
+            LFound_in_block := False;
+            LLast_block_number := ABlock_number;
+            LList.Clear;
+            If not ASender.Node.Bank.Storage.LoadBlockChainBlock(LOpComp,ABlock_number) then begin
+              TLog.NewLog(ltdebug,ClassName,'Block '+inttostr(ABlock_number)+' not found. Cannot read operations');
+              Exit;
+            end;
+            LFound_in_block := LOpComp.OperationsHashTree.GetOperationsAffectingAccount(ASearchedAccount_number,LList) > 0;
+              // Reverse order:
+            for i := LList.Count - 1 downto 0 do begin
+              LOperation := LOpComp.Operation[LList.Items[i]];
+              if LOperation is TOpData then begin
+                //
+                LOpData := TOpData( LOperation );
+                // Search by filter:
+                if ((Not ASearchBySender) Or (ASearchSender = LOpData.Data.account_sender))
+                   and ((Not ASearchByTarget) Or (ASearchTarget = LOpData.Data.account_target))
+                   and ((Not ASearchByGUID) Or (EqualGUIDs(ASearchGUID,LOpData.Data.guid)))
+                   and ((Not ASearchByDataSequence) Or (ASearchDataSequence = LOpData.Data.dataSequence))
+                   and ((Not ASearchByDataType) Or (ASearchDataType = LOpData.Data.dataType))
+                then begin
+                  if (LFoundCounter>=AStartOperation) And (LFoundCounter<=AEndOperation) then begin
+                    If TPCOperation.OperationToOperationResume(ABlock_number,LOpData,False,LOpData.SignerAccount,LOperationResume) then begin
+                      LOperationResume.Balance:=-1;
+                      LOperationResume.NOpInsideBlock:=LList.Items[i];
+                      LOperationResume.Block:=ABlock_number;
+                      AOperationsResumeList.Add(LOperationResume);
+                    end;
+                  end;
+                  inc(LFoundCounter);
+                end;
+              end;
+            end; // For LList...
+            //
+            dec(AAct_depth);
+            If (Not LFound_in_block) And (AFirst_Block_Is_Unknown) then begin
+              Dec(ABlock_number);
+            end else begin
+              ABlock_number := LOpComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(ASearchedAccount_number,ABlock_number);
+            end;
+            LOpComp.Clear(true);
+          end;
+        finally
+          LList.Free;
+        end;
+      Finally
+        LOpComp.Free;
+      End;
+    end;
+
+Var LAccount : TAccount;
+  LStartBlock : Cardinal;
+  LMaxDepth : Integer;
+  LSearchedAccount_number: Cardinal;
+  LStartOperation, LMaxOperations: Integer;
+  LFirst_Block_Is_Unknown : Boolean;
+  LSearchBySender : Boolean;
+  LSearchByTarget : Boolean;
+  LSearchByGUID : Boolean; LSearchGUID : TGUID;
+  LSearchByDataSequence : Boolean; LSearchDataSequence : Word;
+  LSearchByDataType : Boolean; LSearchDataType : Word;
+  LOperationsResumeList : TOperationsResumeList;
+  LSender, LTarget : Cardinal;
+  LResultArray : TPCJSONArray;
+  i : Integer;
+begin
+  Result := False;
+
+  LSender := AInputParams.AsCardinal('sender',CT_MaxAccount);
+  LTarget := AInputParams.AsCardinal('target',CT_MaxAccount);
+  LSearchedAccount_number := CT_MaxAccount;
+  LSearchBySender := (LSender>=0) And (LSender<ASender.Node.Bank.AccountsCount);
+  LSearchByTarget := (LTarget>=0) And (LTarget<ASender.Node.Bank.AccountsCount);
+  if (LSearchBySender) then begin
+    LSearchedAccount_number := LSender;
+  end else if (LSearchByTarget) then begin
+    LSearchedAccount_number := LTarget;
+  end else begin
+    AErrorNum := CT_RPC_ErrNum_InvalidData;
+    AErrorDesc := 'Must provide "sender" or "target" valid values';
+    Exit;
+  end;
+
+  if AInputParams.IndexOfName('guid')>=0 then begin
+    try
+      LSearchGUID := StringToGUID( AInputParams.AsString('guid','') );
+      LSearchByGUID := True;
+    except
+      on E:Exception do begin
+        AErrorNum := CT_RPC_ErrNum_InvalidData;
+        AErrorDesc := 'Invalid "guid" param '+E.Message;
+        Exit;
+      end;
+    end;
+  end else LSearchByGUID := False;
+
+  if AInputParams.IndexOfName('data_sequence')>=0 then begin
+    LSearchByDataSequence := True;
+    LSearchDataSequence := AInputParams.AsInteger('data_sequence',0);
+  end else LSearchByDataSequence := False;
+
+  if AInputParams.IndexOfName('data_type')>=0 then begin
+    LSearchByDataType := True;
+    LSearchDataType := AInputParams.AsInteger('data_type',0);
+  end else LSearchByDataType := False;
+
+  LMaxDepth := AInputParams.AsInteger('depth',1000);
+  LStartOperation := AInputParams.AsInteger('start',0);
+  LMaxOperations := AInputParams.AsInteger('max',100);
+  if AInputParams.IndexOfName('startblock')>=0 then begin
+    LStartBlock := AInputParams.AsInteger('startblock',100);
+    LFirst_Block_Is_Unknown := True;
+  end else begin
+    if not ASender.RPCServer.GetMempoolAccount(LSearchedAccount_number,LAccount) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidData;
+      AErrorDesc := 'Invalid account';
+      Exit;
+    end;
+    LFirst_Block_Is_Unknown := False;
+    LStartBlock := LAccount.updated_block;
+    if LStartBlock>=ASender.Node.Bank.BlocksCount then Dec(LStartBlock); // If its updated on mempool, don't look the mempool
+  end;
+
+  LOperationsResumeList := TOperationsResumeList.Create;
+  try
+    DoFindFromBlock(LStartBlock,
+      LSearchedAccount_number,
+      LStartOperation, LStartOperation + LMaxOperations,
+      LMaxDepth, LFirst_Block_Is_Unknown,
+      LSearchBySender, LSender,
+      LSearchByTarget, LTarget,
+      LSearchByGUID, LSearchGUID,
+      LSearchByDataSequence, LSearchDataSequence,
+      LSearchByDataType, LSearchDataType,
+      LOperationsResumeList
+      );
+    //
+    LResultArray := AJSONResponse.GetAsArray('result');
+
+    for i := 0 to LOperationsResumeList.Count-1 do begin
+      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.OperationResume[i],ASender.Node.Bank.BlocksCount,LResultArray.GetAsObject( LResultArray.Count ));
+    end;
+    Result := True;
+  finally
+    LOperationsResumeList.Free;
+  end;
+end;
+
+class function TRPCOpData.OpData_SendOpData(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+var LOpData : TOpData;
+  LSigner, LSender, LTarget : TAccount;
+  LEncodedRawPayload : TRawBytes;
+  LErrors : String;
+  LOPR : TOperationResume;
+begin
+  Result := False;
+  if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('sender',CT_MaxAccount),LSender) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    AErrorDesc := 'Invalid "sender"';
+    Exit;
+  end;
+  if (AInputParams.IndexOfName('signer')>=0) then begin
+    if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('signer',CT_MaxAccount),LSigner) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+      AErrorDesc := 'Invalid "signer"';
+      Exit;
+    end;
+  end else LSigner := LSender; // If no "signer" param, then use "sender" as signer by default
+  if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('target',CT_MaxAccount),LTarget) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    AErrorDesc := 'Invalid "target"';
+    Exit;
+  end;
+
+  if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
+    TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+    AInputParams.AsString('payload_method','none'),
+    AInputParams.AsString('pwd',''),
+    LSender.accountInfo.accountKey,
+    LTarget.accountInfo.accountKey,
+    LEncodedRawPayload,AErrorNum,AErrorDesc) Then Exit;
+
+
+  if Not CreateOpData(ASender,
+    ASender.Node.Bank.SafeBox.CurrentProtocol,
+    AInputParams,
+    LSigner.accountInfo.accountKey,
+    LSigner.account,
+    LSender.account,
+    LTarget.account,
+    LSigner.n_operation,
+    LEncodedRawPayload,
+    AErrorNum, AErrorDesc, LOpData) then Exit;
+
+  if LOpData=nil then Exit;
+  try
+    If not ASender.Node.AddOperation(Nil,LOpData,LErrors) then begin
+      AErrorDesc := 'Error adding operation: '+LErrors;
+      AErrorNum := CT_RPC_ErrNum_InvalidOperation;
+      Exit;
+    end;
+    TPCOperation.OperationToOperationResume(0,LOpData,False,LSender.account,LOPR);
+    TPascalCoinJSONComp.FillOperationObject(LOPR,ASender.Node.Bank.BlocksCount,AJSONResponse.GetAsObject('result'));
+    Result := True;
+  finally
+    LOpData.free;
+  end;
+end;
+
+class function TRPCOpData.OpData_SignOpData(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+var LOpData : TOpData;
+  LEncodedRawPayload : TRawBytes;
+  LErrors : String;
+  LOPR : TOperationResume;
+  LEncodePayloadType : String;
+  LPayloadPubkey, LSignerPubkey : TAccountKey;
+  LOperationsHashTree : TOperationsHashTree;
+  LSigner : Cardinal;
+begin
+  Result := False;
+
+  if Not TPascalCoinJSONComp.HexaStringToOperationsHashTree(AInputParams.AsString('rawoperations',''),LOperationsHashTree,LErrors) then begin
+    AErrorNum:=CT_RPC_ErrNum_InvalidData;
+    AErrorDesc:= 'Error decoding param "rawoperations": '+LErrors;
+    Exit;
+  end;
+  try
+
+    LEncodePayloadType := AInputParams.AsString('payload_method','none');
+    if (LEncodePayloadType='sender') then begin
+      if Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'sender_',LPayloadPubkey,AErrorDesc) then begin
+        AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+        Exit;
+      end;
+    end else if (LEncodePayloadType='dest') then begin
+      if Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'target_',LPayloadPubkey,AErrorDesc) then begin
+        AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+        Exit;
+      end;
+    end;
+
+    if Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'signer_',LSignerPubkey,AErrorDesc) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+      Exit;
+    end;
+
+    if not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(
+      TCrypto.HexaToRaw(AInputParams.AsString('payload','')),
+      AInputParams.AsString('payload_method','dest'),
+      AInputParams.AsString('pwd',''),
+      LPayloadPubkey,
+      LPayloadPubkey,
+      LEncodedRawPayload,AErrorNum,AErrorDesc) then Exit;
+
+    if Not CreateOpData(ASender,
+      AInputParams.AsInteger('protocol',CT_BUILD_PROTOCOL),
+      AInputParams,
+      LSignerPubkey,
+      AInputParams.AsCardinal('signer',CT_MaxAccount),
+      AInputParams.AsCardinal('sender',CT_MaxAccount),
+      AInputParams.AsCardinal('target',CT_MaxAccount),
+      AInputParams.AsCardinal('last_n_operation',0),
+      LEncodedRawPayload,
+      AErrorNum, AErrorDesc, LOpData) then Exit;
+    if LOpData=nil then Exit;
+    try
+      LOperationsHashTree.AddOperationToHashTree(LOpData);
+      TPascalCoinJSONComp.FillOperationsHashTreeObject(LOperationsHashTree,AJSONResponse.GetAsObject('result'));
+      Result := true;
+    finally
+      LOpData.Free;
+    end;
+  finally
+    LOperationsHashTree.Free;
+  end;
+end;
+
+initialization
+  TRPCProcess.RegisterProcessMethod('senddata',TRPCOpData.OpData_SendOpData);
+  TRPCProcess.RegisterProcessMethod('signdata',TRPCOpData.OpData_SignOpData);
+  TRPCProcess.RegisterProcessMethod('finddataoperations',TRPCOpData.OpData_FindOpDataOperations);
+finalization
+  TRPCProcess.UnregisterProcessMethod('senddata');
+  TRPCProcess.UnregisterProcessMethod('signdata');
+  TRPCProcess.UnregisterProcessMethod('finddataoperations');
+end.

+ 4 - 0
src/core/UPCSafeBoxRootHash.pas

@@ -80,6 +80,10 @@ unit UPCSafeBoxRootHash;
 
 }
 
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
 interface
 
 {$I config.inc}

File diff suppressed because it is too large
+ 375 - 188
src/core/URPC.pas


+ 37 - 8
src/core/UTxMultiOperation.pas

@@ -318,7 +318,9 @@ var i : Integer;
   b : Byte;
 begin
   // Will save protocol info
-  w := CT_PROTOCOL_3;
+  if FProtocolVersion<CT_PROTOCOL_5 then
+    w := CT_PROTOCOL_3
+  else w := CT_PROTOCOL_5;
   stream.Write(w,SizeOf(w));
   // Save senders count
   w := Length(FData.txSenders);
@@ -352,12 +354,17 @@ begin
     Stream.Write(chi.N_Operation,Sizeof(chi.N_Operation));
     b := 0;
     if (public_key in chi.Changes_type) then b:=b OR $01;
-    if (account_name in chi.changes_type) then b:=b OR $02;
-    if (account_type in chi.changes_type) then b:=b OR $04;
+    if (account_name in chi.Changes_type) then b:=b OR $02;
+    if (account_type in chi.Changes_type) then b:=b OR $04;
+    if (account_data in chi.Changes_type) then b:=b OR $08;
+
     Stream.Write(b,Sizeof(b));
     TStreamOp.WriteAccountKey(Stream,chi.New_Accountkey);
     TStreamOp.WriteAnsiString(Stream,chi.New_Name);
     Stream.Write(chi.New_Type,Sizeof(chi.New_Type));
+    if FProtocolVersion>=CT_PROTOCOL_5 then begin
+      TStreamOp.WriteAnsiString(Stream,chi.New_Data);
+    end;
     If FSaveSignatureValue then begin
       TStreamOp.WriteAnsiString(Stream,chi.Signature.r);
       TStreamOp.WriteAnsiString(Stream,chi.Signature.s);
@@ -368,7 +375,7 @@ end;
 
 function TOpMultiOperation.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
 var i : Integer;
-  w : Word;
+  w, LSavedProtocol : Word;
   txs : TMultiOpSender;
   txr : TMultiOpReceiver;
   chi : TMultiOpChangeInfo;
@@ -394,8 +401,8 @@ begin
   Result := False;
   Try
     // Read protocol info
-    stream.Read(w,SizeOf(w));
-    If w<>CT_PROTOCOL_3 then Raise Exception.Create('Invalid protocol found');
+    stream.Read(LSavedProtocol,SizeOf(LSavedProtocol));
+    If (Not (LSavedProtocol in [CT_PROTOCOL_3,CT_PROTOCOL_5])) then Raise Exception.Create('Invalid protocol found '+IntToStr(LSavedProtocol));
     // Load senders
     stream.Read(w,SizeOf(w));
     If w>CT_MAX_MultiOperation_Senders then Raise Exception.Create('Max senders');
@@ -441,11 +448,17 @@ begin
         if (b AND $01)=$01 then chi.changes_type:=chi.changes_type + [public_key];
         if (b AND $02)=$02 then chi.changes_type:=chi.changes_type + [account_name];
         if (b AND $04)=$04 then chi.changes_type:=chi.changes_type + [account_type];
+        if (b AND $08)=$08 then chi.changes_type:=chi.changes_type + [account_data];
         // Check
-        if (b AND $F8)<>0 then Exit;
+        if (LSavedProtocol=CT_PROTOCOL_3) and ((b AND $F8)<>0) then Exit;
+        if (b AND $F0)<>0 then Exit;
         TStreamOp.ReadAccountKey(Stream,chi.New_Accountkey);
         TStreamOp.ReadAnsiString(Stream,chi.New_Name);
         Stream.Read(chi.New_Type,Sizeof(chi.New_Type));
+        if (LSavedProtocol<>CT_PROTOCOL_3) then begin
+          TStreamOp.ReadAnsiString(Stream,chi.New_Data);
+        end;
+
         TStreamOp.ReadAnsiString(Stream,chi.Signature.r);
         TStreamOp.ReadAnsiString(Stream,chi.Signature.s);
         //
@@ -677,6 +690,18 @@ begin
         Exit;
       end;
     end;
+    // Account Data protection: (PIP-0024)
+    if (account_data in chi.Changes_type) then begin
+      if Length(chi.New_Data)>CT_MaxAccountDataSize then begin
+        errors := 'New data length ('+IntToStr(Length(chi.New_data))+') > '+IntToStr(CT_MaxAccountDataSize);
+        Exit;
+      end;
+    end else begin
+      if Length(chi.New_Data)<>0 then begin
+        errors := 'New data must be null when no data change';
+        Exit;
+      end;
+    end;
     If (chi.changes_type=[]) then begin
       errors := 'No change';
       Exit;
@@ -705,6 +730,7 @@ begin
       changer.accountInfo.price := 0;
       changer.accountInfo.account_to_pay := 0;
       changer.accountInfo.new_publicKey := CT_TECDSA_Public_Nul;
+      changer.accountInfo.hashed_secret := Nil;
     end;
     If (account_name in chi.Changes_type) then begin
       changer.name := chi.New_Name;
@@ -712,6 +738,9 @@ begin
     If (account_type in chi.Changes_type) then begin
       changer.account_type := chi.New_Type;
     end;
+    If (account_data in chi.Changes_type) then begin
+      changer.account_data := chi.New_Data;
+    end;
     If Not AccountTransaction.UpdateAccountInfo(
            AccountPreviousUpdatedBlock,
            GetOpID,
@@ -998,7 +1027,7 @@ begin
     // check valid Change type
     for ct:=Low(TOpChangeAccountInfoType) to High(TOpChangeAccountInfoType) do begin
       case ct of
-        public_key,account_name,account_type : ; // Allowed
+        public_key,account_name,account_type,account_data : ; // Allowed
       else
         if (ct in changes[i].Changes_type) then begin
           Exit; // Not allowed multioperation change type

+ 113 - 0
src/gui-classic/UFRMHashLock.lfm

@@ -0,0 +1,113 @@
+object FRMHashLock: TFRMHashLock
+  Left = 237
+  Height = 270
+  Top = 179
+  Width = 548
+  BorderStyle = bsDialog
+  Caption = 'Atomic Swap Hash-Lock '
+  ClientHeight = 270
+  ClientWidth = 548
+  Color = clBtnFace
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  OnCloseQuery = FormCloseQuery
+  OnCreate = FormCreate
+  Position = poOwnerFormCenter
+  LCLVersion = '1.8.0.6'
+  object lblError: TLabel
+    Left = 19
+    Height = 13
+    Top = 239
+    Width = 282
+    AutoSize = False
+    Caption = 'Errors detected'
+    Color = clBtnFace
+    Font.Color = clRed
+    Font.Height = -11
+    Font.Name = 'Tahoma'
+    ParentColor = False
+    ParentFont = False
+    Transparent = False
+  end
+  object btnCancel: TBitBtn
+    Left = 424
+    Height = 31
+    Top = 231
+    Width = 116
+    Anchors = [akRight, akBottom]
+    Kind = bkCancel
+    ModalResult = 2
+    OnClick = btnCancelClick
+    TabOrder = 0
+  end
+  object btnSet: TBitBtn
+    Left = 307
+    Height = 31
+    Top = 231
+    Width = 111
+    Anchors = [akRight, akBottom]
+    Caption = 'Set Hash-Lock'
+    Kind = bkOK
+    ModalResult = 1
+    OnClick = btnSetClick
+    TabOrder = 1
+  end
+  object GroupBox1: TGroupBox
+    Left = 8
+    Height = 83
+    Top = 14
+    Width = 532
+    Caption = 'Mode'
+    ClientHeight = 65
+    ClientWidth = 528
+    TabOrder = 2
+    object rbHashLock: TRadioButton
+      Left = 11
+      Height = 19
+      Top = 8
+      Width = 362
+      Caption = 'Hash-Lock - the 32 byte hash-lock provided to you by the counterparty'
+      TabOrder = 0
+    end
+    object rbHashLockKey: TRadioButton
+      Left = 11
+      Height = 19
+      Top = 31
+      Width = 386
+      Caption = 'Hash-Lock Key - a secret that will be hashed to create the 32 byte hash-lock'
+      TabOrder = 1
+    end
+  end
+  object GroupBox2: TGroupBox
+    Left = 8
+    Height = 122
+    Top = 103
+    Width = 532
+    Caption = 'Data'
+    ClientHeight = 104
+    ClientWidth = 528
+    TabOrder = 3
+    object Label2: TLabel
+      Left = 11
+      Height = 13
+      Top = 21
+      Width = 266
+      Caption = 'Please enter the hash lock data as a hexadecimal string'
+      ParentColor = False
+    end
+    object meHashLockData: TMemo
+      Left = 11
+      Height = 49
+      Top = 40
+      Width = 501
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      Font.Color = clBlack
+      Font.Height = -16
+      Font.Name = 'Tahoma'
+      ParentFont = False
+      TabOrder = 0
+      WantReturns = False
+    end
+  end
+end

+ 11 - 2
src/gui-classic/UFRMHashLock.pas

@@ -42,7 +42,12 @@ implementation
 
 uses UCrypto, UAccounts;
 
-{$R *.dfm}
+{$IFnDEF FPC}
+  {$R *.dfm}
+{$ELSE}
+  {$R *.lfm}
+{$ENDIF}
+
 
 procedure TFRMHashLock.btnCancelClick(Sender: TObject);
 begin
@@ -54,6 +59,7 @@ var
   LData : TRawBytes;
   LErr : string;
 begin
+  Try
   Error := '';
   if (NOT rbHashLock.Checked) AND (NOT rbHashLockKey.Checked) then begin
     Error := 'Select the hash-lock mode';
@@ -80,11 +86,14 @@ begin
     end;
     FHashLock := TAccountComp.CalculateHashLock(LData);
   end else Error := 'INTERNAL ERROR: 8356DE573BA748618EDD6603B22D9EAD';
+  Finally
+    if Error='' then ModalResult := MrOk;
+  end;
 end;
 
 procedure TFRMHashLock.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
 begin
-  CanClose := Error = '';
+  //
 end;
 
 procedure TFRMHashLock.FormCreate(Sender: TObject);

+ 31 - 2
src/gui-classic/UFRMOperation.dfm

@@ -105,6 +105,10 @@ object FRMOperation: TFRMOperation
     TabOrder = 1
     object tsOperation: TTabSheet
       TabVisible = False
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object lblFee: TLabel
         Left = 15
         Top = 220
@@ -295,7 +299,7 @@ object FRMOperation: TFRMOperation
         Top = 7
         Width = 524
         Height = 204
-        ActivePage = tsListAccount
+        ActivePage = tsChangeInfo
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
@@ -533,6 +537,10 @@ object FRMOperation: TFRMOperation
         object tsListAccount: TTabSheet
           Caption = 'List Account'
           ImageIndex = 3
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object lblListAccountErrors: TLabel
             Left = 11
             Top = 7
@@ -1022,12 +1030,24 @@ object FRMOperation: TFRMOperation
             Color = clBtnFace
             ParentColor = False
           end
+          object lblChangeAccountData: TLabel
+            Left = 13
+            Top = 87
+            Width = 100
+            Height = 13
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Change Data'
+            Color = clBtnFace
+            ParentColor = False
+          end
           object ebChangeName: TEdit
             Left = 118
             Top = 29
             Width = 258
             Height = 21
             TabOrder = 0
+            TextHint = 'Account name (null or 3..32 chars)'
           end
           object ebChangeType: TEdit
             Left = 118
@@ -1035,6 +1055,15 @@ object FRMOperation: TFRMOperation
             Width = 76
             Height = 21
             TabOrder = 1
+            TextHint = '0..65535'
+          end
+          object ebChangeAccountData: TEdit
+            Left = 118
+            Top = 84
+            Width = 386
+            Height = 21
+            TabOrder = 2
+            TextHint = 'Hexadecimal value (0..32 bytes)'
           end
         end
       end
@@ -1052,7 +1081,7 @@ object FRMOperation: TFRMOperation
       ExplicitLeft = 0
       ExplicitTop = 0
       ExplicitWidth = 0
-      ExplicitHeight = 373
+      ExplicitHeight = 0
       object lblGlobalErrors: TLabel
         Left = 40
         Top = 50

+ 412 - 294
src/gui-classic/UFRMOperation.lfm

@@ -1,12 +1,12 @@
 object FRMOperation: TFRMOperation
-  Left = 772
-  Height = 493
-  Top = 245
+  Left = 564
+  Height = 513
+  Top = 267
   Width = 608
   BorderIcons = [biSystemMenu]
   BorderStyle = bsSingle
   Caption = 'New Operation'
-  ClientHeight = 493
+  ClientHeight = 513
   ClientWidth = 608
   Color = clBtnFace
   Font.Color = clWindowText
@@ -15,8 +15,7 @@ object FRMOperation: TFRMOperation
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   Position = poOwnerFormCenter
-  LCLVersion = '1.8.4.0'
-  Visible = False
+  LCLVersion = '1.8.0.6'
   object lblAccountCaption: TLabel
     Left = 25
     Height = 13
@@ -57,7 +56,7 @@ object FRMOperation: TFRMOperation
   object bbExecute: TBitBtn
     Left = 300
     Height = 31
-    Top = 445
+    Top = 464
     Width = 130
     Caption = 'Execute (F12)'
     Glyph.Data = {
@@ -85,7 +84,7 @@ object FRMOperation: TFRMOperation
   object bbCancel: TBitBtn
     Left = 465
     Height = 31
-    Top = 445
+    Top = 464
     Width = 116
     Cancel = True
     Caption = 'Cancel'
@@ -95,7 +94,7 @@ object FRMOperation: TFRMOperation
   end
   object PageControlLocked: TPageControl
     Left = 25
-    Height = 367
+    Height = 383
     Top = 72
     Width = 556
     ActivePage = tsOperation
@@ -103,7 +102,7 @@ object FRMOperation: TFRMOperation
     TabOrder = 1
     object tsOperation: TTabSheet
       Caption = 'Operation'
-      ClientHeight = 341
+      ClientHeight = 357
       ClientWidth = 548
       TabVisible = False
       object lblFee: TLabel
@@ -116,11 +115,11 @@ object FRMOperation: TFRMOperation
       end
       object gbPayload: TGroupBox
         Left = 13
-        Height = 129
+        Height = 145
         Top = 207
         Width = 521
         Caption = ' Payload: '
-        ClientHeight = 111
+        ClientHeight = 127
         ClientWidth = 517
         TabOrder = 2
         object lblEncryptPassword: TLabel
@@ -141,9 +140,9 @@ object FRMOperation: TFRMOperation
         end
         object lblEncryptionErrors: TLabel
           Left = 255
-          Height = 13
+          Height = 33
           Top = 87
-          Width = 246
+          Width = 182
           AutoSize = False
           Caption = 'Errors detected'
           Font.Color = clRed
@@ -151,6 +150,7 @@ object FRMOperation: TFRMOperation
           Font.Name = 'Tahoma'
           ParentColor = False
           ParentFont = False
+          WordWrap = True
         end
         object lblPayloadLength: TLabel
           Left = 352
@@ -223,6 +223,14 @@ object FRMOperation: TFRMOperation
           OnClick = memoPayloadClick
           TabOrder = 0
         end
+        object cbPayloadAsHex: TCheckBox
+          Left = 444
+          Height = 19
+          Top = 92
+          Width = 54
+          Caption = 'As Hex'
+          TabOrder = 6
+        end
       end
       object ebFee: TEdit
         Left = 90
@@ -232,12 +240,12 @@ object FRMOperation: TFRMOperation
         TabOrder = 1
       end
       object PageControlOpType: TPageControl
-        Left = 15
+        Left = 16
         Height = 167
         Top = 11
         Width = 521
-        ActivePage = tsChangeInfo
-        TabIndex = 5
+        ActivePage = tsListAccount
+        TabIndex = 2
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
@@ -328,285 +336,375 @@ object FRMOperation: TFRMOperation
           ClientHeight = 141
           ClientWidth = 513
           ImageIndex = 1
-          object gbChangeKey: TGroupBox
-            Left = 7
-            Height = 130
-            Top = 6
-            Width = 488
-            Caption = ' Change type: '
-            ClientHeight = 112
-            ClientWidth = 484
+          object lblNewPrivateKey: TLabel
+            Left = 61
+            Height = 13
+            Top = 41
+            Width = 78
+            Caption = 'New private key'
+            ParentColor = False
+          end
+          object lblNewOwnerPublicKey: TLabel
+            Left = 28
+            Height = 13
+            Top = 91
+            Width = 109
+            Caption = 'New owners public key'
+            ParentColor = False
+          end
+          object lblNewOwnerErrors: TLabel
+            Left = 177
+            Height = 13
+            Top = 69
+            Width = 302
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Errors detected'
+            Font.Color = clRed
+            Font.Height = -11
+            Font.Name = 'Tahoma'
+            ParentColor = False
+            ParentFont = False
+          end
+          object lblChangeKeyErrors: TLabel
+            Left = 149
+            Height = 13
+            Top = 16
+            Width = 331
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Errors detected'
+            Font.Color = clRed
+            Font.Height = -11
+            Font.Name = 'Tahoma'
+            ParentColor = False
+            ParentFont = False
+          end
+          object rbChangeKeyWithAnother: TRadioButton
+            Left = 16
+            Height = 19
+            Top = 21
+            Width = 114
+            Caption = 'Change private key'
             TabOrder = 0
-            object lblNewPrivateKey: TLabel
-              Left = 57
-              Height = 13
-              Top = 33
-              Width = 78
-              Caption = 'New private key'
-              ParentColor = False
-            end
-            object lblNewOwnerPublicKey: TLabel
-              Left = 24
-              Height = 13
-              Top = 83
-              Width = 109
-              Caption = 'New owners public key'
-              ParentColor = False
-            end
-            object lblNewOwnerErrors: TLabel
-              Left = 173
-              Height = 13
-              Top = 61
-              Width = 302
-              Alignment = taRightJustify
-              AutoSize = False
-              Caption = 'Errors detected'
-              Font.Color = clRed
-              Font.Height = -11
-              Font.Name = 'Tahoma'
-              ParentColor = False
-              ParentFont = False
-            end
-            object lblChangeKeyErrors: TLabel
-              Left = 145
-              Height = 13
-              Top = 8
-              Width = 331
-              Alignment = taRightJustify
-              AutoSize = False
-              Caption = 'Errors detected'
-              Font.Color = clRed
-              Font.Height = -11
-              Font.Name = 'Tahoma'
-              ParentColor = False
-              ParentFont = False
-            end
-            object rbChangeKeyWithAnother: TRadioButton
-              Left = 12
-              Height = 19
-              Top = 13
-              Width = 114
-              Caption = 'Change private key'
-              TabOrder = 0
-            end
-            object cbNewPrivateKey: TComboBox
-              Left = 145
-              Height = 21
-              Top = 30
-              Width = 294
-              ItemHeight = 13
-              Sorted = True
-              Style = csDropDownList
-              TabOrder = 1
-            end
-            object ebNewPublicKey: TEdit
-              Left = 144
-              Height = 21
-              Top = 80
-              Width = 331
-              TabOrder = 4
-            end
-            object bbChangePrivateKeyKeys: TBitBtn
-              Left = 445
-              Height = 22
-              Top = 29
-              Width = 31
-              Glyph.Data = {
-                36050000424D3605000000000000360400002800000010000000100000000100
-                0800000000000001000000000000000000000001000000010000FF00FF008C6B
-                6C0087707000AE877C000D7FA9006F7C88006D7C94001F7ECE000E80AA001180
-                A7001081AB00048CB900078DBC000B8DBA000C8DBA00088EBC001285B0007882
-                95006092BD005EA8BE000A91C1000F9DCE002087DE0011A7D10030BCDC001F89
-                E00059A9DC0044BADD004ABFE00057AEF4004DB1F90049B2FF004EB7FF0057B1
-                F60050B6FE0022D7FC0024D7FF0024D8FD0039D7FB0035D8FD004BC6DC0046C6
-                E40048D5EE0075D3E90058E9FD006FE6FF0070E6FF0071F9FE007BFFFF008683
-                88008B8697008F989B00969594009C919000AD858000AD868500AB939500A49E
-                9900B1979400B5A09F008AA5AD00CAA08C00CDAC9300C2A69A00F3BE8000C6AE
-                A000CFB7A100D3BBA200F4C88E00F5CB9A00F5D09C00F8D09800DAC5B700E4CC
-                A900EFD2A900ECD1AC00F6DAAB00F5DEB500F5E1B600F9E1B100FEEAB900FFF2
-                BA00A1C6C8008DE6FA0081F8FE008CFAFD008DFCFE0097FCFD009BFBFD00B8ED
-                F600A7FFFF00AAFFFE00ADFFFE00B6F6FF00B1FCFD00B4FFFF00ECDDCC00E8DD
-                D600FFF7C600FCF5CD00FCF7D100FAF6D600FFFBD500FEFED600F7F2D900FEFF
-                D900FFFEDD00C6F5FF00C6FEFF00D2FFFF00FEF7E000FBFCE100FDFFE100FFFF
-                E400E3FEFF00F9F6F200FFFFF400F1FBFC00F5FFFE00FBFFFF00000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                0000000000000000000000000000000000000000000000000000000000000000
-                00000000000000000605000004080408080A000000000011191A000B2A23272D
-                531B080000001116201D0B552C23272E531C1509003207201D000F552C23272B
-                3A3F41030112202000000F552C252938606771684236200000000B5F5D6B3B61
-                74676A67513D000000000B59181735716A676A63474B360000000F282C23396A
-                6A6A6A4C404D360000000B552C2534656A654F455049360000000B552C251343
-                6247446E7336000000000B552C25263C3E4B4E483636000000000F55542F3057
-                523331020000000000000B77766D5F5C5C5C2F08000000000000001476726C5C
-                5A58100000000000000000000F0F0B0F0F0F0000000000000000
-              }
-              OnClick = bbChangePrivateKeyKeysClick
-              TabOrder = 2
-            end
-            object rbChangeKeyTransferAccountToNewOwner: TRadioButton
-              Left = 12
-              Height = 19
-              Top = 60
-              Width = 180
-              Caption = 'Transfer account to a new owner'
-              TabOrder = 3
-            end
+          end
+          object cbNewPrivateKey: TComboBox
+            Left = 149
+            Height = 21
+            Top = 38
+            Width = 294
+            ItemHeight = 13
+            Sorted = True
+            Style = csDropDownList
+            TabOrder = 1
+          end
+          object ebNewPublicKey: TEdit
+            Left = 148
+            Height = 21
+            Top = 88
+            Width = 331
+            TabOrder = 2
+          end
+          object bbChangePrivateKeyKeys: TBitBtn
+            Left = 449
+            Height = 22
+            Top = 37
+            Width = 31
+            Glyph.Data = {
+              36050000424D3605000000000000360400002800000010000000100000000100
+              0800000000000001000000000000000000000001000000010000FF00FF008C6B
+              6C0087707000AE877C000D7FA9006F7C88006D7C94001F7ECE000E80AA001180
+              A7001081AB00048CB900078DBC000B8DBA000C8DBA00088EBC001285B0007882
+              95006092BD005EA8BE000A91C1000F9DCE002087DE0011A7D10030BCDC001F89
+              E00059A9DC0044BADD004ABFE00057AEF4004DB1F90049B2FF004EB7FF0057B1
+              F60050B6FE0022D7FC0024D7FF0024D8FD0039D7FB0035D8FD004BC6DC0046C6
+              E40048D5EE0075D3E90058E9FD006FE6FF0070E6FF0071F9FE007BFFFF008683
+              88008B8697008F989B00969594009C919000AD858000AD868500AB939500A49E
+              9900B1979400B5A09F008AA5AD00CAA08C00CDAC9300C2A69A00F3BE8000C6AE
+              A000CFB7A100D3BBA200F4C88E00F5CB9A00F5D09C00F8D09800DAC5B700E4CC
+              A900EFD2A900ECD1AC00F6DAAB00F5DEB500F5E1B600F9E1B100FEEAB900FFF2
+              BA00A1C6C8008DE6FA0081F8FE008CFAFD008DFCFE0097FCFD009BFBFD00B8ED
+              F600A7FFFF00AAFFFE00ADFFFE00B6F6FF00B1FCFD00B4FFFF00ECDDCC00E8DD
+              D600FFF7C600FCF5CD00FCF7D100FAF6D600FFFBD500FEFED600F7F2D900FEFF
+              D900FFFEDD00C6F5FF00C6FEFF00D2FFFF00FEF7E000FBFCE100FDFFE100FFFF
+              E400E3FEFF00F9F6F200FFFFF400F1FBFC00F5FFFE00FBFFFF00000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              0000000000000000000000000000000000000000000000000000000000000000
+              00000000000000000605000004080408080A000000000011191A000B2A23272D
+              531B080000001116201D0B552C23272E531C1509003207201D000F552C23272B
+              3A3F41030112202000000F552C252938606771684236200000000B5F5D6B3B61
+              74676A67513D000000000B59181735716A676A63474B360000000F282C23396A
+              6A6A6A4C404D360000000B552C2534656A654F455049360000000B552C251343
+              6247446E7336000000000B552C25263C3E4B4E483636000000000F55542F3057
+              523331020000000000000B77766D5F5C5C5C2F08000000000000001476726C5C
+              5A58100000000000000000000F0F0B0F0F0F0000000000000000
+            }
+            OnClick = bbChangePrivateKeyKeysClick
+            TabOrder = 3
+          end
+          object rbChangeKeyTransferAccountToNewOwner: TRadioButton
+            Left = 16
+            Height = 19
+            Top = 68
+            Width = 180
+            Caption = 'Transfer account to a new owner'
+            TabOrder = 4
           end
         end
-        object tsListForSale: TTabSheet
-          Caption = 'List account for sale'
+        object tsListAccount: TTabSheet
+          Caption = 'List Account'
           ClientHeight = 141
           ClientWidth = 513
           ImageIndex = 3
-          object gbSaleType: TGroupBox
-            Left = 8
-            Height = 135
-            Top = 5
-            Width = 499
-            Caption = ' Sale type: '
-            ClientHeight = 117
-            ClientWidth = 495
+          object lblPrice: TLabel
+            Left = 38
+            Height = 13
+            Top = 68
+            Width = 50
+            Alignment = taRightJustify
+            Caption = 'Sale price:'
+            ParentColor = False
+          end
+          object lblSeller: TLabel
+            Left = 200
+            Height = 13
+            Top = 68
+            Width = 136
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Seller Account'
+            ParentColor = False
+          end
+          object lblNewKey: TLabel
+            Left = 39
+            Height = 13
+            Top = 91
+            Width = 49
+            Alignment = taRightJustify
+            Caption = 'Buyer Key'
+            ParentColor = False
+          end
+          object lblTimeLock: TLabel
+            Left = 41
+            Height = 13
+            Top = 115
+            Width = 47
+            Alignment = taRightJustify
+            Caption = 'Time-Lock'
+            ParentColor = False
+          end
+          object lblListAccountErrors: TLabel
+            Left = 16
+            Height = 13
+            Top = 8
+            Width = 483
+            AutoSize = False
+            Caption = 'Errors detected'
+            Font.Color = clRed
+            Font.Height = -11
+            Font.Name = 'Tahoma'
+            ParentColor = False
+            ParentFont = False
+          end
+          object rbListAccountForPublicSale: TRadioButton
+            Left = 16
+            Height = 19
+            Top = 26
+            Width = 106
+            Caption = 'List for Public Sale'
             TabOrder = 0
-            object Label1: TLabel
-              Left = 81
-              Height = 13
-              Top = 44
-              Width = 50
-              Caption = 'Sale price:'
-              ParentColor = False
-            end
-            object Label3: TLabel
-              Left = 229
-              Height = 13
-              Top = 44
-              Width = 142
-              Caption = 'Seller account (where to pay)'
-              ParentColor = False
-            end
-            object lblSaleNewOwnerPublicKey: TLabel
-              Left = 20
-              Height = 13
-              Top = 67
-              Width = 109
-              Caption = 'New owners public key'
-              ParentColor = False
-            end
-            object lblSaleLockedUntilBlock: TLabel
-              Left = 44
-              Height = 13
-              Top = 91
-              Width = 87
-              Caption = 'Locked until block:'
-              ParentColor = False
-            end
-            object lblListAccountErrors: TLabel
-              Left = 145
-              Height = 13
-              Top = 0
-              Width = 331
-              Alignment = taRightJustify
-              AutoSize = False
-              Caption = 'Errors detected'
-              Font.Color = clRed
-              Font.Height = -11
-              Font.Name = 'Tahoma'
-              ParentColor = False
-              ParentFont = False
-            end
-            object rbListAccountForPublicSale: TRadioButton
-              Left = 10
-              Height = 19
-              Top = 2
-              Width = 146
-              Caption = 'List account for public sale'
-              TabOrder = 0
-            end
-            object rbListAccountForPrivateSale: TRadioButton
-              Left = 10
-              Height = 19
-              Top = 22
-              Width = 153
-              Caption = 'List account for private sale'
-              TabOrder = 1
-            end
-            object ebSalePrice: TEdit
-              Left = 137
-              Height = 21
-              Top = 41
-              Width = 86
-              TabOrder = 2
-            end
-            object ebSellerAccount: TEdit
-              Left = 378
-              Height = 21
-              Top = 41
-              Width = 82
-              TabOrder = 3
-            end
-            object ebSaleNewOwnerPublicKey: TEdit
-              Left = 137
-              Height = 21
-              Top = 64
-              Width = 323
-              TabOrder = 4
-            end
-            object ebSaleLockedUntilBlock: TEdit
-              Left = 137
-              Height = 21
-              Top = 88
-              Width = 86
-              TabOrder = 5
-            end
-            object sbSearchListerSellerAccount: TSpeedButton
-              Left = 464
-              Height = 22
-              Top = 41
-              Width = 23
-              Glyph.Data = {
-                36030000424D3803000000000000360000002800000010000000100000000100
-                18000000000000000000120B0000120B00000000000000000000FF00FF4A667C
-                BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
-                FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF
-                00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF
-                2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
-                FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF
-                00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
-                51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00
-                FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF
-                00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
-                FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3
-                B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD
-                EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF
-                FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF
-                FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1
-                C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF
-                FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF
-                E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4
-                C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF
-                FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4
-                C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8
-                DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF
-                FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890
-                86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
-                00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF
-              }
-              OnClick = sbSearchListerSellerAccountClick
-            end
+          end
+          object rbListAccountForPrivateSale: TRadioButton
+            Left = 16
+            Height = 19
+            Top = 46
+            Width = 113
+            Caption = 'List for Private Sale'
+            TabOrder = 1
+          end
+          object ebPrice: TEdit
+            Left = 96
+            Height = 21
+            Top = 65
+            Width = 94
+            TabOrder = 4
+            TextHint = 'PASC Quantity'
+          end
+          object ebSellerAccount: TEdit
+            Left = 344
+            Height = 21
+            Top = 65
+            Width = 122
+            TabOrder = 5
+            TextHint = 'Account Number'
+          end
+          object ebNewKey: TEdit
+            Left = 96
+            Height = 21
+            Top = 88
+            Width = 370
+            TabOrder = 6
+            TextHint = 'BASE58 Encoded Public Key Of New Owner'
+          end
+          object ebTimeLock: TEdit
+            Left = 96
+            Height = 21
+            Top = 112
+            Width = 91
+            TabOrder = 7
+            TextHint = 'Block number'
+          end
+          object sbSearchListerSellerAccount: TSpeedButton
+            Left = 470
+            Height = 22
+            Top = 65
+            Width = 23
+            Glyph.Data = {
+              36030000424D3803000000000000360000002800000010000000100000000100
+              18000000000000000000120B0000120B00000000000000000000FF00FF4A667C
+              BE9596FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FF6B9CC31E89E84B7AA3C89693FF00FFFF00FFFF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4BB4FE51B5FF
+              2089E94B7AA2C69592FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FF51B7FE51B3FF1D87E64E7AA0CA9792FF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              51B7FE4EB2FF1F89E64E7BA2B99497FF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF52B8FE4BB1FF2787D95F6A76FF
+              00FFB0857FC09F94C09F96BC988EFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              FF00FFFF00FF55BDFFB5D6EDBF9D92BB9B8CE7DAC2FFFFE3FFFFE5FDFADAD8C3
+              B3B58D85FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFCEA795FD
+              EEBEFFFFD8FFFFDAFFFFDBFFFFE6FFFFFBEADDDCAE837FFF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFC1A091FBDCA8FEF7D0FFFFDBFFFFE3FFFFF8FFFF
+              FDFFFFFDC6A99CFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFC1A091FEE3ACF1
+              C491FCF2CAFFFFDDFFFFE4FFFFF7FFFFF7FFFFE9EEE5CBB9948CFF00FFFF00FF
+              FF00FFFF00FFFF00FFC2A191FFE6AEEEB581F7DCAEFEFDD8FFFFDFFFFFE3FFFF
+              E4FFFFE0F3ECD2BB968EFF00FFFF00FFFF00FFFF00FFFF00FFBC978CFBE7B7F4
+              C791F2C994F8E5B9FEFCD8FFFFDDFFFFDCFFFFE0E2D2BAB68E86FF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFD9C3A9FFFEE5F7DCB8F2C994F5D4A5FAE8BDFDF4
+              C9FDFBD6B69089FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB58D85E8
+              DEDDFFFEF2F9D8A3F4C48CF9D49FFDEAB8D0B49FB89086FF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FFAD827FC9AA9EEFE0B7EFDFB2E7CEACB890
+              86B89086FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+              00FFFF00FFBA968ABB988CB79188FF00FFFF00FFFF00FFFF00FF
+            }
+            OnClick = sbSearchListerSellerAccountClick
+          end
+          object rbListAccountForAccountSwap: TRadioButton
+            Left = 160
+            Height = 19
+            Top = 26
+            Width = 159
+            Caption = 'List for Atomic Account Swap'
+            TabOrder = 2
+          end
+          object rbListAccountForCoinSwap: TRadioButton
+            Left = 160
+            Height = 19
+            Top = 46
+            Width = 141
+            Caption = 'List for Atomic Coin Swap'
+            TabOrder = 3
+          end
+          object sbTimeLock: TSpeedButton
+            Left = 192
+            Height = 22
+            Top = 112
+            Width = 23
+            Glyph.Data = {
+              36030000424D3603000000000000360000002800000010000000100000000100
+              18000000000000030000120B0000120B00000000000000000000FF00FF53B6F0
+              4A95DFC6CEEDFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FF1B59D3298DEA7AFDFF47A2E6C1C9ECFF00FFFF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF3F9BE4207EE2
+              1C74DE7AFFFF4398E1CFD5F0FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFA3B5E758CDF52B81DD1676DC72FCFF4390DFE5E9F7FF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB385EA
+              57D1F7287CD90F72DB71FFFF4DA9E8ECB4F9FF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FF96A7E258D3F72883D90F5FCF6EFFFF50
+              AFE8B0B7E5FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              FF00FF9EADE456D8F8277CD40E56C86DF6FF2DBEF10E85DD1777D7167EDB166D
+              D17C92D8FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF97A7E356DAF92781D40D
+              97E30ECFFC0ECCFA0ECFFB0ED0FB0ED5FF12BAF45279D1FF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFB486EA56CCF31ED1FA09BCF411BFF410BFF310BFF310BE
+              F30FD3FD05B2EFA2B4E6FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF6486D72C
+              E3FD09C1F311C4F410C4F40EC8F60ECCF70BC1F30BDCFE3581D6FF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FF4A95DF1DE4FD0CC5F311C7F40ED1F70FC2F139C6
+              F01FD6F805D1F843BBEBFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4491DF1A
+              E8FD00C5F206CEF600C2F27AAAE5FF00FF8CC9ED64F4FF70CFEFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FF6390DC44F3FE0ECFF429DAF635D8F5FF00FFFF00
+              FF9DC4EA63FFFF7EBDE8FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFBFCEF0AD
+              EBF972F8FF5DE3F65EEAF964D7F384E4F559F1FC5CE8F8B1C6EDFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FF9EB9E99DE5F77DF9FD63F2FC60F7FD66FC
+              FE77EBF9A7C3EBFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+              00FFCDDAF39CC1EC8AC7EC85C5EB9CC8EDB3C5EDFF00FFFF00FF
+            }
+            OnClick = sbSearchListerSellerAccountClick
+          end
+          object ebHashLock: TEdit
+            Left = 280
+            Height = 21
+            Top = 112
+            Width = 186
+            TabOrder = 8
+          end
+          object btnHashLock: TSpeedButton
+            Left = 470
+            Height = 22
+            Top = 112
+            Width = 23
+            Glyph.Data = {
+              36030000424D3603000000000000360000002800000010000000100000000100
+              18000000000000030000120B0000120B00000000000000000000FF00FF53B6F0
+              4A95DFC6CEEDFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FF1B59D3298DEA7AFDFF47A2E6C1C9ECFF00FFFF00FFFF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF3F9BE4207EE2
+              1C74DE7AFFFF4398E1CFD5F0FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFA3B5E758CDF52B81DD1676DC72FCFF4390DFE5E9F7FF
+              00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFB385EA
+              57D1F7287CD90F72DB71FFFF4DA9E8ECB4F9FF00FFFF00FFFF00FFFF00FFFF00
+              FFFF00FFFF00FFFF00FFFF00FFFF00FF96A7E258D3F72883D90F5FCF6EFFFF50
+              AFE8B0B7E5FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF
+              FF00FF9EADE456D8F8277CD40E56C86DF6FF2DBEF10E85DD1777D7167EDB166D
+              D17C92D8FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF97A7E356DAF92781D40D
+              97E30ECFFC0ECCFA0ECFFB0ED0FB0ED5FF12BAF45279D1FF00FFFF00FFFF00FF
+              FF00FFFF00FFFF00FFB486EA56CCF31ED1FA09BCF411BFF410BFF310BFF310BE
+              F30FD3FD05B2EFA2B4E6FF00FFFF00FFFF00FFFF00FFFF00FFFF00FF6486D72C
+              E3FD09C1F311C4F410C4F40EC8F60ECCF70BC1F30BDCFE3581D6FF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FF4A95DF1DE4FD0CC5F311C7F40ED1F70FC2F139C6
+              F01FD6F805D1F843BBEBFF00FFFF00FFFF00FFFF00FFFF00FFFF00FF4491DF1A
+              E8FD00C5F206CEF600C2F27AAAE5FF00FF8CC9ED64F4FF70CFEFFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FF6390DC44F3FE0ECFF429DAF635D8F5FF00FFFF00
+              FF9DC4EA63FFFF7EBDE8FF00FFFF00FFFF00FFFF00FFFF00FFFF00FFBFCEF0AD
+              EBF972F8FF5DE3F65EEAF964D7F384E4F559F1FC5CE8F8B1C6EDFF00FFFF00FF
+              FF00FFFF00FFFF00FFFF00FFFF00FF9EB9E99DE5F77DF9FD63F2FC60F7FD66FC
+              FE77EBF9A7C3EBFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF00FFFF
+              00FFCDDAF39CC1EC8AC7EC85C5EB9CC8EDB3C5EDFF00FFFF00FF
+            }
+            OnClick = sbSearchListerSellerAccountClick
+          end
+          object lblHashLock: TLabel
+            Left = 222
+            Height = 13
+            Top = 115
+            Width = 49
+            Alignment = taRightJustify
+            Caption = 'Hash-Lock'
+            ParentColor = False
           end
         end
         object tsDelist: TTabSheet
@@ -680,8 +778,8 @@ object FRMOperation: TFRMOperation
             Left = 204
             Height = 13
             Top = 61
-            Width = 231
-            Caption = 'excessive amount will remain on bought account'
+            Width = 234
+            Caption = 'any over-payment will remain on bought account'
             Font.Color = clGray
             Font.Height = -11
             Font.Name = 'Tahoma'
@@ -833,6 +931,7 @@ object FRMOperation: TFRMOperation
             Top = 29
             Width = 258
             TabOrder = 0
+            TextHint = 'Account name (null or 3..32 chars)'
           end
           object lblChangeType: TLabel
             Left = 13
@@ -850,11 +949,30 @@ object FRMOperation: TFRMOperation
             Top = 56
             Width = 76
             TabOrder = 1
+            TextHint = '0..65535'
+          end
+          object lblChangeAccountData: TLabel
+            Left = 13
+            Height = 13
+            Top = 88
+            Width = 100
+            Alignment = taRightJustify
+            AutoSize = False
+            Caption = 'Change Data'
+            ParentColor = False
+          end
+          object ebChangeAccountData: TEdit
+            Left = 118
+            Height = 21
+            Top = 84
+            Width = 386
+            TabOrder = 2
+            TextHint = 'Hexadecimal value (0..32 bytes)'
           end
         end
       end
       object lblSignerAccount: TLabel
-        Left = 176
+        Left = 278
         Height = 13
         Top = 188
         Width = 131
@@ -862,14 +980,14 @@ object FRMOperation: TFRMOperation
         ParentColor = False
       end
       object ebSignerAccount: TEdit
-        Left = 325
+        Left = 427
         Height = 21
         Top = 184
         Width = 82
         TabOrder = 3
       end
       object sbSearchSignerAccount: TSpeedButton
-        Left = 411
+        Left = 513
         Height = 22
         Top = 184
         Width = 23

+ 38 - 14
src/gui-classic/UFRMOperation.pas

@@ -130,6 +130,8 @@ type
     btnHashLock: TSpeedButton;
     sbTimeLock: TSpeedButton;
     cbPayloadAsHex: TCheckBox;
+    lblChangeAccountData: TLabel;
+    ebChangeAccountData: TEdit;
     procedure ebNewPublicKeyExit(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
@@ -169,7 +171,7 @@ type
     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 UpdateOpBuyAccount(Const SenderAccount : TAccount; var AccountToBuy : TAccount; var amount : Int64; var NewPublicKey : TAccountKey; var ARecipientSigned : Boolean; var errors : String) : Boolean;
-    Function UpdateOpChangeInfo(Const TargetAccount : TAccount; var SignerAccount : TAccount; var changeName : Boolean; var newName : TRawBytes; var changeType : Boolean; var newType : Word; var errors : String) : Boolean;
+    Function UpdateOpChangeInfo(Const TargetAccount : TAccount; var SignerAccount : TAccount; var changeName : Boolean; var newName : TRawBytes; var changeType : Boolean; var newType : Word; var AChangeData : Boolean; var ANewData : TRawBytes; var errors : String) : Boolean;
     procedure SetDefaultFee(const Value: Int64);
     Procedure OnSenderAccountsChanged(Sender : TObject);
     procedure OnWalletKeysChanged(Sender : TObject);
@@ -213,9 +215,9 @@ Var errors : String;
   dooperation : Boolean;
   _newOwnerPublicKey : TECDSA_Public;
   LHashLock : T32Bytes;
-  _newName : TRawBytes;
+  _newName, LNewAccountData : TRawBytes;
   _newType : Word;
-  _changeName, _changeType, _V2, _executeSigner, LRecipientSigned : Boolean;
+  _changeName, _changeType, LChangeAccountData, _V2, _executeSigner, LRecipientSigned : Boolean;
   _senderAccounts : TCardinalsArray;
 label loop_start;
 begin
@@ -299,13 +301,17 @@ loop_start:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         else _fee := signerAccount.balance;
         if (rbListAccountForPublicSale.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, CT_OpSubtype_ListAccountForPublicSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,CT_TECDSA_Public_Nul,0,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
+          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);
         end else if (rbListAccountForPrivateSale.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, CT_OpSubtype_ListAccountForPrivateSale, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, CT_HashLock_NUL, FEncodedPayload);
+          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);
         end  else if (rbListAccountForAccountSwap.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, CT_OpSubtype_ListAccountForAccountSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
+          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);
         end  else if (rbListAccountForCoinSwap.Checked) then begin
-          op := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(FNode.Bank.SafeBox.CurrentProtocol, CT_OpSubtype_ListAccountForCoinSwap, signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,LKey.PrivateKey, LHashLock, FEncodedPayload);
+          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);
         end else raise Exception.Create('Select Sale type');
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelistAccount) then begin
@@ -326,13 +332,15 @@ loop_start:
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
         {%region Operation: Change Info}
-        if not UpdateOpChangeInfo(account,signerAccount,_changeName,_newName,_changeType,_newType,errors) then begin
+        if not UpdateOpChangeInfo(account,signerAccount,_changeName,_newName,_changeType,_newType,LChangeAccountData,LNewAccountData,errors) then begin
           If Length(_senderAccounts)=1 then raise Exception.Create(errors);
         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,_fee,FEncodedPayload);
+             _changeName,_newName,_changeType,_newType,
+             LChangeAccountData,LNewAccountData,
+             _fee,FEncodedPayload);
         end;
         {%endregion}
       end else begin
@@ -576,6 +584,7 @@ begin
   //
   ebChangeName.OnChange:=updateInfoClick;
   ebChangeType.OnChange:=updateInfoClick;
+  ebChangeAccountData.OnChange:=updateInfoClick;
   //
   sbSearchDestinationAccount.OnClick := sbSearchDestinationAccountClick;
   sbSearchListerSellerAccount.OnClick := sbSearchListerSellerAccountClick;
@@ -659,10 +668,12 @@ begin
     ebSignerAccount.text := TAccountComp.AccountNumberToAccountTxtNumber(SenderAccounts.Get(0));
     ebChangeName.Text := FNode.GetMempoolAccount(SenderAccounts.Get(0)).name.ToPrintable;
     ebChangeType.Text := IntToStr(FNode.GetMempoolAccount(SenderAccounts.Get(0)).account_type);
+    ebChangeAccountData.Text := FNode.GetMempoolAccount(SenderAccounts.Get(0)).account_data.ToHexaString;
   end else begin
     ebSignerAccount.text := '';
     ebChangeName.Text := '';
     ebChangeType.Text := '';
+    ebChangeAccountData.Text := '';
   end;
   UpdateAccountsInfo;
   UpdateOperationOptions(errors);
@@ -834,7 +845,7 @@ begin
       exit;
     end;
     AccountToBuy := FNode.GetMempoolAccount(c);
-    ARecipientSigned := TAccountComp.IsOperationRecipientSignable(SenderAccount, AccountToBuy, Amount, FNode.Bank.BlocksCount);
+    ARecipientSigned := TAccountComp.IsOperationRecipientSignable(SenderAccount, AccountToBuy, Amount, FNode.Bank.BlocksCount, FNode.Bank.SafeBox.CurrentProtocol);
     if (SenderAccount.account = AccountToBuy.Account) AND (NOT ARecipientSigned) then begin
       errors := 'Not recipient signable';
       exit;
@@ -874,7 +885,8 @@ begin
 end;
 
 function TFRMOperation.UpdateOpChangeInfo(const TargetAccount: TAccount; var SignerAccount : TAccount;
-   var changeName : Boolean; var newName: TRawBytes; var changeType : Boolean; var newType: Word; var errors: String): Boolean;
+   var changeName : Boolean; var newName: TRawBytes; var changeType : Boolean; var newType: Word;
+   var AChangeData : Boolean; var ANewData : TRawBytes; var errors: String): Boolean;
 var auxC : Cardinal;
   i : Integer;
   errCode : Integer;
@@ -943,6 +955,18 @@ begin
       errors := 'Account name and type are the same. Not changed';
       Exit;
     end;
+    if FNode.Bank.SafeBox.CurrentProtocol>=CT_PROTOCOL_5 then begin
+      // Allow Change Account.Data PIP-0024
+      if Not TCrypto.HexaToRaw(ebChangeAccountData.Text,ANewData) then begin
+        errors := 'Invalid hexadecimal value at Data';
+        Exit;
+      end;
+      AChangeData := Not TBaseType.Equals( TargetAccount.account_data , ANewData);
+      if Length(ANewData)>CT_MaxAccountDataSize then begin
+        errors := Format('Data size (%d) greater than %d',[Length(ANewData),CT_MaxAccountDataSize]);
+        Exit;
+      end;
+    end;
   finally
     Result := errors = '';
     if Not Result then begin
@@ -1081,8 +1105,8 @@ Var
   LHashLock : T32Bytes;
   salePrice, amount : Int64;
   auxC : Cardinal;
-  changeName,changeType, LRecipientSigned : Boolean;
-  newName : TRawBytes;
+  changeName,changeType, LRecipientSigned, LChangeAccountData : Boolean;
+  newName, LNewAccountData : TRawBytes;
   newType : Word;
 begin
   Result := false;
@@ -1152,7 +1176,7 @@ begin
   end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
     Result := UpdateOpBuyAccount(GetDefaultSenderAccount,account_to_buy,amount,publicKey,LRecipientSigned, errors);
   end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
-    Result := UpdateOpChangeInfo(GetDefaultSenderAccount,signer_account,changeName,newName,changeType,newType,errors);
+    Result := UpdateOpChangeInfo(GetDefaultSenderAccount,signer_account,changeName,newName,changeType,newType,LChangeAccountData,LNewAccountData,errors);
   end else begin
     errors := 'Must select an operation';
   end;

+ 18 - 2
src/gui-classic/UFRMRandomOperations.lfm

@@ -1,11 +1,15 @@
 object FRMRandomOperations: TFRMRandomOperations
-  Left = 876
+  Left = 744
   Height = 229
-  Top = 391
+  Top = 390
   Width = 439
   Caption = 'Random Operations'
   ClientHeight = 229
   ClientWidth = 439
+  Color = clBtnFace
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
   OnClose = FormClose
   OnCloseQuery = FormCloseQuery
   OnCreate = FormCreate
@@ -31,12 +35,15 @@ object FRMRandomOperations: TFRMRandomOperations
       Align = alClient
       AutoSize = False
       Caption = 'WARNING! This form will generate RANDOM operations using your private keys!!!'
+      Color = clBtnFace
       Font.Color = clRed
       Font.Height = -13
+      Font.Name = 'Tahoma'
       Font.Style = [fsBold]
       Layout = tlCenter
       ParentColor = False
       ParentFont = False
+      Transparent = False
       WordWrap = True
     end
   end
@@ -69,6 +76,15 @@ object FRMRandomOperations: TFRMRandomOperations
         OnClick = bbRandomOperationsClick
         TabOrder = 0
       end
+      object cbMaxSpeedMode: TCheckBox
+        Left = 232
+        Height = 19
+        Top = 6
+        Width = 101
+        Caption = 'Max speed mode'
+        OnClick = cbMaxSpeedModeClick
+        TabOrder = 1
+      end
     end
     object mLogs: TMemo
       Left = 0

+ 266 - 11
src/gui-classic/UFRMRandomOperations.pas

@@ -44,6 +44,7 @@ type
     FOnUpdated: TNotifyEvent;
     FNeedSanitize : Boolean;
     FAllowExecute: Boolean;
+    FMaxOperationsPerSecond: Integer;
     procedure OnBankNewBlock(Sender : TObject);
   protected
     FBankNotify : TPCBankNotify;
@@ -56,7 +57,7 @@ type
     FnCallsToAddNodeFailed : Int64;
     procedure BCExecute; override;
   public
-    Constructor Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt);
+    Constructor Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt; AMaxOperationsPerSecond : Integer);
     Destructor Destroy; override;
     property LastCall_OperationsTotal : Integer read FLastCall_OperationsTotal;
     property LastCall_OperationsExecuted : Integer read FLastCall_OperationsExecuted;
@@ -64,6 +65,7 @@ type
     property LastCall_Error : String read FLastCall_Error;
     property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
     property AllowExecute : Boolean read FAllowExecute write FAllowExecute;
+    property MaxOperationsPerSecond : Integer read FMaxOperationsPerSecond write FMaxOperationsPerSecond;
   end;
 
   { TFRMRandomOperations }
@@ -75,11 +77,13 @@ type
     pnlClient: TPanel;
     pnlTop: TPanel;
     pnlTop1: TPanel;
+    cbMaxSpeedMode : TCheckBox;
     procedure bbRandomOperationsClick(Sender: TObject);
     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
     procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
+    procedure cbMaxSpeedModeClick(Sender: TObject);
   private
     FSourceNode: TNode;
     FSourceWalletKeys: TWalletKeysExt;
@@ -87,6 +91,7 @@ type
     FCurrOperationsComp : TPCOperationsComp;
     FRandomGeneratorThread : TRandomGeneratorThread;
     FInternalLog : TLog;
+    FMaxOperationsPerSecond : Integer;
     procedure SetSourceNode(AValue: TNode);
     procedure SetSourceWalletKeys(AValue: TWalletKeysExt);
     procedure NewLog(logTxt : String);
@@ -104,11 +109,17 @@ type
 
   TRandomGenerateOperation = Class
   private
+    class function GetRandomPayload(Const AStartsWith : String) : TRawBytes;
   public
     class function GetRandomOwnDestination(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out nAccount : Cardinal) : Boolean;
     class function GetRandomSigner(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out iKey : Integer; out nAccount : Cardinal) : Boolean;
     class function GenerateOpTransactions(current_protocol : Word; Maxtransaction : Integer; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
     class function GenerateOpMultiOperation(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
+    class function GenerateOpChangeKey(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
+    class function GenerateOpListAccountForSale(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
+    class function GenerateOpBuyAccount(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Integer;
+    class function GetHashLock_Public : T32Bytes;
+    class function GetHashLock_Private : TRawBytes;
   end;
 
 implementation
@@ -131,14 +142,21 @@ Var nCounter, nTotalRound, iLastSend, i : Integer;
   operationsComp : TPCOperationsComp;
   ohtToAdd : TOperationsHashTree;
   errors : String;
-  nAddedOperations : Integer;
+  nAddedOperations, nMaxTransactionsValue, nExecutedSinceLastTC : Integer;
+  LLastTC : TTickCount;
 begin
   operationsComp := TPCOperationsComp.Create(Nil);
   try
     operationsComp.bank := FSourceNode.Bank;
     iLastSend := -1;
+    LLastTC := TPlatform.GetTickCount;
+    nExecutedSinceLastTC := 0;
     while (Not Terminated) do begin
-      nTotalRound := Random(100);
+      if FMaxOperationsPerSecond<=0 then begin
+        nTotalRound := Random(100);
+      end else begin
+        nTotalRound := Random(FMaxOperationsPerSecond)+1;
+      end;
       nCounter := 0;
       if FNeedSanitize then begin
         FNeedSanitize := False;
@@ -150,15 +168,25 @@ begin
         inc(nCounter);
         //
         Case Random(30) of
-          0..20 : begin
-            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpTransactions(FSourceNode.Bank.SafeBox.CurrentProtocol,100,operationsComp,FSourceWalletKeys));
+          0..10 : begin
+            if FMaxOperationsPerSecond>0 then nMaxTransactionsValue := Random(FMaxOperationsPerSecond)+1
+            else nMaxTransactionsValue := Random(200)+1;
+
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpTransactions(FSourceNode.Bank.SafeBox.CurrentProtocol,nMaxTransactionsValue,operationsComp,FSourceWalletKeys));
+          end;
+          11..15 : begin
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpChangeKey(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys));
           end;
-          21..25 : begin
+          18..22 : begin
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpListAccountForSale(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys));
+          end;
+          25..29 : begin
             If TRandomGenerateOperation.GenerateOpMultiOperation(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(FnOperationsCreated)
             else inc(FnOperationsCreatedFailed);
           end;
         end;
       end;
+
       if (Not Terminated) And (Not FNeedSanitize) And (FAllowExecute) then begin
         //
         ohtToAdd := TOperationsHashTree.Create;
@@ -184,8 +212,18 @@ begin
           ohtToAdd.Free;
         End;
         //
-        if Assigned(FOnUpdated) then FOnUpdated(Self);
+        if FLastCall_OperationsTotal>0 then begin
+          if Assigned(FOnUpdated) then FOnUpdated(Self);
+        end;
+
+        inc(nExecutedSinceLastTC,nAddedOperations);
+
+      end;
+      if (FMaxOperationsPerSecond>0) and (nExecutedSinceLastTC>=FMaxOperationsPerSecond) then begin
+        while (Not Terminated) and (FAllowExecute) and (TPlatform.GetElapsedMilliseconds(LLastTC)<1000) do sleep(5);
       end;
+      LLastTC := TPlatform.GetTickCount;
+      nExecutedSinceLastTC := 0;
       Sleep(1);
     end;
   finally
@@ -193,7 +231,7 @@ begin
   end;
 end;
 
-constructor TRandomGeneratorThread.Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt);
+constructor TRandomGeneratorThread.Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt; AMaxOperationsPerSecond : Integer);
 begin
   FSourceNode := ASourceNode;
   FSourceWalletKeys := ASourceWalletKeys;
@@ -213,6 +251,9 @@ begin
   FBankNotify.OnNewBlock:=OnBankNewBlock;
   FNeedSanitize := True;
   FAllowExecute := False;
+  if AMaxOperationsPerSecond>=0 then
+    FMaxOperationsPerSecond := AMaxOperationsPerSecond
+  else FMaxOperationsPerSecond := 0;
   inherited Create(False);
 end;
 
@@ -226,6 +267,16 @@ end;
 
 { TRandomGenerateOperation }
 
+class function TRandomGenerateOperation.GetHashLock_Private: TRawBytes;
+begin
+  Result.FromString('PRIVATE');
+end;
+
+class function TRandomGenerateOperation.GetHashLock_Public: T32Bytes;
+begin
+  Result := TBaseType.To32Bytes( TCrypto.DoSha256( GetHashLock_Private ) );
+end;
+
 class function TRandomGenerateOperation.GetRandomOwnDestination(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt; out nAccount: Cardinal): Boolean;
 var
   nRounds : Integer;
@@ -248,6 +299,21 @@ begin
   until (Result) Or (nRounds>0);
 end;
 
+class function TRandomGenerateOperation.GetRandomPayload(
+  const AStartsWith: String): TRawBytes;
+var i,j : Integer;
+begin
+  Result.FromString(AStartsWith);
+  j := Random(255);
+  if j<Length(Result) then j := Length(Result);
+
+  SetLength(Result,j);
+  for i := Length(Result) to j-1 do begin
+    Result[j] := Random(127-32)+32;
+  end;
+
+end;
+
 class function TRandomGenerateOperation.GetRandomSigner(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt; out iKey: Integer; out nAccount: Cardinal): Boolean;
 var
   bRoundsIKey, bRoundsNAccount : Boolean;
@@ -290,9 +356,10 @@ class function TRandomGenerateOperation.GenerateOpTransactions(current_protocol
 var nAccount, nAccountTarget : Cardinal;
   iKey, nRounds : Integer;
   opTx : TOpTransaction;
-  senderAcc : TAccount;
+  senderAcc, LDestAcc : TAccount;
   amount,fees : Int64;
   errors : String;
+  LPayload : TRawBytes;
 begin
   Result := 0;
   If Not GetRandomSigner(operationsComp,aWalletKeys,iKey,nAccount) then Exit;
@@ -301,11 +368,17 @@ begin
   nRounds := 0;
   while (nRounds<Maxtransaction) do begin
     senderAcc := operationsComp.SafeBoxTransaction.Account(nAccount);
+    LDestAcc := operationsComp.SafeBoxTransaction.Account(nAccountTarget);
+    if TAccountComp.IsAccountForSwap( LDestAcc.accountInfo ) then begin
+      // Special case, will swap? Will need to provide a HASHLOCK in payload
+      LPayload := GetHashLock_Private;
+    end else LPayload := Nil;
+
     amount := 1; // Minimal amount
     if (Random(500)<1) then fees := 0
     else fees := 1; // Minimal fee
     if (senderAcc.balance>2) then begin
-      opTx := TOpTransaction.CreateTransaction(current_protocol,senderAcc.account,senderAcc.n_operation+1,nAccountTarget,aWalletKeys.Key[iKey].PrivateKey,amount,fees,Nil);
+      opTx := TOpTransaction.CreateTransaction(current_protocol,senderAcc.account,senderAcc.n_operation+1,nAccountTarget,aWalletKeys.Key[iKey].PrivateKey,amount,fees,LPayload);
       Try
         if operationsComp.AddOperation(True,opTx,errors) then inc(Result);
       finally
@@ -317,6 +390,174 @@ begin
   end;
 end;
 
+class function TRandomGenerateOperation.GenerateOpBuyAccount(
+  current_protocol: Word; const operationsComp: TPCOperationsComp;
+  const aWalletKeys: TWalletKeysExt): Integer;
+begin
+  raise Exception.Create('XXXXXX TODO TRandomGenerateOperation');
+end;
+
+class function TRandomGenerateOperation.GenerateOpChangeKey(
+  current_protocol: Word;
+  const operationsComp: TPCOperationsComp;
+  const aWalletKeys: TWalletKeysExt): Integer;
+var nAccount, nAccountTarget : Cardinal;
+  iKey, iNewPubKey : Integer;
+  opCk : TOpChangeKey;
+  senderAcc : TAccount;
+  fees : Int64;
+  errors : String;
+  opClass : TPCOperationClass;
+begin
+  Result := 0;
+  If Not GetRandomSigner(operationsComp,aWalletKeys,iKey,nAccount) then Exit;
+  if Random(1)=0 then begin
+    nAccountTarget := Random( aWalletKeys.AccountsKeyList.AccountKeyList[iKey].Count );
+    opClass := TOpChangeKeySigned;
+  end else begin
+    nAccountTarget := nAccount;
+    opClass := TOpChangeKey;
+  end;
+
+  if Not GetRandomOwnDestination(operationsComp,aWalletKeys,nAccountTarget) then Exit;
+  iNewPubKey := Random(aWalletKeys.Count);
+
+  if iKey=iNewPubKey then Exit;
+
+  senderAcc := operationsComp.SafeBoxTransaction.Account(nAccount);
+  if (Random(500)<1) then fees := 0
+  else fees := 1; // Minimal fee
+  if (senderAcc.balance>2) then begin
+    opCk := TOpChangeKey(opClass.NewInstance).Create(current_protocol,senderAcc.account,senderAcc.n_operation+1,nAccountTarget,
+      aWalletKeys.Key[iKey].PrivateKey,
+      aWalletKeys.Key[iNewPubKey].AccountKey,
+      fees,Nil);
+    Try
+      if operationsComp.AddOperation(True,opCk,errors) then inc(Result);
+    finally
+      opCk.Free;
+    end;
+  end;
+
+end;
+
+class function TRandomGenerateOperation.GenerateOpListAccountForSale(
+  current_protocol: Word; const operationsComp: TPCOperationsComp;
+  const aWalletKeys: TWalletKeysExt): Integer;
+var nSigner, nTarget : Cardinal;
+  iKey, iNewPubKey, i, j : Integer;
+  opList : TOpListAccountForSaleOrSwap;
+  opDelist : TOpDelistAccountForSale;
+  SignerAccount, AuxAccount : TAccount;
+  fees : Int64;
+  errors : String;
+  DoDelist : Boolean;
+begin
+  Result := 0;
+  If Not GetRandomSigner(operationsComp,aWalletKeys,iKey,nSigner) then Exit;
+  SignerAccount := operationsComp.SafeBoxTransaction.Account(nSigner);
+
+  if (Random(500)<1) then fees := 0
+  else fees := 1; // Minimal fee
+
+  if TAccountComp.IsAccountForSaleOrSwap(SignerAccount.accountInfo) then begin
+    // Delist:
+    i := Random( aWalletKeys.AccountsKeyList.AccountKeyList[ iKey ].Count );
+    j := i;
+    DoDelist := False;
+    Repeat
+      AuxAccount := operationsComp.SafeBoxTransaction.Account(j);
+      if TAccountComp.IsAccountForSale(AuxAccount.accountInfo) then begin
+        nTarget := j;
+        DoDelist := True;
+      end;
+
+      //
+      if j<aWalletKeys.AccountsKeyList.AccountKeyList[ iKey ].Count then inc(j)
+      else j:=0;
+    Until (j=i) or (DoDelist);
+    if (DoDelist) then begin
+      try
+        opDelist := TOpDelistAccountForSale.CreateDelistAccountForSale(current_protocol,
+          nSigner,SignerAccount.n_operation+1,nTarget,fees,
+          aWalletKeys.Key[iKey].PrivateKey,
+          GetRandomPayload(''));
+        if operationsComp.AddOperation(True,opDelist,errors) then inc(Result);
+      finally
+        opDelist.Free;
+      end;
+    end;
+
+  end else begin
+
+    if Random(1)=0 then begin
+      nTarget := Random( aWalletKeys.AccountsKeyList.AccountKeyList[iKey].Count );
+    end else begin
+      nTarget := nSigner;
+    end;
+
+
+    opList := Nil;
+    try
+      case Random(4) of
+        0 : // Private sale:
+          begin
+            // Private sale:
+            opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
+              as_ForSale,
+              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
+              aWalletKeys.Key[ Random(aWalletKeys.Count) ].AccountKey,
+              operationsComp.OperationBlock.block + Random(1000),
+              aWalletKeys.Key[iKey].PrivateKey,
+              CT_HashLock_NUL,
+              GetRandomPayload(''));
+          end;
+        1 : // Publis sale:
+          begin
+            opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
+              as_ForSale,
+              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
+              CT_TECDSA_Public_Nul,
+              0,
+              aWalletKeys.Key[iKey].PrivateKey,
+              CT_HashLock_NUL,
+              GetRandomPayload(''));
+          end;
+        2 : // Atomic Account Swap
+          begin
+            opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
+              as_ForAtomicAccountSwap,
+              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
+              aWalletKeys.Key[ Random(aWalletKeys.Count) ].AccountKey,
+              operationsComp.OperationBlock.block + Random(1000),
+              aWalletKeys.Key[iKey].PrivateKey,
+              GetHashLock_Public,
+              GetRandomPayload(''));
+          end;
+        3 : // Atomic Coin Swap
+          begin
+            opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
+              as_ForAtomicCoinSwap,
+              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
+              CT_TECDSA_Public_Nul,
+              operationsComp.OperationBlock.block + Random(1000),
+              aWalletKeys.Key[iKey].PrivateKey,
+              GetHashLock_Public,
+              GetRandomPayload(''));
+          end;
+      end;
+      if operationsComp.AddOperation(True,opList,errors) then inc(Result);
+    finally
+      opList.Free;
+    end;
+
+  end;
+end;
+
 class function TRandomGenerateOperation.GenerateOpMultiOperation(current_protocol : Word; const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
    procedure DoSign(opMulti : TOpMultiOperation);
    var n : Integer;
@@ -432,6 +673,7 @@ begin
   FInternalLog := TLog.Create(Self);
   FInternalLog.ProcessGlobalLogs := False;
   FInternalLog.OnNewLog := OnInternalLog;
+  FMaxOperationsPerSecond := 0;
   mLogs.Clear;
 end;
 
@@ -443,6 +685,8 @@ begin
 end;
 
 procedure TFRMRandomOperations.bbRandomOperationsClick(Sender: TObject);
+var sValue : String;
+  n : Integer;
 begin
   {$IFDEF TESTNET}
   If IsProcessingRandomOperations then begin
@@ -450,6 +694,12 @@ begin
     bbRandomOperations.Caption:='GENERATE RANDOM';
   end else begin
     if Assigned(FRandomGeneratorThread) then begin
+      sValue := IntToStr(FMaxOperationsPerSecond);
+      if InputQuery('Max Operations per second','Max operations per second (0=Max)',sValue) then begin
+        n := StrToIntDef(sValue,-1);
+        if (n>=0) then FMaxOperationsPerSecond := n;
+      end;
+      FRandomGeneratorThread.MaxOperationsPerSecond := n;
       FRandomGeneratorThread.AllowExecute := True;
       bbRandomOperations.Caption:='STOP RANDOM';
     end else bbRandomOperations.Caption:='???';
@@ -459,6 +709,11 @@ begin
   {$ENDIF}
 end;
 
+procedure TFRMRandomOperations.cbMaxSpeedModeClick(Sender: TObject);
+begin
+//
+end;
+
 procedure TFRMRandomOperations.FormClose(Sender: TObject; var CloseAction: TCloseAction);
 begin
   CloseAction := caFree;
@@ -516,7 +771,7 @@ begin
     FreeAndNil(FRandomGeneratorThread);
   end;
   if (Not DestroyOnly) And Assigned(FSourceNode) And Assigned(FSourceWalletKeys) then begin
-    FRandomGeneratorThread := TRandomGeneratorThread.Create(FSourceNode,FSourceWalletKeys);
+    FRandomGeneratorThread := TRandomGeneratorThread.Create(FSourceNode,FSourceWalletKeys,FMaxOperationsPerSecond);
     FRandomGeneratorThread.OnUpdated:=OnRandomGeneratoThreadUpdated;
   end;
 end;

+ 34 - 1
src/gui-classic/UFRMWallet.pas

@@ -888,6 +888,7 @@ procedure TFRMWallet.FillAccountInformation(const Strings: TStrings;
   const AccountNumber: Cardinal);
 Var account : TAccount;
   s : String;
+  LjsonObj : TPCJSONObject;
 begin
   if AccountNumber<0 then exit;
   account := FNode.GetMempoolAccount(AccountNumber);
@@ -900,6 +901,10 @@ begin
   Strings.Add(Format('Updated on block: %d  (%d blocks ago)',[account.updated_block,FNode.Bank.BlocksCount-account.updated_block]));
   Strings.Add(Format('Public key type: %s',[TAccountComp.GetECInfoTxt(account.accountInfo.accountKey.EC_OpenSSL_NID)]));
   Strings.Add(Format('Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.accountKey)]));
+  if Length(account.account_data)>0 then
+    Strings.Add(Format('Account Data: %s',[account.account_data.ToHexaString]))
+  else Strings.Add(Format('Account Data: (No data)',[]));
+  Strings.Add(Format('Account Seal: %s',[account.account_seal.ToHexaString]));
   if TAccountComp.IsAccountForSale(account.accountInfo) then begin
     Strings.Add('');
     Strings.Add('** Account is for sale: **');
@@ -918,7 +923,35 @@ begin
           [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount]));
       end;
     end;
+  end else if TAccountComp.IsAccountForSwap(account.accountInfo) then begin
+    Strings.Add('');
+    if TAccountComp.IsAccountForAccountSwap(account.accountInfo) then begin
+      Strings.Add('** Account is for Atomic Account Swap: **');
+      Strings.Add(Format('New Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.new_publicKey)]));
+    end else if TAccountComp.IsAccountForCoinSwap(account.accountInfo) then begin
+      Strings.Add('** Account is for Atomic Coin Swap: **');
+      Strings.Add(Format('Amount to swap: %s',[TAccountComp.FormatMoney(account.accountInfo.price)]));
+      Strings.Add(Format('Counterparty account: %s',[TAccountComp.AccountNumberToAccountTxtNumber(account.accountInfo.account_to_pay)]));
+    end;
+    Strings.Add(Format('Public secret to find: %s',[account.accountInfo.hashed_secret.ToHexaString]));
+    Strings.Add('');
+    if TAccountComp.IsAccountLocked(account.accountInfo,FNode.Bank.BlocksCount) then begin
+      Strings.Add(Format('SWAP IS SECURE UNTIL BLOCK %d (current %d, remains %d)',
+          [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount,account.accountInfo.locked_until_block-FNode.Bank.BlocksCount]));
+    end else begin
+        Strings.Add(Format('SWAP IS NOT SECURE (Expired on block %d, current %d)',
+          [account.accountInfo.locked_until_block,FNode.Bank.BlocksCount]));
+    end;
+  end;
+  LjsonObj := TPCJSONObject.Create;
+  Try
+    TPascalCoinJSONComp.FillAccountObject(account,LjsonObj);
+    Strings.Add('ACCOUNT JSON:');
+    Strings.Add(LjsonObj.ToJSON(False));
+  Finally
+    LjsonObj.Free;
   end;
+
 end;
 
 procedure TFRMWallet.FillOperationInformation(const Strings: TStrings;
@@ -966,7 +999,7 @@ begin
   jsonObj := TPCJSONObject.Create;
   Try
     TPascalCoinJSONComp.FillOperationObject(OperationResume,FNode.Bank.BlocksCount,jsonObj);
-    Strings.Add('JSON:');
+    Strings.Add('OPERATION JSON:');
     Strings.Add(jsonObj.ToJSON(False));
   Finally
     jsonObj.Free;

+ 18 - 2
src/libraries/pascalcoin/UJSONFunctions.pas

@@ -43,6 +43,8 @@ Type
   TJSONValue = TJSONData;
   {$ENDIF}
 
+  { TPCJSONData }
+
   TPCJSONData = Class
   private
     FParent : TPCJSONData;
@@ -57,6 +59,7 @@ Type
     Function ToJSON(pretty : Boolean) : String;
     Procedure SaveToStream(Stream : TStream);
     Procedure Assign(PCJSONData : TPCJSONData);
+    class function JSONFormatSettings : TFormatSettings;
   End;
 
   TPCJSONDataClass = Class of TPCJSONData;
@@ -178,6 +181,8 @@ Type
 
 implementation
 
+var _JSON_FormatSettings : TFormatSettings;
+
 Function UTF8JSONEncode(plainTxt : String; includeSeparator : Boolean) : String;
 Var ws : String;
   i : Integer;
@@ -998,7 +1003,14 @@ begin
   inherited;
 end;
 
-class function TPCJSONData.ParseJSONValue(Const JSONObject: TBytes): TPCJSONData;
+class function TPCJSONData.JSONFormatSettings: TFormatSettings;
+begin
+  Result := _JSON_FormatSettings;
+end;
+
+
+class function TPCJSONData.ParseJSONValue(const JSONObject: TBytes
+  ): TPCJSONData;
 Var JS : TJSONValue;
   {$IFDEF FPC}
   jss : TJSONStringType;
@@ -1047,7 +1059,8 @@ begin
   Stream.Write(s[Low(s)],Length(s));
 end;
 
-class function TPCJSONData.ParseJSONValue(Const JSONObject: String): TPCJSONData;
+class function TPCJSONData.ParseJSONValue(const JSONObject: String
+  ): TPCJSONData;
 begin
   Result := ParseJSONValue( TEncoding.ASCII.GetBytes(JSONObject) );
 end;
@@ -1064,4 +1077,7 @@ end;
 
 initialization
   _objectsCount := 0;
+  _JSON_FormatSettings := FormatSettings;
+  _JSON_FormatSettings.ThousandSeparator := ',';
+  _JSON_FormatSettings.DecimalSeparator := '.';
 end.

+ 8 - 40
src/pascalcoin_wallet_classic.dproj

@@ -4,7 +4,7 @@
         <MainSource>pascalcoin_wallet_classic.dpr</MainSource>
         <Base>True</Base>
         <Config Condition="'$(Config)'==''">Debug</Config>
-        <TargetedPlatforms>1025</TargetedPlatforms>
+        <TargetedPlatforms>1027</TargetedPlatforms>
         <AppType>Application</AppType>
         <FrameworkType>VCL</FrameworkType>
         <ProjectVersion>18.6</ProjectVersion>
@@ -75,26 +75,26 @@
         <VerInfo_Locale>1033</VerInfo_Locale>
         <VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=</VerInfo_Keys>
         <DCC_UnitSearchPath>.\libraries\hashlib4pascal;.\libraries\sphere10;.\libraries\pascalcoin;.\libraries\synapse;.\core;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
+        <DCC_DcuOutput>.\lib\$(Platform)\$(Config)</DCC_DcuOutput>
     </PropertyGroup>
     <PropertyGroup Condition="'$(Base_Win32)'!=''">
         <DCC_Namespace>System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
         <BT_BuildType>Debug</BT_BuildType>
-        <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
         <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
-        <VerInfo_Locale>1033</VerInfo_Locale>
         <Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
         <AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
         <UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
         <UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
+        <Icon_MainIcon>pascalcoin_wallet_classic.ico</Icon_MainIcon>
     </PropertyGroup>
     <PropertyGroup Condition="'$(Base_Win64)'!=''">
         <UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
         <UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
         <DCC_Namespace>System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)</DCC_Namespace>
         <BT_BuildType>Debug</BT_BuildType>
-        <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
         <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
         <Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
+        <Icon_MainIcon>pascalcoin_wallet_classic.ico</Icon_MainIcon>
     </PropertyGroup>
     <PropertyGroup Condition="'$(Cfg_1)'!=''">
         <DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
@@ -109,6 +109,7 @@
     <PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
         <AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
         <AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
+        <AppEnableHighDPI>true</AppEnableHighDPI>
     </PropertyGroup>
     <PropertyGroup Condition="'$(Cfg_2)'!=''">
         <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
@@ -127,6 +128,7 @@
     <PropertyGroup Condition="'$(Cfg_2_Win64)'!=''">
         <AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
         <AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
+        <AppEnableHighDPI>true</AppEnableHighDPI>
     </PropertyGroup>
     <ItemGroup>
         <DelphiCompile Include="$(MainSource)">
@@ -254,7 +256,7 @@
             <Platforms>
                 <Platform value="iOSDevice64">True</Platform>
                 <Platform value="Win32">True</Platform>
-                <Platform value="Win64">False</Platform>
+                <Platform value="Win64">True</Platform>
             </Platforms>
             <Deployment Version="3">
                 <DeployFile LocalName="pascalcoin_wallet_classic.exe" Configuration="Debug" Class="ProjectOutput">
@@ -272,6 +274,7 @@
                         <Operation>1</Operation>
                     </Platform>
                     <Platform Name="Win32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
                         <Operation>0</Operation>
                     </Platform>
                 </DeployClass>
@@ -407,11 +410,6 @@
                         <Operation>1</Operation>
                         <Extensions>.framework</Extensions>
                     </Platform>
-                    <Platform Name="OSX64">
-                        <RemoteDir>Contents\MacOS</RemoteDir>
-                        <Operation>1</Operation>
-                        <Extensions>.framework</Extensions>
-                    </Platform>
                     <Platform Name="Win32">
                         <Operation>0</Operation>
                     </Platform>
@@ -434,11 +432,6 @@
                         <Operation>1</Operation>
                         <Extensions>.dylib</Extensions>
                     </Platform>
-                    <Platform Name="OSX64">
-                        <RemoteDir>Contents\MacOS</RemoteDir>
-                        <Operation>1</Operation>
-                        <Extensions>.dylib</Extensions>
-                    </Platform>
                     <Platform Name="Win32">
                         <Operation>0</Operation>
                         <Extensions>.dll;.bpl</Extensions>
@@ -462,11 +455,6 @@
                         <Operation>1</Operation>
                         <Extensions>.dylib</Extensions>
                     </Platform>
-                    <Platform Name="OSX64">
-                        <RemoteDir>Contents\MacOS</RemoteDir>
-                        <Operation>1</Operation>
-                        <Extensions>.dylib</Extensions>
-                    </Platform>
                     <Platform Name="Win32">
                         <Operation>0</Operation>
                         <Extensions>.bpl</Extensions>
@@ -489,10 +477,6 @@
                         <RemoteDir>Contents\Resources\StartUp\</RemoteDir>
                         <Operation>0</Operation>
                     </Platform>
-                    <Platform Name="OSX64">
-                        <RemoteDir>Contents\Resources\StartUp\</RemoteDir>
-                        <Operation>0</Operation>
-                    </Platform>
                     <Platform Name="Win32">
                         <Operation>0</Operation>
                     </Platform>
@@ -640,30 +624,18 @@
                         <RemoteDir>..\</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
-                    <Platform Name="OSX64">
-                        <RemoteDir>..\</RemoteDir>
-                        <Operation>1</Operation>
-                    </Platform>
                 </DeployClass>
                 <DeployClass Name="ProjectOSXInfoPList">
                     <Platform Name="OSX32">
                         <RemoteDir>Contents</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
-                    <Platform Name="OSX64">
-                        <RemoteDir>Contents</RemoteDir>
-                        <Operation>1</Operation>
-                    </Platform>
                 </DeployClass>
                 <DeployClass Name="ProjectOSXResource">
                     <Platform Name="OSX32">
                         <RemoteDir>Contents\Resources</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
-                    <Platform Name="OSX64">
-                        <RemoteDir>Contents\Resources</RemoteDir>
-                        <Operation>1</Operation>
-                    </Platform>
                 </DeployClass>
                 <DeployClass Required="true" Name="ProjectOutput">
                     <Platform Name="Android">
@@ -686,10 +658,6 @@
                         <RemoteDir>Contents\MacOS</RemoteDir>
                         <Operation>1</Operation>
                     </Platform>
-                    <Platform Name="OSX64">
-                        <RemoteDir>Contents\MacOS</RemoteDir>
-                        <Operation>1</Operation>
-                    </Platform>
                     <Platform Name="Win32">
                         <Operation>0</Operation>
                     </Platform>

Some files were not shown because too many files changed in this diff