From 3f6c2ecff156e3f83022c6f78e0011f6a1373edc Mon Sep 17 00:00:00 2001 From: Christian Lehmann Date: Wed, 28 Nov 2018 16:39:53 +0100 Subject: [PATCH 01/34] Fixed #1201: Posting invalid json to IRI will result in detailed error message. --- src/main/java/com/iota/iri/service/API.java | 9 ++++++++- .../com/iota/iri/service/APIIntegrationTests.java | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index c6ff0e1218..8320fd5e36 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -1,5 +1,6 @@ package com.iota.iri.service; +import com.google.gson.JsonSyntaxException; import com.iota.iri.*; import com.iota.iri.conf.APIConfig; import com.iota.iri.conf.ConsensusConfig; @@ -368,7 +369,13 @@ private AbstractResponse process(final String requestString, InetSocketAddress s try { // Request JSON data into map - final Map request = gson.fromJson(requestString, Map.class); + final Map request; + try { + request = gson.fromJson(requestString, Map.class); + } + catch(JsonSyntaxException jsonSyntaxException) { + return ExceptionResponse.create("Invalid JSON syntax: " + jsonSyntaxException.getMessage()); + } if (request == null) { return ExceptionResponse.create("Invalid request payload: '" + requestString + "'"); } diff --git a/src/test/java/com/iota/iri/service/APIIntegrationTests.java b/src/test/java/com/iota/iri/service/APIIntegrationTests.java index 89a0e99bd9..4931576d2f 100644 --- a/src/test/java/com/iota/iri/service/APIIntegrationTests.java +++ b/src/test/java/com/iota/iri/service/APIIntegrationTests.java @@ -146,6 +146,18 @@ private static Gson gson() { return new GsonBuilder().create(); } + @Test + public void sendNonJsonBody() { + responseSpec.statusCode(500); + given(). + body("thisIsInvalidJson"). + when(). + post("/"). + then(). + spec(responseSpec). + body(containsString("Invalid JSON syntax")); + } + @Test public void shouldTestGetNodeInfo() { From 48dd9d46de890a4beb21232bd206d02c87ba90ad Mon Sep 17 00:00:00 2001 From: Christian Lehmann Date: Wed, 28 Nov 2018 17:01:06 +0100 Subject: [PATCH 02/34] Added specification response for status code 500. --- .../iota/iri/service/APIIntegrationTests.java | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/test/java/com/iota/iri/service/APIIntegrationTests.java b/src/test/java/com/iota/iri/service/APIIntegrationTests.java index 4931576d2f..2ec40d1314 100644 --- a/src/test/java/com/iota/iri/service/APIIntegrationTests.java +++ b/src/test/java/com/iota/iri/service/APIIntegrationTests.java @@ -46,7 +46,8 @@ public class APIIntegrationTests { // Expect to connect to any service worldwide in under 100 ms // and to any online machine local in 1 ms. The 50 ms default value is a suggested compromise. private static final int CONNECTION_TIMEOUT = 50; - private static ResponseSpecification responseSpec; + private static ResponseSpecification specResponseSuccess; + private static ResponseSpecification specResponseError; // Constants used in tests private static final String[] URIS = {"udp://8.8.8.8:14266", "udp://8.8.8.5:14266"}; private static final String[] ADDRESSES = {"RVORZ9SIIP9RCYMREUIXXVPQIPHVCNPQ9HZWYKFWYWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVA"}; @@ -115,10 +116,17 @@ public static void tearDown() { RestAssured.port = Integer.parseInt(portStr); RestAssured.baseURI = hostName; - ResponseSpecBuilder builder = new ResponseSpecBuilder(); - builder.expectStatusCode(200); - builder.expectBody(containsString("duration")); - responseSpec = builder.build(); + // Define response specification for http status code 200 + specResponseSuccess = new ResponseSpecBuilder(). + expectStatusCode(200). + expectBody(containsString("duration")). + build(); + + // Define response specification for http status code 500 + specResponseError = new ResponseSpecBuilder(). + expectStatusCode(500). + expectBody(containsString("duration")). + build(); } /** @@ -148,13 +156,12 @@ private static Gson gson() { @Test public void sendNonJsonBody() { - responseSpec.statusCode(500); given(). body("thisIsInvalidJson"). when(). post("/"). then(). - spec(responseSpec). + spec(specResponseError). body(containsString("Invalid JSON syntax")); } @@ -169,7 +176,7 @@ public void shouldTestGetNodeInfo() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("appName")). body(containsString("appVersion")). body(containsString("duration")). @@ -202,7 +209,7 @@ public void shouldTestGetNeighbors() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("neighbors")). body(containsString("address")). body(containsString("numberOfAllTransactions")). @@ -222,7 +229,7 @@ public void shouldTestAddNeighbors() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("addedNeighbors")); } @@ -237,7 +244,7 @@ public void shouldTestRemoveNeighbors() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("removedNeighbors")); } @@ -252,7 +259,7 @@ public void shouldTestGetTips() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("hashes")); } @@ -267,7 +274,7 @@ public void shouldTestFindTransactions() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("hashes")); } @@ -282,7 +289,7 @@ public void shouldTestGetTrytes() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("trytes")); } @@ -299,7 +306,7 @@ public void shouldTestGetInclusionStates() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("states")); } @@ -317,7 +324,7 @@ public void shouldTestGetBalances() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("milestone")); } @@ -334,7 +341,7 @@ public void shouldTestGetTransactionsToApprove() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("trunkTransaction")). body(containsString("branchTransaction")); } @@ -351,7 +358,7 @@ public void shouldTestBroadcastTransactions() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). log().all().and(); } @@ -367,7 +374,7 @@ public void shouldTestStoreTransactions() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). log().all().and(); } @@ -386,7 +393,7 @@ public void shouldTestattachToTangle() { when(). post("/"). then(). - spec(responseSpec). + spec(specResponseSuccess). body(containsString("trytes")); } @@ -420,7 +427,7 @@ private List sendTransfer(String[] trytesArray, String branch, String tr when(). post("/"). then(). - log().all().and().spec(responseSpec); + log().all().and().spec(specResponseSuccess); return trytes; } From 59df6fdf875ca3bed7e118ce228aad54e0b9519c Mon Sep 17 00:00:00 2001 From: Christian Lehmann Date: Thu, 29 Nov 2018 14:10:31 +0100 Subject: [PATCH 03/34] Added specification response for status code 400. Statuscode 500 should only be returned on server internal errors. This request is a bad request from the client and 400 will be returned. --- src/main/java/com/iota/iri/service/API.java | 4 +- .../iota/iri/service/APIIntegrationTests.java | 40 +++++++------- .../iri/utils/MapIdentityManagerTest.java | 52 +++++++++++++++++++ 3 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 src/test/java/com/iota/iri/utils/MapIdentityManagerTest.java diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index 8320fd5e36..4ab7f3306d 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -374,10 +374,10 @@ private AbstractResponse process(final String requestString, InetSocketAddress s request = gson.fromJson(requestString, Map.class); } catch(JsonSyntaxException jsonSyntaxException) { - return ExceptionResponse.create("Invalid JSON syntax: " + jsonSyntaxException.getMessage()); + return ErrorResponse.create("Invalid JSON syntax: " + jsonSyntaxException.getMessage()); } if (request == null) { - return ExceptionResponse.create("Invalid request payload: '" + requestString + "'"); + return ErrorResponse.create("Invalid request payload: '" + requestString + "'"); } // Did the requester ask for a command? diff --git a/src/test/java/com/iota/iri/service/APIIntegrationTests.java b/src/test/java/com/iota/iri/service/APIIntegrationTests.java index 2ec40d1314..b526b038b0 100644 --- a/src/test/java/com/iota/iri/service/APIIntegrationTests.java +++ b/src/test/java/com/iota/iri/service/APIIntegrationTests.java @@ -46,8 +46,8 @@ public class APIIntegrationTests { // Expect to connect to any service worldwide in under 100 ms // and to any online machine local in 1 ms. The 50 ms default value is a suggested compromise. private static final int CONNECTION_TIMEOUT = 50; - private static ResponseSpecification specResponseSuccess; - private static ResponseSpecification specResponseError; + private static ResponseSpecification specSuccessResponse; + private static ResponseSpecification specErrorResponse; // Constants used in tests private static final String[] URIS = {"udp://8.8.8.8:14266", "udp://8.8.8.5:14266"}; private static final String[] ADDRESSES = {"RVORZ9SIIP9RCYMREUIXXVPQIPHVCNPQ9HZWYKFWYWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVA"}; @@ -117,14 +117,14 @@ public static void tearDown() { RestAssured.baseURI = hostName; // Define response specification for http status code 200 - specResponseSuccess = new ResponseSpecBuilder(). + specSuccessResponse = new ResponseSpecBuilder(). expectStatusCode(200). expectBody(containsString("duration")). build(); // Define response specification for http status code 500 - specResponseError = new ResponseSpecBuilder(). - expectStatusCode(500). + specErrorResponse = new ResponseSpecBuilder(). + expectStatusCode(400). expectBody(containsString("duration")). build(); } @@ -161,7 +161,7 @@ public void sendNonJsonBody() { when(). post("/"). then(). - spec(specResponseError). + spec(specErrorResponse). body(containsString("Invalid JSON syntax")); } @@ -176,7 +176,7 @@ public void shouldTestGetNodeInfo() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("appName")). body(containsString("appVersion")). body(containsString("duration")). @@ -209,7 +209,7 @@ public void shouldTestGetNeighbors() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("neighbors")). body(containsString("address")). body(containsString("numberOfAllTransactions")). @@ -229,7 +229,7 @@ public void shouldTestAddNeighbors() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("addedNeighbors")); } @@ -244,7 +244,7 @@ public void shouldTestRemoveNeighbors() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("removedNeighbors")); } @@ -259,7 +259,7 @@ public void shouldTestGetTips() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("hashes")); } @@ -274,7 +274,7 @@ public void shouldTestFindTransactions() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("hashes")); } @@ -289,7 +289,7 @@ public void shouldTestGetTrytes() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("trytes")); } @@ -306,7 +306,7 @@ public void shouldTestGetInclusionStates() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("states")); } @@ -324,7 +324,7 @@ public void shouldTestGetBalances() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("milestone")); } @@ -341,7 +341,7 @@ public void shouldTestGetTransactionsToApprove() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("trunkTransaction")). body(containsString("branchTransaction")); } @@ -358,7 +358,7 @@ public void shouldTestBroadcastTransactions() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). log().all().and(); } @@ -374,7 +374,7 @@ public void shouldTestStoreTransactions() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). log().all().and(); } @@ -393,7 +393,7 @@ public void shouldTestattachToTangle() { when(). post("/"). then(). - spec(specResponseSuccess). + spec(specSuccessResponse). body(containsString("trytes")); } @@ -427,7 +427,7 @@ private List sendTransfer(String[] trytesArray, String branch, String tr when(). post("/"). then(). - log().all().and().spec(specResponseSuccess); + log().all().and().spec(specSuccessResponse); return trytes; } diff --git a/src/test/java/com/iota/iri/utils/MapIdentityManagerTest.java b/src/test/java/com/iota/iri/utils/MapIdentityManagerTest.java new file mode 100644 index 0000000000..8569ff8ba9 --- /dev/null +++ b/src/test/java/com/iota/iri/utils/MapIdentityManagerTest.java @@ -0,0 +1,52 @@ +package com.iota.iri.utils; + +import io.undertow.security.idm.Account; +import io.undertow.security.idm.PasswordCredential; +import io.undertow.security.idm.X509CertificateCredential; +import org.junit.Test; + +import java.util.HashMap; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class MapIdentityManagerTest { + private String testUser = "testUser"; + private char[] testPassword = "testPassword".toCharArray(); + + @Test + public void verifyAccountWithClearTextPassword() { + HashMap users = new HashMap() {{ + put(testUser, testPassword); + }}; + MapIdentityManager identityManager = new MapIdentityManager(users); + Account account = identityManager.verify(testUser, new PasswordCredential(testPassword)); + + assertEquals("testUser needs equal to user returned by account", testUser, account.getPrincipal().getName()); + assertThat("Roles must not be implemented", account.getRoles(), is(empty())); + } + + @Test + public void verifyAccountWithTrytes() { + HashMap users = new HashMap() {{ + put(testUser, "E9V9PAVSGWQMBDFUFW9SZKV9SO9ATLTLCDTCKKRXZTSFKSCHBISFHZJPIGLP9DEIWYPHJUINIQWSRETQT".toCharArray()); + }}; + MapIdentityManager identityManager = new MapIdentityManager(users); + Account account = identityManager.verify(testUser, new PasswordCredential(testPassword)); + + assertEquals("testUser needs equal to user returned by account", testUser, account.getPrincipal().getName()); + } + + @Test + public void verifyAccountWithUnsupportedCredentialType() { + HashMap users = new HashMap() {{ + put(testUser, testPassword); + }}; + MapIdentityManager identityManager = new MapIdentityManager(users); + Account account = identityManager.verify(testUser, new X509CertificateCredential(null)); + assertNull("Must be null, because credential type is not supported", account); + } +} From dd84629d9f380c2de4c6235ffce18e0bdd1c45a5 Mon Sep 17 00:00:00 2001 From: Christian Lehmann Date: Thu, 29 Nov 2018 14:14:30 +0100 Subject: [PATCH 04/34] Fixed codacy-bot comment. --- src/main/java/com/iota/iri/service/API.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index 4ab7f3306d..9ba27015c9 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -369,7 +369,7 @@ private AbstractResponse process(final String requestString, InetSocketAddress s try { // Request JSON data into map - final Map request; + Map request; try { request = gson.fromJson(requestString, Map.class); } From 26628c910821dcbaa55e6f253d15b6935857f3f8 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Tue, 15 Jan 2019 08:59:54 +0200 Subject: [PATCH 05/34] Create ACKNOWLEDGMENTS.MD --- ACKNOWLEDGMENTS.MD | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 ACKNOWLEDGMENTS.MD diff --git a/ACKNOWLEDGMENTS.MD b/ACKNOWLEDGMENTS.MD new file mode 100644 index 0000000000..394b73a354 --- /dev/null +++ b/ACKNOWLEDGMENTS.MD @@ -0,0 +1,7 @@ +# SPECIAL THANKS TO + +### ![alt text](https://www.yourkit.com/images/yklogo.png) +YourKit supports open source projects with its full-featured Java Profiler. +YourKit, LLC is the creator of YourKit Java Profiler +and YourKit .NET Profiler, +innovative and intelligent tools for profiling Java and .NET applications. From 58374092a6d40f74c9a8ab57d9c41afd1179ab32 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Tue, 15 Jan 2019 09:18:41 +0200 Subject: [PATCH 06/34] Delete ACKNOWLEDGMENTS.MD --- ACKNOWLEDGMENTS.MD | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 ACKNOWLEDGMENTS.MD diff --git a/ACKNOWLEDGMENTS.MD b/ACKNOWLEDGMENTS.MD deleted file mode 100644 index 394b73a354..0000000000 --- a/ACKNOWLEDGMENTS.MD +++ /dev/null @@ -1,7 +0,0 @@ -# SPECIAL THANKS TO - -### ![alt text](https://www.yourkit.com/images/yklogo.png) -YourKit supports open source projects with its full-featured Java Profiler. -YourKit, LLC is the creator of YourKit Java Profiler -and YourKit .NET Profiler, -innovative and intelligent tools for profiling Java and .NET applications. From 5375f8229b9c696c1ec14901be9101d319ab6a14 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Tue, 15 Jan 2019 09:22:14 +0200 Subject: [PATCH 07/34] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index fb16e0b625..66057653b9 100644 --- a/README.md +++ b/README.md @@ -116,3 +116,12 @@ IXI_DIR = ixi DEBUG = false DB_PATH = db ``` + +### Special Thanks To + +### ![alt text](https://www.yourkit.com/images/yklogo.png) + +YourKit supports open source projects with its full-featured Java Profiler. +YourKit, LLC is the creator of YourKit Java Profiler +and YourKit .NET Profiler, +innovative and intelligent tools for profiling Java and .NET applications. From 726c5607fab4e2b841c64c55c52b3d354b890c0e Mon Sep 17 00:00:00 2001 From: Georg Mittendorfer Date: Tue, 15 Jan 2019 12:42:51 +0100 Subject: [PATCH 08/34] Fix: dns reverse resolving issue for neighbors that are added with their IP. (#1255) --- src/main/java/com/iota/iri/network/Node.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iota/iri/network/Node.java b/src/main/java/com/iota/iri/network/Node.java index 4a73ccdb8a..cc34b84619 100644 --- a/src/main/java/com/iota/iri/network/Node.java +++ b/src/main/java/com/iota/iri/network/Node.java @@ -186,7 +186,7 @@ private Runnable spawnNeighborDNSRefresherThread() { try { neighbors.forEach(n -> { - final String hostname = n.getAddress().getHostName(); + final String hostname = n.getAddress().getHostString(); checkIp(hostname).ifPresent(ip -> { log.info("DNS Checker: Validating DNS Address '{}' with '{}'", hostname, ip); messageQ.publish("dnscv %s %s", hostname, ip); From 390d8468998c6141860609a0db005fa4f5ab649e Mon Sep 17 00:00:00 2001 From: Mathieu Viossat Date: Wed, 16 Jan 2019 00:20:41 +0100 Subject: [PATCH 09/34] Feature: Add configurable variables for spent addresses DB (#1274) --- .../com/iota/iri/conf/BaseIotaConfig.java | 26 +++++++++++++++++++ .../com/iota/iri/conf/SnapshotConfig.java | 12 +++++++++ .../snapshot/impl/SnapshotProviderImpl.java | 6 ++--- .../SpentAddressesProvider.java | 10 ------- .../impl/SpentAddressesProviderImpl.java | 16 +++++------- 5 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java index 0661110cf3..edb1dd038b 100644 --- a/src/main/java/com/iota/iri/conf/BaseIotaConfig.java +++ b/src/main/java/com/iota/iri/conf/BaseIotaConfig.java @@ -95,6 +95,8 @@ public abstract class BaseIotaConfig implements IotaConfig { protected int localSnapshotsIntervalUnsynced = Defaults.LOCAL_SNAPSHOTS_INTERVAL_UNSYNCED; protected int localSnapshotsDepth = Defaults.LOCAL_SNAPSHOTS_DEPTH; protected String localSnapshotsBasePath = Defaults.LOCAL_SNAPSHOTS_BASE_PATH; + protected String spentAddressesDbPath = Defaults.SPENT_ADDRESSES_DB_PATH; + protected String spentAddressesDbLogPath = Defaults.SPENT_ADDRESSES_DB_LOG_PATH; public BaseIotaConfig() { //empty constructor @@ -592,6 +594,28 @@ public int getNumberOfKeysInMilestone() { return Defaults.NUM_KEYS_IN_MILESTONE; } + @Override + public String getSpentAddressesDbPath() { + return spentAddressesDbPath; + } + + @JsonProperty + @Parameter(names = {"--spent-addresses-db-path"}, description = SnapshotConfig.Descriptions.SPENT_ADDRESSES_DB_PATH) + protected void setSpentAddressesDbPath(String spentAddressesDbPath) { + this.spentAddressesDbPath = spentAddressesDbPath; + } + + @Override + public String getSpentAddressesDbLogPath() { + return spentAddressesDbLogPath; + } + + @JsonProperty + @Parameter(names = {"--spent-addresses-db-log-path"}, description = SnapshotConfig.Descriptions.SPENT_ADDRESSES_DB_LOG_PATH) + protected void setSpentAddressesDbLogPath(String spentAddressesDbLogPath) { + this.spentAddressesDbLogPath = spentAddressesDbLogPath; + } + @Override public boolean isZmqEnabled() { return zmqEnabled; @@ -811,6 +835,8 @@ public interface Defaults { int LOCAL_SNAPSHOTS_INTERVAL_UNSYNCED = 1000; int LOCAL_SNAPSHOTS_DEPTH = 100; int LOCAL_SNAPSHOTS_DEPTH_MIN = 100; + String SPENT_ADDRESSES_DB_PATH = "spent-addresses-db"; + String SPENT_ADDRESSES_DB_LOG_PATH = "spent-addresses-log"; String LOCAL_SNAPSHOTS_BASE_PATH = "mainnet"; String SNAPSHOT_FILE = "/snapshotMainnet.txt"; diff --git a/src/main/java/com/iota/iri/conf/SnapshotConfig.java b/src/main/java/com/iota/iri/conf/SnapshotConfig.java index 579fde5ab9..fd3d90664d 100644 --- a/src/main/java/com/iota/iri/conf/SnapshotConfig.java +++ b/src/main/java/com/iota/iri/conf/SnapshotConfig.java @@ -70,6 +70,16 @@ public interface SnapshotConfig extends Config { */ String getPreviousEpochSpentAddressesFiles(); + /** + * @return {@value Descriptions#SPENT_ADDRESSES_DB_PATH} + */ + String getSpentAddressesDbPath(); + + /** + * @return {@value Descriptions#SPENT_ADDRESSES_DB_LOG_PATH} + */ + String getSpentAddressesDbLogPath(); + interface Descriptions { String LOCAL_SNAPSHOTS_ENABLED = "Flag that determines if local snapshots are enabled."; @@ -88,5 +98,7 @@ interface Descriptions { "leaves (private keys) that the coordinator can use to sign a message."; String PREVIOUS_EPOCH_SPENT_ADDRESSES_FILE = "The file that contains the list of all used addresses " + "from previous epochs"; + String SPENT_ADDRESSES_DB_PATH = "The folder where the spent addresses DB saves its data."; + String SPENT_ADDRESSES_DB_LOG_PATH = "The folder where the spent addresses DB saves its logs."; } } diff --git a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java index 63f1a8b7e4..993bdc4542 100644 --- a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java +++ b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java @@ -232,17 +232,17 @@ private Snapshot loadLocalSnapshot() throws SnapshotException, SpentAddressesExc private void assertSpentAddressesDbExist() throws SpentAddressesException { try { - File spentAddressFolder = new File(SpentAddressesProvider.SPENT_ADDRESSES_DB); + File spentAddressFolder = new File(SnapshotConfig.Descriptions.SPENT_ADDRESSES_DB_LOG_PATH); //If there is at least one file in the db the check should pass if (Files.newDirectoryStream(spentAddressFolder.toPath(), "*.sst").iterator().hasNext()) { return; } } catch (IOException e){ - throw new SpentAddressesException("Can't load " + SpentAddressesProvider.SPENT_ADDRESSES_DB + " folder", e); + throw new SpentAddressesException("Can't load " + SnapshotConfig.Descriptions.SPENT_ADDRESSES_DB_LOG_PATH + " folder", e); } - throw new SpentAddressesException(SpentAddressesProvider.SPENT_ADDRESSES_DB + " folder has no sst files"); + throw new SpentAddressesException(SnapshotConfig.Descriptions.SPENT_ADDRESSES_DB_LOG_PATH + " folder has no sst files"); } /** diff --git a/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesProvider.java b/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesProvider.java index f1cf8a4cb4..e816731bda 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesProvider.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesProvider.java @@ -8,16 +8,6 @@ * Find, mark and store spent addresses */ public interface SpentAddressesProvider { - /** - * Folder where we store spent address data - */ - String SPENT_ADDRESSES_DB = "spent-addresses-db"; - - /** - * Folder where we store spent address logs - */ - String SPENT_ADDRESSES_LOG = "spent-addresses-log"; - /** * Checks if this address hash has been spent from * diff --git a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImpl.java b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImpl.java index 1ae53506fc..a8a2f1aa63 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImpl.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImpl.java @@ -35,16 +35,6 @@ public class SpentAddressesProviderImpl implements SpentAddressesProvider { private SnapshotConfig config; - /** - * Creates a new instance of SpentAddressesProvider - */ - public SpentAddressesProviderImpl() { - this.rocksDBPersistenceProvider = new RocksDBPersistenceProvider(SPENT_ADDRESSES_DB, - SPENT_ADDRESSES_LOG, 1000, - new HashMap>(1) - {{put("spent-addresses", SpentAddress.class);}}, null); - } - /** * Starts the SpentAddressesProvider by reading the previous spent addresses from files. * @@ -56,6 +46,12 @@ public SpentAddressesProviderImpl init(SnapshotConfig config) throws SpentAddressesException { this.config = config; try { + this.rocksDBPersistenceProvider = new RocksDBPersistenceProvider( + config.getSpentAddressesDbPath(), + config.getSpentAddressesDbLogPath(), + 1000, + new HashMap>(1) + {{put("spent-addresses", SpentAddress.class);}}, null); this.rocksDBPersistenceProvider.init(); readPreviousEpochsSpentAddresses(); } From f89be8809fe5a4b565fc97112825e91d264763bc Mon Sep 17 00:00:00 2001 From: Alon Elmaliah Date: Wed, 16 Jan 2019 01:36:24 +0200 Subject: [PATCH 10/34] Change: add a plugin to create reproducible builds. (#1194) * add a plugin to create reproducable builds. * print the SHA256 digest of jars to travis logs. * print the SHA256 digest of jars to travis logs (only for oracle java). --- .travis.yml | 2 ++ pom.xml | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.travis.yml b/.travis.yml index 30904c1763..057c21907e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,8 @@ script: - mvn jacoco:report after_success: + #digest of packaged jar + - test $TRAVIS_JDK_VERSION = "oraclejdk8" && sha256sum target/*.jar* #codacy-coverage send report. Uses Travis Env variable (CODACY_PROJECT_TOKEN) - test $TRAVIS_PULL_REQUEST = "false" && test $TRAVIS_JDK_VERSION = "oraclejdk8" && wget -O codacy-coverage-reporter-assembly-latest.jar $(curl https://api.github.com/repos/codacy/codacy-coverage-reporter/releases/latest | jq -r '.assets[0].browser_download_url') - test $TRAVIS_PULL_REQUEST = "false" && test $TRAVIS_JDK_VERSION = "oraclejdk8" && java -jar codacy-coverage-reporter-assembly-latest.jar report -l Java -r target/site/jacoco/jacoco.xml diff --git a/pom.xml b/pom.xml index 955c127ad0..d16584e193 100644 --- a/pom.xml +++ b/pom.xml @@ -450,6 +450,19 @@ + + io.github.zlika + reproducible-build-maven-plugin + 0.7 + + + package + + strip-jar + + + + From c1881758feb5357a852e0dbb5968a794a02b4d73 Mon Sep 17 00:00:00 2001 From: Alon Elmaliah Date: Wed, 16 Jan 2019 12:33:09 +0200 Subject: [PATCH 11/34] Fix: make storeMessage store transactions (#1186) --- src/main/java/com/iota/iri/service/API.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index c1f89a271f..e9852c6c84 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -1667,8 +1667,9 @@ public void shutDown() { } /** + * * Only available on testnet. - * Creates, attaches, and broadcasts a transaction with this message + * Creates, attaches, stores, and broadcasts a transaction with this message * * @param address The address to add the message to * @param message The message to store @@ -1739,6 +1740,7 @@ private synchronized AbstractResponse storeMessageStatement(String address, Stri // do pow List powResult = attachToTangleStatement(txToApprove.get(0), txToApprove.get(1), 9, transactions); + storeTransactionsStatement(powResult); broadcastTransactionsStatement(powResult); return AbstractResponse.createEmptyResponse(); } From b4e6ee4b6309d91636cd2c7f437a0487844d422f Mon Sep 17 00:00:00 2001 From: legacycode Date: Thu, 17 Jan 2019 20:18:20 +0100 Subject: [PATCH 12/34] Fix: Creates rest endpoint for iotaconfig related settings as suggested (#1200) --- src/main/java/com/iota/iri/service/API.java | 79 ++++++++++--------- .../dto/GetNodeAPIConfigurationResponse.java | 76 ++++++++++++++++++ .../iota/iri/service/APIIntegrationTests.java | 29 ++++++- 3 files changed, 145 insertions(+), 39 deletions(-) create mode 100644 src/main/java/com/iota/iri/service/dto/GetNodeAPIConfigurationResponse.java diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index e9852c6c84..046bbf532f 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -1,13 +1,14 @@ package com.iota.iri.service; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; -import com.iota.iri.*; +import com.iota.iri.BundleValidator; +import com.iota.iri.IRI; +import com.iota.iri.IXI; +import com.iota.iri.Iota; import com.iota.iri.conf.APIConfig; -import com.iota.iri.controllers.AddressViewModel; -import com.iota.iri.controllers.BundleViewModel; -import com.iota.iri.controllers.MilestoneViewModel; -import com.iota.iri.controllers.TagViewModel; -import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.controllers.*; import com.iota.iri.crypto.Curl; import com.iota.iri.crypto.PearlDiver; import com.iota.iri.crypto.Sponge; @@ -19,21 +20,29 @@ import com.iota.iri.service.dto.*; import com.iota.iri.service.tipselection.TipSelector; import com.iota.iri.service.tipselection.impl.WalkValidatorImpl; -import com.iota.iri.service.transactionpruning.async.MilestonePrunerJobQueue; import com.iota.iri.utils.Converter; import com.iota.iri.utils.IotaIOUtils; import com.iota.iri.utils.MapIdentityManager; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - +import io.undertow.Undertow; +import io.undertow.security.api.AuthenticationMechanism; +import io.undertow.security.api.AuthenticationMode; +import io.undertow.security.handlers.AuthenticationCallHandler; +import io.undertow.security.handlers.AuthenticationConstraintHandler; +import io.undertow.security.handlers.AuthenticationMechanismsHandler; +import io.undertow.security.handlers.SecurityInitialHandler; +import io.undertow.security.idm.IdentityManager; +import io.undertow.security.impl.BasicAuthenticationMechanism; +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.util.*; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnio.channels.StreamSinkChannel; import org.xnio.streams.ChannelInputStream; -import java.io.*; +import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; @@ -47,19 +56,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import io.undertow.Undertow; -import io.undertow.security.api.AuthenticationMechanism; -import io.undertow.security.api.AuthenticationMode; -import io.undertow.security.handlers.AuthenticationCallHandler; -import io.undertow.security.handlers.AuthenticationConstraintHandler; -import io.undertow.security.handlers.AuthenticationMechanismsHandler; -import io.undertow.security.handlers.SecurityInitialHandler; -import io.undertow.security.idm.IdentityManager; -import io.undertow.security.impl.BasicAuthenticationMechanism; -import io.undertow.server.HttpHandler; -import io.undertow.server.HttpServerExchange; -import io.undertow.util.*; - import static io.undertow.Handlers.path; /** @@ -103,6 +99,11 @@ public class API { private final static long MAX_TIMESTAMP_VALUE = (long) (Math.pow(3, 27) - 1) / 2; // max positive 27-trits value + private static int counterGetTxToApprove = 0; + private static long ellapsedTime_getTxToApprove = 0L; + private static int counter_PoW = 0; + private static long ellapsedTime_PoW = 0L; + private final int maxFindTxs; private final int maxRequestList; private final int maxGetTrytes; @@ -278,7 +279,7 @@ private void processRequest(final HttpServerExchange exchange) throws IOExceptio final long beginningTime = System.currentTimeMillis(); final String body = IotaIOUtils.toString(cis, StandardCharsets.UTF_8); - final AbstractResponse response; + AbstractResponse response; if (!exchange.getRequestHeaders().contains("X-IOTA-API-Version")) { response = ErrorResponse.create("Invalid API Version"); @@ -414,6 +415,9 @@ private AbstractResponse process(final String requestString, InetSocketAddress s case "getNodeInfo": { return getNodeInfoStatement(); } + case "getNodeAPIConfiguration": { + return getNodeAPIConfigurationStatement(); + } case "getTips": { return getTipsStatement(); } @@ -709,9 +713,6 @@ private synchronized AbstractResponse getTrytesStatement(List hashes) th return GetTrytesResponse.create(elements); } - - private static int counterGetTxToApprove = 0; - /** * Can be 0 or more, and is set to 0 every 100 requests. * Each increase indicates another 2 tips send. @@ -729,8 +730,6 @@ private static void incCounterGetTxToApprove() { counterGetTxToApprove++; } - private static long ellapsedTime_getTxToApprove = 0L; - /** * Can be 0 or more, and is set to 0 every 100 requests. * @@ -911,6 +910,15 @@ private AbstractResponse getNodeInfoStatement() throws Exception{ instance.configuration.getCoordinator()); } + /** + * Returns information about this node configuration. + * + * @return {@link GetNodeAPIConfigurationResponse} + */ + private AbstractResponse getNodeAPIConfigurationStatement() { + return GetNodeAPIConfigurationResponse.create(instance.configuration); + } + /** *

* Get the inclusion states of a set of transactions. @@ -1301,12 +1309,12 @@ private AbstractResponse getBalancesStatement(List addresses, .map(address -> (HashFactory.ADDRESS.create(address))) .collect(Collectors.toCollection(LinkedList::new)); - final List hashes; + List hashes; final Map balances = new HashMap<>(); instance.snapshotProvider.getLatestSnapshot().lockRead(); final int index = instance.snapshotProvider.getLatestSnapshot().getIndex(); - if (tips == null || tips.size() == 0) { + if (tips == null || tips.isEmpty()) { hashes = Collections.singletonList(instance.snapshotProvider.getLatestSnapshot().getHash()); } else { hashes = tips.stream() @@ -1353,8 +1361,6 @@ private AbstractResponse getBalancesStatement(List addresses, .collect(Collectors.toList()), index); } - private static int counter_PoW = 0; - /** * Can be 0 or more, and is set to 0 every 100 requests. * Each increase indicates another 2 tips sent. @@ -1373,8 +1379,6 @@ public static void incCounterPoW() { API.counter_PoW++; } - private static long ellapsedTime_PoW = 0L; - /** * Can be 0 or more, and is set to 0 every 100 requests. * @@ -1602,7 +1606,6 @@ private List getParameterAsList(Map request, String para * @param trytes The String we validate. * @param length The amount of trytes it should contain. * @param zeroAllowed If set to '{@value #ZERO_LENGTH_ALLOWED}', an empty string is also valid. - * @throws ValidationException If the string is not exactly trytes of size length * @return true if the string is valid, otherwise false */ private boolean validTrytes(String trytes, int length, char zeroAllowed) { diff --git a/src/main/java/com/iota/iri/service/dto/GetNodeAPIConfigurationResponse.java b/src/main/java/com/iota/iri/service/dto/GetNodeAPIConfigurationResponse.java new file mode 100644 index 0000000000..4c44ee1f8f --- /dev/null +++ b/src/main/java/com/iota/iri/service/dto/GetNodeAPIConfigurationResponse.java @@ -0,0 +1,76 @@ +package com.iota.iri.service.dto; + +import com.iota.iri.conf.IotaConfig; + +/** + * Contains information about the result of a successful {@link com.iota.iri.service.API#getNodeAPIConfigurationStatement()} API call. + * See {@link com.iota.iri.service.API#getNodeAPIConfigurationStatement()} for how this response is created. + */ +public class GetNodeAPIConfigurationResponse extends AbstractResponse { + private int maxFindTransactions; + private int maxRequestsList; + private int maxGetTrytes; + private int maxBodyLength; + private boolean testNet; + private int milestoneStartIndex; + + /** + * Use factory method {@link GetNodeAPIConfigurationResponse#create(IotaConfig) to create response.} + */ + private GetNodeAPIConfigurationResponse() { + } + + /** + * Creates a new {@link GetNodeAPIConfigurationResponse} with configuration options that should be returned. + * Make sure that you do not return secret informations (e.g. passwords, secrets...). + * + * @param configuration {@link IotaConfig} used to create response. + * @return an {@link GetNodeAPIConfigurationResponse} filled with actual config options. + */ + public static AbstractResponse create(IotaConfig configuration) { + if(configuration == null) { + throw new IllegalStateException("configuration must not be null!"); + } + + final GetNodeAPIConfigurationResponse res = new GetNodeAPIConfigurationResponse(); + + res.maxFindTransactions = configuration.getMaxFindTransactions(); + res.maxRequestsList = configuration.getMaxRequestsList(); + res.maxGetTrytes = configuration.getMaxGetTrytes(); + res.maxBodyLength = configuration.getMaxBodyLength(); + res.testNet = configuration.isTestnet(); + res.milestoneStartIndex = configuration.getMilestoneStartIndex(); + + return res; + } + + /** {@link IotaConfig#getMaxFindTransactions()} */ + public int getMaxFindTransactions() { + return maxFindTransactions; + } + + /** {@link IotaConfig#getMaxRequestsList()} */ + public int getMaxRequestsList() { + return maxRequestsList; + } + + /** {@link IotaConfig#getMaxGetTrytes()} */ + public int getMaxGetTrytes() { + return maxGetTrytes; + } + + /** {@link IotaConfig#getMaxBodyLength()} */ + public int getMaxBodyLength() { + return maxBodyLength; + } + + /** {@link IotaConfig#isTestnet()} */ + public boolean isTestNet() { + return testNet; + } + + /** {@link IotaConfig#getMilestoneStartIndex()} */ + public int getMilestoneStartIndex() { + return milestoneStartIndex; + } +} diff --git a/src/test/java/com/iota/iri/service/APIIntegrationTests.java b/src/test/java/com/iota/iri/service/APIIntegrationTests.java index 83c633b3bb..73152876a8 100644 --- a/src/test/java/com/iota/iri/service/APIIntegrationTests.java +++ b/src/test/java/com/iota/iri/service/APIIntegrationTests.java @@ -34,6 +34,13 @@ import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.fail; +/** + * Windows developer notes: + * For running this tests on windows you need the RocksDB dependencies. You need to install the + * Visual C++ Redistributable for Visual Studio 2015 x64 from + * https://www.microsoft.com/en-us/download/confirmation.aspx?id=48145 + * Make sure your Java JDK is a 64x version and your JAVA_HOME is set correctly. + */ public class APIIntegrationTests { private static final Boolean spawnNode = true; //can be changed to false to use already deployed node @@ -62,7 +69,7 @@ public class APIIntegrationTests { private static API api; private static IXI ixi; private static IotaConfig configuration; - private static Logger log = LoggerFactory.getLogger(APIIntegrationTests.class); + private static final Logger log = LoggerFactory.getLogger(APIIntegrationTests.class); @BeforeClass @@ -200,6 +207,26 @@ public void shouldTestGetNodeInfo() { body(containsString("transactionsToRequest")); } + @Test + public void shouldTestGetIotaConfig() { + + final Map request = new HashMap<>(); + request.put("command", "getNodeAPIConfiguration"); + + given(). + body(gson().toJson(request)). + when(). + post("/"). + then(). + spec(specSuccessResponse). + body(containsString("maxFindTransactions")). + body(containsString("maxRequestsList")). + body(containsString("maxGetTrytes")). + body(containsString("maxBodyLength")). + body(containsString("testNet")). + body(containsString("milestoneStartIndex")); + } + @Test public void shouldTestGetNeighbors() { From 0365b8f75c57db11b4b1b4c798c0b7a5d6f88e0d Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Tue, 15 Jan 2019 09:55:36 +0200 Subject: [PATCH 13/34] update changelog with changes to v1.6.0 --- changelog.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/changelog.txt b/changelog.txt index 07b3efdd31..a16f8e0bfb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,33 @@ +1.6.0 + - Feat: added config variables for local snapshots (#981) + - Feat: added a generic helper class for dag traversal (#982) + - Feat: renamed Milestone to MilestoneTracker (#983) + - Feat: Introducing the GarbageCollector of local snapshots (#995) + - Feat: Fixes + Improvements for IRI that are required for local snapshots (#1095) + - Feat: Introducing several executor services for IRI (#1131) + - Feat: Making IRI use the initialSnapshot and the solidEntryPoints (#1135) + - MilestoneTracker and LedgerValidator rework (#1151) + - Activate Local Snapshots (#1172) + - Introducing a repair mechanism for corruptions in the ledger database (#1174) + - Introducing a parameter validation for the local snapshot config parameters (#1175) + - Introducing a dedicated Transaction Requester Background Worker (#1178) + - Fix: Removed intertwined locks to prevent deadlocks (#1195) + - Feat: Added an option to fast forward the ledger state (#1196) + - Perf: Massively improved the performance of the replayMilestones method (#1197) + - Refactor: Removed grace time from solid entry points generation (#1199) + - Increase rescan performance of old milestones after IRI restart (#1204) + - Refactor: Removed the old unused classes that got replaced by the local snapshots implementation (#1205) + - Refactor: made boolean parameters receive a value (#1224) + - Feat: Disable snapshot logic completely if disabled in the config (#1228) + - Fix: Node requested old transactions forever (#1235) + - Feat: Write snapshot files to temp files first (#1256) + - Fix first() calls in view models (#1257) + - Spentaddresses are persisted upon pruning (#1258) + - Spent Addresses should be persisted in a seperate db instance (#1263) + - Report local snapshot node transaction history via getNodeInfo (#1264) + - Don't start node if local snapshot is loaded but there is no spent ad… (#1266) + - Change the minimum value for LOCAL_SNAPSHOTS_PRUNING_DELAY (#1267) + 1.5.6 - Global Snapshot (#1250) - Refactor: refactored DedicatedScheduledExecutorService From 91d8f7cf5ff5d74849896ecd665f7de94379658e Mon Sep 17 00:00:00 2001 From: legacycode Date: Tue, 29 Jan 2019 11:39:35 +0100 Subject: [PATCH 14/34] Documentation: Fixed and added javadoc comments for existing classes (#1026) * added documentation * added documentation * Fixed mvn javadoc:javadoc errors, warnings and added missing documentation. * implemented review comments * removed unused import --- src/main/java/com/iota/iri/IRI.java | 14 ++++++++ .../java/com/iota/iri/conf/ConfigFactory.java | 23 ++++++++++-- .../com/iota/iri/conf/SnapshotConfig.java | 2 +- src/main/java/com/iota/iri/service/API.java | 2 +- .../iota/iri/service/ValidationException.java | 1 + .../iri/service/dto/GetNeighborsResponse.java | 2 +- .../iota/iri/storage/PersistenceProvider.java | 4 +-- .../collections/interfaces/UnIterableMap.java | 35 ++++++++++++------- 8 files changed, 64 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/iota/iri/IRI.java b/src/main/java/com/iota/iri/IRI.java index c09a7aa94c..49e107bf5d 100644 --- a/src/main/java/com/iota/iri/IRI.java +++ b/src/main/java/com/iota/iri/IRI.java @@ -60,6 +60,12 @@ public static void main(String[] args) throws Exception { IRILauncher.main(args); } + /** + * Reads the logging configuration file and logging level from system properties. You can set this values as + * arguments to the Java VM by passing -Dlogback.configurationFile=/path/to/config.xml -Dlogging-level=DEBUG + * to the Java VM. If no system properties are specified the logback default values and logging-level INFO will + * be used. + */ private static void configureLogging() { String config = System.getProperty("logback.configurationFile"); String level = System.getProperty("logging-level", "").toUpperCase(); @@ -178,6 +184,14 @@ private static IotaConfig createConfiguration(String[] args) { return iotaConfig; } + /** + * Parses the command line arguments for a config file that can be provided by parameter -c + * or parameter --config. If no filename was provided we fall back to iota.ini file. + * If no iota.ini file can be found return null. + * + * @param args command line arguments passed to main method. + * @return File the chosen file to use as config, or null. + */ private static File chooseConfigFile(String[] args) { int index = Math.max(ArrayUtils.indexOf(args, "-c"), ArrayUtils.indexOf(args, "--config")); if (index != -1) { diff --git a/src/main/java/com/iota/iri/conf/ConfigFactory.java b/src/main/java/com/iota/iri/conf/ConfigFactory.java index ecde4098d2..354a3eb8bd 100644 --- a/src/main/java/com/iota/iri/conf/ConfigFactory.java +++ b/src/main/java/com/iota/iri/conf/ConfigFactory.java @@ -13,8 +13,17 @@ import java.io.IOException; import java.util.Properties; +/** + * Creates the global {@link IotaConfig} object with iri specific settings. + */ public class ConfigFactory { + /** + * Creates the {@link IotaConfig} object for {@link TestnetConfig} or {@link MainnetConfig}. + * + * @param isTestnet true if {@link TestnetConfig} should be created. + * @return return the {@link IotaConfig} configuration. + */ public static IotaConfig createIotaConfig(boolean isTestnet) { IotaConfig iotaConfig; if (isTestnet) { @@ -26,8 +35,18 @@ public static IotaConfig createIotaConfig(boolean isTestnet) { return iotaConfig; } - public static IotaConfig createFromFile(File configFile, boolean testnet) throws IOException, - IllegalArgumentException { + /** + * Creates the {@link IotaConfig} object for {@link TestnetConfig} or {@link MainnetConfig} from config file. Parse + * the config file for TESTNET=true. If TESTNET=true is found we creates the + * {@link TestnetConfig} object, else creates the {@link MainnetConfig}. + * + * @param configFile A property file with configuration options. + * @param testnet When true a {@link TestnetConfig} is created. + * @return the {@link IotaConfig} configuration. + * + * @throws IOException When config file could not be found. + */ + public static IotaConfig createFromFile(File configFile, boolean testnet) throws IOException { IotaConfig iotaConfig; try (FileInputStream confStream = new FileInputStream(configFile)) { diff --git a/src/main/java/com/iota/iri/conf/SnapshotConfig.java b/src/main/java/com/iota/iri/conf/SnapshotConfig.java index fd3d90664d..5a1bb36679 100644 --- a/src/main/java/com/iota/iri/conf/SnapshotConfig.java +++ b/src/main/java/com/iota/iri/conf/SnapshotConfig.java @@ -41,7 +41,7 @@ public interface SnapshotConfig extends Config { long getSnapshotTime(); /** - * return {@value Descriptions#SNAPSHOT_FILE} + * @return {@value Descriptions#SNAPSHOT_FILE} */ String getSnapshotFile(); diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index 046bbf532f..d1deda9244 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -839,7 +839,7 @@ private synchronized AbstractResponse getTipsStatement() throws Exception { * These trytes are returned by attachToTangle, or by doing proof of work somewhere else. * * @param trytes Transaction data to be stored. - * @throws Exception When storing or updating a transaction fails + * @throws Exception When storing or updating a transaction fails. **/ public void storeTransactionsStatement(List trytes) throws Exception { final List elements = new LinkedList<>(); diff --git a/src/main/java/com/iota/iri/service/ValidationException.java b/src/main/java/com/iota/iri/service/ValidationException.java index 4cf96c29b4..a71ef6d4f1 100644 --- a/src/main/java/com/iota/iri/service/ValidationException.java +++ b/src/main/java/com/iota/iri/service/ValidationException.java @@ -11,6 +11,7 @@ public ValidationException() { /** * Initializes a new instance of the ValidationException with the specified detail message. + * @param msg message shown in exception details. */ public ValidationException(String msg) { super(msg); diff --git a/src/main/java/com/iota/iri/service/dto/GetNeighborsResponse.java b/src/main/java/com/iota/iri/service/dto/GetNeighborsResponse.java index e98694f45b..3643b0dc8a 100644 --- a/src/main/java/com/iota/iri/service/dto/GetNeighborsResponse.java +++ b/src/main/java/com/iota/iri/service/dto/GetNeighborsResponse.java @@ -30,7 +30,7 @@ public class GetNeighborsResponse extends AbstractResponse { private Neighbor[] neighbors; /** - * + * @see com.iota.iri.service.dto.GetNeighborsResponse.Neighbor * @return {@link #neighbors} */ public Neighbor[] getNeighbors() { diff --git a/src/main/java/com/iota/iri/storage/PersistenceProvider.java b/src/main/java/com/iota/iri/storage/PersistenceProvider.java index 11e24a3024..9cb73dfb73 100644 --- a/src/main/java/com/iota/iri/storage/PersistenceProvider.java +++ b/src/main/java/com/iota/iri/storage/PersistenceProvider.java @@ -44,8 +44,8 @@ public interface PersistenceProvider { /** * Atomically delete all {@code models}. - * @param models key value pairs that to be expunged from the db - * @throws Exception + * @param models key value pairs that to be expunged from the db. + * @throws Exception if data could not be expunged from the db. */ void deleteBatch(Collection>> models) throws Exception; diff --git a/src/main/java/com/iota/iri/utils/collections/interfaces/UnIterableMap.java b/src/main/java/com/iota/iri/utils/collections/interfaces/UnIterableMap.java index 26eb6d5131..30b88bc64b 100644 --- a/src/main/java/com/iota/iri/utils/collections/interfaces/UnIterableMap.java +++ b/src/main/java/com/iota/iri/utils/collections/interfaces/UnIterableMap.java @@ -3,7 +3,6 @@ import java.util.Collection; import java.util.Map; - /** * Similar to {@link Map} but hides key retrieval functionality. * Thus one can't iterate over key or entries. @@ -14,50 +13,62 @@ */ public interface UnIterableMap { - /** - * {See {@link Map#size()}} + * @see Map#size() + * @return {@link Map#size()} */ int size(); /** - * {See {@link Map#isEmpty()}} + * @see Map#isEmpty() + * @return {@link Map#isEmpty()} */ boolean isEmpty(); /** - * {See {@link Map#containsKey(Object)}} + * @see Map#containsKey(Object) + * @param key {@link Map#containsKey(Object)} + * @return {@link Map#containsKey(Object)} */ boolean containsKey(K key); /** - * {See {@link Map#containsValue(Object)}} + * @see Map#containsValue(Object) + * @param value {@link Map#containsValue(Object)} + * @return {@link Map#containsValue(Object)} */ boolean containsValue(V value); /** - * - * {See {@link Map#get}} + * @see Map#get(Object) + * @param key {@link Map#get(Object)} + * @return {@link Map#get(Object)} */ V get(K key); /** - * {See {@link Map#put} + * @see Map#put(Object, Object) + * @param key {@link Map#put(Object, Object)} + * @param value {@link Map#put(Object, Object)} + * @return {@link Map#put(Object, Object)} */ V put(K key, V value); /** - * {See {@link Map#keySet()}} + * @see Map#remove(Object) + * @param key {@link Map#remove(Object)} + * @return {@link Map#remove(Object)} */ V remove(K key); /** - * {See {@link Map#clear()}} + * @see Map#clear() */ void clear(); /** - * {See {@link Map#values} + * @see Map#values() + * @return {@link Map#values()} */ Collection values(); } From e8a2dd0d5d69efb64d39a19f8b3b713e825e993e Mon Sep 17 00:00:00 2001 From: lhf <45928776+lhfbc@users.noreply.github.com> Date: Tue, 29 Jan 2019 18:42:17 +0800 Subject: [PATCH 15/34] Change: Convert from trytes to asciiString (#1302) --- .../java/com/iota/iri/utils/Converter.java | 56 +++++++++++++++---- .../com/iota/iri/utils/ConverterTest.java | 10 ++++ 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/iota/iri/utils/Converter.java b/src/main/java/com/iota/iri/utils/Converter.java index b544085296..7befc8f763 100644 --- a/src/main/java/com/iota/iri/utils/Converter.java +++ b/src/main/java/com/iota/iri/utils/Converter.java @@ -114,16 +114,6 @@ public static void bytes(final byte[] trits, byte[] dest) { bytes(trits, 0, dest, 0, trits.length); } - /** - * Allocates trits (a byte array) large enough to fit trytes, used for converting trytes to trits. - * - * @param tryteCount length of tryte array (to be converted) - * @return a new byte array large enough to fit trytes - */ - public static byte[] allocateTritsForTrytes(int tryteCount) { - return new byte[tryteCount * NUMBER_OF_TRITS_IN_A_TRYTE]; - } - /** * Converts bytes array to trits array based on {@link #NUMBER_OF_TRITS_IN_A_BYTE}.
* the inverse of {@link #bytes(byte[], int, byte[], int, int)}.
@@ -147,6 +137,17 @@ public static void getTrits(final byte[] bytes, final byte[] trits) { // Trytes <-> Trits + + /** + * Allocates trits (a byte array) large enough to fit trytes, used for converting trytes to trits. + * + * @param tryteCount length of tryte array (to be converted) + * @return a new byte array large enough to fit trytes + */ + public static byte[] allocateTritsForTrytes(int tryteCount) { + return new byte[tryteCount * NUMBER_OF_TRITS_IN_A_TRYTE]; + } + /** * Converts a tryte string to trits (bytes array) based on {@link #NUMBER_OF_TRITS_IN_A_TRYTE}.
* this method will override the content of {@code dest} @@ -289,6 +290,41 @@ public static String asciiToTrytes(String input) { return sb.toString(); } + /** + * Converts a {@code TryteString} into an {@code ASCII string} representation. + *

+ * every 2 Trytes are converted to a ASCII character. + *

+ * + * @param trytes Trytes string + * @return ASCII string. + */ + public static String trytesToAscii(String input) { + String trytes; + if (input.length() % 2 != 0) { + trytes = input + '9'; + } else { + trytes = input; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < trytes.length() - 1; i += 2) { + int firstValue = TRYTE_ALPHABET.indexOf(trytes.charAt(i)); + int secondValue = TRYTE_ALPHABET.indexOf(trytes.charAt(i + 1)); + + if (firstValue == -1 || secondValue == -1) { + throw new IllegalArgumentException("Input contains illegal character."); + } + + int asciiValue = secondValue * 27 + firstValue; + if (asciiValue > 255) { + throw new IllegalArgumentException("Calculated result exceed the range of ASCII."); + } + sb.append((char)asciiValue); + } + return sb.toString(); + } + public static Pair longPair(byte[] trits) { long[] low = new long[trits.length]; long[] hi = new long[trits.length]; diff --git a/src/test/java/com/iota/iri/utils/ConverterTest.java b/src/test/java/com/iota/iri/utils/ConverterTest.java index d66ae333c4..405f7f56db 100644 --- a/src/test/java/com/iota/iri/utils/ConverterTest.java +++ b/src/test/java/com/iota/iri/utils/ConverterTest.java @@ -1,6 +1,16 @@ package com.iota.iri.utils; +import org.junit.Assert; +import org.junit.Test; + public class ConverterTest { + @Test + public void testTrytesToAscii() { + String trytes = "OBGCZBOBMBCCOBNBNCACOBBCDCVBCC9"; + Assert.assertEquals(Converter.trytesToAscii(trytes), "EXPECTED_RESULT\0"); + trytes = "RBOBVBVBYB999999999"; + Assert.assertEquals(Converter.trytesToAscii(trytes), "HELLO\0\0\0\0\0"); + } } \ No newline at end of file From 88558c165173785460e4e33e7830aa9e28dffc5a Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Tue, 29 Jan 2019 19:13:43 +0100 Subject: [PATCH 16/34] Fix: test existence of spent addresses db do not point to correct folder (#1305) --- .../iri/service/snapshot/impl/SnapshotProviderImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java index 993bdc4542..37fbda8ec0 100644 --- a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java +++ b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotProviderImpl.java @@ -231,18 +231,19 @@ private Snapshot loadLocalSnapshot() throws SnapshotException, SpentAddressesExc } private void assertSpentAddressesDbExist() throws SpentAddressesException { + String spentAddressesDbPath = config.getSpentAddressesDbPath(); try { - File spentAddressFolder = new File(SnapshotConfig.Descriptions.SPENT_ADDRESSES_DB_LOG_PATH); + File spentAddressFolder = new File(spentAddressesDbPath); //If there is at least one file in the db the check should pass if (Files.newDirectoryStream(spentAddressFolder.toPath(), "*.sst").iterator().hasNext()) { return; } } catch (IOException e){ - throw new SpentAddressesException("Can't load " + SnapshotConfig.Descriptions.SPENT_ADDRESSES_DB_LOG_PATH + " folder", e); + throw new SpentAddressesException("Can't load " + spentAddressesDbPath + " folder", e); } - throw new SpentAddressesException(SnapshotConfig.Descriptions.SPENT_ADDRESSES_DB_LOG_PATH + " folder has no sst files"); + throw new SpentAddressesException(spentAddressesDbPath + " folder has no sst files"); } /** From bf402225942f790e056e04cd893141f802f7b90c Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Wed, 30 Jan 2019 13:12:45 +0100 Subject: [PATCH 17/34] Fix: added a buffer to LSManager (#1286) * added a buffer to LSManager * Added the out of sync buffer when we are in sync --- .../impl/LocalSnapshotManagerImpl.java | 60 +++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/iota/iri/service/snapshot/impl/LocalSnapshotManagerImpl.java b/src/main/java/com/iota/iri/service/snapshot/impl/LocalSnapshotManagerImpl.java index b90ec2981a..d55cfdcc1a 100644 --- a/src/main/java/com/iota/iri/service/snapshot/impl/LocalSnapshotManagerImpl.java +++ b/src/main/java/com/iota/iri/service/snapshot/impl/LocalSnapshotManagerImpl.java @@ -26,6 +26,12 @@ public class LocalSnapshotManagerImpl implements LocalSnapshotManager { * due. */ private static final int LOCAL_SNAPSHOT_RESCAN_INTERVAL = 10000; + + /** + * To prevent jumping back and forth in and out of sync, there is a buffer in between. + * Only when the latest milestone and latest snapshot differ more than this number, we fall out of sync + */ + private static final int LOCAL_SNAPSHOT_SYNC_BUFFER = 5; /** * Logger for this class allowing us to dump debug and status messages. @@ -51,6 +57,11 @@ public class LocalSnapshotManagerImpl implements LocalSnapshotManager { * Configuration with important snapshot related parameters. */ private SnapshotConfig config; + + /** + * If this node is currently seen as in sync + */ + private boolean isInSync; /** * Holds a reference to the {@link ThreadIdentifier} for the monitor thread. @@ -85,6 +96,8 @@ public LocalSnapshotManagerImpl init(SnapshotProvider snapshotProvider, Snapshot this.snapshotService = snapshotService; this.transactionPruner = transactionPruner; this.config = config; + + this.isInSync = false; return this; } @@ -117,10 +130,7 @@ public void shutdown() { */ private void monitorThread(LatestMilestoneTracker latestMilestoneTracker) { while (!Thread.currentThread().isInterrupted()) { - int localSnapshotInterval = latestMilestoneTracker.isInitialScanComplete() && - snapshotProvider.getLatestSnapshot().getIndex() == latestMilestoneTracker.getLatestMilestoneIndex() - ? config.getLocalSnapshotsIntervalSynced() - : config.getLocalSnapshotsIntervalUnsynced(); + int localSnapshotInterval = getSnapshotInterval(isInSync(latestMilestoneTracker)); int latestSnapshotIndex = snapshotProvider.getLatestSnapshot().getIndex(); int initialSnapshotIndex = snapshotProvider.getInitialSnapshot().getIndex(); @@ -136,4 +146,46 @@ private void monitorThread(LatestMilestoneTracker latestMilestoneTracker) { ThreadUtils.sleep(LOCAL_SNAPSHOT_RESCAN_INTERVAL); } } + + /** + * A snapshot is taken in an interval. + * This interval changes based on the state of the node. + * + * @param inSync if this node is in sync + * @return the current interval in which we take local snapshots + */ + private int getSnapshotInterval(boolean inSync) { + return inSync + ? config.getLocalSnapshotsIntervalSynced() + : config.getLocalSnapshotsIntervalUnsynced(); + } + + /** + * A node is defined in sync when the latest snapshot milestone index and the latest milestone index are equal. + * In order to prevent a bounce between in and out of sync, a buffer is added when a node became in sync. + * + * This will always return false if we are not done scanning milestone candidates during initialization. + * + * @param latestMilestoneTracker tracker we use to determine milestones + * @return true if we are in sync, otherwise false + */ + private boolean isInSync(LatestMilestoneTracker latestMilestoneTracker) { + if (!latestMilestoneTracker.isInitialScanComplete()) { + return false; + } + + int latestIndex = latestMilestoneTracker.getLatestMilestoneIndex(); + int latestSnapshot = snapshotProvider.getLatestSnapshot().getIndex(); + + // If we are out of sync, only a full sync will get us in + if (!isInSync && latestIndex == latestSnapshot) { + isInSync = true; + + // When we are in sync, only dropping below the buffer gets us out of sync + } else if (latestSnapshot < latestIndex - LOCAL_SNAPSHOT_SYNC_BUFFER) { + isInSync = false; + } + + return isInSync; + } } From 0c9ed77797c589e99a5d7127cca8370b5831e0a4 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Thu, 31 Jan 2019 11:10:35 +0100 Subject: [PATCH 18/34] Fix: Batch process spent addresses to avoid out of memory issues (#1314) --- src/main/java/com/iota/iri/Iota.java | 2 +- .../spentaddresses/SpentAddressesService.java | 7 +- .../impl/SpentAddressesServiceImpl.java | 192 +++++++++++++----- 3 files changed, 146 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index 9c3dc2e51a..f62fe864a6 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -200,7 +200,7 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx //because we check whether spent addresses data exists snapshotProvider.init(configuration); spentAddressesProvider.init(configuration); - spentAddressesService.init(tangle, snapshotProvider, spentAddressesProvider); + spentAddressesService.init(tangle, snapshotProvider, spentAddressesProvider, configuration); snapshotService.init(tangle, snapshotProvider, spentAddressesService, spentAddressesProvider, configuration); if (localSnapshotManager != null) { localSnapshotManager.init(snapshotProvider, snapshotService, transactionPruner, configuration); diff --git a/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java b/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java index 5d5c1ce386..828b772717 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java @@ -13,9 +13,10 @@ public interface SpentAddressesService { /** - * - * @param addressHash - * @return true if it was, else false + * Checks whether the address is associated with a valid signed output + * + * @param addressHash the address in question + * @return true if the address was spent from, else false * @throws SpentAddressesException */ boolean wasAddressSpentFrom(Hash addressHash) throws SpentAddressesException; diff --git a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java index cccf794dd5..136bc566fb 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java @@ -1,10 +1,12 @@ package com.iota.iri.service.spentaddresses.impl; import com.iota.iri.BundleValidator; +import com.iota.iri.conf.MilestoneConfig; import com.iota.iri.controllers.AddressViewModel; import com.iota.iri.controllers.MilestoneViewModel; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.model.Hash; +import com.iota.iri.model.HashFactory; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.spentaddresses.SpentAddressesException; import com.iota.iri.service.spentaddresses.SpentAddressesProvider; @@ -14,96 +16,75 @@ import com.iota.iri.storage.Tangle; import com.iota.iri.utils.dag.DAGHelper; + import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.collections4.CollectionUtils; import pl.touk.throwing.ThrowingPredicate; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Implementation of SpentAddressesService that calculates and checks spent addresses using the {@link Tangle} * */ public class SpentAddressesServiceImpl implements SpentAddressesService { + private static final Logger log = LoggerFactory.getLogger(SpentAddressesServiceImpl.class); + + /** + * Value for batch processing that was determined by testing + */ + private static final int BATCH_INTERVAL = 2500; + private Tangle tangle; private SnapshotProvider snapshotProvider; private SpentAddressesProvider spentAddressesProvider; - + private TailFinder tailFinder; + private MilestoneConfig config; + + /** * Creates a Spent address service using the Tangle - * - * @param tangle Tangle object which is used to load models of addresses - * @param snapshotProvider {@link SnapshotProvider} to find the genesis, used to verify tails + * + * @param tangle Tangle object which is used to load models of addresses + * @param snapshotProvider {@link SnapshotProvider} to find the genesis, used to verify tails * @param spentAddressesProvider Provider for loading/saving addresses to a database. * @return this instance */ - public SpentAddressesServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SpentAddressesProvider spentAddressesProvider) { + public SpentAddressesServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SpentAddressesProvider spentAddressesProvider, + MilestoneConfig config) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.spentAddressesProvider = spentAddressesProvider; this.tailFinder = new TailFinderImpl(tangle); - + this.config = config; + return this; } + @Override public boolean wasAddressSpentFrom(Hash addressHash) throws SpentAddressesException { - if (spentAddressesProvider.containsAddress(addressHash)) { - return true; - } - - try { - Set hashes = AddressViewModel.load(tangle, addressHash).getHashes(); - for (Hash hash : hashes) { - TransactionViewModel tx = TransactionViewModel.fromHash(tangle, hash); - // Check for spending transactions - if (wasTransactionSpentFrom(tx)) { - return true; - } - } - } catch (Exception e) { - throw new SpentAddressesException(e); - } - - return false; + return wasAddressSpentFrom(addressHash, getInitialUnspentAddresses()); } @Override public void persistSpentAddresses(int fromMilestoneIndex, int toMilestoneIndex) throws SpentAddressesException { - Set addressesToCheck = new HashSet<>(); - try { - for (int i = fromMilestoneIndex; i < toMilestoneIndex; i++) { - MilestoneViewModel currentMilestone = MilestoneViewModel.get(tangle, i); - if (currentMilestone != null) { - DAGHelper.get(tangle).traverseApprovees( - currentMilestone.getHash(), - transactionViewModel -> transactionViewModel.snapshotIndex() >= currentMilestone.index(), - transactionViewModel -> addressesToCheck.add(transactionViewModel.getAddressHash()) - ); - } - } - } catch (Exception e) { + try{ + processInBatches(fromMilestoneIndex, toMilestoneIndex, new HashSet<>(), getInitialUnspentAddresses()); + } catch(Exception e){ throw new SpentAddressesException(e); } - - //Can only throw runtime exceptions in streams - try { - spentAddressesProvider.saveAddressesBatch(addressesToCheck.stream() - .filter(ThrowingPredicate.unchecked(this::wasAddressSpentFrom)) - .collect(Collectors.toList())); - } catch (RuntimeException e) { - if (e.getCause() instanceof SpentAddressesException) { - throw (SpentAddressesException) e.getCause(); - } else { - throw e; - } - } } @Override @@ -111,7 +92,8 @@ public void persistSpentAddresses(Collection transactions) try { Collection spentAddresses = transactions.stream() .filter(ThrowingPredicate.unchecked(this::wasTransactionSpentFrom)) - .map(TransactionViewModel::getAddressHash).collect(Collectors.toSet()); + .map(TransactionViewModel::getAddressHash) + .collect(Collectors.toSet()); spentAddressesProvider.saveAddressesBatch(spentAddresses); } catch (RuntimeException e) { @@ -140,4 +122,112 @@ private boolean isBundleValid(Hash tailHash) throws Exception { BundleValidator.validate(tangle, snapshotProvider.getInitialSnapshot(), tailHash); return (CollectionUtils.isNotEmpty(validation) && validation.get(0).get(0).getValidity() == 1); } + + /** + * + * @param addressHash the address in question + * @param checkedAddresses known unspent addresses, used to skip calculations. + * Must contain at least {@link Hash#NULL_HASH} and the coordinator address. + * @return {@code true} if address was spent from, else {@code false} + * @throws SpentAddressesException + * @see #wasAddressSpentFrom(Hash) + */ + private boolean wasAddressSpentFrom(Hash addressHash, Collection checkedAddresses) + throws SpentAddressesException { + if (addressHash == null) { + return false; + } + + if (spentAddressesProvider.containsAddress(addressHash)) { + return true; + } + + //If address has already been checked this session, return false + if (checkedAddresses.contains(addressHash)){ + return false; + } + + try { + Set hashes = AddressViewModel.load(tangle, addressHash).getHashes(); + int setSizeLimit = 100_000; + + //If the hash set returned contains more than 100 000 entries, it likely will not be a spent address. + //To avoid unnecessary overhead while processing, the loop will return false + if (hashes.size() > setSizeLimit){ + checkedAddresses.add(addressHash); + return false; + } + + for (Hash hash: hashes) { + TransactionViewModel tx = TransactionViewModel.fromHash(tangle, hash); + // Check for spending transactions + if (wasTransactionSpentFrom(tx)) { + return true; + } + } + + } catch (Exception e) { + throw new SpentAddressesException(e); + } + + checkedAddresses.add(addressHash); + return false; + } + + //Processing in batches in order to avoid OOM errors + private void processInBatches(int fromMilestoneIndex, int toMilestoneIndex, Collection addressesToCheck, + Set checkedAddresses) throws SpentAddressesException { + try { + int interval = BATCH_INTERVAL; + double numBatches = Math.ceil(((double) toMilestoneIndex - fromMilestoneIndex) / interval); + + for (int batch = 0; batch < numBatches; batch++) { + int batchStart = batch * interval + fromMilestoneIndex; + int batchStop = batchStart + interval <= toMilestoneIndex ? batchStart + interval : toMilestoneIndex; + + for (int i = batchStart; i < batchStop; i++) { + try { + MilestoneViewModel currentMilestone = MilestoneViewModel.get(tangle, i); + if (currentMilestone != null) { + DAGHelper.get(tangle).traverseApprovees( + currentMilestone.getHash(), + transactionViewModel -> transactionViewModel.snapshotIndex() >= currentMilestone.index(), + transactionViewModel -> addressesToCheck.add(transactionViewModel.getAddressHash()) + ); + } + } catch (Exception e) { + throw new SpentAddressesException(e); + } + } + checkAddresses(addressesToCheck, checkedAddresses); + } + }catch(SpentAddressesException e) { + throw e; + } + } + + private void checkAddresses(Collection addressesToCheck, Collection checkedAddresses) + throws SpentAddressesException { + //Can only throw runtime exceptions in streams + try { + spentAddressesProvider.saveAddressesBatch(addressesToCheck.stream() + .filter(ThrowingPredicate.unchecked(address -> wasAddressSpentFrom(address, checkedAddresses))) + .collect(Collectors.toList())); + + //Clear addressesToCheck for next batch + addressesToCheck.clear(); + } catch (RuntimeException e) { + if (e.getCause() instanceof SpentAddressesException) { + throw (SpentAddressesException) e.getCause(); + } else { + throw e; + } + } + + } + + private Set getInitialUnspentAddresses() { + return Stream.of(Hash.NULL_HASH, HashFactory.ADDRESS.create(config.getCoordinator())) + .collect(Collectors.toSet()); + } } From ca704d92075dcf1e18e92c47f97b28a71350a848 Mon Sep 17 00:00:00 2001 From: Olaf van Wijk Date: Mon, 4 Feb 2019 21:41:49 +0700 Subject: [PATCH 19/34] Fix: missing user-agent header in cors (#1319) --- src/main/java/com/iota/iri/service/API.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index d1deda9244..14b1fd3ed3 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -184,7 +184,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception { exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, 0); exchange.getResponseHeaders().put(Headers.ALLOW, allowedMethods); exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Origin"), "*"); - exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Headers"), "Origin, X-Requested-With, Content-Type, Accept, X-IOTA-API-Version"); + exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Headers"), "User-Agent, Origin, X-Requested-With, Content-Type, Accept, X-IOTA-API-Version"); exchange.getResponseSender().close(); return; } From 95d38a24a11bdc85311cea715a4e8920cb7e4ce5 Mon Sep 17 00:00:00 2001 From: Andrea V Date: Mon, 4 Feb 2019 17:47:40 +0100 Subject: [PATCH 20/34] Feature: Fresh transactions to request (#1316) --- .../iri/network/TransactionRequester.java | 29 ++++++++--- .../TransactionRequesterTest.java | 50 ++++++++++++++++++- 2 files changed, 69 insertions(+), 10 deletions(-) rename src/test/java/com/iota/iri/{controllers => network}/TransactionRequesterTest.java (58%) diff --git a/src/main/java/com/iota/iri/network/TransactionRequester.java b/src/main/java/com/iota/iri/network/TransactionRequester.java index e3d1db8b61..1b846d345f 100644 --- a/src/main/java/com/iota/iri/network/TransactionRequester.java +++ b/src/main/java/com/iota/iri/network/TransactionRequester.java @@ -71,7 +71,10 @@ public void requestTransaction(Hash hash, boolean milestone) throws Exception { transactionsToRequest.remove(hash); milestoneTransactionsToRequest.add(hash); } else { - if(!milestoneTransactionsToRequest.contains(hash) && !transactionsToRequestIsFull()) { + if(!milestoneTransactionsToRequest.contains(hash)) { + if (transactionsToRequestIsFull()) { + popEldestTransactionToRequest(); + } transactionsToRequest.add(hash); } } @@ -79,6 +82,20 @@ public void requestTransaction(Hash hash, boolean milestone) throws Exception { } } + /** + * This method removes the oldest transaction in the transactionsToRequest Set. + * + * It used when the queue capacity is reached, and new transactions would be dropped as a result. + */ + // @VisibleForTesting + void popEldestTransactionToRequest() { + Iterator iterator = transactionsToRequest.iterator(); + if (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } + } + /** * This method allows to check if a transaction was requested by the TransactionRequester. * @@ -111,13 +128,14 @@ public Hash transactionToRequest(boolean milestone) throws Exception { synchronized (syncObj) { // repeat while we have transactions that shall be requested while (requestSet.size() != 0) { - // remove the first item in our set for further examination + // get the first item in our set for further examination Iterator iterator = requestSet.iterator(); hash = iterator.next(); - iterator.remove(); // if we have received the transaction in the mean time .... if (TransactionViewModel.exists(tangle, hash)) { + // we remove the transaction from the queue since we got it + iterator.remove(); // ... dump a log message ... log.info("Removed existing tx from request list: " + hash); messageQ.publish("rtl %s", hash); @@ -126,11 +144,6 @@ public Hash transactionToRequest(boolean milestone) throws Exception { continue; } - // ... otherwise -> re-add it at the end of the set ... - // - // Note: we always have enough space since we removed the element before - requestSet.add(hash); - // ... and abort our loop to continue processing with the element we found break; } diff --git a/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java b/src/test/java/com/iota/iri/network/TransactionRequesterTest.java similarity index 58% rename from src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java rename to src/test/java/com/iota/iri/network/TransactionRequesterTest.java index 23f1e5320c..863438f321 100644 --- a/src/test/java/com/iota/iri/controllers/TransactionRequesterTest.java +++ b/src/test/java/com/iota/iri/network/TransactionRequesterTest.java @@ -1,8 +1,8 @@ -package com.iota.iri.controllers; +package com.iota.iri.network; import com.iota.iri.conf.MainnetConfig; +import com.iota.iri.controllers.TransactionViewModelTest; import com.iota.iri.model.Hash; -import com.iota.iri.network.TransactionRequester; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl; import com.iota.iri.storage.Tangle; @@ -11,6 +11,10 @@ import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import static org.junit.Assert.*; /** @@ -76,6 +80,48 @@ public void instance() throws Exception { } + @Test + public void popEldestTransactionToRequest() throws Exception { + TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq); + // Add some Txs to the pool and see if the method pops the eldest one + Hash eldest = TransactionViewModelTest.getRandomTransactionHash(); + txReq.requestTransaction(eldest, false); + txReq.requestTransaction(TransactionViewModelTest.getRandomTransactionHash(), false); + txReq.requestTransaction(TransactionViewModelTest.getRandomTransactionHash(), false); + txReq.requestTransaction(TransactionViewModelTest.getRandomTransactionHash(), false); + + txReq.popEldestTransactionToRequest(); + // Check that the transaction is there no more + assertFalse(txReq.isTransactionRequested(eldest, false)); + } + + @Test + public void transactionRequestedFreshness() throws Exception { + // Add some Txs to the pool and see if the method pops the eldest one + List eldest = new ArrayList(Arrays.asList( + TransactionViewModelTest.getRandomTransactionHash(), + TransactionViewModelTest.getRandomTransactionHash(), + TransactionViewModelTest.getRandomTransactionHash() + )); + TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq); + int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE; + //fill tips list + for (int i = 0; i < 3; i++) { + txReq.requestTransaction(eldest.get(i), false); + } + for (int i = 0; i < capacity; i++) { + Hash hash = TransactionViewModelTest.getRandomTransactionHash(); + txReq.requestTransaction(hash,false); + } + + //check that limit wasn't breached + assertEquals("Queue capacity breached!!", capacity, txReq.numberOfTransactionsToRequest()); + // None of the eldest transactions should be in the pool + for (int i = 0; i < 3; i++) { + assertFalse("Old transaction has been requested", txReq.isTransactionRequested(eldest.get(i), false)); + } + } + @Test public void nonMilestoneCapacityLimited() throws Exception { TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq); From 151a1bba19b0cb2dee907ca1274e480266820162 Mon Sep 17 00:00:00 2001 From: Brord van Wierst Date: Wed, 6 Feb 2019 09:16:19 +0100 Subject: [PATCH 21/34] Fix: Added a NULL_HASH check for genesis transaction (#1309) --- .../network/TransactionRequesterWorker.java | 4 +- .../impl/TransactionRequesterWorkerImpl.java | 45 +++-- .../java/com/iota/iri/TangleMockUtils.java | 20 ++ .../com/iota/iri/TransactionTestUtils.java | 184 +++++++++++++++++- .../iota/iri/TransactionValidatorTest.java | 2 +- .../iri/controllers/BundleViewModelTest.java | 7 +- .../controllers/MilestoneViewModelTest.java | 3 - .../iri/controllers/TagViewModelTest.java | 3 - .../iri/controllers/TipsViewModelTest.java | 11 +- .../controllers/TransactionViewModelTest.java | 47 +---- .../com/iota/iri/crypto/PearlDiverTest.java | 3 +- .../java/com/iota/iri/model/HashTest.java | 27 ++- .../iri/network/TransactionRequesterTest.java | 29 ++- .../TransactionRequesterWorkerImplTest.java | 153 +++++++++++++++ .../impl/MilestoneServiceImplTest.java | 9 +- .../impl/SnapshotServiceImplTest.java | 9 +- .../impl/CumulativeWeightCalculatorTest.java | 2 +- .../impl/EntryPointSelectorImplTest.java | 11 +- .../tipselection/impl/RatingOneTest.java | 4 +- .../tipselection/impl/TailFinderImplTest.java | 32 +-- .../impl/WalkValidatorImplTest.java | 50 ++--- .../tipselection/impl/WalkerAlphaTest.java | 14 +- .../RocksDBPersistenceProviderTest.java | 1 - 23 files changed, 506 insertions(+), 164 deletions(-) create mode 100644 src/test/java/com/iota/iri/network/impl/TransactionRequesterWorkerImplTest.java diff --git a/src/main/java/com/iota/iri/network/TransactionRequesterWorker.java b/src/main/java/com/iota/iri/network/TransactionRequesterWorker.java index 292c6a03b6..52d5e8aec2 100644 --- a/src/main/java/com/iota/iri/network/TransactionRequesterWorker.java +++ b/src/main/java/com/iota/iri/network/TransactionRequesterWorker.java @@ -8,10 +8,12 @@ * as new transactions are received.
*/ public interface TransactionRequesterWorker { + /** * Works through the request queue by sending a request alongside a random tip to each of our neighbors.
+ * @return true when we have send the request to our neighbors, otherwise false */ - void processRequestQueue(); + boolean processRequestQueue(); /** * Starts the background worker that automatically calls {@link #processRequestQueue()} periodically to process the diff --git a/src/main/java/com/iota/iri/network/impl/TransactionRequesterWorkerImpl.java b/src/main/java/com/iota/iri/network/impl/TransactionRequesterWorkerImpl.java index 803a5f2630..e3d6ea3bb6 100644 --- a/src/main/java/com/iota/iri/network/impl/TransactionRequesterWorkerImpl.java +++ b/src/main/java/com/iota/iri/network/impl/TransactionRequesterWorkerImpl.java @@ -30,7 +30,7 @@ public class TransactionRequesterWorkerImpl implements TransactionRequesterWorke /** * The minimum amount of transactions in the request queue that are required for the worker to trigger.
*/ - private static final int REQUESTER_THREAD_ACTIVATION_THRESHOLD = 50; + public static final int REQUESTER_THREAD_ACTIVATION_THRESHOLD = 50; /** * The time (in milliseconds) that the worker waits between its iterations.
@@ -105,26 +105,44 @@ public TransactionRequesterWorkerImpl init(Tangle tangle, TransactionRequester t * traffic like transactions that get relayed by our node.
*/ @Override - public void processRequestQueue() { + public boolean processRequestQueue() { try { - if (transactionRequester.numberOfTransactionsToRequest() >= REQUESTER_THREAD_ACTIVATION_THRESHOLD) { + if (isActive()) { TransactionViewModel transaction = getTransactionToSendWithRequest(); - if (transaction != null && transaction.getType() != TransactionViewModel.PREFILLED_SLOT) { - for (Neighbor neighbor : node.getNeighbors()) { - try { - // automatically adds the hash of a requested transaction when sending a packet - node.sendPacket(transaction, neighbor); - } catch (Exception e) { - log.error("unexpected error while sending request to neighbour", e); - } - } + if (isValidTransaction(transaction)) { + sendToNodes(transaction); + return true; } } } catch (Exception e) { log.error("unexpected error while processing the request queue", e); } + return false; } + private void sendToNodes(TransactionViewModel transaction) { + for (Neighbor neighbor : node.getNeighbors()) { + try { + // automatically adds the hash of a requested transaction when sending a packet + node.sendPacket(transaction, neighbor); + } catch (Exception e) { + log.error("unexpected error while sending request to neighbour", e); + } + } + } + + //Package Private For Testing + boolean isActive() { + return transactionRequester.numberOfTransactionsToRequest() >= REQUESTER_THREAD_ACTIVATION_THRESHOLD; + } + + //Package Private For Testing + boolean isValidTransaction(TransactionViewModel transaction) { + return transaction != null && ( + transaction.getType() != TransactionViewModel.PREFILLED_SLOT + || transaction.getHash().equals(Hash.NULL_HASH)); + } + @Override public void start() { executorService.silentScheduleWithFixedDelay(this::processRequestQueue, 0, REQUESTER_THREAD_INTERVAL, @@ -145,7 +163,8 @@ public void shutdown() { * @return a random tip * @throws Exception if anything unexpected happens while trying to retrieve the random tip. */ - private TransactionViewModel getTransactionToSendWithRequest() throws Exception { + //Package Private For Testing + TransactionViewModel getTransactionToSendWithRequest() throws Exception { Hash tip = tipsViewModel.getRandomSolidTipHash(); if (tip == null) { tip = tipsViewModel.getRandomNonSolidTipHash(); diff --git a/src/test/java/com/iota/iri/TangleMockUtils.java b/src/test/java/com/iota/iri/TangleMockUtils.java index 59df2c9ae9..b1a6409370 100644 --- a/src/test/java/com/iota/iri/TangleMockUtils.java +++ b/src/test/java/com/iota/iri/TangleMockUtils.java @@ -57,12 +57,32 @@ public static Milestone mockMilestone(Tangle tangle, Hash hash, int index) { //region [mockTransaction] ///////////////////////////////////////////////////////////////////////////////////////// + /** + * Creates an empty transaction, which is marked filled and parsed. + * This transaction is returned when the hash is asked to load in the tangle object + * + * @param tangle mocked tangle object that shall retrieve a milestone object when being queried for it + * @param hash + * @return The newly created (empty) transaction + */ public static Transaction mockTransaction(Tangle tangle, Hash hash) { Transaction transaction = new Transaction(); transaction.bytes = new byte[0]; transaction.type = TransactionViewModel.FILLED_SLOT; transaction.parsed = true; + return mockTransaction(tangle, hash, transaction); + } + + /** + * Mocks the tangle object by checking for the hash and returning the transaction. + * + * @param tangle mocked tangle object that shall retrieve a milestone object when being queried for it + * @param hash transaction hash + * @param transaction the transaction we send back + * @return The transaction + */ + public static Transaction mockTransaction(Tangle tangle, Hash hash, Transaction transaction) { try { Mockito.when(tangle.load(Transaction.class, hash)).thenReturn(transaction); Mockito.when(tangle.getLatest(Transaction.class, Hash.class)).thenReturn(new Pair<>(hash, transaction)); diff --git a/src/test/java/com/iota/iri/TransactionTestUtils.java b/src/test/java/com/iota/iri/TransactionTestUtils.java index 7b9dcd14d7..f8b996d3b8 100644 --- a/src/test/java/com/iota/iri/TransactionTestUtils.java +++ b/src/test/java/com/iota/iri/TransactionTestUtils.java @@ -1,36 +1,71 @@ package com.iota.iri; import com.iota.iri.controllers.TransactionViewModel; -import com.iota.iri.controllers.TransactionViewModelTest; import com.iota.iri.crypto.SpongeFactory; import com.iota.iri.model.Hash; +import com.iota.iri.model.HashFactory; import com.iota.iri.model.TransactionHash; +import com.iota.iri.model.persistables.Transaction; import com.iota.iri.utils.Converter; + +import java.util.Arrays; +import java.util.Random; + import org.apache.commons.lang3.StringUtils; public class TransactionTestUtils { + private static Random seed = new Random(); + + /** + * Updates the transaction index in trits. + * + * @param tx The transaction to update + * @param currentIndex The new index to set the transaction to + */ public static void setCurrentIndex(TransactionViewModel tx, long currentIndex) { Converter.copyTrits(currentIndex, tx.trits(), TransactionViewModel.CURRENT_INDEX_TRINARY_OFFSET, TransactionViewModel.CURRENT_INDEX_TRINARY_SIZE); } + /** + * Updates the last transaction index in trits. + * + * @param tx The transaction to update + * @param lastIndex The new last index to set the transaction to + */ public static void setLastIndex(TransactionViewModel tx, long lastIndex) { Converter.copyTrits(lastIndex, tx.trits(), TransactionViewModel.LAST_INDEX_TRINARY_OFFSET, TransactionViewModel.LAST_INDEX_TRINARY_SIZE); } + /** + * Generates a random transaction with a random hash. + * Transaction last and current index are set to the index provided. + * + * @param index The index to set the transaction to + * @return A random transaction which is located on the end of its (nonexistent) bundle + */ public static TransactionViewModel createBundleHead(int index) { - TransactionViewModel tx = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionTrits(), TransactionViewModelTest.getRandomTransactionHash()); + + TransactionViewModel tx = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); setLastIndex(tx, index); setCurrentIndex(tx, index); return tx; } + /** + * Generates a transaction with the specified trunk and branch, and bundle hash from trunk. + * This transaction indices are updated to match the trunk index. + * + * @param trunkTx The trunk transaction + * @param branchHash The branch transaction hash + * @return A transaction in the same bundle as trunk, with its index 1 below trunk index + */ public static TransactionViewModel createTransactionWithTrunkBundleHash(TransactionViewModel trunkTx, Hash branchHash) { TransactionViewModel tx = new TransactionViewModel( - TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(trunkTx.getHash(), branchHash), - TransactionViewModelTest.getRandomTransactionHash()); + getRandomTransactionWithTrunkAndBranch(trunkTx.getHash(), branchHash), + getRandomTransactionHash()); setCurrentIndex(tx, trunkTx.getCurrentIndex() - 1); setLastIndex(tx, trunkTx.lastIndex()); System.arraycopy(trunkTx.trits(), TransactionViewModel.BUNDLE_TRINARY_OFFSET, tx.trits(), @@ -38,22 +73,84 @@ public static TransactionViewModel createTransactionWithTrunkBundleHash(Transact return tx; } + /** + * Generates a transaction with the provided trytes. + * If the trytes are not enough to make a full transaction, 9s are appended. + * Transaction hash is calculated and added. + * + * @param trytes The transaction trytes to use + * @return The transaction + */ public static TransactionViewModel createTransactionWithTrytes(String trytes) { String expandedTrytes = expandTrytes(trytes); byte[] trits = Converter.allocatingTritsFromTrytes(expandedTrytes); return new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); } + /** + * Generates a transaction with the provided trytes, trunk and hash. + * If the trytes are not enough to make a full transaction, 9s are appended. + * Transaction hash is calculated and added. + * + * @param trytes The transaction trytes to use + * @param trunk The trunk transaction hash + * @param branch The branch transaction hash + * @return The transaction + */ public static TransactionViewModel createTransactionWithTrunkAndBranch(String trytes, Hash trunk, Hash branch) { + byte[] trits = createTransactionWithTrunkAndBranchTrits(trytes, trunk, branch); + return new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); + } + + /** + * Generates transaction trits with the provided trytes, trunk and hash. + * If the trytes are not enough to make a full transaction, 9s are appended. + * + * @param trytes The transaction trytes to use + * @param trunk The trunk transaction hash + * @param branch The branch transaction hash + * @return The transaction trits + */ + public static byte[] createTransactionWithTrunkAndBranchTrits(String trytes, Hash trunk, Hash branch) { String expandedTrytes = expandTrytes(trytes); byte[] trits = Converter.allocatingTritsFromTrytes(expandedTrytes); + return getRandomTransactionTritsWithTrunkAndBranch(trits, trunk, branch); + } + + /** + * Generates random transaction trits with the provided trytes, trunk and hash. + * + * @param trunk The trunk transaction hash + * @param branch The branch transaction hash + * @return The transaction trits + */ + public static byte[] getRandomTransactionWithTrunkAndBranch(Hash trunk, Hash branch) { + byte[] trits = getRandomTransactionTrits(); + return getRandomTransactionTritsWithTrunkAndBranch(trits, trunk, branch); + } + + /** + * Generates transaction trits with the provided trits, trunk and hash. + * + * @param trunk The trunk transaction hash + * @param branch The branch transaction hash + * @return trits The transaction trits + */ + public static byte[] getRandomTransactionTritsWithTrunkAndBranch(byte[] trits, Hash trunk, Hash branch) { System.arraycopy(trunk.trits(), 0, trits, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_SIZE); System.arraycopy(branch.trits(), 0, trits, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_SIZE); - return new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); + return trits; } + /** + * Increases a char with the next char in the alphabet, until the char is Z. + * When the char is Z, adds a new char starting at A (So no 9 is used). + * + * @param trytes The Trytes to change. + * @return The changed trytes + */ public static String nextWord(String trytes) { if ("".equals(trytes)) { return "A"; @@ -67,8 +164,85 @@ public static String nextWord(String trytes) { } return trytes + 'A'; } + + /** + * Generates a random transaction. + * + * @return The transaction + */ + public static Transaction getRandomTransaction() { + byte[] trits = getRandomTransactionTrits(); + return buildTransaction(trits); + } + + /** + * Generates random trits for a transaction. + * + * @return The transaction trits + */ + public static byte[] getRandomTransactionTrits() { + return getRandomTrits(TransactionViewModel.TRINARY_SIZE); + } + + /** + * Generates a transaction with only 9s. + * + * @return The transaction + */ + public static Transaction get9Transaction() { + byte[] trits = new byte[TransactionViewModel.TRINARY_SIZE]; + Arrays.fill(trits, (byte) 0); + return buildTransaction(trits); + } + + /** + * Generates random trits for a transaction. + * + * @return The transaction hash + */ + public static Hash getRandomTransactionHash() { + byte[] out = getRandomTrits(Hash.SIZE_IN_TRITS); + return HashFactory.TRANSACTION.create(out); + } + + /** + * Builds a transaction by transforming trits to bytes. + * + * @param trits The trits to build the transaction + * @return The created transaction + */ + public static Transaction buildTransaction(byte[] trits) { + Transaction transaction = new Transaction(); + + transaction.bytes = Converter.allocateBytesForTrits(trits.length); + Converter.bytes(trits, 0, transaction.bytes, 0, trits.length); + transaction.readMetadata( transaction.bytes); + return transaction; + } + + /** + * Appends 9s to the supplied trytes until the trytes are of size {@link TransactionViewModel.TRYTES_SIZE}. + * + * @param trytes the trytes to append to. + * @return The expanded trytes string + */ private static String expandTrytes(String trytes) { return trytes + StringUtils.repeat('9', TransactionViewModel.TRYTES_SIZE - trytes.length()); } + + /** + * Generates random trits of specified size. + * + * @param size the amount of trits to generate + * @return The trits + */ + private static byte[] getRandomTrits(int size) { + byte[] out = new byte[size]; + + for(int i = 0; i < out.length; i++) { + out[i] = (byte) (seed.nextInt(3) - 1); + } + return out; + } } diff --git a/src/test/java/com/iota/iri/TransactionValidatorTest.java b/src/test/java/com/iota/iri/TransactionValidatorTest.java index fcf9929464..82f73b562f 100644 --- a/src/test/java/com/iota/iri/TransactionValidatorTest.java +++ b/src/test/java/com/iota/iri/TransactionValidatorTest.java @@ -18,7 +18,7 @@ import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; -import static com.iota.iri.controllers.TransactionViewModelTest.*; +import static com.iota.iri.TransactionTestUtils.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/src/test/java/com/iota/iri/controllers/BundleViewModelTest.java b/src/test/java/com/iota/iri/controllers/BundleViewModelTest.java index 7246911370..f50f09583b 100644 --- a/src/test/java/com/iota/iri/controllers/BundleViewModelTest.java +++ b/src/test/java/com/iota/iri/controllers/BundleViewModelTest.java @@ -13,9 +13,8 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; -/** - * Created by paul on 5/2/17. - */ +import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; + public class BundleViewModelTest { private static final TemporaryFolder dbFolder = new TemporaryFolder(); private static final TemporaryFolder logFolder = new TemporaryFolder(); @@ -72,7 +71,7 @@ public void getTail() throws Exception { @Test public void firstShouldFindTx() throws Exception { - byte[] trits = TransactionViewModelTest.getRandomTransactionTrits(); + byte[] trits = getRandomTransactionTrits(); TransactionViewModel transactionViewModel = new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); transactionViewModel.store(tangle, snapshotProvider.getInitialSnapshot()); diff --git a/src/test/java/com/iota/iri/controllers/MilestoneViewModelTest.java b/src/test/java/com/iota/iri/controllers/MilestoneViewModelTest.java index 452a4f219a..a8bc08a24d 100644 --- a/src/test/java/com/iota/iri/controllers/MilestoneViewModelTest.java +++ b/src/test/java/com/iota/iri/controllers/MilestoneViewModelTest.java @@ -10,9 +10,6 @@ import static org.junit.Assert.*; -/** - * Created by paul on 4/11/17. - */ public class MilestoneViewModelTest { final TemporaryFolder dbFolder = new TemporaryFolder(); final TemporaryFolder logFolder = new TemporaryFolder(); diff --git a/src/test/java/com/iota/iri/controllers/TagViewModelTest.java b/src/test/java/com/iota/iri/controllers/TagViewModelTest.java index 62d2cd3337..fa6ef5140b 100644 --- a/src/test/java/com/iota/iri/controllers/TagViewModelTest.java +++ b/src/test/java/com/iota/iri/controllers/TagViewModelTest.java @@ -6,9 +6,6 @@ import static org.junit.Assert.*; -/** - * Created by paul on 5/2/17. - */ public class TagViewModelTest { @Before public void setUp() throws Exception { diff --git a/src/test/java/com/iota/iri/controllers/TipsViewModelTest.java b/src/test/java/com/iota/iri/controllers/TipsViewModelTest.java index 8e84f3903c..0be38f4b5d 100644 --- a/src/test/java/com/iota/iri/controllers/TipsViewModelTest.java +++ b/src/test/java/com/iota/iri/controllers/TipsViewModelTest.java @@ -8,11 +8,10 @@ import java.util.concurrent.ExecutionException; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; + import static org.junit.Assert.*; -/** - * Created by paul on 5/2/17. - */ public class TipsViewModelTest { @Before public void setUp() throws Exception { @@ -80,7 +79,7 @@ public void nonsolidCapacityLimited() throws ExecutionException, InterruptedExce int capacity = TipsViewModel.MAX_TIPS; //fill tips list for (int i = 0; i < capacity * 2 ; i++) { - Hash hash = TransactionViewModelTest.getRandomTransactionHash(); + Hash hash = getRandomTransactionHash(); tipsVM.addTipHash(hash); } //check that limit wasn't breached @@ -93,7 +92,7 @@ public void solidCapacityLimited() throws ExecutionException, InterruptedExcepti int capacity = TipsViewModel.MAX_TIPS; //fill tips list for (int i = 0; i < capacity * 2 ; i++) { - Hash hash = TransactionViewModelTest.getRandomTransactionHash(); + Hash hash = getRandomTransactionHash(); tipsVM.addTipHash(hash); tipsVM.setSolid(hash); } @@ -107,7 +106,7 @@ public void totalCapacityLimited() throws ExecutionException, InterruptedExcepti int capacity = TipsViewModel.MAX_TIPS; //fill tips list for (int i = 0; i <= capacity * 4; i++) { - Hash hash = TransactionViewModelTest.getRandomTransactionHash(); + Hash hash = getRandomTransactionHash(); tipsVM.addTipHash(hash); if (i % 2 == 1) { tipsVM.setSolid(hash); diff --git a/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java b/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java index 3d49659576..eb369e763b 100644 --- a/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java +++ b/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java @@ -3,9 +3,7 @@ import com.iota.iri.conf.MainnetConfig; import com.iota.iri.crypto.SpongeFactory; import com.iota.iri.model.Hash; -import com.iota.iri.model.HashFactory; import com.iota.iri.model.TransactionHash; -import com.iota.iri.model.persistables.Transaction; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl; import com.iota.iri.storage.Tangle; @@ -16,6 +14,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.rules.TemporaryFolder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +23,10 @@ import java.util.Random; import java.util.Set; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; + import static org.junit.Assert.*; public class TransactionViewModelTest { @@ -412,44 +415,4 @@ public void firstShouldFindTx() throws Exception { TransactionViewModel result = TransactionViewModel.first(tangle); Assert.assertEquals(transactionViewModel.getHash(), result.getHash()); } - - private Transaction getRandomTransaction(Random seed) { - Transaction transaction = new Transaction(); - - byte[] trits = new byte[TransactionViewModel.SIGNATURE_MESSAGE_FRAGMENT_TRINARY_SIZE]; - for(int i = 0; i < trits.length; i++) { - trits[i] = (byte) (seed.nextInt(3) - 1); - } - - transaction.bytes = Converter.allocateBytesForTrits(trits.length); - Converter.bytes(trits, 0, transaction.bytes, 0, trits.length); - return transaction; - } - public static byte[] getRandomTransactionWithTrunkAndBranch(Hash trunk, Hash branch) { - byte[] trits = getRandomTransactionTrits(); - System.arraycopy(trunk.trits(), 0, trits, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_OFFSET, - TransactionViewModel.TRUNK_TRANSACTION_TRINARY_SIZE); - System.arraycopy(branch.trits(), 0, trits, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_OFFSET, - TransactionViewModel.BRANCH_TRANSACTION_TRINARY_SIZE); - return trits; - } - public static byte[] getRandomTransactionTrits() { - byte[] out = new byte[TransactionViewModel.TRINARY_SIZE]; - - for(int i = 0; i < out.length; i++) { - out[i] = (byte) (seed.nextInt(3) - 1); - } - - return out; - } - - public static Hash getRandomTransactionHash() { - byte[] out = new byte[Hash.SIZE_IN_TRITS]; - - for(int i = 0; i < out.length; i++) { - out[i] = (byte) (seed.nextInt(3) - 1); - } - - return HashFactory.TRANSACTION.create(out); - } } diff --git a/src/test/java/com/iota/iri/crypto/PearlDiverTest.java b/src/test/java/com/iota/iri/crypto/PearlDiverTest.java index 7430de5b7a..68b2655b63 100644 --- a/src/test/java/com/iota/iri/crypto/PearlDiverTest.java +++ b/src/test/java/com/iota/iri/crypto/PearlDiverTest.java @@ -1,5 +1,6 @@ package com.iota.iri.crypto; +import com.iota.iri.TransactionTestUtils; import com.iota.iri.controllers.TransactionViewModelTest; import com.iota.iri.model.Hash; import com.iota.iri.model.TransactionHash; @@ -54,7 +55,7 @@ public void testInvalidTritsLength() { @Ignore("to test pearlDiver iteratively") public void testNoRandomFail() { for (int i = 0; i < 10000; i++) { - byte[] trits = TransactionViewModelTest.getRandomTransactionTrits(); + byte[] trits = TransactionTestUtils.getRandomTransactionTrits(); pearlDiver.search(trits, MIN_WEIGHT_MAGNITUDE, NUM_CORES); Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits); for (int j = Hash.SIZE_IN_TRITS - 1; j > Hash.SIZE_IN_TRITS - MIN_WEIGHT_MAGNITUDE; j--) { diff --git a/src/test/java/com/iota/iri/model/HashTest.java b/src/test/java/com/iota/iri/model/HashTest.java index 83e5a4ef28..bdbb561916 100644 --- a/src/test/java/com/iota/iri/model/HashTest.java +++ b/src/test/java/com/iota/iri/model/HashTest.java @@ -1,21 +1,20 @@ package com.iota.iri.model; import com.iota.iri.controllers.TransactionViewModel; -import com.iota.iri.controllers.TransactionViewModelTest; import com.iota.iri.crypto.SpongeFactory; import com.iota.iri.utils.Converter; + +import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; + import org.junit.Assert; import org.junit.Test; import java.util.Arrays; -/** - * Created by paul on 4/29/17. - */ public class HashTest { @Test public void calculate() throws Exception { - Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, TransactionViewModelTest.getRandomTransactionTrits()); + Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, getRandomTransactionTrits()); Assert.assertNotEquals(0, hash.hashCode()); Assert.assertNotEquals(null, hash.bytes()); Assert.assertNotEquals(null, hash.trits()); @@ -23,7 +22,7 @@ public void calculate() throws Exception { @Test public void calculate1() throws Exception { - Hash hash = TransactionHash.calculate(TransactionViewModelTest.getRandomTransactionTrits(), 0, 729, SpongeFactory.create(SpongeFactory.Mode.CURLP81)); + Hash hash = TransactionHash.calculate(getRandomTransactionTrits(), 0, 729, SpongeFactory.create(SpongeFactory.Mode.CURLP81)); Assert.assertNotEquals(null, hash.bytes()); Assert.assertNotEquals(0, hash.hashCode()); Assert.assertNotEquals(null, hash.trits()); @@ -31,7 +30,7 @@ public void calculate1() throws Exception { @Test public void calculate2() throws Exception { - byte[] trits = TransactionViewModelTest.getRandomTransactionTrits(); + byte[] trits = getRandomTransactionTrits(); byte[] bytes = Converter.allocateBytesForTrits(trits.length); Converter.bytes(trits, bytes); Hash hash = TransactionHash.calculate(bytes, TransactionViewModel.TRINARY_SIZE, SpongeFactory.create(SpongeFactory.Mode.CURLP81)); @@ -48,23 +47,23 @@ public void trailingZeros() throws Exception { @Test public void trits() throws Exception { - Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, TransactionViewModelTest.getRandomTransactionTrits()); + Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, getRandomTransactionTrits()); Assert.assertFalse(Arrays.equals(new byte[Hash.SIZE_IN_TRITS], hash.trits())); } @Test public void equals() throws Exception { - byte[] trits = TransactionViewModelTest.getRandomTransactionTrits(); + byte[] trits = getRandomTransactionTrits(); Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits); Hash hash1 = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits); Assert.assertTrue(hash.equals(hash1)); Assert.assertFalse(hash.equals(Hash.NULL_HASH)); - Assert.assertFalse(hash.equals(TransactionHash.calculate(SpongeFactory.Mode.CURLP81, TransactionViewModelTest.getRandomTransactionTrits()))); + Assert.assertFalse(hash.equals(TransactionHash.calculate(SpongeFactory.Mode.CURLP81, getRandomTransactionTrits()))); } @Test public void hashCodeTest() throws Exception { - byte[] trits = TransactionViewModelTest.getRandomTransactionTrits(); + byte[] trits = getRandomTransactionTrits(); Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits); Assert.assertNotEquals(hash.hashCode(), 0); Assert.assertEquals(Hash.NULL_HASH.hashCode(), -240540129); @@ -72,7 +71,7 @@ public void hashCodeTest() throws Exception { @Test public void toStringTest() throws Exception { - byte[] trits = TransactionViewModelTest.getRandomTransactionTrits(); + byte[] trits = getRandomTransactionTrits(); Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits); Assert.assertEquals(Hash.NULL_HASH.toString(), "999999999999999999999999999999999999999999999999999999999999999999999999999999999"); Assert.assertNotEquals(hash.toString(), "999999999999999999999999999999999999999999999999999999999999999999999999999999999"); @@ -82,7 +81,7 @@ public void toStringTest() throws Exception { @Test public void bytes() throws Exception { - byte[] trits = TransactionViewModelTest.getRandomTransactionTrits(); + byte[] trits = getRandomTransactionTrits(); Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits); Assert.assertTrue(Arrays.equals(new byte[Hash.SIZE_IN_BYTES], Hash.NULL_HASH.bytes())); Assert.assertFalse(Arrays.equals(new byte[Hash.SIZE_IN_BYTES], hash.bytes())); @@ -91,7 +90,7 @@ public void bytes() throws Exception { @Test public void compareTo() throws Exception { - byte[] trits = TransactionViewModelTest.getRandomTransactionTrits(); + byte[] trits = getRandomTransactionTrits(); Hash hash = TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits); Assert.assertEquals(hash.compareTo(Hash.NULL_HASH), -Hash.NULL_HASH.compareTo(hash)); } diff --git a/src/test/java/com/iota/iri/network/TransactionRequesterTest.java b/src/test/java/com/iota/iri/network/TransactionRequesterTest.java index 863438f321..b4f02e88d3 100644 --- a/src/test/java/com/iota/iri/network/TransactionRequesterTest.java +++ b/src/test/java/com/iota/iri/network/TransactionRequesterTest.java @@ -1,7 +1,6 @@ package com.iota.iri.network; import com.iota.iri.conf.MainnetConfig; -import com.iota.iri.controllers.TransactionViewModelTest; import com.iota.iri.model.Hash; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl; @@ -11,15 +10,15 @@ import org.junit.Before; import org.junit.Test; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.junit.Assert.*; -/** - * Created by paul on 5/2/17. - */ + public class TransactionRequesterTest { private static Tangle tangle = new Tangle(); private static SnapshotProvider snapshotProvider; @@ -84,11 +83,11 @@ public void instance() throws Exception { public void popEldestTransactionToRequest() throws Exception { TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq); // Add some Txs to the pool and see if the method pops the eldest one - Hash eldest = TransactionViewModelTest.getRandomTransactionHash(); + Hash eldest = getRandomTransactionHash(); txReq.requestTransaction(eldest, false); - txReq.requestTransaction(TransactionViewModelTest.getRandomTransactionHash(), false); - txReq.requestTransaction(TransactionViewModelTest.getRandomTransactionHash(), false); - txReq.requestTransaction(TransactionViewModelTest.getRandomTransactionHash(), false); + txReq.requestTransaction(getRandomTransactionHash(), false); + txReq.requestTransaction(getRandomTransactionHash(), false); + txReq.requestTransaction(getRandomTransactionHash(), false); txReq.popEldestTransactionToRequest(); // Check that the transaction is there no more @@ -99,9 +98,9 @@ public void popEldestTransactionToRequest() throws Exception { public void transactionRequestedFreshness() throws Exception { // Add some Txs to the pool and see if the method pops the eldest one List eldest = new ArrayList(Arrays.asList( - TransactionViewModelTest.getRandomTransactionHash(), - TransactionViewModelTest.getRandomTransactionHash(), - TransactionViewModelTest.getRandomTransactionHash() + getRandomTransactionHash(), + getRandomTransactionHash(), + getRandomTransactionHash() )); TransactionRequester txReq = new TransactionRequester(tangle, snapshotProvider, mq); int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE; @@ -110,7 +109,7 @@ public void transactionRequestedFreshness() throws Exception { txReq.requestTransaction(eldest.get(i), false); } for (int i = 0; i < capacity; i++) { - Hash hash = TransactionViewModelTest.getRandomTransactionHash(); + Hash hash = getRandomTransactionHash(); txReq.requestTransaction(hash,false); } @@ -128,7 +127,7 @@ public void nonMilestoneCapacityLimited() throws Exception { int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE; //fill tips list for (int i = 0; i < capacity * 2 ; i++) { - Hash hash = TransactionViewModelTest.getRandomTransactionHash(); + Hash hash = getRandomTransactionHash(); txReq.requestTransaction(hash,false); } //check that limit wasn't breached @@ -141,7 +140,7 @@ public void milestoneCapacityNotLimited() throws Exception { int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE; //fill tips list for (int i = 0; i < capacity * 2 ; i++) { - Hash hash = TransactionViewModelTest.getRandomTransactionHash(); + Hash hash = getRandomTransactionHash(); txReq.requestTransaction(hash,true); } //check that limit was surpassed @@ -154,7 +153,7 @@ public void mixedCapacityLimited() throws Exception { int capacity = TransactionRequester.MAX_TX_REQ_QUEUE_SIZE; //fill tips list for (int i = 0; i < capacity * 4 ; i++) { - Hash hash = TransactionViewModelTest.getRandomTransactionHash(); + Hash hash = getRandomTransactionHash(); txReq.requestTransaction(hash, (i % 2 == 1)); } diff --git a/src/test/java/com/iota/iri/network/impl/TransactionRequesterWorkerImplTest.java b/src/test/java/com/iota/iri/network/impl/TransactionRequesterWorkerImplTest.java new file mode 100644 index 0000000000..91060e854b --- /dev/null +++ b/src/test/java/com/iota/iri/network/impl/TransactionRequesterWorkerImplTest.java @@ -0,0 +1,153 @@ +package com.iota.iri.network.impl; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import com.iota.iri.TangleMockUtils; +import com.iota.iri.controllers.TipsViewModel; +import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.model.Hash; +import com.iota.iri.model.persistables.Transaction; +import com.iota.iri.network.Node; +import com.iota.iri.network.TransactionRequester; +import com.iota.iri.network.impl.TransactionRequesterWorkerImpl; +import com.iota.iri.service.snapshot.SnapshotProvider; +import com.iota.iri.storage.Tangle; +import com.iota.iri.zmq.MessageQ; + +import static com.iota.iri.TransactionTestUtils.getRandomTransaction; +import static com.iota.iri.TransactionTestUtils.get9Transaction; +import static com.iota.iri.TransactionTestUtils.buildTransaction; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; + +import static org.mockito.Mockito.when; + +public class TransactionRequesterWorkerImplTest { + + //Good + private static final TransactionViewModel TVMRandomNull = new TransactionViewModel( + getRandomTransaction(), Hash.NULL_HASH); + private static final TransactionViewModel TVMRandomNotNull = new TransactionViewModel( + getRandomTransaction(), getRandomTransactionHash()); + private static final TransactionViewModel TVMAll9Null = new TransactionViewModel( + get9Transaction(), Hash.NULL_HASH); + private static final TransactionViewModel TVMAll9NotNull = new TransactionViewModel( + get9Transaction(), getRandomTransactionHash()); + + //Bad + private static final TransactionViewModel TVMNullNull = new TransactionViewModel((Transaction)null, Hash.NULL_HASH); + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private static SnapshotProvider snapshotProvider; + + @Mock + private static MessageQ messageQ; + + private static TransactionRequester requester; + private static TransactionRequesterWorkerImpl worker; + + @Mock + private Tangle tangle; + + @Mock + private Node node; + + @Mock + private TipsViewModel tipsVM; + + @Before + public void before() { + requester = new TransactionRequester(tangle, snapshotProvider, messageQ); + + worker = new TransactionRequesterWorkerImpl(); + worker.init(tangle, requester, tipsVM, node); + } + + @After + public void tearDown() { + worker.shutdown(); + } + + @Test + public void workerActive() throws Exception { + assertFalse("Empty worker should not be active", worker.isActive()); + + fillRequester(); + + assertTrue("Worker should be active when it requester is over threshold", worker.isActive()); + } + + @Test + public void processRequestQueueTest() throws Exception { + //getTransactionToSendWithRequest starts reading from solid tips, so mock data from that call + when(tipsVM.getRandomSolidTipHash()).thenReturn( + TVMRandomNull.getHash(), + TVMRandomNotNull.getHash(), + TVMAll9Null.getHash(), + TVMAll9NotNull.getHash(), + TVMNullNull.getHash(), + null); + + assertFalse("Unfilled queue shouldnt process", worker.processRequestQueue()); + + //Requester never goes down since nodes don't really request + fillRequester(); + + TangleMockUtils.mockTransaction(tangle, TVMRandomNull.getHash(), buildTransaction(TVMRandomNull.trits())); + assertTrue("Null transaction hash should be processed", worker.processRequestQueue()); + + TangleMockUtils.mockTransaction(tangle, TVMRandomNotNull.getHash(), buildTransaction(TVMRandomNotNull.trits())); + assertTrue("Not null transaction hash should be processed", worker.processRequestQueue()); + + TangleMockUtils.mockTransaction(tangle, TVMAll9Null.getHash(), buildTransaction(TVMAll9Null.trits())); + assertTrue("Null transaction hash should be processed", worker.processRequestQueue()); + + TangleMockUtils.mockTransaction(tangle, TVMAll9NotNull.getHash(), buildTransaction(TVMAll9NotNull.trits())); + assertTrue("All 9s transaction should be processed", worker.processRequestQueue()); + + // Null gets loaded as all 0, so type is 0 -> Filled + TangleMockUtils.mockTransaction(tangle, TVMNullNull.getHash(), null); + assertTrue("0 transaction should be processed", worker.processRequestQueue()); + + // null -> NULL_HASH -> gets loaded as all 0 -> filled + assertTrue("Null transaction should be processed", worker.processRequestQueue()); + } + + @Test + public void validTipToAddTest() throws Exception { + assertTrue("Null transaction hash should always be accepted", worker.isValidTransaction(TVMRandomNull)); + assertTrue("Not null transaction hash should always be accepted", worker.isValidTransaction(TVMRandomNotNull)); + assertTrue("Null transaction hash should always be accepted", worker.isValidTransaction(TVMAll9Null)); + assertTrue("All 9s transaction should be accepted", worker.isValidTransaction(TVMAll9NotNull)); + + // Null gets loaded as all 0, so type is 0 -> Filled + assertTrue("0 transaction should be accepted", worker.isValidTransaction(TVMNullNull)); + + assertFalse("Null transaction should not be accepted", worker.isValidTransaction(null)); + } + + private void fillRequester() throws Exception { + for (int i=0; i< TransactionRequesterWorkerImpl.REQUESTER_THREAD_ACTIVATION_THRESHOLD; i++) { + addRequest(); + } + } + + private void addRequest() throws Exception { + Hash randomHash = getRandomTransactionHash(); + TangleMockUtils.mockTransaction(tangle, randomHash); + requester.requestTransaction(randomHash, false); + } +} diff --git a/src/test/java/com/iota/iri/service/milestone/impl/MilestoneServiceImplTest.java b/src/test/java/com/iota/iri/service/milestone/impl/MilestoneServiceImplTest.java index 7367d8fba8..9d0f9e42a0 100644 --- a/src/test/java/com/iota/iri/service/milestone/impl/MilestoneServiceImplTest.java +++ b/src/test/java/com/iota/iri/service/milestone/impl/MilestoneServiceImplTest.java @@ -12,20 +12,23 @@ import org.junit.Assert; import org.junit.Before; import org.junit.FixMethodOrder; +import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import java.util.Optional; -@RunWith(MockitoJUnitRunner.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class MilestoneServiceImplTest { //region [CONSTANTS FOR THE TEST] ////////////////////////////////////////////////////////////////////////////////// + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + private enum MockedMilestone { A("ARWY9LWHXEWNL9DTN9IGMIMIVSBQUIEIDSFRYTCSXQARRTVEUFSBWFZRQOJUQNAGQLWHTFNVECELCOFYB", 70001), B("BRWY9LWHXEWNL9DTN9IGMIMIVSBQUIEIDSFRYTCSXQARRTVEUFSBWFZRQOJUQNAGQLWHTFNVECELCOFYB", 70002), diff --git a/src/test/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImplTest.java b/src/test/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImplTest.java index 9d98ca6fe9..898b3e639e 100644 --- a/src/test/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImplTest.java +++ b/src/test/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImplTest.java @@ -13,21 +13,24 @@ import org.junit.Assert; import org.junit.Before; import org.junit.FixMethodOrder; +import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import java.util.HashMap; import java.util.Map; -@RunWith(MockitoJUnitRunner.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SnapshotServiceImplTest { //region [CONSTANTS FOR THE TEST] ////////////////////////////////////////////////////////////////////////////////// + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + private enum MockedMilestone { A("ARWY9LWHXEWNL9DTN9IGMIMIVSBQUIEIDSFRYTCSXQARRTVEUFSBWFZRQOJUQNAGQLWHTFNVECELCOFYB", 70001, 1542146728L), B("BRWY9LWHXEWNL9DTN9IGMIMIVSBQUIEIDSFRYTCSXQARRTVEUFSBWFZRQOJUQNAGQLWHTFNVECELCOFYB", 70005, 1546146728L); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java index df4360ef66..a879013327 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java @@ -22,7 +22,7 @@ import java.util.*; -import static com.iota.iri.controllers.TransactionViewModelTest.*; +import static com.iota.iri.TransactionTestUtils.*; public class CumulativeWeightCalculatorTest { private static final TemporaryFolder dbFolder = new TemporaryFolder(); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImplTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImplTest.java index 75997424c8..46a84c8407 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImplTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/EntryPointSelectorImplTest.java @@ -14,21 +14,26 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.FixMethodOrder; +import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; -@RunWith(MockitoJUnitRunner.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class EntryPointSelectorImplTest { + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + @Mock private LatestMilestoneTracker latestMilestoneTracker; + @Mock private Tangle tangle; + private static SnapshotProvider snapshotProvider; @BeforeClass diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java index 9617a84e57..6820e64367 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java @@ -15,7 +15,9 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; -import static com.iota.iri.controllers.TransactionViewModelTest.*; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; public class RatingOneTest { private static final TemporaryFolder dbFolder = new TemporaryFolder(); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java index 52dc8b24f9..cbcce5dbde 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java @@ -1,9 +1,13 @@ package com.iota.iri.service.tipselection.impl; -import com.iota.iri.TransactionTestUtils; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; +import static com.iota.iri.TransactionTestUtils.createBundleHead; +import static com.iota.iri.TransactionTestUtils.createTransactionWithTrunkBundleHash; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; + import com.iota.iri.conf.MainnetConfig; import com.iota.iri.controllers.TransactionViewModel; -import com.iota.iri.controllers.TransactionViewModelTest; import com.iota.iri.model.Hash; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.impl.SnapshotProviderImpl; @@ -51,23 +55,23 @@ public static void setUp() throws Exception { @Test public void findTailTest() throws Exception { - TransactionViewModel txa = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionTrits(), TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel txa = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); txa.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx2 = TransactionTestUtils.createBundleHead(2); + TransactionViewModel tx2 = createBundleHead(2); tx2.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx1 = TransactionTestUtils.createTransactionWithTrunkBundleHash(tx2, txa.getHash()); + TransactionViewModel tx1 = createTransactionWithTrunkBundleHash(tx2, txa.getHash()); tx1.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx0 = TransactionTestUtils.createTransactionWithTrunkBundleHash(tx1, txa.getHash()); + TransactionViewModel tx0 = createTransactionWithTrunkBundleHash(tx1, txa.getHash()); tx0.store(tangle, snapshotProvider.getInitialSnapshot()); //negative index - make sure we stop at 0 - TransactionViewModel txNeg = TransactionTestUtils.createTransactionWithTrunkBundleHash(tx0, txa.getHash()); + TransactionViewModel txNeg = createTransactionWithTrunkBundleHash(tx0, txa.getHash()); txNeg.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel txLateTail = TransactionTestUtils.createTransactionWithTrunkBundleHash(tx1, txa.getHash()); + TransactionViewModel txLateTail = createTransactionWithTrunkBundleHash(tx1, txa.getHash()); txLateTail.store(tangle, snapshotProvider.getInitialSnapshot()); Optional tail = tailFinder.findTail(tx2.getHash()); @@ -78,19 +82,17 @@ public void findTailTest() throws Exception { @Test public void findMissingTailTest() throws Exception { - TransactionViewModel txa = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionTrits(), - TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel txa = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); txa.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx2 = TransactionTestUtils.createBundleHead(2); + TransactionViewModel tx2 = createBundleHead(2); tx2.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx1 = TransactionTestUtils.createTransactionWithTrunkBundleHash(tx2, txa.getHash()); + TransactionViewModel tx1 = createTransactionWithTrunkBundleHash(tx2, txa.getHash()); tx1.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx0 = new TransactionViewModel(TransactionViewModelTest - .getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx2.getHash()), - TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel tx0 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx2.getHash()), + getRandomTransactionHash()); tx0.store(tangle, snapshotProvider.getInitialSnapshot()); Optional tail = tailFinder.findTail(tx2.getHash()); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java index d4025acfb8..9dc291fb63 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java @@ -4,7 +4,6 @@ import com.iota.iri.conf.MainnetConfig; import com.iota.iri.conf.TipSelConfig; import com.iota.iri.controllers.TransactionViewModel; -import com.iota.iri.controllers.TransactionViewModelTest; import com.iota.iri.model.Hash; import com.iota.iri.service.ledger.LedgerService; import com.iota.iri.service.snapshot.SnapshotProvider; @@ -14,24 +13,31 @@ import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; import java.util.HashMap; import java.util.HashSet; -@RunWith(MockitoJUnitRunner.class) public class WalkValidatorImplTest { + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); private static final TemporaryFolder dbFolder = new TemporaryFolder(); private static final TemporaryFolder logFolder = new TemporaryFolder(); private static Tangle tangle; private static SnapshotProvider snapshotProvider; private TipSelConfig config = new MainnetConfig(); + @Mock private static LedgerService ledgerService; @@ -140,7 +146,7 @@ public void belowMaxDepthWithFreshMilestone() throws Exception { tx.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 92); Hash hash = tx.getHash(); for (int i = 0; i < 4 ; i++) { - tx = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(hash, hash), TransactionViewModelTest.getRandomTransactionHash()); + tx = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, hash), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx,0); TransactionTestUtils.setCurrentIndex(tx,0); tx.updateSolid(true); @@ -164,8 +170,8 @@ public void failBelowMaxDepthWithFreshMilestoneDueToLongChain() throws Exception tx.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 92); Hash hash = tx.getHash(); for (int i = 0; i < maxAnalyzedTxs ; i++) { - tx = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(hash, hash), - TransactionViewModelTest.getRandomTransactionHash()); + tx = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, hash), + getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx,0); TransactionTestUtils.setCurrentIndex(tx,0); hash = tx.getHash(); @@ -187,7 +193,7 @@ public void belowMaxDepthOnGenesis() throws Exception { final int maxAnalyzedTxs = config.getBelowMaxDepthTransactionLimit(); Hash hash = Hash.NULL_HASH; for (int i = 0; i < maxAnalyzedTxs - 2 ; i++) { - tx = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(hash, hash), TransactionViewModelTest.getRandomTransactionHash()); + tx = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, hash), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx,0); TransactionTestUtils.setCurrentIndex(tx,0); tx.updateSolid(true); @@ -210,8 +216,8 @@ public void failBelowMaxDepthOnGenesisDueToLongChain() throws Exception { TransactionViewModel tx = null; Hash hash = Hash.NULL_HASH; for (int i = 0; i < maxAnalyzedTxs; i++) { - tx = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(hash, hash), - TransactionViewModelTest.getRandomTransactionHash()); + tx = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, hash), + getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx,0); TransactionTestUtils.setCurrentIndex(tx,0); tx.updateSolid(true); @@ -255,22 +261,22 @@ public void dontMarkWrongTxsAsBelowMaxDepth() throws Exception { txBad.store(tangle, snapshotProvider.getInitialSnapshot()); txBad.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 10); - TransactionViewModel tx2 = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx1.getHash()), - TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel tx2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx1.getHash()), + getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx2,0); TransactionTestUtils.setCurrentIndex(tx2,0); tx2.updateSolid(true); tx2.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx3 = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(tx1.getHash(), txBad.getHash()), - TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel tx3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), txBad.getHash()), + getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx3,0); TransactionTestUtils.setCurrentIndex(tx3,0); tx3.updateSolid(true); tx3.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx4 = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(tx2.getHash(), tx3.getHash()), - TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel tx4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx2.getHash(), tx3.getHash()), + getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx4,0); TransactionTestUtils.setCurrentIndex(tx4,0); tx4.updateSolid(true); @@ -301,22 +307,22 @@ public void allowConfirmedTxToPassBelowMaxDepthAfterMilestoneConfirmation() thro txBad.store(tangle, snapshotProvider.getInitialSnapshot()); txBad.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 10); - TransactionViewModel tx2 = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx1.getHash()), - TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel tx2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx1.getHash()), + getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx2,0); TransactionTestUtils.setCurrentIndex(tx2,0); tx2.updateSolid(true); tx2.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx3 = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(tx1.getHash(), txBad.getHash()), - TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel tx3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), txBad.getHash()), + getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx3,0); TransactionTestUtils.setCurrentIndex(tx3,0); tx3.updateSolid(true); tx3.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx4 = new TransactionViewModel(TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch(tx2.getHash(), tx3.getHash()), - TransactionViewModelTest.getRandomTransactionHash()); + TransactionViewModel tx4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx2.getHash(), tx3.getHash()), + getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx4,0); TransactionTestUtils.setCurrentIndex(tx4,0); tx4.updateSolid(true); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java index 0ebbc6184c..f45e53a474 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java @@ -27,9 +27,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.iota.iri.controllers.TransactionViewModelTest.getRandomTransactionHash; -import static com.iota.iri.controllers.TransactionViewModelTest.getRandomTransactionTrits; -import static com.iota.iri.controllers.TransactionViewModelTest.getRandomTransactionWithTrunkAndBranch; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; +import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; public class WalkerAlphaTest { private static final TemporaryFolder dbFolder = new TemporaryFolder(); @@ -165,7 +165,7 @@ public void showWalkDistributionAlphaZero() throws Exception { //calculate rating RatingCalculator ratingCalculator = new RatingOne(tangle); - UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); + UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); //set a higher rate for transaction2 rating.put(transaction2.getHash(), 10); @@ -214,7 +214,7 @@ public void testWalk() throws Exception { //calculate rating RatingCalculator ratingCalculator = new RatingOne(tangle); - UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); + UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); //reach the tips Hash tip = walker.walk(transaction.getHash(), rating, (o -> true)); @@ -241,7 +241,7 @@ public void testWalkDiamond() throws Exception { //calculate rating RatingCalculator ratingCalculator = new RatingOne(tangle); - UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); + UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); //reach the tips Hash tip = walker.walk(transaction.getHash(), rating, (o -> true)); @@ -271,7 +271,7 @@ public void testWalkChain() throws Exception { //calculate rating RatingCalculator ratingCalculator = new RatingOne(tangle); - UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); + UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); //reach the tips Hash tip = walker.walk(transaction.getHash(), rating, (o -> true)); diff --git a/src/test/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProviderTest.java b/src/test/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProviderTest.java index f9f5e7b0ff..b0f9b49b32 100644 --- a/src/test/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProviderTest.java +++ b/src/test/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProviderTest.java @@ -42,7 +42,6 @@ public void setUp() throws Exception { rocksDBPersistenceProvider.clear(Transaction.class); } - @SuppressWarnings("unchecked") @Test public void testDeleteBatch() throws Exception { Persistable tx = new Transaction(); From 8b50ed988442b705348917961b81034441007659 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Wed, 6 Feb 2019 16:09:41 +0200 Subject: [PATCH 22/34] Persist spent addresses in ledger service --- src/main/java/com/iota/iri/Iota.java | 2 +- .../iri/service/ledger/LedgerService.java | 3 ++- .../ledger/impl/LedgerServiceImpl.java | 21 ++++++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index f62fe864a6..a08e4806a5 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -212,7 +212,7 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx latestMilestoneTracker, messageQ); seenMilestonesRetriever.init(tangle, snapshotProvider, transactionRequester); milestoneSolidifier.init(snapshotProvider, transactionValidator); - ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService); + ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesProvider); if (transactionPruner != null) { transactionPruner.init(tangle, snapshotProvider, spentAddressesService, tipsViewModel, configuration) .restoreState(); diff --git a/src/main/java/com/iota/iri/service/ledger/LedgerService.java b/src/main/java/com/iota/iri/service/ledger/LedgerService.java index ee7a936aaa..830d35d04d 100644 --- a/src/main/java/com/iota/iri/service/ledger/LedgerService.java +++ b/src/main/java/com/iota/iri/service/ledger/LedgerService.java @@ -75,7 +75,8 @@ public interface LedgerService { /** * Generates the accumulated balance changes of the transactions that are directly or indirectly referenced by the - * given transaction relative to the referenced milestone.
+ * given transaction relative to the referenced milestone. Also persists the spent addresses that were found in the + * process
*
* It simply iterates over all approvees that have not been confirmed yet and that have not been processed already * (by being part of the {@code visitedNonMilestoneSubtangleHashes} set) and collects their balance changes.
diff --git a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java index e2b95962e6..29139e0af7 100644 --- a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java +++ b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java @@ -12,9 +12,13 @@ import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.SnapshotService; import com.iota.iri.service.snapshot.impl.SnapshotStateDiffImpl; +import com.iota.iri.service.spentaddresses.SpentAddressesException; +import com.iota.iri.service.spentaddresses.SpentAddressesProvider; import com.iota.iri.storage.Tangle; import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Creates a service instance that allows us to perform ledger state specific operations.
@@ -42,6 +46,8 @@ public class LedgerServiceImpl implements LedgerService { */ private MilestoneService milestoneService; + private SpentAddressesProvider spentAddressesProvider; + /** * Initializes the instance and registers its dependencies.
*
@@ -61,12 +67,13 @@ public class LedgerServiceImpl implements LedgerService { * @return the initialized instance itself to allow chaining */ public LedgerServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotService snapshotService, - MilestoneService milestoneService) { + MilestoneService milestoneService, SpentAddressesProvider spentAddressesProvider) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.snapshotService = snapshotService; this.milestoneService = milestoneService; + this.spentAddressesProvider = spentAddressesProvider; return this; } @@ -178,6 +185,10 @@ public Map generateBalanceDiff(Set visitedTransactions, Hash s for (final List bundleTransactionViewModels : bundleTransactions) { + //ISSUE 1008: generateBalanceDiff should be refactored so we don't have those hidden + // concerns + persistValidatedSpentAddresses(bundleTransactionViewModels); + if (BundleValidator.isInconsistent(bundleTransactionViewModels)) { break; } @@ -217,6 +228,14 @@ public Map generateBalanceDiff(Set visitedTransactions, Hash s return state; } + private void persistValidatedSpentAddresses(List bundleTransactionViewModels) throws SpentAddressesException { + List spentAddresses = bundleTransactionViewModels.stream() + .filter(tx -> tx.value() < 0) + .map(TransactionViewModel::getAddressHash) + .collect(Collectors.toList()); + spentAddressesProvider.saveAddressesBatch(spentAddresses); + } + /** * Generates the {@link com.iota.iri.model.StateDiff} that belongs to the given milestone in the database and marks * all transactions that have been approved by the milestone accordingly by setting their {@code snapshotIndex} From faa01934606eb9990d58b66a49d44a2f6714d52b Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Sun, 10 Feb 2019 13:01:12 +0200 Subject: [PATCH 23/34] Make LedgerServiceImpl more debuggable --- .../com/iota/iri/service/ledger/impl/LedgerServiceImpl.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java index 29139e0af7..cdd2031d0b 100644 --- a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java +++ b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java @@ -8,6 +8,7 @@ import com.iota.iri.service.ledger.LedgerException; import com.iota.iri.service.ledger.LedgerService; import com.iota.iri.service.milestone.MilestoneService; +import com.iota.iri.service.snapshot.Snapshot; import com.iota.iri.service.snapshot.SnapshotException; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.SnapshotService; @@ -17,7 +18,6 @@ import com.iota.iri.storage.Tangle; import java.util.*; -import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -160,7 +160,9 @@ public Map generateBalanceDiff(Set visitedTransactions, Hash s Map state = new HashMap<>(); Set countedTx = new HashSet<>(); - snapshotProvider.getInitialSnapshot().getSolidEntryPoints().keySet().forEach(solidEntryPointHash -> { + Snapshot initialSnapshot = snapshotProvider.getInitialSnapshot(); + Map solidEntryPoints = initialSnapshot.getSolidEntryPoints(); + solidEntryPoints.keySet().forEach(solidEntryPointHash -> { visitedTransactions.add(solidEntryPointHash); countedTx.add(solidEntryPointHash); }); From 3a2be33be151b1db728f7d7d82acee5a6d27796b Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Wed, 6 Feb 2019 16:56:05 +0200 Subject: [PATCH 24/34] Don't calculate spent addresses while taking a local snapshot --- .../snapshot/impl/SnapshotServiceImpl.java | 8 --- .../spentaddresses/SpentAddressesService.java | 8 --- .../impl/SpentAddressesServiceImpl.java | 67 ------------------- 3 files changed, 83 deletions(-) diff --git a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java index f34d24ca37..83e077e23a 100644 --- a/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java +++ b/src/main/java/com/iota/iri/service/snapshot/impl/SnapshotServiceImpl.java @@ -496,14 +496,6 @@ private void cleanupOldData(SnapshotConfig config, TransactionPruner transaction private void persistLocalSnapshot(SnapshotProvider snapshotProvider, Snapshot newSnapshot, SnapshotConfig config) throws SnapshotException { - try { - spentAddressesService.persistSpentAddresses(snapshotProvider.getInitialSnapshot().getIndex(), - newSnapshot.getIndex()); - - } catch (Exception e) { - throw new SnapshotException(e); - } - snapshotProvider.writeSnapshotToDisk(newSnapshot, config.getLocalSnapshotsBasePath()); snapshotProvider.getLatestSnapshot().lockWrite(); diff --git a/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java b/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java index 828b772717..856e20ff28 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java @@ -21,14 +21,6 @@ public interface SpentAddressesService { */ boolean wasAddressSpentFrom(Hash addressHash) throws SpentAddressesException; - /** - * Calculates and persists all spent addresses in between a range that were validly signed - * - * @param fromMilestoneIndex the lower bound milestone index (inclusive) - * @param toMilestoneIndex the upper bound milestone index (exclusive) - * @throws Exception when anything went wrong whilst calculating. - */ - void persistSpentAddresses(int fromMilestoneIndex, int toMilestoneIndex) throws Exception; /** * Persist all the verifiable spent from a given list of transactions diff --git a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java index 136bc566fb..59c85fe797 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java @@ -35,12 +35,6 @@ * */ public class SpentAddressesServiceImpl implements SpentAddressesService { - private static final Logger log = LoggerFactory.getLogger(SpentAddressesServiceImpl.class); - - /** - * Value for batch processing that was determined by testing - */ - private static final int BATCH_INTERVAL = 2500; private Tangle tangle; @@ -78,15 +72,6 @@ public boolean wasAddressSpentFrom(Hash addressHash) throws SpentAddressesExcept return wasAddressSpentFrom(addressHash, getInitialUnspentAddresses()); } - @Override - public void persistSpentAddresses(int fromMilestoneIndex, int toMilestoneIndex) throws SpentAddressesException { - try{ - processInBatches(fromMilestoneIndex, toMilestoneIndex, new HashSet<>(), getInitialUnspentAddresses()); - } catch(Exception e){ - throw new SpentAddressesException(e); - } - } - @Override public void persistSpentAddresses(Collection transactions) throws SpentAddressesException { try { @@ -174,58 +159,6 @@ private boolean wasAddressSpentFrom(Hash addressHash, Collection checkedAd return false; } - //Processing in batches in order to avoid OOM errors - private void processInBatches(int fromMilestoneIndex, int toMilestoneIndex, Collection addressesToCheck, - Set checkedAddresses) throws SpentAddressesException { - try { - int interval = BATCH_INTERVAL; - double numBatches = Math.ceil(((double) toMilestoneIndex - fromMilestoneIndex) / interval); - - for (int batch = 0; batch < numBatches; batch++) { - int batchStart = batch * interval + fromMilestoneIndex; - int batchStop = batchStart + interval <= toMilestoneIndex ? batchStart + interval : toMilestoneIndex; - - for (int i = batchStart; i < batchStop; i++) { - try { - MilestoneViewModel currentMilestone = MilestoneViewModel.get(tangle, i); - if (currentMilestone != null) { - DAGHelper.get(tangle).traverseApprovees( - currentMilestone.getHash(), - transactionViewModel -> transactionViewModel.snapshotIndex() >= currentMilestone.index(), - transactionViewModel -> addressesToCheck.add(transactionViewModel.getAddressHash()) - ); - } - } catch (Exception e) { - throw new SpentAddressesException(e); - } - } - checkAddresses(addressesToCheck, checkedAddresses); - } - }catch(SpentAddressesException e) { - throw e; - } - } - - private void checkAddresses(Collection addressesToCheck, Collection checkedAddresses) - throws SpentAddressesException { - //Can only throw runtime exceptions in streams - try { - spentAddressesProvider.saveAddressesBatch(addressesToCheck.stream() - .filter(ThrowingPredicate.unchecked(address -> wasAddressSpentFrom(address, checkedAddresses))) - .collect(Collectors.toList())); - - //Clear addressesToCheck for next batch - addressesToCheck.clear(); - } catch (RuntimeException e) { - if (e.getCause() instanceof SpentAddressesException) { - throw (SpentAddressesException) e.getCause(); - } else { - throw e; - } - } - - } - private Set getInitialUnspentAddresses() { return Stream.of(Hash.NULL_HASH, HashFactory.ADDRESS.create(config.getCoordinator())) .collect(Collectors.toSet()); From 1aa8c44d0c83760ecf258b0b33abeaf61a845c31 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Thu, 7 Feb 2019 11:45:05 +0200 Subject: [PATCH 25/34] Make BundleValidator testable --- src/main/java/com/iota/iri/BundleValidator.java | 6 +++--- src/main/java/com/iota/iri/Iota.java | 10 +++++++--- src/main/java/com/iota/iri/service/API.java | 2 +- .../iri/service/ledger/impl/LedgerServiceImpl.java | 8 ++++++-- .../service/milestone/impl/MilestoneServiceImpl.java | 7 +++++-- .../spentaddresses/impl/SpentAddressesServiceImpl.java | 8 ++++++-- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/iota/iri/BundleValidator.java b/src/main/java/com/iota/iri/BundleValidator.java index 43e3ffb4c7..46efbb3d80 100644 --- a/src/main/java/com/iota/iri/BundleValidator.java +++ b/src/main/java/com/iota/iri/BundleValidator.java @@ -55,7 +55,7 @@ public class BundleValidator { * If the bundle is invalid then an empty list will be returned. * @throws Exception if a persistence error occured */ - public static List> validate(Tangle tangle, Snapshot initialSnapshot, Hash tailHash) throws Exception { + public List> validate(Tangle tangle, Snapshot initialSnapshot, Hash tailHash) throws Exception { TransactionViewModel tail = TransactionViewModel.fromHash(tangle, tailHash); if (tail.getCurrentIndex() != 0 || tail.getValidity() == -1) { @@ -194,10 +194,10 @@ public static List> validate(Tangle tangle, Snapshot /** * Checks that the bundle's inputs and outputs are balanced. * - * @param transactionViewModels list of transactions that are in a bundle + * @param transactionViewModels collection of transactions that are in a bundle * @return {@code true} if balanced, {@code false} if unbalanced or {@code transactionViewModels} is empty */ - public static boolean isInconsistent(List transactionViewModels) { + public static boolean isInconsistent(Collection transactionViewModels) { long value = 0; for (final TransactionViewModel bundleTransactionViewModel : transactionViewModels) { if (bundleTransactionViewModel.value() != 0) { diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index a08e4806a5..1ab8a5d8d9 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -95,6 +95,8 @@ public class Iota { public final TransactionRequesterWorkerImpl transactionRequesterWorker; + public final BundleValidator bundleValidator; + public final Tangle tangle; public final TransactionValidator transactionValidator; public final TipsSolidifier tipsSolidifier; @@ -137,6 +139,7 @@ public Iota(IotaConfig configuration) throws TransactionPruningException, Snapsh transactionRequesterWorker = new TransactionRequesterWorkerImpl(); // legacy code + bundleValidator = new BundleValidator(); tangle = new Tangle(); messageQ = MessageQ.createWith(configuration); tipsViewModel = new TipsViewModel(); @@ -200,19 +203,20 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx //because we check whether spent addresses data exists snapshotProvider.init(configuration); spentAddressesProvider.init(configuration); - spentAddressesService.init(tangle, snapshotProvider, spentAddressesProvider, configuration); + spentAddressesService.init(tangle, snapshotProvider, spentAddressesProvider, bundleValidator, configuration); snapshotService.init(tangle, snapshotProvider, spentAddressesService, spentAddressesProvider, configuration); if (localSnapshotManager != null) { localSnapshotManager.init(snapshotProvider, snapshotService, transactionPruner, configuration); } - milestoneService.init(tangle, snapshotProvider, snapshotService, messageQ, configuration); + milestoneService.init(tangle, snapshotProvider, snapshotService, bundleValidator, messageQ, configuration); latestMilestoneTracker.init(tangle, snapshotProvider, milestoneService, milestoneSolidifier, messageQ, configuration); latestSolidMilestoneTracker.init(tangle, snapshotProvider, milestoneService, ledgerService, latestMilestoneTracker, messageQ); seenMilestonesRetriever.init(tangle, snapshotProvider, transactionRequester); milestoneSolidifier.init(snapshotProvider, transactionValidator); - ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesProvider); + ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesProvider, + bundleValidator); if (transactionPruner != null) { transactionPruner.init(tangle, snapshotProvider, spentAddressesService, tipsViewModel, configuration) .restoreState(); diff --git a/src/main/java/com/iota/iri/service/API.java b/src/main/java/com/iota/iri/service/API.java index 14b1fd3ed3..fe031d928e 100644 --- a/src/main/java/com/iota/iri/service/API.java +++ b/src/main/java/com/iota/iri/service/API.java @@ -586,7 +586,7 @@ private AbstractResponse checkConsistencyStatement(List transactionsList state = false; info = "tails are not solid (missing a referenced tx): " + transaction; break; - } else if (BundleValidator.validate(instance.tangle, instance.snapshotProvider.getInitialSnapshot(), txVM.getHash()).size() == 0) { + } else if (instance.bundleValidator.validate(instance.tangle, instance.snapshotProvider.getInitialSnapshot(), txVM.getHash()).size() == 0) { state = false; info = "tails are not consistent (bundle is invalid): " + transaction; break; diff --git a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java index cdd2031d0b..46ee3b3add 100644 --- a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java +++ b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java @@ -48,6 +48,8 @@ public class LedgerServiceImpl implements LedgerService { private SpentAddressesProvider spentAddressesProvider; + private BundleValidator bundleValidator; + /** * Initializes the instance and registers its dependencies.
*
@@ -67,13 +69,15 @@ public class LedgerServiceImpl implements LedgerService { * @return the initialized instance itself to allow chaining */ public LedgerServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotService snapshotService, - MilestoneService milestoneService, SpentAddressesProvider spentAddressesProvider) { + MilestoneService milestoneService, SpentAddressesProvider spentAddressesProvider, + BundleValidator bundleValidator) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.snapshotService = snapshotService; this.milestoneService = milestoneService; this.spentAddressesProvider = spentAddressesProvider; + this.bundleValidator = bundleValidator; return this; } @@ -182,7 +186,7 @@ public Map generateBalanceDiff(Set visitedTransactions, Hash s if (transactionViewModel.getCurrentIndex() == 0) { boolean validBundle = false; - final List> bundleTransactions = BundleValidator.validate( + final List> bundleTransactions = bundleValidator.validate( tangle, snapshotProvider.getInitialSnapshot(), transactionViewModel.getHash()); for (final List bundleTransactionViewModels : bundleTransactions) { diff --git a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java index 562c548b0a..5a88ff8c50 100644 --- a/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java +++ b/src/main/java/com/iota/iri/service/milestone/impl/MilestoneServiceImpl.java @@ -69,6 +69,8 @@ public class MilestoneServiceImpl implements MilestoneService { */ private ConsensusConfig config; + private BundleValidator bundleValidator; + /** * This method initializes the instance and registers its dependencies.
*
@@ -88,11 +90,12 @@ public class MilestoneServiceImpl implements MilestoneService { * @return the initialized instance itself to allow chaining */ public MilestoneServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotService snapshotService, - MessageQ messageQ, ConsensusConfig config) { + BundleValidator bundleValidator, MessageQ messageQ, ConsensusConfig config) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.snapshotService = snapshotService; + this.bundleValidator = bundleValidator; this.messageQ = messageQ; this.config = config; @@ -173,7 +176,7 @@ public MilestoneValidity validateMilestone(TransactionViewModel transactionViewM return VALID; } - final List> bundleTransactions = BundleValidator.validate(tangle, + final List> bundleTransactions = bundleValidator.validate(tangle, snapshotProvider.getInitialSnapshot(), transactionViewModel.getHash()); if (bundleTransactions.isEmpty()) { diff --git a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java index 59c85fe797..1b3a0ec89a 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java @@ -46,6 +46,8 @@ public class SpentAddressesServiceImpl implements SpentAddressesService { private MilestoneConfig config; + private BundleValidator bundleValidator; + /** * Creates a Spent address service using the Tangle @@ -55,11 +57,13 @@ public class SpentAddressesServiceImpl implements SpentAddressesService { * @param spentAddressesProvider Provider for loading/saving addresses to a database. * @return this instance */ - public SpentAddressesServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SpentAddressesProvider spentAddressesProvider, + public SpentAddressesServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, + SpentAddressesProvider spentAddressesProvider, BundleValidator bundleValidator, MilestoneConfig config) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.spentAddressesProvider = spentAddressesProvider; + this.bundleValidator = bundleValidator; this.tailFinder = new TailFinderImpl(tangle); this.config = config; @@ -104,7 +108,7 @@ private boolean wasTransactionSpentFrom(TransactionViewModel tx) throws Exceptio private boolean isBundleValid(Hash tailHash) throws Exception { List> validation = - BundleValidator.validate(tangle, snapshotProvider.getInitialSnapshot(), tailHash); + bundleValidator.validate(tangle, snapshotProvider.getInitialSnapshot(), tailHash); return (CollectionUtils.isNotEmpty(validation) && validation.get(0).get(0).getValidity() == 1); } From 1adf36dcf1979b99887a06c1bcd1e84f406e965d Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Thu, 7 Feb 2019 16:49:26 +0200 Subject: [PATCH 26/34] Test persisting of spent addresses while applying a milestone --- .../java/com/iota/iri/TangleMockUtils.java | 66 +++++++++++++- .../com/iota/iri/TransactionTestUtils.java | 48 ++++++---- .../iota/iri/TransactionValidatorTest.java | 4 +- .../ledger/impl/LedgerServiceImplTest.java | 88 +++++++++++++++++++ .../impl/SpentAddressesProviderImplTest.java | 15 ++++ 5 files changed, 203 insertions(+), 18 deletions(-) create mode 100644 src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java create mode 100644 src/test/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImplTest.java diff --git a/src/test/java/com/iota/iri/TangleMockUtils.java b/src/test/java/com/iota/iri/TangleMockUtils.java index b1a6409370..1bee23d7fb 100644 --- a/src/test/java/com/iota/iri/TangleMockUtils.java +++ b/src/test/java/com/iota/iri/TangleMockUtils.java @@ -7,10 +7,14 @@ import com.iota.iri.model.persistables.Milestone; import com.iota.iri.model.persistables.Transaction; import com.iota.iri.storage.Tangle; +import com.iota.iri.utils.Converter; +import com.iota.iri.utils.IotaUtils; import com.iota.iri.utils.Pair; + import org.mockito.Mockito; -import java.util.Map; +import java.lang.reflect.Field; +import java.util.*; /** * Contains utilities that help to mock the retrieval of database entries from the tangle.
@@ -53,6 +57,51 @@ public static Milestone mockMilestone(Tangle tangle, Hash hash, int index) { return milestone; } + public static List mockValidBundle(Tangle tangle, + BundleValidator bundleValidator, + int numOfNonValueTxs, + String... spendsInTrytes) throws Exception { + List bundle = new ArrayList<>(); + String address = "ADDRESS99"; + TransactionViewModel tx = null; + int lastIndex = spendsInTrytes.length + numOfNonValueTxs - 1; + int currentIndex = lastIndex; + for (int i = 0; i < spendsInTrytes.length; i++) { + byte[] trits = new byte[TransactionViewModel.TRINARY_SIZE]; + Converter.trits(spendsInTrytes[i], trits, TransactionViewModel.VALUE_TRINARY_OFFSET); + address = TransactionTestUtils.nextWord(address); + Converter.trits(address, trits, TransactionViewModel.ADDRESS_TRINARY_OFFSET); + if (tx != null) { + TransactionTestUtils.getTransactionTritsWithTrunkAndBranch(trits, tx.getHash(), Hash.NULL_HASH); + } + TransactionTestUtils.setLastIndex(trits, lastIndex); + TransactionTestUtils.setCurrentIndex(trits, currentIndex--); + tx = TransactionTestUtils.createTransactionFromTrits(trits); + tx.setMetadata(); + mockTransaction(tangle, tx); + bundle.add(tx); + } + + for (int i = 0; i < numOfNonValueTxs; i++) { + if (tx == null) { + tx = TransactionTestUtils.createBundleHead(currentIndex--); + } + else { + tx = TransactionTestUtils.createTransactionWithTrunkBundleHash(tx, Hash.NULL_HASH); + } + tx.setMetadata(); + mockTransaction(tangle, tx); + bundle.add(tx); + } + + Collections.reverse(bundle); + Mockito.when(bundleValidator.validate(Mockito.eq(tangle), Mockito.any(), + Mockito.eq(bundle.iterator().next().getHash()))) + .thenReturn(Arrays.asList(bundle)); + + return bundle; + } + //endregion //////////////////////////////////////////////////////////////////////////////////////////////////////// //region [mockTransaction] ///////////////////////////////////////////////////////////////////////////////////////// @@ -93,6 +142,21 @@ public static Transaction mockTransaction(Tangle tangle, Hash hash, Transaction return transaction; } + /** + * Mocks the tangle object by checking for the hash and returning the transaction. + * + * @param tangle mocked tangle object that shall retrieve a milestone object when being queried for it + * @param tvm the transaction we mock in the tangle + * @return The transaction + */ + public static Transaction mockTransaction(Tangle tangle, TransactionViewModel tvm) throws NoSuchFieldException, IllegalAccessException { + Transaction transaction; + Field transactionField = tvm.getClass().getDeclaredField("transaction"); + transactionField.setAccessible(true); + transaction = (Transaction) transactionField.get(tvm); + return mockTransaction(tangle, tvm.getHash(), transaction); + } + //endregion //////////////////////////////////////////////////////////////////////////////////////////////////////// //region [mockStateDiff] /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/java/com/iota/iri/TransactionTestUtils.java b/src/test/java/com/iota/iri/TransactionTestUtils.java index f8b996d3b8..c9200f4531 100644 --- a/src/test/java/com/iota/iri/TransactionTestUtils.java +++ b/src/test/java/com/iota/iri/TransactionTestUtils.java @@ -15,7 +15,7 @@ public class TransactionTestUtils { - private static Random seed = new Random(); + private static Random seed = new Random(1); /** * Updates the transaction index in trits. @@ -24,7 +24,11 @@ public class TransactionTestUtils { * @param currentIndex The new index to set the transaction to */ public static void setCurrentIndex(TransactionViewModel tx, long currentIndex) { - Converter.copyTrits(currentIndex, tx.trits(), TransactionViewModel.CURRENT_INDEX_TRINARY_OFFSET, + setCurrentIndex(tx.trits(), currentIndex); + } + + public static void setCurrentIndex(byte[] trits, long currentIndex) { + Converter.copyTrits(currentIndex, trits, TransactionViewModel.CURRENT_INDEX_TRINARY_OFFSET, TransactionViewModel.CURRENT_INDEX_TRINARY_SIZE); } @@ -35,7 +39,11 @@ public static void setCurrentIndex(TransactionViewModel tx, long currentIndex) { * @param lastIndex The new last index to set the transaction to */ public static void setLastIndex(TransactionViewModel tx, long lastIndex) { - Converter.copyTrits(lastIndex, tx.trits(), TransactionViewModel.LAST_INDEX_TRINARY_OFFSET, + setLastIndex(tx.trits(), lastIndex); + } + + public static void setLastIndex(byte[] trits, long lastIndex) { + Converter.copyTrits(lastIndex, trits, TransactionViewModel.LAST_INDEX_TRINARY_OFFSET, TransactionViewModel.LAST_INDEX_TRINARY_SIZE); } @@ -63,13 +71,15 @@ public static TransactionViewModel createBundleHead(int index) { * @return A transaction in the same bundle as trunk, with its index 1 below trunk index */ public static TransactionViewModel createTransactionWithTrunkBundleHash(TransactionViewModel trunkTx, Hash branchHash) { + byte[] txTrits = getTransactionWithTrunkAndBranch(trunkTx.getHash(), branchHash); + setCurrentIndex(txTrits, trunkTx.getCurrentIndex() - 1); + setLastIndex(txTrits, trunkTx.lastIndex()); + System.arraycopy(trunkTx.trits(), TransactionViewModel.BUNDLE_TRINARY_OFFSET, txTrits, + TransactionViewModel.BUNDLE_TRINARY_OFFSET, TransactionViewModel.BUNDLE_TRINARY_SIZE); TransactionViewModel tx = new TransactionViewModel( - getRandomTransactionWithTrunkAndBranch(trunkTx.getHash(), branchHash), + txTrits, getRandomTransactionHash()); - setCurrentIndex(tx, trunkTx.getCurrentIndex() - 1); - setLastIndex(tx, trunkTx.lastIndex()); - System.arraycopy(trunkTx.trits(), TransactionViewModel.BUNDLE_TRINARY_OFFSET, tx.trits(), - TransactionViewModel.BUNDLE_TRINARY_OFFSET, TransactionViewModel.BUNDLE_TRINARY_SIZE); + return tx; } @@ -84,6 +94,10 @@ public static TransactionViewModel createTransactionWithTrunkBundleHash(Transact public static TransactionViewModel createTransactionWithTrytes(String trytes) { String expandedTrytes = expandTrytes(trytes); byte[] trits = Converter.allocatingTritsFromTrytes(expandedTrytes); + return createTransactionFromTrits(trits); + } + + public static TransactionViewModel createTransactionFromTrits(byte[] trits) { return new TransactionViewModel(trits, TransactionHash.calculate(SpongeFactory.Mode.CURLP81, trits)); } @@ -114,7 +128,7 @@ public static TransactionViewModel createTransactionWithTrunkAndBranch(String tr public static byte[] createTransactionWithTrunkAndBranchTrits(String trytes, Hash trunk, Hash branch) { String expandedTrytes = expandTrytes(trytes); byte[] trits = Converter.allocatingTritsFromTrytes(expandedTrytes); - return getRandomTransactionTritsWithTrunkAndBranch(trits, trunk, branch); + return getTransactionTritsWithTrunkAndBranch(trits, trunk, branch); } /** @@ -124,9 +138,9 @@ public static byte[] createTransactionWithTrunkAndBranchTrits(String trytes, Has * @param branch The branch transaction hash * @return The transaction trits */ - public static byte[] getRandomTransactionWithTrunkAndBranch(Hash trunk, Hash branch) { - byte[] trits = getRandomTransactionTrits(); - return getRandomTransactionTritsWithTrunkAndBranch(trits, trunk, branch); + public static byte[] getTransactionWithTrunkAndBranch(Hash trunk, Hash branch) { + byte[] trits = new byte[TransactionViewModel.TRINARY_SIZE]; + return getTransactionTritsWithTrunkAndBranch(trits, trunk, branch); } /** @@ -136,7 +150,7 @@ public static byte[] getRandomTransactionWithTrunkAndBranch(Hash trunk, Hash bra * @param branch The branch transaction hash * @return trits The transaction trits */ - public static byte[] getRandomTransactionTritsWithTrunkAndBranch(byte[] trits, Hash trunk, Hash branch) { + public static byte[] getTransactionTritsWithTrunkAndBranch(byte[] trits, Hash trunk, Hash branch) { System.arraycopy(trunk.trits(), 0, trits, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_OFFSET, TransactionViewModel.TRUNK_TRANSACTION_TRINARY_SIZE); System.arraycopy(branch.trits(), 0, trits, TransactionViewModel.BRANCH_TRANSACTION_TRINARY_OFFSET, @@ -146,7 +160,8 @@ public static byte[] getRandomTransactionTritsWithTrunkAndBranch(byte[] trits, H /** * Increases a char with the next char in the alphabet, until the char is Z. - * When the char is Z, adds a new char starting at A (So no 9 is used). + * When the char is Z, adds a new char starting at A. + * 9 turns To A. * * @param trytes The Trytes to change. * @return The changed trytes @@ -157,7 +172,10 @@ public static String nextWord(String trytes) { } char[] chars = trytes.toUpperCase().toCharArray(); for (int i = chars.length -1; i>=0; --i) { - if (chars[i] != 'Z') { + if (chars[i] == '9') { + chars[i] = 'A'; + } + else if (chars[i] != 'Z') { ++chars[i]; return new String(chars); } diff --git a/src/test/java/com/iota/iri/TransactionValidatorTest.java b/src/test/java/com/iota/iri/TransactionValidatorTest.java index 82f73b562f..f70c29d516 100644 --- a/src/test/java/com/iota/iri/TransactionValidatorTest.java +++ b/src/test/java/com/iota/iri/TransactionValidatorTest.java @@ -173,7 +173,7 @@ public void testTransactionPropagationFailure() throws Exception { rightChildLeaf.updateSolid(true); rightChildLeaf.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel parent = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(leftChildLeaf.getHash(), + TransactionViewModel parent = new TransactionViewModel(getTransactionWithTrunkAndBranch(leftChildLeaf.getHash(), rightChildLeaf.getHash()), getRandomTransactionHash()); parent.updateSolid(false); parent.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -182,7 +182,7 @@ public void testTransactionPropagationFailure() throws Exception { parentSibling.updateSolid(false); parentSibling.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel grandParent = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(parent.getHash(), + TransactionViewModel grandParent = new TransactionViewModel(getTransactionWithTrunkAndBranch(parent.getHash(), parentSibling.getHash()), getRandomTransactionHash()); grandParent.updateSolid(false); grandParent.store(tangle, snapshotProvider.getInitialSnapshot()); diff --git a/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java b/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java new file mode 100644 index 0000000000..7b6a5471ca --- /dev/null +++ b/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java @@ -0,0 +1,88 @@ +package com.iota.iri.service.ledger.impl; + +import com.iota.iri.BundleValidator; +import com.iota.iri.TangleMockUtils; +import com.iota.iri.TransactionTestUtils; +import com.iota.iri.controllers.TransactionViewModel; +import com.iota.iri.model.Hash; +import com.iota.iri.service.ledger.LedgerException; +import com.iota.iri.service.milestone.MilestoneService; +import com.iota.iri.service.snapshot.Snapshot; +import com.iota.iri.service.snapshot.SnapshotProvider; +import com.iota.iri.service.snapshot.SnapshotService; +import com.iota.iri.service.spentaddresses.SpentAddressesException; +import com.iota.iri.service.spentaddresses.SpentAddressesProvider; +import com.iota.iri.storage.Tangle; +import com.iota.iri.utils.Converter; + +import java.util.*; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.internal.junit.JUnitRule; +import org.mockito.internal.util.collections.Sets; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoJUnitRule; +import org.mockito.junit.MockitoRule; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + + +public class LedgerServiceImplTest { + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + private LedgerServiceImpl ledgerService = new LedgerServiceImpl(); + + @Mock + private Tangle tangle; + + @Mock(answer = Answers.RETURNS_MOCKS) + private SnapshotProvider snapshotProvider; + + @Mock + private SnapshotService snapshotService; + + @Mock + MilestoneService milestoneService; + + @Mock + SpentAddressesProvider spentAddressesProvider; + + @Mock + BundleValidator bundleValidator; + + + public LedgerServiceImplTest() { + + } + + @Before + public void setUp() throws Exception { + ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesProvider, + bundleValidator); + + } + + @Test + public void generateBalanceDiff_persistsSpentAddresses() throws Exception { + List bundle = TangleMockUtils.mockValidBundle(tangle, bundleValidator, 1, + "A", "Z"); + TransactionViewModel tailTx = bundle.get(0); + int milestoneIndex = 1; + when(milestoneService.isTransactionConfirmed(tailTx, milestoneIndex)).thenReturn(false); + when(snapshotProvider.getInitialSnapshot().getSolidEntryPoints()).thenReturn(Collections.emptyMap()); + + ledgerService.generateBalanceDiff(new HashSet<>(), tailTx.getHash(), milestoneIndex); + verify(spentAddressesProvider, times(1)).saveAddressesBatch( + eq(Arrays.asList(bundle.get(1).getAddressHash()))); + } +} \ No newline at end of file diff --git a/src/test/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImplTest.java b/src/test/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImplTest.java new file mode 100644 index 0000000000..659fe9648f --- /dev/null +++ b/src/test/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImplTest.java @@ -0,0 +1,15 @@ +package com.iota.iri.service.spentaddresses.impl; + +import org.junit.Test; + +import static org.junit.Assert.*; + + +public class SpentAddressesProviderImplTest { + + @Test + public void saveAddressBatch() { + + } + +} \ No newline at end of file From 3ec3ae5fe236878a608c8fda4fa41e25ad21b6ce Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Sat, 9 Feb 2019 23:13:48 +0200 Subject: [PATCH 27/34] Refactor so we don't use random trits --- .../controllers/TransactionViewModelTest.java | 12 ++--- .../impl/CumulativeWeightCalculatorTest.java | 54 +++++++++---------- .../tipselection/impl/RatingOneTest.java | 10 ++-- .../tipselection/impl/TailFinderImplTest.java | 4 +- .../impl/WalkValidatorImplTest.java | 22 ++++---- .../tipselection/impl/WalkerAlphaTest.java | 46 ++++++++-------- 6 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java b/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java index eb369e763b..4d059f1201 100644 --- a/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java +++ b/src/test/java/com/iota/iri/controllers/TransactionViewModelTest.java @@ -25,7 +25,7 @@ import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; -import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; +import static com.iota.iri.TransactionTestUtils.getTransactionWithTrunkAndBranch; import static org.junit.Assert.*; @@ -296,11 +296,11 @@ public void updateHeightShouldWork() throws Exception { int count = 4; TransactionViewModel[] transactionViewModels = new TransactionViewModel[count]; Hash hash = getRandomTransactionHash(); - transactionViewModels[0] = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(Hash.NULL_HASH, + transactionViewModels[0] = new TransactionViewModel(getTransactionWithTrunkAndBranch(Hash.NULL_HASH, Hash.NULL_HASH), hash); transactionViewModels[0].store(tangle, snapshotProvider.getInitialSnapshot()); for(int i = 0; ++i < count; ) { - transactionViewModels[i] = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, + transactionViewModels[i] = new TransactionViewModel(getTransactionWithTrunkAndBranch(hash, Hash.NULL_HASH), hash = getRandomTransactionHash()); transactionViewModels[i].store(tangle, snapshotProvider.getInitialSnapshot()); } @@ -318,7 +318,7 @@ public void updateHeightPrefilledSlotShouldFail() throws Exception { TransactionViewModel[] transactionViewModels = new TransactionViewModel[count]; Hash hash = getRandomTransactionHash(); for(int i = 0; ++i < count; ) { - transactionViewModels[i] = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, + transactionViewModels[i] = new TransactionViewModel(getTransactionWithTrunkAndBranch(hash, Hash.NULL_HASH), hash = getRandomTransactionHash()); transactionViewModels[i].store(tangle, snapshotProvider.getInitialSnapshot()); } @@ -366,13 +366,13 @@ public void testManyTXInDB() throws Exception { int interval1 = 50; int interval = interval1*10; log.info("Starting Test. #TX: {}", TransactionViewModel.getNumberOfStoredTransactions(tangle)); - new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(Hash.NULL_HASH, Hash.NULL_HASH), hash).store(tangle, snapshotProvider.getInitialSnapshot()); + new TransactionViewModel(getTransactionWithTrunkAndBranch(Hash.NULL_HASH, Hash.NULL_HASH), hash).store(tangle, snapshotProvider.getInitialSnapshot()); TransactionViewModel transactionViewModel; boolean pop = false; for (i = 0; i++ < max;) { hash = getRandomTransactionHash(); j = hashes.size(); - transactionViewModel = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hashes.get(seed.nextInt(j)), hashes.get(seed.nextInt(j))), hash); + transactionViewModel = new TransactionViewModel(getTransactionWithTrunkAndBranch(hashes.get(seed.nextInt(j)), hashes.get(seed.nextInt(j))), hash); start = System.nanoTime(); transactionViewModel.store(tangle, snapshotProvider.getInitialSnapshot()); diff = System.nanoTime() - start; diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java index a879013327..b0462179ae 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/CumulativeWeightCalculatorTest.java @@ -58,13 +58,13 @@ public static void setUp() throws Exception { public void testCalculateCumulativeWeight() throws Exception { TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction1.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction1.getHash(), transaction1.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction2.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction2.getHash(), transaction1.getHash()), getRandomTransactionHash()); - transaction4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction2.getHash(), + transaction4 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction2.getHash(), transaction3.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); transaction1.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -89,11 +89,11 @@ public void testCalculateCumulativeWeight() throws Exception { public void testCalculateCumulativeWeightDiamond() throws Exception { TransactionViewModel transaction, transaction1, transaction2, transaction3; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction1.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction1.getHash(), transaction2.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); transaction1.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -121,13 +121,13 @@ public void testCalculateCumulativeWeightDiamond() throws Exception { public void testCalculateCumulativeWeightLinear() throws Exception { TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction1.getHash(), transaction1.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction2.getHash(), transaction2.getHash()), getRandomTransactionHash()); - transaction4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction4 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction3.getHash(), transaction3.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); transaction1.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -158,17 +158,17 @@ public void testCalculateCumulativeWeight2() throws Exception { TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4, transaction5, transaction6; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction4 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction5 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction5 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction3.getHash(), transaction2.getHash()), getRandomTransactionHash()); - transaction6 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction6 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction4.getHash(), transaction5.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -212,7 +212,7 @@ public void cwCalculationSameAsLegacy() throws Exception { for (int i = 1; i < hashes.length; i++) { hashes[i] = getRandomTransactionHash(); TransactionViewModel transactionViewModel = new TransactionViewModel( - getRandomTransactionWithTrunkAndBranch(hashes[i - random.nextInt(i) - 1], + getTransactionWithTrunkAndBranch(hashes[i - random.nextInt(i) - 1], hashes[i - random.nextInt(i) - 1]), hashes[i]); transactionViewModel.store(tangle, snapshotProvider.getInitialSnapshot()); log.debug(String.format("current transaction %.4s \n with trunk %.4s \n and branch %.4s", hashes[i], @@ -237,7 +237,7 @@ public void cwCalculationSameAsLegacy() throws Exception { public void testTangleWithCircle() throws Exception { TransactionViewModel transaction; Hash randomTransactionHash = getRandomTransactionHash(); - transaction = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(randomTransactionHash, randomTransactionHash), randomTransactionHash); + transaction = new TransactionViewModel(getTransactionWithTrunkAndBranch(randomTransactionHash, randomTransactionHash), randomTransactionHash); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -250,13 +250,13 @@ public void testTangleWithCircle() throws Exception { public void testTangleWithCircle2() throws Exception { TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4; Hash randomTransactionHash2 = getRandomTransactionHash(); - transaction = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction = new TransactionViewModel(getTransactionWithTrunkAndBranch( randomTransactionHash2, randomTransactionHash2), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction1.getHash(), transaction1.getHash()), randomTransactionHash2); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -272,12 +272,12 @@ public void testTangleWithCircle2() throws Exception { public void testCollsionsInDiamondTangle() throws Exception { TransactionViewModel transaction, transaction1, transaction2, transaction3; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); Hash transactionHash2 = getHashWithSimilarPrefix(transaction1); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), transactionHash2); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction1.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction1.getHash(), transaction2.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); transaction1.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -328,7 +328,7 @@ private long ratingTime(int size) throws Exception { Random random = new Random(); for (int i = 1; i < hashes.length; i++) { hashes[i] = getRandomTransactionHash(); - new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hashes[i - random.nextInt(i) - 1], + new TransactionViewModel(getTransactionWithTrunkAndBranch(hashes[i - random.nextInt(i) - 1], hashes[i - random.nextInt(i) - 1]), hashes[i]).store(tangle, snapshotProvider.getInitialSnapshot()); } long start = System.currentTimeMillis(); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java index 6820e64367..3d82de9f6a 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/RatingOneTest.java @@ -17,7 +17,7 @@ import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; -import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; +import static com.iota.iri.TransactionTestUtils.getTransactionWithTrunkAndBranch; public class RatingOneTest { private static final TemporaryFolder dbFolder = new TemporaryFolder(); @@ -52,13 +52,13 @@ public static void setUp() throws Exception { public void testCalculate() throws Exception { TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction1.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction1.getHash(), transaction1.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction2.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction2.getHash(), transaction1.getHash()), getRandomTransactionHash()); - transaction4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction2.getHash(), + transaction4 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction2.getHash(), transaction3.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); transaction1.store(tangle, snapshotProvider.getInitialSnapshot()); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java index cbcce5dbde..e34d19f81f 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/TailFinderImplTest.java @@ -4,7 +4,7 @@ import static com.iota.iri.TransactionTestUtils.createBundleHead; import static com.iota.iri.TransactionTestUtils.createTransactionWithTrunkBundleHash; import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; -import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; +import static com.iota.iri.TransactionTestUtils.getTransactionWithTrunkAndBranch; import com.iota.iri.conf.MainnetConfig; import com.iota.iri.controllers.TransactionViewModel; @@ -91,7 +91,7 @@ public void findMissingTailTest() throws Exception { TransactionViewModel tx1 = createTransactionWithTrunkBundleHash(tx2, txa.getHash()); tx1.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx0 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx2.getHash()), + TransactionViewModel tx0 = new TransactionViewModel(getTransactionWithTrunkAndBranch(tx1.getHash(), tx2.getHash()), getRandomTransactionHash()); tx0.store(tangle, snapshotProvider.getInitialSnapshot()); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java index 9dc291fb63..74a9d525d3 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/WalkValidatorImplTest.java @@ -21,7 +21,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; +import static com.iota.iri.TransactionTestUtils.getTransactionWithTrunkAndBranch; import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; import java.util.HashMap; @@ -146,7 +146,7 @@ public void belowMaxDepthWithFreshMilestone() throws Exception { tx.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 92); Hash hash = tx.getHash(); for (int i = 0; i < 4 ; i++) { - tx = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, hash), getRandomTransactionHash()); + tx = new TransactionViewModel(getTransactionWithTrunkAndBranch(hash, hash), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx,0); TransactionTestUtils.setCurrentIndex(tx,0); tx.updateSolid(true); @@ -170,7 +170,7 @@ public void failBelowMaxDepthWithFreshMilestoneDueToLongChain() throws Exception tx.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 92); Hash hash = tx.getHash(); for (int i = 0; i < maxAnalyzedTxs ; i++) { - tx = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, hash), + tx = new TransactionViewModel(getTransactionWithTrunkAndBranch(hash, hash), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx,0); TransactionTestUtils.setCurrentIndex(tx,0); @@ -193,7 +193,7 @@ public void belowMaxDepthOnGenesis() throws Exception { final int maxAnalyzedTxs = config.getBelowMaxDepthTransactionLimit(); Hash hash = Hash.NULL_HASH; for (int i = 0; i < maxAnalyzedTxs - 2 ; i++) { - tx = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, hash), getRandomTransactionHash()); + tx = new TransactionViewModel(getTransactionWithTrunkAndBranch(hash, hash), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx,0); TransactionTestUtils.setCurrentIndex(tx,0); tx.updateSolid(true); @@ -216,7 +216,7 @@ public void failBelowMaxDepthOnGenesisDueToLongChain() throws Exception { TransactionViewModel tx = null; Hash hash = Hash.NULL_HASH; for (int i = 0; i < maxAnalyzedTxs; i++) { - tx = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(hash, hash), + tx = new TransactionViewModel(getTransactionWithTrunkAndBranch(hash, hash), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx,0); TransactionTestUtils.setCurrentIndex(tx,0); @@ -261,21 +261,21 @@ public void dontMarkWrongTxsAsBelowMaxDepth() throws Exception { txBad.store(tangle, snapshotProvider.getInitialSnapshot()); txBad.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 10); - TransactionViewModel tx2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx1.getHash()), + TransactionViewModel tx2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(tx1.getHash(), tx1.getHash()), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx2,0); TransactionTestUtils.setCurrentIndex(tx2,0); tx2.updateSolid(true); tx2.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), txBad.getHash()), + TransactionViewModel tx3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(tx1.getHash(), txBad.getHash()), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx3,0); TransactionTestUtils.setCurrentIndex(tx3,0); tx3.updateSolid(true); tx3.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx2.getHash(), tx3.getHash()), + TransactionViewModel tx4 = new TransactionViewModel(getTransactionWithTrunkAndBranch(tx2.getHash(), tx3.getHash()), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx4,0); TransactionTestUtils.setCurrentIndex(tx4,0); @@ -307,21 +307,21 @@ public void allowConfirmedTxToPassBelowMaxDepthAfterMilestoneConfirmation() thro txBad.store(tangle, snapshotProvider.getInitialSnapshot()); txBad.setSnapshot(tangle, snapshotProvider.getInitialSnapshot(), 10); - TransactionViewModel tx2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), tx1.getHash()), + TransactionViewModel tx2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(tx1.getHash(), tx1.getHash()), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx2,0); TransactionTestUtils.setCurrentIndex(tx2,0); tx2.updateSolid(true); tx2.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx1.getHash(), txBad.getHash()), + TransactionViewModel tx3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(tx1.getHash(), txBad.getHash()), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx3,0); TransactionTestUtils.setCurrentIndex(tx3,0); tx3.updateSolid(true); tx3.store(tangle, snapshotProvider.getInitialSnapshot()); - TransactionViewModel tx4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(tx2.getHash(), tx3.getHash()), + TransactionViewModel tx4 = new TransactionViewModel(getTransactionWithTrunkAndBranch(tx2.getHash(), tx3.getHash()), getRandomTransactionHash()); TransactionTestUtils.setLastIndex(tx4,0); TransactionTestUtils.setCurrentIndex(tx4,0); diff --git a/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java b/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java index f45e53a474..a303e13507 100644 --- a/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java +++ b/src/test/java/com/iota/iri/service/tipselection/impl/WalkerAlphaTest.java @@ -29,7 +29,7 @@ import static com.iota.iri.TransactionTestUtils.getRandomTransactionTrits; import static com.iota.iri.TransactionTestUtils.getRandomTransactionHash; -import static com.iota.iri.TransactionTestUtils.getRandomTransactionWithTrunkAndBranch; +import static com.iota.iri.TransactionTestUtils.getTransactionWithTrunkAndBranch; public class WalkerAlphaTest { private static final TemporaryFolder dbFolder = new TemporaryFolder(); @@ -71,11 +71,11 @@ public void testWalkEndsOnlyInRating() throws Exception { //build a small tangle - 1,2,3,4 point to transaction TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -88,7 +88,7 @@ public void testWalkEndsOnlyInRating() throws Exception { UnIterableMap rating = ratingCalculator.calculate(transaction.getHash()); //add 4 after the rating was calculated - transaction4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction4 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); transaction4.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -108,11 +108,11 @@ public void showWalkDistributionAlphaHalf() throws Exception { //build a small tangle - 1,2,3,4 point to transaction TransactionViewModel transaction, transaction1, transaction2, transaction3; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -151,11 +151,11 @@ public void showWalkDistributionAlphaZero() throws Exception { //build a small tangle - 1,2,3,4 point to transaction TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -170,7 +170,7 @@ public void showWalkDistributionAlphaZero() throws Exception { rating.put(transaction2.getHash(), 10); //add 4 after the rating was calculated - transaction4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction4 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); transaction4.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -198,13 +198,13 @@ public void testWalk() throws Exception { //build a small tangle TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4; transaction = new TransactionViewModel(getRandomTransactionTrits(), Hash.NULL_HASH); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction1.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction1.getHash(), transaction1.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction2.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction2.getHash(), transaction1.getHash()), getRandomTransactionHash()); - transaction4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction2.getHash(), + transaction4 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction2.getHash(), transaction3.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); transaction1.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -228,11 +228,11 @@ public void testWalkDiamond() throws Exception { //build a small tangle TransactionViewModel transaction, transaction1, transaction2, transaction3; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction.getHash(), + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch(transaction1.getHash(), + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch(transaction1.getHash(), transaction2.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); transaction1.store(tangle, snapshotProvider.getInitialSnapshot()); @@ -255,13 +255,13 @@ public void testWalkChain() throws Exception { //build a small tangle TransactionViewModel transaction, transaction1, transaction2, transaction3, transaction4; transaction = new TransactionViewModel(getRandomTransactionTrits(), getRandomTransactionHash()); - transaction1 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction1 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction.getHash(), transaction.getHash()), getRandomTransactionHash()); - transaction2 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction2 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction1.getHash(), transaction1.getHash()), getRandomTransactionHash()); - transaction3 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction3 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction2.getHash(), transaction2.getHash()), getRandomTransactionHash()); - transaction4 = new TransactionViewModel(getRandomTransactionWithTrunkAndBranch( + transaction4 = new TransactionViewModel(getTransactionWithTrunkAndBranch( transaction3.getHash(), transaction3.getHash()), getRandomTransactionHash()); transaction.store(tangle, snapshotProvider.getInitialSnapshot()); transaction1.store(tangle, snapshotProvider.getInitialSnapshot()); From a3fd33bdffd6835acc428610740e182f854f3363 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Mon, 11 Feb 2019 15:44:43 +0200 Subject: [PATCH 28/34] When checking for existence go through bloom filter first --- .../iri/storage/rocksDB/RocksDBPersistenceProvider.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java b/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java index a7646c2dc1..af50a58af5 100644 --- a/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java +++ b/src/main/java/com/iota/iri/storage/rocksDB/RocksDBPersistenceProvider.java @@ -98,8 +98,12 @@ public void delete(Class model, Indexable index) throws Exception { @Override public boolean exists(Class model, Indexable key) throws Exception { - ColumnFamilyHandle handle = classTreeMap.get(model); - return handle != null && db.get(handle, key.bytes()) != null; + if (mayExist(model, key)) { + //ensure existence + ColumnFamilyHandle handle = classTreeMap.get(model); + return handle != null && db.get(handle, key.bytes()) != null; + } + return false; } @Override From 6cc26b35061daf36f1705f3073c774de19ff64b6 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Mon, 11 Feb 2019 15:48:16 +0200 Subject: [PATCH 29/34] SpendAddressProvider checks for existence via correct interface --- .../service/spentaddresses/impl/SpentAddressesProviderImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImpl.java b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImpl.java index a8a2f1aa63..c59c61dc6e 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImpl.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesProviderImpl.java @@ -86,7 +86,7 @@ private void readSpentAddressesFromStream(InputStream in) throws SpentAddressesE @Override public boolean containsAddress(Hash addressHash) throws SpentAddressesException { try { - return ((SpentAddress) rocksDBPersistenceProvider.get(SpentAddress.class, addressHash)).exists(); + return rocksDBPersistenceProvider.exists(SpentAddress.class, addressHash); } catch (Exception e) { throw new SpentAddressesException(e); } From f851a43701925636e9fcf691183ab424757dc5fd Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Tue, 12 Feb 2019 13:04:18 +0200 Subject: [PATCH 30/34] Persist spent-addresses asynchronously --- src/main/java/com/iota/iri/Iota.java | 2 +- .../ledger/impl/LedgerServiceImpl.java | 20 ++++--------- .../spentaddresses/SpentAddressesService.java | 8 +++++ .../impl/SpentAddressesServiceImpl.java | 24 +++++++++++++-- .../java/com/iota/iri/utils/IotaUtils.java | 16 ++++++++++ .../ledger/impl/LedgerServiceImplTest.java | 30 +++++++------------ 6 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index 1ab8a5d8d9..ec90fcf8f8 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -215,7 +215,7 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx latestMilestoneTracker, messageQ); seenMilestonesRetriever.init(tangle, snapshotProvider, transactionRequester); milestoneSolidifier.init(snapshotProvider, transactionValidator); - ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesProvider, + ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesService, bundleValidator); if (transactionPruner != null) { transactionPruner.init(tangle, snapshotProvider, spentAddressesService, tipsViewModel, configuration) diff --git a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java index 46ee3b3add..ff80f39664 100644 --- a/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java +++ b/src/main/java/com/iota/iri/service/ledger/impl/LedgerServiceImpl.java @@ -13,12 +13,10 @@ import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.SnapshotService; import com.iota.iri.service.snapshot.impl.SnapshotStateDiffImpl; -import com.iota.iri.service.spentaddresses.SpentAddressesException; -import com.iota.iri.service.spentaddresses.SpentAddressesProvider; +import com.iota.iri.service.spentaddresses.SpentAddressesService; import com.iota.iri.storage.Tangle; import java.util.*; -import java.util.stream.Collectors; /** * Creates a service instance that allows us to perform ledger state specific operations.
@@ -46,7 +44,7 @@ public class LedgerServiceImpl implements LedgerService { */ private MilestoneService milestoneService; - private SpentAddressesProvider spentAddressesProvider; + private SpentAddressesService spentAddressesService; private BundleValidator bundleValidator; @@ -69,14 +67,14 @@ public class LedgerServiceImpl implements LedgerService { * @return the initialized instance itself to allow chaining */ public LedgerServiceImpl init(Tangle tangle, SnapshotProvider snapshotProvider, SnapshotService snapshotService, - MilestoneService milestoneService, SpentAddressesProvider spentAddressesProvider, + MilestoneService milestoneService, SpentAddressesService spentAddressesService, BundleValidator bundleValidator) { this.tangle = tangle; this.snapshotProvider = snapshotProvider; this.snapshotService = snapshotService; this.milestoneService = milestoneService; - this.spentAddressesProvider = spentAddressesProvider; + this.spentAddressesService = spentAddressesService; this.bundleValidator = bundleValidator; return this; @@ -193,7 +191,8 @@ public Map generateBalanceDiff(Set visitedTransactions, Hash s //ISSUE 1008: generateBalanceDiff should be refactored so we don't have those hidden // concerns - persistValidatedSpentAddresses(bundleTransactionViewModels); + spentAddressesService + .persistValidatedSpentAddressesAsync(bundleTransactionViewModels); if (BundleValidator.isInconsistent(bundleTransactionViewModels)) { break; @@ -234,13 +233,6 @@ public Map generateBalanceDiff(Set visitedTransactions, Hash s return state; } - private void persistValidatedSpentAddresses(List bundleTransactionViewModels) throws SpentAddressesException { - List spentAddresses = bundleTransactionViewModels.stream() - .filter(tx -> tx.value() < 0) - .map(TransactionViewModel::getAddressHash) - .collect(Collectors.toList()); - spentAddressesProvider.saveAddressesBatch(spentAddresses); - } /** * Generates the {@link com.iota.iri.model.StateDiff} that belongs to the given milestone in the database and marks diff --git a/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java b/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java index 856e20ff28..3aeb474cea 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/SpentAddressesService.java @@ -28,4 +28,12 @@ public interface SpentAddressesService { * @throws SpentAddressesException */ void persistSpentAddresses(Collection transactions) throws SpentAddressesException; + + /** + * Persists the spent addresses of all pre-verified valid transactions in an asynchronous manner + * + * @param transactions Transactions that have their signatures verified beforehand. + * Non spent transactions will be filtered out when persisting + */ + void persistValidatedSpentAddressesAsync(Collection transactions); } diff --git a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java index 1b3a0ec89a..ad95531959 100644 --- a/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java +++ b/src/main/java/com/iota/iri/service/spentaddresses/impl/SpentAddressesServiceImpl.java @@ -3,7 +3,6 @@ import com.iota.iri.BundleValidator; import com.iota.iri.conf.MilestoneConfig; import com.iota.iri.controllers.AddressViewModel; -import com.iota.iri.controllers.MilestoneViewModel; import com.iota.iri.controllers.TransactionViewModel; import com.iota.iri.model.Hash; import com.iota.iri.model.HashFactory; @@ -14,10 +13,11 @@ import com.iota.iri.service.tipselection.TailFinder; import com.iota.iri.service.tipselection.impl.TailFinderImpl; import com.iota.iri.storage.Tangle; -import com.iota.iri.utils.dag.DAGHelper; +import com.iota.iri.utils.IotaUtils; import java.util.*; +import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -36,6 +36,8 @@ */ public class SpentAddressesServiceImpl implements SpentAddressesService { + private static final Logger log = LoggerFactory.getLogger(SpentAddressesServiceImpl.class); + private Tangle tangle; private SnapshotProvider snapshotProvider; @@ -48,9 +50,11 @@ public class SpentAddressesServiceImpl implements SpentAddressesService { private BundleValidator bundleValidator; + private final ExecutorService asyncSpentAddressesPersistor = + IotaUtils.createNamedSingleThreadExecutor("Persist Spent Addresses Async"); /** - * Creates a Spent address service using the Tangle + * Creates a Spent address service using the Tangler * * @param tangle Tangle object which is used to load models of addresses * @param snapshotProvider {@link SnapshotProvider} to find the genesis, used to verify tails @@ -90,6 +94,20 @@ public void persistSpentAddresses(Collection transactions) } } + public void persistValidatedSpentAddressesAsync(Collection transactions) { + asyncSpentAddressesPersistor.submit(() -> { + try { + List spentAddresses = transactions.stream() + .filter(tx -> tx.value() < 0) + .map(TransactionViewModel::getAddressHash) + .collect(Collectors.toList()); + spentAddressesProvider.saveAddressesBatch(spentAddresses); + } catch (Exception e) { + log.error("Failed to persist spent-addresses... Counting on the Milestone Pruner to finish the job", e); + } + }); + } + private boolean wasTransactionSpentFrom(TransactionViewModel tx) throws Exception { Optional tailFromTx = tailFinder.findTailFromTx(tx); if (tailFromTx.isPresent() && tx.value() < 0) { diff --git a/src/main/java/com/iota/iri/utils/IotaUtils.java b/src/main/java/com/iota/iri/utils/IotaUtils.java index d3c61e8126..7a751e4d65 100644 --- a/src/main/java/com/iota/iri/utils/IotaUtils.java +++ b/src/main/java/com/iota/iri/utils/IotaUtils.java @@ -10,6 +10,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -56,4 +58,18 @@ public static List getAllSetters(Class clazz) { public static List createImmutableList(T... values) { return Collections.unmodifiableList(Arrays.asList(values)); } + + /** + * Creates a single thread executor service that has an unbounded queue. + + * @param name the name to give to the thread + * @return a named single thread executor service + * + * @see com.iota.iri.utils.thread.BoundedScheduledExecutorService + * @see com.iota.iri.utils.thread.DedicatedScheduledExecutorService + */ + public static ExecutorService createNamedSingleThreadExecutor(String name) { + return Executors.newSingleThreadExecutor(r -> new Thread(r, name)); + + } } diff --git a/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java b/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java index 7b6a5471ca..461fe55f90 100644 --- a/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java +++ b/src/test/java/com/iota/iri/service/ledger/impl/LedgerServiceImplTest.java @@ -2,31 +2,23 @@ import com.iota.iri.BundleValidator; import com.iota.iri.TangleMockUtils; -import com.iota.iri.TransactionTestUtils; import com.iota.iri.controllers.TransactionViewModel; -import com.iota.iri.model.Hash; -import com.iota.iri.service.ledger.LedgerException; import com.iota.iri.service.milestone.MilestoneService; -import com.iota.iri.service.snapshot.Snapshot; import com.iota.iri.service.snapshot.SnapshotProvider; import com.iota.iri.service.snapshot.SnapshotService; -import com.iota.iri.service.spentaddresses.SpentAddressesException; -import com.iota.iri.service.spentaddresses.SpentAddressesProvider; +import com.iota.iri.service.spentaddresses.SpentAddressesService; import com.iota.iri.storage.Tangle; -import com.iota.iri.utils.Converter; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Answers; import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.internal.junit.JUnitRule; -import org.mockito.internal.util.collections.Sets; import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoJUnitRule; import org.mockito.junit.MockitoRule; import static org.mockito.Matchers.eq; @@ -52,13 +44,13 @@ public class LedgerServiceImplTest { private SnapshotService snapshotService; @Mock - MilestoneService milestoneService; + private MilestoneService milestoneService; @Mock - SpentAddressesProvider spentAddressesProvider; + private SpentAddressesService spentAddressesService; @Mock - BundleValidator bundleValidator; + private BundleValidator bundleValidator; public LedgerServiceImplTest() { @@ -66,8 +58,8 @@ public LedgerServiceImplTest() { } @Before - public void setUp() throws Exception { - ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesProvider, + public void setUp() { + ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesService, bundleValidator); } @@ -80,9 +72,7 @@ public void generateBalanceDiff_persistsSpentAddresses() throws Exception { int milestoneIndex = 1; when(milestoneService.isTransactionConfirmed(tailTx, milestoneIndex)).thenReturn(false); when(snapshotProvider.getInitialSnapshot().getSolidEntryPoints()).thenReturn(Collections.emptyMap()); - ledgerService.generateBalanceDiff(new HashSet<>(), tailTx.getHash(), milestoneIndex); - verify(spentAddressesProvider, times(1)).saveAddressesBatch( - eq(Arrays.asList(bundle.get(1).getAddressHash()))); + verify(spentAddressesService, times(1)).persistValidatedSpentAddressesAsync(eq(bundle)); } } \ No newline at end of file From 7afb72ad272595f1e564f263bcc3a4fc8dc4f612 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Mon, 18 Feb 2019 18:17:18 +0100 Subject: [PATCH 31/34] Fix: Do not persist pruner state (#1342) Will reduce resource consumption --- src/main/java/com/iota/iri/Iota.java | 3 +- .../transactionpruning/TransactionPruner.java | 26 ---- .../async/AsyncTransactionPruner.java | 142 +----------------- .../async/MilestonePrunerJobQueue.java | 20 +-- .../async/SimpleJobQueue.java | 2 - .../jobs/MilestonePrunerJob.java | 2 - 6 files changed, 12 insertions(+), 183 deletions(-) diff --git a/src/main/java/com/iota/iri/Iota.java b/src/main/java/com/iota/iri/Iota.java index ec90fcf8f8..dd24a03c0a 100644 --- a/src/main/java/com/iota/iri/Iota.java +++ b/src/main/java/com/iota/iri/Iota.java @@ -218,8 +218,7 @@ private void injectDependencies() throws SnapshotException, TransactionPruningEx ledgerService.init(tangle, snapshotProvider, snapshotService, milestoneService, spentAddressesService, bundleValidator); if (transactionPruner != null) { - transactionPruner.init(tangle, snapshotProvider, spentAddressesService, tipsViewModel, configuration) - .restoreState(); + transactionPruner.init(tangle, snapshotProvider, spentAddressesService, tipsViewModel, configuration); } transactionRequesterWorker.init(tangle, transactionRequester, tipsViewModel, node); } diff --git a/src/main/java/com/iota/iri/service/transactionpruning/TransactionPruner.java b/src/main/java/com/iota/iri/service/transactionpruning/TransactionPruner.java index ed6c62de3a..732a25c12c 100644 --- a/src/main/java/com/iota/iri/service/transactionpruning/TransactionPruner.java +++ b/src/main/java/com/iota/iri/service/transactionpruning/TransactionPruner.java @@ -30,30 +30,4 @@ public interface TransactionPruner { */ void processJobs() throws TransactionPruningException; - /** - * This method saves the current state of the {@link TransactionPruner}, so it can later be restored by - * {@link #restoreState()}. - * - * It is used to maintain the state between IRI restarts and pick up pruning where it stopped when IRI shut down. - * - * @throws TransactionPruningException if anything goes wrong while saving the current state - */ - void saveState() throws TransactionPruningException; - - /** - * Restores the state of the {@link TransactionPruner} after being saved before by {@link #saveState()}. - * - * It is used to keep the state between IRI restarts and pick up pruning where it stopped when IRI shut down. - * - * @throws TransactionPruningException if anything goes wrong while restoring the state - */ - void restoreState() throws TransactionPruningException; - - /** - * This method removes all queued jobs and resets the state of the {@link TransactionPruner}. It can for example be - * used to cleanup after tests. - * - * @throws TransactionPruningException if anything goes wrong while clearing the jobs - * */ - void clear() throws TransactionPruningException; } diff --git a/src/main/java/com/iota/iri/service/transactionpruning/async/AsyncTransactionPruner.java b/src/main/java/com/iota/iri/service/transactionpruning/async/AsyncTransactionPruner.java index dd7784b844..796afa0c57 100644 --- a/src/main/java/com/iota/iri/service/transactionpruning/async/AsyncTransactionPruner.java +++ b/src/main/java/com/iota/iri/service/transactionpruning/async/AsyncTransactionPruner.java @@ -104,11 +104,6 @@ public class AsyncTransactionPruner implements TransactionPruner { */ private final Map, JobQueue> jobQueues = new HashMap<>(); - /** - * Holds a flag that indicates if the state shall be persisted. - */ - private boolean persistRequested = false; - /** * This method initializes the instance and registers its dependencies.
*
@@ -162,8 +157,6 @@ public void addJob(TransactionPrunerJob job) throws TransactionPruningException // this call is "unchecked" to a "raw" JobQueue and it is intended since the matching JobQueue is defined by the // registered job types getJobQueue(job.getClass()).addJob(job); - - saveState(); } /** @@ -184,65 +177,17 @@ public void processJobs() throws TransactionPruningException { } } - /** - * {@inheritDoc} - * - * We incorporate a background job that periodically saves the state rather than doing it "live", to reduce the cost - * of this operation. While this can theoretically lead to a situation where the saved state is not 100% correct and - * the latest changes get lost (if IRI crashes or gets restarted before the new changes could be persisted), the - * impact is marginal because it only leads to some floating "zombie" transactions that will stay in the database. - * This will be "solved" once we persist the changes in the database instead of a file on the hard disk. For now the - * trade off between faster processing times and leaving some garbage is reasonable. - */ - @Override - public void saveState() { - persistRequested = true; - } - - /** - * {@inheritDoc} - * - * It reads the state by parsing the state file and passing it into the registered parsers for each job type. - * - * Every line holds a job entry that starts with the fully qualified class name of the job followed by a ";" and the - * serialized representation of the job. - */ - @Override - public void restoreState() throws TransactionPruningException { - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(new BufferedInputStream(new FileInputStream(getStateFile()))) - )) { - String line; - while ((line = reader.readLine()) != null) { - String[] parts = line.split(";", 2); - if (parts.length >= 2) { - JobParser jobParser = jobParsers.get(parts[0]); - if (jobParser == null) { - throw new TransactionPruningException("could not determine a parser for cleanup job of type " + parts[0]); - } - - addJob(jobParser.parse(parts[1])); - } - } - } catch (IOException e) { - if (getStateFile().exists()) { - throw new TransactionPruningException("could not read the state file", e); - } - } - } /** - * {@inheritDoc} + * This method removes all queued jobs and resets the state of the {@link TransactionPruner}. It can for example be + * used to cleanup after tests. * * It cycles through all registered {@link JobQueue}s and clears them before persisting the state. */ - @Override - public void clear() throws TransactionPruningException { + void clear() { for (JobQueue jobQueue : jobQueues.values()) { jobQueue.clear(); } - - saveStateNow(); } /** @@ -254,7 +199,6 @@ public void clear() throws TransactionPruningException { */ public void start() { ThreadUtils.spawnThread(this::processJobsThread, cleanupThreadIdentifier); - ThreadUtils.spawnThread(this::persistThread, persisterThreadIdentifier); } /** @@ -283,73 +227,6 @@ private void processJobsThread() { } } - /** - * This method contains the logic for persisting the pruner state, that gets executed in a separate {@link Thread}. - * - * It periodically checks the {@link #persistRequested} flag and triggers the writing of the state file until the - * {@link AsyncTransactionPruner} is shutting down. - */ - private void persistThread() { - while(!Thread.currentThread().isInterrupted()) { - try { - if (persistRequested) { - saveStateNow(); - - persistRequested = false; - } - } catch(TransactionPruningException e) { - log.error("could not persist transaction pruner state", e); - } - - ThreadUtils.sleep(GARBAGE_COLLECTOR_PERSIST_INTERVAL); - } - } - - /** - * This method creates a serialized version of the given job. - * - * @param job job that shall get serialized - * @return serialized representation of the job - */ - private String serializeJobEntry(TransactionPrunerJob job) { - return job.getClass().getCanonicalName() + ";" + job.serialize(); - } - - /** - * Saves the state by serializing the jobs into a state file that is stored on the hard disk of the node. - * - * If no jobs are queued it removes the state file. - * - * @throws TransactionPruningException if anything goes wrong while persisting the state - */ - private void saveStateNow() throws TransactionPruningException { - try { - AtomicInteger jobsPersisted = new AtomicInteger(0); - - Files.write( - Paths.get(getStateFile().getAbsolutePath()), - () -> jobQueues.values().stream() - .flatMap(JobQueue::stream) - .map(jobEntry -> { - jobsPersisted.incrementAndGet(); - - return this.serializeJobEntry(jobEntry); - }) - .iterator() - ); - - if (jobsPersisted.get() == 0) { - try { - Files.deleteIfExists(Paths.get(getStateFile().getAbsolutePath())); - } catch (IOException e) { - throw new TransactionPruningException("failed to remove the state file", e); - } - } - } catch(Exception e) { - throw new TransactionPruningException("failed to write the state file", e); - } - } - /** * Registers the job queue that is responsible for processing the jobs of the given type. * @@ -395,19 +272,6 @@ private JobQueue getJobQueue(Class jobClass) thr return jobQueue; } - /** - * This method returns a file handle to state file. - * - * It constructs the path of the file by appending the corresponding file extension to the - * {@link com.iota.iri.conf.BaseIotaConfig#localSnapshotsBasePath} config variable. If the path is relative, it - * places the file relative to the current working directory, which is usually the location of the iri.jar. - * - * @return File handle to the state file. - */ - private File getStateFile() { - return new File(config.getLocalSnapshotsBasePath() + ".snapshot.gc"); - } - /** * Functional interface for the lambda function that takes care of parsing a specific job from its serialized String * representation into the corresponding object in memory. diff --git a/src/main/java/com/iota/iri/service/transactionpruning/async/MilestonePrunerJobQueue.java b/src/main/java/com/iota/iri/service/transactionpruning/async/MilestonePrunerJobQueue.java index afd741eb70..be74b0a5ff 100644 --- a/src/main/java/com/iota/iri/service/transactionpruning/async/MilestonePrunerJobQueue.java +++ b/src/main/java/com/iota/iri/service/transactionpruning/async/MilestonePrunerJobQueue.java @@ -111,21 +111,17 @@ public void clear() { public void processJobs() throws TransactionPruningException { MilestonePrunerJob currentJob; while (!Thread.currentThread().isInterrupted() && (currentJob = jobs.peek()) != null) { - try { - currentJob.process(); + currentJob.process(); - youngestFullyCleanedMilestoneIndex = currentJob.getTargetIndex(); + youngestFullyCleanedMilestoneIndex = currentJob.getTargetIndex(); - // we always leave the last job in the queue to be able to "serialize" the queue status and allow - // to skip already processed milestones even when IRI restarts - if (jobs.size() == 1) { - break; - } - - jobs.poll(); - } finally { - transactionPruner.saveState(); + // we always leave the last job in the queue to be able to "serialize" the queue status and allow + // to skip already processed milestones even when IRI restarts + if (jobs.size() == 1) { + break; } + + jobs.poll(); } } diff --git a/src/main/java/com/iota/iri/service/transactionpruning/async/SimpleJobQueue.java b/src/main/java/com/iota/iri/service/transactionpruning/async/SimpleJobQueue.java index e73ee81797..b4fe881219 100644 --- a/src/main/java/com/iota/iri/service/transactionpruning/async/SimpleJobQueue.java +++ b/src/main/java/com/iota/iri/service/transactionpruning/async/SimpleJobQueue.java @@ -88,8 +88,6 @@ public void processJobs() throws TransactionPruningException { } throw e; - } finally { - transactionPruner.saveState(); } } } diff --git a/src/main/java/com/iota/iri/service/transactionpruning/jobs/MilestonePrunerJob.java b/src/main/java/com/iota/iri/service/transactionpruning/jobs/MilestonePrunerJob.java index b795a3de83..b182cc37c9 100644 --- a/src/main/java/com/iota/iri/service/transactionpruning/jobs/MilestonePrunerJob.java +++ b/src/main/java/com/iota/iri/service/transactionpruning/jobs/MilestonePrunerJob.java @@ -131,8 +131,6 @@ public void process() throws TransactionPruningException { setStatus(TransactionPrunerJobStatus.DONE); } } - - getTransactionPruner().saveState(); } } catch (TransactionPruningException e) { setStatus(TransactionPrunerJobStatus.FAILED); From 62882706cf674800b2c8fccfd79fa137cd677c55 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Tue, 19 Feb 2019 12:19:20 +0200 Subject: [PATCH 32/34] up version to 1.6.1-RELEASE-BETA --- DOCKER.md | 6 +++--- pom.xml | 2 +- src/main/java/com/iota/iri/IRI.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DOCKER.md b/DOCKER.md index 3fbf4ea7f7..52f5f281a4 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -2,13 +2,13 @@ Run the official iotaledger/iri container, passing the mandatory -p option: -```docker run iotaledger/iri:v1.6.0-RELEASE -p 14265``` +```docker run iotaledger/iri:v1.6.1-RELEASE-BETA -p 14265``` This will get your a running IRI with its API listening on port 14265, no neighbours and an empty database. The IRI Docker container by default expects data at /iri/data. Use the `-v` option of the `docker run` command to mount volumes so to have persistent data. You can also pass more command line options to the docker run command and those will be passed to IRI. If you want to use a iri.ini file with the docker container, supposing it's stored under /path/to/conf/iri.ini on your docker host, then pass `-v /path/to/conf:/iri/conf` and add -c /iri/conf/iri.ini as docker run arguments. So for example the `docker run` command above would become: -```docker run -v /path/to/conf:/iri/conf -v /path/to/data:/iri/data iotaledger/iri:v1.6.0-RELEASE -p 14265 -c /iri/conf/iri.ini``` +```docker run -v /path/to/conf:/iri/conf -v /path/to/data:/iri/data iotaledger/iri:v1.6.1-RELEASE-BETA -p 14265 -c /iri/conf/iri.ini``` Please refer to the IRI documentation for further command line options and iri.ini options. @@ -61,7 +61,7 @@ ExecStart=/usr/bin/docker run \ -p 14265:14265 \ -p 15600:15600 \ -p 14600:14600/udp \ -iotaledger/iri:v1.6.0-RELEASE \ +iotaledger/iri:v1.6.1-RELEASE-BETA \ -p 14265 \ --zmq-enabled \ --testnet diff --git a/pom.xml b/pom.xml index d16584e193..c154739fc8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.iota iri - 1.6.0-RELEASE + 1.6.1-RELEASE-BETA IRI IOTA Reference Implementation diff --git a/src/main/java/com/iota/iri/IRI.java b/src/main/java/com/iota/iri/IRI.java index 49e107bf5d..7a8289fd44 100644 --- a/src/main/java/com/iota/iri/IRI.java +++ b/src/main/java/com/iota/iri/IRI.java @@ -42,7 +42,7 @@ public class IRI { public static final String MAINNET_NAME = "IRI"; public static final String TESTNET_NAME = "IRI Testnet"; - public static final String VERSION = "1.6.0-RELEASE"; + public static final String VERSION = "1.6.1-RELEASE-BETA"; /** * The entry point of IRI. From d6db55f4f46ed331c21343975d863226318d46fc Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Thu, 21 Feb 2019 13:47:02 +0200 Subject: [PATCH 33/34] Change version to 1.6.1-RELEASE --- DOCKER.md | 6 +++--- pom.xml | 2 +- src/main/java/com/iota/iri/IRI.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DOCKER.md b/DOCKER.md index 52f5f281a4..14e92f860b 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -2,13 +2,13 @@ Run the official iotaledger/iri container, passing the mandatory -p option: -```docker run iotaledger/iri:v1.6.1-RELEASE-BETA -p 14265``` +```docker run iotaledger/iri:v1.6.1-RELEASE -p 14265``` This will get your a running IRI with its API listening on port 14265, no neighbours and an empty database. The IRI Docker container by default expects data at /iri/data. Use the `-v` option of the `docker run` command to mount volumes so to have persistent data. You can also pass more command line options to the docker run command and those will be passed to IRI. If you want to use a iri.ini file with the docker container, supposing it's stored under /path/to/conf/iri.ini on your docker host, then pass `-v /path/to/conf:/iri/conf` and add -c /iri/conf/iri.ini as docker run arguments. So for example the `docker run` command above would become: -```docker run -v /path/to/conf:/iri/conf -v /path/to/data:/iri/data iotaledger/iri:v1.6.1-RELEASE-BETA -p 14265 -c /iri/conf/iri.ini``` +```docker run -v /path/to/conf:/iri/conf -v /path/to/data:/iri/data iotaledger/iri:v1.6.1-RELEASE -p 14265 -c /iri/conf/iri.ini``` Please refer to the IRI documentation for further command line options and iri.ini options. @@ -61,7 +61,7 @@ ExecStart=/usr/bin/docker run \ -p 14265:14265 \ -p 15600:15600 \ -p 14600:14600/udp \ -iotaledger/iri:v1.6.1-RELEASE-BETA \ +iotaledger/iri:v1.6.1-RELEASE \ -p 14265 \ --zmq-enabled \ --testnet diff --git a/pom.xml b/pom.xml index c154739fc8..660fbe35b6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.iota iri - 1.6.1-RELEASE-BETA + 1.6.1-RELEASE IRI IOTA Reference Implementation diff --git a/src/main/java/com/iota/iri/IRI.java b/src/main/java/com/iota/iri/IRI.java index 7a8289fd44..bd84b60d52 100644 --- a/src/main/java/com/iota/iri/IRI.java +++ b/src/main/java/com/iota/iri/IRI.java @@ -42,7 +42,7 @@ public class IRI { public static final String MAINNET_NAME = "IRI"; public static final String TESTNET_NAME = "IRI Testnet"; - public static final String VERSION = "1.6.1-RELEASE-BETA"; + public static final String VERSION = "1.6.1-RELEASE"; /** * The entry point of IRI. From 86103960d99f8ee7dced72cf851f7b43add84524 Mon Sep 17 00:00:00 2001 From: Gal Rogozinski Date: Thu, 21 Feb 2019 15:56:07 +0200 Subject: [PATCH 34/34] Update changelog --- changelog.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/changelog.txt b/changelog.txt index a16f8e0bfb..e2b7e93691 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,22 @@ +1.6.1 + - Fix: Db exists() method optimization (#1338) + - Fix: Do not persist pruner state (#1342) + - Fix: Reduce db access and cpu usage needed to persist spent addresses (#1332) + - Fix: Added a NULL_HASH check for genesis transaction (#1309) + - Feature: Fresh transactions to request (#1316) + - Fix: missing user-agent header in cors (#1319) + - Fix: Batch process spent addresses to avoid out of memory issues (#1314) + - Fix: added a buffer to LSManager (#1286) + - Fix: test existence of spent addresses db do not point to correct folder (#1305) + - Change: Convert from trytes to asciiString (#1302) + - Documentation: Fixed and added javadoc comments for existing classes (#1026) + - Fix: Creates rest endpoint for iotaconfig related settings as suggested (#1200) + - Fix: make storeMessage store transactions (#1186) + - Change: add a plugin to create reproducible builds. (#1194) + - Feature: Add configurable variables for spent addresses DB (#1274) + - Fix: Posting invalid json to IRI will result in detailed error message (#1202) + - Fix: dns reverse resolving issue for neighbors that are added with their IP. (#1255) + 1.6.0 - Feat: added config variables for local snapshots (#981) - Feat: added a generic helper class for dag traversal (#982)