Block summaries (repository/data/message/synchronizer) + BlockGenerator

Also refactored some tests.

Original commit was 06fe8fc, with commit message:
Initial implementation of random block generator, etc.
This commit is contained in:
catbref 2019-05-02 17:46:13 +01:00
parent 747f5e41cf
commit 57b982d2fb
9 changed files with 259 additions and 61 deletions

View File

@ -66,6 +66,15 @@ public class BlockGenerator extends Thread {
List<Block> newBlocks = new ArrayList<>();
while (running) {
// Sleep for a while
try {
repository.discardChanges(); // Free repository locks, if any
Thread.sleep(1000); // No point sleeping less than this as block timestamp millisecond values must be the same
} catch (InterruptedException e) {
// We've been interrupted - time to exit
return;
}
// Check blockchain hasn't changed
BlockData lastBlockData = blockRepository.getLastBlock();
if (previousBlock == null || !Arrays.equals(previousBlock.getSignature(), lastBlockData.getSignature())) {
@ -155,15 +164,6 @@ public class BlockGenerator extends Thread {
} finally {
blockchainLock.unlock();
}
// Sleep for a while
try {
repository.discardChanges(); // Free repository locks, if any
Thread.sleep(1000); // No point sleeping less than this as block timestamp millisecond values must be the same
} catch (InterruptedException e) {
// We've been interrupted - time to exit
return;
}
}
} catch (DataException e) {
LOGGER.warn("Repository issue while running block generator", e);

View File

@ -11,9 +11,12 @@ import org.qora.block.Block;
import org.qora.block.Block.ValidationResult;
import org.qora.block.GenesisBlock;
import org.qora.data.block.BlockData;
import org.qora.data.network.BlockSummaryData;
import org.qora.network.Peer;
import org.qora.network.message.BlockMessage;
import org.qora.network.message.BlockSummariesMessage;
import org.qora.network.message.GetBlockMessage;
import org.qora.network.message.GetBlockSummariesMessage;
import org.qora.network.message.GetSignaturesMessage;
import org.qora.network.message.Message;
import org.qora.network.message.Message.MessageType;
@ -264,6 +267,18 @@ public class Synchronizer {
return blockSignatures;
}
private List<BlockSummaryData> getBlockSummaries(Peer peer, byte[] parentSignature, int numberRequested) {
Message getBlockSummariesMessage = new GetBlockSummariesMessage(parentSignature, numberRequested);
Message message = peer.getResponse(getBlockSummariesMessage);
if (message == null || message.getType() != MessageType.BLOCK_SUMMARIES)
return null;
BlockSummariesMessage blockSummariesMessage = (BlockSummariesMessage) message;
return blockSummariesMessage.getBlockSummaries();
}
private List<byte[]> getBlockSignatures(Peer peer, byte[] parentSignature, int numberRequested) {
// TODO numberRequested is v2+ feature
Message getSignaturesMessage = new GetSignaturesMessage(parentSignature);

View File

@ -0,0 +1,37 @@
package org.qora.data.network;
import org.qora.data.block.BlockData;
public class BlockSummaryData {
// Properties
private int height;
private byte[] signature;
private byte[] generatorPublicKey;
// Constructors
public BlockSummaryData(int height, byte[] signature, byte[] generatorPublicKey) {
this.height = height;
this.signature = signature;
this.generatorPublicKey = generatorPublicKey;
}
public BlockSummaryData(BlockData blockData) {
this(blockData.getHeight(), blockData.getSignature(), blockData.getGeneratorPublicKey());
}
// Getters / setters
public int getHeight() {
return this.height;
}
public byte[] getSignature() {
return this.signature;
}
public byte[] getGeneratorPublicKey() {
return this.generatorPublicKey;
}
}

View File

@ -0,0 +1,81 @@
package org.qora.network.message;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.qora.data.block.BlockData;
import org.qora.data.network.BlockSummaryData;
import org.qora.transform.Transformer;
import org.qora.transform.block.BlockTransformer;
import com.google.common.primitives.Ints;
public class BlockSummariesMessage extends Message {
private static final int BLOCK_SUMMARY_LENGTH = BlockTransformer.BLOCK_SIGNATURE_LENGTH + Transformer.INT_LENGTH + Transformer.PUBLIC_KEY_LENGTH;
private List<BlockSummaryData> blockSummaries;
public BlockSummariesMessage(List<BlockData> blocksData) {
// Extract what we need from block data
this(-1, blocksData.stream().map(blockData -> new BlockSummaryData(blockData)).collect(Collectors.toList()));
}
private BlockSummariesMessage(int id, List<BlockSummaryData> blockSummaries) {
super(id, MessageType.BLOCK_SUMMARIES);
this.blockSummaries = blockSummaries;
}
public List<BlockSummaryData> getBlockSummaries() {
return this.blockSummaries;
}
public static Message fromByteBuffer(int id, ByteBuffer bytes) throws UnsupportedEncodingException {
int count = bytes.getInt();
if (bytes.remaining() != count * BLOCK_SUMMARY_LENGTH)
return null;
List<BlockSummaryData> blockSummaries = new ArrayList<>();
for (int i = 0; i < count; ++i) {
int height = bytes.getInt();
byte[] signature = new byte[BlockTransformer.BLOCK_SIGNATURE_LENGTH];
bytes.get(signature);
byte[] generatorPublicKey = new byte[Transformer.PUBLIC_KEY_LENGTH];
bytes.get(generatorPublicKey);
BlockSummaryData blockSummary = new BlockSummaryData(height, signature, generatorPublicKey);
blockSummaries.add(blockSummary);
}
return new BlockSummariesMessage(id, blockSummaries);
}
@Override
protected byte[] toData() {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bytes.write(Ints.toByteArray(this.blockSummaries.size()));
for (BlockSummaryData blockSummary : this.blockSummaries) {
bytes.write(blockSummary.getHeight());
bytes.write(blockSummary.getSignature());
bytes.write(blockSummary.getGeneratorPublicKey());
}
return bytes.toByteArray();
} catch (IOException e) {
return null;
}
}
}

View File

@ -0,0 +1,64 @@
package org.qora.network.message;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import org.qora.transform.Transformer;
import org.qora.transform.block.BlockTransformer;
public class GetBlockSummariesMessage extends Message {
private static final int BLOCK_SIGNATURE_LENGTH = BlockTransformer.BLOCK_SIGNATURE_LENGTH;
private byte[] parentSignature;
private int numberRequested;
public GetBlockSummariesMessage(byte[] parentSignature, int numberRequested) {
this(-1, parentSignature, numberRequested);
}
private GetBlockSummariesMessage(int id, byte[] parentSignature, int numberRequested) {
super(id, MessageType.GET_BLOCK_SUMMARIES);
this.parentSignature = parentSignature;
this.numberRequested = numberRequested;
}
public byte[] getParentSignature() {
return this.parentSignature;
}
public int getNumberRequested() {
return this.numberRequested;
}
public static Message fromByteBuffer(int id, ByteBuffer bytes) throws UnsupportedEncodingException {
if (bytes.remaining() != BLOCK_SIGNATURE_LENGTH + Transformer.INT_LENGTH)
return null;
byte[] parentSignature = new byte[BLOCK_SIGNATURE_LENGTH];
bytes.get(parentSignature);
int numberRequested = bytes.getInt();
return new GetBlockSummariesMessage(id, parentSignature, numberRequested);
}
@Override
protected byte[] toData() {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bytes.write(this.parentSignature);
bytes.write(this.numberRequested);
return bytes.toByteArray();
} catch (IOException e) {
return null;
}
}
}

View File

@ -40,7 +40,9 @@ public abstract class Message {
VERSION(10),
PEER_ID(11),
PROOF(12),
PEERS_V2(13);
PEERS_V2(13),
GET_BLOCK_SUMMARIES(14),
BLOCK_SUMMARIES(15);
public final int value;
public final Method fromByteBuffer;

View File

@ -112,6 +112,11 @@ public interface BlockRepository {
*/
public List<BlockData> getBlocksWithGenerator(byte[] generatorPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException;
/**
* Returns blocks within height range.
*/
public List<BlockData> getBlocks(int firstBlockHeight, int lastBlockHeight) throws DataException;
/**
* Saves block into repository.
*

View File

@ -215,6 +215,26 @@ public class HSQLDBBlockRepository implements BlockRepository {
}
}
@Override
public List<BlockData> getBlocks(int firstBlockHeight, int lastBlockHeight) throws DataException {
String sql = "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE height BETWEEN ? AND ?";
List<BlockData> blockData = new ArrayList<>();
try (ResultSet resultSet = this.repository.checkedExecute(sql, firstBlockHeight, lastBlockHeight)) {
if (resultSet == null)
return blockData;
do {
blockData.add(getBlockFromResultSet(resultSet));
} while (resultSet.next());
return blockData;
} catch (SQLException e) {
throw new DataException("Unable to fetch height-ranged blocks from repository", e);
}
}
@Override
public void save(BlockData blockData) throws DataException {
HSQLDBSaver saveHelper = new HSQLDBSaver("Blocks");

View File

@ -27,6 +27,7 @@ import org.qora.data.transaction.MultiPaymentTransactionData;
import org.qora.data.transaction.PaymentTransactionData;
import org.qora.data.transaction.RegisterNameTransactionData;
import org.qora.data.transaction.SellNameTransactionData;
import org.qora.data.transaction.TransactionData;
import org.qora.data.transaction.TransferAssetTransactionData;
import org.qora.data.transaction.UpdateNameTransactionData;
import org.qora.data.transaction.VoteOnPollTransactionData;
@ -156,9 +157,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, paymentTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(paymentTransactionData);
block.sign();
Block block = forgeBlock(paymentTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -217,9 +216,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, registerNameTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(registerNameTransactionData);
block.sign();
Block block = forgeBlock(registerNameTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -273,9 +270,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, updateNameTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(updateNameTransactionData);
block.sign();
Block block = forgeBlock(updateNameTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -319,9 +314,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, sellNameTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(sellNameTransactionData);
block.sign();
Block block = forgeBlock(sellNameTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -371,9 +364,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, cancelSellNameTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(cancelSellNameTransactionData);
block.sign();
Block block = forgeBlock(cancelSellNameTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -418,9 +409,7 @@ public class TransactionTests extends Common {
byte[] buyersReference = somePaymentTransaction.getTransactionData().getSignature();
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(somePaymentTransaction.getTransactionData());
block.sign();
Block block = forgeBlock(somePaymentTransaction.getTransactionData());
block.process();
repository.saveChanges();
@ -437,9 +426,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, buyNameTransaction.isValid());
// Forge new block with transaction
block = new Block(repository, parentBlockData, generator);
block.addTransaction(buyNameTransactionData);
block.sign();
block = forgeBlock(buyNameTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -490,9 +477,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, createPollTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(createPollTransactionData);
block.sign();
Block block = forgeBlock(createPollTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -549,9 +534,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, voteOnPollTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(voteOnPollTransactionData);
block.sign();
Block block = forgeBlock(voteOnPollTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -617,9 +600,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, issueAssetTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(issueAssetTransactionData);
block.sign();
Block block = forgeBlock(issueAssetTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -707,9 +688,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, transferAssetTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(transferAssetTransactionData);
block.sign();
Block block = forgeBlock(transferAssetTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -787,9 +766,7 @@ public class TransactionTests extends Common {
byte[] buyersReference = somePaymentTransaction.getTransactionData().getSignature();
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(somePaymentTransaction.getTransactionData());
block.sign();
Block block = forgeBlock(somePaymentTransaction.getTransactionData());
block.process();
repository.saveChanges();
@ -811,9 +788,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, createOrderTransaction.isValid());
// Forge new block with transaction
block = new Block(repository, parentBlockData, generator);
block.addTransaction(createOrderTransactionData);
block.sign();
block = forgeBlock(createOrderTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -893,9 +868,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, cancelOrderTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(cancelOrderTransactionData);
block.sign();
Block block = forgeBlock(cancelOrderTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -968,9 +941,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, createOrderTransaction.isValid());
// Forge new block with transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(createOrderTransactionData);
block.sign();
Block block = forgeBlock(createOrderTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -1078,9 +1049,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, multiPaymentTransaction.isValid());
// Forge new block with payment transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(multiPaymentTransactionData);
block.sign();
Block block = forgeBlock(multiPaymentTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -1148,9 +1117,7 @@ public class TransactionTests extends Common {
assertEquals(ValidationResult.OK, messageTransaction.isValid());
// Forge new block with message transaction
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(messageTransactionData);
block.sign();
Block block = forgeBlock(messageTransactionData);
assertTrue("Block signatures invalid", block.isSignatureValid());
assertEquals("Block is invalid", Block.ValidationResult.OK, block.isValid());
@ -1174,4 +1141,11 @@ public class TransactionTests extends Common {
assertTrue("Recipient's new balance incorrect", expectedBalance.compareTo(actualBalance) == 0);
}
private Block forgeBlock(TransactionData transactionData) throws DataException {
Block block = new Block(repository, parentBlockData, generator);
block.addTransaction(transactionData);
block.sign();
return block;
}
}