Browse Source

First changes for V4

Added first source changes for V4. Testing mode only!
PascalCoin 7 years ago
parent
commit
bc297e0cbe

+ 30 - 0
README.md

@@ -34,6 +34,36 @@ Also, consider a donation at PascalCoin development account: "0-10"
 
 
 ## History:  
 ## History:  
 
 
+TODO: Bug in Lazarus optimization cause Access Violation on "getpendings" call (must be 0 or 1). techworker/ugo4brain in discord 2018-05-11 #development channel
+### Build XXXXXX - CURRENT
+- Implementation of Hard fork on block XXXXXXX (PENDING... TODO !)
+  - PIP - 0016: Layer-2 protocol support
+  - New digest hash for signature verifications
+  - Added OrderedAccountKeysList that allows an indexed search of public keys in the safebox with mem optimization
+- JSON-RPC changes:
+  - New params for "findaccounts":
+    - "exact" (Boolean, True by default): Only apply when "name" param has value, will return exact match or not
+    - "listed" (Boolean, False by default): Will return only listed for sale accounts
+    - "min_balance","max_balance" (PASCURRENCY): Filter by balance
+  - New return param in "Multioperation Object"
+    - "digest" (HEXASTRING): Returns the digest value that must be signed
+  - New param for "multioperationsignoffline"
+    - "protocol" (Integer): Must provide protocol version. By default will use build protocol constant CT_BUILD_PROTOCOL
+  - New param for "signsendto"
+    - "protocol" (Integer): Must provide protocol version. By default will use build protocol constant CT_BUILD_PROTOCOL
+  - New param for "signchangekey"
+    - "protocol" (Integer): Must provide protocol version. By default will use build protocol constant CT_BUILD_PROTOCOL
+  - New param for "signlistaccountforsale"
+    - "protocol" (Integer): Must provide protocol version. By default will use build protocol constant CT_BUILD_PROTOCOL
+  - New param for "signdelistaccountforsale"
+    - "protocol" (Integer): Must provide protocol version. By default will use build protocol constant CT_BUILD_PROTOCOL
+  - New param for "signchangeaccountinfo"
+    - "protocol" (Integer): Must provide protocol version. By default will use build protocol constant CT_BUILD_PROTOCOL
+  - New param for "signbuyaccount"
+    - "protocol" (Integer): Must provide protocol version. By default will use build protocol constant CT_BUILD_PROTOCOL
+- Bug fixed: DoProcess_GetAccount_Request request type=1 (single account) didn't returned only 1
+
+
 ### Build 3.0.1 - 2018-05-07
 ### Build 3.0.1 - 2018-05-07
 - Deprecated use of OpenSSL v1.0 versions. Only allowed OpenSSL v1.1 versions
 - Deprecated use of OpenSSL v1.0 versions. Only allowed OpenSSL v1.1 versions
 - JSON-RPC Added param "openssl" on "nodestatus" call. Will return OpenSSL library version as described in OpenSSL_version_num ( https://www.openssl.org/docs/man1.1.0/crypto/OPENSSL_VERSION_NUMBER.html )
 - JSON-RPC Added param "openssl" on "nodestatus" call. Will return OpenSSL library version as described in OpenSSL_version_num ( https://www.openssl.org/docs/man1.1.0/crypto/OPENSSL_VERSION_NUMBER.html )

+ 8 - 0
src/config.inc

@@ -34,6 +34,9 @@
   {$DEFINE PRODUCTION}
   {$DEFINE PRODUCTION}
   {.$DEFINE TESTNET}
   {.$DEFINE TESTNET}
 
 
+  // Account Key Storage is for memory reduction because uses a unified storage to store all public keys and then use only a pointer to them
+  {$DEFINE useAccountKeyStorage}
+
   // For GUI: Allows to show average time on blockchain explorer
   // For GUI: Allows to show average time on blockchain explorer
   {$DEFINE SHOW_AVERAGE_TIME_STATS}
   {$DEFINE SHOW_AVERAGE_TIME_STATS}
 
 
@@ -96,4 +99,9 @@ ERROR: You must select ONE option!
 {$IFDEF OpenSSL10}
 {$IFDEF OpenSSL10}
   ERROR: OpenSLL v1.0 is not longer valid, use OpenSSL v1.1 instead
   ERROR: OpenSLL v1.0 is not longer valid, use OpenSSL v1.1 instead
 {$ENDIF}
 {$ENDIF}
+{$IFDEF PRODUCTION}
+{$IFDEF TESTING_NO_POW_CHECK}
+  ERROR: TESTING_NO_POW_CHECK not valid on PRODUCTION MODE! Use it for testing purposes only!
+{$ENDIF}
+{$ENDIF}
 
 

+ 26 - 2
src/core/UAccountKeyStorage.pas

@@ -10,11 +10,11 @@ uses
   Classes, SysUtils, UAccounts, UThread, UBaseTypes;
   Classes, SysUtils, UAccounts, UThread, UBaseTypes;
 
 
 type
 type
-  TAccountKeyStorateData = record
+  TAccountKeyStorageData = record
     ptrAccountKey : PAccountKey;
     ptrAccountKey : PAccountKey;
     counter : Integer;
     counter : Integer;
   end;
   end;
-  PAccountKeyStorageData = ^TAccountKeyStorateData;
+  PAccountKeyStorageData = ^TAccountKeyStorageData;
 
 
   { TAccountKeyStorage }
   { TAccountKeyStorage }
 
 
@@ -28,6 +28,7 @@ type
     constructor Create;
     constructor Create;
     destructor Destroy; override;
     destructor Destroy; override;
     function AddAccountKey(Const accountKey: TAccountKey) : PAccountKey;
     function AddAccountKey(Const accountKey: TAccountKey) : PAccountKey;
+    function AddAccountKeyExt(Const accountKey: TAccountKey) : PAccountKeyStorageData;
     procedure RemoveAccountKey(Const accountKey: TAccountKey);
     procedure RemoveAccountKey(Const accountKey: TAccountKey);
     class function KS : TAccountKeyStorage;
     class function KS : TAccountKeyStorage;
     function LockList : TList;
     function LockList : TList;
@@ -124,6 +125,29 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TAccountKeyStorage.AddAccountKeyExt(const accountKey: TAccountKey): PAccountKeyStorageData;
+  // This function will return allocated pointer and will increase counter like AddAccountKey
+var l : TList;
+  i : Integer;
+begin
+  Result := Nil;
+  l := FAccountKeys.LockList;
+  try
+    If Find(l,accountKey,i) then begin
+      Result := PAccountKeyStorageData(l[i]);
+      inc(Result^.counter);
+    end else begin
+      New(Result);
+      New(Result^.ptrAccountKey);
+      Result^.counter:=1;
+      Result^.ptrAccountKey^:=accountKey;
+      l.Insert(i,Result);
+    end;
+  finally
+    FAccountKeys.UnlockList;
+  end;
+end;
+
 procedure TAccountKeyStorage.RemoveAccountKey(const accountKey: TAccountKey);
 procedure TAccountKeyStorage.RemoveAccountKey(const accountKey: TAccountKey);
 var l : TList;
 var l : TList;
   i : Integer;
   i : Integer;

+ 250 - 33
src/core/UAccounts.pas

@@ -228,6 +228,7 @@ Type
     function Lock : TList;
     function Lock : TList;
     procedure Unlock;
     procedure Unlock;
     function HasAccountKeyChanged : Boolean;
     function HasAccountKeyChanged : Boolean;
+    procedure CopyFrom(const source : TOrderedAccountKeysList);
   End;
   End;
 
 
   // Maintans a Cardinal ordered (without duplicates) list with TRawData each
   // Maintans a Cardinal ordered (without duplicates) list with TRawData each
@@ -283,6 +284,8 @@ Type
   TOrderedAccountList = Class;
   TOrderedAccountList = Class;
   TOrderedBlockAccountList = Class;
   TOrderedBlockAccountList = Class;
 
 
+  TProgressNotify = procedure(sender : TObject; const message : AnsiString; curPos, totalCount : Int64) of object;
+
   { TPCSafeBox }
   { TPCSafeBox }
 
 
   TAccountUpdateStyle = (aus_transaction_commit, aus_rollback, aus_commiting_from_otherchain);
   TAccountUpdateStyle = (aus_transaction_commit, aus_rollback, aus_commiting_from_otherchain);
@@ -292,6 +295,9 @@ Type
     FBlockAccountsList : TList; // Used when has no PreviousSafebox
     FBlockAccountsList : TList; // Used when has no PreviousSafebox
     FModifiedBlocksSeparatedChain : TOrderedBlockAccountList; // Used when has PreviousSafebox (Used if we are on a Separated chain)
     FModifiedBlocksSeparatedChain : TOrderedBlockAccountList; // Used when has PreviousSafebox (Used if we are on a Separated chain)
     //
     //
+    // OrderedAccountKeysList (Added after Build 3.0.1) allows an indexed search of public keys in the safebox with mem optimization
+    FOrderedAccountKeysList : TOrderedAccountKeysList;
+    //
     FListOfOrderedAccountKeysList : TList;
     FListOfOrderedAccountKeysList : TList;
     FBufferBlocksHash: TRawBytes;
     FBufferBlocksHash: TRawBytes;
     FOrderedByName : TOrderedRawList;
     FOrderedByName : TOrderedRawList;
@@ -325,6 +331,7 @@ Type
     Function AddNew(Const blockChain : TOperationBlock) : TBlockAccount;
     Function AddNew(Const blockChain : TOperationBlock) : TBlockAccount;
     function DoUpgradeToProtocol2 : Boolean;
     function DoUpgradeToProtocol2 : Boolean;
     function DoUpgradeToProtocol3 : Boolean;
     function DoUpgradeToProtocol3 : Boolean;
+    function DoUpgradeToProtocol4 : Boolean;
   public
   public
     Constructor Create;
     Constructor Create;
     Destructor Destroy; override;
     Destructor Destroy; override;
@@ -336,7 +343,8 @@ Type
     Procedure CopyFrom(accounts : TPCSafeBox);
     Procedure CopyFrom(accounts : TPCSafeBox);
     Class Function CalcBlockHash(const block : TBlockAccount; useProtocol2Method : Boolean):TRawBytes;
     Class Function CalcBlockHash(const block : TBlockAccount; useProtocol2Method : Boolean):TRawBytes;
     Class Function BlockAccountToText(Const block : TBlockAccount):AnsiString;
     Class Function BlockAccountToText(Const block : TBlockAccount):AnsiString;
-    Function LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; var LastReadBlock : TBlockAccount; var errors : AnsiString) : Boolean;
+    Function LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; var LastReadBlock : TBlockAccount; var errors : AnsiString) : Boolean; overload;
+    Function LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; progressNotify : TProgressNotify; var LastReadBlock : TBlockAccount; var errors : AnsiString) : Boolean; overload;
     Class Function LoadSafeBoxStreamHeader(Stream : TStream; var sbHeader : TPCSafeBoxHeader) : Boolean;
     Class Function LoadSafeBoxStreamHeader(Stream : TStream; var sbHeader : TPCSafeBoxHeader) : Boolean;
     Class Function SaveSafeBoxStreamHeader(Stream : TStream; protocol : Word; OffsetStartBlock, OffsetEndBlock, CurrentSafeBoxBlocksCount : Cardinal) : Boolean;
     Class Function SaveSafeBoxStreamHeader(Stream : TStream; protocol : Word; OffsetStartBlock, OffsetEndBlock, CurrentSafeBoxBlocksCount : Cardinal) : Boolean;
     Class Function MustSafeBoxBeSaved(BlocksCount : Cardinal) : Boolean;
     Class Function MustSafeBoxBeSaved(BlocksCount : Cardinal) : Boolean;
@@ -368,6 +376,7 @@ Type
     Property PreviousSafeboxOriginBlock : Integer Read FPreviousSafeboxOriginBlock;
     Property PreviousSafeboxOriginBlock : Integer Read FPreviousSafeboxOriginBlock;
     Function GetMinimumAvailableSnapshotBlock : Integer;
     Function GetMinimumAvailableSnapshotBlock : Integer;
     Function HasSnapshotForBlock(block_number : Cardinal) : Boolean;
     Function HasSnapshotForBlock(block_number : Cardinal) : Boolean;
+    Property OrderedAccountKeysList : TOrderedAccountKeysList read FOrderedAccountKeysList;
   End;
   End;
 
 
 
 
@@ -452,7 +461,7 @@ Type
   public
   public
     Constructor Create(SafeBox : TPCSafeBox);
     Constructor Create(SafeBox : TPCSafeBox);
     Destructor Destroy; override;
     Destructor Destroy; override;
-    Function TransferAmount(previous : TAccountPreviousBlockInfo; sender,target : Cardinal; n_operation : Cardinal; amount, fee : UInt64; var errors : AnsiString) : Boolean;
+    Function TransferAmount(previous : TAccountPreviousBlockInfo; sender,signer,target : Cardinal; n_operation : Cardinal; amount, fee : UInt64; var errors : AnsiString) : Boolean;
     Function TransferAmounts(previous : TAccountPreviousBlockInfo; const senders, n_operations : Array of Cardinal; const sender_amounts : Array of UInt64; const receivers : Array of Cardinal; const receivers_amounts : Array of UInt64; var errors : AnsiString) : Boolean;
     Function TransferAmounts(previous : TAccountPreviousBlockInfo; const senders, n_operations : Array of Cardinal; const sender_amounts : Array of UInt64; const receivers : Array of Cardinal; const receivers_amounts : Array of UInt64; var errors : AnsiString) : Boolean;
     Function UpdateAccountInfo(previous : TAccountPreviousBlockInfo; signer_account, signer_n_operation, target_account: Cardinal; accountInfo: TAccountInfo; newName : TRawBytes; newType : Word; fee: UInt64; var errors : AnsiString) : Boolean;
     Function UpdateAccountInfo(previous : TAccountPreviousBlockInfo; signer_account, signer_n_operation, target_account: Cardinal; accountInfo: TAccountInfo; newName : TRawBytes; newType : Word; fee: UInt64; var errors : AnsiString) : Boolean;
     Function BuyAccount(previous : TAccountPreviousBlockInfo; buyer,account_to_buy,seller: Cardinal; n_operation : Cardinal; amount, account_price, fee : UInt64; const new_account_key : TAccountKey; var errors : AnsiString) : Boolean;
     Function BuyAccount(previous : TAccountPreviousBlockInfo; buyer,account_to_buy,seller: Cardinal; n_operation : Cardinal; amount, account_price, fee : UInt64; const new_account_key : TAccountKey; var errors : AnsiString) : Boolean;
@@ -1881,6 +1890,9 @@ end;
 procedure TPCSafeBox.AccountKeyListAddAccounts(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 procedure TPCSafeBox.AccountKeyListAddAccounts(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 Var i : Integer;
 Var i : Integer;
 begin
 begin
+  If Assigned(FOrderedAccountKeysList) then begin
+    FOrderedAccountKeysList.AddAccounts(AccountKey,accounts);
+  end;
   for i := 0 to FListOfOrderedAccountKeysList.count-1 do begin
   for i := 0 to FListOfOrderedAccountKeysList.count-1 do begin
     TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).AddAccounts(AccountKey,accounts);
     TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).AddAccounts(AccountKey,accounts);
   end;
   end;
@@ -1889,6 +1901,9 @@ end;
 procedure TPCSafeBox.AccountKeyListRemoveAccount(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 procedure TPCSafeBox.AccountKeyListRemoveAccount(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 Var i : Integer;
 Var i : Integer;
 begin
 begin
+  If Assigned(FOrderedAccountKeysList) then begin
+    FOrderedAccountKeysList.RemoveAccounts(AccountKey,accounts);
+  end;
   for i := 0 to FListOfOrderedAccountKeysList.count-1 do begin
   for i := 0 to FListOfOrderedAccountKeysList.count-1 do begin
     TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).RemoveAccounts(AccountKey,accounts);
     TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).RemoveAccounts(AccountKey,accounts);
   end;
   end;
@@ -2053,6 +2068,8 @@ begin
     Result := (FCurrentProtocol<CT_PROTOCOL_2) and (BlocksCount >= CT_Protocol_Upgrade_v2_MinBlock);
     Result := (FCurrentProtocol<CT_PROTOCOL_2) and (BlocksCount >= CT_Protocol_Upgrade_v2_MinBlock);
   end else if (newProtocolVersion=CT_PROTOCOL_3) then begin
   end else if (newProtocolVersion=CT_PROTOCOL_3) then begin
     Result := (FCurrentProtocol=CT_PROTOCOL_2) And (BlocksCount >= CT_Protocol_Upgrade_v3_MinBlock);
     Result := (FCurrentProtocol=CT_PROTOCOL_2) And (BlocksCount >= CT_Protocol_Upgrade_v3_MinBlock);
+  end else if (newProtocolVersion=CT_PROTOCOL_4) then begin
+    Result := (FCurrentProtocol=CT_PROTOCOL_3) And (BlocksCount >= CT_Protocol_Upgrade_v4_MinBlock);
   end else Result := False;
   end else Result := False;
 end;
 end;
 
 
@@ -2141,6 +2158,10 @@ Var i : Integer;
 begin
 begin
   StartThreadSafe;
   StartThreadSafe;
   Try
   Try
+    If Assigned(FOrderedAccountKeysList) then begin
+      FOrderedAccountKeysList.Clear;
+    end;
+
     for i := 0 to FBlockAccountsList.Count - 1 do begin
     for i := 0 to FBlockAccountsList.Count - 1 do begin
       P := FBlockAccountsList.Items[i];
       P := FBlockAccountsList.Items[i];
       Dispose(P);
       Dispose(P);
@@ -2181,6 +2202,7 @@ procedure TPCSafeBox.CopyFrom(accounts: TPCSafeBox);
 Var i,j : Cardinal;
 Var i,j : Cardinal;
   P : PBlockAccount;
   P : PBlockAccount;
   BA : TBlockAccount;
   BA : TBlockAccount;
+  lastOAKL : TOrderedAccountKeysList;
 begin
 begin
   StartThreadSafe;
   StartThreadSafe;
   Try
   Try
@@ -2195,6 +2217,10 @@ begin
       end;
       end;
       Clear;
       Clear;
       if accounts.BlocksCount>0 then begin
       if accounts.BlocksCount>0 then begin
+        If Assigned(FOrderedAccountKeysList) And Assigned(accounts.FOrderedAccountKeysList) then begin
+          lastOAKL := FOrderedAccountKeysList;
+          FOrderedAccountKeysList:=Nil;
+        end else lastOAKL := Nil;
         FBlockAccountsList.Capacity:=accounts.BlocksCount;
         FBlockAccountsList.Capacity:=accounts.BlocksCount;
         for i := 0 to accounts.BlocksCount - 1 do begin
         for i := 0 to accounts.BlocksCount - 1 do begin
           BA := accounts.Block(i);
           BA := accounts.Block(i);
@@ -2206,6 +2232,10 @@ begin
             AccountKeyListAddAccounts(BA.accounts[j].accountInfo.accountKey,[BA.accounts[j].account]);
             AccountKeyListAddAccounts(BA.accounts[j].accountInfo.accountKey,[BA.accounts[j].account]);
           end;
           end;
         end;
         end;
+        If Assigned(lastOAKL) then begin
+          lastOAKL.CopyFrom(accounts.FOrderedAccountKeysList);
+          FOrderedAccountKeysList:=lastOAKL;
+        end;
       end;
       end;
       FTotalBalance := accounts.TotalBalance;
       FTotalBalance := accounts.TotalBalance;
       FTotalFee := accounts.FTotalFee;
       FTotalFee := accounts.FTotalFee;
@@ -2238,6 +2268,7 @@ begin
   FAddedNamesSincePreviousSafebox := TOrderedRawList.Create;
   FAddedNamesSincePreviousSafebox := TOrderedRawList.Create;
   FDeletedNamesSincePreviousSafebox := TOrderedRawList.Create;
   FDeletedNamesSincePreviousSafebox := TOrderedRawList.Create;
   FSubChains := TList.Create;
   FSubChains := TList.Create;
+  FOrderedAccountKeysList := TOrderedAccountKeysList.Create(Nil,True);
   Clear;
   Clear;
 end;
 end;
 
 
@@ -2259,6 +2290,7 @@ begin
   FreeAndNil(FAddedNamesSincePreviousSafebox);
   FreeAndNil(FAddedNamesSincePreviousSafebox);
   FreeAndNil(FDeletedNamesSincePreviousSafebox);
   FreeAndNil(FDeletedNamesSincePreviousSafebox);
   FreeAndNil(FSubChains);
   FreeAndNil(FSubChains);
+  FreeAndNil(FOrderedAccountKeysList);
   If Assigned(FPreviousSafeBox) then begin
   If Assigned(FPreviousSafeBox) then begin
     FPreviousSafeBox.FSubChains.Remove(Self); // Remove from current snapshot
     FPreviousSafeBox.FSubChains.Remove(Self); // Remove from current snapshot
     FPreviousSafeBox := Nil;
     FPreviousSafeBox := Nil;
@@ -2609,12 +2641,19 @@ begin
   TLog.NewLog(ltInfo,ClassName,'End Upgraded to protocol 3 - New safeboxhash:'+TCrypto.ToHexaString(FSafeBoxHash));
   TLog.NewLog(ltInfo,ClassName,'End Upgraded to protocol 3 - New safeboxhash:'+TCrypto.ToHexaString(FSafeBoxHash));
 end;
 end;
 
 
+function TPCSafeBox.DoUpgradeToProtocol4: Boolean;
+begin
+  FCurrentProtocol := CT_PROTOCOL_4;
+  Result := True;
+  TLog.NewLog(ltInfo,ClassName,'End Upgraded to protocol 4 - New safeboxhash:'+TCrypto.ToHexaString(FSafeBoxHash));
+end;
+
 procedure TPCSafeBox.EndThreadSave;
 procedure TPCSafeBox.EndThreadSave;
 begin
 begin
   FLock.Release;
   FLock.Release;
 end;
 end;
 
 
-function TPCSafeBox.LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; var LastReadBlock : TBlockAccount; var errors : AnsiString) : Boolean;
+function TPCSafeBox.LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; progressNotify : TProgressNotify; var LastReadBlock : TBlockAccount; var errors : AnsiString) : Boolean;
 Var
 Var
   iblock,iacc : Cardinal;
   iblock,iacc : Cardinal;
   s : AnsiString;
   s : AnsiString;
@@ -2641,12 +2680,17 @@ begin
         CT_PROTOCOL_1 : FCurrentProtocol := 1;
         CT_PROTOCOL_1 : FCurrentProtocol := 1;
         CT_PROTOCOL_2 : FCurrentProtocol := 2;
         CT_PROTOCOL_2 : FCurrentProtocol := 2;
         CT_PROTOCOL_3 : FCurrentProtocol := 3;
         CT_PROTOCOL_3 : FCurrentProtocol := 3;
+        CT_PROTOCOL_4 : FCurrentProtocol := 4;
       else exit;
       else exit;
       end;
       end;
       if (sbHeader.blocksCount=0) Or (sbHeader.startBlock<>0) Or (sbHeader.endBlock<>(sbHeader.blocksCount-1)) then begin
       if (sbHeader.blocksCount=0) Or (sbHeader.startBlock<>0) Or (sbHeader.endBlock<>(sbHeader.blocksCount-1)) then begin
         errors := Format('Safebox Stream contains blocks from %d to %d (of %d blocks). Not valid',[sbHeader.startBlock,sbHeader.endBlock,sbHeader.blocksCount]);
         errors := Format('Safebox Stream contains blocks from %d to %d (of %d blocks). Not valid',[sbHeader.startBlock,sbHeader.endBlock,sbHeader.blocksCount]);
         exit;
         exit;
       end;
       end;
+      if Assigned(progressNotify) then begin
+        progressNotify(Self,Format('Start reading Safebox from blocks %d to %d (total %d blocks)',[sbHeader.startBlock,sbHeader.endBlock,sbHeader.blocksCount]),0,sbHeader.endBlock-sbHeader.startBlock+1);
+      end;
+
       // Offset zone
       // Offset zone
       posOffsetZone := Stream.Position;
       posOffsetZone := Stream.Position;
       If checkAll then begin
       If checkAll then begin
@@ -2662,6 +2706,9 @@ begin
       SetLength(FBufferBlocksHash,sbHeader.blocksCount*32); // Initialize for high speed reading
       SetLength(FBufferBlocksHash,sbHeader.blocksCount*32); // Initialize for high speed reading
       errors := 'Corrupted stream';
       errors := 'Corrupted stream';
       for iblock := 0 to sbHeader.blockscount-1 do begin
       for iblock := 0 to sbHeader.blockscount-1 do begin
+        if (Assigned(progressNotify)) and ((iblock MOD 1000)=0) then begin
+          progressNotify(Self,Format('Reading Safebox block %d/%d',[iblock+1,sbHeader.endBlock-sbHeader.startBlock+1]),iblock,sbHeader.endBlock-sbHeader.startBlock+1);
+        end;
         errors := 'Corrupted stream reading block blockchain '+inttostr(iblock+1)+'/'+inttostr(sbHeader.blockscount);
         errors := 'Corrupted stream reading block blockchain '+inttostr(iblock+1)+'/'+inttostr(sbHeader.blockscount);
         if (checkAll) then begin
         if (checkAll) then begin
           If (offsets[iblock]<>Stream.Position-posOffsetZone) then begin
           If (offsets[iblock]<>Stream.Position-posOffsetZone) then begin
@@ -2749,6 +2796,9 @@ begin
         LastReadBlock := block;
         LastReadBlock := block;
         Inc(FWorkSum,block.blockchainInfo.compact_target);
         Inc(FWorkSum,block.blockchainInfo.compact_target);
       end;
       end;
+      if (Assigned(progressNotify)) then begin
+        progressNotify(Self,'Checking Safebox integrity',sbHeader.blocksCount,sbHeader.blocksCount);
+      end;
       If checkAll then begin
       If checkAll then begin
         If (offsets[sbHeader.blockscount]<>0) And (offsets[sbHeader.blockscount]<>Stream.Position-posOffsetZone) then begin
         If (offsets[sbHeader.blockscount]<>0) And (offsets[sbHeader.blockscount]<>Stream.Position-posOffsetZone) then begin
           errors := errors + Format(' - Final offset[%d]=%d <> Eof Position:%d offset:%d',[sbHeader.blockscount,offsets[sbHeader.blockscount],Stream.Position-posOffsetZone,posOffsetZone]);
           errors := errors + Format(' - Final offset[%d]=%d <> Eof Position:%d offset:%d',[sbHeader.blockscount,offsets[sbHeader.blockscount],Stream.Position-posOffsetZone,posOffsetZone]);
@@ -2783,6 +2833,13 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TPCSafeBox.LoadSafeBoxFromStream(Stream: TStream; checkAll: Boolean; var LastReadBlock: TBlockAccount; var errors: AnsiString): Boolean;
+var pn : TProgressNotify;
+begin
+  pn := Nil;
+  Result := LoadSafeBoxFromStream(Stream,checkAll,pn,LastReadBlock,errors);
+end;
+
 class function TPCSafeBox.LoadSafeBoxStreamHeader(Stream: TStream; var sbHeader : TPCSafeBoxHeader) : Boolean;
 class function TPCSafeBox.LoadSafeBoxStreamHeader(Stream: TStream; var sbHeader : TPCSafeBoxHeader) : Boolean;
   // This function reads SafeBox stream info and sets position at offset start zone if valid, otherwise sets position to actual position
   // This function reads SafeBox stream info and sets position at offset start zone if valid, otherwise sets position to actual position
 Var w : Word;
 Var w : Word;
@@ -2799,7 +2856,7 @@ begin
     if (s<>CT_MagicIdentificator) then exit;
     if (s<>CT_MagicIdentificator) then exit;
     if Stream.Size<8 then exit;
     if Stream.Size<8 then exit;
     Stream.Read(w,SizeOf(w));
     Stream.Read(w,SizeOf(w));
-    if not (w in [CT_PROTOCOL_1,CT_PROTOCOL_2,CT_PROTOCOL_3]) then exit;
+    if not (w in [CT_PROTOCOL_1,CT_PROTOCOL_2,CT_PROTOCOL_3,CT_PROTOCOL_4]) then exit;
     sbHeader.protocol := w;
     sbHeader.protocol := w;
     Stream.Read(safeBoxBankVersion,2);
     Stream.Read(safeBoxBankVersion,2);
     if safeBoxBankVersion<>CT_SafeBoxBankVersion then exit;
     if safeBoxBankVersion<>CT_SafeBoxBankVersion then exit;
@@ -3179,7 +3236,12 @@ begin
         errors := 'Invalid PascalCoin protocol version: '+IntToStr( newOperationBlock.protocol_version )+' Current: '+IntToStr(CurrentProtocol)+' Previous:'+IntToStr(lastBlock.protocol_version);
         errors := 'Invalid PascalCoin protocol version: '+IntToStr( newOperationBlock.protocol_version )+' Current: '+IntToStr(CurrentProtocol)+' Previous:'+IntToStr(lastBlock.protocol_version);
         exit;
         exit;
       end;
       end;
-      If (newOperationBlock.protocol_version=CT_PROTOCOL_3) then begin
+      If (newOperationBlock.protocol_version=CT_PROTOCOL_4) then begin
+        If (newOperationBlock.block<CT_Protocol_Upgrade_v4_MinBlock) then begin
+          errors := 'Upgrade to protocol version 4 available at block: '+IntToStr(CT_Protocol_Upgrade_v4_MinBlock);
+          exit;
+        end;
+      end else If (newOperationBlock.protocol_version=CT_PROTOCOL_3) then begin
         If (newOperationBlock.block<CT_Protocol_Upgrade_v3_MinBlock) then begin
         If (newOperationBlock.block<CT_Protocol_Upgrade_v3_MinBlock) then begin
           errors := 'Upgrade to protocol version 3 available at block: '+IntToStr(CT_Protocol_Upgrade_v3_MinBlock);
           errors := 'Upgrade to protocol version 3 available at block: '+IntToStr(CT_Protocol_Upgrade_v3_MinBlock);
           exit;
           exit;
@@ -3248,7 +3310,7 @@ begin
     exit;
     exit;
   end;
   end;
   // Valid protocol version
   // Valid protocol version
-  if (Not (newOperationBlock.protocol_version in [CT_PROTOCOL_1,CT_PROTOCOL_2,CT_PROTOCOL_3])) then begin
+  if (Not (newOperationBlock.protocol_version in [CT_PROTOCOL_1,CT_PROTOCOL_2,CT_PROTOCOL_3,CT_PROTOCOL_4])) then begin
     errors := 'Invalid protocol version '+IntToStr(newOperationBlock.protocol_version);
     errors := 'Invalid protocol version '+IntToStr(newOperationBlock.protocol_version);
     exit;
     exit;
   end;
   end;
@@ -3309,7 +3371,7 @@ begin
     tsReal := (ts1 - ts2);
     tsReal := (ts1 - ts2);
     If (protocolVersion=CT_PROTOCOL_1) then begin
     If (protocolVersion=CT_PROTOCOL_1) then begin
       Result := TPascalCoinProtocol.GetNewTarget(tsTeorical, tsReal,protocolVersion,False,TPascalCoinProtocol.TargetFromCompact(lastBlock.compact_target));
       Result := TPascalCoinProtocol.GetNewTarget(tsTeorical, tsReal,protocolVersion,False,TPascalCoinProtocol.TargetFromCompact(lastBlock.compact_target));
-    end else if (protocolVersion<=CT_PROTOCOL_3) then begin
+    end else if (protocolVersion<=CT_PROTOCOL_4) then begin
       CalcBack := CalcBack DIV CT_CalcNewTargetLimitChange_SPLIT;
       CalcBack := CalcBack DIV CT_CalcNewTargetLimitChange_SPLIT;
       If CalcBack=0 then CalcBack := 1;
       If CalcBack=0 then CalcBack := 1;
       ts2 := Block(BlocksCount-CalcBack-1).blockchainInfo.timestamp;
       ts2 := Block(BlocksCount-CalcBack-1).blockchainInfo.timestamp;
@@ -3766,6 +3828,15 @@ begin
         end;
         end;
       end;
       end;
     end;
     end;
+    if (FFreezedAccounts.FCurrentProtocol<CT_PROTOCOL_4) And (operationBlock.protocol_version=CT_PROTOCOL_4) then begin
+      // First block with V4 protocol
+      if FFreezedAccounts.CanUpgradeToProtocol(CT_PROTOCOL_4) then begin
+        TLog.NewLog(ltInfo,ClassName,'Protocol upgrade to v4');
+        If not FFreezedAccounts.DoUpgradeToProtocol4 then begin
+          raise Exception.Create('Cannot upgrade to protocol v4 !');
+        end;
+      end;
+    end;
     Result := true;
     Result := true;
   finally
   finally
     FFreezedAccounts.EndThreadSave;
     FFreezedAccounts.EndThreadSave;
@@ -3888,11 +3959,10 @@ begin
   CleanTransaction;
   CleanTransaction;
 end;
 end;
 
 
-function TPCSafeBoxTransaction.TransferAmount(previous : TAccountPreviousBlockInfo; sender, target: Cardinal;
+function TPCSafeBoxTransaction.TransferAmount(previous : TAccountPreviousBlockInfo; sender,signer,target: Cardinal;
   n_operation: Cardinal; amount, fee: UInt64; var errors: AnsiString): Boolean;
   n_operation: Cardinal; amount, fee: UInt64; var errors: AnsiString): Boolean;
 Var
 Var
-  intSender, intTarget : Integer;
-  PaccSender, PaccTarget : PAccount;
+  PaccSender, PaccTarget,PaccSigner : PAccount;
 begin
 begin
   Result := false;
   Result := false;
   errors := '';
   errors := '';
@@ -3901,8 +3971,9 @@ begin
     exit;
     exit;
   end;
   end;
   if (sender<0) Or (sender>=(Origin_BlocksCount*CT_AccountsPerBlock)) Or
   if (sender<0) Or (sender>=(Origin_BlocksCount*CT_AccountsPerBlock)) Or
+     (signer<0) Or (signer>=(Origin_BlocksCount*CT_AccountsPerBlock)) Or
      (target<0) Or (target>=(Origin_BlocksCount*CT_AccountsPerBlock)) then begin
      (target<0) Or (target>=(Origin_BlocksCount*CT_AccountsPerBlock)) then begin
-     errors := 'Invalid sender or target on transfer';
+     errors := 'Invalid sender, signer or target on transfer';
      exit;
      exit;
   end;
   end;
   if TAccountComp.IsAccountBlockedByProtocol(sender,Origin_BlocksCount) then begin
   if TAccountComp.IsAccountBlockedByProtocol(sender,Origin_BlocksCount) then begin
@@ -3915,13 +3986,35 @@ begin
   end;
   end;
   PaccSender := GetInternalAccount(sender);
   PaccSender := GetInternalAccount(sender);
   PaccTarget := GetInternalAccount(target);
   PaccTarget := GetInternalAccount(target);
-  if (PaccSender^.n_operation+1<>n_operation) then begin
-    errors := 'Incorrect n_operation';
-    Exit;
-  end;
-  if (PaccSender^.balance < (amount+fee)) then begin
-    errors := 'Insuficient founds';
-    Exit;
+
+  if (sender=signer) then begin
+    if (PaccSender^.n_operation+1<>n_operation) then begin
+      errors := 'Incorrect sender n_operation';
+      Exit;
+    end;
+    if (PaccSender^.balance < (amount+fee)) then begin
+      errors := 'Insuficient funds';
+      Exit;
+    end;
+    PaccSigner := Nil;
+  end else begin
+    PaccSigner := GetInternalAccount(signer);
+    if (PaccSigner^.n_operation+1<>n_operation) then begin
+      errors := 'Incorrect signer n_operation';
+      Exit;
+    end;
+    if (PaccSender^.balance < (amount)) then begin
+      errors := 'Insuficient sender funds';
+      Exit;
+    end;
+    if (PaccSigner^.balance < (fee)) then begin
+      errors := 'Insuficient signer funds';
+      Exit;
+    end;
+    if (TAccountComp.IsAccountLocked(PaccSigner^.accountInfo,Origin_BlocksCount)) then begin
+      errors := 'Signer account is locked until block '+Inttostr(PaccSigner^.accountInfo.locked_until_block);
+      Exit;
+    end;
   end;
   end;
   if ((PaccTarget^.balance + amount)>CT_MaxWalletAmount) then begin
   if ((PaccTarget^.balance + amount)>CT_MaxWalletAmount) then begin
     errors := 'Max account balance';
     errors := 'Max account balance';
@@ -3949,8 +4042,19 @@ begin
     PaccTarget^.updated_block := Origin_BlocksCount;
     PaccTarget^.updated_block := Origin_BlocksCount;
   end;
   end;
 
 
-  PaccSender^.n_operation := n_operation;
-  PaccSender^.balance := PaccSender^.balance - (amount + fee);
+  if (sender<>signer) then begin
+    previous.UpdateIfLower(PaccSigner^.account,PaccSigner^.updated_block);
+    if (PaccSigner^.updated_block<>Origin_BlocksCount) then begin
+      PaccSigner^.previous_updated_block := PaccSigner^.updated_block;
+      PaccSigner^.updated_block := Origin_BlocksCount;
+    end;
+    PaccSigner^.n_operation := n_operation;
+    PaccSigner^.balance := PaccSender^.balance - (fee);
+    PaccSender^.balance := PaccSender^.balance - (amount);
+  end else begin
+    PaccSender^.n_operation := n_operation;
+    PaccSender^.balance := PaccSender^.balance - (amount + fee);
+  end;
   PaccTarget^.balance := PaccTarget^.balance + (amount);
   PaccTarget^.balance := PaccTarget^.balance + (amount);
 
 
   Dec(FTotalBalance,fee);
   Dec(FTotalBalance,fee);
@@ -4349,13 +4453,17 @@ end;
 { TOrderedAccountKeysList }
 { TOrderedAccountKeysList }
 Type
 Type
   TOrderedAccountKeyList = Record
   TOrderedAccountKeyList = Record
+    {$IFDEF useAccountKeyStorage}
+    accountKeyPtr : PAccountKey;
+    {$ELSE}
     rawaccountkey : TRawBytes;
     rawaccountkey : TRawBytes;
+    {$ENDIF}
     accounts_number : TOrderedCardinalList;
     accounts_number : TOrderedCardinalList;
     changes_counter : Integer;
     changes_counter : Integer;
   end;
   end;
   POrderedAccountKeyList = ^TOrderedAccountKeyList;
   POrderedAccountKeyList = ^TOrderedAccountKeyList;
 Const
 Const
-  CT_TOrderedAccountKeyList_NUL : TOrderedAccountKeyList = (rawaccountkey:'';accounts_number:Nil;changes_counter:0);
+  CT_TOrderedAccountKeyList_NUL : TOrderedAccountKeyList = ({$IFDEF useAccountKeyStorage}accountKeyPtr:Nil{$ELSE}rawaccountkey:''{$ENDIF};accounts_number:Nil;changes_counter:0);
 
 
 function SortOrdered(Item1, Item2: Pointer): Integer;
 function SortOrdered(Item1, Item2: Pointer): Integer;
 begin
 begin
@@ -4365,14 +4473,18 @@ end;
 procedure TOrderedAccountKeysList.AddAccountKey(const AccountKey: TAccountKey);
 procedure TOrderedAccountKeysList.AddAccountKey(const AccountKey: TAccountKey);
 Var P : POrderedAccountKeyList;
 Var P : POrderedAccountKeyList;
   i,j : Integer;
   i,j : Integer;
-  lockedList : TList;
+  lockedList, safeboxLockedList : TList;
 begin
 begin
   lockedList := Lock;
   lockedList := Lock;
   Try
   Try
     if Not Find(lockedList,AccountKey,i) then begin
     if Not Find(lockedList,AccountKey,i) then begin
       New(P);
       New(P);
       P^ := CT_TOrderedAccountKeyList_NUL;
       P^ := CT_TOrderedAccountKeyList_NUL;
+      {$IFDEF useAccountKeyStorage}
+      P^.accountKeyPtr:=TAccountKeyStorage.KS.AddAccountKey(AccountKey);
+      {$ELSE}
       P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
       P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
+      {$ENDIF}
       P^.accounts_number := TOrderedCardinalList.Create;
       P^.accounts_number := TOrderedCardinalList.Create;
       inc(P^.changes_counter);
       inc(P^.changes_counter);
       inc(FTotalChanges);
       inc(FTotalChanges);
@@ -4380,13 +4492,25 @@ begin
       // Search this key in the AccountsList and add all...
       // Search this key in the AccountsList and add all...
       j := 0;
       j := 0;
       if Assigned(FAccountList) then begin
       if Assigned(FAccountList) then begin
-        For i:=0 to FAccountList.AccountsCount-1 do begin
-          If TAccountComp.EqualAccountKeys(FAccountList.Account(i).accountInfo.accountkey,AccountKey) then begin
-            // Note: P^.accounts will be ascending ordered due to "for i:=0 to ..."
-            P^.accounts_number.Add(i);
+        If (Assigned(FAccountList.OrderedAccountKeysList)) And (FAccountList.OrderedAccountKeysList<>Self) then begin
+          safeboxLockedList := FAccountList.OrderedAccountKeysList.Lock;
+          try
+            i := FAccountList.OrderedAccountKeysList.IndexOfAccountKey(AccountKey);
+            if (i>=0) then begin
+              P^.accounts_number.CopyFrom( FAccountList.OrderedAccountKeysList.AccountKeyList[i] );
+            end;
+          finally
+            FAccountList.OrderedAccountKeysList.Unlock;
+          end;
+        end else begin
+          For i:=0 to FAccountList.AccountsCount-1 do begin
+            If TAccountComp.EqualAccountKeys(FAccountList.Account(i).accountInfo.accountkey,AccountKey) then begin
+              // Note: P^.accounts will be ascending ordered due to "for i:=0 to ..."
+              P^.accounts_number.Add(i);
+            end;
           end;
           end;
         end;
         end;
-        TLog.NewLog(ltdebug,Classname,Format('Adding account key (%d of %d) %s',[j,FAccountList.AccountsCount,TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
+        TLog.NewLog(ltdebug,Classname,Format('Adding account key (%d with %d accounts) %s',[lockedList.Count,P^.accounts_number.Count,TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
       end else begin
       end else begin
         TLog.NewLog(ltdebug,Classname,Format('Adding account key (no Account List) %s',[TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
         TLog.NewLog(ltdebug,Classname,Format('Adding account key (no Account List) %s',[TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
       end;
       end;
@@ -4396,7 +4520,8 @@ begin
   end;
   end;
 end;
 end;
 
 
-Procedure TOrderedAccountKeysList.AddAccountKeys(Const AccountKeys : array of TAccountKey);
+procedure TOrderedAccountKeysList.AddAccountKeys(
+  const AccountKeys: array of TAccountKey);
 var i : integer;
 var i : integer;
 begin
 begin
   for i := Low(AccountKeys) to High(AccountKeys) do
   for i := Low(AccountKeys) to High(AccountKeys) do
@@ -4415,7 +4540,11 @@ begin
     end else if (FAutoAddAll) then begin
     end else if (FAutoAddAll) then begin
       New(P);
       New(P);
       P^ := CT_TOrderedAccountKeyList_NUL;
       P^ := CT_TOrderedAccountKeyList_NUL;
+      {$IFDEF useAccountKeyStorage}
+      P^.accountKeyPtr:=TAccountKeyStorage.KS.AddAccountKey(AccountKey);
+      {$ELSE}
       P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
       P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
+      {$ENDIF}
       P^.accounts_number := TOrderedCardinalList.Create;
       P^.accounts_number := TOrderedCardinalList.Create;
       lockedList.Insert(i,P);
       lockedList.Insert(i,P);
     end else exit;
     end else exit;
@@ -4467,6 +4596,40 @@ begin
   Result := FTotalChanges>0;
   Result := FTotalChanges>0;
 end;
 end;
 
 
+procedure TOrderedAccountKeysList.CopyFrom(const source: TOrderedAccountKeysList);
+var selfList,sourceList : TList;
+  i : Integer;
+  newP,sourceP : POrderedAccountKeyList;
+begin
+  If Self=source then Exit;
+  selfList := Lock;
+  try
+    sourceList := source.Lock;
+    try
+      Clear;
+      selfList.Capacity:=sourceList.Capacity;
+      for i:=0 to sourceList.Count-1 do begin
+        sourceP := POrderedAccountKeyList(sourceList[i]);
+        new(newP);
+        newP^ := CT_TOrderedAccountKeyList_NUL;
+        newP^.accounts_number := TOrderedCardinalList.Create;
+        newP^.accounts_number.CopyFrom(sourceP^.accounts_number);
+        newP^.changes_counter:=sourceP^.changes_counter;
+        {$IFDEF useAccountKeyStorage}
+        newP^.accountKeyPtr:=TAccountKeyStorage.KS.AddAccountKey(sourceP^.accountKeyPtr^);
+        {$ELSE}
+        newP^.rawaccountkey:=sourceP^.rawaccountkey;
+        {$ENDIF}
+        selfList.Add(newP);
+      end;
+    finally
+      source.Unlock;
+    end;
+  finally
+    Unlock;
+  end;
+end;
+
 procedure TOrderedAccountKeysList.ClearAccounts(RemoveAccountList : Boolean);
 procedure TOrderedAccountKeysList.ClearAccounts(RemoveAccountList : Boolean);
 Var P : POrderedAccountKeyList;
 Var P : POrderedAccountKeyList;
   i : Integer;
   i : Integer;
@@ -4478,6 +4641,9 @@ begin
       P := lockedList[i];
       P := lockedList[i];
       inc(P^.changes_counter);
       inc(P^.changes_counter);
       if RemoveAccountList then begin
       if RemoveAccountList then begin
+        {$IFDEF useAccountKeyStorage}
+        TAccountKeyStorage.KS.RemoveAccountKey(P^.accountKeyPtr^);
+        {$ENDIF}
         P^.accounts_number.Free;
         P^.accounts_number.Free;
         Dispose(P);
         Dispose(P);
       end else begin
       end else begin
@@ -4539,17 +4705,30 @@ begin
 end;
 end;
 
 
 function TOrderedAccountKeysList.Find(lockedList : TList; const AccountKey: TAccountKey; var Index: Integer): Boolean;
 function TOrderedAccountKeysList.Find(lockedList : TList; const AccountKey: TAccountKey; var Index: Integer): Boolean;
-var L, H, I, C: Integer;
+var L, H, I: Integer;
+  C : Int64;
+  {$IFDEF useAccountKeyStorage}
+  pacsd : PAccountKeyStorageData;
+  {$ELSE}
   rak : TRawBytes;
   rak : TRawBytes;
+  {$ENDIF}
 begin
 begin
   Result := False;
   Result := False;
-  rak := TAccountComp.AccountKey2RawString(AccountKey);
   L := 0;
   L := 0;
   H := lockedList.Count - 1;
   H := lockedList.Count - 1;
+  {$IFDEF useAccountKeyStorage}
+  pacsd:=TAccountKeyStorage.KS.AddAccountKeyExt(AccountKey);
+  {$ELSE}
+  rak := TAccountComp.AccountKey2RawString(AccountKey);
+  {$ENDIF}
   while L <= H do
   while L <= H do
   begin
   begin
     I := (L + H) shr 1;
     I := (L + H) shr 1;
+    {$IFDEF useAccountKeyStorage}
+    C := PtrInt(POrderedAccountKeyList(lockedList[I])^.accountKeyPtr)-PtrInt(pacsd^.ptrAccountKey);
+    {$ELSE}
     C := TBaseType.BinStrComp( POrderedAccountKeyList(lockedList[I]).rawaccountkey, rak );
     C := TBaseType.BinStrComp( POrderedAccountKeyList(lockedList[I]).rawaccountkey, rak );
+    {$ENDIF}
     if C < 0 then L := I + 1 else
     if C < 0 then L := I + 1 else
     begin
     begin
       H := I - 1;
       H := I - 1;
@@ -4560,6 +4739,9 @@ begin
       end;
       end;
     end;
     end;
   end;
   end;
+  {$IFDEF useAccountKeyStorage}
+  Dec(pacsd^.counter);
+  {$ENDIF}
   Index := L;
   Index := L;
 end;
 end;
 
 
@@ -4575,16 +4757,23 @@ begin
 end;
 end;
 
 
 function TOrderedAccountKeysList.GetAccountKey(index: Integer): TAccountKey;
 function TOrderedAccountKeysList.GetAccountKey(index: Integer): TAccountKey;
-Var raw : TRawBytes;
-  lockedList : TList;
+Var lockedList : TList;
+  {$IFDEF useAccountKeyStorage}
+  {$ELSE}
+  raw : TRawBytes;
+  {$ENDIF}
 begin
 begin
   lockedList := Lock;
   lockedList := Lock;
   Try
   Try
+    {$IFDEF useAccountKeyStorage}
+    Result := POrderedAccountKeyList(lockedList[index])^.accountKeyPtr^;
+    {$ELSE}
     raw := POrderedAccountKeyList(lockedList[index]).rawaccountkey;
     raw := POrderedAccountKeyList(lockedList[index]).rawaccountkey;
+    Result := TAccountComp.RawString2Accountkey(raw);
+    {$ENDIF}
   finally
   finally
     Unlock;
     Unlock;
   end;
   end;
-  Result := TAccountComp.RawString2Accountkey(raw);
 end;
 end;
 
 
 function TOrderedAccountKeysList.GetAccountKeyList(index: Integer): TOrderedCardinalList;
 function TOrderedAccountKeysList.GetAccountKeyList(index: Integer): TOrderedCardinalList;
@@ -4643,6 +4832,9 @@ begin
       lockedList.Delete(i);
       lockedList.Delete(i);
       // Free it
       // Free it
       P^.accounts_number.free;
       P^.accounts_number.free;
+      {$IFDEF useAccountKeyStorage}
+      TAccountKeyStorage.KS.RemoveAccountKey(AccountKey);
+      {$ENDIF}
       Dispose(P);
       Dispose(P);
     end;
     end;
   finally
   finally
@@ -4664,6 +4856,9 @@ begin
     // Remove from list
     // Remove from list
     lockedList.Delete(i);
     lockedList.Delete(i);
     // Free it
     // Free it
+    {$IFDEF useAccountKeyStorage}
+    TAccountKeyStorage.KS.RemoveAccountKey(AccountKey);
+    {$ENDIF}
     P^.accounts_number.free;
     P^.accounts_number.free;
     Dispose(P);
     Dispose(P);
   finally
   finally
@@ -4846,9 +5041,18 @@ end;
 { TOrderedCardinalList }
 { TOrderedCardinalList }
 
 
 function TOrderedCardinalList.Add(Value: Cardinal): Integer;
 function TOrderedCardinalList.Add(Value: Cardinal): Integer;
+var nc : Integer;
 begin
 begin
   if Find(Value,Result) then exit
   if Find(Value,Result) then exit
   else begin
   else begin
+    nc := FOrderedList.Capacity;
+    if (nc <= FOrderedList.Count) then begin
+      nc := nc SHL 1;
+      if (nc > FOrderedList.Capacity) then begin
+        {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Increase capacity from %d to %d for %s',[FOrderedList.Capacity,nc,IntToHex(Integer(FOrderedList),8)]));{$ENDIF}
+        FOrderedList.Capacity:=nc;
+      end;
+    end;
     FOrderedList.Insert(Result,TObject(Value));
     FOrderedList.Insert(Result,TObject(Value));
     NotifyChanged;
     NotifyChanged;
   end;
   end;
@@ -4867,6 +5071,7 @@ begin
   Disable;
   Disable;
   Try
   Try
     Clear;
     Clear;
+    FOrderedList.Capacity:=Sender.FOrderedList.Capacity;
     for I := 0 to Sender.Count - 1 do begin
     for I := 0 to Sender.Count - 1 do begin
       Add(Sender.Get(i));
       Add(Sender.Get(i));
     end;
     end;
@@ -4912,6 +5117,18 @@ begin
   Result := False;
   Result := False;
   L := 0;
   L := 0;
   H := FOrderedList.Count - 1;
   H := FOrderedList.Count - 1;
+  // Optimization when inserting always a ordered list
+  if (H>0) then begin
+    C := Int64(FOrderedList[H]) - Int64(Value);
+    if (C<0) then begin
+      Index := H+1;
+      Exit;
+    end else if (C=0) then begin
+      Index := H;
+      Result := True;
+      Exit;
+    end;
+  end;
   while L <= H do
   while L <= H do
   begin
   begin
     I := (L + H) shr 1;
     I := (L + H) shr 1;

+ 29 - 15
src/core/UBlockChain.pas

@@ -214,6 +214,7 @@ Type
     procedure AffectedAccounts(list : TList); virtual; abstract;
     procedure AffectedAccounts(list : TList); virtual; abstract;
     class function OpType: Byte; virtual; abstract;
     class function OpType: Byte; virtual; abstract;
     Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
     Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; virtual; abstract;
     function OperationAmount : Int64; virtual; abstract;
     function OperationAmount : Int64; virtual; abstract;
     function OperationAmountByAccount(account : Cardinal) : Int64; virtual;
     function OperationAmountByAccount(account : Cardinal) : Int64; virtual;
     function OperationFee: Int64; virtual; abstract;
     function OperationFee: Int64; virtual; abstract;
@@ -396,7 +397,7 @@ Type
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
     Function DoMoveBlockChain(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; virtual; abstract;
     Function DoMoveBlockChain(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; virtual; abstract;
     Function DoSaveBank : Boolean; virtual; abstract;
     Function DoSaveBank : Boolean; virtual; abstract;
-    Function DoRestoreBank(max_block : Int64) : Boolean; virtual; abstract;
+    Function DoRestoreBank(max_block : Int64; restoreProgressNotify : TProgressNotify) : Boolean; virtual; abstract;
     Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); virtual; abstract;
     Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); virtual; abstract;
     Function BlockExists(Block : Cardinal) : Boolean; virtual; abstract;
     Function BlockExists(Block : Cardinal) : Boolean; virtual; abstract;
     function GetFirstBlockNumber: Int64; virtual; abstract;
     function GetFirstBlockNumber: Int64; virtual; abstract;
@@ -412,7 +413,7 @@ Type
     Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean;
     Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean;
     Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal);
     Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal);
     Function SaveBank : Boolean;
     Function SaveBank : Boolean;
-    Function RestoreBank(max_block : Int64) : Boolean;
+    Function RestoreBank(max_block : Int64; restoreProgressNotify : TProgressNotify = Nil) : Boolean;
     Constructor Create(AOwner : TComponent); Override;
     Constructor Create(AOwner : TComponent); Override;
     Property Orphan : TOrphan read FOrphan write SetOrphan;
     Property Orphan : TOrphan read FOrphan write SetOrphan;
     Property ReadOnly : Boolean read FReadOnly write SetReadOnly;
     Property ReadOnly : Boolean read FReadOnly write SetReadOnly;
@@ -455,12 +456,12 @@ Type
     procedure AssignTo(Dest: TPersistent); Override;
     procedure AssignTo(Dest: TPersistent); Override;
     function GetActualTargetSecondsAverage(BackBlocks : Cardinal): Real;
     function GetActualTargetSecondsAverage(BackBlocks : Cardinal): Real;
     function GetTargetSecondsAverage(FromBlock,BackBlocks : Cardinal): Real;
     function GetTargetSecondsAverage(FromBlock,BackBlocks : Cardinal): Real;
-    function LoadBankFromStream(Stream : TStream; useSecureLoad : Boolean; var errors : AnsiString) : Boolean;
+    function LoadBankFromStream(Stream : TStream; useSecureLoad : Boolean; progressNotify : TProgressNotify; var errors : AnsiString) : Boolean;
     Procedure Clear;
     Procedure Clear;
     Function LoadOperations(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
     Function LoadOperations(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
     Property SafeBox : TPCSafeBox read FSafeBox;
     Property SafeBox : TPCSafeBox read FSafeBox;
     Function AddNewBlockChainBlock(Operations: TPCOperationsComp; MaxAllowedTimestamp : Cardinal; var newBlock: TBlockAccount; var errors: AnsiString): Boolean;
     Function AddNewBlockChainBlock(Operations: TPCOperationsComp; MaxAllowedTimestamp : Cardinal; var newBlock: TBlockAccount; var errors: AnsiString): Boolean;
-    Procedure DiskRestoreFromOperations(max_block : Int64);
+    Procedure DiskRestoreFromOperations(max_block : Int64; restoreProgressNotify : TProgressNotify = Nil);
     Procedure UpdateValuesFromSafebox;
     Procedure UpdateValuesFromSafebox;
     Procedure NewLog(Operations: TPCOperationsComp; Logtype: TLogType; Logtxt: AnsiString);
     Procedure NewLog(Operations: TPCOperationsComp; Logtype: TLogType; Logtxt: AnsiString);
     Property OnLog: TPCBankLog read FOnLog write FOnLog;
     Property OnLog: TPCBankLog read FOnLog write FOnLog;
@@ -633,17 +634,19 @@ begin
   End;
   End;
 end;
 end;
 
 
-procedure TPCBank.DiskRestoreFromOperations(max_block : Int64);
+procedure TPCBank.DiskRestoreFromOperations(max_block : Int64; restoreProgressNotify : TProgressNotify = Nil);
 Var
 Var
   errors: AnsiString;
   errors: AnsiString;
   newBlock: TBlockAccount;
   newBlock: TBlockAccount;
   Operations: TPCOperationsComp;
   Operations: TPCOperationsComp;
   n : Int64;
   n : Int64;
+  tc : TTickCount;
 begin
 begin
   if FIsRestoringFromFile then begin
   if FIsRestoringFromFile then begin
     TLog.NewLog(lterror,Classname,'Is Restoring!!!');
     TLog.NewLog(lterror,Classname,'Is Restoring!!!');
     raise Exception.Create('Is restoring!');
     raise Exception.Create('Is restoring!');
   end;
   end;
+  tc := TPlatform.GetTickCount;
   TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
   TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
   try
   try
     FUpgradingToV2 := NOT Storage.HasUpgradedToVersion2;
     FUpgradingToV2 := NOT Storage.HasUpgradedToVersion2;
@@ -653,7 +656,7 @@ begin
       Storage.Initialize;
       Storage.Initialize;
       If (max_block<Storage.LastBlock) then n := max_block
       If (max_block<Storage.LastBlock) then n := max_block
       else n := Storage.LastBlock;
       else n := Storage.LastBlock;
-      Storage.RestoreBank(n);
+      Storage.RestoreBank(n,restoreProgressNotify);
       // Restore last blockchain
       // Restore last blockchain
       if (BlocksCount>0) And (SafeBox.CurrentProtocol=CT_PROTOCOL_1) then begin
       if (BlocksCount>0) And (SafeBox.CurrentProtocol=CT_PROTOCOL_1) then begin
         if Not Storage.LoadBlockChainBlock(FLastBlockCache,BlocksCount-1) then begin
         if Not Storage.LoadBlockChainBlock(FLastBlockCache,BlocksCount-1) then begin
@@ -679,6 +682,10 @@ begin
                 If (BlocksCount MOD (CT_BankToDiskEveryNBlocks*10))=0 then begin
                 If (BlocksCount MOD (CT_BankToDiskEveryNBlocks*10))=0 then begin
                   Storage.SaveBank;
                   Storage.SaveBank;
                 end;
                 end;
+                if (Assigned(restoreProgressNotify)) And (TPlatform.GetElapsedMilliseconds(tc)>1000) then begin
+                  tc := TPlatform.GetTickCount;
+                  restoreProgressNotify(Self,Format('Reading blockchain block %d/%d',[Operations.OperationBlock.block,Storage.LastBlock]),BlocksCount,Storage.LastBlock);
+                end;
               end;
               end;
             end else break;
             end else break;
           end else break;
           end else break;
@@ -701,7 +708,7 @@ procedure TPCBank.UpdateValuesFromSafebox;
 Var aux : AnsiString;
 Var aux : AnsiString;
   i : Integer;
   i : Integer;
 begin
 begin
-  { Will update current Bank state based on Safbox state
+  { Will update current Bank state based on Safebox state
     Used when commiting a Safebox or rolling back }
     Used when commiting a Safebox or rolling back }
   Try
   Try
     TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
     TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
@@ -790,7 +797,7 @@ begin
   end else Result := true;
   end else Result := true;
 end;
 end;
 
 
-function TPCBank.LoadBankFromStream(Stream: TStream; useSecureLoad : Boolean; var errors: AnsiString): Boolean;
+function TPCBank.LoadBankFromStream(Stream: TStream; useSecureLoad : Boolean; progressNotify : TProgressNotify; var errors: AnsiString): Boolean;
 Var LastReadBlock : TBlockAccount;
 Var LastReadBlock : TBlockAccount;
   i : Integer;
   i : Integer;
   auxSB : TPCSafeBox;
   auxSB : TPCSafeBox;
@@ -800,7 +807,7 @@ begin
     If useSecureLoad then begin
     If useSecureLoad then begin
       // When on secure load will load Stream in a separate SafeBox, changing only real SafeBox if successfully
       // When on secure load will load Stream in a separate SafeBox, changing only real SafeBox if successfully
       auxSB := TPCSafeBox.Create;
       auxSB := TPCSafeBox.Create;
-      Result := auxSB.LoadSafeBoxFromStream(Stream,true,LastReadBlock,errors);
+      Result := auxSB.LoadSafeBoxFromStream(Stream,true,progressNotify,LastReadBlock,errors);
       If Not Result then Exit;
       If Not Result then Exit;
     end;
     end;
     TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
     TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
@@ -808,7 +815,7 @@ begin
       If Assigned(auxSB) then begin
       If Assigned(auxSB) then begin
         SafeBox.CopyFrom(auxSB);
         SafeBox.CopyFrom(auxSB);
       end else begin
       end else begin
-        Result := SafeBox.LoadSafeBoxFromStream(Stream,false,LastReadBlock,errors);
+        Result := SafeBox.LoadSafeBoxFromStream(Stream,false,progressNotify,LastReadBlock,errors);
       end;
       end;
       If Not Result then exit;
       If Not Result then exit;
       If SafeBox.BlocksCount>0 then FLastOperationBlock := SafeBox.Block(SafeBox.BlocksCount-1).blockchainInfo
       If SafeBox.BlocksCount>0 then FLastOperationBlock := SafeBox.Block(SafeBox.BlocksCount-1).blockchainInfo
@@ -969,6 +976,8 @@ begin
         FOperationBlock.protocol_version := CT_PROTOCOL_2; // If minting... upgrade to Protocol 2
         FOperationBlock.protocol_version := CT_PROTOCOL_2; // If minting... upgrade to Protocol 2
       end else if (FOperationBlock.protocol_version=CT_PROTOCOL_2) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_3)) then begin
       end else if (FOperationBlock.protocol_version=CT_PROTOCOL_2) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_3)) then begin
         FOperationBlock.protocol_version := CT_PROTOCOL_3; // If minting... upgrade to Protocol 3
         FOperationBlock.protocol_version := CT_PROTOCOL_3; // If minting... upgrade to Protocol 3
+      end else if (FOperationBlock.protocol_version=CT_PROTOCOL_3) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_4)) then begin
+        FOperationBlock.protocol_version := CT_PROTOCOL_4; // If minting... upgrade to Protocol 4
       end;
       end;
       FOperationBlock.block := FBank.BlocksCount;
       FOperationBlock.block := FBank.BlocksCount;
       FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(FBank.BlocksCount);
       FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(FBank.BlocksCount);
@@ -1195,7 +1204,7 @@ begin
     // In build prior to 1.0.4 soob only can have 2 values: 0 or 1
     // In build prior to 1.0.4 soob only can have 2 values: 0 or 1
     // In build 1.0.4 soob can has 2 more values: 2 or 3
     // In build 1.0.4 soob can has 2 more values: 2 or 3
     // In build 2.0 soob can has 1 more value: 4
     // In build 2.0 soob can has 1 more value: 4
-    // In build 3.0 soob can hast value: 5
+    // In build 3.0 soob can have value: 5
     // In future, old values 0 and 1 will no longer be used!
     // In future, old values 0 and 1 will no longer be used!
     // - Value 0 and 2 means that contains also operations
     // - Value 0 and 2 means that contains also operations
     // - Value 1 and 3 means that only contains operationblock info
     // - Value 1 and 3 means that only contains operationblock info
@@ -1328,6 +1337,9 @@ begin
       end else if (FOperationBlock.protocol_version=CT_PROTOCOL_2) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_3)) then begin
       end else if (FOperationBlock.protocol_version=CT_PROTOCOL_2) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_3)) then begin
         TLog.NewLog(ltinfo,ClassName,'New miner protocol version to 3 at sanitize');
         TLog.NewLog(ltinfo,ClassName,'New miner protocol version to 3 at sanitize');
         FOperationBlock.protocol_version := CT_PROTOCOL_3;
         FOperationBlock.protocol_version := CT_PROTOCOL_3;
+      end else if (FOperationBlock.protocol_version=CT_PROTOCOL_3) And (FBank.SafeBox.CanUpgradeToProtocol(CT_PROTOCOL_4)) then begin
+        TLog.NewLog(ltinfo,ClassName,'New miner protocol version to 4 at sanitize');
+        FOperationBlock.protocol_version := CT_PROTOCOL_4;
       end;
       end;
       FOperationBlock.block := FBank.BlocksCount;
       FOperationBlock.block := FBank.BlocksCount;
       FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(FBank.BlocksCount);
       FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(FBank.BlocksCount);
@@ -2201,9 +2213,9 @@ begin
   Result := DoMoveBlockChain(StartBlock,DestOrphan,DestStorage);
   Result := DoMoveBlockChain(StartBlock,DestOrphan,DestStorage);
 end;
 end;
 
 
-function TStorage.RestoreBank(max_block: Int64): Boolean;
+function TStorage.RestoreBank(max_block: Int64; restoreProgressNotify : TProgressNotify = Nil): Boolean;
 begin
 begin
-  Result := DoRestoreBank(max_block);
+  Result := DoRestoreBank(max_block,restoreProgressNotify);
 end;
 end;
 
 
 function TStorage.SaveBank: Boolean;
 function TStorage.SaveBank: Boolean;
@@ -2469,8 +2481,7 @@ begin
 end;
 end;
 
 
 class function TPCOperation.OperationToOperationResume(Block : Cardinal; Operation: TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume): Boolean;
 class function TPCOperation.OperationToOperationResume(Block : Cardinal; Operation: TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number: Cardinal; var OperationResume: TOperationResume): Boolean;
-Var spayload : AnsiString;
-  s : AnsiString;
+Var s : AnsiString;
 begin
 begin
   OperationResume := CT_TOperationResume_NUL;
   OperationResume := CT_TOperationResume_NUL;
   OperationResume.Block:=Block;
   OperationResume.Block:=Block;
@@ -2610,6 +2621,9 @@ begin
       OperationResume.Amount := Operation.OperationAmountByAccount(Affected_account_number);
       OperationResume.Amount := Operation.OperationAmountByAccount(Affected_account_number);
       OperationResume.Fee := 0;
       OperationResume.Fee := 0;
       Result := True;
       Result := True;
+    end;
+    CT_Op_Data : Begin
+      Result := True;
     end
     end
   else Exit;
   else Exit;
   end;
   end;

+ 13 - 3
src/core/UConst.pas

@@ -81,7 +81,7 @@ Const
 
 
   CT_MaxClientsConnected = {$IFDEF FPC}140{$ELSE}80{$ENDIF};
   CT_MaxClientsConnected = {$IFDEF FPC}140{$ELSE}80{$ENDIF};
 
 
-  CT_BankToDiskEveryNBlocks = {$IFDEF PRODUCTION}100{$ELSE}10{$ENDIF}; // Build 1.5 changed from 500 to 100;
+  CT_BankToDiskEveryNBlocks = {$IFDEF PRODUCTION}100{$ELSE}100{$ENDIF}; // Build 1.5 changed from 500 to 100;
 
 
   CT_NID_secp256k1 = 714;
   CT_NID_secp256k1 = 714;
   CT_NID_secp384r1 = 715;
   CT_NID_secp384r1 = 715;
@@ -95,12 +95,16 @@ Const
   CT_PROTOCOL_1 = 1;
   CT_PROTOCOL_1 = 1;
   CT_PROTOCOL_2 = 2;
   CT_PROTOCOL_2 = 2;
   CT_PROTOCOL_3 = 3;
   CT_PROTOCOL_3 = 3;
+  CT_PROTOCOL_4 = 4;
+  CT_BUILD_PROTOCOL = CT_PROTOCOL_3;
+
   CT_BlockChain_Protocol_Available: Word = $0003; // Protocol 3 flag
   CT_BlockChain_Protocol_Available: Word = $0003; // Protocol 3 flag
   CT_Protocol_Upgrade_v2_MinBlock = {$IFDEF PRODUCTION}115000{$ELSE}50{$ENDIF};
   CT_Protocol_Upgrade_v2_MinBlock = {$IFDEF PRODUCTION}115000{$ELSE}50{$ENDIF};
   CT_Protocol_Upgrade_v3_MinBlock = {$IFDEF PRODUCTION}210000{$ELSE}250{$ENDIF};
   CT_Protocol_Upgrade_v3_MinBlock = {$IFDEF PRODUCTION}210000{$ELSE}250{$ENDIF};
+  CT_Protocol_Upgrade_v4_MinBlock = {$IFDEF PRODUCTION}999999{$ELSE}400{$ENDIF}; // NOTE: Upgrade to V4 not decided!   TODO!
 
 
 
 
-  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$03000040{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
+  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$04000000{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
 
 
   CT_NetProtocol_Version: Word = $0007; // Version 3.0.2 only allows net protocol 7 (Introduced on 3.0.0)
   CT_NetProtocol_Version: Word = $0007; // Version 3.0.2 only allows net protocol 7 (Introduced on 3.0.0)
   // IMPORTANT NOTE!!!
   // IMPORTANT NOTE!!!
@@ -126,6 +130,8 @@ Const
   CT_Op_ChangeAccountInfo = $08;
   CT_Op_ChangeAccountInfo = $08;
   // Protocol 3 new operations
   // Protocol 3 new operations
   CT_Op_MultiOperation = $09;  // PIP-0017
   CT_Op_MultiOperation = $09;  // PIP-0017
+  // Protocol 4 new operations
+  CT_Op_Data = $0A;            // PIP-0016
 
 
   CT_Protocol_v3_PIP11_Percent = 20; // PIP-0011 20% Percent proposed and voted by PIP-0011
   CT_Protocol_v3_PIP11_Percent = 20; // PIP-0011 20% Percent proposed and voted by PIP-0011
 
 
@@ -149,8 +155,12 @@ Const
   CT_OpSubtype_ChangeAccountInfo          = 81;
   CT_OpSubtype_ChangeAccountInfo          = 81;
   CT_OpSubtype_MultiOperation_Global      = 91;
   CT_OpSubtype_MultiOperation_Global      = 91;
   CT_OpSubtype_MultiOperation_AccountInfo = 92;
   CT_OpSubtype_MultiOperation_AccountInfo = 92;
+  CT_OpSubtype_Data_GlobalInfo            = 101;
+  CT_OpSubtype_Data_Sender                = 102;
+  CT_OpSubtype_Data_Signer                = 103;
+  CT_OpSubtype_Data_Receiver              = 104;
 
 
-  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'3.0.1'{$ELSE}{$IFDEF TESTNET}'TESTNET 3.3.1'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'3.0.2'{$ELSE}{$IFDEF TESTNET}'TESTNET 3.0.2'{$ELSE}{$ENDIF}{$ENDIF};
 
 
   CT_Discover_IPs =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.dynamic-dns.net;pascalcoin2.dynamic-dns.net;pascalcoin1.dns1.us;pascalcoin2.dns1.us;pascalcoin1.dns2.us;pascalcoin2.dns2.us';
   CT_Discover_IPs =  'bpascal1.dynamic-dns.net;bpascal2.dynamic-dns.net;pascalcoin1.dynamic-dns.net;pascalcoin2.dynamic-dns.net;pascalcoin1.dns1.us;pascalcoin2.dns1.us;pascalcoin1.dns2.us;pascalcoin2.dns2.us';
 
 

+ 5 - 0
src/core/UCrypto.pas

@@ -510,6 +510,11 @@ begin
   end else begin
   end else begin
     Result := false;
     Result := false;
   end;
   end;
+  {$IFDEF HIGHLOG}
+  TLog.NewLog(ltdebug,ClassName,Format('ECDSAVerify %s x:%s y:%s Digest:%s Signature r:%s s:%s',
+    [TAccountComp.GetECInfoTxt(PubKey.EC_OpenSSL_NID),ToHexaString(PubKey.x),ToHexaString(PubKey.y),
+      ToHexaString(digest),ToHexaString(Signature.r),ToHexaString(Signature.s)]));
+  {$ENDIF}
   BN_CTX_free(ctx);
   BN_CTX_free(ctx);
   EC_POINT_free(pub_key);
   EC_POINT_free(pub_key);
   EC_GROUP_free(ECG);
   EC_GROUP_free(ECG);

+ 6 - 5
src/core/UFileStorage.pas

@@ -61,7 +61,7 @@ Type
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; override;
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; override;
     Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; override;
     Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean; override;
     Function DoSaveBank : Boolean; override;
     Function DoSaveBank : Boolean; override;
-    Function DoRestoreBank(max_block : Int64) : Boolean; override;
+    Function DoRestoreBank(max_block : Int64; restoreProgressNotify : TProgressNotify) : Boolean; override;
     Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); override;
     Procedure DoDeleteBlockChainBlocks(StartingDeleteBlock : Cardinal); override;
     Function BlockExists(Block : Cardinal) : Boolean; override;
     Function BlockExists(Block : Cardinal) : Boolean; override;
     Function LockBlockChainStream : TFileStream;
     Function LockBlockChainStream : TFileStream;
@@ -436,7 +436,7 @@ begin
   End;
   End;
 end;
 end;
 
 
-function TFileStorage.DoRestoreBank(max_block: Int64): Boolean;
+function TFileStorage.DoRestoreBank(max_block: Int64; restoreProgressNotify : TProgressNotify): Boolean;
 var
 var
     sr: TSearchRec;
     sr: TSearchRec;
     FileAttrs: Integer;
     FileAttrs: Integer;
@@ -446,7 +446,7 @@ var
     ms : TMemoryStream;
     ms : TMemoryStream;
     errors : AnsiString;
     errors : AnsiString;
     blockscount : Cardinal;
     blockscount : Cardinal;
-    sbHeader : TPCSafeBoxHeader;
+    sbHeader, goodSbHeader : TPCSafeBoxHeader;
 begin
 begin
   LockBlockChainStream;
   LockBlockChainStream;
   Try
   Try
@@ -463,6 +463,7 @@ begin
               (sbHeader.startBlock=0) And (sbHeader.endBlock=sbHeader.startBlock+sbHeader.blocksCount-1) then begin
               (sbHeader.startBlock=0) And (sbHeader.endBlock=sbHeader.startBlock+sbHeader.blocksCount-1) then begin
               filename := auxfn;
               filename := auxfn;
               blockscount := sbHeader.blocksCount;
               blockscount := sbHeader.blocksCount;
+              goodSbHeader := sbHeader;
             end;
             end;
           end;
           end;
         end;
         end;
@@ -470,7 +471,7 @@ begin
       FindClose(sr);
       FindClose(sr);
     end;
     end;
     if (filename<>'') then begin
     if (filename<>'') then begin
-      TLog.NewLog(ltinfo,Self.ClassName,'Loading SafeBox with '+inttostr(blockscount)+' blocks from file '+filename);
+      TLog.NewLog(ltinfo,Self.ClassName,'Loading SafeBox protocol:'+IntToStr(goodSbHeader.protocol)+' with '+inttostr(blockscount)+' blocks from file '+filename);
       fs := TFileStream.Create(filename,fmOpenRead);
       fs := TFileStream.Create(filename,fmOpenRead);
       try
       try
         ms := TMemoryStream.Create;
         ms := TMemoryStream.Create;
@@ -478,7 +479,7 @@ begin
           ms.CopyFrom(fs,0);
           ms.CopyFrom(fs,0);
           fs.Position := 0;
           fs.Position := 0;
           ms.Position := 0;
           ms.Position := 0;
-          if not Bank.LoadBankFromStream(ms,False,errors) then begin
+          if not Bank.LoadBankFromStream(ms,False,restoreProgressNotify,errors) then begin
             TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
             TLog.NewLog(lterror,ClassName,'Error reading bank from file: '+filename+ ' Error: '+errors);
           end;
           end;
         Finally
         Finally

+ 1 - 1
src/core/UNetProtocol.pas

@@ -1862,7 +1862,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         TNode.Node.Bank.SafeBox.StartThreadSafe;
         TNode.Node.Bank.SafeBox.StartThreadSafe;
         try
         try
           receiveData.Position:=0;
           receiveData.Position:=0;
-          If TNode.Node.Bank.LoadBankFromStream(receiveData,True,errors) then begin
+          If TNode.Node.Bank.LoadBankFromStream(receiveData,True,Nil,errors) then begin
             TLog.NewLog(ltInfo,ClassName,'Received new safebox!');
             TLog.NewLog(ltInfo,ClassName,'Received new safebox!');
             If Not IsMyBlockchainValid then begin
             If Not IsMyBlockchainValid then begin
               TNode.Node.Bank.Storage.EraseStorage;
               TNode.Node.Bank.Storage.EraseStorage;

+ 3 - 3
src/core/UNode.pas

@@ -87,7 +87,7 @@ Type
     Function FindNOperation(block, account, n_operation : Cardinal; var OpResume : TOperationResume) : TSearchOperationResult;
     Function FindNOperation(block, account, n_operation : Cardinal; var OpResume : TOperationResume) : TSearchOperationResult;
     Function FindNOperations(account, start_block : Cardinal; allow_search_previous : Boolean; n_operation_low, n_operation_high : Cardinal; OpResumeList : TOperationsResumeList) : TSearchOperationResult;
     Function FindNOperations(account, start_block : Cardinal; allow_search_previous : Boolean; n_operation_low, n_operation_high : Cardinal; OpResumeList : TOperationsResumeList) : TSearchOperationResult;
     //
     //
-    Procedure InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF);
+    Procedure InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
     Procedure AutoDiscoverNodes(Const ips : AnsiString);
     Procedure AutoDiscoverNodes(Const ips : AnsiString);
     Function IsBlockChainValid(var WhyNot : AnsiString) : Boolean;
     Function IsBlockChainValid(var WhyNot : AnsiString) : Boolean;
     Function IsReady(Var CurrentProcess : AnsiString) : Boolean;
     Function IsReady(Var CurrentProcess : AnsiString) : Boolean;
@@ -949,13 +949,13 @@ begin
   Result := found;
   Result := found;
 end;
 end;
 
 
-procedure TNode.InitSafeboxAndOperations(max_block_to_read : Cardinal);
+procedure TNode.InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
 var opht : TOperationsHashTree;
 var opht : TOperationsHashTree;
   oprl : TOperationsResumeList;
   oprl : TOperationsResumeList;
   errors : AnsiString;
   errors : AnsiString;
   n : Integer;
   n : Integer;
 begin
 begin
-  Bank.DiskRestoreFromOperations(max_block_to_read);
+  Bank.DiskRestoreFromOperations(max_block_to_read,restoreProgressNotify);
   opht := TOperationsHashTree.Create;
   opht := TOperationsHashTree.Create;
   oprl := TOperationsResumeList.Create;
   oprl := TOperationsResumeList.Create;
   try
   try

+ 577 - 249
src/core/UOpTransaction.pas

@@ -79,8 +79,6 @@ Type
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; override;
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; override;
     procedure AffectedAccounts(list : TList); override;
     procedure AffectedAccounts(list : TList); override;
     //
     //
-    Class Function GetTransactionHashToSign(const trans : TOpTransactionData) : TRawBytes;
-    Class Function DoSignOperation(key : TECPrivateKey; var trans : TOpTransactionData) : Boolean;
     class function OpType : Byte; override;
     class function OpType : Byte; override;
     function OperationAmount : Int64; override;
     function OperationAmount : Int64; override;
     function OperationFee : Int64; override;
     function OperationFee : Int64; override;
@@ -92,8 +90,9 @@ Type
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Property Data : TOpTransactionData read FData;
     Property Data : TOpTransactionData read FData;
 
 
-    Constructor CreateTransaction(sender, n_operation, target: Cardinal; key: TECPrivateKey; amount, fee: UInt64; payload: TRawBytes);
+    Constructor CreateTransaction(current_protocol : Word; sender, n_operation, target: Cardinal; key: TECPrivateKey; amount, fee: UInt64; payload: TRawBytes);
     Function toString : String; Override;
     Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
   End;
 
 
   { TOpChangeKey }
   { TOpChangeKey }
@@ -107,8 +106,6 @@ Type
     function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
     function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
     procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
     procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
   public
   public
-    Class Function GetOperationHashToSign(const op : TOpChangeKeyData) : TRawBytes;
-    Class Function DoSignOperation(key : TECPrivateKey; var op : TOpChangeKeyData) : Boolean;
     class function OpType : Byte; override;
     class function OpType : Byte; override;
 
 
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
@@ -121,9 +118,10 @@ Type
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
     procedure AffectedAccounts(list : TList); override;
     procedure AffectedAccounts(list : TList); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
-    Constructor Create(account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; payload: TRawBytes);
+    Constructor Create(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; payload: TRawBytes);
     Property Data : TOpChangeKeyData read FData;
     Property Data : TOpChangeKeyData read FData;
     Function toString : String; Override;
     Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
   End;
 
 
   { TOpChangeKeySigned }
   { TOpChangeKeySigned }
@@ -159,6 +157,7 @@ Type
     Constructor Create(account_number, n_operation: Cardinal; fee: UInt64);
     Constructor Create(account_number, n_operation: Cardinal; fee: UInt64);
     Property Data : TOpRecoverFoundsData read FData;
     Property Data : TOpRecoverFoundsData read FData;
     Function toString : String; Override;
     Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
   End;
 
 
   // NEW OPERATIONS PROTOCOL 2
   // NEW OPERATIONS PROTOCOL 2
@@ -211,9 +210,6 @@ Type
     function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
     function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
     procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
     procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
   public
   public
-    Class Function GetOperationHashToSign(const operation : TOpListAccountData) : TRawBytes;
-    Class Function DoSignOperation(key : TECPrivateKey; var operation : TOpListAccountData) : Boolean;
-
     Function IsPrivateSale : Boolean;
     Function IsPrivateSale : Boolean;
     Function IsDelist : Boolean; virtual; abstract;
     Function IsDelist : Boolean; virtual; abstract;
 
 
@@ -230,19 +226,20 @@ Type
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Property Data : TOpListAccountData read FData;
     Property Data : TOpListAccountData read FData;
     Function toString : String; Override;
     Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
   End;
 
 
   TOpListAccountForSale = Class(TOpListAccount)
   TOpListAccountForSale = Class(TOpListAccount)
   public
   public
     class function OpType : Byte; override;
     class function OpType : Byte; override;
-    Constructor CreateListAccountForSale(account_signer, n_operation, account_target: Cardinal; account_price, fee : UInt64; account_to_pay:Cardinal; new_public_key:TAccountKey; locked_until_block : Cardinal; key:TECPrivateKey; payload: TRawBytes);
+    Constructor CreateListAccountForSale(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; account_price, fee : UInt64; account_to_pay:Cardinal; new_public_key:TAccountKey; locked_until_block : Cardinal; key:TECPrivateKey; payload: TRawBytes);
     Function IsDelist : Boolean; override;
     Function IsDelist : Boolean; override;
   End;
   End;
 
 
   TOpDelistAccountForSale = Class(TOpListAccount)
   TOpDelistAccountForSale = Class(TOpListAccount)
   public
   public
     class function OpType : Byte; override;
     class function OpType : Byte; override;
-    Constructor CreateDelistAccountForSale(account_signer, n_operation, account_target: Cardinal; fee: UInt64; key: TECPrivateKey; payload: TRawBytes);
+    Constructor CreateDelistAccountForSale(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; fee: UInt64; key: TECPrivateKey; payload: TRawBytes);
     Function IsDelist : Boolean; override;
     Function IsDelist : Boolean; override;
   End;
   End;
 
 
@@ -253,7 +250,7 @@ Type
     procedure InitializeData; override;
     procedure InitializeData; override;
   public
   public
     class function OpType : Byte; override;
     class function OpType : Byte; override;
-    Constructor CreateBuy(account_number, n_operation, account_to_buy, account_to_pay: Cardinal; price, amount, fee : UInt64; new_public_key:TAccountKey; key:TECPrivateKey; payload: TRawBytes);
+    Constructor CreateBuy(current_protocol : Word; account_number, n_operation, account_to_buy, account_to_pay: Cardinal; price, amount, fee : UInt64; new_public_key:TAccountKey; key:TECPrivateKey; payload: TRawBytes);
   End;
   End;
 
 
   { TOpChangeAccountInfo }
   { TOpChangeAccountInfo }
@@ -267,8 +264,6 @@ Type
     function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
     function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
     procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
     procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
   public
   public
-    Class Function GetOperationHashToSign(const op : TOpChangeAccountInfoData) : TRawBytes;
-    Class Function DoSignOperation(key : TECPrivateKey; var op : TOpChangeAccountInfoData) : Boolean;
     class function OpType : Byte; override;
     class function OpType : Byte; override;
 
 
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
@@ -281,15 +276,68 @@ Type
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
     procedure AffectedAccounts(list : TList); override;
     procedure AffectedAccounts(list : TList); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
-    Constructor CreateChangeAccountInfo(account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
+    Constructor CreateChangeAccountInfo(current_protocol : word;
+      account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
       change_key : Boolean; const new_account_key : TAccountKey;
       change_key : Boolean; const new_account_key : TAccountKey;
       change_name: Boolean; const new_name : TRawBytes;
       change_name: Boolean; const new_name : TRawBytes;
       change_type: Boolean; const new_type : Word;
       change_type: Boolean; const new_type : Word;
       fee: UInt64; payload: TRawBytes);
       fee: UInt64; payload: TRawBytes);
     Property Data : TOpChangeAccountInfoData read FData;
     Property Data : TOpChangeAccountInfoData read FData;
     Function toString : String; Override;
     Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+  End;
+
+
+  TOpDataData = Record
+    account_signer,              // The account paying fees (if any) and signing operation
+    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
+    dataType : Word;             // 2 byte data type
+    dataSequence : Word;         // 2 byte data sequence
+    amount: UInt64;              // Allow amount=0
+    fee: UInt64;                 // Allow fee=0
+    payload: TRawBytes;          // Standard arbitrary data with length<=256
+    sign: TECDSA_SIG;
+  End;
+
+  { TOpData }
+
+  TOpData = Class(TPCOperation)
+  private
+    FData : TOpDataData;
+  protected
+    procedure InitializeData; override;
+    function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean; override;
+    function LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean; override;
+    procedure FillOperationResume(Block : Cardinal; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume); override;
+  public
+    class function OpType : Byte; override;
+
+    function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
+    function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; override;
+    function OperationAmount : Int64; override;
+    function OperationFee : Int64; override;
+    function OperationPayload : TRawBytes; override;
+    function SignerAccount : Cardinal; override;
+    function DestinationAccount : Int64; override;
+    function N_Operation : Cardinal; override;
+    procedure AffectedAccounts(list : TList); override;
+    function OperationAmountByAccount(account : Cardinal) : Int64; override;
+    Constructor CreateOpData(
+      account_signer, account_sender, account_target : Cardinal; signer_key:TECPrivateKey;
+      n_operation : Cardinal;
+      dataType, dataSequence : Word;
+      amount, fee : UInt64;
+      payload: TRawBytes);
+    Property Data : TOpDataData read FData;
+    Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
   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:'';sign:(r:'';s:''));
+
 Procedure RegisterOperationsClass;
 Procedure RegisterOperationsClass;
 
 
 implementation
 implementation
@@ -308,6 +356,7 @@ Begin
   TPCOperationsComp.RegisterOperationClass(TOpChangeKeySigned);
   TPCOperationsComp.RegisterOperationClass(TOpChangeKeySigned);
   TPCOperationsComp.RegisterOperationClass(TOpChangeAccountInfo);
   TPCOperationsComp.RegisterOperationClass(TOpChangeAccountInfo);
   TPCOperationsComp.RegisterOperationClass(TOpMultiOperation);
   TPCOperationsComp.RegisterOperationClass(TOpMultiOperation);
+  TPCOperationsComp.RegisterOperationClass(TOpData);
 End;
 End;
 
 
 { TOpChangeAccountInfo }
 { TOpChangeAccountInfo }
@@ -390,57 +439,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-class function TOpChangeAccountInfo.GetOperationHashToSign(const op: TOpChangeAccountInfoData): TRawBytes;
-var Stream : TMemoryStream;
-  b : Byte;
-begin
-  Stream := TMemoryStream.Create;
-  try
-    Stream.Write(op.account_signer,Sizeof(op.account_signer));
-    Stream.Write(op.account_target,Sizeof(op.account_target));
-    Stream.Write(op.n_operation,Sizeof(op.n_operation));
-    Stream.Write(op.fee,Sizeof(op.fee));
-    TStreamOp.WriteAnsiString(Stream,op.payload);
-    TStreamOp.WriteAccountKey(Stream,op.public_key);
-    b := 0;
-    if (public_key in op.changes_type) then b:=b OR $01;
-    if (account_name in op.changes_type) then b:=b OR $02;
-    if (account_type in op.changes_type) then b:=b OR $04;
-    Stream.Write(b,Sizeof(b));
-    TStreamOp.WriteAccountKey(Stream,op.new_accountkey);
-    TStreamOp.WriteAnsiString(Stream,op.new_name);
-    Stream.Write(op.new_type,Sizeof(op.new_type));
-    Stream.Position := 0;
-    setlength(Result,Stream.Size);
-    Stream.ReadBuffer(Result[1],Stream.Size);
-  finally
-    Stream.Free;
-  end;
-end;
-
-class function TOpChangeAccountInfo.DoSignOperation(key: TECPrivateKey; var op: TOpChangeAccountInfoData): Boolean;
-var raw : TRawBytes;
-  _sign : TECDSA_SIG;
-begin
-  If Not Assigned(key.PrivateKey) then begin
-    Result := false;
-    op.sign.r:='';
-    op.sign.s:='';
-    exit;
-  end;
-  raw := GetOperationHashToSign(op);
-  Try
-    _sign := TCrypto.ECDSASign(key.PrivateKey,raw);
-    op.sign := _sign;
-    Result := true;
-  Except
-    op.sign.r:='';
-    op.sign.s:='';
-    Result := false;
-  End;
-  SetLength(raw,0);
-end;
-
 class function TOpChangeAccountInfo.OpType: Byte;
 class function TOpChangeAccountInfo.OpType: Byte;
 begin
 begin
   Result := CT_Op_ChangeAccountInfo;
   Result := CT_Op_ChangeAccountInfo;
@@ -540,7 +538,7 @@ begin
     end;
     end;
   end;
   end;
 
 
-  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
+  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol),FData.sign) then begin
     errors := 'Invalid sign';
     errors := 'Invalid sign';
     FHasValidSignature := false;
     FHasValidSignature := false;
     exit;
     exit;
@@ -606,7 +604,8 @@ begin
   else Result := 0;
   else Result := 0;
 end;
 end;
 
 
-constructor TOpChangeAccountInfo.CreateChangeAccountInfo(account_signer, n_operation,
+constructor TOpChangeAccountInfo.CreateChangeAccountInfo(current_protocol : word;
+  account_signer, n_operation,
   account_target: Cardinal; key: TECPrivateKey; change_key: Boolean;
   account_target: Cardinal; key: TECPrivateKey; change_key: Boolean;
   const new_account_key: TAccountKey; change_name: Boolean;
   const new_account_key: TAccountKey; change_name: Boolean;
   const new_name: TRawBytes; change_type: Boolean; const new_type: Word;
   const new_name: TRawBytes; change_type: Boolean; const new_type: Word;
@@ -633,11 +632,13 @@ begin
     FData.changes_type:=FData.changes_type + [account_type];
     FData.changes_type:=FData.changes_type + [account_type];
     FData.new_type:=new_type;
     FData.new_type:=new_type;
   end;
   end;
-  If Not DoSignOperation(key,FData) then begin
-    TLog.NewLog(lterror,Classname,'Error signing a new Change Info operation');
-    FHasValidSignature := false;
-  end else begin
+
+  if Assigned(key) then begin
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
     FHasValidSignature := true;
     FHasValidSignature := true;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a new Change Info operation');
+    FHasValidSignature := false;
   end;
   end;
 end;
 end;
 
 
@@ -660,6 +661,40 @@ begin
      TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
      TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
 end;
 end;
 
 
+function TOpChangeAccountInfo.GetDigestToSign(current_protocol : Word): TRawBytes;
+var Stream : TMemoryStream;
+  b : Byte;
+begin
+  Stream := TMemoryStream.Create;
+  try
+    Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
+    Stream.Write(FData.account_target,Sizeof(FData.account_target));
+    Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
+    Stream.Write(FData.fee,Sizeof(FData.fee));
+    TStreamOp.WriteAnsiString(Stream,FData.payload);
+    TStreamOp.WriteAccountKey(Stream,FData.public_key);
+    b := 0;
+    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;
+    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_3) then begin
+      Stream.Position := 0;
+      setlength(Result,Stream.Size);
+      Stream.ReadBuffer(Result[1],Stream.Size);
+    end else begin
+      b := OpType;
+      Stream.Write(b,1);
+      Result := TCrypto.DoSha256(Stream.Memory,Stream.Size);
+    end;
+  finally
+    Stream.Free;
+  end;
+end;
+
 { TOpTransaction }
 { TOpTransaction }
 
 
 procedure TOpTransaction.AffectedAccounts(list: TList);
 procedure TOpTransaction.AffectedAccounts(list: TList);
@@ -671,7 +706,8 @@ begin
   end;
   end;
 end;
 end;
 
 
-constructor TOpTransaction.CreateTransaction(sender, n_operation, target: Cardinal;
+constructor TOpTransaction.CreateTransaction(current_protocol : Word;
+  sender, n_operation, target: Cardinal;
   key: TECPrivateKey; amount, fee: UInt64; payload: TRawBytes);
   key: TECPrivateKey; amount, fee: UInt64; payload: TRawBytes);
 begin
 begin
   inherited Create;
   inherited Create;
@@ -683,11 +719,12 @@ begin
   FData.payload := payload;
   FData.payload := payload;
   // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
   // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
   // FData.public_key := key.PublicKey;
   // FData.public_key := key.PublicKey;
-  If Not DoSignOperation(key,FData) then begin
-    TLog.NewLog(lterror,Classname,'Error signing a new Transaction');
-    FHasValidSignature := false;
-  end else begin
+  if Assigned(key) then begin
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
     FHasValidSignature := true;
     FHasValidSignature := true;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a new Transaction');
+    FHasValidSignature := false;
   end;
   end;
 end;
 end;
 
 
@@ -766,7 +803,7 @@ begin
   end;
   end;
 
 
   // Check signature
   // Check signature
-  _h := GetTransactionHashToSign(FData);
+  _h := GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol);
   if (Not TCrypto.ECDSAVerify(sender.accountInfo.accountkey,_h,FData.sign)) then begin
   if (Not TCrypto.ECDSAVerify(sender.accountInfo.accountkey,_h,FData.sign)) then begin
     errors := 'Invalid sign';
     errors := 'Invalid sign';
     FHasValidSignature := false;
     FHasValidSignature := false;
@@ -839,33 +876,10 @@ begin
     end;
     end;
     Result := AccountTransaction.BuyAccount(AccountPreviousUpdatedBlock,sender.account,target.account,seller.account,FData.n_operation,FData.amount,target.accountInfo.price,FData.fee,FData.new_accountkey,errors);
     Result := AccountTransaction.BuyAccount(AccountPreviousUpdatedBlock,sender.account,target.account,seller.account,FData.n_operation,FData.amount,target.accountInfo.price,FData.fee,FData.new_accountkey,errors);
   end else begin
   end else begin
-    Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,FData.sender,FData.target,FData.n_operation,FData.amount,FData.fee,errors);
+    Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,FData.sender,FData.sender,FData.target,FData.n_operation,FData.amount,FData.fee,errors);
   end;
   end;
 end;
 end;
 
 
-class function TOpTransaction.DoSignOperation(key : TECPrivateKey; var trans : TOpTransactionData) : Boolean;
-var s : AnsiString;
-  _sign : TECDSA_SIG;
-begin
-  If Not Assigned(key.PrivateKey) then begin
-    Result := false;
-    trans.sign.r:='';
-    trans.sign.s:='';
-    exit;
-  end;
-  s := GetTransactionHashToSign(trans);
-  Try
-    _sign := TCrypto.ECDSASign(key.PrivateKey,s);
-    trans.sign := _sign;
-    Result := true;
-  Except
-    trans.sign.r:='';
-    trans.sign.s:='';
-    Result := false;
-  End;
-  SetLength(s,0);
-end;
-
 function TOpTransaction.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
 function TOpTransaction.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
 Var ms : TMemoryStream;
 Var ms : TMemoryStream;
 begin
 begin
@@ -898,40 +912,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-class function TOpTransaction.GetTransactionHashToSign(const trans: TOpTransactionData): TRawBytes;
-Var ms : TMemoryStream;
-begin
-  ms := TMemoryStream.Create;
-  try
-    ms.Write(trans.sender,Sizeof(trans.sender));
-    ms.Write(trans.n_operation,Sizeof(trans.n_operation));
-    ms.Write(trans.target,Sizeof(trans.target));
-    ms.Write(trans.amount,Sizeof(trans.amount));
-    ms.Write(trans.fee,Sizeof(trans.fee));
-    if length(trans.payload)>0 then
-      ms.WriteBuffer(trans.payload[1],length(trans.payload));
-    ms.Write(trans.public_key.EC_OpenSSL_NID,Sizeof(trans.public_key.EC_OpenSSL_NID));
-    if length(trans.public_key.x)>0 then
-      ms.WriteBuffer(trans.public_key.x[1],length(trans.public_key.x));
-    if length(trans.public_key.y)>0 then
-      ms.WriteBuffer(trans.public_key.y[1],length(trans.public_key.y));
-    if trans.opTransactionStyle=buy_account then begin
-      ms.Write(trans.AccountPrice,Sizeof(trans.AccountPrice));
-      ms.Write(trans.SellerAccount,Sizeof(trans.SellerAccount));
-      ms.Write(trans.new_accountkey.EC_OpenSSL_NID,Sizeof(trans.new_accountkey.EC_OpenSSL_NID));
-      if length(trans.new_accountkey.x)>0 then
-        ms.WriteBuffer(trans.new_accountkey.x[1],length(trans.new_accountkey.x));
-      if length(trans.new_accountkey.y)>0 then
-        ms.WriteBuffer(trans.new_accountkey.y[1],length(trans.new_accountkey.y));
-    end;
-    SetLength(Result,ms.Size);
-    ms.Position := 0;
-    ms.ReadBuffer(Result[1],ms.Size);
-  finally
-    ms.Free;
-  end;
-end;
-
 procedure TOpTransaction.InitializeData;
 procedure TOpTransaction.InitializeData;
 begin
 begin
   inherited;
   inherited;
@@ -1128,6 +1108,47 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TOpTransaction.GetDigestToSign(current_protocol : Word): TRawBytes;
+Var ms : TMemoryStream;
+  b : Byte;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(FData.sender,Sizeof(FData.sender));
+    ms.Write(FData.n_operation,Sizeof(FData.n_operation));
+    ms.Write(FData.target,Sizeof(FData.target));
+    ms.Write(FData.amount,Sizeof(FData.amount));
+    ms.Write(FData.fee,Sizeof(FData.fee));
+    if length(FData.payload)>0 then
+      ms.WriteBuffer(FData.payload[1],length(FData.payload));
+    ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
+    if length(FData.public_key.x)>0 then
+      ms.WriteBuffer(FData.public_key.x[1],length(FData.public_key.x));
+    if length(FData.public_key.y)>0 then
+      ms.WriteBuffer(FData.public_key.y[1],length(FData.public_key.y));
+    if FData.opTransactionStyle=buy_account then begin
+      ms.Write(FData.AccountPrice,Sizeof(FData.AccountPrice));
+      ms.Write(FData.SellerAccount,Sizeof(FData.SellerAccount));
+      ms.Write(FData.new_accountkey.EC_OpenSSL_NID,Sizeof(FData.new_accountkey.EC_OpenSSL_NID));
+      if length(FData.new_accountkey.x)>0 then
+        ms.WriteBuffer(FData.new_accountkey.x[1],length(FData.new_accountkey.x));
+      if length(FData.new_accountkey.y)>0 then
+        ms.WriteBuffer(FData.new_accountkey.y[1],length(FData.new_accountkey.y));
+    end;
+    if (current_protocol<=CT_PROTOCOL_3) then begin
+      ms.Position := 0;
+      SetLength(Result,ms.Size);
+      ms.ReadBuffer(Result[1],ms.Size);
+    end else begin
+      b := OpType;
+      ms.Write(b,1);
+      Result := TCrypto.DoSha256(ms.Memory,ms.Size);
+    end;
+  finally
+    ms.Free;
+  end;
+end;
+
 { TOpChangeKey }
 { TOpChangeKey }
 
 
 procedure TOpChangeKey.AffectedAccounts(list: TList);
 procedure TOpChangeKey.AffectedAccounts(list: TList);
@@ -1142,7 +1163,7 @@ begin
   else Result := 0;
   else Result := 0;
 end;
 end;
 
 
-constructor TOpChangeKey.Create(account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; payload: TRawBytes);
+constructor TOpChangeKey.Create(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; payload: TRawBytes);
 begin
 begin
   inherited Create;
   inherited Create;
   FData.account_signer := account_signer;
   FData.account_signer := account_signer;
@@ -1158,10 +1179,13 @@ begin
   // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
   // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
   // FData.public_key := key.PublicKey;
   // FData.public_key := key.PublicKey;
   FData.new_accountkey := new_account_key;
   FData.new_accountkey := new_account_key;
-  If Not DoSignOperation(key,FData) then begin
-    TLog.NewLog(lterror,Classname,'Error signing a new Change key');
+  if Assigned(key) then begin
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
+    FHasValidSignature := true;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a new Change key');
     FHasValidSignature := false;
     FHasValidSignature := false;
-  end else FHasValidSignature := true;
+  end;
 end;
 end;
 
 
 function TOpChangeKey.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean;
 function TOpChangeKey.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean;
@@ -1245,7 +1269,7 @@ begin
     end;
     end;
   end;
   end;
 
 
-  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
+  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol),FData.sign) then begin
     errors := 'Invalid sign';
     errors := 'Invalid sign';
     FHasValidSignature := false;
     FHasValidSignature := false;
     exit;
     exit;
@@ -1268,23 +1292,6 @@ begin
          FData.fee,errors);
          FData.fee,errors);
 end;
 end;
 
 
-class function TOpChangeKey.DoSignOperation(key: TECPrivateKey; var op: TOpChangeKeyData): Boolean;
-var s : AnsiString;
-  _sign : TECDSA_SIG;
-begin
-  s := GetOperationHashToSign(op);
-  Try
-    _sign := TCrypto.ECDSASign(key.PrivateKey,s);
-    op.sign := _sign;
-    Result := true;
-  Except
-    On E:Exception do begin
-      Result := false;
-      TLog.NewLog(lterror,ClassName,'Error signing ChangeKey operation: '+E.Message);
-    end;
-  End;
-end;
-
 function TOpChangeKey.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
 function TOpChangeKey.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
 var ms : TMemoryStream;
 var ms : TMemoryStream;
   s : AnsiString;
   s : AnsiString;
@@ -1319,35 +1326,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-class function TOpChangeKey.GetOperationHashToSign(const op: TOpChangeKeyData): TRawBytes;
-var ms : TMemoryStream;
-  s : AnsiString;
-begin
-  ms := TMemoryStream.Create;
-  try
-    ms.Write(op.account_signer,Sizeof(op.account_signer));
-    if (op.account_signer<>op.account_target) then ms.Write(op.account_target,Sizeof(op.account_target));
-    ms.Write(op.n_operation,Sizeof(op.n_operation));
-    ms.Write(op.fee,Sizeof(op.fee));
-    if length(op.payload)>0 then
-      ms.WriteBuffer(op.payload[1],length(op.payload));
-    ms.Write(op.public_key.EC_OpenSSL_NID,Sizeof(op.public_key.EC_OpenSSL_NID));
-    if length(op.public_key.x)>0 then
-      ms.WriteBuffer(op.public_key.x[1],length(op.public_key.x));
-    if length(op.public_key.y)>0 then
-      ms.WriteBuffer(op.public_key.y[1],length(op.public_key.y));
-    s := TAccountComp.AccountKey2RawString(op.new_accountkey);
-    if length(s)>0 then
-      ms.WriteBuffer(s[1],length(s));
-    ms.Position := 0;
-    setlength(Result,ms.Size);
-    ms.ReadBuffer(Result[1],ms.Size);
-  finally
-    ms.Free;
-  end;
-end;
-
-
 procedure TOpChangeKey.InitializeData;
 procedure TOpChangeKey.InitializeData;
 begin
 begin
   inherited;
   inherited;
@@ -1463,6 +1441,41 @@ begin
     TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
     TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
 end;
 end;
 
 
+function TOpChangeKey.GetDigestToSign(current_protocol : Word): TRawBytes;
+var ms : TMemoryStream;
+  s : AnsiString;
+  b : Byte;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(FData.account_signer,Sizeof(FData.account_signer));
+    if (FData.account_signer<>FData.account_target) then ms.Write(FData.account_target,Sizeof(FData.account_target));
+    ms.Write(FData.n_operation,Sizeof(FData.n_operation));
+    ms.Write(FData.fee,Sizeof(FData.fee));
+    if length(FData.payload)>0 then
+      ms.WriteBuffer(FData.payload[1],length(FData.payload));
+    ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
+    if length(FData.public_key.x)>0 then
+      ms.WriteBuffer(FData.public_key.x[1],length(FData.public_key.x));
+    if length(FData.public_key.y)>0 then
+      ms.WriteBuffer(FData.public_key.y[1],length(FData.public_key.y));
+    s := TAccountComp.AccountKey2RawString(FData.new_accountkey);
+    if length(s)>0 then
+      ms.WriteBuffer(s[1],length(s));
+    if (current_protocol<=CT_PROTOCOL_3) then begin
+      ms.Position := 0;
+      setlength(Result,ms.Size);
+      ms.ReadBuffer(Result[1],ms.Size);
+    end else begin
+      b := OpType;
+      ms.Write(b,1);
+      Result := TCrypto.DoSha256(ms.Memory,ms.Size);
+    end;
+  finally
+    ms.Free;
+  end;
+end;
+
 { TOpChangeKeySigned }
 { TOpChangeKeySigned }
 
 
 class function TOpChangeKeySigned.OpType: Byte;
 class function TOpChangeKeySigned.OpType: Byte;
@@ -1517,7 +1530,7 @@ begin
     exit;
     exit;
   end;
   end;
   FPrevious_Signer_updated_block := acc.updated_block;
   FPrevious_Signer_updated_block := acc.updated_block;
-  Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,FData.account,FData.account,FData.n_operation,0,FData.fee,errors);
+  Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,FData.account,FData.account,FData.account,FData.n_operation,0,FData.fee,errors);
 end;
 end;
 
 
 function TOpRecoverFounds.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
 function TOpRecoverFounds.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
@@ -1616,6 +1629,11 @@ begin
     TAccountComp.FormatMoney(FData.fee),fData.n_operation]);
     TAccountComp.FormatMoney(FData.fee),fData.n_operation]);
 end;
 end;
 
 
+function TOpRecoverFounds.GetDigestToSign(current_protocol : Word): TRawBytes;
+begin
+  Result := ''; // Nothing to be signed!
+end;
+
 { TOpListAccount }
 { TOpListAccount }
 
 
 procedure TOpListAccount.AffectedAccounts(list: TList);
 procedure TOpListAccount.AffectedAccounts(list: TList);
@@ -1735,7 +1753,7 @@ begin
     exit;
     exit;
   end;
   end;
 
 
-  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
+  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol),FData.sign) then begin
     errors := 'Invalid sign';
     errors := 'Invalid sign';
     FHasValidSignature := false;
     FHasValidSignature := false;
     exit;
     exit;
@@ -1763,60 +1781,12 @@ begin
          FData.fee,errors);
          FData.fee,errors);
 end;
 end;
 
 
-class function TOpListAccount.DoSignOperation(key: TECPrivateKey; var operation: TOpListAccountData): Boolean;
-var s : AnsiString;
-  _sign : TECDSA_SIG;
-begin
-  s := GetOperationHashToSign(operation);
-  Try
-    _sign := TCrypto.ECDSASign(key.PrivateKey,s);
-    operation.sign := _sign;
-    Result := true;
-  Except
-    On E:Exception do begin
-      Result := false;
-      TLog.NewLog(lterror,ClassName,'Error signing Account for sale operation: '+E.Message);
-    end;
-  End;
-end;
-
 function TOpListAccount.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
 function TOpListAccount.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
 begin
 begin
   // This Operation is new from protocol V2, so we cannot hash it as a previous protocol!
   // This Operation is new from protocol V2, so we cannot hash it as a previous protocol!
   Result := inherited GetBufferForOpHash(true);
   Result := inherited GetBufferForOpHash(true);
 end;
 end;
 
 
-class function TOpListAccount.GetOperationHashToSign(const operation: TOpListAccountData): TRawBytes;
-var ms : TMemoryStream;
-  s : AnsiString;
-begin
-  ms := TMemoryStream.Create;
-  try
-    ms.Write(operation.account_signer,Sizeof(operation.account_signer));
-    ms.Write(operation.account_target,Sizeof(operation.account_target));
-    ms.Write(operation.n_operation,Sizeof(operation.n_operation));
-    ms.Write(operation.account_price,Sizeof(operation.account_price));
-    ms.Write(operation.account_to_pay,Sizeof(operation.account_to_pay));
-    ms.Write(operation.fee,Sizeof(operation.fee));
-    if length(operation.payload)>0 then
-      ms.WriteBuffer(operation.payload[1],length(operation.payload));
-    ms.Write(operation.public_key.EC_OpenSSL_NID,Sizeof(operation.public_key.EC_OpenSSL_NID));
-    if length(operation.public_key.x)>0 then
-      ms.WriteBuffer(operation.public_key.x[1],length(operation.public_key.x));
-    if length(operation.public_key.y)>0 then
-      ms.WriteBuffer(operation.public_key.y[1],length(operation.public_key.y));
-    s := TAccountComp.AccountKey2RawString(operation.new_public_key);
-    if length(s)>0 then
-      ms.WriteBuffer(s[1],length(s));
-    ms.Write(operation.locked_until_block,Sizeof(operation.locked_until_block));
-    ms.Position := 0;
-    setlength(Result,ms.Size);
-    ms.ReadBuffer(Result[1],ms.Size);
-  finally
-    ms.Free;
-  end;
-end;
-
 procedure TOpListAccount.InitializeData;
 procedure TOpListAccount.InitializeData;
 begin
 begin
   inherited;
   inherited;
@@ -1988,9 +1958,47 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TOpListAccount.GetDigestToSign(current_protocol : Word): TRawBytes;
+var ms : TMemoryStream;
+  s : TRawBytes;
+  b : Byte;
+begin
+  ms := TMemoryStream.Create;
+  try
+    ms.Write(FData.account_signer,Sizeof(FData.account_signer));
+    ms.Write(FData.account_target,Sizeof(FData.account_target));
+    ms.Write(FData.n_operation,Sizeof(FData.n_operation));
+    ms.Write(FData.account_price,Sizeof(FData.account_price));
+    ms.Write(FData.account_to_pay,Sizeof(FData.account_to_pay));
+    ms.Write(FData.fee,Sizeof(FData.fee));
+    if length(FData.payload)>0 then
+      ms.WriteBuffer(FData.payload[1],length(FData.payload));
+    ms.Write(FData.public_key.EC_OpenSSL_NID,Sizeof(FData.public_key.EC_OpenSSL_NID));
+    if length(FData.public_key.x)>0 then
+      ms.WriteBuffer(FData.public_key.x[1],length(FData.public_key.x));
+    if length(FData.public_key.y)>0 then
+      ms.WriteBuffer(FData.public_key.y[1],length(FData.public_key.y));
+    s := TAccountComp.AccountKey2RawString(FData.new_public_key);
+    if length(s)>0 then
+      ms.WriteBuffer(s[1],length(s));
+    ms.Write(FData.locked_until_block,Sizeof(FData.locked_until_block));
+    if (current_protocol<=CT_PROTOCOL_3) then begin
+      ms.Position := 0;
+      setlength(Result,ms.Size);
+      ms.ReadBuffer(Result[1],ms.Size);
+    end else begin
+      b := OpType;
+      ms.Write(b,1);
+      Result := TCrypto.DoSha256(ms.Memory,ms.Size);
+    end;
+  finally
+    ms.Free;
+  end;
+end;
+
 { TOpListAccountForSale }
 { TOpListAccountForSale }
 
 
-constructor TOpListAccountForSale.CreateListAccountForSale(account_signer, n_operation, account_target: Cardinal;
+constructor TOpListAccountForSale.CreateListAccountForSale(current_protocol : Word; account_signer, n_operation, account_target: Cardinal;
   account_price, fee: UInt64; account_to_pay: Cardinal;
   account_price, fee: UInt64; account_to_pay: Cardinal;
   new_public_key: TAccountKey; locked_until_block: Cardinal; key: TECPrivateKey;
   new_public_key: TAccountKey; locked_until_block: Cardinal; key: TECPrivateKey;
   payload: TRawBytes);
   payload: TRawBytes);
@@ -2008,10 +2016,14 @@ begin
   // FData.public_key := key.PublicKey;
   // FData.public_key := key.PublicKey;
   FData.new_public_key := new_public_key;
   FData.new_public_key := new_public_key;
   FData.locked_until_block := locked_until_block;
   FData.locked_until_block := locked_until_block;
-  If Not DoSignOperation(key,FData) then begin
-    TLog.NewLog(lterror,Classname,'Error signing a new list account for sale operation');
+
+  if Assigned(key) then begin
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
+    FHasValidSignature := true;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a new list account for sale operation');
     FHasValidSignature := false;
     FHasValidSignature := false;
-  end else FHasValidSignature := true;
+  end;
 end;
 end;
 
 
 function TOpListAccountForSale.IsDelist: Boolean;
 function TOpListAccountForSale.IsDelist: Boolean;
@@ -2026,7 +2038,7 @@ end;
 
 
 { TOpDelistAccountForSale }
 { TOpDelistAccountForSale }
 
 
-constructor TOpDelistAccountForSale.CreateDelistAccountForSale(account_signer, n_operation, account_target: Cardinal; fee: UInt64; key: TECPrivateKey; payload: TRawBytes);
+constructor TOpDelistAccountForSale.CreateDelistAccountForSale(current_protocol : Word; account_signer, n_operation, account_target: Cardinal; fee: UInt64; key: TECPrivateKey; payload: TRawBytes);
 begin
 begin
   inherited Create;
   inherited Create;
   FData.account_signer := account_signer;
   FData.account_signer := account_signer;
@@ -2035,10 +2047,13 @@ begin
   FData.n_operation := n_operation;
   FData.n_operation := n_operation;
   FData.fee := fee;
   FData.fee := fee;
   FData.payload := payload;
   FData.payload := payload;
-  If Not DoSignOperation(key,FData) then begin
-    TLog.NewLog(lterror,Classname,'Error signing a delist account operation');
+  if Assigned(key) then begin
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
+    FHasValidSignature := true;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a delist account operation');
     FHasValidSignature := false;
     FHasValidSignature := false;
-  end else FHasValidSignature := true;
+  end;
 end;
 end;
 
 
 function TOpDelistAccountForSale.IsDelist: Boolean;
 function TOpDelistAccountForSale.IsDelist: Boolean;
@@ -2053,7 +2068,7 @@ end;
 
 
 { TOpBuyAccount }
 { TOpBuyAccount }
 
 
-constructor TOpBuyAccount.CreateBuy(account_number, n_operation, account_to_buy,
+constructor TOpBuyAccount.CreateBuy(current_protocol : Word; account_number, n_operation, account_to_buy,
   account_to_pay: Cardinal; price, amount, fee: UInt64;
   account_to_pay: Cardinal; price, amount, fee: UInt64;
   new_public_key: TAccountKey; key: TECPrivateKey; payload: TRawBytes);
   new_public_key: TAccountKey; key: TECPrivateKey; payload: TRawBytes);
 begin
 begin
@@ -2070,10 +2085,14 @@ begin
   FData.AccountPrice := price;
   FData.AccountPrice := price;
   FData.SellerAccount := account_to_pay;
   FData.SellerAccount := account_to_pay;
   FData.new_accountkey := new_public_key;
   FData.new_accountkey := new_public_key;
-  If Not DoSignOperation(key,FData) then begin
-    TLog.NewLog(lterror,Classname,'Error signing a new Buy operation');
+
+  if Assigned(key) then begin
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(current_protocol));
+    FHasValidSignature := true;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a new Buy operation');
     FHasValidSignature := false;
     FHasValidSignature := false;
-  end else FHasValidSignature := true;
+  end;
 end;
 end;
 
 
 procedure TOpBuyAccount.InitializeData;
 procedure TOpBuyAccount.InitializeData;
@@ -2087,6 +2106,315 @@ begin
   Result := CT_Op_BuyAccount;
   Result := CT_Op_BuyAccount;
 end;
 end;
 
 
+
+{ TOpData }
+
+procedure TOpData.InitializeData;
+begin
+  inherited InitializeData;
+  FData := CT_TOpDataData_NUL;
+end;
+
+function TOpData.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
+begin
+  Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
+  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));
+  Stream.Write(FData.dataType,Sizeof(FData.dataType));
+  Stream.Write(FData.dataSequence,Sizeof(FData.dataSequence));
+  Stream.Write(FData.amount,Sizeof(FData.amount));
+  Stream.Write(FData.fee,Sizeof(FData.fee));
+  TStreamOp.WriteAnsiString(Stream,FData.payload);
+  TStreamOp.WriteAnsiString(Stream,FData.sign.r);
+  TStreamOp.WriteAnsiString(Stream,FData.sign.s);
+  Result := true;
+end;
+
+function TOpData.LoadOpFromStream(Stream: TStream; LoadExtendedData: Boolean): Boolean;
+begin
+  Result := false;
+  if Stream.Size-Stream.Position < 36  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));
+  Stream.Read(FData.dataType,Sizeof(FData.dataType));
+  Stream.Read(FData.dataSequence,Sizeof(FData.dataSequence));
+  Stream.Read(FData.amount,Sizeof(FData.amount));
+  Stream.Read(FData.fee,Sizeof(FData.fee));
+  if TStreamOp.ReadAnsiString(Stream,FData.payload)<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.sign.r)<0 then exit;
+  if TStreamOp.ReadAnsiString(Stream,FData.sign.s)<0 then exit;
+  Result := true;
+end;
+
+procedure TOpData.FillOperationResume(Block: Cardinal;
+  getInfoForAllAccounts: Boolean; Affected_account_number: Cardinal;
+  var OperationResume: TOperationResume);
+begin
+  inherited FillOperationResume(Block, getInfoForAllAccounts,Affected_account_number, OperationResume);
+  if (getInfoForAllAccounts) then
+    OperationResume.OpSubtype := CT_OpSubtype_Data_GlobalInfo
+  else if (Affected_account_number=FData.account_sender) then
+    OperationResume.OpSubtype := CT_OpSubtype_Data_Sender
+  else if (Affected_account_number=FData.account_target) then
+    OperationResume.OpSubtype := CT_OpSubtype_Data_Receiver
+  else if (Affected_account_number=FData.account_signer) then
+    OperationResume.OpSubtype := CT_OpSubtype_Data_Signer;
+
+  SetLength(OperationResume.Senders,1);
+  OperationResume.Senders[0] := CT_TMultiOpSender_NUL;
+
+  OperationResume.Senders[0].Account:=FData.account_sender;
+  OperationResume.Senders[0].Payload:=FData.payload;
+  if (FData.account_sender=FData.account_signer) then 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);
+  end else begin
+    OperationResume.Senders[0].Amount:=Int64(FData.amount)*(-1);
+    SetLength(OperationResume.Changers,1);
+    OperationResume.Changers[0] := CT_TMultiOpChangeInfo_NUL;
+    OperationResume.Changers[0].Account:=FData.account_signer;
+    OperationResume.Changers[0].Fee:=FData.fee;
+    OperationResume.Changers[0].N_Operation:=FData.n_operation;
+    OperationResume.Changers[0].Signature:=FData.sign;
+  end;
+  //
+  SetLength(OperationResume.Receivers,1);
+  OperationResume.Receivers[0] := CT_TMultiOpReceiver_NUL;
+  OperationResume.Receivers[0].Account:=FData.account_target;
+  OperationResume.Receivers[0].Amount:=FData.amount;
+  OperationResume.Receivers[0].Payload:=FData.payload;
+  //
+  OperationResume.n_operation:=FData.n_operation;
+  if (Affected_account_number = FData.account_signer) or (getInfoForAllAccounts) then begin
+    OperationResume.Fee:=Int64(FData.fee)*(-1);
+  end else OperationResume.Fee:=0;
+  OperationResume.OperationTxt := ToString;
+  if (getInfoForAllAccounts) then OperationResume.Amount:= OperationAmount
+  else OperationResume.Amount := OperationAmountByAccount(Affected_account_number);
+end;
+
+class function TOpData.OpType: Byte;
+begin
+  Result := CT_Op_Data;
+end;
+
+function TOpData.GetBufferForOpHash(UseProtocolV2: Boolean): TRawBytes;
+begin
+  Result:=inherited GetBufferForOpHash(UseProtocolV2);
+end;
+
+function TOpData.DoOperation(
+  AccountPreviousUpdatedBlock: TAccountPreviousBlockInfo;
+  AccountTransaction: TPCSafeBoxTransaction; var errors: AnsiString): Boolean;
+Var account_signer, account_sender, account_target : TAccount;
+begin
+  Result := false;
+  if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_4) then begin
+    errors := 'OpData is not allowed on Protocol < 4';
+    exit;
+  end;
+  if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
+    errors := 'Invalid signer account number';
+    Exit;
+  end;
+  if (FData.account_sender>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
+    errors := 'Invalid sender account number';
+    Exit;
+  end;
+  if (FData.account_target>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
+    errors := 'Invalid target account number';
+    Exit;
+  end;
+  if TAccountComp.IsAccountBlockedByProtocol(FData.account_signer, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := 'Signer account is blocked for protocol';
+    Exit;
+  end;
+  if TAccountComp.IsAccountBlockedByProtocol(FData.account_sender, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := 'Sender account is blocked for protocol';
+    Exit;
+  end;
+  if TAccountComp.IsAccountBlockedByProtocol(FData.account_target, AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := 'Target account is blocked for protocol';
+    Exit;
+  end;
+
+  if (FData.fee<0) Or (FData.fee>CT_MaxTransactionFee) then begin
+    errors := 'Invalid fee: '+Inttostr(FData.fee);
+    exit;
+  end;
+  if (FData.amount<0) Or (FData.amount>CT_MaxTransactionAmount) then begin
+    errors := 'Invalid amount: '+Inttostr(FData.fee);
+    exit;
+  end;
+
+  account_signer := AccountTransaction.Account(FData.account_signer);
+  account_sender := AccountTransaction.Account(FData.account_sender);
+  account_target := AccountTransaction.Account(FData.account_target);
+  // Is signer locked?
+  if (TAccountComp.IsAccountLocked(account_signer.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
+    errors := 'Signer account is currently locked';
+    exit;
+  end;
+  if (FData.account_signer<>FData.account_sender) then begin
+    // Both accounts must have same PUBLIC KEY!
+    if Not TAccountComp.EqualAccountKeys(account_signer.accountInfo.accountKey,account_sender.accountInfo.accountKey) then begin
+      errors := 'Signer and sender accounts have different public key';
+      Exit;
+    end;
+    if (account_signer.balance<FData.fee) then begin
+      errors := 'Insuficient signer funds';
+      exit;
+    end;
+    if (account_sender.balance<FData.amount) then begin
+      errors := 'Insuficient sender funds';
+      exit;
+    end;
+    // Is sender locked?
+    if (TAccountComp.IsAccountLocked(account_sender.accountInfo,AccountTransaction.FreezedSafeBox.BlocksCount)) then begin
+      errors := 'Sender account is currently locked';
+      exit;
+    end;
+  end else begin
+    // signer = sender
+    if (account_signer.balance<(FData.fee + FData.amount)) then begin
+      errors := 'Insuficient funds';
+      exit;
+    end;
+  end;
+
+  if ((account_signer.n_operation+1)<>FData.n_operation) then begin
+    errors := 'Invalid n_operation';
+    Exit;
+  end;
+
+  if (length(FData.payload)>CT_MaxPayloadSize) then begin
+    errors := 'Invalid Payload size:'+inttostr(length(FData.payload))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
+    Exit;
+  end;
+
+  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol),FData.sign) then begin
+    errors := 'Invalid sign';
+    FHasValidSignature := false;
+    Exit;
+  end else FHasValidSignature := true;
+
+  Result := AccountTransaction.TransferAmount(AccountPreviousUpdatedBlock,
+    FData.account_sender,FData.account_signer,FData.account_target,
+    FData.n_operation,FData.amount,FData.fee,errors);
+end;
+
+function TOpData.OperationAmount: Int64;
+begin
+  Result := FData.amount + FData.fee;
+end;
+
+function TOpData.OperationFee: Int64;
+begin
+  Result := FData.fee;
+end;
+
+function TOpData.OperationPayload: TRawBytes;
+begin
+  Result := FData.payload;
+end;
+
+function TOpData.SignerAccount: Cardinal;
+begin
+  Result := FData.account_signer;
+end;
+
+function TOpData.DestinationAccount: Int64;
+begin
+  Result := FData.account_target;
+end;
+
+function TOpData.N_Operation: Cardinal;
+begin
+  Result := FData.n_operation;
+end;
+
+procedure TOpData.AffectedAccounts(list: TList);
+begin
+  list.Add(TObject(FData.account_signer));
+  if (FData.account_signer<>FData.account_sender) then begin
+    list.Add(TObject(FData.account_sender));
+  end;
+  if (FData.account_signer<>FData.account_target) And (FData.account_sender<>FData.account_target) then begin
+    list.Add(TObject(FData.account_target));
+  end;
+end;
+
+function TOpData.OperationAmountByAccount(account: Cardinal): Int64;
+begin
+  Result := 0;
+  if (account = FData.account_signer) then begin
+    Result := Int64(FData.fee) * (-1);
+  end;
+  if (account = FData.account_sender) then begin
+    Result := Result + (Int64(FData.amount)*(-1));
+  end;
+  if (account = FData.account_target) then begin
+    Result := Result + FData.amount;
+  end;
+end;
+
+constructor TOpData.CreateOpData(account_signer, account_sender,
+  account_target: Cardinal; signer_key: TECPrivateKey; n_operation: Cardinal;
+  dataType, dataSequence: Word; amount, fee: UInt64; payload: TRawBytes);
+begin
+  Inherited Create;
+  FData.account_sender:=account_sender;
+  FData.account_signer:=account_signer;
+  FData.account_target:=account_target;
+  FData.amount:=amount;
+  FData.fee:=fee;
+  FData.n_operation:=n_operation;
+  FData.payload:=payload;
+  FData.dataSequence:=dataSequence;
+  FData.dataType:=dataType;
+  if Assigned(signer_key) then begin
+    FData.sign := TCrypto.ECDSASign(signer_key.PrivateKey, GetDigestToSign(CT_PROTOCOL_4));
+    FHasValidSignature := true;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a new OpData');
+    FHasValidSignature := false;
+  end;
+end;
+
+function TOpData.toString: String;
+begin
+  Result := Format('OpData from:%d to:%d type:%d sequence:%d Amount:%s',
+    [FData.account_sender,FData.account_target,FData.dataType,FData.dataSequence,
+     TAccountComp.FormatMoney(FData.amount)]);
+end;
+
+function TOpData.GetDigestToSign(current_protocol: Word): TRawBytes;
+var Stream : TStream;
+  b : Byte;
+begin
+  Stream := TMemoryStream.Create;
+  Try
+    Stream.Write(FData.account_signer,Sizeof(FData.account_signer));
+    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));
+    Stream.Write(FData.dataType,Sizeof(FData.dataType));
+    Stream.Write(FData.dataSequence,Sizeof(FData.dataSequence));
+    Stream.Write(FData.amount,Sizeof(FData.amount));
+    Stream.Write(FData.fee,Sizeof(FData.fee));
+    TStreamOp.WriteAnsiString(Stream,FData.payload);
+    b := OpType;
+    Stream.Write(b,1);
+    Result := TStreamOp.SaveStreamToRaw(Stream);
+  finally
+    Stream.Free;
+  end;
+end;
+
 initialization
 initialization
   RegisterOperationsClass;
   RegisterOperationsClass;
 end.
 end.

+ 67 - 46
src/core/URPC.pas

@@ -53,7 +53,7 @@ Type
     class procedure FillBlockObject(nBlock : Cardinal; ANode : TNode; jsonObject: TPCJSONObject);
     class procedure FillBlockObject(nBlock : Cardinal; ANode : TNode; jsonObject: TPCJSONObject);
     class procedure FillOperationObject(Const OPR : TOperationResume; currentNodeBlocksCount : Cardinal; jsonObject : TPCJSONObject);
     class procedure FillOperationObject(Const OPR : TOperationResume; currentNodeBlocksCount : Cardinal; jsonObject : TPCJSONObject);
     class procedure FillOperationsHashTreeObject(Const OperationsHashTree : TOperationsHashTree; jsonObject : TPCJSONObject);
     class procedure FillOperationsHashTreeObject(Const OperationsHashTree : TOperationsHashTree; jsonObject : TPCJSONObject);
-    class procedure FillMultiOperationObject(Const multiOperation : TOpMultiOperation; jsonObject : TPCJSONObject);
+    class procedure FillMultiOperationObject(current_protocol : Word; Const multiOperation : TOpMultiOperation; jsonObject : TPCJSONObject);
     class procedure FillPublicKeyObject(const PubKey : TAccountKey; jsonObj : TPCJSONObject);
     class procedure FillPublicKeyObject(const PubKey : TAccountKey; jsonObj : TPCJSONObject);
   end;
   end;
 
 
@@ -294,7 +294,7 @@ begin
   jsonObject.GetAsVariant('rawoperations').Value:=OperationsHashTreeToHexaString(OperationsHashTree);
   jsonObject.GetAsVariant('rawoperations').Value:=OperationsHashTreeToHexaString(OperationsHashTree);
 end;
 end;
 
 
-class procedure TPascalCoinJSONComp.FillMultiOperationObject(const multiOperation: TOpMultiOperation; jsonObject: TPCJSONObject);
+class procedure TPascalCoinJSONComp.FillMultiOperationObject(current_protocol : Word; const multiOperation: TOpMultiOperation; jsonObject: TPCJSONObject);
 Var i, nSigned, nNotSigned : Integer;
 Var i, nSigned, nNotSigned : Integer;
   opht : TOperationsHashTree;
   opht : TOperationsHashTree;
   jsonArr : TPCJSONArray;
   jsonArr : TPCJSONArray;
@@ -350,6 +350,10 @@ begin
   end;
   end;
   jsonObject.GetAsVariant('amount').Value:=ToJSONCurrency( multiOperation.OperationAmount );
   jsonObject.GetAsVariant('amount').Value:=ToJSONCurrency( multiOperation.OperationAmount );
   jsonObject.GetAsVariant('fee').Value:=ToJSONCurrency( multiOperation.OperationFee );
   jsonObject.GetAsVariant('fee').Value:=ToJSONCurrency( multiOperation.OperationFee );
+  // New params for third party signing: (3.0.2)
+  if (current_protocol>CT_PROTOCOL_3) then begin
+    jsonObject.GetAsVariant('digest').Value:=TCrypto.ToHexaString(multiOperation.GetDigestToSign(current_protocol));
+  end;
 
 
   jsonObject.GetAsVariant('senders_count').Value:=Length(multiOperation.Data.txSenders);
   jsonObject.GetAsVariant('senders_count').Value:=Length(multiOperation.Data.txSenders);
   jsonObject.GetAsVariant('receivers_count').Value:=Length(multiOperation.Data.txReceivers);
   jsonObject.GetAsVariant('receivers_count').Value:=Length(multiOperation.Data.txReceivers);
@@ -852,7 +856,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
 
 
   // This function creates a TOpTransaction without looking for balance/private key of sender account
   // This function creates a TOpTransaction without looking for balance/private key of sender account
   // It assumes that sender,target,sender_last_n_operation,senderAccountKey and targetAccountKey are correct
   // It assumes that sender,target,sender_last_n_operation,senderAccountKey and targetAccountKey are correct
-  Function CreateOperationTransaction(sender, target, sender_last_n_operation : Cardinal; amount, fee : UInt64;
+  Function CreateOperationTransaction(current_protocol : Word; sender, target, sender_last_n_operation : Cardinal; amount, fee : UInt64;
     Const senderAccounKey, targetAccountKey : TAccountKey; Const RawPayload : TRawBytes;
     Const senderAccounKey, targetAccountKey : TAccountKey; Const RawPayload : TRawBytes;
     Const Payload_method, EncodePwd : AnsiString) : TOpTransaction;
     Const Payload_method, EncodePwd : AnsiString) : TOpTransaction;
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
@@ -892,7 +896,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
     end else f_raw := '';
     end else f_raw := '';
-    Result := TOpTransaction.CreateTransaction(sender,sender_last_n_operation+1,target,_RPCServer.FWalletKeys.Key[i].PrivateKey,amount,fee,f_raw);
+    Result := TOpTransaction.CreateTransaction(current_protocol, sender,sender_last_n_operation+1,target,_RPCServer.FWalletKeys.Key[i].PrivateKey,amount,fee,f_raw);
     if Not Result.HasValidSignature then begin
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
       FreeAndNil(Result);
       ErrorNum:=CT_RPC_ErrNum_InternalError;
       ErrorNum:=CT_RPC_ErrNum_InternalError;
@@ -926,7 +930,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       sacc := FNode.Operations.SafeBoxTransaction.Account(sender);
       sacc := FNode.Operations.SafeBoxTransaction.Account(sender);
       tacc := FNode.Operations.SafeBoxTransaction.Account(target);
       tacc := FNode.Operations.SafeBoxTransaction.Account(target);
 
 
-      opt := CreateOperationTransaction(sender,target,sacc.n_operation,amount,fee,sacc.accountInfo.accountKey,tacc.accountInfo.accountKey,RawPayload,Payload_method,EncodePwd);
+      opt := CreateOperationTransaction(FNode.Bank.SafeBox.CurrentProtocol,sender,target,sacc.n_operation,amount,fee,sacc.accountInfo.accountKey,tacc.accountInfo.accountKey,RawPayload,Payload_method,EncodePwd);
       if opt=nil then exit;
       if opt=nil then exit;
       try
       try
         If not FNode.AddOperation(Nil,opt,errors) then begin
         If not FNode.AddOperation(Nil,opt,errors) then begin
@@ -945,7 +949,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
-  Function SignOpSendTo(Const HexaStringOperationsHashTree : TRawBytes; sender, target : Cardinal;
+  Function SignOpSendTo(Const HexaStringOperationsHashTree : TRawBytes; current_protocol : Word;
+    sender, target : Cardinal;
     Const senderAccounKey, targetAccountKey : TAccountKey;
     Const senderAccounKey, targetAccountKey : TAccountKey;
     last_sender_n_operation : Cardinal;
     last_sender_n_operation : Cardinal;
     amount, fee : UInt64; Const RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : Boolean;
     amount, fee : UInt64; Const RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : Boolean;
@@ -961,7 +966,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       Exit;
       Exit;
     end;
     end;
     Try
     Try
-      opt := CreateOperationTransaction(sender,target,last_sender_n_operation,amount,fee,senderAccounKey,targetAccountKey,RawPayload,Payload_method,EncodePwd);
+      opt := CreateOperationTransaction(current_protocol, sender,target,last_sender_n_operation,amount,fee,senderAccounKey,targetAccountKey,RawPayload,Payload_method,EncodePwd);
       if opt=nil then exit;
       if opt=nil then exit;
       try
       try
         OperationsHashTree.AddOperationToHashTree(opt);
         OperationsHashTree.AddOperationToHashTree(opt);
@@ -977,7 +982,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
 
 
   // This function creates a TOpChangeKey without looking for private key of account
   // This function creates a TOpChangeKey without looking for private key of account
   // It assumes that account_signer,account_last_n_operation, account_target and account_pubkey are correct
   // It assumes that account_signer,account_last_n_operation, account_target and account_pubkey are correct
-  Function CreateOperationChangeKey(account_signer, account_last_n_operation, account_target : Cardinal; const account_pubkey, new_pubkey : TAccountKey; fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpChangeKey;
+  Function CreateOperationChangeKey(current_protocol : Word; account_signer, account_last_n_operation, account_target : Cardinal; const account_pubkey, new_pubkey : TAccountKey; fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpChangeKey;
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
   var i : Integer;
   var i : Integer;
     errors : AnsiString;
     errors : AnsiString;
@@ -1016,9 +1021,9 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       end;
       end;
     end else f_raw := '';
     end else f_raw := '';
     If account_signer=account_target then begin
     If account_signer=account_target then begin
-      Result := TOpChangeKey.Create(account_signer,account_last_n_operation+1,account_target,_RPCServer.FWalletKeys.Key[i].PrivateKey,new_pubkey,fee,f_raw);
+      Result := TOpChangeKey.Create(current_protocol,account_signer,account_last_n_operation+1,account_target,_RPCServer.FWalletKeys.Key[i].PrivateKey,new_pubkey,fee,f_raw);
     end else begin
     end else begin
-      Result := TOpChangeKeySigned.Create(account_signer,account_last_n_operation+1,account_target,_RPCServer.FWalletKeys.Key[i].PrivateKey,new_pubkey,fee,f_raw);
+      Result := TOpChangeKeySigned.Create(current_protocol,account_signer,account_last_n_operation+1,account_target,_RPCServer.FWalletKeys.Key[i].PrivateKey,new_pubkey,fee,f_raw);
     end;
     end;
     if Not Result.HasValidSignature then begin
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
       FreeAndNil(Result);
@@ -1045,7 +1050,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       end;
       end;
       acc_signer := FNode.Operations.SafeBoxTransaction.Account(account_signer);
       acc_signer := FNode.Operations.SafeBoxTransaction.Account(account_signer);
 
 
-      opck := CreateOperationChangeKey(account_signer,acc_signer.n_operation,account_target,acc_signer.accountInfo.accountKey,new_pub_key,fee,RawPayload,Payload_method,EncodePwd);
+      opck := CreateOperationChangeKey(FNode.Bank.SafeBox.CurrentProtocol,account_signer,acc_signer.n_operation,account_target,acc_signer.accountInfo.accountKey,new_pub_key,fee,RawPayload,Payload_method,EncodePwd);
       if not assigned(opck) then exit;
       if not assigned(opck) then exit;
       try
       try
         If not FNode.AddOperation(Nil,opck,errors) then begin
         If not FNode.AddOperation(Nil,opck,errors) then begin
@@ -1066,7 +1071,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
 
 
   // This function creates a TOpListAccountForSale without looking for actual state (cold wallet)
   // This function creates a TOpListAccountForSale without looking for actual state (cold wallet)
   // It assumes that account_number,account_last_n_operation and account_pubkey are correct
   // It assumes that account_number,account_last_n_operation and account_pubkey are correct
-  Function CreateOperationListAccountForSale(account_signer, account_last_n_operation, account_listed : Cardinal; const account_signer_pubkey: TAccountKey;
+  Function CreateOperationListAccountForSale(current_protocol : Word; account_signer, account_last_n_operation, account_listed : Cardinal; const account_signer_pubkey: TAccountKey;
     account_price : UInt64; locked_until_block : Cardinal; account_to_pay : Cardinal; Const new_account_pubkey : TAccountKey;
     account_price : UInt64; locked_until_block : Cardinal; account_to_pay : Cardinal; Const new_account_pubkey : TAccountKey;
     fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpListAccountForSale;
     fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpListAccountForSale;
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
@@ -1107,7 +1112,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
     end else f_raw := '';
     end else f_raw := '';
-    Result := TOpListAccountForSale.CreateListAccountForSale(account_signer,account_last_n_operation+1,account_listed,account_price,fee,account_to_pay,new_account_pubkey,locked_until_block,
+    Result := TOpListAccountForSale.CreateListAccountForSale(current_protocol, account_signer,account_last_n_operation+1,account_listed,account_price,fee,account_to_pay,new_account_pubkey,locked_until_block,
      _RPCServer.FWalletKeys.Key[i].PrivateKey,f_raw);
      _RPCServer.FWalletKeys.Key[i].PrivateKey,f_raw);
     if Not Result.HasValidSignature then begin
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
       FreeAndNil(Result);
@@ -1119,7 +1124,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
 
 
   // This function creates a TOpDelistAccountForSale without looking for actual state (cold wallet)
   // This function creates a TOpDelistAccountForSale without looking for actual state (cold wallet)
   // It assumes that account_number,account_last_n_operation are correct
   // It assumes that account_number,account_last_n_operation are correct
-  Function CreateOperationDelistAccountForSale(account_signer, account_last_n_operation, account_delisted : Cardinal; const account_signer_pubkey: TAccountKey;
+  Function CreateOperationDelistAccountForSale(current_protocol : Word; account_signer, account_last_n_operation, account_delisted : Cardinal; const account_signer_pubkey: TAccountKey;
     fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpDelistAccountForSale;
     fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpDelistAccountForSale;
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
   var i : Integer;
   var i : Integer;
@@ -1155,7 +1160,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
     end else f_raw := '';
     end else f_raw := '';
-    Result := TOpDelistAccountForSale.CreateDelistAccountForSale(account_signer,account_last_n_operation+1,account_delisted,fee,_RPCServer.FWalletKeys.Key[i].PrivateKey,f_raw);
+    Result := TOpDelistAccountForSale.CreateDelistAccountForSale(current_protocol,account_signer,account_last_n_operation+1,account_delisted,fee,_RPCServer.FWalletKeys.Key[i].PrivateKey,f_raw);
     if Not Result.HasValidSignature then begin
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
       FreeAndNil(Result);
       ErrorNum:=CT_RPC_ErrNum_InternalError;
       ErrorNum:=CT_RPC_ErrNum_InternalError;
@@ -1167,7 +1172,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
   // This function creates a TOpBuyAccount without looking for actual state (cold wallet)
   // This function creates a TOpBuyAccount without looking for actual state (cold wallet)
   // It assumes that account_number,account_last_n_operation and account_pubkey are correct
   // It assumes that account_number,account_last_n_operation and account_pubkey are correct
   // Also asumes that amount is >= price and other needed conditions
   // Also asumes that amount is >= price and other needed conditions
-  Function CreateOperationBuyAccount(account_number, account_last_n_operation : Cardinal; const account_pubkey: TAccountKey;
+  Function CreateOperationBuyAccount(current_protocol : Word; account_number, account_last_n_operation : Cardinal; const account_pubkey: TAccountKey;
     account_to_buy : Cardinal; account_price, amount : UInt64; account_to_pay : Cardinal; Const new_account_pubkey : TAccountKey;
     account_to_buy : Cardinal; account_price, amount : UInt64; account_to_pay : Cardinal; Const new_account_pubkey : TAccountKey;
     fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpBuyAccount;
     fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpBuyAccount;
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
@@ -1207,7 +1212,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
     end else f_raw := '';
     end else f_raw := '';
-    Result := TOpBuyAccount.CreateBuy(account_number,account_last_n_operation+1,account_to_buy,account_to_pay,account_price,amount,fee,new_account_pubkey,_RPCServer.FWalletKeys.Key[i].PrivateKey,f_raw);
+    Result := TOpBuyAccount.CreateBuy(current_protocol,account_number,account_last_n_operation+1,account_to_buy,account_to_pay,account_price,amount,fee,new_account_pubkey,_RPCServer.FWalletKeys.Key[i].PrivateKey,f_raw);
     if Not Result.HasValidSignature then begin
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
       FreeAndNil(Result);
       ErrorNum:=CT_RPC_ErrNum_InternalError;
       ErrorNum:=CT_RPC_ErrNum_InternalError;
@@ -1292,7 +1297,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
               Exit;
               Exit;
             end;
             end;
             acc := FNode.Operations.SafeBoxTransaction.Account(accountsnumber.Get(ian));
             acc := FNode.Operations.SafeBoxTransaction.Account(accountsnumber.Get(ian));
-            opck := CreateOperationChangeKey(acc.account,acc.n_operation,acc.account,acc.accountInfo.accountKey,new_pub_key,fee,RawPayload,Payload_method,EncodePwd);
+            opck := CreateOperationChangeKey(FNode.Bank.SafeBox.CurrentProtocol,acc.account,acc.n_operation,acc.account,acc.accountInfo.accountKey,new_pub_key,fee,RawPayload,Payload_method,EncodePwd);
             if not assigned(opck) then exit;
             if not assigned(opck) then exit;
             try
             try
               operationsht.AddOperationToHashTree(opck);
               operationsht.AddOperationToHashTree(opck);
@@ -1328,7 +1333,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
-  Function SignOpChangeKey(Const HexaStringOperationsHashTree : TRawBytes; account_signer, account_target : Cardinal;
+  Function SignOpChangeKey(Const HexaStringOperationsHashTree : TRawBytes; current_protocol : Word; account_signer, account_target : Cardinal;
     Const actualAccounKey, newAccountKey : TAccountKey;
     Const actualAccounKey, newAccountKey : TAccountKey;
     last_n_operation : Cardinal;
     last_n_operation : Cardinal;
     fee : UInt64; Const RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : Boolean;
     fee : UInt64; Const RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : Boolean;
@@ -1344,7 +1349,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       Exit;
       Exit;
     end;
     end;
     Try
     Try
-      opck := CreateOperationChangeKey(account_signer,last_n_operation,account_target,actualAccounKey,newAccountKey,fee,RawPayload,Payload_method,EncodePwd);
+      opck := CreateOperationChangeKey(current_protocol,account_signer,last_n_operation,account_target,actualAccounKey,newAccountKey,fee,RawPayload,Payload_method,EncodePwd);
       if opck=nil then exit;
       if opck=nil then exit;
       try
       try
         OperationsHashTree.AddOperationToHashTree(opck);
         OperationsHashTree.AddOperationToHashTree(opck);
@@ -1526,7 +1531,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     Result := CapturePubKeyExt(params,prefix,pubkey,errortxt);
     Result := CapturePubKeyExt(params,prefix,pubkey,errortxt);
   end;
   end;
 
 
-  function SignListAccountForSaleEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; const actualAccounKey : TAccountKey; last_n_operation : Cardinal) : boolean;
+  function SignListAccountForSaleEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; current_protocol : Word; const actualAccounKey : TAccountKey; last_n_operation : Cardinal) : boolean;
     // params:
     // params:
     // "account_signer" is the account that signs operations and pays the fee
     // "account_signer" is the account that signs operations and pays the fee
     // "account_target" is the account being listed
     // "account_target" is the account being listed
@@ -1584,7 +1589,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         Exit;
         Exit;
       end;
       end;
     end else new_pubkey := CT_TECDSA_Public_Nul;
     end else new_pubkey := CT_TECDSA_Public_Nul;
-    opSale := CreateOperationListAccountForSale(account_signer,last_n_operation,account_target,actualAccounKey,price,locked_until_block,
+    opSale := CreateOperationListAccountForSale(current_protocol, account_signer,last_n_operation,account_target,actualAccounKey,price,locked_until_block,
       seller_account, new_pubkey,fee,
       seller_account, new_pubkey,fee,
       TCrypto.HexaToRaw(params.AsString('payload','')),
       TCrypto.HexaToRaw(params.AsString('payload','')),
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
@@ -1602,6 +1607,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     OperationsHashTree : TOperationsHashTree;
     OperationsHashTree : TOperationsHashTree;
     accountpubkey : TAccountKey;
     accountpubkey : TAccountKey;
     last_n_operation : Cardinal;
     last_n_operation : Cardinal;
+    current_protocol : Word;
   begin
   begin
     Result := false;
     Result := false;
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
@@ -1616,7 +1622,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
       last_n_operation := params.AsCardinal('last_n_operation',0);
       last_n_operation := params.AsCardinal('last_n_operation',0);
-      If not SignListAccountForSaleEx(params,OperationsHashTree,accountpubkey,last_n_operation) then Exit
+      current_protocol := params.AsCardinal('protocol',CT_BUILD_PROTOCOL);
+      If not SignListAccountForSaleEx(params,OperationsHashTree,current_protocol, accountpubkey,last_n_operation) then Exit
       else Result := True;
       else Result := True;
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
     finally
     finally
@@ -1624,7 +1631,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
-  function SignDelistAccountForSaleEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; const actualAccountKey : TAccountKey; last_n_operation : Cardinal) : boolean;
+  function SignDelistAccountForSaleEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; current_protocol : Word; const actualAccountKey : TAccountKey; last_n_operation : Cardinal) : boolean;
     // params:
     // params:
     // "account_signer" is the account that signs operations and pays the fee
     // "account_signer" is the account that signs operations and pays the fee
     // "account_target" is the delisted account
     // "account_target" is the delisted account
@@ -1656,7 +1663,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       ErrorDesc := 'Invalid fee value';
       ErrorDesc := 'Invalid fee value';
       Exit;
       Exit;
     end;
     end;
-    opDelist := CreateOperationDelistAccountForSale(account_signer,last_n_operation,account_target,actualAccountKey,fee,TCrypto.HexaToRaw(params.AsString('payload','')),
+    opDelist := CreateOperationDelistAccountForSale(current_protocol,account_signer,last_n_operation,account_target,actualAccountKey,fee,TCrypto.HexaToRaw(params.AsString('payload','')),
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
     if opDelist=nil then exit;
     if opDelist=nil then exit;
     try
     try
@@ -1669,7 +1676,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
 
 
   // This function creates a TOpChangeAccountInfo without looking for actual state (cold wallet)
   // This function creates a TOpChangeAccountInfo without looking for actual state (cold wallet)
   // It assumes that account_number,account_last_n_operation and account_pubkey are correct
   // It assumes that account_number,account_last_n_operation and account_pubkey are correct
-  Function CreateOperationChangeAccountInfo(account_signer, account_last_n_operation, account_target: Cardinal; const account_signer_pubkey: TAccountKey;
+  Function CreateOperationChangeAccountInfo(current_protocol : Word; account_signer, account_last_n_operation, account_target: Cardinal; const account_signer_pubkey: TAccountKey;
     changePubKey : Boolean; Const new_account_pubkey : TAccountKey;
     changePubKey : Boolean; Const new_account_pubkey : TAccountKey;
     changeName: Boolean; Const new_name : TRawBytes;
     changeName: Boolean; Const new_name : TRawBytes;
     changeType: Boolean; new_type : Word;
     changeType: Boolean; new_type : Word;
@@ -1712,7 +1719,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
     end else f_raw := '';
     end else f_raw := '';
-    Result := TOpChangeAccountInfo.CreateChangeAccountInfo(account_signer,account_last_n_operation+1,account_target,
+    Result := TOpChangeAccountInfo.CreateChangeAccountInfo(current_protocol,
+      account_signer,account_last_n_operation+1,account_target,
       _RPCServer.FWalletKeys.Key[i].PrivateKey,
       _RPCServer.FWalletKeys.Key[i].PrivateKey,
       changePubKey,new_account_pubkey,changeName,new_name,changeType,new_type,
       changePubKey,new_account_pubkey,changeName,new_name,changeType,new_type,
       fee,f_raw);
       fee,f_raw);
@@ -1724,7 +1732,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   End;
   End;
 
 
-  function SignChangeAccountInfoEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; const actualAccountKey : TAccountKey; last_n_operation : Cardinal) : boolean;
+  function SignChangeAccountInfoEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; current_protocol : Word; const actualAccountKey : TAccountKey; last_n_operation : Cardinal) : boolean;
     // params:
     // params:
     // "account_signer" is the account that signs operations and pays the fee
     // "account_signer" is the account that signs operations and pays the fee
     // "account_target" is the target to change info
     // "account_target" is the target to change info
@@ -1791,7 +1799,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       changeType:=False;
       changeType:=False;
     end;
     end;
 
 
-    opChangeInfo := CreateOperationChangeAccountInfo(account_signer,last_n_operation,account_target,actualAccountKey,
+    opChangeInfo := CreateOperationChangeAccountInfo(current_protocol,account_signer,last_n_operation,account_target,actualAccountKey,
       changeKey,new_pubkey,
       changeKey,new_pubkey,
       changeName,new_name,
       changeName,new_name,
       changeType,new_type,
       changeType,new_type,
@@ -1811,6 +1819,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     OperationsHashTree : TOperationsHashTree;
     OperationsHashTree : TOperationsHashTree;
     accountpubkey : TAccountKey;
     accountpubkey : TAccountKey;
     last_n_operation : Cardinal;
     last_n_operation : Cardinal;
+    current_protocol : Word;
   begin
   begin
     Result := false;
     Result := false;
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
@@ -1825,7 +1834,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
       last_n_operation := params.AsCardinal('last_n_operation',0);
       last_n_operation := params.AsCardinal('last_n_operation',0);
-      If not SignChangeAccountInfoEx(params,OperationsHashTree,accountpubkey,last_n_operation) then Exit
+      current_protocol := params.AsCardinal('protocol',CT_BUILD_PROTOCOL);
+      If not SignChangeAccountInfoEx(params,OperationsHashTree,current_protocol,accountpubkey,last_n_operation) then Exit
       else Result := True;
       else Result := True;
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
     finally
     finally
@@ -1838,6 +1848,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     OperationsHashTree : TOperationsHashTree;
     OperationsHashTree : TOperationsHashTree;
     accountpubkey : TAccountKey;
     accountpubkey : TAccountKey;
     last_n_operation : Cardinal;
     last_n_operation : Cardinal;
+    current_protocol : Word;
   begin
   begin
     Result := false;
     Result := false;
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
@@ -1852,7 +1863,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
       last_n_operation := params.AsCardinal('last_n_operation',0);
       last_n_operation := params.AsCardinal('last_n_operation',0);
-      If not SignDelistAccountForSaleEx(params,OperationsHashTree,accountpubkey,last_n_operation) then Exit
+      current_protocol := params.AsCardinal('protocol',CT_BUILD_PROTOCOL);
+      If not SignDelistAccountForSaleEx(params,OperationsHashTree,current_protocol,accountpubkey,last_n_operation) then Exit
       else Result := True;
       else Result := True;
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
     finally
     finally
@@ -1860,7 +1872,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
-  function SignBuyAccountEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; const buyerAccountKey : TAccountKey; last_n_operation : Cardinal) : boolean;
+  function SignBuyAccountEx(params : TPCJSONObject; OperationsHashTree : TOperationsHashTree; current_protocol : Word; const buyerAccountKey : TAccountKey; last_n_operation : Cardinal) : boolean;
     // params:
     // params:
     // "buyer_account" is the buyer account
     // "buyer_account" is the buyer account
     // "account_to_purchase" is the account to purchase
     // "account_to_purchase" is the account to purchase
@@ -1917,7 +1929,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         Exit;
         Exit;
       end;
       end;
     end else new_pubkey := CT_TECDSA_Public_Nul;
     end else new_pubkey := CT_TECDSA_Public_Nul;
-    opBuy := CreateOperationBuyAccount(buyer_account,last_n_operation,buyerAccountKey,account_to_purchase,price,amount,seller_account,new_pubkey,fee,
+    opBuy := CreateOperationBuyAccount(current_protocol,buyer_account,last_n_operation,buyerAccountKey,account_to_purchase,price,amount,seller_account,new_pubkey,fee,
       TCrypto.HexaToRaw(params.AsString('payload','')),
       TCrypto.HexaToRaw(params.AsString('payload','')),
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
     if opBuy=nil then exit;
     if opBuy=nil then exit;
@@ -1934,6 +1946,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     OperationsHashTree : TOperationsHashTree;
     OperationsHashTree : TOperationsHashTree;
     accountpubkey : TAccountKey;
     accountpubkey : TAccountKey;
     last_n_operation : Cardinal;
     last_n_operation : Cardinal;
+    current_protocol : Word;
   begin
   begin
     Result := false;
     Result := false;
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
@@ -1948,7 +1961,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
         exit;
       end;
       end;
       last_n_operation := params.AsCardinal('last_n_operation',0);
       last_n_operation := params.AsCardinal('last_n_operation',0);
-      If not SignBuyAccountEx(params,OperationsHashTree,accountpubkey,last_n_operation) then Exit
+      current_protocol := params.AsCardinal('protocol',CT_BUILD_PROTOCOL);
+      If not SignBuyAccountEx(params,OperationsHashTree,current_protocol,accountpubkey,last_n_operation) then Exit
       else Result := True;
       else Result := True;
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
     finally
     finally
@@ -1998,7 +2012,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
           ErrorDesc := 'account_signer and account_target have distinct keys. Cannot sign';
           ErrorDesc := 'account_signer and account_target have distinct keys. Cannot sign';
           Exit;
           Exit;
         end;
         end;
-        If not SignListAccountForSaleEx(params,OperationsHashTree,account_signer.accountInfo.accountKey,account_signer.n_operation) then Exit;
+        If not SignListAccountForSaleEx(params,OperationsHashTree,FNode.Bank.SafeBox.CurrentProtocol, account_signer.accountInfo.accountKey,account_signer.n_operation) then Exit;
         opt := OperationsHashTree.GetOperation(0);
         opt := OperationsHashTree.GetOperation(0);
         If not FNode.AddOperation(Nil,opt,errors) then begin
         If not FNode.AddOperation(Nil,opt,errors) then begin
           ErrorNum := CT_RPC_ErrNum_InternalError;
           ErrorNum := CT_RPC_ErrNum_InternalError;
@@ -2057,7 +2071,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
           ErrorDesc := 'account_signer and account_target have distinct keys. Cannot sign';
           ErrorDesc := 'account_signer and account_target have distinct keys. Cannot sign';
           Exit;
           Exit;
         end;
         end;
-        If not SignDelistAccountForSaleEx(params,OperationsHashTree,account_signer.accountInfo.accountKey,account_signer.n_operation) then Exit;
+        If not SignDelistAccountForSaleEx(params,OperationsHashTree,FNode.Bank.SafeBox.CurrentProtocol,account_signer.accountInfo.accountKey,account_signer.n_operation) then Exit;
         opt := OperationsHashTree.GetOperation(0);
         opt := OperationsHashTree.GetOperation(0);
         If not FNode.AddOperation(Nil,opt,errors) then begin
         If not FNode.AddOperation(Nil,opt,errors) then begin
           ErrorNum := CT_RPC_ErrNum_InternalError;
           ErrorNum := CT_RPC_ErrNum_InternalError;
@@ -2099,7 +2113,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
           Exit;
           Exit;
         end;
         end;
         buyer_account := FNode.Operations.SafeBoxTransaction.Account(c_account);
         buyer_account := FNode.Operations.SafeBoxTransaction.Account(c_account);
-        If not SignBuyAccountEx(params,OperationsHashTree,buyer_account.accountInfo.accountKey,buyer_account.n_operation) then Exit;
+        If not SignBuyAccountEx(params,OperationsHashTree,FNode.Bank.SafeBox.CurrentProtocol,buyer_account.accountInfo.accountKey,buyer_account.n_operation) then Exit;
         opt := OperationsHashTree.GetOperation(0);
         opt := OperationsHashTree.GetOperation(0);
         If not FNode.AddOperation(Nil,opt,errors) then begin
         If not FNode.AddOperation(Nil,opt,errors) then begin
           ErrorNum := CT_RPC_ErrNum_InternalError;
           ErrorNum := CT_RPC_ErrNum_InternalError;
@@ -2158,7 +2172,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
           ErrorDesc := 'account_signer and account_target have distinct keys. Cannot sign';
           ErrorDesc := 'account_signer and account_target have distinct keys. Cannot sign';
           Exit;
           Exit;
         end;
         end;
-        If not SignChangeAccountInfoEx(params,OperationsHashTree,account_signer.accountInfo.accountKey,account_signer.n_operation) then Exit;
+        If not SignChangeAccountInfoEx(params,OperationsHashTree,FNode.Bank.SafeBox.CurrentProtocol,account_signer.accountInfo.accountKey,account_signer.n_operation) then Exit;
         opt := OperationsHashTree.GetOperation(0);
         opt := OperationsHashTree.GetOperation(0);
         If not FNode.AddOperation(Nil,opt,errors) then begin
         If not FNode.AddOperation(Nil,opt,errors) then begin
           ErrorNum := CT_RPC_ErrNum_InternalError;
           ErrorNum := CT_RPC_ErrNum_InternalError;
@@ -2435,7 +2449,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         end;
         end;
       end;
       end;
       // Return multioperation object:
       // Return multioperation object:
-      TPascalCoinJSONComp.FillMultiOperationObject(mop,GetResultObject);
+      TPascalCoinJSONComp.FillMultiOperationObject(FNode.Bank.SafeBox.CurrentProtocol,mop,GetResultObject);
     finally
     finally
       OperationsHashTree.Free;
       OperationsHashTree.Free;
     end;
     end;
@@ -2515,11 +2529,12 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
-  procedure InternalMultiOperationSignCold(multiOperation : TOpMultiOperation; accounts_and_keys : TPCJSONArray; var signedAccounts : Integer);
+  procedure InternalMultiOperationSignCold(multiOperation : TOpMultiOperation; current_protocol : Word; accounts_and_keys : TPCJSONArray; var signedAccounts : Integer);
     { Signs a multioperation in a Cold storage, so cannot check if current signatures are valid because public keys of accounts are unknown
     { Signs a multioperation in a Cold storage, so cannot check if current signatures are valid because public keys of accounts are unknown
       accounts_and_keys is a JSON ARRAY with Objects:
       accounts_and_keys is a JSON ARRAY with Objects:
       - "account"
       - "account"
       - "b58_pubkey" or "enc_pubkey" : The public key of the "account"
       - "b58_pubkey" or "enc_pubkey" : The public key of the "account"
+      - "protocol"
     }
     }
   var i,iKey : Integer;
   var i,iKey : Integer;
     pubKey : TAccountKey;
     pubKey : TAccountKey;
@@ -2534,7 +2549,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         iKey := _RPCServer.FWalletKeys.IndexOfAccountKey(pubKey);
         iKey := _RPCServer.FWalletKeys.IndexOfAccountKey(pubKey);
         if (iKey>=0) then begin
         if (iKey>=0) then begin
           if (Assigned(_RPCServer.FWalletKeys.Key[iKey].PrivateKey)) then begin
           if (Assigned(_RPCServer.FWalletKeys.Key[iKey].PrivateKey)) then begin
-            inc(signedAccounts,multiOperation.DoSignMultiOperationSigner(nAccount,_RPCServer.FWalletKeys.Key[iKey].PrivateKey));
+            inc(signedAccounts,multiOperation.DoSignMultiOperationSigner(current_protocol, nAccount,_RPCServer.FWalletKeys.Key[iKey].PrivateKey));
           end;
           end;
         end;
         end;
       end;
       end;
@@ -2546,12 +2561,14 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     senderOperationsHashTree : TOperationsHashTree;
     senderOperationsHashTree : TOperationsHashTree;
     mop : TOpMultiOperation;
     mop : TOpMultiOperation;
     i,j : Integer;
     i,j : Integer;
+    protocol : Word;
   begin
   begin
     { This will SIGN a MultiOperation on a HexaStringOperationsHashTree in COLD mode (without knowledge of current public keys)
     { This will SIGN a MultiOperation on a HexaStringOperationsHashTree in COLD mode (without knowledge of current public keys)
       Must provide param "accounts_and_keys"
       Must provide param "accounts_and_keys"
       - "accounts_and_keys" is a JSON ARRAY with Objects:
       - "accounts_and_keys" is a JSON ARRAY with Objects:
         - "account"
         - "account"
         - "b58_pubkey" or "enc_pubkey" : The public key of the "account"
         - "b58_pubkey" or "enc_pubkey" : The public key of the "account"
+      Must provide "protocol" version, by default will use current build protocol
       Will Return an OperationsHashTree Object
       Will Return an OperationsHashTree Object
     }
     }
     Result := false;
     Result := false;
@@ -2561,15 +2578,16 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
       ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
       Exit;
       Exit;
     end;
     end;
+    protocol := params.GetAsVariant('protocol').AsCardinal(CT_BUILD_PROTOCOL);
     if Not HexaStringToOperationsHashTreeAndGetMultioperation(HexaStringOperationsHashTree,False,senderOperationsHashTree,mop,errors) then begin
     if Not HexaStringToOperationsHashTreeAndGetMultioperation(HexaStringOperationsHashTree,False,senderOperationsHashTree,mop,errors) then begin
       ErrorNum:=CT_RPC_ErrNum_InvalidData;
       ErrorNum:=CT_RPC_ErrNum_InvalidData;
       ErrorDesc:= 'Error decoding param previous operations hash tree raw value: '+errors;
       ErrorDesc:= 'Error decoding param previous operations hash tree raw value: '+errors;
       Exit;
       Exit;
     end;
     end;
     Try
     Try
-      InternalMultiOperationSignCold(mop,params.GetAsArray('accounts_and_keys'),j);
+      InternalMultiOperationSignCold(mop,protocol,params.GetAsArray('accounts_and_keys'),j);
       // Return multioperation object:
       // Return multioperation object:
-      TPascalCoinJSONComp.FillMultiOperationObject(mop,GetResultObject);
+      TPascalCoinJSONComp.FillMultiOperationObject(protocol,mop,GetResultObject);
       Result := True;
       Result := True;
     finally
     finally
       senderOperationsHashTree.Free;
       senderOperationsHashTree.Free;
@@ -2611,7 +2629,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
             if (iKey>=0) then begin
             if (iKey>=0) then begin
               if (assigned(_RPCServer.FWalletKeys.Key[iKey].PrivateKey)) then begin
               if (assigned(_RPCServer.FWalletKeys.Key[iKey].PrivateKey)) then begin
                 // Can sign
                 // Can sign
-                inc(nSignedAccounts, mop.DoSignMultiOperationSigner(nAccount,_RPCServer.FWalletKeys.Key[iKey].PrivateKey) );
+                inc(nSignedAccounts, mop.DoSignMultiOperationSigner(FNode.Bank.SafeBox.CurrentProtocol,nAccount,_RPCServer.FWalletKeys.Key[iKey].PrivateKey) );
               end;
               end;
             end;
             end;
           end;
           end;
@@ -2620,7 +2638,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         lSigners.Free;
         lSigners.Free;
       end;
       end;
       // Return multioperation object:
       // Return multioperation object:
-      TPascalCoinJSONComp.FillMultiOperationObject(mop,GetResultObject);
+      TPascalCoinJSONComp.FillMultiOperationObject(FNode.Bank.SafeBox.CurrentProtocol,mop,GetResultObject);
       Result := True;
       Result := True;
     finally
     finally
       senderOperationsHashTree.Free;
       senderOperationsHashTree.Free;
@@ -3108,6 +3126,7 @@ begin
     end;
     end;
     Result := SignOpSendTo(
     Result := SignOpSendTo(
        params.AsString('rawoperations',''),
        params.AsString('rawoperations',''),
+       params.AsCardinal('protocol',CT_BUILD_PROTOCOL),
        params.AsCardinal('sender',CT_MaxAccount),params.AsCardinal('target',CT_MaxAccount),
        params.AsCardinal('sender',CT_MaxAccount),params.AsCardinal('target',CT_MaxAccount),
        senderpubkey,destpubkey,
        senderpubkey,destpubkey,
        params.AsCardinal('last_n_operation',0),
        params.AsCardinal('last_n_operation',0),
@@ -3202,7 +3221,9 @@ begin
       ErrorNum := CT_RPC_ErrNum_InvalidPubKey;
       ErrorNum := CT_RPC_ErrNum_InvalidPubKey;
       exit;
       exit;
     end;
     end;
-    Result := SignOpChangeKey(params.AsString('rawoperations',''),c2,c,
+    Result := SignOpChangeKey(params.AsString('rawoperations',''),
+       params.AsCardinal('protocol',CT_BUILD_PROTOCOL),
+       c2,c,
        senderpubkey,destpubkey,
        senderpubkey,destpubkey,
        params.AsCardinal('last_n_operation',0),
        params.AsCardinal('last_n_operation',0),
        ToPascalCoins(params.AsDouble('fee',0)),
        ToPascalCoins(params.AsDouble('fee',0)),

+ 38 - 31
src/core/UTxMultiOperation.pas

@@ -115,8 +115,7 @@ Type
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; override;
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; override;
     procedure AffectedAccounts(list : TList); override;
     procedure AffectedAccounts(list : TList); override;
     //
     //
-    Function GetTransactionHashToSign : TRawBytes;
-    Function DoSignMultiOperationSigner(SignerAccount : Cardinal; key : TECPrivateKey) : Integer;
+    Function DoSignMultiOperationSigner(current_protocol : Word; SignerAccount : Cardinal; key : TECPrivateKey) : Integer;
     class function OpType : Byte; override;
     class function OpType : Byte; override;
     function OperationAmount : Int64; override;
     function OperationAmount : Int64; override;
     function OperationFee : Int64; override;
     function OperationFee : Int64; override;
@@ -130,7 +129,7 @@ Type
     function GetAccountN_Operation(account : Cardinal) : Cardinal; override;
     function GetAccountN_Operation(account : Cardinal) : Cardinal; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     //
     //
-    Constructor CreateMultiOperation(const senders : TMultiOpSenders; const receivers : TMultiOpReceivers; const changes : TMultiOpChangesInfo; const senders_keys, changes_keys: Array of TECPrivateKey);
+    Constructor CreateMultiOperation(current_protocol : Word; const senders : TMultiOpSenders; const receivers : TMultiOpReceivers; const changes : TMultiOpChangesInfo; const senders_keys, changes_keys: Array of TECPrivateKey);
     Destructor Destroy; override;
     Destructor Destroy; override;
     Function AddTx(const senders : TMultiOpSenders; const receivers : TMultiOpReceivers; setInRandomOrder : Boolean) : Boolean;
     Function AddTx(const senders : TMultiOpSenders; const receivers : TMultiOpReceivers; setInRandomOrder : Boolean) : Boolean;
     Function AddChangeInfos(const changes : TMultiOpChangesInfo; setInRandomOrder : Boolean) : Boolean;
     Function AddChangeInfos(const changes : TMultiOpChangesInfo; setInRandomOrder : Boolean) : Boolean;
@@ -147,6 +146,7 @@ Type
     //
     //
     Function toString : String; Override;
     Function toString : String; Override;
     Property Data : TOpMultiOperationData read FData;
     Property Data : TOpMultiOperationData read FData;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
   End;
 
 
 implementation
 implementation
@@ -446,7 +446,7 @@ begin
   SetLength(errors,0);
   SetLength(errors,0);
   // Do check it!
   // Do check it!
   Try
   Try
-    ophtosign := GetTransactionHashToSign;
+    ophtosign := GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol);
     // Tx verification
     // Tx verification
     For i:=Low(FData.txSenders) to High(FData.txSenders) do begin
     For i:=Low(FData.txSenders) to High(FData.txSenders) do begin
       acc := AccountTransaction.Account(FData.txSenders[i].Account);
       acc := AccountTransaction.Account(FData.txSenders[i].Account);
@@ -692,29 +692,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-function TOpMultiOperation.GetTransactionHashToSign : TRawBytes;
-Var ms : TMemoryStream;
-  rb : TRawBytes;
-  old : Boolean;
-begin
-  ms := TMemoryStream.Create;
-  try
-    old := FSaveSignatureValue;
-    Try
-      FSaveSignatureValue:=False;
-      SaveOpToStream(ms,False);
-    finally
-      FSaveSignatureValue:=old;
-    end;
-    SetLength(Result,ms.Size);
-    ms.Position := 0;
-    ms.ReadBuffer(Result[1],ms.Size);
-  finally
-    ms.Free;
-  end;
-end;
-
-function TOpMultiOperation.DoSignMultiOperationSigner(SignerAccount : Cardinal; key : TECPrivateKey) : Integer;
+function TOpMultiOperation.DoSignMultiOperationSigner(current_protocol : Word; SignerAccount : Cardinal; key : TECPrivateKey) : Integer;
 Var i : Integer;
 Var i : Integer;
   raw : TRawBytes;
   raw : TRawBytes;
   _sign : TECDSA_SIG;
   _sign : TECDSA_SIG;
@@ -723,7 +701,7 @@ begin
   If Not Assigned(key.PrivateKey) then begin
   If Not Assigned(key.PrivateKey) then begin
     exit;
     exit;
   end;
   end;
-  raw := GetTransactionHashToSign;
+  raw := GetDigestToSign(current_protocol);
   Try
   Try
     _sign := TCrypto.ECDSASign(key.PrivateKey,raw);
     _sign := TCrypto.ECDSASign(key.PrivateKey,raw);
   Except
   Except
@@ -848,7 +826,7 @@ begin
   until i<0;
   until i<0;
 end;
 end;
 
 
-constructor TOpMultiOperation.CreateMultiOperation(
+constructor TOpMultiOperation.CreateMultiOperation(current_protocol : Word;
   const senders: TMultiOpSenders; const receivers: TMultiOpReceivers;
   const senders: TMultiOpSenders; const receivers: TMultiOpReceivers;
   const changes: TMultiOpChangesInfo; const senders_keys,
   const changes: TMultiOpChangesInfo; const senders_keys,
   changes_keys: array of TECPrivateKey);
   changes_keys: array of TECPrivateKey);
@@ -862,13 +840,13 @@ begin
   If (length(senders_keys)<>length(senders)) then exit; // Cannot sign!
   If (length(senders_keys)<>length(senders)) then exit; // Cannot sign!
   If (length(changes_keys)<>length(changes)) then exit; // Cannot sign!
   If (length(changes_keys)<>length(changes)) then exit; // Cannot sign!
   For i:=low(senders) to high(senders) do begin
   For i:=low(senders) to high(senders) do begin
-    If DoSignMultiOperationSigner(senders[i].Account,senders_keys[i])=0 then begin
+    If DoSignMultiOperationSigner(current_protocol,senders[i].Account,senders_keys[i])=0 then begin
       TLog.NewLog(lterror,Classname,'Error signing a new MultiOperation sender');
       TLog.NewLog(lterror,Classname,'Error signing a new MultiOperation sender');
       Exit;
       Exit;
     end;
     end;
   end;
   end;
   For i:=Low(changes) to high(changes) do begin
   For i:=Low(changes) to high(changes) do begin
-    If DoSignMultiOperationSigner(changes[i].Account,changes_keys[i])=0 then begin
+    If DoSignMultiOperationSigner(current_protocol,changes[i].Account,changes_keys[i])=0 then begin
       TLog.NewLog(lterror,Classname,'Error signing a new MultiOperation change');
       TLog.NewLog(lterror,Classname,'Error signing a new MultiOperation change');
       Exit;
       Exit;
     end;
     end;
@@ -1040,5 +1018,34 @@ begin
      TAccountComp.FormatMoney(FTotalFee)]);
      TAccountComp.FormatMoney(FTotalFee)]);
 end;
 end;
 
 
+function TOpMultiOperation.GetDigestToSign(current_protocol : Word): TRawBytes;
+Var ms : TMemoryStream;
+  rb : TRawBytes;
+  old : Boolean;
+  b : Byte;
+begin
+  ms := TMemoryStream.Create;
+  try
+    old := FSaveSignatureValue;
+    Try
+      FSaveSignatureValue:=False;
+      SaveOpToStream(ms,False);
+    finally
+      FSaveSignatureValue:=old;
+    end;
+    if (current_protocol<=CT_PROTOCOL_3) then begin
+      ms.Position := 0;
+      setlength(Result,ms.Size);
+      ms.ReadBuffer(Result[1],ms.Size);
+    end else begin
+      b := OpType;
+      ms.Write(b,1);
+      Result := TCrypto.DoSha256(ms.Memory,ms.Size);
+    end;
+  finally
+    ms.Free;
+  end;
+end;
+
 end.
 end.
 
 

+ 2 - 2
src/gui-classic/UFRMAbout.pas

@@ -60,7 +60,7 @@ uses
   ShellApi,
   ShellApi,
 {$ELSE}
 {$ELSE}
 {$ENDIF}
 {$ENDIF}
-  UFolderHelper, UConst, UNode;
+  UFolderHelper, UConst, UNode, UOpenSSL;
 
 
 {$IFnDEF FPC}
 {$IFnDEF FPC}
   {$R *.dfm}
   {$R *.dfm}
@@ -70,7 +70,7 @@ uses
 
 
 procedure TFRMAbout.FormCreate(Sender: TObject);
 procedure TFRMAbout.FormCreate(Sender: TObject);
 begin
 begin
-  lblBuild.Caption :=  'Build: '+CT_ClientAppVersion;
+  lblBuild.Caption :=  'Build: '+CT_ClientAppVersion+' OpenSSL: '+IntToHex(OpenSSLVersion,8);
   lblProtocolVersion.Caption := Format('BlockChain Protocol: %d (%d)  -  Net Protocol: %d (%d)',[TNode.Node.Bank.SafeBox.CurrentProtocol,CT_BlockChain_Protocol_Available,
   lblProtocolVersion.Caption := Format('BlockChain Protocol: %d (%d)  -  Net Protocol: %d (%d)',[TNode.Node.Bank.SafeBox.CurrentProtocol,CT_BlockChain_Protocol_Available,
     CT_NetProtocol_Version, CT_NetProtocol_Available]);
     CT_NetProtocol_Version, CT_NetProtocol_Available]);
 end;
 end;

+ 8 - 8
src/gui-classic/UFRMOperation.pas

@@ -276,7 +276,7 @@ loop_start:
         end else begin
         end else begin
         end;
         end;
         if dooperation then begin
         if dooperation then begin
-          op := TOpTransaction.CreateTransaction(account.account,account.n_operation+1,destAccount.account,wk.PrivateKey,_amount,_fee,FEncodedPayload);
+          op := TOpTransaction.CreateTransaction(FNode.Bank.Safebox.CurrentProtocol,account.account,account.n_operation+1,destAccount.account,wk.PrivateKey,_amount,_fee,FEncodedPayload);
           inc(_totalamount,_amount);
           inc(_totalamount,_amount);
           inc(_totalfee,_fee);
           inc(_totalfee,_fee);
         end;
         end;
@@ -296,11 +296,11 @@ loop_start:
           if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
           if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
           else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
           else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
           else _fee := signerAccount.balance - uint64(_totalSignerFee);
           else _fee := signerAccount.balance - uint64(_totalSignerFee);
-          op := TOpChangeKeySigned.Create(signerAccount.account,signerAccount.n_operation+_signer_n_ops+1,account.account,wk.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
+          op := TOpChangeKeySigned.Create(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+_signer_n_ops+1,account.account,wk.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
           inc(_signer_n_ops);
           inc(_signer_n_ops);
           inc(_totalSignerFee, _fee);
           inc(_totalSignerFee, _fee);
         end else begin
         end else begin
-          op := TOpChangeKey.Create(account.account,account.n_operation+1,account.account,wk.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
+          op := TOpChangeKey.Create(FNode.Bank.SafeBox.CurrentProtocol,account.account,account.n_operation+1,account.account,wk.PrivateKey,_newOwnerPublicKey,_fee,FEncodedPayload);
         end;
         end;
         inc(_totalfee,_fee);
         inc(_totalfee,_fee);
         operationstxt := 'Change private key to '+TAccountComp.GetECInfoTxt(_newOwnerPublicKey.EC_OpenSSL_NID);
         operationstxt := 'Change private key to '+TAccountComp.GetECInfoTxt(_newOwnerPublicKey.EC_OpenSSL_NID);
@@ -312,9 +312,9 @@ loop_start:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         else _fee := signerAccount.balance;
         else _fee := signerAccount.balance;
         if (rbListAccountForPublicSale.Checked) then begin
         if (rbListAccountForPublicSale.Checked) then begin
-          op := TOpListAccountForSale.CreateListAccountForSale(signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,CT_TECDSA_Public_Nul,0,wk.PrivateKey,FEncodedPayload);
+          op := TOpListAccountForSale.CreateListAccountForSale(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,CT_TECDSA_Public_Nul,0,wk.PrivateKey,FEncodedPayload);
         end else if (rbListAccountForPrivateSale.Checked) then begin
         end else if (rbListAccountForPrivateSale.Checked) then begin
-          op := TOpListAccountForSale.CreateListAccountForSale(signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,wk.PrivateKey,FEncodedPayload);
+          op := TOpListAccountForSale.CreateListAccountForSale(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1+iAcc, account.account,_salePrice,_fee,destAccount.account,_newOwnerPublicKey,_lockedUntil,wk.PrivateKey,FEncodedPayload);
         end else raise Exception.Create('Select Sale type');
         end else raise Exception.Create('Select Sale type');
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelist) then begin
       end else if (PageControlOpType.ActivePage = tsDelist) then begin
@@ -323,12 +323,12 @@ loop_start:
         // Special fee account:
         // Special fee account:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         else _fee := signerAccount.balance;
         else _fee := signerAccount.balance;
-        op := TOpDelistAccountForSale.CreateDelistAccountForSale(signerAccount.account,signerAccount.n_operation+1+iAcc,account.account,_fee,wk.PrivateKey,FEncodedPayload);
+        op := TOpDelistAccountForSale.CreateDelistAccountForSale(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1+iAcc,account.account,_fee,wk.PrivateKey,FEncodedPayload);
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
         {%region Operation: Buy Account}
         {%region Operation: Buy Account}
         if Not UpdateOpBuyAccount(account,accountToBuy,_amount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
         if Not UpdateOpBuyAccount(account,accountToBuy,_amount,_newOwnerPublicKey,errors) then raise Exception.Create(errors);
-        op := TOpBuyAccount.CreateBuy(account.account,account.n_operation+1,accountToBuy.account,accountToBuy.accountInfo.account_to_pay,
+        op := TOpBuyAccount.CreateBuy(FNode.Bank.Safebox.CurrentProtocol,account.account,account.n_operation+1,accountToBuy.account,accountToBuy.accountInfo.account_to_pay,
           accountToBuy.accountInfo.price,_amount,_fee,_newOwnerPublicKey,wk.PrivateKey,FEncodedPayload);
           accountToBuy.accountInfo.price,_amount,_fee,_newOwnerPublicKey,wk.PrivateKey,FEncodedPayload);
         {%endregion}
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
@@ -338,7 +338,7 @@ loop_start:
         end else begin
         end else begin
           if signerAccount.balance>DefaultFee then _fee := DefaultFee
           if signerAccount.balance>DefaultFee then _fee := DefaultFee
           else _fee := signerAccount.balance;
           else _fee := signerAccount.balance;
-          op := TOpChangeAccountInfo.CreateChangeAccountInfo(signerAccount.account,signerAccount.n_operation+1,account.account,wk.PrivateKey,false,CT_TECDSA_Public_Nul,
+          op := TOpChangeAccountInfo.CreateChangeAccountInfo(FNode.Bank.SafeBox.CurrentProtocol,signerAccount.account,signerAccount.n_operation+1,account.account,wk.PrivateKey,false,CT_TECDSA_Public_Nul,
              _changeName,_newName,_changeType,_newType,_fee,FEncodedPayload);
              _changeName,_newName,_changeType,_newType,_fee,FEncodedPayload);
         end;
         end;
         {%endregion}
         {%endregion}

+ 2 - 2
src/gui-classic/UFRMOperationsExplorer.pas

@@ -333,7 +333,7 @@ begin
     If (j>=0) then begin
     If (j>=0) then begin
       // Can sign
       // Can sign
       If (Assigned(FSourceWalletKeys.Key[j].PrivateKey)) then begin
       If (Assigned(FSourceWalletKeys.Key[j].PrivateKey)) then begin
-        inc(n, mop.DoSignMultiOperationSigner(mop.Data.txSenders[i].Account,FSourceWalletKeys.Key[j].PrivateKey));
+        inc(n, mop.DoSignMultiOperationSigner(SourceNode.Bank.SafeBox.CurrentProtocol,mop.Data.txSenders[i].Account,FSourceWalletKeys.Key[j].PrivateKey));
       end;
       end;
     end;
     end;
   end;
   end;
@@ -342,7 +342,7 @@ begin
     If (j>=0) then begin
     If (j>=0) then begin
       // Can sign
       // Can sign
       If (Assigned(FSourceWalletKeys.Key[j].PrivateKey)) then begin
       If (Assigned(FSourceWalletKeys.Key[j].PrivateKey)) then begin
-        inc(n, mop.DoSignMultiOperationSigner(mop.Data.changesInfo[i].Account,FSourceWalletKeys.Key[j].PrivateKey));
+        inc(n, mop.DoSignMultiOperationSigner(SourceNode.Bank.SafeBox.CurrentProtocol,mop.Data.changesInfo[i].Account,FSourceWalletKeys.Key[j].PrivateKey));
       end;
       end;
     end;
     end;
   end;
   end;

+ 9 - 9
src/gui-classic/UFRMRandomOperations.pas

@@ -71,8 +71,8 @@ type
   private
   private
   public
   public
     class function GetRandomSigner(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out iKey : Integer; out nAccount : Cardinal) : Boolean;
     class function GetRandomSigner(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out iKey : Integer; out nAccount : Cardinal) : Boolean;
-    class function GenerateOpTransaction(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
-    class function GenerateOpMultiOperation(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
+    class function GenerateOpTransaction(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
+    class function GenerateOpMultiOperation(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
   end;
   end;
 
 
 implementation
 implementation
@@ -112,7 +112,7 @@ begin
   Result := True;
   Result := True;
 end;
 end;
 
 
-class function TRandomGenerateOperation.GenerateOpTransaction(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
+class function TRandomGenerateOperation.GenerateOpTransaction(current_protocol : Word; const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
 var nAccount : Cardinal;
 var nAccount : Cardinal;
   iKey : Integer;
   iKey : Integer;
   opTx : TOpTransaction;
   opTx : TOpTransaction;
@@ -139,7 +139,7 @@ begin
   until (destAcc.account <> senderAcc.account);
   until (destAcc.account <> senderAcc.account);
 
 
   // Search account
   // Search account
-  opTx := TOpTransaction.CreateTransaction(senderAcc.account,senderAcc.n_operation+1,destAcc.account,aWalletKeys.Key[iKey].PrivateKey,amount,fees,'');
+  opTx := TOpTransaction.CreateTransaction(current_protocol,senderAcc.account,senderAcc.n_operation+1,destAcc.account,aWalletKeys.Key[iKey].PrivateKey,amount,fees,'');
   Try
   Try
     Result := operationsComp.AddOperation(True,opTx,errors);
     Result := operationsComp.AddOperation(True,opTx,errors);
   finally
   finally
@@ -147,7 +147,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-class function TRandomGenerateOperation.GenerateOpMultiOperation(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
+class function TRandomGenerateOperation.GenerateOpMultiOperation(current_protocol : Word; const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
    procedure DoSign(opMulti : TOpMultiOperation);
    procedure DoSign(opMulti : TOpMultiOperation);
    var n : Integer;
    var n : Integer;
      i,j : Integer;
      i,j : Integer;
@@ -158,7 +158,7 @@ class function TRandomGenerateOperation.GenerateOpMultiOperation(const operation
        If (j>=0) then begin
        If (j>=0) then begin
          // Can sign
          // Can sign
          If (Assigned(aWalletKeys.Key[j].PrivateKey)) then begin
          If (Assigned(aWalletKeys.Key[j].PrivateKey)) then begin
-           inc(n, opMulti.DoSignMultiOperationSigner(opMulti.Data.txSenders[i].Account,aWalletKeys.Key[j].PrivateKey));
+           inc(n, opMulti.DoSignMultiOperationSigner(current_protocol,opMulti.Data.txSenders[i].Account,aWalletKeys.Key[j].PrivateKey));
          end;
          end;
        end;
        end;
      end;
      end;
@@ -167,7 +167,7 @@ class function TRandomGenerateOperation.GenerateOpMultiOperation(const operation
        If (j>=0) then begin
        If (j>=0) then begin
          // Can sign
          // Can sign
          If (Assigned(aWalletKeys.Key[j].PrivateKey)) then begin
          If (Assigned(aWalletKeys.Key[j].PrivateKey)) then begin
-           inc(n, opMulti.DoSignMultiOperationSigner(opMulti.Data.changesInfo[i].Account,aWalletKeys.Key[j].PrivateKey));
+           inc(n, opMulti.DoSignMultiOperationSigner(current_protocol,opMulti.Data.changesInfo[i].Account,aWalletKeys.Key[j].PrivateKey));
          end;
          end;
        end;
        end;
      end;
      end;
@@ -319,10 +319,10 @@ begin
   While (nCounter<max) And (Not FStopRandomOperations) do begin
   While (nCounter<max) And (Not FStopRandomOperations) do begin
     Case Random(30) of
     Case Random(30) of
       0..20 : begin
       0..20 : begin
-        If TRandomGenerateOperation.GenerateOpTransaction(operationsComp,FSourceWalletKeys) then inc(nCounter);
+        If TRandomGenerateOperation.GenerateOpTransaction(SourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(nCounter);
       end;
       end;
       21..25 : begin
       21..25 : begin
-        If TRandomGenerateOperation.GenerateOpMultiOperation(operationsComp,FSourceWalletKeys) then inc(nCounter);
+        If TRandomGenerateOperation.GenerateOpMultiOperation(SourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(nCounter);
       end;
       end;
     else Sleep(10);
     else Sleep(10);
     end;
     end;

+ 2 - 2
src/gui-classic/UFRMWallet.lfm

@@ -388,9 +388,9 @@ object FRMWallet: TFRMWallet
     Height = 466
     Height = 466
     Top = 91
     Top = 91
     Width = 865
     Width = 865
-    ActivePage = tsBlockChain
+    ActivePage = tsMyAccounts
     Align = alClient
     Align = alClient
-    TabIndex = 2
+    TabIndex = 0
     TabOrder = 2
     TabOrder = 2
     OnChange = PageControlChange
     OnChange = PageControlChange
     object tsMyAccounts: TTabSheet
     object tsMyAccounts: TTabSheet

+ 193 - 33
src/gui-classic/UFRMWallet.pas

@@ -31,6 +31,7 @@ uses
   ExtCtrls, ComCtrls, UWallet, StdCtrls, ULog, Grids, UAppParams, UBlockChain,
   ExtCtrls, ComCtrls, UWallet, StdCtrls, ULog, Grids, UAppParams, UBlockChain,
   UNode, UGridUtils, UJSONFunctions, UAccounts, Menus, ImgList, UNetProtocol,
   UNode, UGridUtils, UJSONFunctions, UAccounts, Menus, ImgList, UNetProtocol,
   UCrypto, Buttons, UPoolMining, URPC, UFRMAccountSelect, UConst,
   UCrypto, Buttons, UPoolMining, URPC, UFRMAccountSelect, UConst,
+  UAccountKeyStorage,
   UFRMRPCCalls, UTxMultiOperation;
   UFRMRPCCalls, UTxMultiOperation;
 
 
 Const
 Const
@@ -231,6 +232,11 @@ type
     Procedure FinishedLoadingApp;
     Procedure FinishedLoadingApp;
     Procedure FillAccountInformation(Const Strings : TStrings; Const AccountNumber : Cardinal);
     Procedure FillAccountInformation(Const Strings : TStrings; Const AccountNumber : Cardinal);
     Procedure FillOperationInformation(Const Strings : TStrings; Const OperationResume : TOperationResume);
     Procedure FillOperationInformation(Const Strings : TStrings; Const OperationResume : TOperationResume);
+    {$IFDEF TESTING_NO_POW_CHECK}
+    Procedure InitMenuForTesting;
+    Procedure Test_CreateABlock(Sender: TObject);
+    Procedure Test_ShowPublicKeys(Sender: TObject);
+    {$ENDIF}
   protected
   protected
     { Private declarations }
     { Private declarations }
     FNode : TNode;
     FNode : TNode;
@@ -307,17 +313,32 @@ Uses UFolderHelper, UOpenSSL, UOpenSSLdef, UTime, UFileStorage,
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText, USettings;
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText, USettings;
 
 
 Type
 Type
+
+  { TThreadActivate }
+
   TThreadActivate = Class(TPCThread)
   TThreadActivate = Class(TPCThread)
+    procedure OnProgressNotify(sender : TObject; const mesage : AnsiString; curPos, totalCount : Int64);
   protected
   protected
     procedure BCExecute; override;
     procedure BCExecute; override;
   End;
   End;
 
 
 { TThreadActivate }
 { TThreadActivate }
 
 
+procedure TThreadActivate.OnProgressNotify(sender: TObject; const mesage: AnsiString; curPos, totalCount: Int64);
+var pct : String;
+begin
+  If Assigned(FRMWallet.FBackgroundPanel) then begin
+   // FRMWallet.FBackgroundPanel.Font.Color:=lblNodeStatus.Font.Color;
+    if (totalCount>0) then pct := Format('%.1f',[curPos*100/totalCount])+'%'
+    else pct := '';
+    FRMWallet.FBackgroundPanel.Caption:='Please wait until finished: '+mesage+' '+pct;
+  end;
+end;
+
 procedure TThreadActivate.BCExecute;
 procedure TThreadActivate.BCExecute;
 begin
 begin
   // Read Operations saved from disk
   // Read Operations saved from disk
-  TNode.Node.InitSafeboxAndOperations; // New Build 2.1.4 to load pending operations buffer
+  TNode.Node.InitSafeboxAndOperations($FFFFFFFF,OnProgressNotify); // New Build 2.1.4 to load pending operations buffer
   TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
   TNode.Node.AutoDiscoverNodes(CT_Discover_IPs);
   TNode.Node.NetServer.Active := true;
   TNode.Node.NetServer.Active := true;
   Synchronize( FRMWallet.DoUpdateAccounts );
   Synchronize( FRMWallet.DoUpdateAccounts );
@@ -366,7 +387,6 @@ begin
     TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
     TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
     TFileStorage(FNode.Bank.Storage).Initialize;
     TFileStorage(FNode.Bank.Storage).Initialize;
     // Init Grid
     // Init Grid
-    //FAccountsGrid.Node := FNode;
     FSelectedAccountsGrid.Node := FNode;
     FSelectedAccountsGrid.Node := FNode;
     FWalletKeys.OnChanged.Add( OnWalletChanged );
     FWalletKeys.OnChanged.Add( OnWalletChanged );
     FAccountsGrid.Node := FNode;
     FAccountsGrid.Node := FNode;
@@ -377,17 +397,8 @@ begin
     FBlockChainGrid.HashRateAs := TShowHashRateAs(i);
     FBlockChainGrid.HashRateAs := TShowHashRateAs(i);
     // Reading database
     // Reading database
     TThreadActivate.Create(false).FreeOnTerminate := true;
     TThreadActivate.Create(false).FreeOnTerminate := true;
-    FNodeNotifyEvents.Node := FNode;
-    // Init
-    TNetData.NetData.OnReceivedHelloMessage := OnReceivedHelloMessage;
-    TNetData.NetData.OnStatisticsChanged := OnNetStatisticsChanged;
-    TNetData.NetData.OnNetConnectionsUpdated := onNetConnectionsUpdated;
-    TNetData.NetData.OnNodeServersUpdated := OnNetNodeServersUpdated;
-    TNetData.NetData.OnBlackListUpdated := OnNetBlackListUpdated;
-    //
-    TimerUpdateStatus.Interval := 1000;
-    TimerUpdateStatus.Enabled := true;
     UpdateConfigChanged;
     UpdateConfigChanged;
+    UpdateNodeStatus;
   Except
   Except
     On E:Exception do begin
     On E:Exception do begin
       E.Message := 'An error occurred during initialization. Application cannot continue:'+#10+#10+E.Message+#10+#10+'Application will close...';
       E.Message := 'An error occurred during initialization. Application cannot continue:'+#10+#10+E.Message+#10+#10+'Application will close...';
@@ -745,6 +756,17 @@ end;
 
 
 procedure TFRMWallet.FinishedLoadingApp;
 procedure TFRMWallet.FinishedLoadingApp;
 begin
 begin
+  FNodeNotifyEvents.Node := FNode;
+  // Init
+  TNetData.NetData.OnReceivedHelloMessage := OnReceivedHelloMessage;
+  TNetData.NetData.OnStatisticsChanged := OnNetStatisticsChanged;
+  TNetData.NetData.OnNetConnectionsUpdated := onNetConnectionsUpdated;
+  TNetData.NetData.OnNodeServersUpdated := OnNetNodeServersUpdated;
+  TNetData.NetData.OnBlackListUpdated := OnNetBlackListUpdated;
+  //
+  TimerUpdateStatus.Interval := 1000;
+  TimerUpdateStatus.Enabled := true;
+  //
   FPoolMiningServer := TPoolMiningServer.Create;
   FPoolMiningServer := TPoolMiningServer.Create;
   FPoolMiningServer.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
   FPoolMiningServer.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
   FPoolMiningServer.MinerAccountKey := GetAccountKeyForMiner;
   FPoolMiningServer.MinerAccountKey := GetAccountKeyForMiner;
@@ -757,9 +779,14 @@ begin
   end;
   end;
   PageControl.Visible:=True;
   PageControl.Visible:=True;
   PageControl.Enabled:=True;
   PageControl.Enabled:=True;
+
+
+
+  UpdatePrivateKeys;
 end;
 end;
 
 
-procedure TFRMWallet.FillAccountInformation(const Strings: TStrings; Const AccountNumber: Cardinal);
+procedure TFRMWallet.FillAccountInformation(const Strings: TStrings;
+  const AccountNumber: Cardinal);
 Var account : TAccount;
 Var account : TAccount;
   s : String;
   s : String;
 begin
 begin
@@ -795,7 +822,8 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TFRMWallet.FillOperationInformation(const Strings: TStrings; Const OperationResume: TOperationResume);
+procedure TFRMWallet.FillOperationInformation(const Strings: TStrings;
+  const OperationResume: TOperationResume);
 var i : Integer;
 var i : Integer;
   jsonObj : TPCJSONObject;
   jsonObj : TPCJSONObject;
 begin
 begin
@@ -846,6 +874,109 @@ begin
   end;
   end;
 end;
 end;
 
 
+{$IFDEF TESTING_NO_POW_CHECK}
+procedure TFRMWallet.InitMenuForTesting;
+var mi : TMenuItem;
+begin
+  miAbout.AddSeparator;
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Create a block';
+  mi.OnClick:=Test_CreateABlock;
+  miAbout.Add(mi);
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Show public keys state';
+  mi.OnClick:=Test_ShowPublicKeys;
+  miAbout.Add(mi);
+end;
+
+procedure TFRMWallet.Test_CreateABlock(Sender: TObject);
+var ops : TPCOperationsComp;
+  nba : TBlockAccount;
+  errors : AnsiString;
+begin
+  {$IFDEF TESTNET}
+  ops := TPCOperationsComp.Create(Nil);
+  Try
+    ops.bank := FNode.Bank;
+    ops.CopyFrom(FNode.Operations);
+    ops.BlockPayload:=IntToStr(FNode.Bank.BlocksCount);
+    ops.nonce := FNode.Bank.BlocksCount;
+    ops.UpdateTimestamp;
+    FNode.AddNewBlockChain(Nil,ops,nba,errors);
+  finally
+    ops.Free;
+  end;
+  {$ELSE}
+  Raise Exception.Create('NOT ALLOWED!');
+  {$ENDIF}
+end;
+
+procedure TFRMWallet.Test_ShowPublicKeys(Sender: TObject);
+var F : TFRMMemoText;
+  i : Integer;
+  sl : TStrings;
+  ak : TAccountKey;
+  nmin,nmax : Integer;
+  l : TList;
+  Pacsd : PAccountKeyStorageData;
+  acc : TAccount;
+begin
+  sl := TStringList.Create;
+  try
+    for i:=0 to FNode.Bank.SafeBox.AccountsCount-1 do begin
+      acc := FNode.Bank.SafeBox.Account(i);
+      if acc.accountInfo.new_publicKey.EC_OpenSSL_NID<>0 then begin
+        sl.Add(Format('Account %d new public key %d %s',[acc.account,
+          acc.accountInfo.new_publicKey.EC_OpenSSL_NID,
+          TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(acc.accountInfo.new_publicKey))]));
+      end;
+    end;
+    l := TAccountKeyStorage.KS.LockList;
+    try
+      sl.Add(Format('%d public keys in TAccountKeyStorage data',[l.count]));
+      for i:=0 to l.count-1 do begin
+        Pacsd := l[i];
+        if (Pacsd^.counter<=0) then begin
+          sl.Add(Format('%d/%d public keys counter %d',[i+1,l.count,Pacsd^.counter]));
+        end;
+        if FNode.Bank.SafeBox.OrderedAccountKeysList.IndexOfAccountKey(Pacsd^.ptrAccountKey^)<0 then begin
+          sl.Add(Format('%d/%d public keys counter %d Type %d NOT FOUND %s',[i+1,l.count,Pacsd^.counter,
+          Pacsd^.ptrAccountKey^.EC_OpenSSL_NID,
+          TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(Pacsd^.ptrAccountKey^))]));
+        end;
+      end;
+    finally
+      TAccountKeyStorage.KS.UnlockList;
+    end;
+    sl.Add(Format('%d public keys in %d accounts',[FNode.Bank.SafeBox.OrderedAccountKeysList.Count,FNode.Bank.Safebox.AccountsCount]));
+    for i:=0 to FNode.Bank.SafeBox.OrderedAccountKeysList.Count-1 do begin
+      ak := FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKey[i];
+      if ( FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Count > 0) then begin
+        nmin := FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Get(0);
+        nmax := FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Get( FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Count-1 );
+      end else begin
+        nmin := -1; nmax := -1;
+      end;
+      sl.Add(Format('%d/%d %d accounts (%d to %d) for key type %d %s',[
+        i+1,FNode.Bank.SafeBox.OrderedAccountKeysList.Count,
+        FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Count,
+        nmin,nmax,
+        ak.EC_OpenSSL_NID,
+        TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(ak)) ]));
+    end;
+    F := TFRMMemoText.Create(Self);
+    try
+      F.InitData('Keys in safebox',sl.Text);
+      F.ShowModal;
+    finally
+      F.Free;
+    end;
+  finally
+    sl.Free;
+  end;
+end;
+{$ENDIF}
+
 function TFRMWallet.ForceMining: Boolean;
 function TFRMWallet.ForceMining: Boolean;
 begin
 begin
   Result := false;
   Result := false;
@@ -937,6 +1068,10 @@ begin
   cbHashRateUnits.Items.Add('Th/s');
   cbHashRateUnits.Items.Add('Th/s');
   cbHashRateUnits.Items.Add('Ph/s');
   cbHashRateUnits.Items.Add('Ph/s');
   cbHashRateUnits.Items.Add('Eh/s');
   cbHashRateUnits.Items.Add('Eh/s');
+  {$IFDEF TESTING_NO_POW_CHECK}
+  // Things for testing purposes only
+  InitMenuForTesting;
+  {$ENDIF}
 end;
 end;
 
 
 procedure TFRMWallet.ebHashRateBackBlocksKeyPress(Sender: TObject; var Key: char);
 procedure TFRMWallet.ebHashRateBackBlocksKeyPress(Sender: TObject; var Key: char);
@@ -1504,7 +1639,8 @@ begin
     UpdateBlockChainState;
     UpdateBlockChainState;
   Except
   Except
     On E:Exception do begin
     On E:Exception do begin
-      E.Message := 'Error at OnNewAccount '+E.Message;
+      E.Message := 'Exception at OnNewAccount '+E.ClassName+': '+E.Message;
+      TLog.NewLog(lterror,ClassName,E.Message);
       Raise;
       Raise;
     end;
     end;
   end;
   end;
@@ -1558,6 +1694,7 @@ end;
 procedure TFRMWallet.OnNodeKeysActivity(Sender: TObject);
 procedure TFRMWallet.OnNodeKeysActivity(Sender: TObject);
 begin
 begin
   DoUpdateAccounts;
   DoUpdateAccounts;
+//  Application.ProcessMessages; // XXXXXXXXXXX
 end;
 end;
 
 
 procedure TFRMWallet.OnReceivedHelloMessage(Sender: TObject);
 procedure TFRMWallet.OnReceivedHelloMessage(Sender: TObject);
@@ -1717,6 +1854,7 @@ begin
     UpdateConnectionStatus;
     UpdateConnectionStatus;
     UpdateBlockChainState;
     UpdateBlockChainState;
     UpdateNodeStatus;
     UpdateNodeStatus;
+//    Application.ProcessMessages; // XXXXXXXXXXX
   Except
   Except
     On E:Exception do begin
     On E:Exception do begin
       E.Message := 'Exception at TimerUpdate '+E.ClassName+': '+E.Message;
       E.Message := 'Exception at TimerUpdate '+E.ClassName+': '+E.Message;
@@ -1749,29 +1887,39 @@ begin
         if cbMyPrivateKeys.ItemIndex=0 then begin
         if cbMyPrivateKeys.ItemIndex=0 then begin
           // All keys in the wallet
           // All keys in the wallet
           for i := 0 to FWalletKeys.Count - 1 do begin
           for i := 0 to FWalletKeys.Count - 1 do begin
-            j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
-            if (j>=0) then begin
-              l := FOrderedAccountsKeyList.AccountKeyList[j];
-              for k := 0 to l.Count - 1 do begin
-                if applyfilter then begin
-                  acc := FNode.Bank.SafeBox.Account(l.Get(k));
-                  if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
-                end else accl.Add(l.Get(k));
+            FOrderedAccountsKeyList.Lock; // Protection v4
+            Try
+              j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
+              if (j>=0) then begin
+                l := FOrderedAccountsKeyList.AccountKeyList[j];
+                for k := 0 to l.Count - 1 do begin
+                  if applyfilter then begin
+                    acc := FNode.Bank.SafeBox.Account(l.Get(k));
+                    if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
+                  end else accl.Add(l.Get(k));
+                end;
               end;
               end;
+            finally
+              FOrderedAccountsKeyList.Unlock;
             end;
             end;
           end;
           end;
         end else begin
         end else begin
           i := PtrInt(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex]);
           i := PtrInt(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex]);
           if (i>=0) And (i<FWalletKeys.Count) then begin
           if (i>=0) And (i<FWalletKeys.Count) then begin
-            j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
-            if (j>=0) then begin
-              l := FOrderedAccountsKeyList.AccountKeyList[j];
-              for k := 0 to l.Count - 1 do begin
-                if applyfilter then begin
-                  acc := FNode.Bank.SafeBox.Account(l.Get(k));
-                  if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
-                end else accl.Add(l.Get(k));
+            FOrderedAccountsKeyList.Lock; // Protection v4
+            Try
+              j := FOrderedAccountsKeyList.IndexOfAccountKey(FWalletKeys[i].AccountKey);
+              if (j>=0) then begin
+                l := FOrderedAccountsKeyList.AccountKeyList[j];
+                for k := 0 to l.Count - 1 do begin
+                  if applyfilter then begin
+                    acc := FNode.Bank.SafeBox.Account(l.Get(k));
+                    if (acc.balance>=FMinAccountBalance) And (acc.balance<=FMaxAccountBalance) then accl.Add(acc.account);
+                  end else accl.Add(l.Get(k));
+                end;
               end;
               end;
+            finally
+              FOrderedAccountsKeyList.Unlock;
             end;
             end;
           end;
           end;
         end;
         end;
@@ -1795,6 +1943,7 @@ begin
   // Show Totals:
   // Show Totals:
   lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
   lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
   UpdateOperations;
   UpdateOperations;
+//  Application.ProcessMessages; // XXXXXXXXXXX
 end;
 end;
 
 
 procedure TFRMWallet.UpdateAvailableConnections;
 procedure TFRMWallet.UpdateAvailableConnections;
@@ -1846,8 +1995,13 @@ begin
     end else lblCurrentBlock.Caption :=  '(none)';
     end else lblCurrentBlock.Caption :=  '(none)';
     lblCurrentAccounts.Caption := Inttostr(FNode.Bank.AccountsCount);
     lblCurrentAccounts.Caption := Inttostr(FNode.Bank.AccountsCount);
     lblCurrentBlockTime.Caption := UnixTimeToLocalElapsedTime(FNode.Bank.LastOperationBlock.timestamp);
     lblCurrentBlockTime.Caption := UnixTimeToLocalElapsedTime(FNode.Bank.LastOperationBlock.timestamp);
-    lblOperationsPending.Caption := Inttostr(FNode.Operations.Count);
-    lblCurrentDifficulty.Caption := InttoHex(FNode.Operations.OperationBlock.compact_target,8);
+    FNode.Operations.Lock;
+    try
+      lblOperationsPending.Caption := Inttostr(FNode.Operations.Count);
+      lblCurrentDifficulty.Caption := InttoHex(FNode.Operations.OperationBlock.compact_target,8);
+    finally
+      FNode.Operations.Unlock;
+    end;
     favg := FNode.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage);
     favg := FNode.Bank.GetActualTargetSecondsAverage(CT_CalcNewTargetBlocksAverage);
     f := (CT_NewLineSecondsAvg - favg) / CT_NewLineSecondsAvg;
     f := (CT_NewLineSecondsAvg - favg) / CT_NewLineSecondsAvg;
     lblTimeAverage.Caption := 'Last '+Inttostr(CT_CalcNewTargetBlocksAverage)+': '+FormatFloat('0.0',favg)+' sec. (Optimal '+Inttostr(CT_NewLineSecondsAvg)+'s) Deviation '+FormatFloat('0.00%',f*100);
     lblTimeAverage.Caption := 'Last '+Inttostr(CT_CalcNewTargetBlocksAverage)+': '+FormatFloat('0.0',favg)+' sec. (Optimal '+Inttostr(CT_NewLineSecondsAvg)+'s) Deviation '+FormatFloat('0.00%',f*100);
@@ -2009,6 +2163,8 @@ begin
   cbMyPrivateKeys.items.BeginUpdate;
   cbMyPrivateKeys.items.BeginUpdate;
   Try
   Try
     cbMyPrivateKeys.Items.Clear;
     cbMyPrivateKeys.Items.Clear;
+    if assigned(FOrderedAccountsKeyList) then FOrderedAccountsKeyList.SafeBox.StartThreadSafe;
+    Try
     For i:=0 to FWalletKeys.Count-1 do begin
     For i:=0 to FWalletKeys.Count-1 do begin
       wk := FWalletKeys.Key[i];
       wk := FWalletKeys.Key[i];
       if assigned(FOrderedAccountsKeyList) then begin
       if assigned(FOrderedAccountsKeyList) then begin
@@ -2025,6 +2181,10 @@ begin
       end;
       end;
       cbMyPrivateKeys.Items.AddObject(s,TObject(i));
       cbMyPrivateKeys.Items.AddObject(s,TObject(i));
     end;
     end;
+
+    finally
+      if assigned(FOrderedAccountsKeyList) then FOrderedAccountsKeyList.SafeBox.EndThreadSave;
+    end;
     cbMyPrivateKeys.Sorted := true;
     cbMyPrivateKeys.Sorted := true;
     cbMyPrivateKeys.Sorted := false;
     cbMyPrivateKeys.Sorted := false;
     cbMyPrivateKeys.Items.InsertObject(0,'(All my private keys)',TObject(-1));
     cbMyPrivateKeys.Items.InsertObject(0,'(All my private keys)',TObject(-1));