Browse Source

add random operations form to experimental GUI

Ugochukwu Mmaduekwe 6 years ago
parent
commit
7cad7b8546

+ 4 - 5
src/gui-experimental/UFRMMainForm.lfm

@@ -22,7 +22,6 @@ object FRMMainForm: TFRMMainForm
   OnResize = FormResize
   OnResize = FormResize
   Position = poScreenCenter
   Position = poScreenCenter
   ShowHint = True
   ShowHint = True
-  LCLVersion = '1.8.4.0'
   Visible = False
   Visible = False
   object paLogoPanel: TPanel
   object paLogoPanel: TPanel
     Left = 0
     Left = 0
@@ -45,7 +44,7 @@ object FRMMainForm: TFRMMainForm
   end
   end
   object paSyncPanel: TPanel
   object paSyncPanel: TPanel
     Left = 0
     Left = 0
-    Height = 477
+    Height = 497
     Top = 80
     Top = 80
     Width = 870
     Width = 870
     Align = alClient
     Align = alClient
@@ -62,7 +61,7 @@ object FRMMainForm: TFRMMainForm
   end
   end
   object paWalletPanel: TPanel
   object paWalletPanel: TPanel
     Left = 0
     Left = 0
-    Height = 477
+    Height = 497
     Top = 80
     Top = 80
     Width = 870
     Width = 870
     Align = alClient
     Align = alClient
@@ -79,7 +78,7 @@ object FRMMainForm: TFRMMainForm
   object sbStatusBar: TStatusBar
   object sbStatusBar: TStatusBar
     Left = 0
     Left = 0
     Height = 23
     Height = 23
-    Top = 557
+    Top = 577
     Width = 870
     Width = 870
     Panels = <    
     Panels = <    
       item
       item
@@ -108,7 +107,7 @@ object FRMMainForm: TFRMMainForm
     AnchorSideBottom.Side = asrBottom
     AnchorSideBottom.Side = asrBottom
     Left = 783
     Left = 783
     Height = 15
     Height = 15
-    Top = 561
+    Top = 581
     Width = 70
     Width = 70
     Align = alNone
     Align = alNone
     Anchors = [akTop, akRight]
     Anchors = [akTop, akRight]

+ 53 - 1
src/gui-experimental/UFRMMainForm.pas

@@ -113,6 +113,14 @@ type
     procedure OnNetStatisticsChanged(Sender: TObject);
     procedure OnNetStatisticsChanged(Sender: TObject);
     procedure OnWalletChanged(Sender: TObject);
     procedure OnWalletChanged(Sender: TObject);
     procedure OnUIRefreshTimer(Sender: TObject);
     procedure OnUIRefreshTimer(Sender: TObject);
+    {$IFDEF TESTNET}
+    procedure InitMenuForTesting;
+    procedure Test_RandomOperations(Sender: TObject);
+    procedure Test_ShowPublicKeys(Sender: TObject);
+    {$IFDEF TESTING_NO_POW_CHECK}
+    procedure Test_CreateABlock(Sender: TObject);
+    {$ENDIF}
+    {$ENDIF}
   protected
   protected
     procedure RefreshConnectionStatusDisplay;
     procedure RefreshConnectionStatusDisplay;
     procedure RefreshWalletLockIcon;
     procedure RefreshWalletLockIcon;
@@ -162,6 +170,10 @@ begin
   paLogoPanel.AddControlDockCenter(TCTRLBanner.Create(Self));
   paLogoPanel.AddControlDockCenter(TCTRLBanner.Create(Self));
   paSyncPanel.AddControlDockCenter(FSyncControl);
   paSyncPanel.AddControlDockCenter(FSyncControl);
   // note: wallet control is lazily constructed
   // note: wallet control is lazily constructed
+  {$IFDEF TESTNET}
+  // Things for testing purposes only
+  InitMenuForTesting;
+  {$ENDIF}
 end;
 end;
 
 
 procedure TFRMMainForm.FormCloseQuery(Sender: TObject; var CanClose: boolean);
 procedure TFRMMainForm.FormCloseQuery(Sender: TObject; var CanClose: boolean);
@@ -367,6 +379,46 @@ begin
   RefreshConnectionStatusDisplay;
   RefreshConnectionStatusDisplay;
 end;
 end;
 
 
+{$IFDEF TESTNET}
+procedure TFRMMainForm.InitMenuForTesting;
+var mi : TMenuItem;
+begin
+  mi := TMenuItem.Create(meMainMenu);
+  mi.Caption:='-';
+  miAboutMenu.Add(mi);
+  {$IFDEF TESTING_NO_POW_CHECK}
+  mi := TMenuItem.Create(meMainMenu);
+  mi.Caption:='Create a block';
+  mi.OnClick:=Test_CreateABlock;
+  miAboutMenu.Add(mi);
+  {$ENDIF}
+  mi := TMenuItem.Create(meMainMenu);
+  mi.Caption:='Show public keys state';
+  mi.OnClick:=Test_ShowPublicKeys;
+  miAboutMenu.Add(mi);
+  mi := TMenuItem.Create(meMainMenu);
+  mi.Caption:='Create Random operations';
+  mi.OnClick:=Test_RandomOperations;
+  miAboutMenu.Add(mi);
+end;
+
+procedure TFRMMainForm.Test_RandomOperations(Sender: TObject);
+begin
+  TUserInterface.ShowRandomOperationsDialog(Self);
+end;
+
+procedure TFRMMainForm.Test_ShowPublicKeys(Sender: TObject);
+begin
+  TUserInterface.ShowPublicKeysDialog(Self);
+end;
+{$IFDEF TESTING_NO_POW_CHECK}
+procedure TFRMMainForm.Test_CreateABlock(Sender: TObject);
+begin
+  TUserInterface.CreateABlock;
+end;
+{$ENDIF}
+{$ENDIF}
+
 {%endregion}
 {%endregion}
 
 
 {%region Handlers: Menu Items }
 {%region Handlers: Menu Items }
@@ -421,7 +473,7 @@ begin
   TUserInterface.ShowAboutBox(Self);
   TUserInterface.ShowAboutBox(Self);
 end;
 end;
 
 
-procedure TFRMMainForm.MiCloseClick(Sender: TObject);
+procedure TFRMMainForm.miCloseClick(Sender: TObject);
 begin
 begin
   Close;
   Close;
 end;
 end;

+ 1 - 2
src/gui-experimental/UFRMMemoText.lfm

@@ -13,8 +13,7 @@ object FRMMemoText: TFRMMemoText
   Font.Name = 'Tahoma'
   Font.Name = 'Tahoma'
   OnCreate = FormCreate
   OnCreate = FormCreate
   Position = poOwnerFormCenter
   Position = poOwnerFormCenter
-  LCLVersion = '1.8.4.0'
-  Visible = False
+  LCLVersion = '1.8.0.6'
   object pnlBottom: TPanel
   object pnlBottom: TPanel
     Left = 0
     Left = 0
     Height = 55
     Height = 55

+ 13 - 21
src/gui-experimental/UFRMMemoText.pas

@@ -1,29 +1,18 @@
 unit UFRMMemoText;
 unit UFRMMemoText;
 
 
-{ Copyright (c) 2016 by PascalCoin Developers
-
-  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
-
-  THIS LICENSE HEADER MUST NOT BE REMOVED.
-}
-
 interface
 interface
 
 
-{$I ..\config.inc}
-
 uses
 uses
+{$IFnDEF FPC}
+  Windows,
+{$ELSE}
   LCLIntf, LCLType, LMessages,
   LCLIntf, LCLType, LMessages,
+{$ENDIF}
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
-  Dialogs, StdCtrls, Buttons, ExtCtrls, UCommon.UI;
+  Dialogs, StdCtrls, Buttons, ExtCtrls;
 
 
 type
 type
-  TFRMMemoText = class(TApplicationForm)
+  TFRMMemoText = class(TForm)
     pnlBottom: TPanel;
     pnlBottom: TPanel;
     Memo: TMemo;
     Memo: TMemo;
     bbCancel: TBitBtn;
     bbCancel: TBitBtn;
@@ -32,20 +21,23 @@ type
     { Private declarations }
     { Private declarations }
   public
   public
     { Public declarations }
     { Public declarations }
-    Procedure InitData(Title : String; text : String);
+    Procedure InitData(const Title : String; const text : String);
   end;
   end;
 
 
 implementation
 implementation
 
 
-{$R *.lfm}
-
+{$IFnDEF FPC}
+  {$R *.dfm}
+{$ELSE}
+  {$R *.lfm}
+{$ENDIF}
 
 
 procedure TFRMMemoText.FormCreate(Sender: TObject);
 procedure TFRMMemoText.FormCreate(Sender: TObject);
 begin
 begin
   Memo.Clear;
   Memo.Clear;
 end;
 end;
 
 
-procedure TFRMMemoText.InitData(Title, text: String);
+procedure TFRMMemoText.InitData(const Title, text: String);
 begin
 begin
   Caption := Title;
   Caption := Title;
   Memo.Lines.Text := text;
   Memo.Lines.Text := text;

+ 88 - 0
src/gui-experimental/UFRMRandomOperations.lfm

@@ -0,0 +1,88 @@
+object FRMRandomOperations: TFRMRandomOperations
+  Left = 876
+  Height = 229
+  Top = 391
+  Width = 439
+  Caption = 'Random Operations'
+  ClientHeight = 229
+  ClientWidth = 439
+  OnClose = FormClose
+  OnCloseQuery = FormCloseQuery
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  Position = poOwnerFormCenter
+  LCLVersion = '1.8.2.0'
+  Visible = False
+  object pnlTop: TPanel
+    Left = 0
+    Height = 66
+    Top = 0
+    Width = 439
+    Align = alTop
+    BevelOuter = bvNone
+    BorderWidth = 10
+    ClientHeight = 66
+    ClientWidth = 439
+    TabOrder = 0
+    object lblTopCaption: TLabel
+      Left = 10
+      Height = 46
+      Top = 10
+      Width = 419
+      Align = alClient
+      AutoSize = False
+      Caption = 'WARNING! This form will generate RANDOM operations using your private keys!!!'
+      Font.Color = clRed
+      Font.Height = -13
+      Font.Style = [fsBold]
+      Layout = tlCenter
+      ParentColor = False
+      ParentFont = False
+      WordWrap = True
+    end
+  end
+  object pnlClient: TPanel
+    Left = 0
+    Height = 163
+    Top = 66
+    Width = 439
+    Align = alClient
+    BevelOuter = bvNone
+    ClientHeight = 163
+    ClientWidth = 439
+    TabOrder = 1
+    object pnlTop1: TPanel
+      Left = 0
+      Height = 42
+      Top = 0
+      Width = 439
+      Align = alTop
+      BevelOuter = bvNone
+      ClientHeight = 42
+      ClientWidth = 439
+      TabOrder = 0
+      object bbRandomOperations: TButton
+        Left = 16
+        Height = 25
+        Top = 8
+        Width = 195
+        Caption = 'Random Operations'
+        OnClick = bbRandomOperationsClick
+        TabOrder = 0
+      end
+    end
+    object mLogs: TMemo
+      Left = 0
+      Height = 121
+      Top = 42
+      Width = 439
+      Align = alClient
+      Lines.Strings = (
+        'mLogs'
+      )
+      ScrollBars = ssBoth
+      TabOrder = 1
+      WordWrap = False
+    end
+  end
+end

+ 500 - 0
src/gui-experimental/UFRMRandomOperations.pas

@@ -0,0 +1,500 @@
+unit UFRMRandomOperations;
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{ Copyright (c) 2016-2018 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 Pascal Coin, a P2P crypto currency without need of
+  historical operations.
+
+  If you like it, consider a donation using BitCoin:
+  16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
+
+  }
+
+interface
+
+{$I ../config.inc}
+
+uses
+  {$IFnDEF FPC}
+    Windows, AppEvnts,
+  {$ELSE}
+    LCLIntf, LCLType, LMessages, FileUtil,
+  {$ENDIF}
+  Classes, SysUtils,  Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, Menus, ActnList, UAccounts, UBlockChain, UNode, UCrypto, UBaseTypes,
+  UWallet, UConst, UTxMultiOperation, UOpTransaction, UThread;
+
+type
+
+  { TRandomGeneratorThread }
+
+  TRandomGeneratorThread = Class(TPCThread)
+  private
+    FLastCall_Error: String;
+    FLastCall_OperationsExecuted: Integer;
+    FLastCall_OperationsFailed: Integer;
+    FLastCall_OperationsTotal: Integer;
+    FOnUpdated: TNotifyEvent;
+    FNeedSanitize : Boolean;
+    FAllowExecute: Boolean;
+    procedure OnBankNewBlock(Sender : TObject);
+  protected
+    FBankNotify : TPCBankNotify;
+    FSourceNode: TNode;
+    FSourceWalletKeys: TWalletKeysExt;
+    FnOperationsCreated : Int64;
+    FnOperationsCreatedFailed : Int64;
+    FnOperationsExecutedOk : Int64;
+    FnCallsToAddNodeTotal : Int64;
+    FnCallsToAddNodeFailed : Int64;
+    procedure BCExecute; override;
+  public
+    Constructor Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt);
+    Destructor Destroy; override;
+    property LastCall_OperationsTotal : Integer read FLastCall_OperationsTotal;
+    property LastCall_OperationsExecuted : Integer read FLastCall_OperationsExecuted;
+    property LastCall_OperationsFailed : Integer read FLastCall_OperationsFailed;
+    property LastCall_Error : String read FLastCall_Error;
+    property OnUpdated : TNotifyEvent read FOnUpdated write FOnUpdated;
+    property AllowExecute : Boolean read FAllowExecute write FAllowExecute;
+  end;
+
+  { TFRMRandomOperations }
+
+  TFRMRandomOperations = class(TForm)
+    bbRandomOperations: TButton;
+    lblTopCaption: TLabel;
+    mLogs: TMemo;
+    pnlClient: TPanel;
+    pnlTop: TPanel;
+    pnlTop1: TPanel;
+    procedure bbRandomOperationsClick(Sender: TObject);
+    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
+    procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+  private
+    FSourceNode: TNode;
+    FSourceWalletKeys: TWalletKeysExt;
+    FBankNotify : TPCBankNotify;
+    FCurrOperationsComp : TPCOperationsComp;
+    FRandomGeneratorThread : TRandomGeneratorThread;
+    procedure SetSourceNode(AValue: TNode);
+    procedure SetSourceWalletKeys(AValue: TWalletKeysExt);
+    procedure NewLog(logTxt : String);
+    procedure OnBankNewBlock(Sender : TObject);
+    procedure UpdateRandomGeneratorThread(DestroyOnly : Boolean);
+    procedure OnRandomGeneratoThreadUpdated(Sender : TObject);
+    function IsProcessingRandomOperations : Boolean;
+  public
+    Property SourceNode : TNode read FSourceNode write SetSourceNode;
+    Property SourceWalletKeys : TWalletKeysExt read FSourceWalletKeys write SetSourceWalletKeys;
+  end;
+
+  { TRandomGenerateOperation }
+
+  TRandomGenerateOperation = Class
+  private
+  public
+    class function GetRandomSigner(const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt; out iKey : Integer; out nAccount : Cardinal) : Boolean;
+    class function GenerateOpTransaction(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
+    class function GenerateOpMultiOperation(current_protocol : Word; const operationsComp : TPCOperationsComp; const aWalletKeys : TWalletKeysExt) : Boolean;
+  end;
+
+implementation
+
+{$IFnDEF FPC}
+  {$R *.dfm}
+{$ELSE}
+  {$R *.lfm}
+{$ENDIF}
+
+Uses ULog;
+
+{ TRandomGeneratorThread }
+
+procedure TRandomGeneratorThread.OnBankNewBlock(Sender: TObject);
+begin
+  FNeedSanitize := True;
+end;
+
+procedure TRandomGeneratorThread.BCExecute;
+Var nCounter, nTotalRound, iLastSend, i : Integer;
+  operationsComp : TPCOperationsComp;
+  ohtToAdd : TOperationsHashTree;
+  errors : AnsiString;
+  nAddedOperations : Integer;
+begin
+  operationsComp := TPCOperationsComp.Create(Nil);
+  try
+    operationsComp.bank := FSourceNode.Bank;
+    iLastSend := -1;
+    while (Not Terminated) do begin
+      nTotalRound := Random(100);
+      nCounter := 0;
+      if FNeedSanitize then begin
+        FNeedSanitize := False;
+        operationsComp.SanitizeOperations;
+        iLastSend := operationsComp.Count-1;
+        TLog.NewLog(ltdebug,ClassName,Format('Sanitized. Current pending operations %d',[operationsComp.Count]));
+      end;
+      while (nCounter<nTotalRound) And (Not Terminated) And (FAllowExecute) do begin
+        inc(nCounter);
+        //
+        Case Random(30) of
+          0..20 : begin
+            If TRandomGenerateOperation.GenerateOpTransaction(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(FnOperationsCreated)
+            else inc(FnOperationsCreatedFailed);
+          end;
+          21..25 : begin
+            If TRandomGenerateOperation.GenerateOpMultiOperation(FSourceNode.Bank.SafeBox.CurrentProtocol,operationsComp,FSourceWalletKeys) then inc(FnOperationsCreated)
+            else inc(FnOperationsCreatedFailed);
+          end;
+        end;
+      end;
+      if (Not Terminated) And (Not FNeedSanitize) And (FAllowExecute) then begin
+        //
+        ohtToAdd := TOperationsHashTree.Create;
+        Try
+          for i := iLastSend+1 to operationsComp.OperationsHashTree.OperationsCount-1 do begin
+            ohtToAdd.AddOperationToHashTree(operationsComp.Operation[i]);
+          end;
+          errors := '';
+          nAddedOperations := FSourceNode.AddOperations(Nil,ohtToAdd,nil,errors);
+          iLastSend := operationsComp.OperationsHashTree.OperationsCount-1;
+          // Notify info
+          inc(FnCallsToAddNodeTotal);
+          inc(FnOperationsExecutedOk,nAddedOperations);
+          FLastCall_OperationsTotal:=ohtToAdd.OperationsCount;
+          FLastCall_OperationsExecuted:=nAddedOperations;
+          FLastCall_OperationsFailed:=ohtToAdd.OperationsCount - nAddedOperations;
+          FLastCall_Error:=errors;
+          if (ohtToAdd.OperationsCount <> nAddedOperations) then begin
+            inc(FnOperationsCreatedFailed,(ohtToAdd.OperationsCount - nAddedOperations));
+            inc(FnCallsToAddNodeFailed,1);
+          end;
+        Finally
+          ohtToAdd.Free;
+        End;
+        //
+        if Assigned(FOnUpdated) then FOnUpdated(Self);
+      end;
+      Sleep(1);
+    end;
+  finally
+    operationsComp.Free;
+  end;
+end;
+
+constructor TRandomGeneratorThread.Create(ASourceNode: TNode; ASourceWalletKeys: TWalletKeysExt);
+begin
+  FSourceNode := ASourceNode;
+  FSourceWalletKeys := ASourceWalletKeys;
+  FnOperationsCreated := 0;
+  FnOperationsCreatedFailed := 0;
+  FnOperationsExecutedOk := 0;
+  FnCallsToAddNodeTotal := 0;
+  FnCallsToAddNodeFailed := 0;
+  FLastCall_Error:='';
+  FLastCall_OperationsFailed:=0;
+  FLastCall_OperationsExecuted:=0;
+  FLastCall_OperationsTotal:=0;
+  //FOperationsComp := TPCOperationsComp.Create(Nil);
+  //FOperationsComp.bank := FSourceNode.Bank;
+  FBankNotify := TPCBankNotify.Create(Nil);
+  FBankNotify.Bank := FSourceNode.Bank;
+  FBankNotify.OnNewBlock:=OnBankNewBlock;
+  FNeedSanitize := True;
+  FAllowExecute := False;
+  inherited Create(False);
+end;
+
+destructor TRandomGeneratorThread.Destroy;
+begin
+  FBankNotify.Bank := Nil;
+  FBankNotify.OnNewBlock := Nil;
+  FBankNotify.Free;
+  inherited Destroy;
+end;
+
+{ TRandomGenerateOperation }
+
+class function TRandomGenerateOperation.GetRandomSigner(const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt; out iKey: Integer; out nAccount: Cardinal): Boolean;
+var availAccounts : TOrderedCardinalList;
+  acc : TAccount;
+  i, nRounds : Integer;
+begin
+  Result := False; iKey := -1; nAccount:=0; nRounds := 0;
+  if (aWalletKeys.AccountsKeyList.Count<=0) then Exit;
+  Repeat
+    iKey := Random( aWalletKeys.AccountsKeyList.Count );
+    i := aWalletKeys.AccountsKeyList.Count;
+    if i<0 then Exit;
+    availAccounts := aWalletKeys.AccountsKeyList.AccountKeyList[iKey];
+    if availAccounts.Count<=0 then Exit; // No valid accounts
+    i := availAccounts.Count;
+    if (i<0) then Exit;
+    // Sender:
+    nAccount := availAccounts.Get( Random(availAccounts.Count) );
+    acc.balance := 0;
+    If Not TAccountComp.IsAccountBlockedByProtocol(nAccount,operationsComp.SafeBoxTransaction.FreezedSafeBox.BlocksCount) then begin
+      if (operationsComp.OperationsHashTree.CountOperationsBySameSignerWithoutFee(nAccount)<=0) then acc := operationsComp.SafeBoxTransaction.Account(nAccount);
+    end;
+    inc(nRounds);
+    if (nRounds>1000) then Exit;
+  until (acc.balance>0);
+  Result := True;
+end;
+
+class function TRandomGenerateOperation.GenerateOpTransaction(current_protocol : Word; const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
+var nAccount : Cardinal;
+  iKey : Integer;
+  opTx : TOpTransaction;
+  senderAcc,destAcc : TAccount;
+  amount,fees : Int64;
+  errors : AnsiString;
+begin
+  Result := False;
+  If Not GetRandomSigner(operationsComp,aWalletKeys,iKey,nAccount) then Exit;
+  senderAcc := operationsComp.SafeBoxTransaction.Account(nAccount);
+  amount := Random(Integer(senderAcc.balance));
+  if amount<=0 then Exit;
+  If (senderAcc.balance - amount)>0 then begin
+    fees := Random( senderAcc.balance - amount )
+  end else fees := 0;
+
+  iKey := aWalletKeys.IndexOfAccountKey( aWalletKeys.AccountsKeyList.AccountKey[iKey] );
+  if iKey<0 then Exit;
+  if Not aWalletKeys.Key[iKey].HasPrivateKey then Exit;
+  if Not Assigned(aWalletKeys.Key[iKey].PrivateKey) then Exit;
+  // Dest
+  Repeat
+    destAcc := operationsComp.SafeBoxTransaction.Account( Random(operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount) );
+  until (destAcc.account <> senderAcc.account);
+
+  // Search account
+  opTx := TOpTransaction.CreateTransaction(current_protocol,senderAcc.account,senderAcc.n_operation+1,destAcc.account,aWalletKeys.Key[iKey].PrivateKey,amount,fees,'');
+  Try
+    Result := operationsComp.AddOperation(True,opTx,errors);
+  finally
+    opTx.Free;
+  end;
+end;
+
+class function TRandomGenerateOperation.GenerateOpMultiOperation(current_protocol : Word; const operationsComp: TPCOperationsComp; const aWalletKeys: TWalletKeysExt): Boolean;
+   procedure DoSign(opMulti : TOpMultiOperation);
+   var n : Integer;
+     i,j : Integer;
+   begin
+     n := 0;
+     For i:=0 to High(opMulti.Data.txSenders) do begin
+       j := aWalletKeys.IndexOfAccountKey(operationsComp.bank.SafeBox.Account(opMulti.Data.txSenders[i].Account).accountInfo.accountKey);
+       If (j>=0) then begin
+         // Can sign
+         If (Assigned(aWalletKeys.Key[j].PrivateKey)) then begin
+           inc(n, opMulti.DoSignMultiOperationSigner(current_protocol,opMulti.Data.txSenders[i].Account,aWalletKeys.Key[j].PrivateKey));
+         end;
+       end;
+     end;
+     For i:=0 to High(opMulti.Data.changesInfo) do begin
+       j := aWalletKeys.IndexOfAccountKey(operationsComp.bank.SafeBox.Account(opMulti.Data.changesInfo[i].Account).accountInfo.accountKey);
+       If (j>=0) then begin
+         // Can sign
+         If (Assigned(aWalletKeys.Key[j].PrivateKey)) then begin
+           inc(n, opMulti.DoSignMultiOperationSigner(current_protocol,opMulti.Data.changesInfo[i].Account,aWalletKeys.Key[j].PrivateKey));
+         end;
+       end;
+     end;
+   end;
+
+Var opMulti : TOpMultiOperation;
+  iKey : Integer;
+  nAccount : Cardinal;
+  sender : TMultiOpSender;
+  receiver : TMultiOpReceiver;
+  changer: TMultiOpChangeInfo;
+  acc : TAccount;
+  errors : AnsiString;
+begin
+  Result := False;
+  opMulti := TOpMultiOperation.Create;
+  Try
+    if (Random(100)<5) then begin
+      Repeat
+        If GetRandomSigner(operationsComp,aWalletKeys,iKey,nAccount) then begin
+          acc := operationsComp.SafeBoxTransaction.Account(nAccount);
+          if Not OpMulti.IsSignerAccount(nAccount) then begin
+            sender := CT_TMultiOpSender_NUL;
+            sender.Account:=nAccount;
+            sender.Amount:= Random( Integer(acc.balance) );
+            sender.N_Operation:=acc.n_operation+1;
+            opMulti.AddTxSender(sender);
+          end;
+        end;
+      until (Random(100)=0) Or ((Length(opMulti.Data.txSenders)>Random(1000)) And (opMulti.OperationFee>Random(1000000)));
+      Repeat
+        nAccount := Random( operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount );
+        If Not TAccountComp.IsAccountBlockedByProtocol(nAccount,operationsComp.SafeBoxTransaction.FreezedSafeBox.BlocksCount) then begin
+          receiver := CT_TMultiOpReceiver_NUL;
+          receiver.Account:=nAccount;
+          receiver.Amount:=Random( Integer(opMulti.OperationFee) );
+          opMulti.AddTxReceiver(receiver);
+        end;
+      until (Random(100)=0) Or (opMulti.OperationFee<=0);
+    end;
+    if (Random(100)<5) then begin
+      Repeat
+        changer := CT_TMultiOpChangeInfo_NUL;
+        If GetRandomSigner(operationsComp,aWalletKeys,iKey,nAccount) then begin
+          acc := operationsComp.SafeBoxTransaction.Account(nAccount);
+          if Not OpMulti.IsSignerAccount(nAccount) then begin
+            changer.Account:=nAccount;
+            changer.N_Operation:=acc.n_operation+1;
+            case Random(3) of // public_key,account_name,account_type
+              0 : begin
+                changer.Changes_type:=[public_key];
+                acc := operationsComp.SafeBoxTransaction.Account(Random(operationsComp.SafeBoxTransaction.FreezedSafeBox.AccountsCount));
+                changer.New_Accountkey := acc.accountInfo.accountKey;
+              end;
+              1 : begin
+                changer.Changes_type:=[account_name];
+                changer.New_Name:='random'+IntToStr(Random(100)); // <- This will generate collisions
+              end;
+            else
+              changer.Changes_type:=[account_type];
+              changer.New_Type:=Random(65535);
+            end;
+            opMulti.AddChangeInfo(changer);
+          end;
+        end;
+      until (Random(100)=0) Or (Length(opMulti.Data.changesInfo)>Random(1000));
+    end;
+    // If nothing added... invalid op.
+    if (Length(opMulti.Data.changesInfo)=0) And (Length(opMulti.Data.txSenders)=0) And (Length(opMulti.Data.txReceivers)=0) then Exit;
+    //
+    DoSign(opMulti);
+    if Not opMulti.CheckSignatures(operationsComp.SafeBoxTransaction,errors) then begin
+      errors := opMulti.toString + errors;
+      Exit;
+    end;
+    Result := operationsComp.AddOperation(True,opMulti,errors);
+  finally
+    opMulti.Free;
+  end;
+end;
+
+{ TFRMRandomOperations }
+
+procedure TFRMRandomOperations.FormCreate(Sender: TObject);
+begin
+  FSourceNode := Nil;
+  FSourceWalletKeys := Nil;
+  FBankNotify := TPCBankNotify.Create(Nil);
+  FBankNotify.OnNewBlock:=OnBankNewBlock;
+  FCurrOperationsComp := TPCOperationsComp.Create(Nil);
+  FRandomGeneratorThread := Nil;
+  mLogs.Clear;
+end;
+
+procedure TFRMRandomOperations.FormDestroy(Sender: TObject);
+begin
+  UpdateRandomGeneratorThread(True);
+  FreeAndNil(FBankNotify);
+  FreeAndNil(FCurrOperationsComp);
+end;
+
+procedure TFRMRandomOperations.bbRandomOperationsClick(Sender: TObject);
+begin
+  {$IFDEF TESTNET}
+  If IsProcessingRandomOperations then begin
+    FRandomGeneratorThread.AllowExecute := False;
+    bbRandomOperations.Caption:='GENERATE RANDOM';
+  end else begin
+    if Assigned(FRandomGeneratorThread) then begin
+      FRandomGeneratorThread.AllowExecute := True;
+      bbRandomOperations.Caption:='STOP RANDOM';
+    end else bbRandomOperations.Caption:='???';
+  end;
+  {$ELSE}
+  Raise Exception.Create('Random operations not valid in PRODUCTION MODE');
+  {$ENDIF}
+end;
+
+procedure TFRMRandomOperations.FormClose(Sender: TObject; var CloseAction: TCloseAction);
+begin
+  CloseAction := caFree;
+end;
+
+procedure TFRMRandomOperations.FormCloseQuery(Sender: TObject; var CanClose: boolean);
+begin
+  CanClose := True;
+end;
+
+procedure TFRMRandomOperations.SetSourceNode(AValue: TNode);
+begin
+  if FSourceNode=AValue then Exit;
+  FBankNotify.Bank := Nil;
+  FSourceNode:=AValue;
+
+  If Assigned(AValue) then begin
+    FBankNotify.Bank := AValue.Bank;
+    FCurrOperationsComp.bank := AValue.Bank;
+  end;
+  UpdateRandomGeneratorThread(False);
+end;
+
+procedure TFRMRandomOperations.SetSourceWalletKeys(AValue: TWalletKeysExt);
+begin
+  if FSourceWalletKeys=AValue then Exit;
+  FSourceWalletKeys:=AValue;
+  UpdateRandomGeneratorThread(False);
+end;
+
+procedure TFRMRandomOperations.NewLog(logTxt: String);
+begin
+  if length(logTxt)>300 then logTxt := Copy(logTxt,1,300)+'...';
+  
+  mLogs.Lines.Add(Format('%s %s',[FormatDateTime('hh:nn:ss.zzz',Now),logTxt]));
+end;
+
+procedure TFRMRandomOperations.OnBankNewBlock(Sender: TObject);
+begin
+  NewLog(Format('Updating to new block %d',[FBankNotify.Bank.BlocksCount]));
+  FCurrOperationsComp.SanitizeOperations;
+end;
+
+procedure TFRMRandomOperations.UpdateRandomGeneratorThread(DestroyOnly : Boolean);
+begin
+  if Assigned(FRandomGeneratorThread) then begin
+    FRandomGeneratorThread.AllowExecute:=False;
+    FRandomGeneratorThread.Terminate;
+    FRandomGeneratorThread.WaitFor;
+    FreeAndNil(FRandomGeneratorThread);
+  end;
+  if (Not DestroyOnly) And Assigned(FSourceNode) And Assigned(FSourceWalletKeys) then begin
+    FRandomGeneratorThread := TRandomGeneratorThread.Create(FSourceNode,FSourceWalletKeys);
+    FRandomGeneratorThread.OnUpdated:=OnRandomGeneratoThreadUpdated;
+  end;
+end;
+
+procedure TFRMRandomOperations.OnRandomGeneratoThreadUpdated(Sender: TObject);
+Var RGT : TRandomGeneratorThread;
+begin
+  RGT := TRandomGeneratorThread(Sender);
+  NewLog(Format('Generated %d Operations-> Ok:%d Failed:%d Errors:%s',[RGT.LastCall_OperationsTotal,RGT.LastCall_OperationsExecuted,RGT.LastCall_OperationsFailed,RGT.LastCall_Error]));
+end;
+
+function TFRMRandomOperations.IsProcessingRandomOperations: Boolean;
+begin
+  Result := Assigned(FRandomGeneratorThread) And (FRandomGeneratorThread.AllowExecute);
+end;
+
+end.
+

+ 113 - 2
src/gui-experimental/UUserInterface.pas

@@ -29,7 +29,7 @@ uses
   UBlockChain, UAccounts, UNode, UWallet, UConst, UFolderHelper, UGridUtils, URPC, UPoolMining,
   UBlockChain, UAccounts, UNode, UWallet, UConst, UFolderHelper, UGridUtils, URPC, UPoolMining,
   ULog, UThread, UNetProtocol, UCrypto, UBaseTypes,
   ULog, UThread, UNetProtocol, UCrypto, UBaseTypes,
   UFRMMainForm, UCTRLSyncronization, UFRMAccountExplorer, UFRMOperationExplorer, UFRMPendingOperations, UFRMOperation,
   UFRMMainForm, UCTRLSyncronization, UFRMAccountExplorer, UFRMOperationExplorer, UFRMPendingOperations, UFRMOperation,
-  UFRMLogs, UFRMMessages, UFRMNodes, UFRMBlockExplorer, UFRMWalletKeys;
+  UFRMLogs, UFRMMessages, UFRMNodes, UFRMBlockExplorer, UFRMWalletKeys {$IFDEF TESTNET},UFRMRandomOperations, UAccountKeyStorage{$ENDIF};
 
 
 type
 type
   { Forward Declarations }
   { Forward Declarations }
@@ -151,6 +151,11 @@ type
       class procedure RunInBackground;
       class procedure RunInBackground;
       class procedure RunInForeground;
       class procedure RunInForeground;
       class procedure CheckNodeIsReady;
       class procedure CheckNodeIsReady;
+      {$IFDEF TESTNET}
+      {$IFDEF TESTING_NO_POW_CHECK}
+      class procedure CreateABlock;
+      {$ENDIF}
+      {$ENDIF}
 
 
       // Show Dialogs
       // Show Dialogs
       class procedure ShowSendDialog(const AAccounts : array of Cardinal);
       class procedure ShowSendDialog(const AAccounts : array of Cardinal);
@@ -171,6 +176,10 @@ type
       class procedure ShowMemoText(parentForm: TForm; const ATitle : AnsiString; text : TStrings); overload;
       class procedure ShowMemoText(parentForm: TForm; const ATitle : AnsiString; text : TStrings); overload;
       class procedure UnlockWallet(parentForm: TForm);
       class procedure UnlockWallet(parentForm: TForm);
       class procedure ChangeWalletPassword(parentForm: TForm);
       class procedure ChangeWalletPassword(parentForm: TForm);
+      {$IFDEF TESTNET}
+      class procedure ShowPublicKeysDialog(parentForm: TForm);
+      class procedure ShowRandomOperationsDialog(parentForm: TForm);
+      {$ENDIF}
       class procedure ShowInfo(parentForm : TForm; const ACaption, APrompt : String);
       class procedure ShowInfo(parentForm : TForm; const ACaption, APrompt : String);
       class procedure ShowWarning(parentForm : TForm; const ACaption, APrompt : String);
       class procedure ShowWarning(parentForm : TForm; const ACaption, APrompt : String);
       class procedure ShowError(parentForm : TForm; const ACaption, APrompt : String);
       class procedure ShowError(parentForm : TForm; const ACaption, APrompt : String);
@@ -486,7 +495,7 @@ begin
   ShowWallet;
   ShowWallet;
 end;
 end;
 
 
-class procedure TUserInterface.OnUITimerRefresh(Sender: Tobject);
+class procedure TUserInterface.OnUITimerRefresh(Sender: TObject);
 var
 var
   LActive, LDiscoveringPeers, LGettingNewBlockchain, LRemoteHasBiggerBlock, LNoConnections : boolean;
   LActive, LDiscoveringPeers, LGettingNewBlockchain, LRemoteHasBiggerBlock, LNoConnections : boolean;
   LState : TUserInterfaceState;
   LState : TUserInterfaceState;
@@ -776,6 +785,108 @@ begin
   'If you lose your password your accounts and funds will be lost forever.');
   'If you lose your password your accounts and funds will be lost forever.');
 end;
 end;
 
 
+{$IFDEF TESTNET}
+class procedure TUserInterface.ShowRandomOperationsDialog(parentForm: TForm);
+begin
+  with TFRMRandomOperations.Create(parentForm) do begin
+    try
+      SourceNode := TUserInterface.Node;
+      SourceWalletKeys := TWallet.Keys;
+      ShowModal;
+    finally
+      Free;
+    end;
+  end;
+end;
+
+class procedure TUserInterface.ShowPublicKeysDialog(parentForm: TForm);
+var
+  sl : TStrings;
+  ak : TAccountKey;
+  i, nmin,nmax : Integer;
+  l : TList;
+  Pacsd : PAccountKeyStorageData;
+  acc : TAccount;
+begin
+   sl := TStringList.Create;
+  try
+    for i:=0 to FNode.Bank.SafeBox.AccountsCount-1 do begin
+      acc := FNode.Bank.SafeBox.Account(i);
+      if acc.accountInfo.new_publicKey.EC_OpenSSL_NID<>0 then begin
+        sl.Add(Format('Account %d new public key %d %s',[acc.account,
+          acc.accountInfo.new_publicKey.EC_OpenSSL_NID,
+          TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(acc.accountInfo.new_publicKey))]));
+      end;
+    end;
+    l := TAccountKeyStorage.KS.LockList;
+    try
+      sl.Add(Format('%d public keys in TAccountKeyStorage data',[l.count]));
+      for i:=0 to l.count-1 do begin
+        Pacsd := l[i];
+        if (Pacsd^.counter<=0) then begin
+          sl.Add(Format('%d/%d public keys counter %d',[i+1,l.count,Pacsd^.counter]));
+        end;
+        if FNode.Bank.SafeBox.OrderedAccountKeysList.IndexOfAccountKey(Pacsd^.ptrAccountKey^)<0 then begin
+          sl.Add(Format('%d/%d public keys counter %d Type %d NOT FOUND %s',[i+1,l.count,Pacsd^.counter,
+          Pacsd^.ptrAccountKey^.EC_OpenSSL_NID,
+          TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(Pacsd^.ptrAccountKey^))]));
+        end;
+      end;
+    finally
+      TAccountKeyStorage.KS.UnlockList;
+    end;
+    sl.Add(Format('%d public keys in %d accounts',[FNode.Bank.SafeBox.OrderedAccountKeysList.Count,FNode.Bank.Safebox.AccountsCount]));
+    for i:=0 to FNode.Bank.SafeBox.OrderedAccountKeysList.Count-1 do begin
+      ak := FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKey[i];
+      if ( FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Count > 0) then begin
+        nmin := FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Get(0);
+        nmax := FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Get( FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Count-1 );
+      end else begin
+        nmin := -1; nmax := -1;
+      end;
+      sl.Add(Format('%d/%d %d accounts (%d to %d) for key type %d %s',[
+        i+1,FNode.Bank.SafeBox.OrderedAccountKeysList.Count,
+        FNode.Bank.SafeBox.OrderedAccountKeysList.AccountKeyList[i].Count,
+        nmin,nmax,
+        ak.EC_OpenSSL_NID,
+        TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(ak)) ]));
+    end;
+    with TFRMMemoText.Create(parentForm) do begin
+    try
+      InitData('Keys in safebox',sl.Text);
+      ShowModal;
+    finally
+      Free;
+    end;
+
+    end;
+  finally
+    sl.Free;
+  end;
+end;
+
+{$IFDEF TESTING_NO_POW_CHECK}
+class procedure TUserInterface.CreateABlock;
+var
+  ops : TPCOperationsComp;
+  nba : TBlockAccount;
+  errors : AnsiString;
+begin
+  ops := TPCOperationsComp.Create(Nil);
+  Try
+    ops.bank := FNode.Bank;
+    ops.CopyFrom(FNode.Operations);
+    ops.BlockPayload:= IntToStr(FNode.Bank.BlocksCount);
+    ops.nonce := FNode.Bank.BlocksCount;
+    ops.UpdateTimestamp;
+    FNode.AddNewBlockChain(Nil,ops,nba,errors);
+  finally
+    ops.Free;
+  end;
+end;
+{$ENDIF}
+{$ENDIF}
+
 class procedure TUserInterface.UnlockWallet(parentForm: TForm);
 class procedure TUserInterface.UnlockWallet(parentForm: TForm);
 Var pwd : String;
 Var pwd : String;
 begin
 begin

+ 12 - 1
src/pascalcoin_wallet_experimental.lpi

@@ -82,7 +82,7 @@
         <PackageName Value="LCL"/>
         <PackageName Value="LCL"/>
       </Item1>
       </Item1>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="95">
+    <Units Count="97">
       <Unit0>
       <Unit0>
         <Filename Value="pascalcoin_wallet_experimental.dpr"/>
         <Filename Value="pascalcoin_wallet_experimental.dpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
@@ -599,6 +599,17 @@
         <Filename Value="core\URandomHash.pas"/>
         <Filename Value="core\URandomHash.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit94>
       </Unit94>
+      <Unit95>
+        <Filename Value="gui-experimental\UFRMRandomOperations.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMRandomOperations"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit95>
+      <Unit96>
+        <Filename Value="gui-experimental\UFRMMemoText.lfm"/>
+        <IsPartOfProject Value="True"/>
+      </Unit96>
     </Units>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>