Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Replace "GlobalStatus" state management #2183

Merged
merged 3 commits into from
Jun 20, 2021

Conversation

cyrossignol
Copy link
Member

@cyrossignol cyrossignol commented Jun 18, 2021

This removes the legacy GlobalStatus data structure used for polling for GUI information from core state and replaces it with the "push" pattern employed by other GUI features where we can avoid the locks. It fixes intermittent block height lag in the GUI during the initial sync.

As part of the change, I rewrote the basic miner status container into a formal API that manages thread safety internally. This also centralizes several pieces of duplicated logic into one location and enables localization for miner alert messages.

@cyrossignol cyrossignol added gui refactor This is for refactoring (if also an enhancement, use that label too). labels Jun 18, 2021
@cyrossignol cyrossignol added this to the Ingrid milestone Jun 18, 2021
@cyrossignol cyrossignol self-assigned this Jun 18, 2021
@cyrossignol cyrossignol marked this pull request as draft June 18, 2021 15:19
@jamescowens
Copy link
Member

@cyrossignol your change to a flag treatment bears a lot of similarity to what I was thinking with the miner status in https://github.com/jamescowens/Gridcoin-Research/commits/optimize_global_status, but of course your PR goes much farther in straightening this out.

@jamescowens
Copy link
Member

I am going to go through the locking on this very carefully to make sure we have actually solved the problem and not just pushed the problem around.

@jamescowens jamescowens self-requested a review June 18, 2021 16:26
@jamescowens
Copy link
Member

jamescowens commented Jun 18, 2021

I like it. You moved my age-based caching scheme to the previously unused NotifyBlocks signal to coarsen the updates, and they are push style from the core rather than pull from the GUI so there is no lock contention. I think that does it. :)

Copy link
Member

@jamescowens jamescowens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only major comment on this is that I think we are still going to get lock contention although less than before. We generate signals from the core on a change of state that passes the aging filter, which propagate via the Qt signal queue to the GUI and end up calling ClientModel::updateNumBlocks(), which is in a different (GUI thread) and then has to fight with the running core thread to get a lock on cs_main. I think it is better to put this in a core data class (containing num_blocks, difficulty, and netweight), do the filtering directly in that class being called from the GridcoinServices() function (which already posseses the lock on cs_main). The Core class for those should also have its own internal lock to protect the CACHED values during access of this data class by the GUI. So this core class is internally thread-safe via an internal lock for accessing the cached values, but requires an external cs_main lock (already taken) to have the cache values refreshed FROM the core (in the core thread context).

I also think GRC::GetEstimatedTimetoStake() should be moved back from ClientModel::updateMinerStatus(bool staking, double coin_weight) to the above mentioned core class. While GetEstimatedTimetoStake is certainly dependent on miner status and can be filtered by miner status changed, it is more dependent on core state (because of the change in difficulty).

My guess is that you should move the age filter to this core class to wrap the above functions.

The above would essentially elimate all lock contention, because the heavy functions that require a cs_main lock will be executed in a thread that already has that lock in place. The internal class thread safety lock to protect access of the cached values in from the GUI side based on signals will be cheap and nearly instantaneous.

(I know this basically means a return of sorts of the global state class, but this is a different form of it.)

What do you think?

I will fetch your branch and tinker with this a little...

src/gridcoin/staking/status.h Show resolved Hide resolved
src/gridcoin/staking/status.h Show resolved Hide resolved
// search_timestamp has already been granularized to the stake time mask.
// The StakeMiner loop has a sleep of 8 seconds. You can have no more than
// one successful stake in STAKE_TIMESTAMP_MASK, so only increment the
// counter if this iteration is with an nTime in the next mask interval.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

search_timestamp (hehe... you changed the name...)

@jamescowens
Copy link
Member

I will post a PR on your branch tomorrow with some draft thoughts on this.

@cyrossignol
Copy link
Member Author

cyrossignol commented Jun 19, 2021

@jamescowens As you noted, in regard to this PR, the queries that the GUI needs to synchronize threads to fetch core state for are:

  • Current difficulty
  • Estimated network weight
  • Estimated staking frequency
  • Estimated chain length (peer block count)

Current difficulty is a trivial calculation--we can probably push it to the GUI along with the chain tip info without any significant overhead.

Since the next two are an interpretation of the recent chain history, and the last metric is a rather wild guess, I didn't really see a need to handle those perfectly for the overview fields. In the uncommon instances where an in-sync node misses the lock in the GUI thread, the smoothed values won't be significantly off, and the next block or miner pass will very likely resolve the discrepancy. The high rate of cs_main locking during the initial sync can degrade the accuracy of those values, but the numbers fluctuate so intensely while syncing that the information is practically meaningless anyway.

I'm reluctant to reintroduce core state to cache the approximate values. I don't see any harm if the GUI exhibits a slight lag for these for a few moments every now and then. In fact, I think that skipping a cycle is better when the node is busy. We avoid additional thread safety complexity, the overhead of the mutex is tiny compared to the scans that these routines perform, and updating the values will hold up the main thread no matter which thread these execute in.

@jamescowens
Copy link
Member

Some counterpoints to your comments:

  1. I am not talking about resurrecting the monstrosity that is core state. In essence we are talking about where the following fields need to be:
    mutable std::atomic<int64_t> m_last_update_ms;
    mutable std::atomic m_cached_num_blocks;
    mutable std::atomic m_cached_num_blocks_of_peers;
    mutable std::atomic<int64_t> m_cached_best_block_time;
    mutable std::atomic m_cached_difficulty;
    mutable std::atomic m_cached_net_weight;
    mutable std::atomic m_cached_etts_days;
  2. In relation to the above, you have placed those in the clientmodel for use by the GUI, but this precludes updating those fields from the core directly, which in some instances is the correct thing to do... I will get to that in a moment.
  3. The issue that I noted that was a performance issue was not just one missed cycle, but up to several minutes where there were no updates on the main screen during a sync. In the original code this is due to your introduction of the try lock at

void BitcoinGUI::updateGlobalStatus()
{
LogPrint(BCLog::MISC, "BitcoinGUI::updateGlobalStatus()");

// This is needed to prevent segfaulting due to early GUI initialization compared to core.
if (miner_first_pass_complete)
{
    TRY_LOCK(cs_main, locked_main);

    if (!locked_main) {
        return;
    }

to relieve the locking contention on the GUI versus the core. This isn't a problem when the wallet is caught up and the core state is only changing once every 90 seconds or so, but what we were seeing is that the try-locks were falling through on many cycles when the core was busy.
4. The above issue is not alleviated in the current design. Essentially the same problem occurs at

void ClientModel::updateNumBlocks(int height, int64_t best_time)
{
m_cached_num_blocks = height;
m_cached_best_block_time = best_time;

{
    TRY_LOCK(cs_main, lockMain);

    if (lockMain) {
        m_cached_num_blocks_of_peers = ::GetNumBlocksOfPeers();
        m_cached_difficulty = GRC::GetCurrentDifficulty();
        m_cached_net_weight = GRC::GetEstimatedNetworkWeight() / 80.0;

        emit difficultyChanged(getDifficulty());
    }
}

emit numBlocksChanged(getNumBlocks(), getNumBlocksOfPeers());

}

  1. The values that are computed by these functions are not approximate in the sense that they do represent the correct values for the chain at the height that they are computed. I am not worried about a signal delay in slowly changing state when we are in sync. I am worried about when we are syncing and we are doing ~1000 blocks/sec.
  2. People have come to expect that the staking fields are updated on a regular basis during sync (a few seconds between updates). You could argue we could solve this by doing the same thing for the staking section that we do for the researcher section... blank it out with a message that the "wallet is not in sync...", but I do not think that will be well received, and we can't do that for the blocks field anyway, because then there would be no visible indication of syncing progress.

@jamescowens
Copy link
Member

As I said, I am posting a PR with a sketch of what I am talking about later today, and I would like you to look at it with an open mind to my points.

@jamescowens
Copy link
Member

@cyrossignol I posted a PR to your branch with my thoughts. Maybe there is something of value in there. You had already forced pushed after I had based my branch, so it is based on the original PR. I didn't rebase it because all the changes I made are in the last commit.

Copy link
Member

@iFoggz iFoggz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK;

Excellent work. Spent some time looking through it and liking what I am seeing. Glad these areas of much needed improvements are being undertaken and look forward to testing stage.

@cyrossignol
Copy link
Member Author

Thanks, @jamescowens. Sorry about the force-push...wasn't thinking. I'll take a look at your PR here in a bit!

...and we can't do that for the blocks field anyway, because then there would be no visible indication of syncing progress.

To clarify: as implemented in this PR, the blocks counter will always update (immediately for in-sync, or at each MODEL_UPDATE_DELAY interval during inital block download). Only the estimate fields defer updates when busy.

@jamescowens
Copy link
Member

jamescowens commented Jun 19, 2021

Yes. With my approach all four update, because they are driven from the call to...

// This is fine to call each invocation of GridcoinServices. It is internally caching, and will
// only actually update at MODEL_UPDATE_DELAY intervals.
g_blockchain_status.UpdateBlockChainStatus(pindexBest->nHeight, pindexBest->GetBlockTime());

in GridcoinServices().

My real point is summed up thusely... if you are going to convert this to a push style approach go all the way with it, and push while you still possess the cs_main lock. Your implementation is a partial push/partial pull approach. (The signal and the blockheight is pushed, but the others are pull...)

@jamescowens
Copy link
Member

I think both implementations suffer from the trigger after finishing sync problem... When the core finishes sync, depending on exactly where the model update interval last fell, the last signal could be a number of blocks behind the actual core state at the end of sync. I saw 20 blocks behind in playing around with this. This will not true up until the next block is received and processed, because there is nothing from the core to trigger the state change until that happens. This means the out-of-sync indicator stays lit and the block counter is "stuck" slightly behind the head until that next block is received.

@jamescowens
Copy link
Member

One other advantage of my approach is that the g_blockchain_status cached member fields could be used by rpc calls to lessen the load on the core. I haven't looked at that carefully yet.

@cyrossignol
Copy link
Member Author

cyrossignol commented Jun 19, 2021

My real point is summed up thusely... if you are going to convert this to a push style approach go all the way with it, and push while you still possess the cs_main lock. Your implementation is a partial push/partial pull approach. (The signal and the blockheight is pushed, but the others are pull...)

@jamescowens I was balancing the pushes for cheap/trivial values with pulls for the expensive operations that the GUI could defer while a node is busy. In other words, I didn't want to run the updates for net weight, etts, etc., immediately if the core still has work to do.

When the core finishes sync, depending on exactly where the model update interval last fell, the last signal could be a number of blocks behind the actual core state at the end of sync. I saw 20 blocks behind in playing around with this. This will not true up until the next block is received and processed, because there is nothing from the core to trigger the state change until that happens. This means the out-of-sync indicator stays lit and the block counter is "stuck" slightly behind the head until that next block is received.

Interesting... I haven't seen this happen in my tests. In this PR, the GUI skips the condition for the model update interval as soon as the core reports that the node is in-sync, so every block is processed after that point (well before the sync finishes). Will take a closer look. I think we need to work a check for IsInitialBlockDownload() into cyrossignol#6 somewhere.

One other advantage of my approach is that the g_blockchain_status cached member fields could be used by rpc calls to lessen the load on the core.

I agree—the caches in your PR could speed up the performance of getmininginfo rather well. However, it might not be appropriate for an RPC call to return stale information cached below the model update interval threshold—an RPC result should always be an exact snapshot, no?

@jamescowens
Copy link
Member

jamescowens commented Jun 19, 2021

I think it depends on the rpc call and the nature of the info. If you take the position that these metrics are estimates and not exact, then you shouldn't care about 4 seconds for etts, for example. The two heaviest calls here are by far the GetEstimatedNetworkWeight (which is really a call to GetAverageDifficulty over a 40 block interval) and the GetEstimatedTimetoStake() (the heaviest in the whole group), and I think a 4 sec lag for these is acceptable in rpc, but it may not be worth the bother. Unless you are literally pelting the wallet with some sort of stress test (which I have done before), the juice is probably not worth the squeeze.

One last point on my suggestions. To me, conceptually the items
mutable std::atomic<int64_t> m_last_update_ms;
mutable std::atomic m_cached_num_blocks;
mutable std::atomic m_cached_num_blocks_of_peers;
mutable std::atomic<int64_t> m_cached_best_block_time;
mutable std::atomic m_cached_difficulty;
mutable std::atomic m_cached_net_weight;
mutable std::atomic m_cached_etts_days;
are core parameters. If you are going to the bother to store the miner parameters in g_miner_status as the miner does its work (in the miner thread), it seems to me these deserve to be in a small peer class to that in status.h/cpp and not relegated into the clientmodel of the GUI.

I haven't run a side by side of your latest versus this BTW. I need to do that tonight.

@jamescowens
Copy link
Member

jamescowens commented Jun 19, 2021

See my comments on my PR to your branch....cyrossignol#6

@jamescowens
Copy link
Member

jamescowens commented Jun 20, 2021

Looking at them side by side, with your latest version of this PR, I think it is close enough to stick with your design. Hmm.... Actually difficulty is not updating on yours. It is staying at 0. Let's see when it actually updates. Maybe it is a region on testnet where the difficulty is really below 0.000. Yep. that looks to be the case.

This changes the miner status data structure from a dumb container into
an API that manages thread safety internally.
This replaces the GetLastStake() free function with a method as a member
of the MinerStatus class. This function relies on the miner status state
and doesn't belong in the miner code.
This removes the legacy GlobalStatus container which the GUI uses to
poll for information from core state and replaces it with the "push"
pattern employed by other GUI features where we can avoid the locks.
It fixes intermittent block height lag in the GUI during the initial
sync.

Because some information sources for the overview page changed, this
moves the API to fetch the current poll title to the voting model.
@cyrossignol
Copy link
Member Author

I think it depends on the rpc call and the nature of the info. If you take the position that these metrics are estimates and not exact, then you shouldn't care about 4 seconds for etts, for example...

Agreed.

One last point on my suggestions. To me, conceptually the items[...]are core parameters. If you are going to the bother to store the miner parameters in g_miner_status as the miner does its work (in the miner thread), it seems to me these deserve to be in a small peer class to that in status.h/cpp and not relegated into the clientmodel of the GUI.

That's a fair argument—I agree with your point here too. Since the miner status is purely for presentation, I did peek from the opposite perspective while I worked on this to see whether the MinerStatus state could make more sense as a GUI implementation with direct APIs for RPC (it doesn't, of course).

Anyway, my observation is really just pedantic for the sake of discussing design philosophy. Realistically, the benefits of the miner status state far outweigh an academic separation of concerns. We may be able to justify the BlockChainStatus cache too if we can remove the overhead from the main thread needed to keep the cache fresh for nodes that will never use it. I'm not trying to shoot the idea down—it could be an enhancement for the future.

@cyrossignol
Copy link
Member Author

cyrossignol commented Jun 20, 2021

Rebased and changed the current difficulty field to a pushed value so that it stays fresh along with block count.

@jamescowens
Copy link
Member

Let's leave the design as is for now. We can always resurrect my idea if needed. The overhead of g_blockchain_status.UpdateBlockChainStatus can be removed entirely by wrapping it externally with a conditional that checks fQtActive I think.

@cyrossignol cyrossignol marked this pull request as ready for review June 20, 2021 20:54
@jamescowens
Copy link
Member

Tested again sync from zero on testnet and mainnet. Looks good. Merging.

@jamescowens jamescowens merged commit 9c8be26 into gridcoin-community:development Jun 20, 2021
jamescowens added a commit to jamescowens/Gridcoin-Research that referenced this pull request Aug 1, 2021
Added
 - util, rpc. gui: Changes for snapshotdownload and add feature sync from zero gridcoin-community#2093 (@iFoggz)
 - gui: Implement GUI version of consolidateunspent (coin control part) gridcoin-community#2111 (@jamescowens)
 - gui: Implement consolidateunspent wizard gridcoin-community#2125 (@jamescowens)
 - qt: Add antialiasing to traffic graph widget gridcoin-community#2150 (@barton2526)
 - util: Port of ArgsManager and a significant subset of src/util gridcoin-community#2146 (@jamescowens)
 - doc: add issue templates for bug reports and feature requests gridcoin-community#2147 (@Pythonix)
 - gui, rpc: Implement dynamic stakesplitting control, settings changes via rpc, and dynamic changes to sidestaking via rpc gridcoin-community#2164 (@jamescowens)
 - rpc: Implement getblocksbatch gridcoin-community#2205 (@jamescowens)
 - voting, rpc, gui: Implement demand loading of historical poll by poll id and AVW calculation gridcoin-community#2210 (@jamescowens)
 - gui: Show GUI error dialog if command line parsing fails gridcoin-community#2218 (@jamescowens)
 - gui: Implement close confirmation. gridcoin-community#2216 (@denravonska)
 - build: Use -fstack-reuse=none gridcoin-community#2232 (@barton2526)

Changed
 - doc: Update build doc gridcoin-community#2078 (@iFoggz)
 - gui: Normalize button and input control appearance gridcoin-community#2096 (@cyrossignol)
 - consensus: Implement GetMinimumRequiredConnectionsForStaking gridcoin-community#2097 (@jamescowens)
 - refactor: move CTransaction to primitives gridcoin-community#2006 (@div72)
 - consensus, refactor, test: Merkle gridcoin-community#2094 (@div72)
 - gui: Update diagnostics gridcoin-community#2095 (@jamescowens)
 - gui: Refresh UI styles and sidebar/statusbar design gridcoin-community#2102 (@cyrossignol)
 - gui: Set standard base Qt style on Windows and macOS gridcoin-community#2114 (@cyrossignol)
 - build, refactor: bump to C++17 gridcoin-community#2113 (@div72)
 - util, rpc, gui: Implement GetMaxInputsForConsolidationTxn() gridcoin-community#2119 (@jamescowens)
 - gui: Refresh overview page design gridcoin-community#2117 (@cyrossignol)
 - depends: change boost mirror gridcoin-community#2122 (@div72)
 - refactor: small cleanup gridcoin-community#2123 (@div72)
 - build: Update depends Qt recipe to version 5.12.10 gridcoin-community#2129 (@cyrossignol)
 - build: Bump Codespell to 2.0.0 gridcoin-community#2135 (@barton2526)
 - gui: Refresh "send coins" page design gridcoin-community#2126 (@cyrossignol)
 - gui: Optimize locks to improve responsiveness gridcoin-community#2137 (@cyrossignol)
 - gui: Refresh "receive payment" page design gridcoin-community#2138 (@cyrossignol)
 - gui: Add empty placeholder to recent transactions list gridcoin-community#2140 (@cyrossignol)
 - gui: Refresh transaction history page design gridcoin-community#2143 (@cyrossignol)
 - gui: Refresh address book page design gridcoin-community#2145 (@cyrossignol)
 - doc: Update http to https where possible gridcoin-community#2148 (@barton2526)
 - depends: Update dependencies gridcoin-community#2153 (@barton2526)
 - depends: Bump python to 3.6 gridcoin-community#2159 (@barton2526)
 - test: Update cppcheck linter to c++17 gridcoin-community#2157 (@barton2526)
 - test: Drop Travis specific workarounds, Mention commit id in error, Fix typos, Update spellcheck ignore words gridcoin-community#2158 (@barton2526)
 - gui: Overhaul the voting UI gridcoin-community#2151 (@cyrossignol)
 - wallet: simplify nTimeSmart calculation gridcoin-community#2144 (@div72)
 - gui: Refresh checkbox and radio button styles gridcoin-community#2170 (@cyrossignol)
 - build: Bump libevent to 2.1.11 gridcoin-community#2172 (@barton2526)
 - build: Update native_mac_alias, Remove big sur patch file in qt recipe gridcoin-community#2173 (@barton2526)
 - docs: Misc Grammar gridcoin-community#2176 (@barton2526)
 - build: miniupnpc 2.2.2 gridcoin-community#2179 (@barton2526)
 - rpc: Refresh rainbymagnitude gridcoin-community#2163 (@jamescowens)
 - util: optimize HexStr gridcoin-community#2185 (@div72)
 - refactor: misc style changes gridcoin-community#2177 (@div72)
 - rpc: consolidatemsunspent changes. gridcoin-community#2136 (@iFoggz)
 - refactor: Replace "GlobalStatus" state management gridcoin-community#2183 (@cyrossignol)
 - rpc, util: Remove use of ArgsManager::NETWORK_ONLY for now gridcoin-community#2190 (@jamescowens)
 - doc: Replace hidden service with onion service, Capitalize "Tor" gridcoin-community#2193 (@barton2526)
 - gui: Update Qt Linguist localization files gridcoin-community#2192 (@cyrossignol)
 - script: Shell script cleanups gridcoin-community#2195 (@barton2526)
 - build: set minimum required Boost to 1.58.0 gridcoin-community#2194 (@barton2526)
 - build, util: Prevent execution for Windows versions less than Windows 7 gridcoin-community#2203 (@jamescowens)
 - build: Tweak NSIS Windows installer gridcoin-community#2204 (@jamescowens)
 - build: Add bison in depends gridcoin-community#2206 (@iFoggz)
 - build: macOS toolchain bump gridcoin-community#2207 (@div72)
 - doc: Update build-unix.md gridcoin-community#2212 (@springfielddatarecovery)
 - build: Bump minimum python version to 3.6, Remove python2 references gridcoin-community#2219 (@barton2526)
 - depends: Change openSSL source path to Github gridcoin-community#2237 (@barton2526)
 - doc: Fix typo in bug report template gridcoin-community#2243 (@jamescowens)
 - ci: fold depends output gridcoin-community#2244 (@div72)

Removed
 - wallet: remove dead hardcoded addnodes gridcoin-community#2116 (@sweede-se)
 - rpc: Remove readconfig gridcoin-community#2248 (@jamescowens)
 - rpc: Remove obsolete comparesnapshotaccrual RPC function gridcoin-community#2100 (@jamescowens)
 - rpc: Remove memorypool RPC Command gridcoin-community#2214 (@RoboticMind)
 - rpc: Remove deprecated RPC commands gridcoin-community#2101 (@jamescowens)
 - Remove CCT from README, add Discord gridcoin-community#2134 (@barton2526)
 - refactor: Remove obsolete pubsub method definitions gridcoin-community#2191 (@barton2526)
 - refactor: Remove msMiningErrorsIncluded & msMiningErrorsExcluded gridcoin-community#2215 (@RoboticMind)
 - qt: Remove obsolete topLevelWidget(), Remove obsolete QRegExpValidator gridcoin-community#2198 (@barton2526)
 - net: Drop support of the insecure miniUPnPc versions gridcoin-community#2178 (@barton2526)
 - log: remove deprecated db log category gridcoin-community#2201 (@barton2526)
 - doc: Remove CCT from README and release process docs gridcoin-community#2175 (@barton2526)
 - build: Remove travis references gridcoin-community#2156 (@barton2526)

Fixed
 - gui: Fix macOS and designer font sizes gridcoin-community#2098 (@cyrossignol)
 - gui: Have the TrafficGraphWidget respect the selected stylesheet. gridcoin-community#2107 (@jamescowens)
 - gui: Fix macOS display inconsistencies gridcoin-community#2106 (@cyrossignol)
 - gui: Fix RPC console auto-complete background color gridcoin-community#2108 (@cyrossignol)
 - gui: Avoid reloading redundant stylesheets gridcoin-community#2109 (@cyrossignol)
 - gui: Fix "no active beacon" status gridcoin-community#2110 (@cyrossignol)
 - gui: Fix dark theme link text color visibility gridcoin-community#2115 (@cyrossignol)
 - scraper, util, qt: Fix several deprecations and warnings gridcoin-community#2131 (@jamescowens)
 - gui: Fix duplicate time in GUIUtil::dateTimeStr() gridcoin-community#2139 (@cyrossignol)
 - gui: Fix debug console traffic graph legend colors gridcoin-community#2142 (@cyrossignol)
 - gui: Fix nomenclature gridcoin-community#2104 (@jamescowens)
 - doc: Fix Typos gridcoin-community#2149 (@barton2526)
 - doc: Fix "master" branch build status badge in readme gridcoin-community#2167 (@cyrossignol)
 - gui: Fix Inter font rendering on Windows with FreeType gridcoin-community#2169 (@cyrossignol)
 - gui: Fix assert on non-existent data directory and GUI datadir chooser corner case issues gridcoin-community#2174 (@jamescowens)
 - gui: Fix display artifact in poll loading indicator gridcoin-community#2180 (@cyrossignol)
 - rpc, logging: Minor fixes for sidestake logging gridcoin-community#2187 (@jamescowens)
 - gui: Fix fractional scaling for dialog sizes gridcoin-community#2189 (@cyrossignol)
 - doc: Random fixes gridcoin-community#2197 (@barton2526)
 - doc: getbalance should say GRC not "btc" gridcoin-community#2199 (@barton2526)
 - net: Add missing verification of IPv6 address in CNetAddr::GetIn6Addr¦ gridcoin-community#2200 (@barton2526)
 - doc: remove duplicate line from .gitignore gridcoin-community#2202 (@Pythonix)
 - util: Tweak exception handling in MilliTimer class to eliminate compiler warnings gridcoin-community#2233 (@jamescowens)
 - depends: patch missing include in qt gridcoin-community#2234 (@div72)
 - wallet, rpc: Check each input for IsMine() in GetAddressGroupings gridcoin-community#2242 (@jamescowens)
 - util, qt: Fix snapshot download gridcoin-community#2246 (@jamescowens)
 - gui: Fix Column Widths in RPC Console. Elide long strings in their center. Indent user agent. gridcoin-community#2241 (@barton2526)
 - qt: Fix crash during download snapshot on macOS gridcoin-community#2250 (@jamescowens)
 - qt: Don't allow to open the debug window during splashscreen & verification state gridcoin-community#2245 (@barton2526)
 - gui: Fix address book selected model record when editing gridcoin-community#2253 (@cyrossignol)
 - researcher: Check wallet status before beacon renewal gridcoin-community#2254 (@cyrossignol)
 - qt: Prevent pasting (no label) as label in consolidation transaction gridcoin-community#2255 (@jamescowens)
@cyrossignol cyrossignol deleted the global-status branch August 9, 2021 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gui refactor This is for refactoring (if also an enhancement, use that label too).
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants