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: Tenure change predicate #672

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/chainhook-cli/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ impl Config {
(false, true, false, _) => Config::testnet_default(),
(false, false, true, _) => Config::mainnet_default(),
(false, false, false, Some(config_path)) => Config::from_file_path(config_path)?,
_ => Err("Invalid combination of arguments".to_string())?,
_ => Err("Must include environment flag (for example --devnet)".to_string())?,
};
Ok(config)
}
Expand Down
2 changes: 1 addition & 1 deletion components/chainhook-cli/src/config/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,5 +178,5 @@ fn it_has_default_config_for_each_network() {
let config = Config::default(false, false, false, &Some(path)).unwrap();
assert_eq!(config.network.bitcoin_network, BitcoinNetwork::Regtest);
assert_eq!(config.network.stacks_network, StacksNetwork::Devnet);
Config::default(true, true, false, &None).expect_err("expected invalid combination error");
Config::default(true, true, false, &None).expect_err("expected missing environment flag error");
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::scan::stacks::{Record, RecordKind};
use crate::service::tests::helpers::mock_bitcoin_rpc::TipData;
use chainhook_sdk::indexer::bitcoin::NewBitcoinBlock;
use chainhook_sdk::indexer::stacks::{NewBlock, NewEvent, NewTransaction, RewardSet, RewardSetSigner};
use chainhook_sdk::indexer::stacks::{
NewBlock, NewEvent, NewTransaction, RewardSet, RewardSetSigner,
};
use chainhook_sdk::types::{
FTBurnEventData, FTMintEventData, FTTransferEventData, NFTBurnEventData, NFTMintEventData,
NFTTransferEventData, STXBurnEventData, STXLockEventData, STXMintEventData,
Expand Down Expand Up @@ -87,6 +89,13 @@ fn create_stacks_new_event(
} else {
None
};
let tenure_change_event = if let StacksTransactionEventPayload::TenureChangeEvent(data) = &event
{
event_type = "tenure_change_event".to_string();
Some(serde_json::to_value(data).unwrap())
} else {
None
};
let contract_event = if let StacksTransactionEventPayload::SmartContractEvent(data) = &event {
event_type = "smart_contract_print_event".to_string();
Some(serde_json::to_value(data).unwrap())
Expand All @@ -112,6 +121,7 @@ fn create_stacks_new_event(
data_map_insert_event: None,
data_map_update_event: None,
data_map_delete_event: None,
tenure_change_event,
contract_event,
}
}
Expand Down Expand Up @@ -336,12 +346,7 @@ pub async fn mine_stacks_block(
.map_err(|e| format!("failed to send new_block request: {}", e))?
.text()
.await
.map_err(|e| {
format!(
"failed to parse response for new_block request: {}",
e
)
})?;
.map_err(|e| format!("failed to parse response for new_block request: {}", e))?;
Ok(())
}

Expand Down Expand Up @@ -414,12 +419,7 @@ async fn call_new_burn_block(
.map_err(|e| format!("failed to send new_burn_block request: {}", e))?
.text()
.await
.map_err(|e| {
format!(
"failed to parse response for new_burn_block request: {}",
e
)
})?;
.map_err(|e| format!("failed to parse response for new_burn_block request: {}", e))?;
Ok(())
}

Expand Down
54 changes: 41 additions & 13 deletions components/chainhook-sdk/src/chainhooks/stacks/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::observer::EventObserverConfig;
use crate::utils::{AbstractStacksBlock, Context, MAX_BLOCK_HEIGHTS_ENTRIES};

use super::types::validate_txid;
use super::types::{
append_error_context, BlockIdentifierIndexRule, ChainhookInstance, ExactMatchingRule,
HookAction,
};
use super::types::validate_txid;
use chainhook_types::{
BlockIdentifier, StacksChainEvent, StacksNetwork, StacksTransactionData,
StacksTransactionEvent, StacksTransactionEventPayload, StacksTransactionKind,
Expand Down Expand Up @@ -258,6 +258,7 @@ pub enum StacksPredicate {
FtEvent(StacksFtEventBasedPredicate),
NftEvent(StacksNftEventBasedPredicate),
StxEvent(StacksStxEventBasedPredicate),
TenureChange(TenureChangeBasedPredicate),
Txid(ExactMatchingRule),
}

Expand Down Expand Up @@ -299,6 +300,7 @@ impl StacksPredicate {
StacksPredicate::FtEvent(_) => {}
StacksPredicate::NftEvent(_) => {}
StacksPredicate::StxEvent(_) => {}
StacksPredicate::TenureChange(_) => {}
StacksPredicate::Txid(ExactMatchingRule::Equals(txid)) => {
if let Err(e) = validate_txid(txid) {
return Err(append_error_context(
Expand Down Expand Up @@ -452,6 +454,12 @@ pub struct StacksStxEventBasedPredicate {
pub actions: Vec<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct TenureChangeBasedPredicate {
pub actions: Vec<String>,
}

#[derive(Clone)]
pub struct StacksTriggerChainhook<'a> {
pub chainhook: &'a StacksChainhookInstance,
Expand Down Expand Up @@ -484,17 +492,13 @@ pub struct StacksChainhookOccurrencePayload {
}

impl StacksChainhookOccurrencePayload {
pub fn from_trigger(
trigger: StacksTriggerChainhook<'_>,
) -> StacksChainhookOccurrencePayload {
pub fn from_trigger(trigger: StacksTriggerChainhook<'_>) -> StacksChainhookOccurrencePayload {
StacksChainhookOccurrencePayload {
apply: trigger
.apply
.into_iter()
.map(|(transactions, block)| {
let transactions = transactions
.into_iter().cloned()
.collect::<Vec<_>>();
let transactions = transactions.into_iter().cloned().collect::<Vec<_>>();
StacksApplyTransactionPayload {
block_identifier: block.get_identifier().clone(),
transactions,
Expand All @@ -505,9 +509,7 @@ impl StacksChainhookOccurrencePayload {
.rollback
.into_iter()
.map(|(transactions, block)| {
let transactions = transactions
.into_iter().cloned()
.collect::<Vec<_>>();
let transactions = transactions.into_iter().cloned().collect::<Vec<_>>();
StacksRollbackTransactionPayload {
block_identifier: block.get_identifier().clone(),
transactions,
Expand Down Expand Up @@ -790,6 +792,7 @@ pub fn evaluate_stacks_predicate_on_block<'a>(
| StacksPredicate::FtEvent(_)
| StacksPredicate::NftEvent(_)
| StacksPredicate::StxEvent(_)
| StacksPredicate::TenureChange(_)
| StacksPredicate::PrintEvent(_)
| StacksPredicate::Txid(_) => unreachable!(),
}
Expand All @@ -816,7 +819,7 @@ pub fn evaluate_stacks_predicate_on_transaction<'a>(
_ => false,
},
StacksPredicate::ContractDeployment(StacksContractDeploymentPredicate::ImplementTrait(
stacks_trait,
_stacks_trait,
)) => match &transaction.metadata.kind {
StacksTransactionKind::ContractDeployment(_actual_deployment) => {
ctx.try_log(|logger| {
Expand Down Expand Up @@ -922,6 +925,18 @@ pub fn evaluate_stacks_predicate_on_transaction<'a>(
}
false
}
StacksPredicate::TenureChange(_expected_event) => {
for event in transaction.metadata.receipt.events.iter() {
if matches!(
&event.event_payload,
StacksTransactionEventPayload::TenureChangeEvent(_)
) {
return true;
}
}
false
}

StacksPredicate::StxEvent(expected_event) => {
let expecting_mint = expected_event.actions.contains(&"mint".to_string());
let expecting_transfer = expected_event.actions.contains(&"transfer".to_string());
Expand Down Expand Up @@ -949,7 +964,9 @@ pub fn evaluate_stacks_predicate_on_transaction<'a>(
}
StacksPredicate::PrintEvent(expected_event) => {
for event in transaction.metadata.receipt.events.iter() {
if let StacksTransactionEventPayload::SmartContractEvent(actual) = &event.event_payload {
if let StacksTransactionEventPayload::SmartContractEvent(actual) =
&event.event_payload
{
if actual.topic == "print" {
match expected_event {
StacksPrintEventBasedPredicate::Contains {
Expand Down Expand Up @@ -1212,6 +1229,17 @@ pub fn serialized_event_with_decoded_clarity_value(
"position": event.position
})
}
StacksTransactionEventPayload::TenureChangeEvent(_payload) => {
json!({
"type": "TenureChangeEvent",
// "data": {
// "contract_identifier": payload.contract_identifier,
// "topic": payload.topic,
// "value": serialized_decoded_clarity_value(&payload.tenure_change, ctx),
// },
"position": event.position
})
}
}
}

Expand All @@ -1235,7 +1263,7 @@ pub fn serialized_decoded_clarity_value(hex_value: &str, ctx: &Context) -> serde
Ok(bytes) => bytes,
_ => return json!(hex_value.to_string()),
};

match ClarityValue::consensus_deserialize(&mut Cursor::new(&value_bytes)) {
Ok(value) => serialize_to_json(&value),
Err(e) => {
Expand Down
48 changes: 23 additions & 25 deletions components/chainhook-sdk/src/indexer/stacks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ pub struct NewBlock {

#[serde(skip_serializing_if = "Option::is_none")]
pub block_time: Option<u64>,

#[serde(skip_serializing_if = "Option::is_none")]
pub signer_bitvec: Option<String>,

#[serde(skip_serializing_if = "Option::is_none")]
pub signer_signature: Option<Vec<String>>,

Expand Down Expand Up @@ -147,6 +147,7 @@ pub struct NewEvent {
pub data_map_insert_event: Option<JsonValue>,
pub data_map_update_event: Option<JsonValue>,
pub data_map_delete_event: Option<JsonValue>,
pub tenure_change_event: Option<JsonValue>,
pub contract_event: Option<JsonValue>,
}

Expand Down Expand Up @@ -278,6 +279,15 @@ impl NewEvent {
index: self.event_index,
},
});
} else if let Some(ref event_data) = self.tenure_change_event {
let data: TenureChangeEventData =
serde_json::from_value(event_data.clone()).expect("Unable to decode event_data");
return Ok(StacksTransactionEvent {
event_payload: StacksTransactionEventPayload::TenureChangeEvent(data.clone()),
position: StacksTransactionEventPosition {
index: self.event_index,
},
});
} else if let Some(ref event_data) = self.contract_event {
let data: SmartContractEventData =
serde_json::from_value(event_data.clone()).expect("Unable to decode event_data");
Expand Down Expand Up @@ -387,8 +397,7 @@ pub fn standardize_stacks_block(
}
return Err(format!(
"unable to standardize block #{} ({})",
block.block_height,
e
block.block_height, e
));
}
};
Expand Down Expand Up @@ -629,7 +638,6 @@ pub fn get_value_description(raw_value: &str, ctx: &Context) -> String {
_ => return raw_value.to_string(),
};


match ClarityValue::consensus_deserialize(&mut Cursor::new(&value_bytes)) {
Ok(value) => format!("{}", value),
Err(e) => {
Expand Down Expand Up @@ -703,12 +711,12 @@ pub fn get_tx_description(
if let ClarityValue::Tuple(outter) = *data.data {
if let Some(ClarityValue::Tuple(inner)) = outter.data_map.get("data") {
if let (
Some(ClarityValue::Principal(stacking_address)),
Some(ClarityValue::UInt(amount_ustx)),
Some(ClarityValue::Principal(delegate)),
Some(ClarityValue::Optional(pox_addr)),
Some(ClarityValue::Optional(unlock_burn_height)),
) = (
Some(ClarityValue::Principal(stacking_address)),
Some(ClarityValue::UInt(amount_ustx)),
Some(ClarityValue::Principal(delegate)),
Some(ClarityValue::Optional(pox_addr)),
Some(ClarityValue::Optional(unlock_burn_height)),
) = (
&outter.data_map.get("stacker"),
&inner.data_map.get("amount-ustx"),
&inner.data_map.get("delegate-to"),
Expand All @@ -728,17 +736,13 @@ pub fn get_tx_description(
Some(value) => match &**value {
ClarityValue::Tuple(address_comps) => {
match (
&address_comps
.data_map
.get("version"),
&address_comps.data_map.get("version"),
&address_comps
.data_map
.get("hashbytes"),
) {
(
Some(ClarityValue::UInt(
_version,
)),
Some(ClarityValue::UInt(_version)),
Some(ClarityValue::Sequence(
SequenceData::Buffer(
_hashbytes,
Expand All @@ -763,14 +767,7 @@ pub fn get_tx_description(
},
}),
);
return Ok((
description,
tx_type,
0,
0,
"".to_string(),
None,
));
return Ok((description, tx_type, 0, 0, "".to_string(), None));
}
}
}
Expand Down Expand Up @@ -1324,6 +1321,7 @@ pub fn get_standardized_stacks_receipt(
StacksTransactionEventPayload::DataMapInsertEvent(_data) => {}
StacksTransactionEventPayload::DataMapUpdateEvent(_data) => {}
StacksTransactionEventPayload::DataMapDeleteEvent(_data) => {}
StacksTransactionEventPayload::TenureChangeEvent(_data) => {}
StacksTransactionEventPayload::SmartContractEvent(data) => {
mutated_contracts_radius.insert(data.contract_identifier.clone());
}
Expand Down
19 changes: 15 additions & 4 deletions components/chainhook-sdk/src/indexer/stacks/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use chainhook_types::{
FTBurnEventData, FTMintEventData, FTTransferEventData, NFTBurnEventData, NFTMintEventData,
NFTTransferEventData, STXBurnEventData, STXLockEventData, STXMintEventData,
STXTransferEventData, SmartContractEventData, StacksTransactionEventPayload,
TenureChangeEventData,
};

use crate::indexer::tests::helpers::stacks_events::create_new_event_from_stacks_event;
Expand Down Expand Up @@ -213,10 +214,10 @@ fn test_stacks_vector_040() {
process_stacks_blocks_and_check_expectations((helpers::stacks_shapes::get_vector_040(), None));
}

// #[test]
// fn test_stacks_vector_041() {
// process_stacks_blocks_and_check_expectations((helpers::shapes::get_vector_041(), None));
// }
#[test]
fn test_stacks_vector_041() {
process_stacks_blocks_and_check_expectations((helpers::stacks_shapes::get_vector_041(), None));
}

#[test]
fn test_stacks_vector_042() {
Expand Down Expand Up @@ -363,6 +364,15 @@ fn test_stacks_vector_055() {
topic: "print".to_string(),
hex_value: String::new(),
}); "smart_contract_print_event")]
#[test_case(StacksTransactionEventPayload::TenureChangeEvent(TenureChangeEventData {
tenure_consensus_hash: String::new(),
prev_tenure_consensus_hash: String::new(),
burn_view_consensus_hash: String::new(),
previous_tenure_end: String::new(),
previous_tenure_blocks: "9".to_string(),
cause: "block_found".to_string(),
pubkey_hash: String::new(),
}); "tenure_change_event")]
fn new_events_can_be_converted_into_chainhook_event(original_event: StacksTransactionEventPayload) {
let new_event = create_new_event_from_stacks_event(original_event.clone());
let event = new_event.into_chainhook_event().unwrap();
Expand Down Expand Up @@ -392,6 +402,7 @@ fn into_chainhook_event_rejects_invalid_missing_event() {
data_map_insert_event: None,
data_map_update_event: None,
data_map_delete_event: None,
tenure_change_event: None,
contract_event: None,
};
new_event
Expand Down
Loading