-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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 option handling #3889
Refactor option handling #3889
Conversation
My section headings for the description did not come through in the original PR submission. I've re-added them above, and they should improve readability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NACK - I activated yesterday codacy's PR Quality Review for the Bisq repository. It found for your PR a couple of issues which you can view in the Details link next to the PR check. Please review and push fixes for this issues to trigger a re-review by codacy. Thanks!
I've just pushed fixes for the code quality issues, but a number of false positives will remain unfixed. Please see my commit comment for details. @ripcurlx, this may be an example of a PMD rule we want to eliminate. Would like to know why it's a false positive, though. Those imports are in fact used. |
Also note that the |
I just removed the unused on demand import pattern that seems to have caused the issue in the |
So only a merge conflict resolution with WalletsSetup.java is missing to make this PR mergeable again. |
Right, I've fixed that locally, and just talked with @ripcurlx about how we want to actually go ahead with the merge. No need to go into detail about it here, but anyone else who would like to review this PR, please do, thanks. |
^^ Travis build (https://travis-ci.org/bisq-network/bisq/builds/635462277) I just triggered it manually and everything seems to compile fine. Not sure why it is not updated by GitHub. |
gradle/witness/gradle-witness.gradle
Outdated
'io.opencensus:opencensus-api:8e2cb0f6391d8eb0a1bcd01e7748883f0033b1941754f4ed3f19d2c3e4276fc8', | ||
'io.opencensus:opencensus-contrib-grpc-metrics:29fc79401082301542cab89d7054d2f0825f184492654c950020553ef4ff0ef8', | ||
'io.opencensus:opencensus-contrib-http-util:d62fd27175a842bde135f6f6b1d6f25d42e9bd59a87bc98709a4760fe399ee14', | ||
'io.perfmark:perfmark-api:b734ba2149712409a44eabdb799f64768578fee0defe1418bb108fe32ea43e1a', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cbeams As we decided to be as restrictive as possible with new dependencies: Could you please point out why they are necessary (especially for the non-google libs)? Although they are required for the GRPC API I think it is good practice to have some documentation (even if it is just a comment in the PR) every time we add new dependencies to the project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Some of these and a number of other dependencies are removed in a commit that currently exists only in my fork's grpc-api branch. These changes were made as part of further refactorings of @chimp1984's original PoC, and it was an oversight that I didn't include them here. It will require some rework of that commit to do so, but is probably worth it to make sure we don't ship a release that includes these deps, even if they will be removed soon thereafter. I'll push that additional commit as soon as possible. Though note that I'll probably push it against the grpc-poc
branch (PR #3888), as it's really part of that change as opposed to this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Really enjoyed the command line access already 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result = paymentAccounts.toString(); | ||
break; | ||
case "placeOffer": | ||
// test input: placeOffer CNY BUY 750000000 true -0.2251 1000000 500000 0.12 5a972121-c30a-4b0e-b519-b17b63795d16 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: 0.12 should be at least 0.15 otherwise the security deposit is too low for our current setup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add this comment to #3888.
log.error(e.toString(), e); | ||
} | ||
break; | ||
case "stopServer": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somehow this command didn't work for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add this comment to #3888.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NACK - Because of #3889 (review)
ACK - For functionality and the remaining parts of the code 👍
@ripcurlx re: #3889 (review), this is also regarding #3888. To be clear, can you re-iterate that your ACK is for the changes specific to this PR, i.e. all things |
Actually for both - So I read through all the changes regarding |
Prior to this commit, BisqExecutable has been responsible for parsing command line and config file options and BisqEnvironment has been responsible for assigning default values to those options and providing access to option values to callers throughout the codebase. This approach has worked, but at considerable costs in complexity, verbosity, and lack of any type-safety in option values. BisqEnvironment is based on the Spring Framework's Environment abstraction, which provides a great deal of flexibility in handling command line options, environment variables, and more, but also operates on the assumption that such inputs have String-based values. After having this infrastructure in place for years now, it has become evident that using Spring's Environment abstraction was both overkill for what we needed and limited us from getting the kind of concision and type saftey that we want. The Environment abstraction is by default actually too flexible. For example, Bisq does not want or need to have environment variables potentially overriding configuration file values, as this increases our attack surface and makes our threat model more complex. This is why we explicitly removed support for handling environment variables quite some time ago. The BisqEnvironment class has also organically evolved toward becoming a kind of "God object", responsible for more than just option handling. It is also, for example, responsible for tracking the status of the user's local Bitcoin node, if any. It is also responsible for writing values to the bisq.properties config file when certain ban filters arrive via the p2p network. In the commits that follow, these unrelated functions will be factored out appropriately in order to separate concerns. As a solution to these problems, this commit begins the process of eliminating BisqEnvironment in favor of a new, bespoke Config class custom-tailored to Bisq's needs. Config removes the responsibility for option parsing from BisqExecutable, and in the end provides "one-stop shopping" for all option parsing and access needs. The changes included in this commit represent a proof of concept for the Config class, where handling of a number of options has been moved from BisqEnvironment and BisqExecutable over to Config. Because the migration is only partial, both Config and BisqEnvironment are injected side-by-side into calling code that needs access to options. As the migration is completed, BisqEnvironment will be removed entirely, and only the Config object will remain. An additional benefit of the elimination of BisqEnvironment is that it will allow us to remove our dependency on the Spring Framework (with the exception of the standalone pricenode application, which is Spring-based by design). Note that while this change and those that follow it are principally a refactoring effort, certain functional changes have been introduced. For example, Bisq now supports a `--configFile` argument at the command line that functions very similarly to Bitcoin Core's `-conf` option.
Set OptionParser.allowsUnrecognizedOptions(true) to make sure BisqEnvironment doesn't fail while options are still being transferred one-by-one to Config.
NOTE: This removes entirely the old BisqExecutable.appDataDir method implemented for the v0.5.3 hotfix that renames the data dir from 'bisq' to 'Bisq'. See a7f3d68 for details.
This adds a few basic comments to help understand the structure of the Config class and also reorders several assignments and statements for clarity.
Prior to this commit, the way that the appDataDir and its subdirectories were created was a haphazard process that worked but in a fragile and non-obvious way. When Config was instantiated, an attempt to call btcNetworkDir.mkdir() was made, but if appDataDir did not already exist, this call would always fail because mkdir() does not create parent directories. This problem was never detected, though, because the KeyStorage class happened to call mkdirs() on its 'keys' subdirectory, which, because of the plural mkdirs() call ended up creating the whole ${appDataDir}/${btcNetworkDir}/keys hierarchy. Other btcNetworkDir subdirectories such as tor/ and db/ then benefited from the hierarchy already existing when they attempted to call mkdir() for their own dirs. So the whole arrangement worked only because KeyStorage happened to make a mkdirs() call and because that code in KeyStorage happened to get invoked before the code that managed the other subdirectories. This change ensures that appDataDir and all its subdirectories are created up front, such that they are guaranteed to exist by the time they are injected into Storage, KeyStorage, WalletsSetup and TorSetup. The hierarchy is unchanged, structured as it always has been: ${appDataDir} └── btc_mainnet ├── db ├── keys ├── wallet └── tor Note that the tor/ subdirectory actually gets deleted and re-created within the TorSetup infrastructure regardless of whether the directory exists beforehand.
Previously this static property had been managed within BaseCurrencyNetwork itself and was accessed directly by callers. Now it is managed within Config, made private and accessed only via the new and well-documented baseCurrencyNetwork() method. The same goes for baseCurrencyNetworkParameters(). It is unfortunate that we must retain these mutable static fields and accessors, but after trying to eliminate them entirely, this approach is the lesser of two evils; attempting to use a Config instance and instance methods only ends up being quite cumbersome to implement, requiring Config to be injected into many more classes than it currently is. Getting access to the BaseCurrencyNetwork is basically a special case, and treating it specially as a static field is in the end the most pragmatic approach.
See Javadoc added in this change and the previous commit message for further detail and context.
This new class encapsulates all functionality related to detecting a local Bitcoin node and reporting whether or not it was detected. Previously this functionality was spread across the Config class (formerly BisqEnvironment) with its mutable static isLocalBitcoinNodeRunning property and the BisqSetup class with its checkIfLocalHostNodeIsRunning method. All of this functionality now lives within the LocalBitcoinNode class, an instance of which is wired up via Guice and injected wherever necessary. Note that the code for detecting whether the node is running has been simplified, in that it is no longer wrapped in its own dedicated Thread. There appears to be no performance benefit from doing so, and leaving it in place would have made testing more difficult than necessary. Several methods in BisqSetup have also been refactored to accept callbacks indicating which step should be run next. This has the effect of clarifying when the step2()-step5() methods will be called.
This method is used only by BisqExecutable and so has been moved there, made private and documented accordingly.
This test is now named consintently and sorted next to other config file tests.
This behavior had already been implemented prior to this commit, but has now been tested and improved with refactoring and logging messages. Note that this approach emulates Bitcoin Core's own behavior. When running, for example, `bitcoind -conf=rel/path/to/bitcoin.conf`, the relative path is prefixed / fully qualified by the value of the `datadir` option. So if `datadir` equals `~/Library/Application Support/Bitcoin`, then the `conf` option value above would be fully qualified as ~/Library/Application Support/Bitcoin/rel/path/to/bitcoin.conf If the argument to `-conf` is an absolute path, e.g. `/tmp/bitcoin.conf`, then that absolute path is used without further modification or qualification. It is assumed that the rationale for this behavior is to avoid accidentally running against the wrong conf file because `bitcoind` was invoked in a different directory than usual or because a malicious actor replaced the relative conf file with their own version. Bisq's new `--configFile` option works (and is now tested to work) in the same way: relative paths get prefixed by the value of Config.getAppDataDir(), and absolute paths are processed unmodified.
Previously (as of the prior commit), a warning was issued if a non-default config file path was specified at the command line, and then the default config file path was used as a fallback. On review, however, it would be better to halt execution immediately if the config file does not exist. There is no risk of breaking backward compatibility by doing this as Bisq never had a --configFile option before the recent commits that introduce it. Furthermore, there is no clear benefit to the fallback approach. If the user specifies a given config file and it does not exist, they may not see the warning message in the log, and they may be left with the impression that they are running against their custom config file when in fact they are running against the default (which may be empty or non-existent itself). Thus throwing an exception as is now done in this commit should make everything more explicit and clear.
Previously ConfigTests constructed Config instances with string-based options, e.g.: Config config = new Config("--appName=My-Bisq"); The advantage here is clarity, but the downside is repetition of the option names without any reference to their corresponding Config.* constants. One solution to the problem would be to format the option strings using constants declared in the Config class, e.g.: Config config = new Config(format("--%s=My-Bisq", APP_NAME)); but this is verbose, cumbersome to read and write and requires repeating he '--' and '=' option syntax. This commit introduces the Opt class and the opt() and configWithOpts() methods to ConfigTests to make testing easier while using constant references and preserving readability. e.g.: Config config = configWithOpts(opt(APP_NAME, "My-Bisq")); In the process of making these changes a bug was discovered in the monitor submodule's P2PNetworkLoad class and that has been fixed here as well. This change also required introducing several option name constants that had not previously been extracted in order to be referenced within ConfigTests. For consistency and completeness, all additional option names that did not previously have a contstant now have one.
There is currently no explicit rule for how option-related elements are ordered in the code, but it is important to understand that the order in which options are registered via parser.accept() calls is the order in which they appear in --help output. It would be nice to group options together into sections and separate them in the --help output with section headers similar to the way that Bitcoin Core's help output does it, but this is not a built-in option with the JOpt Simple library, and not worth trying to hack into place at the moment.
See updated Config Javadoc for rationale.
Previously, Travis CI was failing non-deterministically due to a race condition in which a thread was started in order to call the blocking ServerSocket.accept() method, and sometimes the subsequent attempt by LocalBitcoinNode.detectAndRun() to connect to that socket's port would occur before the thread had actually called the accept() method. This commit simplifies the approach by removing the thread entirely. As it turns out, calling accept() is not necessary; simply constructing a new ServerSocket() binds to and listens on the given port, such that a subsequent attempt to connect() will succeed.
Per Codacy report at https://app.codacy.com/gh/bisq-network/bisq/pullRequest?prid=4835062 Note that the items claiming that bisq.common.config.Config.* is an unused import are false positives. These imports are in fact used in every case.
918c6c1
to
dd5690f
Compare
I've just rebased this PR branch against current @ripcurlx, your NACK has been addressed in #3888, which as mentioned above is now merged. Please re-review and ACK if you see fit. No substantive changes have been made; only those that were necessary to eliminate conflicts resulting from the rebase. @sqrrm you should be good to go to review this now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NACK
See comments on exception usage.
Comments on braces are not blocking but it would be good to address the multiline blocks.
Apart from those comments it looks ready to merge.
The exception issue is gone so no longer blocking, would still be good to address the braces. |
@sqrrm wrote:
First some data: Of 171
Note that 3 of the 15 above are actually aberrations in which the brace is on a newline (@wiz, I'm looking at you), so the total number is actually 12. Of 3346
As far as I am aware, we have not written down any rules on this. There is nothing in the bisq-network/style repository's issues, for example. In the absence of any explicit rule, I would generally agree that one should fall back to the default rule to "follow convention" and thus the With that said, I'd like to argue for embracing braceless public void myMethod(String arg) {
if (arg == null)
throw new IllegalArgumentException(...);
// normal logic follows
} To add braces around the guard logic is simply to add line noise. One wants the eye to flow over the guard logic, understanding it intuitively as guard logic, and to then get to the meat of the actual method. Adding braces makes it feel "heavier", subtly making the reader wonder whether there might be something there that requires more thought than: "this is just a null check". I would argue the same for the public boolean has(OptionSpec<?> option) {
for (OptionSet optionSet : optionSets)
if (optionSet.has(option))
return true;
return false;
} I argue that nothing beneficial comes from adding braces here, and that doing so is to follow a convention blindly, purely for the sake of consistency, and at the cost of concision, readability and clarity. Now let's consider a counterexample. The following is also taken from this PR in for (String line : lines) {
if (ConfigFileOption.isOption(line)) {
ConfigFileOption existingOption = ConfigFileOption.parse(line);
if (existingOption.name.equals(name)) {
fileAlreadyContainsTargetOption = true;
if (!existingOption.arg.equals(arg)) {
ConfigFileOption newOption = new ConfigFileOption(name, arg);
writer.println(newOption);
log.warn("Overwrote existing config file option '{}' as '{}'", existingOption, newOption);
continue;
}
}
}
writer.println(line);
}
// additional logic follows It so happens that the way this is written, it would cause logic and/or compiler errors if any of these braces were left out, but consider if it had been written slightly differently and the author chose to eliminate braces wherever possible (note that this code is wrong and not functionally equivalent to the above; the changes I'm making are just for the purposes of illustration): for (String line : lines)
if (ConfigFileOption.isOption(line)) {
ConfigFileOption existingOption = ConfigFileOption.parse(line);
if (existingOption.name.equals(name)) {
fileAlreadyContainsTargetOption = true;
if (!existingOption.arg.equals(arg)) {
ConfigFileOption newOption = new ConfigFileOption(name, arg);
writer.println(newOption);
log.warn("Overwrote existing config file option '{}' as '{}'", existingOption, newOption);
}
else
writer.println(line);
}
}
// additional logic follows In the latter example above, the author has omitted braces from the So: lest this become a bikeshed session, I propose we leave the braceless |
I agree that the guard Since there is some contention here, and I was following a discussion I had with Manfred a while back on using braces I'll ack this one. I also don't think it's important enough to even discuss too much. Let's move on and make sure we avoid cases like your horrible example. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK
Guilty as charged. Would like me to do a PR to fix that? |
This is the fourth and final PR in a series. It should ideally be merged immediately after PRs #3886, #3887 and #3888, in that order.
Introduction
As detailed in commit 614035f, this change replaces
BisqEnvironment
with a newConfig
class that unifies, simplifies and otherwise improves Bisq's option handling in a number of ways. Please read that commit message first to get proper context and rationale.Guidance for reviewers
Many of the commits in this PR represent the step-by-step, option-by-option migration from
BisqEnvironment
toConfig
. I recommend carefully reviewing at least several of these commits to understand what this migration means and how things work in the new infrastructure, but carefully reviewing every one of them is probably overkill. Please note that every commit in this PR should compile and run without errors.How these changes were tested
The majority of commits (and certainly the final one) were subjected to a complete Gradle build cycle including all tests, followed by bringing up a localnet environment using the Makefile introduced in PR #3718, e.g.:
Note that the Bisq instances launched by
make deploy
make use of many Bisq command line options. So ensuring that these nodes came up error-free was an important part of making sure that the migration was working at every step.Testing was also performed on an ad-hoc basis against a non-localnet Bisq instance, ensuring that passing certain options works as expected, e.g.
The option being tested above is
--ignoreLocalBtcNode
, and passing a newly created temp directory to--appDataDir
was a technique I commonly used to avoid any possible interference with my real production Bisq installation. In this case, I would verify that--ignoreLocalBtcNode
worked as expected by ensuring the local Bitcoin node popup did not appear in the Bisq client and that the default approach of connecting to Bisq's Bitcoin node federation took place as expected. I did not test each and every option in this way; after making a number of such migrations, I got quite confident in the process. If a migration deviated from the typical process in some way, then I usually manually tested that change.Refactorings not strictly related to option handling
Introduction of
ConfigFileEditor
to encapsulate the process of making automated updates to thebisq.properties
config file. See commit 614035f.Introduction of
LocalBitcoinNode
to encapsulate everything to do with detecting whether a local Bitcoin node is running. Of particular note here is the elimination of a dedicated thread for doing the actual detection. See commit 30bef16 for details.Functional / user-facing / non-refactoring changes
From commit 614035f:
Note that while this change and those that follow it are principally a
refactoring effort, certain functional changes have been introduced. For
example, Bisq now supports a
--configFile
argument at the command linethat functions very similarly to Bitcoin Core's
-conf
option.Commit 799eb12 renames the
numConnectionForBtc
option tonumConnectionsForBtc
.Other notes
Please disregard the unrelated Kotlin, Git and gRPC proof of concept commits in this PR. They belong respectively to PRs #3886, #3887 and #3888 and will disappear here as each gets merged.