Make filtering of signer transactions global and use in miner

Signed-off-by: Jacinta Ferrant <jacinta@trustmachines.co>
This commit is contained in:
Jacinta Ferrant
2024-02-27 13:12:20 -05:00
parent 533bf19f7c
commit f6aa7e3514
10 changed files with 1101 additions and 607 deletions

View File

@@ -49,7 +49,7 @@ use stacks::chainstate::nakamoto::{
NakamotoBlock, NakamotoBlockHeader, NakamotoChainState, SetupBlockResult,
};
use stacks::chainstate::stacks::address::PoxAddress;
use stacks::chainstate::stacks::boot::SIGNERS_VOTING_NAME;
use stacks::chainstate::stacks::boot::{SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME};
use stacks::chainstate::stacks::db::{ChainStateBootData, ClarityTx, StacksChainState};
use stacks::chainstate::stacks::miner::{
BlockBuilder, BlockBuilderSettings, BlockLimitFunction, MinerStatus, TransactionResult,
@@ -934,7 +934,7 @@ impl MockamotoNode {
let vote_payload = TransactionPayload::new_contract_call(
boot_code_addr(false),
SIGNERS_VOTING_NAME,
"vote-for-aggregate-public-key",
SIGNERS_VOTING_FUNCTION_NAME,
vec![
ClarityValue::UInt(0),
aggregate_public_key_val,

View File

@@ -331,48 +331,43 @@ impl BlockMinerThread {
})
.collect();
// There may be more than signer messages, but odds are there is only one transacton per signer
let mut transactions_to_include = Vec::with_capacity(signer_messages.len());
if signer_messages.is_empty() {
return Ok(vec![]);
}
// Get all nonces for the signers from clarity DB to use to validate transactions
let account_nonces = chainstate
.with_read_only_clarity_tx(&sortdb.index_conn(), &self.parent_tenure_id, |clarity_tx| {
clarity_tx.with_clarity_db_readonly(|clarity_db| {
addresses
.iter()
.map(|address| {
(
address.clone(),
clarity_db
.get_account_nonce(&address.clone().into())
.unwrap_or(0),
)
})
.collect::<HashMap<StacksAddress, u64>>()
})
})
.unwrap_or_default();
let mut filtered_transactions: HashMap<StacksAddress, StacksTransaction> = HashMap::new();
for (_slot, signer_message) in signer_messages {
match signer_message {
SignerMessage::Transactions(transactions) => {
for transaction in transactions {
let address = transaction.origin_address();
let nonce = transaction.get_origin_nonce();
if !addresses.contains(&address) {
test_debug!("Miner: ignoring transaction ({:?}) with nonce {nonce} from address {address}", transaction.txid());
continue;
}
let cur_nonce = chainstate
.with_read_only_clarity_tx(
&sortdb.index_conn(),
&self.parent_tenure_id,
|clarity_tx| {
clarity_tx.with_clarity_db_readonly(|clarity_db| {
clarity_db.get_account_nonce(&address.into()).unwrap_or(0)
})
},
)
.unwrap_or(0);
if cur_nonce > nonce {
test_debug!("Miner: ignoring transaction ({:?}) with nonce {nonce} from address {address}", transaction.txid());
continue;
}
debug!("Miner: including signer transaction.";
"nonce" => {nonce},
"origin_address" => %address,
"txid" => %transaction.txid()
);
// TODO : filter out transactions that are not valid votes. Do not include transactions with invalid/duplicate nonces for the same address.
transactions_to_include.push(transaction);
}
NakamotoSigners::update_filtered_transactions(
&mut filtered_transactions,
&account_nonces,
self.config.is_mainnet(),
transactions,
)
}
_ => {} // Any other message is ignored
}
}
Ok(transactions_to_include)
Ok(filtered_transactions.into_values().collect())
}
fn wait_for_signer_signature(

View File

@@ -30,7 +30,9 @@ use stacks::chainstate::nakamoto::miner::NakamotoBlockBuilder;
use stacks::chainstate::nakamoto::test_signers::TestSigners;
use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoChainState};
use stacks::chainstate::stacks::address::PoxAddress;
use stacks::chainstate::stacks::boot::{MINERS_NAME, SIGNERS_VOTING_NAME};
use stacks::chainstate::stacks::boot::{
MINERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME,
};
use stacks::chainstate::stacks::db::StacksChainState;
use stacks::chainstate::stacks::miner::{BlockBuilder, BlockLimitFunction, TransactionResult};
use stacks::chainstate::stacks::{StacksTransaction, ThresholdSignature, TransactionPayload};
@@ -455,7 +457,7 @@ pub fn boot_to_epoch_3(
300,
&StacksAddress::burn_address(false),
SIGNERS_VOTING_NAME,
"vote-for-aggregate-public-key",
SIGNERS_VOTING_FUNCTION_NAME,
&[
clarity::vm::Value::UInt(i as u128),
aggregate_public_key.clone(),
@@ -564,7 +566,7 @@ fn signer_vote_if_needed(
300,
&StacksAddress::burn_address(false),
SIGNERS_VOTING_NAME,
"vote-for-aggregate-public-key",
SIGNERS_VOTING_FUNCTION_NAME,
&[
clarity::vm::Value::UInt(i as u128),
aggregate_public_key.clone(),

View File

@@ -7,23 +7,35 @@ use std::time::{Duration, Instant};
use std::{env, thread};
use clarity::boot_util::boot_code_id;
use clarity::vm::Value;
use libsigner::{
BlockResponse, RejectCode, RunningSigner, Signer, SignerEventReceiver, SignerMessage,
BLOCK_MSG_ID,
};
use rand::thread_rng;
use rand_core::RngCore;
use stacks::burnchains::Txid;
use stacks::chainstate::coordinator::comm::CoordinatorChannels;
use stacks::chainstate::nakamoto::signer_set::NakamotoSigners;
use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader, NakamotoBlockVote};
use stacks::chainstate::stacks::boot::SIGNERS_NAME;
use stacks::chainstate::stacks::boot::{
SIGNERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME,
};
use stacks::chainstate::stacks::miner::TransactionEvent;
use stacks::chainstate::stacks::{StacksPrivateKey, StacksTransaction, ThresholdSignature};
use stacks::chainstate::stacks::{
StacksPrivateKey, StacksTransaction, ThresholdSignature, TransactionAnchorMode,
TransactionAuth, TransactionPayload, TransactionPostConditionMode, TransactionSmartContract,
TransactionVersion,
};
use stacks::core::StacksEpoch;
use stacks::net::api::postblock_proposal::BlockValidateResponse;
use stacks::util_lib::strings::StacksString;
use stacks_common::bitvec::BitVec;
use stacks_common::codec::{read_next, StacksMessageCodec};
use stacks_common::consts::SIGNER_SLOTS_PER_USER;
use stacks_common::types::chainstate::{ConsensusHash, StacksBlockId, TrieHash};
use stacks_common::consts::{CHAIN_ID_TESTNET, SIGNER_SLOTS_PER_USER};
use stacks_common::types::chainstate::{
ConsensusHash, StacksAddress, StacksBlockId, StacksPublicKey, TrieHash,
};
use stacks_common::types::StacksEpochId;
use stacks_common::util::hash::{MerkleTree, Sha512Trunc256Sum};
use stacks_common::util::secp256k1::MessageSignature;
@@ -545,38 +557,190 @@ impl SignerTest {
.unwrap();
// Get the signer indices
let reward_cycle = self.get_current_reward_cycle();
let valid_signer_index = self.get_signer_index(reward_cycle);
let round = self
.stacks_client
.get_last_round(reward_cycle)
.expect("FATAL: failed to get round")
.unwrap_or(0)
.saturating_add(1);
let point = Point::from(Scalar::random(&mut rand::thread_rng()));
let invalid_nonce_tx = self
.stacks_client
.build_vote_for_aggregate_public_key(
valid_signer_index,
round,
point,
reward_cycle,
let signer_private_key = self.signer_stacks_private_keys[0];
let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, false);
let contract_addr = vote_contract_id.issuer.into();
let contract_name = vote_contract_id.name.clone();
let signer_index = thread_rng().next_u64();
let signer_index_arg = Value::UInt(signer_index as u128);
let point = Point::from(Scalar::random(&mut thread_rng()));
let point_arg =
Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff");
let round = thread_rng().next_u64();
let round_arg = Value::UInt(round as u128);
let reward_cycle_arg = Value::UInt(reward_cycle as u128);
let valid_function_args = vec![
signer_index_arg.clone(),
point_arg.clone(),
round_arg.clone(),
reward_cycle_arg.clone(),
];
// Create a invalid transaction that is not a contract call
let invalid_not_contract_call = StacksTransaction {
version: TransactionVersion::Testnet,
chain_id: CHAIN_ID_TESTNET,
auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(),
anchor_mode: TransactionAnchorMode::Any,
post_condition_mode: TransactionPostConditionMode::Allow,
post_conditions: vec![],
payload: TransactionPayload::SmartContract(
TransactionSmartContract {
name: "test-contract".into(),
code_body: StacksString::from_str("(/ 1 0)").unwrap(),
},
None,
0, // Old nonce
),
};
let invalid_contract_address = StacksClient::build_signed_contract_call_transaction(
&StacksAddress::p2pkh(false, &StacksPublicKey::from_private(&signer_private_key)),
contract_name.clone(),
SIGNERS_VOTING_FUNCTION_NAME.into(),
&valid_function_args,
&signer_private_key,
TransactionVersion::Testnet,
CHAIN_ID_TESTNET,
1,
10,
)
.unwrap();
let invalid_contract_name = StacksClient::build_signed_contract_call_transaction(
&contract_addr,
"bad-signers-contract-name".into(),
SIGNERS_VOTING_FUNCTION_NAME.into(),
&valid_function_args,
&signer_private_key,
TransactionVersion::Testnet,
CHAIN_ID_TESTNET,
1,
10,
)
.unwrap();
let invalid_signers_vote_function = StacksClient::build_signed_contract_call_transaction(
&contract_addr,
contract_name.clone(),
"some-other-function".into(),
&valid_function_args,
&signer_private_key,
TransactionVersion::Testnet,
CHAIN_ID_TESTNET,
1,
10,
)
.unwrap();
let invalid_function_arg_signer_index =
StacksClient::build_signed_contract_call_transaction(
&contract_addr,
contract_name.clone(),
SIGNERS_VOTING_FUNCTION_NAME.into(),
&[
point_arg.clone(),
point_arg.clone(),
round_arg.clone(),
reward_cycle_arg.clone(),
],
&signer_private_key,
TransactionVersion::Testnet,
CHAIN_ID_TESTNET,
1,
10,
)
.expect("FATAL: failed to build vote for aggregate public key");
.unwrap();
let invalid_function_arg_key = StacksClient::build_signed_contract_call_transaction(
&contract_addr,
contract_name.clone(),
SIGNERS_VOTING_FUNCTION_NAME.into(),
&[
signer_index_arg.clone(),
signer_index_arg.clone(),
round_arg.clone(),
reward_cycle_arg.clone(),
],
&signer_private_key,
TransactionVersion::Testnet,
CHAIN_ID_TESTNET,
1,
10,
)
.unwrap();
let invalid_function_arg_round = StacksClient::build_signed_contract_call_transaction(
&contract_addr,
contract_name.clone(),
SIGNERS_VOTING_FUNCTION_NAME.into(),
&[
signer_index_arg.clone(),
point_arg.clone(),
point_arg.clone(),
reward_cycle_arg.clone(),
],
&signer_private_key,
TransactionVersion::Testnet,
CHAIN_ID_TESTNET,
1,
10,
)
.unwrap();
let invalid_function_arg_reward_cycle =
StacksClient::build_signed_contract_call_transaction(
&contract_addr,
contract_name.clone(),
SIGNERS_VOTING_FUNCTION_NAME.into(),
&[
signer_index_arg.clone(),
point_arg.clone(),
round_arg.clone(),
point_arg.clone(),
],
&signer_private_key,
TransactionVersion::Testnet,
CHAIN_ID_TESTNET,
1,
10,
)
.unwrap();
let invalid_nonce = StacksClient::build_signed_contract_call_transaction(
&contract_addr,
contract_name.clone(),
SIGNERS_VOTING_FUNCTION_NAME.into(),
&valid_function_args,
&signer_private_key,
TransactionVersion::Testnet,
CHAIN_ID_TESTNET,
0, // Old nonce
10,
)
.unwrap();
let invalid_stacks_client = StacksClient::new(StacksPrivateKey::new(), host, false);
let invalid_signer_tx = invalid_stacks_client
.build_vote_for_aggregate_public_key(
valid_signer_index,
round,
point,
reward_cycle,
None,
0,
)
.build_vote_for_aggregate_public_key(0, round, point, reward_cycle, None, 0)
.expect("FATAL: failed to build vote for aggregate public key");
// TODO: add invalid contract calls (one with non 'vote-for-aggregate-public-key' function call and one with invalid function args)
vec![invalid_nonce_tx, invalid_signer_tx]
vec![
invalid_nonce,
invalid_not_contract_call,
invalid_contract_name,
invalid_contract_address,
invalid_signers_vote_function,
invalid_function_arg_key,
invalid_function_arg_reward_cycle,
invalid_function_arg_round,
invalid_function_arg_signer_index,
invalid_signer_tx,
]
}
fn shutdown(self) {