diff --git a/bundled-addons/aegis/lib/tron-decode.js b/bundled-addons/aegis/lib/tron-decode.js index 8ecc60ae..396fc3b9 100644 --- a/bundled-addons/aegis/lib/tron-decode.js +++ b/bundled-addons/aegis/lib/tron-decode.js @@ -81,9 +81,20 @@ module.exports = function makeTronDecode({ sha256, base58check }) { } return out; } - const one = (fields, n) => fields.find((f) => f.field === n); - const bytesOf = (fields, n) => { const f = one(fields, n); return f && f.wire === 2 ? f.value : null; }; - const numOf = (fields, n) => { const f = one(fields, n); return f && f.wire === 0 ? f.value : null; }; + // A singular field that appears twice is legal protobuf: the parser keeps + // the LAST copy (and merges repeated sub-messages). java-tron does that, so + // reading the first copy would show the user one recipient and amount while + // the chain executes another — and Any.value is opaque bytes, so the signed + // hash is the same either way. Refuse instead of guessing, and refuse a + // known field sent with the wrong wire type for the same reason. + const one = (fields, n, wire) => { + const hits = fields.filter((f) => f.field === n); + if (hits.length > 1) throw new Error(`field ${n} appears ${hits.length} times; refusing an ambiguous transaction`); + if (hits[0] && hits[0].wire !== wire) throw new Error(`field ${n} has wire type ${hits[0].wire}, expected ${wire}`); + return hits[0]; + }; + const bytesOf = (fields, n) => { const f = one(fields, n, 2); return f ? f.value : null; }; + const numOf = (fields, n) => { const f = one(fields, n, 0); return f ? f.value : null; }; // 21-byte Tron address (0x41 || h20) → "T…" base58check. Anything else is // returned as hex so the overlay never hides a malformed field.