Browse Source

PIP-0015

First PIP-0015 implementation
PascalCoin 7 years ago
parent
commit
51884d86e1
3 changed files with 85 additions and 34 deletions
  1. 1 0
      README.md
  2. 2 2
      src/core/UConst.pas
  3. 82 32
      src/core/UNetProtocol.pas

+ 1 - 0
README.md

@@ -37,6 +37,7 @@ Also, consider a donation at PascalCoin development account: "0-10"
 TODO: Bug in Lazarus optimization cause Access Violation on "getpendings" call (must be 0 or 1). techworker/ugo4brain in discord 2018-05-11 #development channel
 TODO: Bug in Lazarus optimization cause Access Violation on "getpendings" call (must be 0 or 1). techworker/ugo4brain in discord 2018-05-11 #development channel
 ### Build XXXXXX - CURRENT
 ### Build XXXXXX - CURRENT
 - Implementation of Hard fork on block XXXXXXX (PENDING... TODO !)
 - Implementation of Hard fork on block XXXXXXX (PENDING... TODO !)
+  - PIP - 0015: Fast Block Propagation
   - PIP - 0016: Layer-2 protocol support
   - PIP - 0016: Layer-2 protocol support
   - New digest hash for signature verifications
   - New digest hash for signature verifications
   - Added OrderedAccountKeysList that allows an indexed search of public keys in the safebox with mem optimization
   - Added OrderedAccountKeysList that allows an indexed search of public keys in the safebox with mem optimization

+ 2 - 2
src/core/UConst.pas

@@ -116,7 +116,7 @@ Const
   CT_NetProtocol_Version: Word = $0007; // Version 3.0.2 only allows net protocol 7 (Introduced on 3.0.0)
   CT_NetProtocol_Version: Word = $0007; // Version 3.0.2 only allows net protocol 7 (Introduced on 3.0.0)
   // IMPORTANT NOTE!!!
   // IMPORTANT NOTE!!!
   // NetProtocol_Available MUST BE always >= NetProtocol_version
   // NetProtocol_Available MUST BE always >= NetProtocol_version
-  CT_NetProtocol_Available: Word = $0008;  // Remember, >= NetProtocol_version !!!
+  CT_NetProtocol_Available: Word = {$IFDEF PRODUCTION}$0007{$ELSE}$0008{$ENDIF};  // Remember, >= NetProtocol_version !!!
 
 
   CT_MaxAccountOperationsPerBlockWithoutFee = 1;
   CT_MaxAccountOperationsPerBlockWithoutFee = 1;
 
 
@@ -167,7 +167,7 @@ Const
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Receiver              = 104;
   CT_OpSubtype_Data_Receiver              = 104;
 
 
-  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'3.0.2'{$ELSE}{$IFDEF TESTNET}'TESTNET 3.0.3'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : AnsiString = {$IFDEF PRODUCTION}'3.0.2'{$ELSE}{$IFDEF TESTNET}'TESTNET 3.0.4'{$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'{$IFDEF TESTNET}+';99.254.181.147;159.89.12.242;18.236.158.185'{$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'{$IFDEF TESTNET}+';99.254.181.147;159.89.12.242;18.236.158.185'{$ENDIF};
 
 

+ 82 - 32
src/core/UNetProtocol.pas

@@ -44,6 +44,7 @@ Const
   CT_NetOp_GetBlockHeaders      = $0005; // Sends from and to. Receive a number of OperationsBlock to check
   CT_NetOp_GetBlockHeaders      = $0005; // Sends from and to. Receive a number of OperationsBlock to check
   CT_NetOp_GetBlocks            = $0010;
   CT_NetOp_GetBlocks            = $0010;
   CT_NetOp_NewBlock             = $0011;
   CT_NetOp_NewBlock             = $0011;
+  CT_NetOp_NewBlock_Fast_Propagation = $0012; // New V4 protocol: Allows PIP-0015 Fast block propagation
   CT_NetOp_AddOperations        = $0020;
   CT_NetOp_AddOperations        = $0020;
   CT_NetOp_GetSafeBox           = $0021; // V2 Protocol: Allows to send/receive Safebox in chunk parts
   CT_NetOp_GetSafeBox           = $0021; // V2 Protocol: Allows to send/receive Safebox in chunk parts
 
 
@@ -2118,6 +2119,7 @@ begin
     CT_NetOp_Message : Result := 'MESSAGE';
     CT_NetOp_Message : Result := 'MESSAGE';
     CT_NetOp_GetBlockHeaders : Result := 'GET BLOCK HEADERS';
     CT_NetOp_GetBlockHeaders : Result := 'GET BLOCK HEADERS';
     CT_NetOp_NewBlock : Result := 'NEW BLOCK';
     CT_NetOp_NewBlock : Result := 'NEW BLOCK';
+    CT_NetOp_NewBlock_Fast_Propagation : Result := 'NEW BLOCK FAST PROPAGATION';
     CT_NetOp_AddOperations : Result := 'ADD OPERATIONS';
     CT_NetOp_AddOperations : Result := 'ADD OPERATIONS';
     CT_NetOp_GetSafeBox : Result := 'GET SAFEBOX';
     CT_NetOp_GetSafeBox : Result := 'GET SAFEBOX';
     CT_NetOp_GetPendingOperations : Result := 'GET PENDING OPERATIONS';
     CT_NetOp_GetPendingOperations : Result := 'GET PENDING OPERATIONS';
@@ -3350,10 +3352,14 @@ begin
 end;
 end;
 
 
 procedure TNetConnection.DoProcess_NewBlock(HeaderData: TNetHeaderData; DataBuffer: TStream);
 procedure TNetConnection.DoProcess_NewBlock(HeaderData: TNetHeaderData; DataBuffer: TStream);
+var op : TPCOperationsComp;
 var bacc : TBlockAccount;
 var bacc : TBlockAccount;
-    op : TPCOperationsComp;
   errors : AnsiString;
   errors : AnsiString;
   DoDisconnect : Boolean;
   DoDisconnect : Boolean;
+  oprefcount : Cardinal;
+  i, iNodeOpReference : Integer;
+  opReference : TOpReference;
+  opReferencesArr : TOpReferenceArray;
 begin
 begin
   errors := '';
   errors := '';
   DoDisconnect := true;
   DoDisconnect := true;
@@ -3370,38 +3376,60 @@ begin
         exit;
         exit;
       end else begin
       end else begin
         DoDisconnect := false;
         DoDisconnect := false;
-        if DataBuffer.Size - DataBuffer.Position >= SizeOf(FRemoteAccumulatedWork) then begin
-          DataBuffer.Read(FRemoteAccumulatedWork,SizeOf(FRemoteAccumulatedWork));
+        DataBuffer.Read(FRemoteAccumulatedWork,SizeOf(FRemoteAccumulatedWork));
+        if op.IsOnlyOperationBlock then begin
+          TLog.NewLog(ltdebug,ClassName,'Received NEW FAST PROPAGATION BLOCK with height: '+inttostr(op.OperationBlock.block)+' Accumulated work '+IntToStr(FRemoteAccumulatedWork));
+        end else begin
           TLog.NewLog(ltdebug,ClassName,'Received NEW BLOCK with height: '+inttostr(op.OperationBlock.block)+' Accumulated work '+IntToStr(FRemoteAccumulatedWork));
           TLog.NewLog(ltdebug,ClassName,'Received NEW BLOCK with height: '+inttostr(op.OperationBlock.block)+' Accumulated work '+IntToStr(FRemoteAccumulatedWork));
-        end else FRemoteAccumulatedWork := 0;
+        end;
         FRemoteOperationBlock := op.OperationBlock;
         FRemoteOperationBlock := op.OperationBlock;
-        //
-        if FRemoteAccumulatedWork=0 then begin
-          // Old version. No data
-          if (op.OperationBlock.block>TNode.Node.Bank.BlocksCount) then begin
-            TNetData.NetData.GetNewBlockChainFromClient(Self,Format('BlocksCount:%d > my BlocksCount:%d',[op.OperationBlock.block+1,TNode.Node.Bank.BlocksCount]));
-          end else if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
+        if (FRemoteAccumulatedWork>TNode.Node.Bank.SafeBox.WorkSum) then begin
+          if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
             // New block candidate:
             // New block candidate:
-            If Not TNode.Node.AddNewBlockChain(Self,op,bacc,errors) then begin
-              // Received a new invalid block... perhaps I'm an orphan blockchain
-              TNetData.NetData.GetNewBlockChainFromClient(Self,'Has a distinct block. '+errors);
+            if (op.IsOnlyOperationBlock) then begin
+              // Received a FAST PROPAGATION BLOCK as described at PIP-0015
+              // Fill operations reference:
+              DoDisconnect := True;
+              if DataBuffer.Read(oprefcount,SizeOf(oprefcount))<>SizeOf(oprefcount) then Exit;
+              if DataBuffer.Size - DataBuffer.Position < (oprefcount * SizeOf(TOpReference)) then Exit;
+              SetLength(opReferencesArr,oprefcount);
+              for i := 1 to oprefcount do begin
+                if DataBuffer.Read(opReference,SizeOf(opReference))<>SizeOf(opReference) then Exit;
+                opReferencesArr[High(opReferencesArr)] := opReference;
+              end;
+              DoDisconnect := False;
+              // Try TNode locking process
+              If TNode.Node.TryLockNode(3000) then begin
+                Try
+                  if (op.OperationBlock.block<>TNode.Node.Bank.BlocksCount) then Exit; // Meanwhile other threads have added it
+                  // Fill not found operations:
+                  for i:=0 to High(opReferencesArr) do begin
+                    iNodeOpReference := TNode.Node.Operations.OperationsHashTree.IndexOfOpReference(opReferencesArr[i]);
+                    if iNodeOpReference<0 then begin
+                      // Warning: Operation not found on mempool... cannot continue, must call block
+                      TLog.NewLog(ltinfo,ClassName,Format('OpReference %d:%d not found on MemPool for new block %d at OpReference %d/%d',
+                        [TPCOperation.GetOpReferenceAccount(opReferencesArr[i]),TPCOperation.GetOpReferenceN_Operation(opReferencesArr[i]),
+                        op.OperationBlock.block,i+1,Length(opReferencesArr)]));
+                      Send_GetBlocks(op.OperationBlock.block,1,oprefcount);
+                      Exit;
+                    end;
+                  end;
+                Finally
+                  TNode.Node.UnlockNode;
+                End;
+              end;
             end;
             end;
-          end;
-        end else begin
-          if (FRemoteAccumulatedWork>TNode.Node.Bank.SafeBox.WorkSum) then begin
-            if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
-              // New block candidate:
-              If Not TNode.Node.AddNewBlockChain(Self,op,bacc,errors) then begin
-                // Really is a new block? (Check it)
-                if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
-                  // Received a new invalid block... perhaps I'm an orphan blockchain
-                  TNetData.NetData.GetNewBlockChainFromClient(Self,'Higher Work with same block height. I''m a orphan blockchain candidate');
-                end;
+            //
+            If Not TNode.Node.AddNewBlockChain(Self,op,bacc,errors) then begin
+              // Really is a new block? (Check it)
+              if (op.OperationBlock.block=TNode.Node.Bank.BlocksCount) then begin
+                // Received a new invalid block... perhaps I'm an orphan blockchain
+                TNetData.NetData.GetNewBlockChainFromClient(Self,'Higher Work with same block height. I''m a orphan blockchain candidate');
               end;
               end;
-            end else begin
-              // Received a new higher work
-              TNetData.NetData.GetNewBlockChainFromClient(Self,Format('Higher Work and distinct blocks count. Need to download BlocksCount:%d  my BlocksCount:%d',[op.OperationBlock.block+1,TNode.Node.Bank.BlocksCount]));
             end;
             end;
+          end else begin
+            // Received a new higher work
+            TNetData.NetData.GetNewBlockChainFromClient(Self,Format('Higher Work and distinct blocks count. Need to download BlocksCount:%d  my BlocksCount:%d',[op.OperationBlock.block+1,TNode.Node.Bank.BlocksCount]));
           end;
           end;
         end;
         end;
       end;
       end;
@@ -3482,7 +3510,7 @@ begin
                       DoProcess_GetOperationsBlock_Request(HeaderData,ReceiveDataBuffer)
                       DoProcess_GetOperationsBlock_Request(HeaderData,ReceiveDataBuffer)
                     else TLog.NewLog(ltdebug,Classname,'Received old response of: '+TNetData.HeaderDataToText(HeaderData));
                     else TLog.NewLog(ltdebug,Classname,'Received old response of: '+TNetData.HeaderDataToText(HeaderData));
                   End;
                   End;
-                  CT_NetOp_NewBlock : Begin
+                  CT_NetOp_NewBlock, CT_NetOp_NewBlock_Fast_Propagation : Begin
                     DoProcess_NewBlock(HeaderData,ReceiveDataBuffer);
                     DoProcess_NewBlock(HeaderData,ReceiveDataBuffer);
                   End;
                   End;
                   CT_NetOp_AddOperations : Begin
                   CT_NetOp_AddOperations : Begin
@@ -3940,10 +3968,13 @@ begin
   End;
   End;
 end;
 end;
 
 
-function TNetConnection.Send_NewBlockFound(const NewBlock: TPCOperationsComp
-  ): Boolean;
+function TNetConnection.Send_NewBlockFound(const NewBlock: TPCOperationsComp): Boolean;
 var data : TStream;
 var data : TStream;
   request_id : Integer;
   request_id : Integer;
+  netOp : Word;
+  c : Cardinal;
+  i : Integer;
+  opRef : TOpReference;
 begin
 begin
   Result := false;
   Result := false;
   if Not Connected then exit;
   if Not Connected then exit;
@@ -3969,9 +4000,28 @@ begin
     data := TMemoryStream.Create;
     data := TMemoryStream.Create;
     try
     try
       request_id := TNetData.NetData.NewRequestId;
       request_id := TNetData.NetData.NewRequestId;
-      NewBlock.SaveBlockToStream(false,data);
+      if (FNetProtocolVersion.protocol_available = CT_NetProtocol_Available)
+        and (pos(CT_ClientAppVersion,FClientAppVersion)>0) // XXXXXXXXXXXX Albert: ONLY FOR TESTING PURPOSE, NOT NEEDED ON PRODUCTION, REMOVE
+        then begin
+        // Will send a FAST PROPAGATION BLOCK as described at PIP-0015
+        netOp := CT_NetOp_NewBlock_Fast_Propagation;
+      end else begin
+        netOp := CT_NetOp_NewBlock;
+      end;
+      NewBlock.SaveBlockToStream(netOp = CT_NetOp_NewBlock_Fast_Propagation,data); // Will save all only if not FAST PROPAGATION
       data.Write(TNode.Node.Bank.SafeBox.WorkSum,SizeOf(TNode.Node.Bank.SafeBox.WorkSum));
       data.Write(TNode.Node.Bank.SafeBox.WorkSum,SizeOf(TNode.Node.Bank.SafeBox.WorkSum));
-      Send(ntp_autosend,CT_NetOp_NewBlock,0,request_id,data);
+      if (netOp = CT_NetOp_NewBlock_Fast_Propagation) then begin
+        // Fill with OpReference data:
+        c := NewBlock.OperationsHashTree.OperationsCount;
+        data.Write(c,SizeOf(c));
+        if (c>0) then begin
+          for i := 0 to (Integer(c)-1) do begin
+            opRef := NewBlock.Operation[i].GetOpReference;
+            data.Write(opRef,SizeOf(opRef));
+          end;
+        end;
+      end;
+      Send(ntp_autosend,netOp,0,request_id,data);
     finally
     finally
       data.Free;
       data.Free;
     end;
     end;