|
4 | 4 |
|
5 | 5 | from typing import Generator, List, Optional, Text, Tuple |
6 | 6 |
|
7 | | -from iota.crypto import Curl |
8 | 7 | from iota.crypto.kerl import Kerl |
9 | 8 | from iota.crypto.signing import validate_signature_fragments |
10 | 9 | from iota.transaction.base import Bundle, Transaction |
|
13 | 12 | 'BundleValidator', |
14 | 13 | ] |
15 | 14 |
|
| 15 | + |
| 16 | +# |
| 17 | +# In very rare cases, the IOTA protocol may switch hash algorithms. |
| 18 | +# When this happens, the IOTA Foundation will create a snapshot, so |
| 19 | +# that all new objects on the Tangle use the new hash algorithm. |
| 20 | +# |
| 21 | +# However, the snapshot will still contain references to addresses |
| 22 | +# created using the legacy hash algorithm, so the bundle validator has |
| 23 | +# to be able to use that as a fallback when validation fails. |
| 24 | +# |
| 25 | +SUPPORTED_SPONGE = Kerl |
| 26 | +LEGACY_SPONGE = None # Curl |
| 27 | + |
| 28 | + |
16 | 29 | class BundleValidator(object): |
17 | 30 | """ |
18 | 31 | Checks a bundle and its transactions for problems. |
@@ -129,7 +142,7 @@ def _create_validator(self): |
129 | 142 | # Input is malformed; signature fragments after the first |
130 | 143 | # should have zero value. |
131 | 144 | yield ( |
132 | | - 'Transaction {i} has invalid amount ' |
| 145 | + 'Transaction {i} has invalid value ' |
133 | 146 | '(expected 0, actual {actual}).'.format( |
134 | 147 | actual = txn.value, |
135 | 148 |
|
@@ -171,40 +184,42 @@ def _get_bundle_signature_errors(self, groups): |
171 | 184 | :return: |
172 | 185 | List of error messages. If empty, signature fragments are valid. |
173 | 186 | """ |
174 | | - # Start with Kerl. If we encounter any errors, we'll go back and |
175 | | - # try again with Curl instead. |
176 | | - # Note that we keep track of how far we got with Kerl; this will be |
177 | | - # important later. |
178 | | - kerl_pos = None |
179 | | - kerl_errors = [] |
180 | | - |
181 | | - for kerl_pos, group in enumerate(groups): # type: Tuple[int, List[Transaction]] |
182 | | - error = self._get_group_signature_error(group, Kerl) |
| 187 | + # Start with the currently-supported hash algo. |
| 188 | + current_pos = None |
| 189 | + current_errors = [] |
| 190 | + for current_pos, group in enumerate(groups): # type: Tuple[int, List[Transaction]] |
| 191 | + error = self._get_group_signature_error(group, SUPPORTED_SPONGE) |
183 | 192 | if error: |
184 | | - kerl_errors.append(error) |
| 193 | + current_errors.append(error) |
| 194 | + |
| 195 | + # Pause and retry with the legacy algo. |
185 | 196 | break |
186 | 197 |
|
187 | | - # If Kerl failed, then go back and try with Curl. |
188 | | - if kerl_errors: |
| 198 | + # If validation failed, then go back and try with the legacy algo |
| 199 | + # (only applies if we are currently transitioning to a new algo). |
| 200 | + if current_errors and LEGACY_SPONGE: |
189 | 201 | for group in groups: |
190 | | - if self._get_group_signature_error(group, Curl): |
191 | | - # Curl doesn't work, either; no point in continuing. |
| 202 | + # noinspection PyTypeChecker |
| 203 | + if self._get_group_signature_error(group, LEGACY_SPONGE): |
| 204 | + # Legacy algo doesn't work, either; no point in continuing. |
192 | 205 | break |
193 | 206 | else: |
194 | 207 | # If we get here, then we were able to validate the signature |
195 | | - # fragments successfully using Curl. |
| 208 | + # fragments successfully using the legacy algorithm. |
196 | 209 | return [] |
197 | 210 |
|
198 | | - # If we get here, then Curl validation failed. |
199 | | - # We know that the bundle is invalid, but we will continue |
200 | | - # validating with Kerl, so that we can return an error message for |
201 | | - # each invalid input. |
202 | | - kerl_errors.extend(filter(None, ( |
203 | | - self._get_group_signature_error(group, Kerl) |
204 | | - for group in groups[kerl_pos+1:] |
| 211 | + # If we get here, then validation also failed when using the legacy |
| 212 | + # algorithm. |
| 213 | + |
| 214 | + # At this point, we know that the bundle is invalid, but we will |
| 215 | + # continue validating with the supported algorithm anyway, so that |
| 216 | + # we can return an error message for every invalid input. |
| 217 | + current_errors.extend(filter(None, ( |
| 218 | + self._get_group_signature_error(group, SUPPORTED_SPONGE) |
| 219 | + for group in groups[current_pos+1:] |
205 | 220 | ))) |
206 | 221 |
|
207 | | - return kerl_errors |
| 222 | + return current_errors |
208 | 223 |
|
209 | 224 | @staticmethod |
210 | 225 | def _get_group_signature_error(group, sponge_type): |
@@ -233,8 +248,8 @@ def _get_group_signature_error(group, sponge_type): |
233 | 248 |
|
234 | 249 | return ( |
235 | 250 | 'Transaction {i} has invalid signature ' |
236 | | - '(using {fragments} fragments).'.format( |
237 | | - fragments = len(group), |
238 | | - i = group[0].current_index, |
239 | | - ) |
| 251 | + '(using {fragments} fragments).'.format( |
| 252 | + fragments = len(group), |
| 253 | + i = group[0].current_index, |
| 254 | + ) |
240 | 255 | ) |
0 commit comments