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: burn op definition and storage for vote-for-agg-key #4376

Merged
merged 36 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5a661fd
fix: rc_consensus_hash in the burn view is the stacks tip consensus h…
jcnelson Feb 6, 2024
ec96e7a
fix: rc_consensus_hash in the burn view is the stacks tip consensus h…
jcnelson Feb 6, 2024
bc716db
fix: a bad slot signature should be a distinct error
jcnelson Feb 6, 2024
99c209c
fix: NACK getchunks and getchunksinv requests with NackErrorCodes::St…
jcnelson Feb 6, 2024
b259ba3
fix: fix comments on rc_consensus_hash
jcnelson Feb 6, 2024
8e04978
fix: force an initial burnchain view load for the p2p network if it h…
jcnelson Feb 6, 2024
ec91821
feat: test neighbors with stale views
jcnelson Feb 6, 2024
adba578
feat: track neighbors with stale views
jcnelson Feb 6, 2024
4eb625a
chore: test that a peer with a stale view will not be acknowledged, b…
jcnelson Feb 6, 2024
e97b244
fix: instantiate burnchain DB earlier in the test framework, since th…
jcnelson Feb 6, 2024
5aaf813
Merge pull request #4345 from stacks-network/hotfix/stackerdb-stale-view
wileyj Feb 6, 2024
d3f20d2
Merge branch 'master' of https://github.com/stacks-network/stacks-blo…
jcnelson Feb 10, 2024
7f7707c
fix: .expect_result() returns Result<Result<..>, ..>, so `try!` to re…
jcnelson Feb 10, 2024
4edafde
Merge pull request #4361 from stacks-network/hotfix/masked-result
jcnelson Feb 10, 2024
f3db98d
use latest commit for workflow generating archive checksums
wileyj Feb 13, 2024
021eaae
Merge pull request #4374 from stacks-network/ci/fix-checksum-step
jcnelson Feb 14, 2024
a3e49c8
feat: burn op definition and storage for vote-for-agg-key
hstove Feb 14, 2024
233a91d
wip: process vote-for-aggregate-key burn ops
hstove Feb 21, 2024
11e5170
feat: integration tests for vote-for-agg-key burn op
hstove Feb 21, 2024
ea58fc7
feat: validate keys used in burn op
hstove Feb 21, 2024
460d80b
feat: test that aggregate key was properly set
hstove Feb 28, 2024
71b29bd
fix: remove unnecessary sortdb schema changes
hstove Feb 28, 2024
b3f0b46
Merge remote-tracking branch 'origin/next' into feat/vote-for-key-burnop
hstove Feb 28, 2024
bad9570
fix: comment language
hstove Feb 28, 2024
4e0b56a
fix: missed sortdb version check
hstove Mar 4, 2024
204b8af
Merge remote-tracking branch 'origin/next' into feat/vote-for-key-burnop
hstove Mar 7, 2024
f815092
fix: merge conflicts
hstove Mar 7, 2024
458abfa
fix: update tests with new stack-stx args
hstove Mar 8, 2024
27087a3
fix: cargo fmt error
hstove Mar 8, 2024
24e43c4
Merge remote-tracking branch 'origin/next' into feat/vote-for-key-burnop
hstove Mar 8, 2024
13251ac
fix: cargo fmt error
hstove Mar 8, 2024
c489245
Merge remote-tracking branch 'origin' into feat/vote-for-key-burnop
hstove Mar 11, 2024
c3d3ab7
Revert "Merge remote-tracking branch 'origin' into feat/vote-for-key-…
hstove Mar 11, 2024
ed4e310
Merge remote-tracking branch 'origin/next' into feat/vote-for-key-burnop
hstove Mar 11, 2024
4093353
Merge branch 'next' into feat/vote-for-key-burnop
hstove Mar 11, 2024
e3a76f7
feat: fix nakamoto integration test
hstove Mar 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
test-name:
- tests::bitcoin_regtest::bitcoind_integration_test
- tests::integrations::integration_test_get_info
- tests::neon_integrations::antientropy_integration_test
- tests::neon_integrations::antientropy_integration_test
- tests::neon_integrations::bad_microblock_pubkey
- tests::neon_integrations::bitcoind_forking_test
- tests::neon_integrations::bitcoind_integration_test
Expand Down Expand Up @@ -70,12 +70,14 @@ jobs:
- tests::neon_integrations::use_latest_tip_integration_test
- tests::neon_integrations::confirm_unparsed_ongoing_ops
- tests::neon_integrations::min_txs
- tests::neon_integrations::vote_for_aggregate_key_burn_op_test
- tests::should_succeed_handling_malformed_and_valid_txs
- tests::nakamoto_integrations::simple_neon_integration
- tests::nakamoto_integrations::mine_multiple_per_tenure_integration
- tests::nakamoto_integrations::block_proposal_api_endpoint
- tests::nakamoto_integrations::miner_writes_proposed_block_to_stackerdb
- tests::nakamoto_integrations::correct_burn_outs
- tests::nakamoto_integrations::vote_for_aggregate_key_burn_op
- tests::signer::stackerdb_dkg
- tests::signer::stackerdb_sign
- tests::signer::stackerdb_block_proposal
Expand All @@ -88,7 +90,7 @@ jobs:
uses: stacks-network/actions/stacks-core/testenv@main
with:
btc-version: "25.0"

## Run test matrix using restored cache of archive file
## - Test will timeout after env.TEST_TIMEOUT minutes
- name: Run Tests
Expand Down
2 changes: 1 addition & 1 deletion stackslib/src/burnchains/bitcoin/bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub fn parse_script<'a>(script: &'a Script) -> Vec<Instruction<'a>> {

impl BitcoinTxInputStructured {
/// Parse a script instruction stream encoding a p2pkh scritpsig into a BitcoinTxInput
fn from_bitcoin_p2pkh_script_sig(
pub fn from_bitcoin_p2pkh_script_sig(
instructions: &Vec<Instruction>,
input_txid: (Txid, u32),
) -> Option<BitcoinTxInputStructured> {
Expand Down
34 changes: 33 additions & 1 deletion stackslib/src/burnchains/burnchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use crate::chainstate::burn::distribution::BurnSamplePoint;
use crate::chainstate::burn::operations::leader_block_commit::MissedBlockCommit;
use crate::chainstate::burn::operations::{
BlockstackOperationType, DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp,
StackStxOp, TransferStxOp,
StackStxOp, TransferStxOp, VoteForAggregateKeyOp,
};
use crate::chainstate::burn::{BlockSnapshot, Opcodes};
use crate::chainstate::coordinator::comm::CoordinatorChannels;
Expand Down Expand Up @@ -133,6 +133,9 @@ impl BurnchainStateTransition {
all_block_commits.insert(op.txid.clone(), op.clone());
block_commits.push(op.clone());
}
BlockstackOperationType::VoteForAggregateKey(_) => {
accepted_ops.push(block_ops[i].clone());
}
};
}

Expand Down Expand Up @@ -851,6 +854,35 @@ impl Burnchain {
None
}
}
x if x == Opcodes::VoteForAggregateKey as u8 => {
let pre_stx_txid = VoteForAggregateKeyOp::get_sender_txid(burn_tx).ok()?;
let pre_stx_tx = match pre_stx_op_map.get(&pre_stx_txid) {
Some(tx_ref) => Some(BlockstackOperationType::PreStx(tx_ref.clone())),
None => burnchain_db.find_burnchain_op(indexer, pre_stx_txid),
};
if let Some(BlockstackOperationType::PreStx(pre_stx)) = pre_stx_tx {
let sender = &pre_stx.output;
match VoteForAggregateKeyOp::from_tx(block_header, burn_tx, sender) {
Ok(op) => Some(BlockstackOperationType::VoteForAggregateKey(op)),
Err(e) => {
warn!(
"Failed to parse vote-for-aggregate-key tx";
"txid" => %burn_tx.txid(),
"data" => %to_hex(&burn_tx.data()),
"error" => ?e,
);
None
}
}
} else {
warn!(
"Failed to find corresponding input to VoteForAggregateKeyOp";
"txid" => %burn_tx.txid().to_string(),
"pre_stx_txid" => %pre_stx_txid.to_string()
);
None
}
}

_ => None,
}
Expand Down
7 changes: 7 additions & 0 deletions stackslib/src/chainstate/burn/db/processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ impl<'a> SortitionHandleTx<'a> {
);
BurnchainError::OpError(e)
}),
BlockstackOperationType::VoteForAggregateKey(ref op) => op.check().map_err(|e| {
warn!(
"REJECTED({}) vote for aggregate key op {} at {},{}: {:?}",
op.block_height, &op.txid, op.block_height, op.vtxindex, &e
);
BurnchainError::OpError(e)
}),
}
}

Expand Down
152 changes: 150 additions & 2 deletions stackslib/src/chainstate/burn/db/sortdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use stacks_common::types::chainstate::{
BlockHeaderHash, BurnchainHeaderHash, PoxId, SortitionId, StacksAddress, StacksBlockId,
TrieHash, VRFSeed,
};
use stacks_common::types::StacksPublicKeyBuffer;
use stacks_common::util::hash::{hex_bytes, to_hex, Hash160, Sha512Trunc256Sum};
use stacks_common::util::secp256k1::{MessageSignature, Secp256k1PublicKey};
use stacks_common::util::vrf::*;
Expand All @@ -58,7 +59,7 @@ use crate::chainstate::burn::operations::leader_block_commit::{
};
use crate::chainstate::burn::operations::{
BlockstackOperationType, DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp,
StackStxOp, TransferStxOp,
StackStxOp, TransferStxOp, VoteForAggregateKeyOp,
};
use crate::chainstate::burn::{
BlockSnapshot, ConsensusHash, ConsensusHashExtensions, Opcodes, OpsHash, SortitionHash,
Expand Down Expand Up @@ -392,6 +393,39 @@ impl FromRow<TransferStxOp> for TransferStxOp {
}
}

impl FromRow<VoteForAggregateKeyOp> for VoteForAggregateKeyOp {
fn from_row<'a>(row: &'a Row) -> Result<VoteForAggregateKeyOp, db_error> {
let txid = Txid::from_column(row, "txid")?;
let vtxindex: u32 = row.get_unwrap("vtxindex");
let block_height = u64::from_column(row, "block_height")?;
let burn_header_hash = BurnchainHeaderHash::from_column(row, "burn_header_hash")?;

let sender = StacksAddress::from_column(row, "sender_addr")?;
let aggregate_key_str: String = row.get_unwrap("aggregate_key");
let aggregate_key: StacksPublicKeyBuffer = serde_json::from_str(&aggregate_key_str)
.expect("CORRUPTION: DB stored bad transition ops");
let round: u32 = row.get_unwrap("round");
let reward_cycle = u64::from_column(row, "reward_cycle")?;
let signer_index: u16 = row.get_unwrap("signer_index");
let signer_key_str: String = row.get_unwrap("signer_key");
let signer_key: StacksPublicKeyBuffer = serde_json::from_str(&signer_key_str)
.expect("CORRUPTION: DB stored bad transition ops");

Ok(VoteForAggregateKeyOp {
txid,
vtxindex,
block_height,
burn_header_hash,
sender,
aggregate_key,
round,
reward_cycle,
signer_index,
signer_key,
})
}
}

impl FromColumn<ASTRules> for ASTRules {
fn from_column<'a>(row: &'a Row, column_name: &str) -> Result<ASTRules, db_error> {
let x: u8 = row.get_unwrap(column_name);
Expand Down Expand Up @@ -650,7 +684,7 @@ const SORTITION_DB_SCHEMA_6: &'static [&'static str] = &[r#"
const SORTITION_DB_SCHEMA_7: &'static [&'static str] = &[r#"
DELETE FROM epochs;"#];

const LAST_SORTITION_DB_INDEX: &'static str = "index_delegate_stx_burn_header_hash";
const LAST_SORTITION_DB_INDEX: &'static str = "index_vote_for_aggregate_key_burn_header_hash";
const SORTITION_DB_SCHEMA_8: &'static [&'static str] = &[
r#"ALTER TABLE snapshots ADD miner_pk_hash TEXT DEFAULT NULL"#,
r#"
Expand All @@ -668,6 +702,23 @@ const SORTITION_DB_SCHEMA_8: &'static [&'static str] = &[
block_hash TEXT NOT NULL,
block_height INTEGER NOT NULL
);"#,
r#"
-- table definition for `vote-for-aggregate-key` burn op
CREATE TABLE vote_for_aggregate_key (
txid TEXT NOT NULL,
vtxindex INTEGER NOT NULL,
block_height INTEGER NOT NULL,
burn_header_hash TEXT NOT NULL,

sender_addr TEXT NOT NULL,
aggregate_key TEXT NOT NULL,
round INTEGER NOT NULL,
reward_cycle INTEGER NOT NULL,
signer_index INTEGER NOT NULL,
signer_key TEXT NOT NULL,

PRIMARY KEY(txid,burn_header_Hash)
);"#,
];

const SORTITION_DB_INDEXES: &'static [&'static str] = &[
hstove marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -690,6 +741,7 @@ const SORTITION_DB_INDEXES: &'static [&'static str] = &[
"CREATE INDEX IF NOT EXISTS index_pox_payouts ON snapshots(pox_payouts);",
"CREATE INDEX IF NOT EXISTS index_burn_header_hash_pox_valid ON snapshots(burn_header_hash,pox_valid);",
"CREATE INDEX IF NOT EXISTS index_delegate_stx_burn_header_hash ON delegate_stx(burn_header_hash);",
"CREATE INDEX IF NOT EXISTS index_vote_for_aggregate_key_burn_header_hash ON vote_for_aggregate_key(burn_header_hash);",
];

pub struct SortitionDB {
Expand Down Expand Up @@ -4287,6 +4339,20 @@ impl SortitionDB {
)
}

/// Get the list of `vote-for-aggregate-key` operations processed in a given burnchain block.
/// This will be the same list in each PoX fork; it's up to the Stacks block-processing logic
/// to reject them.
pub fn get_vote_for_aggregate_key_ops(
conn: &Connection,
burn_header_hash: &BurnchainHeaderHash,
) -> Result<Vec<VoteForAggregateKeyOp>, db_error> {
query_rows(
conn,
"SELECT * FROM vote_for_aggregate_key WHERE burn_header_hash = ? ORDER BY vtxindex",
&[burn_header_hash],
)
}

/// Get the list of Transfer-STX operations processed in a given burnchain block.
/// This will be the same list in each PoX fork; it's up to the Stacks block-processing logic
/// to reject them.
Expand Down Expand Up @@ -5258,6 +5324,13 @@ impl<'a> SortitionHandleTx<'a> {
);
self.insert_delegate_stx(op)
}
BlockstackOperationType::VoteForAggregateKey(ref op) => {
info!(
"ACCEPTED({}) vote for aggregate key {} at {},{}",
op.block_height, &op.txid, op.block_height, op.vtxindex
);
self.insert_vote_for_aggregate_key(op)
}
}
}

Expand Down Expand Up @@ -5325,6 +5398,29 @@ impl<'a> SortitionHandleTx<'a> {
Ok(())
}

/// Insert a vote-for-aggregate-key op
fn insert_vote_for_aggregate_key(
&mut self,
op: &VoteForAggregateKeyOp,
) -> Result<(), db_error> {
let args: &[&dyn ToSql] = &[
&op.txid,
&op.vtxindex,
&u64_to_sql(op.block_height)?,
&op.burn_header_hash,
&op.sender.to_string(),
&serde_json::to_string(&op.aggregate_key).unwrap(),
&op.round,
&u64_to_sql(op.reward_cycle)?,
&op.signer_index,
&serde_json::to_string(&op.signer_key).unwrap(),
];

self.execute("REPLACE INTO vote_for_aggregate_key (txid, vtxindex, block_height, burn_header_hash, sender_addr, aggregate_key, round, reward_cycle, signer_index, signer_key) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)", args)?;

Ok(())
}

/// Insert a transfer-stx op
fn insert_transfer_stx(&mut self, op: &TransferStxOp) -> Result<(), db_error> {
let args: &[&dyn ToSql] = &[
Expand Down Expand Up @@ -9966,6 +10062,11 @@ pub mod tests {
)
.unwrap();
let mut db = SortitionDB::connect_test(block_height, &first_burn_hash).unwrap();
let vote_pubkey = StacksPublicKey::from_hex(
"02d8015134d9db8178ac93acbc43170a2f20febba5087a5b0437058765ad5133d0",
)
.unwrap();
let vote_key: StacksPublicKeyBuffer = vote_pubkey.to_bytes_compressed().as_slice().into();

let good_ops = vec![
BlockstackOperationType::TransferStx(TransferStxOp {
Expand Down Expand Up @@ -10008,6 +10109,19 @@ pub mod tests {
block_height,
burn_header_hash: first_burn_hash.clone(),
}),
BlockstackOperationType::VoteForAggregateKey(VoteForAggregateKeyOp {
sender: StacksAddress::new(6, Hash160([6u8; 20])),
aggregate_key: vote_key,
signer_key: vote_key,
round: 1,
reward_cycle: 2,
signer_index: 3,

txid: Txid([0x05; 32]),
vtxindex: 4,
block_height,
burn_header_hash: first_burn_hash.clone(),
}),
];

let mut tx = db.tx_begin_at_tip();
Expand Down Expand Up @@ -10038,6 +10152,13 @@ pub mod tests {
good_ops[2]
);

let ops = SortitionDB::get_vote_for_aggregate_key_ops(db.conn(), &first_burn_hash).unwrap();
assert_eq!(ops.len(), 1);
assert_eq!(
BlockstackOperationType::VoteForAggregateKey(ops[0].clone()),
good_ops[3]
);

// if the same ops get mined in a different burnchain block, they will still be available
let good_ops_2 = vec![
BlockstackOperationType::TransferStx(TransferStxOp {
Expand Down Expand Up @@ -10077,6 +10198,19 @@ pub mod tests {
block_height,
burn_header_hash: fork_burn_hash.clone(),
}),
BlockstackOperationType::VoteForAggregateKey(VoteForAggregateKeyOp {
sender: StacksAddress::new(6, Hash160([6u8; 20])),
aggregate_key: StacksPublicKeyBuffer([0x01; 33]),
signer_key: StacksPublicKeyBuffer([0x02; 33]),
round: 1,
reward_cycle: 2,
signer_index: 3,

txid: Txid([0x05; 32]),
vtxindex: 4,
block_height,
burn_header_hash: fork_burn_hash.clone(),
}),
];

let mut tx = db.tx_begin_at_tip();
Expand Down Expand Up @@ -10108,6 +10242,13 @@ pub mod tests {
good_ops[2]
);

let ops = SortitionDB::get_vote_for_aggregate_key_ops(db.conn(), &first_burn_hash).unwrap();
assert_eq!(ops.len(), 1);
assert_eq!(
BlockstackOperationType::VoteForAggregateKey(ops[0].clone()),
good_ops[3]
);

// and so are the new ones
let ops = SortitionDB::get_transfer_stx_ops(db.conn(), &fork_burn_hash).unwrap();
assert_eq!(ops.len(), 1);
Expand All @@ -10129,5 +10270,12 @@ pub mod tests {
BlockstackOperationType::DelegateStx(ops[0].clone()),
good_ops_2[2]
);

let ops = SortitionDB::get_vote_for_aggregate_key_ops(db.conn(), &fork_burn_hash).unwrap();
assert_eq!(ops.len(), 1);
assert_eq!(
BlockstackOperationType::VoteForAggregateKey(ops[0].clone()),
good_ops_2[3]
);
}
}
4 changes: 4 additions & 0 deletions stackslib/src/chainstate/burn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub enum Opcodes {
PreStx = 'p' as u8,
TransferStx = '$' as u8,
DelegateStx = '#' as u8,
VoteForAggregateKey = 'v' as u8,
}

// a burnchain block snapshot
Expand Down Expand Up @@ -191,6 +192,7 @@ impl Opcodes {
const HTTP_PEG_IN: &'static str = "peg_in";
const HTTP_PEG_OUT_REQUEST: &'static str = "peg_out_request";
const HTTP_PEG_OUT_FULFILL: &'static str = "peg_out_fulfill";
const HTTP_VOTE_FOR_AGGREGATE_KEY: &'static str = "vote_for_aggregate_key";

pub fn to_http_str(&self) -> &'static str {
match self {
Expand All @@ -200,6 +202,7 @@ impl Opcodes {
Opcodes::PreStx => Self::HTTP_PRE_STX,
Opcodes::TransferStx => Self::HTTP_TRANSFER_STX,
Opcodes::DelegateStx => Self::HTTP_DELEGATE_STX,
Opcodes::VoteForAggregateKey => Self::HTTP_VOTE_FOR_AGGREGATE_KEY,
}
}

Expand All @@ -211,6 +214,7 @@ impl Opcodes {
Self::HTTP_PRE_STX => Opcodes::PreStx,
Self::HTTP_TRANSFER_STX => Opcodes::TransferStx,
Self::HTTP_DELEGATE_STX => Opcodes::DelegateStx,
Self::HTTP_VOTE_FOR_AGGREGATE_KEY => Opcodes::VoteForAggregateKey,
_ => return None,
};

Expand Down