Browse Source

Synchronizer asks for approval-pending transaction from peer if needed

pull/67/head
catbref 5 years ago
parent
commit
041773cf41
  1. 26
      src/main/java/org/qora/controller/Controller.java
  2. 58
      src/main/java/org/qora/controller/Synchronizer.java
  3. 54
      src/main/java/org/qora/network/message/GetTransactionMessage.java
  4. 3
      src/main/java/org/qora/network/message/Message.java

26
src/main/java/org/qora/controller/Controller.java

@ -37,6 +37,7 @@ import org.qora.network.message.GetBlockSummariesMessage;
import org.qora.network.message.GetPeersMessage;
import org.qora.network.message.GetSignaturesMessage;
import org.qora.network.message.GetSignaturesV2Message;
import org.qora.network.message.GetTransactionMessage;
import org.qora.network.message.HeightMessage;
import org.qora.network.message.HeightV2Message;
import org.qora.network.message.Message;
@ -500,7 +501,7 @@ public class Controller extends Thread {
break;
}
case GET_BLOCK:
case GET_BLOCK: {
GetBlockMessage getBlockMessage = (GetBlockMessage) message;
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);
}
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:
TransactionMessage transactionMessage = (TransactionMessage) message;

58
src/main/java/org/qora/controller/Synchronizer.java

@ -13,6 +13,8 @@ import org.qora.block.BlockChain;
import org.qora.block.GenesisBlock;
import org.qora.data.block.BlockData;
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.message.BlockMessage;
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.GetSignaturesMessage;
import org.qora.network.message.GetSignaturesV2Message;
import org.qora.network.message.GetTransactionMessage;
import org.qora.network.message.Message;
import org.qora.network.message.Message.MessageType;
import org.qora.network.message.SignaturesMessage;
import org.qora.network.message.TransactionMessage;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.repository.RepositoryManager;
import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.TransactionType;
import org.qora.utils.Base58;
import org.qora.utils.NTP;
public class Synchronizer {
@ -231,6 +237,46 @@ public class Synchronizer {
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()) {
LOGGER.info(String.format("Peer %s sent block with invalid signature for height %d", peer, ourHeight));
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();
}
}

54
src/main/java/org/qora/network/message/GetTransactionMessage.java

@ -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;
}
}
}

3
src/main/java/org/qora/network/message/Message.java

@ -64,7 +64,8 @@ public abstract class Message {
GET_SIGNATURES_V2(16),
PEER_VERIFY(17),
VERIFICATION_CODES(18),
HEIGHT_V2(19);
HEIGHT_V2(19),
GET_TRANSACTION(20);
public final int value;
public final Method fromByteBuffer;

Loading…
Cancel
Save