Skip to content

Commit

Permalink
Merge pull request stacks-network#3974 from jbencin/feat/add-tenure-c…
Browse files Browse the repository at this point in the history
…hange

Feat: Add `TenureChange` transaction type
  • Loading branch information
jbencin authored Nov 21, 2023
2 parents 243e9c1 + 252b77f commit 2980e17
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 82 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions stackslib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ stacks-common = { path = "../stacks-common" }
pox-locking = { path = "../pox-locking" }
libstackerdb = { path = "../libstackerdb" }
siphasher = "0.3.7"
wsts = "4.0.0"
p256k1 = "5.5.0"

[target.'cfg(unix)'.dependencies]
nix = "0.23"
Expand Down
6 changes: 3 additions & 3 deletions stackslib/src/chainstate/nakamoto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ use super::stacks::db::{
use super::stacks::events::StacksTransactionReceipt;
use super::stacks::{
Error as ChainstateError, StacksBlock, StacksBlockHeader, StacksMicroblock, StacksTransaction,
TenureChangeError, TenureChangePayload, TransactionPayload,
TenureChangeError, TenureChangePayload, ThresholdSignature, TransactionPayload,
};
use crate::burnchains::{PoxConstants, Txid};
use crate::chainstate::burn::db::sortdb::SortitionDB;
Expand Down Expand Up @@ -475,14 +475,14 @@ impl NakamotoBlock {
}

let validate = |tc: &StacksTransaction| -> Result<(), TenureChangeError> {
if let TransactionPayload::TenureChange(tc) = &tc.payload {
if let TransactionPayload::TenureChange(tc, signature) = &tc.payload {
if tc.previous_tenure_end != self.header.parent_block_id {
return Err(TenureChangeError::PreviousTenureInvalid);
}

// TODO: check number of blocks in previous tenure
// TODO: check tenure change cause
tc.validate()
tc.validate(signature)
} else {
// placeholder error
Err(TenureChangeError::NotNakamoto)
Expand Down
63 changes: 35 additions & 28 deletions stackslib/src/chainstate/nakamoto/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ use crate::chainstate::stacks::db::{
StacksHeaderInfo,
};
use crate::chainstate::stacks::{
CoinbasePayload, SchnorrThresholdSignature, StacksBlock, StacksBlockHeader, StacksTransaction,
StacksTransactionSigner, TenureChangeCause, TenureChangePayload, TokenTransferMemo,
CoinbasePayload, StacksBlock, StacksBlockHeader, StacksTransaction, StacksTransactionSigner,
TenureChangeCause, TenureChangePayload, ThresholdSignature, TokenTransferMemo,
TransactionAnchorMode, TransactionAuth, TransactionPayload, TransactionVersion,
};
use crate::core;
Expand Down Expand Up @@ -149,24 +149,28 @@ pub fn test_nakamoto_first_tenure_block_syntactic_validation() {
stacker_signature: MessageSignature::empty(),
};

let tenure_change_payload = TransactionPayload::TenureChange(TenureChangePayload {
previous_tenure_end: header.parent_block_id.clone(),
previous_tenure_blocks: 1,
cause: TenureChangeCause::BlockFound,
pubkey_hash: Hash160([0x02; 20]),
signature: SchnorrThresholdSignature {},
signers: vec![],
});

let invalid_tenure_change_payload = TransactionPayload::TenureChange(TenureChangePayload {
// bad parent block ID
previous_tenure_end: StacksBlockId([0x00; 32]),
previous_tenure_blocks: 1,
cause: TenureChangeCause::BlockFound,
pubkey_hash: Hash160([0x02; 20]),
signature: SchnorrThresholdSignature {},
signers: vec![],
});
let tenure_change_payload = TransactionPayload::TenureChange(
TenureChangePayload {
previous_tenure_end: header.parent_block_id.clone(),
previous_tenure_blocks: 1,
cause: TenureChangeCause::BlockFound,
pubkey_hash: Hash160([0x02; 20]),
signers: vec![],
},
ThresholdSignature::mock(),
);

let invalid_tenure_change_payload = TransactionPayload::TenureChange(
TenureChangePayload {
// bad parent block ID
previous_tenure_end: StacksBlockId([0x00; 32]),
previous_tenure_blocks: 1,
cause: TenureChangeCause::BlockFound,
pubkey_hash: Hash160([0x02; 20]),
signers: vec![],
},
ThresholdSignature::mock(),
);

let proof_bytes = hex_bytes("9275df67a68c8745c0ff97b48201ee6db447f7c93b23ae24cdc2400f52fdb08a1a6ac7ec71bf9c9c76e96ee4675ebff60625af28718501047bfd87b810c2d2139b73c23bd69de66360953a642c2a330a").unwrap();
let proof = VRFProof::from_bytes(&proof_bytes[..].to_vec()).unwrap();
Expand Down Expand Up @@ -458,14 +462,17 @@ pub fn test_load_store_update_nakamoto_blocks() {
runtime: 104,
};

let tenure_change_payload = TransactionPayload::TenureChange(TenureChangePayload {
previous_tenure_end: epoch2_parent_block_id.clone(),
previous_tenure_blocks: 1,
cause: TenureChangeCause::BlockFound,
pubkey_hash: Hash160([0x02; 20]),
signature: SchnorrThresholdSignature {},
signers: vec![],
});
let tenure_change_payload = TransactionPayload::TenureChange(
TenureChangePayload {
previous_tenure_end: epoch2_parent_block_id.clone(),
previous_tenure_blocks: 1,
cause: TenureChangeCause::BlockFound,
pubkey_hash: Hash160([0x02; 20]),
signers: vec![],
},
ThresholdSignature::mock(),
);

let mut tenure_change_tx = StacksTransaction::new(
TransactionVersion::Testnet,
TransactionAuth::from_p2pkh(&private_key).unwrap(),
Expand Down
3 changes: 1 addition & 2 deletions stackslib/src/chainstate/nakamoto/tests/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl TestMiner {
TransactionVersion::Testnet,
// TODO: this needs to be a schnorr signature
self.as_transaction_auth().unwrap(),
TransactionPayload::TenureChange(tenure_change),
TransactionPayload::TenureChange(tenure_change, ThresholdSignature::mock()),
);
tx_tenure_change.chain_id = 0x80000000;
tx_tenure_change.anchor_mode = TransactionAnchorMode::OnChainOnly;
Expand Down Expand Up @@ -374,7 +374,6 @@ impl TestStacksNode {
previous_tenure_blocks,
cause: tenure_change_cause,
pubkey_hash: miner.nakamoto_miner_hash160(),
signature: SchnorrThresholdSignature::empty(),
signers: vec![],
};

Expand Down
3 changes: 1 addition & 2 deletions stackslib/src/chainstate/stacks/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1786,13 +1786,12 @@ mod test {
previous_tenure_blocks: 0,
cause: TenureChangeCause::BlockFound,
pubkey_hash: Hash160([0x00; 20]),
signature: SchnorrThresholdSignature::empty(),
signers: vec![],
};
let tx_tenure_change = StacksTransaction::new(
TransactionVersion::Testnet,
origin_auth.clone(),
TransactionPayload::TenureChange(tenure_change_payload),
TransactionPayload::TenureChange(tenure_change_payload, ThresholdSignature::mock()),
);

let dup_txs = vec![
Expand Down
2 changes: 1 addition & 1 deletion stackslib/src/chainstate/stacks/db/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1345,7 +1345,7 @@ impl StacksChainState {
let receipt = StacksTransactionReceipt::from_coinbase(tx.clone());
Ok(receipt)
}
TransactionPayload::TenureChange(ref _payload) => {
TransactionPayload::TenureChange(ref payload, ref signature) => {
// post-conditions are not allowed for this variant, since they're non-sensical.
// Their presence in this variant makes the transaction invalid.
if tx.post_conditions.len() > 0 {
Expand Down
41 changes: 19 additions & 22 deletions stackslib/src/chainstate/stacks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,9 +625,9 @@ impl_byte_array_serde!(TokenTransferMemo);
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum TenureChangeCause {
/// A valid winning block-commit, end current tenure
/// A valid winning block-commit
BlockFound = 0,
/// No winning block-commits, extend current tenure
/// No winning block-commits
NoBlockFound = 1,
/// A null miner won the block-commit
NullMiner = 2,
Expand All @@ -646,29 +646,23 @@ impl TryFrom<u8> for TenureChangeCause {
}
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SchnorrThresholdSignature {
//pub point: wsts::Point,
//pub scalar: wsts::Scalar,
}

impl SchnorrThresholdSignature {
pub fn empty() -> SchnorrThresholdSignature {
SchnorrThresholdSignature {}
}
}

/// Reasons why a `TenureChange` transaction can be bad
pub enum TenureChangeError {
SignatureInvalid,
/// Not signed by required threshold (>70%)
SignatureThresholdNotReached,
SignatureInvalid,
/// `previous_tenure_end` does not match parent block
PreviousTenureInvalid,
/// Block is not a Nakamoto block
NotNakamoto,
}

/// Schnorr threshold signature using types from `wsts`
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ThresholdSignature {
R: wsts::Point,
z: wsts::Scalar,
}

/// A transaction from Stackers to signal new mining tenure
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TenureChangePayload {
Expand All @@ -680,8 +674,6 @@ pub struct TenureChangePayload {
pub cause: TenureChangeCause,
/// The ECDSA public key hash of the current tenure
pub pubkey_hash: Hash160,
/// A Schnorr signature from at least 70% of the Stackers
pub signature: SchnorrThresholdSignature,
/// A bitmap of which Stackers signed
pub signers: Vec<u8>,
}
Expand All @@ -694,7 +686,8 @@ pub enum TransactionPayload {
// the previous epoch leader sent two microblocks with the same sequence, and this is proof
PoisonMicroblock(StacksMicroblockHeader, StacksMicroblockHeader),
Coinbase(CoinbasePayload, Option<PrincipalData>, Option<VRFProof>),
TenureChange(TenureChangePayload),
/// Must contain a Schnorr threshold signature from at least 70% of the Stackers
TenureChange(TenureChangePayload, ThresholdSignature),
}

impl TransactionPayload {
Expand Down Expand Up @@ -1320,6 +1313,10 @@ pub mod test {
Some(proof.clone()),
),
TransactionPayload::PoisonMicroblock(mblock_header_1, mblock_header_2),
TransactionPayload::TenureChange(
TenureChangePayload::mock(),
ThresholdSignature::mock(),
),
];

// create all kinds of transactions
Expand All @@ -1346,8 +1343,8 @@ pub mod test {

let tx = StacksTransaction {
version: (*version).clone(),
chain_id: chain_id,
auth: auth,
chain_id,
auth,
anchor_mode: (*anchor_mode).clone(),
post_condition_mode: (*post_condition_mode).clone(),
post_conditions: tx_post_condition.clone(),
Expand Down Expand Up @@ -1436,7 +1433,7 @@ pub mod test {
};

StacksBlock {
header: header,
header,
txs: txs_anchored,
}
}
Expand Down
Loading

0 comments on commit 2980e17

Please sign in to comment.