Skip to content

Commit 4e82714

Browse files
author
Ludo Galabru
committed
fix: update chainhook schema
1 parent 887467c commit 4e82714

File tree

5 files changed

+90
-55
lines changed

5 files changed

+90
-55
lines changed

components/chainhook-event-observer/Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "chainhook-event-observer"
3-
version = "1.0.4"
3+
version = "1.0.6"
44
description = "Stateless Transaction Indexing Engine for Stacks and Bitcoin"
55
license = "GPL-3.0"
66
edition = "2021"
@@ -14,13 +14,13 @@ serde-hex = "0.1.0"
1414
serde_derive = "1"
1515
stacks-rpc-client = "1"
1616
clarinet-utils = "1"
17-
clarity-repl = "=1.4.1"
17+
clarity-repl = "=1.5.0"
1818
hiro-system-kit = "0.1.0"
1919
# stacks-rpc-client = { version = "1", path = "../../../clarinet/components/stacks-rpc-client" }
2020
# clarinet-utils = { version = "1", path = "../../../clarinet/components/clarinet-utils" }
2121
# clarity-repl = { version = "1", path = "../../../clarinet/components/clarity-repl" }
2222
# hiro-system-kit = { version = "0.1.0", path = "../../../clarinet/components/hiro-system-kit" }
23-
chainhook-types = { version = "1.0.2", path = "../chainhook-types-rs" }
23+
chainhook-types = { version = "1.0.3", path = "../chainhook-types-rs" }
2424
rocket = { version = "=0.5.0-rc.2", features = ["json"] }
2525
bitcoincore-rpc = "0.16.0"
2626
bitcoincore-rpc-json = "0.16.0"

components/chainhook-event-observer/src/chainhooks/bitcoin/mod.rs

+23-38
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::types::{
22
BitcoinChainhookSpecification, BitcoinPredicateType, ExactMatchingRule, HookAction,
33
InputPredicate, MatchingRule, OrdinalOperations, OutputPredicate, Protocols, StacksOperations,
44
};
5+
use crate::utils::Context;
56
use base58::FromBase58;
67
use bitcoincore_rpc::bitcoin::blockdata::opcodes;
78
use bitcoincore_rpc::bitcoin::blockdata::script::Builder as BitcoinScriptBuilder;
@@ -12,6 +13,7 @@ use chainhook_types::{
1213
StacksBaseChainOperation, TransactionIdentifier,
1314
};
1415
use clarity_repl::clarity::util::hash::to_hex;
16+
use hiro_system_kit::slog;
1517
use reqwest::{Client, Method};
1618
use serde_json::Value as JsonValue;
1719
use std::collections::HashMap;
@@ -56,6 +58,7 @@ pub enum BitcoinChainhookOccurrence {
5658
pub fn evaluate_bitcoin_chainhooks_on_chain_event<'a>(
5759
chain_event: &'a BitcoinChainEvent,
5860
active_chainhooks: Vec<&'a BitcoinChainhookSpecification>,
61+
ctx: &Context,
5962
) -> Vec<BitcoinTriggerChainhook<'a>> {
6063
let mut triggered_chainhooks = vec![];
6164
match chain_event {
@@ -67,7 +70,7 @@ pub fn evaluate_bitcoin_chainhooks_on_chain_event<'a>(
6770
for block in event.new_blocks.iter() {
6871
let mut hits = vec![];
6972
for tx in block.transactions.iter() {
70-
if chainhook.predicate.evaluate_transaction_predicate(&tx) {
73+
if chainhook.predicate.evaluate_transaction_predicate(&tx, ctx) {
7174
hits.push(tx);
7275
}
7376
}
@@ -93,7 +96,7 @@ pub fn evaluate_bitcoin_chainhooks_on_chain_event<'a>(
9396
for block in event.blocks_to_apply.iter() {
9497
let mut hits = vec![];
9598
for tx in block.transactions.iter() {
96-
if chainhook.predicate.evaluate_transaction_predicate(&tx) {
99+
if chainhook.predicate.evaluate_transaction_predicate(&tx, ctx) {
97100
hits.push(tx);
98101
}
99102
}
@@ -104,7 +107,7 @@ pub fn evaluate_bitcoin_chainhooks_on_chain_event<'a>(
104107
for block in event.blocks_to_rollback.iter() {
105108
let mut hits = vec![];
106109
for tx in block.transactions.iter() {
107-
if chainhook.predicate.evaluate_transaction_predicate(&tx) {
110+
if chainhook.predicate.evaluate_transaction_predicate(&tx, ctx) {
108111
hits.push(tx);
109112
}
110113
}
@@ -253,7 +256,11 @@ pub fn handle_bitcoin_hook_action<'a>(
253256
}
254257

255258
impl BitcoinPredicateType {
256-
pub fn evaluate_transaction_predicate(&self, tx: &BitcoinTransactionData) -> bool {
259+
pub fn evaluate_transaction_predicate(
260+
&self,
261+
tx: &BitcoinTransactionData,
262+
ctx: &Context,
263+
) -> bool {
257264
// TODO(lgalabru): follow-up on this implementation
258265
match &self {
259266
BitcoinPredicateType::Block => true,
@@ -291,40 +298,18 @@ impl BitcoinPredicateType {
291298
false
292299
}
293300
BitcoinPredicateType::Outputs(OutputPredicate::P2pkh(ExactMatchingRule::Equals(
294-
address,
295-
))) => {
296-
let pubkey_hash = address
297-
.from_base58()
298-
.expect("Unable to get bytes from btc address");
299-
let script = BitcoinScriptBuilder::new()
300-
.push_opcode(opcodes::all::OP_DUP)
301-
.push_opcode(opcodes::all::OP_HASH160)
302-
.push_slice(&pubkey_hash[1..21])
303-
.push_opcode(opcodes::all::OP_EQUALVERIFY)
304-
.push_opcode(opcodes::all::OP_CHECKSIG)
305-
.into_script();
306-
307-
for output in tx.metadata.outputs.iter() {
308-
if output.script_pubkey == to_hex(script.as_bytes()) {
309-
return true;
310-
}
311-
}
312-
false
313-
}
314-
BitcoinPredicateType::Outputs(OutputPredicate::P2sh(ExactMatchingRule::Equals(
315-
address,
301+
encoded_address,
302+
)))
303+
| BitcoinPredicateType::Outputs(OutputPredicate::P2sh(ExactMatchingRule::Equals(
304+
encoded_address,
316305
))) => {
317-
let script_hash = address
318-
.from_base58()
319-
.expect("Unable to get bytes from btc address");
320-
let script = BitcoinScriptBuilder::new()
321-
.push_opcode(opcodes::all::OP_HASH160)
322-
.push_slice(&script_hash[1..21])
323-
.push_opcode(opcodes::all::OP_EQUAL)
324-
.into_script();
325-
306+
let address = match Address::from_str(encoded_address) {
307+
Ok(address) => address,
308+
Err(_) => return false,
309+
};
310+
let address_bytes = to_hex(address.script_pubkey().as_bytes());
326311
for output in tx.metadata.outputs.iter() {
327-
if output.script_pubkey == to_hex(script.as_bytes()) {
312+
if output.script_pubkey[2..] == address_bytes {
328313
return true;
329314
}
330315
}
@@ -346,9 +331,9 @@ impl BitcoinPredicateType {
346331
},
347332
Err(_) => return false,
348333
};
349-
let script_bytes = to_hex(address.script_pubkey().as_bytes());
334+
let address_bytes = to_hex(address.script_pubkey().as_bytes());
350335
for output in tx.metadata.outputs.iter() {
351-
if output.script_pubkey == script_bytes {
336+
if output.script_pubkey[2..] == address_bytes {
352337
return true;
353338
}
354339
}

components/chainhook-event-observer/src/chainhooks/types.rs

+38-4
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,27 @@ impl ChainhookConfig {
4545
bitcoin
4646
}
4747

48-
pub fn register_hook(&mut self, hook: ChainhookSpecification) {
49-
match hook {
50-
ChainhookSpecification::Stacks(hook) => self.stacks_chainhooks.push(hook),
51-
ChainhookSpecification::Bitcoin(hook) => self.bitcoin_chainhooks.push(hook),
48+
pub fn register_hook(
49+
&mut self,
50+
networks: (&BitcoinNetwork, &StacksNetwork),
51+
hook: ChainhookFullSpecification,
52+
api_key: &ApiKey,
53+
) -> Result<ChainhookSpecification, String> {
54+
let spec = match hook {
55+
ChainhookFullSpecification::Stacks(hook) => {
56+
let mut spec = hook.into_selected_network_specification(networks.1)?;
57+
spec.owner_uuid = api_key.0.clone();
58+
self.stacks_chainhooks.push(spec.clone());
59+
ChainhookSpecification::Stacks(spec)
60+
}
61+
ChainhookFullSpecification::Bitcoin(hook) => {
62+
let mut spec = hook.into_selected_network_specification(networks.0)?;
63+
spec.owner_uuid = api_key.0.clone();
64+
self.bitcoin_chainhooks.push(spec.clone());
65+
ChainhookSpecification::Bitcoin(spec)
66+
}
5267
};
68+
Ok(spec)
5369
}
5470

5571
pub fn deregister_stacks_hook(
@@ -198,6 +214,24 @@ pub enum ChainhookFullSpecification {
198214
Stacks(StacksChainhookFullSpecification),
199215
}
200216

217+
impl ChainhookFullSpecification {
218+
pub fn validate(&self) -> Result<(), String> {
219+
match &self {
220+
Self::Bitcoin(data) => {
221+
for (_, spec) in data.networks.iter() {
222+
let _ = spec.action.validate()?;
223+
}
224+
}
225+
Self::Stacks(data) => {
226+
for (_, spec) in data.networks.iter() {
227+
let _ = spec.action.validate()?;
228+
}
229+
}
230+
}
231+
Ok(())
232+
}
233+
}
234+
201235
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
202236
pub struct BitcoinChainhookFullSpecification {
203237
pub uuid: String,

components/chainhook-event-observer/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern crate serde_json;
1414

1515
pub extern crate bitcoincore_rpc;
1616
pub extern crate redb;
17+
pub use chainhook_types;
1718

1819
pub mod chainhooks;
1920
pub mod indexer;

components/chainhook-event-observer/src/observer/mod.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::chainhooks::stacks::{
66
evaluate_stacks_chainhooks_on_chain_event, handle_stacks_hook_action,
77
StacksChainhookOccurrence, StacksChainhookOccurrencePayload,
88
};
9-
use crate::chainhooks::types::{ChainhookConfig, ChainhookSpecification};
9+
use crate::chainhooks::types::{
10+
ChainhookConfig, ChainhookFullSpecification, ChainhookSpecification,
11+
};
1012
use crate::indexer::bitcoin::{retrieve_full_block_breakdown_with_retry, NewBitcoinBlock};
1113
use crate::indexer::ordinals::{indexing::updater::OrdinalIndexUpdater, initialize_ordinal_index};
1214
use crate::indexer::{self, Indexer, IndexerConfig};
@@ -127,6 +129,7 @@ pub struct EventObserverConfig {
127129
pub display_logs: bool,
128130
pub cache_path: String,
129131
pub bitcoin_network: BitcoinNetwork,
132+
pub stacks_network: StacksNetwork,
130133
}
131134

132135
#[derive(Deserialize, Debug)]
@@ -140,7 +143,7 @@ pub enum ObserverCommand {
140143
PropagateBitcoinChainEvent(BitcoinChainEvent),
141144
PropagateStacksChainEvent(StacksChainEvent),
142145
PropagateStacksMempoolEvent(StacksChainMempoolEvent),
143-
RegisterHook(ChainhookSpecification, ApiKey),
146+
RegisterHook(ChainhookFullSpecification, ApiKey),
144147
DeregisterBitcoinHook(String, ApiKey),
145148
DeregisterStacksHook(String, ApiKey),
146149
NotifyBitcoinTransactionProxied,
@@ -447,7 +450,7 @@ pub async fn start_observer_commands_handler(
447450
let mut chainhooks_occurrences_tracker: HashMap<String, u64> = HashMap::new();
448451
let event_handlers = config.event_handlers.clone();
449452
let mut chainhooks_lookup: HashMap<String, ApiKey> = HashMap::new();
450-
453+
let networks = (&config.bitcoin_network, &config.stacks_network);
451454
loop {
452455
let command = match observer_commands_rx.recv() {
453456
Ok(cmd) => cmd,
@@ -510,6 +513,7 @@ pub async fn start_observer_commands_handler(
510513
let chainhooks_candidates = evaluate_bitcoin_chainhooks_on_chain_event(
511514
&chain_event,
512515
bitcoin_chainhooks,
516+
&ctx,
513517
);
514518

515519
ctx.try_log(|logger| {
@@ -847,20 +851,31 @@ pub async fn start_observer_commands_handler(
847851
continue;
848852
}
849853
};
850-
let mut registered_hook = hook.clone();
851-
registered_hook.set_owner_uuid(&api_key);
852-
hook_formation.register_hook(registered_hook.clone());
853-
chainhooks_lookup.insert(registered_hook.uuid().to_string(), api_key.clone());
854+
855+
let spec = match hook_formation.register_hook(networks, hook, &api_key) {
856+
Ok(uuid) => uuid,
857+
Err(e) => {
858+
ctx.try_log(|logger| {
859+
slog::error!(
860+
logger,
861+
"Unable to retrieve register new chainhook spec: {}",
862+
e.to_string()
863+
)
864+
});
865+
continue;
866+
}
867+
};
868+
chainhooks_lookup.insert(spec.uuid().to_string(), api_key.clone());
854869
ctx.try_log(|logger| {
855870
slog::info!(
856871
logger,
857872
"Registering chainhook {} associated with {:?}",
858-
registered_hook.uuid(),
873+
spec.uuid(),
859874
api_key
860875
)
861876
});
862877
if let Some(ref tx) = observer_events_tx {
863-
let _ = tx.send(ObserverEvent::HookRegistered(registered_hook));
878+
let _ = tx.send(ObserverEvent::HookRegistered(spec));
864879
}
865880
}
866881
},
@@ -1424,7 +1439,7 @@ pub fn handle_get_hooks(
14241439
#[openapi(tag = "Chainhooks")]
14251440
#[post("/v1/chainhooks", format = "application/json", data = "<hook>")]
14261441
pub fn handle_create_hook(
1427-
hook: Json<ChainhookSpecification>,
1442+
hook: Json<ChainhookFullSpecification>,
14281443
background_job_tx: &State<Arc<Mutex<Sender<ObserverCommand>>>>,
14291444
ctx: &State<Context>,
14301445
api_key: ApiKey,

0 commit comments

Comments
 (0)