trc-8004-sdk 0.1.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sdk/__init__.py +123 -0
- sdk/agent_protocol_client.py +180 -0
- sdk/agent_sdk.py +1549 -0
- sdk/chain_utils.py +278 -0
- sdk/cli.py +549 -0
- sdk/client.py +202 -0
- sdk/contract_adapter.py +489 -0
- sdk/exceptions.py +652 -0
- sdk/retry.py +509 -0
- sdk/signer.py +284 -0
- sdk/utils.py +163 -0
- trc_8004_sdk-0.1.0b1.dist-info/METADATA +411 -0
- trc_8004_sdk-0.1.0b1.dist-info/RECORD +16 -0
- trc_8004_sdk-0.1.0b1.dist-info/WHEEL +5 -0
- trc_8004_sdk-0.1.0b1.dist-info/entry_points.txt +2 -0
- trc_8004_sdk-0.1.0b1.dist-info/top_level.txt +1 -0
sdk/exceptions.py
ADDED
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TRC-8004 SDK 异常定义模块
|
|
3
|
+
|
|
4
|
+
提供细粒度的异常类型,便于调用方精确处理错误。
|
|
5
|
+
|
|
6
|
+
Exception Hierarchy:
|
|
7
|
+
SDKError (基类)
|
|
8
|
+
├── ConfigurationError (配置错误)
|
|
9
|
+
│ ├── MissingContractAddressError
|
|
10
|
+
│ ├── InvalidPrivateKeyError
|
|
11
|
+
│ └── ChainIdResolutionError
|
|
12
|
+
├── NetworkError (网络错误)
|
|
13
|
+
│ ├── RPCError
|
|
14
|
+
│ ├── TimeoutError
|
|
15
|
+
│ └── RetryExhaustedError
|
|
16
|
+
├── ContractError (合约错误)
|
|
17
|
+
│ ├── ContractCallError
|
|
18
|
+
│ ├── ContractFunctionNotFoundError
|
|
19
|
+
│ ├── TransactionFailedError
|
|
20
|
+
│ └── InsufficientEnergyError
|
|
21
|
+
├── SignatureError (签名错误)
|
|
22
|
+
│ ├── InvalidSignatureError
|
|
23
|
+
│ └── SignerNotAvailableError
|
|
24
|
+
├── DataError (数据错误)
|
|
25
|
+
│ ├── InvalidAddressError
|
|
26
|
+
│ ├── InvalidHashError
|
|
27
|
+
│ ├── SerializationError
|
|
28
|
+
│ └── DataLoadError
|
|
29
|
+
└── ValidationError (验证错误)
|
|
30
|
+
├── RequestHashMismatchError
|
|
31
|
+
├── FeedbackAuthExpiredError
|
|
32
|
+
└── FeedbackAuthInvalidError
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
>>> from sdk.exceptions import ContractCallError, RetryExhaustedError
|
|
36
|
+
>>> try:
|
|
37
|
+
... sdk.register_agent(...)
|
|
38
|
+
... except RetryExhaustedError as e:
|
|
39
|
+
... print(f"重试耗尽: {e.last_error}")
|
|
40
|
+
... except ContractCallError as e:
|
|
41
|
+
... print(f"合约调用失败: {e.code}")
|
|
42
|
+
|
|
43
|
+
Note:
|
|
44
|
+
- 所有异常都继承自 SDKError
|
|
45
|
+
- 每个异常都有 code 和 details 属性
|
|
46
|
+
- 可以通过捕获父类异常来处理一类错误
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from typing import Optional, Any
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SDKError(Exception):
|
|
53
|
+
"""
|
|
54
|
+
SDK 基础异常类。
|
|
55
|
+
|
|
56
|
+
所有 SDK 异常的基类,提供统一的错误码和详情机制。
|
|
57
|
+
|
|
58
|
+
Attributes:
|
|
59
|
+
code: 错误码字符串,用于程序化处理
|
|
60
|
+
details: 错误详情,可以是任意类型
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
message: 错误消息
|
|
64
|
+
code: 错误码,默认为 "SDK_ERROR"
|
|
65
|
+
details: 错误详情
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
>>> raise SDKError("Something went wrong", code="CUSTOM_ERROR", details={"key": "value"})
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(
|
|
72
|
+
self,
|
|
73
|
+
message: str,
|
|
74
|
+
code: Optional[str] = None,
|
|
75
|
+
details: Optional[Any] = None,
|
|
76
|
+
) -> None:
|
|
77
|
+
super().__init__(message)
|
|
78
|
+
self.code = code or "SDK_ERROR"
|
|
79
|
+
self.details = details
|
|
80
|
+
|
|
81
|
+
def __str__(self) -> str:
|
|
82
|
+
"""返回格式化的错误消息。"""
|
|
83
|
+
if self.details:
|
|
84
|
+
return f"[{self.code}] {super().__str__()} - {self.details}"
|
|
85
|
+
return f"[{self.code}] {super().__str__()}"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# ============ 配置相关异常 ============
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class ConfigurationError(SDKError):
|
|
92
|
+
"""
|
|
93
|
+
配置错误基类。
|
|
94
|
+
|
|
95
|
+
当 SDK 配置不正确时抛出。
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
message: 错误消息
|
|
99
|
+
details: 错误详情
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __init__(self, message: str, details: Optional[Any] = None) -> None:
|
|
103
|
+
super().__init__(message, "CONFIGURATION_ERROR", details)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class MissingContractAddressError(ConfigurationError):
|
|
107
|
+
"""
|
|
108
|
+
合约地址缺失异常。
|
|
109
|
+
|
|
110
|
+
当尝试调用合约但未配置对应地址时抛出。
|
|
111
|
+
|
|
112
|
+
Attributes:
|
|
113
|
+
contract_name: 缺失地址的合约名称
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
contract_name: 合约名称(如 "identity", "validation", "reputation")
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
>>> raise MissingContractAddressError("identity")
|
|
120
|
+
# [MISSING_CONTRACT_ADDRESS] Contract address missing for 'identity'
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def __init__(self, contract_name: str) -> None:
|
|
124
|
+
super().__init__(
|
|
125
|
+
f"Contract address missing for '{contract_name}'",
|
|
126
|
+
details={"contract": contract_name}
|
|
127
|
+
)
|
|
128
|
+
self.code = "MISSING_CONTRACT_ADDRESS"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class InvalidPrivateKeyError(ConfigurationError):
|
|
132
|
+
"""
|
|
133
|
+
私钥格式无效异常。
|
|
134
|
+
|
|
135
|
+
当提供的私钥格式不正确时抛出。
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
reason: 无效原因描述
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
>>> raise InvalidPrivateKeyError("Expected 64 hex characters")
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(self, reason: str = "Invalid format") -> None:
|
|
145
|
+
super().__init__(f"Private key invalid: {reason}")
|
|
146
|
+
self.code = "INVALID_PRIVATE_KEY"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class ChainIdResolutionError(ConfigurationError):
|
|
150
|
+
"""
|
|
151
|
+
Chain ID 解析失败异常。
|
|
152
|
+
|
|
153
|
+
当无法从 RPC 节点获取 Chain ID 时抛出。
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
rpc_url: 尝试连接的 RPC URL
|
|
157
|
+
|
|
158
|
+
Example:
|
|
159
|
+
>>> raise ChainIdResolutionError("https://nile.trongrid.io")
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
def __init__(self, rpc_url: Optional[str] = None) -> None:
|
|
163
|
+
super().__init__(
|
|
164
|
+
"Failed to resolve chain ID from RPC",
|
|
165
|
+
details={"rpc_url": rpc_url}
|
|
166
|
+
)
|
|
167
|
+
self.code = "CHAIN_ID_RESOLUTION_FAILED"
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# ============ 网络相关异常 ============
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class NetworkError(SDKError):
|
|
174
|
+
"""
|
|
175
|
+
网络请求错误基类。
|
|
176
|
+
|
|
177
|
+
当网络请求失败时抛出。
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
message: 错误消息
|
|
181
|
+
details: 错误详情
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
def __init__(self, message: str, details: Optional[Any] = None) -> None:
|
|
185
|
+
super().__init__(message, "NETWORK_ERROR", details)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class RPCError(NetworkError):
|
|
189
|
+
"""
|
|
190
|
+
RPC 调用失败异常。
|
|
191
|
+
|
|
192
|
+
当区块链 RPC 调用失败时抛出。
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
message: 错误消息
|
|
196
|
+
rpc_url: RPC 节点 URL
|
|
197
|
+
method: 调用的方法名
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
>>> raise RPCError("Connection refused", rpc_url="https://...", method="eth_call")
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
def __init__(
|
|
204
|
+
self,
|
|
205
|
+
message: str,
|
|
206
|
+
rpc_url: Optional[str] = None,
|
|
207
|
+
method: Optional[str] = None,
|
|
208
|
+
) -> None:
|
|
209
|
+
super().__init__(
|
|
210
|
+
message,
|
|
211
|
+
details={"rpc_url": rpc_url, "method": method}
|
|
212
|
+
)
|
|
213
|
+
self.code = "RPC_ERROR"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class TimeoutError(NetworkError):
|
|
217
|
+
"""
|
|
218
|
+
请求超时异常。
|
|
219
|
+
|
|
220
|
+
当操作超过指定时间未完成时抛出。
|
|
221
|
+
|
|
222
|
+
Attributes:
|
|
223
|
+
operation: 超时的操作名称
|
|
224
|
+
timeout_seconds: 超时时间
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
operation: 操作名称
|
|
228
|
+
timeout_seconds: 超时时间(秒)
|
|
229
|
+
|
|
230
|
+
Example:
|
|
231
|
+
>>> raise TimeoutError("validation_request", 30.0)
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
def __init__(self, operation: str, timeout_seconds: float) -> None:
|
|
235
|
+
super().__init__(
|
|
236
|
+
f"Operation '{operation}' timed out after {timeout_seconds}s",
|
|
237
|
+
details={"operation": operation, "timeout": timeout_seconds}
|
|
238
|
+
)
|
|
239
|
+
self.code = "TIMEOUT_ERROR"
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class RetryExhaustedError(NetworkError):
|
|
243
|
+
"""
|
|
244
|
+
重试次数耗尽异常。
|
|
245
|
+
|
|
246
|
+
当操作在所有重试尝试后仍然失败时抛出。
|
|
247
|
+
|
|
248
|
+
Attributes:
|
|
249
|
+
last_error: 最后一次尝试的异常
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
operation: 操作名称
|
|
253
|
+
attempts: 尝试次数
|
|
254
|
+
last_error: 最后一次尝试的异常
|
|
255
|
+
|
|
256
|
+
Example:
|
|
257
|
+
>>> try:
|
|
258
|
+
... sdk.register_agent(...)
|
|
259
|
+
... except RetryExhaustedError as e:
|
|
260
|
+
... print(f"Failed after {e.details['attempts']} attempts")
|
|
261
|
+
... print(f"Last error: {e.last_error}")
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
def __init__(
|
|
265
|
+
self,
|
|
266
|
+
operation: str,
|
|
267
|
+
attempts: int,
|
|
268
|
+
last_error: Optional[Exception] = None,
|
|
269
|
+
) -> None:
|
|
270
|
+
super().__init__(
|
|
271
|
+
f"Operation '{operation}' failed after {attempts} attempts",
|
|
272
|
+
details={
|
|
273
|
+
"operation": operation,
|
|
274
|
+
"attempts": attempts,
|
|
275
|
+
"last_error": str(last_error),
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
self.code = "RETRY_EXHAUSTED"
|
|
279
|
+
self.last_error = last_error
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# ============ 合约相关异常 ============
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class ContractError(SDKError):
|
|
286
|
+
"""
|
|
287
|
+
合约交互错误基类。
|
|
288
|
+
|
|
289
|
+
当与智能合约交互失败时抛出。
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
message: 错误消息
|
|
293
|
+
details: 错误详情
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
def __init__(self, message: str, details: Optional[Any] = None) -> None:
|
|
297
|
+
super().__init__(message, "CONTRACT_ERROR", details)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class ContractCallError(ContractError):
|
|
301
|
+
"""
|
|
302
|
+
合约调用失败异常。
|
|
303
|
+
|
|
304
|
+
当合约方法调用失败时抛出。
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
contract: 合约名称
|
|
308
|
+
method: 方法名
|
|
309
|
+
reason: 失败原因
|
|
310
|
+
|
|
311
|
+
Example:
|
|
312
|
+
>>> raise ContractCallError("identity", "register", "revert: already registered")
|
|
313
|
+
"""
|
|
314
|
+
|
|
315
|
+
def __init__(
|
|
316
|
+
self,
|
|
317
|
+
contract: str,
|
|
318
|
+
method: str,
|
|
319
|
+
reason: Optional[str] = None,
|
|
320
|
+
) -> None:
|
|
321
|
+
super().__init__(
|
|
322
|
+
f"Contract call failed: {contract}.{method}",
|
|
323
|
+
details={"contract": contract, "method": method, "reason": reason}
|
|
324
|
+
)
|
|
325
|
+
self.code = "CONTRACT_CALL_FAILED"
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class ContractFunctionNotFoundError(ContractError):
|
|
329
|
+
"""
|
|
330
|
+
合约方法不存在异常。
|
|
331
|
+
|
|
332
|
+
当尝试调用不存在的合约方法时抛出。
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
contract: 合约地址或名称
|
|
336
|
+
method: 方法名
|
|
337
|
+
arity: 参数数量(用于区分重载)
|
|
338
|
+
|
|
339
|
+
Example:
|
|
340
|
+
>>> raise ContractFunctionNotFoundError("TContract...", "unknownMethod", 2)
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
def __init__(
|
|
344
|
+
self,
|
|
345
|
+
contract: str,
|
|
346
|
+
method: str,
|
|
347
|
+
arity: Optional[int] = None,
|
|
348
|
+
) -> None:
|
|
349
|
+
msg = f"Function '{method}' not found in contract '{contract}'"
|
|
350
|
+
if arity is not None:
|
|
351
|
+
msg += f" with arity {arity}"
|
|
352
|
+
super().__init__(
|
|
353
|
+
msg,
|
|
354
|
+
details={"contract": contract, "method": method, "arity": arity}
|
|
355
|
+
)
|
|
356
|
+
self.code = "CONTRACT_FUNCTION_NOT_FOUND"
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class TransactionFailedError(ContractError):
|
|
360
|
+
"""
|
|
361
|
+
交易执行失败异常。
|
|
362
|
+
|
|
363
|
+
当链上交易执行失败时抛出。
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
tx_id: 交易 ID
|
|
367
|
+
reason: 失败原因
|
|
368
|
+
|
|
369
|
+
Example:
|
|
370
|
+
>>> raise TransactionFailedError(tx_id="0x123...", reason="out of gas")
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
def __init__(
|
|
374
|
+
self,
|
|
375
|
+
tx_id: Optional[str] = None,
|
|
376
|
+
reason: Optional[str] = None,
|
|
377
|
+
) -> None:
|
|
378
|
+
super().__init__(
|
|
379
|
+
f"Transaction failed: {reason or 'unknown reason'}",
|
|
380
|
+
details={"tx_id": tx_id, "reason": reason}
|
|
381
|
+
)
|
|
382
|
+
self.code = "TRANSACTION_FAILED"
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class InsufficientEnergyError(ContractError):
|
|
386
|
+
"""
|
|
387
|
+
能量/Gas 不足异常。
|
|
388
|
+
|
|
389
|
+
当账户能量或 Gas 不足以执行交易时抛出。
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
required: 所需能量
|
|
393
|
+
available: 可用能量
|
|
394
|
+
|
|
395
|
+
Example:
|
|
396
|
+
>>> raise InsufficientEnergyError(required=100000, available=50000)
|
|
397
|
+
|
|
398
|
+
Note:
|
|
399
|
+
在 TRON 网络中,能量 (Energy) 用于执行智能合约。
|
|
400
|
+
在 EVM 网络中,对应的是 Gas。
|
|
401
|
+
"""
|
|
402
|
+
|
|
403
|
+
def __init__(
|
|
404
|
+
self,
|
|
405
|
+
required: Optional[int] = None,
|
|
406
|
+
available: Optional[int] = None,
|
|
407
|
+
) -> None:
|
|
408
|
+
super().__init__(
|
|
409
|
+
"Insufficient energy/gas for transaction",
|
|
410
|
+
details={"required": required, "available": available}
|
|
411
|
+
)
|
|
412
|
+
self.code = "INSUFFICIENT_ENERGY"
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
# ============ 签名相关异常 ============
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class SignatureError(SDKError):
|
|
419
|
+
"""
|
|
420
|
+
签名相关错误基类。
|
|
421
|
+
|
|
422
|
+
当签名操作失败时抛出。
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
message: 错误消息
|
|
426
|
+
details: 错误详情
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
def __init__(self, message: str, details: Optional[Any] = None) -> None:
|
|
430
|
+
super().__init__(message, "SIGNATURE_ERROR", details)
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
class InvalidSignatureError(SignatureError):
|
|
434
|
+
"""
|
|
435
|
+
签名无效异常。
|
|
436
|
+
|
|
437
|
+
当签名验证失败时抛出。
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
reason: 无效原因
|
|
441
|
+
|
|
442
|
+
Example:
|
|
443
|
+
>>> raise InvalidSignatureError("Signature length mismatch")
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
def __init__(self, reason: str = "Signature verification failed") -> None:
|
|
447
|
+
super().__init__(reason)
|
|
448
|
+
self.code = "INVALID_SIGNATURE"
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class SignerNotAvailableError(SignatureError):
|
|
452
|
+
"""
|
|
453
|
+
签名器不可用异常。
|
|
454
|
+
|
|
455
|
+
当需要签名但未配置签名器时抛出。
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
reason: 不可用原因
|
|
459
|
+
|
|
460
|
+
Example:
|
|
461
|
+
>>> raise SignerNotAvailableError("Private key not configured")
|
|
462
|
+
"""
|
|
463
|
+
|
|
464
|
+
def __init__(self, reason: str = "Signer not configured") -> None:
|
|
465
|
+
super().__init__(reason)
|
|
466
|
+
self.code = "SIGNER_NOT_AVAILABLE"
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
# ============ 数据相关异常 ============
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
class DataError(SDKError):
|
|
473
|
+
"""
|
|
474
|
+
数据处理错误基类。
|
|
475
|
+
|
|
476
|
+
当数据格式或内容不正确时抛出。
|
|
477
|
+
|
|
478
|
+
Args:
|
|
479
|
+
message: 错误消息
|
|
480
|
+
details: 错误详情
|
|
481
|
+
"""
|
|
482
|
+
|
|
483
|
+
def __init__(self, message: str, details: Optional[Any] = None) -> None:
|
|
484
|
+
super().__init__(message, "DATA_ERROR", details)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class InvalidAddressError(DataError):
|
|
488
|
+
"""
|
|
489
|
+
地址格式无效异常。
|
|
490
|
+
|
|
491
|
+
当提供的区块链地址格式不正确时抛出。
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
address: 无效的地址
|
|
495
|
+
expected_format: 期望的格式描述
|
|
496
|
+
|
|
497
|
+
Example:
|
|
498
|
+
>>> raise InvalidAddressError("invalid_addr", "TRON base58 or 20 bytes hex")
|
|
499
|
+
"""
|
|
500
|
+
|
|
501
|
+
def __init__(
|
|
502
|
+
self,
|
|
503
|
+
address: str,
|
|
504
|
+
expected_format: str = "20 bytes hex",
|
|
505
|
+
) -> None:
|
|
506
|
+
super().__init__(
|
|
507
|
+
f"Invalid address format: {address}",
|
|
508
|
+
details={"address": address, "expected": expected_format}
|
|
509
|
+
)
|
|
510
|
+
self.code = "INVALID_ADDRESS"
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
class InvalidHashError(DataError):
|
|
514
|
+
"""
|
|
515
|
+
哈希格式无效异常。
|
|
516
|
+
|
|
517
|
+
当提供的哈希值格式不正确时抛出。
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
value: 无效的哈希值
|
|
521
|
+
expected_length: 期望的字节长度
|
|
522
|
+
|
|
523
|
+
Example:
|
|
524
|
+
>>> raise InvalidHashError("0x123", expected_length=32)
|
|
525
|
+
"""
|
|
526
|
+
|
|
527
|
+
def __init__(self, value: str, expected_length: int = 32) -> None:
|
|
528
|
+
super().__init__(
|
|
529
|
+
f"Invalid hash format, expected {expected_length} bytes",
|
|
530
|
+
details={"value": value[:20] + "..." if len(value) > 20 else value}
|
|
531
|
+
)
|
|
532
|
+
self.code = "INVALID_HASH"
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
class SerializationError(DataError):
|
|
536
|
+
"""
|
|
537
|
+
序列化失败异常。
|
|
538
|
+
|
|
539
|
+
当数据序列化或反序列化失败时抛出。
|
|
540
|
+
|
|
541
|
+
Args:
|
|
542
|
+
reason: 失败原因
|
|
543
|
+
|
|
544
|
+
Example:
|
|
545
|
+
>>> raise SerializationError("Invalid JSON format")
|
|
546
|
+
"""
|
|
547
|
+
|
|
548
|
+
def __init__(self, reason: str) -> None:
|
|
549
|
+
super().__init__(f"Serialization failed: {reason}")
|
|
550
|
+
self.code = "SERIALIZATION_ERROR"
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class DataLoadError(DataError):
|
|
554
|
+
"""
|
|
555
|
+
数据加载失败异常。
|
|
556
|
+
|
|
557
|
+
当从 URI 加载数据失败时抛出。
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
uri: 数据 URI
|
|
561
|
+
reason: 失败原因
|
|
562
|
+
|
|
563
|
+
Example:
|
|
564
|
+
>>> raise DataLoadError("ipfs://Qm...", "Gateway timeout")
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
def __init__(self, uri: str, reason: Optional[str] = None) -> None:
|
|
568
|
+
super().__init__(
|
|
569
|
+
f"Failed to load data from '{uri}'",
|
|
570
|
+
details={"uri": uri, "reason": reason}
|
|
571
|
+
)
|
|
572
|
+
self.code = "DATA_LOAD_ERROR"
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
# ============ 验证相关异常 ============
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class ValidationError(SDKError):
|
|
579
|
+
"""
|
|
580
|
+
验证相关错误基类。
|
|
581
|
+
|
|
582
|
+
当验证操作失败时抛出。
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
message: 错误消息
|
|
586
|
+
details: 错误详情
|
|
587
|
+
"""
|
|
588
|
+
|
|
589
|
+
def __init__(self, message: str, details: Optional[Any] = None) -> None:
|
|
590
|
+
super().__init__(message, "VALIDATION_ERROR", details)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
class RequestHashMismatchError(ValidationError):
|
|
594
|
+
"""
|
|
595
|
+
请求哈希不匹配异常。
|
|
596
|
+
|
|
597
|
+
当计算的请求哈希与预期不符时抛出。
|
|
598
|
+
|
|
599
|
+
Args:
|
|
600
|
+
expected: 预期的哈希值
|
|
601
|
+
actual: 实际计算的哈希值
|
|
602
|
+
|
|
603
|
+
Example:
|
|
604
|
+
>>> raise RequestHashMismatchError("0xaaa...", "0xbbb...")
|
|
605
|
+
"""
|
|
606
|
+
|
|
607
|
+
def __init__(self, expected: str, actual: str) -> None:
|
|
608
|
+
super().__init__(
|
|
609
|
+
"Request hash mismatch",
|
|
610
|
+
details={"expected": expected, "actual": actual}
|
|
611
|
+
)
|
|
612
|
+
self.code = "REQUEST_HASH_MISMATCH"
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
class FeedbackAuthExpiredError(ValidationError):
|
|
616
|
+
"""
|
|
617
|
+
反馈授权已过期异常。
|
|
618
|
+
|
|
619
|
+
当 feedbackAuth 的有效期已过时抛出。
|
|
620
|
+
|
|
621
|
+
Args:
|
|
622
|
+
expiry: 授权过期时间(Unix 时间戳)
|
|
623
|
+
current: 当前时间(Unix 时间戳)
|
|
624
|
+
|
|
625
|
+
Example:
|
|
626
|
+
>>> raise FeedbackAuthExpiredError(expiry=1700000000, current=1700001000)
|
|
627
|
+
"""
|
|
628
|
+
|
|
629
|
+
def __init__(self, expiry: int, current: int) -> None:
|
|
630
|
+
super().__init__(
|
|
631
|
+
"Feedback authorization has expired",
|
|
632
|
+
details={"expiry": expiry, "current": current}
|
|
633
|
+
)
|
|
634
|
+
self.code = "FEEDBACK_AUTH_EXPIRED"
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
class FeedbackAuthInvalidError(ValidationError):
|
|
638
|
+
"""
|
|
639
|
+
反馈授权无效异常。
|
|
640
|
+
|
|
641
|
+
当 feedbackAuth 格式或签名无效时抛出。
|
|
642
|
+
|
|
643
|
+
Args:
|
|
644
|
+
reason: 无效原因
|
|
645
|
+
|
|
646
|
+
Example:
|
|
647
|
+
>>> raise FeedbackAuthInvalidError("Invalid signature")
|
|
648
|
+
"""
|
|
649
|
+
|
|
650
|
+
def __init__(self, reason: str) -> None:
|
|
651
|
+
super().__init__(f"Invalid feedback authorization: {reason}")
|
|
652
|
+
self.code = "FEEDBACK_AUTH_INVALID"
|