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

Feat/bitcoin wire format rigidity #3126

Merged
merged 18 commits into from
Jun 7, 2022
Merged

Conversation

jcnelson
Copy link
Member

@jcnelson jcnelson commented May 5, 2022

This PR addresses #2094 by making it so that when we process a Stacks block, we search the 6 prior burnchain blocks for unprocessed TransferStx and StackStx transactions and try to apply them in this block. This tracking is handled by mapping each Stacks block's index block hash to the list of burnchain transaction IDs it handled. Then, to process a new Stacks block, we simply do the following:

  1. find all burnchain operations in the past 6 burnchain blocks that are ancestors of the burnchain block that selected this Stacks block
  2. find all burnchain operations processed in the past 6 Stacks blocks that are ancestors of the pending Stacks block (which covers at least 6 burnchain blocks in the past)
  3. remove all burnchain operations in (2) from the list in (1)
  4. process the remaining operations from (3) in blockchain order

…given burnchain header hash, which we will use to pull out a window of previously-mined on-burnchain stacks transactions
…ks to find all on-burnchain blocks that haven't yet been processed in this Stacks fork. Process them all, instead of the ones that just happen to land in this stacks block's burnchain block.
…given the index block hash of a Stacks block.
… Stacks block (keyed by index block hash), so we can quickly load them up when searching for *un*processed burnchain-hosted transactions when processing a new Stacks block.
… can be mined and processed in Stacks blocks even if the burnchain block that contains them does not have a sortition with a stacks block
@codecov
Copy link

codecov bot commented May 5, 2022

Codecov Report

Merging #3126 (43d8fb5) into next (d8f7628) will increase coverage by 0.37%.
The diff coverage is 97.19%.

@@            Coverage Diff             @@
##             next    #3126      +/-   ##
==========================================
+ Coverage   83.63%   84.00%   +0.37%     
==========================================
  Files         268      268              
  Lines      209655   210798    +1143     
==========================================
+ Hits       175338   177077    +1739     
+ Misses      34317    33721     -596     
Impacted Files Coverage Δ
src/core/mod.rs 99.50% <ø> (+22.66%) ⬆️
src/chainstate/burn/db/sortdb.rs 95.21% <88.67%> (-0.02%) ⬇️
testnet/stacks-node/src/tests/epoch_21.rs 96.68% <96.64%> (+96.68%) ⬆️
src/chainstate/stacks/db/mod.rs 87.39% <97.36%> (+0.46%) ⬆️
src/chainstate/stacks/db/blocks.rs 89.51% <98.96%> (+0.50%) ⬆️
src/chainstate/stacks/db/accounts.rs 92.24% <100.00%> (+0.01%) ⬆️
src/chainstate/stacks/db/headers.rs 88.50% <100.00%> (+0.70%) ⬆️
src/net/mod.rs 72.16% <100.00%> (+0.11%) ⬆️
testnet/stacks-node/src/tests/neon_integrations.rs 81.46% <100.00%> (+0.01%) ⬆️
clarity/src/vm/database/sqlite.rs 78.47% <0.00%> (-1.39%) ⬇️
... and 39 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 73df355...43d8fb5. Read the comment docs.

Copy link
Contributor

@gregorycoppola gregorycoppola left a comment

Choose a reason for hiding this comment

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

clarification: Why/when are TransferSTX and StackSTX written to the burnchain? I actually didn't realize that was happening.

Copy link
Contributor

@pavitthrap pavitthrap left a comment

Choose a reason for hiding this comment

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

lgtm

@@ -331,4 +331,33 @@ impl StacksChainState {
.map_err(|e| Error::DBError(db_error::SqliteError(e)))?
.is_some())
}

/// Load up the past N ancestors' index block hashes of a given block, *including* the given
Copy link
Contributor

Choose a reason for hiding this comment

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

past N ancestors'... not including

Copy link
Member Author

Choose a reason for hiding this comment

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

The comment is correct. The given argument index_block_hash is included in the returned list of StacksBlockIds.

@@ -11080,6 +11233,557 @@ pub mod test {
);
}

fn make_transfer_op(
Copy link
Contributor

Choose a reason for hiding this comment

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

Add cfg[test]?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's already in a mod test { ... } block decorated with #[cfg(test)].

peer_config.initial_balances = vec![(addr.to_account_principal(), initial_balance)];
peer_config.epochs = Some(StacksEpoch::unit_test_2_1(0));

let recv_addr =
Copy link
Contributor

Choose a reason for hiding this comment

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

unused

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

peer_config.initial_balances = vec![(addr.to_account_principal(), initial_balance)];
peer_config.epochs = Some(StacksEpoch::unit_test_2_1(0));

let recv_addr =
Copy link
Contributor

Choose a reason for hiding this comment

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

also unused

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

transfer_op
}

#[test]
Copy link
Contributor

Choose a reason for hiding this comment

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

short description of test would be good, ditto for the next test

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

// mine it
next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);

// let's fire off our transfer op that will not land in a sortition pre-2.1
Copy link
Contributor

Choose a reason for hiding this comment

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

this comment could be worded more clearly

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@jcnelson
Copy link
Member Author

clarification: Why/when are TransferSTX and StackSTX written to the burnchain? I actually didn't realize that was happening.

Per SIP-007, these operations exist in order to ensure that Stacks miners will pay a high price to attempt to censor them. If these operations did not exist, then Stacks miners could simply choose to ignore these transactions in the Stacks mempool. But because a Stacks block is only valid if it includes the burnchain transactions in the same burnchain that sortitioned it, the cost of censoring these transactions is the cost of foregoing a Stacks block reward (which is much higher than the cost of the BTC transaction).

The reason we're worried about miner censorship is because there are scenarios where miners can be incentivized to block them. Miners are not only potential stackers, whose income from PoX is influenced to other stackers' stacking, but also are able to take bribes from other stackers in order to keep the stacking minimum down (and thus accrue more BTC). By making censorship very expensive for miners, we make it costly for miners to deviate from honest mining behavior in this matter.

This PR not only makes on-burnchain transfer and stacking operations less brittle and more likely to get confirmed by the network, but also significantly increases the cost of censorship by doing so. Miners would need to forego 6x the block reward to block an on-burnchain stack-stx or transfer-stx operation.

@jcnelson
Copy link
Member Author

jcnelson commented Jun 6, 2022

@kantai @gregorycoppola Need at least one of you to review please :)

@@ -4599,6 +4601,8 @@ impl StacksChainState {
"txid" => %txid,
"burn_block" => %burn_header_hash,
"contract_call_ecode" => %resp.data);
} else {
debug!("Processed StackStx burn op for {} uSTX for {} cycles starting at {} from {} to pay out to {}", stacked_ustx, num_cycles, block_height, &sender, &reward_addr);
Copy link
Member

Choose a reason for hiding this comment

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

All of those fields should be k-v entries:

Suggested change
debug!("Processed StackStx burn op for {} uSTX for {} cycles starting at {} from {} to pay out to {}", stacked_ustx, num_cycles, block_height, &sender, &reward_addr);
debug!("Processed StackStx burnchain op"; "amount_ustx" => stacked_ustx, "num_cycles" => num_cycles, "burn_block_height" => block_height, "sender" => %sender, "reward_addr" => %reward_addr);

Copy link
Member

Choose a reason for hiding this comment

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

Would be useful to add the txid as well

Comment on lines 4675 to 4678
debug!(
"Processed TransferStx {} uSTX from {} to {}",
transfered_ustx, &sender, &recipient
);
Copy link
Member

Choose a reason for hiding this comment

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

Same comment as above: this should be a k-v log, with txid.

Comment on lines 4933 to 4936
if processed_burnchain_txids.contains(&stacking_op.txid) {
continue;
}
all_stacking_burn_ops.push(stacking_op);
Copy link
Member

Choose a reason for hiding this comment

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

The control flow is simpler if the if statement checks for exclusion:

Suggested change
if processed_burnchain_txids.contains(&stacking_op.txid) {
continue;
}
all_stacking_burn_ops.push(stacking_op);
if !processed_burnchain_txids.contains(&stacking_op.txid) {
all_stacking_burn_ops.push(stacking_op);
}

Comment on lines 4881 to 4882
fn get_stacking_and_transfer_burn_ops_v210<'b>(
chainstate_tx: &'b mut ChainstateTx,
Copy link
Member

Choose a reason for hiding this comment

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

I don't think the lifetime generic is necessary here: rust can almost always figure out the lifetimes for references that don't land in the return type.

Comment on lines 4973 to 4979
fn get_stacking_and_transfer_burn_ops<'b>(
chainstate_tx: &'b mut ChainstateTx,
parent_index_hash: &StacksBlockId,
sortdb_conn: &Connection,
burn_tip: &BurnchainHeaderHash,
burn_tip_height: u64,
) -> Result<(Vec<StackStxOp>, Vec<TransferStxOp>), Error> {
Copy link
Member

Choose a reason for hiding this comment

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

I don't think the lifetime generic is necessary here: rust can almost always figure out the lifetimes for references that don't land in the return type.

Copy link
Member

@kantai kantai left a comment

Choose a reason for hiding this comment

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

This all looks great to me -- just superficial comments!

@jcnelson jcnelson merged commit 1c7db10 into next Jun 7, 2022
kantai added a commit that referenced this pull request Aug 1, 2022
…et to the first_block_height before spawning p2p
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants