Skip to content

Commit

Permalink
wip: modify nakamoto block header to use Vec<MessageSignature>
Browse files Browse the repository at this point in the history
  • Loading branch information
hstove committed May 13, 2024
1 parent 75a28ed commit 6abb574
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 86 deletions.
5 changes: 2 additions & 3 deletions stacks-signer/src/client/stacks_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,6 @@ mod tests {
use blockstack_lib::chainstate::stacks::boot::{
NakamotoSignerEntry, PoxStartCycleInfo, RewardSet,
};
use blockstack_lib::chainstate::stacks::ThresholdSignature;
use clarity::vm::types::{
ListData, ListTypeData, ResponseData, SequenceData, TupleData, TupleTypeSignature,
TypeSignature,
Expand Down Expand Up @@ -1238,7 +1237,7 @@ mod tests {
tx_merkle_root: Sha512Trunc256Sum([0x06; 32]),
state_index_root: TrieHash([0x07; 32]),
miner_signature: MessageSignature::empty(),
signer_signature: ThresholdSignature::empty(),
signer_signatures: Vec::<MessageSignature>::new(),
signer_bitvec: BitVec::zeros(1).unwrap(),
};
let block = NakamotoBlock {
Expand All @@ -1262,7 +1261,7 @@ mod tests {
tx_merkle_root: Sha512Trunc256Sum([0x06; 32]),
state_index_root: TrieHash([0x07; 32]),
miner_signature: MessageSignature::empty(),
signer_signature: ThresholdSignature::empty(),
signer_signatures: Vec::<MessageSignature>::new(),
signer_bitvec: BitVec::zeros(1).unwrap(),
};
let block = NakamotoBlock {
Expand Down
8 changes: 4 additions & 4 deletions stacks-signer/src/v1/signerdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ mod tests {
use blockstack_lib::chainstate::nakamoto::{
NakamotoBlock, NakamotoBlockHeader, NakamotoBlockVote,
};
use blockstack_lib::chainstate::stacks::ThresholdSignature;
use blockstack_lib::chainstate::stacks::boot::SIGNERS_MAX_LIST_SIZE;
use libsigner::BlockProposalSigners;
use stacks_common::bitvec::BitVec;
use stacks_common::types::chainstate::{ConsensusHash, StacksBlockId, TrieHash};
Expand All @@ -209,7 +209,7 @@ mod tests {
tx_merkle_root: Sha512Trunc256Sum([0x06; 32]),
state_index_root: TrieHash([0x07; 32]),
miner_signature: MessageSignature::empty(),
signer_signature: ThresholdSignature::empty(),
signer_signatures: Vec::<MessageSignature>::with_capacity(SIGNERS_MAX_LIST_SIZE),
signer_bitvec: BitVec::zeros(1).unwrap(),
};
let block = NakamotoBlock {
Expand Down Expand Up @@ -296,8 +296,8 @@ mod tests {
let old_block_proposal = block_proposal;

let (mut block_info, block_proposal) = create_block_override(|b| {
b.block.header.signer_signature =
old_block_proposal.block.header.signer_signature.clone();
b.block.header.signer_signatures =
old_block_proposal.block.header.signer_signatures.clone();
});
assert_eq!(
block_info.signer_signature_hash(),
Expand Down
77 changes: 45 additions & 32 deletions stackslib/src/chainstate/nakamoto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use lazy_static::{__Deref, lazy_static};
use rusqlite::blob::Blob;
use rusqlite::types::{FromSql, FromSqlError};
use rusqlite::{params, Connection, OpenFlags, OptionalExtension, ToSql, NO_PARAMS};
use serde_json::Value as SerdeValue;
use sha2::{Digest as Sha2Digest, Sha512_256};
use stacks_common::bitvec::BitVec;
use stacks_common::codec::{
Expand Down Expand Up @@ -178,8 +179,8 @@ lazy_static! {
state_index_root TEXT NOT NULL,
-- miner's signature over the block
miner_signature TEXT NOT NULL,
-- signers' signature over the block
signer_signature TEXT NOT NULL,
-- signers' signatures over the block
signer_signatures BLOB NOT NULL,
-- bitvec capturing stacker participation in signature
signer_bitvec TEXT NOT NULL,
-- The following fields are not part of either the StacksHeaderInfo struct
Expand Down Expand Up @@ -305,8 +306,10 @@ pub struct NakamotoBlockHeader {
pub state_index_root: TrieHash,
/// Recoverable ECDSA signature from the tenure's miner.
pub miner_signature: MessageSignature,
/// Schnorr signature over the block header from the signer set active during the tenure.
pub signer_signature: ThresholdSignature,
/// The set of recoverable ECDSA signatures over
/// the block header from the signer set active during the tenure.
/// (ordered by reward set order)
pub signer_signatures: Vec<MessageSignature>,
/// A bitvec which represents the signers that participated in this block signature.
/// The maximum number of entries in the bitvec is 4000.
pub signer_bitvec: BitVec<4000>,
Expand All @@ -325,9 +328,20 @@ impl FromRow<NakamotoBlockHeader> for NakamotoBlockHeader {
let parent_block_id = row.get("parent_block_id")?;
let tx_merkle_root = row.get("tx_merkle_root")?;
let state_index_root = row.get("state_index_root")?;
let signer_signature = row.get("signer_signature")?;
// let signer_signature = row.get("signer_signature")?;
let miner_signature = row.get("miner_signature")?;
let signer_bitvec = row.get("signer_bitvec")?;
let signer_signatures: SerdeValue = row.get_unwrap("signer_signatures");
let signer_signatures = signer_signatures
.as_array()
.map(|values| {
values
.iter()
.cloned()
.map(serde_json::from_value::<MessageSignature>)
.collect::<Result<Vec<_>, serde_json::Error>>()
})
.ok_or_else(|| DBError::Corruption)??;

Ok(NakamotoBlockHeader {
version,
Expand All @@ -337,7 +351,7 @@ impl FromRow<NakamotoBlockHeader> for NakamotoBlockHeader {
parent_block_id,
tx_merkle_root,
state_index_root,
signer_signature,
signer_signatures,
miner_signature,
signer_bitvec,
})
Expand Down Expand Up @@ -389,7 +403,7 @@ impl StacksMessageCodec for NakamotoBlockHeader {
write_next(fd, &self.tx_merkle_root)?;
write_next(fd, &self.state_index_root)?;
write_next(fd, &self.miner_signature)?;
write_next(fd, &self.signer_signature)?;
write_next(fd, &self.signer_signatures)?;
write_next(fd, &self.signer_bitvec)?;

Ok(())
Expand All @@ -405,7 +419,7 @@ impl StacksMessageCodec for NakamotoBlockHeader {
tx_merkle_root: read_next(fd)?,
state_index_root: read_next(fd)?,
miner_signature: read_next(fd)?,
signer_signature: read_next(fd)?,
signer_signatures: read_next(fd)?,
signer_bitvec: read_next(fd)?,
})
}
Expand Down Expand Up @@ -489,11 +503,17 @@ impl NakamotoBlockHeader {
Ok(())
}

/// Verify the block header against an aggregate public key
pub fn verify_signer(&self, signer_aggregate: &Point) -> bool {
let schnorr_signature = &self.signer_signature.0;
let message = self.signer_signature_hash().0;
schnorr_signature.verify(signer_aggregate, &message)
/// Verify the block header against the list of signer signatures
///
/// TODO: ingest the list of signer pubkeys
///
/// TODO: validate against:
/// - Any invalid signatures
/// - Any duplicate signatures
/// - At least the minimum number of signatures
pub fn verify_signer(&self, _signer_aggregate: &Point) -> bool {
let _message = self.signer_signature_hash().0;
true
}

/// Make an "empty" header whose block data needs to be filled in.
Expand All @@ -514,7 +534,7 @@ impl NakamotoBlockHeader {
tx_merkle_root: Sha512Trunc256Sum([0u8; 32]),
state_index_root: TrieHash([0u8; 32]),
miner_signature: MessageSignature::empty(),
signer_signature: ThresholdSignature::empty(),
signer_signatures: Vec::<MessageSignature>::with_capacity(SIGNERS_MAX_LIST_SIZE),
signer_bitvec: BitVec::ones(bitvec_len)
.expect("BUG: bitvec of length-1 failed to construct"),
}
Expand All @@ -531,7 +551,7 @@ impl NakamotoBlockHeader {
tx_merkle_root: Sha512Trunc256Sum([0u8; 32]),
state_index_root: TrieHash([0u8; 32]),
miner_signature: MessageSignature::empty(),
signer_signature: ThresholdSignature::empty(),
signer_signatures: Vec::<MessageSignature>::with_capacity(SIGNERS_MAX_LIST_SIZE),
signer_bitvec: BitVec::zeros(1).expect("BUG: bitvec of length-1 failed to construct"),
}
}
Expand All @@ -547,7 +567,7 @@ impl NakamotoBlockHeader {
tx_merkle_root: Sha512Trunc256Sum([0u8; 32]),
state_index_root: TrieHash([0u8; 32]),
miner_signature: MessageSignature::empty(),
signer_signature: ThresholdSignature::empty(),
signer_signatures: Vec::<MessageSignature>::with_capacity(SIGNERS_MAX_LIST_SIZE),
signer_bitvec: BitVec::zeros(1).expect("BUG: bitvec of length-1 failed to construct"),
}
}
Expand Down Expand Up @@ -1690,13 +1710,15 @@ impl NakamotoChainState {
/// Does nothing if:
/// * we already have the block
/// Returns true if we stored the block; false if not.
///
/// TODO: ingest the list of signer keys (instead of aggregate key)
pub fn accept_block(
config: &ChainstateConfig,
block: NakamotoBlock,
db_handle: &mut SortitionHandleConn,
staging_db_tx: &NakamotoStagingBlocksTx,
headers_conn: &Connection,
aggregate_public_key: &Point,
_aggregate_public_key: &Point,
) -> Result<bool, ChainstateError> {
test_debug!("Consider Nakamoto block {}", &block.block_id());
// do nothing if we already have this block
Expand Down Expand Up @@ -1743,19 +1765,7 @@ impl NakamotoChainState {
return Ok(false);
};

let schnorr_signature = &block.header.signer_signature.0;
if !db_handle.expects_signer_signature(
&block.header.consensus_hash,
schnorr_signature,
&block.header.signer_signature_hash().0,
aggregate_public_key,
)? {
let msg = format!(
"Received block, but the signer signature does not match the active stacking cycle"
);
warn!("{}", msg; "aggregate_key" => %aggregate_public_key);
return Err(ChainstateError::InvalidStacksBlock(msg));
}
// TODO: verify signer signatures

// if we pass all the tests, then along the way, we will have verified (in
// Self::validate_nakamoto_block_burnchain) that the consensus hash of this block is on the
Expand Down Expand Up @@ -2236,6 +2246,9 @@ impl NakamotoChainState {

let vrf_proof_bytes = vrf_proof.map(|proof| proof.to_hex());

let signer_signatures = serde_json::to_string(&header.signer_signatures)
.expect("Unable to serialize signer signatures");

let args: &[&dyn ToSql] = &[
&u64_to_sql(*stacks_block_height)?,
&index_root,
Expand All @@ -2249,7 +2262,7 @@ impl NakamotoChainState {
&u64_to_sql(header.chain_length)?,
&u64_to_sql(header.burn_spent)?,
&header.miner_signature,
&header.signer_signature,
&signer_signatures,
&header.tx_merkle_root,
&header.state_index_root,
&block_hash,
Expand All @@ -2271,7 +2284,7 @@ impl NakamotoChainState {
header_type,
version, chain_length, burn_spent,
miner_signature, signer_signature, tx_merkle_root, state_index_root,
miner_signature, signer_signatures, tx_merkle_root, state_index_root,
block_hash,
index_block_hash,
Expand Down
29 changes: 16 additions & 13 deletions stackslib/src/chainstate/nakamoto/test_signers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use std::collections::{HashSet, VecDeque};
use std::path::{Path, PathBuf};
use std::{fs, io};

use clarity::util::secp256k1::{MessageSignature, Secp256k1PrivateKey};
use clarity::vm::clarity::ClarityConnection;
use clarity::vm::costs::{ExecutionCost, LimitedCostTracker};
use clarity::vm::types::*;
Expand Down Expand Up @@ -77,6 +78,8 @@ pub struct TestSigners {
pub party_key_ids: Vec<Vec<u32>>,
/// The cycle for which the signers are valid
pub cycle: u64,
/// The signer's private keys
pub signer_keys: Vec<Secp256k1PrivateKey>,
}

impl Default for TestSigners {
Expand Down Expand Up @@ -104,6 +107,11 @@ impl Default for TestSigners {
})
.collect();

let mut signer_keys = Vec::<Secp256k1PrivateKey>::new();
for _ in 0..num_keys {
signer_keys.push(Secp256k1PrivateKey::default());
}

// Generate an aggregate public key
let poly_commitments = match wsts::v2::test_helpers::dkg(&mut signer_parties, &mut rng) {
Ok(poly_commitments) => poly_commitments,
Expand All @@ -124,37 +132,32 @@ impl Default for TestSigners {
threshold,
party_key_ids,
cycle: 0,
signer_keys,
}
}
}

impl TestSigners {
// TODO: sign using vec of signatures
pub fn sign_nakamoto_block(&mut self, block: &mut NakamotoBlock, cycle: u64) {
// Update the aggregate public key if the cycle has changed
if self.cycle != cycle {
self.generate_aggregate_key(cycle);
}

let mut rng = rand_core::OsRng;
let msg = block.header.signer_signature_hash().0;
let (nonces, sig_shares, key_ids) =
wsts::v2::test_helpers::sign(msg.as_slice(), &mut self.signer_parties, &mut rng);

let mut sig_aggregator = wsts::v2::Aggregator::new(self.num_keys, self.threshold);
sig_aggregator
.init(&self.poly_commitments)
.expect("aggregator init failed");
let signature = sig_aggregator
.sign(msg.as_slice(), &nonces, &sig_shares, &key_ids)
.expect("aggregator sig failed");
let signer_signatures = self
.signer_keys
.iter()
.map(|key| key.sign(&msg).unwrap())
.collect::<Vec<MessageSignature>>();

test_debug!(
"Signed Nakamoto block {} with {} (rc {})",
block.block_id(),
&self.aggregate_public_key,
cycle
);
block.header.signer_signature = ThresholdSignature(signature);
block.header.signer_signatures = signer_signatures;
}

// Generate and assign a new aggregate public key
Expand Down
Loading

0 comments on commit 6abb574

Please sign in to comment.