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

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

@@ -113,6 +113,14 @@ type
     procedure OnNetStatisticsChanged(Sender: TObject);
     procedure OnWalletChanged(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
     procedure RefreshConnectionStatusDisplay;
     procedure RefreshWalletLockIcon;
@@ -162,6 +170,10 @@ begin
   paLogoPanel.AddControlDockCenter(TCTRLBanner.Create(Self));
   paSyncPanel.AddControlDockCenter(FSyncControl);
   // note: wallet control is lazily constructed
+  {$IFDEF TESTNET}
+  // Things for testing purposes only
+  InitMenuForTesting;
+  {$ENDIF}
 end;
 
 procedure TFRMMainForm.FormCloseQuery(Sender: TObject; var CanClose: boolean);
@@ -367,6 +379,46 @@ begin
   RefreshConnectionStatusDisplay;
 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}
 
 {%region Handlers: Menu Items }
@@ -421,7 +473,7 @@ begin
   TUserInterface.ShowAboutBox(Self);
 end;
 
-procedure TFRMMainForm.MiCloseClick(Sender: TObject);
+procedure TFRMMainForm.miCloseClick(Sender: TObject);
 begin
   Close;
 end;

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

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

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

@@ -1,29 +1,18 @@
 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
 
-{$I ..\config.inc}
-
 uses
+{$IFnDEF FPC}
+  Windows,
+{$ELSE}
   LCLIntf, LCLType, LMessages,
+{$ENDIF}
   Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
-  Dialogs, StdCtrls, Buttons, ExtCtrls, UCommon.UI;
+  Dialogs, StdCtrls, Buttons, ExtCtrls;
 
 type
-  TFRMMemoText = class(TApplicationForm)
+  TFRMMemoText = class(TForm)
     pnlBottom: TPanel;
     Memo: TMemo;
     bbCancel: TBitBtn;
@@ -32,20 +21,23 @@ type
     { Private declarations }
   public
     { Public declarations }
-    Procedure InitData(Title : String; text : String);
+    Procedure InitData(const Title : String; const text : String);
   end;
 
 implementation
 
-{$R *.lfm}
-
+{$IFnDEF FPC}
+  {$R *.dfm}
+{$ELSE}
+  {$R *.lfm}
+{$ENDIF}
 
 procedure TFRMMemoText.FormCreate(Sender: TObject);
 begin
   Memo.Clear;
 end;
 
-procedure TFRMMemoText.InitData(Title, text: String);
+procedure TFRMMemoText.InitData(const Title, text: String);
 begin
   Caption := Title;
   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,
   ULog, UThread, UNetProtocol, UCrypto, UBaseTypes,
   UFRMMainForm, UCTRLSyncronization, UFRMAccountExplorer, UFRMOperationExplorer, UFRMPendingOperations, UFRMOperation,
-  UFRMLogs, UFRMMessages, UFRMNodes, UFRMBlockExplorer, UFRMWalletKeys;
+  UFRMLogs, UFRMMessages, UFRMNodes, UFRMBlockExplorer, UFRMWalletKeys {$IFDEF TESTNET},UFRMRandomOperations, UAccountKeyStorage{$ENDIF};
 
 type
   { Forward Declarations }
@@ -151,6 +151,11 @@ type
       class procedure RunInBackground;
       class procedure RunInForeground;
       class procedure CheckNodeIsReady;
+      {$IFDEF TESTNET}
+      {$IFDEF TESTING_NO_POW_CHECK}
+      class procedure CreateABlock;
+      {$ENDIF}
+      {$ENDIF}
 
       // Show Dialogs
       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 UnlockWallet(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 ShowWarning(parentForm : TForm; const ACaption, APrompt : String);
       class procedure ShowError(parentForm : TForm; const ACaption, APrompt : String);
@@ -486,7 +495,7 @@ begin
   ShowWallet;
 end;
 
-class procedure TUserInterface.OnUITimerRefresh(Sender: Tobject);
+class procedure TUserInterface.OnUITimerRefresh(Sender: TObject);
 var
   LActive, LDiscoveringPeers, LGettingNewBlockchain, LRemoteHasBiggerBlock, LNoConnections : boolean;
   LState : TUserInterfaceState;
@@ -776,6 +785,108 @@ begin
   'If you lose your password your accounts and funds will be lost forever.');
 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);
 Var pwd : String;
 begin

+ 12 - 1
src/pascalcoin_wallet_experimental.lpi

@@ -82,7 +82,7 @@
         <PackageName Value="LCL"/>
       </Item1>
     </RequiredPackages>
-    <Units Count="95">
+    <Units Count="97">
       <Unit0>
         <Filename Value="pascalcoin_wallet_experimental.dpr"/>
         <IsPartOfProject Value="True"/>
@@ -599,6 +599,17 @@
         <Filename Value="core\URandomHash.pas"/>
         <IsPartOfProject Value="True"/>
       </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>
   </ProjectOptions>
   <CompilerOptions>