mirror of
https://github.com/alexgo-io/bitcoin-indexer.git
synced 2026-01-12 16:52:57 +08:00
fix: update chainhook schema
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user