Parcourir la source

Reorganize code and create specific file for "sendto" and "signsendto" JSON-RPC calls

This will help future EPasa implementation
Pascal Coin il y a 4 ans
Parent
commit
3211f72a0a
2 fichiers modifiés avec 292 ajouts et 165 suppressions
  1. 267 0
      src/core/UPCRPCSend.pas
  2. 25 165
      src/core/URPC.pas

+ 267 - 0
src/core/UPCRPCSend.pas

@@ -0,0 +1,267 @@
+unit UPCRPCSend;
+
+{ Copyright (c) 2021 by PascalCoin developers, orignal code by Albert Molina
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  If you like it, consider a donation using Bitcoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+interface
+
+{$I ./../config.inc}
+
+Uses classes, SysUtils,
+  UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  URPC, UCrypto, UWallet, UBlockChain, ULog, UPCOrderedLists, UPCDataTypes;
+
+
+Type
+  TRPCSend = Class
+  private
+  public
+    class function CreateOperationTransaction(const ARPCProcess : TRPCProcess;
+      ACurrent_protocol : Word; ASender, ATarget, ASender_last_n_operation : Cardinal; AAmount, AFee : UInt64;
+      Const ASenderAccounKey, ATargetAccountKey : TAccountKey; Const ARawPayload : TRawBytes;
+      Const APayload_method, AEncodePwd : String; var AErrorNum: Integer; var AErrorDesc: String) : TOpTransaction;
+    //
+    class function SendTo(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+    class function SignSendTo(const ASender : TRPCProcess; const AMethodName : String; AInputParams, AJSONResponse : TPCJSONObject; var AErrorNum : Integer; var AErrorDesc : String) : Boolean;
+  End;
+
+implementation
+
+{ TRPCFindAccounts }
+
+class function TRPCSend.CreateOperationTransaction(const ARPCProcess : TRPCProcess;
+  ACurrent_protocol: Word;
+  ASender, ATarget, ASender_last_n_operation: Cardinal; AAmount, AFee: UInt64;
+  const ASenderAccounKey, ATargetAccountKey: TAccountKey;
+  const ARawPayload: TRawBytes; const APayload_method,
+  AEncodePwd: String; var AErrorNum: Integer; var AErrorDesc: String): TOpTransaction;
+
+Var LOpPayload : TOperationPayload;
+  LPrivateKey : TECPrivateKey;
+Begin
+  Result := Nil;
+  if Not ARPCProcess.RPCServer.CheckAndGetPrivateKeyInWallet(ASenderAccounKey,LPrivateKey,AErrorNum,AErrorDesc) then Exit(Nil);
+  if Not TPascalCoinJSONComp.CheckAndGetEncodedRAWPayload(ARawPayload,APayload_method,AEncodePwd,ASenderAccounKey,ATargetAccountKey,LOpPayload,AErrorNum,AErrorDesc) then Exit(Nil);
+  Result := TOpTransaction.CreateTransaction(ACurrent_protocol, ASender,ASender_last_n_operation+1, ATarget, LPrivateKey, AAmount, AFee, LOpPayload);
+  if Not Result.HasValidSignature then begin
+    FreeAndNil(Result);
+    AErrorNum:=CT_RPC_ErrNum_InternalError;
+    AErrorDesc:='Invalid signature';
+    exit;
+  end;
+end;
+
+class function TRPCSend.SendTo(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+
+{ JSON-RPC "sendto"
+
+### sendto
+Executes a transaction operation from "sender" to "target"
+
+##### Params
+- `sender` : Integer - Sender account
+- `target` : Integer - Destination account
+- `amount` : PASCURRENCY - Coins to be transferred
+- `fee` : PASCURRENCY - Fee of the operation
+- `payload` : HEXASTRING - Payload "item" that will be included in this operation
+- `payload_method` : String - Encode type of the item payload
+  - `none` : Not encoded. Will be visible for everybody
+  - `dest` (default) : Using Public key of "target" account. Only "target" will be able to decrypt this payload
+  - `sender` : Using sender Public key. Only "sender" will be able to decrypt this payload
+  - `aes` : Encrypted data using `pwd` param
+- `pwd` : String - Used to encrypt payload with `aes` as a `payload_method`. If none equals to empty password
+
+##### Result
+If transaction is successfull will return a JSON Object in "[Operation Object]" format.
+Otherwise, will return a JSON-RPC error code with description
+
+}
+
+var LSender, LTarget : TAccount;
+ LAmount, LFee : UInt64;
+ LRawPayload : TRawBytes;
+ LPayload_method, LEncodePwd, LErrors : String;
+ LOpt : TOpTransaction;
+ LOpr : TOperationResume;
+begin
+  // Get Parameters
+  Result := False;
+
+  if (Not ASender.RPCServer.AllowUsePrivateKeys) then begin
+    // Protection when server is locked to avoid private keys call
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit;
+  end;
+  If Not ASender.RPCServer.WalletKeys.IsValidPassword then begin
+    AErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+    AErrorDesc := 'Wallet is password protected. Unlock first';
+    exit;
+  end;
+
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',ASender.Node.Bank,LSender.account,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    Exit;
+  end else LSender := ASender.Node.GetMempoolAccount(LSender.account);
+
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'target',ASender.Node.Bank,LTarget.account,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    Exit;
+  end else LTarget := ASender.Node.GetMempoolAccount(LTarget.account);
+
+  LAmount := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('amount',0));
+  LFee := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('fee',0));
+  LRawPayload := TCrypto.HexaToRaw(AInputParams.AsString('payload',''));
+  LPayload_method := AInputParams.AsString('payload_method','dest');
+  LEncodePwd := AInputParams.AsString('pwd','');
+
+  ASender.Node.OperationSequenceLock.Acquire;  // Use lock to prevent N_Operation race-condition on concurrent sends
+  try
+    LOpt := CreateOperationTransaction(ASender, ASender.Node.Bank.SafeBox.CurrentProtocol,
+      LSender.account, LTarget.account, LSender.n_operation, LAmount, LFee,
+      LSender.accountInfo.accountKey, LTarget.accountInfo.accountKey,
+      LRawPayload, LPayload_method, LEncodePwd, AErrorNum, AErrorDesc);
+    if Assigned(LOpt) then
+    try
+      If not ASender.Node.AddOperation(Nil,LOpt,LErrors) then begin
+        AErrorDesc := 'Error adding operation: '+LErrors;
+        AErrorNum := CT_RPC_ErrNum_InvalidOperation;
+        Exit;
+      end;
+      TPCOperation.OperationToOperationResume(0,LOpt,False,LSender.account,LOpr);
+      TPascalCoinJSONComp.FillOperationObject(LOpr,ASender.Node.Bank.BlocksCount,AJSONResponse.GetAsObject('result'));
+      Result := true;
+    finally
+      LOpt.free;
+    end;
+  finally
+    ASender.Node.OperationSequenceLock.Release;
+  end;
+end;
+
+class function TRPCSend.SignSendTo(const ASender: TRPCProcess;
+  const AMethodName: String; AInputParams, AJSONResponse: TPCJSONObject;
+  var AErrorNum: Integer; var AErrorDesc: String): Boolean;
+
+{ JSON-RPC "signsendto"
+
+### signsendto
+
+Creates and signs a "Send to" operation without checking information and without transfering to the network.
+It's usefull for "cold wallets" that are off-line (not synchronized with the network) and only holds private keys
+
+##### Params
+- `rawoperations` : HEXASTRING (optional) - If we want to add a sign operation with other previous operations, here we must put previous `rawoperations` result
+- `sender` : Integer - Sender account
+- `target` : Integer - Target account
+- `sender_enc_pubkey` or `sender_b58_pubkey` : HEXASTRING - Public key (in encoded format or b58 format) of the sender account
+- `target_enc_pubkey` or `target_b58_pubkey` : HEXASTRING - Public key (in encoded format or b58 format) of the target account
+- `last_n_operation` : Last value of `n_operation` obtained with an [Account object](#account-object), for example when called to [getaccount](#getaccount)
+- `amount`,`fee`,`payload`,`payload_method`,`pwd` : Same values that calling [sendto](#sendto)
+
+##### Result
+
+Wallet must be unlocked and sender private key (searched with provided public key) must be in wallet.
+No other checks are made (no checks for valid target, valid n_operation, valid amount or fee ...)
+Returns a [Raw Operations Object](#raw-operations-object)
+
+}
+var LSender, LTarget : Cardinal;
+ LSenderPubKey, LTargetPubKey : TAccountKey;
+ LHexaStringOperationsHashTree, LErrors : String;
+ LProtocol : Integer;
+ LOperationsHashTree : TOperationsHashTree;
+ LOpt : TOpTransaction;
+ LOpr : TOperationResume;
+ LRawPayload : TRawBytes;
+ LPayload_method, LEncodePwd : String;
+ LAmount, LFee : UInt64;
+begin
+  Result := False;
+
+  if (Not ASender.RPCServer.AllowUsePrivateKeys) then begin
+    // Protection when server is locked to avoid private keys call
+    AErrorNum := CT_RPC_ErrNum_NotAllowedCall;
+    Exit;
+  end;
+  If Not ASender.RPCServer.WalletKeys.IsValidPassword then begin
+    AErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
+    AErrorDesc := 'Wallet is password protected. Unlock first';
+    exit;
+  end;
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',Nil,LSender,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    Exit;
+  end;
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'target',Nil,LTarget,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidAccount;
+    Exit;
+  end;
+  If Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'sender_',LSenderPubKey,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+    exit;
+  end;
+  If Not TPascalCoinJSONComp.CapturePubKey(AInputParams,'target_',LTargetPubKey,AErrorDesc) then begin
+    AErrorNum := CT_RPC_ErrNum_InvalidPubKey;
+    exit;
+  end;
+
+  LAmount := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('amount',0));
+  LFee := TPascalCoinJSONComp.ToPascalCoins(AInputParams.AsDouble('fee',0));
+  LRawPayload := TCrypto.HexaToRaw(AInputParams.AsString('payload',''));
+  LPayload_method := AInputParams.AsString('payload_method','dest');
+  LEncodePwd := AInputParams.AsString('pwd','');
+
+  LHexaStringOperationsHashTree := AInputParams.AsString('rawoperations','');
+  LProtocol := AInputParams.AsCardinal('protocol',CT_BUILD_PROTOCOL);
+
+  if Not TPascalCoinJSONComp.HexaStringToOperationsHashTree(LHexaStringOperationsHashTree,LProtocol,LOperationsHashTree,LErrors) then begin
+    AErrorNum:=CT_RPC_ErrNum_InvalidData;
+    AErrorDesc:= 'Error decoding param "rawoperations": '+LErrors;
+    Exit;
+  end;
+  Try
+    LOpt := CreateOperationTransaction(ASender,LProtocol,LSender,LTarget,
+      AInputParams.AsCardinal('last_n_operation',0),
+      LAmount, LFee,
+      LSenderPubKey, LTargetPubKey,
+      LRawPayload,LPayload_method,LEncodePwd, AErrorNum, AErrorDesc);
+    if Assigned(LOpt) then
+    try
+      LOperationsHashTree.AddOperationToHashTree(LOpt);
+      TPascalCoinJSONComp.FillOperationsHashTreeObject(LOperationsHashTree,AJSONResponse.GetAsObject('result'));
+      Result := true;
+    finally
+      LOpt.Free;
+    end;
+  Finally
+    LOperationsHashTree.Free;
+  End;
+end;
+
+initialization
+  TRPCProcess.RegisterProcessMethod('signsendto',TRPCSend.SignSendTo);
+  TRPCProcess.RegisterProcessMethod('sendto',TRPCSend.SendTo);
+finalization
+  TRPCProcess.UnregisterProcessMethod('signsendto');
+  TRPCProcess.UnregisterProcessMethod('sendto');
+end.

+ 25 - 165
src/core/URPC.pas

@@ -72,6 +72,7 @@ Type
     class Function HexaStringToOperationsHashTree(Const AHexaStringOperationsHashTree : String; ACurrentProtocol : Word; out AOperationsHashTree : TOperationsHashTree; var AErrors : String) : Boolean;
     class Function CapturePubKey(const AInputParams : TPCJSONObject; const APrefix : String; var APubKey : TAccountKey; var AErrortxt : String) : Boolean;
     class function CheckAndGetEncodedRAWPayload(Const ARawPayload : TRawBytes; Const APayload_method, AEncodePwdForAES : String; const ASenderAccounKey, ATargetAccountKey : TAccountKey; out AOperationPayload : TOperationPayload; Var AErrorNum : Integer; Var AErrorDesc : String) : Boolean;
+    class Function CaptureAccountNumber(const AInputParams : TPCJSONObject; const AParamName : String; const ABank : TPCBank; var AAccountNumber : Cardinal; var AErrorParam : String) : Boolean;
   end;
 
   TRPCServerThread = Class;
@@ -155,6 +156,7 @@ implementation
 
 Uses  {$IFNDEF FPC}windows,{$ENDIF}
   SysUtils, Synautil,
+  UPCRPCSend,
   UPCRPCOpData, UPCRPCFindAccounts, UPCRPCFindBlocks, UPCRPCFileUtils;
 
 Type
@@ -345,6 +347,28 @@ Begin
   end;
 end;
 
+class function TPascalCoinJSONComp.CaptureAccountNumber(const AInputParams : TPCJSONObject;
+  const AParamName: String; const ABank : TPCBank; var AAccountNumber: Cardinal;
+  var AErrorParam: String): Boolean;
+var LParamValue : String;
+Begin
+  LParamValue := AInputParams.AsString(AParamName,'');
+  if Length(LParamValue)>0 then begin
+    Result := TAccountComp.AccountTxtNumberToAccountNumber(LParamValue,AAccountNumber);
+    if Not Result then begin
+      AErrorParam := Format('"%s" is no valid Account number for Param "%s"',[LParamValue,AParamName]);
+    end else if Assigned(ABank) then begin
+      if (AAccountNumber<0) or (AAccountNumber>=ABank.AccountsCount) then begin
+        Result := False;
+        AErrorParam := Format('Account %d does not exist in safebox (param "%s")',[AAccountNumber,AParamName]);
+      end;
+    end;
+  end else begin
+    Result := False;
+    AErrorParam := Format('Param "%s" not provided or null',[AParamName]);
+  end;
+end;
+
 class function TPascalCoinJSONComp.CapturePubKey(
   const AInputParams: TPCJSONObject; const APrefix: String;
   var APubKey: TAccountKey; var AErrortxt: String): Boolean;
@@ -1175,98 +1199,9 @@ function TRPCProcess.ProcessMethod(const method: String; params: TPCJSONObject;
   Function CaptureAccountNumber(const AParamName : String; const ACheckAccountNumberExistsInSafebox : Boolean; var AAccountNumber : Cardinal; var AErrorParam : String) : Boolean;
   var LParamValue : String;
   Begin
-    LParamValue := params.AsString(AParamName,'');
-    if Length(LParamValue)>0 then begin
-      Result := TAccountComp.AccountTxtNumberToAccountNumber(LParamValue,AAccountNumber);
-      if Not Result then begin
-        AErrorParam := Format('"%s" is no valid Account number for Param "%s"',[LParamValue,AParamName]);
-      end else if (ACheckAccountNumberExistsInSafebox) then begin
-        if (AAccountNumber<0) or (AAccountNumber>=FNode.Bank.AccountsCount) then begin
-          Result := False;
-          AErrorParam := Format('Account %d does not exist in safebox (param "%s")',[AAccountNumber,AParamName]);
-        end;
-      end;
-    end else begin
-      Result := False;
-      AErrorParam := Format('Param "%s" not provided or null',[AParamName]);
-    end;
+    Result := TPascalCoinJSONComp.CaptureAccountNumber(params,AParamName,FNode.Bank,AAccountNumber,AErrorParam);
   End;
 
-  Function OpSendTo(sender, target : Cardinal; amount, fee : UInt64; Const RawPayload : TRawBytes; Const Payload_method, EncodePwd : String) : Boolean;
-  // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
-  Var opt : TOpTransaction;
-    sacc,tacc : TAccount;
-    errors : String;
-    opr : TOperationResume;
-  begin
-    FNode.OperationSequenceLock.Acquire;  // Use lock to prevent N_Operation race-condition on concurrent sends
-    try
-      Result := false;
-      if (sender<0) or (sender>=FNode.Bank.AccountsCount) then begin
-        If (sender=CT_MaxAccount) then ErrorDesc := 'Need sender'
-        else ErrorDesc:='Invalid sender account '+Inttostr(sender);
-        ErrorNum:=CT_RPC_ErrNum_InvalidAccount;
-        Exit;
-      end;
-      if (target<0) or (target>=FNode.Bank.AccountsCount) then begin
-        If (target=CT_MaxAccount) then ErrorDesc := 'Need target'
-        else ErrorDesc:='Invalid target account '+Inttostr(target);
-        ErrorNum:=CT_RPC_ErrNum_InvalidAccount;
-        Exit;
-      end;
-      sacc := FNode.GetMempoolAccount(sender);
-      tacc := FNode.GetMempoolAccount(target);
-
-      opt := CreateOperationTransaction(FNode.Bank.SafeBox.CurrentProtocol,sender,target,sacc.n_operation,amount,fee,sacc.accountInfo.accountKey,tacc.accountInfo.accountKey,RawPayload,Payload_method,EncodePwd);
-      if opt=nil then exit;
-      try
-        If not FNode.AddOperation(Nil,opt,errors) then begin
-          ErrorDesc := 'Error adding operation: '+errors;
-          ErrorNum := CT_RPC_ErrNum_InvalidOperation;
-          Exit;
-        end;
-        TPCOperation.OperationToOperationResume(0,opt,False,sender,opr);
-        FillOperationResumeToJSONObject(opr,GetResultObject);
-        Result := true;
-      finally
-        opt.free;
-      end;
-    finally
-      FNode.OperationSequenceLock.Release;
-    end;
-  end;
-
-  Function SignOpSendTo(Const HexaStringOperationsHashTree : String; current_protocol : Word;
-    sender, target : Cardinal;
-    Const senderAccounKey, targetAccountKey : TAccountKey;
-    last_sender_n_operation : Cardinal;
-    amount, fee : UInt64; Const RawPayload : TRawBytes; Const Payload_method, EncodePwd : String) : Boolean;
-  // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
-  var OperationsHashTree : TOperationsHashTree;
-    errors : String;
-    opt : TOpTransaction;
-  begin
-    Result := false;
-    if Not TPascalCoinJSONComp.HexaStringToOperationsHashTree(HexaStringOperationsHashTree,current_protocol,OperationsHashTree,errors) then begin
-      ErrorNum:=CT_RPC_ErrNum_InvalidData;
-      ErrorDesc:= 'Error decoding param "rawoperations": '+errors;
-      Exit;
-    end;
-    Try
-      opt := CreateOperationTransaction(current_protocol, sender,target,last_sender_n_operation,amount,fee,senderAccounKey,targetAccountKey,RawPayload,Payload_method,EncodePwd);
-      if opt=nil then exit;
-      try
-        OperationsHashTree.AddOperationToHashTree(opt);
-        TPascalCoinJSONComp.FillOperationsHashTreeObject(OperationsHashTree,GetResultObject);
-        Result := true;
-      finally
-        opt.Free;
-      end;
-    Finally
-      OperationsHashTree.Free;
-    End;
-  end;
-
   // This function creates a TOpChangeKey without looking for private key of account
   // It assumes that account_signer,account_last_n_operation, account_target and account_pubkey are correct
   Function CreateOperationChangeKey(current_protocol : Word; account_signer, account_last_n_operation, account_target : Cardinal; const account_pubkey, new_pubkey : TAccountKey; fee : UInt64; RawPayload : TRawBytes; Const Payload_method, EncodePwd : String) : TOpChangeKey;
@@ -3227,81 +3162,6 @@ begin
     // Search for all operations signed by "account" and n_operation value between "n_operation_min" and "n_operation_max", start searching at "block" (0=all)
     // "block" = 0 search in all blocks, pending operations included
     Result := findNOperations;
-  end else if (method='sendto') then begin
-    // Sends "amount" coins from "sender" to "target" with "fee"
-    // If "payload" is present, it will be encoded using "payload_method"
-    // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
-    // Returns a JSON "Operation Resume format" object when successfull
-    // Note: "ophash" will contain block "0" = "pending block"
-    if (Not _RPCServer.AllowUsePrivateKeys) then begin
-      // Protection when server is locked to avoid private keys call
-      ErrorNum := CT_RPC_ErrNum_NotAllowedCall;
-      Exit;
-    end;
-    If Not _RPCServer.WalletKeys.IsValidPassword then begin
-      ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
-      ErrorDesc := 'Wallet is password protected. Unlock first';
-      exit;
-    end;
-    if Not CaptureAccountNumber('sender',True,c2,ErrorDesc) then begin
-      ErrorNum := CT_RPC_ErrNum_InvalidAccount;
-      Exit;
-    end;
-    if Not CaptureAccountNumber('target',True,c3,ErrorDesc) then begin
-      ErrorNum := CT_RPC_ErrNum_InvalidAccount;
-      Exit;
-    end;
-    Result := OpSendTo(c2,c3,
-       ToPascalCoins(params.AsDouble('amount',0)),
-       ToPascalCoins(params.AsDouble('fee',0)),
-       TCrypto.HexaToRaw(params.AsString('payload','')),
-       params.AsString('payload_method','dest'),params.AsString('pwd',''));
-  end else if (method='signsendto') then begin
-    // Create a Transaction operation and adds it into a "rawoperations" (that can include
-    // previous operations). This RPC method is usefull ffor cold storage, because doesn't
-    // need to check or verify accounts status/public key, assuming that passed values
-    // are ok.
-    // Signs a transaction of "amount" coins from "sender" to "target" with "fee", using "sender_enc_pubkey" or "sender_b58_pubkey"
-    // and "last_n_operation" of sender. Also, needs "target_enc_pubkey" or "target_b58_pubkey"
-    // If "payload" is present, it will be encoded using "payload_method"
-    // "payload_method" types: "none","dest"(default),"sender","aes"(must provide "pwd" param)
-    // Returns a JSON "Operations info" containing old "rawoperations" plus new Transaction
-    if (Not _RPCServer.AllowUsePrivateKeys) then begin
-      // Protection when server is locked to avoid private keys call
-      ErrorNum := CT_RPC_ErrNum_NotAllowedCall;
-      Exit;
-    end;
-    If Not _RPCServer.WalletKeys.IsValidPassword then begin
-      ErrorNum := CT_RPC_ErrNum_WalletPasswordProtected;
-      ErrorDesc := 'Wallet is password protected. Unlock first';
-      exit;
-    end;
-    if Not CaptureAccountNumber('sender',False,c2,ErrorDesc) then begin
-      ErrorNum := CT_RPC_ErrNum_InvalidAccount;
-      Exit;
-    end;
-    if Not CaptureAccountNumber('target',False,c3,ErrorDesc) then begin
-      ErrorNum := CT_RPC_ErrNum_InvalidAccount;
-      Exit;
-    end;
-    If Not CapturePubKey('sender_',senderpubkey,ErrorDesc) then begin
-      ErrorNum := CT_RPC_ErrNum_InvalidPubKey;
-      exit;
-    end;
-    If Not CapturePubKey('target_',destpubkey,ErrorDesc) then begin
-      ErrorNum := CT_RPC_ErrNum_InvalidPubKey;
-      exit;
-    end;
-    Result := SignOpSendTo(
-       params.AsString('rawoperations',''),
-       params.AsCardinal('protocol',CT_BUILD_PROTOCOL),
-       c2,c3,
-       senderpubkey,destpubkey,
-       params.AsCardinal('last_n_operation',0),
-       ToPascalCoins(params.AsDouble('amount',0)),
-       ToPascalCoins(params.AsDouble('fee',0)),
-       TCrypto.HexaToRaw(params.AsString('payload','')),
-       params.AsString('payload_method','dest'),params.AsString('pwd',''));
   end else if (method='changekey') then begin
     // Change key of "account" to "new_enc_pubkey" or "new_b58_pubkey" (encoded public key format) with "fee"
     // If "payload" is present, it will be encoded using "payload_method"