Browse Source

Merge pull request #7 from PascalCoinDev/master

Testnet 5 Beta 3
Pascal Coin 5 years ago
parent
commit
c7f9173e4f

+ 8 - 1
README.md

@@ -46,7 +46,14 @@ Also, consider a donation at PascalCoin development account: "0-10"
 - Implementation of PIP-0027 (E-PASA Inifine Address-Space) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0027.md
   - Third party apps/implementations of hard coded operations need to pay attention of an extra byte added on each operation using Payload
   - TODO: Explain "in core" changes
-- Implementation of PIP-0012 (Recover Accounts option after 10 years) -> https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0012.md
+- 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 (sender or signer)
+  - 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
+  - Discovered 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
 - Updated "OP_DATA" operation: (PIP-0016)
   - New digest hash value for OP_DATA ( PIP-0016 ) on Protocol 5
   - Added "id" field (GUID/UUID type as described on PIP-0016), was missing on V4, added on V5

+ 292 - 143
src/core/UAccounts.pas

@@ -60,7 +60,8 @@ Type
     block_payload : TRawBytes;  // RAW Payload that a miner can include to a blockchain
     initial_safe_box_hash: TRawBytes; // RAW Safe Box Hash value (32 bytes, it's a Sha256)
     operations_hash: TRawBytes; // RAW sha256 (32 bytes) of Operations
-    proof_of_work: TRawBytes;   // RAW Double Sha256
+    proof_of_work: TRawBytes;   // RAW 32 bytes
+    previous_proof_of_work: TRawBytes; // RAW 32 bytes
   end;
 
   { TPascalCoinProtocol }
@@ -90,7 +91,8 @@ Type
     account: Cardinal;        // FIXED value. Account number
     accountInfo : TAccountInfo;
     balance: UInt64;          // Balance, always >= 0
-    updated_block: Cardinal;  // Number of block where was updated
+    updated_on_block: Cardinal;   // Number of block where was updated (active or passive mode)
+    updated_on_block_active_mode: Cardinal; // Number of block where was used (active mode only)
     n_operation: Cardinal;    // count number of owner operations (when receive, this is not updated)
     name : TRawBytes;         // Protocol 2. Unique name
     account_type : Word;      // Protocol 2. Layer 2 use case
@@ -150,7 +152,7 @@ Type
     Class function IsAccountForAccountSwap(const AAccountInfo: TAccountInfo) : Boolean;
     Class Function IsAccountForSaleOrSwap(const AAccountInfo: TAccountInfo) : Boolean;
     Class Function IsAccountForSaleOrSwapAcceptingTransactions(const AAccount: TAccount; ACurrentBlock : Integer; ACurrentProtocol : Word; const APayload : TRawBytes) : Boolean;
-    Class Function IsOperationRecipientSignable(const ASender, ATarget : TAccount; AIncomingFunds : UInt64; ACurrentBlock : Integer; ACurrentProtocol : Word) : Boolean;
+    Class Function IsOperationRecipientSignable(const ASender, ATarget : TAccount; ACurrentBlock : Integer; ACurrentProtocol : Word) : Boolean;
     Class Function GetECInfoTxt(Const EC_OpenSSL_NID: Word) : String;
     Class Procedure ValidsEC_OpenSSL_NID(list : TList<Word>);
     Class Function AccountKey2RawString(const account: TAccountKey): TRawBytes; overload;
@@ -308,7 +310,8 @@ Type
     Procedure UpdateAccount(account_number : Cardinal; const newAccountInfo: TAccountInfo; const newName : TRawBytes; newType : Word;
          newBalance: UInt64; newN_operation: Cardinal;
          const newAccountData, newAccountSeal : TRawBytes;
-         accountUpdateStyle : TAccountUpdateStyle; newUpdated_block, newPrevious_Updated_block : Cardinal);
+         accountUpdateStyle : TAccountUpdateStyle; newUpdated_block, newUpdated_block_active_mode, newPrevious_Updated_block : Cardinal;
+         AHasBenUpdatedOnActiveMode : Boolean);
     Function AddNew(Const blockChain : TOperationBlock) : TBlockAccount;
     function DoUpgradeToProtocol2 : Boolean;
     function DoUpgradeToProtocol3 : Boolean;
@@ -436,6 +439,7 @@ Type
       LatestOpIDUsedForSeal : TRawBytes;
       AccountSealed : PAccount;
       SealChangesCounter : Integer;
+      UsedAsActiveMode : Boolean;
     End;
     PSealedAccount = ^TSealedAccount;
     {TSealedAccountList}
@@ -468,7 +472,7 @@ Type
     Function Origin_FindAccountByName(const account_name : TRawBytes) : Integer;
   protected
     Function GetInternalAccount(account_number : Cardinal; var APtrSealedAccount : PSealedAccount) : PAccount;
-    procedure UpdateSeal(APtrSealedAccount : PSealedAccount; AOpID : TRawBytes);
+    procedure UpdateSealAndActiveModeFlag(APtrSealedAccount : PSealedAccount; AOpID : TRawBytes; ASetUsedAsActiveMode : Boolean);
   public
     Constructor Create(SafeBox : TPCSafeBox);
     Destructor Destroy; override;
@@ -509,18 +513,18 @@ Type
 
 Const
   CT_OperationBlock_NUL : TOperationBlock = (block:0;account_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);reward:0;fee:0;protocol_version:0;
-    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:Nil;initial_safe_box_hash:Nil;operations_hash:Nil;proof_of_work:Nil);
+    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:Nil;initial_safe_box_hash:Nil;operations_hash:Nil;proof_of_work:Nil;previous_proof_of_work:Nil);
   CT_AccountInfo_NUL : TAccountInfo = (state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);hashed_secret:Nil);
-  CT_Account_NUL : TAccount = (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_block:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0);
+  CT_Account_NUL : TAccount = (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0);
   CT_BlockAccount_NUL : TBlockAccount = (
     blockchainInfo:(block:0;account_key:(EC_OpenSSL_NID:0;x:Nil;y:Nil);reward:0;fee:0;protocol_version:0;
-    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:Nil;initial_safe_box_hash:Nil;operations_hash:Nil;proof_of_work:Nil);
+    protocol_available:0;timestamp:0;compact_target:0;nonce:0;block_payload:Nil;initial_safe_box_hash:Nil;operations_hash:Nil;proof_of_work:Nil;previous_proof_of_work:Nil);
     accounts:(
-    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_block:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0),
-    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_block:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0),
-    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_block:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0),
-    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_block:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0),
-    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_block:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0)
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0),
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0),
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0),
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0),
+    (account:0;accountInfo:(state:as_Unknown;accountKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil);locked_until_block:0;price:0;account_to_pay:0;new_publicKey:(EC_OpenSSL_NID:0;x:Nil;y:Nil));balance:0;updated_on_block:0;updated_on_block_active_mode:0;n_operation:0;name:Nil;account_type:0;account_data:Nil;account_seal:Nil;previous_updated_block:0)
     );
     block_hash:Nil;
     accumulatedWork:0);
@@ -601,8 +605,8 @@ Begin
     for i:=0 to sb.BlocksCount-1 do begin
       bl_my := sb.Block(i);
       for j:=Low(bl_my.accounts) to High(bl_my.accounts) do begin
-        If maxBlock < (bl_my.accounts[j].updated_block) then begin
-          Raise Exception.Create(Format('%s Integrity on (i)=%d for block account:%d updated on %d > maxBlock %d',[title, i,bl_my.accounts[j].account,bl_my.accounts[j].updated_block,maxBlock]));
+        If (maxBlock < (bl_my.accounts[j].updated_on_block)) or (maxBlock < (bl_my.accounts[j].updated_on_block_active_mode)) or (bl_my.accounts[j].updated_on_block<bl_my.accounts[j].updated_on_block_active_mode) then begin
+          Raise Exception.Create(Format('%s Integrity on (i)=%d for block account:%d updated on %d , active updated on %d ,maxBlock %d',[title, i,bl_my.accounts[j].account,bl_my.accounts[j].updated_on_block,bl_my.accounts[j].updated_on_block_active_mode,maxBlock]));
         end;
       end;
       auxH.Replace(i*32,bl_my.block_hash[Low(bl_my.block_hash)],Length(bl_my.block_hash));
@@ -729,8 +733,41 @@ begin
   try
     ms.WriteBuffer(operationBlock.initial_safe_box_hash[Low(operationBlock.initial_safe_box_hash)],length(operationBlock.initial_safe_box_hash));
     ms.WriteBuffer(operationBlock.operations_hash[Low(operationBlock.operations_hash)],length(operationBlock.operations_hash));
-    // Note about fee: Fee is stored in 8 bytes, but only digest first 4 low bytes
-    ms.Write(operationBlock.fee,4);
+    if operationBlock.protocol_version<CT_PROTOCOL_5 then begin
+      // Note about fee: Fee is stored in 8 bytes, but only digest first 4 low bytes
+      ms.Write(operationBlock.fee,4);
+    end else begin
+      // UPDATE PROTOCOL 5 - September 2019
+      ms.Write(operationBlock.fee,SizeOf(operationBlock.fee)); // Changed from 4 to 8 bytes in Little endian
+
+      // UPDATE PROTOCOL 5 - September 2019
+      //
+      // IMPORTANT SECURITY FIX:
+      //
+      // Since protocol 2 the Safebox can be checkpointed, this means that the
+      // Safebox can be self-checked mantaining Proof-of-Work consistency.
+      // (Introduced on PIP-0003 created by Herman Schoenfeld)
+      //
+      // The problem is that in order to protect "parallelization" of the PoW
+      // process must ensure that cannot create a header for block N until N-1
+      // has been created. On V4 version this protection is made using the
+      // "initial_safe_box_hash" that is correct and protects parallelization
+      // when adding a block on a previous safebox. The issue is that when
+      // only computing PoW of the Safebox (without blockchain) then this N-1
+      // value obtained on "initial_safe_box_hash" cannot be checked.
+      //
+      // In order to eliminate parallelization possibility for a hacked
+      // new Safebox the best way is to include in N block info obtained
+      // after N-1 block has been calculated (like blockchain does).
+      //
+      // The solution is to include the "previous PoW" obtained on N-1 on the
+      // digest for next N block, like a traditional blockchain does
+      //
+      // This important issue and security fix was discovered by Herman:
+      // Herman Schoenfeld <[email protected]>
+
+      ms.WriteBuffer(operationBlock.previous_proof_of_work[Low(operationBlock.previous_proof_of_work)],Length(operationBlock.previous_proof_of_work));
+    end;
     SetLength(Part3,ms.Size);
     ms.Position := 0;
     ms.ReadBuffer(Part3[Low(Part3)],ms.Size);
@@ -838,8 +875,14 @@ begin
     // Part 3
     ms.WriteBuffer(operationBlock.initial_safe_box_hash[Low(operationBlock.initial_safe_box_hash)],length(operationBlock.initial_safe_box_hash));
     ms.WriteBuffer(operationBlock.operations_hash[Low(operationBlock.operations_hash)],length(operationBlock.operations_hash));
-    // Note about fee: Fee is stored in 8 bytes (Int64), but only digest first 4 low bytes
-    ms.Write(operationBlock.fee,4);
+    if operationBlock.protocol_version<CT_PROTOCOL_5 then begin
+      // Note about fee: Fee is stored in 8 bytes (Int64), but only digest first 4 low bytes
+      ms.Write(operationBlock.fee,4);
+    end else begin
+      // UPDATE PROTOCOL 5 - September 2019
+      ms.Write(operationBlock.fee,SizeOf(operationBlock.fee)); // Changed from 4 to 8 bytes in Little endian
+      ms.WriteBuffer(operationBlock.previous_proof_of_work[Low(operationBlock.previous_proof_of_work)],Length(operationBlock.previous_proof_of_work));
+    end;
     ms.Write(operationBlock.timestamp,4);
     ms.Write(operationBlock.nonce,4);
     if CT_ACTIVATE_RANDOMHASH_V4 AND (operationBlock.protocol_version >= CT_PROTOCOL_4) then begin
@@ -1214,6 +1257,8 @@ end;
 
 class procedure TAccountComp.SaveAccountToAStream(Stream: TStream; const Account: TAccount; current_protocol : Word);
 var w : Word;
+  LTmpSeal : T20Bytes;
+  LTmpRaw : TRawBytes;
 begin
   if current_protocol<CT_PROTOCOL_5 then
     w := CT_PROTOCOL_4
@@ -1222,13 +1267,19 @@ begin
   Stream.Write(Account.account,Sizeof(Account.account));
   TStreamOp.WriteAnsiString(Stream,AccountInfo2RawString(Account.accountInfo));
   Stream.Write(Account.balance,Sizeof(Account.balance));
-  Stream.Write(Account.updated_block,Sizeof(Account.updated_block));
+  Stream.Write(Account.updated_on_block,Sizeof(Account.updated_on_block));
+  if current_protocol>=CT_PROTOCOL_5 then begin
+    Stream.Write(Account.updated_on_block_active_mode,Sizeof(Account.updated_on_block_active_mode));
+  end;
   Stream.Write(Account.n_operation,Sizeof(Account.n_operation));
   TStreamOp.WriteAnsiString(Stream,Account.name);
   Stream.Write(Account.account_type,SizeOf(Account.account_type));
   if current_protocol>=CT_PROTOCOL_5 then begin
     TStreamOp.WriteAnsiString(Stream,Account.account_data);
-    TStreamOp.WriteAnsiString(Stream,Account.account_seal);
+    // Account Seal is allways a 20 bytes as described on PIP-0029
+    LTmpSeal := TBaseType.To20Bytes(Account.account_seal);
+    LTmpRaw := TBaseType.T20BytesToRawBytes(LTmpSeal);
+    TStreamOp.WriteAnsiString(Stream,LTmpRaw);
   end;
 end;
 
@@ -1246,13 +1297,16 @@ begin
   TAccountComp.RawString2AccountInfo(raw,Account.accountInfo);
   if (Stream.Size - Stream.Position<20) then Exit;
   Stream.Read(Account.balance,Sizeof(Account.balance));
-  Stream.Read(Account.updated_block,Sizeof(Account.updated_block));
+  Stream.Read(Account.updated_on_block,Sizeof(Account.updated_on_block));
+  if LSaved_protocol>=CT_PROTOCOL_5 then begin
+    Stream.Read(Account.updated_on_block_active_mode,Sizeof(Account.updated_on_block_active_mode));
+  end;
   Stream.Read(Account.n_operation,Sizeof(Account.n_operation));
   if TStreamOp.ReadAnsiString(Stream,Account.name)<0 then Exit;
   if Stream.Read(Account.account_type,SizeOf(Account.account_type)) <> 2 then Exit;
   if LSaved_protocol>=CT_PROTOCOL_5 then begin
     if TStreamOp.ReadAnsiString(Stream,Account.account_data)<0 then Exit;
-    if TStreamOp.ReadAnsiString(Stream,Account.account_seal)<0 then Exit;
+    if TStreamOp.ReadAnsiString(Stream,Account.account_seal,20)<0 then Exit;
   end;
   Result := True;
 end;
@@ -1424,7 +1478,8 @@ begin
   Result := (account1.account = account2.account)
           And (EqualAccountInfos(account1.accountInfo,account2.accountInfo))
           And (account1.balance = account2.balance)
-          And (account1.updated_block = account2.updated_block)
+          And (account1.updated_on_block = account2.updated_on_block)
+          And (account1.updated_on_block_active_mode = account2.updated_on_block_active_mode)
           And (account1.n_operation = account2.n_operation)
           And (TBaseType.Equals(account1.name,account2.name))
           And (account1.account_type = account2.account_type)
@@ -1444,10 +1499,14 @@ begin
           And (opBlock1.timestamp = opBlock2.timestamp)
           And (opBlock1.compact_target = opBlock2.compact_target)
           And (opBlock1.nonce = opBlock2.nonce)
-          And (TBaseType.BinStrComp(opBlock1.block_payload,opBlock2.block_payload)=0)
-          And (TBaseType.BinStrComp(opBlock1.initial_safe_box_hash,opBlock2.initial_safe_box_hash)=0)
-          And (TBaseType.BinStrComp(opBlock1.operations_hash,opBlock2.operations_hash)=0)
-          And (TBaseType.BinStrComp(opBlock1.proof_of_work,opBlock2.proof_of_work)=0);
+          And (TBaseType.Equals(opBlock1.block_payload,opBlock2.block_payload))
+          And (TBaseType.Equals(opBlock1.initial_safe_box_hash,opBlock2.initial_safe_box_hash))
+          And (TBaseType.Equals(opBlock1.operations_hash,opBlock2.operations_hash))
+          And (TBaseType.Equals(opBlock1.proof_of_work,opBlock2.proof_of_work))
+          And ( (opBlock1.protocol_version < CT_PROTOCOL_5)
+                OR
+                (TBaseType.Equals(opBlock1.previous_proof_of_work,opBlock2.previous_proof_of_work))
+              );
 end;
 
 class function TAccountComp.EqualBlockAccounts(const blockAccount1, blockAccount2: TBlockAccount): Boolean;
@@ -1565,38 +1624,49 @@ begin
   if (ACurrentProtocol<CT_PROTOCOL_5) then begin
     // V4 and below only allows Private sales (No Swaps)
     if Not (IsAccountForPrivateSale(AAccount.accountInfo)) then Exit;
+  end else begin
+    // V5 only will allow PRIVATE SALES or SWAPS while locked
+    if (IsAccountForPublicSale(AAccount.accountInfo)) Then Exit; // Public sales not allowed
+    if (Not (IsAccountLocked(AAccount.accountInfo,ACurrentBlock))) then Exit; // Time lock expired
   end;
 
-  if (AAccount.accountInfo.state in [as_ForSale, as_ForAtomicAccountSwap]) then
+  if (AAccount.accountInfo.state in [as_ForSale, as_ForAtomicAccountSwap]) then begin
     if NOT IsValidAccountKey(AAccount.accountInfo.new_publicKey,errors) then
       exit;
+  end;
 
-   if (AAccount.accountInfo.state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) then
-     if NOT IsValidAccountInfoHashLockKey(AAccount.accountInfo, APayload) then
-       exit;
+  if (AAccount.accountInfo.state in [as_ForAtomicAccountSwap, as_ForAtomicCoinSwap]) then begin
+    if NOT IsValidAccountInfoHashLockKey(AAccount.accountInfo, APayload) then
+      exit;
+  end;
   Result := True;
 end;
 
-Class Function TAccountComp.IsOperationRecipientSignable(const ASender, ATarget : TAccount; AIncomingFunds : UInt64; ACurrentBlock : Integer; ACurrentProtocol : Word) : Boolean;
+Class Function TAccountComp.IsOperationRecipientSignable(const ASender, ATarget : TAccount; ACurrentBlock : Integer; ACurrentProtocol : Word) : Boolean;
 begin
   // V5 - Allow recipient-signed operations under following conditions:
   //  - Sender Account = Target Account
-  //  - Target Account is listed for SWAP (Atomic Swap)
-  //  - Target Account is time-locked to new-owner-key R and time-lock is active
-  //  - (Target.Balance + Operation.Quantity) >= Target.SalePrice
-  //  - Signed by new-owner-key R
+  //  - Target Account is listed:
+  //      - Listed for ACCOUNT SWAP
+  //      or
+  //      - Listed for PRIVATE SALE
+  //  - Target Account is time-locked and time-lock is active
   //
   //  This allows following use-cases:
   //  - Private account sale where buyer does not have existing account to initiate transaction
   //  - Atomic account swap where counterparty does not have existing account to initiate transaction
+  //
+  // Note: this does not validate recipient signature, only determines if
+  // it is recipient signable
+  //
   Result := (ACurrentProtocol >= CT_PROTOCOL_5) AND
             (ASender.account = ATarget.account) AND
             TAccountComp.IsAccountLocked(ATarget.accountInfo, ACurrentBlock) AND
-            TAccountComp.IsAccountForSwap(ATarget.accountInfo) AND
-           ((ATarget.balance + AIncomingFunds) >= (ATarget.accountInfo.price));
-
- // Note: this does not validate recipient signature, only determines if
- // it is recipient signable
+            (
+              (TAccountComp.IsAccountForAccountSwap(ATarget.accountInfo))
+              OR
+              (TAccountComp.IsAccountForPrivateSale(ATarget.accountInfo))
+            );
 end;
 
 class function TAccountComp.IsAccountLocked(const AccountInfo: TAccountInfo; blocks_count: Cardinal): Boolean;
@@ -1619,6 +1689,9 @@ begin
   TStreamOp.WriteAnsiString(stream, operationBlock.initial_safe_box_hash);
   TStreamOp.WriteAnsiString(stream, operationBlock.operations_hash);
   TStreamOp.WriteAnsiString(stream, operationBlock.proof_of_work);
+  if (operationBlock.protocol_version>=CT_PROTOCOL_5) then begin
+    TStreamOp.WriteAnsiString(stream, operationBlock.previous_proof_of_work);
+  end;
 end;
 
 class function TAccountComp.LoadTOperationBlockFromStream(const stream: TStream; var operationBlock: TOperationBlock): Boolean;
@@ -1638,13 +1711,16 @@ begin
   if TStreamOp.ReadAnsiString(stream, operationBlock.initial_safe_box_hash) < 0 then Exit;
   if TStreamOp.ReadAnsiString(stream, operationBlock.operations_hash) < 0 then Exit;
   if TStreamOp.ReadAnsiString(stream, operationBlock.proof_of_work) < 0 then Exit;
+  if operationBlock.protocol_version>=CT_PROTOCOL_5 then begin
+    if TStreamOp.ReadAnsiString(stream, operationBlock.previous_proof_of_work) < 0 then Exit;
+  end;
   Result := True;
 end;
 
 class function TAccountComp.AccountToTxt(const Account: TAccount): String;
 begin
-  Result := Format('%s Balance:%s N_Op:%d UpdB:%d Type:%d Name:%s PK:%s Data:%s Seal:%s',[AccountNumberToAccountTxtNumber(Account.account),
-    FormatMoney(Account.balance),Account.n_operation,Account.updated_block,Account.account_type,
+  Result := Format('%s Balance:%s N_Op:%d UpdB:%d UpdBA:%d Type:%d Name:%s PK:%s Data:%s Seal:%s',[AccountNumberToAccountTxtNumber(Account.account),
+    FormatMoney(Account.balance),Account.n_operation,Account.updated_on_block,Account.updated_on_block_active_mode,Account.account_type,
       Account.name.ToPrintable,TCrypto.ToHexaString(TAccountComp.AccountInfo2RawString(Account.accountInfo)),
       Account.account_data.ToHexaString,Account.account_seal.ToHexaString ]);
 end;
@@ -1706,7 +1782,7 @@ end;
 class function TAccountComp.IsValidNewAccountKey(const AAccountInfo : TAccountInfo; const ANewKey : TAccountKey; AProtocolVersion : Integer) : Boolean;
 begin
   Result :=False;
-  if AProtocolVersion <= CT_PROTOCOL_5 then begin
+  if AProtocolVersion < CT_PROTOCOL_5 then begin
     // V2 - V4 Rules
     // - Private Sale: non-null and must match stored new-key in account_info
     // - Public Sale: non-null
@@ -1906,7 +1982,8 @@ Type
     accountInfo : TDynRawBytes;
     {$ENDIF}
     balance: UInt64;
-    updated_block: Cardinal;
+    updated_on_block: Cardinal;
+    updated_on_block_active_mode: Cardinal;
     n_operation: Cardinal;
     name : TRawBytes;
     account_type : Word;
@@ -1933,6 +2010,7 @@ Type
     initial_safe_box_hash: T32Bytes; // 32 direct bytes instead of use an AnsiString (-8 bytes)
     operations_hash: T32Bytes;       // 32 direct bytes instead of use an AnsiString (-8 bytes)
     proof_of_work: T32Bytes;         // 32 direct bytes instead of use an AnsiString (-8 bytes)
+    previous_proof_of_work: T32Bytes;
   end;
 
   TMemBlockAccount = Record // TBlockAccount with less memory usage
@@ -1969,7 +2047,8 @@ begin
   TBaseType.To256RawBytes(raw,dest.accountInfo);
   {$ENDIF}
   dest.balance := source.balance;
-  dest.updated_block:=source.updated_block;
+  dest.updated_on_block:=source.updated_on_block;
+  dest.updated_on_block_active_mode:=source.updated_on_block_active_mode;
   dest.n_operation:=source.n_operation;
   dest.name:=source.name;
   dest.account_type:=source.account_type;
@@ -2001,7 +2080,8 @@ begin
   TAccountComp.RawString2AccountInfo(raw,dest.accountInfo);
   {$ENDIF}
   dest.balance := source.balance;
-  dest.updated_block:=source.updated_block;
+  dest.updated_on_block:=source.updated_on_block;
+  dest.updated_on_block_active_mode:=source.updated_on_block_active_mode;
   dest.n_operation:=source.n_operation;
   dest.name:=source.name;
   dest.account_type:=source.account_type;
@@ -2037,6 +2117,7 @@ Begin
   TBaseType.To32Bytes(source.blockchainInfo.initial_safe_box_hash,dest.blockchainInfo.initial_safe_box_hash);
   TBaseType.To32Bytes(source.blockchainInfo.operations_hash,dest.blockchainInfo.operations_hash);
   TBaseType.To32Bytes(source.blockchainInfo.proof_of_work,dest.blockchainInfo.proof_of_work);
+  TBaseType.To32Bytes(source.blockchainInfo.previous_proof_of_work,dest.blockchainInfo.previous_proof_of_work);
 
   for i := Low(source.accounts) to High(source.accounts) do begin
     ToTMemAccount(source.accounts[i],dest.accounts[i]);
@@ -2073,6 +2154,11 @@ begin
   TBaseType.ToRawBytes(source.blockchainInfo.initial_safe_box_hash,dest.blockchainInfo.initial_safe_box_hash);
   TBaseType.ToRawBytes(source.blockchainInfo.operations_hash,dest.blockchainInfo.operations_hash);
   TBaseType.ToRawBytes(source.blockchainInfo.proof_of_work,dest.blockchainInfo.proof_of_work);
+  if (source.blockchainInfo.protocol_version>=CT_PROTOCOL_5) then begin
+    TBaseType.ToRawBytes(source.blockchainInfo.previous_proof_of_work,dest.blockchainInfo.previous_proof_of_work);
+  end else begin
+    dest.blockchainInfo.previous_proof_of_work := Nil;
+  end;
 
   for i := Low(source.accounts) to High(source.accounts) do begin
     ToTAccount(source.accounts[i],(block_number*CT_AccountsPerBlock)+i,dest.accounts[i]);
@@ -2178,7 +2264,8 @@ begin
     Result.accounts[i] := CT_Account_NUL;
     Result.accounts[i].account := base_addr + i;
     Result.accounts[i].accountInfo.state := as_Normal;
-    Result.accounts[i].updated_block := BlocksCount;
+    Result.accounts[i].updated_on_block := BlocksCount;
+    Result.accounts[i].updated_on_block_active_mode := BlocksCount;
     Result.accounts[i].n_operation := 0;
     if (acc_4_for_dev) And (i=CT_AccountsPerBlock-1) then begin
       Result.accounts[i].accountInfo.accountKey := account_dev.accountInfo.accountKey;
@@ -2741,8 +2828,10 @@ procedure TPCSafeBox.CommitToPrevious;
           blockAccount.accounts[j].account_data,
           blockAccount.accounts[j].account_seal,
           aus_commiting_from_otherchain,
-          blockAccount.accounts[j].updated_block,
-          blockAccount.accounts[j].previous_updated_block
+          blockAccount.accounts[j].updated_on_block,
+          blockAccount.accounts[j].updated_on_block_active_mode,
+          blockAccount.accounts[j].previous_updated_block,
+          False // Not used when aus_commiting_from_otherchain
           );
       end;
     end;
@@ -2875,9 +2964,10 @@ procedure TPCSafeBox.RollBackToSnapshot(snapshotBlock: Cardinal);
            blockAccount.accounts[j].account_data,
            blockAccount.accounts[j].account_seal,
            aus_rollback,
-           blockAccount.accounts[j].updated_block,
-           blockAccount.accounts[j].previous_updated_block
-           );
+           blockAccount.accounts[j].updated_on_block,
+           blockAccount.accounts[j].updated_on_block_active_mode,
+           blockAccount.accounts[j].previous_updated_block,
+           False); // Not used when aus_rollback
        end;
      end;
    end;
@@ -3035,6 +3125,8 @@ end;
 function TPCSafeBox.DoUpgradeToProtocol5: Boolean;
 var LAux : TRawBytes;
   LBlockNumber : Cardinal;
+  i : Integer;
+  LPtrBlockAccount : PBlockAccount;
 begin
   Result := True;
   // V5 adds a new "CalcSafeBoxHash" method, so need to recalc
@@ -3045,6 +3137,11 @@ begin
   FBufferBlocksHash.Clear;
   FBufferBlocksHash.SafeBoxHashCalcType := sbh_Merkle_Root_Hash;
   for LBlockNumber := 0 to BlocksCount - 1 do begin
+    LPtrBlockAccount := PBlockAccount(FBlockAccountsList.Items[LBlockNumber]);
+    for i := Low(LPtrBlockAccount^.accounts) to High(LPtrBlockAccount^.accounts) do begin
+      // Set the initial "updated_on_block_active_mode" value at least like "updated_on_block"
+      LPtrBlockAccount^.accounts[i].updated_on_block_active_mode := LPtrBlockAccount^.accounts[i].updated_on_block;
+    end;
     {$IFDEF uselowmem}
     TBaseType.To32Bytes(CalcBlockHash( Block(LBlockNumber), CT_PROTOCOL_5),PBlockAccount(FBlockAccountsList.Items[LBlockNumber])^.block_hash);
     FBufferBlocksHash.Add( PBlockAccount(FBlockAccountsList.Items[LBlockNumber])^.block_hash[0], 32 );
@@ -3065,7 +3162,7 @@ end;
 function TPCSafeBox.LoadSafeBoxFromStream(Stream : TStream; checkAll : Boolean; checkSafeboxHash : TRawBytes; progressNotify : TProgressNotify; previousCheckedSafebox : TPCSafebox; var LastReadBlock : TBlockAccount; var errors : String) : Boolean;
 Var
   iblock,iacc : Cardinal;
-  raw : TRawBytes;
+  raw, LPreviousProofOfWork : TRawBytes;
   block : TBlockAccount;
   P : PBlockAccount;
   i,j : Integer;
@@ -3136,6 +3233,7 @@ begin
       try
         if Assigned(LPCOperationsBlockValidator) then
           LPCOperationsBlockValidator.StartThreads;
+        LPreviousProofOfWork := Nil;
       for iblock := 0 to sbHeader.blockscount-1 do begin
         if (Assigned(progressNotify)) and ((TPlatform.GetElapsedMilliseconds(tc)>=500)) then begin
           tc := TPlatform.GetTickCount;
@@ -3158,7 +3256,10 @@ begin
           if TStreamOp.ReadAnsiString(Stream,raw)<0 then exit;
           block.accounts[iacc].accountInfo := TAccountComp.RawString2AccountInfo(raw);
           if Stream.Read(block.accounts[iacc].balance,SizeOf(UInt64))<SizeOf(UInt64) then exit;
-          if Stream.Read(block.accounts[iacc].updated_block,4)<4 then exit;
+          if Stream.Read(block.accounts[iacc].updated_on_block,4)<4 then exit;
+          if FCurrentProtocol>=CT_PROTOCOL_5 then begin
+            if Stream.Read(block.accounts[iacc].updated_on_block_active_mode,4)<4 then exit;
+          end;
           if Stream.Read(block.accounts[iacc].n_operation,4)<4 then exit;
           If FCurrentProtocol>=CT_PROTOCOL_2 then begin
             if TStreamOp.ReadAnsiString(Stream,block.accounts[iacc].name)<0 then exit;
@@ -3241,6 +3342,20 @@ begin
             end;
           end;
         end;
+        // Checking previous_proof_of_work
+        if block.blockchainInfo.protocol_version>=CT_PROTOCOL_5 then begin
+          if (Not TBaseType.Equals(block.blockchainInfo.previous_proof_of_work,LPreviousProofOfWork)) then begin
+            errors := errors + ' > previous_proof_of_work does not match!';
+            Exit;
+          end;
+        end else begin
+          // Ensure no value on "previous_proof_of_work" field
+          if (Length(block.blockchainInfo.previous_proof_of_work)>0)
+            and (Not TBaseType.Equals(block.blockchainInfo.previous_proof_of_work,LPreviousProofOfWork)) then begin
+            errors := errors + ' > contains previous_proof_of_work on protocol<5 different than needed!';
+            Exit;
+          end;
+        end;
         // Add
         New(P);
         ToTMemBlockAccount(block,P^);
@@ -3261,6 +3376,8 @@ begin
             FCurrentProtocol := CT_PROTOCOL_5;
           end;
         end;
+        // Assign to previous
+        LPreviousProofOfWork := block.blockchainInfo.proof_of_work;
       end;
         if Assigned(LPCOperationsBlockValidator) then begin
           repeat
@@ -3273,7 +3390,7 @@ begin
               if (Assigned(progressNotify)) and ((TPlatform.GetElapsedMilliseconds(tc)>=500)) then begin
                 tc := TPlatform.GetTickCount;
                 progressNotify(Self,Format('Validating OperationBlock info %d/%d',[LValidatedOPOk,LValidatedOPOk+LValidatedOPPending]),LValidatedOPOk,LValidatedOPOk+LValidatedOPPending);
-              end else Sleep(100)
+              end else Sleep(10)
             end;
           until LValidatedOPPending<=0 ;
         end;
@@ -3414,7 +3531,10 @@ begin
       Stream.Write(b.accounts[iacc].account,Sizeof(b.accounts[iacc].account));
       TStreamOp.WriteAnsiString(Stream,TAccountComp.AccountInfo2RawString(b.accounts[iacc].accountInfo));
       Stream.Write(b.accounts[iacc].balance,Sizeof(b.accounts[iacc].balance));
-      Stream.Write(b.accounts[iacc].updated_block,Sizeof(b.accounts[iacc].updated_block));
+      Stream.Write(b.accounts[iacc].updated_on_block,Sizeof(b.accounts[iacc].updated_on_block));
+      if FCurrentProtocol>=CT_PROTOCOL_5 then begin
+        Stream.Write(b.accounts[iacc].updated_on_block_active_mode,Sizeof(b.accounts[iacc].updated_on_block_active_mode));
+      end;
       Stream.Write(b.accounts[iacc].n_operation,Sizeof(b.accounts[iacc].n_operation));
       If FCurrentProtocol>=CT_PROTOCOL_2 then begin
         TStreamOp.WriteAnsiString(Stream,b.accounts[iacc].name);
@@ -3790,10 +3910,11 @@ begin
       end;
     end else begin
       // If we are here protocol didn't changed... make sure it's not a upgrade block!
-      if (newOperationBlock.block = CT_Protocol_Upgrade_v2_MinBlock)
-           or (newOperationBlock.block = CT_Protocol_Upgrade_v3_MinBlock)
-           or (newOperationBlock.block = CT_Protocol_Upgrade_v4_MinBlock)
-           or (newOperationBlock.block = CT_Protocol_Upgrade_v5_MinBlock) then begin
+      if ((newOperationBlock.block = CT_Protocol_Upgrade_v2_MinBlock) and (newOperationBlock.protocol_version<>CT_PROTOCOL_2))
+           or ((newOperationBlock.block = CT_Protocol_Upgrade_v3_MinBlock) and (newOperationBlock.protocol_version<>CT_PROTOCOL_3))
+           or ((newOperationBlock.block = CT_Protocol_Upgrade_v4_MinBlock) and (newOperationBlock.protocol_version<>CT_PROTOCOL_4))
+           or ((newOperationBlock.block = CT_Protocol_Upgrade_v5_MinBlock) and (newOperationBlock.protocol_version<>CT_PROTOCOL_5))
+           then begin
          errors := Format('In block %d protocol must be upgraded! Current %d',[newOperationBlock.block,newOperationBlock.protocol_version]);
          exit;
       end;
@@ -3828,6 +3949,11 @@ begin
       exit;
     end;
   end;
+  if (newOperationBlock.protocol_version >= CT_PROTOCOL_5)
+     And (Not TBaseType.Equals(lastBlock.proof_of_work, newOperationBlock.previous_proof_of_work)) then begin
+    errors := 'Proof of work N-1 is different than newOperationBlock.previous_proof_of_work '+lastBlock.proof_of_work.ToHexaString+'<>'+newOperationBlock.previous_proof_of_work.ToHexaString;
+    Exit;
+  end;
   {$IFnDEF TESTING_NO_POW_CHECK}
   if (TBaseType.BinStrComp(newOperationBlock.proof_of_work,target_hash)>0) then begin
     errors := 'Proof of work is higher than target '+TCrypto.ToHexaString(newOperationBlock.proof_of_work)+' > '+TCrypto.ToHexaString(target_hash);
@@ -4046,7 +4172,7 @@ procedure TPCSafeBox.SearchBlockWhenOnSeparatedChain(blockNumber: Cardinal; out
     // Is valid?
     maxUB := 0;
     for j:=Low(blockAccount.accounts) to High(blockAccount.accounts) do begin
-      If blockAccount.accounts[j].updated_block>maxUB then maxUB := blockAccount.accounts[j].updated_block;
+      If blockAccount.accounts[j].updated_on_block>maxUB then maxUB := blockAccount.accounts[j].updated_on_block;
     end;
     Result := (maxUB <= FPreviousSafeboxOriginBlock);
   end;
@@ -4087,7 +4213,8 @@ end;
 procedure TPCSafeBox.UpdateAccount(account_number : Cardinal; const newAccountInfo: TAccountInfo;
   const newName : TRawBytes; newType : Word; newBalance: UInt64; newN_operation: Cardinal;
   const newAccountData, newAccountSeal : TRawBytes;
-  accountUpdateStyle : TAccountUpdateStyle; newUpdated_block, newPrevious_Updated_block : Cardinal);
+  accountUpdateStyle : TAccountUpdateStyle; newUpdated_block, newUpdated_block_active_mode, newPrevious_Updated_block : Cardinal;
+  AHasBenUpdatedOnActiveMode : Boolean);
 Var iBlock : Cardinal;
   i,j,iAccount, iDeleted, iAdded : Integer;
   lastbalance : UInt64;
@@ -4125,7 +4252,8 @@ begin
   If (accountUpdateStyle In [aus_rollback,aus_commiting_from_otherchain]) then begin
     // Directly update name and updated values
     blockAccount.accounts[iAccount].name:=newName;
-    blockAccount.accounts[iAccount].updated_block:=newUpdated_block;
+    blockAccount.accounts[iAccount].updated_on_block:=newUpdated_block;
+    blockAccount.accounts[iAccount].updated_on_block_active_mode:=newUpdated_block_active_mode;
     blockAccount.accounts[iAccount].previous_updated_block:=newPrevious_Updated_block;
   end else begin
     // Name:
@@ -4192,9 +4320,13 @@ begin
       end;
     end;
     // Will update previous_updated_block only on first time/block
-    If blockAccount.accounts[iAccount].updated_block<>BlocksCount then begin
-      blockAccount.accounts[iAccount].previous_updated_block := blockAccount.accounts[iAccount].updated_block;
-      blockAccount.accounts[iAccount].updated_block := BlocksCount;
+    If blockAccount.accounts[iAccount].updated_on_block<>BlocksCount then begin
+      blockAccount.accounts[iAccount].previous_updated_block := blockAccount.accounts[iAccount].updated_on_block;
+      blockAccount.accounts[iAccount].updated_on_block := BlocksCount;
+    end;
+    if (AHasBenUpdatedOnActiveMode) then begin
+      // This flag will indicate that this account has been used as ACTIVE MODE so needs to update on which block was updated
+      blockAccount.accounts[iAccount].updated_on_block_active_mode := BlocksCount;
     end;
   end;
 
@@ -4339,27 +4471,27 @@ begin
   // NOTE:
   // At this point, we have checked integrity, cannot check later!
 
-  UpdateSeal(LPBuyerAccount_Sealed,AOpID);
-  UpdateSeal(LPAccountToBuy_Sealed,AOpID);
-  UpdateSeal(LPSellerAccount_Sealed,AOpID);
+  UpdateSealAndActiveModeFlag(LPBuyerAccount_Sealed,AOpID,True);  // Only the buyer account is the Active account
+  UpdateSealAndActiveModeFlag(LPAccountToBuy_Sealed,AOpID,False);
+  UpdateSealAndActiveModeFlag(LPSellerAccount_Sealed,AOpID,False);
 
-  APrevious.UpdateIfLower(LPBuyerAccount^.account,LPBuyerAccount^.updated_block);
-  APrevious.UpdateIfLower(LPAccountToBuy^.account,LPAccountToBuy^.updated_block);
-  APrevious.UpdateIfLower(LPSellerAccount^.account,LPSellerAccount^.updated_block);
+  APrevious.UpdateIfLower(LPBuyerAccount^.account,LPBuyerAccount^.updated_on_block);
+  APrevious.UpdateIfLower(LPAccountToBuy^.account,LPAccountToBuy^.updated_on_block);
+  APrevious.UpdateIfLower(LPSellerAccount^.account,LPSellerAccount^.updated_on_block);
 
-  If LPBuyerAccount^.updated_block<>Origin_BlocksCount then begin
-    LPBuyerAccount^.previous_updated_block := LPBuyerAccount^.updated_block;
-    LPBuyerAccount^.updated_block := Origin_BlocksCount;
+  If LPBuyerAccount^.updated_on_block<>Origin_BlocksCount then begin
+    LPBuyerAccount^.previous_updated_block := LPBuyerAccount^.updated_on_block;
+    LPBuyerAccount^.updated_on_block := Origin_BlocksCount;
   end;
 
-  If LPAccountToBuy^.updated_block<>Origin_BlocksCount then begin
-    LPAccountToBuy^.previous_updated_block := LPAccountToBuy^.updated_block;
-    LPAccountToBuy^.updated_block := Origin_BlocksCount;
+  If LPAccountToBuy^.updated_on_block<>Origin_BlocksCount then begin
+    LPAccountToBuy^.previous_updated_block := LPAccountToBuy^.updated_on_block;
+    LPAccountToBuy^.updated_on_block := Origin_BlocksCount;
   end;
 
-  If LPSellerAccount^.updated_block<>Origin_BlocksCount then begin
-    LPSellerAccount^.previous_updated_block := LPSellerAccount^.updated_block;
-    LPSellerAccount^.updated_block := Origin_BlocksCount;
+  If LPSellerAccount^.updated_on_block<>Origin_BlocksCount then begin
+    LPSellerAccount^.previous_updated_block := LPSellerAccount^.updated_on_block;
+    LPSellerAccount^.updated_on_block := Origin_BlocksCount;
   end;
 
   // Inc buyer n_operation
@@ -4398,6 +4530,7 @@ function TPCSafeBoxTransaction.Commit(const operationBlock: TOperationBlock;
   var errors: String): Boolean;
 Var i : Integer;
   Pa : PAccount;
+  PSealed : PSealedAccount;
 begin
   Result := false;
   errors := '';
@@ -4408,7 +4541,8 @@ begin
       exit;
     end;
     for i := 0 to FOrderedList.FList.Count - 1 do begin
-      Pa := PSealedAccount (FOrderedList.FList[i])^.AccountSealed;
+      PSealed := PSealedAccount(FOrderedList.FList[i]);
+      Pa := PSealed^.AccountSealed;
       FFreezedAccounts.UpdateAccount(Pa^.account,
             Pa^.accountInfo,
             Pa^.name,
@@ -4418,7 +4552,8 @@ begin
             Pa^.account_data,
             Pa^.account_seal,
             aus_transaction_commit,
-            0,0);
+            0,0,0,
+            PSealed^.UsedAsActiveMode);
     end;
     //
     if (Origin_TotalBalance<>FTotalBalance) then begin
@@ -4641,32 +4776,37 @@ begin
     Exit;
   end;
 
-  UpdateSeal(PaccSender_Sealed,AOpID);
-  UpdateSeal(PaccTarget_Sealed,AOpID);
+  UpdateSealAndActiveModeFlag(PaccSender_Sealed,AOpID,True);
+  UpdateSealAndActiveModeFlag(PaccTarget_Sealed,AOpID,False);
 
-  previous.UpdateIfLower(PaccSender^.account,PaccSender^.updated_block);
-  previous.UpdateIfLower(PaccTarget^.account,PaccTarget^.updated_block);
+  previous.UpdateIfLower(PaccSender^.account,PaccSender^.updated_on_block);
+  previous.UpdateIfLower(PaccTarget^.account,PaccTarget^.updated_on_block);
 
-  If PaccSender^.updated_block<>Origin_BlocksCount then begin
-    PaccSender^.previous_updated_block := PaccSender^.updated_block;
-    PaccSender^.updated_block := Origin_BlocksCount;
+  If PaccSender^.updated_on_block<>Origin_BlocksCount then begin
+    PaccSender^.previous_updated_block := PaccSender^.updated_on_block;
+    PaccSender^.updated_on_block := Origin_BlocksCount;
   end;
 
-  If PaccTarget^.updated_block<>Origin_BlocksCount then begin
-    PaccTarget^.previous_updated_block := PaccTarget.updated_block;
-    PaccTarget^.updated_block := Origin_BlocksCount;
+  If PaccTarget^.updated_on_block<>Origin_BlocksCount then begin
+    PaccTarget^.previous_updated_block := PaccTarget.updated_on_block;
+    PaccTarget^.updated_on_block := Origin_BlocksCount;
   end;
 
   if (sender<>signer) then begin
-    UpdateSeal(PaccSigner_Sealed,AOpID);
-    previous.UpdateIfLower(PaccSigner^.account,PaccSigner^.updated_block);
-    if (PaccSigner^.updated_block<>Origin_BlocksCount) then begin
-      PaccSigner^.previous_updated_block := PaccSigner^.updated_block;
-      PaccSigner^.updated_block := Origin_BlocksCount;
+    UpdateSealAndActiveModeFlag(PaccSigner_Sealed,AOpID,True);
+    previous.UpdateIfLower(PaccSigner^.account,PaccSigner^.updated_on_block);
+    if (PaccSigner^.updated_on_block<>Origin_BlocksCount) then begin
+      PaccSigner^.previous_updated_block := PaccSigner^.updated_on_block;
+      PaccSigner^.updated_on_block := Origin_BlocksCount;
     end;
     PaccSigner^.n_operation := n_operation;
     PaccSigner^.balance := PaccSender^.balance - (fee);
     PaccSender^.balance := PaccSender^.balance - (amount);
+    if FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_5 then begin
+      // On Protocol 5, n_operation of the sender will be automatically updated
+      PaccSender^.n_operation := PaccSender^.n_operation + 1;
+    end;
+
   end else begin
     PaccSender^.n_operation := n_operation;
     PaccSender^.balance := PaccSender^.balance - (amount + fee);
@@ -4776,23 +4916,27 @@ begin
   // Ok, execute!
   for i:=Low(senders) to High(senders) do begin
     PaccSender := GetInternalAccount(senders[i],PaccSender_Sealed);
-    previous.UpdateIfLower(PaccSender^.account,PaccSender^.updated_block);
-    If PaccSender^.updated_block<>Origin_BlocksCount then begin
-      PaccSender^.previous_updated_block := PaccSender^.updated_block;
-      PaccSender^.updated_block := Origin_BlocksCount;
+
+    UpdateSealAndActiveModeFlag(PaccSender_Sealed,AOpID,True);
+
+    previous.UpdateIfLower(PaccSender^.account,PaccSender^.updated_on_block);
+    If PaccSender^.updated_on_block<>Origin_BlocksCount then begin
+      PaccSender^.previous_updated_block := PaccSender^.updated_on_block;
+      PaccSender^.updated_on_block := Origin_BlocksCount;
     end;
-    UpdateSeal(PaccSender_Sealed,AOpID);
     Inc(PaccSender^.n_operation);
     PaccSender^.balance := PaccSender^.balance - (sender_amounts[i]);
   end;
   for i:=Low(receivers) to High(receivers) do begin
     PaccTarget := GetInternalAccount(receivers[i],PaccTarget_Sealed);
-    previous.UpdateIfLower(PaccTarget^.account,PaccTarget^.updated_block);
-    If PaccTarget^.updated_block<>Origin_BlocksCount then begin
-      PaccTarget^.previous_updated_block := PaccTarget.updated_block;
-      PaccTarget^.updated_block := Origin_BlocksCount;
+
+    UpdateSealAndActiveModeFlag(PaccTarget_Sealed,AOpID,False);
+
+    previous.UpdateIfLower(PaccTarget^.account,PaccTarget^.updated_on_block);
+    If PaccTarget^.updated_on_block<>Origin_BlocksCount then begin
+      PaccTarget^.previous_updated_block := PaccTarget.updated_on_block;
+      PaccTarget^.updated_on_block := Origin_BlocksCount;
     end;
-    UpdateSeal(PaccTarget_Sealed,AOpID);
     PaccTarget^.balance := PaccTarget^.balance + receivers_amounts[i];
   end;
   Dec(FTotalBalance,nTotalFee);
@@ -4883,23 +5027,30 @@ begin
     Exit;
   end;
   // All Ok, can do changes
-  previous.UpdateIfLower(P_signer^.account,P_signer^.updated_block);
-  if P_signer^.updated_block <> Origin_BlocksCount then begin
-    P_signer^.previous_updated_block := P_signer^.updated_block;
-    P_signer^.updated_block := Origin_BlocksCount;
+
+  UpdateSealAndActiveModeFlag(P_signer_Sealed,AOpID,True);
+  UpdateSealAndActiveModeFlag(P_target_Sealed,AOpID,True); // BOTH signer and target are ACTIVE
+
+  previous.UpdateIfLower(P_signer^.account,P_signer^.updated_on_block);
+  if P_signer^.updated_on_block <> Origin_BlocksCount then begin
+    P_signer^.previous_updated_block := P_signer^.updated_on_block;
+    P_signer^.updated_on_block := Origin_BlocksCount;
   end;
   if (signer_account<>target_account) then begin
-    previous.UpdateIfLower(P_target^.account,P_target^.updated_block);
-    if P_target^.updated_block <> Origin_BlocksCount then begin
-      P_target^.previous_updated_block := P_target^.updated_block;
-      P_target^.updated_block := Origin_BlocksCount;
+    previous.UpdateIfLower(P_target^.account,P_target^.updated_on_block);
+    if P_target^.updated_on_block <> Origin_BlocksCount then begin
+      P_target^.previous_updated_block := P_target^.updated_on_block;
+      P_target^.updated_on_block := Origin_BlocksCount;
     end;
   end;
 
-  UpdateSeal(P_signer_Sealed,AOpID);
-  UpdateSeal(P_target_Sealed,AOpID);
-
   P_signer^.n_operation := signer_n_operation;
+  if (signer_account<>target_account) and
+     (FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_5) then begin
+      // On Protocol 5, n_operation of the target will be automatically updated
+      P_target^.n_operation := P_target^.n_operation + 1;
+  end;
+
   P_target^.accountInfo := accountInfo;
   P_target^.name := newName;
   P_target^.account_data := newData;
@@ -4910,9 +5061,12 @@ begin
   Result := true;
 end;
 
-procedure TPCSafeBoxTransaction.UpdateSeal(APtrSealedAccount: PSealedAccount; AOpID: TRawBytes);
+procedure TPCSafeBoxTransaction.UpdateSealAndActiveModeFlag(APtrSealedAccount : PSealedAccount; AOpID : TRawBytes; ASetUsedAsActiveMode : Boolean);
 begin
   FOrderedList.DoUpdateSealIfNeeded(APtrSealedAccount,AOpID);
+  if ASetUsedAsActiveMode then begin
+    APtrSealedAccount^.UsedAsActiveMode := True;
+  end;
 end;
 
 { TPCSafeBoxTransaction.TSealedAccountList }
@@ -4944,6 +5098,7 @@ begin
     New(p^.AccountSealed);
     p^.AccountSealed^ := PSealedAccount(ASource.FList[i])^.AccountSealed^;
     p^.SealChangesCounter := PSealedAccount(ASource.FList[i])^.SealChangesCounter;
+    p^.UsedAsActiveMode := PSealedAccount(ASource.FList[i])^.UsedAsActiveMode;
     FList.Add(p);
   end;
 end;
@@ -4977,8 +5132,8 @@ begin
       Inc(APtrSealedAccount^.SealChangesCounter);
       LStream := TMemoryStream.Create;
       Try
-        APtrSealedAccount^.AccountSealed.SerializeAccount(LStream,FSafeBoxTransaction.FreezedSafeBox.CurrentProtocol); // Serialize with LATEST seal value
         // New Seal = RIPEMD160(  SHA2_256(    SerializedAccount ++ OpID ++ LatestSafeboxHash  ) )
+        APtrSealedAccount^.AccountSealed.SerializeAccount(LStream,FSafeBoxTransaction.FreezedSafeBox.CurrentProtocol); // Serialize with LATEST seal value
         LStream.WriteBuffer( AOpID[0], Length(AOpID));
         LStream.WriteBuffer( FSafeBoxTransaction.FOldSafeBoxHash[0], Length(FSafeBoxTransaction.FOldSafeBoxHash) );
         APtrSealedAccount^.AccountSealed^.account_seal := TCrypto.DoRipeMD160AsRaw( TCrypto.DoSha256(LStream.Memory,LStream.Size) );
@@ -5024,6 +5179,7 @@ begin
     New( Result^.AccountSealed );
     Result^.AccountSealed^ := FSafeBoxTransaction.FreezedSafeBox.Account(account_number);
     Result^.SealChangesCounter := 0;
+    Result^.UsedAsActiveMode := False;
     FList.Insert(i,Result);
   end else begin
     Result := FList.Items[i];
@@ -5795,28 +5951,21 @@ end;
 
 procedure TAccount_Helper.SerializeAccount(AStream: TStream; current_protocol : Word);
 var LRaw : TRawBytes;
-  LTmpSeal : T20Bytes;
 begin
-  AStream.Write(Self.account,4);
-  LRaw := TAccountComp.AccountInfo2RawString(Self.accountInfo);
-  AStream.WriteBuffer(LRaw[Low(LRaw)],Length(LRaw));
-  AStream.Write(Self.balance,8);
-  AStream.Write(Self.updated_block,4);
-  AStream.Write(Self.n_operation,4);
-  if (current_protocol>=2) then begin
-      // Use new Protocol 2 fields
-    If Length(Self.name)>0 then begin
-      AStream.WriteBuffer(Self.name[Low(Self.name)],Length(Self.name));
-    end;
-    AStream.Write(Self.account_type,2);
-    if current_protocol>=5 then begin
-      // Adding PROTOCOL 5 new fields
-      If Length(Self.account_data)>0 then begin
-        AStream.WriteBuffer(Self.account_data[0],Length(Self.account_data));
+  if current_protocol>=CT_PROTOCOL_5 then TAccountComp.SaveAccountToAStream(AStream,Self,current_protocol)
+  else begin
+    AStream.Write(Self.account,4);
+    LRaw := TAccountComp.AccountInfo2RawString(Self.accountInfo);
+    AStream.WriteBuffer(LRaw[Low(LRaw)],Length(LRaw));
+    AStream.Write(Self.balance,8);
+    AStream.Write(Self.updated_on_block,4);
+    AStream.Write(Self.n_operation,4);
+    if (current_protocol>=2) then begin
+        // Use new Protocol 2 fields
+      If Length(Self.name)>0 then begin
+        AStream.WriteBuffer(Self.name[Low(Self.name)],Length(Self.name));
       end;
-      // Account Seal is allways a 20 bytes as described on PIP-0029
-      LTmpSeal := TBaseType.To20Bytes(Self.account_seal);
-      AStream.WriteBuffer(LTmpSeal[0],20);
+      AStream.Write(Self.account_type,2);
     end;
   end;
 end;

+ 56 - 41
src/core/UBlockChain.pas

@@ -237,7 +237,7 @@ Type
     Property Previous_Signer_updated_block : Cardinal read FPrevious_Signer_updated_block; // deprecated
     Property Previous_Destination_updated_block : Cardinal read FPrevious_Destination_updated_block; // deprecated
     Property Previous_Seller_updated_block : Cardinal read FPrevious_Seller_updated_block; // deprecated
-    function IsValidECDSASignature(const PubKey: TECDSA_Public; current_protocol : Word; const Signature: TECDSA_SIG): Boolean;
+    function IsValidECDSASignature(const PubKey: TECDSA_Public; const Signature: TECDSA_SIG): Boolean;
     procedure CopyUsedPubkeySignatureFrom(SourceOperation : TPCOperation); virtual;
     function SaveOperationPayloadToStream(const AStream : TStream; const APayload : TOperationPayload) : Boolean;
     function LoadOperationPayloadFromStream(const AStream : TStream; out APayload : TOperationPayload) : Boolean;
@@ -250,7 +250,7 @@ Type
     procedure AffectedAccounts(list : TList<Cardinal>); virtual; abstract;
     class function OpType: Byte; virtual; abstract;
     Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; getInfoForAllAccounts : Boolean; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; virtual; abstract;
+    Function GetDigestToSign : TRawBytes; virtual; abstract;
     function OperationAmount : Int64; virtual; abstract;
     function OperationAmountByAccount(account : Cardinal) : Int64; virtual;
     function OperationFee: Int64; virtual; abstract;
@@ -360,7 +360,7 @@ Type
     Property TotalAmount : Int64 read FTotalAmount;
     Property TotalFee : Int64 read FTotalFee;
     function SaveOperationsHashTreeToStream(Stream: TStream; SaveToStorage : Boolean): Boolean;
-    function LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; LoadProtocolVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors : String): Boolean;
+    function LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors : String): Boolean;
     function IndexOfOperation(op : TPCOperation) : Integer;
     function CountOperationsBySameSignerWithoutFee(account_number : Cardinal) : Integer;
     Procedure Delete(index : Integer);
@@ -1229,6 +1229,11 @@ 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]);
+        Tlog.NewLog(lterror,ClassName,errors);
+        Exit;
+      end;
       // Only process when in current address, prevent do it when reading operations from file
       if FOperationsHashTree.CanAddOperationToHashTree(op) then begin
         Result := op.DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, errors);
@@ -1367,6 +1372,7 @@ begin
         FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
       end;
       FOperationBlock.initial_safe_box_hash := FBank.SafeBox.SafeBoxHash;
+      FOperationBlock.previous_proof_of_work := FBank.LastOperationBlock.proof_of_work;
       If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then
         FOperationBlock.timestamp := FBank.LastOperationBlock.timestamp;
     end else begin
@@ -1376,6 +1382,7 @@ begin
       FOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash; // Nothing for first line
       FOperationBlock.protocol_version := CT_PROTOCOL_1;
       FOperationsHashTree.Max0feeOperationsBySigner := -1;
+      FOperationBlock.previous_proof_of_work := Nil;
     end;
     FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
     FOperationBlock.fee := 0;
@@ -1484,20 +1491,7 @@ end;
 class function TPCOperationsComp.EqualsOperationBlock(const OperationBlock1,
   OperationBlock2: TOperationBlock): Boolean;
 begin
-
-  Result := (OperationBlock1.block=OperationBlock2.block)
-           And (TAccountComp.EqualAccountKeys(OperationBlock1.account_key,OperationBlock2.account_key))
-           And (OperationBlock1.reward=OperationBlock2.reward)
-           And (OperationBlock1.fee=OperationBlock2.fee)
-           And (OperationBlock1.protocol_version=OperationBlock2.protocol_version)
-           And (OperationBlock1.protocol_available=OperationBlock2.protocol_available)
-           And (OperationBlock1.timestamp=OperationBlock2.timestamp)
-           And (OperationBlock1.compact_target=OperationBlock2.compact_target)
-           And (OperationBlock1.nonce=OperationBlock2.nonce)
-           And (TBaseType.Equals(OperationBlock1.block_payload,OperationBlock2.block_payload))
-           And (TBaseType.Equals(OperationBlock1.initial_safe_box_hash,OperationBlock2.initial_safe_box_hash))
-           And (TBaseType.Equals(OperationBlock1.operations_hash,OperationBlock2.operations_hash))
-           And (TBaseType.Equals(OperationBlock1.proof_of_work,OperationBlock2.proof_of_work));
+  Result := TAccountComp.EqualOperationBlocks(OperationBlock1,OperationBlock2);
 end;
 
 function TPCOperationsComp.GetAccountKey: TAccountKey;
@@ -1638,22 +1632,22 @@ begin
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.initial_safe_box_hash) < 0 then exit;
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.operations_hash) < 0 then exit;
     if TStreamOp.ReadAnsiString(Stream, FOperationBlock.proof_of_work) < 0 then exit;
+    if FOperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
+      if TStreamOp.ReadAnsiString(Stream, FOperationBlock.previous_proof_of_work) < 0 then exit;
+      load_protocol_version := FOperationBlock.protocol_version;
+    end;
     If FIsOnlyOperationBlock then begin
       Result := true;
       exit;
     end;
     //
-    if FOperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
-      load_protocol_version := FOperationBlock.protocol_version;
-    end;
-
     // Fee will be calculated for each operation. Set it to 0 and check later for integrity
     lastfee := OperationBlock.fee;
     FOperationBlock.fee := 0;
     if FOperationBlock.protocol_version>=CT_PROTOCOL_4 then begin
       FOperationsHashTree.Max0feeOperationsBySigner := 1;
     end else FOperationsHashTree.Max0feeOperationsBySigner := -1;
-    Result := FOperationsHashTree.LoadOperationsHashTreeFromStream(Stream,LoadingFromStorage,load_protocol_version,FPreviousUpdatedBlocks,errors);
+    Result := FOperationsHashTree.LoadOperationsHashTreeFromStream(Stream,LoadingFromStorage,FOperationBlock.protocol_version,load_protocol_version,FPreviousUpdatedBlocks,errors);
     if not Result then begin
       exit;
     end;
@@ -1744,14 +1738,17 @@ begin
         FOperationBlock.compact_target := FBank.Safebox.GetActualCompactTargetHash(FOperationBlock.protocol_version);
       end;
       FOperationBlock.initial_safe_box_hash := FBank.SafeBox.SafeBoxHash;
-      If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then
+      If FBank.LastOperationBlock.timestamp>FOperationBlock.timestamp then begin
         FOperationBlock.timestamp := FBank.LastOperationBlock.timestamp;
+      end;
+      FOperationBlock.previous_proof_of_work := FBank.LastOperationBlock.proof_of_work;
     end else begin
       FOperationBlock.block := 0;
       FOperationBlock.reward := TPascalCoinProtocol.GetRewardForNewLine(0);
       FOperationBlock.compact_target := CT_MinCompactTarget_v1;
       FOperationBlock.initial_safe_box_hash := TPCSafeBox.InitialSafeboxHash;
       FOperationBlock.protocol_version := CT_PROTOCOL_1;
+      FOperationBlock.previous_proof_of_work := Nil;
     end;
     FOperationBlock.proof_of_work:=Nil;
     FOperationBlock.protocol_available := CT_BlockChain_Protocol_Available;
@@ -1768,17 +1765,22 @@ begin
       lastn := FOperationsHashTree.OperationsCount;
       for i:=0 to lastn-1 do begin
         op := FOperationsHashTree.GetOperation(i);
-        if (aux.CanAddOperationToHashTree(op)) then begin
-          if (op.DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors)) then begin
-            if aux.AddOperationToHashTree(op) then begin
-              inc(n);
-              inc(FOperationBlock.fee,op.OperationFee);
-              {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString){$ENDIF};
-            end else begin
-              TLog.NewLog(lterror,ClassName,Format('Undo operation.DoExecute at Sanitize due limits reached. Executing %d operations',[aux.OperationsCount]));
-              FPreviousUpdatedBlocks.Clear;
-              FSafeBoxTransaction.Rollback;
-              for iUndo := 0 to aux.OperationsCount-1 do aux.GetOperation(iUndo).DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, auxs);
+        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
+                inc(n);
+                inc(FOperationBlock.fee,op.OperationFee);
+                {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,Classname,'Sanitizing (pos:'+inttostr(i+1)+'/'+inttostr(lastn)+'): '+op.ToString){$ENDIF};
+              end else begin
+                TLog.NewLog(lterror,ClassName,Format('Undo operation.DoExecute at Sanitize due limits reached. Executing %d operations',[aux.OperationsCount]));
+                FPreviousUpdatedBlocks.Clear;
+                FSafeBoxTransaction.Rollback;
+                for iUndo := 0 to aux.OperationsCount-1 do aux.GetOperation(iUndo).DoOperation(FPreviousUpdatedBlocks, FSafeBoxTransaction, auxs);
+              end;
             end;
           end;
         end;
@@ -1793,7 +1795,7 @@ begin
     Calc_Digest_Parts; // Does not need to recalc PoW
     Unlock;
   End;
-  if (n>0) then begin
+  if (n>0) or (lastn<>n) then begin
     TLog.NewLog(ltdebug,Classname,Format('Sanitize operations (before %d - after %d)',[lastn,n]));
   end;
 end;
@@ -1849,6 +1851,9 @@ begin
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.initial_safe_box_hash);
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.operations_hash);
     TStreamOp.WriteAnsiString(Stream, FOperationBlock.proof_of_work);
+    if FOperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
+      TStreamOp.WriteAnsiString(Stream, FOperationBlock.previous_proof_of_work);
+    end;
     { Basic size calculation:
     protocols : 2 words = 4 bytes
     block : 4 bytes
@@ -1889,6 +1894,9 @@ begin
   TStreamOp.WriteAnsiString(Stream, OperationBlock.initial_safe_box_hash);
   TStreamOp.WriteAnsiString(Stream, OperationBlock.operations_hash);
   TStreamOp.WriteAnsiString(Stream, OperationBlock.proof_of_work);
+  if OperationBlock.protocol_version>=CT_PROTOCOL_5 then begin
+    TStreamOp.WriteAnsiString(Stream, OperationBlock.previous_proof_of_work);
+  end;
   Result := true;
 end;
 
@@ -2011,8 +2019,12 @@ begin
     TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(SafeBoxTransaction,OperationsHashTree,Nil);
     //
     for i := 0 to Count - 1 do begin
+      if (Operation[i].ProtocolVersion<>OperationBlock.protocol_version) then begin
+        errors := 'Error executing operation invalid protocol at '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors+' Op:'+Operation[i].ToString;
+        exit;
+      end;
       If Not Operation[i].DoOperation(FPreviousUpdatedBlocks, SafeBoxTransaction,errors) then begin
-        errors := 'Error executing operation '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors;
+        errors := 'Error executing operation '+inttostr(i+1)+'/'+inttostr(Count)+': '+errors+' Op:'+Operation[i].ToString;
         exit;
       end;
     end;
@@ -2615,7 +2627,7 @@ begin
   Index := L;
 end;
 
-function TOperationsHashTree.LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; LoadProtocolVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors: String): Boolean;
+function TOperationsHashTree.LoadOperationsHashTreeFromStream(Stream: TStream; LoadingFromStorage : Boolean; ASetOperationsToProtocolVersion : Word; ALoadVersion : Word; PreviousUpdatedBlocks : TAccountPreviousBlockInfo; var errors: String): Boolean;
 Var c, i: Cardinal;
   OpType: Cardinal;
   bcop: TPCOperation;
@@ -2648,10 +2660,10 @@ begin
         errors := 'Invalid operation structure ' + inttostr(i) + '/' + inttostr(c) + ' optype not valid:' + InttoHex(OpType, 4);
         exit;
       end;
-      bcop := OpClass.Create(LoadProtocolVersion);
+      bcop := OpClass.Create(ASetOperationsToProtocolVersion);
       Try
         if LoadingFromStorage then begin
-          If not bcop.LoadFromStorage(Stream,LoadProtocolVersion,PreviousUpdatedBlocks) 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;
           end;
@@ -3093,7 +3105,7 @@ begin
   //
 end;
 
-function TPCOperation.IsValidECDSASignature(const PubKey: TECDSA_Public; current_protocol: Word; const Signature: TECDSA_SIG): Boolean;
+function TPCOperation.IsValidECDSASignature(const PubKey: TECDSA_Public; const Signature: TECDSA_SIG): Boolean;
 begin
   // Will reuse FHasValidSignature if checked previously and was True
   // Introduced on Build 4.0.2 to increase speed using MEMPOOL verified operations instead of verify again everytime
@@ -3105,7 +3117,7 @@ begin
     end;
   end;
   if (Not FHasValidSignature) then begin
-    FHasValidSignature := TCrypto.ECDSAVerify(PubKey,GetDigestToSign(current_protocol),Signature);
+    FHasValidSignature := TCrypto.ECDSAVerify(PubKey,GetDigestToSign,Signature);
     If FHasValidSignature then begin;
       FUsedPubkeyForSignature := PubKey;
     end;
@@ -3413,6 +3425,9 @@ begin
         OperationResume.Fee := 0;
         Result := true;
       end else exit;
+      if (TOpBuyAccount(Operation).Data.sender = TOpBuyAccount(Operation).Data.target) then begin
+        OperationResume.Amount := TOpBuyAccount(Operation).Data.AccountPrice;
+      end;
     End;
     CT_Op_ChangeAccountInfo : Begin
       OperationResume.DestAccount := Operation.DestinationAccount;

+ 9 - 7
src/core/UConst.pas

@@ -54,12 +54,12 @@ Const
 
   CT_FirstReward: UInt64 = 1000000; // 4 decimals... First reward = 100,0000
   CT_MinReward: UInt64 = 10000; // 4 decimals... Min reward = 1,0000
-  CT_NewLineRewardDecrease: Cardinal = {$IFDEF PRODUCTION}210240{$ELSE}{$IFDEF TESTNET}20000{$ENDIF}{$ENDIF};
+  CT_NewLineRewardDecrease: Cardinal = {$IFDEF PRODUCTION}210240{$ELSE}{$IFDEF TESTNET}10000{$ENDIF}{$ENDIF};
 
-  CT_WaitNewBlocksBeforeTransaction = 100;
+  CT_WaitNewBlocksBeforeTransaction = {$IFDEF PRODUCTION}100{$ELSE}{$IFDEF TESTNET}10{$ENDIF}{$ENDIF};
 
-  CT_RecoverFoundsWaitInactiveCount = 1051200;  // After 10 years... if an account has no operations, money will be a reward for a miner!
-  CT_MaxFutureBlocksLockedAccount = 105120; // Maximum future blocks an account can be locked
+  CT_RecoverFoundsWaitInactiveCount = {$IFDEF PRODUCTION}420480{$ELSE}{$IFDEF TESTNET}1000{$ENDIF}{$ENDIF};  // After 4 years... if an account has no operations, money will be a reward for a miner!
+  CT_MaxFutureBlocksLockedAccount = {$IFDEF PRODUCTION}105120{$ELSE}{$IFDEF TESTNET}CT_RecoverFoundsWaitInactiveCount{$ENDIF}{$ENDIF}; // Maximum future blocks an account can be locked
 
   CT_MaxTransactionAmount = 1000000000000; // ... can be deleted
   CT_MaxTransactionFee = 100000000;
@@ -127,7 +127,7 @@ Const
   CT_Protocol_Upgrade_v5_MinBlock = {$IFDEF PRODUCTION}999999999{$ELSE}500{$ENDIF}; // TODO Need define v5 for production!
 
 
-  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$05000002{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
+  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$05000003{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
 
   CT_NetProtocol_Version: Word = $0009; // Version 4.0.2 Will allow only net protocol 9
   // IMPORTANT NOTE!!!
@@ -138,7 +138,9 @@ Const
 
   CT_SafeBoxBankVersion : Word = 3; // Protocol 2 upgraded safebox version from 2 to 3
 
-  CT_MagicIdentificator: String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoinTESTNET_5.Beta.2'{$ENDIF}; //
+  CT_MagicIdentificator: String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoinTESTNET_5.Beta.3'{$ENDIF}; //
+
+  CT_PascalCoin_Data_Folder : String = {$IFDEF PRODUCTION}'PascalCoin'{$ELSE}'PascalCoin_TESTNET_5.Beta.3'{$ENDIF}; //
 
   CT_PseudoOp_Reward = $0;
   // Value of Operations type in Protocol 1
@@ -193,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.2'{$ELSE}{$ENDIF}{$ENDIF};
+  CT_ClientAppVersion : String = {$IFDEF PRODUCTION}'4.1'{$ELSE}{$IFDEF TESTNET}'TESTNET 5.Beta.3'{$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};

+ 1 - 1
src/core/UFileStorage.pas

@@ -325,7 +325,7 @@ begin
     if fs.Size>0 then begin
       if Assigned(Bank) then LCurrentProtocol := Bank.SafeBox.CurrentProtocol
       else LCurrentProtocol := CT_BUILD_PROTOCOL;
-      If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,LCurrentProtocol,Nil,errors) then begin
+      If OperationsHashTree.LoadOperationsHashTreeFromStream(fs,true,LCurrentProtocol,LCurrentProtocol, Nil,errors) then begin
         TLog.NewLog(ltInfo,ClassName,Format('DoLoadPendingBufferOperations loaded operations:%d',[OperationsHashTree.OperationsCount]));
       end else TLog.NewLog(ltError,ClassName,Format('DoLoadPendingBufferOperations ERROR (Protocol %d): loaded operations:%d errors:%s',[LCurrentProtocol,OperationsHashTree.OperationsCount,errors]));
     end;

+ 4 - 4
src/core/UNetProtocol.pas

@@ -1592,7 +1592,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
         BlocksList.Free;
       end;
     until (distinctmin=distinctmax);
-    Result := (OperationBlock.proof_of_work <> CT_OperationBlock_NUL.proof_of_work);
+    Result := (Not TBaseType.Equals(OperationBlock.proof_of_work,CT_OperationBlock_NUL.proof_of_work));
   End;
 
   procedure GetNewBank(start_block : Int64);
@@ -1792,7 +1792,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
       if (c>=safebox_blockscount) then c := safebox_blockscount-1;
       sendData.Write(c,SizeOf(c));
       if (from_block>c) or (c>=safebox_blockscount) then begin
-        errors := 'ERROR DEV 20170727-1';
+        errors := Format('ERROR DEV 20170727-1 fromblock:%d c:%d safebox_blockscount:%d',[from_block,c,safebox_blockscount]);
         Exit;
       end;
       if Connection.NetProtocolVersion.protocol_version<9 then begin
@@ -3266,7 +3266,7 @@ begin
       //
       opht := TOperationsHashTree.Create;
       try
-        If Not opht.LoadOperationsHashTreeFromStream(dataReceived,False,0,Nil,errors) then begin
+        If Not opht.LoadOperationsHashTreeFromStream(dataReceived,False,TNode.Node.Bank.SafeBox.CurrentProtocol,TNode.Node.Bank.SafeBox.CurrentProtocol,Nil,errors) then begin
           DisconnectInvalidClient(False,'Invalid operations hash tree stream: '+errors);
           Exit;
         end;
@@ -3780,7 +3780,7 @@ var operationsComp : TPCOperationsComp;
     end;
     // Now we have nfpboarr with full data
     for i := 0 to High(nfpboarr) do begin
-      auxOp := TPCOperation.GetOperationFromStreamData(Self.FNetProtocolVersion.protocol_version,  nfpboarr[i].opStreamData );
+      auxOp := TPCOperation.GetOperationFromStreamData(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)]);
         Exit;

+ 13 - 7
src/core/UNode.pas

@@ -123,6 +123,7 @@ Type
     Property UpdateBlockchain : Boolean read FUpdateBlockchain write FUpdateBlockchain;
     procedure MarkVerifiedECDSASignaturesFromMemPool(newOperationsToValidate : TPCOperationsComp);
     class function NodeVersion : String;
+    class function GetPascalCoinDataFolder : String;
   End;
 
   TThreadSafeNodeNotifyEvent = Class(TPCThread)
@@ -203,7 +204,7 @@ Type
 
 implementation
 
-Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator;
+Uses UOpTransaction, UConst, UTime, UCommon, UPCOperationsSignatureValidator, UFolderHelper;
 
 var _Node : TNode;
 
@@ -1005,15 +1006,15 @@ begin
   if account_number>=Bank.SafeBox.AccountsCount then Exit;
   if StartOperation>EndOperation then Exit;
   acc := Bank.SafeBox.Account(account_number);
-  if (acc.updated_block>0) Or (acc.account=0) then Begin
-    if (SearchBackwardsStartingAtBlock=0) Or (SearchBackwardsStartingAtBlock>=acc.updated_block) then begin
-      startBlock := acc.updated_block;
+  if (acc.updated_on_block>0) Or (acc.account=0) then Begin
+    if (SearchBackwardsStartingAtBlock=0) Or (SearchBackwardsStartingAtBlock>=acc.updated_on_block) then begin
+      startBlock := acc.updated_on_block;
       lastBalance := acc.balance;
     end else begin
       startBlock := SearchBackwardsStartingAtBlock;
       lastBalance := -1;
     end;
-    DoGetFromBlock(startBlock,lastBalance,MaxDepth,0,startBlock<>acc.updated_block);
+    DoGetFromBlock(startBlock,lastBalance,MaxDepth,0,startBlock<>acc.updated_on_block);
   end;
 end;
 
@@ -1092,7 +1093,7 @@ begin
           end;
         end;
       end;
-      block := Bank.SafeBox.Account(account).updated_block;
+      block := Bank.SafeBox.Account(account).updated_on_block;
     finally
       UnlockMempoolRead;
     end;
@@ -1207,7 +1208,7 @@ begin
       UnlockMempoolRead;
     End;
     // block=0 and not found... start searching at block updated by account updated_block
-    block := Bank.SafeBox.Account(account).updated_block;
+    block := Bank.SafeBox.Account(account).updated_on_block;
     if Bank.SafeBox.Account(account).n_operation<n_operation then exit; // n_operation is greater than found in safebox
   end;
   if (block=0) or (block>=Bank.BlocksCount) then exit;
@@ -1289,6 +1290,11 @@ begin
   end;
 end;
 
+class function TNode.GetPascalCoinDataFolder: String;
+begin
+  Result := TFolderHelper.GetDataFolder(CT_PascalCoin_Data_Folder);
+end;
+
 function TNode.LockMempoolRead: TPCOperationsComp;
 begin
   FLockMempool.Acquire;

+ 103 - 77
src/core/UOpTransaction.pas

@@ -106,7 +106,7 @@ Type
 
     Constructor CreateTransaction(ACurrentProtocol : Word; sender, n_operation, target: Cardinal; key: TECPrivateKey; amount, fee: UInt64; const payload: TOperationPayload);
     Function toString : String; Override;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    Function GetDigestToSign : TRawBytes; override;
 
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
@@ -137,7 +137,7 @@ Type
     Constructor Create(ACurrentProtocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; const payload: TOperationPayload);
     Property Data : TOpChangeKeyData read FData;
     Function toString : String; Override;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    Function GetDigestToSign : TRawBytes; override;
 
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
@@ -175,7 +175,7 @@ Type
     Constructor Create(ACurrentProtocol : word; account_number, n_operation: Cardinal; fee: UInt64; new_accountkey : TAccountKey);
     Property Data : TOpRecoverFoundsData read FData;
     Function toString : String; Override;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    Function GetDigestToSign : TRawBytes; override;
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
@@ -247,7 +247,7 @@ Type
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Property Data : TOpListAccountData read FData;
     Function toString : String; Override;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    Function GetDigestToSign : TRawBytes; override;
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
@@ -308,7 +308,7 @@ Type
       fee: UInt64; const payload: TOperationPayload);
     Property Data : TOpChangeAccountInfoData read FData;
     Function toString : String; Override;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    Function GetDigestToSign : TRawBytes; override;
 
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
@@ -354,7 +354,7 @@ Type
     Constructor CreateOpData( ACurrentProtocol : word; account_signer, account_sender, account_target : Cardinal; signer_key:TECPrivateKey; n_operation : Cardinal; dataType, dataSequence : Word; AGUID : TGUID; amount, fee : UInt64; const payload: TOperationPayload);
     Property Data : TOpDataData read FData;
     Function toString : String; Override;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    Function GetDigestToSign : TRawBytes; override;
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
 
@@ -394,7 +394,7 @@ function TOpChangeAccountInfo.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBo
 var LAccount : TAccount;
 begin
   LAccount := ASafeBoxTransaction.Account(FData.account_signer);
-  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
 end;
 
 function TOpChangeAccountInfo.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
@@ -526,7 +526,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 (AccountTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) then begin
+    If (ProtocolVersion>=CT_PROTOCOL_2) then begin
       Exit; // BUG from protocol 1
     end;
   end;
@@ -535,7 +535,7 @@ begin
     errors := 'Account signer is currently locked';
     exit;
   end;
-  if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
+  if (ProtocolVersion<CT_PROTOCOL_2) then begin
     errors := 'NOT ALLOWED ON PROTOCOL 1';
     exit;
   end;
@@ -590,13 +590,13 @@ begin
   end;
 
   // Check signature
-  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
+  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,FData.sign) then begin
     errors := 'Invalid ECDSA signature';
     Exit;
   end;
 
-  FPrevious_Signer_updated_block := account_signer.updated_block;
-  FPrevious_Destination_updated_block := account_target.updated_block;
+  FPrevious_Signer_updated_block := account_signer.updated_on_block;
+  FPrevious_Destination_updated_block := account_target.updated_on_block;
   If (public_key in FData.changes_type) then begin
     account_target.accountInfo.accountKey := FData.new_accountkey;
   end;
@@ -696,7 +696,7 @@ begin
   end;
 
   if Assigned(key) then begin
-    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(ACurrentProtocol));
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
     FHasValidSignature := true;
     FUsedPubkeyForSignature := key.PublicKey;
   end else begin
@@ -728,7 +728,7 @@ begin
      TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload.payload_raw)]);
 end;
 
-function TOpChangeAccountInfo.GetDigestToSign(current_protocol : Word): TRawBytes;
+function TOpChangeAccountInfo.GetDigestToSign: TRawBytes;
 var Stream : TMemoryStream;
   b : Byte;
 begin
@@ -749,10 +749,10 @@ begin
     TStreamOp.WriteAccountKey(Stream,FData.new_accountkey);
     TStreamOp.WriteAnsiString(Stream,FData.new_name);
     Stream.Write(FData.new_type,Sizeof(FData.new_type));
-    if (current_protocol>=CT_PROTOCOL_5) then begin
+    if (ProtocolVersion>=CT_PROTOCOL_5) then begin
       TStreamOp.WriteAnsiString(Stream,FData.new_data);
     end;
-    if (current_protocol<=CT_PROTOCOL_3) then begin
+    if (ProtocolVersion<=CT_PROTOCOL_3) then begin
       Stream.Position := 0;
       setlength(Result,Stream.Size);
       Stream.ReadBuffer(Result[Low(Result)],Stream.Size);
@@ -791,7 +791,7 @@ begin
   // V2: No need to store public key because it's at safebox. Saving at least 64 bytes!
   // FData.public_key := key.PublicKey;
   if Assigned(key) then begin
-    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(ACurrentProtocol));
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
     FHasValidSignature := true;
     FUsedPubkeyForSignature := key.PublicKey;
   end else begin
@@ -811,7 +811,7 @@ begin
   Result := false;
   AErrors := '';
   LCurrentBlock := ASafeBoxTransaction.FreezedSafeBox.BlocksCount;
-  LCurrentProtocol := ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol;
+  LCurrentProtocol := ProtocolVersion;
 
   {$region 'Common Validation'}
 
@@ -845,10 +845,16 @@ begin
   LSender := ASafeBoxTransaction.Account(FData.sender);
   LTarget := ASafeBoxTransaction.Account(FData.target);
 
-  // V5 - Allow recipient-signed transactions. This is defined as
+  // V5 - Allow recipient-signed OP_BUY operations. This is defined as
   //  - Sender Account = Target Account
-  LRecipientSignable := TAccountComp.IsOperationRecipientSignable(LSender, LTarget, FData.Amount, LCurrentBlock, LCurrentProtocol);
-  LIsCoinSwap := TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo);
+  //  - Account (sender = target) is for PRIVATE SALE or ACCOUNT SWAP
+  //  - TIME LOCK not expired
+  LRecipientSignable :=
+    ( FData.opTransactionStyle = buy_Account )
+    And (TAccountComp.IsOperationRecipientSignable(LSender, LTarget, LCurrentBlock, LCurrentProtocol));
+
+  LIsCoinSwap := TAccountComp.IsAccountForCoinSwap(LTarget.accountInfo)
+    And (TAccountComp.IsAccountForSaleOrSwapAcceptingTransactions(LTarget, LCurrentBlock, LCurrentProtocol, 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]);
@@ -869,7 +875,7 @@ begin
   end;
   LTotalAmount := FData.amount + FData.fee;
   if (LSender.balance<LTotalAmount) then begin
-    AErrors := Format('Insufficient funds %d < (%d + %d = %d)',[LSender.balance,FData.amount,FData.fee,LTotalAmount]);
+    AErrors := Format('Insufficient sender funds %d < (%d + %d = %d)',[LSender.balance,FData.amount,FData.fee,LTotalAmount]);
     Exit;
   end;
   if (LTarget.balance+FData.amount>CT_MaxWalletAmount) then begin
@@ -890,18 +896,18 @@ begin
     exit;
   end;
   // Check signature
-  if LRecipientSignable AND (NOT IsValidECDSASignature(LSender.accountInfo.new_publicKey, LCurrentProtocol, FData.sign)) then begin
+  if LRecipientSignable AND (NOT IsValidECDSASignature(LSender.accountInfo.new_publicKey, FData.sign)) then begin
     AErrors := 'Invalid recipient-signed ECDSA signature';
     Exit;
-  end else If (NOT LRecipientSignable) AND (NOT IsValidECDSASignature(LSender.accountInfo.accountkey, ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol, FData.sign)) then begin
+  end else If (NOT LRecipientSignable) AND (NOT IsValidECDSASignature(LSender.accountInfo.accountkey, FData.sign)) then begin
     AErrors := 'Invalid ECDSA signature';
     Exit;
   end;
 
   {$endregion}
 
-  FPrevious_Signer_updated_block := LSender.updated_block;
-  FPrevious_Destination_updated_block := LTarget.updated_block;
+  FPrevious_Signer_updated_block := LSender.updated_on_block;
+  FPrevious_Destination_updated_block := LTarget.updated_on_block;
 
   // Is buy account ?
   if (FData.opTransactionStyle = buy_Account ) then begin
@@ -916,9 +922,22 @@ begin
       Exit;
     end;
 
-    if (TAccountComp.IsAccountForSwap(LTarget.accountInfo) AND (LCurrentProtocol<CT_PROTOCOL_5)) then begin
-      AErrors := 'Atomic swaps are not allowed until Protocol 5';
-      exit;
+    if (LCurrentProtocol < CT_PROTOCOL_5) then begin
+      if (TAccountComp.IsAccountForSwap(LTarget.accountInfo)) then begin
+        AErrors := 'Atomic swaps are not allowed until Protocol 5';
+        exit;
+      end;
+    end else begin
+      if (Not TAccountComp.IsAccountForPublicSale(LTarget.accountInfo)) then begin
+        // On V5 cannot BUY accounts with time-lock EXPIRED  (private sale or Swaps)
+        if (Not TAccountComp.IsAccountLocked(LTarget.accountInfo,LCurrentBlock)) then begin
+          AErrors := Format('Target %s time lock expired on block %d (Current %d)',
+            [TAccountComp.AccountNumberToAccountTxtNumber(LTarget.account),
+            LTarget.accountInfo.locked_until_block,
+            LCurrentBlock]);
+          Exit;
+        end;
+      end;
     end;
 
     LSeller := ASafeBoxTransaction.Account(FData.SellerAccount);
@@ -931,14 +950,20 @@ begin
       AErrors := Format('Seller account %d is not expected account %d',[FData.SellerAccount,LTarget.accountInfo.account_to_pay]);
       exit;
     end;
-    LTotalAmount := LTarget.accountInfo.price;
-    if LRecipientSignable then
-      LTotalAmount := LTotalAmount + FData.fee;
-    
-    if (LTarget.balance + FData.amount) < LTotalAmount then begin
-      AErrors := Format('Account %d balance (%d) + amount (%d) < price (%d)',[LTarget.account,LTarget.balance,FData.amount,LTotalAmount]);
-      exit;
+
+    if (LSender.account = LTarget.account) then begin
+      // Self signed operation, amount is not used because has no effect
+      if (LSender.balance + FData.fee) < LTarget.accountInfo.price then begin
+        AErrors := Format('Self signed Account %d balance (%d) + fee (%d) < target price (%d)',[LTarget.account,LTarget.balance,FData.fee,LTarget.accountInfo.price]);
+        exit;
+      end;
+    end else begin
+      if (LTarget.balance + FData.amount) < LTarget.accountInfo.price then begin
+        AErrors := Format('Target Account %d balance (%d) + amount (%d) < target price (%d)',[LTarget.account,LTarget.balance,FData.amount,LTarget.accountInfo.price]);
+        exit;
+      end;
     end;
+
     if (FData.AccountPrice<>LTarget.accountInfo.price) then begin
       AErrors := Format('Signed price (%d) is not the same of account price (%d)',[FData.AccountPrice,LTarget.accountInfo.price]);
       exit;
@@ -954,7 +979,7 @@ begin
     If Not (TAccountComp.IsValidAccountKey(FData.new_accountkey,AErrors)) then exit; // BUG 20171511
     LBuyAccountNewPubkey := FData.new_accountkey;
     {$endregion}
-    FPrevious_Seller_updated_block := LSeller.updated_block;
+    FPrevious_Seller_updated_block := LSeller.updated_on_block;
   end else if // (is auto buy) OR (is transaction that can buy)
               (FData.opTransactionStyle = transaction_with_auto_buy_account) OR
               (FData.opTransactionStyle = transaction_with_auto_atomic_swap) OR
@@ -1007,7 +1032,7 @@ begin
     FData.AccountPrice := LTarget.accountInfo.price;
     FData.SellerAccount := LTarget.accountInfo.account_to_pay;
     LSeller := ASafeBoxTransaction.Account(LTarget.accountInfo.account_to_pay);
-    FPrevious_Seller_updated_block := LSeller.updated_block;
+    FPrevious_Seller_updated_block := LSeller.updated_on_block;
     if TAccountComp.IsAccountForCoinSwap( LTarget.accountInfo ) then begin
       // We will save extra info that account key has not changed
       FData.new_accountkey := CT_TECDSA_Public_Nul;
@@ -1109,7 +1134,7 @@ function TOpTransaction.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTrans
 var LAccount : TAccount;
 begin
   LAccount := ASafeBoxTransaction.Account(FData.sender);
-  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
 end;
 
 function TOpTransaction.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
@@ -1314,7 +1339,7 @@ begin
   end;
 end;
 
-function TOpTransaction.GetDigestToSign(current_protocol : Word): TRawBytes;
+function TOpTransaction.GetDigestToSign: TRawBytes;
 Var ms : TMemoryStream;
   b : Byte;
 begin
@@ -1325,7 +1350,7 @@ begin
     ms.Write(FData.target,Sizeof(FData.target));
     ms.Write(FData.amount,Sizeof(FData.amount));
     ms.Write(FData.fee,Sizeof(FData.fee));
-    if current_protocol>=CT_PROTOCOL_5 then begin
+    if ProtocolVersion>=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
@@ -1344,7 +1369,7 @@ begin
       if Length(FData.new_accountkey.y)>0 then
         ms.WriteBuffer(FData.new_accountkey.y[Low(FData.new_accountkey.y)],Length(FData.new_accountkey.y));
     end;
-    if (current_protocol<=CT_PROTOCOL_3) then begin
+    if (ProtocolVersion<=CT_PROTOCOL_3) then begin
       ms.Position := 0;
       SetLength(Result,ms.Size);
       ms.ReadBuffer(Result[Low(Result)],ms.Size);
@@ -1389,7 +1414,7 @@ begin
   // FData.public_key := key.PublicKey;
   FData.new_accountkey := new_account_key;
   if Assigned(key) then begin
-    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(ACurrentProtocol));
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
     FHasValidSignature := true;
     FUsedPubkeyForSignature := key.PublicKey;
   end else begin
@@ -1436,7 +1461,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 (AccountTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) then begin
+    If (ProtocolVersion>=CT_PROTOCOL_2) then begin
       Exit; // BUG from protocol 1
     end;
   end;
@@ -1449,7 +1474,7 @@ begin
     exit;
   end;
   // NEW v2 protocol protection: Does not allow to change key for same key
-  if (AccountTransaction.FreezedSafeBox.CurrentProtocol>=CT_PROTOCOL_2) then begin
+  if (ProtocolVersion>=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;
@@ -1473,20 +1498,20 @@ begin
       errors := 'Signer and target accounts have different public key';
       exit;
     end;
-    if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_2) then begin
+    if (ProtocolVersion<CT_PROTOCOL_2) then begin
       errors := 'NOT ALLOWED ON PROTOCOL 1';
       exit;
     end;
   end;
 
   // Check signature
-  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
+  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,FData.sign) then begin
     errors := 'Invalid ECDSA signature';
     Exit;
   end;
 
-  FPrevious_Signer_updated_block := account_signer.updated_block;
-  FPrevious_Destination_updated_block := account_target.updated_block;
+  FPrevious_Signer_updated_block := account_signer.updated_on_block;
+  FPrevious_Destination_updated_block := account_target.updated_on_block;
   account_target.accountInfo.accountKey := FData.new_accountkey;
   // Set to normal:
   account_target.accountInfo.state := as_Normal;
@@ -1548,7 +1573,7 @@ function TOpChangeKey.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransac
 var LAccount : TAccount;
 begin
   LAccount := ASafeBoxTransaction.Account(FData.account_signer);
-  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
 end;
 
 function TOpChangeKey.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
@@ -1660,7 +1685,7 @@ begin
     TAccountComp.FormatMoney(FData.fee),FData.n_operation,Length(FData.payload.payload_raw)]);
 end;
 
-function TOpChangeKey.GetDigestToSign(current_protocol : Word): TRawBytes;
+function TOpChangeKey.GetDigestToSign: TRawBytes;
 var ms : TMemoryStream;
   raw : TRawBytes;
   b : Byte;
@@ -1671,7 +1696,7 @@ begin
     if (FData.account_signer<>FData.account_target) then ms.Write(FData.account_target,Sizeof(FData.account_target));
     ms.Write(FData.n_operation,Sizeof(FData.n_operation));
     ms.Write(FData.fee,Sizeof(FData.fee));
-    if current_protocol>=CT_PROTOCOL_5 then begin
+    if ProtocolVersion>=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
@@ -1684,7 +1709,7 @@ begin
     raw := TAccountComp.AccountKey2RawString(FData.new_accountkey);
     if Length(raw)>0 then
       ms.WriteBuffer(raw[Low(raw)],Length(raw));
-    if (current_protocol<=CT_PROTOCOL_3) then begin
+    if (ProtocolVersion<=CT_PROTOCOL_3) then begin
       ms.Position := 0;
       SetLength(Result,ms.Size);
       ms.ReadBuffer(Result[Low(Result)],ms.Size);
@@ -1735,8 +1760,8 @@ begin
     errors := 'account is locked';
     Exit;
   end;
-  if (acc.updated_block + CT_RecoverFoundsWaitInactiveCount >= AccountTransaction.FreezedSafeBox.BlocksCount) then begin
-    errors := Format('Account is active to recover founds! Account %d Updated %d + %d >= BlockCount : %d',[FData.account,acc.updated_block,CT_RecoverFoundsWaitInactiveCount,AccountTransaction.FreezedSafeBox.BlocksCount]);
+  if (acc.updated_on_block_active_mode + CT_RecoverFoundsWaitInactiveCount >= AccountTransaction.FreezedSafeBox.BlocksCount) then begin
+    errors := Format('Account is active to recover founds! Account %d Updated %d + %d >= BlockCount : %d',[FData.account,acc.updated_on_block_active_mode,CT_RecoverFoundsWaitInactiveCount,AccountTransaction.FreezedSafeBox.BlocksCount]);
     Exit;
   end;
   if (TAccountComp.AccountBlock(FData.account) + CT_RecoverFoundsWaitInactiveCount >= AccountTransaction.FreezedSafeBox.BlocksCount) then begin
@@ -1758,7 +1783,7 @@ begin
   if Not TAccountComp.IsValidAccountKey(FData.new_accountkey,errors) then begin
     Exit;
   end;
-  FPrevious_Signer_updated_block := acc.updated_block;
+  FPrevious_Signer_updated_block := acc.updated_on_block;
   Result := AccountTransaction.UpdateAccountInfo(AccountPreviousUpdatedBlock,
     GetOpID,
     FData.account,FData.n_operation, FData.account,
@@ -1873,7 +1898,7 @@ begin
     TAccountComp.AccountKey2RawString(FData.new_accountkey).ToHexaString]);
 end;
 
-function TOpRecoverFounds.GetDigestToSign(current_protocol : Word): TRawBytes;
+function TOpRecoverFounds.GetDigestToSign: TRawBytes;
 begin
   SetLength(Result,0); // Nothing to be signed!
 end;
@@ -1893,7 +1918,8 @@ begin
   else Result := 0;
 end;
 
-function TOpListAccount.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;Var
+function TOpListAccount.DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
+Var
   account_signer, account_target : TAccount;
   LIsDelist, LIsSale, LIsPrivateSale, LIsPublicSale, LIsSwap, LIsAccountSwap, LIsCoinSwap : boolean;
   LCurrentProtocol : Integer;
@@ -1928,7 +1954,7 @@ begin
     LIsCoinSwap := false;
   end;
 
-  LCurrentProtocol := AccountTransaction.FreezedSafeBox.CurrentProtocol;
+  LCurrentProtocol := ProtocolVersion;
   if (LCurrentProtocol<CT_PROTOCOL_2) then begin
     errors := 'List/Delist Account is not allowed on Protocol 1';
     exit;
@@ -2056,13 +2082,13 @@ begin
   end;
 
   // Check signature
-  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,LCurrentProtocol,FData.sign) then begin
+  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,FData.sign) then begin
     errors := 'Invalid ECDSA signature';
     Exit;
   end;
 
-  FPrevious_Signer_updated_block := account_signer.updated_block;
-  FPrevious_Destination_updated_block := account_target.updated_block;
+  FPrevious_Signer_updated_block := account_signer.updated_on_block;
+  FPrevious_Destination_updated_block := account_target.updated_on_block;
 
   if LIsDelist then begin
     account_target.accountInfo.state := as_Normal;
@@ -2116,7 +2142,7 @@ function TOpListAccount.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTrans
 var LAccount : TAccount;
 begin
   LAccount := ASafeBoxTransaction.Account(FData.account_signer);
-  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
 end;
 
 function TOpListAccount.LoadOpFromStream(Stream: TStream; LoadExtendedData : Boolean): Boolean;
@@ -2332,7 +2358,7 @@ begin
   end;
 end;
 
-function TOpListAccount.GetDigestToSign(current_protocol : Word): TRawBytes;
+function TOpListAccount.GetDigestToSign: TRawBytes;
 var ms : TMemoryStream;
   s : TRawBytes;
   b : Byte;
@@ -2348,7 +2374,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 current_protocol>=CT_PROTOCOL_5 then begin
+    if ProtocolVersion>=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
@@ -2363,12 +2389,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 (current_protocol >= CT_PROTOCOL_5) then begin
+    if (ProtocolVersion >= 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 (current_protocol<=CT_PROTOCOL_3) then begin
+    if (ProtocolVersion<=CT_PROTOCOL_3) then begin
       ms.Position := 0;
       SetLength(Result,ms.Size);
       ms.ReadBuffer(Result[Low(Result)],ms.Size);
@@ -2423,7 +2449,7 @@ begin
 
 
   if Assigned(AKey) then begin
-    FData.sign := TCrypto.ECDSASign(AKey.PrivateKey, GetDigestToSign(ACurrentProtocol));
+    FData.sign := TCrypto.ECDSASign(AKey.PrivateKey, GetDigestToSign);
     FHasValidSignature := true;
     FUsedPubkeyForSignature := AKey.PublicKey;
   end else begin
@@ -2459,7 +2485,7 @@ begin
   FData.fee := fee;
   FData.payload := payload;
   if Assigned(key) then begin
-    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(ACurrentProtocol));
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
     FHasValidSignature := true;
     FUsedPubkeyForSignature := key.PublicKey;
   end else begin
@@ -2494,7 +2520,7 @@ begin
   FData.new_accountkey := new_public_key;
 
   if Assigned(key) then begin
-    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign(ACurrentProtocol));
+    FData.sign := TCrypto.ECDSASign(key.PrivateKey, GetDigestToSign);
     FHasValidSignature := true;
     FUsedPubkeyForSignature := key.PublicKey;
   end else begin
@@ -2527,7 +2553,7 @@ function TOpData.IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction:
 var LAccount : TAccount;
 begin
   LAccount := ASafeBoxTransaction.Account(FData.account_signer);
-  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,ASafeBoxTransaction.FreezedSafeBox.CurrentProtocol,FData.sign);
+  Result := IsValidECDSASignature(LAccount.accountInfo.accountkey,FData.sign);
 end;
 
 function TOpData.SaveOpToStream(Stream: TStream; SaveExtendedData: Boolean): Boolean;
@@ -2642,7 +2668,7 @@ function TOpData.DoOperation(
 Var account_signer, account_sender, account_target : TAccount;
 begin
   Result := false;
-  if (AccountTransaction.FreezedSafeBox.CurrentProtocol<CT_PROTOCOL_4) then begin
+  if (ProtocolVersion<CT_PROTOCOL_4) then begin
     errors := 'OpData is not allowed on Protocol < 4';
     exit;
   end;
@@ -2726,7 +2752,7 @@ begin
   end;
 
   // Check signature
-  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,AccountTransaction.FreezedSafeBox.CurrentProtocol,FData.sign) then begin
+  If Not IsValidECDSASignature(account_signer.accountInfo.accountkey,FData.sign) then begin
     errors := 'Invalid ECDSA signature';
     Exit;
   end;
@@ -2808,7 +2834,7 @@ begin
   FData.dataType:=dataType;
   FData.guid := AGUID;
   if Assigned(signer_key) then begin
-    FData.sign := TCrypto.ECDSASign(signer_key.PrivateKey, GetDigestToSign(ACurrentProtocol));
+    FData.sign := TCrypto.ECDSASign(signer_key.PrivateKey, GetDigestToSign);
     FHasValidSignature := true;
     FUsedPubkeyForSignature := signer_key.PublicKey;
   end else begin
@@ -2824,7 +2850,7 @@ begin
      TAccountComp.FormatMoney(FData.amount)]);
 end;
 
-function TOpData.GetDigestToSign(current_protocol: Word): TRawBytes;
+function TOpData.GetDigestToSign: TRawBytes;
 var Stream : TMemoryStream;
   b : Byte;
 begin
@@ -2835,7 +2861,7 @@ begin
     Stream.Write(FData.account_target,Sizeof(FData.account_target));
     Stream.Write(FData.n_operation,Sizeof(FData.n_operation));
     // VERSION 5: write the GUID to the digest
-    if current_protocol >= CT_PROTOCOL_5 then begin
+    if ProtocolVersion >= CT_PROTOCOL_5 then begin
       TStreamOp.WriteGUID(Stream,FData.guid);
     end;
     Stream.Write(FData.dataType,Sizeof(FData.dataType));
@@ -2845,7 +2871,7 @@ begin
     SaveOperationPayloadToStream(Stream,FData.payload);
     b := OpType;
     Stream.Write(b,1);
-    if (current_protocol<=CT_PROTOCOL_4) then begin
+    if (ProtocolVersion<=CT_PROTOCOL_4) then begin
       Result := TStreamOp.SaveStreamToRaw(Stream);
     end else begin
       Result := TCrypto.DoSha256(Stream.Memory,Stream.Size);

+ 7 - 4
src/core/UPCOperationsSignatureValidator.pas

@@ -150,7 +150,7 @@ class procedure TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures
 var LList : TList<TPCOperation>;
   i : Integer;
   LMultiThreadValidator : TPCOperationsSignatureValidator;
-  LValidatedOk, LValidatedError, LValidatedTotal : Integer;
+  LValidatedOk, LValidatedError, LValidatedTotal, LGlobalOperationsCount : Integer;
   LTC : TTickCount;
 begin
   if _Cpus<=0 then begin
@@ -158,9 +158,11 @@ begin
   end;
   if _Cpus<=1 then Exit;
 
+  LGlobalOperationsCount := 0;
   LList := TList<TPCOperation>.Create;
   Try
     for i := 0 to APCOperationsCompList.Count-1 do begin
+      Inc(LGlobalOperationsCount, APCOperationsCompList[i].Count );
       APCOperationsCompList[i].OperationsHashTree.GetOperationsList(LList,True);
     end;
     LTC := TPlatform.GetTickCount;
@@ -171,7 +173,7 @@ begin
       LValidatedError := LMultiThreadValidator.FValidatedErrorCount;
       LTC := TPlatform.GetElapsedMilliseconds(LTC);
       if (LValidatedTotal>0) and (LTC>0) and ((LValidatedOk>0) or (LValidatedError>0))  then begin
-        TLog.NewLog(ltdebug,ClassName,Format('Validated %d operations from %d Blocks with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,APCOperationsCompList.Count,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
+        TLog.NewLog(ltdebug,ClassName,Format('Validated %d/%d operations from %d Blocks with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,LGlobalOperationsCount,APCOperationsCompList.Count,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
       end;
     finally
       LMultiThreadValidator.Free;
@@ -187,7 +189,7 @@ end;
 class procedure TPCOperationsSignatureValidator.MultiThreadPreValidateSignatures(
   ASafeBoxTransaction: TPCSafeBoxTransaction; AOperationsHashTree: TOperationsHashTree; AProgressNotify : TProgressNotify);
 var LMultiThreadValidator : TPCOperationsSignatureValidator;
-  LValidatedOk, LValidatedError, LValidatedTotal : Integer;
+  LValidatedOk, LValidatedError, LValidatedTotal, LGlobalOperationsCount : Integer;
   LTC : TTickCount;
   LList : TList<TPCOperation>;
 begin
@@ -197,6 +199,7 @@ begin
   if _Cpus<=1 then Exit;
   if AOperationsHashTree.OperationsCount<_Cpus then Exit;   // If less than cpus, no need for multithreading...
 
+  LGlobalOperationsCount := AOperationsHashTree.OperationsCount;
   LTC := TPlatform.GetTickCount;
   LMultiThreadValidator := TPCOperationsSignatureValidator.Create(ASafeBoxTransaction,AProgressNotify);
   try
@@ -210,7 +213,7 @@ begin
       LValidatedError := LMultiThreadValidator.FValidatedErrorCount;
       LTC := TPlatform.GetElapsedMilliseconds(LTC);
       if (LValidatedTotal>0) and (LTC>0) and ((LValidatedOk>0) or (LValidatedError>0))  then begin
-        TLog.NewLog(ltdebug,ClassName,Format('Validated %d operations with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
+        TLog.NewLog(ltdebug,ClassName,Format('Validated %d/%d operations with %d signatures Ok and %d signatures Error in %d miliseconds avg %.2f op/sec',[LValidatedTotal,LGlobalOperationsCount,LValidatedOk,LValidatedError,LTC,LValidatedTotal*1000/LTC]));
       end;
     Finally
       LList.Free;

+ 1 - 1
src/core/UPCRPCOpData.pas

@@ -276,7 +276,7 @@ begin
       Exit;
     end;
     LFirst_Block_Is_Unknown := False;
-    LStartBlock := LAccount.updated_block;
+    LStartBlock := LAccount.updated_on_block;
     if LStartBlock>=ASender.Node.Bank.BlocksCount then Dec(LStartBlock); // If its updated on mempool, don't look the mempool
   end;
 

+ 1 - 0
src/core/UPoolMining.pas

@@ -690,6 +690,7 @@ begin
       response_result.GetAsVariant('initial_sbh').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.initial_safe_box_hash );
       response_result.GetAsVariant('operations_hash').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.operations_hash );
       response_result.GetAsVariant('pow').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.proof_of_work );
+      response_result.GetAsVariant('previous_pow').Value := TCrypto.ToHexaString( FNodeNotifyEvents.Node.Bank.LastOperationBlock.previous_proof_of_work );
       Client.SendJSONRPCResponse(response_result,id_value);
     Finally
       response_result.Free;

+ 4 - 3
src/core/URPC.pas

@@ -420,7 +420,8 @@ Begin
   jsonObj.GetAsVariant('balance').Value:=TAccountComp.FormatMoneyDecimal(account.balance);
   jsonObj.GetAsVariant('balance_s').Value:=TAccountComp.FormatMoney(account.balance);
   jsonObj.GetAsVariant('n_operation').Value:=account.n_operation;
-  jsonObj.GetAsVariant('updated_b').Value:=account.updated_block;
+  jsonObj.GetAsVariant('updated_b').Value:=account.updated_on_block;
+  jsonObj.GetAsVariant('n_operation_updated_block').Value:=account.updated_on_block_active_mode;
   case account.accountInfo.state of
     as_Normal : jsonObj.GetAsVariant('state').Value:='normal';
     as_ForSale : begin
@@ -529,7 +530,7 @@ begin
   jsonObject.GetAsVariant('fee').Value:=TAccountComp.FormatMoneyDecimal( multiOperation.OperationFee );
   // New params for third party signing: (3.0.2)
   if (current_protocol>CT_PROTOCOL_3) then begin
-    jsonObject.GetAsVariant('digest').Value:=TCrypto.ToHexaString(multiOperation.GetDigestToSign(current_protocol));
+    jsonObject.GetAsVariant('digest').Value:=TCrypto.ToHexaString(multiOperation.GetDigestToSign);
   end;
 
   jsonObject.GetAsVariant('senders_count').Value:=Length(multiOperation.Data.txSenders);
@@ -568,7 +569,7 @@ Begin
     ms.Position := 0;
     AOperationsHashTree := TOperationsHashTree.Create;
     if (Length(Lraw)>0) then begin
-      If not AOperationsHashTree.LoadOperationsHashTreeFromStream(ms,False,ACurrentProtocol,Nil,AErrors) then begin
+      If not AOperationsHashTree.LoadOperationsHashTreeFromStream(ms,False,ACurrentProtocol,ACurrentProtocol,Nil,AErrors) then begin
         FreeAndNil(AOperationsHashTree);
         Exit;
       end;

+ 2 - 2
src/core/UServerApp.pas

@@ -281,7 +281,7 @@ begin
   // Check OpenSSL dll
   if Not LoadSSLCrypt then raise Exception.Create('Cannot load '+SSL_C_LIB+#10+'To use this software make sure this file is available on you system or reinstall the application');
   TCrypto.InitCrypto;
-  FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+  FWalletKeys.WalletFileName := TNode.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
   // Creating Node:
   FNode := TNode.Node;
   // RPC Server
@@ -291,7 +291,7 @@ begin
   FRPC.Active:=true;
   // Check Database
   FNode.Bank.StorageClass := TFileStorage;
-  TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+  TFileStorage(FNode.Bank.Storage).DatabaseFolder := TNode.GetPascalCoinDataFolder+PathDelim+'Data';
   // Reading database
   Log(sltInfo,'Reading database and constructing PascalCoin accounts');
   FNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);

+ 2 - 2
src/core/USettings.pas

@@ -132,7 +132,7 @@ type
 implementation
 
 uses
-  Classes, SysUtils, UConst, UFolderHelper;
+  Classes, SysUtils, UConst, UNode;
 
 
 { TSettings }
@@ -140,7 +140,7 @@ uses
 class procedure TSettings.Load;
 begin
   FAppParams := TAppParams.Create(nil);
-  FAppParams.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+  FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
 end;
 
 class procedure TSettings.Save;

+ 5 - 5
src/core/UTxMultiOperation.pas

@@ -155,7 +155,7 @@ Type
     //
     Function toString : String; Override;
     Property Data : TOpMultiOperationData read FData;
-    Function GetDigestToSign(current_protocol : Word) : TRawBytes; override;
+    Function GetDigestToSign : TRawBytes; override;
 
     function IsValidSignatureBasedOnCurrentSafeboxState(ASafeBoxTransaction : TPCSafeBoxTransaction) : Boolean; override;
   End;
@@ -501,7 +501,7 @@ begin
   Result := False;
   // Do check it!
   Try
-    ophtosign := GetDigestToSign(AccountTransaction.FreezedSafeBox.CurrentProtocol);
+    ophtosign := GetDigestToSign;
     // Tx verification
     For i:=Low(FData.txSenders) to High(FData.txSenders) do begin
       acc := AccountTransaction.Account(FData.txSenders[i].Account);
@@ -784,7 +784,7 @@ begin
   If Not key.HasPrivateKey then begin
     exit;
   end;
-  raw := GetDigestToSign(current_protocol);
+  raw := GetDigestToSign;
   Try
     _sign := TCrypto.ECDSASign(key.PrivateKey,raw);
   Except
@@ -1107,7 +1107,7 @@ begin
      TAccountComp.FormatMoney(FTotalFee)]);
 end;
 
-function TOpMultiOperation.GetDigestToSign(current_protocol : Word): TRawBytes;
+function TOpMultiOperation.GetDigestToSign: TRawBytes;
 Var ms : TMemoryStream;
   rb : TRawBytes;
   old : Boolean;
@@ -1122,7 +1122,7 @@ begin
     finally
       FSaveSignatureValue:=old;
     end;
-    if (current_protocol<=CT_PROTOCOL_3) then begin
+    if (ProtocolVersion<=CT_PROTOCOL_3) then begin
       ms.Position := 0;
       SetLength(Result,ms.Size);
       ms.ReadBuffer(Result[Low(Result)],ms.Size);

+ 2 - 2
src/core/UWallet.pas

@@ -156,7 +156,7 @@ uses
   {$IFDEF INTERNAL_USE_FOLDERHELPER_UNIT}
   UFolderHelper,
   {$ENDIF}
-  UPCEncryption;
+  UPCEncryption, UNode;
 
 Const
   CT_PrivateKeyFile_Magic = 'TWalletKeys';
@@ -634,7 +634,7 @@ begin
   try
     if not Assigned(FKeys) then
       FKeys := TWalletKeysExt.Create(nil);
-    FKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+    FKeys.WalletFileName := TNode.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
   except
     on E:Exception do begin
       E.Message := 'Cannot open your wallet... Perhaps another instance of Pascal Coin is active!'+#10+#10+E.Message;

+ 4 - 4
src/core/upcdaemon.pas

@@ -147,7 +147,7 @@ var
     TLog.NewLog(ltInfo,ClassName,'RPC server is active on port '+IntToStr(port));
     If FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,true) then begin
       FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,true);
-      FRPC.LogFileName:= TFolderHelper.GetPascalCoinDataFolder+PathDelim+'pascalcoin_rpc.log';
+      FRPC.LogFileName:= TNode.GetPascalCoinDataFolder+PathDelim+'pascalcoin_rpc.log';
       TLog.NewLog(ltInfo,ClassName,'Activating RPC logs on file '+FRPC.LogFileName);
     end else begin
       FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_RPC_SAVELOGS,false);
@@ -231,7 +231,7 @@ begin
         raise Exception.Create('Cannot load '+SSL_C_LIB+#10+'To use this software make sure this file is available on you system or reinstall the application');
       end;
       TCrypto.InitCrypto;
-      FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+      FWalletKeys.WalletFileName := TNode.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
       // Creating Node:
       FNode := TNode.Node;
       {$IFDEF TESTNET}
@@ -242,7 +242,7 @@ begin
       Try
         // Check Database
         FNode.Bank.StorageClass := TFileStorage;
-        TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+        TFileStorage(FNode.Bank.Storage).DatabaseFolder := TNode.GetPascalCoinDataFolder+PathDelim+'Data';
         TFileStorage(FNode.Bank.Storage).LowMemoryUsage := FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_LOWMEMORY,False);
         // By default daemon will not download checkpoint except if specified on INI file
         TNetData.NetData.MinFutureBlocksToDownloadNewSafebox := FIniFile.ReadInteger(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_MINPENDINGBLOCKSTODOWNLOADCHECKPOINT,0);
@@ -288,7 +288,7 @@ begin
   FIniFile := TIniFile.Create(ExtractFileDir(Application.ExeName)+PathDelim+'pascalcoin_daemon.ini');
   If FIniFile.ReadBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,true) then begin
     _FLog.SaveTypes:=CT_TLogTypes_ALL;
-    _FLog.FileName:=TFolderHelper.GetPascalCoinDataFolder+PathDelim+'pascalcoin_'+FormatDateTime('yyyymmddhhnn',Now)+'.log';
+    _FLog.FileName:=TNode.GetPascalCoinDataFolder+PathDelim+'pascalcoin_'+FormatDateTime('yyyymmddhhnn',Now)+'.log';
     FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,true);
   end else begin
     FIniFile.WriteBool(CT_INI_SECTION_GLOBAL,CT_INI_IDENT_SAVELOGS,false);

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

@@ -173,7 +173,7 @@ procedure TSearchThread.BCExecute;
       If validAccKey then begin
         isValid := TAccountComp.EqualAccountKeys(account.accountInfo.accountKey,FSearchValues.inAccountKey);
       end else if (Assigned(FSearchValues.inWalletKeys)) then begin
-        isValid := FSearchValues.inWalletKeys.IndexOfAccountKey(account.accountInfo.accountKey)>=0;
+        isValid := (FSearchValues.inWalletKeys.IndexOfAccountKey(account.accountInfo.accountKey)>=0) or (FSearchValues.onlyForPrivateSaleToMe);
       end;
       If isValid And (FSearchValues.onlyForSaleOrSwap) then begin
         isValid := TAccountComp.IsAccountForSaleOrSwap(account.accountInfo);

+ 2 - 2
src/gui-classic/UFRMOperation.dfm

@@ -295,7 +295,7 @@ object FRMOperation: TFRMOperation
         Top = 7
         Width = 524
         Height = 204
-        ActivePage = tsChangeInfo
+        ActivePage = tsBuyAccount
         TabOrder = 0
         OnChange = PageControlOpTypeChange
         object tsTransaction: TTabSheet
@@ -814,7 +814,7 @@ object FRMOperation: TFRMOperation
           object lblBuyAccountErrors: TLabel
             Left = 13
             Top = 10
-            Width = 331
+            Width = 484
             Height = 13
             AutoSize = False
             Caption = 'Errors detected'

+ 30 - 5
src/gui-classic/UFRMOperation.pas

@@ -244,8 +244,18 @@ loop_start:
       account := FNode.GetMempoolAccount(_senderAccounts[iAcc]);
       If Not UpdatePayload(account, errors) then
         raise Exception.Create('Error encoding payload of sender account '+TAccountComp.AccountNumberToAccountTxtNumber(account.account)+': '+errors);
-      if NOT WalletKeys.TryGetKey(account.accountInfo.accountKey, LKey) then
-        Raise Exception.Create('Sender account private key not found in Wallet');
+      if NOT WalletKeys.TryGetKey(account.accountInfo.accountKey, LKey) then begin
+
+        if  (
+             (TAccountComp.IsAccountForPrivateSale(account.accountInfo)) or
+             (TAccountComp.IsAccountForAccountSwap(account.accountInfo))
+             )
+            and (Not TAccountComp.IsNullAccountKey(account.accountInfo.new_publicKey)) then begin
+
+          if NOT WalletKeys.TryGetKey(account.accountInfo.new_publicKey, LKey) then
+            Raise Exception.Create('New sender account private key not found in Wallet');
+        end else Raise Exception.Create('Sender account private key not found in Wallet');
+      end;
       dooperation := true;
       // Default fee
       if account.balance > uint64(DefaultFee) then _fee := DefaultFee else _fee := account.balance;
@@ -864,7 +874,7 @@ begin
       exit;
     end;
     AccountToBuy := FNode.GetMempoolAccount(c);
-    ARecipientSigned := TAccountComp.IsOperationRecipientSignable(SenderAccount, AccountToBuy, Amount, FNode.Bank.BlocksCount, FNode.Bank.SafeBox.CurrentProtocol);
+    ARecipientSigned := TAccountComp.IsOperationRecipientSignable(SenderAccount, AccountToBuy, FNode.Bank.BlocksCount, FNode.Bank.SafeBox.CurrentProtocol);
     if (SenderAccount.account = AccountToBuy.Account) AND (NOT ARecipientSigned) then begin
       errors := 'Not recipient signable';
       exit;
@@ -873,13 +883,21 @@ begin
     If not TAccountComp.IsAccountForSaleOrSwap(AccountToBuy.accountInfo) then begin
       errors := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(c)+' is not for sale or swap';
       exit;
+    end else if (TAccountComp.IsAccountForCoinSwap(AccountToBuy.accountInfo)) then begin
+      errors := 'Account '+TAccountComp.AccountNumberToAccountTxtNumber(c)+' is for '+TAccountComp.FormatMoney(AccountToBuy.accountInfo.price)+' COIN swap, cannot buy';
+      exit;
     end;
+
     If Not TAccountComp.TxtToMoney(ebBuyAmount.Text,amount) then begin
       errors := 'Invalid amount value';
       exit;
     end;
-     if (AccountToBuy.accountInfo.price>amount) AND (NOT TAccountComp.IsAccountForCoinSwap(AccountToBuy.accountInfo)) then begin
-      errors := 'Account price '+TAccountComp.FormatMoney(AccountToBuy.accountInfo.price);
+    if (AccountToBuy.accountInfo.price>amount) AND (NOT ARecipientSigned) then begin
+      errors := Format('Account price %s < (amount:%s + balance:%s = %s)',[TAccountComp.FormatMoney(AccountToBuy.accountInfo.price),
+        TAccountComp.FormatMoney(amount),
+        TAccountComp.FormatMoney(AccountToBuy.balance),
+        TAccountComp.FormatMoney(amount + AccountToBuy.balance)
+        ]);
       exit;
     end;
     if TAccountComp.IsAccountForSale(AccountToBuy.accountInfo) AND (amount+DefaultFee > SenderAccount.balance) then begin
@@ -1149,6 +1167,13 @@ begin
       for iAcc := 0 to SenderAccounts.Count - 1 do begin
         sender_account := TNode.Node.Bank.SafeBox.Account(SenderAccounts.Get(iAcc));
         iWallet := WalletKeys.IndexOfAccountKey(sender_account.accountInfo.accountKey);
+        if (iWallet<0) and (
+             (TAccountComp.IsAccountForPrivateSale(sender_account.accountInfo)) or
+             (TAccountComp.IsAccountForAccountSwap(sender_account.accountInfo))
+             )
+            and (Not TAccountComp.IsNullAccountKey(sender_account.accountInfo.new_publicKey)) then begin
+          iWallet := WalletKeys.IndexOfAccountKey(sender_account.accountInfo.new_publicKey);
+        end;
         if (iWallet<0) then begin
           errors := 'Private key of account '+TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account)+' not found in wallet';
           lblGlobalErrors.Caption := errors;

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

@@ -547,7 +547,7 @@ begin
   Try
     ms.Write(raw[1],Length(raw));
     ms.Position:=0;
-    If Not opht.LoadOperationsHashTreeFromStream(ms,false,0,Nil,errors) then Raise Exception.Create(errors);
+    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
       FOperationsHashTree.AddOperationToHashTree(opht.GetOperation(i));
     end;

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

@@ -94,7 +94,7 @@ type
 
 implementation
 
-uses UConst, UAccounts, ULog, UCrypto, UFolderHelper, USettings, UGUIUtils, UNetProtocol;
+uses UConst, UAccounts, ULog, UCrypto, UNode, USettings, UGUIUtils, UNetProtocol;
 
 {$IFnDEF FPC}
   {$R *.dfm}
@@ -148,9 +148,9 @@ end;
 procedure TFRMPascalCoinWalletConfig.bbOpenDataFolderClick(Sender: TObject);
 begin
   {$IFDEF FPC}
-  OpenDocument(pchar(TFolderHelper.GetPascalCoinDataFolder))
+  OpenDocument(pchar(TNode.GetPascalCoinDataFolder))
   {$ELSE}
-  shellexecute(0, 'open', pchar(TFolderHelper.GetPascalCoinDataFolder), nil, nil, SW_SHOW)
+  shellexecute(0, 'open', pchar(TNode.GetPascalCoinDataFolder), nil, nil, SW_SHOW)
   {$ENDIF}
 end;
 

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

@@ -143,6 +143,7 @@ Var nCounter, nTotalRound, iLastSend, i : Integer;
   errors : String;
   nAddedOperations, nMaxTransactionsValue, nExecutedSinceLastTC : Integer;
   LLastTC : TTickCount;
+  LProtocol : Word;
 begin
   operationsComp := TPCOperationsComp.Create(Nil);
   try
@@ -165,22 +166,23 @@ begin
       end;
       while (nCounter<nTotalRound) And (Not Terminated) And (FAllowExecute) do begin
         inc(nCounter);
+        LProtocol := operationsComp.OperationBlock.protocol_version;
         //
         Case Random(30) of
           0..10 : begin
             if FMaxOperationsPerSecond>0 then nMaxTransactionsValue := Random(FMaxOperationsPerSecond)+1
             else nMaxTransactionsValue := Random(200)+1;
 
-            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpTransactions(FSourceNode.Bank.SafeBox.CurrentProtocol,nMaxTransactionsValue,operationsComp,FSourceWalletKeys));
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpTransactions(LProtocol,nMaxTransactionsValue,operationsComp,FSourceWalletKeys));
           end;
           11..15 : begin
-            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpChangeKey(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys));
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpChangeKey(LProtocol,operationsComp,FSourceWalletKeys));
           end;
           18..22 : begin
-            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpListAccountForSale(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys));
+            inc(FnOperationsCreated,TRandomGenerateOperation.GenerateOpListAccountForSale(LProtocol,operationsComp,FSourceWalletKeys));
           end;
           25..29 : begin
-            If TRandomGenerateOperation.GenerateOpMultiOperation(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(FnOperationsCreated)
+            If TRandomGenerateOperation.GenerateOpMultiOperation(LProtocol,operationsComp,FSourceWalletKeys) then inc(FnOperationsCreated)
             else inc(FnOperationsCreatedFailed);
           end;
         end;

+ 28 - 0
src/gui-classic/UFRMWallet.dfm

@@ -671,6 +671,10 @@ object FRMWallet: TFRMWallet
         object tsMultiSelectAccounts: TTabSheet
           Caption = 'Selected Accounts For Batch Operation'
           ImageIndex = 1
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgSelectedAccounts: TDrawGrid
             Left = 41
             Top = 31
@@ -862,6 +866,10 @@ object FRMWallet: TFRMWallet
     object tsPendingOperations: TTabSheet
       Caption = 'Pending Operations'
       ImageIndex = 5
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object dgPendingOperations: TDrawGrid
         Left = 0
         Top = 86
@@ -908,6 +916,10 @@ object FRMWallet: TFRMWallet
     object tsBlockChain: TTabSheet
       Caption = 'Block Explorer'
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel2: TPanel
         Left = 0
         Top = 0
@@ -1000,6 +1012,10 @@ object FRMWallet: TFRMWallet
     object tsOperations: TTabSheet
       Caption = 'Operations Explorer'
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel1: TPanel
         Left = 0
         Top = 0
@@ -1048,6 +1064,10 @@ object FRMWallet: TFRMWallet
     object tsLogs: TTabSheet
       Caption = 'Logs'
       ImageIndex = 2
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object pnlTopLogs: TPanel
         Left = 0
         Top = 0
@@ -1079,6 +1099,10 @@ object FRMWallet: TFRMWallet
     object tsNodeStats: TTabSheet
       Caption = 'Node Stats'
       ImageIndex = 3
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
         857
         438)
@@ -1148,6 +1172,10 @@ object FRMWallet: TFRMWallet
     object tsMessages: TTabSheet
       Caption = 'Messages'
       ImageIndex = 6
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
         857
         438)

+ 42 - 15
src/gui-classic/UFRMWallet.pas

@@ -243,8 +243,8 @@ type
     Procedure FillAccountInformation(Const Strings : TStrings; Const AccountNumber : Cardinal);
     Procedure FillOperationInformation(Const Strings : TStrings; Const OperationResume : TOperationResume);
     Procedure InitMacOSMenu;
-    {$IFDEF TESTNET}
     Procedure InitMenuForTesting;
+    {$IFDEF TESTNET}
     Procedure Test_RandomOperations(Sender: TObject);
     Procedure Test_AskForFreeAccount(Sender: TObject);
     {$IFDEF TESTING_NO_POW_CHECK}
@@ -252,6 +252,7 @@ type
     {$ENDIF}
     {$ENDIF}
     Procedure Test_ShowPublicKeys(Sender: TObject);
+    Procedure Test_ShowOperationsInMemory(Sender: TObject);
     procedure OnAccountsGridUpdatedData(Sender : TObject);
   protected
     { Private declarations }
@@ -428,7 +429,7 @@ begin
     TCrypto.InitCrypto;
     // Read Wallet
     Try
-      FWalletKeys.WalletFileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
+      FWalletKeys.WalletFileName := TNode.GetPascalCoinDataFolder+PathDelim+'WalletKeys.dat';
     Except
       On E:Exception do begin
         E.Message := 'Cannot open your wallet... Perhaps another instance of Pascal Coin is active!'+#10+#10+E.Message;
@@ -451,7 +452,7 @@ begin
     WalletKeys.SafeBox := FNode.Bank.SafeBox;
     // Check Database
     FNode.Bank.StorageClass := TFileStorage;
-    TFileStorage(FNode.Bank.Storage).DatabaseFolder := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'Data';
+    TFileStorage(FNode.Bank.Storage).DatabaseFolder := TNode.GetPascalCoinDataFolder+PathDelim+'Data';
     TFileStorage(FNode.Bank.Storage).Initialize;
     // Init Grid
     FSelectedAccountsGrid.Node := FNode;
@@ -901,7 +902,8 @@ begin
   Strings.Add('');
   Strings.Add(Format('Current balance: %s',[TAccountComp.FormatMoney(account.balance)]));
   Strings.Add('');
-  Strings.Add(Format('Updated on block: %d  (%d blocks ago)',[account.updated_block,FNode.Bank.BlocksCount-account.updated_block]));
+  Strings.Add(Format('Updated on block: %d  (%d blocks ago)',[account.updated_on_block,FNode.Bank.BlocksCount-account.updated_on_block]));
+  Strings.Add(Format('Updated on block as active mode: %d  (%d blocks ago)',[account.updated_on_block_active_mode,FNode.Bank.BlocksCount-account.updated_on_block_active_mode]));
   Strings.Add(Format('Public key type: %s',[TAccountComp.GetECInfoTxt(account.accountInfo.accountKey.EC_OpenSSL_NID)]));
   Strings.Add(Format('Base58 Public key: %s',[TAccountComp.AccountPublicKeyExport(account.accountInfo.accountKey)]));
   if Length(account.account_data)>0 then
@@ -1056,13 +1058,13 @@ begin
 end;
 
 
-{$IFDEF TESTNET}
 procedure TFRMWallet.InitMenuForTesting;
 var mi : TMenuItem;
 begin
   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';
@@ -1070,10 +1072,6 @@ begin
   miAbout.Add(mi);
   {$ENDIF}
   mi := TMenuItem.Create(MainMenu);
-  mi.Caption:='Show public keys state';
-  mi.OnClick:=Test_ShowPublicKeys;
-  miAbout.Add(mi);
-  mi := TMenuItem.Create(MainMenu);
   mi.Caption:='Create Random operations';
   mi.OnClick:=Test_RandomOperations;
   miAbout.Add(mi);
@@ -1085,6 +1083,15 @@ 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;
+  miAbout.Add(mi);
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Show operations in memory';
+  mi.OnClick:=Test_ShowOperationsInMemory;
+  miAbout.Add(mi);
 
 end;
 
@@ -1112,6 +1119,7 @@ begin
 end;
 {$ENDIF}
 
+{$IFDEF TESTNET}
 procedure TFRMWallet.Test_RandomOperations(Sender: TObject);
 Var FRM : TFRMRandomOperations;
 begin
@@ -1135,6 +1143,27 @@ end;
 
 {$ENDIF}
 
+procedure TFRMWallet.Test_ShowOperationsInMemory(Sender: TObject);
+var LFRM : TFRMMemoText;
+  i, nOps : Integer;
+  Lslist : TStrings;
+begin
+  Lslist := TStringList.Create;
+  try
+    TPCOperationsStorage.PCOperationsStorage.GetStats(Lslist);
+    nOps := TPCOperationsStorage.PCOperationsStorage.Count;
+    LFRM := TFRMMemoText.Create(Self);
+    try
+      LFRM.InitData('Operations in Memory '+IntToStr(nOps),Lslist.Text);
+      LFRM.ShowModal;
+    finally
+      LFRM.Free;
+    end;
+  finally
+    Lslist.Free;
+  end;
+end;
+
 procedure TFRMWallet.Test_ShowPublicKeys(Sender: TObject);
 var F : TFRMMemoText;
   i : Integer;
@@ -1253,9 +1282,9 @@ begin
   FLog := TLog.Create(Self);
   FLog.OnNewLog := OnNewLog;
   FLog.SaveTypes := [];
-  If Not ForceDirectories(TFolderHelper.GetPascalCoinDataFolder) then raise Exception.Create('Cannot create dir: '+TFolderHelper.GetPascalCoinDataFolder);
+  If Not ForceDirectories(TNode.GetPascalCoinDataFolder) then raise Exception.Create('Cannot create dir: '+TNode.GetPascalCoinDataFolder);
   FAppParams := TAppParams.Create(self);
-  FAppParams.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
+  FAppParams.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'AppParams.prm';
   FNodeNotifyEvents := TNodeNotifyEvents.Create(Self);
   FNodeNotifyEvents.OnBlocksChanged := OnNewAccount;
   FNodeNotifyEvents.OnNodeMessageEvent := OnNodeMessageEvent;
@@ -1321,10 +1350,8 @@ begin
   cbHashRateUnits.Items.Add('Th/s');
   cbHashRateUnits.Items.Add('Ph/s');
   cbHashRateUnits.Items.Add('Eh/s');
-  {$IFDEF TESTNET}
   // Things for testing purposes only
   InitMenuForTesting;
-  {$ENDIF}
   {$ifdef DARWIN}
   // this is macOS specific menu layout
   InitMacOSMenu;
@@ -2320,7 +2347,7 @@ begin
   if FAppParams.ParamByName[CT_PARAM_SaveLogFiles].GetAsBoolean(false) then begin
     if FAppParams.ParamByName[CT_PARAM_SaveDebugLogs].GetAsBoolean(false) then FLog.SaveTypes := CT_TLogTypes_ALL
     else FLog.SaveTypes := CT_TLogTypes_DEFAULT;
-    FLog.FileName := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
+    FLog.FileName := TNode.GetPascalCoinDataFolder+PathDelim+'PascalCointWallet.log';
   end else begin
     FLog.SaveTypes := [];
     FLog.FileName := '';
@@ -2335,7 +2362,7 @@ begin
     finally
       FNode.UnlockMempoolWrite;
     end;
-    FNode.NodeLogFilename := TFolderHelper.GetPascalCoinDataFolder+PathDelim+'blocks.log';
+    FNode.NodeLogFilename := TNode.GetPascalCoinDataFolder+PathDelim+'blocks.log';
   end;
   if Assigned(FPoolMiningServer) then begin
     if FPoolMiningServer.Port<>FAppParams.ParamByName[CT_PARAM_JSONRPCMinerServerPort].GetAsInteger(CT_JSONRPCMinerServer_Port) then begin

+ 2 - 2
src/gui-classic/UGridUtils.pas

@@ -672,7 +672,7 @@ begin
     n_acc := AccountNumber(ARow);
     if (n_acc>=0) then begin
       BufferGetAccount(n_acc,account,LNodeBlocksCount,LNodeAccountsCount);
-      ndiff := LNodeBlocksCount - account.updated_block;
+      ndiff := LNodeBlocksCount - account.updated_on_block;
       if (gdSelected in State) then
         If (gdFocused in State) then DrawGrid.Canvas.Brush.Color := clGradientActiveCaption
         else DrawGrid.Canvas.Brush.Color := clGradientInactiveCaption
@@ -702,7 +702,7 @@ begin
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         End;
         act_updated : Begin
-          s := Inttostr(account.updated_block);
+          s := Inttostr(account.updated_on_block);
           Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
         End;
         act_n_operation : Begin

+ 5 - 9
src/libraries/pascalcoin/UFolderHelper.pas

@@ -20,8 +20,6 @@ unit UFolderHelper;
   {$MODE Delphi}
 {$ENDIF}
 
-{$I config.inc}
-
 interface
 
 Type TFileVersionInfo = record
@@ -51,7 +49,7 @@ Type TFileVersionInfo = record
     {$ENDIF}
     class function GetAppDataFolder : string; static;
   public
-    class function GetPascalCoinDataFolder : string; static;
+    class Function GetDataFolder(const AFolderName : string): string; static;
     class Function GetTFileVersionInfo(Const FileName : String) : TFileVersionInfo; static;
   end;
 
@@ -108,13 +106,11 @@ begin
 end;
 {$ENDIF}
 
-class function TFolderHelper.GetPascalCoinDataFolder: string;
+class function TFolderHelper.GetDataFolder(const AFolderName : string): string;
 begin
-  {$IFDEF TESTNET}
-  Result := GetAppDataFolder+PathDelim+'PascalCoin_TESTNET';
-  {$ELSE}
-  Result := GetAppDataFolder+PathDelim+'PascalCoin';
-  {$ENDIF}
+  if (AFolderName<>'') then
+    Result := GetAppDataFolder+PathDelim+AFolderName
+  else Result := GetAppDataFolder;
 end;
 
 class function TFolderHelper.GetTFileVersionInfo(Const FileName: String): TFileVersionInfo;