Skip to content
This repository was archived by the owner on Jan 13, 2023. It is now read-only.

Commit 8c102c3

Browse files
authored
Merge pull request #73 from normpad/develop
Fixes replayed bundle being reversed
2 parents 4466174 + f057862 commit 8c102c3

File tree

9 files changed

+339
-188
lines changed

9 files changed

+339
-188
lines changed

iota/commands/extended/replay_bundle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def _execute(self, request):
4747
minWeightMagnitude = min_weight_magnitude,
4848

4949

50-
trytes = bundle.as_tryte_strings(head_to_tail=True),
50+
trytes = bundle.as_tryte_strings(),
5151
)
5252

5353

iota/transaction/base.py

Lines changed: 88 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from iota.crypto import Curl, HASH_LENGTH
1111
from iota.json import JsonSerializable
1212
from iota.transaction.types import BundleHash, Fragment, TransactionHash, \
13-
TransactionTrytes
13+
TransactionTrytes, Nonce
1414
from iota.types import Address, Hash, Tag, TryteString, TrytesCompatible, \
1515
int_from_trits, trits_from_int
1616

@@ -53,14 +53,18 @@ def from_tryte_string(cls, trytes, hash_=None):
5353
signature_message_fragment = Fragment(tryte_string[0:2187]),
5454
address = Address(tryte_string[2187:2268]),
5555
value = int_from_trits(tryte_string[2268:2295].as_trits()),
56-
tag = Tag(tryte_string[2295:2322]),
56+
legacy_tag = Tag(tryte_string[2295:2322]),
5757
timestamp = int_from_trits(tryte_string[2322:2331].as_trits()),
5858
current_index = int_from_trits(tryte_string[2331:2340].as_trits()),
5959
last_index = int_from_trits(tryte_string[2340:2349].as_trits()),
6060
bundle_hash = BundleHash(tryte_string[2349:2430]),
6161
trunk_transaction_hash = TransactionHash(tryte_string[2430:2511]),
6262
branch_transaction_hash = TransactionHash(tryte_string[2511:2592]),
63-
nonce = Hash(tryte_string[2592:2673]),
63+
tag = Tag(tryte_string[2592:2619]),
64+
attachment_timestamp = int_from_trits(tryte_string[2619:2628].as_trits()),
65+
attachment_timestamp_lower_bound = int_from_trits(tryte_string[2628:2637].as_trits()),
66+
attachment_timestamp_upper_bound = int_from_trits(tryte_string[2637:2646].as_trits()),
67+
nonce = Nonce(tryte_string[2646:2673]),
6468
)
6569

6670
def __init__(
@@ -69,16 +73,20 @@ def __init__(
6973
signature_message_fragment,
7074
address,
7175
value,
72-
tag,
7376
timestamp,
7477
current_index,
7578
last_index,
7679
bundle_hash,
7780
trunk_transaction_hash,
7881
branch_transaction_hash,
82+
tag,
83+
attachment_timestamp,
84+
attachment_timestamp_lower_bound,
85+
attachment_timestamp_upper_bound,
7986
nonce,
87+
legacy_tag = None
8088
):
81-
# type: (Optional[TransactionHash], Optional[Fragment], Address, int, Optional[Tag], int, Optional[int], Optional[int], Optional[BundleHash], Optional[TransactionHash], Optional[TransactionHash], Optional[Hash]) -> None
89+
# type: (Optional[TransactionHash], Optional[Fragment], Address, int, int, Optional[int], Optional[int], Optional[BundleHash], Optional[TransactionHash], Optional[TransactionHash], Optional[Tag], Optional[int], Optional[int], Optional[int] Optional[Hash]) -> None
8290
self.hash = hash_ # type: Optional[TransactionHash]
8391
"""
8492
Transaction ID, generated by taking a hash of the transaction
@@ -104,12 +112,12 @@ def __init__(
104112
Can be negative (i.e., for spending inputs).
105113
"""
106114

107-
self.tag = tag # type: Optional[Tag]
115+
self._legacy_tag = legacy_tag # type: Optional[Tag]
108116
"""
109-
Optional classification tag applied to this transaction.
117+
Optional classification legacy_tag applied to this transaction.
110118
"""
111119

112-
self.nonce = nonce # type: Optional[Hash]
120+
self.nonce = nonce # type: Optional[Nonce]
113121
"""
114122
Unique value used to increase security of the transaction hash.
115123
"""
@@ -154,6 +162,17 @@ def __init__(
154162
155163
The branch transaction generally has no significance.
156164
"""
165+
166+
self.tag = tag # type: Optional[Tag]
167+
"""
168+
Optional classification tag applied to this transaction.
169+
"""
170+
171+
self.attachment_timestamp = attachment_timestamp # type: int
172+
173+
self.attachment_timestamp_lower_bound = attachment_timestamp_lower_bound # type: int
174+
175+
self.attachment_timestamp_upper_bound = attachment_timestamp_upper_bound # type: int
157176

158177
self.signature_message_fragment = signature_message_fragment # type: Optional[Fragment]
159178
"""
@@ -227,6 +246,36 @@ def last_index_as_trytes(self):
227246
"""
228247
# Note that we are padding to 27 _trits_.
229248
return TryteString.from_trits(trits_from_int(self.last_index, pad=27))
249+
250+
@property
251+
def attachment_timestamp_as_trytes(self):
252+
# type: () -> TryteString
253+
"""
254+
Returns a TryteString representation of the transaction's
255+
attachment timestamp.
256+
"""
257+
#Note that we are padding to 27 _trits_.
258+
return TryteString.from_trits(trits_from_int(self.attachment_timestamp, pad=27))
259+
260+
@property
261+
def attachment_timestamp_lower_bound_as_trytes(self):
262+
# type: () -> TryteString
263+
"""
264+
Returns a TryteString representation of the transaction's
265+
attachment timestamp lower bound.
266+
"""
267+
#Note that we are padding to 27 _trits_.
268+
return TryteString.from_trits(trits_from_int(self.attachment_timestamp_lower_bound, pad=27))
269+
270+
@property
271+
def attachment_timestamp_upper_bound_as_trytes(self):
272+
# type: () -> TryteString
273+
"""
274+
Returns a TryteString representation of the transaction's
275+
attachment timestamp upper bound.
276+
"""
277+
#Note that we are padding to 27 _trits_.
278+
return TryteString.from_trits(trits_from_int(self.attachment_timestamp_upper_bound, pad=27))
230279

231280
def as_json_compatible(self):
232281
# type: () -> dict
@@ -237,18 +286,22 @@ def as_json_compatible(self):
237286
- :py:class:`iota.json.JsonEncoder`.
238287
"""
239288
return {
240-
'hash_': self.hash,
241-
'signature_message_fragment': self.signature_message_fragment,
242-
'address': self.address,
243-
'value': self.value,
244-
'tag': self.tag,
245-
'timestamp': self.timestamp,
246-
'current_index': self.current_index,
247-
'last_index': self.last_index,
248-
'bundle_hash': self.bundle_hash,
249-
'trunk_transaction_hash': self.trunk_transaction_hash,
250-
'branch_transaction_hash': self.branch_transaction_hash,
251-
'nonce': self.nonce,
289+
'hash_': self.hash,
290+
'signature_message_fragment': self.signature_message_fragment,
291+
'address': self.address,
292+
'value': self.value,
293+
'legacy_tag': self.legacy_tag,
294+
'timestamp': self.timestamp,
295+
'current_index': self.current_index,
296+
'last_index': self.last_index,
297+
'bundle_hash': self.bundle_hash,
298+
'trunk_transaction_hash': self.trunk_transaction_hash,
299+
'branch_transaction_hash': self.branch_transaction_hash,
300+
'tag': self.tag,
301+
'attachment_timestamp': self.attachment_timestamp,
302+
'attachment_timestamp_lower_bound': self.attachment_timestamp_lower_bound,
303+
'attachment_timestamp_upper_bound': self.attachment_timestamp_upper_bound,
304+
'nonce': self.nonce,
252305
}
253306

254307
def as_tryte_string(self):
@@ -260,13 +313,17 @@ def as_tryte_string(self):
260313
self.signature_message_fragment
261314
+ self.address.address
262315
+ self.value_as_trytes
263-
+ self.tag
316+
+ self.legacy_tag
264317
+ self.timestamp_as_trytes
265318
+ self.current_index_as_trytes
266319
+ self.last_index_as_trytes
267320
+ self.bundle_hash
268321
+ self.trunk_transaction_hash
269322
+ self.branch_transaction_hash
323+
+ self.tag
324+
+ self.attachment_timestamp_as_trytes
325+
+ self.attachment_timestamp_lower_bound_as_trytes
326+
+ self.attachment_timestamp_upper_bound_as_trytes
270327
+ self.nonce
271328
)
272329

@@ -279,11 +336,20 @@ def get_signature_validation_trytes(self):
279336
return (
280337
self.address.address
281338
+ self.value_as_trytes
282-
+ self.tag
339+
+ self.legacy_tag
283340
+ self.timestamp_as_trytes
284341
+ self.current_index_as_trytes
285342
+ self.last_index_as_trytes
286343
)
344+
345+
@property
346+
def legacy_tag(self):
347+
# type: () -> Tag
348+
"""
349+
Return the legacy tag of the transaction.
350+
If no legacy tag was set, returns the tag instead.
351+
"""
352+
return self._legacy_tag or self.tag
287353

288354

289355
class Bundle(JsonSerializable, Sequence[Transaction]):

iota/transaction/creation.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from iota.crypto.types import PrivateKey
1414
from iota.exceptions import with_context
1515
from iota.transaction.base import Bundle, Transaction
16-
from iota.transaction.types import BundleHash, Fragment, TransactionHash
16+
from iota.transaction.types import BundleHash, Fragment, TransactionHash, Nonce
1717
from iota.transaction.utils import get_current_timestamp
1818
from iota.types import Address, Hash, Tag, TryteString
1919

@@ -36,23 +36,26 @@ def __init__(self, address, value, tag=None, message=None, timestamp=None):
3636
timestamp = get_current_timestamp()
3737

3838
super(ProposedTransaction, self).__init__(
39-
address = address,
40-
tag = Tag(b'') if tag is None else tag,
41-
timestamp = timestamp,
42-
value = value,
39+
address = address,
40+
tag = Tag(b'') if tag is None else tag,
41+
timestamp = timestamp,
42+
value = value,
4343

4444
# These values will be populated when the bundle is finalized.
45-
bundle_hash = None,
46-
current_index = None,
47-
hash_ = None,
48-
last_index = None,
49-
signature_message_fragment = None,
45+
bundle_hash = None,
46+
current_index = None,
47+
hash_ = None,
48+
last_index = None,
49+
signature_message_fragment = None,
50+
attachment_timestamp = 0,
51+
attachment_timestamp_lower_bound = 0,
52+
attachment_timestamp_upper_bound = 0,
5053

5154
# These values start out empty; they will be populated when the
5255
# node does PoW.
53-
branch_transaction_hash = TransactionHash(b''),
54-
nonce = Hash(b''),
55-
trunk_transaction_hash = TransactionHash(b''),
56+
branch_transaction_hash = TransactionHash(b''),
57+
nonce = Nonce(b''),
58+
trunk_transaction_hash = TransactionHash(b''),
5659
)
5760

5861
self.message = TryteString(b'') if message is None else message

iota/transaction/types.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
'Fragment',
1212
'TransactionHash',
1313
'TransactionTrytes',
14+
'Nonce'
1415
]
1516

1617

@@ -72,3 +73,25 @@ def __init__(self, trytes):
7273
'trytes': trytes,
7374
},
7475
)
76+
77+
class Nonce(TryteString):
78+
"""
79+
A TryteString that acts as a transaction nonce.
80+
"""
81+
LEN = 27
82+
83+
def __init__(self, trytes):
84+
# type: (TrytesCompatible) -> None
85+
super(Nonce, self).__init__(trytes, pad=self.LEN)
86+
87+
if len(self._trytes) > self.LEN:
88+
raise with_context(
89+
exc = ValueError('{cls} values must be {len} trytes long.'.format(
90+
cls = type(self).__name__,
91+
len = self.LEN
92+
)),
93+
94+
context = {
95+
'trytes': trytes,
96+
},
97+
)

test/commands/extended/get_bundles_test.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from filters.test import BaseFilterTestCase
99

1010
from iota import Address, BadApiResponse, Bundle, BundleHash, Fragment, Hash, \
11-
Iota, Tag, Transaction, TransactionHash, TransactionTrytes
11+
Iota, Tag, Transaction, TransactionHash, TransactionTrytes, Nonce
1212
from iota.adapter import MockAdapter
1313
from iota.commands.extended.get_bundles import GetBundlesCommand
1414
from iota.filters import Trytes
@@ -141,21 +141,24 @@ def test_single_transaction(self):
141141
"""
142142
transaction =\
143143
Transaction(
144-
current_index = 0,
145-
last_index = 0,
146-
tag = Tag(b''),
147-
timestamp = 1484960990,
148-
value = 0,
144+
current_index = 0,
145+
last_index = 0,
146+
tag = Tag(b''),
147+
timestamp = 1484960990,
148+
value = 0,
149+
attachment_timestamp = 1484960990,
150+
attachment_timestamp_lower_bound = 12,
151+
attachment_timestamp_upper_bound = 0,
149152

150153
# These values are not relevant for 0-value transactions.
151-
nonce = Hash(b''),
154+
nonce = Nonce(b''),
152155
signature_message_fragment = Fragment(b''),
153156

154157
# This value is computed automatically, so it has to be real.
155158
hash_ =
156159
TransactionHash(
157-
b'UGQBSMKGNXXWDCS9XZCFTPUXFADCT9I9KCNQGUXK'
158-
b'NDJDUXLWODOVJQWJHCLWTODAELDXGL9SMQYQZFWHE',
160+
b'XPJIYZWPF9LBCYZPNBFARDRCSUGJGF9TWZT9K9PX'
161+
b'VYDFPZOZBGXUCKLTJEUCFBEKQQ9VCSQVQDMMJQAY9',
159162
),
160163

161164
address =
@@ -238,8 +241,8 @@ def test_multiple_transactions(self):
238241
b'999999999999999999999999999999999999999999999999999999999999999999'
239242
b'999999999999999999999999999999999999999999999999999999999999999999'
240243
b'999999999WUQXEGBVIECGIWO9IGSYKWWPYCIVUJJGSJPWGIAFJPYSF9NSQOHWAHS9P'
241-
b'9PWQHOBXNNQIF9IRHVQXKPZW999999999999999999999999999999999999999999'
242-
b'999999999999HNLFMVD99A99999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
244+
b'9PWQHOBXNNQIF9IRHVQXKPZW999999999999999999999999999XZUIENOTTBKJMDP'
245+
b'RXWGQYG9PWGTHNLFMVD99A99999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
243246
b'DOGSEXIHKIBWSLDAWUKZCZMK9Z9YZSPCKBDJSVDPRQLJSTKUMTNVSXBGUEHHGAIWWQ'
244247
b'BCJZHZAQOWZMAIDAFUZBVMUVPWQJLUGGQKNKLMGTWXXNZKUCBJLEDAMYVRGABAWBY9'
245248
b'999MYIYBTGIOQYYZFJBLIAWMPSZEFFTXUZPCDIXSLLQDQSFYGQSQOGSPKCZNLVSZ9L'
@@ -285,8 +288,8 @@ def test_multiple_transactions(self):
285288
b'999999999999999999999999999999999999999999999999999999999999999999'
286289
b'999999999999999999999999999999999999999999999999999999999999999999'
287290
b'999999999999999999999999999999999999999999999999999999999999999999'
288-
b'999999999999999999999999999999999999999999999999999999999999999999'
289-
b'999999999999HNLFMVD99999999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
291+
b'999999999999999999999999999999999999999999999999999SYRABNN9JD9PNDL'
292+
b'IKUNCECUELTOHNLFMVD99999999999A99999999PDQWLVVDPUU9VIBODGMRIAZPGQX'
290293
b'DOGSEXIHKIBWSLDAWUKZCZMK9Z9YZSPCKBDJSVDPRQLJSTKUMTNVSXFSEWUNJOEGNU'
291294
b'I9QOCRFMYSIFAZLJHKZBPQZZYFG9ORYCRDX9TOMJPFCRB9R9KPUUGFPVOWYXFIWEW9'
292295
b'999BGUEHHGAIWWQBCJZHZAQOWZMAIDAFUZBVMUVPWQJLUGGQKNKLMGTWXXNZKUCBJL'
@@ -353,7 +356,6 @@ def test_multiple_transactions(self):
353356
b'DRXICGYDGSVPXFTILFFGAPICYHGGJ9OHXINFX9999'
354357
),
355358
)
356-
357359
self.maxDiff = None
358360
self.assertListEqual(
359361
response['bundles'][0].as_json_compatible(),

test/commands/extended/get_transfers_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ def create_generator(ag, start, step=1):
410410
bundle_hash = None,
411411
trunk_transaction_hash = None,
412412
branch_transaction_hash = None,
413+
attachment_timestamp = 1483033814,
414+
attachment_timestamp_lower_bound = 12,
415+
attachment_timestamp_upper_bound = 0,
413416
nonce = None,
414417
)
415418
])
@@ -526,6 +529,9 @@ def create_generator(ag, start, step=1):
526529
bundle_hash = None,
527530
trunk_transaction_hash = None,
528531
branch_transaction_hash = None,
532+
attachment_timestamp = 1483033814,
533+
attachment_timestamp_lower_bound = 12,
534+
attachment_timestamp_upper_bound = 0,
529535
nonce = None,
530536
)
531537
])
@@ -601,6 +607,9 @@ def create_generator(ag, start, step=1):
601607
bundle_hash = None,
602608
trunk_transaction_hash = None,
603609
branch_transaction_hash = None,
610+
attachment_timestamp = 1483033814,
611+
attachment_timestamp_lower_bound = 12,
612+
attachment_timestamp_upper_bound = 0,
604613
nonce = None,
605614
)
606615
])

0 commit comments

Comments
 (0)