Browse Source

Merge pull request #15 from PascalCoinDev/master

Testnet 5 Beta 4.1
Pascal Coin 5 years ago
parent
commit
faec4849ce

+ 14 - 14
src/core/UAccounts.pas

@@ -139,10 +139,10 @@ Type
   TAccountComp = Class
   private
   public
-    Class Function IsValidAccountKey(const AAccountInfo: TAccountKey; var errors : String): Boolean;
+    Class Function IsValidAccountKey(const AAccountInfo: TAccountKey; ACurrentProtocol : Word; var errors : String): Boolean;
     Class function IsNullAccountKey(const AAccountInfo : TAccountKey) : Boolean;
     Class function IsValidNewAccountKey(const AAccountInfo : TAccountInfo; const ANewKey : TAccountKey; AProtocolVersion : Integer) : Boolean;
-    Class Function IsValidAccountInfo(const AAccountInfo: TAccountInfo; var errors : String): Boolean;
+    Class Function IsValidAccountInfo(const AAccountInfo: TAccountInfo; ACurrentProtocol : Word; var errors : String): Boolean;
     Class Function IsValidAccountInfoHashLockKey(const AAccountInfo : TAccountInfo; const AKey : TRawBytes) : Boolean;
     Class Function IsValidHashLockKey(const AKey : TRawBytes; out AError : String) : Boolean;
     Class Function CalculateHashLock(const AKey : TRawBytes) : T32Bytes;
@@ -1649,7 +1649,7 @@ begin
   end;
 
   if (AAccount.accountInfo.state in [as_ForSale, as_ForAtomicAccountSwap]) then begin
-    if NOT IsValidAccountKey(AAccount.accountInfo.new_publicKey,errors) then
+    if NOT IsValidAccountKey(AAccount.accountInfo.new_publicKey,ACurrentProtocol,errors) then
       exit;
   end;
 
@@ -1743,7 +1743,7 @@ begin
       Account.account_data.ToHexaString,Account.account_seal.ToHexaString ]);
 end;
 
-class function TAccountComp.IsValidAccountInfo(const AAccountInfo: TAccountInfo; var errors: String): Boolean;
+class function TAccountComp.IsValidAccountInfo(const AAccountInfo: TAccountInfo; ACurrentProtocol : Word; var errors: String): Boolean;
 Var s : String;
 begin
   errors := '';
@@ -1753,38 +1753,38 @@ begin
         Result := false;
       end;
     as_Normal: begin
-        Result := IsValidAccountKey(AAccountInfo.accountKey,errors);
+        Result := IsValidAccountKey(AAccountInfo.accountKey,ACurrentProtocol,errors);
       end;
     as_ForSale: begin
-        Result := IsValidAccountKey(AAccountInfo.accountKey,errors);
+        Result := IsValidAccountKey(AAccountInfo.accountKey,ACurrentProtocol,errors);
         if (Result) And (IsAccountForPrivateSale(AAccountInfo)) then begin
-          if Not IsValidAccountKey(AAccountInfo.new_publicKey,s) then begin
+          if Not IsValidAccountKey(AAccountInfo.new_publicKey,ACurrentProtocol,s) then begin
             Result := False;
             errors := 'Invalid new_publicKey: '+s;
           end;
         end;
       end;
     as_ForAtomicAccountSwap: begin
-        Result := IsValidAccountKey(AAccountInfo.accountKey,errors);
-        if (Result) And (Not IsValidAccountKey(AAccountInfo.new_publicKey,s)) then begin
+        Result := IsValidAccountKey(AAccountInfo.accountKey,ACurrentProtocol,errors);
+        if (Result) And (Not IsValidAccountKey(AAccountInfo.new_publicKey,ACurrentProtocol,s)) then begin
           Result := False;
           errors := 'Invalid AccountSwap.new_publicKey: '+s;
         end;
       end;
     as_ForAtomicCoinSwap: begin
-      Result := IsValidAccountKey(AAccountInfo.accountKey,errors);
+      Result := IsValidAccountKey(AAccountInfo.accountKey,ACurrentProtocol,errors);
     end
   else
     raise Exception.Create('DEVELOP ERROR 20170214-3');
   end;
 end;
 
-class function TAccountComp.IsValidAccountKey(const AAccountInfo: TAccountKey; var errors : String): Boolean;
+class function TAccountComp.IsValidAccountKey(const AAccountInfo: TAccountKey;  ACurrentProtocol : Word; var errors : String): Boolean;
 begin
   errors := '';
   case AAccountInfo.EC_OpenSSL_NID of
     CT_NID_secp256k1,CT_NID_secp384r1,CT_NID_sect283k1,CT_NID_secp521r1 : begin
-      Result := TECPrivateKey.IsValidPublicKey(AAccountInfo,errors);
+      Result := TECPrivateKey.IsValidPublicKey(AAccountInfo,ACurrentProtocol,errors);
     end;
   else
     errors := Format('Invalid AccountKey type:%d (Unknown type) - Length x:%d y:%d',[AAccountInfo.EC_OpenSSL_NID,length(AAccountInfo.x),length(AAccountInfo.y)]);
@@ -3314,7 +3314,7 @@ begin
             FOrderedByName.Add(LBlock.accounts[iacc].name,LBlock.accounts[iacc].account);
           end;
           If checkAll then begin
-            if not TAccountComp.IsValidAccountInfo(LBlock.accounts[iacc].accountInfo,aux_errors) then begin
+            if not TAccountComp.IsValidAccountInfo(LBlock.accounts[iacc].accountInfo,FCurrentProtocol,aux_errors) then begin
               errors := errors + ' > '+aux_errors;
               Exit;
             end;
@@ -4014,7 +4014,7 @@ begin
   Result := False;
   errors := '';
   // Check Account key
-  if Not TAccountComp.IsValidAccountKey(newOperationBlock.account_key,errors) then begin
+  if Not TAccountComp.IsValidAccountKey(newOperationBlock.account_key,newOperationBlock.protocol_version,errors) then begin
     exit;
   end;
   // reward

+ 81 - 47
src/core/UBlockChain.pas

@@ -353,8 +353,8 @@ Type
     Procedure CopyFromHashTree(Sender : TOperationsHashTree);
     Property TotalAmount : Int64 read FTotalAmount;
     Property TotalFee : Int64 read FTotalFee;
-    function SaveOperationsHashTreeToStream(Stream: TStream; SaveToStorage : Boolean): Boolean;
-    function LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors : String): Boolean;
+    function SaveOperationsHashTreeToStream(AStream: TStream; ASaveToStorage : Boolean): Boolean;
+    function LoadOperationsHashTreeFromStream(AStream: TStream; ALoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadFromStorageVersion : Word; APreviousUpdatedBlocks : TAccountPreviousBlockInfo; var AErrors : String): Boolean;
     function IndexOfOperation(op : TPCOperation) : Integer;
     function CountOperationsBySameSignerWithoutFee(account_number : Cardinal) : Integer;
     Procedure Delete(index : Integer);
@@ -1221,8 +1221,8 @@ Begin
         errors := 'Bank blockcount<>OperationBlock.Block';
         exit;
       end;
-      if OperationBlock.protocol_version <> op.ProtocolVersion then begin
-        errors := Format('Operation protocol:%d <> current protocol:%d on %s',[op.ProtocolVersion,OperationBlock.protocol_version, op.ToString]);
+      if OperationBlock.protocol_version < op.ProtocolVersion then begin
+        errors := Format('Operation protocol:%d > current protocol:%d on %s',[op.ProtocolVersion,OperationBlock.protocol_version, op.ToString]);
         Tlog.NewLog(lterror,ClassName,errors);
         Exit;
       end;
@@ -2616,60 +2616,85 @@ begin
   Index := L;
 end;
 
-function TOperationsHashTree.LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors: String): Boolean;
+function TOperationsHashTree.LoadOperationsHashTreeFromStream(AStream: TStream; ALoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadFromStorageVersion : Word; APreviousUpdatedBlocks : TAccountPreviousBlockInfo; var AErrors : String): Boolean;
 Var c, i: Cardinal;
-  OpType: Cardinal;
-  bcop: TPCOperation;
+  LOpTypeWord : Word;
+  LOpProtocolVersion : Word;
+  LOperation: TPCOperation;
   j: Integer;
-  OpClass: TPCOperationClass;
-  lastNE : TNotifyEvent;
+  LOpClass: TPCOperationClass;
+  LLastNE : TNotifyEvent;
 begin
   Result := false;
   //
-  If Stream.Read(c, 4)<4 then begin
-    errors := 'Cannot read operations count';
-    exit;
+  If AStream.Read(c, 4)<4 then begin
+    AErrors := 'Cannot read operations count';
+    Exit;
   end;
-  lastNE := FOnChanged;
+  LLastNE := FOnChanged;
   FOnChanged:=Nil;
   try
     // c = operations count
     for i := 1 to c do begin
-      if Stream.Size - Stream.Position < 4 then begin
-        errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c);
-        exit;
+      if AStream.Size - AStream.Position < 4 then begin
+        AErrors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c);
+        Exit;
+      end;
+      // New proposal for V5:
+      // Previously (V4 and below) didn't saved which protocol was used for an
+      // operation. That didn't allowed to save info based on protocol version
+      // On V4 the "OpType" was saved using a 4 bytes (uInt32) little endian
+      // but OpType is always a value <=255 so only 1 byte is needed.
+      // On V5 the first 2 bytes will be the "OpType" and the other 2 bytes
+      // will be used to store Protocol (5) or (0 = Protocol 4) that will
+      // allow fully compatiblity with third party clients that save operations
+      // On V4:
+      // AStream.Read(LOpTypeCardinal, 4);
+      // On V5:
+      AStream.Read(LOpTypeWord, 2);
+      AStream.Read(LOpProtocolVersion, 2);
+      if LOpProtocolVersion=0 then begin
+        // For backward compatibility (not saved protocol version)
+        // will assume that is a version from V1..V4
+        if (ASetOperationsToProtocolVersion <= CT_PROTOCOL_4) {$IFDEF TESTNET}or (ALoadingFromStorage){$ENDIF} then
+          LOpProtocolVersion := ASetOperationsToProtocolVersion
+        else LOpProtocolVersion := CT_PROTOCOL_4;
+      end;
+      if (LOpProtocolVersion<1) or (LOpProtocolVersion>ASetOperationsToProtocolVersion) then begin
+        AErrors := 'Invalid protocol version '+IntToStr(LOpProtocolVersion)+' ('+IntToStr(ASetOperationsToProtocolVersion)+') found at ' + inttostr(i) + '/' + inttostr(c) + ' with optype:' + InttoHex(LOpTypeWord, 2);
+        Exit;
       end;
-      Stream.Read(OpType, 4);
-      j := TPCOperationsComp.IndexOfOperationClassByOpType(OpType);
+
+      j := TPCOperationsComp.IndexOfOperationClassByOpType(LOpTypeWord);
       if j >= 0 then
-        OpClass := _OperationsClass[j]
+        LOpClass := _OperationsClass[j]
       else
-        OpClass := Nil;
-      if Not Assigned(OpClass) then begin
-        errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c) + ' optype not valid:' + InttoHex(OpType, 4);
-        exit;
+        LOpClass := Nil;
+      if Not Assigned(LOpClass) then begin
+        AErrors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c) + ' optype not valid:' + InttoHex(LOpTypeWord, 2);
+        Exit;
       end;
-      bcop := OpClass.Create(ASetOperationsToProtocolVersion);
+      LOperation := LOpClass.Create(LOpProtocolVersion);
       Try
-        if LoadingFromStorage then begin
-          If not bcop.LoadFromStorage(Stream,ALoadVersion,PreviousUpdatedBlocks) then begin
-            errors := 'Invalid operation load from storage ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
-            exit;
+        if ALoadingFromStorage then begin
+          If not LOperation.LoadFromStorage(AStream,ALoadFromStorageVersion,APreviousUpdatedBlocks) then begin
+            AErrors := 'Invalid operation load from storage ' + inttostr(i) + '/' + inttostr(c)+' Class:'+LOpClass.ClassName;
+            Exit;
           end;
-        end else if not bcop.LoadFromNettransfer(Stream) then begin
-          errors := 'Invalid operation load from stream ' + inttostr(i) + '/' + inttostr(c)+' Class:'+OpClass.ClassName;
-          exit;
+        end else if not LOperation.LoadFromNettransfer(AStream) then begin
+          AErrors := 'Invalid operation load from stream ' + inttostr(i) + '/' + inttostr(c)+' Class:'+LOpClass.ClassName;
+          Exit;
         end;
-        AddOperationToHashTree(bcop);
+        AddOperationToHashTree(LOperation);
       Finally
-        FreeAndNil(bcop);
+        FreeAndNil(LOperation);
       end;
     end;
   finally
-    FOnChanged := lastNE;
+    FOnChanged := LLastNE;
   end;
   If Assigned(FOnChanged) then FOnChanged(Self);
-  errors := '';
+  AErrors := '';
   Result := true;
 end;
 
@@ -2744,22 +2769,31 @@ begin
   End;
 end;
 
-function TOperationsHashTree.SaveOperationsHashTreeToStream(Stream: TStream; SaveToStorage: Boolean): Boolean;
-Var c, i, OpType: Cardinal;
-  bcop: TPCOperation;
-  l : TList<Pointer>;
+function TOperationsHashTree.SaveOperationsHashTreeToStream(AStream: TStream; ASaveToStorage: Boolean): Boolean;
+Var c, i : Cardinal;
+  LOpTypeWord : Word;
+  LOpProtocol : Word;
+  LOperation: TPCOperation;
+  Llist : TList<Pointer>;
 begin
-  l := FHashTreeOperations.LockList;
+  LList := FHashTreeOperations.LockList;
   Try
-    c := l.Count;
-    Stream.Write(c, 4);
+    c := Llist.Count;
+    AStream.Write(c, 4);
     // c = operations count
     for i := 1 to c do begin
-      bcop := GetOperation(i - 1);
-      OpType := bcop.OpType;
-      Stream.write(OpType, 4);
-      if SaveToStorage then bcop.SaveToStorage(Stream)
-      else bcop.SaveToNettransfer(Stream);
+      LOperation := GetOperation(i - 1);
+      LOpTypeWord := LOperation.OpType;
+      if LOperation.ProtocolVersion >= CT_PROTOCOL_5 then
+        LOpProtocol := LOperation.ProtocolVersion
+      else LOpProtocol := 0;
+      // On V5 will save LOpProtocol when LOperation.ProtocolVersion >= V5
+      // On V4 LOpProtocol was not saved (always 0): AStream.write(OpType, 4);
+      AStream.Write(LOpTypeWord,2);
+      AStream.Write(LOpProtocol,2);
+
+      if ASaveToStorage then LOperation.SaveToStorage(AStream)
+      else LOperation.SaveToNettransfer(AStream);
     end;
     Result := true;
   Finally

+ 2 - 2
src/core/UConst.pas

@@ -132,7 +132,7 @@ Const
   CT_NetProtocol_Version: Word = $0009; // Version 4.0.2 Will allow only net protocol 9
   // IMPORTANT NOTE!!!
   // NetProtocol_Available MUST BE always >= NetProtocol_version
-  CT_NetProtocol_Available: Word = {$IFDEF PRODUCTION}$0009{$ELSE}$0009{$ENDIF};  // Version 4.0.0 will start accepting protocol 8 but 4.0.1 will accept 9 due to 4.0.0 bug
+  CT_NetProtocol_Available: Word = {$IFDEF PRODUCTION}$0009{$ELSE}$000A{$ENDIF};  //
 
   CT_MaxAccountOperationsPerBlockWithoutFee = 1;
 
@@ -195,7 +195,7 @@ Const
   CT_OpSubtype_Data_Signer                = 103;
   CT_OpSubtype_Data_Receiver              = 104;
 
-  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'4.1'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.Beta.4'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'4.1'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.Beta.4.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};

+ 9 - 5
src/core/UCrypto.pas

@@ -61,8 +61,8 @@ Type
     Property PublicKey : TECDSA_Public read GetPublicKey;
     Function SetPrivateKeyFromHexa(AEC_OpenSSL_NID : Word; const hexa : String) : Boolean;
     Property EC_OpenSSL_NID : Word Read GetEC_OpenSSL_NID;
-    class function IsValidPublicKey(PubKey : TECDSA_Public; var errors : String) : Boolean; overload;
-    class function IsValidPublicKey(PubKey : TECDSA_Public) : Boolean; overload;
+    class function IsValidPublicKey(PubKey : TECDSA_Public; ACurrentProtocol : Word; var errors : String) : Boolean; overload;
+    class function IsValidPublicKey(PubKey : TECDSA_Public; ACurrentProtocol : Word) : Boolean; overload;
     // Exports a Private key in a RAW saving 2 bytes for EC_OpenSSL_NID, 2 bytes for private key length and private key as a RAW
     Function ExportToRaw : TRawBytes;
     // Imports a Private key saved with "ExportToRaw" format
@@ -377,7 +377,7 @@ begin
   End;
 end;
 
-class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public; var errors : String): Boolean;
+class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public; ACurrentProtocol : Word; var errors : String): Boolean;
 {$IFDEF Use_OpenSSL}
 Var BNx,BNy : PBIGNUM;
   ECG : PEC_GROUP;
@@ -390,6 +390,7 @@ begin
     errors := 'Invalid NID '+IntToStr(PubKey.EC_OpenSSL_NID);
     Exit(False);
   end;
+  Result := (Length(PubKey.x)<100) And (Length(PubKey.y)<100);
 {$IFDEF Use_OpenSSL}
   BNx := BN_bin2bn(PAnsiChar(PubKey.x),length(PubKey.x),nil);
   if Not Assigned(BNx) then Exit;
@@ -397,6 +398,9 @@ begin
     BNy := BN_bin2bn(PAnsiChar(PubKey.y),length(PubKey.y),nil);
     if Not Assigned(BNy) then Exit;
     try
+      if ACurrentProtocol>=CT_PROTOCOL_5 then begin
+        Exit;
+      end;
       ECG := EC_GROUP_new_by_curve_name(PubKey.EC_OpenSSL_NID);
       if Not Assigned(ECG) then Exit;
       try
@@ -430,10 +434,10 @@ begin
 {$ENDIF}
 end;
 
-class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public): Boolean;
+class function TECPrivateKey.IsValidPublicKey(PubKey: TECDSA_Public; ACurrentProtocol : Word): Boolean;
 var Ltmp : String;
 begin
-  Result := IsValidPublicKey(PubKey,Ltmp);
+  Result := IsValidPublicKey(PubKey,ACurrentProtocol,Ltmp);
 end;
 
 procedure TECPrivateKey.SetPrivateKeyInfo(const Value: TECPrivateKeyInfo);

+ 40 - 28
src/core/UNetProtocol.pas

@@ -2667,23 +2667,29 @@ begin
       errors := 'Not autosend';
       exit;
     end;
-    if DataBuffer.Size<4 then begin
-      errors := 'Invalid databuffer size';
-      exit;
-    end;
-    DataBuffer.Read(c,4);
-    for i := 1 to c do begin
-      errors := 'Invalid operation '+inttostr(i)+'/'+inttostr(c);
-      if not DataBuffer.Read(optype,1)=1 then exit;
-      opclass := TPCOperationsComp.GetOperationClassByOpType(optype);
-      if Not Assigned(opclass) then exit;
-      op := opclass.Create(TNode.Node.Bank.SafeBox.CurrentProtocol);
-      Try
-        op.LoadFromNettransfer(DataBuffer);
-        operations.AddOperationToHashTree(op);
-      Finally
-        op.Free;
-      End;
+    if (NetProtocolVersion.protocol_available>=CT_NetProtocol_Available) then begin
+      if Not operations.LoadOperationsHashTreeFromStream(DataBuffer,False,TNode.Node.Bank.SafeBox.CurrentProtocol,TNode.Node.Bank.SafeBox.CurrentProtocol,Nil,errors) then Exit;
+    end else begin
+      // TODO:
+      // After V5 Activation all this code can be deleted, not used anymore
+      if DataBuffer.Size<4 then begin
+        errors := 'Invalid databuffer size';
+        exit;
+      end;
+      DataBuffer.Read(c,4);
+      for i := 1 to c do begin
+        errors := 'Invalid operation '+inttostr(i)+'/'+inttostr(c);
+        if not DataBuffer.Read(optype,1)=1 then exit;
+        opclass := TPCOperationsComp.GetOperationClassByOpType(optype);
+        if Not Assigned(opclass) then exit;
+        op := opclass.Create(TNode.Node.Bank.SafeBox.CurrentProtocol);
+        Try
+          op.LoadFromNettransfer(DataBuffer);
+          operations.AddOperationToHashTree(op);
+        Finally
+          op.Free;
+        End;
+      end;
     end;
     DoDisconnect := false;
   finally
@@ -3511,7 +3517,7 @@ Begin
       exit;
     end;
     FClientPublicKey := TAccountComp.RawString2Accountkey(RawAccountKey);
-    If Not TAccountComp.IsValidAccountKey(FClientPublicKey,errors) then begin
+    If Not TAccountComp.IsValidAccountKey(FClientPublicKey,CT_BUILD_PROTOCOL,errors) then begin
       DisconnectInvalidClient(false,'Invalid Public key: '+TNetData.HeaderDataToText(HeaderData)+' errors: '+errors);
       exit;
     end;
@@ -4279,7 +4285,7 @@ begin
 end;
 
 function TNetConnection.Send_AddOperations(Operations : TOperationsHashTree) : Boolean;
-Var data : TMemoryStream;
+Var LStream : TStream;
   c1, request_id : Cardinal;
   i, nOpsToSend : Integer;
   optype : Byte;
@@ -4304,20 +4310,26 @@ begin
       end;
       if FBufferToSendOperations.OperationsCount>0 then begin
         TLog.NewLog(ltdebug,ClassName,Format('Sending %d Operations to %s (inProc:%d, Received:%d)',[FBufferToSendOperations.OperationsCount,ClientRemoteAddr,nOpsToSend,FBufferReceivedOperationsHash.Count]));
-        data := TMemoryStream.Create;
+        LStream := TMemoryStream.Create;
         try
           request_id := TNetData.NetData.NewRequestId;
-          c1 := FBufferToSendOperations.OperationsCount;
-          data.Write(c1,4);
-          for i := 0 to FBufferToSendOperations.OperationsCount-1 do begin
-            optype := FBufferToSendOperations.GetOperation(i).OpType;
-            data.Write(optype,1);
-            FBufferToSendOperations.GetOperation(i).SaveToNettransfer(data);
+          if (NetProtocolVersion.protocol_available>=CT_NetProtocol_Available) then begin
+            FBufferToSendOperations.SaveOperationsHashTreeToStream(LStream,False)
+          end else begin
+            // TODO:
+            // After V5 Activation all this code can be deleted, not used anymore
+            c1 := FBufferToSendOperations.OperationsCount;
+            LStream.Write(c1,4);
+            for i := 0 to FBufferToSendOperations.OperationsCount-1 do begin
+              optype := FBufferToSendOperations.GetOperation(i).OpType;
+              LStream.Write(optype,1);
+              FBufferToSendOperations.GetOperation(i).SaveToNettransfer(LStream);
+            end;
           end;
-          Send(ntp_autosend,CT_NetOp_AddOperations,0,request_id,data);
+          Send(ntp_autosend,CT_NetOp_AddOperations,0,request_id,LStream);
           FBufferToSendOperations.ClearHastThree;
         finally
-          data.Free;
+          LStream.Free;
         end;
       end{$IFDEF HIGHLOG} else TLog.NewLog(ltdebug,ClassName,Format('Not sending any operations to %s (inProc:%d, Received:%d, Sent:%d)',[ClientRemoteAddr,nOpsToSend,FBufferReceivedOperationsHash.Count,FBufferToSendOperations.OperationsCount])){$ENDIF};
     finally

+ 1 - 1
src/core/UNode.pas

@@ -876,7 +876,7 @@ end;
 
 class function TNode.NodeVersion: String;
 begin
-  Result := CT_ClientAppVersion{$IFDEF LINUX}+'L'{$ELSE}+'W'{$ENDIF}{$IFDEF FPC}{$IFDEF LCL}+'l'{$ELSE}+'f'{$ENDIF}{$ENDIF}{$IFDEF FPC}{$IFDEF CPU32}+'32b'{$ELSE}+'64b'{$ENDIF}{$ELSE}{$IFDEF CPU32BITS}+'32b'{$ELSE}+'64b'{$ENDIF}{$ENDIF}+'of';
+  Result := CT_ClientAppVersion{$IFDEF LINUX}+'L'{$ELSE}+'W'{$ENDIF}{$IFDEF FPC}{$IFDEF LCL}+'l'{$ELSE}+'f'{$ENDIF}{$ENDIF}{$IFDEF FPC}{$IFDEF CPU32}+'32b'{$ELSE}+'64b'{$ENDIF}{$ELSE}{$IFDEF CPU32BITS}+'32b'{$ELSE}+'64b'{$ENDIF}{$ENDIF};
 end;
 
 procedure TNode.Notification(AComponent: TComponent; Operation: TOperation);

+ 43 - 35
src/core/UOpTransaction.pas

@@ -490,8 +490,10 @@ end;
 
 function TOpChangeAccountInfo.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
 Var account_signer, account_target : TAccount;
+  LSafeboxCurrentProtocol : Integer;
 begin
   Result := false;
+  LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
   if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
     errors := 'Invalid account number';
     Exit;
@@ -526,7 +528,7 @@ begin
   end;
   if (length(FData.payload.payload_raw)>CT_MaxPayloadSize) then begin
     errors := 'Invalid Payload size:'+inttostr(length(FData.payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
-    If (ProtocolVersion>=CT_PROTOCOL_2) then begin
+    If (LSafeboxCurrentProtocol>=CT_PROTOCOL_2) then begin
       Exit; // BUG from protocol 1
     end;
   end;
@@ -535,12 +537,12 @@ begin
     errors := 'Account signer is currently locked';
     exit;
   end;
-  if (ProtocolVersion<CT_PROTOCOL_2) then begin
+  if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
     errors := 'NOT ALLOWED ON PROTOCOL 1';
     exit;
   end;
   If (public_key in FData.changes_type) then begin
-    If Not TAccountComp.IsValidAccountKey( FData.new_accountkey, errors ) then begin
+    If Not TAccountComp.IsValidAccountKey( FData.new_accountkey, LSafeboxCurrentProtocol, errors) then begin
       exit;
     end;
   end;
@@ -803,13 +805,13 @@ Var s_new, t_new : Int64;
   LTotalAmount : Cardinal;
   LSender,LTarget,LSeller : TAccount;
   LRecipientSignable, LIsCoinSwap : Boolean;
-  LCurrentBlock, LCurrentProtocol : Integer;
+  LCurrentBlock, LSafeboxCurrentProtocol : Integer;
   LBuyAccountNewPubkey : TAccountKey;
 begin
   Result := false;
   AErrors := '';
   LCurrentBlock := ASafeBoxTransaction.FreezedSafeBox.BlocksCount;
-  LCurrentProtocol := ProtocolVersion;
+  LSafeboxCurrentProtocol := ASafeboxTransaction.FreezedSafeBox.CurrentProtocol;
 
   {$region 'Common Validation'}
 
@@ -835,7 +837,7 @@ begin
   end;
   if (length(FData.payload.payload_raw)>CT_MaxPayloadSize) then begin
     AErrors := 'Invalid Payload size:'+inttostr(length(FData.payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
-    If (LCurrentProtocol>=CT_PROTOCOL_2) then begin
+    If (LSafeboxCurrentProtocol>=CT_PROTOCOL_2) then begin
       Exit; // BUG from protocol 1
     end;
   end;
@@ -849,10 +851,10 @@ begin
   //  - TIME LOCK not expired
   LRecipientSignable :=
     ( FData.opTransactionStyle = buy_Account )
-    And (TAccountComp.IsOperationRecipientSignable(LSender, LTarget, LCurrentBlock, LCurrentProtocol));
+    And (TAccountComp.IsOperationRecipientSignable(LSender, LTarget, LCurrentBlock, LSafeboxCurrentProtocol));
 
   LIsCoinSwap := TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo)
-    And (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, LCurrentProtocol, FData.payload.payload_raw));
+    And (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, LSafeboxCurrentProtocol, FData.payload.payload_raw));
 
   if (FData.sender=FData.target) AND (NOT LRecipientSignable) then begin
     AErrors := Format('Sender=Target and Target is not recipient-signable. Account: %d',[FData.sender]);
@@ -908,7 +910,7 @@ begin
   // Is buy account ?
   if (FData.opTransactionStyle = buy_Account ) then begin
     {$region 'Buy Account Validation'}
-    if (LCurrentProtocol<CT_PROTOCOL_2) then begin
+    if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
       AErrors := 'Buy account is not allowed on Protocol 1';
       exit;
     end;
@@ -918,7 +920,7 @@ begin
       Exit;
     end;
 
-    if (LCurrentProtocol < CT_PROTOCOL_5) then begin
+    if (LSafeboxCurrentProtocol < CT_PROTOCOL_5) then begin
       if (TAccountComp.IsAccountForSwap(LTarget.accountInfo)) then begin
         AErrors := 'Atomic swaps are not allowed until Protocol 5';
         exit;
@@ -964,7 +966,7 @@ begin
       AErrors := Format('Signed price (%d) is not the same of account price (%d)',[FData.AccountPrice,LTarget.accountInfo.price]);
       exit;
     end;
-    if NOT TAccountComp.IsValidNewAccountKey(LTarget.accountInfo, FData.new_accountkey, LCurrentProtocol) then begin
+    if NOT TAccountComp.IsValidNewAccountKey(LTarget.accountInfo, FData.new_accountkey, LSafeboxCurrentProtocol) then begin
       AErrors := Format('Specified new public key for %d does not equal (or is not valid) the new public key stored in account: %s <> %s',
       [LTarget.account,
        TAccountComp.AccountKey2RawString(LTarget.accountInfo.new_publicKey).ToHexaString,
@@ -972,23 +974,23 @@ begin
       exit;
     end;
 
-    If Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,AErrors)) then exit; // BUG 20171511
+    If Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,LSafeboxCurrentProtocol,AErrors)) then exit;
     LBuyAccountNewPubkey := FData.new_accountkey;
     {$endregion}
   end else if // (is auto buy) OR (is transaction that can buy)
               (
                 (FData.opTransactionStyle in [transaction,transaction_with_auto_buy_account,transaction_with_auto_atomic_swap]) AND
-                (LCurrentProtocol >= CT_PROTOCOL_2) AND
-                (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, LCurrentProtocol, FData.payload.payload_raw)) AND
+                (LSafeboxCurrentProtocol >= CT_PROTOCOL_2) AND
+                (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, LSafeboxCurrentProtocol, FData.payload.payload_raw)) AND
                 ((LTarget.balance + FData.amount >= LTarget.accountInfo.price))
               )  then begin
     {$region 'Transaction Auto Buy Validation'}
-    if (LCurrentProtocol<CT_PROTOCOL_2) then begin
+    if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
       AErrors := 'Tx-Buy account is not allowed on Protocol 1';
       exit;
     end;
 
-    If (LCurrentProtocol<CT_PROTOCOL_5) then begin
+    If (LSafeboxCurrentProtocol<CT_PROTOCOL_5) then begin
       if (TAccountComp.IsAccountForSwap( LTarget.accountInfo )) then begin
         AErrors := 'Tx-Buy atomic swaps are not allowed until Protocol 5';
         exit;
@@ -996,14 +998,14 @@ begin
         // the below line was a bug fix that introduced a new bug, and is retained here for
         // V2-V4 consistency
         //------
-        if Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,AErrors)) then exit; // BUG 20171511
+        if Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,LSafeboxCurrentProtocol,AErrors)) then exit;
         //------
       end;
     end;
 
     // Check that stored "new_publicKey" is valid (when not in coin swap)
     if (Not TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo)) and
-       (Not (TAccountComp.IsValidAccountKey(LTarget.accountInfo.new_publicKey,AErrors))) then exit;
+       (Not (TAccountComp.IsValidAccountKey(LTarget.accountInfo.new_publicKey,LSafeboxCurrentProtocol,AErrors))) then exit;
 
     // NOTE: This is a Transaction opereation (not a buy account operation) that
     // has some "added" effects (private sale, swap...)
@@ -1042,13 +1044,13 @@ begin
 
   if (FData.opTransactionStyle in [buy_account, transaction_with_auto_buy_account, transaction_with_auto_atomic_swap]) then begin
     // account purchase
-    if (LCurrentProtocol<CT_PROTOCOL_2) then begin
+    if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
       AErrors := 'NOT ALLOWED ON PROTOCOL 1';
       exit;
     end;
 
     if (LTarget.accountInfo.state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) AND
-       (LCurrentProtocol<CT_PROTOCOL_5) then begin
+       (LSafeboxCurrentProtocol<CT_PROTOCOL_5) then begin
       AErrors := 'NOT ALLOWED UNTIL PROTOCOL 5';
       exit;
     end;
@@ -1417,8 +1419,10 @@ end;
 
 function TOpChangeKey.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
 Var account_signer, account_target : TAccount;
+  LSafeboxCurrentProtocol : Integer;
 begin
   Result := false;
+  LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
   if (FData.account_signer>=AccountTransaction.FreezedSafeBox.AccountsCount) then begin
     errors := 'Invalid account number';
     Exit;
@@ -1453,7 +1457,7 @@ begin
   end;
   if (length(FData.payload.payload_raw)>CT_MaxPayloadSize) then begin
     errors := 'Invalid Payload size:'+inttostr(length(FData.payload.payload_raw))+' (Max: '+inttostr(CT_MaxPayloadSize)+')';
-    If (ProtocolVersion>=CT_PROTOCOL_2) then begin
+    If (LSafeboxCurrentProtocol>=CT_PROTOCOL_2) then begin
       Exit; // BUG from protocol 1
     end;
   end;
@@ -1462,11 +1466,11 @@ begin
     errors := 'Account signer is currently locked';
     exit;
   end;
-  If Not TAccountComp.IsValidAccountKey( FData.new_accountkey, errors ) then begin
+  If Not TAccountComp.IsValidAccountKey( FData.new_accountkey,LSafeboxCurrentProtocol,errors) then begin
     exit;
   end;
   // NEW v2 protocol protection: Does not allow to change key for same key
-  if (ProtocolVersion>=CT_PROTOCOL_2) then begin
+  if (LSafeboxCurrentProtocol>=CT_PROTOCOL_2) then begin
     if (TAccountComp.EqualAccountKeys(account_target.accountInfo.accountKey,FData.new_accountkey)) then begin
       errors := 'New public key is the same public key';
       exit;
@@ -1490,7 +1494,7 @@ begin
       errors := 'Signer and target accounts have different public key';
       exit;
     end;
-    if (ProtocolVersion<CT_PROTOCOL_2) then begin
+    if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
       errors := 'NOT ALLOWED ON PROTOCOL 1';
       exit;
     end;
@@ -1739,8 +1743,10 @@ end;
 
 function TOpRecoverFounds.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
 Var acc : TAccount;
+  LSafeboxCurrentProtocol : Integer;
 begin
   Result := false;
+  LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
   if TAccountComp.IsAccountBlockedByProtocol(FData.account,AccountTransaction.FreezedSafeBox.BlocksCount) then begin
     errors := 'account is blocked for protocol';
     Exit;
@@ -1770,7 +1776,7 @@ begin
     errors := 'Insuficient funds';
     exit;
   end;
-  if Not TAccountComp.IsValidAccountKey(FData.new_accountkey,errors) then begin
+  if Not TAccountComp.IsValidAccountKey(FData.new_accountkey,LSafeboxCurrentProtocol,errors) then begin
     Exit;
   end;
   Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
@@ -1911,7 +1917,7 @@ function TOpListAccount.DoOperation(AccountPreviousUpdatedBlock : TAccountPrevio
 Var
   account_signer, account_target : TAccount;
   LIsDelist, LIsSale, LIsPrivateSale, LIsPublicSale, LIsSwap, LIsAccountSwap, LIsCoinSwap : boolean;
-  LCurrentProtocol : Integer;
+  LSafeboxCurrentProtocol : Integer;
 begin
   Result := false;
   // Determine which flow this function will execute
@@ -1943,12 +1949,12 @@ begin
     LIsCoinSwap := false;
   end;
 
-  LCurrentProtocol := ProtocolVersion;
-  if (LCurrentProtocol<CT_PROTOCOL_2) then begin
+  LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
+  if (LSafeboxCurrentProtocol<CT_PROTOCOL_2) then begin
     errors := 'List/Delist Account is not allowed on Protocol 1';
     exit;
   end;
-  if LIsSwap AND (LCurrentProtocol<CT_PROTOCOL_5) then begin
+  if LIsSwap AND (LSafeboxCurrentProtocol<CT_PROTOCOL_5) then begin
     errors := 'Atomic Swaps are not allowed before Protocol 5';
     exit;
   end;
@@ -2000,11 +2006,11 @@ begin
       exit;
     end;
     if LIsPrivateSale OR LIsAccountSwap then begin
-      If Not TAccountComp.IsValidAccountKey( FData.new_public_key, errors ) then begin
+      If Not TAccountComp.IsValidAccountKey( FData.new_public_key,LSafeboxCurrentProtocol,errors) then begin
         errors := 'Invalid new public key: '+errors;
         exit;
       end;
-    end else if (LCurrentProtocol>=CT_PROTOCOL_5) then begin
+    end else if (LSafeboxCurrentProtocol>=CT_PROTOCOL_5) then begin
       // COIN SWAP or PUBLIC SALE must set FData.new_public_key to NULL
       if Not TAccountComp.IsNullAccountKey(FData.new_public_key) then begin
         errors := 'Coin swap/Public sale needs a NULL new public key';
@@ -2360,7 +2366,7 @@ begin
     ms.Write(FData.account_price,Sizeof(FData.account_price));
     ms.Write(FData.account_to_pay,Sizeof(FData.account_to_pay));
     ms.Write(FData.fee,Sizeof(FData.fee));
-    if ProtocolVersion>=CT_PROTOCOL_5 then begin
+    if FProtocolVersion>=CT_PROTOCOL_5 then begin
       ms.Write(FData.payload.payload_type,SizeOf(FData.payload.payload_type));
     end;
     if Length(FData.payload.payload_raw)>0 then
@@ -2375,12 +2381,12 @@ begin
       ms.WriteBuffer(s[Low(s)],Length(s));
     ms.Write(FData.locked_until_block,Sizeof(FData.locked_until_block));
     // VERSION 5: write the new account state and hash-lock
-    if (ProtocolVersion >= CT_PROTOCOL_5) then begin
+    if (FProtocolVersion >= CT_PROTOCOL_5) then begin
       w := Word(FData.account_state);
       ms.Write(w, 2);
       TStreamOp.WriteAnsiString(ms, FData.hash_lock); // the hash-lock if any
     end;
-    if (ProtocolVersion<=CT_PROTOCOL_3) then begin
+    if (FProtocolVersion<=CT_PROTOCOL_3) then begin
       ms.Position := 0;
       SetLength(Result,ms.Size);
       ms.ReadBuffer(Result[Low(Result)],ms.Size);
@@ -2652,9 +2658,11 @@ function TOpData.DoOperation(
   AccountPreviousUpdatedBlock: TAccountPreviousBlockInfo;
   AccountTransaction: TPCSafeBoxTransaction; var errors: String): Boolean;
 Var account_signer, account_sender, account_target : TAccount;
+  LSafeboxCurrentProtocol : Integer;
 begin
   Result := false;
-  if (ProtocolVersion<CT_PROTOCOL_4) then begin
+  LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
+  if (LSafeboxCurrentProtocol<CT_PROTOCOL_4) then begin
     errors := 'OpData is not allowed on Protocol < 4';
     exit;
   end;

+ 3 - 3
src/core/URPC.pas

@@ -375,7 +375,7 @@ begin
     APubKey := TAccountComp.RawString2Accountkey(TCrypto.HexaToRaw(AInputParams.AsString(APrefix+'enc_pubkey','')));
   end;
   // Final confirmation
-  If Not TAccountComp.IsValidAccountKey(APubKey,LErrors_aux) then begin
+  If Not TAccountComp.IsValidAccountKey(APubKey,CT_BUILD_PROTOCOL,LErrors_aux) then begin
     AErrortxt := 'Invalid public key: '+LErrors_aux;
   end else Result := True;
 end;
@@ -3571,7 +3571,7 @@ begin
       ErrorNum:= CT_RPC_ErrNum_InvalidPubKey;
       exit;
     end;
-    if TAccountComp.IsValidAccountKey(account.accountInfo.accountKey,ansistr) then begin
+    if TAccountComp.IsValidAccountKey(account.accountInfo.accountKey,CT_BUILD_PROTOCOL,ansistr) then begin
       jsonresponse.GetAsVariant('result').Value:=TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(account.accountInfo.accountKey));
       Result := True;
     end else begin
@@ -3588,7 +3588,7 @@ begin
       ErrorNum := CT_RPC_ErrNum_InvalidPubKey;
       exit;
     end;
-    if (TAccountComp.IsValidAccountKey(account.accountInfo.accountKey,ansistr)) then begin
+    if (TAccountComp.IsValidAccountKey(account.accountInfo.accountKey,CT_BUILD_PROTOCOL,ansistr)) then begin
       TPascalCoinJSONComp.FillPublicKeyObject(account.accountInfo.accountKey,GetResultObject);
       Result := True;
     end else begin

+ 4 - 2
src/core/UTxMultiOperation.pas

@@ -557,11 +557,13 @@ var i,j : Integer;
   senders,senders_n_operation,receivers : Array of Cardinal;
   senders_amount : Array of UInt64;
   receivers_amount : Array of UInt64;
+  LSafeboxCurrentProtocol : Integer;
 begin
   // Check valid info:
   Result := False;
   errors := '';
-  if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_3) then begin
+  LSafeboxCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
+  if (LSafeboxCurrentProtocol<CT_PROTOCOL_3) then begin
     errors := 'NEED PROTOCOL 3';
     exit;
   end;
@@ -670,7 +672,7 @@ begin
       exit;
     end;
     If (public_key in chi.Changes_type) then begin
-      If Not TAccountComp.IsValidAccountKey( chi.New_Accountkey, errors ) then begin
+      If Not TAccountComp.IsValidAccountKey( chi.New_Accountkey, LSafeboxCurrentProtocol, errors) then begin
         Exit;
       end;
     end;

+ 1 - 1
src/core/UWallet.pas

@@ -719,7 +719,7 @@ begin
       raise Exception.Create('Invalid public key value (Not hexa or not an imported format)'+#10+errors);
     accountKey := TAccountComp.RawString2Accountkey(raw);
   end;
-  If not TAccountComp.IsValidAccountKey(accountKey,errors) then
+  If not TAccountComp.IsValidAccountKey(accountKey,CT_BUILD_PROTOCOL,errors) then
     raise Exception.Create('This data is not a valid public key'+#10+errors);
   if FKeys.IndexOfAccountKey(accountKey)>=0 then
     raise exception.Create('This key exists on your wallet');

+ 4 - 2
src/gui-classic/UFRMAccountSelect.pas

@@ -161,12 +161,14 @@ procedure TSearchThread.BCExecute;
     errors : String;
     i : Integer;
     LBlocksCount : Integer;
+    LCurrentProtocol : Word;
   begin
     SetLength(FAccounts,0);
     c := 0;
     maxC := FSearchValues.SafeBox.AccountsCount-1;
-    validAccKey := TAccountComp.IsValidAccountKey(FSearchValues.inAccountKey,errors);
+    validAccKey := TAccountComp.IsValidAccountKey(FSearchValues.inAccountKey,CT_BUILD_PROTOCOL,errors);
     LBlocksCount := FSearchValues.SafeBox.BlocksCount;
+    LCurrentProtocol := FSearchValues.SafeBox.CurrentProtocol;
     while (c<=maxC) And (Not Terminated) And (Not FDoStopSearch) do begin
       account := FSearchValues.SafeBox.Account(c);
       isValid := True;
@@ -195,7 +197,7 @@ procedure TSearchThread.BCExecute;
       end;
       If IsValid And (Length(FSearchValues.searchName)>0) then begin
         i := TBaseType.FindIn(FSearchValues.searchName,account.name);
-        IsValid := i>0;
+        IsValid := i>=0;
       end;
       //
       if IsValid then begin

+ 3 - 3
src/gui-classic/UFRMOperationsExplorer.pas

@@ -684,7 +684,7 @@ begin
   If Assigned(op) then begin
     mOperationInfo.Lines.Add(Format('%s',[op.ToString]));
     mOperationInfo.Lines.Add('');
-    mOperationInfo.Lines.Add(Format('OpType:%d ClassName:%s',[op.OpType,op.ClassName]));
+    mOperationInfo.Lines.Add(Format('OpType:%d ClassName:%s Protocol:%d',[op.OpType,op.ClassName,op.ProtocolVersion]));
     l := TList<Cardinal>.Create;
     Try
       op.AffectedAccounts(l); aux := '';
@@ -707,7 +707,7 @@ begin
       mOperationInfo.Lines.Add(Format('Size: %.2f Kb (%d bytes)',[ms.Size/1024,ms.Size]));
       ms.Position:=0;
       SetLength(raw,ms.Size);
-      ms.ReadBuffer(raw[1],ms.Size);
+      ms.ReadBuffer(raw[0],ms.Size);
       mOperationExport.Lines.Text := TCrypto.ToHexaString(raw);
     finally
       ms.Free;
@@ -739,7 +739,7 @@ begin
       if (opht.OperationsCount>0) then begin
         ms.Position:=0;
         SetLength(raw,ms.Size);
-        ms.ReadBuffer(raw[1],ms.Size);
+        ms.ReadBuffer(raw[0],ms.Size);
         mOperationExport.Lines.Text := TCrypto.ToHexaString(raw);
         jsonObj := TPCJSONObject.Create;
         Try

+ 7 - 4
src/gui-classic/UFRMRandomOperations.pas

@@ -168,6 +168,8 @@ begin
         inc(nCounter);
         LProtocol := operationsComp.OperationBlock.protocol_version;
         //
+        if Random(10)<5 then LProtocol := 4; // XXXXXXXXXXXXXXXXX TEST OLD
+
         Case Random(30) of
           0..10 : begin
             if FMaxOperationsPerSecond>0 then nMaxTransactionsValue := Random(FMaxOperationsPerSecond)+1
@@ -186,6 +188,7 @@ begin
             else inc(FnOperationsCreatedFailed);
           end;
         end;
+        inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpListAccountForSale(LProtocol,operationsComp,FSourceWalletKeys));
       end;
 
       if (Not Terminated) And (Not FNeedSanitize) And (FAllowExecute) then begin
@@ -332,7 +335,7 @@ begin
       iInt := Random(aWalletKeys.AccountsKeyList.AccountKeyList[iKey].Count);
       Repeat
         nAccount := aWalletKeys.AccountsKeyList.AccountKeyList[iKey].Get( iInt );
-        If Not TAccountComp.IsAccountBlockedByProtocol(nAccount,operationsComp.SafeBoxTransaction.FreezedSafeBox.BlocksCount) then begin
+        If (nAccount>0) And (Not TAccountComp.IsAccountBlockedByProtocol(nAccount,operationsComp.SafeBoxTransaction.FreezedSafeBox.BlocksCount)) then begin
           if (operationsComp.OperationsHashTree.CountOperationsBySameSignerWithoutFee(nAccount)<=0) then begin
             Result := True;
             Exit;
@@ -493,7 +496,7 @@ begin
 
   end else begin
 
-    if Random(1)=0 then begin
+    if Random(2)=0 then begin
       nTarget := Random( aWalletKeys.AccountsKeyList.AccountKeyList[iKey].Count );
     end else begin
       nTarget := nSigner;
@@ -516,7 +519,7 @@ begin
               CT_HashLock_NUL,
               GetRandomPayload(''));
           end;
-        1 : // Publis sale:
+        1 : // Public sale:
           begin
             opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
               as_ForSale,
@@ -532,7 +535,7 @@ begin
           begin
             opList := TOpListAccountForSaleOrSwap.CreateListAccountForSaleOrSwap(current_protocol,
               as_ForAtomicAccountSwap,
-              nSigner, SignerAccount.n_operation+1, nTarget, Random(50000)+1,fees,
+              nSigner, SignerAccount.n_operation+1, nTarget,0,fees,
               Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount ),
               aWalletKeys.Key[ Random(aWalletKeys.Count) ].AccountKey,
               operationsComp.OperationBlock.block + Random(1000),

+ 218 - 6
src/gui-classic/UFRMWallet.pas

@@ -253,6 +253,7 @@ type
     {$ENDIF}
     Procedure Test_ShowPublicKeys(Sender: TObject);
     Procedure Test_ShowOperationsInMemory(Sender: TObject);
+    Procedure Test_FindAccountsForPrivateBuyOrSwapToMe(Sender : TObject);
     procedure OnAccountsGridUpdatedData(Sender : TObject);
   protected
     { Private declarations }
@@ -828,6 +829,9 @@ end;
 
 procedure TFRMWallet.ebFindAccountNumberChange(Sender: TObject);
 Var an : Cardinal;
+  LAccountNameRawValue : TRawBytes;
+  LErrors : String;
+  LAccNames : TOrderedRawList;
 begin
   if Trim(ebFindAccountNumber.Text)='' then begin
     ebFindAccountNumber.Color := clWindow;
@@ -840,9 +844,25 @@ begin
       ebFindAccountNumber.Font.Color := clRed;
     end;
   end else begin
-    // Invalid value
-    ebFindAccountNumber.Color := clRed;
-    ebFindAccountNumber.Font.Color := clWindowText;
+    LAccountNameRawValue.FromString(ebFindAccountNumber.Text);
+    LAccNames := TOrderedRawList.Create;
+    Try
+      if FNode.Bank.SafeBox.FindAccountsStartingByName(LAccountNameRawValue,LAccNames,1)>0 then begin
+        an := LAccNames.GetTag(0);
+        ebFindAccountNumber.Color := clWindow;
+        if FAccountsGrid.MoveRowToAccount(an) then begin
+          ebFindAccountNumber.Font.Color := clWindowText;
+        end else begin
+          ebFindAccountNumber.Font.Color := clRed;
+        end;
+      end else begin
+        // Invalid value
+        ebFindAccountNumber.Color := clRed;
+        ebFindAccountNumber.Font.Color := clWindowText;
+      end;
+    Finally
+      LAccNames.Free;
+    End;
   end;
 end;
 
@@ -1061,10 +1081,10 @@ end;
 procedure TFRMWallet.InitMenuForTesting;
 var mi : TMenuItem;
 begin
+{$IFDEF TESTNET}
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='-';
   miAbout.Add(mi);
-{$IFDEF TESTNET}
   {$IFDEF TESTING_NO_POW_CHECK}
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Create a block';
@@ -1083,7 +1103,6 @@ begin
   mi.Caption:='Diagnostic Tool';
   mi.OnClick:=Test_ShowDiagnosticTool;
   miAbout.Add(mi);
-{$ENDIF}
   mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Show public keys state';
   mi.OnClick:=Test_ShowPublicKeys;
@@ -1092,7 +1111,11 @@ begin
   mi.Caption:='Show operations in memory';
   mi.OnClick:=Test_ShowOperationsInMemory;
   miAbout.Add(mi);
-
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Search accounts for private or swap to me';
+  mi.OnClick:=Test_FindAccountsForPrivateBuyOrSwapToMe;
+  miAbout.Add(mi);
+{$ENDIF}
 end;
 
 {$IFDEF TESTING_NO_POW_CHECK}
@@ -1682,6 +1705,195 @@ begin
   end;
 end;
 
+procedure TFRMWallet.Test_FindAccountsForPrivateBuyOrSwapToMe(Sender: TObject);
+{ This procedure will search in Safebox all accounts in "for_private_sale" state
+  or in "for_account_swap" that can be self-signed using one of my private keys
+  }
+
+  function CaptureSender0Coins(var AAccountSender0Coins : TAccount; var ANeededWalletKey : TWalletKey) : Boolean;
+  var ii : Integer;
+  begin
+    //
+    Result := False;
+    for ii := 0 to WalletKeys.AccountsKeyList.Count-1 do begin
+      if WalletKeys.AccountsKeyList.AccountKeyList[ii].Count>0 then begin
+        if WalletKeys.TryGetKey(WalletKeys.AccountsKeyList.AccountKey[ii],ANeededWalletKey) then begin
+          AAccountSender0Coins := FNode.Bank.SafeBox.Account( WalletKeys.AccountsKeyList.AccountKeyList[ii].Get(0) );
+          Result := True;
+        end;
+      end;
+    end;
+
+  end;
+
+
+var i : Integer;
+  LLines : TStrings;
+  LAccount, LAccountSender0Coins : TAccount;
+  LAccountOpDesc : String;
+  LCountAccountsFound_total, LCountAccountsFound_Operation : Integer;
+  LNeededWalletKey : TWalletKey;
+  s : String;
+  LOpTransaction : TOpTransaction;
+  LOperationsHashTree, LGlobalOperationsHashTree : TOperationsHashTree;
+  LStream : TStream;
+  LRaw : TRawBytes;
+  LFRM : TFRMMemoText;
+  LOpPayload : TOperationPayload;
+begin
+  if Not WalletKeys.IsValidPassword then raise Exception.Create('Your wallet keys are locked');
+  LOpPayload := CT_TOperationPayload_NUL;
+  if InputQuery('Search ATOMIC SWAP by SECRET','Insert SECRET value (use 0x... for Hexadecimal, otherwise will be a String)',s) then begin
+    if s.StartsWith('0x') then begin
+      if not UCommon.TryHex2Bytes(s,LOpPayload.payload_raw) then raise Exception.Create('SECRET value is not an Hexadecimal'+#10+s);
+    end else begin
+      LOpPayload.payload_raw.FromString(s);
+    end;
+  end;
+  LCountAccountsFound_total := 0;
+  LCountAccountsFound_Operation := 0;
+  LLines := TStringList.Create;
+  LGlobalOperationsHashTree := TOperationsHashTree.Create;
+  try
+    for i:=0 to FNode.Bank.SafeBox.AccountsCount-1 do begin
+      LAccountOpDesc := '';
+      LAccount := FNode.Bank.SafeBox.Account(i);
+
+      LOpTransaction := Nil;
+      Try
+        Case LAccount.accountInfo.state of
+          as_ForSale : begin
+            if Not TAccountComp.IsNullAccountKey( LAccount.accountInfo.new_publicKey ) then begin
+              if Not WalletKeys.TryGetKey(LAccount.accountInfo.new_publicKey,LNeededWalletKey) then Continue;
+              if Not Assigned(LNeededWalletKey.PrivateKey) then Continue; // Key not available!
+            end else Continue;
+            // Private sale to me
+            // Is in time?
+            if TAccountComp.IsAccountLocked(LAccount.accountInfo,FNode.Bank.BlocksCount) then begin
+              //
+              if LAccount.balance>=LAccount.accountInfo.price then begin
+                LAccountOpDesc := Format('Account %s is for private sale to me and with enough balance to pay %s (balance %s)',[
+                  TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),
+                  TAccountComp.FormatMoney(LAccount.accountInfo.price),
+                  TAccountComp.FormatMoney(LAccount.balance)]);
+                // No key needed... just a transaction SELF SIGNED
+                LOpTransaction := TOpBuyAccount.CreateBuy(FNode.Bank.SafeBox.CurrentProtocol,
+                    LAccount.account, LAccount.n_operation+1,
+                    LAccount.account, LAccount.accountInfo.account_to_pay, LAccount.accountInfo.price,
+                    0,0,
+                    LAccount.accountInfo.new_publicKey,
+                    LNeededWalletKey.PrivateKey,
+                    LOpPayload);
+              end else begin
+                LAccountOpDesc := Format('Account %s is for private sale to me but needs a Buy operation paying %s PASC (%s pending)',[
+                   TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),
+                   TAccountComp.FormatMoney(LAccount.accountInfo.price),
+                   TAccountComp.FormatMoney(LAccount.accountInfo.price - LAccount.balance)]);
+              end;
+            end else begin
+              LAccountOpDesc := Format('Account %s is for private sale to me but is out-of-lock period',[TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account)]);
+            end;
+          end;
+          as_ForAtomicAccountSwap : begin
+            if Not WalletKeys.TryGetKey(LAccount.accountInfo.new_publicKey,LNeededWalletKey) then Continue;
+            if TAccountComp.IsAccountLocked(LAccount.accountInfo,FNode.Bank.BlocksCount) then begin
+              if TAccountComp.IsValidAccountInfoHashLockKey(LAccount.accountInfo,LOpPayload.payload_raw) then begin
+                // Atomic Account swap using provided SECRET
+                LAccountOpDesc := Format('Account %s is for Atomic Account Swap to me using SECRET %s',[TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),UCommon.Bytes2Hex(LOpPayload.payload_raw,True)]);
+                //
+                // No key needed... just a Buy Operation SELF SIGNED
+                LOpTransaction := TOpBuyAccount.CreateBuy(FNode.Bank.SafeBox.CurrentProtocol,
+                    LAccount.account, LAccount.n_operation+1,
+                    LAccount.account, LAccount.accountInfo.account_to_pay, LAccount.accountInfo.price,
+                    0,0,
+                    LAccount.accountInfo.new_publicKey,
+                    LNeededWalletKey.PrivateKey,
+                    LOpPayload);
+              end else begin
+                LAccountOpDesc := Format('Account %s is for Atomic Account Swap to me but SECRET %s is not valid',[TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),UCommon.Bytes2Hex(LOpPayload.payload_raw,True)]);
+              end;
+            end else begin
+              LAccountOpDesc := Format('Account %s is for Atomic Account Swap to me but is out-of-lock period',[TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account)]);
+            end;
+
+          end;
+          as_ForAtomicCoinSwap : begin
+            if Not TAccountComp.IsValidAccountInfoHashLockKey(LAccount.accountInfo,LOpPayload.payload_raw) then Continue;
+            // Atomic Coin swap using provided SECRET
+            if TAccountComp.IsAccountLocked(LAccount.accountInfo,FNode.Bank.BlocksCount) then begin
+              // Single transaction using amount 0 from ANY sender
+              if CaptureSender0Coins(LAccountSender0Coins,LNeededWalletKey) then begin
+                // Atomic Account swap using provided SECRET
+                LAccountOpDesc := Format('Account %s is for Atomic Coin Swap to me using SECRET %s',[TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),UCommon.Bytes2Hex(LOpPayload.payload_raw,True)]);
+                //
+                // No key needed... just a transaction SELF SIGNED
+                LOpTransaction := TOpTransaction.CreateTransaction(FNode.Bank.SafeBox.CurrentProtocol,
+                    LAccountSender0Coins.account, LAccountSender0Coins.n_operation+1,
+                    LAccount.account,
+                    LNeededWalletKey.PrivateKey,
+                    0,0, // No Amount no Fee
+                    LOpPayload);
+              end else begin
+                LAccountOpDesc := Format('Account %s is for Atomic Coin Swap using SECRET %s but I have no key to sign',[TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),UCommon.Bytes2Hex(LOpPayload.payload_raw,True)]);
+              end;
+            end else begin
+              LAccountOpDesc := Format('Account %s is for Atomic Coin Swap using SECRET %s but is out-of-lock period',[TAccountComp.AccountNumberToAccountTxtNumber(LAccount.account),UCommon.Bytes2Hex(LOpPayload.payload_raw,True)]);
+            end;
+          end;
+        else Continue;
+        End;
+
+        // Do
+        Inc(LCountAccountsFound_total);
+        LLines.Add(Format('%s',[LAccountOpDesc]));
+        if Assigned(LOpTransaction) then begin
+          Inc(LCountAccountsFound_Operation);
+          LOperationsHashTree := TOperationsHashTree.Create;
+          LStream := TMemoryStream.Create;
+          try
+            LOperationsHashTree.AddOperationToHashTree(LOpTransaction);
+            LGlobalOperationsHashTree.AddOperationToHashTree(LOpTransaction);
+            LLines.Add(Format('Operation: %s',[LOpTransaction.ToString]));
+            LOperationsHashTree.SaveOperationsHashTreeToStream(LStream,False);
+            LRaw.FromStream(LStream);
+            LLines.Add(Format('rawoperations (for JSON-RPC call): %s',[LRaw.ToHexaString]));
+          finally
+            LOperationsHashTree.Free;
+            LStream.Free;
+          end;
+        end;
+
+      Finally
+        FreeAndNil(LOpTransaction);
+      End;
+    end; // For
+    LLines.Add('');
+    LLines.Add(Format('Found %d of %d available account from a Safebox with %d accounts',[
+      LCountAccountsFound_Operation,
+      LCountAccountsFound_total,
+      FNode.Bank.SafeBox.AccountsCount]));
+    LStream := TMemoryStream.Create;
+    try
+      LGlobalOperationsHashTree.SaveOperationsHashTreeToStream(LStream,False);
+      LRaw.FromStream(LStream);
+      LLines.Add(Format('rawoperations (for JSON-RPC call) of %d operations: %s',[LGlobalOperationsHashTree.OperationsCount, LRaw.ToHexaString]));
+    finally
+      LStream.Free;
+    end;
+    //
+    LFRM := TFRMMemoText.Create(Self);
+    try
+      LFRM.InitData('',LLines.Text);
+      LFRM.ShowModal;
+    finally
+      LFRM.Free;
+    end;
+  finally
+    LLines.Free;
+    LGlobalOperationsHashTree.Free;
+  end;
+end;
+
 procedure TFRMWallet.Test_ShowDiagnosticTool(Sender: TObject);
 {$IFDEF TESTNET}
 var

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

@@ -426,7 +426,7 @@ begin
     if Length(raw)=0 then raise Exception.Create('Invalid public key value (Not hexa or not an imported format)'+#10+errors);
     account := TAccountComp.RawString2Accountkey(raw);
   end;
-  If not TAccountComp.IsValidAccountKey(account,errors) then raise Exception.Create('This data is not a valid public key'+#10+errors);
+  If not TAccountComp.IsValidAccountKey(account,CT_BUILD_PROTOCOL,errors) then raise Exception.Create('This data is not a valid public key'+#10+errors);
   if WalletKeys.IndexOfAccountKey(account)>=0 then raise exception.Create('This key exists on your wallet');
   s := 'Imported public key '+DateTimeToStr(now);
   if InputQuery('Set a name','Name for this private key:',s) then begin