fix: update chainhook schema

This commit is contained in:
Ludo Galabru
2023-03-13 21:52:22 -05:00
parent 887467c9f2
commit 4e8271491b
5 changed files with 90 additions and 55 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "chainhook-event-observer"
version = "1.0.4"
version = "1.0.6"
description = "Stateless Transaction Indexing Engine for Stacks and Bitcoin"
license = "GPL-3.0"
edition = "2021"
@@ -14,13 +14,13 @@ serde-hex = "0.1.0"
serde_derive = "1"
stacks-rpc-client = "1"
clarinet-utils = "1"
clarity-repl = "=1.4.1"
clarity-repl = "=1.5.0"
hiro-system-kit = "0.1.0"
# stacks-rpc-client = { version = "1", path = "../../../clarinet/components/stacks-rpc-client" }
# clarinet-utils = { version = "1", path = "../../../clarinet/components/clarinet-utils" }
# clarity-repl = { version = "1", path = "../../../clarinet/components/clarity-repl" }
# hiro-system-kit = { version = "0.1.0", path = "../../../clarinet/components/hiro-system-kit" }
chainhook-types = { version = "1.0.2", path = "../chainhook-types-rs" }
chainhook-types = { version = "1.0.3", path = "../chainhook-types-rs" }
rocket = { version = "=0.5.0-rc.2", features = ["json"] }
bitcoincore-rpc = "0.16.0"
bitcoincore-rpc-json = "0.16.0"

View File

@@ -2,6 +2,7 @@ use super::types::{
BitcoinChainhookSpecification, BitcoinPredicateType, ExactMatchingRule, HookAction,
InputPredicate, MatchingRule, OrdinalOperations, OutputPredicate, Protocols, StacksOperations,
};
use crate::utils::Context;
use base58::FromBase58;
use bitcoincore_rpc::bitcoin::blockdata::opcodes;
use bitcoincore_rpc::bitcoin::blockdata::script::Builder as BitcoinScriptBuilder;
@@ -12,6 +13,7 @@ use chainhook_types::{
StacksBaseChainOperation, TransactionIdentifier,
};
use clarity_repl::clarity::util::hash::to_hex;
use hiro_system_kit::slog;
use reqwest::{Client, Method};
use serde_json::Value as JsonValue;
use std::collections::HashMap;
@@ -56,6 +58,7 @@ pub enum BitcoinChainhookOccurrence {
pub fn evaluate_bitcoin_chainhooks_on_chain_event<'a>(
chain_event: &'a BitcoinChainEvent,
active_chainhooks: Vec<&'a BitcoinChainhookSpecification>,
ctx: &Context,
) -> Vec<BitcoinTriggerChainhook<'a>> {
let mut triggered_chainhooks = vec![];
match chain_event {
@@ -67,7 +70,7 @@ pub fn evaluate_bitcoin_chainhooks_on_chain_event<'a>(
for block in event.new_blocks.iter() {
let mut hits = vec![];
for tx in block.transactions.iter() {
if chainhook.predicate.evaluate_transaction_predicate(&tx) {
if chainhook.predicate.evaluate_transaction_predicate(&tx, ctx) {
hits.push(tx);
}
}
@@ -93,7 +96,7 @@ pub fn evaluate_bitcoin_chainhooks_on_chain_event<'a>(
for block in event.blocks_to_apply.iter() {
let mut hits = vec![];
for tx in block.transactions.iter() {
if chainhook.predicate.evaluate_transaction_predicate(&tx) {
if chainhook.predicate.evaluate_transaction_predicate(&tx, ctx) {
hits.push(tx);
}
}
@@ -104,7 +107,7 @@ pub fn evaluate_bitcoin_chainhooks_on_chain_event<'a>(
for block in event.blocks_to_rollback.iter() {
let mut hits = vec![];
for tx in block.transactions.iter() {
if chainhook.predicate.evaluate_transaction_predicate(&tx) {
if chainhook.predicate.evaluate_transaction_predicate(&tx, ctx) {
hits.push(tx);
}
}
@@ -253,7 +256,11 @@ pub fn handle_bitcoin_hook_action<'a>(
}
impl BitcoinPredicateType {
pub fn evaluate_transaction_predicate(&self, tx: &BitcoinTransactionData) -> bool {
pub fn evaluate_transaction_predicate(
&self,
tx: &BitcoinTransactionData,
ctx: &Context,
) -> bool {
// TODO(lgalabru): follow-up on this implementation
match &self {
BitcoinPredicateType::Block => true,
@@ -291,40 +298,18 @@ impl BitcoinPredicateType {
false
}
BitcoinPredicateType::Outputs(OutputPredicate::P2pkh(ExactMatchingRule::Equals(
address,
encoded_address,
)))
| BitcoinPredicateType::Outputs(OutputPredicate::P2sh(ExactMatchingRule::Equals(
encoded_address,
))) => {
let pubkey_hash = address
.from_base58()
.expect("Unable to get bytes from btc address");
let script = BitcoinScriptBuilder::new()
.push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&pubkey_hash[1..21])
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script();
let address = match Address::from_str(encoded_address) {
Ok(address) => address,
Err(_) => return false,
};
let address_bytes = to_hex(address.script_pubkey().as_bytes());
for output in tx.metadata.outputs.iter() {
if output.script_pubkey == to_hex(script.as_bytes()) {
return true;
}
}
false
}
BitcoinPredicateType::Outputs(OutputPredicate::P2sh(ExactMatchingRule::Equals(
address,
))) => {
let script_hash = address
.from_base58()
.expect("Unable to get bytes from btc address");
let script = BitcoinScriptBuilder::new()
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&script_hash[1..21])
.push_opcode(opcodes::all::OP_EQUAL)
.into_script();
for output in tx.metadata.outputs.iter() {
if output.script_pubkey == to_hex(script.as_bytes()) {
if output.script_pubkey[2..] == address_bytes {
return true;
}
}
@@ -346,9 +331,9 @@ impl BitcoinPredicateType {
},
Err(_) => return false,
};
let script_bytes = to_hex(address.script_pubkey().as_bytes());
let address_bytes = to_hex(address.script_pubkey().as_bytes());
for output in tx.metadata.outputs.iter() {
if output.script_pubkey == script_bytes {
if output.script_pubkey[2..] == address_bytes {
return true;
}
}

View File

@@ -45,11 +45,27 @@ impl ChainhookConfig {
bitcoin
}
pub fn register_hook(&mut self, hook: ChainhookSpecification) {
match hook {
ChainhookSpecification::Stacks(hook) => self.stacks_chainhooks.push(hook),
ChainhookSpecification::Bitcoin(hook) => self.bitcoin_chainhooks.push(hook),
pub fn register_hook(
&mut self,
networks: (&BitcoinNetwork, &StacksNetwork),
hook: ChainhookFullSpecification,
api_key: &ApiKey,
) -> Result<ChainhookSpecification, String> {
let spec = match hook {
ChainhookFullSpecification::Stacks(hook) => {
let mut spec = hook.into_selected_network_specification(networks.1)?;
spec.owner_uuid = api_key.0.clone();
self.stacks_chainhooks.push(spec.clone());
ChainhookSpecification::Stacks(spec)
}
ChainhookFullSpecification::Bitcoin(hook) => {
let mut spec = hook.into_selected_network_specification(networks.0)?;
spec.owner_uuid = api_key.0.clone();
self.bitcoin_chainhooks.push(spec.clone());
ChainhookSpecification::Bitcoin(spec)
}
};
Ok(spec)
}
pub fn deregister_stacks_hook(
@@ -198,6 +214,24 @@ pub enum ChainhookFullSpecification {
Stacks(StacksChainhookFullSpecification),
}
impl ChainhookFullSpecification {
pub fn validate(&self) -> Result<(), String> {
match &self {
Self::Bitcoin(data) => {
for (_, spec) in data.networks.iter() {
let _ = spec.action.validate()?;
}
}
Self::Stacks(data) => {
for (_, spec) in data.networks.iter() {
let _ = spec.action.validate()?;
}
}
}
Ok(())
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
pub struct BitcoinChainhookFullSpecification {
pub uuid: String,

View File

@@ -14,6 +14,7 @@ extern crate serde_json;
pub extern crate bitcoincore_rpc;
pub extern crate redb;
pub use chainhook_types;
pub mod chainhooks;
pub mod indexer;

View File

@@ -6,7 +6,9 @@ use crate::chainhooks::stacks::{
evaluate_stacks_chainhooks_on_chain_event, handle_stacks_hook_action,
StacksChainhookOccurrence, StacksChainhookOccurrencePayload,
};
use crate::chainhooks::types::{ChainhookConfig, ChainhookSpecification};
use crate::chainhooks::types::{
ChainhookConfig, ChainhookFullSpecification, ChainhookSpecification,
};
use crate::indexer::bitcoin::{retrieve_full_block_breakdown_with_retry, NewBitcoinBlock};
use crate::indexer::ordinals::{indexing::updater::OrdinalIndexUpdater, initialize_ordinal_index};
use crate::indexer::{self, Indexer, IndexerConfig};
@@ -127,6 +129,7 @@ pub struct EventObserverConfig {
pub display_logs: bool,
pub cache_path: String,
pub bitcoin_network: BitcoinNetwork,
pub stacks_network: StacksNetwork,
}
#[derive(Deserialize, Debug)]
@@ -140,7 +143,7 @@ pub enum ObserverCommand {
PropagateBitcoinChainEvent(BitcoinChainEvent),
PropagateStacksChainEvent(StacksChainEvent),
PropagateStacksMempoolEvent(StacksChainMempoolEvent),
RegisterHook(ChainhookSpecification, ApiKey),
RegisterHook(ChainhookFullSpecification, ApiKey),
DeregisterBitcoinHook(String, ApiKey),
DeregisterStacksHook(String, ApiKey),
NotifyBitcoinTransactionProxied,
@@ -447,7 +450,7 @@ pub async fn start_observer_commands_handler(
let mut chainhooks_occurrences_tracker: HashMap<String, u64> = HashMap::new();
let event_handlers = config.event_handlers.clone();
let mut chainhooks_lookup: HashMap<String, ApiKey> = HashMap::new();
let networks = (&config.bitcoin_network, &config.stacks_network);
loop {
let command = match observer_commands_rx.recv() {
Ok(cmd) => cmd,
@@ -510,6 +513,7 @@ pub async fn start_observer_commands_handler(
let chainhooks_candidates = evaluate_bitcoin_chainhooks_on_chain_event(
&chain_event,
bitcoin_chainhooks,
&ctx,
);
ctx.try_log(|logger| {
@@ -847,20 +851,31 @@ pub async fn start_observer_commands_handler(
continue;
}
};
let mut registered_hook = hook.clone();
registered_hook.set_owner_uuid(&api_key);
hook_formation.register_hook(registered_hook.clone());
chainhooks_lookup.insert(registered_hook.uuid().to_string(), api_key.clone());
let spec = match hook_formation.register_hook(networks, hook, &api_key) {
Ok(uuid) => uuid,
Err(e) => {
ctx.try_log(|logger| {
slog::error!(
logger,
"Unable to retrieve register new chainhook spec: {}",
e.to_string()
)
});
continue;
}
};
chainhooks_lookup.insert(spec.uuid().to_string(), api_key.clone());
ctx.try_log(|logger| {
slog::info!(
logger,
"Registering chainhook {} associated with {:?}",
registered_hook.uuid(),
spec.uuid(),
api_key
)
});
if let Some(ref tx) = observer_events_tx {
let _ = tx.send(ObserverEvent::HookRegistered(registered_hook));
let _ = tx.send(ObserverEvent::HookRegistered(spec));
}
}
},
@@ -1424,7 +1439,7 @@ pub fn handle_get_hooks(
#[openapi(tag = "Chainhooks")]
#[post("/v1/chainhooks", format = "application/json", data = "<hook>")]
pub fn handle_create_hook(
hook: Json<ChainhookSpecification>,
hook: Json<ChainhookFullSpecification>,
background_job_tx: &State<Arc<Mutex<Sender<ObserverCommand>>>>,
ctx: &State<Context>,
api_key: ApiKey,