Skip to content

Commit

Permalink
Merge pull request #4376 from stacks-network/feat/vote-for-key-burnop
Browse files Browse the repository at this point in the history
feat: burn op definition and storage for vote-for-agg-key
  • Loading branch information
hstove committed Mar 13, 2024
2 parents 87c0c00 + e3a76f7 commit 9058e22
Show file tree
Hide file tree
Showing 18 changed files with 1,601 additions and 33 deletions.
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] = &[
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

0 comments on commit 9058e22

Please sign in to comment.