forked from Qortal/qortal
Initial work on BLOCK_SUMMARIES_V2, part of a bigger arc to improve synchronization.
Touches quite a few files because: * Deprecate HEIGHT_V2 because it doesn't contain enough info to be fully useful during sync. Newer peers will re-use BLOCK_SUMMARIES_V2. * For newer peers, instead of sending / broadcasting HEIGHT_V2, send top N block summaries instead, to avoid requests for minor reorgs. * When responding to GET_BLOCK, and we don't actually have the requested block, we currently send an empty BLOCK_SUMMARIES message instead of not responding, which would cause a slow timeout in Synchronizer. This pattern has spread to other network message response code, so now we introduce a generic 'unknown' message type for all these cases. * Remove PeerChainTipData class entirely and re-use BlockSummaryData instead. * Each Peer instance used to hold PeerChainTipData - essentially single latest block summary - but now holds a List of latest block summaries. * PeerChainTipData getter/setter methods modified for compatibility at this point in time. * Repository methods that return BlockSummaryData (or lists of) now try to fully populate them, including newly added block reference field. * Re-worked Peer.canUseCommonBlockData() to be more readable * Cherry-picked patch to Message.fromByteBuffer() to pass an empty, read-only ByteBuffer to subclass fromByteBuffer() methods, instead of null. This allows natural use of BufferUnderflowException if a subclass tries to use read(), or hasRemaining(), etc. from an empty data-payload message. Previously this could have caused an NPE.
This commit is contained in:
parent
863a5eff97
commit
94cdc10151
@ -1,7 +1,7 @@
|
|||||||
package org.qortal.api.model;
|
package org.qortal.api.model;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import org.qortal.data.network.PeerChainTipData;
|
import org.qortal.data.block.BlockSummaryData;
|
||||||
import org.qortal.data.network.PeerData;
|
import org.qortal.data.network.PeerData;
|
||||||
import org.qortal.network.Handshake;
|
import org.qortal.network.Handshake;
|
||||||
import org.qortal.network.Peer;
|
import org.qortal.network.Peer;
|
||||||
@ -63,11 +63,11 @@ public class ConnectedPeer {
|
|||||||
this.age = "connecting...";
|
this.age = "connecting...";
|
||||||
}
|
}
|
||||||
|
|
||||||
PeerChainTipData peerChainTipData = peer.getChainTipData();
|
BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
if (peerChainTipData != null) {
|
if (peerChainTipData != null) {
|
||||||
this.lastHeight = peerChainTipData.getLastHeight();
|
this.lastHeight = peerChainTipData.getHeight();
|
||||||
this.lastBlockSignature = peerChainTipData.getLastBlockSignature();
|
this.lastBlockSignature = peerChainTipData.getSignature();
|
||||||
this.lastBlockTimestamp = peerChainTipData.getLastBlockTimestamp();
|
this.lastBlockTimestamp = peerChainTipData.getTimestamp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,9 @@ import org.qortal.data.block.CommonBlockData;
|
|||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
import org.qortal.network.Network;
|
import org.qortal.network.Network;
|
||||||
import org.qortal.network.Peer;
|
import org.qortal.network.Peer;
|
||||||
|
import org.qortal.network.message.BlockSummariesV2Message;
|
||||||
|
import org.qortal.network.message.HeightV2Message;
|
||||||
|
import org.qortal.network.message.Message;
|
||||||
import org.qortal.repository.BlockRepository;
|
import org.qortal.repository.BlockRepository;
|
||||||
import org.qortal.repository.DataException;
|
import org.qortal.repository.DataException;
|
||||||
import org.qortal.repository.Repository;
|
import org.qortal.repository.Repository;
|
||||||
@ -431,16 +434,9 @@ public class BlockMinter extends Thread {
|
|||||||
blockchainLock.unlock();
|
blockchainLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newBlockMinted) {
|
if (newBlockMinted) {
|
||||||
// Broadcast our new chain to network
|
// Broadcast our new chain to network
|
||||||
BlockData newBlockData = newBlock.getBlockData();
|
Network.getInstance().broadcastOurChain();
|
||||||
|
|
||||||
Network network = Network.getInstance();
|
|
||||||
network.broadcast(broadcastPeer -> network.buildHeightMessage(broadcastPeer, newBlockData));
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// We've been interrupted - time to exit
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
|
@ -45,7 +45,6 @@ import org.qortal.data.account.AccountData;
|
|||||||
import org.qortal.data.block.BlockData;
|
import org.qortal.data.block.BlockData;
|
||||||
import org.qortal.data.block.BlockSummaryData;
|
import org.qortal.data.block.BlockSummaryData;
|
||||||
import org.qortal.data.naming.NameData;
|
import org.qortal.data.naming.NameData;
|
||||||
import org.qortal.data.network.PeerChainTipData;
|
|
||||||
import org.qortal.data.network.PeerData;
|
import org.qortal.data.network.PeerData;
|
||||||
import org.qortal.data.transaction.ChatTransactionData;
|
import org.qortal.data.transaction.ChatTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
@ -731,25 +730,25 @@ public class Controller extends Thread {
|
|||||||
|
|
||||||
public static final Predicate<Peer> hasNoRecentBlock = peer -> {
|
public static final Predicate<Peer> hasNoRecentBlock = peer -> {
|
||||||
final Long minLatestBlockTimestamp = getMinimumLatestBlockTimestamp();
|
final Long minLatestBlockTimestamp = getMinimumLatestBlockTimestamp();
|
||||||
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
final BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
return peerChainTipData == null || peerChainTipData.getLastBlockTimestamp() == null || peerChainTipData.getLastBlockTimestamp() < minLatestBlockTimestamp;
|
return peerChainTipData == null || peerChainTipData.getTimestamp() == null || peerChainTipData.getTimestamp() < minLatestBlockTimestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final Predicate<Peer> hasNoOrSameBlock = peer -> {
|
public static final Predicate<Peer> hasNoOrSameBlock = peer -> {
|
||||||
final BlockData latestBlockData = getInstance().getChainTip();
|
final BlockData latestBlockData = getInstance().getChainTip();
|
||||||
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
final BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
return peerChainTipData == null || peerChainTipData.getLastBlockSignature() == null || Arrays.equals(latestBlockData.getSignature(), peerChainTipData.getLastBlockSignature());
|
return peerChainTipData == null || peerChainTipData.getSignature() == null || Arrays.equals(latestBlockData.getSignature(), peerChainTipData.getSignature());
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final Predicate<Peer> hasOnlyGenesisBlock = peer -> {
|
public static final Predicate<Peer> hasOnlyGenesisBlock = peer -> {
|
||||||
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
final BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
return peerChainTipData == null || peerChainTipData.getLastHeight() == null || peerChainTipData.getLastHeight() == 1;
|
return peerChainTipData == null || peerChainTipData.getHeight() == 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final Predicate<Peer> hasInferiorChainTip = peer -> {
|
public static final Predicate<Peer> hasInferiorChainTip = peer -> {
|
||||||
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
final BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
final List<ByteArray> inferiorChainTips = Synchronizer.getInstance().inferiorChainSignatures;
|
final List<ByteArray> inferiorChainTips = Synchronizer.getInstance().inferiorChainSignatures;
|
||||||
return peerChainTipData == null || peerChainTipData.getLastBlockSignature() == null || inferiorChainTips.contains(ByteArray.wrap(peerChainTipData.getLastBlockSignature()));
|
return peerChainTipData == null || peerChainTipData.getSignature() == null || inferiorChainTips.contains(ByteArray.wrap(peerChainTipData.getSignature()));
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final Predicate<Peer> hasOldVersion = peer -> {
|
public static final Predicate<Peer> hasOldVersion = peer -> {
|
||||||
@ -1011,8 +1010,7 @@ public class Controller extends Thread {
|
|||||||
network.broadcast(peer -> peer.isOutbound() ? network.buildPeersMessage(peer) : new GetPeersMessage());
|
network.broadcast(peer -> peer.isOutbound() ? network.buildPeersMessage(peer) : new GetPeersMessage());
|
||||||
|
|
||||||
// Send our current height
|
// Send our current height
|
||||||
BlockData latestBlockData = getChainTip();
|
network.broadcastOurChain();
|
||||||
network.broadcast(peer -> network.buildHeightMessage(peer, latestBlockData));
|
|
||||||
|
|
||||||
// Request unconfirmed transaction signatures, but only if we're up-to-date.
|
// Request unconfirmed transaction signatures, but only if we're up-to-date.
|
||||||
// If we're NOT up-to-date then priority is synchronizing first
|
// If we're NOT up-to-date then priority is synchronizing first
|
||||||
@ -1219,6 +1217,10 @@ public class Controller extends Thread {
|
|||||||
onNetworkHeightV2Message(peer, message);
|
onNetworkHeightV2Message(peer, message);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BLOCK_SUMMARIES_V2:
|
||||||
|
onNetworkBlockSummariesV2Message(peer, message);
|
||||||
|
break;
|
||||||
|
|
||||||
case GET_TRANSACTION:
|
case GET_TRANSACTION:
|
||||||
TransactionImporter.getInstance().onNetworkGetTransactionMessage(peer, message);
|
TransactionImporter.getInstance().onNetworkGetTransactionMessage(peer, message);
|
||||||
break;
|
break;
|
||||||
@ -1373,8 +1375,10 @@ public class Controller extends Thread {
|
|||||||
// Send valid, yet unexpected message type in response, so peer's synchronizer doesn't have to wait for timeout
|
// Send valid, yet unexpected message type in response, so peer's synchronizer doesn't have to wait for timeout
|
||||||
LOGGER.debug(() -> String.format("Sending 'block unknown' response to peer %s for GET_BLOCK request for unknown block %s", peer, Base58.encode(signature)));
|
LOGGER.debug(() -> String.format("Sending 'block unknown' response to peer %s for GET_BLOCK request for unknown block %s", peer, Base58.encode(signature)));
|
||||||
|
|
||||||
// We'll send empty block summaries message as it's very short
|
// Send generic 'unknown' message as it's very short
|
||||||
Message blockUnknownMessage = new BlockSummariesMessage(Collections.emptyList());
|
Message blockUnknownMessage = peer.getPeersVersion() >= GenericUnknownMessage.MINIMUM_PEER_VERSION
|
||||||
|
? new GenericUnknownMessage()
|
||||||
|
: new BlockSummariesMessage(Collections.emptyList());
|
||||||
blockUnknownMessage.setId(message.getId());
|
blockUnknownMessage.setId(message.getId());
|
||||||
if (!peer.sendMessage(blockUnknownMessage))
|
if (!peer.sendMessage(blockUnknownMessage))
|
||||||
peer.disconnect("failed to send block-unknown response");
|
peer.disconnect("failed to send block-unknown response");
|
||||||
@ -1423,11 +1427,15 @@ public class Controller extends Thread {
|
|||||||
this.stats.getBlockSummariesStats.requests.incrementAndGet();
|
this.stats.getBlockSummariesStats.requests.incrementAndGet();
|
||||||
|
|
||||||
// If peer's parent signature matches our latest block signature
|
// If peer's parent signature matches our latest block signature
|
||||||
// then we can short-circuit with an empty response
|
// then we have no blocks after that and can short-circuit with an empty response
|
||||||
BlockData chainTip = getChainTip();
|
BlockData chainTip = getChainTip();
|
||||||
if (chainTip != null && Arrays.equals(parentSignature, chainTip.getSignature())) {
|
if (chainTip != null && Arrays.equals(parentSignature, chainTip.getSignature())) {
|
||||||
Message blockSummariesMessage = new BlockSummariesMessage(Collections.emptyList());
|
Message blockSummariesMessage = peer.getPeersVersion() >= BlockSummariesV2Message.MINIMUM_PEER_VERSION
|
||||||
|
? new BlockSummariesV2Message(Collections.emptyList())
|
||||||
|
: new BlockSummariesMessage(Collections.emptyList());
|
||||||
|
|
||||||
blockSummariesMessage.setId(message.getId());
|
blockSummariesMessage.setId(message.getId());
|
||||||
|
|
||||||
if (!peer.sendMessage(blockSummariesMessage))
|
if (!peer.sendMessage(blockSummariesMessage))
|
||||||
peer.disconnect("failed to send block summaries");
|
peer.disconnect("failed to send block summaries");
|
||||||
|
|
||||||
@ -1483,7 +1491,9 @@ public class Controller extends Thread {
|
|||||||
this.stats.getBlockSummariesStats.fullyFromCache.incrementAndGet();
|
this.stats.getBlockSummariesStats.fullyFromCache.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
Message blockSummariesMessage = new BlockSummariesMessage(blockSummaries);
|
Message blockSummariesMessage = peer.getPeersVersion() >= BlockSummariesV2Message.MINIMUM_PEER_VERSION
|
||||||
|
? new BlockSummariesV2Message(blockSummaries)
|
||||||
|
: new BlockSummariesMessage(blockSummaries);
|
||||||
blockSummariesMessage.setId(message.getId());
|
blockSummariesMessage.setId(message.getId());
|
||||||
if (!peer.sendMessage(blockSummariesMessage))
|
if (!peer.sendMessage(blockSummariesMessage))
|
||||||
peer.disconnect("failed to send block summaries");
|
peer.disconnect("failed to send block summaries");
|
||||||
@ -1558,18 +1568,48 @@ public class Controller extends Thread {
|
|||||||
// If peer is inbound and we've not updated their height
|
// If peer is inbound and we've not updated their height
|
||||||
// then this is probably their initial HEIGHT_V2 message
|
// then this is probably their initial HEIGHT_V2 message
|
||||||
// so they need a corresponding HEIGHT_V2 message from us
|
// so they need a corresponding HEIGHT_V2 message from us
|
||||||
if (!peer.isOutbound() && (peer.getChainTipData() == null || peer.getChainTipData().getLastHeight() == null))
|
if (!peer.isOutbound() && peer.getChainTipData() == null) {
|
||||||
peer.sendMessage(Network.getInstance().buildHeightMessage(peer, getChainTip()));
|
Message responseMessage = Network.getInstance().buildHeightOrChainTipInfo(peer);
|
||||||
|
|
||||||
|
if (responseMessage == null || !peer.sendMessage(responseMessage)) {
|
||||||
|
peer.disconnect("failed to send our chain tip info");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update peer chain tip data
|
// Update peer chain tip data
|
||||||
PeerChainTipData newChainTipData = new PeerChainTipData(heightV2Message.getHeight(), heightV2Message.getSignature(), heightV2Message.getTimestamp(), heightV2Message.getMinterPublicKey());
|
BlockSummaryData newChainTipData = new BlockSummaryData(heightV2Message.getHeight(), heightV2Message.getSignature(), heightV2Message.getMinterPublicKey(), heightV2Message.getTimestamp());
|
||||||
peer.setChainTipData(newChainTipData);
|
peer.setChainTipData(newChainTipData);
|
||||||
|
|
||||||
// Potentially synchronize
|
// Potentially synchronize
|
||||||
Synchronizer.getInstance().requestSync();
|
Synchronizer.getInstance().requestSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onNetworkBlockSummariesV2Message(Peer peer, Message message) {
|
||||||
|
BlockSummariesV2Message blockSummariesV2Message = (BlockSummariesV2Message) message;
|
||||||
|
|
||||||
|
if (!Settings.getInstance().isLite()) {
|
||||||
|
// If peer is inbound and we've not updated their height
|
||||||
|
// then this is probably their initial BLOCK_SUMMARIES_V2 message
|
||||||
|
// so they need a corresponding BLOCK_SUMMARIES_V2 message from us
|
||||||
|
if (!peer.isOutbound() && peer.getChainTipData() == null) {
|
||||||
|
Message responseMessage = Network.getInstance().buildHeightOrChainTipInfo(peer);
|
||||||
|
|
||||||
|
if (responseMessage == null || !peer.sendMessage(responseMessage)) {
|
||||||
|
peer.disconnect("failed to send our chain tip info");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update peer chain tip data
|
||||||
|
peer.setChainTipSummaries(blockSummariesV2Message.getBlockSummaries());
|
||||||
|
|
||||||
|
// Potentially synchronize
|
||||||
|
Synchronizer.getInstance().requestSync();
|
||||||
|
}
|
||||||
|
|
||||||
private void onNetworkGetAccountMessage(Peer peer, Message message) {
|
private void onNetworkGetAccountMessage(Peer peer, Message message) {
|
||||||
GetAccountMessage getAccountMessage = (GetAccountMessage) message;
|
GetAccountMessage getAccountMessage = (GetAccountMessage) message;
|
||||||
String address = getAccountMessage.getAddress();
|
String address = getAccountMessage.getAddress();
|
||||||
@ -1585,8 +1625,8 @@ public class Controller extends Thread {
|
|||||||
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
||||||
LOGGER.debug(() -> String.format("Sending 'account unknown' response to peer %s for GET_ACCOUNT request for unknown account %s", peer, address));
|
LOGGER.debug(() -> String.format("Sending 'account unknown' response to peer %s for GET_ACCOUNT request for unknown account %s", peer, address));
|
||||||
|
|
||||||
// We'll send empty block summaries message as it's very short
|
// Send generic 'unknown' message as it's very short
|
||||||
Message accountUnknownMessage = new BlockSummariesMessage(Collections.emptyList());
|
Message accountUnknownMessage = new GenericUnknownMessage();
|
||||||
accountUnknownMessage.setId(message.getId());
|
accountUnknownMessage.setId(message.getId());
|
||||||
if (!peer.sendMessage(accountUnknownMessage))
|
if (!peer.sendMessage(accountUnknownMessage))
|
||||||
peer.disconnect("failed to send account-unknown response");
|
peer.disconnect("failed to send account-unknown response");
|
||||||
@ -1621,8 +1661,8 @@ public class Controller extends Thread {
|
|||||||
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
||||||
LOGGER.debug(() -> String.format("Sending 'account unknown' response to peer %s for GET_ACCOUNT_BALANCE request for unknown account %s and asset ID %d", peer, address, assetId));
|
LOGGER.debug(() -> String.format("Sending 'account unknown' response to peer %s for GET_ACCOUNT_BALANCE request for unknown account %s and asset ID %d", peer, address, assetId));
|
||||||
|
|
||||||
// We'll send empty block summaries message as it's very short
|
// Send generic 'unknown' message as it's very short
|
||||||
Message accountUnknownMessage = new BlockSummariesMessage(Collections.emptyList());
|
Message accountUnknownMessage = new GenericUnknownMessage();
|
||||||
accountUnknownMessage.setId(message.getId());
|
accountUnknownMessage.setId(message.getId());
|
||||||
if (!peer.sendMessage(accountUnknownMessage))
|
if (!peer.sendMessage(accountUnknownMessage))
|
||||||
peer.disconnect("failed to send account-unknown response");
|
peer.disconnect("failed to send account-unknown response");
|
||||||
@ -1665,8 +1705,8 @@ public class Controller extends Thread {
|
|||||||
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
||||||
LOGGER.debug(() -> String.format("Sending 'account unknown' response to peer %s for GET_ACCOUNT_TRANSACTIONS request for unknown account %s", peer, address));
|
LOGGER.debug(() -> String.format("Sending 'account unknown' response to peer %s for GET_ACCOUNT_TRANSACTIONS request for unknown account %s", peer, address));
|
||||||
|
|
||||||
// We'll send empty block summaries message as it's very short
|
// Send generic 'unknown' message as it's very short
|
||||||
Message accountUnknownMessage = new BlockSummariesMessage(Collections.emptyList());
|
Message accountUnknownMessage = new GenericUnknownMessage();
|
||||||
accountUnknownMessage.setId(message.getId());
|
accountUnknownMessage.setId(message.getId());
|
||||||
if (!peer.sendMessage(accountUnknownMessage))
|
if (!peer.sendMessage(accountUnknownMessage))
|
||||||
peer.disconnect("failed to send account-unknown response");
|
peer.disconnect("failed to send account-unknown response");
|
||||||
@ -1702,8 +1742,8 @@ public class Controller extends Thread {
|
|||||||
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
||||||
LOGGER.debug(() -> String.format("Sending 'account unknown' response to peer %s for GET_ACCOUNT_NAMES request for unknown account %s", peer, address));
|
LOGGER.debug(() -> String.format("Sending 'account unknown' response to peer %s for GET_ACCOUNT_NAMES request for unknown account %s", peer, address));
|
||||||
|
|
||||||
// We'll send empty block summaries message as it's very short
|
// Send generic 'unknown' message as it's very short
|
||||||
Message accountUnknownMessage = new BlockSummariesMessage(Collections.emptyList());
|
Message accountUnknownMessage = new GenericUnknownMessage();
|
||||||
accountUnknownMessage.setId(message.getId());
|
accountUnknownMessage.setId(message.getId());
|
||||||
if (!peer.sendMessage(accountUnknownMessage))
|
if (!peer.sendMessage(accountUnknownMessage))
|
||||||
peer.disconnect("failed to send account-unknown response");
|
peer.disconnect("failed to send account-unknown response");
|
||||||
@ -1737,8 +1777,8 @@ public class Controller extends Thread {
|
|||||||
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
// Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout
|
||||||
LOGGER.debug(() -> String.format("Sending 'name unknown' response to peer %s for GET_NAME request for unknown name %s", peer, name));
|
LOGGER.debug(() -> String.format("Sending 'name unknown' response to peer %s for GET_NAME request for unknown name %s", peer, name));
|
||||||
|
|
||||||
// We'll send empty block summaries message as it's very short
|
// Send generic 'unknown' message as it's very short
|
||||||
Message nameUnknownMessage = new BlockSummariesMessage(Collections.emptyList());
|
Message nameUnknownMessage = new GenericUnknownMessage();
|
||||||
nameUnknownMessage.setId(message.getId());
|
nameUnknownMessage.setId(message.getId());
|
||||||
if (!peer.sendMessage(nameUnknownMessage))
|
if (!peer.sendMessage(nameUnknownMessage))
|
||||||
peer.disconnect("failed to send name-unknown response");
|
peer.disconnect("failed to send name-unknown response");
|
||||||
@ -1786,14 +1826,14 @@ public class Controller extends Thread {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
if (peerChainTipData == null) {
|
if (peerChainTipData == null) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disregard peers that don't have a recent block
|
// Disregard peers that don't have a recent block
|
||||||
if (peerChainTipData.getLastBlockTimestamp() == null || peerChainTipData.getLastBlockTimestamp() < minLatestBlockTimestamp) {
|
if (peerChainTipData.getTimestamp() == null || peerChainTipData.getTimestamp() < minLatestBlockTimestamp) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import org.qortal.block.BlockChain;
|
|||||||
import org.qortal.data.block.BlockData;
|
import org.qortal.data.block.BlockData;
|
||||||
import org.qortal.data.block.BlockSummaryData;
|
import org.qortal.data.block.BlockSummaryData;
|
||||||
import org.qortal.data.block.CommonBlockData;
|
import org.qortal.data.block.CommonBlockData;
|
||||||
import org.qortal.data.network.PeerChainTipData;
|
|
||||||
import org.qortal.data.transaction.RewardShareTransactionData;
|
import org.qortal.data.transaction.RewardShareTransactionData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
import org.qortal.event.Event;
|
import org.qortal.event.Event;
|
||||||
@ -282,7 +281,7 @@ public class Synchronizer extends Thread {
|
|||||||
BlockData priorChainTip = Controller.getInstance().getChainTip();
|
BlockData priorChainTip = Controller.getInstance().getChainTip();
|
||||||
|
|
||||||
synchronized (this.syncLock) {
|
synchronized (this.syncLock) {
|
||||||
this.syncPercent = (priorChainTip.getHeight() * 100) / peer.getChainTipData().getLastHeight();
|
this.syncPercent = (priorChainTip.getHeight() * 100) / peer.getChainTipData().getHeight();
|
||||||
|
|
||||||
// Only update SysTray if we're potentially changing height
|
// Only update SysTray if we're potentially changing height
|
||||||
if (this.syncPercent < 100) {
|
if (this.syncPercent < 100) {
|
||||||
@ -312,7 +311,7 @@ public class Synchronizer extends Thread {
|
|||||||
|
|
||||||
case INFERIOR_CHAIN: {
|
case INFERIOR_CHAIN: {
|
||||||
// Update our list of inferior chain tips
|
// Update our list of inferior chain tips
|
||||||
ByteArray inferiorChainSignature = ByteArray.wrap(peer.getChainTipData().getLastBlockSignature());
|
ByteArray inferiorChainSignature = ByteArray.wrap(peer.getChainTipData().getSignature());
|
||||||
if (!inferiorChainSignatures.contains(inferiorChainSignature))
|
if (!inferiorChainSignatures.contains(inferiorChainSignature))
|
||||||
inferiorChainSignatures.add(inferiorChainSignature);
|
inferiorChainSignatures.add(inferiorChainSignature);
|
||||||
|
|
||||||
@ -320,7 +319,8 @@ public class Synchronizer extends Thread {
|
|||||||
LOGGER.debug(() -> String.format("Refused to synchronize with peer %s (%s)", peer, syncResult.name()));
|
LOGGER.debug(() -> String.format("Refused to synchronize with peer %s (%s)", peer, syncResult.name()));
|
||||||
|
|
||||||
// Notify peer of our superior chain
|
// Notify peer of our superior chain
|
||||||
if (!peer.sendMessage(Network.getInstance().buildHeightMessage(peer, priorChainTip)))
|
Message message = Network.getInstance().buildHeightOrChainTipInfo(peer);
|
||||||
|
if (message == null || !peer.sendMessage(message))
|
||||||
peer.disconnect("failed to notify peer of our superior chain");
|
peer.disconnect("failed to notify peer of our superior chain");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -341,7 +341,7 @@ public class Synchronizer extends Thread {
|
|||||||
// fall-through...
|
// fall-through...
|
||||||
case NOTHING_TO_DO: {
|
case NOTHING_TO_DO: {
|
||||||
// Update our list of inferior chain tips
|
// Update our list of inferior chain tips
|
||||||
ByteArray inferiorChainSignature = ByteArray.wrap(peer.getChainTipData().getLastBlockSignature());
|
ByteArray inferiorChainSignature = ByteArray.wrap(peer.getChainTipData().getSignature());
|
||||||
if (!inferiorChainSignatures.contains(inferiorChainSignature))
|
if (!inferiorChainSignatures.contains(inferiorChainSignature))
|
||||||
inferiorChainSignatures.add(inferiorChainSignature);
|
inferiorChainSignatures.add(inferiorChainSignature);
|
||||||
|
|
||||||
@ -369,8 +369,7 @@ public class Synchronizer extends Thread {
|
|||||||
// Reset our cache of inferior chains
|
// Reset our cache of inferior chains
|
||||||
inferiorChainSignatures.clear();
|
inferiorChainSignatures.clear();
|
||||||
|
|
||||||
Network network = Network.getInstance();
|
Network.getInstance().broadcastOurChain();
|
||||||
network.broadcast(broadcastPeer -> network.buildHeightMessage(broadcastPeer, newChainTip));
|
|
||||||
|
|
||||||
EventBus.INSTANCE.notify(new NewChainTipEvent(priorChainTip, newChainTip));
|
EventBus.INSTANCE.notify(new NewChainTipEvent(priorChainTip, newChainTip));
|
||||||
}
|
}
|
||||||
@ -513,13 +512,13 @@ public class Synchronizer extends Thread {
|
|||||||
final BlockData ourLatestBlockData = repository.getBlockRepository().getLastBlock();
|
final BlockData ourLatestBlockData = repository.getBlockRepository().getLastBlock();
|
||||||
final int ourInitialHeight = ourLatestBlockData.getHeight();
|
final int ourInitialHeight = ourLatestBlockData.getHeight();
|
||||||
|
|
||||||
PeerChainTipData peerChainTipData = peer.getChainTipData();
|
BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
int peerHeight = peerChainTipData.getLastHeight();
|
int peerHeight = peerChainTipData.getHeight();
|
||||||
byte[] peersLastBlockSignature = peerChainTipData.getLastBlockSignature();
|
byte[] peersLastBlockSignature = peerChainTipData.getSignature();
|
||||||
|
|
||||||
byte[] ourLastBlockSignature = ourLatestBlockData.getSignature();
|
byte[] ourLastBlockSignature = ourLatestBlockData.getSignature();
|
||||||
LOGGER.debug(String.format("Fetching summaries from peer %s at height %d, sig %.8s, ts %d; our height %d, sig %.8s, ts %d", peer,
|
LOGGER.debug(String.format("Fetching summaries from peer %s at height %d, sig %.8s, ts %d; our height %d, sig %.8s, ts %d", peer,
|
||||||
peerHeight, Base58.encode(peersLastBlockSignature), peer.getChainTipData().getLastBlockTimestamp(),
|
peerHeight, Base58.encode(peersLastBlockSignature), peerChainTipData.getTimestamp(),
|
||||||
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp()));
|
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp()));
|
||||||
|
|
||||||
List<BlockSummaryData> peerBlockSummaries = new ArrayList<>();
|
List<BlockSummaryData> peerBlockSummaries = new ArrayList<>();
|
||||||
@ -637,9 +636,9 @@ public class Synchronizer extends Thread {
|
|||||||
return peers;
|
return peers;
|
||||||
|
|
||||||
// Count the number of blocks this peer has beyond our common block
|
// Count the number of blocks this peer has beyond our common block
|
||||||
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
final BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
final int peerHeight = peerChainTipData.getLastHeight();
|
final int peerHeight = peerChainTipData.getHeight();
|
||||||
final byte[] peerLastBlockSignature = peerChainTipData.getLastBlockSignature();
|
final byte[] peerLastBlockSignature = peerChainTipData.getSignature();
|
||||||
final int peerAdditionalBlocksAfterCommonBlock = peerHeight - commonBlockSummary.getHeight();
|
final int peerAdditionalBlocksAfterCommonBlock = peerHeight - commonBlockSummary.getHeight();
|
||||||
// Limit the number of blocks we are comparing. FUTURE: we could request more in batches, but there may not be a case when this is needed
|
// Limit the number of blocks we are comparing. FUTURE: we could request more in batches, but there may not be a case when this is needed
|
||||||
int summariesRequired = Math.min(peerAdditionalBlocksAfterCommonBlock, MAXIMUM_REQUEST_SIZE);
|
int summariesRequired = Math.min(peerAdditionalBlocksAfterCommonBlock, MAXIMUM_REQUEST_SIZE);
|
||||||
@ -727,8 +726,9 @@ public class Synchronizer extends Thread {
|
|||||||
|
|
||||||
LOGGER.debug(String.format("Listing peers with common block %.8s...", Base58.encode(commonBlockSummary.getSignature())));
|
LOGGER.debug(String.format("Listing peers with common block %.8s...", Base58.encode(commonBlockSummary.getSignature())));
|
||||||
for (Peer peer : peersSharingCommonBlock) {
|
for (Peer peer : peersSharingCommonBlock) {
|
||||||
final int peerHeight = peer.getChainTipData().getLastHeight();
|
BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
final Long peerLastBlockTimestamp = peer.getChainTipData().getLastBlockTimestamp();
|
final int peerHeight = peerChainTipData.getHeight();
|
||||||
|
final Long peerLastBlockTimestamp = peerChainTipData.getTimestamp();
|
||||||
final int peerAdditionalBlocksAfterCommonBlock = peerHeight - commonBlockSummary.getHeight();
|
final int peerAdditionalBlocksAfterCommonBlock = peerHeight - commonBlockSummary.getHeight();
|
||||||
final CommonBlockData peerCommonBlockData = peer.getCommonBlockData();
|
final CommonBlockData peerCommonBlockData = peer.getCommonBlockData();
|
||||||
|
|
||||||
@ -825,7 +825,7 @@ public class Synchronizer extends Thread {
|
|||||||
// Calculate the length of the shortest peer chain sharing this common block
|
// Calculate the length of the shortest peer chain sharing this common block
|
||||||
int minChainLength = 0;
|
int minChainLength = 0;
|
||||||
for (Peer peer : peersSharingCommonBlock) {
|
for (Peer peer : peersSharingCommonBlock) {
|
||||||
final int peerHeight = peer.getChainTipData().getLastHeight();
|
final int peerHeight = peer.getChainTipData().getHeight();
|
||||||
final int peerAdditionalBlocksAfterCommonBlock = peerHeight - commonBlockSummary.getHeight();
|
final int peerAdditionalBlocksAfterCommonBlock = peerHeight - commonBlockSummary.getHeight();
|
||||||
|
|
||||||
if (peerAdditionalBlocksAfterCommonBlock < minChainLength || minChainLength == 0)
|
if (peerAdditionalBlocksAfterCommonBlock < minChainLength || minChainLength == 0)
|
||||||
@ -933,13 +933,13 @@ public class Synchronizer extends Thread {
|
|||||||
final BlockData ourLatestBlockData = repository.getBlockRepository().getLastBlock();
|
final BlockData ourLatestBlockData = repository.getBlockRepository().getLastBlock();
|
||||||
final int ourInitialHeight = ourLatestBlockData.getHeight();
|
final int ourInitialHeight = ourLatestBlockData.getHeight();
|
||||||
|
|
||||||
PeerChainTipData peerChainTipData = peer.getChainTipData();
|
BlockSummaryData peerChainTipData = peer.getChainTipData();
|
||||||
int peerHeight = peerChainTipData.getLastHeight();
|
int peerHeight = peerChainTipData.getHeight();
|
||||||
byte[] peersLastBlockSignature = peerChainTipData.getLastBlockSignature();
|
byte[] peersLastBlockSignature = peerChainTipData.getSignature();
|
||||||
|
|
||||||
byte[] ourLastBlockSignature = ourLatestBlockData.getSignature();
|
byte[] ourLastBlockSignature = ourLatestBlockData.getSignature();
|
||||||
String syncString = String.format("Synchronizing with peer %s at height %d, sig %.8s, ts %d; our height %d, sig %.8s, ts %d", peer,
|
String syncString = String.format("Synchronizing with peer %s at height %d, sig %.8s, ts %d; our height %d, sig %.8s, ts %d", peer,
|
||||||
peerHeight, Base58.encode(peersLastBlockSignature), peer.getChainTipData().getLastBlockTimestamp(),
|
peerHeight, Base58.encode(peersLastBlockSignature), peerChainTipData.getTimestamp(),
|
||||||
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp());
|
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp());
|
||||||
LOGGER.info(syncString);
|
LOGGER.info(syncString);
|
||||||
|
|
||||||
@ -1313,7 +1313,7 @@ public class Synchronizer extends Thread {
|
|||||||
// Final check to make sure the peer isn't out of date (except for when we're in recovery mode)
|
// Final check to make sure the peer isn't out of date (except for when we're in recovery mode)
|
||||||
if (!recoveryMode && peer.getChainTipData() != null) {
|
if (!recoveryMode && peer.getChainTipData() != null) {
|
||||||
final Long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
|
final Long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
|
||||||
final Long peerLastBlockTimestamp = peer.getChainTipData().getLastBlockTimestamp();
|
final Long peerLastBlockTimestamp = peer.getChainTipData().getTimestamp();
|
||||||
if (peerLastBlockTimestamp == null || peerLastBlockTimestamp < minLatestBlockTimestamp) {
|
if (peerLastBlockTimestamp == null || peerLastBlockTimestamp < minLatestBlockTimestamp) {
|
||||||
LOGGER.info(String.format("Peer %s is out of date, so abandoning sync attempt", peer));
|
LOGGER.info(String.format("Peer %s is out of date, so abandoning sync attempt", peer));
|
||||||
return SynchronizationResult.CHAIN_TIP_TOO_OLD;
|
return SynchronizationResult.CHAIN_TIP_TOO_OLD;
|
||||||
|
@ -595,9 +595,10 @@ public class ArbitraryDataFileManager extends Thread {
|
|||||||
// Send valid, yet unexpected message type in response, so peer's synchronizer doesn't have to wait for timeout
|
// Send valid, yet unexpected message type in response, so peer's synchronizer doesn't have to wait for timeout
|
||||||
LOGGER.debug(String.format("Sending 'file unknown' response to peer %s for GET_FILE request for unknown file %s", peer, arbitraryDataFile));
|
LOGGER.debug(String.format("Sending 'file unknown' response to peer %s for GET_FILE request for unknown file %s", peer, arbitraryDataFile));
|
||||||
|
|
||||||
// We'll send empty block summaries message as it's very short
|
// Send generic 'unknown' message as it's very short
|
||||||
// TODO: use a different message type here
|
Message fileUnknownMessage = peer.getPeersVersion() >= GenericUnknownMessage.MINIMUM_PEER_VERSION
|
||||||
Message fileUnknownMessage = new BlockSummariesMessage(Collections.emptyList());
|
? new GenericUnknownMessage()
|
||||||
|
: new BlockSummariesMessage(Collections.emptyList());
|
||||||
fileUnknownMessage.setId(message.getId());
|
fileUnknownMessage.setId(message.getId());
|
||||||
if (!peer.sendMessage(fileUnknownMessage)) {
|
if (!peer.sendMessage(fileUnknownMessage)) {
|
||||||
LOGGER.debug("Couldn't sent file-unknown response");
|
LOGGER.debug("Couldn't sent file-unknown response");
|
||||||
|
@ -11,11 +11,12 @@ public class BlockSummaryData {
|
|||||||
private int height;
|
private int height;
|
||||||
private byte[] signature;
|
private byte[] signature;
|
||||||
private byte[] minterPublicKey;
|
private byte[] minterPublicKey;
|
||||||
private int onlineAccountsCount;
|
|
||||||
|
|
||||||
// Optional, set during construction
|
// Optional, set during construction
|
||||||
|
private Integer onlineAccountsCount;
|
||||||
private Long timestamp;
|
private Long timestamp;
|
||||||
private Integer transactionCount;
|
private Integer transactionCount;
|
||||||
|
private byte[] reference;
|
||||||
|
|
||||||
// Optional, set after construction
|
// Optional, set after construction
|
||||||
private Integer minterLevel;
|
private Integer minterLevel;
|
||||||
@ -25,6 +26,15 @@ public class BlockSummaryData {
|
|||||||
protected BlockSummaryData() {
|
protected BlockSummaryData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Constructor typically populated with fields from HeightV2Message */
|
||||||
|
public BlockSummaryData(int height, byte[] signature, byte[] minterPublicKey, long timestamp) {
|
||||||
|
this.height = height;
|
||||||
|
this.signature = signature;
|
||||||
|
this.minterPublicKey = minterPublicKey;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor typically populated with fields from BlockSummariesMessage */
|
||||||
public BlockSummaryData(int height, byte[] signature, byte[] minterPublicKey, int onlineAccountsCount) {
|
public BlockSummaryData(int height, byte[] signature, byte[] minterPublicKey, int onlineAccountsCount) {
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
@ -32,13 +42,16 @@ public class BlockSummaryData {
|
|||||||
this.onlineAccountsCount = onlineAccountsCount;
|
this.onlineAccountsCount = onlineAccountsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockSummaryData(int height, byte[] signature, byte[] minterPublicKey, int onlineAccountsCount, long timestamp, int transactionCount) {
|
/** Constructor typically populated with fields from BlockSummariesV2Message */
|
||||||
|
public BlockSummaryData(int height, byte[] signature, byte[] minterPublicKey, Integer onlineAccountsCount,
|
||||||
|
Long timestamp, Integer transactionCount, byte[] reference) {
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
this.minterPublicKey = minterPublicKey;
|
this.minterPublicKey = minterPublicKey;
|
||||||
this.onlineAccountsCount = onlineAccountsCount;
|
this.onlineAccountsCount = onlineAccountsCount;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.transactionCount = transactionCount;
|
this.transactionCount = transactionCount;
|
||||||
|
this.reference = reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockSummaryData(BlockData blockData) {
|
public BlockSummaryData(BlockData blockData) {
|
||||||
@ -49,6 +62,7 @@ public class BlockSummaryData {
|
|||||||
|
|
||||||
this.timestamp = blockData.getTimestamp();
|
this.timestamp = blockData.getTimestamp();
|
||||||
this.transactionCount = blockData.getTransactionCount();
|
this.transactionCount = blockData.getTransactionCount();
|
||||||
|
this.reference = blockData.getReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters / setters
|
// Getters / setters
|
||||||
@ -65,7 +79,7 @@ public class BlockSummaryData {
|
|||||||
return this.minterPublicKey;
|
return this.minterPublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOnlineAccountsCount() {
|
public Integer getOnlineAccountsCount() {
|
||||||
return this.onlineAccountsCount;
|
return this.onlineAccountsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +91,10 @@ public class BlockSummaryData {
|
|||||||
return this.transactionCount;
|
return this.transactionCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getReference() {
|
||||||
|
return this.reference;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getMinterLevel() {
|
public Integer getMinterLevel() {
|
||||||
return this.minterLevel;
|
return this.minterLevel;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package org.qortal.data.block;
|
package org.qortal.data.block;
|
||||||
|
|
||||||
import org.qortal.data.network.PeerChainTipData;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
@ -14,14 +12,14 @@ public class CommonBlockData {
|
|||||||
private BlockSummaryData commonBlockSummary = null;
|
private BlockSummaryData commonBlockSummary = null;
|
||||||
private List<BlockSummaryData> blockSummariesAfterCommonBlock = null;
|
private List<BlockSummaryData> blockSummariesAfterCommonBlock = null;
|
||||||
private BigInteger chainWeight = null;
|
private BigInteger chainWeight = null;
|
||||||
private PeerChainTipData chainTipData = null;
|
private BlockSummaryData chainTipData = null;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
protected CommonBlockData() {
|
protected CommonBlockData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommonBlockData(BlockSummaryData commonBlockSummary, PeerChainTipData chainTipData) {
|
public CommonBlockData(BlockSummaryData commonBlockSummary, BlockSummaryData chainTipData) {
|
||||||
this.commonBlockSummary = commonBlockSummary;
|
this.commonBlockSummary = commonBlockSummary;
|
||||||
this.chainTipData = chainTipData;
|
this.chainTipData = chainTipData;
|
||||||
}
|
}
|
||||||
@ -49,7 +47,7 @@ public class CommonBlockData {
|
|||||||
this.chainWeight = chainWeight;
|
this.chainWeight = chainWeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PeerChainTipData getChainTipData() {
|
public BlockSummaryData getChainTipData() {
|
||||||
return this.chainTipData;
|
return this.chainTipData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
package org.qortal.data.network;
|
|
||||||
|
|
||||||
public class PeerChainTipData {
|
|
||||||
|
|
||||||
/** Latest block height as reported by peer. */
|
|
||||||
private Integer lastHeight;
|
|
||||||
/** Latest block signature as reported by peer. */
|
|
||||||
private byte[] lastBlockSignature;
|
|
||||||
/** Latest block timestamp as reported by peer. */
|
|
||||||
private Long lastBlockTimestamp;
|
|
||||||
/** Latest block minter public key as reported by peer. */
|
|
||||||
private byte[] lastBlockMinter;
|
|
||||||
|
|
||||||
public PeerChainTipData(Integer lastHeight, byte[] lastBlockSignature, Long lastBlockTimestamp, byte[] lastBlockMinter) {
|
|
||||||
this.lastHeight = lastHeight;
|
|
||||||
this.lastBlockSignature = lastBlockSignature;
|
|
||||||
this.lastBlockTimestamp = lastBlockTimestamp;
|
|
||||||
this.lastBlockMinter = lastBlockMinter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getLastHeight() {
|
|
||||||
return this.lastHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getLastBlockSignature() {
|
|
||||||
return this.lastBlockSignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getLastBlockTimestamp() {
|
|
||||||
return this.lastBlockTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getLastBlockMinter() {
|
|
||||||
return this.lastBlockMinter;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ import org.qortal.controller.arbitrary.ArbitraryDataFileListManager;
|
|||||||
import org.qortal.controller.arbitrary.ArbitraryDataManager;
|
import org.qortal.controller.arbitrary.ArbitraryDataManager;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
import org.qortal.data.block.BlockData;
|
import org.qortal.data.block.BlockData;
|
||||||
|
import org.qortal.data.block.BlockSummaryData;
|
||||||
import org.qortal.data.network.PeerData;
|
import org.qortal.data.network.PeerData;
|
||||||
import org.qortal.data.transaction.TransactionData;
|
import org.qortal.data.transaction.TransactionData;
|
||||||
import org.qortal.network.message.*;
|
import org.qortal.network.message.*;
|
||||||
@ -90,6 +91,8 @@ public class Network {
|
|||||||
|
|
||||||
private static final long DISCONNECTION_CHECK_INTERVAL = 10 * 1000L; // milliseconds
|
private static final long DISCONNECTION_CHECK_INTERVAL = 10 * 1000L; // milliseconds
|
||||||
|
|
||||||
|
private static final int BROADCAST_CHAIN_TIP_DEPTH = 7; // Just enough to fill a SINGLE TCP packet (~1440 bytes)
|
||||||
|
|
||||||
// Generate our node keys / ID
|
// Generate our node keys / ID
|
||||||
private final Ed25519PrivateKeyParameters edPrivateKeyParams = new Ed25519PrivateKeyParameters(new SecureRandom());
|
private final Ed25519PrivateKeyParameters edPrivateKeyParams = new Ed25519PrivateKeyParameters(new SecureRandom());
|
||||||
private final Ed25519PublicKeyParameters edPublicKeyParams = edPrivateKeyParams.generatePublicKey();
|
private final Ed25519PublicKeyParameters edPublicKeyParams = edPrivateKeyParams.generatePublicKey();
|
||||||
@ -1087,10 +1090,16 @@ public class Network {
|
|||||||
|
|
||||||
if (peer.isOutbound()) {
|
if (peer.isOutbound()) {
|
||||||
if (!Settings.getInstance().isLite()) {
|
if (!Settings.getInstance().isLite()) {
|
||||||
// Send our height
|
// Send our height / chain tip info
|
||||||
Message heightMessage = buildHeightMessage(peer, Controller.getInstance().getChainTip());
|
Message message = this.buildHeightOrChainTipInfo(peer);
|
||||||
if (!peer.sendMessage(heightMessage)) {
|
|
||||||
peer.disconnect("failed to send height/info");
|
if (message == null) {
|
||||||
|
peer.disconnect("Couldn't build our chain tip info");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!peer.sendMessage(message)) {
|
||||||
|
peer.disconnect("failed to send height / chain tip info");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1164,10 +1173,47 @@ public class Network {
|
|||||||
return new PeersV2Message(peerAddresses);
|
return new PeersV2Message(peerAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message buildHeightMessage(Peer peer, BlockData blockData) {
|
/** Builds either (legacy) HeightV2Message or (newer) BlockSummariesV2Message, depending on peer version.
|
||||||
// HEIGHT_V2 contains way more useful info
|
*
|
||||||
return new HeightV2Message(blockData.getHeight(), blockData.getSignature(),
|
* @return Message, or null if DataException was thrown.
|
||||||
blockData.getTimestamp(), blockData.getMinterPublicKey());
|
*/
|
||||||
|
public Message buildHeightOrChainTipInfo(Peer peer) {
|
||||||
|
if (peer.getPeersVersion() >= BlockSummariesV2Message.MINIMUM_PEER_VERSION) {
|
||||||
|
int latestHeight = Controller.getInstance().getChainHeight();
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
List<BlockSummaryData> latestBlockSummaries = repository.getBlockRepository().getBlockSummaries(latestHeight - BROADCAST_CHAIN_TIP_DEPTH, latestHeight);
|
||||||
|
return new BlockSummariesV2Message(latestBlockSummaries);
|
||||||
|
} catch (DataException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For older peers
|
||||||
|
BlockData latestBlockData = Controller.getInstance().getChainTip();
|
||||||
|
return new HeightV2Message(latestBlockData.getHeight(), latestBlockData.getSignature(),
|
||||||
|
latestBlockData.getTimestamp(), latestBlockData.getMinterPublicKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void broadcastOurChain() {
|
||||||
|
BlockData latestBlockData = Controller.getInstance().getChainTip();
|
||||||
|
int latestHeight = latestBlockData.getHeight();
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
List<BlockSummaryData> latestBlockSummaries = repository.getBlockRepository().getBlockSummaries(latestHeight - BROADCAST_CHAIN_TIP_DEPTH, latestHeight);
|
||||||
|
Message latestBlockSummariesMessage = new BlockSummariesV2Message(latestBlockSummaries);
|
||||||
|
|
||||||
|
// For older peers
|
||||||
|
Message heightMessage = new HeightV2Message(latestBlockData.getHeight(), latestBlockData.getSignature(),
|
||||||
|
latestBlockData.getTimestamp(), latestBlockData.getMinterPublicKey());
|
||||||
|
|
||||||
|
Network.getInstance().broadcast(broadcastPeer -> broadcastPeer.getPeersVersion() >= BlockSummariesV2Message.MINIMUM_PEER_VERSION
|
||||||
|
? latestBlockSummariesMessage
|
||||||
|
: heightMessage
|
||||||
|
);
|
||||||
|
} catch (DataException e) {
|
||||||
|
LOGGER.warn("Couldn't broadcast our chain tip info", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message buildNewTransactionMessage(Peer peer, TransactionData transactionData) {
|
public Message buildNewTransactionMessage(Peer peer, TransactionData transactionData) {
|
||||||
|
@ -6,8 +6,8 @@ import com.google.common.net.InetAddresses;
|
|||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.qortal.controller.Controller;
|
import org.qortal.controller.Controller;
|
||||||
|
import org.qortal.data.block.BlockSummaryData;
|
||||||
import org.qortal.data.block.CommonBlockData;
|
import org.qortal.data.block.CommonBlockData;
|
||||||
import org.qortal.data.network.PeerChainTipData;
|
|
||||||
import org.qortal.data.network.PeerData;
|
import org.qortal.data.network.PeerData;
|
||||||
import org.qortal.network.message.ChallengeMessage;
|
import org.qortal.network.message.ChallengeMessage;
|
||||||
import org.qortal.network.message.Message;
|
import org.qortal.network.message.Message;
|
||||||
@ -148,7 +148,7 @@ public class Peer {
|
|||||||
/**
|
/**
|
||||||
* Latest block info as reported by peer.
|
* Latest block info as reported by peer.
|
||||||
*/
|
*/
|
||||||
private PeerChainTipData peersChainTipData;
|
private List<BlockSummaryData> peersChainTipData = Collections.emptyList();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our common block with this peer
|
* Our common block with this peer
|
||||||
@ -353,28 +353,34 @@ public class Peer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PeerChainTipData getChainTipData() {
|
public BlockSummaryData getChainTipData() {
|
||||||
synchronized (this.peerInfoLock) {
|
List<BlockSummaryData> chainTipSummaries = this.peersChainTipData;
|
||||||
return this.peersChainTipData;
|
|
||||||
}
|
if (chainTipSummaries.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Return last entry, which should have greatest height
|
||||||
|
return chainTipSummaries.get(chainTipSummaries.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setChainTipData(PeerChainTipData chainTipData) {
|
public void setChainTipData(BlockSummaryData chainTipData) {
|
||||||
synchronized (this.peerInfoLock) {
|
this.peersChainTipData = Collections.singletonList(chainTipData);
|
||||||
this.peersChainTipData = chainTipData;
|
}
|
||||||
}
|
|
||||||
|
public List<BlockSummaryData> getChainTipSummaries() {
|
||||||
|
return this.peersChainTipData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChainTipSummaries(List<BlockSummaryData> chainTipSummaries) {
|
||||||
|
this.peersChainTipData = List.copyOf(chainTipSummaries);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommonBlockData getCommonBlockData() {
|
public CommonBlockData getCommonBlockData() {
|
||||||
synchronized (this.peerInfoLock) {
|
return this.commonBlockData;
|
||||||
return this.commonBlockData;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCommonBlockData(CommonBlockData commonBlockData) {
|
public void setCommonBlockData(CommonBlockData commonBlockData) {
|
||||||
synchronized (this.peerInfoLock) {
|
this.commonBlockData = commonBlockData;
|
||||||
this.commonBlockData = commonBlockData;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSyncInProgress() {
|
public boolean isSyncInProgress() {
|
||||||
@ -904,20 +910,22 @@ public class Peer {
|
|||||||
// Common block data
|
// Common block data
|
||||||
|
|
||||||
public boolean canUseCachedCommonBlockData() {
|
public boolean canUseCachedCommonBlockData() {
|
||||||
PeerChainTipData peerChainTipData = this.getChainTipData();
|
BlockSummaryData peerChainTipData = this.getChainTipData();
|
||||||
CommonBlockData commonBlockData = this.getCommonBlockData();
|
if (peerChainTipData == null || peerChainTipData.getSignature() == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (peerChainTipData != null && commonBlockData != null) {
|
CommonBlockData commonBlockData = this.getCommonBlockData();
|
||||||
PeerChainTipData commonBlockChainTipData = commonBlockData.getChainTipData();
|
if (commonBlockData == null)
|
||||||
if (peerChainTipData.getLastBlockSignature() != null && commonBlockChainTipData != null
|
return false;
|
||||||
&& commonBlockChainTipData.getLastBlockSignature() != null) {
|
|
||||||
if (Arrays.equals(peerChainTipData.getLastBlockSignature(),
|
BlockSummaryData commonBlockChainTipData = commonBlockData.getChainTipData();
|
||||||
commonBlockChainTipData.getLastBlockSignature())) {
|
if (commonBlockChainTipData == null || commonBlockChainTipData.getSignature() == null)
|
||||||
return true;
|
return false;
|
||||||
}
|
|
||||||
}
|
if (!Arrays.equals(peerChainTipData.getSignature(), commonBlockChainTipData.getSignature()))
|
||||||
}
|
return false;
|
||||||
return false;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
package org.qortal.network.message;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import com.google.common.primitives.Longs;
|
||||||
|
import org.qortal.data.block.BlockSummaryData;
|
||||||
|
import org.qortal.transform.Transformer;
|
||||||
|
import org.qortal.transform.block.BlockTransformer;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.BufferUnderflowException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BlockSummariesV2Message extends Message {
|
||||||
|
|
||||||
|
public static final long MINIMUM_PEER_VERSION = 0x03000400cbL;
|
||||||
|
|
||||||
|
private static final int BLOCK_SUMMARY_V2_LENGTH = BlockTransformer.BLOCK_SIGNATURE_LENGTH /* block signature */
|
||||||
|
+ Transformer.PUBLIC_KEY_LENGTH /* minter public key */
|
||||||
|
+ Transformer.INT_LENGTH /* online accounts count */
|
||||||
|
+ Transformer.LONG_LENGTH /* block timestamp */
|
||||||
|
+ Transformer.INT_LENGTH /* transactions count */
|
||||||
|
+ BlockTransformer.BLOCK_SIGNATURE_LENGTH; /* block reference */
|
||||||
|
|
||||||
|
private List<BlockSummaryData> blockSummaries;
|
||||||
|
|
||||||
|
public BlockSummariesV2Message(List<BlockSummaryData> blockSummaries) {
|
||||||
|
super(MessageType.BLOCK_SUMMARIES_V2);
|
||||||
|
|
||||||
|
// Shortcut for when there are no summaries
|
||||||
|
if (blockSummaries.isEmpty()) {
|
||||||
|
this.dataBytes = Message.EMPTY_DATA_BYTES;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// First summary's height
|
||||||
|
bytes.write(Ints.toByteArray(blockSummaries.get(0).getHeight()));
|
||||||
|
|
||||||
|
for (BlockSummaryData blockSummary : blockSummaries) {
|
||||||
|
bytes.write(blockSummary.getSignature());
|
||||||
|
bytes.write(blockSummary.getMinterPublicKey());
|
||||||
|
bytes.write(Ints.toByteArray(blockSummary.getOnlineAccountsCount()));
|
||||||
|
bytes.write(Longs.toByteArray(blockSummary.getTimestamp()));
|
||||||
|
bytes.write(Ints.toByteArray(blockSummary.getTransactionCount()));
|
||||||
|
bytes.write(blockSummary.getReference());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AssertionError("IOException shouldn't occur with ByteArrayOutputStream");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataBytes = bytes.toByteArray();
|
||||||
|
this.checksumBytes = Message.generateChecksum(this.dataBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockSummariesV2Message(int id, List<BlockSummaryData> blockSummaries) {
|
||||||
|
super(id, MessageType.BLOCK_SUMMARIES_V2);
|
||||||
|
|
||||||
|
this.blockSummaries = blockSummaries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BlockSummaryData> getBlockSummaries() {
|
||||||
|
return this.blockSummaries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Message fromByteBuffer(int id, ByteBuffer bytes) {
|
||||||
|
int height = bytes.getInt();
|
||||||
|
|
||||||
|
// Expecting bytes remaining to be exact multiples of BLOCK_SUMMARY_V2_LENGTH
|
||||||
|
if (bytes.remaining() % BLOCK_SUMMARY_V2_LENGTH != 0)
|
||||||
|
throw new BufferUnderflowException();
|
||||||
|
|
||||||
|
List<BlockSummaryData> blockSummaries = new ArrayList<>();
|
||||||
|
while (bytes.hasRemaining()) {
|
||||||
|
byte[] signature = new byte[BlockTransformer.BLOCK_SIGNATURE_LENGTH];
|
||||||
|
bytes.get(signature);
|
||||||
|
|
||||||
|
byte[] minterPublicKey = new byte[Transformer.PUBLIC_KEY_LENGTH];
|
||||||
|
bytes.get(minterPublicKey);
|
||||||
|
|
||||||
|
int onlineAccountsCount = bytes.getInt();
|
||||||
|
|
||||||
|
long timestamp = bytes.getLong();
|
||||||
|
|
||||||
|
int transactionsCount = bytes.getInt();
|
||||||
|
|
||||||
|
byte[] reference = new byte[BlockTransformer.BLOCK_SIGNATURE_LENGTH];
|
||||||
|
bytes.get(reference);
|
||||||
|
|
||||||
|
BlockSummaryData blockSummary = new BlockSummaryData(height, signature, minterPublicKey,
|
||||||
|
onlineAccountsCount, timestamp, transactionsCount, reference);
|
||||||
|
blockSummaries.add(blockSummary);
|
||||||
|
|
||||||
|
height++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BlockSummariesV2Message(id, blockSummaries);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package org.qortal.network.message;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class GenericUnknownMessage extends Message {
|
||||||
|
|
||||||
|
public static final long MINIMUM_PEER_VERSION = 0x03000400cbL;
|
||||||
|
|
||||||
|
public GenericUnknownMessage() {
|
||||||
|
super(MessageType.GENERIC_UNKNOWN);
|
||||||
|
|
||||||
|
this.dataBytes = EMPTY_DATA_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GenericUnknownMessage(int id) {
|
||||||
|
super(id, MessageType.GENERIC_UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Message fromByteBuffer(int id, ByteBuffer bytes) {
|
||||||
|
return new GenericUnknownMessage(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -21,6 +21,7 @@ public enum MessageType {
|
|||||||
HEIGHT_V2(10, HeightV2Message::fromByteBuffer),
|
HEIGHT_V2(10, HeightV2Message::fromByteBuffer),
|
||||||
PING(11, PingMessage::fromByteBuffer),
|
PING(11, PingMessage::fromByteBuffer),
|
||||||
PONG(12, PongMessage::fromByteBuffer),
|
PONG(12, PongMessage::fromByteBuffer),
|
||||||
|
GENERIC_UNKNOWN(13, GenericUnknownMessage::fromByteBuffer),
|
||||||
|
|
||||||
// Requesting data
|
// Requesting data
|
||||||
PEERS_V2(20, PeersV2Message::fromByteBuffer),
|
PEERS_V2(20, PeersV2Message::fromByteBuffer),
|
||||||
@ -41,6 +42,7 @@ public enum MessageType {
|
|||||||
|
|
||||||
BLOCK_SUMMARIES(70, BlockSummariesMessage::fromByteBuffer),
|
BLOCK_SUMMARIES(70, BlockSummariesMessage::fromByteBuffer),
|
||||||
GET_BLOCK_SUMMARIES(71, GetBlockSummariesMessage::fromByteBuffer),
|
GET_BLOCK_SUMMARIES(71, GetBlockSummariesMessage::fromByteBuffer),
|
||||||
|
BLOCK_SUMMARIES_V2(72, BlockSummariesV2Message::fromByteBuffer),
|
||||||
|
|
||||||
ONLINE_ACCOUNTS(80, OnlineAccountsMessage::fromByteBuffer),
|
ONLINE_ACCOUNTS(80, OnlineAccountsMessage::fromByteBuffer),
|
||||||
GET_ONLINE_ACCOUNTS(81, GetOnlineAccountsMessage::fromByteBuffer),
|
GET_ONLINE_ACCOUNTS(81, GetOnlineAccountsMessage::fromByteBuffer),
|
||||||
|
@ -143,13 +143,17 @@ public class HSQLDBBlockArchiveRepository implements BlockArchiveRepository {
|
|||||||
byte[] blockMinterPublicKey = resultSet.getBytes(3);
|
byte[] blockMinterPublicKey = resultSet.getBytes(3);
|
||||||
|
|
||||||
// Fetch additional info from the archive itself
|
// Fetch additional info from the archive itself
|
||||||
int onlineAccountsCount = 0;
|
Integer onlineAccountsCount = null;
|
||||||
|
Long timestamp = null;
|
||||||
|
Integer transactionCount = null;
|
||||||
|
byte[] reference = null;
|
||||||
|
|
||||||
BlockData blockData = this.fromSignature(signature);
|
BlockData blockData = this.fromSignature(signature);
|
||||||
if (blockData != null) {
|
if (blockData != null) {
|
||||||
onlineAccountsCount = blockData.getOnlineAccountsCount();
|
onlineAccountsCount = blockData.getOnlineAccountsCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockSummaryData blockSummary = new BlockSummaryData(height, signature, blockMinterPublicKey, onlineAccountsCount);
|
BlockSummaryData blockSummary = new BlockSummaryData(height, signature, blockMinterPublicKey, onlineAccountsCount, timestamp, transactionCount, reference);
|
||||||
blockSummaries.add(blockSummary);
|
blockSummaries.add(blockSummary);
|
||||||
} while (resultSet.next());
|
} while (resultSet.next());
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
@Override
|
@Override
|
||||||
public List<BlockSummaryData> getBlockSummariesBySigner(byte[] signerPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException {
|
public List<BlockSummaryData> getBlockSummariesBySigner(byte[] signerPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException {
|
||||||
StringBuilder sql = new StringBuilder(512);
|
StringBuilder sql = new StringBuilder(512);
|
||||||
sql.append("SELECT signature, height, Blocks.minter, online_accounts_count FROM ");
|
sql.append("SELECT signature, height, Blocks.minter, online_accounts_count, minted_when, transaction_count, Blocks.reference FROM ");
|
||||||
|
|
||||||
// List of minter account's public key and reward-share public keys with minter's public key
|
// List of minter account's public key and reward-share public keys with minter's public key
|
||||||
sql.append("(SELECT * FROM (VALUES (CAST(? AS QortalPublicKey))) UNION (SELECT reward_share_public_key FROM RewardShares WHERE minter_public_key = ?)) AS PublicKeys (public_key) ");
|
sql.append("(SELECT * FROM (VALUES (CAST(? AS QortalPublicKey))) UNION (SELECT reward_share_public_key FROM RewardShares WHERE minter_public_key = ?)) AS PublicKeys (public_key) ");
|
||||||
@ -322,8 +322,12 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
int height = resultSet.getInt(2);
|
int height = resultSet.getInt(2);
|
||||||
byte[] blockMinterPublicKey = resultSet.getBytes(3);
|
byte[] blockMinterPublicKey = resultSet.getBytes(3);
|
||||||
int onlineAccountsCount = resultSet.getInt(4);
|
int onlineAccountsCount = resultSet.getInt(4);
|
||||||
|
long timestamp = resultSet.getLong(5);
|
||||||
|
int transactionCount = resultSet.getInt(6);
|
||||||
|
byte[] reference = resultSet.getBytes(7);
|
||||||
|
|
||||||
BlockSummaryData blockSummary = new BlockSummaryData(height, signature, blockMinterPublicKey, onlineAccountsCount);
|
BlockSummaryData blockSummary = new BlockSummaryData(height, signature, blockMinterPublicKey, onlineAccountsCount,
|
||||||
|
timestamp, transactionCount, reference);
|
||||||
blockSummaries.add(blockSummary);
|
blockSummaries.add(blockSummary);
|
||||||
} while (resultSet.next());
|
} while (resultSet.next());
|
||||||
|
|
||||||
@ -355,7 +359,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BlockSummaryData> getBlockSummaries(int firstBlockHeight, int lastBlockHeight) throws DataException {
|
public List<BlockSummaryData> getBlockSummaries(int firstBlockHeight, int lastBlockHeight) throws DataException {
|
||||||
String sql = "SELECT signature, height, minter, online_accounts_count, minted_when, transaction_count "
|
String sql = "SELECT signature, height, minter, online_accounts_count, minted_when, transaction_count, reference "
|
||||||
+ "FROM Blocks WHERE height BETWEEN ? AND ?";
|
+ "FROM Blocks WHERE height BETWEEN ? AND ?";
|
||||||
|
|
||||||
List<BlockSummaryData> blockSummaries = new ArrayList<>();
|
List<BlockSummaryData> blockSummaries = new ArrayList<>();
|
||||||
@ -371,9 +375,10 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
int onlineAccountsCount = resultSet.getInt(4);
|
int onlineAccountsCount = resultSet.getInt(4);
|
||||||
long timestamp = resultSet.getLong(5);
|
long timestamp = resultSet.getLong(5);
|
||||||
int transactionCount = resultSet.getInt(6);
|
int transactionCount = resultSet.getInt(6);
|
||||||
|
byte[] reference = resultSet.getBytes(7);
|
||||||
|
|
||||||
BlockSummaryData blockSummary = new BlockSummaryData(height, signature, minterPublicKey, onlineAccountsCount,
|
BlockSummaryData blockSummary = new BlockSummaryData(height, signature, minterPublicKey, onlineAccountsCount,
|
||||||
timestamp, transactionCount);
|
timestamp, transactionCount, reference);
|
||||||
blockSummaries.add(blockSummary);
|
blockSummaries.add(blockSummary);
|
||||||
} while (resultSet.next());
|
} while (resultSet.next());
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user