mirror of
https://github.com/alexgo-io/bitcoin-indexer.git
synced 2026-06-13 16:19:01 +08:00
feat: introduce new predicate + refactor schemas
This commit is contained in:
@@ -47,15 +47,71 @@ struct Opts {
|
||||
|
||||
#[derive(Subcommand, PartialEq, Clone, Debug)]
|
||||
enum Command {
|
||||
/// Manage predicates
|
||||
#[clap(subcommand)]
|
||||
Predicates(PredicatesCommand),
|
||||
/// Start chainhook-cli
|
||||
#[clap(subcommand)]
|
||||
Node(NodeCommand),
|
||||
/// Start chainhook-cli in replay mode
|
||||
#[clap(name = "replay", bin_name = "replay")]
|
||||
Replay(ReplayCommand),
|
||||
}
|
||||
|
||||
#[derive(Subcommand, PartialEq, Clone, Debug)]
|
||||
#[clap(bin_name = "predicate", aliases = &["predicate"])]
|
||||
enum PredicatesCommand {
|
||||
/// Generate new predicate
|
||||
#[clap(name = "new", bin_name = "new", aliases = &["generate"])]
|
||||
New(NewPredicate),
|
||||
/// Scan blocks (one-off) from specified network and apply provided predicate
|
||||
#[clap(name = "scan", bin_name = "scan")]
|
||||
Scan(ScanCommand),
|
||||
Scan(ScanPredicate),
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Clone, Debug)]
|
||||
struct NewPredicate {
|
||||
/// Predicate's name
|
||||
pub name: String,
|
||||
/// Path to Clarinet.toml
|
||||
#[clap(long = "manifest-path")]
|
||||
pub manifest_path: Option<String>,
|
||||
/// Generate a Bitcoin chainhook
|
||||
#[clap(long = "bitcoin", conflicts_with = "stacks")]
|
||||
pub bitcoin: bool,
|
||||
/// Generate a Stacks chainhook
|
||||
#[clap(long = "stacks", conflicts_with = "bitcoin")]
|
||||
pub stacks: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Clone, Debug)]
|
||||
struct ScanPredicate {
|
||||
pub devnet: bool,
|
||||
/// Target Testnet network
|
||||
#[clap(
|
||||
long = "testnet",
|
||||
conflicts_with = "devnet",
|
||||
conflicts_with = "mainnet"
|
||||
)]
|
||||
pub testnet: bool,
|
||||
/// Target Mainnet network
|
||||
#[clap(
|
||||
long = "mainnet",
|
||||
conflicts_with = "testnet",
|
||||
conflicts_with = "devnet"
|
||||
)]
|
||||
pub mainnet: bool,
|
||||
/// Load config file path
|
||||
#[clap(
|
||||
long = "config-path",
|
||||
conflicts_with = "mainnet",
|
||||
conflicts_with = "testnet",
|
||||
conflicts_with = "devnet"
|
||||
)]
|
||||
pub config_path: Option<String>,
|
||||
/// Load chainhook file path (yaml format)
|
||||
#[clap(long = "predicate-path", short = 'p')]
|
||||
pub chainhook_spec_path: String,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, PartialEq, Clone, Debug)]
|
||||
@@ -131,39 +187,6 @@ struct ReplayCommand {
|
||||
pub bitcoind_rpc_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Clone, Debug)]
|
||||
struct ScanCommand {
|
||||
pub devnet: bool,
|
||||
/// Target Testnet network
|
||||
#[clap(
|
||||
long = "testnet",
|
||||
conflicts_with = "devnet",
|
||||
conflicts_with = "mainnet"
|
||||
)]
|
||||
pub testnet: bool,
|
||||
/// Target Mainnet network
|
||||
#[clap(
|
||||
long = "mainnet",
|
||||
conflicts_with = "testnet",
|
||||
conflicts_with = "devnet"
|
||||
)]
|
||||
pub mainnet: bool,
|
||||
/// Load config file path
|
||||
#[clap(
|
||||
long = "config-path",
|
||||
conflicts_with = "mainnet",
|
||||
conflicts_with = "testnet",
|
||||
conflicts_with = "devnet"
|
||||
)]
|
||||
pub config_path: Option<String>,
|
||||
/// Load chainhook file path (yaml format)
|
||||
#[clap(
|
||||
long = "chainhook-spec-path",
|
||||
short = 'p'
|
||||
)]
|
||||
pub chainhook_spec_path: String,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let logger = hiro_system_kit::log::setup_logger();
|
||||
let _guard = hiro_system_kit::log::setup_global_logger(logger.clone());
|
||||
@@ -194,6 +217,30 @@ pub fn main() {
|
||||
start_node(config, ctx);
|
||||
}
|
||||
},
|
||||
Command::Predicates(subcmd) => match subcmd {
|
||||
PredicatesCommand::New(cmd) => {
|
||||
// Predicates can either be generated manually by letting developers
|
||||
// craft their own json payload, or using the interactive approach.
|
||||
// A list of contracts is displayed, then list of methods, then list of events detected
|
||||
// 3 files are generated:
|
||||
// predicates/simnet/name.json
|
||||
// predicates/devnet/name.json
|
||||
// predicates/testnet/name.json
|
||||
// predicates/mainnet/name.json
|
||||
let manifest = clarinet_files::get_manifest_location(None);
|
||||
}
|
||||
PredicatesCommand::Scan(cmd) => {
|
||||
let config =
|
||||
match Config::default(cmd.devnet, cmd.testnet, cmd.mainnet, &cmd.config_path) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
println!("{e}");
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
start_scan(config, ctx);
|
||||
}
|
||||
},
|
||||
Command::Replay(cmd) => {
|
||||
let mut config =
|
||||
match Config::default(cmd.devnet, cmd.testnet, cmd.mainnet, &cmd.config_path) {
|
||||
@@ -230,41 +277,28 @@ pub fn main() {
|
||||
}
|
||||
start_replay(config, cmd.apply_trigger, ctx);
|
||||
}
|
||||
Command::Scan(cmd) => {
|
||||
let config =
|
||||
match Config::default(cmd.devnet, cmd.testnet, cmd.mainnet, &cmd.config_path) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
println!("{e}");
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
start_scan(config, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn install_ctrlc_handler(terminate_tx: Sender<DigestingCommand>, ctx: Context) {
|
||||
ctrlc::set_handler(move || {
|
||||
warn!(
|
||||
&ctx.expect_logger(),
|
||||
"Manual interruption signal received"
|
||||
);
|
||||
warn!(&ctx.expect_logger(), "Manual interruption signal received");
|
||||
terminate_tx
|
||||
.send(DigestingCommand::Kill)
|
||||
.expect("Unable to terminate service");
|
||||
}).expect("Error setting Ctrl-C handler");
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
}
|
||||
|
||||
|
||||
pub fn download_dataset_if_required(config: &mut Config, ctx: &Context) -> bool {
|
||||
if config.is_initial_ingestion_required() {
|
||||
// Download default tsv.
|
||||
if config.rely_on_remote_tsv() && config.should_download_remote_tsv() {
|
||||
let url = config.expected_remote_tsv_url();
|
||||
let mut destination_path = config.expected_cache_path();
|
||||
destination_path.push(archive::default_tsv_file_path(&config.network.stacks_network));
|
||||
destination_path.push(archive::default_tsv_file_path(
|
||||
&config.network.stacks_network,
|
||||
));
|
||||
// Download archive if not already present in cache
|
||||
if !destination_path.exists() {
|
||||
info!(ctx.expect_logger(), "Downloading {}", url);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::types::{
|
||||
BitcoinChainhookSpecification, BitcoinPredicateType, ExactMatchingRule, HookAction,
|
||||
KeyRegistrationPredicate, LockSTXPredicate, MatchingRule, PobPredicate, PoxPredicate,
|
||||
TransferSTXPredicate,
|
||||
InputPredicate, MatchingRule, OrdinalOperations, OutputPredicate, Protocols, Scopes,
|
||||
StacksOperations, TxinPredicate,
|
||||
};
|
||||
use base58::FromBase58;
|
||||
use bitcoincore_rpc::bitcoin::blockdata::opcodes;
|
||||
@@ -9,8 +9,8 @@ use bitcoincore_rpc::bitcoin::blockdata::script::Builder as BitcoinScriptBuilder
|
||||
use bitcoincore_rpc::bitcoin::util::address::Payload;
|
||||
use bitcoincore_rpc::bitcoin::Address;
|
||||
use chainhook_types::{
|
||||
BitcoinBlockData, BitcoinChainEvent, BitcoinTransactionData, StacksBaseChainOperation,
|
||||
TransactionIdentifier,
|
||||
BitcoinBlockData, BitcoinChainEvent, BitcoinTransactionData, OrdinalOperation,
|
||||
StacksBaseChainOperation, TransactionIdentifier,
|
||||
};
|
||||
use clarity_repl::clarity::util::hash::to_hex;
|
||||
use reqwest::{Client, Method};
|
||||
@@ -144,6 +144,7 @@ pub fn serialize_bitcoin_payload_to_json<'a>(
|
||||
"inputs": transaction.metadata.inputs,
|
||||
"outputs": transaction.metadata.outputs,
|
||||
"stacks_operations": transaction.metadata.stacks_operations,
|
||||
"ordinal_operations": transaction.metadata.ordinal_operations,
|
||||
"proof": proofs.get(&transaction.transaction_identifier),
|
||||
}),
|
||||
})
|
||||
@@ -231,11 +232,13 @@ pub fn handle_bitcoin_hook_action<'a>(
|
||||
impl BitcoinChainhookSpecification {
|
||||
pub fn evaluate_transaction_predicate(&self, tx: &BitcoinTransactionData) -> bool {
|
||||
// TODO(lgalabru): follow-up on this implementation
|
||||
match &self.predicate.kind {
|
||||
BitcoinPredicateType::TransactionIdentifierHash(ExactMatchingRule::Equals(txid)) => {
|
||||
match &self.predicate {
|
||||
BitcoinPredicateType::Txid(ExactMatchingRule::Equals(txid)) => {
|
||||
tx.transaction_identifier.hash.eq(txid)
|
||||
}
|
||||
BitcoinPredicateType::OpReturn(MatchingRule::Equals(hex_bytes)) => {
|
||||
BitcoinPredicateType::Scope(Scopes::Outputs(OutputPredicate::OpReturn(
|
||||
MatchingRule::Equals(hex_bytes),
|
||||
))) => {
|
||||
for output in tx.metadata.outputs.iter() {
|
||||
if output.script_pubkey.eq(hex_bytes) {
|
||||
return true;
|
||||
@@ -243,7 +246,9 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::OpReturn(MatchingRule::StartsWith(hex_bytes)) => {
|
||||
BitcoinPredicateType::Scope(Scopes::Outputs(OutputPredicate::OpReturn(
|
||||
MatchingRule::StartsWith(hex_bytes),
|
||||
))) => {
|
||||
for output in tx.metadata.outputs.iter() {
|
||||
if output.script_pubkey.starts_with(hex_bytes) {
|
||||
return true;
|
||||
@@ -251,7 +256,9 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::OpReturn(MatchingRule::EndsWith(hex_bytes)) => {
|
||||
BitcoinPredicateType::Scope(Scopes::Outputs(OutputPredicate::OpReturn(
|
||||
MatchingRule::EndsWith(hex_bytes),
|
||||
))) => {
|
||||
for output in tx.metadata.outputs.iter() {
|
||||
if output.script_pubkey.ends_with(hex_bytes) {
|
||||
return true;
|
||||
@@ -259,7 +266,9 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::P2pkh(ExactMatchingRule::Equals(address)) => {
|
||||
BitcoinPredicateType::Scope(Scopes::Outputs(OutputPredicate::P2pkh(
|
||||
ExactMatchingRule::Equals(address),
|
||||
))) => {
|
||||
let pubkey_hash = address
|
||||
.from_base58()
|
||||
.expect("Unable to get bytes from btc address");
|
||||
@@ -278,7 +287,9 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::P2sh(ExactMatchingRule::Equals(address)) => {
|
||||
BitcoinPredicateType::Scope(Scopes::Outputs(OutputPredicate::P2sh(
|
||||
ExactMatchingRule::Equals(address),
|
||||
))) => {
|
||||
let script_hash = address
|
||||
.from_base58()
|
||||
.expect("Unable to get bytes from btc address");
|
||||
@@ -295,8 +306,12 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::P2wpkh(ExactMatchingRule::Equals(encoded_address))
|
||||
| BitcoinPredicateType::P2wsh(ExactMatchingRule::Equals(encoded_address)) => {
|
||||
BitcoinPredicateType::Scope(Scopes::Outputs(OutputPredicate::P2wpkh(
|
||||
ExactMatchingRule::Equals(encoded_address),
|
||||
)))
|
||||
| BitcoinPredicateType::Scope(Scopes::Outputs(OutputPredicate::P2wsh(
|
||||
ExactMatchingRule::Equals(encoded_address),
|
||||
))) => {
|
||||
let address = match Address::from_str(encoded_address) {
|
||||
Ok(address) => match address.payload {
|
||||
Payload::WitnessProgram {
|
||||
@@ -315,7 +330,22 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::Pob(PobPredicate::Any) => {
|
||||
BitcoinPredicateType::Scope(Scopes::Inputs(InputPredicate::Txid(predicate))) => {
|
||||
// TODO(lgalabru): add support for transaction chainhing, if enabled
|
||||
for input in tx.metadata.inputs.iter() {
|
||||
if input.previous_output.txid.eq(&predicate.txid)
|
||||
&& input.previous_output.vout.eq(&predicate.vout)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::Scope(Scopes::Inputs(InputPredicate::WitnessScript(_))) => {
|
||||
// TODO(lgalabru)
|
||||
unimplemented!()
|
||||
}
|
||||
BitcoinPredicateType::Protocol(Protocols::Stacks(StacksOperations::Pob)) => {
|
||||
for op in tx.metadata.stacks_operations.iter() {
|
||||
if let StacksBaseChainOperation::PobBlockCommitment(_) = op {
|
||||
return true;
|
||||
@@ -323,7 +353,7 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::Pox(PoxPredicate::Any) => {
|
||||
BitcoinPredicateType::Protocol(Protocols::Stacks(StacksOperations::Pox)) => {
|
||||
for op in tx.metadata.stacks_operations.iter() {
|
||||
if let StacksBaseChainOperation::PoxBlockCommitment(_) = op {
|
||||
return true;
|
||||
@@ -331,45 +361,9 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::Pox(PoxPredicate::Recipient(MatchingRule::Equals(address))) => {
|
||||
for op in tx.metadata.stacks_operations.iter() {
|
||||
if let StacksBaseChainOperation::PoxBlockCommitment(commitment) = op {
|
||||
for reward in commitment.rewards.iter() {
|
||||
if reward.recipient.eq(address) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::Pox(PoxPredicate::Recipient(MatchingRule::StartsWith(
|
||||
prefix,
|
||||
))) => {
|
||||
for op in tx.metadata.stacks_operations.iter() {
|
||||
if let StacksBaseChainOperation::PoxBlockCommitment(commitment) = op {
|
||||
for reward in commitment.rewards.iter() {
|
||||
if reward.recipient.starts_with(prefix) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::Pox(PoxPredicate::Recipient(MatchingRule::EndsWith(suffix))) => {
|
||||
for op in tx.metadata.stacks_operations.iter() {
|
||||
if let StacksBaseChainOperation::PoxBlockCommitment(commitment) = op {
|
||||
for reward in commitment.rewards.iter() {
|
||||
if reward.recipient.ends_with(suffix) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::KeyRegistration(KeyRegistrationPredicate::Any) => {
|
||||
BitcoinPredicateType::Protocol(Protocols::Stacks(
|
||||
StacksOperations::KeyRegistration,
|
||||
)) => {
|
||||
for op in tx.metadata.stacks_operations.iter() {
|
||||
if let StacksBaseChainOperation::KeyRegistration(_) = op {
|
||||
return true;
|
||||
@@ -377,7 +371,7 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::TransferSTX(TransferSTXPredicate::Any) => {
|
||||
BitcoinPredicateType::Protocol(Protocols::Stacks(StacksOperations::TransferSTX)) => {
|
||||
for op in tx.metadata.stacks_operations.iter() {
|
||||
if let StacksBaseChainOperation::TransferSTX(_) = op {
|
||||
return true;
|
||||
@@ -385,7 +379,7 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::LockSTX(LockSTXPredicate::Any) => {
|
||||
BitcoinPredicateType::Protocol(Protocols::Stacks(StacksOperations::LockSTX)) => {
|
||||
for op in tx.metadata.stacks_operations.iter() {
|
||||
if let StacksBaseChainOperation::LockSTX(_) = op {
|
||||
return true;
|
||||
@@ -393,6 +387,16 @@ impl BitcoinChainhookSpecification {
|
||||
}
|
||||
false
|
||||
}
|
||||
BitcoinPredicateType::Protocol(Protocols::Ordinal(
|
||||
OrdinalOperations::NewInscription,
|
||||
)) => {
|
||||
for op in tx.metadata.ordinal_operations.iter() {
|
||||
if let OrdinalOperation::InscriptionReveal(_) = op {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl ChainhookConfig {
|
||||
|
||||
pub fn get_serialized_bitcoin_predicates(
|
||||
&self,
|
||||
) -> Vec<(&String, &BitcoinNetwork, &BitcoinTransactionFilterPredicate)> {
|
||||
) -> Vec<(&String, &BitcoinNetwork, &BitcoinPredicateType)> {
|
||||
let mut bitcoin = vec![];
|
||||
for chainhook in self.bitcoin_chainhooks.iter() {
|
||||
bitcoin.push((&chainhook.uuid, &chainhook.network, &chainhook.predicate));
|
||||
@@ -189,7 +189,7 @@ pub struct BitcoinChainhookSpecification {
|
||||
pub end_block: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expire_after_occurrence: Option<u64>,
|
||||
pub predicate: BitcoinTransactionFilterPredicate,
|
||||
pub predicate: BitcoinPredicateType,
|
||||
pub action: HookAction,
|
||||
}
|
||||
|
||||
@@ -276,42 +276,82 @@ impl ScriptTemplate {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct BitcoinTransactionFilterPredicate {
|
||||
pub scope: Scope,
|
||||
#[serde(flatten)]
|
||||
pub kind: BitcoinPredicateType,
|
||||
pub predicate: BitcoinPredicateType,
|
||||
}
|
||||
|
||||
impl BitcoinTransactionFilterPredicate {
|
||||
pub fn new(scope: Scope, kind: BitcoinPredicateType) -> BitcoinTransactionFilterPredicate {
|
||||
BitcoinTransactionFilterPredicate { scope, kind }
|
||||
pub fn new(predicate: BitcoinPredicateType) -> BitcoinTransactionFilterPredicate {
|
||||
BitcoinTransactionFilterPredicate { predicate }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(tag = "type", content = "rule")]
|
||||
pub enum BitcoinPredicateType {
|
||||
TransactionIdentifierHash(ExactMatchingRule),
|
||||
Txid(ExactMatchingRule),
|
||||
Scope(Scopes),
|
||||
Protocol(Protocols),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Scopes {
|
||||
Inputs(InputPredicate),
|
||||
Outputs(OutputPredicate),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum InputPredicate {
|
||||
Txid(TxinPredicate),
|
||||
WitnessScript(MatchingRule),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OutputPredicate {
|
||||
OpReturn(MatchingRule),
|
||||
P2pkh(ExactMatchingRule),
|
||||
P2sh(ExactMatchingRule),
|
||||
P2wpkh(ExactMatchingRule),
|
||||
P2wsh(ExactMatchingRule),
|
||||
Pox(PoxPredicate),
|
||||
Pob(PobPredicate),
|
||||
KeyRegistration(KeyRegistrationPredicate),
|
||||
TransferSTX(TransferSTXPredicate),
|
||||
LockSTX(LockSTXPredicate),
|
||||
}
|
||||
|
||||
pub fn get_canonical_magic_bytes(network: &BitcoinNetwork) -> [u8; 2] {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Protocols {
|
||||
Stacks(StacksOperations),
|
||||
Ordinal(OrdinalOperations),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StacksOperations {
|
||||
Pox,
|
||||
Pob,
|
||||
KeyRegistration,
|
||||
TransferSTX,
|
||||
LockSTX,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum OrdinalOperations {
|
||||
NewInscription,
|
||||
}
|
||||
|
||||
pub fn get_stacks_canonical_magic_bytes(network: &BitcoinNetwork) -> [u8; 2] {
|
||||
match network {
|
||||
BitcoinNetwork::Mainnet => ['X' as u8, '2' as u8],
|
||||
BitcoinNetwork::Testnet => ['T' as u8, '2' as u8],
|
||||
BitcoinNetwork::Regtest => ['i' as u8, 'd' as u8],
|
||||
BitcoinNetwork::Mainnet => *b"X2",
|
||||
BitcoinNetwork::Testnet => *b"T2",
|
||||
BitcoinNetwork::Regtest => *b"id",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ordinal_canonical_magic_bytes() -> (usize, [u8; 3]) {
|
||||
return (37, *b"ord");
|
||||
}
|
||||
|
||||
pub struct PoxConfig {
|
||||
pub genesis_block_height: u64,
|
||||
pub prepare_phase_len: u64,
|
||||
@@ -386,33 +426,9 @@ impl TryFrom<u8> for StacksOpcodes {
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum KeyRegistrationPredicate {
|
||||
Any,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TransferSTXPredicate {
|
||||
Any,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum LockSTXPredicate {
|
||||
Any,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PoxPredicate {
|
||||
Any,
|
||||
Recipient(MatchingRule),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PobPredicate {
|
||||
Any,
|
||||
pub struct TxinPredicate {
|
||||
pub txid: String,
|
||||
pub vout: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::chainhooks::types::{
|
||||
BitcoinChainhookSpecification, BitcoinPredicateType, BitcoinTransactionFilterPredicate,
|
||||
ChainhookConfig, ChainhookSpecification, ExactMatchingRule, HookAction, Scope,
|
||||
StacksChainhookSpecification, StacksContractCallBasedPredicate,
|
||||
ChainhookConfig, ChainhookSpecification, ExactMatchingRule, HookAction, OutputPredicate, Scope,
|
||||
Scopes, StacksChainhookSpecification, StacksContractCallBasedPredicate,
|
||||
StacksTransactionFilterPredicate,
|
||||
};
|
||||
use crate::indexer::tests::helpers::transactions::generate_test_tx_bitcoin_p2pkh_transfer;
|
||||
@@ -89,10 +89,9 @@ fn bitcoin_chainhook_p2pkh(
|
||||
start_block: None,
|
||||
end_block: None,
|
||||
expire_after_occurrence,
|
||||
predicate: BitcoinTransactionFilterPredicate {
|
||||
scope: Scope::Outputs,
|
||||
kind: BitcoinPredicateType::P2pkh(ExactMatchingRule::Equals(address.to_string())),
|
||||
},
|
||||
predicate: BitcoinPredicateType::Scope(Scopes::Outputs(OutputPredicate::P2pkh(
|
||||
ExactMatchingRule::Equals(address.to_string()),
|
||||
))),
|
||||
action: HookAction::Noop,
|
||||
};
|
||||
spec
|
||||
|
||||
@@ -268,9 +268,26 @@ pub struct BitcoinTransactionMetadata {
|
||||
pub inputs: Vec<TxIn>,
|
||||
pub outputs: Vec<TxOut>,
|
||||
pub stacks_operations: Vec<StacksBaseChainOperation>,
|
||||
pub ordinal_operations: Vec<OrdinalOperation>,
|
||||
pub proof: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub enum OrdinalOperation {
|
||||
InscriptionCommit(OrdinalInscriptionCommitData),
|
||||
InscriptionReveal(OrdinalInscriptionRevealData),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct OrdinalInscriptionCommitData {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct OrdinalInscriptionRevealData {
|
||||
pub satoshi_point: String,
|
||||
pub content_type: String,
|
||||
pub content: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub enum StacksBaseChainOperation {
|
||||
PoxBlockCommitment(PoxBlockCommitmentData),
|
||||
|
||||
Reference in New Issue
Block a user