@@ -126,31 +126,72 @@ static bool CheckTxScriptsSanity(const CMutableTransaction& tx)
126
126
127
127
static bool DecodeTx (CMutableTransaction& tx, const std::vector<unsigned char >& tx_data, bool try_no_witness, bool try_witness)
128
128
{
129
+ // General strategy:
130
+ // - Decode both with extended serialization (which interprets the 0x0001 tag as a marker for
131
+ // the presense of witnesses) and with legacy serialization (which interprets the tag as a
132
+ // 0-input 1-output incomplete transaction).
133
+ // - Restricted by try_no_witness (which disables legacy if false) and try_witness (which
134
+ // disables extended if false).
135
+ // - Ignore serializations that do not fully consume the hex string.
136
+ // - If neither succeeds, fail.
137
+ // - If only one succeeds, return that one.
138
+ // - If both decode attempts succeed:
139
+ // - If only one passes the CheckTxScriptsSanity check, return that one.
140
+ // - If neither or both pass CheckTxScriptsSanity, return the extended one.
141
+
142
+ CMutableTransaction tx_extended, tx_legacy;
143
+ bool ok_extended = false , ok_legacy = false ;
144
+
145
+ // Try decoding with extended serialization support, and remember if the result successfully
146
+ // consumes the entire input.
129
147
if (try_witness) {
130
148
CDataStream ssData (tx_data, SER_NETWORK, PROTOCOL_VERSION);
131
149
try {
132
- ssData >> tx;
133
- // If transaction looks sane, we don't try other mode even if requested
134
- if (ssData.empty () && (!try_no_witness || CheckTxScriptsSanity (tx))) {
135
- return true ;
136
- }
150
+ ssData >> tx_extended;
151
+ if (ssData.empty ()) ok_extended = true ;
137
152
} catch (const std::exception&) {
138
153
// Fall through.
139
154
}
140
155
}
141
156
157
+ // Optimization: if extended decoding succeeded and the result passes CheckTxScriptsSanity,
158
+ // don't bother decoding the other way.
159
+ if (ok_extended && CheckTxScriptsSanity (tx_extended)) {
160
+ tx = std::move (tx_extended);
161
+ return true ;
162
+ }
163
+
164
+ // Try decoding with legacy serialization, and remember if the result successfully consumes the entire input.
142
165
if (try_no_witness) {
143
166
CDataStream ssData (tx_data, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
144
167
try {
145
- ssData >> tx;
146
- if (ssData.empty ()) {
147
- return true ;
148
- }
168
+ ssData >> tx_legacy;
169
+ if (ssData.empty ()) ok_legacy = true ;
149
170
} catch (const std::exception&) {
150
171
// Fall through.
151
172
}
152
173
}
153
174
175
+ // If legacy decoding succeeded and passes CheckTxScriptsSanity, that's our answer, as we know
176
+ // at this point that extended decoding either failed or doesn't pass the sanity check.
177
+ if (ok_legacy && CheckTxScriptsSanity (tx_legacy)) {
178
+ tx = std::move (tx_legacy);
179
+ return true ;
180
+ }
181
+
182
+ // If extended decoding succeeded, and neither decoding passes sanity, return the extended one.
183
+ if (ok_extended) {
184
+ tx = std::move (tx_extended);
185
+ return true ;
186
+ }
187
+
188
+ // If legacy decoding succeeded and extended didn't, return the legacy one.
189
+ if (ok_legacy) {
190
+ tx = std::move (tx_legacy);
191
+ return true ;
192
+ }
193
+
194
+ // If none succeeded, we failed.
154
195
return false ;
155
196
}
156
197
0 commit comments