Browse Source

Introducing TEPasaDecoder

Pascal Coin 4 years ago
parent
commit
14c4078d55
4 changed files with 233 additions and 11 deletions
  1. 32 0
      src/core/UEPasa.pas
  2. 167 0
      src/core/UEPasaDecoder.pas
  3. 6 3
      src/gui-classic/UFRMWallet.pas
  4. 28 8
      src/gui-classic/UGridUtils.pas

+ 32 - 0
src/core/UEPasa.pas

@@ -73,6 +73,7 @@ type
   public
     function HasTrait(APayloadTrait: TPayloadTrait): Boolean; inline;
     function ToProtocolValue : byte;
+    function IsValid : Boolean;
   end;
 
   { TEPasa }
@@ -244,6 +245,33 @@ begin
   Result := APayloadTrait in Self;
 end;
 
+function TPayloadTypeHelper.IsValid: Boolean;
+var LValue, LEncryptedBits, LFormattedBits : Byte;
+begin
+  { As described on PIP-0027 E-PASA:
+    Bits 0..3 describe how payload is encrypted. 1 option (and only 1) must be selected
+    Bits 4..6 describe how is data encoded: String, Hexa or Base58. 1 option (and 1 only 1) must be selected
+
+    IsValid = 1 bit from 0..3 and 1 bit from 4..6 must be selected
+  }
+  LValue := Self.ToProtocolValue;
+  LEncryptedBits := (LValue and $0F); // 0000 1111
+  LFormattedBits := (LValue and $70); // 0111 0000
+  Result :=
+      (
+         ((LEncryptedBits and BYTE_BIT_0)=BYTE_BIT_0)
+      or ((LEncryptedBits and BYTE_BIT_1)=BYTE_BIT_1)
+      or ((LEncryptedBits and BYTE_BIT_2)=BYTE_BIT_2)
+      or ((LEncryptedBits and BYTE_BIT_3)=BYTE_BIT_3)
+      )
+      and
+      (
+         ((LFormattedBits and BYTE_BIT_4)=BYTE_BIT_4)
+      or ((LFormattedBits and BYTE_BIT_5)=BYTE_BIT_5)
+      or ((LFormattedBits and BYTE_BIT_6)=BYTE_BIT_6)
+      );
+end;
+
 function TPayloadTypeHelper.ToProtocolValue : Byte;
 begin
   Result := TEPasaComp.GetPayloadTypeProtocolByte(Self);
@@ -426,6 +454,10 @@ begin
   end;
 
   if (not AOmitExtendedChecksum) then begin
+    if (ExtendedChecksum='') then begin
+      // Need to compute:
+      ExtendedChecksum := TEPasaComp.ComputeExtendedChecksum(Result);
+    end;
     Result := Result + string.Format(':%s', [ExtendedChecksum]);
   end;
 end;

+ 167 - 0
src/core/UEPasaDecoder.pas

@@ -0,0 +1,167 @@
+unit UEPasaDecoder;
+
+{ Copyright (c) PascalCoin Developers - Herman Schoenfeld - Albert Molina
+
+  PIP-0027: E-PASA Reference Implementation
+  See: https://github.com/PascalCoin/PascalCoin/blob/master/PIP/PIP-0027.md
+
+  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:
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+  {$ZEROBASEDSTRINGS OFF}
+{$ENDIF FPC}
+
+interface
+
+{$I ./../config.inc}
+
+uses
+  SysUtils,
+  TypInfo,
+  {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF},
+  UBlockChain, UNode, UBaseTypes,
+  UAccounts,
+  UEncoding,
+  UEPasa,
+  UWallet;
+
+type
+  TDecodeEPasaResult = (der_Decoded, der_Undefined, der_NonDeterministic, der_InvalidPayloadType, der_AccountNameNotFound, der_NotEnoughData, der_PrivateKeyNotFound, der_PasswordNotFound);
+
+  TEPasaDecoder = Class
+  public
+    class Function TryDecodeEPASA(AAccount : Cardinal; const APayload : TOperationPayload; const ANode : TNode; const AWalletKeys : TWalletKeys; const APasswords : TList<String>;
+      out ADecodeEPasaResult : TDecodeEPasaResult; out AEPasa : TEPasa) : Boolean; overload;
+    class Function TryDecodeEPASA(AAccount : Cardinal; const APayload : TOperationPayload; const ANode : TNode; const AWalletKeys : TWalletKeys; const APasswords : TList<String>;
+      out AEPasa : TEPasa) : Boolean; overload;
+  End;
+
+implementation
+
+uses UPCEncryption, UCommon;
+
+{ TEPasaDecoder }
+
+class function TEPasaDecoder.TryDecodeEPASA(AAccount: Cardinal;
+  const APayload: TOperationPayload; const ANode : TNode; const AWalletKeys: TWalletKeys;
+  const APasswords: TList<String>; out ADecodeEPasaResult: TDecodeEPasaResult;
+  out AEPasa: TEPasa): Boolean;
+var
+  LUnencryptedPayloadBytes, LPwd : TBytes;
+  LDone : Boolean;
+  i : Integer;
+begin
+  LUnencryptedPayloadBytes := Nil;
+  AEPasa.Clear;
+  Result := False;
+  ADecodeEPasaResult := der_Decoded;
+  AEPasa.Account := AAccount;
+  AEPasa.AccountChecksum := TEPasa.CalculateAccountChecksum(AAccount);
+  AEPasa.PayloadType := TEPasaComp.FromProtocolValue(APayload.payload_type);
+  if AEPasa.PayloadType.HasTrait(ptNonDeterministic) then begin
+    ADecodeEPasaResult := der_NonDeterministic;
+    Exit(False);
+  end;
+  if Not AEPasa.PayloadType.IsValid then begin
+    ADecodeEPasaResult := der_InvalidPayloadType;
+    Exit(False);
+  end;
+
+  if AEPasa.PayloadType.HasTrait(ptAddressedByName) then begin
+    if (AEPasa.PayloadType.HasTrait(ptPublic) and
+       AEPasa.PayloadType.HasTrait(ptBase58Formatted)) then begin
+      // PayToKey candidate...
+      AEPasa.AccountName := '@';
+    end else begin
+      if Assigned(ANode) then begin
+        AEPasa.AccountName := ANode.GetMempoolAccount(AAccount).name.ToPrintable;
+      end;
+      if AEPasa.AccountName='' then begin
+        ADecodeEPasaResult := der_AccountNameNotFound; // Will continue processing
+      end;
+    end;
+  end;
+
+  // payload data
+  if (Length(APayload.payload_raw)=0) then begin
+    // Nothing to decode...
+  end else if (AEPasa.PayloadType.HasTrait(ptSenderKeyEncrypted)) or (AEPasa.PayloadType.HasTrait(ptRecipientKeyEncrypted)) then begin
+    if Assigned(AWalletKeys) then begin
+      LDone := False;
+      i := 0;
+      while (Not LDone) and (i < AWalletKeys.Count) do begin
+        if Assigned(AWalletKeys.Key[i].PrivateKey) then begin
+          if TPCEncryption.DoPascalCoinECIESDecrypt(AWalletKeys.Key[i].PrivateKey.PrivateKey,APayload.payload_raw,LUnencryptedPayloadBytes) then begin
+            LDone := True;
+          end;
+        end;
+        inc(i);
+      end;
+      if Not LDone then begin
+        ADecodeEPasaResult := der_PrivateKeyNotFound;
+        Exit(False);
+      end;
+    end else begin
+      ADecodeEPasaResult := der_NotEnoughData;
+      Exit(False);
+    end;
+  end else if (AEPasa.PayloadType.HasTrait(ptPasswordEncrypted)) then begin
+    if Assigned(APasswords) then begin
+      LDone := False;
+      i := 0;
+      while (Not LDone) and (i < APasswords.Count) do begin
+        LPwd.FromString(APasswords[i]);
+        if TPCEncryption.DoPascalCoinAESDecrypt(APayload.payload_raw,LPwd,LUnencryptedPayloadBytes) then begin
+          LDone := True;
+        end;
+        inc(i);
+      end;
+      if Not LDone then begin
+        ADecodeEPasaResult := der_PasswordNotFound;
+        Exit(False);
+      end;
+    end else begin
+      ADecodeEPasaResult := der_NotEnoughData;
+      Exit(False);
+    end;
+  end else begin
+    if (Not AEPasa.PayloadType.HasTrait(ptPublic)) then begin
+      // Internal Error
+      ADecodeEPasaResult := der_Undefined;
+      Exit(False);
+    end;
+    LUnencryptedPayloadBytes := APayload.payload_raw;
+  end;
+  // LUnencryptedPayloadBytes Has Value in RAW
+  if (AEPasa.PayloadType.HasTrait(ptAsciiFormatted)) then begin
+    AEPasa.Payload := LUnencryptedPayloadBytes.ToString;
+  end else if (AEPasa.PayloadType.HasTrait(ptHexFormatted)) then begin
+    AEPasa.Payload := THexEncoding.Encode(LUnencryptedPayloadBytes,False);
+  end else if (AEPasa.PayloadType.HasTrait(ptBase58Formatted)) then begin
+    AEPasa.Payload := TPascalBase58Encoding.Encode(LUnencryptedPayloadBytes);
+  end else begin
+    // Internal error
+    ADecodeEPasaResult := der_Undefined;
+    Exit(False);
+  end;
+  Result := true;
+end;
+
+class function TEPasaDecoder.TryDecodeEPASA(AAccount: Cardinal;
+  const APayload: TOperationPayload; const ANode : TNode; const AWalletKeys: TWalletKeys;
+  const APasswords: TList<String>; out AEPasa: TEPasa): Boolean;
+var LDecodeEPasaResult: TDecodeEPasaResult;
+begin
+  Result := TryDecodeEPASA(AAccount,APayload,ANode,AWalletKeys,APasswords,LDecodeEPasaResult,AEPasa);
+end;
+
+end.

+ 6 - 3
src/gui-classic/UFRMWallet.pas

@@ -333,7 +333,7 @@ Uses UFolderHelper,{$IFDEF USE_GNUGETTEXT}gnugettext,{$ENDIF}
   {$ENDIF}
   UPCTNetDataExtraMessages,
   UFRMAskForAccount,
-  UAbstractBTree,
+  UAbstractBTree, UEPasaDecoder,
   UFRMAbout, UFRMOperation, UFRMWalletKeys, UFRMPayloadDecoder, UFRMNodesIp, UFRMMemoText,
   UCommon, UPCOrderedLists;
 
@@ -1010,10 +1010,10 @@ begin
   If (Length(OperationResume.OperationHash_OLD)>0) then begin
     Strings.Add(Format('Old Operation Hash (old_ophash): %s',[TCrypto.ToHexaString(OperationResume.OperationHash_OLD)]));
   end;
-  if TAccountComp.TryDecodeEPASAPartial(OperationResume.DestAccount,OperationResume.OriginalPayload.payload_raw,OperationResume.OriginalPayload.payload_type,LEPASA) then begin
+  if TEPasaDecoder.TryDecodeEPASA(OperationResume.DestAccount,OperationResume.OriginalPayload,FNode,FWalletKeys,Nil,LEPASA) then begin
     Strings.Add('EPASA: '+LEPASA.ToString);
   end else Strings.Add('No EPASA format');
-  Strings.Add(Format('Payload type:%d length:%d',[OperationResume.OriginalPayload.payload_type, length(OperationResume.OriginalPayload.payload_raw)]));
+  Strings.Add(Format('Payload type:%s length:%d',['0x'+IntToHex(OperationResume.OriginalPayload.payload_type), length(OperationResume.OriginalPayload.payload_raw)]));
   if (Length(OperationResume.OriginalPayload.payload_raw)>0) then begin
     If OperationResume.PrintablePayload<>'' then begin
       Strings.Add(Format('Payload (human): %s',[OperationResume.PrintablePayload]));
@@ -1352,14 +1352,17 @@ begin
   FOperationsAccountGrid := TOperationsGrid.Create(Self);
   FOperationsAccountGrid.DrawGrid := dgAccountOperations;
   FOperationsAccountGrid.MustShowAlwaysAnAccount := true;
+  FOperationsAccountGrid.WalletKeys := FWalletKeys;
   FPendingOperationsGrid := TOperationsGrid.Create(Self);
   FPendingOperationsGrid.DrawGrid := dgPendingOperations;
   FPendingOperationsGrid.AccountNumber := -1; // all
   FPendingOperationsGrid.PendingOperations := true;
+  FPendingOperationsGrid.WalletKeys := FWalletKeys;
   FOperationsExplorerGrid := TOperationsGrid.Create(Self);
   FOperationsExplorerGrid.DrawGrid := dgOperationsExplorer;
   FOperationsExplorerGrid.AccountNumber := -1;
   FOperationsExplorerGrid.PendingOperations := False;
+  FOperationsExplorerGrid.WalletKeys := FWalletKeys;
   FBlockChainGrid := TBlockChainGrid.Create(Self);
   FBlockChainGrid.DrawGrid := dgBlockChainExplorer;
   FBlockChainGrid.ShowTimeAverageColumns:={$IFDEF SHOW_AVERAGE_TIME_STATS}True;{$ELSE}False;{$ENDIF}

+ 28 - 8
src/gui-classic/UGridUtils.pas

@@ -134,7 +134,7 @@ Type
 
   TOperationsGridUpdateThread = Class(TPCThread)
     FOperationsGrid : TOperationsGrid;
-    procedure DoUpdateOperationsGrid(ANode : TNode; var AList : TList<TOperationResume>);
+    procedure DoUpdateOperationsGrid(const ANode : TNode; const AWalleTKeys : TWalletKeys; const APasswords : TList<String>; var AList : TList<TOperationResume>);
   protected
     procedure BCExecute; override;
   public
@@ -152,6 +152,8 @@ Type
     FBlockEnd: Int64;
     FMustShowAlwaysAnAccount: Boolean;
     FOperationsGridUpdateThread : TOperationsGridUpdateThread;
+    FWalletKeys: TWalletKeys;
+    FPasswords: TList<String>;
     Procedure OnNodeNewOperation(Sender : TObject);
     Procedure OnNodeNewAccount(Sender : TObject);
     Procedure InitGrid;
@@ -177,6 +179,8 @@ Type
     Property AccountNumber : Int64 read FAccountNumber write SetAccountNumber;
     Property MustShowAlwaysAnAccount : Boolean read FMustShowAlwaysAnAccount write SetMustShowAlwaysAnAccount;
     Property Node : TNode read GetNode write SetNode;
+    property WalletKeys : TWalletKeys read FWalletKeys write FWalletKeys;
+    property Passwords : TList<String> read FPasswords;
     Procedure UpdateAccountOperations; virtual;
     Procedure ShowModalDecoder(WalletKeys: TWalletKeys; AppParams : TAppParams);
     Property BlockStart : Int64 read FBlockStart write SetBlockStart;
@@ -281,6 +285,7 @@ implementation
 
 uses
   Graphics, SysUtils, UTime, UOpTransaction, UConst,
+  UEPasa, UEPasaDecoder,
   UFRMPayloadDecoder, ULog;
 
 { TAccountsGridUpdateThread }
@@ -940,7 +945,7 @@ var list : TList<TOperationResume>;
 begin
   list := TList<TOperationResume>.Create;
   try
-    DoUpdateOperationsGrid(FOperationsGrid.Node,list);
+    DoUpdateOperationsGrid(FOperationsGrid.Node,FOperationsGrid.WalletKeys,FOperationsGrid.Passwords,list);
     if (Not Terminated) then begin
       FOperationsGrid.FOperationsResume.Clear;
       for i := 0 to list.Count-1 do begin
@@ -961,7 +966,8 @@ begin
   Suspended := False;
 end;
 
-procedure TOperationsGridUpdateThread.DoUpdateOperationsGrid(ANode: TNode; var AList: TList<TOperationResume>);
+procedure TOperationsGridUpdateThread.DoUpdateOperationsGrid(const ANode : TNode; const AWalleTKeys : TWalletKeys;
+  const APasswords : TList<String>; var AList: TList<TOperationResume>);
 Var list : TList<Cardinal>;
   i,j : Integer;
   OPR : TOperationResume;
@@ -970,6 +976,7 @@ Var list : TList<Cardinal>;
   bstart,bend : int64;
   LOperationsResume : TOperationsResumeList;
   LLockedMempool : TPCOperationsComp;
+  LEPasa : TEPasa;
 begin
   if Not Assigned(ANode) then exit;
   AList.Clear;
@@ -1061,6 +1068,13 @@ begin
       end;
     end;
   Finally
+    for i := 0 to AList.Count-1 do begin
+      OPR := AList[i];
+      if TEPasaDecoder.TryDecodeEPASA(OPR.AffectedAccount,OPR.OriginalPayload,ANode,AWalleTKeys,APasswords,LEPasa) then begin
+        OPR.PrintablePayload := LEPasa.ToString(True);
+        AList[i] := OPR;
+      end;
+    end;
   End;
 end;
 
@@ -1068,6 +1082,8 @@ end;
 
 constructor TOperationsGrid.Create(AOwner: TComponent);
 begin
+  FPasswords := TList<String>.Create;
+  FWalletKeys := Nil;
   FAccountNumber := 0;
   FDrawGrid := Nil;
   MustShowAlwaysAnAccount := false;
@@ -1091,6 +1107,7 @@ begin
   end;
   FOperationsResume.Free;
   FNodeNotifyEvents.Free;
+  FPasswords.Free;
   inherited;
 end;
 
@@ -1208,11 +1225,14 @@ begin
         end;
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfRight,tfVerticalCenter,tfSingleLine]);
       end else if ACol=7 then begin
-        if Length(opr.Receivers)>0 then begin
-          s := opr.Receivers[0].AccountEPASA.ToString();
-          if s='' then s := opr.PrintablePayload;
-        end else s := opr.PrintablePayload;
-        Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfLeft,tfVerticalCenter,tfSingleLine]);
+        s := opr.PrintablePayload;
+        if opr.OriginalPayload.payload_type>0 then begin
+          DrawGrid.Canvas.Font.Color := clBlue;
+        end;
+        if opr.OriginalPayload.payload_raw.ToString=s then begin
+          DrawGrid.Canvas.Font.Style := [fsBold];
+        end;
+        Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfLeft,tfVerticalCenter,tfSingleLine])
       end else begin
         s := '(???)';
         Canvas_TextRect(DrawGrid.Canvas,Rect,s,State,[tfCenter,tfVerticalCenter,tfSingleLine]);