Browse Source

Merge upstream

Herman Schoenfeld 7 years ago
parent
commit
e128027946

+ 120 - 20
src/core/UAccounts.pas

@@ -63,10 +63,11 @@ Type
     Class Function TargetToCompact(target: TRawBytes): Cardinal;
     Class Function TargetFromCompact(encoded: Cardinal): TRawBytes;
     Class Function GetNewTarget(vteorical, vreal: Cardinal; Const actualTarget: TRawBytes; protocol_version : Word): TRawBytes;
-    Class Procedure CalcProofOfWork_Part1(const operationBlock : TOperationBlock; var Part1 : TRawBytes);
-    Class Procedure CalcProofOfWork_Part3(const operationBlock : TOperationBlock; var Part3 : TRawBytes);
-    Class Procedure CalcProofOfWork(const operationBlock : TOperationBlock; var PoW : TRawBytes);
+    Class Procedure CalcProofOfWork_Part1(const operationBlock : TOperationBlock; out Part1 : TRawBytes);
+    Class Procedure CalcProofOfWork_Part3(const operationBlock : TOperationBlock; out Part3 : TRawBytes);
+    Class Procedure CalcProofOfWork(const operationBlock : TOperationBlock; out PoW : TRawBytes);
     Class Function IsValidMinerBlockPayload(const newBlockPayload : TRawBytes) : Boolean;
+    class procedure GetRewardDistributionForNewBlock(const OperationBlock : TOperationBlock; out acc_0_miner_reward, acc_4_dev_reward : Int64; out acc_4_for_dev : Boolean);
   end;
 
   TAccount = Record
@@ -415,6 +416,8 @@ Type
     Property Data[index : Integer] : TAccountPreviousBlockInfoData read GetData;
     Function GetPreviousUpdatedBlock(account : Cardinal; defaultValue : Cardinal) : Cardinal;
     Function Count : Integer;
+    procedure SaveToStream(stream : TStream);
+    function LoadFromStream(stream : TStream) : Boolean;
   end;
 
   { TPCSafeBoxTransaction }
@@ -635,7 +638,7 @@ begin
   end;
 end;
 
-class procedure TPascalCoinProtocol.CalcProofOfWork_Part1(const operationBlock: TOperationBlock; var Part1: TRawBytes);
+class procedure TPascalCoinProtocol.CalcProofOfWork_Part1(const operationBlock: TOperationBlock; out Part1: TRawBytes);
 var ms : TMemoryStream;
   s : AnsiString;
 begin
@@ -657,7 +660,7 @@ begin
   end;
 end;
 
-class procedure TPascalCoinProtocol.CalcProofOfWork_Part3(const operationBlock: TOperationBlock; var Part3: TRawBytes);
+class procedure TPascalCoinProtocol.CalcProofOfWork_Part3(const operationBlock: TOperationBlock; out Part3: TRawBytes);
 var ms : TMemoryStream;
 begin
   ms := TMemoryStream.Create;
@@ -674,7 +677,7 @@ begin
   end;
 end;
 
-class procedure TPascalCoinProtocol.CalcProofOfWork(const operationBlock: TOperationBlock; var PoW: TRawBytes);
+class procedure TPascalCoinProtocol.CalcProofOfWork(const operationBlock: TOperationBlock; out PoW: TRawBytes);
 var ms : TMemoryStream;
   s : AnsiString;
 begin
@@ -717,6 +720,19 @@ begin
   Result := True;
 end;
 
+class procedure TPascalCoinProtocol.GetRewardDistributionForNewBlock(const OperationBlock : TOperationBlock; out acc_0_miner_reward, acc_4_dev_reward : Int64; out acc_4_for_dev : Boolean);
+begin
+  if OperationBlock.protocol_version<CT_PROTOCOL_3 then begin
+    acc_0_miner_reward := OperationBlock.reward + OperationBlock.fee;
+    acc_4_dev_reward := 0;
+    acc_4_for_dev := False;
+  end else begin
+    acc_4_dev_reward := (OperationBlock.reward * CT_Protocol_v3_PIP11_Percent) DIV 100;
+    acc_0_miner_reward := OperationBlock.reward + OperationBlock.fee - acc_4_dev_reward;
+    acc_4_for_dev := True;
+  end;
+end;
+
 class function TPascalCoinProtocol.GetRewardForNewLine(line_index: Cardinal): UInt64;
 Var n, i : Cardinal;
 begin
@@ -1676,32 +1692,77 @@ begin
 end;
 
 function TPCSafeBox.AddNew(const blockChain: TOperationBlock): TBlockAccount;
+{ PIP-0011 (dev reward) workflow: (** Only on V3 protocol **)
+  - Account 0 is Master Account
+  - Account 0 type field (2 bytes: 0..65535) will store a Value, this value is the "dev account"
+  - The "dev account" can be any account between 0..65535, and can be changed at any time.
+  - The 80% of the blockChain.reward + miner fees will be added on first mined account (like V1 and V2)
+  - The miner will also receive ownership of first four accounts (Before, all accounts where for miner)
+  - The "dev account" will receive the last created account ownership and the 20% of the blockChain.reward
+  - Example:
+    - Account(0).type = 12345    <-- dev account = 12345
+    - blockChain.block = 234567  <-- New block height. Accounts generated from 1172835..1172839
+    - blockChain.reward = 50 PASC
+    - blockChain.fee = 0.9876 PASC
+    - blockChain.account_key = Miner public key
+    - New generated accounts:
+      - [0] = 1172835 balance: 40.9876 owner: Miner
+      - [1] = 1172836 balance: 0       owner: Miner
+      - [2] = 1172837 balance: 0       owner: Miner
+      - [3] = 1172838 balance: 0       owner: Miner
+      - [4] = 1172839 balance: 10.0000 owner: Account 12345 owner, same owner than "dev account"
+    - Safebox balance increase: 50 PASC
+  }
 
 var i, base_addr : Integer;
   Pblock : PBlockAccount;
-  accs : Array of cardinal;
+  accs_miner, accs_dev : Array of cardinal;
   Psnapshot : PSafeboxSnapshot;
+  //
+  account_dev,
+  account_0 : TAccount;
+  //
+  acc_0_miner_reward,acc_4_dev_reward : Int64;
+  acc_4_for_dev : Boolean;
 begin
   Result := CT_BlockAccount_NUL;
   Result.blockchainInfo := blockChain;
   If blockChain.block<>BlocksCount then Raise Exception.Create(Format('ERROR DEV 20170427-2 blockchain.block:%d <> BlocksCount:%d',[blockChain.block,BlocksCount]));
   If blockChain.fee<>FTotalFee then Raise Exception.Create(Format('ERROR DEV 20170427-3 blockchain.fee:%d <> Safebox.TotalFee:%d',[blockChain.fee,FTotalFee]));
 
+  TPascalCoinProtocol.GetRewardDistributionForNewBlock(blockChain,acc_0_miner_reward,acc_4_dev_reward,acc_4_for_dev);
+  account_dev := CT_Account_NUL;
+  If (acc_4_for_dev) then begin
+    account_0 := Account(0); // Account 0 is master account, will store "dev account" in type field
+    If (AccountsCount>account_0.account_type) then begin
+      account_dev := Account(account_0.account_type);
+    end else account_dev := account_0;
+  end;
+
   base_addr := BlocksCount * CT_AccountsPerBlock;
-  setlength(accs,length(Result.accounts));
+  setlength(accs_miner,0);
+  setlength(accs_dev,0);
   for i := Low(Result.accounts) to High(Result.accounts) do begin
     Result.accounts[i] := CT_Account_NUL;
     Result.accounts[i].account := base_addr + i;
     Result.accounts[i].accountInfo.state := as_Normal;
-    Result.accounts[i].accountInfo.accountKey := blockChain.account_key;
     Result.accounts[i].updated_block := BlocksCount;
     Result.accounts[i].n_operation := 0;
-    if i=Low(Result.accounts) then begin
-      // Only first account wins the reward + fee
-      Result.accounts[i].balance := blockChain.reward + blockChain.fee;
+    if (acc_4_for_dev) And (i=CT_AccountsPerBlock-1) then begin
+      Result.accounts[i].accountInfo.accountKey := account_dev.accountInfo.accountKey;
+      SetLength(accs_dev,length(accs_dev)+1);
+      accs_dev[High(accs_dev)] := base_addr + i;
+      Result.accounts[i].balance := acc_4_dev_reward;
     end else begin
+      Result.accounts[i].accountInfo.accountKey := blockChain.account_key;
+      SetLength(accs_miner,length(accs_miner)+1);
+      accs_miner[High(accs_miner)] := base_addr + i;
+      if i=Low(Result.accounts) then begin
+        // Only first account wins the reward + fee
+        Result.accounts[i].balance := acc_0_miner_reward;
+      end else begin
+      end;
     end;
-    accs[i] := base_addr + i;
   end;
   Inc(FWorkSum,Result.blockchainInfo.compact_target);
   Result.AccumulatedWork := FWorkSum;
@@ -1717,7 +1778,12 @@ begin
   FBufferBlocksHash := FBufferBlocksHash+Result.block_hash;
   Inc(FTotalBalance,blockChain.reward + blockChain.fee);
   Dec(FTotalFee, blockChain.fee);
-  AccountKeyListAddAccounts(blockChain.account_key,accs);
+  If (length(accs_miner)>0) then begin
+    AccountKeyListAddAccounts(blockChain.account_key,accs_miner);
+  end;
+  If (length(accs_dev)>0) then begin
+    AccountKeyListAddAccounts(account_dev.accountInfo.accountKey,accs_dev);
+  end;
   // Calculating new value of safebox
   FSafeBoxHash := CalcSafeBoxHash;
 
@@ -3598,7 +3664,6 @@ end;
 function TPCSafeBoxTransaction.Commit(const operationBlock: TOperationBlock;
   var errors: AnsiString): Boolean;
 Var i : Integer;
-  B : TBlockAccount;
   Pa : PAccount;
 begin
   Result := false;
@@ -3627,11 +3692,7 @@ begin
     if (Origin_TotalFee<>FTotalFee) then begin
       TLog.NewLog(lterror,ClassName,Format('Invalid integrity fee! StrongBox:%d Transaction:%d',[Origin_TotalFee,FTotalFee]));
     end;
-    B := FFreezedAccounts.AddNew(operationBlock);
-    if (B.accounts[0].balance<>(operationBlock.reward + FTotalFee)) then begin
-      TLog.NewLog(lterror,ClassName,Format('Invalid integrity reward! Account:%d Balance:%d  Reward:%d Fee:%d (Reward+Fee:%d)',
-        [B.accounts[0].account,B.accounts[0].balance,operationBlock.reward,FTotalFee,operationBlock.reward+FTotalFee]));
-    end;
+    FFreezedAccounts.AddNew(operationBlock);
     CleanTransaction;
     //
     if (FFreezedAccounts.FCurrentProtocol<CT_PROTOCOL_2) And (operationBlock.protocol_version=CT_PROTOCOL_2) then begin
@@ -4560,6 +4621,45 @@ begin
   Result := FList.Count;
 end;
 
+procedure TAccountPreviousBlockInfo.SaveToStream(stream: TStream);
+var i : Integer;
+  c : Cardinal;
+  apbi : TAccountPreviousBlockInfoData;
+begin
+  c := Count;
+  stream.Write(c,SizeOf(c)); // Save 4 bytes for count
+  for i:=0 to Count-1 do begin
+    apbi := GetData(i);
+    stream.Write(apbi.Account,SizeOf(apbi.Account)); // 4 bytes for account
+    stream.Write(apbi.Previous_updated_block,SizeOf(apbi.Previous_updated_block)); // 4 bytes for block number
+  end;
+end;
+
+function TAccountPreviousBlockInfo.LoadFromStream(stream: TStream): Boolean;
+Var lastAcc,nposStreamStart : Int64;
+  c : Cardinal;
+  i : Integer;
+  apbi : TAccountPreviousBlockInfoData;
+begin
+  Result := False;
+  clear;
+  nposStreamStart:=stream.Position;
+  Try
+    lastAcc := -1;
+    if (stream.Read(c,SizeOf(c))<SizeOf(c)) then Exit;
+    for i:=1 to c do begin
+      if stream.Read(apbi.Account,SizeOf(apbi.Account)) < SizeOf(apbi.Account) then Exit; // 4 bytes for account
+      if stream.Read(apbi.Previous_updated_block,SizeOf(apbi.Previous_updated_block)) < SizeOf(apbi.Previous_updated_block) then Exit; // 4 bytes for block number
+      if (lastAcc >= apbi.Account) then Exit;
+      Add(apbi.Account,apbi.Previous_updated_block);
+      lastAcc := apbi.Account;
+    end;
+    Result := True;
+  finally
+    if Not Result then stream.Position:=nposStreamStart;
+  end;
+end;
+
 { TOrderedCardinalList }
 
 function TOrderedCardinalList.Add(Value: Cardinal): Integer;

+ 52 - 27
src/core/UBlockChain.pas

@@ -213,6 +213,7 @@ Type
     class function OpType: Byte; virtual; abstract;
     Class Function OperationToOperationResume(Block : Cardinal; Operation : TPCOperation; Affected_account_number : Cardinal; var OperationResume : TOperationResume) : Boolean; virtual;
     function OperationAmount : Int64; virtual; abstract;
+    function OperationAmountByAccount(account : Cardinal) : Int64; virtual;
     function OperationFee: UInt64; virtual; abstract;
     function OperationPayload : TRawBytes; virtual; abstract;
     function SignerAccount : Cardinal; virtual; abstract;
@@ -541,7 +542,7 @@ begin
       Result := true;
     Finally
       if Not Result then begin
-        NewLog(Operations, lterror, 'Invalid new block '+inttostr(Operations.OperationBlock.block)+': ' + errors);
+        NewLog(Operations, lterror, 'Invalid new block '+inttostr(Operations.OperationBlock.block)+': ' + errors+ ' > '+TPCOperationsComp.OperationBlockToText(Operations.OperationBlock));
       end;
       Operations.Unlock;
     End;
@@ -1193,23 +1194,28 @@ begin
     // In build prior to 1.0.4 soob only can have 2 values: 0 or 1
     // In build 1.0.4 soob can has 2 more values: 2 or 3
     // In build 2.0 soob can has 1 more value: 4
+    // In build 3.0 soob can hast value: 5
     // In future, old values 0 and 1 will no longer be used!
     // - Value 0 and 2 means that contains also operations
     // - Value 1 and 3 means that only contains operationblock info
     // - Value 2 and 3 means that contains protocol info prior to block number
     // - Value 4 means that is loading from storage using protocol v2 (so, includes always operations)
+    // - Value 5 means that is loading from storage using TAccountPreviousBlockInfo
     load_protocol_version := CT_PROTOCOL_1;
     if (soob in [0,2]) then FIsOnlyOperationBlock:=false
     else if (soob in [1,3]) then FIsOnlyOperationBlock:=true
     else if (soob in [4]) then begin
       FIsOnlyOperationBlock:=false;
       load_protocol_version := CT_PROTOCOL_2;
+    end else if (soob in [5]) then begin
+      FIsOnlyOperationBlock:=False;
+      load_protocol_version := CT_PROTOCOL_3;
     end else begin
       errors := 'Invalid value in protocol header! Found:'+inttostr(soob)+' - Check if your application version is Ok';
       exit;
     end;
 
-    if (soob in [2,3,4]) then begin
+    if (soob in [2,3,4,5]) then begin
       Stream.Read(FOperationBlock.protocol_version, Sizeof(FOperationBlock.protocol_version));
       Stream.Read(FOperationBlock.protocol_available, Sizeof(FOperationBlock.protocol_available));
     end else begin
@@ -1242,6 +1248,13 @@ begin
     if not Result then begin
       exit;
     end;
+    If load_protocol_version>=CT_PROTOCOL_3 then begin
+      Result := FPreviousUpdatedBlocks.LoadFromStream(Stream);
+      If Not Result then begin
+        errors := 'Invalid PreviousUpdatedBlock stream';
+        Exit;
+      end;
+    end;
     //
     FOperationBlock.fee := FOperationsHashTree.TotalFee;
     FOperationBlock.operations_hash := FOperationsHashTree.HashTree;
@@ -1390,6 +1403,8 @@ begin
       if (SaveToStorage) then begin
         // Introduced on protocol v2: soob = 4 when saving to storage
         soob := 4;
+        // XXXXXXXXXXXXXXXX
+        soob := 5; // V3 will always save PreviousUpdatedBlocks
       end;
     end;
     Stream.Write(soob,1);
@@ -1421,6 +1436,9 @@ begin
     }
     if (Not save_only_OperationBlock) then begin
       Result := FOperationsHashTree.SaveOperationsHashTreeToStream(Stream,SaveToStorage);
+      If (Result) And (SaveToStorage) And (soob=5) then begin
+        FPreviousUpdatedBlocks.SaveToStream(Stream);
+      end;
     end else Result := true;
   finally
     Unlock;
@@ -1944,12 +1962,11 @@ begin
       TCrypto.DoSha256(FHashTree+h,FHashTree);
     end;
     npos := list.Add(P);
-    If FindOrderedBySha(list,op.Sha256,i) then begin
-      // Is inserting a value already found!
-      auxs :=Format('MyListCount:%d OrderedBySha Pos:%d from %d Hash:%s PointsTo:%d',[list.Count,i,FListOrderedBySha256.Count,TCrypto.ToHexaString(Op.Sha256),PtrInt(FListOrderedBySha256[i])]);
-      TLog.NewLog(ltError,ClassName,'DEV ERROR 20180213-2 Inserting a duplicate Sha256! '+Op.ToString+' > '+auxs );
+    // Improvement: Will allow to add duplicate Operations, so add only first to orderedBySha
+    If Not FindOrderedBySha(list,op.Sha256,i) then begin
+      // Protection: Will add only once
+      FListOrderedBySha256.Insert(i,TObject(npos));
     end;
-    FListOrderedBySha256.Insert(i,TObject(npos));
     // Improvement TOperationsHashTree speed 2.1.6
     // Mantain an ordered Accounts list with data
     If Not FindOrderedByAccountData(list,op.SignerAccount,i) then begin
@@ -2353,26 +2370,25 @@ begin
 end;
 
 function TPCOperation.LoadFromStorage(Stream: TStream; LoadProtocolVersion:Word; APreviousUpdatedBlocks : TAccountPreviousBlockInfo): Boolean;
-Var w : Word;
-  i : Integer;
-  cAccount,cPreviousUpdated : Cardinal;
 begin
   Result := false;
-  If LoadOpFromStream(Stream, LoadProtocolVersion>=2) then begin
-    if Stream.Size - Stream.Position<8 then exit;
-    Stream.Read(FPrevious_Signer_updated_block,Sizeof(FPrevious_Signer_updated_block));
-    Stream.Read(FPrevious_Destination_updated_block,Sizeof(FPrevious_Destination_updated_block));
-    if (LoadProtocolVersion=2) then begin
-      Stream.Read(FPrevious_Seller_updated_block,Sizeof(FPrevious_Seller_updated_block));
-    end;
-    if Assigned(APreviousUpdatedBlocks) then begin
-      // Add to previous list!
-      if SignerAccount>=0 then
-        APreviousUpdatedBlocks.UpdateIfLower(SignerAccount,FPrevious_Signer_updated_block);
-      if DestinationAccount>=0 then
-        APreviousUpdatedBlocks.UpdateIfLower(DestinationAccount,FPrevious_Destination_updated_block);
-      if SellerAccount>=0 then
-        APreviousUpdatedBlocks.UpdateIfLower(SellerAccount,FPrevious_Seller_updated_block);
+  If LoadOpFromStream(Stream, LoadProtocolVersion>=CT_PROTOCOL_2) then begin
+    If LoadProtocolVersion<CT_PROTOCOL_3 then begin
+      if Stream.Size - Stream.Position<8 then exit;
+      Stream.Read(FPrevious_Signer_updated_block,Sizeof(FPrevious_Signer_updated_block));
+      Stream.Read(FPrevious_Destination_updated_block,Sizeof(FPrevious_Destination_updated_block));
+      if (LoadProtocolVersion=CT_PROTOCOL_2) then begin
+        Stream.Read(FPrevious_Seller_updated_block,Sizeof(FPrevious_Seller_updated_block));
+      end;
+      if Assigned(APreviousUpdatedBlocks) then begin
+        // Add to previous list!
+        if SignerAccount>=0 then
+          APreviousUpdatedBlocks.UpdateIfLower(SignerAccount,FPrevious_Signer_updated_block);
+        if DestinationAccount>=0 then
+          APreviousUpdatedBlocks.UpdateIfLower(DestinationAccount,FPrevious_Destination_updated_block);
+        if SellerAccount>=0 then
+          APreviousUpdatedBlocks.UpdateIfLower(SellerAccount,FPrevious_Seller_updated_block);
+      end;
     end;
     Result := true;
   end;
@@ -2579,7 +2595,8 @@ begin
       OperationResume.isMultiOperation:=True;
       OperationResume.OpSubtype := CT_OpSubtype_MultiOperation;
       OperationResume.OperationTxt := Operation.ToString;
-      OperationResume.Amount := Operation.OperationAmount * (-1);
+      OperationResume.Amount := Operation.OperationAmountByAccount(Affected_account_number);
+      OperationResume.Fee := 0;
       Result := True;
     end
   else Exit;
@@ -2637,10 +2654,13 @@ function TPCOperation.SaveToStorage(Stream: TStream): Boolean;
 
 begin
   Result := SaveOpToStream(Stream,True);
+  { XXXXXXXXXXXXXXX
+  DEPRECATED on V3, will use TPreviousUpdatedBlocks
   Stream.Write(FPrevious_Signer_updated_block,Sizeof(FPrevious_Signer_updated_block));
   Stream.Write(FPrevious_Destination_updated_block,SizeOf(FPrevious_Destination_updated_block));
   Stream.Write(FPrevious_Seller_updated_block,SizeOf(FPrevious_Seller_updated_block));
-  Result := true;
+
+  Result := true; }
 end;
 
 function TPCOperation.Sha256: TRawBytes;
@@ -2651,6 +2671,11 @@ begin
   Result := FBufferedSha256;
 end;
 
+function TPCOperation.OperationAmountByAccount(account: Cardinal): Int64;
+begin
+  Result := 0;
+end;
+
 { TOperationsResumeList }
 
 Type POperationResume = ^TOperationResume;

+ 8 - 6
src/core/UConst.pas

@@ -62,7 +62,7 @@ Const
   CT_MaxTransactionFee = 100000000;
   CT_MaxWalletAmount = 10000000000000; // ... can be deleted
   //
-  CT_MinCompactTarget: Cardinal = {$IFDEF PRODUCTION}$19000000{$ELSE}{$IFDEF TESTNET}$19000000{$ELSE}{$ENDIF}{$ENDIF}; // First compact target of block 0
+  CT_MinCompactTarget: Cardinal = {$IFDEF PRODUCTION}$19000000{$ELSE}{$IFDEF TESTNET}$17000000{$ELSE}{$ENDIF}{$ENDIF}; // First compact target of block 0
 
   CT_CalcNewTargetBlocksAverage: Cardinal = 100;
   CT_CalcNewTargetLimitChange_SPLIT = 10;
@@ -96,11 +96,11 @@ Const
   CT_PROTOCOL_2 = 2;
   CT_PROTOCOL_3 = 3;
   CT_BlockChain_Protocol_Available: Word = $0003; // Protocol 3 flag
-  CT_Protocol_Upgrade_v2_MinBlock = {$IFDEF PRODUCTION}115000{$ELSE}600{$ENDIF};
-  CT_Protocol_Upgrade_v3_MinBlock = {$IFDEF PRODUCTION}210000{$ELSE}800{$ENDIF};
+  CT_Protocol_Upgrade_v2_MinBlock = {$IFDEF PRODUCTION}115000{$ELSE}50{$ENDIF};
+  CT_Protocol_Upgrade_v3_MinBlock = {$IFDEF PRODUCTION}210000{$ELSE}250{$ENDIF};
 
 
-  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$0A04FFFF{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
+  CT_MagicNetIdentification = {$IFDEF PRODUCTION}$0A043580{$ELSE}$03000000{$ENDIF}; // Unix timestamp 168048000 ... It's Albert birthdate!
 
   CT_NetProtocol_Version: Word = $0006; // Version 2.1.2 only allows net protocol 6 (Introduced on 2.0.0)
   // IMPORTANT NOTE!!!
@@ -127,6 +127,8 @@ Const
   // Protocol 3 new operations
   CT_Op_MultiOperation = $09;  // PIP-0017
 
+  CT_Protocol_v3_PIP11_Percent = 20; // PIP-0011 20% Percent proposed and voted by PIP-0011
+
   CT_PseudoOpSubtype_Miner                = 1;
   CT_PseudoOpSubtype_Developer            = 2;
 
@@ -153,8 +155,8 @@ Const
 
   CT_TRUE_FALSE : Array[Boolean] Of AnsiString = ('FALSE','TRUE');
 
-  CT_MAX_0_fee_operations_per_block_by_miner = {$IFDEF PRODUCTION}2000{$ELSE}{$IFDEF TESTNET}20{$ELSE}{$ENDIF}{$ENDIF};
-  CT_MAX_Operations_per_block_by_miner =  {$IFDEF PRODUCTION}10000{$ELSE}{$IFDEF TESTNET}50{$ELSE}{$ENDIF}{$ENDIF};
+  CT_MAX_0_fee_operations_per_block_by_miner = {$IFDEF PRODUCTION}2000{$ELSE}{$IFDEF TESTNET}2000{$ELSE}{$ENDIF}{$ENDIF};
+  CT_MAX_Operations_per_block_by_miner =  {$IFDEF PRODUCTION}10000{$ELSE}{$IFDEF TESTNET}50000{$ELSE}{$ENDIF}{$ENDIF};
 
   CT_MAX_MultiOperation_Senders = 1000;
   CT_MAX_MultiOperation_Receivers = 10000;

+ 10 - 7
src/core/UCrypto.pas

@@ -73,8 +73,9 @@ Type
     class function HexaToRaw(const HexaString : AnsiString) : TRawBytes;
     class function DoSha256(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
     class function DoSha256(const TheMessage : AnsiString) : TRawBytes; overload;
-    class procedure DoSha256(const TheMessage : AnsiString; var ResultSha256 : TRawBytes);  overload;
-    class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; Var ResultSha256 : TRawBytes); overload;
+    class procedure DoSha256(const TheMessage : AnsiString; out ResultSha256 : TRawBytes);  overload;
+    class function DoDoubleSha256(const TheMessage : AnsiString) : TRawBytes; overload;
+    class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
     class function DoRipeMD160_HEXASTRING(const TheMessage : AnsiString) : TRawBytes; overload;
     class function DoRipeMD160AsRaw(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
     class function DoRipeMD160AsRaw(const TheMessage : AnsiString) : TRawBytes; overload;
@@ -333,10 +334,8 @@ end;
   Note: Delphi is slowly when working with Strings (allowing space)... so to
   increase speed we use a String as a pointer, and only increase speed if
   needed. Also the same with functions "GetMem" and "FreeMem" }
-class procedure TCrypto.DoDoubleSha256(p: PAnsiChar; plength: Cardinal;
-  Var ResultSha256: TRawBytes);
+class procedure TCrypto.DoDoubleSha256(p: PAnsiChar; plength: Cardinal; out ResultSha256: TRawBytes);
 Var PS : PAnsiChar;
-  PC : PAnsiChar;
 begin
   If length(ResultSha256)<>32 then SetLength(ResultSha256,32);
   PS := @ResultSha256[1];
@@ -344,6 +343,11 @@ begin
   SHA256(PS,32,PS);
 end;
 
+class function TCrypto.DoDoubleSha256(const TheMessage: AnsiString): TRawBytes;
+begin
+  Result := DoSha256(DoSha256(TheMessage));
+end;
+
 class function TCrypto.DoRipeMD160_HEXASTRING(const TheMessage: AnsiString): TRawBytes;
 Var PS : PAnsiChar;
   PC : PAnsiChar;
@@ -396,9 +400,8 @@ end;
   Note: Delphi is slowly when working with Strings (allowing space)... so to
   increase speed we use a String as a pointer, and only increase speed if
   needed. Also the same with functions "GetMem" and "FreeMem" }
-class procedure TCrypto.DoSha256(const TheMessage: AnsiString; var ResultSha256: TRawBytes);
+class procedure TCrypto.DoSha256(const TheMessage: AnsiString; out ResultSha256: TRawBytes);
 Var PS : PAnsiChar;
-  PC : PAnsiChar;
 begin
   If length(ResultSha256)<>32 then SetLength(ResultSha256,32);
   PS := @ResultSha256[1];

+ 6 - 3
src/core/UNetProtocol.pas

@@ -1646,18 +1646,18 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
             Bank.Storage.MoveBlockChainBlocks(start_block,TNode.Node.Bank.Storage.Orphan,TNode.Node.Bank.Storage);
             //
             If IsUsingSnapshot then begin
-              TLog.NewLog(ltInfo,ClassName,'Commiting new chain to Safebox');
+              TLog.NewLog(ltInfo,CT_LogSender,'Commiting new chain to Safebox');
               Bank.SafeBox.CommitToPrevious;
               Bank.UpdateValuesFromSafebox;
               {$IFDEF Check_Safebox_Names_Consistency}
               If Not Check_Safebox_Names_Consistency(Bank.SafeBox,'Commited',errors) then begin
-                TLog.NewLog(lterror,ClassName,'Fatal safebox consistency error getting bank at block '+IntTosTr(start_block)+' : '+errors);
+                TLog.NewLog(lterror,CT_LogSender,'Fatal safebox consistency error getting bank at block '+IntTosTr(start_block)+' : '+errors);
                 Sleep(1000);
                 halt(0);
               end;
               {$ENDIF}
             end else begin
-              TLog.NewLog(ltInfo,ClassName,'Restoring modified Safebox from Disk');
+              TLog.NewLog(ltInfo,CT_LogSender,'Restoring modified Safebox from Disk');
               TNode.Node.Bank.DiskRestoreFromOperations(CT_MaxBlock);
             end;
           Finally
@@ -2820,6 +2820,9 @@ Var dataSend, dataReceived : TMemoryStream;
   errors : AnsiString;
   i : Integer;
 begin
+  {$IFDEF PRODUCTION}
+  If FNetProtocolVersion.protocol_available<=6 then Exit; // Note: GetPendingOperations started on protocol_available=7
+  {$ENDIF}
   request_id := 0;
   cAddedOperations := 0;
   if Not Connected then exit;

+ 37 - 18
src/core/UNode.pas

@@ -63,6 +63,7 @@ Type
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
   public
     Class Function Node : TNode;
+    Class Function NodeExists : Boolean;
     Class Procedure DecodeIpStringToNodeServerAddressArray(Const Ips : AnsiString; Var NodeServerAddressArray : TNodeServerAddressArray);
     Class Function EncodeNodeServerAddressArrayToIpString(Const NodeServerAddressArray : TNodeServerAddressArray) : AnsiString;
     Constructor Create(AOwner : TComponent); override;
@@ -516,7 +517,7 @@ begin
 end;
 
 class procedure TNode.DecodeIpStringToNodeServerAddressArray(
-  const Ips: AnsiString; var NodeServerAddressArray: TNodeServerAddressArray);
+  const Ips: AnsiString; Var NodeServerAddressArray: TNodeServerAddressArray);
   Function GetIp(var ips_string : AnsiString; var nsa : TNodeServerAddress) : Boolean;
   Const CT_IP_CHARS = ['a'..'z','A'..'Z','0'..'9','.','-','_'];
   var i : Integer;
@@ -658,7 +659,7 @@ begin
   Result := true;
 end;
 
-function TNode.IsReady(var CurrentProcess: AnsiString): Boolean;
+function TNode.IsReady(Var CurrentProcess: AnsiString): Boolean;
 begin
   Result := false;
   CurrentProcess := '';
@@ -690,6 +691,11 @@ begin
   Result := _Node;
 end;
 
+class function TNode.NodeExists: Boolean;
+begin
+  Result := Assigned(_Node);
+end;
+
 procedure TNode.Notification(AComponent: TComponent; Operation: TOperation);
 begin
   inherited;
@@ -716,6 +722,8 @@ procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperatio
     i : Integer;
     last_block_number : Integer;
     found_in_block : Boolean;
+    acc_0_miner_reward, acc_4_dev_reward : Int64;
+    acc_4_for_dev : Boolean;
   begin
     if (act_depth<=0) then exit;
     opc := TPCOperationsComp.Create(Nil);
@@ -753,23 +761,34 @@ procedure TNode.GetStoredOperationsFromAccount(const OperationsResume: TOperatio
           end;
 
           // Is a new block operation?
-          if (TAccountComp.AccountBlock(account_number)=block_number) And ((account_number MOD CT_AccountsPerBlock)=0) then begin
-            OPR := CT_TOperationResume_NUL;
-            OPR.valid := true;
-            OPR.Block := block_number;
-            OPR.time := opc.OperationBlock.timestamp;
-            OPR.AffectedAccount := account_number;
-            OPR.Amount := opc.OperationBlock.reward;
-            OPR.Fee := opc.OperationBlock.fee;
-            If last_balance>=0 then begin
-              OPR.Balance := last_balance;
-            end else OPR.Balance := -1; // Undetermined
-            OPR.OperationTxt := 'Blockchain reward';
-            if (nOpsCounter>=StartOperation) And (nOpsCounter<=EndOperation) then begin
-              OperationsResume.Add(OPR);
+          if (TAccountComp.AccountBlock(account_number)=block_number) then begin
+            TPascalCoinProtocol.GetRewardDistributionForNewBlock(opc.OperationBlock,acc_0_miner_reward,acc_4_dev_reward,acc_4_for_dev);
+            If ((account_number MOD CT_AccountsPerBlock)=0) Or
+               (  ((account_number MOD CT_AccountsPerBlock)=CT_AccountsPerBlock-1) AND (acc_4_for_dev)  ) then begin
+              OPR := CT_TOperationResume_NUL;
+              OPR.OpType:=CT_PseudoOp_Reward;
+              OPR.valid := true;
+              OPR.Block := block_number;
+              OPR.time := opc.OperationBlock.timestamp;
+              OPR.AffectedAccount := account_number;
+              If ((account_number MOD CT_AccountsPerBlock)=0) then begin
+                OPR.Amount := acc_0_miner_reward;
+                OPR.OperationTxt := 'Miner reward';
+                OPR.OpSubtype:=CT_PseudoOpSubtype_Miner;
+              end else begin
+                OPR.Amount := acc_4_dev_reward;
+                OPR.OperationTxt := 'Dev reward';
+                OPR.OpSubtype:=CT_PseudoOpSubtype_Developer;
+              end;
+              If last_balance>=0 then begin
+               OPR.Balance := last_balance;
+              end else OPR.Balance := -1; // Undetermined
+              if (nOpsCounter>=StartOperation) And (nOpsCounter<=EndOperation) then begin
+               OperationsResume.Add(OPR);
+              end;
+              inc(nOpsCounter);
+              found_in_block := True;
             end;
-            inc(nOpsCounter);
-            found_in_block := True;
           end;
           //
           dec(act_depth);

+ 4 - 4
src/core/USha256.pas

@@ -20,10 +20,10 @@ function SHA256ToStr(Hash: TSHA256HASH): String;
 
 
 Function CanBeModifiedOnLastChunk(MessageTotalLength : Int64; var startBytePos : integer) : Boolean;
-Procedure PascalCoinPrepareLastChunk(Const messageToHash : AnsiString; var stateForLastChunk : TSHA256HASH; var bufferForLastChunk : TChunk);
+Procedure PascalCoinPrepareLastChunk(Const messageToHash : AnsiString; out stateForLastChunk : TSHA256HASH; out bufferForLastChunk : TChunk);
 Function ExecuteLastChunk(const stateForLastChunk : TSHA256HASH; const bufferForLastChunk : TChunk; nPos : Integer; nOnce,Timestamp : Cardinal) : TSHA256HASH;
 Function ExecuteLastChunkAndDoSha256(Const stateForLastChunk : TSHA256HASH; const bufferForLastChunk : TChunk; nPos : Integer; nOnce,Timestamp : Cardinal) : TSHA256HASH;
-Procedure PascalCoinExecuteLastChunkAndDoSha256(Const stateForLastChunk : TSHA256HASH; const bufferForLastChunk : TChunk; nPos : Integer; nOnce,Timestamp : Cardinal; var ResultSha256 : AnsiString);
+Procedure PascalCoinExecuteLastChunkAndDoSha256(Const stateForLastChunk : TSHA256HASH; const bufferForLastChunk : TChunk; nPos : Integer; nOnce,Timestamp : Cardinal; out ResultSha256 : AnsiString);
 Function Sha256HashToRaw(Const hash : TSHA256HASH) : AnsiString;
 
 implementation
@@ -243,7 +243,7 @@ Begin
   Result := (startBytePos >= 0) And ((startBytePos MOD 4)=0) And (startBytePos<=48);
 End;
 
-Procedure PascalCoinPrepareLastChunk(Const messageToHash : AnsiString; var stateForLastChunk : TSHA256HASH; var bufferForLastChunk : TChunk);
+Procedure PascalCoinPrepareLastChunk(Const messageToHash : AnsiString; out stateForLastChunk : TSHA256HASH; out bufferForLastChunk : TChunk);
 var
   i,j,k,iPos: Integer;
   Size: int64;
@@ -352,7 +352,7 @@ Begin
     Result[k]:= Result[k] + H[k];
 End;
 
-Procedure PascalCoinExecuteLastChunkAndDoSha256(Const stateForLastChunk : TSHA256HASH; const bufferForLastChunk : TChunk; nPos : Integer; nOnce,Timestamp : Cardinal; var ResultSha256 : AnsiString);
+Procedure PascalCoinExecuteLastChunkAndDoSha256(Const stateForLastChunk : TSHA256HASH; const bufferForLastChunk : TChunk; nPos : Integer; nOnce,Timestamp : Cardinal; out ResultSha256 : AnsiString);
 Var  H: TSHA256HASH;
 Begin
   H := ExecuteLastChunkAndDoSha256(stateForLastChunk,bufferForLastChunk,nPos,nOnce,Timestamp);

+ 64 - 30
src/core/UTxMultiOperation.pas

@@ -126,11 +126,15 @@ Type
     function SellerAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function GetAccountN_Operation(account : Cardinal) : Cardinal; override;
+    function OperationAmountByAccount(account : Cardinal) : Int64; override;
     //
     Constructor CreateMultiOperation(const senders : TMultiOpSenders; const receivers : TMultiOpReceivers; const changes : TMultiOpChangesInfo; const senders_keys, changes_keys: Array of TECPrivateKey);
     Destructor Destroy; override;
     Function AddTx(const senders : TMultiOpSenders; const receivers : TMultiOpReceivers; setInRandomOrder : Boolean) : Boolean;
-    Function AddChangeInfo(const changes : TMultiOpChangesInfo; setInRandomOrder : Boolean) : Boolean;
+    Function AddChangeInfos(const changes : TMultiOpChangesInfo; setInRandomOrder : Boolean) : Boolean;
+    Function AddTxSender(const sender : TMultiOpSender) : Boolean;
+    Function AddTxReceiver(const receiver : TMultiOpReceiver) : Boolean;
+    Function AddChangeInfo(const changeInfo : TMultiOpChangeInfo) : Boolean;
     //
     Function IndexOfAccountSender(nAccount : Cardinal) : Integer; overload;
     class Function IndexOfAccountSender(nAccount : Cardinal; startPos : Integer; const txSenders : TMultiOpSenders) : Integer; overload;
@@ -142,7 +146,6 @@ Type
     Property Data : TOpMultiOperationData read FData;
   End;
 
-
 implementation
 
 Uses ULog, UConst;
@@ -357,7 +360,7 @@ begin
       end;
     end;
     Result := AddTx(txsenders,txreceivers,False); // Important: Set in same order!
-    Result := Result And AddChangeInfo(changes,False); // Important: Set in same order!
+    Result := Result And AddChangeInfos(changes,False); // Important: Set in same order!
   Except
     On E:Exception do begin
       TLog.NewLog(lterror,Self.ClassName,'('+E.ClassName+'):'+E.Message);
@@ -584,12 +587,14 @@ begin
     end;
   end;
   // Execute!
-  If Not AccountTransaction.TransferAmounts(AccountPreviousUpdatedBlock,
-    senders,senders_n_operation,senders_amount,
-    receivers,receivers_amount,errors) then Begin
-    TLog.NewLog(ltError,ClassName,'FATAL ERROR DEV 20180312-1 '+errors); // This must never happen!
-    Raise Exception.Create('FATAL ERROR DEV 20180312-1 '+errors); // This must never happen!
-    Exit;
+  If (length(senders)>0) then begin
+    If Not AccountTransaction.TransferAmounts(AccountPreviousUpdatedBlock,
+      senders,senders_n_operation,senders_amount,
+      receivers,receivers_amount,errors) then Begin
+      TLog.NewLog(ltError,ClassName,'FATAL ERROR DEV 20180312-1 '+errors); // This must never happen!
+      Raise Exception.Create('FATAL ERROR DEV 20180312-1 '+errors); // This must never happen!
+      Exit;
+    end;
   end;
   for i:=Low(FData.changesInfo) to High(FData.changesInfo) do begin
     chi := FData.changesInfo[i];
@@ -769,6 +774,24 @@ begin
   Result := 0;
 end;
 
+function TOpMultiOperation.OperationAmountByAccount(account: Cardinal): Int64;
+Var i,j : Integer;
+begin
+  Result := 0;
+  i := IndexOfAccountSender(account);
+  if (i>=0) then begin
+    Result := FData.txSenders[i].Amount * (-1);
+  end;
+  j := 0;
+  Repeat
+    i := IndexOfAccountReceiver(account,j);
+    if (i>=0) then begin
+      Result := Result + FData.txReceivers[i].Amount;
+    end;
+    j := i+1;
+  until i<0;
+end;
+
 constructor TOpMultiOperation.CreateMultiOperation(
   const senders: TMultiOpSenders; const receivers: TMultiOpReceivers;
   const changes: TMultiOpChangesInfo; const senders_keys,
@@ -777,7 +800,7 @@ Var i : Integer;
 begin
   inherited Create;
   AddTx(senders,receivers,True);
-  AddChangeInfo(changes,True);
+  AddChangeInfos(changes,True);
   // Protection for "Exit"
   FSignatureChecked:=True;
   FHasValidSignature:=False;
@@ -864,7 +887,7 @@ begin
   Result := True;
 end;
 
-function TOpMultiOperation.AddChangeInfo(const changes: TMultiOpChangesInfo; setInRandomOrder : Boolean): Boolean;
+function TOpMultiOperation.AddChangeInfos(const changes: TMultiOpChangesInfo; setInRandomOrder : Boolean): Boolean;
 Var i,j,k : Integer;
 begin
   Result := False;
@@ -901,6 +924,34 @@ begin
   Result := True;
 end;
 
+function TOpMultiOperation.AddTxSender(const sender: TMultiOpSender): Boolean;
+Var senders : TMultiOpSenders;
+  receivers : TMultiOpReceivers;
+begin
+  SetLength(senders,1);
+  senders[0] := sender;
+  SetLength(receivers,0);
+  Result := AddTx(senders,receivers,True);
+end;
+
+function TOpMultiOperation.AddTxReceiver(const receiver: TMultiOpReceiver): Boolean;
+Var senders : TMultiOpSenders;
+  receivers : TMultiOpReceivers;
+begin
+  SetLength(senders,0);
+  SetLength(receivers,1);
+  receivers[0] := receiver;
+  Result := AddTx(senders,receivers,True);
+end;
+
+function TOpMultiOperation.AddChangeInfo(const changeInfo: TMultiOpChangeInfo): Boolean;
+Var changes : TMultiOpChangesInfo;
+begin
+  SetLength(changes,1);
+  changes[0] := changeInfo;
+  Result := AddChangeInfos(changes,True);
+end;
+
 destructor TOpMultiOperation.Destroy;
 begin
   SetLength(FData.txSenders,0);
@@ -910,26 +961,9 @@ begin
 end;
 
 function TOpMultiOperation.toString: String;
-var i : Integer;
-  ssenders,sreceivers,schanges : String;
 begin
-  ssenders := '';
-  for i:=low(FData.txSenders) to High(FData.txSenders) do begin
-    ssenders := ssenders + Format('%d:(%s;%s;%d)',[i+1,TAccountComp.AccountNumberToAccountTxtNumber(FData.txSenders[i].Account),
-        TAccountComp.FormatMoney(FData.txSenders[i].Amount),FData.txSenders[i].N_Operation]);
-  end;
-  sreceivers:='';
-  for i:=low(FData.txReceivers) to High(FData.txReceivers) do begin
-    sreceivers := sreceivers + Format('%d:(%s;%s)',[i+1,TAccountComp.AccountNumberToAccountTxtNumber(FData.txReceivers[i].Account),
-        TAccountComp.FormatMoney(FData.txReceivers[i].Amount)]);
-  end;
-  schanges := '';
-  for i:=low(FData.changesInfo) to High(FData.changesInfo) do begin
-    schanges := schanges + Format('%d:(%s;%d)',[i+1,TAccountComp.AccountNumberToAccountTxtNumber(FData.changesInfo[i].Account),
-        FData.changesInfo[i].N_Operation]);
-  end;
-  Result := Format('Multioperation senders %s receivers %s changes %s Amount:%s Fees:%s',
-    [ssenders,sreceivers,schanges,
+  Result := Format('Multioperation senders:%d receivers:%d changes:%d Amount:%s Fees:%s',
+    [length(FData.txSenders),length(FData.txReceivers),length(FData.changesInfo),
      TAccountComp.FormatMoney(FTotalAmount),
      TAccountComp.FormatMoney(FTotalFee)]);
 end;