forked from Qortal/qortal
Synchronizer asks for approval-pending transaction from peer if needed
This commit is contained in:
parent
c83d888a7d
commit
041773cf41
@ -37,6 +37,7 @@ import org.qora.network.message.GetBlockSummariesMessage;
|
|||||||
import org.qora.network.message.GetPeersMessage;
|
import org.qora.network.message.GetPeersMessage;
|
||||||
import org.qora.network.message.GetSignaturesMessage;
|
import org.qora.network.message.GetSignaturesMessage;
|
||||||
import org.qora.network.message.GetSignaturesV2Message;
|
import org.qora.network.message.GetSignaturesV2Message;
|
||||||
|
import org.qora.network.message.GetTransactionMessage;
|
||||||
import org.qora.network.message.HeightMessage;
|
import org.qora.network.message.HeightMessage;
|
||||||
import org.qora.network.message.HeightV2Message;
|
import org.qora.network.message.HeightV2Message;
|
||||||
import org.qora.network.message.Message;
|
import org.qora.network.message.Message;
|
||||||
@ -500,7 +501,7 @@ public class Controller extends Thread {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GET_BLOCK:
|
case GET_BLOCK: {
|
||||||
GetBlockMessage getBlockMessage = (GetBlockMessage) message;
|
GetBlockMessage getBlockMessage = (GetBlockMessage) message;
|
||||||
byte[] signature = getBlockMessage.getSignature();
|
byte[] signature = getBlockMessage.getSignature();
|
||||||
|
|
||||||
@ -522,6 +523,29 @@ public class Controller extends Thread {
|
|||||||
LOGGER.error(String.format("Repository issue while send block %s to peer %s", Base58.encode(signature), peer), e);
|
LOGGER.error(String.format("Repository issue while send block %s to peer %s", Base58.encode(signature), peer), e);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GET_TRANSACTION: {
|
||||||
|
GetTransactionMessage getTransactionMessage = (GetTransactionMessage) message;
|
||||||
|
byte[] signature = getTransactionMessage.getSignature();
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature);
|
||||||
|
if (transactionData == null) {
|
||||||
|
LOGGER.debug(String.format("Ignoring GET_TRANSACTION request from peer %s for unknown transaction %s", peer, Base58.encode(signature)));
|
||||||
|
// Send no response at all???
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message transactionMessage = new TransactionMessage(transactionData);
|
||||||
|
transactionMessage.setId(message.getId());
|
||||||
|
if (!peer.sendMessage(transactionMessage))
|
||||||
|
peer.disconnect("failed to send transaction");
|
||||||
|
} catch (DataException e) {
|
||||||
|
LOGGER.error(String.format("Repository issue while send transaction %s to peer %s", Base58.encode(signature), peer), e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case TRANSACTION:
|
case TRANSACTION:
|
||||||
TransactionMessage transactionMessage = (TransactionMessage) message;
|
TransactionMessage transactionMessage = (TransactionMessage) message;
|
||||||
|
@ -13,6 +13,8 @@ import org.qora.block.BlockChain;
|
|||||||
import org.qora.block.GenesisBlock;
|
import org.qora.block.GenesisBlock;
|
||||||
import org.qora.data.block.BlockData;
|
import org.qora.data.block.BlockData;
|
||||||
import org.qora.data.network.BlockSummaryData;
|
import org.qora.data.network.BlockSummaryData;
|
||||||
|
import org.qora.data.transaction.GroupApprovalTransactionData;
|
||||||
|
import org.qora.data.transaction.TransactionData;
|
||||||
import org.qora.network.Peer;
|
import org.qora.network.Peer;
|
||||||
import org.qora.network.message.BlockMessage;
|
import org.qora.network.message.BlockMessage;
|
||||||
import org.qora.network.message.BlockSummariesMessage;
|
import org.qora.network.message.BlockSummariesMessage;
|
||||||
@ -20,13 +22,17 @@ import org.qora.network.message.GetBlockMessage;
|
|||||||
import org.qora.network.message.GetBlockSummariesMessage;
|
import org.qora.network.message.GetBlockSummariesMessage;
|
||||||
import org.qora.network.message.GetSignaturesMessage;
|
import org.qora.network.message.GetSignaturesMessage;
|
||||||
import org.qora.network.message.GetSignaturesV2Message;
|
import org.qora.network.message.GetSignaturesV2Message;
|
||||||
|
import org.qora.network.message.GetTransactionMessage;
|
||||||
import org.qora.network.message.Message;
|
import org.qora.network.message.Message;
|
||||||
import org.qora.network.message.Message.MessageType;
|
import org.qora.network.message.Message.MessageType;
|
||||||
import org.qora.network.message.SignaturesMessage;
|
import org.qora.network.message.SignaturesMessage;
|
||||||
|
import org.qora.network.message.TransactionMessage;
|
||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
import org.qora.repository.Repository;
|
import org.qora.repository.Repository;
|
||||||
import org.qora.repository.RepositoryManager;
|
import org.qora.repository.RepositoryManager;
|
||||||
import org.qora.transaction.Transaction;
|
import org.qora.transaction.Transaction;
|
||||||
|
import org.qora.transaction.Transaction.TransactionType;
|
||||||
|
import org.qora.utils.Base58;
|
||||||
import org.qora.utils.NTP;
|
import org.qora.utils.NTP;
|
||||||
|
|
||||||
public class Synchronizer {
|
public class Synchronizer {
|
||||||
@ -231,6 +237,46 @@ public class Synchronizer {
|
|||||||
return SynchronizationResult.NO_REPLY;
|
return SynchronizationResult.NO_REPLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If block contains GROUP_APPROVAL transactions then we need to make sure we have the relevant pending transactions too
|
||||||
|
for (Transaction transaction : newBlock.getTransactions()) {
|
||||||
|
TransactionData transactionData = transaction.getTransactionData();
|
||||||
|
|
||||||
|
if (transactionData.getType() != TransactionType.GROUP_APPROVAL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GroupApprovalTransactionData groupApprovalTransactionData = (GroupApprovalTransactionData) transactionData;
|
||||||
|
|
||||||
|
byte[] pendingSignature = groupApprovalTransactionData.getPendingSignature();
|
||||||
|
|
||||||
|
if (repository.getTransactionRepository().exists(pendingSignature))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LOGGER.debug(String.format("Fetching unknown approval-pending transaction %s from peer %s, needed for block at height %d", Base58.encode(pendingSignature), peer, ourHeight));
|
||||||
|
|
||||||
|
TransactionData pendingTransactionData = this.fetchTransaction(peer, pendingSignature);
|
||||||
|
if (pendingTransactionData == null) {
|
||||||
|
LOGGER.info(String.format("Peer %s failed to respond with pending transaction %s", peer, Base58.encode(pendingSignature)));
|
||||||
|
return SynchronizationResult.NO_REPLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the signature is valid at least!
|
||||||
|
Transaction pendingTransaction = Transaction.fromData(repository, pendingTransactionData);
|
||||||
|
if (!pendingTransaction.isSignatureValid()) {
|
||||||
|
LOGGER.info(String.format("Peer %s sent pending transaction %s with invalid signature", peer, Base58.encode(pendingSignature)));
|
||||||
|
return SynchronizationResult.INVALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction.ValidationResult transactionResult = pendingTransaction.isValidUnconfirmed();
|
||||||
|
if (transactionResult != Transaction.ValidationResult.OK) {
|
||||||
|
LOGGER.info(String.format("Peer %s sent invalid (%s) pending transaction %s", peer, transactionResult.name(), Base58.encode(pendingSignature)));
|
||||||
|
return SynchronizationResult.INVALID_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to our unconfirmed pile
|
||||||
|
this.repository.getTransactionRepository().save(pendingTransactionData);
|
||||||
|
this.repository.getTransactionRepository().unconfirmTransaction(pendingTransactionData);
|
||||||
|
}
|
||||||
|
|
||||||
if (!newBlock.isSignatureValid()) {
|
if (!newBlock.isSignatureValid()) {
|
||||||
LOGGER.info(String.format("Peer %s sent block with invalid signature for height %d", peer, ourHeight));
|
LOGGER.info(String.format("Peer %s sent block with invalid signature for height %d", peer, ourHeight));
|
||||||
return SynchronizationResult.INVALID_DATA;
|
return SynchronizationResult.INVALID_DATA;
|
||||||
@ -391,4 +437,16 @@ public class Synchronizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TransactionData fetchTransaction(Peer peer, byte[] signature) {
|
||||||
|
Message getTransactionMessage = new GetTransactionMessage(signature);
|
||||||
|
|
||||||
|
Message message = peer.getResponse(getTransactionMessage);
|
||||||
|
if (message == null || message.getType() != MessageType.TRANSACTION)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
TransactionMessage transactionMessage = (TransactionMessage) message;
|
||||||
|
|
||||||
|
return transactionMessage.getTransactionData();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
public class GetTransactionMessage extends Message {
|
||||||
|
|
||||||
|
private static final int TRANSACTION_SIGNATURE_LENGTH = Transformer.SIGNATURE_LENGTH;
|
||||||
|
|
||||||
|
private byte[] signature;
|
||||||
|
|
||||||
|
public GetTransactionMessage(byte[] signature) {
|
||||||
|
this(-1, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GetTransactionMessage(int id, byte[] signature) {
|
||||||
|
super(id, MessageType.GET_TRANSACTION);
|
||||||
|
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSignature() {
|
||||||
|
return this.signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Message fromByteBuffer(int id, ByteBuffer bytes) throws UnsupportedEncodingException {
|
||||||
|
if (bytes.remaining() != TRANSACTION_SIGNATURE_LENGTH)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
byte[] signature = new byte[TRANSACTION_SIGNATURE_LENGTH];
|
||||||
|
|
||||||
|
bytes.get(signature);
|
||||||
|
|
||||||
|
return new GetTransactionMessage(id, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] toData() {
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
bytes.write(this.signature);
|
||||||
|
|
||||||
|
return bytes.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -64,7 +64,8 @@ public abstract class Message {
|
|||||||
GET_SIGNATURES_V2(16),
|
GET_SIGNATURES_V2(16),
|
||||||
PEER_VERIFY(17),
|
PEER_VERIFY(17),
|
||||||
VERIFICATION_CODES(18),
|
VERIFICATION_CODES(18),
|
||||||
HEIGHT_V2(19);
|
HEIGHT_V2(19),
|
||||||
|
GET_TRANSACTION(20);
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
public final Method fromByteBuffer;
|
public final Method fromByteBuffer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user