chainhook_types/
rosetta.rs

1use super::bitcoin::{TxIn, TxOut};
2use crate::contract_interface::ContractInterface;
3use crate::ordinals::OrdinalOperation;
4use crate::{events::*, Brc20Operation, StacksStackerDbChunk, DEFAULT_STACKS_NODE_RPC};
5use schemars::JsonSchema;
6use std::cmp::Ordering;
7use std::collections::HashSet;
8use std::fmt::Display;
9use std::hash::{Hash, Hasher};
10
11/// BlockIdentifier uniquely identifies a block in a particular network.
12#[derive(Debug, Clone, Deserialize, Serialize, Default)]
13pub struct BlockIdentifier {
14    /// Also known as the block height.
15    pub index: u64,
16    pub hash: String,
17}
18
19impl BlockIdentifier {
20    pub fn get_hash_bytes_str(&self) -> &str {
21        &self.hash[2..]
22    }
23
24    pub fn get_hash_bytes(&self) -> Vec<u8> {
25        hex::decode(&self.get_hash_bytes_str()).unwrap()
26    }
27}
28
29impl Display for BlockIdentifier {
30    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
31        write!(
32            f,
33            "Block #{} ({}...{})",
34            self.index,
35            &self.hash.as_str()[0..6],
36            &self.hash.as_str()[62..]
37        )
38    }
39}
40
41impl Hash for BlockIdentifier {
42    fn hash<H: Hasher>(&self, state: &mut H) {
43        self.hash.hash(state);
44    }
45}
46
47impl Ord for BlockIdentifier {
48    fn cmp(&self, other: &Self) -> Ordering {
49        (other.index, &other.hash).cmp(&(self.index, &self.hash))
50    }
51}
52
53impl PartialOrd for BlockIdentifier {
54    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
55        Some(other.cmp(self))
56    }
57}
58
59impl PartialEq for BlockIdentifier {
60    fn eq(&self, other: &Self) -> bool {
61        self.hash == other.hash
62    }
63}
64
65impl Eq for BlockIdentifier {}
66
67/// StacksBlock contain an array of Transactions that occurred at a particular
68/// BlockIdentifier. A hard requirement for blocks returned by Rosetta
69/// implementations is that they MUST be _inalterable_: once a client has
70/// requested and received a block identified by a specific BlockIndentifier,
71/// all future calls for that same BlockIdentifier must return the same block
72/// contents.
73#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
74pub struct StacksBlockData {
75    pub block_identifier: BlockIdentifier,
76    pub parent_block_identifier: BlockIdentifier,
77    /// The timestamp of the block in milliseconds since the Unix Epoch. The
78    /// timestamp is stored in milliseconds because some blockchains produce
79    /// blocks more often than once a second.
80    pub timestamp: i64,
81    pub transactions: Vec<StacksTransactionData>,
82    pub metadata: StacksBlockMetadata,
83}
84
85/// StacksMicroblock contain an array of Transactions that occurred at a particular
86/// BlockIdentifier.
87#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
88pub struct StacksMicroblockData {
89    pub block_identifier: BlockIdentifier,
90    pub parent_block_identifier: BlockIdentifier,
91    /// The timestamp of the block in milliseconds since the Unix Epoch. The
92    /// timestamp is stored in milliseconds because some blockchains produce
93    /// blocks more often than once a second.
94    pub timestamp: i64,
95    pub transactions: Vec<StacksTransactionData>,
96    pub metadata: StacksMicroblockMetadata,
97}
98
99#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
100pub struct StacksMicroblockMetadata {
101    pub anchor_block_identifier: BlockIdentifier,
102}
103
104#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
105pub struct StacksMicroblocksTrail {
106    pub microblocks: Vec<StacksMicroblockData>,
107}
108
109#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
110pub struct StacksBlockMetadata {
111    pub bitcoin_anchor_block_identifier: BlockIdentifier,
112    pub pox_cycle_index: u32,
113    pub pox_cycle_position: u32,
114    pub pox_cycle_length: u32,
115    pub confirm_microblock_identifier: Option<BlockIdentifier>,
116    pub stacks_block_hash: String,
117
118    // Fields included in Nakamoto block headers
119    pub block_time: Option<u64>,
120    pub signer_bitvec: Option<String>,
121    pub signer_signature: Option<Vec<String>>,
122    pub signer_public_keys: Option<Vec<String>>,
123
124    // Available starting in epoch3, only included in blocks where the pox cycle rewards are first calculated
125    pub cycle_number: Option<u64>,
126    pub reward_set: Option<StacksBlockMetadataRewardSet>,
127
128    // Available in /new_block messages sent from stacks-core v3.0 and newer
129    pub tenure_height: Option<u64>,
130}
131
132#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
133pub struct StacksBlockMetadataRewardSet {
134    pub pox_ustx_threshold: String,
135    pub rewarded_addresses: Vec<String>,
136    pub signers: Option<Vec<StacksBlockMetadataRewardSetSigner>>,
137}
138
139#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
140pub struct StacksBlockMetadataRewardSetSigner {
141    pub signing_key: String,
142    pub weight: u32,
143    pub stacked_amt: String,
144}
145
146/// BitcoinBlock contain an array of Transactions that occurred at a particular
147/// BlockIdentifier. A hard requirement for blocks returned by Rosetta
148/// implementations is that they MUST be _inalterable_: once a client has
149/// requested and received a block identified by a specific BlockIndentifier,
150/// all future calls for that same BlockIdentifier must return the same block
151/// contents.
152#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
153pub struct BitcoinBlockData {
154    pub block_identifier: BlockIdentifier,
155    pub parent_block_identifier: BlockIdentifier,
156    /// The timestamp of the block in milliseconds since the Unix Epoch. The
157    /// timestamp is stored in milliseconds because some blockchains produce
158    /// blocks more often than once a second.
159    pub timestamp: u32,
160    pub transactions: Vec<BitcoinTransactionData>,
161    pub metadata: BitcoinBlockMetadata,
162}
163
164#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
165pub struct BitcoinBlockMetadata {
166    pub network: BitcoinNetwork,
167}
168
169/// The timestamp of the block in milliseconds since the Unix Epoch. The
170/// timestamp is stored in milliseconds because some blockchains produce blocks
171/// more often than once a second.
172#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize)]
173pub struct Timestamp(i64);
174
175/// Transactions contain an array of Operations that are attributable to the
176/// same TransactionIdentifier.
177#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
178pub struct StacksTransactionData {
179    pub transaction_identifier: TransactionIdentifier,
180    pub operations: Vec<Operation>,
181    /// Transactions that are related to other transactions should include the
182    /// transaction_identifier of these transactions in the metadata.
183    pub metadata: StacksTransactionMetadata,
184}
185
186#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
187#[serde(tag = "type", content = "data")]
188pub enum StacksTransactionKind {
189    ContractCall(StacksContractCallData),
190    ContractDeployment(StacksContractDeploymentData),
191    NativeTokenTransfer,
192    Coinbase,
193    TenureChange,
194    BitcoinOp(BitcoinOpData),
195    Unsupported,
196}
197
198#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
199#[serde(tag = "type", content = "data")]
200pub enum BitcoinOpData {
201    StackSTX(StackSTXData),
202    DelegateStackSTX(DelegateStackSTXData),
203}
204
205#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
206pub struct StackSTXData {
207    pub locked_amount: String,
208    pub unlock_height: String,
209    pub stacking_address: String,
210}
211
212#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
213pub struct DelegateStackSTXData {
214    pub stacking_address: String,
215    pub amount: String,
216    pub delegate: String,
217    pub pox_address: Option<String>,
218    pub unlock_height: Option<String>,
219}
220
221#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
222pub struct StacksContractCallData {
223    pub contract_identifier: String,
224    pub method: String,
225    pub args: Vec<String>,
226}
227
228#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
229pub struct StacksContractDeploymentData {
230    pub contract_identifier: String,
231    pub code: String,
232}
233
234/// Extra data for Transaction
235#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
236pub struct StacksTransactionMetadata {
237    pub success: bool,
238    pub raw_tx: String,
239    pub result: String,
240    pub sender: String,
241    pub nonce: u64,
242    pub fee: u64,
243    pub kind: StacksTransactionKind,
244    pub receipt: StacksTransactionReceipt,
245    pub description: String,
246    #[serde(skip_serializing_if = "Option::is_none")]
247    pub sponsor: Option<String>,
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub execution_cost: Option<StacksTransactionExecutionCost>,
250    pub position: StacksTransactionPosition,
251    pub proof: Option<String>,
252    #[serde(skip_serializing_if = "Option::is_none")]
253    pub contract_abi: Option<ContractInterface>,
254}
255
256/// TODO
257#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
258#[serde(untagged)]
259pub enum StacksTransactionPosition {
260    AnchorBlock(AnchorBlockPosition),
261    MicroBlock(MicroBlockPosition),
262}
263
264impl StacksTransactionPosition {
265    pub fn anchor_block(index: usize) -> StacksTransactionPosition {
266        StacksTransactionPosition::AnchorBlock(AnchorBlockPosition { index })
267    }
268
269    pub fn micro_block(
270        micro_block_identifier: BlockIdentifier,
271        index: usize,
272    ) -> StacksTransactionPosition {
273        StacksTransactionPosition::MicroBlock(MicroBlockPosition {
274            micro_block_identifier,
275            index,
276        })
277    }
278}
279
280#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
281pub struct AnchorBlockPosition {
282    index: usize,
283}
284
285#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
286pub struct MicroBlockPosition {
287    micro_block_identifier: BlockIdentifier,
288    index: usize,
289}
290
291#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
292pub struct StacksTransactionExecutionCost {
293    pub write_length: u64,
294    pub write_count: u64,
295    pub read_length: u64,
296    pub read_count: u64,
297    pub runtime: u64,
298}
299
300/// Extra event data for Transaction
301#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Default)]
302pub struct StacksTransactionReceipt {
303    pub mutated_contracts_radius: HashSet<String>,
304    pub mutated_assets_radius: HashSet<String>,
305    pub contract_calls_stack: HashSet<String>,
306    pub events: Vec<StacksTransactionEvent>,
307}
308
309impl StacksTransactionReceipt {
310    pub fn new(
311        mutated_contracts_radius: HashSet<String>,
312        mutated_assets_radius: HashSet<String>,
313        events: Vec<StacksTransactionEvent>,
314    ) -> StacksTransactionReceipt {
315        StacksTransactionReceipt {
316            mutated_contracts_radius,
317            mutated_assets_radius,
318            contract_calls_stack: HashSet::new(),
319            events,
320        }
321    }
322}
323
324/// Transactions contain an array of Operations that are attributable to the
325/// same TransactionIdentifier.
326#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
327pub struct BitcoinTransactionData {
328    pub transaction_identifier: TransactionIdentifier,
329    pub operations: Vec<Operation>,
330    /// Transactions that are related to other transactions should include the
331    /// transaction_identifier of these transactions in the metadata.
332    pub metadata: BitcoinTransactionMetadata,
333}
334
335/// Extra data for Transaction
336#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
337pub struct BitcoinTransactionMetadata {
338    pub inputs: Vec<TxIn>,
339    pub outputs: Vec<TxOut>,
340    pub stacks_operations: Vec<StacksBaseChainOperation>,
341    pub ordinal_operations: Vec<OrdinalOperation>,
342    pub brc20_operation: Option<Brc20Operation>,
343    pub proof: Option<String>,
344    pub fee: u64,
345    pub index: u32,
346}
347
348#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
349#[serde(rename_all = "snake_case")]
350pub enum StacksBaseChainOperation {
351    BlockCommitted(StacksBlockCommitmentData),
352    LeaderRegistered(KeyRegistrationData),
353    StxTransferred(TransferSTXData),
354    StxLocked(LockSTXData),
355}
356
357#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
358#[serde(rename_all = "snake_case")]
359pub struct StacksBlockCommitmentData {
360    pub block_hash: String,
361    pub pox_cycle_index: u64,
362    pub pox_cycle_length: u64,
363    pub pox_cycle_position: u64,
364    pub pox_sats_burnt: u64,
365    pub pox_sats_transferred: Vec<PoxReward>,
366    // pub mining_address_pre_commit: Option<String>,
367    pub mining_address_post_commit: Option<String>,
368    pub mining_sats_left: u64,
369}
370
371#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
372#[serde(rename_all = "snake_case")]
373pub struct PoxReward {
374    pub recipient_address: String,
375    pub amount: u64,
376}
377
378#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
379pub struct KeyRegistrationData;
380
381#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
382pub struct PobBlockCommitmentData {
383    pub signers: Vec<String>,
384    pub stacks_block_hash: String,
385    pub amount: u64,
386}
387
388#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
389pub struct BlockCommitmentData {
390    pub stacks_block_hash: String,
391}
392
393#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
394pub struct TransferSTXData {
395    pub sender: String,
396    pub recipient: String,
397    pub amount: String,
398}
399
400#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
401pub struct LockSTXData {
402    pub sender: String,
403    pub amount: String,
404    pub duration: u64,
405}
406
407/// The transaction_identifier uniquely identifies a transaction in a particular
408/// network and block or in the mempool.
409#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash, PartialOrd, Ord)]
410pub struct TransactionIdentifier {
411    /// Any transactions that are attributable only to a block (ex: a block
412    /// event) should use the hash of the block as the identifier.
413    pub hash: String,
414}
415
416impl TransactionIdentifier {
417    pub fn new(txid: &str) -> Self {
418        let lowercased_txid = txid.to_lowercase();
419        Self {
420            hash: match lowercased_txid.starts_with("0x") {
421                true => lowercased_txid,
422                false => format!("0x{}", lowercased_txid),
423            },
424        }
425    }
426
427    pub fn get_hash_bytes_str(&self) -> &str {
428        &self.hash[2..]
429    }
430
431    pub fn get_hash_bytes(&self) -> Vec<u8> {
432        hex::decode(&self.get_hash_bytes_str()).unwrap()
433    }
434
435    pub fn get_8_hash_bytes(&self) -> [u8; 8] {
436        let bytes = self.get_hash_bytes();
437        [
438            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
439        ]
440    }
441}
442
443#[derive(
444    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, strum::EnumIter, strum::IntoStaticStr,
445)]
446#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
447pub enum OperationType {
448    Credit,
449    Debit,
450    Lock,
451}
452
453#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)]
454pub struct OperationMetadata {
455    /// Has to be specified for ADD_KEY, REMOVE_KEY, and STAKE operations
456    #[serde(skip_serializing_if = "Option::is_none")]
457    pub public_key: Option<PublicKey>,
458    // TODO(lgalabru): ???
459    //#[serde(skip_serializing_if = "Option::is_none")]
460    // pub access_key: Option<TODO>,
461    /// Has to be specified for DEPLOY_CONTRACT operation
462    #[serde(skip_serializing_if = "Option::is_none")]
463    pub code: Option<String>,
464    /// Has to be specified for FUNCTION_CALL operation
465    #[serde(skip_serializing_if = "Option::is_none")]
466    pub method_name: Option<String>,
467    /// Has to be specified for FUNCTION_CALL operation
468    #[serde(skip_serializing_if = "Option::is_none")]
469    pub args: Option<String>,
470}
471
472/// PublicKey contains a public key byte array for a particular CurveType
473/// encoded in hex. Note that there is no PrivateKey struct as this is NEVER the
474/// concern of an implementation.
475#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
476pub struct PublicKey {
477    /// Hex-encoded public key bytes in the format specified by the CurveType.
478    pub hex_bytes: Option<String>,
479    pub curve_type: CurveType,
480}
481
482/// CurveType is the type of cryptographic curve associated with a PublicKey.
483#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
484#[serde(rename_all = "lowercase")]
485pub enum CurveType {
486    /// `y (255-bits) || x-sign-bit (1-bit)` - `32 bytes` (<https://ed25519.cr.yp.to/ed25519-20110926.pdf>)
487    Edwards25519,
488    /// SEC compressed - `33 bytes` (<https://secg.org/sec1-v2.pdf#subsubsection.2.3.3>)
489    Secp256k1,
490}
491
492/// Operations contain all balance-changing information within a transaction.
493/// They are always one-sided (only affect 1 AccountIdentifier) and can
494/// succeed or fail independently from a Transaction.
495#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
496pub struct Operation {
497    pub operation_identifier: OperationIdentifier,
498
499    /// Restrict referenced related_operations to identifier indexes < the
500    /// current operation_identifier.index. This ensures there exists a clear
501    /// DAG-structure of relations. Since operations are one-sided, one could
502    /// imagine relating operations in a single transfer or linking operations
503    /// in a call tree.
504    #[serde(skip_serializing_if = "Option::is_none")]
505    pub related_operations: Option<Vec<OperationIdentifier>>,
506
507    /// The network-specific type of the operation. Ensure that any type that
508    /// can be returned here is also specified in the NetworkStatus. This can
509    /// be very useful to downstream consumers that parse all block data.
510    #[serde(rename = "type")]
511    pub type_: OperationType,
512
513    /// The network-specific status of the operation. Status is not defined on
514    /// the transaction object because blockchains with smart contracts may have
515    /// transactions that partially apply. Blockchains with atomic transactions
516    /// (all operations succeed or all operations fail) will have the same
517    /// status for each operation.
518    #[serde(skip_serializing_if = "Option::is_none")]
519    pub status: Option<OperationStatusKind>,
520
521    pub account: AccountIdentifier,
522
523    #[serde(skip_serializing_if = "Option::is_none")]
524    pub amount: Option<Amount>,
525
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub metadata: Option<OperationMetadata>,
528}
529
530/// The operation_identifier uniquely identifies an operation within a
531/// transaction.
532#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
533pub struct OperationIdentifier {
534    /// The operation index is used to ensure each operation has a unique
535    /// identifier within a transaction. This index is only relative to the
536    /// transaction and NOT GLOBAL. The operations in each transaction should
537    /// start from index 0. To clarify, there may not be any notion of an
538    /// operation index in the blockchain being described.
539    pub index: u32,
540
541    /// Some blockchains specify an operation index that is essential for
542    /// client use. For example, Bitcoin uses a network_index to identify
543    /// which UTXO was used in a transaction.  network_index should not be
544    /// populated if there is no notion of an operation index in a blockchain
545    /// (typically most account-based blockchains).
546    #[serde(skip_serializing_if = "Option::is_none")]
547    pub network_index: Option<i64>,
548}
549
550#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, strum::EnumIter)]
551#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
552pub enum OperationStatusKind {
553    Success,
554}
555
556/// The account_identifier uniquely identifies an account within a network. All
557/// fields in the account_identifier are utilized to determine this uniqueness
558/// (including the metadata field, if populated).
559#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
560pub struct AccountIdentifier {
561    /// The address may be a cryptographic public key (or some encoding of it)
562    /// or a provided username.
563    pub address: String,
564
565    #[serde(skip_serializing_if = "Option::is_none")]
566    pub sub_account: Option<SubAccountIdentifier>,
567    /* Rosetta Spec also optionally provides:
568     *
569     * /// Blockchains that utilize a username model (where the address is not a
570     * /// derivative of a cryptographic public key) should specify the public
571     * /// key(s) owned by the address in metadata.
572     * #[serde(skip_serializing_if = "Option::is_none")]
573     * pub metadata: Option<serde_json::Value>, */
574}
575
576/// An account may have state specific to a contract address (ERC-20 token)
577/// and/or a stake (delegated balance). The sub_account_identifier should
578/// specify which state (if applicable) an account instantiation refers to.
579#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
580pub struct SubAccountIdentifier {
581    /// The SubAccount address may be a cryptographic value or some other
582    /// identifier (ex: bonded) that uniquely specifies a SubAccount.
583    pub address: SubAccount,
584    /* Rosetta Spec also optionally provides:
585     *
586     * /// If the SubAccount address is not sufficient to uniquely specify a
587     * /// SubAccount, any other identifying information can be stored here.  It is
588     * /// important to note that two SubAccounts with identical addresses but
589     * /// differing metadata will not be considered equal by clients.
590     * #[serde(skip_serializing_if = "Option::is_none")]
591     * pub metadata: Option<serde_json::Value>, */
592}
593
594#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
595#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
596pub enum SubAccount {
597    LiquidBalanceForStorage,
598    Locked,
599}
600
601/// Amount is some Value of a Currency. It is considered invalid to specify a
602/// Value without a Currency.
603#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
604pub struct Amount {
605    /// Value of the transaction in atomic units represented as an
606    /// arbitrary-sized signed integer.  For example, 1 BTC would be represented
607    /// by a value of 100000000.
608    pub value: u128,
609
610    pub currency: Currency,
611    /* Rosetta Spec also optionally provides:
612     *
613     * #[serde(skip_serializing_if = "Option::is_none")]
614     * pub metadata: Option<serde_json::Value>, */
615}
616
617/// Currency is composed of a canonical Symbol and Decimals. This Decimals value
618/// is used to convert an Amount.Value from atomic units (Satoshis) to standard
619/// units (Bitcoins).
620#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
621pub struct Currency {
622    /// Canonical symbol associated with a currency.
623    pub symbol: String,
624
625    /// Number of decimal places in the standard unit representation of the
626    /// amount.  For example, BTC has 8 decimals. Note that it is not possible
627    /// to represent the value of some currency in atomic units that is not base
628    /// 10.
629    pub decimals: u32,
630
631    /// Any additional information related to the currency itself.
632    #[serde(skip_serializing_if = "Option::is_none")]
633    pub metadata: Option<CurrencyMetadata>,
634}
635
636#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
637#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
638pub enum CurrencyStandard {
639    Sip09,
640    Sip10,
641    None,
642}
643
644#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
645pub struct CurrencyMetadata {
646    pub asset_class_identifier: String,
647    pub asset_identifier: Option<String>,
648    pub standard: CurrencyStandard,
649}
650
651#[allow(dead_code)]
652#[derive(Debug, Clone, PartialEq, Serialize)]
653pub enum BlockchainEvent {
654    BlockchainUpdatedWithHeaders(BlockchainUpdatedWithHeaders),
655    BlockchainUpdatedWithReorg(BlockchainUpdatedWithReorg),
656}
657
658#[derive(Debug, Clone, PartialEq, Serialize)]
659pub struct BlockchainUpdatedWithHeaders {
660    pub new_headers: Vec<BlockHeader>,
661    pub confirmed_headers: Vec<BlockHeader>,
662}
663
664#[derive(Debug, Clone, PartialEq, Serialize)]
665pub struct BlockchainUpdatedWithReorg {
666    pub headers_to_rollback: Vec<BlockHeader>,
667    pub headers_to_apply: Vec<BlockHeader>,
668    pub confirmed_headers: Vec<BlockHeader>,
669}
670
671#[derive(Clone, Debug, PartialEq, Serialize)]
672#[serde(tag = "type", content = "data")]
673pub enum StacksNonConsensusEventPayloadData {
674    SignerMessage(StacksStackerDbChunk),
675}
676
677#[derive(Clone, Debug, PartialEq, Serialize)]
678pub struct StacksNonConsensusEventData {
679    pub payload: StacksNonConsensusEventPayloadData,
680    pub received_at_ms: u64,
681    pub received_at_block: BlockIdentifier,
682}
683
684#[derive(Debug, Clone, PartialEq, Serialize)]
685pub struct BlockHeader {
686    pub block_identifier: BlockIdentifier,
687    pub parent_block_identifier: BlockIdentifier,
688}
689
690#[allow(dead_code)]
691#[derive(Debug, Clone, PartialEq, Serialize)]
692pub enum BitcoinChainEvent {
693    ChainUpdatedWithBlocks(BitcoinChainUpdatedWithBlocksData),
694    ChainUpdatedWithReorg(BitcoinChainUpdatedWithReorgData),
695}
696
697#[derive(Debug, Clone, PartialEq, Serialize)]
698pub struct BitcoinChainUpdatedWithBlocksData {
699    pub new_blocks: Vec<BitcoinBlockData>,
700    pub confirmed_blocks: Vec<BitcoinBlockData>,
701}
702
703#[derive(Debug, Clone, PartialEq, Serialize)]
704pub struct BitcoinChainUpdatedWithReorgData {
705    pub blocks_to_rollback: Vec<BitcoinBlockData>,
706    pub blocks_to_apply: Vec<BitcoinBlockData>,
707    pub confirmed_blocks: Vec<BitcoinBlockData>,
708}
709
710#[derive(Debug, Clone, PartialEq, Serialize)]
711pub struct StacksChainUpdatedWithNonConsensusEventsData {
712    pub events: Vec<StacksNonConsensusEventData>,
713}
714
715#[allow(dead_code)]
716#[derive(Debug, Clone, PartialEq, Serialize)]
717pub enum StacksChainEvent {
718    ChainUpdatedWithBlocks(StacksChainUpdatedWithBlocksData),
719    ChainUpdatedWithReorg(StacksChainUpdatedWithReorgData),
720    ChainUpdatedWithMicroblocks(StacksChainUpdatedWithMicroblocksData),
721    ChainUpdatedWithMicroblocksReorg(StacksChainUpdatedWithMicroblocksReorgData),
722    ChainUpdatedWithNonConsensusEvents(StacksChainUpdatedWithNonConsensusEventsData),
723}
724
725impl StacksChainEvent {
726    pub fn get_confirmed_blocks(self) -> Vec<StacksBlockData> {
727        match self {
728            StacksChainEvent::ChainUpdatedWithBlocks(event) => event.confirmed_blocks,
729            StacksChainEvent::ChainUpdatedWithReorg(event) => event.confirmed_blocks,
730            _ => vec![],
731        }
732    }
733
734    pub fn get_latest_block_identifier(&self) -> Option<&BlockIdentifier> {
735        match self {
736            StacksChainEvent::ChainUpdatedWithBlocks(event) => event
737                .new_blocks
738                .last()
739                .and_then(|b| Some(&b.block.block_identifier)),
740            StacksChainEvent::ChainUpdatedWithReorg(event) => event
741                .blocks_to_apply
742                .last()
743                .and_then(|b| Some(&b.block.block_identifier)),
744            StacksChainEvent::ChainUpdatedWithMicroblocks(event) => event
745                .new_microblocks
746                .first()
747                .and_then(|b| Some(&b.metadata.anchor_block_identifier)),
748            StacksChainEvent::ChainUpdatedWithMicroblocksReorg(event) => event
749                .microblocks_to_apply
750                .first()
751                .and_then(|b| Some(&b.metadata.anchor_block_identifier)),
752            StacksChainEvent::ChainUpdatedWithNonConsensusEvents(_) => None,
753        }
754    }
755}
756
757#[derive(Debug, Clone, PartialEq, Serialize)]
758pub struct StacksBlockUpdate {
759    pub block: StacksBlockData,
760    pub parent_microblocks_to_rollback: Vec<StacksMicroblockData>,
761    pub parent_microblocks_to_apply: Vec<StacksMicroblockData>,
762}
763
764impl StacksBlockUpdate {
765    pub fn new(block: StacksBlockData) -> StacksBlockUpdate {
766        StacksBlockUpdate {
767            block,
768            parent_microblocks_to_rollback: vec![],
769            parent_microblocks_to_apply: vec![],
770        }
771    }
772}
773
774#[derive(Debug, Clone, PartialEq, Serialize)]
775pub struct StacksChainUpdatedWithBlocksData {
776    pub new_blocks: Vec<StacksBlockUpdate>,
777    pub confirmed_blocks: Vec<StacksBlockData>,
778}
779
780#[derive(Debug, Clone, PartialEq, Serialize)]
781pub struct StacksChainUpdatedWithReorgData {
782    pub blocks_to_rollback: Vec<StacksBlockUpdate>,
783    pub blocks_to_apply: Vec<StacksBlockUpdate>,
784    pub confirmed_blocks: Vec<StacksBlockData>,
785}
786
787#[derive(Debug, Clone, PartialEq, Serialize)]
788pub struct StacksChainUpdatedWithMicroblocksData {
789    pub new_microblocks: Vec<StacksMicroblockData>,
790}
791
792#[derive(Debug, Clone, PartialEq, Serialize)]
793pub struct StacksChainUpdatedWithMicroblocksReorgData {
794    pub microblocks_to_rollback: Vec<StacksMicroblockData>,
795    pub microblocks_to_apply: Vec<StacksMicroblockData>,
796}
797
798#[derive(
799    Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize, JsonSchema,
800)]
801#[serde(rename_all = "snake_case")]
802pub enum StacksNetwork {
803    Simnet,
804    Devnet,
805    Testnet,
806    Mainnet,
807}
808
809impl std::fmt::Display for StacksNetwork {
810    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
811        write!(f, "{}", self.as_str())
812    }
813}
814impl StacksNetwork {
815    pub fn from_str(network: &str) -> Result<StacksNetwork, String> {
816        let value = match network {
817            "devnet" => StacksNetwork::Devnet,
818            "testnet" => StacksNetwork::Testnet,
819            "mainnet" => StacksNetwork::Mainnet,
820            "simnet" => StacksNetwork::Simnet,
821            _ => {
822                return Err(format!(
823                    "network '{}' unsupported (mainnet, testnet, devnet, simnet)",
824                    network
825                ))
826            }
827        };
828        Ok(value)
829    }
830
831    pub fn as_str(&self) -> &str {
832        match self {
833            StacksNetwork::Devnet => "devnet",
834            StacksNetwork::Testnet => "testnet",
835            StacksNetwork::Mainnet => "mainnet",
836            StacksNetwork::Simnet => "simnet",
837        }
838    }
839
840    pub fn is_simnet(&self) -> bool {
841        match self {
842            StacksNetwork::Simnet => true,
843            _ => false,
844        }
845    }
846
847    pub fn is_testnet(&self) -> bool {
848        match self {
849            StacksNetwork::Testnet => true,
850            _ => false,
851        }
852    }
853
854    pub fn either_devnet_or_testnet(&self) -> bool {
855        match self {
856            StacksNetwork::Devnet | StacksNetwork::Testnet => true,
857            _ => false,
858        }
859    }
860
861    pub fn either_testnet_or_mainnet(&self) -> bool {
862        match self {
863            StacksNetwork::Mainnet | StacksNetwork::Testnet => true,
864            _ => false,
865        }
866    }
867
868    pub fn is_devnet(&self) -> bool {
869        match self {
870            StacksNetwork::Devnet => true,
871            _ => false,
872        }
873    }
874
875    pub fn is_mainnet(&self) -> bool {
876        match self {
877            StacksNetwork::Mainnet => true,
878            _ => false,
879        }
880    }
881
882    pub fn get_networks(&self) -> (BitcoinNetwork, StacksNetwork) {
883        match &self {
884            StacksNetwork::Simnet => (BitcoinNetwork::Regtest, StacksNetwork::Simnet),
885            StacksNetwork::Devnet => (BitcoinNetwork::Testnet, StacksNetwork::Devnet),
886            StacksNetwork::Testnet => (BitcoinNetwork::Testnet, StacksNetwork::Testnet),
887            StacksNetwork::Mainnet => (BitcoinNetwork::Mainnet, StacksNetwork::Mainnet),
888        }
889    }
890}
891
892#[allow(dead_code)]
893#[derive(
894    Debug, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
895)]
896#[serde(rename_all = "snake_case")]
897pub enum BitcoinNetwork {
898    Regtest,
899    Testnet,
900    Signet,
901    Mainnet,
902}
903
904impl std::fmt::Display for BitcoinNetwork {
905    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
906        write!(f, "{}", self.as_str())
907    }
908}
909impl BitcoinNetwork {
910    pub fn from_str(network: &str) -> Result<BitcoinNetwork, String> {
911        let value = match network {
912            "regtest" => BitcoinNetwork::Regtest,
913            "testnet" => BitcoinNetwork::Testnet,
914            "mainnet" => BitcoinNetwork::Mainnet,
915            "signet" => BitcoinNetwork::Signet,
916            _ => {
917                return Err(format!(
918                    "network '{}' unsupported (mainnet, testnet, regtest, signet)",
919                    network
920                ))
921            }
922        };
923        Ok(value)
924    }
925
926    pub fn as_str(&self) -> &str {
927        match self {
928            BitcoinNetwork::Regtest => "regtest",
929            BitcoinNetwork::Testnet => "testnet",
930            BitcoinNetwork::Mainnet => "mainnet",
931            BitcoinNetwork::Signet => "signet",
932        }
933    }
934}
935
936#[derive(Deserialize, Debug, Clone, PartialEq)]
937pub enum BitcoinBlockSignaling {
938    Stacks(StacksNodeConfig),
939    ZeroMQ(String),
940}
941
942#[derive(Deserialize, Debug, Clone, PartialEq)]
943pub struct StacksNodeConfig {
944    pub rpc_url: String,
945    pub ingestion_port: u16,
946}
947
948impl StacksNodeConfig {
949    pub fn new(rpc_url: String, ingestion_port: u16) -> StacksNodeConfig {
950        StacksNodeConfig {
951            rpc_url,
952            ingestion_port,
953        }
954    }
955
956    pub fn default_localhost(ingestion_port: u16) -> StacksNodeConfig {
957        StacksNodeConfig {
958            rpc_url: DEFAULT_STACKS_NODE_RPC.to_string(),
959            ingestion_port,
960        }
961    }
962}
963
964impl BitcoinBlockSignaling {
965    pub fn should_ignore_bitcoin_block_signaling_through_stacks(&self) -> bool {
966        match &self {
967            BitcoinBlockSignaling::Stacks(_) => false,
968            _ => true,
969        }
970    }
971
972    pub fn is_bitcoind_zmq_block_signaling_expected(&self) -> bool {
973        match &self {
974            BitcoinBlockSignaling::ZeroMQ(_) => false,
975            _ => true,
976        }
977    }
978}