Browse Source

Merge pull request #19 from PascalCoinDev/master

Release 5.1
Pascal Coin 5 years ago
parent
commit
1ca49aea26

+ 7 - 1
CHANGELOG.md

@@ -1,5 +1,11 @@
 # Changelog
 
+## Build 5.1.0 - 2019-11-25
+- Fixed FastPropagation bug when Operations are not on Mempool and needs to obtain from a node with different version (V4 vs V5)
+- Fixed TOpChangeAccountInfo bug when changing Account.Data (not available on V4, V5 must not propagate while on V4 protocol)
+- Fixed GUI bug when using coma as decimal separator
+- Upgraded Net Protocol to 11 (needed to detect/fix errors of FastPropagation )
+
 ## Build 5.0.0 - 2019-11-07
 - MANDATORY UPGRADE - HARD FORK ACTIVATION WILL OCCUR ON BLOCK 378000
 - Upgrade to Protocol 5 (Hard fork)
@@ -19,7 +25,7 @@
 - Partial implementation of PIP-0012 (Recover Accounts option after 4 years) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0012.md
   - Accounts updated counter will only update when executing operations in active mode (See PIP-0037)
   - If account is a receiver (passive mode) then n_operation_update_block will not update value and can be recovered after 4 years (as defined on original PascalCoin v1 WhitePaper)
-- Fixed important security issue related to PIP-0003 caused by possible "parallelization" of the Proof-of-work
+- Fixed important security issue related to PIP-0003 caused by possible "parallelization" of the Proof-of-work (found by Herman Schoenfeld <[email protected]>)
   - Modified length of the digest to be mined, adding previous proof-of-work to avoid parallelization
   - Added extra 4 missed bytes of fee (missing since V1)
   - The total length of the digest to be mined has increased in 36 bytes (32 for PoW and 4 for missing fee bytes). Those bytes are added on "Part 3" of the digest

+ 21 - 9
src/core/UAccounts.pas

@@ -1923,24 +1923,37 @@ end;
 class function TAccountComp.TxtToMoney(const moneytxt: String;
   var money: Int64): Boolean;
 Var s : String;
+  LPosThousand, LPosDecimal : Integer;
+  LMoneyString : String;
 begin
   money := 0;
-  if Trim(moneytxt)='' then begin
+  LMoneyString := Trim(moneytxt);
+  if LMoneyString.Length=0 then begin
     Result := true;
     exit;
   end;
   try
-    If pos({$IFDEF DELPHIXE}FormatSettings.{$ENDIF}DecimalSeparator,moneytxt)<=0 then begin
-      // No decimal separator, consider ThousandSeparator as a decimal separator
-      s := StringReplace(moneytxt,{$IFDEF DELPHIXE}FormatSettings.{$ENDIF}ThousandSeparator,{$IFDEF DELPHIXE}FormatSettings.{$ENDIF}DecimalSeparator,[rfReplaceAll]);
-    end else begin
-      s := StringReplace(moneytxt,{$IFDEF DELPHIXE}FormatSettings.{$ENDIF}ThousandSeparator,'',[rfReplaceAll]);
+    LPosThousand := LMoneyString.IndexOf( TPCJSONData.JSONFormatSettings.ThousandSeparator );
+    LPosDecimal  := LMoneyString.IndexOf( TPCJSONData.JSONFormatSettings.DecimalSeparator );
+
+    if (LPosThousand>0) then begin
+      if (LPosThousand < LPosDecimal ) then begin
+        // Remove thousand values
+        LMoneyString := LMoneyString.Replace(String(TPCJSONData.JSONFormatSettings.ThousandSeparator),'',[rfReplaceAll]);
+      end else begin
+        // Possible 15.123.456,7890 format ( coma (,) = decimal separator )
+        // Remove decimal "." and convert thousand to decimal
+        LMoneyString := LMoneyString.Replace(String(TPCJSONData.JSONFormatSettings.DecimalSeparator),'',[rfReplaceAll]);
+        LMoneyString := LMoneyString.Replace(TPCJSONData.JSONFormatSettings.ThousandSeparator,TPCJSONData.JSONFormatSettings.DecimalSeparator,[rfReplaceAll]);
+      end;
     end;
-    money := Round( StrToFloat(s)*10000 );
+
+    money := Round( StrToFloat(LMoneyString,TPCJSONData.JSONFormatSettings)*10000 );
     Result := true;
   Except
     result := false;
   end;
+
 end;
 
 class procedure TAccountComp.ValidsEC_OpenSSL_NID(list: TList<Word>);
@@ -3976,8 +3989,7 @@ begin
   end;
   // initial_safe_box_hash: Only can be checked when adding new blocks, not when restoring a safebox
   If checkSafeBoxHash then begin
-    // TODO: Can use FSafeBoxHash instead of CalcSafeBoxHash ???? Quick speed if possible
-    if (Not TBaseType.Equals(newOperationBlock.initial_safe_box_hash,CalcSafeBoxHash)) then begin
+    if (Not TBaseType.Equals(newOperationBlock.initial_safe_box_hash,FSafeBoxHash)) then begin
       errors := 'BlockChain Safe box hash invalid: '+TCrypto.ToHexaString(newOperationBlock.initial_safe_box_hash)+' var: '+
         TCrypto.ToHexaString(FSafeBoxHash)+
         ' Calculated:'+TCrypto.ToHexaString(CalcSafeBoxHash);

+ 57 - 12
src/core/UBlockChain.pas

@@ -278,7 +278,8 @@ Type
     function GetOpID : TRawBytes; // OPID is RipeMD160 hash of the operation
     //
     function GetOperationStreamData : TBytes;
-    class function GetOperationFromStreamData(ACurrentProtocol: word; StreamData : TBytes) : TPCOperation;
+    function GetOperationStreamData_OLD_V4_Version : TBytes; // deprecated
+    class function GetOperationFromStreamData(AUseV5EncodeStyle : Boolean; ACurrentProtocol: word; StreamData : TBytes) : TPCOperation;
     //
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; virtual; abstract;
   End;
@@ -1757,10 +1758,6 @@ begin
       lastn := FOperationsHashTree.OperationsCount;
       for i:=0 to lastn-1 do begin
         op := FOperationsHashTree.GetOperation(i);
-        if OperationBlock.protocol_version <> op.ProtocolVersion then begin
-          errors := Format('Sanitize Operation protocol:%d <> current protocol:%d on %s',[op.ProtocolVersion,OperationBlock.protocol_version, op.ToString]);
-          Tlog.NewLog(lterror,ClassName,errors);
-        end else begin
           if (aux.CanAddOperationToHashTree(op)) then begin
             if (op.DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors)) then begin
               if aux.AddOperationToHashTree(op) then begin
@@ -1775,7 +1772,6 @@ begin
               end;
             end;
           end;
-        end;
       end;
     Finally
       aux2 := FOperationsHashTree;
@@ -2972,27 +2968,47 @@ begin
   end else Raise Exception.Create('ERROR DEV 20170426-1'); // This should never happen, if good coded
 end;
 
-class function TPCOperation.GetOperationFromStreamData(ACurrentProtocol: word; StreamData : TBytes): TPCOperation;
+class function TPCOperation.GetOperationFromStreamData(AUseV5EncodeStyle : Boolean; ACurrentProtocol: word; StreamData : TBytes): TPCOperation;
   // Loads an TPCOperation saved using "GetOperationStreamData"
-  // 1 byte for OpType
+  // For compatiblity will allow V4..V5 encode stype
+  // Old V4: 1 byte for OpType
+  // New V5: 2 bytes for OpType and 2 bytes for ProtocolVersion
   // N bytes for Operation specific data (saved at SaveOpToStream)
+  //
+  // NOTE:
+  // AFTER V5 activation, all nodes must use new AUseV5EcnodeStyle = TRUE
 Var stream : TStream;
   b : Byte;
   j: Integer;
   OpClass: TPCOperationClass;
   auxOp: TPCOperation;
+  LOpType, LOperationProtocolVersion : Word;
 begin
   Result := Nil;
   stream := TMemoryStream.Create;
   Try
     stream.WriteBuffer(StreamData[0],Length(StreamData)); // Fixed bug 4.0.0
     stream.Position := 0;
-    stream.Read(b,1);
-    j := TPCOperationsComp.IndexOfOperationClassByOpType(b);
+
+    if (AUseV5EncodeStyle) then begin
+      // 2 bytes (UInt16) for OpType
+      // 2 bytes (UInt16) for ProtocolVersion
+      Stream.Read(LOpType,2);
+      Stream.Read(LOperationProtocolVersion,2);
+      if (LOperationProtocolVersion<=0) or (LOperationProtocolVersion>CT_BUILD_PROTOCOL) then Exit;
+    end else begin
+      // 1 bytes (UInt8) for OpType
+      // Fixed ProtocolVersion = 4
+      stream.Read(b,1);
+      LOpType := b;
+      LOperationProtocolVersion:=ACurrentProtocol;
+    end;
+
+    j := TPCOperationsComp.IndexOfOperationClassByOpType(LOpType);
     if j >= 0 then
       OpClass := _OperationsClass[j]
     else Exit;
-    auxOp := OpClass.Create(ACurrentProtocol);
+    auxOp := OpClass.Create(LOperationProtocolVersion);
     if auxOp.LoadOpFromStream(stream,False) then Result := auxOp
     else auxOp.Free;
   Finally
@@ -3000,10 +3016,14 @@ begin
   End;
 end;
 
-function TPCOperation.GetOperationStreamData: TBytes;
+function TPCOperation.GetOperationStreamData_OLD_V4_Version: TBytes;
   // OperationStreamData fills an array of bytes with info needed to store an operation
   // 1 byte for OpType
   // N bytes for Operation specific data (saved at SaveOpToStream)
+
+  //
+  // THIS FUNCTION IS DEPRECATED, Usable only for V4 to V5 upgrade process
+  //
 var stream : TStream;
   b : Byte;
 begin
@@ -3020,6 +3040,31 @@ begin
   End;
 end;
 
+function TPCOperation.GetOperationStreamData: TBytes;
+  // OperationStreamData fills an array of bytes with info needed to store an operation
+  // 2 bytes for OpType
+  // 2 bytes for ProtocolVersion
+  // N bytes for Operation specific data (saved at SaveOpToStream)
+var stream : TStream;
+  LOpType, LOperationProtocolVersion : Word;
+begin
+  stream := TMemoryStream.Create;
+  Try
+    LOpType := Self.OpType;
+    LOperationProtocolVersion := Self.ProtocolVersion;
+
+    Stream.Write(LOpType,2);
+    Stream.Write(LOperationProtocolVersion,2);
+
+    SaveOpToStream(stream,False);
+    SetLength(Result,stream.Size);
+    stream.Position := 0;
+    stream.ReadBuffer(Result[0],stream.Size); // Fixed bug 4.0.0
+  Finally
+    stream.Free;
+  End;
+end;
+
 function TPCOperation.GetOpID: TRawBytes;
 begin
   Result := RipeMD160;

+ 2 - 2
src/core/UConst.pas

@@ -132,7 +132,7 @@ Const
   CT_NetProtocol_Version: Word = 9; // TODO Need to upgrade to 10 after V5 Hardfork
   // IMPORTANT NOTE!!!
   // NetProtocol_Available MUST BE always >= NetProtocol_version
-  CT_NetProtocol_Available: Word = {$IFDEF PRODUCTION}10{$ELSE}10{$ENDIF};
+  CT_NetProtocol_Available: Word = {$IFDEF PRODUCTION}11{$ELSE}11{$ENDIF};
 
   CT_MaxAccountOperationsPerBlockWithoutFee = 1;
 
@@ -195,7 +195,7 @@ Const
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Receiver              = 104;
 
-  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'5.0'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.0'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'5.1'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.1'{$ELSE}{$ENDIF}{$ENDIF};
 
   CT_Discover_IPs = {$IFDEF PRODUCTION}'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'
                     {$ELSE}'pascaltestnet1.dynamic-dns.net;pascaltestnet2.dynamic-dns.net;pascaltestnet1.dns1.us;pascaltestnet2.dns1.us'{$ENDIF};

+ 28 - 16
src/core/UNetProtocol.pas

@@ -399,9 +399,9 @@ Type
     Procedure DoProcess_Message(HeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure DoProcess_GetBlocks_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure DoProcess_GetBlocks_Response(HeaderData : TNetHeaderData; DataBuffer: TStream);
-    Procedure DoProcess_GetBlockchainOperations_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_GetBlockchainOperations_Request(AHeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure DoProcess_GetOperationsBlock_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
-    Procedure DoProcess_NewBlock(HeaderData : TNetHeaderData; DataBuffer: TStream);
+    Procedure DoProcess_NewBlock(AHeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure DoProcess_AddOperations(HeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure DoProcess_GetSafeBox_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
     Procedure DoProcess_GetPendingOperations_Request(HeaderData : TNetHeaderData; DataBuffer: TStream);
@@ -2719,7 +2719,7 @@ begin
   end;
 end;
 
-procedure TNetConnection.DoProcess_GetBlockchainOperations_Request(HeaderData: TNetHeaderData; DataBuffer: TStream);
+procedure TNetConnection.DoProcess_GetBlockchainOperations_Request(AHeaderData: TNetHeaderData; DataBuffer: TStream);
   {
   As described on PIP-0015 this will return Operations stored in a specified Block of the Blockchain
   Input:
@@ -2761,7 +2761,7 @@ begin
   DoDisconnect := true;
   outputBuffer := TMemoryStream.Create;
   try
-    if HeaderData.header_type<>ntp_request then begin
+    if AHeaderData.header_type<>ntp_request then begin
       errors := 'Not request';
       Exit;
     end;
@@ -2784,7 +2784,11 @@ begin
         opc := GetBlock(bufferOperationsBlock, cBlock);
         if Assigned(opc) then begin
           if (cBlockOpIndex<opc.Count) then begin
-            opsdata := opc.Operation[cBlockOpIndex].GetOperationStreamData;
+            if AHeaderData.protocol.protocol_available >= 11 then begin
+              opsdata := opc.Operation[cBlockOpIndex].GetOperationStreamData;
+            end else begin
+              opsdata := opc.Operation[cBlockOpIndex].GetOperationStreamData_OLD_V4_Version;
+            end;
             c := Length(opsdata);
             outputBuffer.Write(c,SizeOf(c));
             outputBuffer.WriteBuffer(opsdata[0],Length(opsdata)); // Fixed bug 4.0.0
@@ -2803,7 +2807,7 @@ begin
       DoDisconnect := False;
       // Send back
       outputBuffer.Position := 0;
-      Send(ntp_response,HeaderData.operation,0,HeaderData.request_id,outputBuffer);
+      Send(ntp_response,AHeaderData.operation,0,AHeaderData.request_id,outputBuffer);
     Finally
       opindexdata.Free;
       for i := 0 to bufferOperationsBlock.Count-1 do begin
@@ -2814,7 +2818,7 @@ begin
   finally
     outputBuffer.Free;
     if DoDisconnect then begin
-      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
+      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(AHeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
     end;
   end;
 end;
@@ -3672,11 +3676,12 @@ begin
   end;
 end;
 
-procedure TNetConnection.DoProcess_NewBlock(HeaderData: TNetHeaderData; DataBuffer: TStream);
+procedure TNetConnection.DoProcess_NewBlock(AHeaderData: TNetHeaderData; DataBuffer: TStream);
 Type
   TNewFastPropagationBlockOperation = Record
     opReference : TOpReference;
     opStreamData : TBytes;
+    opStreamDataUsingV5EncodeStyle : Boolean;
   end;
   TNewFastPropagationBlockOperationsArray = Array of TNewFastPropagationBlockOperation;
 
@@ -3690,7 +3695,7 @@ var operationsComp : TPCOperationsComp;
     i,iNodeOpReference : Integer;
     sendStream, receiveStream : TStream;
     block_op_ref : UInt64;
-    headerData : TNetHeaderData;
+    LHeaderData : TNetHeaderData;
     auxOp : TPCOperation;
     tc : TTickCount;
     original_OperationBlock : TOperationBlock;
@@ -3710,6 +3715,7 @@ var operationsComp : TPCOperationsComp;
       for i := 0 to Integer(Integer(oprefcount)-1) do begin
         if DataBuffer.Read(nfpboarr[i].opReference,SizeOf(TOpReference))<>SizeOf(TOpReference) then Exit;
         SetLength(nfpboarr[i].opStreamData,0);
+        nfpboarr[i].opStreamDataUsingV5EncodeStyle:=False;
       end;
     end;
     DoDisconnect := False;
@@ -3726,6 +3732,7 @@ var operationsComp : TPCOperationsComp;
             iNodeOpReference := LLockedMempool.OperationsHashTree.IndexOfOpReference(nfpboarr[i].opReference);
             if iNodeOpReference>=0 then begin
               nfpboarr[i].opStreamData := LLockedMempool.OperationsHashTree.GetOperation(iNodeOpReference).GetOperationStreamData;
+              nfpboarr[i].opStreamDataUsingV5EncodeStyle:=True; // Flag to indicate that opStreamData is saved in V5 format
             end else begin
               inc(notFoundOpReferencesCount);
             end;
@@ -3756,12 +3763,12 @@ var operationsComp : TPCOperationsComp;
           end;
         end;
         // Send & wait
-        if Not DoSendAndWaitForResponse(CT_NetOp_GetBlockchainOperations,TNetData.NetData.NewRequestId,sendStream,receiveStream,5000,headerData) then begin
+        if Not DoSendAndWaitForResponse(CT_NetOp_GetBlockchainOperations,TNetData.NetData.NewRequestId,sendStream,receiveStream,5000,LHeaderData) then begin
           TLog.NewLog(ltdebug,ClassName,Format('Not received Pending operations (%d of %d) in Fast propagation block %d',[notFoundOpReferencesCount,oprefcount,operationsComp.OperationBlock.block]));
           Exit;
         end;
         DoDisconnect := True; // If bad received data... then DoDisconnect
-        if (headerData.is_error) then Exit;
+        if (LHeaderData.is_error) then Exit;
         receiveStream.Position := 0;
         receiveStream.Read(c,SizeOf(c));
         if (c<>notFoundOpReferencesCount) then Exit; // Error!
@@ -3773,6 +3780,7 @@ var operationsComp : TPCOperationsComp;
             if receiveStream.Size - receiveStream.Position < c then Exit; // Not enough received data
             SetLength(nfpboarr[i].opStreamData,c);
             receiveStream.ReadBuffer(nfpboarr[i].opStreamData[0],c); // Fixed bug 4.0.0
+            nfpboarr[i].opStreamDataUsingV5EncodeStyle := (LHeaderData.protocol.protocol_available>=11)
           end;
         end;
         DoDisconnect := False;
@@ -3783,12 +3791,16 @@ var operationsComp : TPCOperationsComp;
     end;
     // Now we have nfpboarr with full data
     for i := 0 to High(nfpboarr) do begin
-      auxOp := TPCOperation.GetOperationFromStreamData(original_OperationBlock.protocol_version,  nfpboarr[i].opStreamData );
+      auxOp := TPCOperation.GetOperationFromStreamData( (nfpboarr[i].opStreamDataUsingV5EncodeStyle), original_OperationBlock.protocol_version , nfpboarr[i].opStreamData );
       if not Assigned(auxOp) then begin
-        errors := Format('Op index not available (%d/%d) OpReference:%d size:%d',[i,High(nfpboarr),nfpboarr[i].opReference,Length(nfpboarr[i].opStreamData)]);
+        errors := Format('ERR 20191126-1 Op index not available (%d/%d) OpReference:%s size:%d',[i,High(nfpboarr),IntToHex(nfpboarr[i].opReference,8),Length(nfpboarr[i].opStreamData)]);
+        TLog.NewLog(lterror,ClassName,errors);
         Exit;
       end else begin
-        if Not operationsComp.AddOperation(False,auxOp,errors) then Exit;
+        if Not operationsComp.AddOperation(False,auxOp,errors) then begin
+          TLog.NewLog(lterror,ClassName,Format('ERR 20191126-2 Invalid operation %d/%d Err:%s Operation:%s',[i,High(nfpboarr),errors,auxOp.ToString]));
+          Exit;
+        end;
         auxOp.Free;
       end;
     end;
@@ -3823,7 +3835,7 @@ begin
   errors := '';
   DoDisconnect := true;
   try
-    if HeaderData.header_type<>ntp_autosend then begin
+    if AHeaderData.header_type<>ntp_autosend then begin
       errors := 'Not autosend';
       exit;
     end;
@@ -3879,7 +3891,7 @@ begin
     end;
   finally
     if DoDisconnect then begin
-      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(HeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
+      DisconnectInvalidClient(false,errors+' > '+TNetData.HeaderDataToText(AHeaderData)+' BuffSize: '+inttostr(DataBuffer.Size));
     end;
   end;
 end;

+ 1 - 4
src/core/UNode.pas

@@ -319,10 +319,7 @@ begin
               inc(j);
             end;
           end;
-          if j>0 then begin
-            TLog.NewLog(ltInfo,ClassName,'Buffer Sent operations: Deleted '+IntToStr(j)+' old operations');
-          end;
-          TLog.NewLog(ltdebug,ClassName,'Buffer Sent operations: '+IntToStr(FSentOperations.Count));
+          TLog.NewLog(ltdebug,ClassName,'Buffer Sent operations: '+IntToStr(FSentOperations.Count)+' Deleted old operations: '+IntToStr(j));
           // Notify to clients
           {$IFnDEF TESTING_NO_POW_CHECK}
           if FBroadcastData then begin

+ 7 - 1
src/core/UOpTransaction.pas

@@ -441,6 +441,7 @@ begin
   if (b AND $04)=$04 then FData.changes_type:=FData.changes_type + [account_type];
   if (b AND $08)=$08 then FData.changes_type:=FData.changes_type + [account_data];
   // Check
+  if (FProtocolVersion<CT_PROTOCOL_5) and ((b AND $F8)<>0) then Exit;
   if (b AND $F0)<>0 then Exit;
   if TStreamOp.ReadAccountKey(Stream,FData.new_accountkey)<0 then Exit;
   if TStreamOp.ReadAnsiString(Stream,FData.new_name)<0 then Exit;
@@ -607,7 +608,12 @@ begin
     account_target.account_type := FData.new_type;
   end;
   If (account_data in FData.changes_type) then begin
-    account_target.account_data := FData.new_data;
+    if LSafeboxCurrentProtocol>=CT_PROTOCOL_5 then begin
+      account_target.account_data := FData.new_data
+    end else begin
+      errors := 'Account Data not available until protocol 5';
+      Exit;
+    end;
   end;
   Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
          GetOpID,

+ 16 - 10
src/core/UPCOperationsSignatureValidator.pas

@@ -101,16 +101,19 @@ begin
 end;
 
 function TPCOperationsSignatureValidator.GetNextOperation(AValidatorThread : TPCOperationsSignatureValidatorThread) : TPCOperation;
-var LIndex : Integer;
+var LOp : TPCOperation;
 begin
   FLock.Acquire;
   try
-    // Search new
-    LIndex := FLastIndexOperations + 1; // Move to next
-    if (LIndex<FOperationsList.Count) then begin
-      Result := FOperationsList[LIndex];
-      FLastIndexOperations := Lindex;
-    end else Result := Nil;
+    Result := Nil;
+    // Search next Operation without valid signature
+    While (Result=Nil) do begin
+      Inc(FLastIndexOperations);
+      if (FLastIndexOperations<FOperationsList.Count) then begin
+        LOp := FOperationsList[FLastIndexOperations];
+        if Not LOp.HasValidSignature then Result := LOp;
+      end else Break;
+    end;
   finally
     FLock.Release;
   end;
@@ -127,7 +130,7 @@ begin
   if _Cpus<=0 then begin
     _Cpus := TCPUTool.GetLogicalCPUCount;
   end;
-  if _Cpus<=1 then Exit;
+  if (_Cpus<=1) or (APCOperationsList.Count < (_Cpus*10)) then Exit; // Minimum 2 CPU's and more than 10 ops per CPU
 
     LTC := TPlatform.GetTickCount;
     LMultiThreadValidator := TPCOperationsSignatureValidator.Create(ASafeBoxTransaction,AProgressNotify);
@@ -165,6 +168,9 @@ begin
       Inc(LGlobalOperationsCount, APCOperationsCompList[i].Count );
       APCOperationsCompList[i].OperationsHashTree.GetOperationsList(LList,True);
     end;
+
+    if (LList.Count < (_Cpus*10)) then Exit; // Minimum 10 operations per CPU
+
     LTC := TPlatform.GetTickCount;
     LMultiThreadValidator := TPCOperationsSignatureValidator.Create(ASafeBoxTransaction,AProgressNotify);
     try
@@ -197,7 +203,7 @@ begin
     _Cpus := TCPUTool.GetLogicalCPUCount;
   end;
   if _Cpus<=1 then Exit;
-  if AOperationsHashTree.OperationsCount<_Cpus then Exit;   // If less than cpus, no need for multithreading...
+  if AOperationsHashTree.OperationsCount<(_Cpus*10) then Exit;   // Minimum 10 operations per CPU
 
   LGlobalOperationsCount := AOperationsHashTree.OperationsCount;
   LTC := TPlatform.GetTickCount;
@@ -206,7 +212,7 @@ begin
     LList := TList<TPCOperation>.Create;
     Try
       AOperationsHashTree.GetOperationsList(Llist,True);
-      if LList.Count<_Cpus then Exit; // No need for multithreading...
+      if LList.Count<(_Cpus*10) then Exit; // No need for multithreading...
 
       LValidatedTotal := LMultiThreadValidator.Validate(LList);
       LValidatedOk := LMultiThreadValidator.FValidatedOkCount;

+ 4 - 0
src/core/UTxMultiOperation.pas

@@ -694,6 +694,10 @@ begin
     end;
     // Account Data protection: (PIP-0024)
     if (account_data in chi.Changes_type) then begin
+      if (LSafeboxCurrentProtocol<CT_PROTOCOL_5) then begin
+        errors := 'Account Data not available until protocol 5';
+        Exit;
+      end;
       if Length(chi.New_Data)>CT_MaxAccountDataSize then begin
         errors := 'New data length ('+IntToStr(Length(chi.New_data))+') > '+IntToStr(CT_MaxAccountDataSize);
         Exit;

+ 1 - 7
src/gui-classic/UFRMMemoText.dfm

@@ -24,8 +24,6 @@ object FRMMemoText: TFRMMemoText
     Align = alBottom
     BevelOuter = bvNone
     TabOrder = 0
-    ExplicitTop = 245
-    ExplicitWidth = 619
     DesignSize = (
       745
       55)
@@ -35,11 +33,9 @@ object FRMMemoText: TFRMMemoText
       Width = 116
       Height = 31
       Anchors = [akTop, akRight]
-      DoubleBuffered = True
       Kind = bkCancel
-      ParentDoubleBuffered = False
+      NumGlyphs = 2
       TabOrder = 0
-      ExplicitLeft = 494
     end
   end
   object Memo: TMemo
@@ -60,7 +56,5 @@ object FRMMemoText: TFRMMemoText
     ScrollBars = ssBoth
     TabOrder = 1
     WordWrap = False
-    ExplicitWidth = 619
-    ExplicitHeight = 245
   end
 end

+ 64 - 0
src/gui-classic/UFRMMemoText.pas

@@ -18,12 +18,22 @@ type
     bbCancel: TBitBtn;
     procedure FormCreate(Sender: TObject);
   private
+    FAllowInput: Boolean;
+    FbbOk : TBitBtn;
+    procedure SetAllowInput(const Value: Boolean);
+    procedure SetDataText(const Value: String);
+    function GetDataText: String;
     { Private declarations }
   public
     { Public declarations }
     Procedure InitData(const Title : String; const text : String);
+    property AllowInput : Boolean read FAllowInput write SetAllowInput;
+    property DataText : String read GetDataText write SetDataText;
   end;
 
+
+function InputMemoQuery(const ATitle : String; AAllowMultiline : Boolean; var AText : String) : Boolean;
+
 implementation
 
 {$IFnDEF FPC}
@@ -32,9 +42,37 @@ implementation
   {$R *.lfm}
 {$ENDIF}
 
+function InputMemoQuery(const ATitle : String; AAllowMultiline : Boolean; var AText : String) : Boolean;
+Var LFRM : TFRMMemoText;
+begin
+  LFRM := TFRMMemoText.Create(Nil);
+  try
+    LFRM.InitData(ATitle,AText);
+    LFRM.AllowInput := True;
+    if AAllowMultiline then begin
+      LFRM.Memo.ScrollBars := ssBoth;
+    end else begin
+      LFRM.Memo.ScrollBars := ssNone;
+      LFRM.Memo.WordWrap := True;
+    end;
+    if LFRM.ShowModal=MrOk then begin
+      AText := LFRM.DataText;
+      Result := True;
+    end else Result := False;
+  finally
+    LFRM.Free;
+  end;
+end;
+
 procedure TFRMMemoText.FormCreate(Sender: TObject);
 begin
   Memo.Clear;
+  FbbOk := Nil;
+end;
+
+function TFRMMemoText.GetDataText: String;
+begin
+  Result := Memo.Lines.Text;
 end;
 
 procedure TFRMMemoText.InitData(const Title, text: String);
@@ -43,4 +81,30 @@ begin
   Memo.Lines.Text := text;
 end;
 
+procedure TFRMMemoText.SetAllowInput(const Value: Boolean);
+begin
+  FAllowInput := Value;
+  Memo.ReadOnly := Not FAllowInput;
+  if FAllowInput then begin
+    if Not Assigned(FbbOk) then begin
+      FbbOk := TBitBtn.Create(Self);
+      FbbOk.Parent := bbCancel.Parent;
+      FbbOk.Anchors := bbCancel.Anchors;
+      FbbOk.Left := bbCancel.Left - bbCancel.Width - 10;
+      FbbOk.Top := bbCancel.Top;
+      FbbOk.Width := bbCancel.Width;
+      FbbOk.Height := bbCancel.Height;
+      FbbOk.Kind := bkOK;
+      FbbOk.ModalResult := MrOk;
+      FbbOk.Caption := 'Ok';
+    end;
+  end else FreeAndNil(FbbOk);
+
+end;
+
+procedure TFRMMemoText.SetDataText(const Value: String);
+begin
+  Memo.Lines.Text := Value;
+end;
+
 end.

+ 1 - 1
src/gui-classic/UFRMOperation.pas

@@ -999,7 +999,7 @@ begin
         errors := Format('Data size (%d) greater than %d',[Length(ANewData),CT_MaxAccountDataSize]);
         Exit;
       end;
-    end;
+    end else AChangeData:=False;
     If (SenderAccounts.Count=1) And (TBaseType.Equals(newName,TargetAccount.name)) And (newType=TargetAccount.account_type)
       And (TBaseType.Equals(ANewData,TargetAccount.account_data)) then begin
       errors := 'No changes on fields. Not changed';

+ 7 - 8
src/gui-classic/UFRMOperationsExplorer.pas

@@ -132,7 +132,7 @@ Uses
 {$IFDEF TESTNET}
    UFRMRandomOperations,
 {$ENDIF}
-   UFRMRPCCalls;
+   UFRMRPCCalls, UFRMMemoText;
 
 
 { TFRMOperationsExplorer }
@@ -530,22 +530,21 @@ end;
 procedure TFRMOperationsExplorer.MiImportOperationsFromTxtClick(Sender: TObject);
 Var i : Integer;
   raw : TRawBytes;
-  aux : AnsiString;
   auxS : String;
   errors : String;
   opht : TOperationsHashTree;
   ms : TMemoryStream;
 begin
-  aux := '';
-  If Not InputQuery(Caption,'Paste a RAW hexadecimal operations:',auxS) then exit;
-  aux := auxS;
-  If Not TCrypto.IsHexString(aux) then Raise Exception.Create('Invalid hexadecimal RAW');
-  raw := TCrypto.HexaToRaw(aux);
+  auxS := '';
+  if Not InputMemoQuery('Paste a RAW hexadecimal operations:',False,auxS) then Exit;
+
+  if Not TCrypto.HexaToRaw(auxS, raw) then Raise Exception.Create('Invalid hexadecimal RAW');
+
   If Length(raw)=0 then Exit;
   ms := TMemoryStream.Create;
   opht := TOperationsHashTree.Create;
   Try
-    ms.Write(raw[1],Length(raw));
+    ms.Write(raw[0],Length(raw));
     ms.Position:=0;
     If Not opht.LoadOperationsHashTreeFromStream(ms,false,TNode.Node.Bank.SafeBox.CurrentProtocol,TNode.Node.Bank.SafeBox.CurrentProtocol,Nil,errors) then Raise Exception.Create(errors);
     For i:=0 to opht.OperationsCount-1 do begin