Skip to content
This repository was archived by the owner on Aug 23, 2020. It is now read-only.

Commit af9b4c5

Browse files
author
Gal Rogozinski
committed
Merge branch 'release-v1.8.5'
2 parents fa79533 + ff5157d commit af9b4c5

File tree

16 files changed

+300
-54
lines changed

16 files changed

+300
-54
lines changed

changelog.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
1.8.5
2+
3+
Protocol-Change:
4+
Added bundle validity rules - check that bundles confirm only tails and 2 bundles at most (#1786)
5+
Check validity rules on tip-sel and check-consistency only (#1802)
6+
7+
18
1.8.4
29

310
Hotfix: Ensure proper creation of solid entrypoints (#1702)

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>com.iota</groupId>
77
<artifactId>iri</artifactId>
8-
<version>1.8.4</version>
8+
<version>1.8.5</version>
99
<name>IRI</name>
1010
<description>IOTA Reference Implementation</description>
1111

python-regression/tests/features/steps/api_test_steps.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,11 @@ def compare_thread_return(step, api_call):
120120
del response_list['duration']
121121
if 'info' in response_list:
122122
del response_list['info']
123-
response_keys = response_list.keys()
123+
response_keys = list(response_list.keys())
124124

125125
expected_values = {}
126126
api_utils.prepare_options(step.hashes,expected_values)
127-
keys = expected_values.keys()
127+
keys = list(expected_values.keys())
128128

129129
# Confirm that the lists are of equal length before comparing
130130
assert len(keys) == len(response_keys), \

python-regression/util/neighbor_logic/neighbor_logic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ def check_if_neighbors(api, neighbors, expected_neighbor):
2626

2727
if is_neighbor is False:
2828
tcp_address = "tcp://" + expected_neighbor
29-
api.add_neighbors([tcp_address.decode()])
29+
api.add_neighbors([tcp_address])
3030
logger.info('{} added as neighbor'.format(tcp_address))

python-regression/util/test_logic/value_fetch_logic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def fetch_node_address(value):
7676
host = world.machine['nodes'][value]['host']
7777
port = world.machine['nodes'][value]['ports']['gossip-tcp']
7878
address = "tcp://" + host + ":" + str(port)
79-
return [address.decode()]
79+
return [address]
8080

8181

8282
def fetch_static_value(value):

src/main/java/com/iota/iri/BundleValidator.java

Lines changed: 113 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.iota.iri.utils.Converter;
99

1010
import java.util.*;
11+
import com.google.common.annotations.VisibleForTesting;
1112

1213
/**
1314
* Validates bundles.
@@ -55,22 +56,33 @@ public enum Validity {
5556
*/
5657
public static final int MODE_VALIDATE_SEMANTICS = 1 << 2;
5758

59+
/**
60+
* Instructs the validation code to validate all transactions within the bundle approve via their branch the trunk
61+
* transaction of the head transaction
62+
*/
63+
public static final int MODE_VALIDATE_BUNDLE_TX_APPROVAL = 1 << 3;
64+
65+
/**
66+
* Instructs the validation code to validate that the bundle only approves tail txs.
67+
*/
68+
public static final int MODE_VALIDATE_TAIL_APPROVAL = 1 << 4;
69+
5870
/**
5971
* Instructs the validation code to fully validate the semantics, bundle hash and signatures of the given bundle.
6072
*/
61-
public static final int MODE_VALIDATE_ALL = MODE_VALIDATE_SIGNATURES | MODE_VALIDATE_BUNDLE_HASH | MODE_VALIDATE_SEMANTICS;
73+
public static final int MODE_VALIDATE_ALL = MODE_VALIDATE_SIGNATURES | MODE_VALIDATE_BUNDLE_HASH | MODE_VALIDATE_SEMANTICS | MODE_VALIDATE_TAIL_APPROVAL | MODE_VALIDATE_BUNDLE_TX_APPROVAL;
6274

6375
/**
6476
* Instructs the validation code to skip checking the bundle's already computed validity and instead to proceed to
6577
* validate the bundle further.
6678
*/
67-
public static final int MODE_SKIP_CACHED_VALIDITY = 1 << 3;
79+
public static final int MODE_SKIP_CACHED_VALIDITY = 1 << 5;
6880

6981
/**
7082
* Instructs the validation code to skip checking whether the tail transaction is present or a tail transaction was
7183
* given as the start transaction.
7284
*/
73-
public static final int MODE_SKIP_TAIL_TX_EXISTENCE = 1 << 4;
85+
public static final int MODE_SKIP_TAIL_TX_EXISTENCE = 1 << 6;
7486

7587
/**
7688
* Fetches a bundle of transactions identified by the {@code tailHash} and validates the transactions. Bundle is a
@@ -85,6 +97,8 @@ public enum Validity {
8597
* <li>Total bundle value is 0 (inputs and outputs are balanced)</li>
8698
* <li>Recalculate the bundle hash by absorbing and squeezing the transactions' essence</li>
8799
* <li>Validate the signature on input transactions</li>
100+
* <li>The bundle must only approve tail transactions</li>
101+
* <li>All transactions within the bundle approve via their branch the trunk transaction of the head transaction.</li>
88102
* </ol>
89103
* <p>
90104
* As well as the following syntactic checks:
@@ -96,9 +110,11 @@ public enum Validity {
96110
* we lose the last trit in the process</li>
97111
* </ol>
98112
*
99-
* @param tangle used to fetch the bundle's transactions from the persistence layer
100-
* @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
101-
* @param tailHash the hash of the last transaction in a bundle.
113+
* @param tangle used to fetch the bundle's transactions from the persistence layer
114+
* @param enforceExtraRules true if enforce {@link #validateBundleTailApproval(Tangle, List)} and
115+
* {@link #validateBundleTransactionsApproval(List)}
116+
* @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
117+
* @param tailHash the hash of the last transaction in a bundle.
102118
* @return A list of transactions of the bundle contained in another list. If the bundle is valid then the tail
103119
* transaction's {@link TransactionViewModel#getValidity()} will return 1, else {@link
104120
* TransactionViewModel#getValidity()} will return -1. If the bundle is invalid then an empty list will be
@@ -108,9 +124,32 @@ public enum Validity {
108124
* validate it again.
109125
* </p>
110126
*/
111-
public List<TransactionViewModel> validate(Tangle tangle, Snapshot initialSnapshot, Hash tailHash) throws Exception {
127+
public List<TransactionViewModel> validate(Tangle tangle, boolean enforceExtraRules, Snapshot initialSnapshot,
128+
Hash tailHash) throws Exception {
129+
int mode = getMode(enforceExtraRules);
130+
return validate(tangle, initialSnapshot, tailHash, mode);
131+
}
132+
133+
/**
134+
* Does {@link #validate(Tangle, boolean, Snapshot, Hash)} but with an option of skipping some checks according to
135+
* the give {@code mode}
136+
*
137+
* @param tangle used to fetch the bundle's transactions from the persistence layer
138+
* @param initialSnapshot the initial snapshot that defines the genesis for our ledger state
139+
* @param tailHash the hash of the last transaction in a bundle.
140+
* @param mode flags that specify which validation checks to perform
141+
* @return A list of transactions of the bundle contained in another list. If the bundle is valid then the tail
142+
* transaction's {@link TransactionViewModel#getValidity()} will return 1, else
143+
* {@link TransactionViewModel#getValidity()} will return -1. If the bundle is invalid then an empty list
144+
* will be returned.
145+
* @throws Exception if a persistence error occurred
146+
* @implNote if {@code tailHash} was already invalidated/validated by a previous call to this method then we don't
147+
* validate it again.
148+
* @see #validate(Tangle, boolean, Snapshot, Hash)
149+
*/
150+
private List<TransactionViewModel> validate(Tangle tangle, Snapshot initialSnapshot, Hash tailHash, int mode) throws Exception {
112151
List<TransactionViewModel> bundleTxs = new LinkedList<>();
113-
switch (validate(tangle, tailHash, MODE_VALIDATE_ALL, bundleTxs)) {
152+
switch (validate(tangle, tailHash, mode, bundleTxs)) {
114153
case VALID:
115154
if (bundleTxs.get(0).getValidity() != 1) {
116155
bundleTxs.get(0).setValidity(tangle, initialSnapshot, 1);
@@ -126,7 +165,14 @@ public List<TransactionViewModel> validate(Tangle tangle, Snapshot initialSnapsh
126165
}
127166
}
128167

129-
private static boolean hasMode(int mode, int has) {
168+
private static int getMode(boolean enforceExtraRules) {
169+
if (enforceExtraRules) {
170+
return MODE_VALIDATE_ALL;
171+
}
172+
return MODE_VALIDATE_SIGNATURES | MODE_VALIDATE_BUNDLE_HASH | MODE_VALIDATE_SEMANTICS;
173+
}
174+
175+
private static boolean hasMode(int mode, int has) {
130176
return (mode & has) == has;
131177
}
132178

@@ -147,7 +193,9 @@ private static boolean hasMode(int mode, int has) {
147193
* @return whether the validation criteria were passed or not
148194
* @throws Exception if an error occurred in the persistence layer
149195
*/
150-
public static Validity validate(Tangle tangle, Hash startTxHash, int validationMode, List<TransactionViewModel> bundleTxs) throws Exception {
196+
@VisibleForTesting
197+
Validity validate(Tangle tangle, Hash startTxHash, int validationMode, List<TransactionViewModel> bundleTxs)
198+
throws Exception {
151199
TransactionViewModel startTx = TransactionViewModel.fromHash(tangle, startTxHash);
152200
if (startTx == null || (!hasMode(validationMode, MODE_SKIP_TAIL_TX_EXISTENCE) &&
153201
(startTx.getCurrentIndex() != 0 || startTx.getValidity() == -1))) {
@@ -160,7 +208,8 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
160208
!hasMode(validationMode, MODE_VALIDATE_SEMANTICS));
161209

162210
// check the semantics of the bundle: total sum, semantics per tx (current/last index), missing txs, supply
163-
Validity bundleSemanticsValidity = validateBundleSemantics(startTx, bundleTxsMapping, bundleTxs, validationMode);
211+
Validity bundleSemanticsValidity = validateBundleSemantics(startTx, bundleTxsMapping, bundleTxs,
212+
validationMode);
164213
if (hasMode(validationMode, MODE_VALIDATE_SEMANTICS) && bundleSemanticsValidity != Validity.VALID) {
165214
return bundleSemanticsValidity;
166215
}
@@ -177,6 +226,21 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
177226
return bundleHashValidity;
178227
}
179228

229+
//verify that the bundle only approves tail txs
230+
if (hasMode(validationMode, MODE_VALIDATE_TAIL_APPROVAL)) {
231+
Validity bundleTailApprovalValidity = validateBundleTailApproval(tangle, bundleTxs);
232+
if (bundleTailApprovalValidity != Validity.VALID) {
233+
return bundleTailApprovalValidity;
234+
}
235+
}
236+
237+
//verify all transactions within the bundle approve via their branch the trunk transaction of the head transaction
238+
if (hasMode(validationMode, MODE_VALIDATE_BUNDLE_TX_APPROVAL)) {
239+
Validity bundleTransactionsApprovalValidity = validateBundleTransactionsApproval(bundleTxs);
240+
if (bundleTransactionsApprovalValidity != Validity.VALID) {
241+
return bundleTransactionsApprovalValidity;
242+
}
243+
}
180244

181245
// verify the signatures of input transactions
182246
if (hasMode(validationMode, MODE_VALIDATE_SIGNATURES)) {
@@ -198,7 +262,8 @@ public static Validity validate(Tangle tangle, Hash startTxHash, int validationM
198262
* @param bundleTxs an empty list which gets filled with the transactions in order of trunk ordering
199263
* @return whether the bundle is semantically valid
200264
*/
201-
public static Validity validateBundleSemantics(TransactionViewModel startTx, Map<Hash, TransactionViewModel> bundleTxsMapping, List<TransactionViewModel> bundleTxs) {
265+
public Validity validateBundleSemantics(TransactionViewModel startTx,
266+
Map<Hash, TransactionViewModel> bundleTxsMapping, List<TransactionViewModel> bundleTxs) {
202267
return validateBundleSemantics(startTx, bundleTxsMapping, bundleTxs, MODE_VALIDATE_SEMANTICS);
203268
}
204269

@@ -221,7 +286,9 @@ public static Validity validateBundleSemantics(TransactionViewModel startTx, Map
221286
* @param validationMode the used validation mode
222287
* @return whether the bundle is semantically valid
223288
*/
224-
private static Validity validateBundleSemantics(TransactionViewModel startTx, Map<Hash, TransactionViewModel> bundleTxsMapping, List<TransactionViewModel> bundleTxs, int validationMode) {
289+
private Validity validateBundleSemantics(TransactionViewModel startTx,
290+
Map<Hash, TransactionViewModel> bundleTxsMapping, List<TransactionViewModel> bundleTxs,
291+
int validationMode) {
225292
TransactionViewModel tvm = startTx;
226293
final long lastIndex = tvm.lastIndex();
227294
long bundleValue = 0;
@@ -379,6 +446,39 @@ public static boolean isInconsistent(Collection<TransactionViewModel> transactio
379446
return (sum != 0 || transactionViewModels.isEmpty());
380447
}
381448

449+
/**
450+
* A bundle is invalid if The branch transaction hash of the non head transactions within a bundle, is not the same
451+
* as the trunk transaction hash of the head transaction.
452+
*
453+
* @param bundleTxs list of transactions that are in a bundle.
454+
* @return Whether the bundle tx chain is valid.
455+
*/
456+
@VisibleForTesting
457+
Validity validateBundleTransactionsApproval(List<TransactionViewModel> bundleTxs) {
458+
Hash headTrunkTransactionHash = bundleTxs.get(bundleTxs.size() - 1).getTrunkTransactionHash();
459+
for(int i = 0; i < bundleTxs.size() - 1; i++){
460+
if(!bundleTxs.get(i).getBranchTransactionHash().equals(headTrunkTransactionHash)){
461+
return Validity.INVALID;
462+
}
463+
}
464+
return Validity.VALID;
465+
}
466+
467+
/**
468+
* A bundle is invalid if the trunk and branch transactions approved by the bundle are non tails.
469+
*
470+
* @param bundleTxs The txs in the bundle.
471+
* @return Whether the bundle approves only tails.
472+
*/
473+
@VisibleForTesting
474+
Validity validateBundleTailApproval(Tangle tangle, List<TransactionViewModel> bundleTxs) throws Exception {
475+
TransactionViewModel headTx = bundleTxs.get(bundleTxs.size() - 1);
476+
TransactionViewModel bundleTrunkTvm = headTx.getTrunkTransaction(tangle);
477+
TransactionViewModel bundleBranchTvm = headTx.getBranchTransaction(tangle);
478+
return bundleTrunkTvm != null && bundleBranchTvm != null && bundleBranchTvm.getCurrentIndex() == 0
479+
&& bundleTrunkTvm.getCurrentIndex() == 0 ? Validity.VALID : Validity.INVALID;
480+
}
481+
382482
/**
383483
* Traverses down the given {@code tail} trunk until all transactions that belong to the same bundle (identified by
384484
* the bundle hash) are found and loaded.

src/main/java/com/iota/iri/network/NeighborRouterImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ private boolean finalizeHandshake(String identity, Neighbor neighbor, SocketChan
508508
return false;
509509
case FAILED:
510510
// faulty handshaking
511-
log.error("dropping connection to neighbor {} as handshaking was faulty", identity);
511+
log.warn("dropping connection to neighbor {} as handshaking was faulty", identity);
512512
closeNeighborConnection(channel, identity, selector);
513513
return false;
514514
default:

src/main/java/com/iota/iri/service/API.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,10 +389,14 @@ private AbstractResponse checkConsistencyStatement(List<String> transactionsList
389389
state = false;
390390
info = "tails are not solid (missing a referenced tx): " + transaction;
391391
break;
392-
} else if (bundleValidator.validate(tangle, snapshotProvider.getInitialSnapshot(), txVM.getHash()).isEmpty()) {
393-
state = false;
394-
info = "tails are not consistent (bundle is invalid): " + transaction;
395-
break;
392+
} else {
393+
if (bundleValidator
394+
.validate(tangle, true, snapshotProvider.getInitialSnapshot(), txVM.getHash())
395+
.isEmpty()) {
396+
state = false;
397+
info = "tails are not consistent (bundle is invalid): " + transaction;
398+
break;
399+
}
396400
}
397401
}
398402

src/main/java/com/iota/iri/service/ledger/LedgerService.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.iota.iri.controllers.MilestoneViewModel;
44
import com.iota.iri.model.Hash;
5+
import com.iota.iri.storage.Tangle;
56

67
import java.util.List;
78
import java.util.Map;
@@ -99,12 +100,16 @@ public interface LedgerService {
99100
* </p>
100101
*
101102
* @param visitedTransactions a set of transaction hashes that shall be considered to be visited already
102-
* @param startTransaction the transaction that marks the start of the dag traversal and that has its approvees
103-
* examined
103+
* @param startTransaction the transaction that marks the start of the dag traversal and that has its approvees
104+
* examined
105+
* @param enforceExtraRules enforce {@link com.iota.iri.BundleValidator#validateBundleTransactionsApproval(List)}
106+
* and {@link com.iota.iri.BundleValidator#validateBundleTailApproval(Tangle, List)}.
107+
* Enforcing them may break backwards compatibility.
104108
* @return a map of the balance changes (addresses associated to their balance) or {@code null} if the balance could
105109
* not be generated due to inconsistencies
106110
* @throws LedgerException if anything unexpected happens while generating the balance changes
107111
*/
108-
Map<Hash, Long> generateBalanceDiff(Set<Hash> visitedTransactions, Hash startTransaction, int milestoneIndex)
112+
Map<Hash, Long> generateBalanceDiff(Set<Hash> visitedTransactions, Hash startTransaction, int milestoneIndex,
113+
boolean enforceExtraRules)
109114
throws LedgerException;
110115
}

src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public boolean isBalanceDiffConsistent(Set<Hash> approvedHashes, Map<Hash, Long>
133133
}
134134
Set<Hash> visitedHashes = new HashSet<>(approvedHashes);
135135
Map<Hash, Long> currentState = generateBalanceDiff(visitedHashes, tip,
136-
snapshotProvider.getLatestSnapshot().getIndex());
136+
snapshotProvider.getLatestSnapshot().getIndex(), true);
137137
if (currentState == null) {
138138
return false;
139139
}
@@ -152,7 +152,8 @@ public boolean isBalanceDiffConsistent(Set<Hash> approvedHashes, Map<Hash, Long>
152152
}
153153

154154
@Override
155-
public Map<Hash, Long> generateBalanceDiff(Set<Hash> visitedTransactions, Hash startTransaction, int milestoneIndex)
155+
public Map<Hash, Long> generateBalanceDiff(Set<Hash> visitedTransactions, Hash startTransaction, int milestoneIndex,
156+
boolean enforceExtraRules)
156157
throws LedgerException {
157158

158159
Map<Hash, Long> state = new HashMap<>();
@@ -176,7 +177,8 @@ public Map<Hash, Long> generateBalanceDiff(Set<Hash> visitedTransactions, Hash s
176177
if (!milestoneService.isTransactionConfirmed(transactionViewModel, milestoneIndex)) {
177178

178179
final List<TransactionViewModel> bundleTransactions = bundleValidator.validate(tangle,
179-
snapshotProvider.getInitialSnapshot(), transactionViewModel.getHash());
180+
enforceExtraRules, snapshotProvider.getInitialSnapshot(),
181+
transactionViewModel.getHash());
180182

181183
if (bundleTransactions.isEmpty()) {
182184
return null;
@@ -257,7 +259,7 @@ private boolean generateStateDiff(MilestoneViewModel milestone) throws LedgerExc
257259
try {
258260
Hash tail = transactionViewModel.getHash();
259261
Map<Hash, Long> balanceChanges = generateBalanceDiff(new HashSet<>(), tail,
260-
snapshotProvider.getLatestSnapshot().getIndex());
262+
snapshotProvider.getLatestSnapshot().getIndex(), false);
261263
successfullyProcessed = balanceChanges != null;
262264
if (successfullyProcessed) {
263265
successfullyProcessed = snapshotProvider.getLatestSnapshot().patchedState(

0 commit comments

Comments
 (0)