-
Notifications
You must be signed in to change notification settings - Fork 681
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
Feat/exit at reward cycle contract #3035
Conversation
Added contracts wip fixed veto process error in chainstate Integration test passing
Thanks for important change. What about these test failures? Are they flakes or real? |
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.
Lots of good code! Still reading. Had some interim comments.
@@ -320,6 +321,41 @@ pub struct PoxConstants { | |||
_shadow: PhantomData<()>, | |||
} | |||
|
|||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] |
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.
can comment explain which part of the code will use this?
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.
use what? The struct or these derived functions?
src/chainstate/burn/db/sortdb.rs
Outdated
self.execute(sql, args)?; | ||
|
||
if let Some(exit_proposal) = exit_at_reward_cycle_info.curr_exit_proposal { | ||
let sql = |
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.
Should this verify that exactly one row was set? Is it possible that this UPDATE
touches zero rows?
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.
The logic right before this inserts a row with that block_id, and it should fail if the insert failed?
Do you think an assertion is still necessary here?
src/chainstate/coordinator/mod.rs
Outdated
if stacks_block_height_in_cycle == 2 { | ||
// We want to process the exit-at-rc contract reward cycle for the reward cycle the | ||
// current block's parent's parent belongs to since the height (in number of Stacks | ||
// blocks) of the current block in its reward cycle is 2. |
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.
It's not clear to me why this is? Why not process the votes in the first block of a reward cycle, or even the last block?
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.
Aaron felt it was a better idea to stall exit contract processing until after the first block of a reward cycle (just to avoid doing more computation in the first block of a reward cycle).
I don't do it in the last block of the reward cycle since that would invalidate the last veto.
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.
Aaron felt it was a better idea to stall exit contract processing until after the first block of a reward cycle (just to avoid doing more computation in the first block of a reward cycle).
I'm not sure the trade-off is worth it here, though (cc @kantai). If you process the exit-at-rc logic at Stacks block height mod 2, then you have to introduce and test corner cases where you have a reward cycle with only one block. If we can instead make the PoX reward set calculation faster (something that's useful on its own anyway, and something that we could reasonably do), then the concern about doing it in the first reward cycle block goes away, and no corner cases are needed. This is all consensus-critical code, so if we can avoid less code even if it means a modest performance hit (or a need to spend some cycles optimizing code), then I think it's worth it. But would love to hear a strong counter-argument.
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.
Let's discuss this in the architecture meeting
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.
I can't come this week :/ Please let me know what y'all talk about
src/chainstate/coordinator/mod.rs
Outdated
{ | ||
// This is an edge case in which the previous reward cycle contains only a single Stacks block. | ||
// It is time to process the exit-at-rc contract for the reward cycle the current block's | ||
// parent's parent belongs to. |
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.
If there's a reward cycle that contains only a single Stacks block, it might simplify things to simply declare that a vote tally will not occur. In such conditions, the network is already well and truly dead anyway.
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.
The processing occurs for the parent cycle (which could be any length) so not sure if we should introduce that rule.
(define-constant MINIMUM_RC_BUFFER_FROM_PRESENT u6) | ||
|
||
;; Data vars | ||
(define-data-var pox-reward-cycle-length uint POX_REWARD_CYCLE_LENGTH) |
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.
Instead of duplicating this here, would it be better if the contract simply pulled this information from .pox
via get-pox-info
?
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.
This also applies to first-burnchain-block-height
below.
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.
It would also be more efficient to store all of these vars as a single tuple data variable, since then reading it would incur only one MARF read. The initial values for these variables could be instantiated directly from contract-call?
's to .pox
.
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.
I avoided using get-pox-info
since that function is defined as define-read-only
(and is not callable).
I made them data vars to mimic the pattern in pox.clar - but I can change them to a tuple.
I think it could make sense to leave them separate since they are few in number, I would need a double get to get the value I require, and because I use them in different functions.
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.
read-only methods are also callable by other contracts, so the initial values for these variables should be settable by doing something like (contract-call? .pox get-pox-info)
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.
If these are settable via the pox get-pox-info call, then you should be able to switch them to constants (rather than data vars), which would simplify some of the contract code.
let mut total_votes = 0; | ||
let invalid_reward_cycles: HashSet<u64> = | ||
HashSet::from_iter(invalid_reward_cycles.into_iter()); | ||
let is_mainnet = self.burnchain.is_mainnet(); |
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.
This method is broken, but we can't fix it because it's consensus-critical. Burnchain::is_mainnet()
always returns false
, since it compares the Stacks network ID to the Bitcoin network ID (oops).
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.
Can you add a docstring to Burnchain::is_mainnet()
to say something to the effect of "BROKEN! DO NOT USE IN NEW PLACES!"
?
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.
Should I make a new is_mainnet_function (like is_mainnet_v2
) and use that instead? In that function I would compare the Stacks network ID against BITCOIN_NETWORK_ID_MAINNET
(aka BITCOIN_MAINNET
).
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.
It doesn't look like this value is ever used in this method -- I think just remove this variable.
src/chainstate/coordinator/mod.rs
Outdated
} | ||
} | ||
} else { | ||
warn!( |
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.
This is bound to happen at least once, right? When this PR goes live on existing chainstate, no block is going to have any reward cycle exit info.
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.
Yes - let's discuss this in the arch meeting
…tinction between veto/vote.
…stead of per block; fixed some tests
Todo from architecture meeting:
|
.burnchain | ||
.exit_contract_constants | ||
.rejection_confirmation_percent; | ||
let is_mainnet = self.burnchain.is_mainnet(); |
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.
It doesn't look like is_mainnet
is ever used in this method -- can be removed.
This looks good to me, I think the only todos are a few superficial changes I noted, and then a larger change to make it so that the contract isn't a boot contract in testnet. That will require changing some of the tests, but that should be doable by just:
I don't think we need to have a contract address for the testnet burnchain config in this PR: a contract can be launched to testnet after this PR merges, and a subsequent PR would set that contract in the config. |
.expect("BUG: failed to construct simple tuple"), | ||
), | ||
) | ||
.expect("BUG: Failed querying rc-proposal-rejections") |
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.
Hmm. What happens if this is evaluated before the contract is launched? Does it panic?
We'll make the call on the future of this PR at the next architecture meeting. |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
This PR implements vote to exit network contract & related logic. It is built off of the ideas in this discussion: #2845
Overview of protocol
Feedback
process_ready_blocks
andprocess_exit_reward_cycle
, I currently do nothing if the node is unable to find the exit cycle info for a particular Stacks block. This should only happen for the first Stacks block. What should this behavior look like?Future steps
Testing