From 99ffd62a6e6ed4607ad8c42d97ff9a58f0b09007 Mon Sep 17 00:00:00 2001 From: catbref Date: Fri, 14 Jun 2019 17:58:54 +0100 Subject: [PATCH] Change to how "best block" is determined. --- .../java/org/qora/block/BlockGenerator.java | 9 +-- .../java/org/qora/controller/Controller.java | 2 +- .../org/qora/controller/Synchronizer.java | 2 +- .../{network => block}/BlockSummaryData.java | 4 +- .../message/BlockSummariesMessage.java | 2 +- .../org/qora/repository/BlockRepository.java | 6 ++ .../hsqldb/HSQLDBBlockRepository.java | 26 ++++++++ src/main/java/org/qora/utils/ByteArray.java | 46 +++++++++++++ .../java/org/qora/test/ByteArrayTests.java | 66 +++++++++++++++++++ 9 files changed, 153 insertions(+), 10 deletions(-) rename src/main/java/org/qora/data/{network => block}/BlockSummaryData.java (90%) create mode 100644 src/main/java/org/qora/utils/ByteArray.java create mode 100644 src/test/java/org/qora/test/ByteArrayTests.java diff --git a/src/main/java/org/qora/block/BlockGenerator.java b/src/main/java/org/qora/block/BlockGenerator.java index d2deab5c..60da346c 100644 --- a/src/main/java/org/qora/block/BlockGenerator.java +++ b/src/main/java/org/qora/block/BlockGenerator.java @@ -142,12 +142,11 @@ public class BlockGenerator extends Thread { boolean newBlockGenerated = false; - generation: try { + try { // Clear repository's "in transaction" state so we don't cause a repository deadlock repository.discardChanges(); List goodBlocks = new ArrayList<>(); - for (Block testBlock : newBlocks) { // Is new block's timestamp valid yet? // We do a separate check as some timestamp checks are skipped for testchains @@ -162,7 +161,7 @@ public class BlockGenerator extends Thread { } if (goodBlocks.isEmpty()) - break generation; + continue; // Pick random generator int winningIndex = new Random().nextInt(goodBlocks.size()); @@ -182,8 +181,10 @@ public class BlockGenerator extends Thread { if (validationResult != ValidationResult.OK) { // No longer valid? Report and discard LOGGER.error("Valid, generated block now invalid '" + validationResult.name() + "' after adding unconfirmed transactions?"); + + // Rebuild block candidates, just to be sure newBlocks.clear(); - break generation; + continue; } // Add to blockchain - something else will notice and broadcast new block to network diff --git a/src/main/java/org/qora/controller/Controller.java b/src/main/java/org/qora/controller/Controller.java index a05cb4ce..6065a287 100644 --- a/src/main/java/org/qora/controller/Controller.java +++ b/src/main/java/org/qora/controller/Controller.java @@ -29,7 +29,7 @@ import org.qora.block.BlockGenerator; import org.qora.controller.Synchronizer.SynchronizationResult; import org.qora.crypto.Crypto; import org.qora.data.block.BlockData; -import org.qora.data.network.BlockSummaryData; +import org.qora.data.block.BlockSummaryData; import org.qora.data.network.PeerData; import org.qora.data.transaction.ArbitraryTransactionData; import org.qora.data.transaction.ArbitraryTransactionData.DataType; diff --git a/src/main/java/org/qora/controller/Synchronizer.java b/src/main/java/org/qora/controller/Synchronizer.java index e835e236..f83dafc4 100644 --- a/src/main/java/org/qora/controller/Synchronizer.java +++ b/src/main/java/org/qora/controller/Synchronizer.java @@ -10,7 +10,7 @@ import org.apache.logging.log4j.Logger; import org.qora.block.Block; import org.qora.block.Block.ValidationResult; import org.qora.data.block.BlockData; -import org.qora.data.network.BlockSummaryData; +import org.qora.data.block.BlockSummaryData; import org.qora.data.transaction.TransactionData; import org.qora.network.Peer; import org.qora.network.message.BlockMessage; diff --git a/src/main/java/org/qora/data/network/BlockSummaryData.java b/src/main/java/org/qora/data/block/BlockSummaryData.java similarity index 90% rename from src/main/java/org/qora/data/network/BlockSummaryData.java rename to src/main/java/org/qora/data/block/BlockSummaryData.java index 1bf35a03..c840e676 100644 --- a/src/main/java/org/qora/data/network/BlockSummaryData.java +++ b/src/main/java/org/qora/data/block/BlockSummaryData.java @@ -1,6 +1,4 @@ -package org.qora.data.network; - -import org.qora.data.block.BlockData; +package org.qora.data.block; public class BlockSummaryData { diff --git a/src/main/java/org/qora/network/message/BlockSummariesMessage.java b/src/main/java/org/qora/network/message/BlockSummariesMessage.java index 74b53552..3a330292 100644 --- a/src/main/java/org/qora/network/message/BlockSummariesMessage.java +++ b/src/main/java/org/qora/network/message/BlockSummariesMessage.java @@ -7,7 +7,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import org.qora.data.network.BlockSummaryData; +import org.qora.data.block.BlockSummaryData; import org.qora.transform.Transformer; import org.qora.transform.block.BlockTransformer; diff --git a/src/main/java/org/qora/repository/BlockRepository.java b/src/main/java/org/qora/repository/BlockRepository.java index e072ff2f..65b9ffce 100644 --- a/src/main/java/org/qora/repository/BlockRepository.java +++ b/src/main/java/org/qora/repository/BlockRepository.java @@ -4,6 +4,7 @@ import java.util.List; import org.qora.api.model.BlockForgerSummary; import org.qora.data.block.BlockData; +import org.qora.data.block.BlockSummaryData; import org.qora.data.block.BlockTransactionData; import org.qora.data.transaction.TransactionData; @@ -117,6 +118,11 @@ public interface BlockRepository { */ public List getBlocks(int firstBlockHeight, int lastBlockHeight) throws DataException; + /** + * Returns block summaries for the passed height range. + */ + public List getBlockSummaries(int firstBlockHeight, int lastBlockHeight) throws DataException; + /** * Saves block into repository. * diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java index 69f9fbf9..43bf8034 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java @@ -9,6 +9,7 @@ import java.util.List; import org.qora.api.model.BlockForgerSummary; import org.qora.data.block.BlockData; +import org.qora.data.block.BlockSummaryData; import org.qora.data.block.BlockTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.repository.BlockRepository; @@ -265,6 +266,31 @@ public class HSQLDBBlockRepository implements BlockRepository { } } + @Override + public List getBlockSummaries(int firstBlockHeight, int lastBlockHeight) throws DataException { + String sql = "SELECT signature, height, generator FROM Blocks WHERE height BETWEEN ? AND ?"; + + List blockSummaries = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql, firstBlockHeight, lastBlockHeight)) { + if (resultSet == null) + return blockSummaries; + + do { + byte[] signature = resultSet.getBytes(1); + int height = resultSet.getInt(2); + byte[] generatorPublicKey = resultSet.getBytes(3); + + BlockSummaryData blockSummary = new BlockSummaryData(height, signature, generatorPublicKey); + blockSummaries.add(blockSummary); + } while (resultSet.next()); + + return blockSummaries; + } catch (SQLException e) { + throw new DataException("Unable to fetch height-ranged block summaries from repository", e); + } + } + @Override public void save(BlockData blockData) throws DataException { HSQLDBSaver saveHelper = new HSQLDBSaver("Blocks"); diff --git a/src/main/java/org/qora/utils/ByteArray.java b/src/main/java/org/qora/utils/ByteArray.java new file mode 100644 index 00000000..06c20b4b --- /dev/null +++ b/src/main/java/org/qora/utils/ByteArray.java @@ -0,0 +1,46 @@ +package org.qora.utils; + +import java.util.Comparator; + +import com.google.common.hash.HashCode; + +public class ByteArray implements Comparable { + + private static final Comparator COMPARATOR; + static { + COMPARATOR = Comparator.comparing(byteArray -> byteArray.comparable); + } + + private final String comparable; + + public final byte[] raw; + + public ByteArray(byte[] content) { + this.comparable = HashCode.fromBytes(content).toString(); + this.raw = content; + } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + + if (!(other instanceof ByteArray)) + return false; + + ByteArray otherByteArray = (ByteArray) other; + + return this.comparable.equals(otherByteArray.comparable); + } + + @Override + public int hashCode() { + return this.comparable.hashCode(); + } + + @Override + public int compareTo(ByteArray other) { + return COMPARATOR.compare(this, other); + } + +} diff --git a/src/test/java/org/qora/test/ByteArrayTests.java b/src/test/java/org/qora/test/ByteArrayTests.java new file mode 100644 index 00000000..93a9f67b --- /dev/null +++ b/src/test/java/org/qora/test/ByteArrayTests.java @@ -0,0 +1,66 @@ +package org.qora.test; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.junit.Before; +import org.junit.Test; +import org.qora.utils.ByteArray; + +public class ByteArrayTests { + + private static List testValues; + + @Before + public void createTestValues() { + Random random = new Random(); + + testValues = new ArrayList<>(); + for (int i = 0; i < 5; ++i) { + byte[] testValue = new byte[32]; + random.nextBytes(testValue); + testValues.add(testValue); + } + } + + public void fillMap(Map map) { + for (byte[] testValue : testValues) + map.put(new ByteArray(testValue), String.valueOf(map.size())); + } + + @Test + public void testByteArray() { + // Create two objects, which will have different references, but same content. + byte[] testValue = testValues.get(0); + ByteArray ba1 = new ByteArray(testValue); + ByteArray ba2 = new ByteArray(testValue); + + // Confirm JVM-assigned references are different + assertNotSame(ba1, ba2); + + // Confirm "equals" works as intended + assertTrue("equals did not return true", ba1.equals(ba2)); + assertEquals("ba1 not equal to ba2", ba1, ba2); + + // Confirm "hashCode" results match + assertEquals("hashCodes do not match", ba1.hashCode(), ba2.hashCode()); + } + + @Test + public void testByteArrayMap() { + Map testMap = new HashMap<>(); + fillMap(testMap); + + // Create new ByteArray object with an existing value. + ByteArray ba = new ByteArray(testValues.get(3)); + + // Confirm object can be found in map + assertTrue("ByteArray not found in map", testMap.containsKey(ba)); + } + +}