7
7
from collections import defaultdict
8
8
from functools import partial
9
9
from 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
11
11
12
12
import attr
13
13
import pylru
47
47
from electrumx .server .session import BAD_REQUEST , DAEMON_ERROR
48
48
from electrumx .server .session .http_session import HttpSession
49
49
from 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
51
51
from electrumx .version import electrumx_version
52
52
53
53
if TYPE_CHECKING :
@@ -953,7 +953,7 @@ async def transaction_decode_raw_tx_blueprint(
953
953
},
954
954
}
955
955
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 )
957
957
tx_out = tx .outputs [0 ]
958
958
atomical_id = location_id_bytes_to_compact (_receive_at_outputs [0 ][- 1 ]["atomical_id" ])
959
959
mint_info = {
@@ -991,7 +991,7 @@ async def transaction_decode_raw_tx_blueprint(
991
991
# Analysis the transaction detail by txid.
992
992
# See BlockProcessor.op_list for the complete op list.
993
993
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 )
995
995
res = self ._tx_detail_cache .get (tx_hash )
996
996
if res :
997
997
# 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):
1011
1011
1012
1012
operation_found_at_inputs = parse_protocols_operations_from_witness_array (tx , tx_hash , True )
1013
1013
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 )
1015
1015
blueprint_builder = AtomicalsTransferBlueprintBuilder (
1016
1016
self .logger ,
1017
1017
atomicals_spent_at_inputs ,
@@ -1043,62 +1043,6 @@ async def get_transaction_detail(self, tx_id: str, height=None, tx_num=-1):
1043
1043
"is_cleanly_assigned" : is_cleanly_assigned ,
1044
1044
},
1045
1045
}
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
- }
1102
1046
1103
1047
async def make_transfer_inputs (result , inputs_atomicals , tx_inputs , make_type ) -> Dict [int , List [Dict ]]:
1104
1048
for atomical_id , input_data in inputs_atomicals .items ():
@@ -1146,6 +1090,8 @@ def make_transfer_outputs(
1146
1090
result [k ].append (_data )
1147
1091
return result
1148
1092
1093
+ operation_type = operation_found_at_inputs .get ("op" , "" ) if operation_found_at_inputs else ""
1094
+
1149
1095
# no operation_found_at_inputs, it will be transfer.
1150
1096
if blueprint_builder .ft_atomicals and atomicals_spent_at_inputs :
1151
1097
if not operation_type and not op_raw :
@@ -1158,6 +1104,66 @@ def make_transfer_outputs(
1158
1104
await make_transfer_inputs (res ["transfers" ]["inputs" ], blueprint_builder .nft_atomicals , tx .inputs , "NFT" )
1159
1105
make_transfer_outputs (res ["transfers" ]["outputs" ], blueprint_builder .nft_output_blueprint .outputs )
1160
1106
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
+
1161
1167
(
1162
1168
payment_id ,
1163
1169
payment_marker_idx ,
@@ -1177,7 +1183,7 @@ def make_transfer_outputs(
1177
1183
return auto_encode_bytes_elements (res )
1178
1184
1179
1185
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 (',' )]
1181
1187
details = await asyncio .gather (* tasks )
1182
1188
return details
1183
1189
0 commit comments