Browse Source

Increase performance speed bytes buffer for PIP-0030

PascalCoin 6 years ago
parent
commit
728c744508
3 changed files with 246 additions and 5 deletions
  1. 24 4
      src/core/UBaseTypes.pas
  2. 126 1
      src/core/UPCSafeBoxRootHash.pas
  3. 96 0
      src/tests/UPCSafeBoxRootHashTests.pas

+ 24 - 4
src/core/UBaseTypes.pas

@@ -74,8 +74,10 @@ Type
     FUsedBytes : Integer;
     FUsedBytes : Integer;
     procedure IncreaseSize(newSize : Integer);
     procedure IncreaseSize(newSize : Integer);
     procedure SetDefaultIncrement(AValue: Integer);
     procedure SetDefaultIncrement(AValue: Integer);
+  protected
+    procedure NotifyUpdated(AStartPos, ACountBytes : Integer); virtual;
   public
   public
-    constructor Create(ADefaultIncrement : Integer);
+    constructor Create(ADefaultIncrement : Integer); virtual;
     constructor CreateCopy(ABytesBuffer : TBytesBuffer);
     constructor CreateCopy(ABytesBuffer : TBytesBuffer);
     destructor Destroy; override;
     destructor Destroy; override;
     function Length : Integer;
     function Length : Integer;
@@ -507,10 +509,17 @@ begin
 end;
 end;
 
 
 procedure TBytesBuffer.SetLength(ANewLength: Integer);
 procedure TBytesBuffer.SetLength(ANewLength: Integer);
+var LLastUsed : Integer;
 begin
 begin
   if ANewLength<0 then raise Exception.Create(Format('Invalid new Length value %d at %s',[ANewLength,ClassName]));
   if ANewLength<0 then raise Exception.Create(Format('Invalid new Length value %d at %s',[ANewLength,ClassName]));
   IncreaseSize(ANewLength);
   IncreaseSize(ANewLength);
+  LLastUsed := FUsedBytes;
   FUsedBytes := ANewLength;
   FUsedBytes := ANewLength;
+  if FUsedBytes>=LLastUsed then begin
+    NotifyUpdated(LLastUsed,FUsedBytes-LLastUsed);
+  end else begin
+    NotifyUpdated(0,FUsedBytes);
+  end;
 end;
 end;
 
 
 function TBytesBuffer.Add(const buffer; bufferSize: Integer): Integer;
 function TBytesBuffer.Add(const buffer; bufferSize: Integer): Integer;
@@ -527,6 +536,7 @@ procedure TBytesBuffer.Clear;
 begin
 begin
   System.SetLength(FBytes,0);
   System.SetLength(FBytes,0);
   FUsedBytes := 0;
   FUsedBytes := 0;
+  NotifyUpdated(0,FUsedBytes);
 end;
 end;
 
 
 function TBytesBuffer.Compare(ABytesBuffer: TBytesBuffer): Integer;
 function TBytesBuffer.Compare(ABytesBuffer: TBytesBuffer): Integer;
@@ -551,6 +561,7 @@ begin
   Move(ABytesBuffer.FBytes[0],FBytes[0],System.Length(FBytes));
   Move(ABytesBuffer.FBytes[0],FBytes[0],System.Length(FBytes));
   FUsedBytes := ABytesBuffer.FUsedBytes;
   FUsedBytes := ABytesBuffer.FUsedBytes;
   FDefaultIncrement := ABytesBuffer.FDefaultIncrement;
   FDefaultIncrement := ABytesBuffer.FDefaultIncrement;
+  NotifyUpdated(0,FUsedBytes);
 end;
 end;
 
 
 constructor TBytesBuffer.Create(ADefaultIncrement: Integer);
 constructor TBytesBuffer.Create(ADefaultIncrement: Integer);
@@ -558,6 +569,7 @@ begin
   System.SetLength(FBytes,0);
   System.SetLength(FBytes,0);
   FUsedBytes:=0;
   FUsedBytes:=0;
   SetDefaultIncrement(ADefaultIncrement);
   SetDefaultIncrement(ADefaultIncrement);
+  NotifyUpdated(0,FUsedBytes);
 end;
 end;
 
 
 constructor TBytesBuffer.CreateCopy(ABytesBuffer: TBytesBuffer);
 constructor TBytesBuffer.CreateCopy(ABytesBuffer: TBytesBuffer);
@@ -583,11 +595,19 @@ begin
   Result := addr(FBytes[0]);
   Result := addr(FBytes[0]);
 end;
 end;
 
 
+procedure TBytesBuffer.NotifyUpdated(AStartPos, ACountBytes: Integer);
+begin
+  //
+end;
+
 function TBytesBuffer.Replace(startPos: Integer; const buffer; bufferSize : Integer): Integer;
 function TBytesBuffer.Replace(startPos: Integer; const buffer; bufferSize : Integer): Integer;
 begin
 begin
-  IncreaseSize(startPos+1+bufferSize);
-  Move(buffer,FBytes[startPos],bufferSize);
-  if (startPos + bufferSize)>FUsedBytes then FUsedBytes := (startPos + bufferSize);
+  if bufferSize>0 then begin
+    IncreaseSize(startPos+1+bufferSize);
+    Move(buffer,FBytes[startPos],bufferSize);
+    if (startPos + bufferSize)>FUsedBytes then FUsedBytes := (startPos + bufferSize);
+    NotifyUpdated(startPos,bufferSize);
+  end;
   Result := FUsedBytes;
   Result := FUsedBytes;
 end;
 end;
 
 

+ 126 - 1
src/core/UPCSafeBoxRootHash.pas

@@ -67,7 +67,7 @@ unit UPCSafeBoxRootHash;
 
 
   Calling "TPCSafeboxRootHash.CheckProof" will validate a previous "GetProof"
   Calling "TPCSafeboxRootHash.CheckProof" will validate a previous "GetProof"
     - If the array is length = 1 then there was only 1 "Account Segment"
     - If the array is length = 1 then there was only 1 "Account Segment"
-    - The array must be: length=1 or length>2 (length=3 not allowed)
+    - The array must be: length=1 or length>2 (length=2 not allowed)
       - Length 1=single account segment, so, equal to SafeBoxRoot
       - Length 1=single account segment, so, equal to SafeBoxRoot
       - 2 accounts segments: Need 3 hashes: The base, sibling and SafeBoxRoot
       - 2 accounts segments: Need 3 hashes: The base, sibling and SafeBoxRoot
 
 
@@ -93,6 +93,33 @@ type
     Levels : Array of TRawBytes;
     Levels : Array of TRawBytes;
   End;
   End;
 
 
+  TSafeboxHashCalcType = (sbh_Single_Sha256, sbh_Merkle_Root_Hash);
+
+  { TBytesBuffer32Safebox is an extension of a TBytesBuffer that will
+    automatically update and calc the SafeboxRootHash when
+    SafeBoxHashCalcType = sbh_Merkle_Root_Hash
+
+    This will increace speed because will only calculate modified
+    blocks when used properly, mantaining integrity of the SafeBoxHash value
+
+    When SafeBoxHashCalcType = sbh_Single_Sha256 (Default) then there is
+    no change versus superclass type TBytesBuffer}
+
+  TBytesBuffer32Safebox = Class(TBytesBuffer)
+  private
+    FNextLevelBytesBuffer : TBytesBuffer32Safebox;
+    FSafeBoxHashCalcType: TSafeboxHashCalcType;
+    procedure SetSafeBoxHashCalcType(const Value: TSafeboxHashCalcType);
+  protected
+    procedure NotifyUpdated(AStartPos, ACountBytes : Integer); override;
+    procedure RedoNextLevelsForMerkleRootHash;
+  public
+    constructor Create(ADefaultIncrement : Integer); override;
+    destructor Destroy; override;
+    function GetSafeBoxHash : TRawBytes;
+    property SafeBoxHashCalcType : TSafeboxHashCalcType read FSafeBoxHashCalcType write SetSafeBoxHashCalcType;
+  End;
+
   TPCSafeboxRootHash = Class
   TPCSafeboxRootHash = Class
     class function CalcNextLevelHash(ABlocksHashBuffer : TBytesBuffer; AStartIndex, ABlocksCount : Integer; ANextLevel : TBytesBuffer) : Boolean;
     class function CalcNextLevelHash(ABlocksHashBuffer : TBytesBuffer; AStartIndex, ABlocksCount : Integer; ANextLevel : TBytesBuffer) : Boolean;
   public
   public
@@ -344,4 +371,102 @@ begin
   end;
   end;
 end;
 end;
 
 
+{ TBytesBuffer32Safebox }
+
+constructor TBytesBuffer32Safebox.Create(ADefaultIncrement: Integer);
+begin
+  FNextLevelBytesBuffer := Nil;
+  FSafeBoxHashCalcType := sbh_Single_Sha256;
+  inherited;
+end;
+
+destructor TBytesBuffer32Safebox.Destroy;
+begin
+  FreeAndNil(FNextLevelBytesBuffer);
+  inherited;
+end;
+
+function TBytesBuffer32Safebox.GetSafeBoxHash: TRawBytes;
+begin
+  if (FSafeBoxHashCalcType = sbh_Single_Sha256) then begin
+    if ((Self.Length MOD 32)=0) and (Self.Length>0) then begin
+      Result := TCrypto.DoSha256(Self.Memory,Self.Length);
+    end else begin
+      Result := Nil;
+    end;
+  end else if (Self.Length=32) then begin
+    System.SetLength(Result,32);
+    Move(Self.Memory^,Result[0],32);
+  end else if (Self.Length>32) and ((Self.Length MOD 32)=0) then begin
+    if Not Assigned(FNextLevelBytesBuffer) then begin
+      RedoNextLevelsForMerkleRootHash;
+    end;
+    Result := FNextLevelBytesBuffer.GetSafeBoxHash;
+  end else begin
+    Result := Nil;
+  end;
+end;
+
+procedure TBytesBuffer32Safebox.NotifyUpdated(AStartPos, ACountBytes: Integer);
+var LLevelItemIndex, LLevelItemsCount : Integer;
+  LPByte : PByte;
+  LSHA256 : TRawBytes;
+begin
+  inherited;
+  if (FSafeBoxHashCalcType = sbh_Single_Sha256) or
+    ((ACountBytes<>32) or ((AStartPos MOD 32)<>0)) or (Self.Length<64) or ((Self.Length MOD 32)<>0) then begin
+    FreeAndNil(FNextLevelBytesBuffer);
+  end else if Not Assigned(FNextLevelBytesBuffer) then begin
+    // First time must "Redo"
+    RedoNextLevelsForMerkleRootHash;
+  end else begin
+    LLevelItemIndex := AStartPos DIV 32;
+    LLevelItemsCount := Self.Length DIV 32;
+    LPByte := Self.Memory;
+    inc(LPByte,AStartPos);
+
+    // Left or right?
+    if (LLevelItemIndex MOD 2)=0 then begin
+      // Even, we are Left
+      if (LLevelItemIndex+1<LLevelItemsCount) then begin
+        LSHA256 := TCrypto.DoSha256(PAnsiChar(LPByte),64);
+        FNextLevelBytesBuffer.Replace((AStartPos DIV 2),LSHA256);
+      end
+      else begin
+        // No sheet on right, same value on next level
+        FNextLevelBytesBuffer.Replace(AStartPos DIV 2,LPByte^,32);
+      end;
+    end else begin
+      // Odd, is on right side
+      Dec(LPByte,32);
+      LSHA256 := TCrypto.DoSha256(PAnsiChar(LPByte),64);
+      FNextLevelBytesBuffer.Replace(((AStartPos-32) DIV 2),LSHA256);
+    end;
+  end;
+end;
+
+procedure TBytesBuffer32Safebox.RedoNextLevelsForMerkleRootHash;
+var i, j : Integer;
+begin
+  if (Self.Length<64) or ((Self.Length MOD 32)<>0) then begin
+    FreeAndNil(FNextLevelBytesBuffer);
+    Exit;
+  end;
+  if Not Assigned(FNextLevelBytesBuffer) then begin
+    FNextLevelBytesBuffer := TBytesBuffer32Safebox.Create(32*1000);
+    FNextLevelBytesBuffer.SafeBoxHashCalcType := Self.SafeBoxHashCalcType;
+  end;
+  j := Self.Length DIV 64;
+  for i := 0 to ((Self.Length DIV 64)-1) do begin
+    NotifyUpdated( (i*64), 32);
+  end;
+end;
+
+procedure TBytesBuffer32Safebox.SetSafeBoxHashCalcType(const Value: TSafeboxHashCalcType);
+begin
+  if FSafeBoxHashCalcType=Value then Exit;
+  FSafeBoxHashCalcType := Value;
+  FreeAndNil(FNextLevelBytesBuffer);
+end;
+
 end.
 end.

+ 96 - 0
src/tests/UPCSafeBoxRootHashTests.pas

@@ -21,6 +21,21 @@ type
     procedure TestSafeboxRootHash_1000_items;
     procedure TestSafeboxRootHash_1000_items;
   end;
   end;
 
 
+  TBytesBuffer32MerkleRootTest = class(TPascalCoinUnitTest)
+  private
+    procedure InitCrypto;
+    procedure TestMerkleRoot_N_items(ANItems : Integer);
+  published
+    procedure TestMerkleRoot_1_item;
+    procedure TestMerkleRoot_11_items;
+    procedure TestMerkleRoot_101_items;
+    procedure TestMerkleRoot_1000_items;
+    procedure TestMerkleRoot_10000_items;
+    procedure TestMerkleRoot_100000_items;
+//    procedure TestSpeed_100000_items;
+  end;
+
+
 implementation
 implementation
 
 
 uses variants, UCommon, UMemory, URandomHash, HlpHashFactory, HlpBitConverter, strutils,
 uses variants, UCommon, UMemory, URandomHash, HlpHashFactory, HlpBitConverter, strutils,
@@ -89,8 +104,89 @@ begin
   end;
   end;
 end;
 end;
 
 
+{ TBytesBuffer32MerkleRootTest }
+
+procedure TBytesBuffer32MerkleRootTest.InitCrypto;
+begin
+  TCrypto.InitCrypto;
+end;
+
+procedure TBytesBuffer32MerkleRootTest.TestMerkleRoot_100000_items;
+begin
+  TestMerkleRoot_N_items(100000);
+end;
+
+procedure TBytesBuffer32MerkleRootTest.TestMerkleRoot_10000_items;
+begin
+  TestMerkleRoot_N_items(10000);
+end;
+
+procedure TBytesBuffer32MerkleRootTest.TestMerkleRoot_1000_items;
+begin
+  TestMerkleRoot_N_items(1000);
+end;
+
+procedure TBytesBuffer32MerkleRootTest.TestMerkleRoot_101_items;
+begin
+  TestMerkleRoot_N_items(101);
+end;
+
+procedure TBytesBuffer32MerkleRootTest.TestMerkleRoot_11_items;
+begin
+  TestMerkleRoot_N_items(11);
+end;
+
+procedure TBytesBuffer32MerkleRootTest.TestMerkleRoot_1_item;
+begin
+  TestMerkleRoot_N_items(1);
+end;
+
+procedure TBytesBuffer32MerkleRootTest.TestMerkleRoot_N_items(ANItems: Integer);
+var LBuffer : TBytesBuffer32Safebox;
+  LRawData, LSafeBoxRoot_1, LSafeBoxRoot_2 : TRawBytes;
+  i, iItem : Integer;
+  LPByte : PByte;
+begin
+  InitCrypto;
+  LBuffer := TBytesBuffer32Safebox.Create(ANItems * 32);
+  try
+    LBuffer.SafeBoxHashCalcType := sbh_Merkle_Root_Hash;
+    SetLength(LRawData,32);
+    for i := 0 to Length(LRawData)-1 do begin
+      LRawData[i] := Random(256);
+    end;
+    for i := 1 to ANItems do begin
+      LBuffer.Add(LRawData);
+    end;
+
+
+    LSafeBoxRoot_1 := TPCSafeboxRootHash.CalcSafeBoxRootHash(LBuffer);
+    LSafeBoxRoot_2 := LBuffer.GetSafeBoxHash;
+
+    Assert(TBaseType.Equals(LSafeBoxRoot_1,LSafeBoxRoot_2),'Not equal safeboxroot values');
+
+    LSafeBoxRoot_1 := TPCSafeboxRootHash.CalcSafeBoxRootHash(LBuffer);
+
+    for iItem := 0 to ANItems-1 do begin
+      SetLength(LRawData,32);
+      LPByte := LBuffer.Memory;
+      Inc(LPByte,iItem*32);
+      Move(LPByte^,LRawData[0],32);
+
+      LBuffer.Replace(iItem*32,LRawData);
+
+      LSafeBoxRoot_2 := LBuffer.GetSafeBoxHash;
+
+      Assert(TBaseType.Equals(LSafeBoxRoot_1,LSafeBoxRoot_2),Format('Not equal safeboxroot values updating item %d/%d',[iItem+1,ANItems]));
+    end;
+  finally
+    LBuffer.Free;
+  end;
+end;
+
 initialization
 initialization
   Randomize;
   Randomize;
   RegisterTest(TPCSafeBoxRootHashTest.Suite);
   RegisterTest(TPCSafeBoxRootHashTest.Suite);
+  RegisterTest(TBytesBuffer32MerkleRootTest.Suite);
 
 
 end.
 end.