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:  
 
+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
 - 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 )

+ 8 - 0
src/config.inc

@@ -34,6 +34,9 @@
   {$DEFINE PRODUCTION}
   {.$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
   {$DEFINE SHOW_AVERAGE_TIME_STATS}
 
@@ -96,4 +99,9 @@ ERROR: You must select ONE option!
 {$IFDEF OpenSSL10}
   ERROR: OpenSLL v1.0 is not longer valid, use OpenSSL v1.1 instead
 {$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;
 
 type
-  TAccountKeyStorateData = record
+  TAccountKeyStorageData = record
     ptrAccountKey : PAccountKey;
     counter : Integer;
   end;
-  PAccountKeyStorageData = ^TAccountKeyStorateData;
+  PAccountKeyStorageData = ^TAccountKeyStorageData;
 
   { TAccountKeyStorage }
 
@@ -28,6 +28,7 @@ type
     constructor Create;
     destructor Destroy; override;
     function AddAccountKey(Const accountKey: TAccountKey) : PAccountKey;
+    function AddAccountKeyExt(Const accountKey: TAccountKey) : PAccountKeyStorageData;
     procedure RemoveAccountKey(Const accountKey: TAccountKey);
     class function KS : TAccountKeyStorage;
     function LockList : TList;
@@ -124,6 +125,29 @@ begin
   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);
 var l : TList;
   i : Integer;

+ 250 - 33
src/core/UAccounts.pas

@@ -228,6 +228,7 @@ Type
     function Lock : TList;
     procedure Unlock;
     function HasAccountKeyChanged : Boolean;
+    procedure CopyFrom(const source : TOrderedAccountKeysList);
   End;
 
   // Maintans a Cardinal ordered (without duplicates) list with TRawData each
@@ -283,6 +284,8 @@ Type
   TOrderedAccountList = Class;
   TOrderedBlockAccountList = Class;
 
+  TProgressNotify = procedure(sender : TObject; const message : AnsiString; curPos, totalCount : Int64) of object;
+
   { TPCSafeBox }
 
   TAccountUpdateStyle = (aus_transaction_commit, aus_rollback, aus_commiting_from_otherchain);
@@ -292,6 +295,9 @@ Type
     FBlockAccountsList : TList; // Used when has no PreviousSafebox
     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;
     FBufferBlocksHash: TRawBytes;
     FOrderedByName : TOrderedRawList;
@@ -325,6 +331,7 @@ Type
     Function AddNew(Const blockChain : TOperationBlock) : TBlockAccount;
     function DoUpgradeToProtocol2 : Boolean;
     function DoUpgradeToProtocol3 : Boolean;
+    function DoUpgradeToProtocol4 : Boolean;
   public
     Constructor Create;
     Destructor Destroy; override;
@@ -336,7 +343,8 @@ Type
     Procedure CopyFrom(accounts : TPCSafeBox);
     Class Function CalcBlockHash(const block : TBlockAccount; useProtocol2Method : Boolean):TRawBytes;
     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 SaveSafeBoxStreamHeader(Stream : TStream; protocol : Word; OffsetStartBlock, OffsetEndBlock, CurrentSafeBoxBlocksCount : Cardinal) : Boolean;
     Class Function MustSafeBoxBeSaved(BlocksCount : Cardinal) : Boolean;
@@ -368,6 +376,7 @@ Type
     Property PreviousSafeboxOriginBlock : Integer Read FPreviousSafeboxOriginBlock;
     Function GetMinimumAvailableSnapshotBlock : Integer;
     Function HasSnapshotForBlock(block_number : Cardinal) : Boolean;
+    Property OrderedAccountKeysList : TOrderedAccountKeysList read FOrderedAccountKeysList;
   End;
 
 
@@ -452,7 +461,7 @@ Type
   public
     Constructor Create(SafeBox : TPCSafeBox);
     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 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;
@@ -1881,6 +1890,9 @@ end;
 procedure TPCSafeBox.AccountKeyListAddAccounts(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 Var i : Integer;
 begin
+  If Assigned(FOrderedAccountKeysList) then begin
+    FOrderedAccountKeysList.AddAccounts(AccountKey,accounts);
+  end;
   for i := 0 to FListOfOrderedAccountKeysList.count-1 do begin
     TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).AddAccounts(AccountKey,accounts);
   end;
@@ -1889,6 +1901,9 @@ end;
 procedure TPCSafeBox.AccountKeyListRemoveAccount(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 Var i : Integer;
 begin
+  If Assigned(FOrderedAccountKeysList) then begin
+    FOrderedAccountKeysList.RemoveAccounts(AccountKey,accounts);
+  end;
   for i := 0 to FListOfOrderedAccountKeysList.count-1 do begin
     TOrderedAccountKeysList( FListOfOrderedAccountKeysList[i] ).RemoveAccounts(AccountKey,accounts);
   end;
@@ -2053,6 +2068,8 @@ begin
     Result := (FCurrentProtocol<CT_PROTOCOL_2) and (BlocksCount >= CT_Protocol_Upgrade_v2_MinBlock);
   end else if (newProtocolVersion=CT_PROTOCOL_3) then begin
     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;
 
@@ -2141,6 +2158,10 @@ Var i : Integer;
 begin
   StartThreadSafe;
   Try
+    If Assigned(FOrderedAccountKeysList) then begin
+      FOrderedAccountKeysList.Clear;
+    end;
+
     for i := 0 to FBlockAccountsList.Count - 1 do begin
       P := FBlockAccountsList.Items[i];
       Dispose(P);
@@ -2181,6 +2202,7 @@ procedure TPCSafeBox.CopyFrom(accounts: TPCSafeBox);
 Var i,j : Cardinal;
   P : PBlockAccount;
   BA : TBlockAccount;
+  lastOAKL : TOrderedAccountKeysList;
 begin
   StartThreadSafe;
   Try
@@ -2195,6 +2217,10 @@ begin
       end;
       Clear;
       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;
         for i := 0 to accounts.BlocksCount - 1 do begin
           BA := accounts.Block(i);
@@ -2206,6 +2232,10 @@ begin
             AccountKeyListAddAccounts(BA.accounts[j].accountInfo.accountKey,[BA.accounts[j].account]);
           end;
         end;
+        If Assigned(lastOAKL) then begin
+          lastOAKL.CopyFrom(accounts.FOrderedAccountKeysList);
+          FOrderedAccountKeysList:=lastOAKL;
+        end;
       end;
       FTotalBalance := accounts.TotalBalance;
       FTotalFee := accounts.FTotalFee;
@@ -2238,6 +2268,7 @@ begin
   FAddedNamesSincePreviousSafebox := TOrderedRawList.Create;
   FDeletedNamesSincePreviousSafebox := TOrderedRawList.Create;
   FSubChains := TList.Create;
+  FOrderedAccountKeysList := TOrderedAccountKeysList.Create(Nil,True);
   Clear;
 end;
 
@@ -2259,6 +2290,7 @@ begin
   FreeAndNil(FAddedNamesSincePreviousSafebox);
   FreeAndNil(FDeletedNamesSincePreviousSafebox);
   FreeAndNil(FSubChains);
+  FreeAndNil(FOrderedAccountKeysList);
   If Assigned(FPreviousSafeBox) then begin
     FPreviousSafeBox.FSubChains.Remove(Self); // Remove from current snapshot
     FPreviousSafeBox := Nil;
@@ -2609,12 +2641,19 @@ begin
   TLog.NewLog(ltInfo,ClassName,'End Upgraded to protocol 3 - New safeboxhash:'+TCrypto.ToHexaString(FSafeBoxHash));
 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;
 begin
   FLock.Release;
 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
   iblock,iacc : Cardinal;
   s : AnsiString;
@@ -2641,12 +2680,17 @@ begin
         CT_PROTOCOL_1 : FCurrentProtocol := 1;
         CT_PROTOCOL_2 : FCurrentProtocol := 2;
         CT_PROTOCOL_3 : FCurrentProtocol := 3;
+        CT_PROTOCOL_4 : FCurrentProtocol := 4;
       else exit;
       end;
       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]);
         exit;
       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
       posOffsetZone := Stream.Position;
       If checkAll then begin
@@ -2662,6 +2706,9 @@ begin
       SetLength(FBufferBlocksHash,sbHeader.blocksCount*32); // Initialize for high speed reading
       errors := 'Corrupted stream';
       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);
         if (checkAll) then begin
           If (offsets[iblock]<>Stream.Position-posOffsetZone) then begin
@@ -2749,6 +2796,9 @@ begin
         LastReadBlock := block;
         Inc(FWorkSum,block.blockchainInfo.compact_target);
       end;
+      if (Assigned(progressNotify)) then begin
+        progressNotify(Self,'Checking Safebox integrity',sbHeader.blocksCount,sbHeader.blocksCount);
+      end;
       If checkAll 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]);
@@ -2783,6 +2833,13 @@ begin
   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;
   // This function reads SafeBox stream info and sets position at offset start zone if valid, otherwise sets position to actual position
 Var w : Word;
@@ -2799,7 +2856,7 @@ begin
     if (s<>CT_MagicIdentificator) then exit;
     if Stream.Size<8 then exit;
     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;
     Stream.Read(safeBoxBankVersion,2);
     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);
         exit;
       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
           errors := 'Upgrade to protocol version 3 available at block: '+IntToStr(CT_Protocol_Upgrade_v3_MinBlock);
           exit;
@@ -3248,7 +3310,7 @@ begin
     exit;
   end;
   // 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);
     exit;
   end;
@@ -3309,7 +3371,7 @@ begin
     tsReal := (ts1 - ts2);
     If (protocolVersion=CT_PROTOCOL_1) then begin
       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;
       If CalcBack=0 then CalcBack := 1;
       ts2 := Block(BlocksCount-CalcBack-1).blockchainInfo.timestamp;
@@ -3766,6 +3828,15 @@ begin
         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;
   finally
     FFreezedAccounts.EndThreadSave;
@@ -3888,11 +3959,10 @@ begin
   CleanTransaction;
 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;
 Var
-  intSender, intTarget : Integer;
-  PaccSender, PaccTarget : PAccount;
+  PaccSender, PaccTarget,PaccSigner : PAccount;
 begin
   Result := false;
   errors := '';
@@ -3901,8 +3971,9 @@ begin
     exit;
   end;
   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
-     errors := 'Invalid sender or target on transfer';
+     errors := 'Invalid sender, signer or target on transfer';
      exit;
   end;
   if TAccountComp.IsAccountBlockedByProtocol(sender,Origin_BlocksCount) then begin
@@ -3915,13 +3986,35 @@ begin
   end;
   PaccSender := GetInternalAccount(sender);
   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;
   if ((PaccTarget^.balance + amount)>CT_MaxWalletAmount) then begin
     errors := 'Max account balance';
@@ -3949,8 +4042,19 @@ begin
     PaccTarget^.updated_block := Origin_BlocksCount;
   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);
 
   Dec(FTotalBalance,fee);
@@ -4349,13 +4453,17 @@ end;
 { TOrderedAccountKeysList }
 Type
   TOrderedAccountKeyList = Record
+    {$IFDEF useAccountKeyStorage}
+    accountKeyPtr : PAccountKey;
+    {$ELSE}
     rawaccountkey : TRawBytes;
+    {$ENDIF}
     accounts_number : TOrderedCardinalList;
     changes_counter : Integer;
   end;
   POrderedAccountKeyList = ^TOrderedAccountKeyList;
 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;
 begin
@@ -4365,14 +4473,18 @@ end;
 procedure TOrderedAccountKeysList.AddAccountKey(const AccountKey: TAccountKey);
 Var P : POrderedAccountKeyList;
   i,j : Integer;
-  lockedList : TList;
+  lockedList, safeboxLockedList : TList;
 begin
   lockedList := Lock;
   Try
     if Not Find(lockedList,AccountKey,i) then begin
       New(P);
       P^ := CT_TOrderedAccountKeyList_NUL;
+      {$IFDEF useAccountKeyStorage}
+      P^.accountKeyPtr:=TAccountKeyStorage.KS.AddAccountKey(AccountKey);
+      {$ELSE}
       P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
+      {$ENDIF}
       P^.accounts_number := TOrderedCardinalList.Create;
       inc(P^.changes_counter);
       inc(FTotalChanges);
@@ -4380,13 +4492,25 @@ begin
       // Search this key in the AccountsList and add all...
       j := 0;
       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;
-        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
         TLog.NewLog(ltdebug,Classname,Format('Adding account key (no Account List) %s',[TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
       end;
@@ -4396,7 +4520,8 @@ begin
   end;
 end;
 
-Procedure TOrderedAccountKeysList.AddAccountKeys(Const AccountKeys : array of TAccountKey);
+procedure TOrderedAccountKeysList.AddAccountKeys(
+  const AccountKeys: array of TAccountKey);
 var i : integer;
 begin
   for i := Low(AccountKeys) to High(AccountKeys) do
@@ -4415,7 +4540,11 @@ begin
     end else if (FAutoAddAll) then begin
       New(P);
       P^ := CT_TOrderedAccountKeyList_NUL;
+      {$IFDEF useAccountKeyStorage}
+      P^.accountKeyPtr:=TAccountKeyStorage.KS.AddAccountKey(AccountKey);
+      {$ELSE}
       P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
+      {$ENDIF}
       P^.accounts_number := TOrderedCardinalList.Create;
       lockedList.Insert(i,P);
     end else exit;
@@ -4467,6 +4596,40 @@ begin
   Result := FTotalChanges>0;
 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);
 Var P : POrderedAccountKeyList;
   i : Integer;
@@ -4478,6 +4641,9 @@ begin
       P := lockedList[i];
       inc(P^.changes_counter);
       if RemoveAccountList then begin
+        {$IFDEF useAccountKeyStorage}
+        TAccountKeyStorage.KS.RemoveAccountKey(P^.accountKeyPtr^);
+        {$ENDIF}
         P^.accounts_number.Free;
         Dispose(P);
       end else begin
@@ -4539,17 +4705,30 @@ begin
 end;
 
 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;
+  {$ENDIF}
 begin
   Result := False;
-  rak := TAccountComp.AccountKey2RawString(AccountKey);
   L := 0;
   H := lockedList.Count - 1;
+  {$IFDEF useAccountKeyStorage}
+  pacsd:=TAccountKeyStorage.KS.AddAccountKeyExt(AccountKey);
+  {$ELSE}
+  rak := TAccountComp.AccountKey2RawString(AccountKey);
+  {$ENDIF}
   while L <= H do
   begin
     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 );
+    {$ENDIF}
     if C < 0 then L := I + 1 else
     begin
       H := I - 1;
@@ -4560,6 +4739,9 @@ begin
       end;
     end;
   end;
+  {$IFDEF useAccountKeyStorage}
+  Dec(pacsd^.counter);
+  {$ENDIF}
   Index := L;
 end;
 
@@ -4575,16 +4757,23 @@ begin
 end;
 
 function TOrderedAccountKeysList.GetAccountKey(index: Integer): TAccountKey;
-Var raw : TRawBytes;
-  lockedList : TList;
+Var lockedList : TList;
+  {$IFDEF useAccountKeyStorage}
+  {$ELSE}
+  raw : TRawBytes;
+  {$ENDIF}
 begin
   lockedList := Lock;
   Try
+    {$IFDEF useAccountKeyStorage}
+    Result := POrderedAccountKeyList(lockedList[index])^.accountKeyPtr^;
+    {$ELSE}
     raw := POrderedAccountKeyList(lockedList[index]).rawaccountkey;
+    Result := TAccountComp.RawString2Accountkey(raw);
+    {$ENDIF}
   finally
     Unlock;
   end;
-  Result := TAccountComp.RawString2Accountkey(raw);
 end;
 
 function TOrderedAccountKeysList.GetAccountKeyList(index: Integer): TOrderedCardinalList;
@@ -4643,6 +4832,9 @@ begin
       lockedList.Delete(i);
       // Free it
       P^.accounts_number.free;
+      {$IFDEF useAccountKeyStorage}
+      TAccountKeyStorage.KS.RemoveAccountKey(AccountKey);
+      {$ENDIF}
       Dispose(P);
     end;
   finally
@@ -4664,6 +4856,9 @@ begin
     // Remove from list
     lockedList.Delete(i);
     // Free it
+    {$IFDEF useAccountKeyStorage}
+    TAccountKeyStorage.KS.RemoveAccountKey(AccountKey);
+    {$ENDIF}
     P^.accounts_number.free;
     Dispose(P);
   finally
@@ -4846,9 +5041,18 @@ end;
 { TOrderedCardinalList }
 
 function TOrderedCardinalList.Add(Value: Cardinal): Integer;
+var nc : Integer;
 begin
   if Find(Value,Result) then exit
   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));
     NotifyChanged;
   end;
@@ -4867,6 +5071,7 @@ begin
   Disable;
   Try
     Clear;
+    FOrderedList.Capacity:=Sender.FOrderedList.Capacity;
     for I := 0 to Sender.Count - 1 do begin
       Add(Sender.Get(i));
     end;
@@ -4912,6 +5117,18 @@ begin
   Result := False;
   L := 0;
   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
   begin
     I := (L + H) shr 1;

+ 29 - 15
src/core/UBlockChain.pas

@@ -214,6 +214,7 @@ Type
     procedure AffectedAccounts(list : TList); 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;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; virtual; abstract;
     function OperationAmount : Int64; virtual; abstract;
     function OperationAmountByAccount(account : Cardinal) : Int64; virtual;
     function OperationFee: Int64; virtual; abstract;
@@ -396,7 +397,7 @@ Type
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; virtual; abstract;
     Function DoMoveBlockChain(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : 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;
     Function BlockExists(Block : Cardinal) : Boolean; virtual; abstract;
     function GetFirstBlockNumber: Int64; virtual; abstract;
@@ -412,7 +413,7 @@ Type
     Function MoveBlockChainBlocks(StartBlock : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : Boolean;
     Procedure DeleteBlockChainBlocks(StartingDeleteBlock : Cardinal);
     Function SaveBank : Boolean;
-    Function RestoreBank(max_block : Int64) : Boolean;
+    Function RestoreBank(max_block : Int64; restoreProgressNotify : TProgressNotify = Nil) : Boolean;
     Constructor Create(AOwner : TComponent); Override;
     Property Orphan : TOrphan read FOrphan write SetOrphan;
     Property ReadOnly : Boolean read FReadOnly write SetReadOnly;
@@ -455,12 +456,12 @@ Type
     procedure AssignTo(Dest: TPersistent); Override;
     function GetActualTargetSecondsAverage(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;
     Function LoadOperations(Operations : TPCOperationsComp; Block : Cardinal) : Boolean;
     Property SafeBox : TPCSafeBox read FSafeBox;
     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 NewLog(Operations: TPCOperationsComp; Logtype: TLogType; Logtxt: AnsiString);
     Property OnLog: TPCBankLog read FOnLog write FOnLog;
@@ -633,17 +634,19 @@ begin
   End;
 end;
 
-procedure TPCBank.DiskRestoreFromOperations(max_block : Int64);
+procedure TPCBank.DiskRestoreFromOperations(max_block : Int64; restoreProgressNotify : TProgressNotify = Nil);
 Var
   errors: AnsiString;
   newBlock: TBlockAccount;
   Operations: TPCOperationsComp;
   n : Int64;
+  tc : TTickCount;
 begin
   if FIsRestoringFromFile then begin
     TLog.NewLog(lterror,Classname,'Is Restoring!!!');
     raise Exception.Create('Is restoring!');
   end;
+  tc := TPlatform.GetTickCount;
   TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
   try
     FUpgradingToV2 := NOT Storage.HasUpgradedToVersion2;
@@ -653,7 +656,7 @@ begin
       Storage.Initialize;
       If (max_block<Storage.LastBlock) then n := max_block
       else n := Storage.LastBlock;
-      Storage.RestoreBank(n);
+      Storage.RestoreBank(n,restoreProgressNotify);
       // Restore last blockchain
       if (BlocksCount>0) And (SafeBox.CurrentProtocol=CT_PROTOCOL_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
                   Storage.SaveBank;
                 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 else break;
           end else break;
@@ -701,7 +708,7 @@ procedure TPCBank.UpdateValuesFromSafebox;
 Var aux : AnsiString;
   i : Integer;
 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 }
   Try
     TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
@@ -790,7 +797,7 @@ begin
   end else Result := true;
 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;
   i : Integer;
   auxSB : TPCSafeBox;
@@ -800,7 +807,7 @@ begin
     If useSecureLoad then begin
       // When on secure load will load Stream in a separate SafeBox, changing only real SafeBox if successfully
       auxSB := TPCSafeBox.Create;
-      Result := auxSB.LoadSafeBoxFromStream(Stream,true,LastReadBlock,errors);
+      Result := auxSB.LoadSafeBoxFromStream(Stream,true,progressNotify,LastReadBlock,errors);
       If Not Result then Exit;
     end;
     TPCThread.ProtectEnterCriticalSection(Self,FBankLock);
@@ -808,7 +815,7 @@ begin
       If Assigned(auxSB) then begin
         SafeBox.CopyFrom(auxSB);
       end else begin
-        Result := SafeBox.LoadSafeBoxFromStream(Stream,false,LastReadBlock,errors);
+        Result := SafeBox.LoadSafeBoxFromStream(Stream,false,progressNotify,LastReadBlock,errors);
       end;
       If Not Result then exit;
       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
       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
+      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;
       FOperationBlock.block := 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 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 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!
     // - Value 0 and 2 means that contains also operations
     // - 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
         TLog.NewLog(ltinfo,ClassName,'New miner protocol version to 3 at sanitize');
         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;
       FOperationBlock.block := FBank.BlocksCount;
       FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(FBank.BlocksCount);
@@ -2201,9 +2213,9 @@ begin
   Result := DoMoveBlockChain(StartBlock,DestOrphan,DestStorage);
 end;
 
-function TStorage.RestoreBank(max_block: Int64): Boolean;
+function TStorage.RestoreBank(max_block: Int64; restoreProgressNotify : TProgressNotify = Nil): Boolean;
 begin
-  Result := DoRestoreBank(max_block);
+  Result := DoRestoreBank(max_block,restoreProgressNotify);
 end;
 
 function TStorage.SaveBank: Boolean;
@@ -2469,8 +2481,7 @@ begin
 end;
 
 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
   OperationResume := CT_TOperationResume_NUL;
   OperationResume.Block:=Block;
@@ -2610,6 +2621,9 @@ begin
       OperationResume.Amount := Operation.OperationAmountByAccount(Affected_account_number);
       OperationResume.Fee := 0;
       Result := True;
+    end;
+    CT_Op_Data : Begin
+      Result := True;
     end
   else Exit;
   end;

+ 13 - 3
src/core/UConst.pas

@@ -81,7 +81,7 @@ Const
 
   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_secp384r1 = 715;
@@ -95,12 +95,16 @@ Const
   CT_PROTOCOL_1 = 1;
   CT_PROTOCOL_2 = 2;
   CT_PROTOCOL_3 = 3;
+  CT_PROTOCOL_4 = 4;
+  CT_BUILD_PROTOCOL = CT_PROTOCOL_3;
+
   CT_BlockChain_Protocol_Available: Word = $0003; // Protocol 3 flag
   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_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)
   // IMPORTANT NOTE!!!
@@ -126,6 +130,8 @@ Const
   CT_Op_ChangeAccountInfo = $08;
   // Protocol 3 new operations
   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
 
@@ -149,8 +155,12 @@ Const
   CT_OpSubtype_ChangeAccountInfo          = 81;
   CT_OpSubtype_MultiOperation_Global      = 91;
   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';
 

+ 5 - 0
src/core/UCrypto.pas

@@ -510,6 +510,11 @@ begin
   end else begin
     Result := false;
   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);
   EC_POINT_free(pub_key);
   EC_GROUP_free(ECG);

+ 6 - 5
src/core/UFileStorage.pas

@@ -61,7 +61,7 @@ Type
     Function DoSaveBlockChain(Operations : TPCOperationsComp) : Boolean; override;
     Function DoMoveBlockChain(Start_Block : Cardinal; Const DestOrphan : TOrphan; DestStorage : TStorage) : 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;
     Function BlockExists(Block : Cardinal) : Boolean; override;
     Function LockBlockChainStream : TFileStream;
@@ -436,7 +436,7 @@ begin
   End;
 end;
 
-function TFileStorage.DoRestoreBank(max_block: Int64): Boolean;
+function TFileStorage.DoRestoreBank(max_block: Int64; restoreProgressNotify : TProgressNotify): Boolean;
 var
     sr: TSearchRec;
     FileAttrs: Integer;
@@ -446,7 +446,7 @@ var
     ms : TMemoryStream;
     errors : AnsiString;
     blockscount : Cardinal;
-    sbHeader : TPCSafeBoxHeader;
+    sbHeader, goodSbHeader : TPCSafeBoxHeader;
 begin
   LockBlockChainStream;
   Try
@@ -463,6 +463,7 @@ begin
               (sbHeader.startBlock=0) And (sbHeader.endBlock=sbHeader.startBlock+sbHeader.blocksCount-1) then begin
               filename := auxfn;
               blockscount := sbHeader.blocksCount;
+              goodSbHeader := sbHeader;
             end;
           end;
         end;
@@ -470,7 +471,7 @@ begin
       FindClose(sr);
     end;
     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);
       try
         ms := TMemoryStream.Create;
@@ -478,7 +479,7 @@ begin
           ms.CopyFrom(fs,0);
           fs.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);
           end;
         Finally

+ 1 - 1
src/core/UNetProtocol.pas

@@ -1862,7 +1862,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         TNode.Node.Bank.SafeBox.StartThreadSafe;
         try
           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!');
             If Not IsMyBlockchainValid then begin
               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 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);
     Function IsBlockChainValid(var WhyNot : AnsiString) : Boolean;
     Function IsReady(Var CurrentProcess : AnsiString) : Boolean;
@@ -949,13 +949,13 @@ begin
   Result := found;
 end;
 
-procedure TNode.InitSafeboxAndOperations(max_block_to_read : Cardinal);
+procedure TNode.InitSafeboxAndOperations(max_block_to_read : Cardinal = $FFFFFFFF; restoreProgressNotify : TProgressNotify = Nil);
 var opht : TOperationsHashTree;
   oprl : TOperationsResumeList;
   errors : AnsiString;
   n : Integer;
 begin
-  Bank.DiskRestoreFromOperations(max_block_to_read);
+  Bank.DiskRestoreFromOperations(max_block_to_read,restoreProgressNotify);
   opht := TOperationsHashTree.Create;
   oprl := TOperationsResumeList.Create;
   try

+ 577 - 249
src/core/UOpTransaction.pas

@@ -79,8 +79,6 @@ Type
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean; 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;
     function OperationAmount : Int64; override;
     function OperationFee : Int64; override;
@@ -92,8 +90,9 @@ Type
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     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 GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
 
   { TOpChangeKey }
@@ -107,8 +106,6 @@ Type
     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 GetOperationHashToSign(const op : TOpChangeKeyData) : TRawBytes;
-    Class Function DoSignOperation(key : TECPrivateKey; var op : TOpChangeKeyData) : Boolean;
     class function OpType : Byte; override;
 
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
@@ -121,9 +118,10 @@ Type
     function N_Operation : Cardinal; override;
     procedure AffectedAccounts(list : TList); 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;
     Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
 
   { TOpChangeKeySigned }
@@ -159,6 +157,7 @@ Type
     Constructor Create(account_number, n_operation: Cardinal; fee: UInt64);
     Property Data : TOpRecoverFoundsData read FData;
     Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
 
   // NEW OPERATIONS PROTOCOL 2
@@ -211,9 +210,6 @@ Type
     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 GetOperationHashToSign(const operation : TOpListAccountData) : TRawBytes;
-    Class Function DoSignOperation(key : TECPrivateKey; var operation : TOpListAccountData) : Boolean;
-
     Function IsPrivateSale : Boolean;
     Function IsDelist : Boolean; virtual; abstract;
 
@@ -230,19 +226,20 @@ Type
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Property Data : TOpListAccountData read FData;
     Function toString : String; Override;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
 
   TOpListAccountForSale = Class(TOpListAccount)
   public
     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;
   End;
 
   TOpDelistAccountForSale = Class(TOpListAccount)
   public
     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;
   End;
 
@@ -253,7 +250,7 @@ Type
     procedure InitializeData; override;
   public
     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;
 
   { TOpChangeAccountInfo }
@@ -267,8 +264,6 @@ Type
     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 GetOperationHashToSign(const op : TOpChangeAccountInfoData) : TRawBytes;
-    Class Function DoSignOperation(key : TECPrivateKey; var op : TOpChangeAccountInfoData) : Boolean;
     class function OpType : Byte; override;
 
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
@@ -281,15 +276,68 @@ Type
     function N_Operation : Cardinal; override;
     procedure AffectedAccounts(list : TList); 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_name: Boolean; const new_name : TRawBytes;
       change_type: Boolean; const new_type : Word;
       fee: UInt64; payload: TRawBytes);
     Property Data : TOpChangeAccountInfoData read FData;
     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;
 
+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;
 
 implementation
@@ -308,6 +356,7 @@ Begin
   TPCOperationsComp.RegisterOperationClass(TOpChangeKeySigned);
   TPCOperationsComp.RegisterOperationClass(TOpChangeAccountInfo);
   TPCOperationsComp.RegisterOperationClass(TOpMultiOperation);
+  TPCOperationsComp.RegisterOperationClass(TOpData);
 End;
 
 { TOpChangeAccountInfo }
@@ -390,57 +439,6 @@ begin
   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;
 begin
   Result := CT_Op_ChangeAccountInfo;
@@ -540,7 +538,7 @@ begin
     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';
     FHasValidSignature := false;
     exit;
@@ -606,7 +604,8 @@ begin
   else Result := 0;
 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;
   const new_account_key: TAccountKey; change_name: Boolean;
   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.new_type:=new_type;
   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;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a new Change Info operation');
+    FHasValidSignature := false;
   end;
 end;
 
@@ -660,6 +661,40 @@ begin
      TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
 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 }
 
 procedure TOpTransaction.AffectedAccounts(list: TList);
@@ -671,7 +706,8 @@ begin
   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);
 begin
   inherited Create;
@@ -683,11 +719,12 @@ begin
   FData.payload := payload;
   // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
   // 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;
+  end else begin
+    TLog.NewLog(ltdebug,Classname,'No key for signing a new Transaction');
+    FHasValidSignature := false;
   end;
 end;
 
@@ -766,7 +803,7 @@ begin
   end;
 
   // Check signature
-  _h := GetTransactionHashToSign(FData);
+  _h := GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol);
   if (Not TCrypto.ECDSAVerify(sender.accountInfo.accountkey,_h,FData.sign)) then begin
     errors := 'Invalid sign';
     FHasValidSignature := false;
@@ -839,33 +876,10 @@ begin
     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);
   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;
 
-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;
 Var ms : TMemoryStream;
 begin
@@ -898,40 +912,6 @@ begin
   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;
 begin
   inherited;
@@ -1128,6 +1108,47 @@ begin
   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 }
 
 procedure TOpChangeKey.AffectedAccounts(list: TList);
@@ -1142,7 +1163,7 @@ begin
   else Result := 0;
 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
   inherited Create;
   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!
   // FData.public_key := key.PublicKey;
   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;
-  end else FHasValidSignature := true;
+  end;
 end;
 
 function TOpChangeKey.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString) : Boolean;
@@ -1245,7 +1269,7 @@ begin
     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';
     FHasValidSignature := false;
     exit;
@@ -1268,23 +1292,6 @@ begin
          FData.fee,errors);
 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;
 var ms : TMemoryStream;
   s : AnsiString;
@@ -1319,35 +1326,6 @@ begin
   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;
 begin
   inherited;
@@ -1463,6 +1441,41 @@ begin
     TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload)]);
 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 }
 
 class function TOpChangeKeySigned.OpType: Byte;
@@ -1517,7 +1530,7 @@ begin
     exit;
   end;
   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;
 
 function TOpRecoverFounds.GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes;
@@ -1616,6 +1629,11 @@ begin
     TAccountComp.FormatMoney(FData.fee),fData.n_operation]);
 end;
 
+function TOpRecoverFounds.GetDigestToSign(current_protocol : Word): TRawBytes;
+begin
+  Result := ''; // Nothing to be signed!
+end;
+
 { TOpListAccount }
 
 procedure TOpListAccount.AffectedAccounts(list: TList);
@@ -1735,7 +1753,7 @@ begin
     exit;
   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';
     FHasValidSignature := false;
     exit;
@@ -1763,60 +1781,12 @@ begin
          FData.fee,errors);
 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;
 begin
   // This Operation is new from protocol V2, so we cannot hash it as a previous protocol!
   Result := inherited GetBufferForOpHash(true);
 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;
 begin
   inherited;
@@ -1988,9 +1958,47 @@ begin
   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 }
 
-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;
   new_public_key: TAccountKey; locked_until_block: Cardinal; key: TECPrivateKey;
   payload: TRawBytes);
@@ -2008,10 +2016,14 @@ begin
   // FData.public_key := key.PublicKey;
   FData.new_public_key := new_public_key;
   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;
-  end else FHasValidSignature := true;
+  end;
 end;
 
 function TOpListAccountForSale.IsDelist: Boolean;
@@ -2026,7 +2038,7 @@ end;
 
 { 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
   inherited Create;
   FData.account_signer := account_signer;
@@ -2035,10 +2047,13 @@ begin
   FData.n_operation := n_operation;
   FData.fee := fee;
   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;
-  end else FHasValidSignature := true;
+  end;
 end;
 
 function TOpDelistAccountForSale.IsDelist: Boolean;
@@ -2053,7 +2068,7 @@ end;
 
 { 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;
   new_public_key: TAccountKey; key: TECPrivateKey; payload: TRawBytes);
 begin
@@ -2070,10 +2085,14 @@ begin
   FData.AccountPrice := price;
   FData.SellerAccount := account_to_pay;
   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;
-  end else FHasValidSignature := true;
+  end;
 end;
 
 procedure TOpBuyAccount.InitializeData;
@@ -2087,6 +2106,315 @@ begin
   Result := CT_Op_BuyAccount;
 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
   RegisterOperationsClass;
 end.

+ 67 - 46
src/core/URPC.pas

@@ -53,7 +53,7 @@ Type
     class procedure FillBlockObject(nBlock : Cardinal; ANode : TNode; jsonObject: TPCJSONObject);
     class procedure FillOperationObject(Const OPR : TOperationResume; currentNodeBlocksCount : Cardinal; 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);
   end;
 
@@ -294,7 +294,7 @@ begin
   jsonObject.GetAsVariant('rawoperations').Value:=OperationsHashTreeToHexaString(OperationsHashTree);
 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;
   opht : TOperationsHashTree;
   jsonArr : TPCJSONArray;
@@ -350,6 +350,10 @@ begin
   end;
   jsonObject.GetAsVariant('amount').Value:=ToJSONCurrency( multiOperation.OperationAmount );
   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('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
   // 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 Payload_method, EncodePwd : AnsiString) : TOpTransaction;
   // "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;
       end;
     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
       FreeAndNil(Result);
       ErrorNum:=CT_RPC_ErrNum_InternalError;
@@ -926,7 +930,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       sacc := FNode.Operations.SafeBoxTransaction.Account(sender);
       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;
       try
         If not FNode.AddOperation(Nil,opt,errors) then begin
@@ -945,7 +949,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     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;
     last_sender_n_operation : Cardinal;
     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;
     end;
     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;
       try
         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
   // 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)
   var i : Integer;
     errors : AnsiString;
@@ -1016,9 +1021,9 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       end;
     end else f_raw := '';
     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
-      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;
     if Not Result.HasValidSignature then begin
       FreeAndNil(Result);
@@ -1045,7 +1050,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       end;
       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;
       try
         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)
   // 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;
     fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpListAccountForSale;
   // "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;
       end;
     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);
     if Not Result.HasValidSignature then begin
       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)
   // 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;
   // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
   var i : Integer;
@@ -1155,7 +1160,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
       end;
     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
       FreeAndNil(Result);
       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)
   // It assumes that account_number,account_last_n_operation and account_pubkey are correct
   // 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;
     fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : TOpBuyAccount;
   // "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;
       end;
     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
       FreeAndNil(Result);
       ErrorNum:=CT_RPC_ErrNum_InternalError;
@@ -1292,7 +1297,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
               Exit;
             end;
             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;
             try
               operationsht.AddOperationToHashTree(opck);
@@ -1328,7 +1333,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     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;
     last_n_operation : Cardinal;
     fee : UInt64; Const RawPayload : TRawBytes; Const Payload_method, EncodePwd : AnsiString) : Boolean;
@@ -1344,7 +1349,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       Exit;
     end;
     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;
       try
         OperationsHashTree.AddOperationToHashTree(opck);
@@ -1526,7 +1531,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     Result := CapturePubKeyExt(params,prefix,pubkey,errortxt);
   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:
     // "account_signer" is the account that signs operations and pays the fee
     // "account_target" is the account being listed
@@ -1584,7 +1589,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         Exit;
       end;
     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,
       TCrypto.HexaToRaw(params.AsString('payload','')),
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
@@ -1602,6 +1607,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     OperationsHashTree : TOperationsHashTree;
     accountpubkey : TAccountKey;
     last_n_operation : Cardinal;
+    current_protocol : Word;
   begin
     Result := false;
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
@@ -1616,7 +1622,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
       end;
       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;
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
     finally
@@ -1624,7 +1631,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     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:
     // "account_signer" is the account that signs operations and pays the fee
     // "account_target" is the delisted account
@@ -1656,7 +1663,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       ErrorDesc := 'Invalid fee value';
       Exit;
     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',''));
     if opDelist=nil then exit;
     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)
   // 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;
     changeName: Boolean; Const new_name : TRawBytes;
     changeType: Boolean; new_type : Word;
@@ -1712,7 +1719,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
       end;
     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,
       changePubKey,new_account_pubkey,changeName,new_name,changeType,new_type,
       fee,f_raw);
@@ -1724,7 +1732,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     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:
     // "account_signer" is the account that signs operations and pays the fee
     // "account_target" is the target to change info
@@ -1791,7 +1799,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       changeType:=False;
     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,
       changeName,new_name,
       changeType,new_type,
@@ -1811,6 +1819,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     OperationsHashTree : TOperationsHashTree;
     accountpubkey : TAccountKey;
     last_n_operation : Cardinal;
+    current_protocol : Word;
   begin
     Result := false;
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
@@ -1825,7 +1834,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
       end;
       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;
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
     finally
@@ -1838,6 +1848,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     OperationsHashTree : TOperationsHashTree;
     accountpubkey : TAccountKey;
     last_n_operation : Cardinal;
+    current_protocol : Word;
   begin
     Result := false;
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
@@ -1852,7 +1863,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
       end;
       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;
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
     finally
@@ -1860,7 +1872,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     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:
     // "buyer_account" is the buyer account
     // "account_to_purchase" is the account to purchase
@@ -1917,7 +1929,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         Exit;
       end;
     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','')),
       params.AsString('payload_method','dest'),params.AsString('pwd',''));
     if opBuy=nil then exit;
@@ -1934,6 +1946,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     OperationsHashTree : TOperationsHashTree;
     accountpubkey : TAccountKey;
     last_n_operation : Cardinal;
+    current_protocol : Word;
   begin
     Result := false;
     if Not HexaStringToOperationsHashTree(HexaStringOperationsHashTree,OperationsHashTree,errors) then begin
@@ -1948,7 +1961,8 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         exit;
       end;
       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;
       TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
     finally
@@ -1998,7 +2012,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
           ErrorDesc := 'account_signer and account_target have distinct keys. Cannot sign';
           Exit;
         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);
         If not FNode.AddOperation(Nil,opt,errors) then begin
           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';
           Exit;
         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);
         If not FNode.AddOperation(Nil,opt,errors) then begin
           ErrorNum := CT_RPC_ErrNum_InternalError;
@@ -2099,7 +2113,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
           Exit;
         end;
         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);
         If not FNode.AddOperation(Nil,opt,errors) then begin
           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';
           Exit;
         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);
         If not FNode.AddOperation(Nil,opt,errors) then begin
           ErrorNum := CT_RPC_ErrNum_InternalError;
@@ -2435,7 +2449,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         end;
       end;
       // Return multioperation object:
-      TPascalCoinJSONComp.FillMultiOperationObject(mop,GetResultObject);
+      TPascalCoinJSONComp.FillMultiOperationObject(FNode.Bank.SafeBox.CurrentProtocol,mop,GetResultObject);
     finally
       OperationsHashTree.Free;
     end;
@@ -2515,11 +2529,12 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     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
       accounts_and_keys is a JSON ARRAY with Objects:
       - "account"
       - "b58_pubkey" or "enc_pubkey" : The public key of the "account"
+      - "protocol"
     }
   var i,iKey : Integer;
     pubKey : TAccountKey;
@@ -2534,7 +2549,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         iKey := _RPCServer.FWalletKeys.IndexOfAccountKey(pubKey);
         if (iKey>=0) 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;
@@ -2546,12 +2561,14 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     senderOperationsHashTree : TOperationsHashTree;
     mop : TOpMultiOperation;
     i,j : Integer;
+    protocol : Word;
   begin
     { This will SIGN a MultiOperation on a HexaStringOperationsHashTree in COLD mode (without knowledge of current public keys)
       Must provide param "accounts_and_keys"
       - "accounts_and_keys" is a JSON ARRAY with Objects:
         - "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
     }
     Result := false;
@@ -2561,15 +2578,16 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
       Exit;
     end;
+    protocol := params.GetAsVariant('protocol').AsCardinal(CT_BUILD_PROTOCOL);
     if Not HexaStringToOperationsHashTreeAndGetMultioperation(HexaStringOperationsHashTree,False,senderOperationsHashTree,mop,errors) then begin
       ErrorNum:=CT_RPC_ErrNum_InvalidData;
       ErrorDesc:= 'Error decoding param previous operations hash tree raw value: '+errors;
       Exit;
     end;
     Try
-      InternalMultiOperationSignCold(mop,params.GetAsArray('accounts_and_keys'),j);
+      InternalMultiOperationSignCold(mop,protocol,params.GetAsArray('accounts_and_keys'),j);
       // Return multioperation object:
-      TPascalCoinJSONComp.FillMultiOperationObject(mop,GetResultObject);
+      TPascalCoinJSONComp.FillMultiOperationObject(protocol,mop,GetResultObject);
       Result := True;
     finally
       senderOperationsHashTree.Free;
@@ -2611,7 +2629,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
             if (iKey>=0) then begin
               if (assigned(_RPCServer.FWalletKeys.Key[iKey].PrivateKey)) then begin
                 // 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;
@@ -2620,7 +2638,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
         lSigners.Free;
       end;
       // Return multioperation object:
-      TPascalCoinJSONComp.FillMultiOperationObject(mop,GetResultObject);
+      TPascalCoinJSONComp.FillMultiOperationObject(FNode.Bank.SafeBox.CurrentProtocol,mop,GetResultObject);
       Result := True;
     finally
       senderOperationsHashTree.Free;
@@ -3108,6 +3126,7 @@ begin
     end;
     Result := SignOpSendTo(
        params.AsString('rawoperations',''),
+       params.AsCardinal('protocol',CT_BUILD_PROTOCOL),
        params.AsCardinal('sender',CT_MaxAccount),params.AsCardinal('target',CT_MaxAccount),
        senderpubkey,destpubkey,
        params.AsCardinal('last_n_operation',0),
@@ -3202,7 +3221,9 @@ begin
       ErrorNum := CT_RPC_ErrNum_InvalidPubKey;
       exit;
     end;
-    Result := SignOpChangeKey(params.AsString('rawoperations',''),c2,c,
+    Result := SignOpChangeKey(params.AsString('rawoperations',''),
+       params.AsCardinal('protocol',CT_BUILD_PROTOCOL),
+       c2,c,
        senderpubkey,destpubkey,
        params.AsCardinal('last_n_operation',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;
     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;
     function OperationAmount : Int64; override;
     function OperationFee : Int64; override;
@@ -130,7 +129,7 @@ Type
     function GetAccountN_Operation(account : Cardinal) : Cardinal; 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;
     Function AddTx(const senders : TMultiOpSenders; const receivers : TMultiOpReceivers; setInRandomOrder : Boolean) : Boolean;
     Function AddChangeInfos(const changes : TMultiOpChangesInfo; setInRandomOrder : Boolean) : Boolean;
@@ -147,6 +146,7 @@ Type
     //
     Function toString : String; Override;
     Property Data : TOpMultiOperationData read FData;
+    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
   End;
 
 implementation
@@ -446,7 +446,7 @@ begin
   SetLength(errors,0);
   // Do check it!
   Try
-    ophtosign := GetTransactionHashToSign;
+    ophtosign := GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol);
     // Tx verification
     For i:=Low(FData.txSenders) to High(FData.txSenders) do begin
       acc := AccountTransaction.Account(FData.txSenders[i].Account);
@@ -692,29 +692,7 @@ begin
   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;
   raw : TRawBytes;
   _sign : TECDSA_SIG;
@@ -723,7 +701,7 @@ begin
   If Not Assigned(key.PrivateKey) then begin
     exit;
   end;
-  raw := GetTransactionHashToSign;
+  raw := GetDigestToSign(current_protocol);
   Try
     _sign := TCrypto.ECDSASign(key.PrivateKey,raw);
   Except
@@ -848,7 +826,7 @@ begin
   until i<0;
 end;
 
-constructor TOpMultiOperation.CreateMultiOperation(
+constructor TOpMultiOperation.CreateMultiOperation(current_protocol : Word;
   const senders: TMultiOpSenders; const receivers: TMultiOpReceivers;
   const changes: TMultiOpChangesInfo; const senders_keys,
   changes_keys: array of TECPrivateKey);
@@ -862,13 +840,13 @@ begin
   If (length(senders_keys)<>length(senders)) then exit; // Cannot sign!
   If (length(changes_keys)<>length(changes)) then exit; // Cannot sign!
   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');
       Exit;
     end;
   end;
   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');
       Exit;
     end;
@@ -1040,5 +1018,34 @@ begin
      TAccountComp.FormatMoney(FTotalFee)]);
 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.
 

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

@@ -60,7 +60,7 @@ uses
   ShellApi,
 {$ELSE}
 {$ENDIF}
-  UFolderHelper, UConst, UNode;
+  UFolderHelper, UConst, UNode, UOpenSSL;
 
 {$IFnDEF FPC}
   {$R *.dfm}
@@ -70,7 +70,7 @@ uses
 
 procedure TFRMAbout.FormCreate(Sender: TObject);
 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,
     CT_NetProtocol_Version, CT_NetProtocol_Available]);
 end;

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

@@ -276,7 +276,7 @@ loop_start:
         end else begin
         end;
         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(_totalfee,_fee);
         end;
@@ -296,11 +296,11 @@ loop_start:
           if uint64(_totalSignerFee) >= signerAccount.balance then _fee := 0
           else if signerAccount.balance - uint64(_totalSignerFee) > uint64(DefaultFee) then _fee := DefaultFee
           else _fee := signerAccount.balance - uint64(_totalSignerFee);
-          op := TOpChangeKeySigned.Create(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(_totalSignerFee, _fee);
         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;
         inc(_totalfee,_fee);
         operationstxt := 'Change private key to '+TAccountComp.GetECInfoTxt(_newOwnerPublicKey.EC_OpenSSL_NID);
@@ -312,9 +312,9 @@ loop_start:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         else _fee := signerAccount.balance;
         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
-          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');
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsDelist) then begin
@@ -323,12 +323,12 @@ loop_start:
         // Special fee account:
         if signerAccount.balance>DefaultFee then _fee := DefaultFee
         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}
       end else if (PageControlOpType.ActivePage = tsBuyAccount) then begin
         {%region Operation: Buy Account}
         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);
         {%endregion}
       end else if (PageControlOpType.ActivePage = tsChangeInfo) then begin
@@ -338,7 +338,7 @@ loop_start:
         end else begin
           if signerAccount.balance>DefaultFee then _fee := DefaultFee
           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);
         end;
         {%endregion}

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

@@ -333,7 +333,7 @@ begin
     If (j>=0) then begin
       // Can sign
       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;
@@ -342,7 +342,7 @@ begin
     If (j>=0) then begin
       // Can sign
       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;

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

@@ -71,8 +71,8 @@ type
   private
   public
     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;
 
 implementation
@@ -112,7 +112,7 @@ begin
   Result := True;
 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;
   iKey : Integer;
   opTx : TOpTransaction;
@@ -139,7 +139,7 @@ begin
   until (destAcc.account <> senderAcc.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
     Result := operationsComp.AddOperation(True,opTx,errors);
   finally
@@ -147,7 +147,7 @@ begin
   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);
    var n : Integer;
      i,j : Integer;
@@ -158,7 +158,7 @@ class function TRandomGenerateOperation.GenerateOpMultiOperation(const operation
        If (j>=0) then begin
          // Can sign
          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;
@@ -167,7 +167,7 @@ class function TRandomGenerateOperation.GenerateOpMultiOperation(const operation
        If (j>=0) then begin
          // Can sign
          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;
@@ -319,10 +319,10 @@ begin
   While (nCounter<max) And (Not FStopRandomOperations) do begin
     Case Random(30) of
       0..20 : begin
-        If TRandomGenerateOperation.GenerateOpTransaction(operationsComp,FSourceWalletKeys) then inc(nCounter);
+        If TRandomGenerateOperation.GenerateOpTransaction(SourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(nCounter);
       end;
       21..25 : begin
-        If TRandomGenerateOperation.GenerateOpMultiOperation(operationsComp,FSourceWalletKeys) then inc(nCounter);
+        If TRandomGenerateOperation.GenerateOpMultiOperation(SourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(nCounter);
       end;
     else Sleep(10);
     end;

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

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

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

@@ -31,6 +31,7 @@ uses
   ExtCtrls, ComCtrls, UWallet, StdCtrls, ULog, Grids, UAppParams, UBlockChain,
   UNode, UGridUtils, UJSONFunctions, UAccounts, Menus, ImgList, UNetProtocol,
   UCrypto, Buttons, UPoolMining, URPC, UFRMAccountSelect, UConst,
+  UAccountKeyStorage,
   UFRMRPCCalls, UTxMultiOperation;
 
 Const
@@ -231,6 +232,11 @@ type
     Procedure FinishedLoadingApp;
     Procedure FillAccountInformation(Const Strings : TStrings; Const AccountNumber : Cardinal);
     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
     { Private declarations }
     FNode : TNode;
@@ -307,17 +313,32 @@ Uses UFolderHelper, UOpenSSL, UOpenSSLdef, UTime, UFileStorage,
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText, USettings;
 
 Type
+
+  { TThreadActivate }
+
   TThreadActivate = Class(TPCThread)
+    procedure OnProgressNotify(sender : TObject; const mesage : AnsiString; curPos, totalCount : Int64);
   protected
     procedure BCExecute; override;
   End;
 
 { 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;
 begin
   // 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.NetServer.Active := true;
   Synchronize( FRMWallet.DoUpdateAccounts );
@@ -366,7 +387,6 @@ begin
     TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
     TFileStorage(FNode.Bank.Storage).Initialize;
     // Init Grid
-    //FAccountsGrid.Node := FNode;
     FSelectedAccountsGrid.Node := FNode;
     FWalletKeys.OnChanged.Add( OnWalletChanged );
     FAccountsGrid.Node := FNode;
@@ -377,17 +397,8 @@ begin
     FBlockChainGrid.HashRateAs := TShowHashRateAs(i);
     // Reading database
     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;
+    UpdateNodeStatus;
   Except
     On E:Exception do begin
       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;
 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.Port := FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port);
   FPoolMiningServer.MinerAccountKey := GetAccountKeyForMiner;
@@ -757,9 +779,14 @@ begin
   end;
   PageControl.Visible:=True;
   PageControl.Enabled:=True;
+
+
+
+  UpdatePrivateKeys;
 end;
 
-procedure TFRMWallet.FillAccountInformation(const Strings: TStrings; Const AccountNumber: Cardinal);
+procedure TFRMWallet.FillAccountInformation(const Strings: TStrings;
+  const AccountNumber: Cardinal);
 Var account : TAccount;
   s : String;
 begin
@@ -795,7 +822,8 @@ begin
   end;
 end;
 
-procedure TFRMWallet.FillOperationInformation(const Strings: TStrings; Const OperationResume: TOperationResume);
+procedure TFRMWallet.FillOperationInformation(const Strings: TStrings;
+  const OperationResume: TOperationResume);
 var i : Integer;
   jsonObj : TPCJSONObject;
 begin
@@ -846,6 +874,109 @@ begin
   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;
 begin
   Result := false;
@@ -937,6 +1068,10 @@ begin
   cbHashRateUnits.Items.Add('Th/s');
   cbHashRateUnits.Items.Add('Ph/s');
   cbHashRateUnits.Items.Add('Eh/s');
+  {$IFDEF TESTING_NO_POW_CHECK}
+  // Things for testing purposes only
+  InitMenuForTesting;
+  {$ENDIF}
 end;
 
 procedure TFRMWallet.ebHashRateBackBlocksKeyPress(Sender: TObject; var Key: char);
@@ -1504,7 +1639,8 @@ begin
     UpdateBlockChainState;
   Except
     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;
     end;
   end;
@@ -1558,6 +1694,7 @@ end;
 procedure TFRMWallet.OnNodeKeysActivity(Sender: TObject);
 begin
   DoUpdateAccounts;
+//  Application.ProcessMessages; // XXXXXXXXXXX
 end;
 
 procedure TFRMWallet.OnReceivedHelloMessage(Sender: TObject);
@@ -1717,6 +1854,7 @@ begin
     UpdateConnectionStatus;
     UpdateBlockChainState;
     UpdateNodeStatus;
+//    Application.ProcessMessages; // XXXXXXXXXXX
   Except
     On E:Exception do begin
       E.Message := 'Exception at TimerUpdate '+E.ClassName+': '+E.Message;
@@ -1749,29 +1887,39 @@ begin
         if cbMyPrivateKeys.ItemIndex=0 then begin
           // All keys in the wallet
           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;
+            finally
+              FOrderedAccountsKeyList.Unlock;
             end;
           end;
         end else begin
           i := PtrInt(cbMyPrivateKeys.Items.Objects[cbMyPrivateKeys.ItemIndex]);
           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;
+            finally
+              FOrderedAccountsKeyList.Unlock;
             end;
           end;
         end;
@@ -1795,6 +1943,7 @@ begin
   // Show Totals:
   lblAccountsBalance.Caption := TAccountComp.FormatMoney(FAccountsGrid.AccountsBalance);
   UpdateOperations;
+//  Application.ProcessMessages; // XXXXXXXXXXX
 end;
 
 procedure TFRMWallet.UpdateAvailableConnections;
@@ -1846,8 +1995,13 @@ begin
     end else lblCurrentBlock.Caption :=  '(none)';
     lblCurrentAccounts.Caption := Inttostr(FNode.Bank.AccountsCount);
     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);
     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);
@@ -2009,6 +2163,8 @@ begin
   cbMyPrivateKeys.items.BeginUpdate;
   Try
     cbMyPrivateKeys.Items.Clear;
+    if assigned(FOrderedAccountsKeyList) then FOrderedAccountsKeyList.SafeBox.StartThreadSafe;
+    Try
     For i:=0 to FWalletKeys.Count-1 do begin
       wk := FWalletKeys.Key[i];
       if assigned(FOrderedAccountsKeyList) then begin
@@ -2025,6 +2181,10 @@ begin
       end;
       cbMyPrivateKeys.Items.AddObject(s,TObject(i));
     end;
+
+    finally
+      if assigned(FOrderedAccountsKeyList) then FOrderedAccountsKeyList.SafeBox.EndThreadSave;
+    end;
     cbMyPrivateKeys.Sorted := true;
     cbMyPrivateKeys.Sorted := false;
     cbMyPrivateKeys.Items.InsertObject(0,'(All my private keys)',TObject(-1));