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

fixDAA; implement wtema #3477

Merged
merged 13 commits into from
Nov 25, 2020
Merged

fixDAA; implement wtema #3477

merged 13 commits into from
Nov 25, 2020

Conversation

tromp
Copy link
Contributor

@tromp tromp commented Oct 17, 2020


name: [WIP] fixDAA
about: implement simpler wtema difficulty adjustment algorithm
title: fixDAA
labels: consensus breaking
assignees: antiochp, quentinlesceller, joltz


addresses mimblewimble/grin-rfcs#61

@antiochp
Copy link
Member

Posted a question related to wtema and FlyClient in the DAA RFC -
mimblewimble/grin-rfcs#61 (comment)

> Utc::now() + Duration::seconds(12 * (consensus::BLOCK_TIME_SEC as i64))
{
// refuse blocks more than 12 blocks intervals in future (as in bitcoin)
if header.timestamp > Utc::now() + Duration::seconds(5 * 60) {
Copy link
Member

Choose a reason for hiding this comment

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

@tromp this is what you were asking in keybase about?

We do not have access to config directly here. But we could potentially expose it via lazy_static! and thread_local! similar to what we do for CHAIN_TYPE and NRD_FEATURE_ENABLED.

That way we could have something like global::get_future_time_limit() following the same pattern as global::get_chain_type().

The reason for introducing the thread_local! complexity is to make this configurable in tests. Otherwise we have a single global config value that ends up causing havoc across multiple tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure this is something we would want to test anytime soon.
Is implementation much simpler if we forego testing different settings?

Copy link
Member

Choose a reason for hiding this comment

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

Not really. We're better off just following the existing pattern I think.
Or just leaving it as hardcoded for now. Maybe just move the constant to global.rs and reference it from there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Following existing pattern now.

core/src/core/block.rs Outdated Show resolved Hide resolved
src/bin/grin.rs Outdated
@@ -148,7 +148,7 @@ fn real_main() -> i32 {

// Initialize our global chain_type and feature flags (NRD kernel support currently).
// These are read via global and not read from config beyond this point.
global::init_global_chain_type(config.members.unwrap().server.chain_type);
global::init_global_chain_type(config.members.as_mut().unwrap().server.chain_type);
Copy link
Member

Choose a reason for hiding this comment

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

as_mut() not required here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

my understanding is that all but the last access to config.members needs to have as_mut(),
as suggested by lines 127 and 128.

Copy link
Member

Choose a reason for hiding this comment

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

Ugh.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah - this whole init of the config needs rethinking in grin.rs. It looks like we have repeatedly just hacked this until it worked...

We should not need to have mut config here as we do not want to inadvertently write to it. But it needs to be mut because something else higher up in the file has modified a default via a mut on some other piece of nested config.

👍 on getting this working for now - but we should revisit at some point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i see that grin-wallet uses

        global::init_global_chain_type(
                config
                        .members
                        .as_ref()
                        .unwrap()
                        .wallet
                        .chain_type
                        .as_ref()
                        .unwrap()
                        .clone(),
        );

perhaps that's better than as_mut() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

do you agree with adding a global::init_global_accept_fee_base() method and associated
global::get_accept_fee_base() for the benefit of grin-wallet?
It would call global::init_global_accept_fee_base() after parsing the grin-wallet.toml file,
and then pass global::get_accept_fee_base() as 4th argument whenever calling txlib::mod::tx_fee

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I decided to have an accept_fee_base parameter in BOTH grin-server.toml and grin-wallet.toml,
and both using global::get_accept_fee_base() to query its value. It's even possible (but not particularly useful) to set then to different values in simultaneously running server and wallet.

As a result, we can simply libtx::mod::tx_fee to no longer have an optional fee base argument. If you want to deviate from the default, you can just change it locally, like I do in some tests.

ftl
} else {
// Global config unset, default to 5 minutes
5 * 60
Copy link
Member

Choose a reason for hiding this comment

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

We should call set_local_future_time_limit() either way here so we just read local config next time.
This way we are simply lazily initializing the local config.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does that apply to is_nrd_enabled() as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

set_local_future_time_limit() always called now

@@ -214,6 +217,7 @@ impl Default for ServerConfig {
dandelion_config: pool::DandelionConfig::default(),
stratum_mining_config: Some(StratumServerConfig::default()),
chain_type: ChainTypes::default(),
future_time_limit: 5 * 60,
Copy link
Member

Choose a reason for hiding this comment

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

We should make 5 * 60 a const in global.rs and reuse it in get_future_time_limit() above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good; but then we should also use config::API_SECRET_FILE_NAME rather than ".api_secret" just above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

default now defined as global::DEFAULT_FUTURE_TIME_LIMIT

@antiochp
Copy link
Member

antiochp commented Oct 29, 2020

Came across this and trying to wrap my head around some of it -
zawy12/difficulty-algorithms#30

POW consensus must:
...
2. Have a future timestamp limit that is > 2x "expected" node time error & << difficulty averaging window (strictly speaking, << block time).

Is (2) here relevant to our decision to use a 5 minute FTL?

@tromp
Copy link
Contributor Author

tromp commented Oct 30, 2020

Is (2) here relevant to our decision to use a 5 minute FTL?

zawy argues that an FTL shorter than the blocktime is the best guard against timestamp manipulation. Which is undeniably true; but other people think that doing so carries too much risk of having the local clocks move out of sync beyond the FTL. So our choice of 5 minutes is a compromise that allows some but not much timestamp manipulation, while keeping the risk of out of sync local time very small (the idea being that a clock off by 5 minutes is quite human noticeable, while 1 minute is not so much).

Copy link
Member

@antiochp antiochp left a comment

Choose a reason for hiding this comment

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

Looks good to me.
I did not review the actual next_wtema_difficulty() implementation.

@@ -148,7 +215,7 @@ fn repeat(interval: u64, diff: HeaderInfo, len: u64, cur_time: Option<u64>) -> V
.collect::<Vec<_>>()
}

fn repeat_offs(from: u64, interval: u64, diff: u64, len: u64) -> Vec<HeaderInfo> {
fn repeat_offs(interval: u64, diff: u64, len: u64, from: u64) -> Vec<HeaderInfo> {
Copy link
Member

Choose a reason for hiding this comment

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

Glad this is not called many times and we don't need to review those params getting juggled around... 😅

@lehnberg
Copy link
Collaborator

fixes: #3473

@tromp tromp changed the title [WIP] fixDAA; implement wtema fixDAA; implement wtema Nov 23, 2020
@antiochp
Copy link
Member

@quentinlesceller @j01tz I added you as reviewers (@tromp has you listed in the PR description).

pub const DMA_WINDOW: u64 = HOUR_HEIGHT;

/// Difficulty adjustment half life is 4 hours
pub const WTEMA_HALF_LIFE: u64 = 4 * HOUR_SEC;
Copy link
Member

Choose a reason for hiding this comment

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

How was WTEMA_HALF_LIFE parameter selection determined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's a tradeoff between response speed and stability. the 240 blocks closely matches BTH's choice of the equivalent of 288 blocks.

let last_diff = last_header.difficulty.to_num();

// wtema difficulty update
let next_diff =
Copy link
Member

Choose a reason for hiding this comment

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

I'm struggling a bit verifying this algorithm against the details in the RFC and WTEMA specification. It seems straightforward enough here, but I think I need to better wrap my head around the WTEMA_HALF_LIFE value. Maybe a line or two of documentation could be helpful here.

Copy link
Contributor

@jaspervdm jaspervdm left a comment

Choose a reason for hiding this comment

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

Looks good! 👍

@@ -1,43 +1,34 @@
# Grin's Proof-of-Work
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the changes in this doc are not relevant to the difficulty adjustment algorithm. Maybe we should have them in a separate PR instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the doc fixes don't need review so this seemed the easiest way to get then applied.


// wtema difficulty update
let next_diff =
last_diff * WTEMA_HALF_LIFE / (WTEMA_HALF_LIFE - BLOCK_TIME_SEC + last_block_time);
Copy link
Contributor

Choose a reason for hiding this comment

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

Took me a bit to process so putting a note here: in terms of the RFC, WTEMA_HALF_LIFE corresponds to W * T. So in this PR we have a W of 240, meaning the difficulty can change by a factor ~e every 240 blocks (4 hours, hence the name of WTEMA_HALF_LIFE) 👍

Copy link
Contributor Author

@tromp tromp Nov 24, 2020

Choose a reason for hiding this comment

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

correct. the name HALF_LIFE is a bit misleading in that sense, since it corresponds not to a doubling.
but that's the terminology that jtoomim and zawy seem to use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants