forked from Qortal/qortal
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:
parent
747f5e41cf
commit
57b982d2fb
@ -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);
|
||||
|
@ -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);
|
||||
|
37
src/main/java/org/qora/data/network/BlockSummaryData.java
Normal file
37
src/main/java/org/qora/data/network/BlockSummaryData.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user