Browse Source

Build 2.1.6

PascalCoin 7 years ago
parent
commit
600d869962

BIN
PascalCoinWallet.res


+ 1 - 1
PascalCoinWalletLazarus.lpi

@@ -23,7 +23,7 @@
       <UseVersionInfo Value="True"/>
       <MajorVersionNr Value="2"/>
       <MinorVersionNr Value="1"/>
-      <RevisionNr Value="5"/>
+      <RevisionNr Value="6"/>
       <StringTable ProductVersion="0.0.0.0"/>
     </VersionInfo>
     <BuildModes Count="1">

BIN
PascalCoinWalletLazarus.res


+ 13 - 0
README.md

@@ -34,6 +34,19 @@ Also, consider a donation at PascalCoin development account: "0-10"
 
 ## History:  
 
+### Build 2.1.6 - 2018-02-14
+- Important improvements
+  - Improved speed when processing operations on start
+  - Improved speed when processing pending operations after a new block found
+  - Deleted duplicate "SanitizeOperations" call
+  - Verify signed operations only once (TPCOperation.FSignatureChecked Boolean)
+  - Improvements in search methods of TOperationsHashTree
+    - Increase speed in search methods thanks to internal ordered lists
+    - Increase speed copying thanks to using FHashTree sender buffer instad of generating new one
+  - Internal bugs
+- Those improvements solved BUG that caused operations not included to blockchain due slow processing with MemPool 
+- NOTE: It's HIGHLY RECOMMENDED to upgrade to this version
+
 ### Build 2.1.5 - 2018-02-09
 - GUI changes:
   - Allow massive accounts "change info" operation

+ 13 - 0
README.txt

@@ -34,6 +34,19 @@ Also, consider a donation at PascalCoin development account: "0-10"
 
 ## History:  
 
+### Build 2.1.6 - 2018-02-14
+- Important improvements
+  - Improved speed when processing operations on start
+  - Improved speed when processing pending operations after a new block found
+  - Deleted duplicate "SanitizeOperations" call
+  - Verify signed operations only once (TPCOperation.FSignatureChecked Boolean)
+  - Improvements in search methods of TOperationsHashTree
+    - Increase speed in search methods thanks to internal ordered lists
+    - Increase speed copying thanks to using FHashTree sender buffer instad of generating new one
+  - Internal bugs
+- Those improvements solved BUG that caused operations not included to blockchain due slow processing with MemPool 
+- NOTE: It's HIGHLY RECOMMENDED to upgrade to this version
+
 ### Build 2.1.5 - 2018-02-09
 - GUI changes:
   - Allow massive accounts "change info" operation

+ 2 - 2
Units/PascalCoin/UAccounts.pas

@@ -2038,7 +2038,7 @@ begin
       exit;
     end;
     destTotalBlocks := ToBlock - FromBlock + 1;
-    TLog.NewLog(ltInfo,ClassName,Format('CopySafeBoxStream from safebox with %d to %d (of %d sbh:%s) to safebox with %d and %d',
+    TLog.NewLog(ltDebug,ClassName,Format('CopySafeBoxStream from safebox with %d to %d (of %d sbh:%s) to safebox with %d and %d',
       [sbHeader.startBlock,sbHeader.endBlock,sbHeader.BlocksCount,TCrypto.ToHexaString(sbHeader.safeBoxHash),FromBlock,ToBlock]));
     // Read Source Offset zone
     posOffsetZoneSource := Source.Position;
@@ -2056,7 +2056,7 @@ begin
     setLength(offsetsDest,destTotalBlocks+1);
     // Blocks zone
     posBlocksZoneDest := Dest.Position;
-    TLog.NewLog(ltInfo,Classname,
+    TLog.NewLog(ltDebug,Classname,
       Format('Copying Safebox Stream from source Position %d (size:%d) to dest %d bytes - OffsetSource[%d] - OffsetSource[%d]',
        [posOffsetZoneSource + offsetsSource[FromBlock - sbHeader.startBlock], Source.Size,
         offsetsSource[ToBlock - sbHeader.startBlock + 1] - offsetsSource[FromBlock - sbHeader.startBlock],

+ 185 - 26
Units/PascalCoin/UBlockChain.pas

@@ -155,6 +155,8 @@ Type
   Private
     Ftag: integer;
   Protected
+    FSignatureChecked : Boolean; // Improvement TPCOperation speed 2.1.6
+    //
     FPrevious_Signer_updated_block: Cardinal;
     FPrevious_Destination_updated_block : Cardinal;
     FPrevious_Seller_updated_block : Cardinal;
@@ -198,18 +200,23 @@ Type
 
   TOperationsHashTree = Class
   private
-    FHashTreeOperations : TPCThreadList;
+    FListOrderedByAccountsData : TList;
+    FListOrderedBySha256 : TList; // Improvement TOperationsHashTree speed 2.1.6
+    FHashTreeOperations : TPCThreadList; // Improvement TOperationsHashTree speed 2.1.6
     FHashTree: TRawBytes;
     FOnChanged: TNotifyEvent;
     FTotalAmount : Int64;
     FTotalFee : Int64;
-    Procedure InternalAddOperationToHashTree(list : TList; op : TPCOperation);
+    Procedure InternalAddOperationToHashTree(list : TList; op : TPCOperation; CalcNewHashTree : Boolean);
+    Function FindOrderedBySha(lockedThreadList : TList; const Value: TRawBytes; var Index: Integer): Boolean;
+    Function FindOrderedByAccountData(lockedThreadList : TList; const account_number : Cardinal; var Index: Integer): Boolean;
+    function GetHashTree: TRawBytes;
   public
     Constructor Create;
     Destructor Destroy; Override;
     Procedure AddOperationToHashTree(op : TPCOperation);
     Procedure ClearHastThree;
-    Property HashTree : TRawBytes read FHashTree;
+    Property HashTree : TRawBytes read GetHashTree;
     Function OperationsCount : Integer;
     Function GetOperation(index : Integer) : TPCOperation;
     Function GetOperationsAffectingAccount(account_number : Cardinal; List : TList) : Integer;
@@ -1220,7 +1227,6 @@ begin
     FOperationBlock.fee := 0;
     //
     SafeBoxTransaction.CleanTransaction;
-    //
     aux := TOperationsHashTree.Create;
     Try
       lastn := FOperationsHashTree.OperationsCount;
@@ -1230,7 +1236,7 @@ begin
           inc(n);
           aux.AddOperationToHashTree(op);
           inc(FOperationBlock.fee,op.OperationFee);
-          TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString);
+          {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString){$ENDIF};
         end;
       end;
     Finally
@@ -1538,13 +1544,19 @@ Type TOperationHashTreeReg = Record
        Op : TPCOperation;
      end;
      POperationHashTreeReg = ^TOperationHashTreeReg;
+     TOperationsHashAccountsData = Record
+       account_number : Cardinal;
+       account_count : Integer;
+       account_without_fee : Integer;
+     end;
+     POperationsHashAccountsData = ^TOperationsHashAccountsData;
 
 procedure TOperationsHashTree.AddOperationToHashTree(op: TPCOperation);
 Var l : TList;
 begin
   l := FHashTreeOperations.LockList;
   try
-    InternalAddOperationToHashTree(l,op);
+    InternalAddOperationToHashTree(l,op,True);
   finally
     FHashTreeOperations.UnlockList;
   end;
@@ -1554,6 +1566,7 @@ procedure TOperationsHashTree.ClearHastThree;
 var l : TList;
   i : Integer;
   P : POperationHashTreeReg;
+  PaccData : POperationsHashAccountsData;
 begin
   l := FHashTreeOperations.LockList;
   try
@@ -1565,9 +1578,15 @@ begin
         P^.Op.Free;
         Dispose(P);
       end;
+      for i:=0 to FListOrderedByAccountsData.Count-1 do begin
+        PaccData := FListOrderedByAccountsData[i];
+        Dispose(PaccData);
+      end;
     Finally
       l.Clear;
-      FHashTree := TCrypto.DoSha256('');
+      FListOrderedBySha256.Clear;
+      FListOrderedByAccountsData.Clear;
+      FHashTree := '';
     End;
     If Assigned(FOnChanged) then FOnChanged(Self);
   finally
@@ -1584,18 +1603,21 @@ begin
   if (Sender = Self) then begin
     exit;
   end;
-  ClearHastThree;
   lme := FHashTreeOperations.LockList;
   try
     lastNE := FOnChanged;
     FOnChanged := Nil;
     try
+      ClearHastThree;
       lsender := Sender.FHashTreeOperations.LockList;
       try
         for i := 0 to lsender.Count - 1 do begin
           PSender := lsender[i];
-          InternalAddOperationToHashTree(lme,PSender^.Op);
+          InternalAddOperationToHashTree(lme,PSender^.Op,False);
         end;
+        // Improvement TOperationsHashTree speed 2.1.6
+        // FHashTree value updated now, not on every for cycle
+        FHashTree:=Sender.FHashTree;
       finally
         Sender.FHashTreeOperations.UnlockList;
       end;
@@ -1611,31 +1633,60 @@ end;
 constructor TOperationsHashTree.Create;
 begin
   FOnChanged:=Nil;
+  FListOrderedBySha256 := TList.Create;
+  FListOrderedByAccountsData := TList.Create;
   FTotalAmount := 0;
   FTotalFee := 0;
-  FHashTree := TCrypto.DoSha256('');
+  FHashTree := '';
   FHashTreeOperations := TPCThreadList.Create('TOperationsHashTree_HashTreeOperations');
 end;
 
 procedure TOperationsHashTree.Delete(index: Integer);
 Var l : TList;
   P : POperationHashTreeReg;
-  i : Integer;
+  i,iDel,iValuePosDeleted : Integer;
+  PaccData : POperationsHashAccountsData;
 begin
   l := FHashTreeOperations.LockList;
   try
     P := l[index];
+
+    // Delete from Ordered
+    If Not FindOrderedBySha(l,P^.Op.Sha256,iDel) then begin
+      TLog.NewLog(ltError,ClassName,'DEV ERROR 20180213-1 Operation not found in ordered list: '+P^.Op.ToString);
+    end else begin
+      iValuePosDeleted := PtrInt(FListOrderedBySha256[iDel]);
+      FListOrderedBySha256.Delete(iDel);
+      // Decrease values > iValuePosDeleted
+      for i := 0 to FListOrderedBySha256.Count - 1 do begin
+        if PtrInt(FListOrderedBySha256[i])>iValuePosDeleted then begin
+          FListOrderedBySha256[i] := TObject( PtrInt(FListOrderedBySha256[i]) - 1 );
+        end;
+      end;
+    end;
+    // Delete from account Data
+    If Not FindOrderedByAccountData(l,P^.Op.SignerAccount,i) then begin
+      TLog.NewLog(ltError,ClassName,Format('DEV ERROR 20180213-3 account %d not found in ordered list: %s',[P^.Op.SignerAccount,P^.Op.ToString]));
+    end else begin
+      PaccData := POperationsHashAccountsData( FListOrderedByAccountsData[i] );
+      Dec(PaccData.account_count);
+      If (P^.Op.OperationFee=0) then Dec(PaccData.account_without_fee);
+      If (PaccData.account_count<=0) then begin
+        Dispose(PaccData);
+        FListOrderedByAccountsData.Delete(i);
+      end;
+    end;
+
     l.Delete(index);
     P^.Op.Free;
     Dispose(P);
     // Recalc operations hash
     FTotalAmount := 0;
     FTotalFee := 0;
-    FHashTree := '';
+    FHashTree := ''; // Init to future recalc
     for i := 0 to l.Count - 1 do begin
       P := l[i];
       // Include to hash tree
-      FHashTree := TCrypto.DoSha256(FHashTree+P^.Op.Sha256);
       P^.Op.tag := i;
       inc(FTotalAmount,P^.Op.OperationAmount);
       inc(FTotalFee,P^.Op.OperationFee);
@@ -1652,9 +1703,32 @@ begin
   ClearHastThree;
   FreeAndNil(FHashTreeOperations);
   SetLength(FHashTree,0);
+  FreeAndNil(FListOrderedBySha256);
+  FreeAndNil(FListOrderedByAccountsData);
   inherited;
 end;
 
+function TOperationsHashTree.GetHashTree: TRawBytes;
+Var l : TList;
+  i : Integer;
+  P : POperationHashTreeReg;
+begin
+  if Length(FHashTree)<>32 then begin
+    l := FHashTreeOperations.LockList;
+    Try
+      TCrypto.DoSha256('',FHashTree);
+      for i := 0 to l.Count - 1 do begin
+        P := l[i];
+        // Include to hash tree
+        TCrypto.DoSha256(FHashTree+P^.Op.Sha256,FHashTree);
+      end;
+    Finally
+      FHashTreeOperations.UnlockList;
+    End;
+  end;
+  Result := FHashTree;
+end;
+
 function TOperationsHashTree.GetOperation(index: Integer): TPCOperation;
 Var l : TList;
 begin
@@ -1691,17 +1765,18 @@ begin
 end;
 
 function TOperationsHashTree.IndexOfOperation(op: TPCOperation): Integer;
-Var
+Var iPosInOrdered : Integer;
   l : TList;
   OpSha256 : TRawBytes;
 begin
   OpSha256 := op.Sha256;
   l := FHashTreeOperations.LockList;
   Try
-    for Result := 0 to l.Count - 1 do begin
-      if POperationHashTreeReg(l[Result])^.Op.Sha256=OpSha256 then exit;
-    end;
-    Result := -1;
+    // Improvement TOperationsHashTree speed 2.1.5.1
+    // Use ordered search
+    If FindOrderedBySha(l,OpSha256,iPosInOrdered) then begin
+      Result := PtrInt(FListOrderedBySha256.Items[iPosInOrdered]);
+    end else Result := -1;
   Finally
     FHashTreeOperations.UnlockList;
   End;
@@ -1714,18 +1789,23 @@ begin
   Result := 0;
   l := FHashTreeOperations.LockList;
   Try
-    for i := 0 to l.Count - 1 do begin
-      if (POperationHashTreeReg(l[i])^.Op.SignerAccount=account_number) And (POperationHashTreeReg(l[i])^.Op.OperationFee=0) then inc(Result);
-    end;
+    // Improvement TOperationsHashTree speed 2.1.5.1
+    // Use ordered accounts Data search
+    If FindOrderedByAccountData(l,account_number,i) then begin
+      Result := POperationsHashAccountsData(FListOrderedByAccountsData[i])^.account_without_fee;
+    end else Result := 0;
   Finally
     FHashTreeOperations.UnlockList;
   End;
 end;
 
-procedure TOperationsHashTree.InternalAddOperationToHashTree(list: TList; op: TPCOperation);
+procedure TOperationsHashTree.InternalAddOperationToHashTree(list: TList; op: TPCOperation; CalcNewHashTree : Boolean);
 Var msCopy : TMemoryStream;
   h : TRawBytes;
   P : POperationHashTreeReg;
+  PaccData : POperationsHashAccountsData;
+  i,npos : Integer;
+  auxs : AnsiString;
 begin
   msCopy := TMemoryStream.Create;
   try
@@ -1738,11 +1818,36 @@ begin
     P^.Op.FPrevious_Signer_updated_block := op.Previous_Signer_updated_block;
     P^.Op.FPrevious_Destination_updated_block := op.FPrevious_Destination_updated_block;
     P^.Op.FPrevious_Seller_updated_block := op.FPrevious_Seller_updated_block;
+    P^.Op.FHasValidSignature:=op.FHasValidSignature;
+    P^.Op.FSignatureChecked:=op.FSignatureChecked;
     h := op.Sha256;
+    P^.Op.FBufferedSha256:=op.FBufferedSha256;
     P^.Op.tag := list.Count;
-    // Include to hash tree
-    FHashTree := TCrypto.DoSha256(FHashTree+h);
-    list.Add(P);
+    // Improvement TOperationsHashTree speed 2.1.6
+    // Include to hash tree (Only if CalcNewHashTree=True)
+    If (CalcNewHashTree) And (Length(FHashTree)=32) then begin
+      TCrypto.DoSha256(FHashTree+h,FHashTree);
+    end;
+    npos := list.Add(P);
+    If FindOrderedBySha(list,op.Sha256,i) then begin
+      // Is inserting a value already found!
+      auxs :=Format('MyListCount:%d OrderedBySha Pos:%d from %d Hash:%s PointsTo:%d',[list.Count,i,FListOrderedBySha256.Count,TCrypto.ToHexaString(Op.Sha256),PtrInt(FListOrderedBySha256[i])]);
+      TLog.NewLog(ltError,ClassName,'DEV ERROR 20180213-2 Inserting a duplicate Sha256! '+Op.ToString+' > '+auxs );
+    end;
+    FListOrderedBySha256.Insert(i,TObject(npos));
+    // Improvement TOperationsHashTree speed 2.1.6
+    // Mantain an ordered Accounts list with data
+    If Not FindOrderedByAccountData(list,op.SignerAccount,i) then begin
+      New(PaccData);
+      PaccData^.account_number:=op.SignerAccount;
+      PaccData^.account_count:=0;
+      PaccData^.account_without_fee:=0;
+      FListOrderedByAccountsData.Insert(i,PaccData);
+    end else PaccData := FListOrderedByAccountsData[i];
+    Inc(PaccData^.account_count);
+    If op.OperationFee=0 then begin
+      Inc(PaccData^.account_without_fee);
+    end;
   finally
     msCopy.Free;
   end;
@@ -1751,6 +1856,57 @@ begin
   If Assigned(FOnChanged) then FOnChanged(Self);
 end;
 
+function TOperationsHashTree.FindOrderedBySha(lockedThreadList : TList; const Value: TRawBytes; var Index: Integer): Boolean;
+var L, H, I : Integer;
+  iLockedThreadListPos : PtrInt;
+  C : Int64;
+  P : POperationHashTreeReg;
+begin
+  Result := False;
+  L := 0;
+  H := FListOrderedBySha256.Count - 1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+    iLockedThreadListPos := PtrInt(FListOrderedBySha256[I]);
+    C := BinStrComp(POperationHashTreeReg(lockedThreadList[iLockedThreadListPos])^.Op.Sha256,Value);
+    if C < 0 then L := I + 1 else
+    begin
+      H := I - 1;
+      if C = 0 then
+      begin
+        Result := True;
+        L := I;
+      end;
+    end;
+  end;
+  Index := L;
+end;
+
+function TOperationsHashTree.FindOrderedByAccountData(lockedThreadList: TList; const account_number: Cardinal; var Index: Integer): Boolean;
+var L, H, I : Integer;
+  C : Int64;
+begin
+  Result := False;
+  L := 0;
+  H := FListOrderedByAccountsData.Count - 1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+    C := Int64(POperationsHashAccountsData(FListOrderedByAccountsData[I])^.account_number) - Int64(account_number);
+    if C < 0 then L := I + 1 else
+    begin
+      H := I - 1;
+      if C = 0 then
+      begin
+        Result := True;
+        L := I;
+      end;
+    end;
+  end;
+  Index := L;
+end;
+
 function TOperationsHashTree.LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage, LoadProtocolV2: Boolean; var errors: AnsiString): Boolean;
 Var c, i: Cardinal;
   OpType: Cardinal;
@@ -1949,6 +2105,8 @@ end;
 
 constructor TPCOperation.Create;
 begin
+  FSignatureChecked := False;
+  FHasValidSignature := False;
   FBufferedSha256:='';
   InitializeData;
 end;
@@ -2030,6 +2188,7 @@ begin
   FPrevious_Seller_updated_block := 0;
   FHasValidSignature := false;
   FBufferedSha256:='';
+  FSignatureChecked := False;
 end;
 
 function TPCOperation.LoadFromNettransfer(Stream: TStream): Boolean;
@@ -2288,7 +2447,7 @@ end;
 
 function TPCOperation.Sha256: TRawBytes;
 begin
-  If FBufferedSha256='' then begin
+  If Length(FBufferedSha256)=0 then begin
     FBufferedSha256 := TCrypto.DoSha256(GetBufferForOpHash(true));
   end;
   Result := FBufferedSha256;

+ 3 - 1
Units/PascalCoin/UConst.pas

@@ -77,6 +77,8 @@ Const
   CT_MinServersConnected = 2;
   CT_MaxServersConnected = 5;
 
+  CT_MaxResendMemPoolOperations = 50000;
+
   CT_MaxClientsConnected = {$IFDEF FPC}140{$ELSE}80{$ENDIF};
 
   CT_BankToDiskEveryNBlocks = {$IFDEF PRODUCTION}100{$ELSE}10{$ENDIF}; // Build 1.5 changed from 500 to 100;
@@ -136,7 +138,7 @@ Const
   CT_OpSubtype_ChangeKeySigned            = 71;
   CT_OpSubtype_ChangeAccountInfo          = 81;
 
-  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'2.1.5'{$ELSE}{$IFDEF TESTNET}'TESTNET 2.1.5'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'2.1.6'{$ELSE}{$IFDEF TESTNET}'TESTNET 2.1.6'{$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';
 

+ 18 - 1
Units/PascalCoin/UCrypto.pas

@@ -63,6 +63,8 @@ Type
     class Function ImportFromRaw(Const raw : TRawBytes) : TECPrivateKey; static;
   End;
 
+  { TCrypto }
+
   TCrypto = Class
   private
   public
@@ -70,6 +72,7 @@ Type
     Class function HexaToRaw(const HexaString : AnsiString) : TRawBytes;
     Class function DoSha256(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
     Class function DoSha256(const TheMessage : AnsiString) : TRawBytes; overload;
+    Class procedure DoSha256(const TheMessage : AnsiString; var ResultSha256 : TRawBytes);  overload;
     Class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; Var ResultSha256 : TRawBytes); overload;
     Class function DoRipeMD160_HEXASTRING(const TheMessage : AnsiString) : TRawBytes; overload;
     Class function DoRipeMD160AsRaw(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
@@ -322,7 +325,8 @@ end;
   Note: Delphi is slowly when working with Strings (allowing space)... so to
   increase speed we use a String as a pointer, and only increase speed if
   needed. Also the same with functions "GetMem" and "FreeMem" }
-class procedure TCrypto.DoDoubleSha256(p: PAnsiChar; plength: Cardinal; var ResultSha256: TRawBytes);
+class procedure TCrypto.DoDoubleSha256(p: PAnsiChar; plength: Cardinal;
+  Var ResultSha256: TRawBytes);
 Var PS : PAnsiChar;
   PC : PAnsiChar;
 begin
@@ -380,6 +384,19 @@ begin
   SHA256(PAnsiChar(TheMessage),Length(TheMessage),PS);
 end;
 
+{ New at Build 2.1.6
+  Note: Delphi is slowly when working with Strings (allowing space)... so to
+  increase speed we use a String as a pointer, and only increase speed if
+  needed. Also the same with functions "GetMem" and "FreeMem" }
+class procedure TCrypto.DoSha256(const TheMessage: AnsiString; var ResultSha256: TRawBytes);
+Var PS : PAnsiChar;
+  PC : PAnsiChar;
+begin
+  If length(ResultSha256)<>32 then SetLength(ResultSha256,32);
+  PS := @ResultSha256[1];
+  SHA256(PAnsiChar(TheMessage),Length(TheMessage),PS);
+end;
+
 class function TCrypto.ECDSASign(Key: PEC_KEY; const digest: AnsiString): TECDSA_SIG;
 Var PECS : PECDSA_SIG;
   p, pr,ps : PAnsiChar;

+ 4 - 1
Units/PascalCoin/UNetProtocol.pas

@@ -2357,7 +2357,9 @@ begin
             op := operations.GetOperation(i);
             FBufferReceivedOperationsHash.Add(op.Sha256);
             c := FBufferToSendOperations.IndexOfOperation(op);
-            if (c>=0) then FBufferToSendOperations.Delete(c);
+            if (c>=0) then begin
+              FBufferToSendOperations.Delete(c);
+            end;
           end;
         Finally
           FBufferLock.Release;
@@ -2633,6 +2635,7 @@ begin
       end;
       // Sending
       Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,responseStream);
+      TLog.NewLog(ltInfo,ClassName,Format('Sending Safebox(%d) chunk[%d..%d] to %s Bytes:%d',[_blockcount,_from,_to,ClientRemoteAddr,responseStream.Size]));
     finally
       responseStream.Free;
     end;

+ 37 - 40
Units/PascalCoin/UNode.pas

@@ -139,6 +139,7 @@ Type
     FNewBlockOperations : TPCOperationsComp;
   protected
     procedure BCExecute; override;
+  public
     Constructor Create(NetConnection : TNetConnection; MakeACopyOfNewBlockOperations: TPCOperationsComp; MakeACopyOfSanitizedOperationsHashTree : TOperationsHashTree);
     destructor Destroy; override;
   End;
@@ -147,6 +148,7 @@ Type
     FNetConnection : TNetConnection;
   protected
     procedure BCExecute; override;
+  public
     Constructor Create(NetConnection : TNetConnection; MakeACopyOfOperationsHashTree : TOperationsHashTree);
     destructor Destroy; override;
   End;
@@ -161,11 +163,9 @@ var _Node : TNode;
 
 function TNode.AddNewBlockChain(SenderConnection: TNetConnection; NewBlockOperations: TPCOperationsComp;
   var newBlockAccount: TBlockAccount; var errors: AnsiString): Boolean;
-Var i,j : Integer;
+Var i,j,maxResend : Integer;
   nc : TNetConnection;
-  ms : TMemoryStream;
   s : String;
-  errors2 : AnsiString;
   OpBlock : TOperationBlock;
   opsht : TOperationsHashTree;
   minBlockResend : Cardinal;
@@ -198,38 +198,26 @@ begin
       errors := 'Duplicated block';
       exit;
     end;
-    ms := TMemoryStream.Create;
-    try
-      FOperations.SaveBlockToStream(false,ms);
-      Result := Bank.AddNewBlockChainBlock(NewBlockOperations,TNetData.NetData.NetworkAdjustedTime.GetMaxAllowedTimestampForNewBlock,newBlockAccount,errors);
-      if Result then begin
-        if Assigned(SenderConnection) then begin
-          FNodeLog.NotifyNewLog(ltupdate,SenderConnection.ClassName,Format(';%d;%s;%s;;%d;%d;%d;%s',[OpBlock.block,SenderConnection.ClientRemoteAddr,OpBlock.block_payload,
-            OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
-        end else begin
-          FNodeLog.NotifyNewLog(ltupdate,ClassName,Format(';%d;%s;%s;;%d;%d;%d;%s',[OpBlock.block,'NIL',OpBlock.block_payload,
-            OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
-        end;
+    // Improvement TNode speed 2.1.6
+    // Does not need to save a FOperations backup because is Sanitized by "TNode.OnBankNewBlock"
+    Result := Bank.AddNewBlockChainBlock(NewBlockOperations,TNetData.NetData.NetworkAdjustedTime.GetMaxAllowedTimestampForNewBlock,newBlockAccount,errors);
+    if Result then begin
+      if Assigned(SenderConnection) then begin
+        FNodeLog.NotifyNewLog(ltupdate,SenderConnection.ClassName,Format(';%d;%s;%s;;%d;%d;%d;%s',[OpBlock.block,SenderConnection.ClientRemoteAddr,OpBlock.block_payload,
+          OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
       end else begin
-        if Assigned(SenderConnection) then begin
-          FNodeLog.NotifyNewLog(lterror,SenderConnection.ClassName,Format(';%d;%s;%s;%s;%d;%d;%d;%s',[OpBlock.block,SenderConnection.ClientRemoteAddr,OpBlock.block_payload,errors,
-            OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
-        end else begin
-          FNodeLog.NotifyNewLog(lterror,ClassName,Format(';%d;%s;%s;%s;%d;%d;%d;%s',[OpBlock.block,'NIL',OpBlock.block_payload,errors,
-            OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
-        end;
+        FNodeLog.NotifyNewLog(ltupdate,ClassName,Format(';%d;%s;%s;;%d;%d;%d;%s',[OpBlock.block,'NIL',OpBlock.block_payload,
+          OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
       end;
-      FOperations.Clear(true);
-      ms.Position:=0;
-      If Not FOperations.LoadBlockFromStream(ms,errors2) then begin
-        TLog.NewLog(lterror,Classname,'Error recovering operations to sanitize: '+errors2);
-        if Result then errors := errors2
-        else errors := errors +' - '+errors2;
+    end else begin
+      if Assigned(SenderConnection) then begin
+        FNodeLog.NotifyNewLog(lterror,SenderConnection.ClassName,Format(';%d;%s;%s;%s;%d;%d;%d;%s',[OpBlock.block,SenderConnection.ClientRemoteAddr,OpBlock.block_payload,errors,
+          OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
+      end else begin
+        FNodeLog.NotifyNewLog(lterror,ClassName,Format(';%d;%s;%s;%s;%d;%d;%d;%s',[OpBlock.block,'NIL',OpBlock.block_payload,errors,
+          OpBlock.timestamp,UnivDateTimeToUnix(DateTime2UnivDateTime(Now)),UnivDateTimeToUnix(DateTime2UnivDateTime(Now)) - OpBlock.timestamp,IntToHex(OpBlock.compact_target,8)]));
       end;
-    finally
-      ms.Free;
     end;
-    FOperations.SanitizeOperations;
     if Result then begin
       opsht := TOperationsHashTree.Create;
       Try
@@ -237,7 +225,9 @@ begin
         If (Bank.LastBlockFound.OperationBlock.block>j) then
           minBlockResend:=Bank.LastBlockFound.OperationBlock.block - j
         else minBlockResend:=1;
-        for i := 0 to FOperations.Count - 1 do begin
+        maxResend := CT_MaxResendMemPoolOperations;
+        i := 0;
+        While (opsht.OperationsCount<maxResend) And (i<FOperations.Count) do begin
           resendOp := FOperations.Operation[i];
           j := FSentOperations.GetTag(resendOp.Sha256);
           if (j=0) Or (j<=minBlockResend) then begin
@@ -247,14 +237,19 @@ begin
             FSentOperations.SetTag(resendOp.Sha256,FOperations.OperationBlock.block); // Set tag new value
             FSentOperations.Add(FOperations.Operation[i].Sha256,Bank.LastBlockFound.OperationBlock.block);
           end else begin
-            TLog.NewLog(ltInfo,ClassName,'Sanitized operation not included to resend (j:'+IntToStr(j)+'>'+inttostr(minBlockResend)+') ('+inttostr(i+1)+'/'+inttostr(FOperations.Count)+'): '+FOperations.Operation[i].ToString);
+            {$IFDEF HIGHLOG}TLog.NewLog(ltInfo,ClassName,'Sanitized operation not included to resend (j:'+IntToStr(j)+'>'+inttostr(minBlockResend)+') ('+inttostr(i+1)+'/'+inttostr(FOperations.Count)+'): '+FOperations.Operation[i].ToString);{$ENDIF}
           end;
+          inc(i);
         end;
-        if opsht.OperationsCount>0 then begin
-          TLog.NewLog(ltinfo,classname,'Resending '+IntToStr(opsht.OperationsCount)+' operations for new block');
-          for i := 0 to opsht.OperationsCount - 1 do begin
-            TLog.NewLog(ltInfo,ClassName,'Resending ('+inttostr(i+1)+'/'+inttostr(opsht.OperationsCount)+'): '+opsht.GetOperation(i).ToString);
-          end;
+        If FOperations.Count>0 then begin
+          TLog.NewLog(ltinfo,classname,Format('Resending %d operations for new block (Buffer Pending Operations:%d)',[opsht.OperationsCount,FOperations.Count]));
+          {$IFDEF HIGHLOG}
+          if opsht.OperationsCount>0 then begin
+            for i := 0 to opsht.OperationsCount - 1 do begin
+              TLog.NewLog(ltInfo,ClassName,'Resending ('+inttostr(i+1)+'/'+inttostr(opsht.OperationsCount)+'): '+opsht.GetOperation(i).ToString);
+            end;
+          end
+          {$ENDIF}
         end;
         // Clean sent operations buffer
         j := 0;
@@ -1199,8 +1194,9 @@ begin
   FSanitizedOperationsHashTree.CopyFromHashTree(MakeACopyOfSanitizedOperationsHashTree);
   FNewBlockOperations := TPCOperationsComp.Create(Nil);
   FNewBlockOperations.CopyFrom(MakeACopyOfNewBlockOperations);
-  Inherited Create(false);
+  Inherited Create(True);
   FreeOnTerminate := true;
+  Suspended:=False;
 end;
 
 destructor TThreadNodeNotifyNewBlock.Destroy;
@@ -1229,8 +1225,9 @@ constructor TThreadNodeNotifyOperations.Create(NetConnection: TNetConnection; Ma
 begin
   FNetConnection := NetConnection;
   FNetConnection.AddOperationsToBufferForSend(MakeACopyOfOperationsHashTree);
-  Inherited Create(false);
+  Inherited Create(True);
   FreeOnTerminate := true;
+  Suspended:=False;
 end;
 
 destructor TThreadNodeNotifyOperations.Destroy;

+ 67 - 23
Units/PascalCoin/UOpTransaction.pas

@@ -509,11 +509,19 @@ begin
     end;
   end;
 
-  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
-    errors := 'Invalid sign';
-    FHasValidSignature := false;
-    exit;
-  end else FHasValidSignature := true;
+  If Not FSignatureChecked then begin
+    If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
+      errors := 'Invalid sign';
+      FHasValidSignature := false;
+      exit;
+    end else FHasValidSignature := true;
+    FSignatureChecked:=True;
+  end else begin
+    If Not FHasValidSignature then begin
+      errors := 'Invalid sign';
+      exit;
+    end;
+  end;
   FPrevious_Signer_updated_block := account_signer.updated_block;
   FPrevious_Destination_updated_block := account_target.updated_block;
   If (public_key in FData.changes_type) then begin
@@ -598,7 +606,10 @@ begin
   If Not DoSignOperation(key,FData) then begin
     TLog.NewLog(lterror,Classname,'Error signing a new Change Info operation');
     FHasValidSignature := false;
-  end else FHasValidSignature := true;
+  end else begin
+    FHasValidSignature := true;
+  end;
+  FSignatureChecked:=True;
 end;
 
 function TOpChangeAccountInfo.toString: String;
@@ -646,7 +657,10 @@ begin
   If Not DoSignOperation(key,FData) then begin
     TLog.NewLog(lterror,Classname,'Error signing a new Transaction');
     FHasValidSignature := false;
-  end else FHasValidSignature := true;
+  end else begin
+    FHasValidSignature := true;
+  end;
+  FSignatureChecked:=True;
 end;
 
 function TOpTransaction.DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors : AnsiString): Boolean;
@@ -724,12 +738,20 @@ begin
   end;
 
   // Check signature
-  _h := GetTransactionHashToSign(FData);
-  if (Not TCrypto.ECDSAVerify(sender.accountInfo.accountkey,_h,FData.sign)) then begin
-    errors := 'Invalid sign';
-    FHasValidSignature := false;
-    Exit;
-  end else FHasValidSignature := true;
+  If Not FSignatureChecked then begin
+    _h := GetTransactionHashToSign(FData);
+    if (Not TCrypto.ECDSAVerify(sender.accountInfo.accountkey,_h,FData.sign)) then begin
+      errors := 'Invalid sign';
+      FHasValidSignature := false;
+      Exit;
+    end else FHasValidSignature := true;
+    FSignatureChecked:=True;
+  end else begin
+    If Not FHasValidSignature then begin
+      errors := 'Invalid sign';
+      exit;
+    end;
+  end;
   //
   FPrevious_Signer_updated_block := sender.updated_block;
   FPrevious_Destination_updated_block := target.updated_block;
@@ -1064,6 +1086,7 @@ begin
     TLog.NewLog(lterror,Classname,'Error signing a new Change key');
     FHasValidSignature := false;
   end else FHasValidSignature := true;
+  FSignatureChecked:=True;
 end;
 
 function TOpChangeKey.DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors: AnsiString): Boolean;
@@ -1147,11 +1170,20 @@ begin
     end;
   end;
 
-  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
-    errors := 'Invalid sign';
-    FHasValidSignature := false;
-    exit;
-  end else FHasValidSignature := true;
+  If Not FSignatureChecked then begin
+    If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
+      errors := 'Invalid sign';
+      FHasValidSignature := false;
+      exit;
+    end else FHasValidSignature := true;
+    FSignatureChecked:=True;
+  end else begin
+    If Not FHasValidSignature then begin
+      errors := 'Invalid sign';
+      exit;
+    end;
+  end;
+
   FPrevious_Signer_updated_block := account_signer.updated_block;
   FPrevious_Destination_updated_block := account_target.updated_block;
   account_target.accountInfo.accountKey := FData.new_accountkey;
@@ -1362,6 +1394,7 @@ begin
   FData.n_operation := n_operation;
   FData.fee := fee;
   FHasValidSignature := true; // Recover founds doesn't need a signature
+  FSignatureChecked := True;
 end;
 
 function TOpRecoverFounds.DoOperation(AccountTransaction : TPCSafeBoxTransaction; var errors: AnsiString): Boolean;
@@ -1591,11 +1624,19 @@ begin
     exit;
   end;
 
-  If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
-    errors := 'Invalid sign';
-    FHasValidSignature := false;
-    exit;
-  end else FHasValidSignature := true;
+  If Not FSignatureChecked then begin
+    If Not TCrypto.ECDSAVerify(account_signer.accountInfo.accountkey,GetOperationHashToSign(FData),FData.sign) then begin
+      errors := 'Invalid sign';
+      FHasValidSignature := false;
+      exit;
+    end else FHasValidSignature := true;
+    FSignatureChecked:=True;
+  end else begin
+    If Not FHasValidSignature then begin
+      errors := 'Invalid sign';
+      exit;
+    end;
+  end;
 
   FPrevious_Signer_updated_block := account_signer.updated_block;
   FPrevious_Destination_updated_block := account_target.updated_block;
@@ -1832,6 +1873,7 @@ begin
     TLog.NewLog(lterror,Classname,'Error signing a new list account for sale operation');
     FHasValidSignature := false;
   end else FHasValidSignature := true;
+  FSignatureChecked:=True;
 end;
 
 function TOpListAccountForSale.IsDelist: Boolean;
@@ -1859,6 +1901,7 @@ begin
     TLog.NewLog(lterror,Classname,'Error signing a delist account operation');
     FHasValidSignature := false;
   end else FHasValidSignature := true;
+  FSignatureChecked:=True;
 end;
 
 function TOpDelistAccountForSale.IsDelist: Boolean;
@@ -1894,6 +1937,7 @@ begin
     TLog.NewLog(lterror,Classname,'Error signing a new Buy operation');
     FHasValidSignature := false;
   end else FHasValidSignature := true;
+  FSignatureChecked:=True;
 end;
 
 procedure TOpBuyAccount.InitializeData;