Browse Source

WatchKeys

Modified TOrderedAccountKeysList to be Thread Safe and added a keys
change detector on every commit at the Safebox
Also added TNodeNotifyEvents.WatchKeys and OnKeyActivity that will be
fired on main thread when a key changed in the Safebox
PascalCoin 7 years ago
parent
commit
1993dbe389
3 changed files with 234 additions and 72 deletions
  1. 207 71
      src/core/UAccounts.pas
  2. 26 0
      src/core/UNode.pas
  3. 1 1
      src/core/UWallet.pas

+ 207 - 71
src/core/UAccounts.pas

@@ -193,12 +193,17 @@ Type
   TAccountKeyArray = array of TAccountKey;
   TAccountKeyArray = array of TAccountKey;
 
 
   // This is a class to quickly find accountkeys and their respective account number/s
   // This is a class to quickly find accountkeys and their respective account number/s
+
+  { TOrderedAccountKeysList }
+
   TOrderedAccountKeysList = Class
   TOrderedAccountKeysList = Class
   Private
   Private
     FAutoAddAll : Boolean;
     FAutoAddAll : Boolean;
     FAccountList : TPCSafeBox;
     FAccountList : TPCSafeBox;
-    FOrderedAccountKeysList : TList; // An ordered list of pointers to quickly find account keys in account list
-    Function Find(Const AccountKey: TAccountKey; var Index: Integer): Boolean;
+    FOrderedAccountKeysList : TPCThreadList; // An ordered list of pointers to quickly find account keys in account list
+    FTotalChanges : Integer;
+    Function Find(lockedList : TList; Const AccountKey: TAccountKey; var Index: Integer): Boolean;
+    function GetAccountKeyChanges(index : Integer): Integer;
     function GetAccountKeyList(index: Integer): TOrderedCardinalList;
     function GetAccountKeyList(index: Integer): TOrderedCardinalList;
     function GetAccountKey(index: Integer): TAccountKey;
     function GetAccountKey(index: Integer): TAccountKey;
   protected
   protected
@@ -213,10 +218,15 @@ Type
     Function IndexOfAccountKey(Const AccountKey : TAccountKey) : Integer;
     Function IndexOfAccountKey(Const AccountKey : TAccountKey) : Integer;
     Property AccountKeyList[index : Integer] : TOrderedCardinalList read GetAccountKeyList;
     Property AccountKeyList[index : Integer] : TOrderedCardinalList read GetAccountKeyList;
     Property AccountKey[index : Integer] : TAccountKey read GetAccountKey;
     Property AccountKey[index : Integer] : TAccountKey read GetAccountKey;
+    Property AccountKeyChanges[index : Integer] : Integer read GetAccountKeyChanges;
+    procedure ClearAccountKeyChanges;
     Function Count : Integer;
     Function Count : Integer;
     Property SafeBox : TPCSafeBox read FAccountList;
     Property SafeBox : TPCSafeBox read FAccountList;
     Procedure Clear;
     Procedure Clear;
     function ToArray : TAccountKeyArray;
     function ToArray : TAccountKeyArray;
+    function Lock : TList;
+    procedure Unlock;
+    function HasAccountKeyChanged : Boolean;
   End;
   End;
 
 
   // Maintans a Cardinal ordered (without duplicates) list with TRawData each
   // Maintans a Cardinal ordered (without duplicates) list with TRawData each
@@ -3313,8 +3323,7 @@ begin
           Result:=TPascalCoinProtocol.TargetFromCompact(lastBlock.compact_target);
           Result:=TPascalCoinProtocol.TargetFromCompact(lastBlock.compact_target);
         end else begin
         end else begin
           // New on V3 protocol:
           // New on V3 protocol:
-          // Harmonization of the sinusoidal effect modifying the rise / fall by 50% calculating over the "stop" area
-//XXXXXXXXXX          Result := TPascalCoinProtocol.GetNewTarget(tsTeoricalStop,(tsTeoricalStop + tsRealStop) DIV 2,protocolVersion,True,TPascalCoinProtocol.TargetFromCompact(lastBlock.compact_target));
+          // Harmonization of the sinusoidal effect modifying the rise / fall over the "stop" area
           Result := TPascalCoinProtocol.GetNewTarget(tsTeoricalStop,tsRealStop,protocolVersion,True,TPascalCoinProtocol.TargetFromCompact(lastBlock.compact_target));
           Result := TPascalCoinProtocol.GetNewTarget(tsTeoricalStop,tsRealStop,protocolVersion,True,TPascalCoinProtocol.TargetFromCompact(lastBlock.compact_target));
         end;
         end;
       end;
       end;
@@ -4340,8 +4349,11 @@ Type
   TOrderedAccountKeyList = Record
   TOrderedAccountKeyList = Record
     rawaccountkey : TRawBytes;
     rawaccountkey : TRawBytes;
     accounts_number : TOrderedCardinalList;
     accounts_number : TOrderedCardinalList;
+    changes_counter : Integer;
   end;
   end;
   POrderedAccountKeyList = ^TOrderedAccountKeyList;
   POrderedAccountKeyList = ^TOrderedAccountKeyList;
+Const
+  CT_TOrderedAccountKeyList_NUL : TOrderedAccountKeyList = (rawaccountkey:'';accounts_number:Nil;changes_counter:0);
 
 
 function SortOrdered(Item1, Item2: Pointer): Integer;
 function SortOrdered(Item1, Item2: Pointer): Integer;
 begin
 begin
@@ -4351,78 +4363,136 @@ end;
 procedure TOrderedAccountKeysList.AddAccountKey(const AccountKey: TAccountKey);
 procedure TOrderedAccountKeysList.AddAccountKey(const AccountKey: TAccountKey);
 Var P : POrderedAccountKeyList;
 Var P : POrderedAccountKeyList;
   i,j : Integer;
   i,j : Integer;
+  lockedList : TList;
 begin
 begin
-  if Not Find(AccountKey,i) then begin
-    New(P);
-    P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
-    P^.accounts_number := TOrderedCardinalList.Create;
-    FOrderedAccountKeysList.Insert(i,P);
-    // Search this key in the AccountsList and add all...
-    j := 0;
-    if Assigned(FAccountList) then begin
-      For i:=0 to FAccountList.AccountsCount-1 do begin
-        If TAccountComp.EqualAccountKeys(FAccountList.Account(i).accountInfo.accountkey,AccountKey) then begin
-          // Note: P^.accounts will be ascending ordered due to "for i:=0 to ..."
-          P^.accounts_number.Add(i);
+  lockedList := Lock;
+  Try
+    if Not Find(lockedList,AccountKey,i) then begin
+      New(P);
+      P^ := CT_TOrderedAccountKeyList_NUL;
+      P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
+      P^.accounts_number := TOrderedCardinalList.Create;
+      inc(P^.changes_counter);
+      inc(FTotalChanges);
+      lockedList.Insert(i,P);
+      // Search this key in the AccountsList and add all...
+      j := 0;
+      if Assigned(FAccountList) then begin
+        For i:=0 to FAccountList.AccountsCount-1 do begin
+          If TAccountComp.EqualAccountKeys(FAccountList.Account(i).accountInfo.accountkey,AccountKey) then begin
+            // Note: P^.accounts will be ascending ordered due to "for i:=0 to ..."
+            P^.accounts_number.Add(i);
+          end;
         end;
         end;
+        TLog.NewLog(ltdebug,Classname,Format('Adding account key (%d of %d) %s',[j,FAccountList.AccountsCount,TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
+      end else begin
+        TLog.NewLog(ltdebug,Classname,Format('Adding account key (no Account List) %s',[TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
       end;
       end;
-      TLog.NewLog(ltdebug,Classname,Format('Adding account key (%d of %d) %s',[j,FAccountList.AccountsCount,TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
-    end else begin
-      TLog.NewLog(ltdebug,Classname,Format('Adding account key (no Account List) %s',[TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(AccountKey))]));
     end;
     end;
+  finally
+    Unlock;
   end;
   end;
 end;
 end;
 
 
 procedure TOrderedAccountKeysList.AddAccounts(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 procedure TOrderedAccountKeysList.AddAccounts(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 Var P : POrderedAccountKeyList;
 Var P : POrderedAccountKeyList;
   i,i2 : Integer;
   i,i2 : Integer;
+  lockedList : TList;
 begin
 begin
-  if Find(AccountKey,i) then begin
-    P :=  POrderedAccountKeyList(FOrderedAccountKeysList[i]);
-  end else if (FAutoAddAll) then begin
-    New(P);
-    P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
-    P^.accounts_number := TOrderedCardinalList.Create;
-    FOrderedAccountKeysList.Insert(i,P);
-  end else exit;
-  for i := Low(accounts) to High(accounts) do begin
-    P^.accounts_number.Add(accounts[i]);
+  lockedList := Lock;
+  Try
+    if Find(lockedList,AccountKey,i) then begin
+      P :=  POrderedAccountKeyList(lockedList[i]);
+    end else if (FAutoAddAll) then begin
+      New(P);
+      P^ := CT_TOrderedAccountKeyList_NUL;
+      P^.rawaccountkey := TAccountComp.AccountKey2RawString(AccountKey);
+      P^.accounts_number := TOrderedCardinalList.Create;
+      lockedList.Insert(i,P);
+    end else exit;
+    for i := Low(accounts) to High(accounts) do begin
+      P^.accounts_number.Add(accounts[i]);
+    end;
+    inc(P^.changes_counter);
+    inc(FTotalChanges);
+  finally
+    Unlock;
   end;
   end;
 end;
 end;
 
 
 procedure TOrderedAccountKeysList.Clear;
 procedure TOrderedAccountKeysList.Clear;
 begin
 begin
-  ClearAccounts(true);
+  Lock;
+  Try
+    ClearAccounts(true);
+    FTotalChanges := 1; // 1 = At least 1 change
+  finally
+    Unlock;
+  end;
 end;
 end;
 
 
 function TOrderedAccountKeysList.ToArray : TAccountKeyArray;
 function TOrderedAccountKeysList.ToArray : TAccountKeyArray;
 var i : Integer;
 var i : Integer;
 begin
 begin
-  SetLength(Result, Count);
-  for i := 0 to Count - 1 do Result[i] := Self.AccountKey[i];
+  Lock;
+  Try
+    SetLength(Result, Count);
+    for i := 0 to Count - 1 do Result[i] := Self.AccountKey[i];
+  finally
+    Unlock;
+  end;
+end;
+
+function TOrderedAccountKeysList.Lock: TList;
+begin
+  Result := FOrderedAccountKeysList.LockList;
+end;
+
+procedure TOrderedAccountKeysList.Unlock;
+begin
+  FOrderedAccountKeysList.UnlockList;
+end;
+
+function TOrderedAccountKeysList.HasAccountKeyChanged: Boolean;
+begin
+  Result := FTotalChanges>0;
 end;
 end;
 
 
 procedure TOrderedAccountKeysList.ClearAccounts(RemoveAccountList : Boolean);
 procedure TOrderedAccountKeysList.ClearAccounts(RemoveAccountList : Boolean);
 Var P : POrderedAccountKeyList;
 Var P : POrderedAccountKeyList;
   i : Integer;
   i : Integer;
+  lockedList : TList;
 begin
 begin
-  for i := 0 to FOrderedAccountKeysList.Count - 1 do begin
-    P := FOrderedAccountKeysList[i];
+  lockedList := Lock;
+  Try
+    for i := 0 to  lockedList.Count - 1 do begin
+      P := lockedList[i];
+      inc(P^.changes_counter);
+      if RemoveAccountList then begin
+        P^.accounts_number.Free;
+        Dispose(P);
+      end else begin
+        P^.accounts_number.Clear;
+      end;
+    end;
     if RemoveAccountList then begin
     if RemoveAccountList then begin
-      P^.accounts_number.Free;
-      Dispose(P);
-    end else begin
-      P^.accounts_number.Clear;
+      lockedList.Clear;
     end;
     end;
-  end;
-  if RemoveAccountList then begin
-    FOrderedAccountKeysList.Clear;
+    FTotalChanges:=lockedList.Count + 1; // At least 1 change
+  finally
+    Unlock;
   end;
   end;
 end;
 end;
 
 
 function TOrderedAccountKeysList.Count: Integer;
 function TOrderedAccountKeysList.Count: Integer;
+var lockedList : TList;
 begin
 begin
-  Result := FOrderedAccountKeysList.Count;
+  lockedList := Lock;
+  Try
+    Result := lockedList.Count;
+  finally
+    Unlock;
+  end;
 end;
 end;
 
 
 constructor TOrderedAccountKeysList.Create(AccountList : TPCSafeBox; AutoAddAll : Boolean);
 constructor TOrderedAccountKeysList.Create(AccountList : TPCSafeBox; AutoAddAll : Boolean);
@@ -4431,13 +4501,19 @@ begin
   TLog.NewLog(ltdebug,Classname,'Creating an Ordered Account Keys List adding all:'+CT_TRUE_FALSE[AutoAddAll]);
   TLog.NewLog(ltdebug,Classname,'Creating an Ordered Account Keys List adding all:'+CT_TRUE_FALSE[AutoAddAll]);
   FAutoAddAll := AutoAddAll;
   FAutoAddAll := AutoAddAll;
   FAccountList := AccountList;
   FAccountList := AccountList;
-  FOrderedAccountKeysList := TList.Create;
+  FTotalChanges:=0;
+  FOrderedAccountKeysList := TPCThreadList.Create(ClassName);
   if Assigned(AccountList) then begin
   if Assigned(AccountList) then begin
-    AccountList.FListOfOrderedAccountKeysList.Add(Self);
-    if AutoAddAll then begin
-      for i := 0 to AccountList.AccountsCount - 1 do begin
-        AddAccountKey(AccountList.Account(i).accountInfo.accountkey);
+    Lock;
+    Try
+      AccountList.FListOfOrderedAccountKeysList.Add(Self);
+      if AutoAddAll then begin
+        for i := 0 to AccountList.AccountsCount - 1 do begin
+          AddAccountKey(AccountList.Account(i).accountInfo.accountkey);
+        end;
       end;
       end;
+    finally
+      Unlock;
     end;
     end;
   end;
   end;
 end;
 end;
@@ -4453,18 +4529,18 @@ begin
   inherited;
   inherited;
 end;
 end;
 
 
-function TOrderedAccountKeysList.Find(const AccountKey: TAccountKey; var Index: Integer): Boolean;
+function TOrderedAccountKeysList.Find(lockedList : TList; const AccountKey: TAccountKey; var Index: Integer): Boolean;
 var L, H, I, C: Integer;
 var L, H, I, C: Integer;
   rak : TRawBytes;
   rak : TRawBytes;
 begin
 begin
   Result := False;
   Result := False;
   rak := TAccountComp.AccountKey2RawString(AccountKey);
   rak := TAccountComp.AccountKey2RawString(AccountKey);
   L := 0;
   L := 0;
-  H := FOrderedAccountKeysList.Count - 1;
+  H := lockedList.Count - 1;
   while L <= H do
   while L <= H do
   begin
   begin
     I := (L + H) shr 1;
     I := (L + H) shr 1;
-    C := CompareStr( POrderedAccountKeyList(FOrderedAccountKeysList[I]).rawaccountkey, rak );
+    C := TBaseType.BinStrComp( POrderedAccountKeyList(lockedList[I]).rawaccountkey, rak );
     if C < 0 then L := I + 1 else
     if C < 0 then L := I + 1 else
     begin
     begin
       H := I - 1;
       H := I - 1;
@@ -4478,52 +4554,112 @@ begin
   Index := L;
   Index := L;
 end;
 end;
 
 
+function TOrderedAccountKeysList.GetAccountKeyChanges(index : Integer): Integer;
+var lockedList : TList;
+begin
+  lockedList := Lock;
+  Try
+    Result :=  POrderedAccountKeyList(lockedList[index])^.changes_counter;
+  finally
+    Unlock;
+  end;
+end;
+
 function TOrderedAccountKeysList.GetAccountKey(index: Integer): TAccountKey;
 function TOrderedAccountKeysList.GetAccountKey(index: Integer): TAccountKey;
 Var raw : TRawBytes;
 Var raw : TRawBytes;
+  lockedList : TList;
 begin
 begin
-  raw := POrderedAccountKeyList(FOrderedAccountKeysList[index]).rawaccountkey;
+  lockedList := Lock;
+  Try
+    raw := POrderedAccountKeyList(lockedList[index]).rawaccountkey;
+  finally
+    Unlock;
+  end;
   Result := TAccountComp.RawString2Accountkey(raw);
   Result := TAccountComp.RawString2Accountkey(raw);
 end;
 end;
 
 
 function TOrderedAccountKeysList.GetAccountKeyList(index: Integer): TOrderedCardinalList;
 function TOrderedAccountKeysList.GetAccountKeyList(index: Integer): TOrderedCardinalList;
+var lockedList : TList;
 begin
 begin
-  Result := POrderedAccountKeyList(FOrderedAccountKeysList[index]).accounts_number;
+  lockedList := Lock;
+  Try
+    Result := POrderedAccountKeyList(lockedList[index]).accounts_number;
+  finally
+    Unlock;
+  end;
 end;
 end;
 
 
 function TOrderedAccountKeysList.IndexOfAccountKey(const AccountKey: TAccountKey): Integer;
 function TOrderedAccountKeysList.IndexOfAccountKey(const AccountKey: TAccountKey): Integer;
+var lockedList : TList;
+begin
+  lockedList := Lock;
+  Try
+    If Not Find(lockedList,AccountKey,Result) then Result := -1;
+  finally
+    Unlock;
+  end;
+end;
+
+procedure TOrderedAccountKeysList.ClearAccountKeyChanges;
+var i : Integer;
+  lockedList : TList;
 begin
 begin
-  If Not Find(AccountKey,Result) then Result := -1;
+  lockedList := Lock;
+  Try
+    for i:=0 to lockedList.Count-1 do begin
+      POrderedAccountKeyList(lockedList[i])^.changes_counter:=0;
+    end;
+    FTotalChanges:=0;
+  finally
+    Unlock;
+  end;
 end;
 end;
 
 
 procedure TOrderedAccountKeysList.RemoveAccounts(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 procedure TOrderedAccountKeysList.RemoveAccounts(const AccountKey: TAccountKey; const accounts: array of Cardinal);
 Var P : POrderedAccountKeyList;
 Var P : POrderedAccountKeyList;
   i,j : Integer;
   i,j : Integer;
+  lockedList : TList;
 begin
 begin
-  if Not Find(AccountKey,i) then exit; // Nothing to do
-  P :=  POrderedAccountKeyList(FOrderedAccountKeysList[i]);
-  for j := Low(accounts) to High(accounts) do begin
-    P^.accounts_number.Remove(accounts[j]);
-  end;
-  if (P^.accounts_number.Count=0) And (FAutoAddAll) then begin
-    // Remove from list
-    FOrderedAccountKeysList.Delete(i);
-    // Free it
-    P^.accounts_number.free;
-    Dispose(P);
+  lockedList := Lock;
+  Try
+    if Not Find(lockedList,AccountKey,i) then exit; // Nothing to do
+    P :=  POrderedAccountKeyList(lockedList[i]);
+    inc(P^.changes_counter);
+    inc(FTotalChanges);
+    for j := Low(accounts) to High(accounts) do begin
+      P^.accounts_number.Remove(accounts[j]);
+    end;
+    if (P^.accounts_number.Count=0) And (FAutoAddAll) then begin
+      // Remove from list
+      lockedList.Delete(i);
+      // Free it
+      P^.accounts_number.free;
+      Dispose(P);
+    end;
+  finally
+    Unlock;
   end;
   end;
 end;
 end;
 
 
 procedure TOrderedAccountKeysList.RemoveAccountKey(const AccountKey: TAccountKey);
 procedure TOrderedAccountKeysList.RemoveAccountKey(const AccountKey: TAccountKey);
 Var P : POrderedAccountKeyList;
 Var P : POrderedAccountKeyList;
   i,j : Integer;
   i,j : Integer;
+  lockedList : TList;
 begin
 begin
-  if Not Find(AccountKey,i) then exit; // Nothing to do
-  P :=  POrderedAccountKeyList(FOrderedAccountKeysList[i]);
-  // Remove from list
-  FOrderedAccountKeysList.Delete(i);
-  // Free it
-  P^.accounts_number.free;
-  Dispose(P);
+  lockedList := Lock;
+  Try
+    if Not Find(lockedList,AccountKey,i) then exit; // Nothing to do
+    P :=  POrderedAccountKeyList(lockedList[i]);
+    inc(P^.changes_counter);
+    inc(FTotalChanges);
+    // Remove from list
+    lockedList.Delete(i);
+    // Free it
+    P^.accounts_number.free;
+    Dispose(P);
+  finally
+    Unlock;
+  end;
 end;
 end;
 
 
 { TAccountPreviousBlockInfo }
 { TAccountPreviousBlockInfo }

+ 26 - 0
src/core/UNode.pas

@@ -116,15 +116,18 @@ Type
   TNodeNotifyEvents = Class(TComponent)
   TNodeNotifyEvents = Class(TComponent)
   private
   private
     FNode: TNode;
     FNode: TNode;
+    FOnKeyActivity: TNotifyEvent;
     FPendingNotificationsList : TPCThreadList;
     FPendingNotificationsList : TPCThreadList;
     FThreadSafeNodeNotifyEvent : TThreadSafeNodeNotifyEvent;
     FThreadSafeNodeNotifyEvent : TThreadSafeNodeNotifyEvent;
     FOnBlocksChanged: TNotifyEvent;
     FOnBlocksChanged: TNotifyEvent;
     FOnOperationsChanged: TNotifyEvent;
     FOnOperationsChanged: TNotifyEvent;
     FMessages : TStringList;
     FMessages : TStringList;
     FOnNodeMessageEvent: TNodeMessageEvent;
     FOnNodeMessageEvent: TNodeMessageEvent;
+    FWatchKeys: TOrderedAccountKeysList;
     procedure SetNode(const Value: TNode);
     procedure SetNode(const Value: TNode);
     Procedure NotifyBlocksChanged;
     Procedure NotifyBlocksChanged;
     Procedure NotifyOperationsChanged;
     Procedure NotifyOperationsChanged;
+    procedure SetWatchKeys(AValue: TOrderedAccountKeysList);
   protected
   protected
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
   public
   public
@@ -134,6 +137,8 @@ Type
     Property OnBlocksChanged : TNotifyEvent read FOnBlocksChanged write FOnBlocksChanged;
     Property OnBlocksChanged : TNotifyEvent read FOnBlocksChanged write FOnBlocksChanged;
     Property OnOperationsChanged : TNotifyEvent read FOnOperationsChanged write FOnOperationsChanged;
     Property OnOperationsChanged : TNotifyEvent read FOnOperationsChanged write FOnOperationsChanged;
     Property OnNodeMessageEvent : TNodeMessageEvent read FOnNodeMessageEvent write FOnNodeMessageEvent;
     Property OnNodeMessageEvent : TNodeMessageEvent read FOnNodeMessageEvent write FOnNodeMessageEvent;
+    Property WatchKeys : TOrderedAccountKeysList read FWatchKeys write SetWatchKeys;
+    Property OnKeyActivity : TNotifyEvent read FOnKeyActivity write FOnKeyActivity;
   End;
   End;
 
 
   TThreadNodeNotifyNewBlock = Class(TPCThread)
   TThreadNodeNotifyNewBlock = Class(TPCThread)
@@ -1099,6 +1104,8 @@ begin
   FOnOperationsChanged := Nil;
   FOnOperationsChanged := Nil;
   FOnBlocksChanged := Nil;
   FOnBlocksChanged := Nil;
   FOnNodeMessageEvent := Nil;
   FOnNodeMessageEvent := Nil;
+  FWatchKeys := Nil;
+  FOnKeyActivity:=Nil;
   FMessages := TStringList.Create;
   FMessages := TStringList.Create;
   FPendingNotificationsList := TPCThreadList.Create('TNodeNotifyEvents_PendingNotificationsList');
   FPendingNotificationsList := TPCThreadList.Create('TNodeNotifyEvents_PendingNotificationsList');
   FThreadSafeNodeNotifyEvent := TThreadSafeNodeNotifyEvent.Create(Self);
   FThreadSafeNodeNotifyEvent := TThreadSafeNodeNotifyEvent.Create(Self);
@@ -1134,6 +1141,12 @@ begin
   if Assigned(FThreadSafeNodeNotifyEvent) then FThreadSafeNodeNotifyEvent.FNotifyOperationsChanged := true;
   if Assigned(FThreadSafeNodeNotifyEvent) then FThreadSafeNodeNotifyEvent.FNotifyOperationsChanged := true;
 end;
 end;
 
 
+procedure TNodeNotifyEvents.SetWatchKeys(AValue: TOrderedAccountKeysList);
+begin
+  if FWatchKeys=AValue then Exit;
+  FWatchKeys:=AValue;
+end;
+
 procedure TNodeNotifyEvents.SetNode(const Value: TNode);
 procedure TNodeNotifyEvents.SetNode(const Value: TNode);
 begin
 begin
   if FNode=Value then exit;
   if FNode=Value then exit;
@@ -1166,17 +1179,21 @@ end;
 
 
 procedure TThreadSafeNodeNotifyEvent.SynchronizedProcess;
 procedure TThreadSafeNodeNotifyEvent.SynchronizedProcess;
 Var i : Integer;
 Var i : Integer;
+  can_alert_keys : Boolean;
 begin
 begin
   Try
   Try
     If (Terminated) Or (Not Assigned(FNodeNotifyEvents)) then exit;
     If (Terminated) Or (Not Assigned(FNodeNotifyEvents)) then exit;
+    can_alert_keys := False;
     if FNotifyBlocksChanged then begin
     if FNotifyBlocksChanged then begin
       FNotifyBlocksChanged := false;
       FNotifyBlocksChanged := false;
+      can_alert_keys := True;
       DebugStep:='Notify OnBlocksChanged';
       DebugStep:='Notify OnBlocksChanged';
       if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnBlocksChanged)) then
       if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnBlocksChanged)) then
         FNodeNotifyEvents.FOnBlocksChanged(FNodeNotifyEvents);
         FNodeNotifyEvents.FOnBlocksChanged(FNodeNotifyEvents);
     end;
     end;
     if FNotifyOperationsChanged then begin
     if FNotifyOperationsChanged then begin
       FNotifyOperationsChanged := false;
       FNotifyOperationsChanged := false;
+      can_alert_keys := True;
       DebugStep:='Notify OnOperationsChanged';
       DebugStep:='Notify OnOperationsChanged';
       if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnOperationsChanged)) then
       if Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FOnOperationsChanged)) then
         FNodeNotifyEvents.FOnOperationsChanged(FNodeNotifyEvents);
         FNodeNotifyEvents.FOnOperationsChanged(FNodeNotifyEvents);
@@ -1191,6 +1208,15 @@ begin
       end;
       end;
       FNodeNotifyEvents.FMessages.Clear;
       FNodeNotifyEvents.FMessages.Clear;
     end;
     end;
+    if (can_alert_keys) And Assigned(FNodeNotifyEvents) And (Assigned(FNodeNotifyEvents.FWatchKeys)) then begin
+      DebugStep:='Notify WatchKeys';
+      If FNodeNotifyEvents.FWatchKeys.HasAccountKeyChanged then begin
+        FNodeNotifyEvents.FWatchKeys.ClearAccountKeyChanges;
+        if Assigned(FNodeNotifyEvents.FOnKeyActivity) then begin
+          FNodeNotifyEvents.FOnKeyActivity(FNodeNotifyEvents);
+        end;
+      end;
+    end;
   Except
   Except
     On E:Exception do begin
     On E:Exception do begin
       TLog.NewLog(lterror,ClassName,'Exception inside a Synchronized process: '+E.ClassName+':'+E.Message+' Step:'+DebugStep);
       TLog.NewLog(lterror,ClassName,'Exception inside a Synchronized process: '+E.ClassName+':'+E.Message+' Step:'+DebugStep);

+ 1 - 1
src/core/UWallet.pas

@@ -258,7 +258,7 @@ begin
   while L <= H do
   while L <= H do
   begin
   begin
     I := (L + H) shr 1;
     I := (L + H) shr 1;
-    C := CompareStr( PWalletKey(FSearchableKeys[I]).SearchableAccountKey, rak );
+    C := TBaseType.BinStrComp( PWalletKey(FSearchableKeys[I]).SearchableAccountKey, rak );
     if C < 0 then L := I + 1 else
     if C < 0 then L := I + 1 else
     begin
     begin
       H := I - 1;
       H := I - 1;