Selaa lähdekoodia

PIP-0033 implemented method "finddataoperations"

PascalCoin 6 vuotta sitten
vanhempi
commit
7bcf85e7a6
2 muutettua tiedostoa jossa 200 lisäystä ja 9 poistoa
  1. 4 1
      README.md
  2. 196 8
      src/core/UPCRPCOpData.pas

+ 4 - 1
README.md

@@ -61,6 +61,10 @@ Also, consider a donation at PascalCoin development account: "0-10"
     - New param "new_data" (HEXASTRING) if provided will change Account Data info. Limited from 0 to 32 bytes.
   - New method "senddata" as described on PIP-0033 returning an "Operation Object"
   - New method "signdata" as described on PIP-0033 returning a "Raw Operations Object"
+  - New method "finddataoperations" as described on PIP-0033 returning an ARRAY of "Raw Operations Object"
+    - News params added to original PIP
+      - "depth" (Integer, 1000 by default) : Will search backward in blocks a maximum of "depth" blocks
+      - "startblock" (Integer, optional) : If defined, will start at a specified block, otherwise will start at last "sender" or "target" updated block
   - Updated "Account Object" return values:
     - "state": Can return "normal", "listed", "account_swap", "coin_swap"
     - "hashed_secret" : (HEXASTRING) will contain the SHA256( SECRET ) value that must match Payload received data for Atomic Swaps (only when "state" in "account_swap" or "coin_swap")
@@ -85,7 +89,6 @@ TODO
 - TODO: RPC calls for PIP-0029
 - TODO: RPC calls for PIP-0030
 - TODO: RPC calls for PIP-0016
-- TODO: RPC "finddataoperations" call for PIP-0033
 
 ### Build 4.0.3.1 - 2019-04-12
 - Fixed core bug #182 in RPC calls

+ 196 - 8
src/core/UPCRPCOpData.pas

@@ -27,7 +27,7 @@ interface
 Uses classes, SysUtils,
   UJSONFunctions, UAccounts, UBaseTypes, UOpTransaction, UConst,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
-  URPC, UCrypto, UWallet, UBlockChain;
+  URPC, UCrypto, UWallet, UBlockChain, ULog;
 
 
 Type
@@ -106,15 +106,203 @@ class function TRPCOpData.OpData_FindOpDataOperations(
   const ASender: TRPCProcess; const AMethodName: String; AInputParams,
   AJSONResponse: TPCJSONObject; var AErrorNum: Integer;
   var AErrorDesc: String): Boolean;
+
+    Procedure DoFindFromBlock(ABlock_number : Integer;
+      ASearchedAccount_number: Cardinal;
+      AStartOperation, AEndOperation: Integer;
+      AAct_depth : Integer; AFirst_Block_Is_Unknown : Boolean;
+      ASearchBySender : Boolean; ASearchSender : Cardinal;
+      ASearchByTarget : Boolean; ASearchTarget : Cardinal;
+      ASearchByGUID : Boolean; ASearchGUID : TGUID;
+      ASearchByDataSequence : Boolean; ASearchDataSequence : Word;
+      ASearchByDataType : Boolean; ASearchDataType : Word;
+      AOperationsResumeList : TOperationsResumeList
+      );
+
+      Function EqualGUIDs(const AGuid_A, AGuid_B : TGUID) : Boolean;
+      var i : Integer;
+      begin
+        Result := (AGuid_A.D1 = AGuid_B.D1)
+          and (AGuid_A.D2 = AGuid_B.D2)
+          and (AGuid_A.D3 = AGuid_B.D3);
+        i := Low(AGuid_A.D4);
+        while (Result) and (i<=High(AGuid_A.D4)) do begin
+          Result := (AGuid_A.D4[i] = AGuid_B.D4[i]);
+          inc(i);
+        end;
+      end;
+
+    var LOpComp : TPCOperationsComp;
+      LOperation : TPCOperation;
+      LOpData : TOpData;
+      LOperationResume : TOperationResume;
+      LList : TList<Cardinal>;
+      i : Integer;
+      LLast_block_number : Integer;
+      LFound_in_block : Boolean;
+      LFoundCounter : Integer;
+    begin
+      if (AAct_depth<=0) then exit;
+      LFoundCounter := 0;
+      LOpComp := TPCOperationsComp.Create(Nil);
+      Try
+        LList := TList<Cardinal>.Create;
+        try
+          LLast_block_number := ABlock_number+1;
+          while (LLast_block_number>ABlock_number) And (AAct_depth>0)
+            And (ABlock_number >= (ASearchedAccount_number DIV CT_AccountsPerBlock))
+            And (LFoundCounter <= AEndOperation) do begin
+
+            if Assigned(ASender) then begin
+              // Threading protection
+              if ASender.Terminated then Exit;
+            end;
+            LFound_in_block := False;
+            LLast_block_number := ABlock_number;
+            LList.Clear;
+            If not ASender.Node.Bank.Storage.LoadBlockChainBlock(LOpComp,ABlock_number) then begin
+              TLog.NewLog(ltdebug,ClassName,'Block '+inttostr(ABlock_number)+' not found. Cannot read operations');
+              Exit;
+            end;
+            LFound_in_block := LOpComp.OperationsHashTree.GetOperationsAffectingAccount(ASearchedAccount_number,LList) > 0;
+              // Reverse order:
+            for i := LList.Count - 1 downto 0 do begin
+              LOperation := LOpComp.Operation[LList.Items[i]];
+              if LOperation is TOpData then begin
+                //
+                LOpData := TOpData( LOperation );
+                // Search by filter:
+                if ((Not ASearchBySender) Or (ASearchSender = LOpData.Data.account_sender))
+                   and ((Not ASearchByTarget) Or (ASearchTarget = LOpData.Data.account_target))
+                   and ((Not ASearchByGUID) Or (EqualGUIDs(ASearchGUID,LOpData.Data.guid)))
+                   and ((Not ASearchByDataSequence) Or (ASearchDataSequence = LOpData.Data.dataSequence))
+                   and ((Not ASearchByDataType) Or (ASearchDataType = LOpData.Data.dataType))
+                then begin
+                  if (LFoundCounter>=AStartOperation) And (LFoundCounter<=AEndOperation) then begin
+                    If TPCOperation.OperationToOperationResume(ABlock_number,LOpData,False,LOpData.SignerAccount,LOperationResume) then begin
+                      LOperationResume.Balance:=-1;
+                      LOperationResume.NOpInsideBlock:=LList.Items[i];
+                      LOperationResume.Block:=ABlock_number;
+                      AOperationsResumeList.Add(LOperationResume);
+                    end;
+                  end;
+                  inc(LFoundCounter);
+                end;
+              end;
+            end; // For LList...
+            //
+            dec(AAct_depth);
+            If (Not LFound_in_block) And (AFirst_Block_Is_Unknown) then begin
+              Dec(ABlock_number);
+            end else begin
+              ABlock_number := LOpComp.PreviousUpdatedBlocks.GetPreviousUpdatedBlock(ASearchedAccount_number,ABlock_number);
+            end;
+            LOpComp.Clear(true);
+          end;
+        finally
+          LList.Free;
+        end;
+      Finally
+        LOpComp.Free;
+      End;
+    end;
+
+Var LAccount : TAccount;
+  LStartBlock : Cardinal;
+  LMaxDepth : Integer;
+  LSearchedAccount_number: Cardinal;
+  LStartOperation, LMaxOperations: Integer;
+  LFirst_Block_Is_Unknown : Boolean;
+  LSearchBySender : Boolean;
+  LSearchByTarget : Boolean;
+  LSearchByGUID : Boolean; LSearchGUID : TGUID;
+  LSearchByDataSequence : Boolean; LSearchDataSequence : Word;
+  LSearchByDataType : Boolean; LSearchDataType : Word;
+  LOperationsResumeList : TOperationsResumeList;
+  LSender, LTarget : Cardinal;
+  LResultArray : TPCJSONArray;
+  i : Integer;
 begin
-  // TODO TODO TODO TODO
-  // TODO TODO TODO TODO
-  // TODO TODO TODO TODO
-  // TODO TODO TODO TODO
-  // TODO TODO TODO TODO
-  AErrorNum := CT_RPC_ErrNum_NotImplemented;
-  AErrorDesc := 'This method is not implemented. PENDING TODO';
   Result := False;
+
+  LSender := AInputParams.AsCardinal('sender',CT_MaxAccount);
+  LTarget := AInputParams.AsCardinal('target',CT_MaxAccount);
+  LSearchedAccount_number := CT_MaxAccount;
+  LSearchBySender := (LSender>=0) And (LSender<ASender.Node.Bank.AccountsCount);
+  LSearchByTarget := (LTarget>=0) And (LTarget<ASender.Node.Bank.AccountsCount);
+  if (LSearchBySender) then begin
+    LSearchedAccount_number := LSender;
+  end else if (LSearchByTarget) then begin
+    LSearchedAccount_number := LTarget;
+  end else begin
+    AErrorNum := CT_RPC_ErrNum_InvalidData;
+    AErrorDesc := 'Must provide "sender" or "target" valid values';
+    Exit;
+  end;
+
+  if AInputParams.IndexOfName('guid')>=0 then begin
+    try
+      LSearchGUID := StringToGUID( AInputParams.AsString('guid','') );
+      LSearchByGUID := True;
+    except
+      on E:Exception do begin
+        AErrorNum := CT_RPC_ErrNum_InvalidData;
+        AErrorDesc := 'Invalid "guid" param '+E.Message;
+        Exit;
+      end;
+    end;
+  end else LSearchByGUID := False;
+
+  if AInputParams.IndexOfName('data_sequence')>=0 then begin
+    LSearchByDataSequence := True;
+    LSearchDataSequence := AInputParams.AsInteger('data_sequence',0);
+  end else LSearchByDataSequence := False;
+
+  if AInputParams.IndexOfName('data_type')>=0 then begin
+    LSearchByDataType := True;
+    LSearchDataType := AInputParams.AsInteger('data_type',0);
+  end else LSearchByDataType := False;
+
+  LMaxDepth := AInputParams.AsInteger('depth',1000);
+  LStartOperation := AInputParams.AsInteger('start',0);
+  LMaxOperations := AInputParams.AsInteger('max',100);
+  if AInputParams.IndexOfName('startblock')>=0 then begin
+    LStartBlock := AInputParams.AsInteger('startblock',100);
+    LFirst_Block_Is_Unknown := True;
+  end else begin
+    if not ASender.RPCServer.GetMempoolAccount(LSearchedAccount_number,LAccount) then begin
+      AErrorNum := CT_RPC_ErrNum_InvalidData;
+      AErrorDesc := 'Invalid account';
+      Exit;
+    end;
+    LFirst_Block_Is_Unknown := False;
+    LStartBlock := LAccount.updated_block;
+    if LStartBlock>=ASender.Node.Bank.BlocksCount then Dec(LStartBlock); // If its updated on mempool, don't look the mempool
+  end;
+
+  LOperationsResumeList := TOperationsResumeList.Create;
+  try
+    DoFindFromBlock(LStartBlock,
+      LSearchedAccount_number,
+      LStartOperation, LStartOperation + LMaxOperations,
+      LMaxDepth, LFirst_Block_Is_Unknown,
+      LSearchBySender, LSender,
+      LSearchByTarget, LTarget,
+      LSearchByGUID, LSearchGUID,
+      LSearchByDataSequence, LSearchDataSequence,
+      LSearchByDataType, LSearchDataType,
+      LOperationsResumeList
+      );
+    //
+    LResultArray := AJSONResponse.GetAsArray('result');
+
+    for i := 0 to LOperationsResumeList.Count-1 do begin
+      TPascalCoinJSONComp.FillOperationObject(LOperationsResumeList.OperationResume[i],ASender.Node.Bank.BlocksCount,LResultArray.GetAsObject( LResultArray.Count ));
+    end;
+    Result := True;
+  finally
+    LOperationsResumeList.Free;
+  end;
 end;
 
 class function TRPCOpData.OpData_SendOpData(const ASender: TRPCProcess;