Browse Source

Allow use Hardcoded RandomHash table

This allows usage of a pre-calculated RandomHash digest/hash pairs for quick processing blocks validation.
Note that must call TPascalCoinProtocol.AllowUseHardcodedRandomHashTable procedure in order to be used
PascalCoin 6 years ago
parent
commit
8514a47cfe
2 changed files with 348 additions and 6 deletions
  1. 77 6
      src/core/UAccounts.pas
  2. 271 0
      src/core/UPCHardcodedRandomHashTable.pas

+ 77 - 6
src/core/UAccounts.pas

@@ -25,6 +25,7 @@ interface
 uses
 uses
   Classes, SysUtils, UConst, UCrypto, SyncObjs, UThread, UBaseTypes,
   Classes, SysUtils, UConst, UCrypto, SyncObjs, UThread, UBaseTypes,
   UPCOrderedLists, UPCDataTypes, UPCSafeBoxRootHash,
   UPCOrderedLists, UPCDataTypes, UPCSafeBoxRootHash,
+  UPCHardcodedRandomHashTable,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 {$I config.inc}
 {$I config.inc}
@@ -65,6 +66,10 @@ Type
 
 
   TPascalCoinProtocol = Class
   TPascalCoinProtocol = Class
   public
   public
+    FPCHardcodedRandomHashTable : TPCHardcodedRandomHashTable;
+  public
+    constructor Create;
+    destructor Destroy; override;
     Class Function MinimumTarget(protocol_version : Integer): Cardinal;
     Class Function MinimumTarget(protocol_version : Integer): Cardinal;
     Class Function ResetTarget(current_target : Cardinal; protocol_version : Integer): Cardinal;
     Class Function ResetTarget(current_target : Cardinal; protocol_version : Integer): Cardinal;
     Class Function GetRewardForNewLine(line_index: Cardinal): UInt64;
     Class Function GetRewardForNewLine(line_index: Cardinal): UInt64;
@@ -77,6 +82,7 @@ Type
     Class Function IsValidMinerBlockPayload(const newBlockPayload : TRawBytes) : Boolean;
     Class Function IsValidMinerBlockPayload(const newBlockPayload : TRawBytes) : Boolean;
     class procedure GetRewardDistributionForNewBlock(const OperationBlock : TOperationBlock; out acc_0_miner_reward, acc_4_dev_reward : Int64; out acc_4_for_dev : Boolean);
     class procedure GetRewardDistributionForNewBlock(const OperationBlock : TOperationBlock; out acc_0_miner_reward, acc_4_dev_reward : Int64; out acc_4_for_dev : Boolean);
     class Function CalcSafeBoxHash(ABlocksHashBuffer : TBytesBuffer; protocol_version : Integer) : TRawBytes;
     class Function CalcSafeBoxHash(ABlocksHashBuffer : TBytesBuffer; protocol_version : Integer) : TRawBytes;
+    class Function AllowUseHardcodedRandomHashTable(const AHardcodedFileName : String; const AHardcodedSha256Value : TRawBytes) : Boolean;
   end;
   end;
 
 
   TAccount = Record
   TAccount = Record
@@ -602,6 +608,8 @@ end;
 
 
 { TPascalCoinProtocol }
 { TPascalCoinProtocol }
 
 
+var _INTERNAL_PascalCoinProtocol : TPascalCoinProtocol = Nil;
+
 class function TPascalCoinProtocol.GetNewTarget(vteorical, vreal: Cardinal; protocol_version : Integer; isSlowMovement : Boolean; const actualTarget: TRawBytes): TRawBytes;
 class function TPascalCoinProtocol.GetNewTarget(vteorical, vreal: Cardinal; protocol_version : Integer; isSlowMovement : Boolean; const actualTarget: TRawBytes): TRawBytes;
 Var
 Var
   bnact, bnaux: TBigNum;
   bnact, bnaux: TBigNum;
@@ -741,9 +749,64 @@ begin
   end;
   end;
 end;
 end;
 
 
+class function TPascalCoinProtocol.AllowUseHardcodedRandomHashTable(
+  const AHardcodedFileName: String;
+  const AHardcodedSha256Value: TRawBytes): Boolean;
+var LTmp : TPCHardcodedRandomHashTable;
+  LFileStream : TFileStream;
+  LInternalHardcodedSha256 : TRawBytes;
+begin
+  Result := False;
+  If Not FileExists(AHardcodedFileName) then begin
+    TLog.NewLog(ltdebug,ClassName,Format('Hardcoded RandomHash from file not found:%s',
+      [AHardcodedFileName] ));
+    Exit;
+  end;
+  LTmp := TPCHardcodedRandomHashTable.Create;
+  try
+    LFileStream := TFileStream.Create(AHardcodedFileName,fmOpenRead+fmShareDenyNone);
+    try
+      if LTmp.LoadFromStream(LFileStream,LInternalHardcodedSha256) then begin
+        if TBaseType.Equals(LInternalHardcodedSha256, AHardcodedSha256Value) then begin
+          if Not Assigned(_INTERNAL_PascalCoinProtocol) then begin
+            _INTERNAL_PascalCoinProtocol := TPascalCoinProtocol.Create;
+          end;
+          _INTERNAL_PascalCoinProtocol.FPCHardcodedRandomHashTable.CopyFrom(LTmp);
+          TLog.NewLog(ltinfo,ClassName,Format('Added %d (%d) Hardcoded RandomHash from file:%s (%s)',
+            [LTmp.Count,_INTERNAL_PascalCoinProtocol.FPCHardcodedRandomHashTable.Count,
+            AHardcodedFileName,AHardcodedSha256Value.ToHexaString] ));
+          Result := True;
+        end;
+      end;
+      if not Result then begin
+         TLog.NewLog(lterror,ClassName,Format('Hardcoded RandomHash file invalid:%s (%s %s) %d',
+           [AHardcodedFileName,AHardcodedSha256Value.ToHexaString,
+            LInternalHardcodedSha256.ToHexaString,
+            LTmp.Count] ));
+      end;
+    finally
+      LFileStream.Free;
+    end;
+  finally
+    LTmp.Free;
+  end;
+end;
+
+constructor TPascalCoinProtocol.Create;
+begin
+  FPCHardcodedRandomHashTable := TPCHardcodedRandomHashTable.Create;
+end;
+
+destructor TPascalCoinProtocol.Destroy;
+begin
+  FreeAndNil(FPCHardcodedRandomHashTable);
+  inherited;
+end;
+
 class procedure TPascalCoinProtocol.CalcProofOfWork(const operationBlock: TOperationBlock; out PoW: TRawBytes);
 class procedure TPascalCoinProtocol.CalcProofOfWork(const operationBlock: TOperationBlock; out PoW: TRawBytes);
 var ms : TMemoryStream;
 var ms : TMemoryStream;
   accKeyRaw : TRawBytes;
   accKeyRaw : TRawBytes;
+  LDigest : TRawBytes;
 begin
 begin
   ms := TMemoryStream.Create;
   ms := TMemoryStream.Create;
   try
   try
@@ -764,9 +827,14 @@ begin
     ms.Write(operationBlock.fee,4);
     ms.Write(operationBlock.fee,4);
     ms.Write(operationBlock.timestamp,4);
     ms.Write(operationBlock.timestamp,4);
     ms.Write(operationBlock.nonce,4);
     ms.Write(operationBlock.nonce,4);
-    if CT_ACTIVATE_RANDOMHASH_V4 AND (operationBlock.protocol_version >= CT_PROTOCOL_4) then
-      TCrypto.DoRandomHash(ms.Memory,ms.Size,PoW)
-    else
+    if CT_ACTIVATE_RANDOMHASH_V4 AND (operationBlock.protocol_version >= CT_PROTOCOL_4) then begin
+      if Assigned(_INTERNAL_PascalCoinProtocol) then begin
+        SetLength(LDigest,ms.Size);
+        Move(ms.Memory^,LDigest[0],ms.Size);
+        if _INTERNAL_PascalCoinProtocol.FPCHardcodedRandomHashTable.FindRandomHashByDigest(LDigest,PoW) then Exit;
+      end;
+      TCrypto.DoRandomHash(ms.Memory,ms.Size,PoW);
+    end else
       TCrypto.DoDoubleSha256(ms.Memory,ms.Size,PoW);
       TCrypto.DoDoubleSha256(ms.Memory,ms.Size,PoW);
   finally
   finally
     ms.Free;
     ms.Free;
@@ -1417,7 +1485,7 @@ end;
 class function TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(const account: TAccount; const APayload : TRawBytes): Boolean;
 class function TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(const account: TAccount; const APayload : TRawBytes): Boolean;
 var errors : String;
 var errors : String;
 begin
 begin
-  Result := false;
+  Result := False;
   if Not IsAccountForSaleOrSwap(account.accountInfo) then
   if Not IsAccountForSaleOrSwap(account.accountInfo) then
     exit;
     exit;
 
 
@@ -1428,8 +1496,7 @@ begin
    if (account.accountInfo.state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) then
    if (account.accountInfo.state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) then
      if NOT IsValidHashLockKey(account, APayload) then
      if NOT IsValidHashLockKey(account, APayload) then
        exit;
        exit;
-
-  Result := true;
+  Result := True;
 end;
 end;
 
 
 class function TAccountComp.IsAccountLocked(const AccountInfo: TAccountInfo; blocks_count: Cardinal): Boolean;
 class function TAccountComp.IsAccountLocked(const AccountInfo: TAccountInfo; blocks_count: Cardinal): Boolean;
@@ -5514,4 +5581,8 @@ begin
   end;
   end;
 end;
 end;
 
 
+initialization
+  _INTERNAL_PascalCoinProtocol := Nil;
+finalization
+  FreeAndNil(_INTERNAL_PascalCoinProtocol);
 end.
 end.

+ 271 - 0
src/core/UPCHardcodedRandomHashTable.pas

@@ -0,0 +1,271 @@
+unit UPCHardcodedRandomHashTable;
+
+{ Copyright (c) 2019 by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{$I config.inc}
+
+interface
+
+uses
+  SysUtils, Classes,
+  UCrypto, UBaseTypes, UPCDataTypes, UPCSafeBoxRootHash, UConst,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
+
+
+type
+  TPCHardcodedRandomHashTable = Class
+    {
+     This object will store an ordered list of pairs <Digest><RandomHash>
+     ordered by <Digest> for quick searching.
+     Is usefull to store Hardcoded values of RandomHash (as described
+     on PIP-0009)
+     https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0009.md
+
+     For security reasons, this object data will be stored in a secured Stream
+     (For example a file) where last value of the stream is a SHA256 hash
+     of the previous content.
+
+     The "LoadFromStream" function will return TRUE only if the SHA256 hash
+     stored in the Stream matches the loaded data, preventing corruption
+
+     Also, an external App can hardcode this SHA256 value (obtained
+     in a call to GetHardcodedSha256) for securize this usage
+
+     }
+    Type
+    TRow = Record
+      RandomHashValue : T32Bytes;
+      DigestValue : TRawBytes;
+    End;
+  private
+    FList : TList<TRow>;
+    FHardcodedSha256: TRawBytes;
+    function Find(const ADigest : TRawBytes; var AIndex : Integer) : Boolean;
+    function GetRandomHash(AIndex : Integer) : TRawBytes;
+    function GetRow(AIndex : Integer) : TRow;
+    procedure Clear;
+    procedure SaveToStreamWithoutHardcodedSha256(AStream : TStream);
+  public
+    constructor Create;
+    destructor Destroy; override;
+    function LoadFromStream(AStream : TStream; var AHardcodedSha256 : TRawBytes) : Boolean;
+    procedure SaveToStream(AStream : TStream);
+    procedure AddRandomHash(const ARandomHash, ADigest : TRawBytes; ACheckIntegrity : Boolean);
+    function Count : Integer;
+    function FindRandomHashByDigest(const ADigest : TRawBytes; var ARandomHash : TRawBytes) : Boolean;
+    function GetHardcodedSha256 : TRawBytes;  // Will return a SHA256 of the object content for hardcode external apps
+    procedure CopyFrom(ASource : TPCHardcodedRandomHashTable);
+  End;
+
+implementation
+
+uses UAccounts;
+
+const
+  CT_MagicValue_Stream_Header = 'TPCHardcodedRandomHashTable';
+  CT_MagicValue_Stream_Version = 1;
+
+{ TPCHardcodedRandomHashTable }
+
+procedure TPCHardcodedRandomHashTable.AddRandomHash(const ARandomHash, ADigest: TRawBytes; ACheckIntegrity: Boolean);
+var i,j, LMemPos_0, LMemPos_1 : Integer;
+  LRaw : TRawBytes;
+  LRow : TRow;
+  LRowPtr : Pointer;
+  LRowPosition : Int64;
+begin
+  if ACheckIntegrity then begin
+    LRaw := TCrypto.DoRandomHash(ADigest);
+    if TBaseType.BinStrComp(LRaw,ARandomHash)<>0 then raise Exception.Create('RandomHash(Digest) & Provided Hash value does not match');
+  end;
+  if Length(ARandomHash)<>32 then raise Exception.Create('Invalid RandomHash length<>32');
+
+  if Find(ADigest,i) then begin
+    if TBaseType.BinStrComp(GetRandomHash(i),ARandomHash)<>0 then raise Exception.Create('Digest found with another RandomHash value');
+  end else begin
+    FHardcodedSha256 := Nil;
+
+    LRow.RandomHashValue := TBaseType.To32Bytes(ARandomHash);
+    LRow.DigestValue := ADigest;
+    FList.Insert(i,LRow);
+  end;
+end;
+
+procedure TPCHardcodedRandomHashTable.Clear;
+begin
+  FList.Clear;
+  FHardcodedSha256 := Nil;
+end;
+
+procedure TPCHardcodedRandomHashTable.CopyFrom(ASource: TPCHardcodedRandomHashTable);
+var i, iIndex : Integer;
+begin
+  if ASource=Self then Exit;
+  for i := 0 to ASource.Count-1 do begin
+    if Not Find(ASource.FList[i].DigestValue,iIndex) then begin
+      FList.Insert(iIndex,ASource.FList[i]);
+      FHardcodedSha256 := Nil;
+    end;
+  end;
+end;
+
+function TPCHardcodedRandomHashTable.Count: Integer;
+begin
+  Result := FList.Count;
+end;
+
+constructor TPCHardcodedRandomHashTable.Create;
+begin
+  FList := TList<TRow>.Create;
+  FHardcodedSha256 := Nil;
+end;
+
+destructor TPCHardcodedRandomHashTable.Destroy;
+begin
+  Clear;
+  FreeAndNil(FList);
+  inherited;
+end;
+
+function TPCHardcodedRandomHashTable.Find(const ADigest : TRawBytes; var AIndex : Integer) : Boolean;
+var L, H, I: Integer;
+  C : Int64;
+  LPtr : PByte;
+  LRowDigest : TRawBytes;
+begin
+  Result := False;
+  L := 0;
+  H := Count-1;
+  while L <= H do
+  begin
+    I := (L + H) shr 1;
+
+    LRowDigest := GetRow(I).DigestValue;
+
+    C := TBaseType.BinStrComp(LRowDigest,ADigest);
+    if C < 0 then L := I + 1 else
+    begin
+      H := I - 1;
+      if C = 0 then
+      begin
+        Result := True;
+        L := I;
+      end;
+    end;
+  end;
+  AIndex := L;
+end;
+
+function TPCHardcodedRandomHashTable.FindRandomHashByDigest(const ADigest: TRawBytes; var ARandomHash: TRawBytes): Boolean;
+var i : Integer;
+begin
+  if Find(ADigest,i) then begin
+    ARandomHash := TBaseType.T32BytesToRawBytes( FList.Items[i].RandomHashValue );
+    Result := True;
+  end else begin
+    Result := False;
+    SetLength(ARandomHash,0);
+  end;
+end;
+
+function TPCHardcodedRandomHashTable.GetHardcodedSha256: TRawBytes;
+var LMemStream : TMemoryStream;
+begin
+  if Length(FHardcodedSha256)<>32 then begin
+    LMemStream := TMemoryStream.Create;
+    try
+      SaveToStreamWithoutHardcodedSha256(LMemStream);
+      FHardcodedSha256 := TCrypto.DoSha256( PAnsiChar(LMemStream.Memory), LMemStream.Size );
+    finally
+      LMemStream.Free;
+    end;
+  end;
+  Result := FHardcodedSha256;
+end;
+
+function TPCHardcodedRandomHashTable.GetRandomHash(AIndex: Integer): TRawBytes;
+begin
+  Result := TBaseType.ToRawBytes( GetRow(AIndex).RandomHashValue );
+end;
+
+function TPCHardcodedRandomHashTable.GetRow(AIndex: Integer): TRow;
+begin
+  Result := FList.Items[AIndex];
+end;
+
+function TPCHardcodedRandomHashTable.LoadFromStream(AStream: TStream; var AHardcodedSha256 : TRawBytes): Boolean;
+var i : Integer;
+  LRaw, LRaw2 : TRawBytes;
+  LTotalRows : UInt32;
+  LVersion : Word;
+begin
+  Clear;
+  TStreamOp.ReadAnsiString(AStream,LRaw);
+  LRaw2.FromString(CT_MagicValue_Stream_Header);
+  Result := False;
+  if (Not TBaseType.Equals(LRaw,LRaw2)) then begin
+    Exit(False);
+  end;
+  AStream.Read(LVersion,2);
+  if Not LVersion=CT_MagicValue_Stream_Version then Exit(False);
+
+  AStream.Read(LTotalRows,4);
+  for i := 1 to LTotalRows do begin
+    if TStreamOp.ReadAnsiString(AStream,LRaw,32)<0 then Exit(False); // RandomHash value
+    if TStreamOp.ReadAnsiString(AStream,LRaw2)<0 then Exit(False);// Digest value
+    AddRandomHash(LRaw,LRaw2,False);
+  end;
+
+  // Last value must be a
+  if TStreamOp.ReadAnsiString(AStream,AHardcodedSha256)<0 then Exit(False);
+  if TBaseType.BinStrComp(AHardcodedSha256,GetHardcodedSha256)<>0 then Exit(False);
+
+  Result := True;
+end;
+
+procedure TPCHardcodedRandomHashTable.SaveToStream(AStream: TStream);
+begin
+  SaveToStreamWithoutHardcodedSha256(AStream);
+
+  // Last value to the Stream must be
+  TStreamOp.WriteAnsiString(AStream,GetHardcodedSha256);
+end;
+
+procedure TPCHardcodedRandomHashTable.SaveToStreamWithoutHardcodedSha256(AStream: TStream);
+var LVersion : Word;
+  LRaw : TRawBytes;
+  LTotalRows : UInt32;
+  i : Integer;
+begin
+  LRaw.FromString(CT_MagicValue_Stream_Header);
+  TStreamOp.WriteAnsiString(AStream,LRaw);
+  LVersion := CT_MagicValue_Stream_Version;
+  AStream.Write(LVersion,2);
+  //
+  LTotalRows := Count;
+  AStream.Write(LTotalRows,4);
+  for i := 0 to Count-1 do begin
+    TStreamOp.WriteAnsiString(AStream,FList[i].RandomHashValue);
+    TStreamOp.WriteAnsiString(AStream,FList[i].DigestValue);
+  end;
+end;
+
+end.