Browse Source

Needed updates for compiler and some extra improvements on RPC

Pascal Coin 3 years ago
parent
commit
93aee77673
5 changed files with 301 additions and 144 deletions
  1. 7 6
      src/core/UNetProtocol.pas
  2. 13 13
      src/core/UOpTransaction.pas
  3. 1 1
      src/core/UPCRPCOpData.pas
  4. 277 121
      src/core/URPC.pas
  5. 3 3
      src/core/UTxMultiOperation.pas

+ 7 - 6
src/core/UNetProtocol.pas

@@ -1659,8 +1659,8 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
     try
     try
       Bank.StorageClass := TNode.Node.Bank.StorageClass;
       Bank.StorageClass := TNode.Node.Bank.StorageClass;
       Bank.Orphan := TNode.Node.Bank.Orphan;
       Bank.Orphan := TNode.Node.Bank.Orphan;
-      Bank.Storage.ReadOnly := true;
       Bank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
       Bank.Storage.CopyConfiguration(TNode.Node.Bank.Storage);
+      Bank.Storage.ReadOnly := true;
 
 
 
 
       if start_block>=0 then begin
       if start_block>=0 then begin
@@ -1671,6 +1671,7 @@ Const CT_LogSender = 'GetNewBlockChainFromClient';
           IsUsingSnapshot := True;
           IsUsingSnapshot := True;
 
 
           Bank.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
           Bank.Orphan := FormatDateTime('yyyymmddhhnnss',DateTime2UnivDateTime(now));
+          Bank.Storage.StorageFilename := '';
           Bank.Storage.ReadOnly := false;
           Bank.Storage.ReadOnly := false;
 
 
         end else begin
         end else begin
@@ -4459,7 +4460,7 @@ begin
         nOpsToSend := Operations.OperationsCount;
         nOpsToSend := Operations.OperationsCount;
       end;
       end;
       if FBufferToSendOperations.OperationsCount>0 then begin
       if FBufferToSendOperations.OperationsCount>0 then begin
-        TLog.NewLog(ltdebug,ClassName,Format('Sending %d Operations to %s (inProc:%d, Received:%d)',[FBufferToSendOperations.OperationsCount,ClientRemoteAddr,nOpsToSend,FBufferReceivedOperationsHash.Count]));
+        {$IFDEF HIGHLOG}TLog.NewLog(ltdebug,ClassName,Format('Sending %d Operations to %s (inProc:%d, Received:%d)',[FBufferToSendOperations.OperationsCount,ClientRemoteAddr,nOpsToSend,FBufferReceivedOperationsHash.Count]));{$ENDIF}
         LStream := TMemoryStream.Create;
         LStream := TMemoryStream.Create;
         try
         try
           request_id := TNetData.NetData.NewRequestId;
           request_id := TNetData.NetData.NewRequestId;
@@ -5166,7 +5167,7 @@ begin
     inc(P^.counter);
     inc(P^.counter);
     inc(FTotalCounter);
     inc(FTotalCounter);
     UpdateMedian(l);
     UpdateMedian(l);
-    TLog.NewLog(ltDebug,ClassName,Format('AddNewIp (%s,%d) - Total:%d/%d Offset:%d',[clientIp,clientTimestamp,l.Count,FTotalCounter,FTimeOffset]));
+    {$IFDEF HIGHLOG}TLog.NewLog(ltDebug,ClassName,Format('AddNewIp (%s,%d) - Total:%d/%d Offset:%d',[clientIp,clientTimestamp,l.Count,FTotalCounter,FTimeOffset]));{$ENDIF}
   finally
   finally
     FTimesList.UnlockList;
     FTimesList.UnlockList;
   end;
   end;
@@ -5241,9 +5242,9 @@ begin
       Dec(FTotalCounter);
       Dec(FTotalCounter);
     end;
     end;
     UpdateMedian(l);
     UpdateMedian(l);
-    if (i>=0) then
-      TLog.NewLog(ltDebug,ClassName,Format('RemoveIp (%s) - Total:%d/%d Offset:%d',[clientIp,l.Count,FTotalCounter,FTimeOffset]))
-    else TLog.NewLog(ltError,ClassName,Format('RemoveIp not found (%s) - Total:%d/%d Offset:%d',[clientIp,l.Count,FTotalCounter,FTimeOffset]))
+    if (i>=0) then begin
+      {$IFDEF HIGHLOG}TLog.NewLog(ltDebug,ClassName,Format('RemoveIp (%s) - Total:%d/%d Offset:%d',[clientIp,l.Count,FTotalCounter,FTimeOffset])){$ENDIF}
+    end else TLog.NewLog(ltError,ClassName,Format('RemoveIp not found (%s) - Total:%d/%d Offset:%d',[clientIp,l.Count,FTotalCounter,FTimeOffset]))
   finally
   finally
     FTimesList.UnlockList;
     FTimesList.UnlockList;
   end;
   end;

+ 13 - 13
src/core/UOpTransaction.pas

@@ -27,7 +27,7 @@ interface
 
 
 Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
 Uses UCrypto, UBlockChain, Classes, UAccounts, UBaseTypes,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  UPCDataTypes, UEPasa;
+  UPCDataTypes, UEPasa, UOrderedList;
 
 
 Type
 Type
   // Operations Type
   // Operations Type
@@ -91,7 +91,7 @@ Type
   public
   public
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
     function GetBufferForOpHash(UseProtocolV2 : Boolean): TRawBytes; override;
     function DoOperation(APrevious : TAccountPreviousBlockInfo; ASafeBoxTransaction : TPCSafeBoxTransaction; var AErrors : String) : Boolean; override;
     function DoOperation(APrevious : TAccountPreviousBlockInfo; ASafeBoxTransaction : TPCSafeBoxTransaction; var AErrors : String) : Boolean; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     //
     //
     class function OpType : Byte; override;
     class function OpType : Byte; override;
     function OperationAmount : Int64; override;
     function OperationAmount : Int64; override;
@@ -132,7 +132,7 @@ Type
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Constructor Create(ACurrentProtocol : Word; account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey; new_account_key : TAccountKey; fee: UInt64; const payload: TOperationPayload);
     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;
     Property Data : TOpChangeKeyData read FData;
@@ -171,7 +171,7 @@ Type
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     Constructor Create(ACurrentProtocol : word; account_number, n_operation: Cardinal; fee: UInt64; new_accountkey : TAccountKey);
     Constructor Create(ACurrentProtocol : word; account_number, n_operation: Cardinal; fee: UInt64; new_accountkey : TAccountKey);
     Property Data : TOpRecoverFoundsData read FData;
     Property Data : TOpRecoverFoundsData read FData;
     Function toString : String; Override;
     Function toString : String; Override;
@@ -243,7 +243,7 @@ Type
     function DestinationAccount : Int64; override;
     function DestinationAccount : Int64; override;
     function SellerAccount : Int64; override;
     function SellerAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Property Data : TOpListAccountData read FData;
     Property Data : TOpListAccountData read FData;
     Function toString : String; Override;
     Function toString : String; Override;
@@ -297,7 +297,7 @@ Type
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     Constructor CreateChangeAccountInfo(ACurrentProtocol : word;
     Constructor CreateChangeAccountInfo(ACurrentProtocol : word;
       account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
       account_signer, n_operation, account_target: Cardinal; key:TECPrivateKey;
@@ -349,7 +349,7 @@ Type
     function SignerAccount : Cardinal; override;
     function SignerAccount : Cardinal; override;
     function DestinationAccount : Int64; override;
     function DestinationAccount : Int64; override;
     function N_Operation : Cardinal; override;
     function N_Operation : Cardinal; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     function OperationAmountByAccount(account : Cardinal) : Int64; override;
     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);
     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;
     Property Data : TOpDataData read FData;
@@ -655,7 +655,7 @@ begin
   Result := FData.n_operation;
   Result := FData.n_operation;
 end;
 end;
 
 
-procedure TOpChangeAccountInfo.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpChangeAccountInfo.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account_signer);
   list.Add(FData.account_signer);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
@@ -774,7 +774,7 @@ end;
 
 
 { TOpTransaction }
 { TOpTransaction }
 
 
-procedure TOpTransaction.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpTransaction.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.sender);
   list.Add(FData.sender);
   list.Add(FData.target);
   list.Add(FData.target);
@@ -1392,7 +1392,7 @@ end;
 
 
 { TOpChangeKey }
 { TOpChangeKey }
 
 
-procedure TOpChangeKey.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpChangeKey.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account_signer);
   list.Add(FData.account_signer);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
   if (FData.account_target<>FData.account_signer) then list.Add(FData.account_target);
@@ -1739,7 +1739,7 @@ end;
 
 
 { TOpRecoverFounds }
 { TOpRecoverFounds }
 
 
-procedure TOpRecoverFounds.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpRecoverFounds.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account);
   list.Add(FData.account);
 end;
 end;
@@ -1930,7 +1930,7 @@ end;
 
 
 { TOpListAccount }
 { TOpListAccount }
 
 
-procedure TOpListAccount.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpListAccount.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account_signer);
   list.Add(FData.account_signer);
   if FData.account_signer<>FData.account_target then
   if FData.account_signer<>FData.account_target then
@@ -2817,7 +2817,7 @@ begin
   Result := FData.n_operation;
   Result := FData.n_operation;
 end;
 end;
 
 
-procedure TOpData.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpData.AffectedAccounts(list: TOrderedList<Cardinal>);
 begin
 begin
   list.Add(FData.account_signer);
   list.Add(FData.account_signer);
   if (FData.account_signer<>FData.account_sender) then begin
   if (FData.account_signer<>FData.account_sender) then begin

+ 1 - 1
src/core/UPCRPCOpData.pas

@@ -343,7 +343,7 @@ begin
     LResultArray := AJSONResponse.GetAsArray('result');
     LResultArray := AJSONResponse.GetAsArray('result');
 
 
     for i := 0 to LOperationsResumeList.Count-1 do begin
     for i := 0 to LOperationsResumeList.Count-1 do begin
-      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.OperationResume[i],ASender.Node.Bank.BlocksCount,
+      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.Items[i],ASender.Node.Bank.BlocksCount,
         ASender.Node,ASender.RPCServer.WalletKeys,ASender.RPCServer.PayloadPasswords,
         ASender.Node,ASender.RPCServer.WalletKeys,ASender.RPCServer.PayloadPasswords,
         LResultArray.GetAsObject( LResultArray.Count ));
         LResultArray.GetAsObject( LResultArray.Count ));
     end;
     end;

+ 277 - 121
src/core/URPC.pas

@@ -24,6 +24,8 @@ interface
 
 
 {$I ./../config.inc}
 {$I ./../config.inc}
 
 
+{$DEFINE RPC_PROTECT_MASSIVE_CALLS}
+
 Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
 Uses UThread, ULog, UConst, UNode, UAccounts, UCrypto, UBlockChain,
   UNetProtocol, UOpTransaction, UWallet, UTime, UPCEncryption, UTxMultiOperation,
   UNetProtocol, UOpTransaction, UWallet, UTime, UPCEncryption, UTxMultiOperation,
   UJSONFunctions, classes, blcksock, synsock,
   UJSONFunctions, classes, blcksock, synsock,
@@ -52,6 +54,7 @@ Const
   CT_RPC_ErrNum_AmbiguousPayload = 1017;
   CT_RPC_ErrNum_AmbiguousPayload = 1017;
   CT_RPC_ErrNum_InvalidSignature = 1020;
   CT_RPC_ErrNum_InvalidSignature = 1020;
   CT_RPC_ErrNum_NotAllowedCall = 1021;
   CT_RPC_ErrNum_NotAllowedCall = 1021;
+  CT_RPC_ErrNum_MaxCalls = 1022;
 
 
 
 
 Type
 Type
@@ -162,6 +165,7 @@ Type
     class procedure RegisterProcessMethod(Const AMethodName : String; ARPCProcessMethod : TRPCProcessMethod);
     class procedure RegisterProcessMethod(Const AMethodName : String; ARPCProcessMethod : TRPCProcessMethod);
     class procedure UnregisterProcessMethod(Const AMethodName : String);
     class procedure UnregisterProcessMethod(Const AMethodName : String);
     class function FindRegisteredProcessMethod(Const AMethodName : String) : TRPCProcessMethod;
     class function FindRegisteredProcessMethod(Const AMethodName : String) : TRPCProcessMethod;
+    class procedure ProcessMethodCalled(Const AMethodName : String; AStartTickCount : TTickCount);
   end;
   end;
 
 
 implementation
 implementation
@@ -171,17 +175,27 @@ Uses
   SysUtils, Synautil,
   SysUtils, Synautil,
   UEPasaDecoder,
   UEPasaDecoder,
   UPCRPCSend,
   UPCRPCSend,
+  UOrderedList,
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
 
 
 Type
 Type
   TRegisteredRPCProcessMethod = Record
   TRegisteredRPCProcessMethod = Record
     MethodName : String;
     MethodName : String;
     RPCProcessMethod : TRPCProcessMethod;
     RPCProcessMethod : TRPCProcessMethod;
+    CallsCounter : Integer;
+    ElapsedMilis : Int64;
+    procedure Clear;
   end;
   end;
+  PRegisteredRPCProcessMethod = ^TRegisteredRPCProcessMethod;
 
 
 var _RPCServer : TRPCServer = Nil;
 var _RPCServer : TRPCServer = Nil;
 
 
-  _RPCProcessMethods : TList<TRegisteredRPCProcessMethod> = Nil;
+  _RPCProcessMethods : TOrderedList<PRegisteredRPCProcessMethod> = Nil;
+
+function TRegisteredRPCProcessMethod_Comparer(const ALeft,ARight : PRegisteredRPCProcessMethod) : Integer;
+begin
+  Result := AnsiCompareText(ALeft.MethodName , ARight.MethodName);
+end;
 
 
 { TPascalCoinJSONComp }
 { TPascalCoinJSONComp }
 
 
@@ -292,7 +306,7 @@ Begin
   end;
   end;
   if OPR.valid then begin
   if OPR.valid then begin
     jsonObject.GetAsVariant('block').Value:=OPR.Block;
     jsonObject.GetAsVariant('block').Value:=OPR.Block;
-    jsonObject.GetAsVariant('time').Value:=OPR.time;
+    if OPR.time>0 then jsonObject.GetAsVariant('time').Value:=OPR.time;
     jsonObject.GetAsVariant('opblock').Value:=OPR.NOpInsideBlock;
     jsonObject.GetAsVariant('opblock').Value:=OPR.NOpInsideBlock;
     if (OPR.Block>0) And (OPR.Block<currentNodeBlocksCount) then
     if (OPR.Block>0) And (OPR.Block<currentNodeBlocksCount) then
       jsonObject.GetAsVariant('maturation').Value := currentNodeBlocksCount - OPR.Block - 1
       jsonObject.GetAsVariant('maturation').Value := currentNodeBlocksCount - OPR.Block - 1
@@ -1032,19 +1046,40 @@ end;
 
 
 class function TRPCProcess.FindRegisteredProcessMethod(const AMethodName: String): TRPCProcessMethod;
 class function TRPCProcess.FindRegisteredProcessMethod(const AMethodName: String): TRPCProcessMethod;
 var i : Integer;
 var i : Integer;
+  P : PRegisteredRPCProcessMethod;
 begin
 begin
   Result := Nil;
   Result := Nil;
   if Not Assigned(_RPCProcessMethods) then Exit;
   if Not Assigned(_RPCProcessMethods) then Exit;
-  i := 0;
-  while (i<_RPCProcessMethods.Count) and (Not Assigned(Result)) do begin
-    if AnsiSameStr( _RPCProcessMethods.Items[i].MethodName , AMethodName) then begin
-      Result := _RPCProcessMethods.Items[i].RPCProcessMethod;
+  New(P);
+  Try
+    P.Clear;
+    P.MethodName := AMethodName;
+    if _RPCProcessMethods.Find(P,i) then begin
+      Result := _RPCProcessMethods.Get(i).RPCProcessMethod;
     end;
     end;
-    inc(i);
-  end;
+  Finally
+    Dispose(P);
+  End;
 end;
 end;
 
 
 procedure TRPCProcess.BCExecute;
 procedure TRPCProcess.BCExecute;
+  function ValidMethodName(const AMethod : String) : Boolean;
+  var i : Integer;
+  begin
+    Result := False;
+    for i:=0 to AMethod.Length-1 do begin
+      case AMethod.Chars[i] of
+        'a'..'z',
+        'A'..'Z',
+        '0'..'9',
+        '_','.' : ; // Nothing to do
+        '-' : if i=0 then Exit; // Cannot start with "-"
+      else Exit; // Not a valid char
+      end;
+    end;
+    Result := True;
+  end;
+
 var
 var
   timeout: integer;
   timeout: integer;
   s: string;
   s: string;
@@ -1054,10 +1089,10 @@ var
   resultcode: integer;
   resultcode: integer;
   inputdata : TRawBytes;
   inputdata : TRawBytes;
   js,jsresult : TPCJSONData;
   js,jsresult : TPCJSONData;
-  jsonobj,jsonresponse : TPCJSONObject;
+  jsonobj,jsonresponse, paramsJSON : TPCJSONObject;
   errNum : Integer; errDesc : String;
   errNum : Integer; errDesc : String;
   jsonrequesttxt,
   jsonrequesttxt,
-  jsonresponsetxt, methodName, paramsTxt : String;
+  jsonresponsetxt, methodName, paramsTxt, senderIP : String;
   valid : Boolean;
   valid : Boolean;
   i : Integer;
   i : Integer;
   Headers : TStringList;
   Headers : TStringList;
@@ -1066,6 +1101,7 @@ var
   LOnStartLiveConnectionCount : Integer;
   LOnStartLiveConnectionCount : Integer;
 begin
 begin
   LOnStartLiveConnectionCount := FRPCServer.FLiveConnectionsCount;
   LOnStartLiveConnectionCount := FRPCServer.FLiveConnectionsCount;
+  senderIP := '';
   callcounter := _RPCServer.GetNewCallCounter;
   callcounter := _RPCServer.GetNewCallCounter;
   tc := TPlatform.GetTickCount;
   tc := TPlatform.GetTickCount;
   methodName := '';
   methodName := '';
@@ -1139,9 +1175,27 @@ begin
             errDesc := '';
             errDesc := '';
             try
             try
               methodName := jsonobj.AsString('method','');
               methodName := jsonobj.AsString('method','');
-              paramsTxt := jsonobj.GetAsObject('params').ToJSON(false);
+              paramsJSON := jsonobj.GetAsObject('params');
+              senderIP := Trim(jsonObj.AsString('remoteaddr','')); //
+              paramsTxt := paramsJSON.ToJSON(false);
               {$IFDEF HIGHLOG}TLog.NewLog(ltinfo,Classname,FSock.GetRemoteSinIP+':'+inttostr(FSock.GetRemoteSinPort)+' Processing method '+methodName+' params '+paramsTxt);{$ENDIF}
               {$IFDEF HIGHLOG}TLog.NewLog(ltinfo,Classname,FSock.GetRemoteSinIP+':'+inttostr(FSock.GetRemoteSinPort)+' Processing method '+methodName+' params '+paramsTxt);{$ENDIF}
-              Valid := ProcessMethod(methodName,jsonobj.GetAsObject('params'),jsonresponse,errNum,errDesc);
+              valid := True;
+              {$IFDEF RPC_PROTECT_MASSIVE_CALLS}
+              if (senderIP<>'') and (ValidMethodName(methodName)) then begin
+                if TNetData.NetData.IpInfos.Update_And_ReachesLimits(senderIP,'rpcmethod',methodName,0,True,
+                 TArray<TLimitLifetime>.Create(TLimitLifetime.Create(60,50,0),TLimitLifetime.Create(3600,500,0))) then  begin
+                   valid := false;
+                   errNum := CT_RPC_ErrNum_MaxCalls;
+                   errDesc := Format('IP:%s Reached limit %s',[senderIP,methodName]);
+                   jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
+                   jsonresponse.GetAsObject('error').GetAsVariant('message').Value:=errDesc;
+                 end;
+              end;
+              {$ENDIF}
+              if valid then begin
+
+              TRPCProcess.ProcessMethodCalled(methodName,tc);
+              Valid := ProcessMethod(methodName,paramsJSON,jsonresponse,errNum,errDesc);
               if not Valid then begin
               if not Valid then begin
                 if (errNum<>0) or (errDesc<>'') then begin
                 if (errNum<>0) or (errDesc<>'') then begin
                   jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
                   jsonresponse.GetAsObject('error').GetAsVariant('code').Value:=errNum;
@@ -1151,6 +1205,8 @@ begin
                   jsonresponse.GetAsObject('error').GetAsVariant('message').Value:='Unknown error processing method';
                   jsonresponse.GetAsObject('error').GetAsVariant('message').Value:='Unknown error processing method';
                 end;
                 end;
               end;
               end;
+
+              end;
             Except
             Except
               on E:Exception do begin
               on E:Exception do begin
                 TLog.NewLog(lterror,Classname,'Exception processing method'+methodName+' ('+E.ClassName+'): '+E.Message);
                 TLog.NewLog(lterror,Classname,'Exception processing method'+methodName+' ('+E.ClassName+'): '+E.Message);
@@ -1197,8 +1253,14 @@ begin
           FSock.SendString(jsonresponsetxt);
           FSock.SendString(jsonresponsetxt);
         end;
         end;
       end;
       end;
-      _RPCServer.AddRPCLog(FSock.GetRemoteSinIP+':'+InttoStr(FSock.GetRemoteSinPort),callcounter,'Method:'+methodName+' Params:'+paramsTxt+' '+Inttostr(errNum)+':'+errDesc+' Time:'+FormatFloat('0.000',(TPlatform.GetElapsedMilliseconds(tc)/1000))
+      if senderIP<>'' then begin
+        senderIP := FSock.GetRemoteSinIP+':'+InttoStr(FSock.GetRemoteSinPort) + ' @'+senderIP;
+      end else begin
+        senderIP := FSock.GetRemoteSinIP+':'+InttoStr(FSock.GetRemoteSinPort);
+      end;
+      _RPCServer.AddRPCLog(senderIP,callcounter,'Method:'+methodName+' Params:'+paramsTxt+' '+Inttostr(errNum)+':'+errDesc+' Time:'+FormatFloat('0.000',(TPlatform.GetElapsedMilliseconds(tc)/1000))
         +' '+LOnStartLiveConnectionCount.ToString+'->'+FRPCServer.FLiveConnectionsCount.ToString);
         +' '+LOnStartLiveConnectionCount.ToString+'->'+FRPCServer.FLiveConnectionsCount.ToString);
+      TRPCProcess.ProcessMethodCalled(methodName,tc);
     finally
     finally
       jsonresponse.free;
       jsonresponse.free;
       Headers.Free;
       Headers.Free;
@@ -1284,6 +1346,68 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
+  Function GetBlockOperation(ABlock, AOpBlock : Integer; jsonObject : TPCJSONObject) : Boolean;
+  var LOpResumeList : TOperationsResumeList;
+    LOperationBlock : TOperationBlock;
+    LOperationsCount : Integer;
+    LOperationsAmount : Int64;
+  begin
+    FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
+    try
+    LOpResumeList := TOperationsResumeList.Create;
+    Try
+      if not FNode.Bank.Storage.GetBlockOperations(ABlock,AOpBlock,1,LOperationBlock,LOperationsCount,LOperationsAmount,LOpResumeList) then begin
+        ErrorNum := CT_RPC_ErrNum_InvalidOperation;
+        ErrorDesc := 'Cannot load Block: '+ABlock.ToString+' OpBlock: '+AOpBlock.ToString;
+        Result := False;
+        Exit;
+      end;
+      if LOpResumeList.Count<>1 then Exit(False);
+      TPascalCoinJSONComp.FillOperationObject(LOpResumeList.Items[0],
+          FNode.Bank.BlocksCount,
+          Node,RPCServer.WalletKeys,RPCServer.PayloadPasswords,
+          jsonObject);
+      Result := True;
+    Finally
+      LOpResumeList.Free;
+    End;
+    finally
+      FNode.OperationSequenceLock.Release;
+    end;
+  end;
+
+
+  Function GetBlockOperations(ABlock, AOpBlockStartIndex, AMaxOperations : Integer; jsonArray : TPCJSONArray) : Boolean;
+  var LOpResumeList : TOperationsResumeList;
+    LOperationBlock : TOperationBlock;
+    LOperationsCount : Integer;
+    LOperationsAmount : Int64;
+    i : Integer;
+  begin
+    FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
+    try
+    LOpResumeList := TOperationsResumeList.Create;
+    Try
+      if not FNode.Bank.Storage.GetBlockOperations(ABlock,AOpBlockStartIndex,AMaxOperations,LOperationBlock,LOperationsCount,LOperationsAmount,LOpResumeList) then begin
+        ErrorNum := CT_RPC_ErrNum_InvalidOperation;
+        ErrorDesc := 'Cannot load Block: '+ABlock.ToString+' OpBlock: '+AOpBlockStartIndex.ToString+' Max: '+AMaxOperations.ToString;
+        Result := False;
+        Exit;
+      end;
+      for i := 0 to LOpResumeList.Count-1 do begin
+        TPascalCoinJSONComp.FillOperationObject(LOpResumeList.Items[i],FNode.Bank.BlocksCount,
+            Node,RPCServer.WalletKeys,RPCServer.PayloadPasswords,
+            jsonArray.GetAsObject(jsonArray.Count));
+      end;
+      Result := True;
+    Finally
+      LOpResumeList.Free;
+    End;
+    finally
+      FNode.OperationSequenceLock.Release;
+    end;
+  end;
+
   Procedure FillOperationResumeToJSONObject(Const OPR : TOperationResume; jsonObject : TPCJSONObject);
   Procedure FillOperationResumeToJSONObject(Const OPR : TOperationResume; jsonObject : TPCJSONObject);
   Begin
   Begin
     TPascalCoinJSONComp.FillOperationObject(OPR,FNode.Bank.BlocksCount,
     TPascalCoinJSONComp.FillOperationObject(OPR,FNode.Bank.BlocksCount,
@@ -1340,7 +1464,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       end;
       end;
       if (nCounter<maxReg) then begin
       if (nCounter<maxReg) then begin
         if (startReg<0) then startReg := 0; // Prevent -1 value
         if (startReg<0) then startReg := 0; // Prevent -1 value
-        FNode.GetStoredOperationsFromAccount(OperationsResume,accountNumber,maxBlocksDepth,startReg,startReg+maxReg-1,forceStartBlock);
+        FNode.Bank.Storage.GetAccountOperations(accountNumber,maxBlocksDepth,startReg,maxReg,forceStartBlock,OperationsResume);
       end;
       end;
       for i:=0 to OperationsResume.Count-1 do begin
       for i:=0 to OperationsResume.Count-1 do begin
         Obj := jsonArray.GetAsObject(jsonArray.Count);
         Obj := jsonArray.GetAsObject(jsonArray.Count);
@@ -1434,6 +1558,38 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
     end;
     end;
   end;
   end;
 
 
+  Procedure GetMethodsCallsStats;
+  var i : Integer;
+    obj: TPCJSONObject;
+    P : PRegisteredRPCProcessMethod;
+    LCalls, LMilis : Int64;
+  Begin
+    if Not Assigned(_RPCProcessMethods) then Exit;
+    LCalls := 0;
+    LMilis := 0;
+    i := 0;
+    while (i<_RPCProcessMethods.Count) do begin
+      P := _RPCProcessMethods.Get(i);
+      obj := GetResultArray.GetAsObject(GetResultArray.Count);
+      obj.GetAsVariant('method').Value := P.MethodName;
+      obj.GetAsVariant('calls').Value := P.CallsCounter;
+      obj.GetAsVariant('seconds').Value := FormatFloat('0.000',P.ElapsedMilis/1000);
+      if P.CallsCounter>0 then begin
+        obj.GetAsVariant('secs_average').Value := FormatFloat('0.000',(P.ElapsedMilis/1000)/P.CallsCounter);
+      end;
+      inc(LCalls,P.CallsCounter);
+      inc(LMilis,P.ElapsedMilis);
+      inc(i);
+    end;
+    obj := GetResultArray.GetAsObject(GetResultArray.Count);
+    obj.GetAsVariant('method').Value := 'TOTAL';
+    obj.GetAsVariant('calls').Value := LCalls;
+    obj.GetAsVariant('seconds').Value := FormatFloat('0.000',LMilis/1000);
+    if LCalls>0 then begin
+      obj.GetAsVariant('secs_average').Value := FormatFloat('0.000',(LMilis/1000)/LCalls);
+    end;
+  end;
+
   // This function creates a TOpTransaction without looking for balance/private key of sender account
   // This function creates a TOpTransaction without looking for balance/private key of sender account
   // It assumes that sender,target,sender_last_n_operation,senderAccountKey and targetAccountKey are correct
   // It assumes that sender,target,sender_last_n_operation,senderAccountKey and targetAccountKey are correct
   Function CreateOperationTransaction(current_protocol : Word; sender, target, sender_last_n_operation : Cardinal; amount, fee : UInt64;
   Function CreateOperationTransaction(current_protocol : Word; sender, target, sender_last_n_operation : Cardinal; amount, fee : UInt64;
@@ -2552,7 +2708,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
   function FindNOperations : Boolean;
   function FindNOperations : Boolean;
   Var oprl : TOperationsResumeList;
   Var oprl : TOperationsResumeList;
     start_block, account, n_operation_min, n_operation_max : Cardinal;
     start_block, account, n_operation_min, n_operation_max : Cardinal;
-    sor : TSearchOperationResult;
+    sor : TSearchOpHashResult;
     jsonarr : TPCJSONArray;
     jsonarr : TPCJSONArray;
     i : Integer;
     i : Integer;
   begin
   begin
@@ -2575,13 +2731,13 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       start_block := params.AsCardinal('start_block',0); // Optional: 0 = Search all
       start_block := params.AsCardinal('start_block',0); // Optional: 0 = Search all
       sor := FNode.FindNOperations(account,start_block,true,n_operation_min,n_operation_max,oprl);
       sor := FNode.FindNOperations(account,start_block,true,n_operation_min,n_operation_max,oprl);
       Case sor of
       Case sor of
-        found : Result := True;
-        invalid_params : begin
+        OpHash_found : Result := True;
+        OpHash_invalid_params : begin
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorDesc:='Not found using block/account/n_operation';
             ErrorDesc:='Not found using block/account/n_operation';
             exit;
             exit;
           end;
           end;
-        blockchain_block_not_found : begin
+        OpHash_block_not_found : begin
             ErrorNum := CT_RPC_ErrNum_InvalidBlock;
             ErrorNum := CT_RPC_ErrNum_InvalidBlock;
             ErrorDesc:='Blockchain file does not contain all blocks to find';
             ErrorDesc:='Blockchain file does not contain all blocks to find';
             exit;
             exit;
@@ -2591,7 +2747,7 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
       jsonarr := jsonresponse.GetAsArray('result');
       jsonarr := jsonresponse.GetAsArray('result');
       if oprl.Count>0 then begin;
       if oprl.Count>0 then begin;
         for i:=0 to oprl.Count-1 do begin
         for i:=0 to oprl.Count-1 do begin
-          FillOperationResumeToJSONObject(oprl.OperationResume[i],jsonarr.GetAsObject(jsonarr.Count));
+          FillOperationResumeToJSONObject(oprl.Items[i],jsonarr.GetAsObject(jsonarr.Count));
         end;
         end;
       end;
       end;
     finally
     finally
@@ -3299,81 +3455,15 @@ begin
     // Param "block" contains block. Null = Pending operation
     // Param "block" contains block. Null = Pending operation
     // Param "opblock" contains operation inside a block: (0..getblock.operations-1)
     // Param "opblock" contains operation inside a block: (0..getblock.operations-1)
     // Returns a JSON object with operation values as "Operation resume format"
     // Returns a JSON object with operation values as "Operation resume format"
-    FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
-    try
-    c := params.GetAsVariant('block').AsCardinal(CT_MaxBlock);
-    if (c>=0) And (c<FNode.Bank.BlocksCount) then begin
-      pcops := TPCOperationsComp.Create(Nil);
-      try
-        If Not FNode.Bank.LoadOperations(pcops,c) then begin
-          ErrorNum := CT_RPC_ErrNum_InternalError;
-          ErrorDesc := 'Cannot load Block: '+IntToStr(c);
-          Exit;
-        end;
-        i := params.GetAsVariant('opblock').AsInteger(0);
-        if (i<0) Or (i>=pcops.Count) then begin
-          ErrorNum := CT_RPC_ErrNum_InvalidOperation;
-          ErrorDesc := 'Block/Operation not found: '+IntToStr(c)+'/'+IntToStr(i)+' BlockOperations:'+IntToStr(pcops.Count);
-          Exit;
-        end;
-        If TPCOperation.OperationToOperationResume(c,pcops.Operation[i],True,pcops.Operation[i].SignerAccount,opr) then begin
-          opr.NOpInsideBlock:=i;
-          opr.time:=pcops.OperationBlock.timestamp;
-          opr.Balance := -1;
-          FillOperationResumeToJSONObject(opr,GetResultObject);
-        end;
-        Result := True;
-      finally
-        pcops.Free;
-      end;
-    end else begin
-      If (c=CT_MaxBlock) then ErrorDesc := 'Need block param'
-      else ErrorDesc := 'Block not found: '+IntToStr(c);
-      ErrorNum := CT_RPC_ErrNum_InvalidBlock;
-    end;
-    finally
-      Node.OperationSequenceLock.Release;
-    end;
+    Result := GetBlockOperation(params.GetAsVariant('block').AsInteger(CT_MaxBlock),
+      params.GetAsVariant('opblock').AsInteger(CT_MaxBlock),GetResultObject);
   end else if (method='getblockoperations') then begin
   end else if (method='getblockoperations') then begin
     // Param "block" contains block
     // Param "block" contains block
     // Returns a JSON array with items as "Operation resume format"
     // Returns a JSON array with items as "Operation resume format"
-    FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
-    try
-    c := params.GetAsVariant('block').AsCardinal(CT_MaxBlock);
-    if (c>=0) And (c<FNode.Bank.BlocksCount) then begin
-      pcops := TPCOperationsComp.Create(Nil);
-      try
-        If Not FNode.Bank.LoadOperations(pcops,c) then begin
-          ErrorNum := CT_RPC_ErrNum_InternalError;
-          ErrorDesc := 'Cannot load Block: '+IntToStr(c);
-          Exit;
-        end;
-        jsonarr := GetResultArray;
-        k := params.AsInteger('max',100);
-        j := params.AsInteger('start',0);
-        for i := 0 to pcops.Count - 1 do begin
-          if (i>=j) then begin
-            If TPCOperation.OperationToOperationResume(c,pcops.Operation[i],True,pcops.Operation[i].SignerAccount,opr) then begin
-              opr.NOpInsideBlock:=i;
-              opr.time:=pcops.OperationBlock.timestamp;
-              opr.Balance := -1; // Don't include!
-              FillOperationResumeToJSONObject(opr,jsonarr.GetAsObject(jsonarr.Count));
-            end;
-          end;
-          if (k>0) And ((i+1)>=(j+k)) then break;
-        end;
-        Result := True;
-      finally
-        pcops.Free;
-      end;
-    end else begin
-      If (c=CT_MaxBlock) then ErrorDesc := 'Need block param'
-      else ErrorDesc := 'Block not found: '+IntToStr(c);
-      ErrorNum := CT_RPC_ErrNum_InvalidBlock;
-    end;
-    finally
-      FNode.OperationSequenceLock.Release;
-    end;
+    Result := GetBlockOperations(params.GetAsVariant('block').AsInteger(CT_MaxBlock),
+      params.GetAsVariant('start').AsInteger(0),
+      params.GetAsVariant('max').AsInteger(100),
+      GetResultArray);
   end else if (method='getaccountoperations') then begin
   end else if (method='getaccountoperations') then begin
     // Returns all the operations affecting an account in "Operation resume format" as an array
     // Returns all the operations affecting an account in "Operation resume format" as an array
     // Param "account" contains account number
     // Param "account" contains account number
@@ -3446,36 +3536,30 @@ begin
       ErrorDesc:='param ophash not found or invalid hexadecimal value "'+params.AsString('ophash','')+'"';
       ErrorDesc:='param ophash not found or invalid hexadecimal value "'+params.AsString('ophash','')+'"';
       exit;
       exit;
     end;
     end;
+    if (Length(r1)<>32) then begin
+      ErrorNum:=CT_RPC_ErrNum_InvalidOperation;
+      ErrorDesc:='param ophash with invalid length (Expected 64 chars for a 32bytes hexadecimal) value length = '+IntToStr(Length(r1));
+      exit;
+    end;
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     try
     try
-    pcops := TPCOperationsComp.Create(Nil);
-    try
-      Case FNode.FindOperationExt(pcops,r1,c,i) of
-        found : ;
-        invalid_params : begin
+      Case FNode.FindOperation(r1,opr) of
+        OpHash_found : ;
+        OpHash_invalid_params : begin
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorNum:=CT_RPC_ErrNum_NotFound;
             ErrorDesc:='ophash not found: "'+params.AsString('ophash','')+'"';
             ErrorDesc:='ophash not found: "'+params.AsString('ophash','')+'"';
             exit;
             exit;
           end;
           end;
-        blockchain_block_not_found : begin
+        OpHash_block_not_found : begin
             ErrorNum := CT_RPC_ErrNum_InternalError;
             ErrorNum := CT_RPC_ErrNum_InternalError;
-            ErrorDesc:='Blockchain block '+IntToStr(c)+' not found to search ophash: "'+params.AsString('ophash','')+'"';
+            ErrorDesc:='Blockchain block not found to search ophash: "'+params.AsString('ophash','')+'"';
             exit;
             exit;
           end;
           end;
       else Raise Exception.Create('ERROR DEV 20171120-4');
       else Raise Exception.Create('ERROR DEV 20171120-4');
       end;
       end;
-      If not TPCOperation.OperationToOperationResume(c,pcops.Operation[i],True,pcops.Operation[i].SignerAccount,opr) then begin
-        ErrorNum := CT_RPC_ErrNum_InternalError;
-        ErrorDesc := 'Error 20161026-1';
-      end;
-      opr.NOpInsideBlock:=i;
-      opr.time:=pcops.OperationBlock.timestamp;
       opr.Balance := -1; // don't include
       opr.Balance := -1; // don't include
       FillOperationResumeToJSONObject(opr,GetResultObject);
       FillOperationResumeToJSONObject(opr,GetResultObject);
       Result := True;
       Result := True;
-    finally
-      pcops.Free;
-    end;
     finally
     finally
       FNode.OperationSequenceLock.Release;
       FNode.OperationSequenceLock.Release;
     end;
     end;
@@ -3489,13 +3573,13 @@ begin
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     FNode.OperationSequenceLock.Acquire; // Added to prevent high concurrent API calls
     try
     try
     Case FNode.FindNOperation(params.AsCardinal('block',0),c,params.AsCardinal('n_operation',0),opr) of
     Case FNode.FindNOperation(params.AsCardinal('block',0),c,params.AsCardinal('n_operation',0),opr) of
-      found : ;
-      invalid_params : begin
+      OpHash_found : ;
+      OpHash_invalid_params : begin
           ErrorNum:=CT_RPC_ErrNum_NotFound;
           ErrorNum:=CT_RPC_ErrNum_NotFound;
           ErrorDesc:='Not found using block/account/n_operation';
           ErrorDesc:='Not found using block/account/n_operation';
           exit;
           exit;
         end;
         end;
-      blockchain_block_not_found : begin
+      OpHash_block_not_found : begin
           ErrorNum := CT_RPC_ErrNum_InvalidBlock;
           ErrorNum := CT_RPC_ErrNum_InvalidBlock;
           ErrorDesc:='Blockchain file does not contain all blocks to find';
           ErrorDesc:='Blockchain file does not contain all blocks to find';
           exit;
           exit;
@@ -3973,6 +4057,14 @@ begin
     end;
     end;
     Get_node_ip_stats;
     Get_node_ip_stats;
     Result := True;
     Result := True;
+  end else if (method='methods_stats') then begin
+    if (Not _RPCServer.AllowUsePrivateKeys) then begin
+      // Protection when server is locked to avoid private keys call
+      ErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+      Exit;
+    end;
+    GetMethodsCallsStats;
+    Result := True;
   end else begin
   end else begin
     LRPCProcessMethod := FindRegisteredProcessMethod(method);
     LRPCProcessMethod := FindRegisteredProcessMethod(method);
     if Assigned(LRPCProcessMethod) then begin
     if Assigned(LRPCProcessMethod) then begin
@@ -3984,26 +4076,77 @@ begin
   end;
   end;
 end;
 end;
 
 
+class procedure TRPCProcess.ProcessMethodCalled(const AMethodName: String;
+  AStartTickCount: TTickCount);
+var
+  P, PFound : PRegisteredRPCProcessMethod;
+  i : Integer;
+begin
+  if Not Assigned(_RPCProcessMethods) then begin
+    _RPCProcessMethods := TOrderedList<PRegisteredRPCProcessMethod>.Create(False,TRegisteredRPCProcessMethod_Comparer);
+  end;
+  New(P);
+  try
+    P.Clear;
+    P.MethodName := AMethodName;
+    if _RPCProcessMethods.Find(P,i) then begin
+      PFound := _RPCProcessMethods.Get(i);
+    end else begin
+      // Create
+      New(PFound);
+      PFound.Clear;
+      PFound.MethodName := AMethodName;
+      _RPCProcessMethods.Add(PFound);
+    end;
+    if (AStartTickCount>0) then begin
+      inc(PFound.CallsCounter);
+      inc(PFound.ElapsedMilis,Int64(TPlatform.GetElapsedMilliseconds(AStartTickCount)));
+    end;
+  finally
+    Dispose(P);
+  end;
+end;
+
 class procedure TRPCProcess.RegisterProcessMethod(const AMethodName: String; ARPCProcessMethod: TRPCProcessMethod);
 class procedure TRPCProcess.RegisterProcessMethod(const AMethodName: String; ARPCProcessMethod: TRPCProcessMethod);
-var LRegistered : TRegisteredRPCProcessMethod;
+var
+  P, PFound : PRegisteredRPCProcessMethod;
+  i : Integer;
 begin
 begin
   if Not Assigned(_RPCProcessMethods) then begin
   if Not Assigned(_RPCProcessMethods) then begin
-    _RPCProcessMethods := TList<TRegisteredRPCProcessMethod>.Create;
+    _RPCProcessMethods := TOrderedList<PRegisteredRPCProcessMethod>.Create(False,TRegisteredRPCProcessMethod_Comparer);
+  end;
+  New(P);
+  try
+    P.Clear;
+    P.MethodName := AMethodName;
+    if _RPCProcessMethods.Find(P,i) then begin
+      PFound := _RPCProcessMethods.Get(i);
+    end else begin
+      // Create
+      New(PFound);
+      PFound.Clear;
+      PFound.MethodName := AMethodName;
+      _RPCProcessMethods.Add(PFound);
+    end;
+    PFound.RPCProcessMethod := ARPCProcessMethod;
+  finally
+    Dispose(P);
   end;
   end;
-  if Assigned(FindRegisteredProcessMethod(AMethodName)) then Exit; // Duplicated!
-  LRegistered.MethodName := AMethodName;
-  LRegistered.RPCProcessMethod := ARPCProcessMethod;
-  _RPCProcessMethods.Add(LRegistered);
 end;
 end;
 
 
 class procedure TRPCProcess.UnregisterProcessMethod(const AMethodName: String);
 class procedure TRPCProcess.UnregisterProcessMethod(const AMethodName: String);
-var i : Integer;
+var
+  P : PRegisteredRPCProcessMethod;
+  i : Integer;
 begin
 begin
   if Not Assigned(_RPCProcessMethods) then Exit;
   if Not Assigned(_RPCProcessMethods) then Exit;
-  for i := _RPCProcessMethods.Count-1 downto 0 do begin
-    if AnsiSameStr(_RPCProcessMethods.Items[i].MethodName , AMethodName) then begin
-      _RPCProcessMethods.Delete(i);
-    end;
+  New(P);
+  try
+    P.Clear;
+    P.MethodName := AMethodName;
+    _RPCProcessMethods.Remove(P);
+  finally
+    Dispose(P);
   end;
   end;
 end;
 end;
 
 
@@ -4055,16 +4198,29 @@ end;
 
 
 procedure DoFinalize;
 procedure DoFinalize;
 var i : Integer;
 var i : Integer;
+  P : PRegisteredRPCProcessMethod;
 begin
 begin
   if Assigned(_RPCProcessMethods) then begin
   if Assigned(_RPCProcessMethods) then begin
     for i := _RPCProcessMethods.Count-1 downto 0 do begin
     for i := _RPCProcessMethods.Count-1 downto 0 do begin
+      P := _RPCProcessMethods.Get(i);
       _RPCProcessMethods.Delete(i);
       _RPCProcessMethods.Delete(i);
+      Dispose(P);
     end;
     end;
   end;
   end;
   FreeAndNil(_RPCProcessMethods);
   FreeAndNil(_RPCProcessMethods);
   FreeAndNil(_RPCServer);
   FreeAndNil(_RPCServer);
 end;
 end;
 
 
+{ TRegisteredRPCProcessMethod }
+
+procedure TRegisteredRPCProcessMethod.Clear;
+begin
+  Self.MethodName := '';
+  Self.RPCProcessMethod := Nil;
+  Self.CallsCounter := 0;
+  Self.ElapsedMilis := 0;
+end;
+
 initialization
 initialization
 finalization
 finalization
   DoFinalize;
   DoFinalize;

+ 3 - 3
src/core/UTxMultiOperation.pas

@@ -25,7 +25,7 @@ interface
 uses
 uses
   Classes, SysUtils, UCrypto, UBlockChain, UAccounts, UBaseTypes, UEPasa,
   Classes, SysUtils, UCrypto, UBlockChain, UAccounts, UBaseTypes, UEPasa,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  UPCDataTypes;
+  UPCDataTypes, UOrderedList;
 
 
 Type
 Type
 
 
@@ -122,7 +122,7 @@ Type
     function CheckSignatures(AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
     function CheckSignatures(AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean;
 
 
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
     function DoOperation(AccountPreviousUpdatedBlock : TAccountPreviousBlockInfo; AccountTransaction : TPCSafeBoxTransaction; var errors : String) : Boolean; override;
-    procedure AffectedAccounts(list : TList<Cardinal>); override;
+    procedure AffectedAccounts(list : TOrderedList<Cardinal>); override;
     //
     //
     Function DoSignMultiOperationSigner(current_protocol : Word; SignerAccount : Cardinal; key : TECPrivateKey) : Integer;
     Function DoSignMultiOperationSigner(current_protocol : Word; SignerAccount : Cardinal; key : TECPrivateKey) : Integer;
     class function OpType : Byte; override;
     class function OpType : Byte; override;
@@ -763,7 +763,7 @@ begin
   Result := True;
   Result := True;
 end;
 end;
 
 
-procedure TOpMultiOperation.AffectedAccounts(list: TList<Cardinal>);
+procedure TOpMultiOperation.AffectedAccounts(list: TOrderedList<Cardinal>);
 Var i : Integer;
 Var i : Integer;
   Procedure _doAdd(nAcc : Cardinal);
   Procedure _doAdd(nAcc : Cardinal);
   Begin
   Begin