Browse Source

New TPCOperationsStorage object

TPCOperationsStorage will be used as a global Operations storage useful when operations are stored on TOperationsHashTree because will use only one instance of operation used on multiple OperationsHashTree lists. For example when propagating operations to connected nodes, will only use one instance
PascalCoin 6 years ago
parent
commit
a5d19f13db
2 changed files with 258 additions and 29 deletions
  1. 257 28
      src/core/UBlockChain.pas
  2. 1 1
      src/core/UNode.pas

+ 257 - 28
src/core/UBlockChain.pas

@@ -198,7 +198,6 @@ Type
 
 
   TPCOperation = Class
   TPCOperation = Class
   Private
   Private
-    Ftag: integer;
   Protected
   Protected
     FPrevious_Signer_updated_block: Cardinal;
     FPrevious_Signer_updated_block: Cardinal;
     FPrevious_Destination_updated_block : Cardinal;
     FPrevious_Destination_updated_block : Cardinal;
@@ -236,7 +235,6 @@ Type
     function SellerAccount : Int64; virtual;
     function SellerAccount : Int64; virtual;
     function N_Operation : Cardinal; virtual; abstract;
     function N_Operation : Cardinal; virtual; abstract;
     function GetAccountN_Operation(account : Cardinal) : Cardinal; virtual;
     function GetAccountN_Operation(account : Cardinal) : Cardinal; virtual;
-    Property tag : integer read Ftag Write Ftag;
     function SaveToNettransfer(Stream: TStream): Boolean;
     function SaveToNettransfer(Stream: TStream): Boolean;
     function LoadFromNettransfer(Stream: TStream): Boolean;
     function LoadFromNettransfer(Stream: TStream): Boolean;
     function SaveToStorage(Stream: TStream): Boolean;
     function SaveToStorage(Stream: TStream): Boolean;
@@ -259,6 +257,42 @@ Type
     class function GetOperationFromStreamData(StreamData : TBytes) : TPCOperation;
     class function GetOperationFromStreamData(StreamData : TBytes) : TPCOperation;
   End;
   End;
 
 
+  TPCOperationStorage = Record
+    ptrPCOperation : TPCOperation;
+    locksCount : Integer;
+  end;
+  PPCOperationTStorage = ^TPCOperationStorage;
+
+  { TPCOperationsStorage }
+
+  // TPCOperationsStorage will be used as a global Operations storage useful when
+  // operations are stored on TOperationsHashTree because will use only one instance
+  // of operation used on multiple OperationsHashTree lists. For example when
+  // propagating operations to connected nodes, will only use one instance
+  TPCOperationsStorage = Class
+  private
+    FIntTotalAdded : Integer;
+    FIntTotalDeleted : Integer;
+    FMaxLocksCount : Integer;
+    FMaxLocksValue : Integer;
+    FPCOperationsStorageList : TPCThreadList; // Lock thread to POperationTStorage list
+    Function FindOrderedByPtrPCOperation(lockedThreadList : TList; const Value: TPCOperation; out Index: Integer): Boolean;
+  protected
+  public
+    Constructor Create;
+    Destructor Destroy; override;
+    //
+    function LockPCOperationsStorage : TList;
+    procedure UnlockPCOperationsStorage;
+    Function Count : Integer;
+    procedure AddPCOperation(APCOperation : TPCOperation);
+    procedure RemovePCOperation(APCOperation : TPCOperation);
+    function FindPCOperation(APCOperation : TPCOperation) : Boolean;
+    function FindPCOperationAndIncCounterIfFound(APCOperation : TPCOperation) : Boolean;
+    class function PCOperationsStorage : TPCOperationsStorage;
+    procedure GetStats(strings : TStrings);
+  end;
+
   { TOperationsHashTree }
   { TOperationsHashTree }
 
 
   TOperationsHashTree = Class
   TOperationsHashTree = Class
@@ -511,6 +545,199 @@ uses
   Variants,
   Variants,
   UTime, UConst, UOpTransaction;
   UTime, UConst, UOpTransaction;
 
 
+{ TPCOperationsStorage }
+
+var
+   _PCOperationsStorage : TPCOperationsStorage;
+
+function TPCOperationsStorage.FindOrderedByPtrPCOperation(lockedThreadList: TList; const Value: TPCOperation; out Index: Integer): Boolean;
+var L, H, I: Integer;
+  C : PtrInt;
+begin
+  Result := False;
+  L := 0;
+  H := lockedThreadList.Count - 1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+    C := PtrInt(PPCOperationTStorage(lockedThreadList[I])^.ptrPCOperation) - PtrInt(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;
+
+constructor TPCOperationsStorage.Create;
+begin
+  FPCOperationsStorageList := TPCThreadList.Create(ClassName);
+  FIntTotalAdded := 0;
+  FIntTotalDeleted := 0;
+  FMaxLocksCount := 0;
+  FMaxLocksValue := 0;
+end;
+
+destructor TPCOperationsStorage.Destroy;
+Var list : TList;
+  P : PPCOperationTStorage;
+  i : Integer;
+  pc : TPCOperation;
+begin
+  list := LockPCOperationsStorage;
+  try
+    for i:=0 to list.Count-1 do begin
+      P := list[i];
+      pc := P^.ptrPCOperation;
+      P^.ptrPCOperation := Nil;
+      P^.locksCount:=-1;
+      pc.Free;
+      Dispose(P);
+    end;
+    inc(FIntTotalDeleted,list.Count);
+  finally
+    list.Clear;
+    UnlockPCOperationsStorage;
+  end;
+  FreeAndNil(FPCOperationsStorageList);
+  inherited Destroy;
+end;
+
+function TPCOperationsStorage.LockPCOperationsStorage: TList;
+begin
+  Result := FPCOperationsStorageList.LockList;
+end;
+
+procedure TPCOperationsStorage.UnlockPCOperationsStorage;
+begin
+  FPCOperationsStorageList.UnlockList;
+end;
+
+function TPCOperationsStorage.Count: Integer;
+var list : TList;
+begin
+  list := LockPCOperationsStorage;
+  try
+    Result := list.Count;
+  finally
+    UnlockPCOperationsStorage;
+  end;
+end;
+
+procedure TPCOperationsStorage.AddPCOperation(APCOperation: TPCOperation);
+var P : PPCOperationTStorage;
+  list : TList;
+  iPos : Integer;
+begin
+  list := LockPCOperationsStorage;
+  try
+    if FindOrderedByPtrPCOperation(list,APCOperation,iPos) then begin
+      P := list[iPos];
+    end else begin
+      New(P);
+      P^.locksCount:=0;
+      P^.ptrPCOperation := APCOperation;
+      list.Insert(iPos,P);
+    end;
+    inc(P^.locksCount);
+    inc(FIntTotalAdded);
+    if (P^.locksCount>FMaxLocksValue) then begin
+      FMaxLocksValue:=P^.locksCount;
+      FMaxLocksCount:=0;
+    end;
+    inc(FMaxLocksCount);
+  finally
+    UnlockPCOperationsStorage;
+  end;
+end;
+
+procedure TPCOperationsStorage.RemovePCOperation(APCOperation: TPCOperation);
+var P : PPCOperationTStorage;
+  list : TList;
+  iPos : Integer;
+begin
+  list := LockPCOperationsStorage;
+  try
+    if FindOrderedByPtrPCOperation(list,APCOperation,iPos) then begin
+      P := list[iPos];
+      Dec(P^.locksCount);
+      if (P^.locksCount<=0) then begin
+        // Remove
+        list.Delete(iPos);
+        P^.ptrPCOperation := Nil;
+        Dispose(P);
+        APCOperation.Free;
+      end;
+      inc(FIntTotalDeleted);
+    end else begin
+      TLog.NewLog(lterror,ClassName,'ERROR DEV 20181218-2 Operation not found in storage to remove: '+APCOperation.ToString);
+    end;
+  finally
+    UnlockPCOperationsStorage;
+  end;
+end;
+
+function TPCOperationsStorage.FindPCOperation(APCOperation: TPCOperation): Boolean;
+var list : TList;
+  iPos : Integer;
+begin
+  list := LockPCOperationsStorage;
+  Try
+    Result := FindOrderedByPtrPCOperation(list,APCOperation,iPos);
+  finally
+    UnlockPCOperationsStorage;
+  end;
+end;
+
+function TPCOperationsStorage.FindPCOperationAndIncCounterIfFound(APCOperation: TPCOperation): Boolean;
+var list : TList;
+  iPos : Integer;
+begin
+  list := LockPCOperationsStorage;
+  Try
+    Result := FindOrderedByPtrPCOperation(list,APCOperation,iPos);
+    if Result then begin
+      Inc(PPCOperationTStorage(list[iPos])^.locksCount);
+      inc(FIntTotalAdded);
+      if (PPCOperationTStorage(list[iPos])^.locksCount>FMaxLocksValue) then begin
+        FMaxLocksValue:=PPCOperationTStorage(list[iPos])^.locksCount;
+        FMaxLocksCount:=0;
+      end;
+      inc(FMaxLocksCount);
+    end;
+  finally
+    UnlockPCOperationsStorage;
+  end;
+end;
+
+class function TPCOperationsStorage.PCOperationsStorage: TPCOperationsStorage;
+begin
+  Result := _PCOperationsStorage;
+end;
+
+procedure TPCOperationsStorage.GetStats(strings: TStrings);
+var list : TList;
+  i : Integer;
+  P : PPCOperationTStorage;
+begin
+  list := LockPCOperationsStorage;
+  try
+    strings.Add(Format('%s Operations:%d Added:%d Deleted:%d',[ClassName,list.Count,FIntTotalAdded,FIntTotalDeleted]));
+    strings.Add(Format('MaxLocks:%d MaxLocksCount:%d',[FMaxLocksValue,FMaxLocksCount]));
+    for i:=0 to list.Count-1 do begin
+      P := PPCOperationTStorage(list[i]);
+      strings.Add(Format('%d %s',[P^.locksCount,P^.ptrPCOperation.ToString]));
+    end;
+  finally
+    UnlockPCOperationsStorage;
+  end;
+end;
+
 { TPCBank }
 { TPCBank }
 
 
 function TPCBank.AccountsCount: Cardinal;
 function TPCBank.AccountsCount: Cardinal;
@@ -1819,7 +2046,7 @@ begin
     Try
     Try
       for i := 0 to l.Count - 1 do begin
       for i := 0 to l.Count - 1 do begin
         P := l[i];
         P := l[i];
-        P^.Op.Free;
+        _PCOperationsStorage.RemovePCOperation(P^.Op);
         Dispose(P);
         Dispose(P);
       end;
       end;
       for i:=0 to FListOrderedByAccountsData.Count-1 do begin
       for i:=0 to FListOrderedByAccountsData.Count-1 do begin
@@ -1947,7 +2174,7 @@ begin
     end;
     end;
 
 
     l.Delete(index);
     l.Delete(index);
-    P^.Op.Free;
+    _PCOperationsStorage.RemovePCOperation(P^.Op);
     Dispose(P);
     Dispose(P);
     // Recalc operations hash
     // Recalc operations hash
     FTotalAmount := 0;
     FTotalAmount := 0;
@@ -1956,7 +2183,6 @@ begin
     for i := 0 to l.Count - 1 do begin
     for i := 0 to l.Count - 1 do begin
       P := l[i];
       P := l[i];
       // Include to hash tree
       // Include to hash tree
-      P^.Op.tag := i;
       inc(FTotalAmount,P^.Op.OperationAmount);
       inc(FTotalAmount,P^.Op.OperationAmount);
       inc(FTotalFee,P^.Op.OperationFee);
       inc(FTotalFee,P^.Op.OperationFee);
     end;
     end;
@@ -2084,7 +2310,7 @@ end;
 
 
 function TOperationsHashTree.InternalAddOperationToHashTree(list: TList; op: TPCOperation; CalcNewHashTree : Boolean) : Boolean;
 function TOperationsHashTree.InternalAddOperationToHashTree(list: TList; op: TPCOperation; CalcNewHashTree : Boolean) : Boolean;
 Var msCopy : TMemoryStream;
 Var msCopy : TMemoryStream;
-  h : TRawBytes;
+  hForNewHash : TRawBytes;
   P : POperationHashTreeReg;
   P : POperationHashTreeReg;
   PaccData : POperationsHashAccountsData;
   PaccData : POperationsHashAccountsData;
   i,npos,iListSigners : Integer;
   i,npos,iListSigners : Integer;
@@ -2094,28 +2320,34 @@ begin
     Result := False;
     Result := False;
     Exit;
     Exit;
   end else Result := True; // Will add:
   end else Result := True; // Will add:
-  msCopy := TMemoryStream.Create;
-  try
     New(P);
     New(P);
-    P^.Op := TPCOperation( op.NewInstance );
-    P^.Op.InitializeData;
-    op.SaveOpToStream(msCopy,true);
-    msCopy.Position := 0;
-    P^.Op.LoadOpFromStream(msCopy, true);
-    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;
-    h := FHashTree + op.Sha256;
-    P^.Op.FBufferedSha256:=op.FBufferedSha256;
-    P^.Op.tag := list.Count;
-    P^.Op.FHasValidSignature := op.FHasValidSignature; // Improvement speed v4.0.2 reusing previously signed value
-    P^.Op.FUsedPubkeyForSignature := op.FUsedPubkeyForSignature;
-    P^.Op.CopyUsedPubkeySignatureFrom(op);
+    if Not _PCOperationsStorage.FindPCOperationAndIncCounterIfFound(op) then begin
+      msCopy := TMemoryStream.Create;
+      try
+        P^.Op := TPCOperation( op.NewInstance );
+        P^.Op.InitializeData;
+        op.SaveOpToStream(msCopy,true);
+        msCopy.Position := 0;
+        P^.Op.LoadOpFromStream(msCopy, true);
+        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; // Improvement speed v4.0.2 reusing previously signed value
+        P^.Op.FUsedPubkeyForSignature := op.FUsedPubkeyForSignature;
+        P^.Op.FBufferedSha256:=op.FBufferedSha256;
+        P^.Op.CopyUsedPubkeySignatureFrom(op);
+        _PCOperationsStorage.AddPCOperation(P^.Op);
+      finally
+        msCopy.Free;
+      end;
+    end else P^.Op := op; // Use same!
+
     // Improvement TOperationsHashTree speed 2.1.6
     // Improvement TOperationsHashTree speed 2.1.6
     // Include to hash tree (Only if CalcNewHashTree=True)
     // Include to hash tree (Only if CalcNewHashTree=True)
     If (CalcNewHashTree) And (Length(FHashTree)=32) then begin
     If (CalcNewHashTree) And (Length(FHashTree)=32) then begin
       // TCrypto.DoSha256(FHashTree+op.Sha256,FHashTree);  COMPILER BUG 2.1.6: Using FHashTree as a "out" param can be initialized prior to be updated first parameter!
       // TCrypto.DoSha256(FHashTree+op.Sha256,FHashTree);  COMPILER BUG 2.1.6: Using FHashTree as a "out" param can be initialized prior to be updated first parameter!
-      TCrypto.DoSha256(h,FHashTree);
+      hForNewHash := FHashTree + op.Sha256;
+      TCrypto.DoSha256(hForNewHash,FHashTree);
     end;
     end;
     npos := list.Add(P);
     npos := list.Add(P);
     //
     //
@@ -2149,9 +2381,6 @@ begin
     finally
     finally
       listSigners.Free;
       listSigners.Free;
     end;
     end;
-  finally
-    msCopy.Free;
-  end;
   inc(FTotalAmount,op.OperationAmount);
   inc(FTotalAmount,op.OperationAmount);
   inc(FTotalFee,op.OperationFee);
   inc(FTotalFee,op.OperationFee);
   If Assigned(FOnChanged) then FOnChanged(Self);
   If Assigned(FOnChanged) then FOnChanged(Self);
@@ -2715,7 +2944,6 @@ end;
 
 
 procedure TPCOperation.InitializeData;
 procedure TPCOperation.InitializeData;
 begin
 begin
-  FTag := 0;
   FPrevious_Signer_updated_block := 0;
   FPrevious_Signer_updated_block := 0;
   FPrevious_Destination_updated_block := 0;
   FPrevious_Destination_updated_block := 0;
   FPrevious_Seller_updated_block := 0;
   FPrevious_Seller_updated_block := 0;
@@ -3140,6 +3368,7 @@ end;
 initialization
 initialization
   SetLength(_OperationsClass, 0);
   SetLength(_OperationsClass, 0);
   RegisterOperationsClass;
   RegisterOperationsClass;
+  _PCOperationsStorage := TPCOperationsStorage.Create;
 finalization
 finalization
-
+  FreeAndNil(_PCOperationsStorage);
 end.
 end.

+ 1 - 1
src/core/UNode.pas

@@ -827,7 +827,7 @@ procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperatio
           for i := l.Count - 1 downto 0 do begin
           for i := l.Count - 1 downto 0 do begin
             op := opc.Operation[PtrInt(l.Items[i])];
             op := opc.Operation[PtrInt(l.Items[i])];
             If TPCOperation.OperationToOperationResume(block_number,Op,False,account_number,OPR) then begin
             If TPCOperation.OperationToOperationResume(block_number,Op,False,account_number,OPR) then begin
-              OPR.NOpInsideBlock := Op.tag; // Note: Used Op.tag to include operation index inside a list
+              OPR.NOpInsideBlock := PtrInt(l.Items[i]);
               OPR.time := opc.OperationBlock.timestamp;
               OPR.time := opc.OperationBlock.timestamp;
               OPR.Block := block_number;
               OPR.Block := block_number;
               If last_balance>=0 then begin
               If last_balance>=0 then begin