@@ -24,7 +24,9 @@ use std::sync::{Arc, Mutex};
24
24
use std:: thread:: sleep;
25
25
use std:: time:: Duration ;
26
26
27
- use clarity:: vm:: analysis:: contract_interface_builder:: build_contract_interface;
27
+ use clarity:: vm:: analysis:: contract_interface_builder:: {
28
+ build_contract_interface, ContractInterface ,
29
+ } ;
28
30
use clarity:: vm:: costs:: ExecutionCost ;
29
31
use clarity:: vm:: events:: { FTEventType , NFTEventType , STXEventType } ;
30
32
use clarity:: vm:: types:: { AssetIdentifier , QualifiedContractIdentifier , Value } ;
@@ -34,7 +36,9 @@ use rand::Rng;
34
36
use rusqlite:: { params, Connection } ;
35
37
use serde_json:: json;
36
38
use stacks:: burnchains:: { PoxConstants , Txid } ;
37
- use stacks:: chainstate:: burn:: operations:: BlockstackOperationType ;
39
+ use stacks:: chainstate:: burn:: operations:: {
40
+ blockstack_op_extended_serialize_opt, BlockstackOperationType ,
41
+ } ;
38
42
use stacks:: chainstate:: burn:: ConsensusHash ;
39
43
use stacks:: chainstate:: coordinator:: BlockEventDispatcher ;
40
44
use stacks:: chainstate:: nakamoto:: NakamotoBlock ;
@@ -59,6 +63,7 @@ use stacks::libstackerdb::StackerDBChunkData;
59
63
use stacks:: net:: api:: postblock_proposal:: {
60
64
BlockValidateOk , BlockValidateReject , BlockValidateResponse ,
61
65
} ;
66
+ use stacks:: net:: api:: { prefix_hex, prefix_hex_codec, prefix_opt_hex} ;
62
67
use stacks:: net:: atlas:: { Attachment , AttachmentInstance } ;
63
68
use stacks:: net:: http:: HttpRequestContents ;
64
69
use stacks:: net:: httpcore:: { send_http_request, StacksHttpRequest } ;
@@ -95,15 +100,6 @@ pub struct EventObserver {
95
100
pub disable_retries : bool ,
96
101
}
97
102
98
- struct ReceiptPayloadInfo < ' a > {
99
- txid : String ,
100
- success : & ' a str ,
101
- raw_result : String ,
102
- raw_tx : String ,
103
- contract_interface_json : serde_json:: Value ,
104
- burnchain_op_json : serde_json:: Value ,
105
- }
106
-
107
103
const STATUS_RESP_TRUE : & str = "success" ;
108
104
const STATUS_RESP_NOT_COMMITTED : & str = "abort_by_response" ;
109
105
const STATUS_RESP_POST_CONDITION : & str = "abort_by_post_condition" ;
@@ -334,6 +330,39 @@ impl RewardSetEventPayload {
334
330
}
335
331
}
336
332
333
+ #[ derive( Debug , PartialEq , Clone , Serialize , Deserialize ) ]
334
+ pub struct TransactionEventPayload < ' a > {
335
+ #[ serde( with = "prefix_hex" ) ]
336
+ /// The transaction id
337
+ pub txid : Txid ,
338
+ /// The transaction index
339
+ pub tx_index : u32 ,
340
+ /// The transaction status
341
+ pub status : & ' a str ,
342
+ #[ serde( with = "prefix_hex_codec" ) ]
343
+ /// The raw transaction result
344
+ pub raw_result : Value ,
345
+ /// The hex encoded raw transaction
346
+ pub raw_tx : String ,
347
+ /// The contract interface
348
+ pub contract_interface : Option < ContractInterface > ,
349
+ /// The burnchain op
350
+ #[ serde( serialize_with = "blockstack_op_extended_serialize_opt" ) ]
351
+ pub burnchain_op : Option < BlockstackOperationType > ,
352
+ /// The transaction execution cost
353
+ pub execution_cost : ExecutionCost ,
354
+ /// The microblock sequence
355
+ pub microblock_sequence : Option < u16 > ,
356
+ #[ serde( with = "prefix_opt_hex" ) ]
357
+ /// The microblock hash
358
+ pub microblock_hash : Option < BlockHeaderHash > ,
359
+ #[ serde( with = "prefix_opt_hex" ) ]
360
+ /// The microblock parent hash
361
+ pub microblock_parent_hash : Option < BlockHeaderHash > ,
362
+ /// Error information if one occurred in the Clarity VM
363
+ pub vm_error : Option < String > ,
364
+ }
365
+
337
366
#[ cfg( test) ]
338
367
static TEST_EVENT_OBSERVER_SKIP_RETRY : LazyLock < TestFlag < bool > > = LazyLock :: new ( TestFlag :: default) ;
339
368
@@ -573,11 +602,14 @@ impl EventObserver {
573
602
} )
574
603
}
575
604
576
- /// Returns tuple of (txid, success, raw_result, raw_tx, contract_interface_json)
577
- fn generate_payload_info_for_receipt ( receipt : & StacksTransactionReceipt ) -> ReceiptPayloadInfo {
605
+ /// Returns transaction event payload to send for new block or microblock event
606
+ fn make_new_block_txs_payload (
607
+ receipt : & StacksTransactionReceipt ,
608
+ tx_index : u32 ,
609
+ ) -> TransactionEventPayload {
578
610
let tx = & receipt. transaction ;
579
611
580
- let success = match ( receipt. post_condition_aborted , & receipt. result ) {
612
+ let status = match ( receipt. post_condition_aborted , & receipt. result ) {
581
613
( false , Value :: Response ( response_data) ) => {
582
614
if response_data. committed {
583
615
STATUS_RESP_TRUE
@@ -587,77 +619,44 @@ impl EventObserver {
587
619
}
588
620
( true , Value :: Response ( _) ) => STATUS_RESP_POST_CONDITION ,
589
621
_ => {
590
- if let TransactionOrigin :: Stacks ( inner_tx) = & tx {
591
- if let TransactionPayload :: PoisonMicroblock ( ..) = & inner_tx. payload {
592
- STATUS_RESP_TRUE
593
- } else {
594
- unreachable ! ( ) // Transaction results should otherwise always be a Value::Response type
595
- }
596
- } else {
597
- unreachable ! ( ) // Transaction results should always be a Value::Response type
622
+ let TransactionOrigin :: Stacks ( inner_tx) = tx else {
623
+ unreachable ! ( "Transaction results should always be a Value::Response type" ) ;
624
+ } ;
625
+ if !matches ! ( inner_tx. payload, TransactionPayload :: PoisonMicroblock ( ..) ) {
626
+ unreachable ! ( "Transaction results should always be a Value::Response type" ) ;
598
627
}
628
+ STATUS_RESP_TRUE
599
629
}
600
630
} ;
601
631
602
- let ( txid, raw_tx, burnchain_op_json) = match tx {
603
- TransactionOrigin :: Burn ( op) => (
604
- op. txid ( ) . to_string ( ) ,
605
- "00" . to_string ( ) ,
606
- BlockstackOperationType :: blockstack_op_to_json ( op) ,
607
- ) ,
632
+ let ( txid, raw_tx, burnchain_op) = match tx {
633
+ TransactionOrigin :: Burn ( op) => ( op. txid ( ) , "0x00" . to_string ( ) , Some ( op. clone ( ) ) ) ,
608
634
TransactionOrigin :: Stacks ( ref tx) => {
609
- let txid = tx. txid ( ) . to_string ( ) ;
610
- let bytes = tx. serialize_to_vec ( ) ;
611
- ( txid, bytes_to_hex ( & bytes) , json ! ( null ) )
635
+ let txid = tx. txid ( ) ;
636
+ let bytes = bytes_to_hex ( & tx. serialize_to_vec ( ) ) ;
637
+ ( txid, format ! ( "0x{ bytes}" ) , None )
612
638
}
613
639
} ;
614
640
615
- let raw_result = {
616
- let bytes = receipt
617
- . result
618
- . serialize_to_vec ( )
619
- . expect ( "FATAL: failed to serialize transaction receipt" ) ;
620
- bytes_to_hex ( & bytes)
621
- } ;
622
- let contract_interface_json = {
623
- match & receipt. contract_analysis {
624
- Some ( analysis) => json ! ( build_contract_interface( analysis)
625
- . expect( "FATAL: failed to serialize contract publish receipt" ) ) ,
626
- None => json ! ( null) ,
627
- }
628
- } ;
629
- ReceiptPayloadInfo {
641
+ TransactionEventPayload {
630
642
txid,
631
- success,
632
- raw_result,
643
+ tx_index,
644
+ status,
645
+ raw_result : receipt. result . clone ( ) ,
633
646
raw_tx,
634
- contract_interface_json,
635
- burnchain_op_json,
647
+ contract_interface : receipt. contract_analysis . as_ref ( ) . map ( |analysis| {
648
+ build_contract_interface ( analysis)
649
+ . expect ( "FATAL: failed to serialize contract publish receipt" )
650
+ } ) ,
651
+ burnchain_op,
652
+ execution_cost : receipt. execution_cost . clone ( ) ,
653
+ microblock_sequence : receipt. microblock_header . as_ref ( ) . map ( |x| x. sequence ) ,
654
+ microblock_hash : receipt. microblock_header . as_ref ( ) . map ( |x| x. block_hash ( ) ) ,
655
+ microblock_parent_hash : receipt. microblock_header . as_ref ( ) . map ( |x| x. prev_block ) ,
656
+ vm_error : receipt. vm_error . clone ( ) ,
636
657
}
637
658
}
638
659
639
- /// Returns json payload to send for new block or microblock event
640
- fn make_new_block_txs_payload (
641
- receipt : & StacksTransactionReceipt ,
642
- tx_index : u32 ,
643
- ) -> serde_json:: Value {
644
- let receipt_payload_info = EventObserver :: generate_payload_info_for_receipt ( receipt) ;
645
-
646
- json ! ( {
647
- "txid" : format!( "0x{}" , & receipt_payload_info. txid) ,
648
- "tx_index" : tx_index,
649
- "status" : receipt_payload_info. success,
650
- "raw_result" : format!( "0x{}" , & receipt_payload_info. raw_result) ,
651
- "raw_tx" : format!( "0x{}" , & receipt_payload_info. raw_tx) ,
652
- "contract_abi" : receipt_payload_info. contract_interface_json,
653
- "burnchain_op" : receipt_payload_info. burnchain_op_json,
654
- "execution_cost" : receipt. execution_cost,
655
- "microblock_sequence" : receipt. microblock_header. as_ref( ) . map( |x| x. sequence) ,
656
- "microblock_hash" : receipt. microblock_header. as_ref( ) . map( |x| format!( "0x{}" , x. block_hash( ) ) ) ,
657
- "microblock_parent_hash" : receipt. microblock_header. as_ref( ) . map( |x| format!( "0x{}" , x. prev_block) ) ,
658
- } )
659
- }
660
-
661
660
fn make_new_attachment_payload (
662
661
attachment : & ( AttachmentInstance , Attachment ) ,
663
662
) -> serde_json:: Value {
@@ -686,7 +685,7 @@ impl EventObserver {
686
685
& self ,
687
686
parent_index_block_hash : StacksBlockId ,
688
687
filtered_events : Vec < ( usize , & ( bool , Txid , & StacksTransactionEvent ) ) > ,
689
- serialized_txs : & Vec < serde_json :: Value > ,
688
+ serialized_txs : & Vec < TransactionEventPayload > ,
690
689
burn_block_hash : BurnchainHeaderHash ,
691
690
burn_block_height : u32 ,
692
691
burn_block_timestamp : u64 ,
0 commit comments