Pārlūkot izejas kodu

Merge pull request #71 from PascalCoinDev/master

PIP-0043
Albert Molina 2 gadi atpakaļ
vecāks
revīzija
8f53f3e9a6

+ 7 - 0
CHANGELOG.md

@@ -1,5 +1,12 @@
 # Changelog
 
+## Build 6 (Pending)
+- MANDATORY UPGRADE - HARD FORK ACTIVATION WILL OCCUR ON BLOCK **(pending)**
+- Upgrade to Protocol 6 (Hard fork)
+- Implementation of PIP-0043 (Update OP_RECOVER to recover only non used, not named PASA's) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0043.md
+- Improvements on downloading Safebox (fresh installation)
+
+
 ## Build 5.7 - 2021-12-23
 - This version will not propagate 0-Fee operations, but will allow 0-Fee operations to the blockchain (CT_AllowPropagate0feeOperations=False)
   - A wallet that wants to execute 0-Fee operations will need to connect to a miner/pool/node running a 5.6 version or solomine

+ 73 - 0
PIP/PIP-0043.md

@@ -0,0 +1,73 @@
+<pre>
+  PIP: PIP-0043
+  Title: Update OP_RECOVER to recover only non used, not named PASA's
+  Type: Protocol
+  Impact: Hard-Fork
+  Author: Albert Molina <[email protected]>  
+  Copyright: Albert Molina, 2023 (All Rights Reserved)
+  Comments-URI: https://discord.gg/Scr8mcwnrC (Discord channel #pip-43)
+  Status: Proposed
+  Created: 2023-03-10
+</pre>
+
+## Summary
+
+Update current OP_RECOVER operation in order to have a similar sense as described on [Original PascalCoin WhitePaper published on July 2016][1] few weeks before Genesis Block and also a community poll made on discord channel 
+
+## Motivation
+
+OP_RECOVER feature was defined on original PascalCoin Whitepaper published on July 2016 as a way to allow recover coins that has lost private key. 
+
+**PascalCoin WhitePaper:** 
+```
+PascalCoin proposes an alternative to the basic operation of Bitcoin, through which change
+several aspects for working on the new virtual currency:
+...
+  - PascalCoin provides a method set by protocol to retrieve coins that are not used
+    instead (lost key). This method only applies if after a certain time the owner does not
+    make any operation with the account private key.
+...
+(Page 2/8)
+```
+
+Basically was a way to mantain a constant and predectible usage of inflation and available coin because burning coins are not possible. 
+
+Current problem is that WhitePaper definition is ambiguous and **certain time** was not specified, initial source code proposal was to **set a time value = 4 years** (420480 blocks in PascalCoin) 
+
+Another problem is that WhitePaper was mixing coins (**PASC**) and accounts (**PASA**), because what is necessary in PascalCoin is an Account (aka PASA), so we can focus on recover PASA instead of recover coins inside PASA
+
+## Proposal
+
+This PIP specifies a more accurated OP_RECOVER that was discussed on Discord Channel
+
+See poll results: https://discordapp.com/channels/383064643482025984/391780165669093377/719437469329915945
+```
+Poll on Discord
+https://discordapp.com/channels/383064643482025984/391780165669093377/719437469329915945
+RESULTS ON 2020-07-21
+1 (22 votes) - Remove PASC/PASA Recovery rule
+2 (27 votes) - Recover only EMPTY non used, not named PASA's
+3 (3 votes) - Change Recovery to 10 year rule
+4 (2 votes) - Leave As Is.
+
+Winner option 2: Will apply on next Hard Fork (Protocol 6)
+```
+
+
+## Implementation
+
+https://github.com/PascalCoin/PascalCoin/commit/290ba9c288202250f891945f629a3d2aff907e08
+
+## Affected PIP's
+
+This PIP deactivates PIP-0012 and PIP-0042
+
+## Backwards Compatibility
+
+This change is not backwards compatible and requires a hard-fork activation. 
+
+## Links
+
+1. [Original PascalCoin WhitePaper published on July 2016. Accessed 2021-09.][1]
+
+[1]: https://github.com/PascalCoin/PascalCoin/blob/c22184dd7a407c6646ab651494822071726ed36e/PascalCoin%20White%20Paper%20-%20EN.pdf

+ 1 - 0
PIP/README.md

@@ -51,4 +51,5 @@ If they wish to continue, copy [this template](PIP-template.md) and ensure your
 | [40](PIP-0040.md)     | Pascal Governance | Gynther and the Interrim Dao-Team | Process | Cancelled |
 | [41](PIP-0041.md) | Pay To Key: in-protocol PASA distribution | Herman Schoenfeld | Protocol | Draft |
 | [42](PIP-0042.md)     | Update OP_RECOVER to initial sense described on original WhitePaper and allow ASK FOR PASA feature | Albert Molina | Protocol | Proposed |
+| [43](PIP-0043.md)     | Update OP_RECOVER to recover only non used, not named PASA's | Albert Molina | Protocol | Proposed |
 

+ 18 - 2
src/core/UAccounts.pas

@@ -281,7 +281,8 @@ Type
     Function LoadSafeBoxChunkFromStream(Stream : TStream; checkAll : Boolean; checkSafeboxHash : TRawBytes; progressNotify : TProgressNotify; previousCheckedSafebox : TPCSafebox; var ALastReadBlock : TBlockAccount; var errors : String) : Boolean;
     Function LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; var LastReadBlock : TBlockAccount; var errors : String) : Boolean; overload;
     Function LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; checkSafeboxHash : TRawBytes; progressNotify : TProgressNotify; previousCheckedSafebox : TPCSafebox; var ALastReadBlock : TBlockAccount; var errors : String) : Boolean; overload;
-    Class Function LoadSafeBoxStreamHeader(Stream : TStream; var sbHeader : TPCSafeBoxHeader) : Boolean;
+    Class Function LoadSafeBoxStreamHeader(Stream : TStream; var sbHeader : TPCSafeBoxHeader; out AStreamFinalPos : Int64) : Boolean; overload;
+    Class Function LoadSafeBoxStreamHeader(Stream : TStream; var sbHeader : TPCSafeBoxHeader) : Boolean; overload;
     Class Function SaveSafeBoxStreamHeader(Stream : TStream; protocol : Word; OffsetStartBlock, OffsetEndBlock, CurrentSafeBoxBlocksCount : Cardinal) : Boolean;
     Class Function MustSafeBoxBeSaved(BlocksCount : Cardinal) : Boolean;
     Class Function InitialSafeboxHash : TRawBytes;
@@ -3837,8 +3838,16 @@ end;
 
 
 function TPCSafeBox.LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; checkSafeboxHash : TRawBytes; progressNotify : TProgressNotify; previousCheckedSafebox : TPCSafebox; var ALastReadBlock : TBlockAccount; var errors : String) : Boolean;
+var Ltc : TTickCount;
 begin
+  Ltc := TPlatform.GetTickCount;
   Result := LoadSafeBoxChunkFromStream(Stream,checkAll,checkSafeboxHash,progressNotify,previousCheckedSafebox,ALastReadBlock,errors);
+  if Result then begin
+    while LoadSafeBoxChunkFromStream(Stream,checkAll,checkSafeboxHash,progressNotify,previousCheckedSafebox,ALastReadBlock,errors) do begin
+      TLog.NewLog(ltdebug,ClassName,Format('Loading safebox from stream... %.2f secs',[TPlatform.GetElapsedMilliseconds(Ltc)/1000]));
+    end;
+  end;
+  TLog.NewLog(ltdebug,ClassName,Format('Finalized Loading safebox from stream in %.2f secs',[TPlatform.GetElapsedMilliseconds(Ltc)/1000]));
 end;
 
 function TPCSafeBox.LoadSafeBoxFromStream(Stream: TStream; checkAll: Boolean; var LastReadBlock: TBlockAccount; var errors: String): Boolean;
@@ -3848,7 +3857,13 @@ begin
   Result := LoadSafeBoxFromStream(Stream,checkAll,Nil,pn,Nil,LastReadBlock,errors);
 end;
 
-class function TPCSafeBox.LoadSafeBoxStreamHeader(Stream: TStream; var sbHeader : TPCSafeBoxHeader) : Boolean;
+class function TPCSafeBox.LoadSafeBoxStreamHeader(Stream: TStream; var sbHeader: TPCSafeBoxHeader): Boolean;
+var LPos : Int64;
+begin
+  Result := LoadSafeBoxStreamHeader(Stream,sbHeader,LPos);
+end;
+
+class function TPCSafeBox.LoadSafeBoxStreamHeader(Stream: TStream; var sbHeader : TPCSafeBoxHeader; out AStreamFinalPos : Int64) : Boolean;
   // This function reads SafeBox stream info and sets position at offset start zone if valid, otherwise sets position to actual position
 Var w : Word;
   raw : TRawBytes;
@@ -3881,6 +3896,7 @@ begin
     If (Stream.Size<offsetPos + (endBlocks)) then exit;
     Stream.Position:=offsetPos + endBlocks;
     If TStreamOp.ReadAnsiString(Stream,sbHeader.safeBoxHash)<0 then exit;
+    AStreamFinalPos := Stream.position;
     // Back
     Stream.Position:=offsetPos;
     Result := True;

+ 2 - 1
src/core/UBaseTypes.pas

@@ -218,7 +218,8 @@ begin
   Move(ASerialized[0],Lsize,2);
   if (2 + Lsize > Length(ASerialized)) then Exit(False);
   SetLength(Self,Lsize);
-  Move(ASerialized[2],Self[0],Lsize);
+  if Lsize>0 then
+    Move(ASerialized[2],Self[0],Lsize);
   Result := True;
 end;
 

+ 1 - 8
src/core/UBlockChain.pas

@@ -1311,18 +1311,11 @@ end;
 
 function TPCBank.LoadBankFileInfo(const AFilename: String;
   var ASafeBoxHeader: TPCSafeBoxHeader): Boolean;
-var fs: TFileStream;
 begin
   Result := false;
   ASafeBoxHeader := CT_PCSafeBoxHeader_NUL;
   If Not FileExists(AFilename) then exit;
-  fs := TFileStream.Create(AFilename,fmOpenRead);
-  try
-    fs.Position:=0;
-    Result := SafeBox.LoadSafeBoxStreamHeader(fs,ASafeBoxHeader);
-  finally
-    fs.Free;
-  end;
+  Result := TPCSafeboxChunks.GetSafeboxHeaderFromFile(AFilename,ASafeBoxHeader);
 end;
 
 function TPCBank.LoadBankFromChunks(AChunks : TPCSafeboxChunks;

+ 122 - 25
src/core/UChunk.pas

@@ -36,7 +36,7 @@ uses
   {$ELSE}
   zlib,
   {$ENDIF}
-  UAccounts, ULog, UConst, UCrypto, UBaseTypes, UPCDataTypes;
+  UBaseTypes, UPCDataTypes;
 
 type
 
@@ -55,26 +55,40 @@ type
 
   TPCSafeboxChunks = Class
   private
-    FChunks : Array of TStream;
+    Type TChunkStreamInfo = Record
+      stream : TStream;
+      streamInitialPosition : Int64;
+      streamFinalPosition : Int64;
+      freeStreamOnClear : Boolean;
+    End;
+  private
+    FChunks : Array of TChunkStreamInfo;
+    FIsMultiChunkStream : Boolean;
   public
     constructor Create;
     destructor Destroy; override;
     procedure Clear;
     function Count : Integer;
-    procedure AddChunk(ASafeboxStreamChunk : TStream);
+    function AddChunk(ASafeboxStreamChunk : TStream; AFreeStreamOnClear : Boolean; ARaiseOnError : Boolean = false) : Boolean;
     function GetSafeboxChunk(index : Integer) : TStream;
     function GetSafeboxChunkHeader(index : Integer) : TPCSafeBoxHeader;
     function IsComplete : Boolean;
     function GetSafeboxHeader : TPCSafeBoxHeader;
+    function SaveSafeboxfile(AFileName : String) : Boolean;
+    function SaveSafeboxStream(AStream : TStream) : Boolean;
+    class function GetSafeboxHeaderFromStream(AStream : TStream; var APCSafeBoxHeader : TPCSafeBoxHeader) : Boolean;
+    class function GetSafeboxHeaderFromFile(AFilename : String; var APCSafeBoxHeader : TPCSafeBoxHeader) : Boolean;
   end;
 
 implementation
 
+uses UAccounts, ULog, UConst;
 { TPCSafeboxChunks }
 
 constructor TPCSafeboxChunks.Create;
 begin
   SetLength(FChunks,0);
+  FIsMultiChunkStream := False;
 end;
 
 destructor TPCSafeboxChunks.Destroy;
@@ -87,9 +101,11 @@ procedure TPCSafeboxChunks.Clear;
 var i : Integer;
 begin
   For i:=0 to Count-1 do begin
-    FChunks[i].Free;
+    if (FChunks[i].freeStreamOnClear) then FChunks[i].stream.Free;
+    if FIsMultiChunkStream then break; // When MultiChunk first stream is the same for all
   end;
   SetLength(FChunks,0);
+  FIsMultiChunkStream := False;
 end;
 
 function TPCSafeboxChunks.Count: Integer;
@@ -97,36 +113,60 @@ begin
   Result := Length(FChunks);
 end;
 
-procedure TPCSafeboxChunks.AddChunk(ASafeboxStreamChunk: TStream);
+function TPCSafeboxChunks.AddChunk(ASafeboxStreamChunk: TStream; AFreeStreamOnClear : Boolean; ARaiseOnError : Boolean) : Boolean;
 var LLastHeader, LsbHeader : TPCSafeBoxHeader;
+  LChunk : TChunkStreamInfo;
+  LCount : Integer;
 begin
-  If Not TPCSafeBox.LoadSafeBoxStreamHeader(ASafeboxStreamChunk,LsbHeader) then begin
-    Raise EPCChunk.Create('SafeBoxStream is not a valid SafeBox to add!');
+  if FIsMultiChunkStream then begin
+    if ARaiseOnError then raise EPCChunk.Create('Cannot add to a MultiChunk Stream')
+    else Exit(False);
   end;
-  if (Count>0) then begin
-    LLastHeader := GetSafeboxChunkHeader(Count-1);
-    if (LsbHeader.ContainsFirstBlock)
-      or (LsbHeader.startBlock<>LLastHeader.endBlock+1)
-      or (LLastHeader.ContainsLastBlock)
-      or (LsbHeader.protocol<>LLastHeader.protocol)
-      or (LsbHeader.blocksCount<>LLastHeader.blocksCount)
-      or (Not LsbHeader.safeBoxHash.IsEqualTo( LLastHeader.safeBoxHash ))
-      then begin
-      raise EPCChunk.Create(Format('Cannot add %s at (%d) %s',[LsbHeader.ToString,Length(FChunks),LLastHeader.ToString]));
+  LCount := 0;
+  repeat
+    LChunk.streamInitialPosition := ASafeboxStreamChunk.position;
+    LChunk.stream := ASafeboxStreamChunk;
+    LChunk.freeStreamOnClear := AFreeStreamOnClear;
+    If Not TPCSafeBox.LoadSafeBoxStreamHeader(ASafeboxStreamChunk,LsbHeader,LChunk.streamFinalPosition) then begin
+      if (ARaiseOnError) and (LCount=0) then Raise EPCChunk.Create('SafeBoxStream is not a valid SafeBox to add!')
+      else Exit(LCount>0);
+    end else if LCount>0 then FIsMultiChunkStream := True;
+
+    if (Count>0) then begin
+      LLastHeader := GetSafeboxChunkHeader(Count-1);
+      if (LsbHeader.ContainsFirstBlock)
+        or (LsbHeader.startBlock<>LLastHeader.endBlock+1)
+        or (LLastHeader.ContainsLastBlock)
+        or (LsbHeader.protocol<>LLastHeader.protocol)
+        or (LsbHeader.blocksCount<>LLastHeader.blocksCount)
+        or (Not LsbHeader.safeBoxHash.IsEqualTo( LLastHeader.safeBoxHash ))
+        then begin
+          if ARaiseOnError then raise EPCChunk.Create(Format('Cannot add %s at (%d) %s',[LsbHeader.ToString,Length(FChunks),LLastHeader.ToString]))
+          else Exit(False);
+      end;
+    end else if (Not LsbHeader.ContainsFirstBlock) then begin
+      if ARaiseOnError then raise EPCChunk.Create(Format('Cannot add %s',[LsbHeader.ToString]))
+      else Exit(False);
     end;
-  end else if (Not LsbHeader.ContainsFirstBlock) then begin
-    raise EPCChunk.Create(Format('Cannot add %s',[LsbHeader.ToString]));
-  end;
-  //
-  SetLength(FChunks,Length(FChunks)+1);
-  FChunks[High(FChunks)] := ASafeboxStreamChunk;
+    //
+    ASafeboxStreamChunk.Position := LChunk.streamFinalPosition;
+    //
+    SetLength(FChunks,Length(FChunks)+1);
+    FChunks[High(FChunks)] := LChunk;
+    inc(LCount);
+  until false;
+  Result := True;
 end;
 
 function TPCSafeboxChunks.GetSafeboxChunk(index: Integer): TStream;
 begin
   if (index<0) or (index>=Count) then raise EPCChunk.Create(Format('Invalid index %d of %d',[index,Length(FChunks)]));
-  Result := FChunks[index];
-  Result.Position := 0;
+  if FIsMultiChunkStream then begin
+    Result := FChunks[0].stream;
+  end else begin
+    Result := FChunks[index].stream;
+  end;
+  Result.Position := FChunks[index].streamInitialPosition;
 end;
 
 function TPCSafeboxChunks.GetSafeboxChunkHeader(index: Integer): TPCSafeBoxHeader;
@@ -146,6 +186,32 @@ begin
   end;
 end;
 
+function TPCSafeboxChunks.SaveSafeboxfile(AFileName: String): Boolean;
+var fs : TFileStream;
+begin
+  fs := TFileStream.Create(AFilename,fmCreate);
+  try
+    Result := SaveSafeboxStream(fs);
+  finally
+    fs.Free;
+  end;
+end;
+
+
+function TPCSafeboxChunks.SaveSafeboxStream(AStream: TStream): Boolean;
+Var
+  iChunk : Integer;
+  Lstream : TStream;
+begin
+  Result := false;
+  for iChunk := 0 to Count-1 do begin
+    Lstream := GetSafeboxChunk(iChunk);
+    AStream.CopyFrom(LStream,FChunks[iChunk].streamFinalPosition - FChunks[iChunk].streamInitialPosition);
+  end;
+  Result := True;
+end;
+
+
 function TPCSafeboxChunks.GetSafeboxHeader: TPCSafeBoxHeader;
 begin
   if Not IsComplete then Raise EPCChunk.Create(Format('Chunks are not complete %d',[Length(FChunks)]));
@@ -153,6 +219,37 @@ begin
   Result.startBlock := 0;
 end;
 
+class function TPCSafeboxChunks.GetSafeboxHeaderFromFile(AFilename: String;
+  var APCSafeBoxHeader: TPCSafeBoxHeader): Boolean;
+var fs: TFileStream;
+begin
+  APCSafeBoxHeader := CT_PCSafeBoxHeader_NUL;
+  if (AFileName.trim()='') or (Not FileExists(AFileName)) then Exit(False);
+  fs := TFileStream.Create(AFilename,fmOpenRead);
+  try
+    Result := TPCSafeboxChunks.GetSafeboxHeaderFromStream(fs,APCSafeBoxHeader);
+  finally
+    fs.Free;
+  end;
+end;
+
+class function TPCSafeboxChunks.GetSafeboxHeaderFromStream(AStream: TStream;
+  var APCSafeBoxHeader: TPCSafeBoxHeader): Boolean;
+var LChunks : TPCSafeboxChunks;
+begin
+  APCSafeBoxHeader := CT_PCSafeBoxHeader_NUL;
+  LChunks := TPCSafeboxChunks.Create;
+  try
+    if LChunks.AddChunk(AStream,False,False) then begin
+      if LChunks.IsComplete then APCSafeBoxHeader := LChunks.GetSafeboxHeader
+      else APCSafeBoxHeader := LChunks.GetSafeboxChunkHeader(0);
+      Result := True;
+    end else Result := False;
+  finally
+    LChunks.Free;
+  end;
+end;
+
 { TPCChunk }
 
 class function TPCChunk.SaveSafeBoxChunkFromSafeBox(SafeBoxStream, DestStream : TStream; fromBlock, toBlock: Cardinal; var errors : String) : Boolean;

+ 3 - 2
src/core/UCrypto.pas

@@ -632,8 +632,9 @@ Var PS : PAnsiChar;
 begin
 {$IFDEF Use_OpenSSL}
   If length(ResultSha256)<>32 then SetLength(ResultSha256,32);
-  PS := @ResultSha256[Low(ResultSha256)];
-  SHA256(@TheMessage[Low(TheMessage)],Length(TheMessage),PS);
+  PS := @ResultSha256[0];
+  if length(TheMessage)=0 then SHA256(Nil,0,PS)
+  else SHA256(@TheMessage[0],Length(TheMessage),PS);
 {$ELSE}
   TPCCryptoLib4Pascal.DoSHA256(TheMessage,ResultSha256);
 {$ENDIF}

+ 19 - 28
src/core/UNetProtocol.pas

@@ -303,6 +303,7 @@ Type
     procedure SetMinServersConnected(AValue: Integer);
     procedure SetNetConnectionsActive(const Value: Boolean);
     procedure SetMinFutureBlocksToDownloadNewSafebox(const Value: Integer);
+    procedure OnDownloadingSafeboxProgressNotify(sender : TObject; const mesage : String; curPos, totalCount : Int64);
   protected
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     Procedure DiscoverServersTerminated(Sender : TObject);
@@ -513,7 +514,7 @@ implementation
 
 uses
   UConst, ULog, UNode, UTime, UPCEncryption, UChunk,
-  UPCOperationsBlockValidator, UPCOperationsSignatureValidator, UOpTransaction,
+  UPCOperationsBlockValidator, UPCOperationsSignatureValidator, UOpTransaction, UPCDownloadSafebox,
   UPCTemporalFileStream;
 
 Const
@@ -1903,8 +1904,8 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
   var LDownloadedSafeboxBlocksCount, request_id : Cardinal;
     LreceivedChunk : TStream;
     safeBoxHeader : TPCSafeBoxHeader;
-    //errors : String;
     i : Integer;
+    LdownSafebox : TPCDownloadSafebox;
   Begin
     Result := False;
     ASafeboxChunks.Clear;
@@ -1920,33 +1921,15 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       Connection.DisconnectInvalidClient(false,'Invalid operation block at DownloadSafeBox '+TPCOperationsComp.OperationBlockToText(ASafeboxLastOperationBlock)+' errors: '+errors);
       Exit;
     end;
-      // Will obtain chunks of 10000 blocks each -> Note: Maximum is CT_MAX_SAFEBOXCHUNK_BLOCKS
-      for i:=0 to ((LDownloadedSafeboxBlocksCount-1) DIV CT_MAX_SAFEBOXCHUNK_BLOCKS) do begin // Bug v3.0.1 and minors
-        FNewBlockChainFromClientStatus := Format('Receiving new safebox with %d blocks (step %d/%d) from %s',
-          [LDownloadedSafeboxBlocksCount,i+1,((LDownloadedSafeboxBlocksCount-1) DIV CT_MAX_SAFEBOXCHUNK_BLOCKS)+1,Connection.ClientRemoteAddr]);
-        LreceivedChunk := TPCTemporalFileStream.Create(Format('CHUNK_%.3d_',[i]));
-        if (Not DownloadSafeBoxChunk(LDownloadedSafeboxBlocksCount,ASafeboxLastOperationBlock.initial_safe_box_hash,(i*CT_MAX_SAFEBOXCHUNK_BLOCKS),((i+1)*CT_MAX_SAFEBOXCHUNK_BLOCKS)-1,LreceivedChunk,safeBoxHeader,errors)) then begin
-          LreceivedChunk.Free;
-          TLog.NewLog(ltError,CT_LogSender,errors);
-          Exit;
-        end;
-        try
-          LreceivedChunk.Position := 0;
-          ASafeboxChunks.AddChunk( LreceivedChunk );
-        Except
-          On E:Exception do begin
-            errors:= Format('(%s) %s',[E.ClassName,E.Message]);
-            Result := false;
-            LreceivedChunk.Free;
-            Exit;
-          end;
-        end;
-      end;
 
-      if Not ASafeboxChunks.IsComplete then begin
-        errors := 'Safebox Chunks is not complete!';
-        Exit;
-      end else Result := True;
+    LdownSafebox := TPCDownloadSafebox.Create;
+    Try
+      LdownSafebox.OnProgressNotify := OnDownloadingSafeboxProgressNotify;
+      Result := LdownSafebox.DownloadSafebox(TThread.CurrentThread,ASafeboxLastOperationBlock,ASafeboxChunks);
+    finally
+      LdownSafebox.Free;
+    end;
+
   end;
 
 
@@ -2306,6 +2289,14 @@ begin
   FNetDataNotifyEventsThread.FNotifyOnStatisticsChanged := true;
 end;
 
+procedure TNetData.OnDownloadingSafeboxProgressNotify(sender: TObject;
+  const mesage: String; curPos, totalCount: Int64);
+Var pct : String;
+begin
+  if (totalCount>0) then pct := FormatFloat('0.00',curPos*100/totalCount)+'%' else pct := '';
+  FNewBlockChainFromClientStatus := Format('%s %s',[mesage,pct]);
+end;
+
 procedure TNetData.OnReadingNewSafeboxProgressNotify(sender: TObject; const mesage: String; curPos, totalCount: Int64);
 Var pct : String;
 begin

+ 300 - 0
src/core/UPCDownloadSafebox.pas

@@ -0,0 +1,300 @@
+unit UPCDownloadSafebox;
+
+{ Copyright (c) 2016-2023 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}
+
+interface
+
+{$I ./../config.inc}
+
+uses
+  Classes, SysUtils,
+  UNetProtocol, UThread,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  UBlockChain,
+  UNode, UPCTemporalFileStream, UChunk,
+  UAccounts, ULog, UConst, UCrypto, UBaseTypes, UPCDataTypes;
+
+type
+  TPCDownloadSafebox = class
+  private
+    FDownloadedBank: TPCBank;
+    FOnProgressNotify: TProgressNotify;
+    type
+    TDownloadSBThread = Class(TPCThread)
+    private
+      FOwner : TPCDownloadSafebox;
+      FBlockStart, FBlocksCount : Cardinal;
+      FStream: TPCTemporalFileStream;
+    protected
+      procedure BCExecute; override;
+    public
+      constructor Create(AOwner : TPCDownloadSafebox; ABlockStart, ACount : Cardinal);
+      destructor Destroy; override;
+      Property Stream : TPCTemporalFileStream read FStream write FStream;
+    End;
+    TPCDownloadSafeboxChunk = record
+      BlockStart : Cardinal;
+      Count : Cardinal;
+      Thread : TDownloadSBThread;
+    end;
+    var
+    FNode : TNode;
+    FSavedSafeboxHighOperationBlock : TOperationBlock;
+    FChunks : TPCThreadList<TPCDownloadSafeboxChunk>;
+  protected
+  public
+    constructor Create;
+    destructor Destroy; override;
+    function DownloadSafebox(AOwnerThread : TThread; ASavedSafeboxHighOperationBlock : TOperationBlock; AChunks : TPCSafeboxChunks) : Boolean;
+    property DownloadedBank : TPCBank read FDownloadedBank;
+    property OnProgressNotify : TProgressNotify read FOnProgressNotify write FOnProgressNotify;
+  end;
+
+implementation
+
+{ TPCDownloadSafebox.TDownloadSBThread }
+
+procedure TPCDownloadSafebox.TDownloadSBThread.BCExecute;
+  function DownloadSafebox(AConnection : TNetConnection; ASafeboxHash : TRawBytes; ASafeboxChunkStream : TStream) : Boolean;
+  Var sendData,receiveData : TStream;
+    headerdata : TNetHeaderData;
+    request_id : Cardinal;
+    c : Cardinal;
+    LRandomMilis : Integer;
+    LsafeBoxHeader : TPCSafeBoxHeader;
+    LErrors : String;
+    Ltc : TTickCount;
+  Begin
+    Result := False;
+    Ltc := TPlatform.GetTickCount;
+    sendData := TMemoryStream.Create;
+    receiveData := TMemoryStream.Create;
+    try
+      c := FOwner.FSavedSafeboxHighOperationBlock.block;
+      sendData.Write(c,4); // 4 bytes for blockcount
+      TStreamOp.WriteAnsiString(SendData,ASafeboxHash); // SafeboxHash
+      sendData.Write(FBlockStart,4);
+      c := FBlockStart + FBlocksCount - 1;
+      sendData.Write(c,4);
+      //
+      request_id := TNetData.NetData.NewRequestId;
+      if AConnection.DoSendAndWaitForResponse(CT_NetOp_GetSafeBox,request_id,sendData,receiveData,30000,headerdata) then begin
+        if HeaderData.is_error then exit;
+        ASafeboxChunkStream.Position := 0;
+        ASafeboxChunkStream.Size:=0;
+        If Not TPCChunk.LoadSafeBoxFromChunk(receiveData,ASafeboxChunkStream,LsafeBoxHeader,LErrors) then begin
+          AConnection.DisconnectInvalidClient(false,'Invalid received chunk: '+LErrors);
+          exit;
+        end;
+        If (Not (TBaseType.Equals(LsafeBoxHeader.safeBoxHash,ASafeboxHash))) or (LsafeBoxHeader.startBlock<>FBlockStart) or (LsafeBoxHeader.endBlock<>c) or
+          (LsafeBoxHeader.protocol<CT_PROTOCOL_2) or
+          (LsafeBoxHeader.protocol>CT_BlockChain_Protocol_Available) then begin
+          Lerrors := Format('Invalid received Safebox chunk Blockscount:%d %d - from:%d %d to %d %d - SafeboxHash:%s %s - Protocol %d',
+              [LsafeBoxHeader.blocksCount,FBlocksCount,LsafeBoxHeader.startBlock,FBlockStart,LsafeBoxHeader.endBlock,c,
+               LsafeBoxHeader.safeBoxHash.ToHexaString,ASafeboxHash.ToHexaString,LsafeBoxHeader.protocol]);
+          AConnection.DisconnectInvalidClient(false,'Invalid received chunk: '+Lerrors);
+          exit;
+        end;
+        Result := True;
+        TLog.NewLog(ltdebug,Self.ClassName,Format('Received Safebox chunk %d..%d from %s in %.2f secs',[FBlockStart,FBlockStart+FBlocksCount,AConnection.ClientRemoteAddr,TPlatform.GetElapsedMilliseconds(Ltc)/1000]));
+      end else begin
+        Lerrors := 'No response on DownloadSafeBoxChunk';
+        TLog.NewLog(ltdebug,Self.ClassName,Lerrors);
+      end;
+    finally
+      receiveData.Free;
+      sendData.Free;
+    end;
+  end;
+var LConnection : TNetConnection;
+begin
+  repeat
+    FStream.Position := 0;
+    FStream.Size := 0; // Clear
+    // Search for a connection
+    FOwner.FChunks.LockList;
+    try
+      if TNetData.NetData.GetConnection(Random(TNetData.NetData.ConnectionsCountAll),LConnection) then begin
+        if Assigned(LConnection) And (Not LConnection.Connected) then LConnection := Nil;
+      end else LConnection := Nil;
+      if Assigned(LConnection) then begin
+        if TNetData.NetData.ConnectionLock(Self,LConnection,100) then begin
+          TNetData.NetData.ConnectionUnlock(LConnection);
+        end else LConnection := Nil;
+      end;
+    finally
+      FOwner.FChunks.UnlockList;
+    end;
+    if Assigned(LConnection) then begin
+      if DownloadSafebox(LConnection,FOwner.FSavedSafeboxHighOperationBlock.initial_safe_box_hash,Self.FStream) then Break;
+    end;
+    Sleep(100);
+  until Terminated;
+end;
+
+constructor TPCDownloadSafebox.TDownloadSBThread.Create(AOwner : TPCDownloadSafebox; ABlockStart, ACount : Cardinal);
+begin
+  FOwner := AOwner;
+  FBlockStart := ABlockStart;
+  FBlocksCount := ACount;
+  FStream := TPCTemporalFileStream.Create(Format('CHUNK_%.8d_%.8d',[ABlockStart,ABlockStart+ACount-1]));
+  inherited Create(True);
+
+  FreeOnTerminate := False;
+  Suspended := False;
+end;
+
+destructor TPCDownloadSafebox.TDownloadSBThread.Destroy;
+begin
+  FreeAndNil(FStream);
+  inherited;
+end;
+
+{ TPCDownloadSafebox }
+
+constructor TPCDownloadSafebox.Create;
+begin
+  FNode := TNode.Node;
+  FDownloadedBank := TPCBank.Create(Nil);
+  FChunks := TPCThreadList<TPCDownloadSafeboxChunk>.Create('');
+  FSavedSafeboxHighOperationBlock := CT_OperationBlock_NUL;
+  FOnProgressNotify := Nil;
+end;
+
+destructor TPCDownloadSafebox.Destroy;
+var i : Integer;
+  Ll : TList<TPCDownloadSafeboxChunk>;
+begin
+  Ll := FChunks.LockList;
+  Try
+    for i:=0 to Ll.Count-1 do begin
+      if assigned(Ll.Items[i].Thread) then begin
+        Ll.Items[i].Thread.Terminate;
+        Ll.Items[i].Thread.WaitFor;
+        Ll.Items[i].Thread.Free;
+      end;
+    end;
+  Finally
+    FChunks.UnlockList;
+  End;
+  FreeAndNil(FChunks);
+  FreeAndNil(FDownloadedBank);
+  inherited;
+end;
+
+function TPCDownloadSafebox.DownloadSafebox(AOwnerThread : TThread; ASavedSafeboxHighOperationBlock: TOperationBlock; AChunks : TPCSafeboxChunks): Boolean;
+var LDownloadedSafeboxBlocksCount, request_id : Cardinal;
+  LreceivedChunk : TStream;
+  safeBoxHeader : TPCSafeBoxHeader;
+  i : Integer;
+  LContinue : Boolean;
+  Ll : TList<TPCDownloadSafeboxChunk>;
+  Ldsbc : TPCDownloadSafeboxChunk;
+  LTerminated : Boolean;
+  LTerminatedCount, LTotal : Integer;
+  LFileName, Lstatus : String;
+Begin
+  Result := False;
+  // Check
+  LDownloadedSafeboxBlocksCount := ((ASavedSafeboxHighOperationBlock.block DIV CT_BankToDiskEveryNBlocks)) * CT_BankToDiskEveryNBlocks;
+  if LDownloadedSafeboxBlocksCount<>ASavedSafeboxHighOperationBlock.block then Exit(False);
+  FSavedSafeboxHighOperationBlock := ASavedSafeboxHighOperationBlock;
+
+  LTotal := 0;
+  Ll := FChunks.LockList;
+  Try
+    for i:=0 to ((LDownloadedSafeboxBlocksCount-1) DIV CT_MAX_SAFEBOXCHUNK_BLOCKS) do begin
+      Ldsbc.BlockStart := (i * CT_MAX_SAFEBOXCHUNK_BLOCKS);
+      Ldsbc.Count := CT_MAX_SAFEBOXCHUNK_BLOCKS;
+      if Ldsbc.BlockStart + Ldsbc.Count > LDownloadedSafeboxBlocksCount then begin
+        Ldsbc.Count := LDownloadedSafeboxBlocksCount - Ldsbc.BlockStart;
+      end;
+      Ldsbc.Thread := Nil;
+      Ll.Add(Ldsbc);
+    end;
+    LTotal := Ll.Count;
+  Finally
+    FChunks.UnlockList;
+  End;
+
+  if Assigned(AOwnerThread) then LContinue := Not AOwnerThread.CheckTerminated
+  else LContinue := True;
+
+  LTerminated := False;
+
+  while (FNode.NetServer.Active) And LContinue And (Not LTerminated) do begin
+    //
+    LTerminatedCount := 0;
+    Ll := FChunks.LockList;
+    Try
+      i := 0;
+      for i:=0 to Ll.Count-1 do begin
+        Ldsbc := Ll.Items[i];
+        if Not Assigned(Ldsbc.Thread) then begin
+          Ldsbc.Thread := TPCDownloadSafebox.TDownloadSBThread.Create(Self,Ldsbc.BlockStart,Ldsbc.Count);
+          Ll.Items[i] := Ldsbc;
+        end else begin
+          if Ldsbc.Thread.Terminated then Inc(LTerminatedCount);
+        end;
+      end;
+      LTerminated := LTerminatedCount >= Ll.Count;
+      Lstatus := Format('Downloading Safebox chunks %d/%d',[LTerminatedCount,Ll.Count]);
+    Finally
+      FChunks.UnlockList;
+    End;
+
+    Sleep(10);
+    //
+    if Assigned(AOwnerThread) then LContinue := Not AOwnerThread.CheckTerminated
+    else LContinue := True;
+    if (LContinue) and (Assigned(FOnProgressNotify)) then begin
+      FOnProgressNotify(Self,LStatus,LTerminatedCount,LTotal);
+    end;
+  end;
+
+  if (LTerminated) And (LContinue) then begin
+    AChunks.Clear;
+    Ll := FChunks.LockList;
+    try
+      for i := 0 to Ll.Count-1 do begin
+        Ll.Items[i].Thread.Stream.Position := 0;
+        AChunks.AddChunk(Ll.Items[i].Thread.Stream,True,True);
+        Ll.Items[i].Thread.Stream := Nil;
+      end;
+      LFileName := TNode.Node.Bank.GetStorageFolder('')+PathDelim+'safebox_'+IntToStr(LDownloadedSafeboxBlocksCount)+'.safebox';
+
+      if (Assigned(FOnProgressNotify)) then begin
+        FOnProgressNotify(Self,Format('Saving Safebox %d chunks to %s',[LTotal,ExtractFileName(LFileName)]),0,0);
+      end;
+      AChunks.SaveSafeboxfile(LFileName);
+    finally
+      FChunks.UnlockList;
+    end;
+    //
+    Result := True;
+  end;
+
+
+end;
+
+initialization
+finalization
+end.