77from collections import defaultdict
88from functools import partial
99from ipaddress import IPv4Address , IPv4Network , IPv6Address , IPv6Network
10- from typing import TYPE_CHECKING , Dict , List , Optional
10+ from typing import TYPE_CHECKING , Any , Dict , List , Optional
1111
1212import attr
1313import pylru
4747from electrumx .server .session import BAD_REQUEST , DAEMON_ERROR
4848from electrumx .server .session .http_session import HttpSession
4949from electrumx .server .session .rpc_session import LocalRPC
50- from electrumx .server .session .util import SESSION_PROTOCOL_MAX , non_negative_integer
50+ from electrumx .server .session .util import SESSION_PROTOCOL_MAX , assert_tx_hash , non_negative_integer
5151from electrumx .version import electrumx_version
5252
5353if TYPE_CHECKING :
@@ -953,7 +953,7 @@ async def transaction_decode_raw_tx_blueprint(
953953 },
954954 }
955955 elif op == "nft" :
956- _receive_at_outputs = self .bp .build_atomicals_receive_at_ouutput_for_validation_only (tx , tx_hash )
956+ _receive_at_outputs = self .bp .build_atomicals_receive_at_output_for_validation_only (tx , tx_hash )
957957 tx_out = tx .outputs [0 ]
958958 atomical_id = location_id_bytes_to_compact (_receive_at_outputs [0 ][- 1 ]["atomical_id" ])
959959 mint_info = {
@@ -991,7 +991,7 @@ async def transaction_decode_raw_tx_blueprint(
991991 # Analysis the transaction detail by txid.
992992 # See BlockProcessor.op_list for the complete op list.
993993 async def get_transaction_detail (self , tx_id : str , height = None , tx_num = - 1 ):
994- tx_hash = hex_str_to_hash (tx_id )
994+ tx_hash = assert_tx_hash (tx_id )
995995 res = self ._tx_detail_cache .get (tx_hash )
996996 if res :
997997 # txid maybe the same, this key should add height add key prefix
@@ -1011,7 +1011,7 @@ async def get_transaction_detail(self, tx_id: str, height=None, tx_num=-1):
10111011
10121012 operation_found_at_inputs = parse_protocols_operations_from_witness_array (tx , tx_hash , True )
10131013 atomicals_spent_at_inputs = self .bp .build_atomicals_spent_at_inputs_for_validation_only (tx )
1014- atomicals_receive_at_outputs = self .bp .build_atomicals_receive_at_ouutput_for_validation_only (tx , tx_hash )
1014+ atomicals_receive_at_outputs = self .bp .build_atomicals_receive_at_output_for_validation_only (tx , tx_hash )
10151015 blueprint_builder = AtomicalsTransferBlueprintBuilder (
10161016 self .logger ,
10171017 atomicals_spent_at_inputs ,
@@ -1043,62 +1043,6 @@ async def get_transaction_detail(self, tx_id: str, height=None, tx_num=-1):
10431043 "is_cleanly_assigned" : is_cleanly_assigned ,
10441044 },
10451045 }
1046- operation_type = operation_found_at_inputs .get ("op" , "" ) if operation_found_at_inputs else ""
1047- if operation_found_at_inputs :
1048- payload = operation_found_at_inputs .get ("payload" )
1049- payload_not_none = payload or {}
1050- res ["info" ]["payload" ] = payload_not_none
1051- if blueprint_builder .is_mint and operation_type in ["dmt" , "ft" ]:
1052- expected_output_index = 0
1053- tx_out = tx .outputs [expected_output_index ]
1054- location = tx_hash + util .pack_le_uint32 (expected_output_index )
1055- # if save into the db, it means mint success
1056- has_atomicals = self .db .get_atomicals_by_location_long_form (location )
1057- if len (has_atomicals ):
1058- ticker_name = payload_not_none .get ("args" , {}).get ("mint_ticker" , "" )
1059- status , candidate_atomical_id , _ = self .bp .get_effective_ticker (ticker_name , self .bp .height )
1060- if status :
1061- atomical_id = location_id_bytes_to_compact (candidate_atomical_id )
1062- res ["info" ] = {
1063- "atomical_id" : atomical_id ,
1064- "location_id" : location_id_bytes_to_compact (location ),
1065- "payload" : payload ,
1066- "outputs" : {
1067- expected_output_index : [
1068- {
1069- "address" : get_address_from_output_script (tx_out .pk_script ),
1070- "atomical_id" : atomical_id ,
1071- "type" : "FT" ,
1072- "index" : expected_output_index ,
1073- "value" : tx_out .value ,
1074- }
1075- ]
1076- },
1077- }
1078- elif operation_type == "nft" :
1079- if atomicals_receive_at_outputs :
1080- expected_output_index = 0
1081- location = tx_hash + util .pack_le_uint32 (expected_output_index )
1082- tx_out = tx .outputs [expected_output_index ]
1083- atomical_id = location_id_bytes_to_compact (
1084- atomicals_receive_at_outputs [expected_output_index ][- 1 ]["atomical_id" ]
1085- )
1086- res ["info" ] = {
1087- "atomical_id" : atomical_id ,
1088- "location_id" : location_id_bytes_to_compact (location ),
1089- "payload" : payload ,
1090- "outputs" : {
1091- expected_output_index : [
1092- {
1093- "address" : get_address_from_output_script (tx_out .pk_script ),
1094- "atomical_id" : atomical_id ,
1095- "type" : "NFT" ,
1096- "index" : expected_output_index ,
1097- "value" : tx_out .value ,
1098- }
1099- ]
1100- },
1101- }
11021046
11031047 async def make_transfer_inputs (result , inputs_atomicals , tx_inputs , make_type ) -> Dict [int , List [Dict ]]:
11041048 for atomical_id , input_data in inputs_atomicals .items ():
@@ -1146,6 +1090,8 @@ def make_transfer_outputs(
11461090 result [k ].append (_data )
11471091 return result
11481092
1093+ operation_type = operation_found_at_inputs .get ("op" , "" ) if operation_found_at_inputs else ""
1094+
11491095 # no operation_found_at_inputs, it will be transfer.
11501096 if blueprint_builder .ft_atomicals and atomicals_spent_at_inputs :
11511097 if not operation_type and not op_raw :
@@ -1158,6 +1104,66 @@ def make_transfer_outputs(
11581104 await make_transfer_inputs (res ["transfers" ]["inputs" ], blueprint_builder .nft_atomicals , tx .inputs , "NFT" )
11591105 make_transfer_outputs (res ["transfers" ]["outputs" ], blueprint_builder .nft_output_blueprint .outputs )
11601106
1107+ if operation_found_at_inputs :
1108+ payload = operation_found_at_inputs .get ("payload" )
1109+ payload_not_none = payload or {}
1110+ res ["info" ]["payload" ] = payload_not_none
1111+ # Mint operation types are "dmt", "nft", "ft", "dft". "dft" is the deploy operation.
1112+ if operation_type in ["dmt" , "ft" ]:
1113+ expected_output_index = 0
1114+ tx_out = tx .outputs [expected_output_index ]
1115+ location = tx_hash + util .pack_le_uint32 (expected_output_index )
1116+ # if save into the db, it means mint success
1117+ has_atomicals = self .db .get_atomicals_by_location_long_form (location )
1118+ if len (has_atomicals ):
1119+ ticker_name = payload_not_none .get ("args" , {}).get ("mint_ticker" , "" )
1120+ status , candidate_atomical_id , _ = self .bp .get_effective_ticker (ticker_name , self .bp .height )
1121+ if status :
1122+ atomical_id = location_id_bytes_to_compact (candidate_atomical_id )
1123+ res ["info" ] = {
1124+ "payload" : payload ,
1125+ "outputs" : {
1126+ expected_output_index : [
1127+ {
1128+ "address" : get_address_from_output_script (tx_out .pk_script ),
1129+ "atomical_id" : atomical_id ,
1130+ "location_id" : location_id_bytes_to_compact (location ),
1131+ "type" : "FT" ,
1132+ "index" : expected_output_index ,
1133+ "value" : tx_out .value ,
1134+ }
1135+ ]
1136+ },
1137+ }
1138+ elif operation_type == "nft" :
1139+ if atomicals_receive_at_outputs :
1140+ outputs : Dict [int , List [Dict [str , Any ]]] = {}
1141+ for expected_output_index , atomicals_receives in atomicals_receive_at_outputs .items ():
1142+ receives : List [Dict [str , Any ]] = []
1143+ for atomicals in atomicals_receives :
1144+ atomical_id = location_id_bytes_to_compact (atomicals ["atomical_id" ])
1145+ if any (
1146+ any (output .get ("atomical_id" ) == atomical_id for output in outputs_list )
1147+ for outputs_list in res ["transfers" ]["outputs" ].values ()
1148+ ):
1149+ continue
1150+ location = tx_hash + util .pack_le_uint32 (expected_output_index )
1151+ tx_out = tx .outputs [expected_output_index ]
1152+ receives .append ({
1153+ "address" : get_address_from_output_script (tx_out .pk_script ),
1154+ "atomical_id" : atomical_id ,
1155+ "location_id" : location_id_bytes_to_compact (location ),
1156+ "type" : "NFT" ,
1157+ "index" : expected_output_index ,
1158+ "value" : tx_out .value ,
1159+ })
1160+ if len (receives ) > 0 :
1161+ outputs [expected_output_index ] = receives
1162+ res ["info" ] = {
1163+ "payload" : payload ,
1164+ "outputs" : outputs ,
1165+ }
1166+
11611167 (
11621168 payment_id ,
11631169 payment_marker_idx ,
@@ -1177,7 +1183,7 @@ def make_transfer_outputs(
11771183 return auto_encode_bytes_elements (res )
11781184
11791185 async def get_transaction_detail_batch (self , tx_ids : str ):
1180- tasks = [self .get_transaction_detail (txid ) for txid in tx_ids .split (',' )]
1186+ tasks = [self .get_transaction_detail (assert_tx_hash ( tx_id )) for tx_id in tx_ids .split (',' )]
11811187 details = await asyncio .gather (* tasks )
11821188 return details
11831189
0 commit comments