verifyTronSignature function
The Tron reply is a fully signed transaction broadcast VERBATIM, so it is checked on both counts: every signature must recover to the owner address, and the transaction must move what the user approved.
Byte equality with the request's rawData is the strong form. When the
bytes differ (a firmware that rebuilds raw_data from the semantic
fields), the fallback compares the fields that decide where the money goes,
plus the validity window against the reference block.
Implementation
VerifyResult verifyTronSignature(VerifyTronSignatureArgs args) {
SignedTronTx signedTx;
final input = args.signedTx;
if (input is SignedTronTx) {
signedTx = input;
} else if (input is String) {
try {
signedTx = splitSignedTronTx(input);
} on EraSdkError catch (e) {
return failed(
'the returned Tron transaction is not readable: ${e.message}');
}
} else {
return failed(
'the returned Tron transaction is not readable: not a SignedTronTx or hex string');
}
if (signedTx.signatures.isEmpty) {
return failed('the returned Tron transaction carries no signature');
}
if (args.from.isEmpty) {
return failed('no owner address to check the signature against');
}
final digest = sha256(signedTx.rawData);
for (final signature in signedTx.signatures) {
final recovered = _recoverTronAddress(digest, signature);
if (recovered == null) return failed('signature could not be checked');
if (recovered != args.from) {
return failed('the signature does not belong to this account');
}
}
if (equalBytes(signedTx.rawData, args.rawData)) return verified;
// Rebuild path: the firmware built its own raw_data from the semantic
// fields. Compare the operation, then the validity window.
final contractResult = _compareContracts(args.rawData, signedTx.rawData);
if (contractResult != null) return contractResult;
final latestBlock = args.latestBlock;
if (latestBlock == null) {
return failed(
'the returned raw_data differs from the request and no latestBlock was provided to check the validity window',
);
}
return _compareWindow(signedTx.rawData, latestBlock);
}