Browse Source

Multithreading in verification

Implementation of TPCOperationsSignatureValidator and TPCOperationsBlockValidator to allow multithreading on pre-validating operations and blocks for fast processing when working on computers with +2 CPU's
PascalCoin 6 years ago
parent
commit
5b4ba669d2

+ 6 - 4
src/core/UAccounts.pas

@@ -308,7 +308,7 @@ Type
     class Function ConcatSafeBoxStream(Source1, Source2, Dest : TStream; var errors : String) : Boolean;
     class function ValidAccountName(const new_name : TRawBytes; var errors : String) : Boolean;
 
-    Function IsValidNewOperationsBlock(Const newOperationBlock : TOperationBlock; checkSafeBoxHash : Boolean; var errors : String) : Boolean;
+    Function IsValidNewOperationsBlock(Const newOperationBlock : TOperationBlock; checkSafeBoxHash, checkValidOperationsBlock : Boolean; var errors : String) : Boolean;
     class Function IsValidOperationBlock(Const newOperationBlock : TOperationBlock; var errors : String) : Boolean;
     Function GetActualTargetHash(protocolVersion : Word): TRawBytes;
     Function GetActualCompactTargetHash(protocolVersion : Word): Cardinal;
@@ -2801,7 +2801,7 @@ begin
               // For TESTNET increase speed purposes, will only check latests blocks
             if ((iblock + (CT_BankToDiskEveryNBlocks * 10)) >= sbHeader.blockscount) then begin
             {$ENDIF}
-              If not IsValidNewOperationsBlock(block.blockchainInfo,False,aux_errors) then begin
+              If not IsValidNewOperationsBlock(block.blockchainInfo,False,True,aux_errors) then begin
                 errors := errors + ' > ' + aux_errors;
                 exit;
               end;
@@ -3279,7 +3279,7 @@ begin
   Result := Copy(_initialSafeboxHash);
 end;
 
-function TPCSafeBox.IsValidNewOperationsBlock(const newOperationBlock: TOperationBlock; checkSafeBoxHash : Boolean; var errors: String): Boolean;
+function TPCSafeBox.IsValidNewOperationsBlock(const newOperationBlock: TOperationBlock; checkSafeBoxHash, checkValidOperationsBlock : Boolean; var errors: String): Boolean;
   { This function will check a OperationBlock info as a valid candidate to be included in the safebox
 
     TOperationBlock contains the info of the new block EXCEPT the operations, including only operations_hash value (SHA256 of the Operations)
@@ -3373,7 +3373,9 @@ begin
     exit;
   end;
   {$ENDIF}
-  Result := IsValidOperationBlock(newOperationBlock,errors);
+  if checkValidOperationsBlock then begin
+    Result := IsValidOperationBlock(newOperationBlock,errors);
+  end else Result := True;
 end;
 
 class function TPCSafeBox.IsValidOperationBlock(const newOperationBlock: TOperationBlock; var errors: String): Boolean;

+ 93 - 16
src/core/UBlockChain.pas

@@ -257,6 +257,8 @@ Type
     //
     function GetOperationStreamData : TBytes;
     class function GetOperationFromStreamData(StreamData : TBytes) : TPCOperation;
+    //
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; virtual; abstract;
   End;
 
   TPCOperationStorage = Record
@@ -339,6 +341,9 @@ Type
     Property OnChanged : TNotifyEvent read FOnChanged write FOnChanged;
     Property Max0feeOperationsBySigner : Integer Read FMax0feeOperationsBySigner write SetMax0feeOperationsBySigner;
     procedure MarkVerifiedECDSASignatures(operationsHashTreeToMark : TOperationsHashTree);
+
+    // Will add all operations of the HashTree to then end of AList without removing previous objects
+    function GetOperationsList(AList : TList<TPCOperation>; AAddOnlyOperationsWithoutNotVerifiedSignature : Boolean) : Integer;
   End;
 
   { TPCOperationsComp }
@@ -357,6 +362,7 @@ Type
     FDisableds : Integer;
     FOperationsLock : TPCCriticalSection;
     FPreviousUpdatedBlocks : TAccountPreviousBlockInfo; // New Protocol V3 struct to store previous updated blocks
+    FHasValidOperationBlockInfo : Boolean;
     function GetOperation(index: Integer): TPCOperation;
     procedure SetBank(const value: TPCBank);
     procedure SetnOnce(const value: Cardinal);
@@ -421,6 +427,7 @@ Type
     Property PoW_Digest_Part3 : TRawBytes read FDigest_Part3;
     //
     Property PreviousUpdatedBlocks : TAccountPreviousBlockInfo read FPreviousUpdatedBlocks; // New Protocol V3 struct to store previous updated blocks
+    Property HasValidOperationBlockInfo : Boolean read FHasValidOperationBlockInfo write FHasValidOperationBlockInfo;
   End;
 
   TPCBankLog = procedure(sender: TPCBank; Operations: TPCOperationsComp; Logtype: TLogType ; const Logtxt: String) of object;
@@ -546,7 +553,9 @@ implementation
 
 uses
   Variants,
-  UTime, UConst, UOpTransaction;
+  UTime, UConst, UOpTransaction, UPCOrderedLists,
+  UPCOperationsSignatureValidator,
+  UPCOperationsBlockValidator;
 
 { TPCOperationsStorage }
 
@@ -890,9 +899,12 @@ procedure TPCBank.DiskRestoreFromOperations(max_block : Int64; restoreProgressNo
 Var
   errors: String;
   newBlock: TBlockAccount;
-  Operations: TPCOperationsComp;
   n : Int64;
   tc : TTickCount;
+  LBlocks : TList<TPCOperationsComp>;
+  LTmpPCOperationsComp : TPCOperationsComp;
+  i,j : Integer;
+  LSafeboxTransaction : TPCSafeBoxTransaction;
 begin
   if FIsRestoringFromFile then begin
     TLog.NewLog(lterror,Classname,'Is Restoring!!!');
@@ -919,16 +931,42 @@ begin
         end;
       end;
       NewLog(Nil, ltinfo,'Start restoring from disk operations (Max '+inttostr(max_block)+') BlockCount: '+inttostr(BlocksCount)+' Orphan: ' +Storage.Orphan);
-      Operations := TPCOperationsComp.Create(Self);
+      LBlocks := TList<TPCOperationsComp>.Create;
       try
         while ((BlocksCount<=max_block)) do begin
-          if Storage.BlockExists(BlocksCount) then begin
-            if Storage.LoadBlockChainBlock(Operations,BlocksCount) then begin
+          i := BlocksCount;
+          j := i + 99;
+          // Load a batch of TPCOperationsComp;
+          try
+            while ((i<=max_block) and (i<=j)) do begin
+              if Storage.BlockExists(i) then begin
+                LTmpPCOperationsComp := TPCOperationsComp.Create(Self);
+                if Storage.LoadBlockChainBlock(LTmpPCOperationsComp,i) then begin
+                  LBlocks.Add(LTmpPCOperationsComp);
+                  inc(i);
+                end else begin
+                  LTmpPCOperationsComp.Free;
+                  Break;
+                end;
+              end else Break;
+            end;
+
+            if (LBlocks.Count=0) then Exit;
+            
+            TPCOperationsBlockValidator.MultiThreadValidateOperationsBlock(LBlocks);
+            LSafeboxTransaction := TPCSafeBoxTransaction.Create(SafeBox);
+            try
+              TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(LSafeboxTransaction,LBlocks);
+            finally
+              LSafeboxTransaction.Free;
+            end;
+
+            for i := 0 to LBlocks.Count-1 do begin
               SetLength(errors,0);
-              if Not AddNewBlockChainBlock(Operations,0,newBlock,errors) then begin
-                NewLog(Operations, lterror,'Error restoring block: ' + Inttostr(BlocksCount)+ ' Errors: ' + errors);
+              if Not AddNewBlockChainBlock(LBlocks[i],0,newBlock,errors) then begin
+                NewLog(LBlocks[i], lterror,'Error restoring block: ' + Inttostr(BlocksCount)+ ' Errors: ' + errors);
                 Storage.DeleteBlockChainBlocks(BlocksCount);
-                break;
+                Exit;
               end else begin
                 // To prevent continuous saving...
                 if ((BlocksCount+(CT_BankToDiskEveryNBlocks*2)) >= Storage.LastBlock ) or
@@ -937,17 +975,26 @@ begin
                 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);
+                  restoreProgressNotify(Self,Format('Reading blockchain block %d/%d',[LBlocks[i].OperationBlock.block,Storage.LastBlock]),BlocksCount,Storage.LastBlock);
                 end;
               end;
-            end else break;
-          end else break;
-        end;
-        if FUpgradingToV2 then Storage.CleanupVersion1Data;
+            end;
+          finally
+            // Free blocks
+            for i := 0 to LBlocks.Count-1 do begin
+              LBlocks[i].Free;
+            end;
+            LBlocks.Clear;
+          end;
+
+        end; // while
+
       finally
-        Operations.Free;
+        LBlocks.Free;
+        if FUpgradingToV2 then Storage.CleanupVersion1Data;
+        NewLog(Nil, ltinfo,'End restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' + Storage.Orphan+' Restored '+Inttostr(BlocksCount)+' blocks');
       end;
-      NewLog(Nil, ltinfo,'End restoring from disk operations (Max '+inttostr(max_block)+') Orphan: ' + Storage.Orphan+' Restored '+Inttostr(BlocksCount)+' blocks');
+
     finally
       FIsRestoringFromFile := False;
       FUpgradingToV2 := false;
@@ -1249,6 +1296,8 @@ begin
     // Note:
     // This function does not initializes "account_key" nor "block_payload" fields
 
+    FHasValidOperationBlockInfo := False;
+
     FOperationBlock.timestamp := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
     if Assigned(FBank) then begin
       resetNewTarget := False;
@@ -1315,6 +1364,7 @@ begin
     FDigest_Part1 := Operations.FDigest_Part1;
     FDigest_Part2_Payload := Operations.FDigest_Part2_Payload;
     FDigest_Part3 := Operations.FDigest_Part3;
+    FHasValidOperationBlockInfo := Operations.FHasValidOperationBlockInfo;
   finally
     Operations.Unlock;
     Unlock;
@@ -1338,6 +1388,8 @@ begin
       FSafeBoxTransaction.CopyFrom(Operations.FSafeBoxTransaction);
     end;
     FPreviousUpdatedBlocks.CopyFrom(Operations.FPreviousUpdatedBlocks);
+
+    FHasValidOperationBlockInfo := False;
     // Recalc all
     Calc_Digest_Parts; // Does not need to recalc PoW
   finally
@@ -1362,6 +1414,7 @@ begin
   FOperationBlock := GetFirstBlock;
   FSafeBoxTransaction := Nil;
   FPreviousUpdatedBlocks := TAccountPreviousBlockInfo.Create;
+  FHasValidOperationBlockInfo := False;
   if Assigned(ABank) then begin
     SetBank( TPCBank(ABank) );
   end else Clear(true);
@@ -1896,10 +1949,13 @@ begin
       exit;
     end;
     // Check OperationBlock info:
-    If not SafeBoxTransaction.FreezedSafeBox.IsValidNewOperationsBlock(OperationBlock,True,errors) then exit;
+    If not SafeBoxTransaction.FreezedSafeBox.IsValidNewOperationsBlock(OperationBlock,True,Not HasValidOperationBlockInfo, errors) then exit;
     // Execute SafeBoxTransaction operations:
     SafeBoxTransaction.Rollback;
     FPreviousUpdatedBlocks.Clear;
+    //
+    TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(SafeBoxTransaction,OperationsHashTree);
+    //
     for i := 0 to Count - 1 do begin
       If Not Operation[i].DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors) then begin
         errors := 'Error executing operation '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors;
@@ -2253,6 +2309,27 @@ begin
   end;
 end;
 
+function TOperationsHashTree.GetOperationsList(AList: TList<TPCOperation>; AAddOnlyOperationsWithoutNotVerifiedSignature : Boolean) : Integer;
+Var LList : TList<Pointer>;
+  i : Integer;
+  LOp : TPCOperation;
+begin
+  Result := 0;
+  LList := FHashTreeOperations.LockList;
+  try
+    for i := 0 to LList.Count-1 do begin
+      LOp := POperationHashTreeReg(LList[i])^.Op;
+      if (Not AAddOnlyOperationsWithoutNotVerifiedSignature) or
+        (AAddOnlyOperationsWithoutNotVerifiedSignature and (not LOp.HasValidSignature)) then begin
+        AList.Add( LOp );
+        inc(Result);
+      end;
+    end;
+  finally
+    FHashTreeOperations.UnlockList;
+  end;
+end;
+
 function TOperationsHashTree.IndexOfOperation(op: TPCOperation): Integer;
 Var iPosInOrdered : Integer;
   l : TList<Pointer>;

+ 49 - 19
src/core/UNetProtocol.pas

@@ -500,7 +500,8 @@ Const
 implementation
 
 uses
-  UConst, ULog, UNode, UTime, UPCEncryption, UChunk;
+  UConst, ULog, UNode, UTime, UPCEncryption, UChunk,
+  UPCOperationsBlockValidator, UPCOperationsSignatureValidator;
 
 Const
   CT_NetTransferType : Array[TNetTransferType] of String = ('Unknown','Request','Response','Autosend');
@@ -2893,11 +2894,15 @@ begin
 end;
 
 procedure TNetConnection.DoProcess_GetBlocks_Response(HeaderData: TNetHeaderData; DataBuffer: TStream);
-  var op : TPCOperationsComp;
-    opcount,i : Cardinal;
+  var LTmpOp : TPCOperationsComp;
+    LOpCountCardinal,c : Cardinal;
+    LOpCount : Integer;
+    i : Integer;
     newBlockAccount : TBlockAccount;
   errors : String;
   DoDisconnect : Boolean;
+  LBlocks : TList<TPCOperationsComp>;
+  LSafeboxTransaction : TPCSafeBoxTransaction;
 begin
   DoDisconnect := true;
   try
@@ -2911,50 +2916,75 @@ begin
     end;
     // DataBuffer contains: from and to
     errors := 'Invalid structure';
-    op := TPCOperationsComp.Create(nil);
+    LBlocks := TList<TPCOperationsComp>.Create;
     Try
-      op.bank := TNode.Node.Bank;
       if DataBuffer.Size-DataBuffer.Position<4 then begin
         DisconnectInvalidClient(false,'DoProcess_GetBlocks_Response invalid format: '+errors);
         exit;
       end;
-      DataBuffer.Read(opcount,4);
+      DataBuffer.Read(LOpCountCardinal,4);
+      LOpCount := LOpCountCardinal;
       DoDisconnect :=false;
-      for I := 1 to opcount do begin
-        if Not op.LoadBlockFromStream(DataBuffer,errors) then begin
-           errors := 'Error decoding block '+inttostr(i)+'/'+inttostr(opcount)+' Errors:'+errors;
-           DoDisconnect := true;
-           exit;
+      for i := 0 to LOpCount-1 do begin
+        LTmpOp := TPCOperationsComp.Create(nil);
+        try
+          LTmpOp.bank := TNode.Node.Bank;
+          if Not LTmpOp.LoadBlockFromStream(DataBuffer,errors) then begin
+             errors := 'Error decoding block '+inttostr(i+1)+'/'+inttostr(LOpCount)+' Errors:'+errors;
+             DoDisconnect := true;
+             Exit;
+          end;
+
+          if (LTmpOp.OperationBlock.block=TNode.Node.Bank.BlocksCount+i) then begin
+            TNode.Node.MarkVerifiedECDSASignaturesFromMemPool(LTmpOp); // Improvement speed v4.0.2
+            LBlocks.Add(LTmpOp);
+            LTmpOp := Nil;
+          end else Break;
+        finally
+          FreeAndNil(LTmpOp);
         end;
-        if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
-          TNode.Node.MarkVerifiedECDSASignaturesFromMemPool(op); // Improvement speed v4.0.2
-          if (TNode.Node.Bank.AddNewBlockChainBlock(op,TNetData.NetData.NetworkAdjustedTime.GetMaxAllowedTimestampForNewBlock, newBlockAccount,errors)) then begin
+      end;
+
+      TPCOperationsBlockValidator.MultiThreadValidateOperationsBlock(LBlocks);
+      LSafeboxTransaction := TPCSafeBoxTransaction.Create(TNode.Node.Bank.SafeBox);
+      try
+        TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(LSafeboxTransaction,LBlocks);
+      finally
+        LSafeboxTransaction.Free;
+      end;
+
+      for i := 0 to LBlocks.Count-1 do begin
+        if (LBlocks[i].OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
+          if (TNode.Node.Bank.AddNewBlockChainBlock(LBlocks[i],TNetData.NetData.NetworkAdjustedTime.GetMaxAllowedTimestampForNewBlock, newBlockAccount,errors)) then begin
             // Ok, one more!
           end else begin
             // Is not a valid entry????
             // Perhaps an orphan blockchain: Me or Client!
             TLog.NewLog(ltinfo,Classname,'Distinct operation block found! My:'+
                 TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.SafeBox.Block(TNode.Node.Bank.BlocksCount-1).blockchainInfo)+
-                ' remote:'+TPCOperationsComp.OperationBlockToText(op.OperationBlock)+' Errors: '+errors);
+                ' remote:'+TPCOperationsComp.OperationBlockToText(LBlocks[i].OperationBlock)+' Errors: '+errors);
           end;
         end else begin
           // Receiving an unexpected operationblock
-          TLog.NewLog(lterror,classname,'Received a distinct block, finalizing: '+TPCOperationsComp.OperationBlockToText(op.OperationBlock)+' (My block: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock)+')' );
+          TLog.NewLog(lterror,classname,'Received a distinct block, finalizing: '+TPCOperationsComp.OperationBlockToText(LBlocks[i].OperationBlock)+' (My block: '+TPCOperationsComp.OperationBlockToText(TNode.Node.Bank.LastOperationBlock)+')' );
           FIsDownloadingBlocks := false;
           exit;
         end;
         sleep(1);
       end;
       FIsDownloadingBlocks := false;
-      if ((opcount>0) And (FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount)) then begin
-        Send_GetBlocks(TNode.Node.Bank.BlocksCount,100,i);
+      if ((LOpCount>0) And (FRemoteOperationBlock.block>=TNode.Node.Bank.BlocksCount)) then begin
+        Send_GetBlocks(TNode.Node.Bank.BlocksCount,100,c);
       end else begin
         // No more blocks to download, download Pending operations
         DoProcess_GetPendingOperations;
       end;
       TNode.Node.NotifyBlocksChanged;
     Finally
-      op.Free;
+      for i := 0 to LBlocks.Count-1 do begin
+        LBlocks[i].Free;
+      end;
+      LBlocks.Free;
     End;
   Finally
     if DoDisconnect then begin

+ 10 - 7
src/core/UNode.pas

@@ -194,7 +194,7 @@ Type
 
 implementation
 
-Uses UOpTransaction, UConst, UTime, UCommon;
+Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator;
 
 var _Node : TNode;
 
@@ -424,6 +424,7 @@ begin
       {$IFDEF BufferOfFutureOperations}
       Process_BufferOfFutureOperations(valids_operations);
       {$ENDIF}
+
       for j := 0 to OperationsHashTree.OperationsCount-1 do begin
         ActOp := OperationsHashTree.GetOperation(j);
         If (FOperations.OperationsHashTree.IndexOfOperation(ActOp)<0) then begin
@@ -477,12 +478,14 @@ begin
               {$IFDEF BufferOfFutureOperations}
               // Used to solve 2.0.0 "invalid order of operations" bug
               If (Assigned(SenderConnection)) Then begin
-                sAcc := FOperations.SafeBoxTransaction.Account(ActOp.SignerAccount);
-                If (sAcc.n_operation<ActOp.N_Operation) Or
-                   ((sAcc.n_operation=ActOp.N_Operation) AND (sAcc.balance=0) And (ActOp.OperationFee>0) And (ActOp.OpType = CT_Op_Changekey)) then begin
-                  If FBufferAuxWaitingOperations.IndexOfOperation(ActOp)<0 then begin
-                    FBufferAuxWaitingOperations.AddOperationToHashTree(ActOp);
-                    TLog.NewLog(ltInfo,Classname,Format('New FromBufferWaitingOperations %d/%d (new buffer size:%d): %s',[j+1,OperationsHashTree.OperationsCount,FBufferAuxWaitingOperations.OperationsCount,ActOp.ToString]));
+                if ActOp.SignerAccount<FOperations.SafeBoxTransaction.FreezedSafeBox.AccountsCount then begin
+                  sAcc := FOperations.SafeBoxTransaction.Account(ActOp.SignerAccount);
+                  If (sAcc.n_operation<ActOp.N_Operation) Or
+                     ((sAcc.n_operation=ActOp.N_Operation) AND (sAcc.balance=0) And (ActOp.OperationFee>0) And (ActOp.OpType = CT_Op_Changekey)) then begin
+                    If FBufferAuxWaitingOperations.IndexOfOperation(ActOp)<0 then begin
+                      FBufferAuxWaitingOperations.AddOperationToHashTree(ActOp);
+                      TLog.NewLog(ltInfo,Classname,Format('New FromBufferWaitingOperations %d/%d (new buffer size:%d): %s',[j+1,OperationsHashTree.OperationsCount,FBufferAuxWaitingOperations.OperationsCount,ActOp.ToString]));
+                    end;
                   end;
                 end;
               end;

+ 49 - 0
src/core/UOpTransaction.pas

@@ -98,6 +98,8 @@ Type
     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;
+
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
   { TOpChangeKey }
@@ -127,6 +129,8 @@ Type
     Property Data : TOpChangeKeyData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
   { TOpChangeKeySigned }
@@ -163,6 +167,7 @@ Type
     Property Data : TOpRecoverFoundsData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
   // NEW OPERATIONS PROTOCOL 2
@@ -232,6 +237,7 @@ Type
     Property Data : TOpListAccountData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
   TOpListAccountForSale = Class(TOpListAccount)
@@ -290,6 +296,8 @@ Type
     Property Data : TOpChangeAccountInfoData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
 
@@ -338,6 +346,7 @@ Type
     Property Data : TOpDataData read FData;
     Function toString : String; Override;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
 Const
@@ -372,6 +381,13 @@ begin
   FData := CT_TOpChangeAccountInfoData_NUL;
 end;
 
+function TOpChangeAccountInfo.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.account_signer);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpChangeAccountInfo.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
 var b : byte;
 begin
@@ -923,6 +939,13 @@ begin
   FData := CT_TOpTransactionData_NUL;
 end;
 
+function TOpTransaction.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.sender);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpTransaction.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
 var b : Byte;
 begin
@@ -1338,6 +1361,13 @@ begin
   FData := CT_TOpChangeKeyData_NUL;
 end;
 
+function TOpChangeKey.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.account_signer);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpChangeKey.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
 var raw : TRawBytes;
 begin
@@ -1564,6 +1594,11 @@ begin
   FData := CT_TOpRecoverFoundsData_NUL;
 end;
 
+function TOpRecoverFounds.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+begin
+  Result := True; // Nobody signs here
+end;
+
 function TOpRecoverFounds.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
 begin
   Result := false;
@@ -1804,6 +1839,13 @@ begin
   Result := (Not IsDelist) And (FData.new_public_key.EC_OpenSSL_NID<>0);
 end;
 
+function TOpListAccount.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.account_signer);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpListAccount.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
 var raw : TRawBytes;
   w : Word;
@@ -2124,6 +2166,13 @@ begin
   FData := CT_TOpDataData_NUL;
 end;
 
+function TOpData.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var LAccount : TAccount;
+begin
+  LAccount := ASafeBoxTransaction.Account(FData.account_signer);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+end;
+
 function TOpData.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
 begin
   Stream.Write(FData.account_signer,Sizeof(FData.account_signer));

+ 8 - 0
src/core/UTxMultiOperation.pas

@@ -156,6 +156,8 @@ Type
     Function toString : String; Override;
     Property Data : TOpMultiOperationData read FData;
     Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+
+    function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
 implementation
@@ -821,6 +823,12 @@ begin
   Result := (IndexOfAccountSender(account)>=0) Or (IndexOfAccountChanger(account)>=0);
 end;
 
+function TOpMultiOperation.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction: TPCSafeBoxTransaction): Boolean;
+var errors : String;
+begin
+  Result := CheckSignatures(ASafeBoxTransaction,errors);
+end;
+
 function TOpMultiOperation.DestinationAccount: Int64;
 begin
   Result:=inherited DestinationAccount;