Browse Source

Improvements on senddata api calls

Allow use account number as a text on senddata and finddata api calls
Race condition protection
Pascal Coin 4 years ago
parent
commit
15a4390188
2 changed files with 35 additions and 8 deletions
  1. 17 8
      src/core/UPCRPCOpData.pas
  2. 18 0
      src/core/URPC.pas

+ 17 - 8
src/core/UPCRPCOpData.pas

@@ -225,8 +225,12 @@ Var LAccount : TAccount;
 begin
   Result := False;
 
-  LSender := AInputParams.AsCardinal('sender',CT_MaxAccount);
-  LTarget := AInputParams.AsCardinal('target',CT_MaxAccount);
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'sender',ASender.Node,LSender,AErrorDesc) then begin
+    LSender := CT_MaxAccount;
+  end;
+  if Not TPascalCoinJSONComp.CaptureAccountNumber(AInputParams,'target',ASender.Node,LTarget,AErrorDesc) then begin
+    LTarget := CT_MaxAccount;
+  end;
   LSearchedAccount_number := CT_MaxAccount;
   LSearchBySender := (LSender>=0) And (LSender<ASender.Node.Bank.AccountsCount);
   LSearchByTarget := (LTarget>=0) And (LTarget<ASender.Node.Bank.AccountsCount);
@@ -317,21 +321,22 @@ var LOpData : TOpData;
   LOPR : TOperationResume;
 begin
   Result := False;
-  if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('sender',CT_MaxAccount),LSender) then begin
+  ASender.Node.OperationSequenceLock.Acquire;  // Use lock to prevent N_Operation race-condition on concurrent operations
+  try
+
+  if Not TPascalCoinJSONComp.CaptureMempoolAccount(AInputParams,'sender',ASender.Node,LSender,AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
-    AErrorDesc := 'Invalid "sender"';
     Exit;
   end;
   if (AInputParams.IndexOfName('signer')>=0) then begin
-    if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('signer',CT_MaxAccount),LSigner) then begin
+    if Not TPascalCoinJSONComp.CaptureMempoolAccount(AInputParams,'signer',ASender.Node,LSigner,AErrorDesc) then begin
       AErrorNum := CT_RPC_ErrNum_InvalidAccount;
-      AErrorDesc := 'Invalid "signer"';
       Exit;
     end;
   end else LSigner := LSender; // If no "signer" param, then use "sender" as signer by default
-  if Not ASender.RPCServer.GetMempoolAccount(AInputParams.AsInteger('target',CT_MaxAccount),LTarget) then begin
+
+  if Not TPascalCoinJSONComp.CaptureMempoolAccount(AInputParams,'target',ASender.Node,LTarget,AErrorDesc) then begin
     AErrorNum := CT_RPC_ErrNum_InvalidAccount;
-    AErrorDesc := 'Invalid "target"';
     Exit;
   end;
 
@@ -371,6 +376,10 @@ begin
   finally
     LOpData.free;
   end;
+
+  finally
+    ASender.Node.OperationSequenceLock.Release;
+  end;
 end;
 
 class function TRPCOpData.OpData_SignOpData(const ASender: TRPCProcess;

+ 18 - 0
src/core/URPC.pas

@@ -79,6 +79,7 @@ Type
     class function CheckAndGetEncodedRAWPayload(Const ARawPayload : TRawBytes; const APayloadType : TPayloadType; Const APayload_method, AEncodePwdForAES : String; const ASenderAccounKey, ATargetAccountKey : TAccountKey; out AOperationPayload : TOperationPayload; Var AErrorNum : Integer; Var AErrorDesc : String) : Boolean;
     class Function CaptureNOperation(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out ALastNOp: Cardinal; var AErrorParam : String) : Boolean;
     class Function CaptureAccountNumber(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out AResolvedAccount: Cardinal; var AErrorParam : String) : Boolean;
+    class Function CaptureMempoolAccount(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out AMempoolAccount: TAccount; var AErrorParam : String) : Boolean;
     class Function CaptureEPASA(const AInputParams : TPCJSONObject; const AParamName : String; const ANode : TNode; out AEPasa: TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : Boolean; var AErrorParam : String) : Boolean; overload;
     class Function CaptureEPASA(const AEPasaText : String; const ANode : TNode; out AEPasa: TEPasa; out AResolvedAccount: Cardinal; out AResolvedKey : TAccountKey; out ARequiresPurchase : Boolean; var AErrorParam : String) : Boolean; overload;
     class Function OverridePayloadParams(const AInputParams : TPCJSONObject; const AEPASA : TEPasa) : Boolean;
@@ -507,6 +508,23 @@ Begin
   end;
 end;
 
+class function TPascalCoinJSONComp.CaptureMempoolAccount(
+  const AInputParams: TPCJSONObject; const AParamName: String;
+  const ANode: TNode; out AMempoolAccount: TAccount;
+  var AErrorParam: String): Boolean;
+var LAccountNumber : Cardinal;
+begin
+  Result := CaptureAccountNumber(AInputParams,AParamName,ANode,LAccountNumber,AErrorParam);
+  if Result then begin
+    if (LAccountNumber>=0) And (LAccountNumber<ANode.Bank.AccountsCount) then begin
+      AMempoolAccount := ANode.GetMempoolAccount(LAccountNumber);
+    end else begin
+      AErrorParam := Format('%d not in range 0..%d for Param "%s"',[LAccountNumber,ANode.Bank.AccountsCount,AParamName]);
+      Result := False;
+    end;
+  end;
+end;
+
 class function TPascalCoinJSONComp.OverridePayloadParams(const AInputParams : TPCJSONObject; const AEPASA : TEPasa) : Boolean;
 var LPayloadmethod_old,LPayloadmethod_new : String;
     LPayload_old, LPayload_new : String;