mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-11 17:55:53 +00:00
TransactionBroadcast: only consider a tx rejected if it has more than half peers signalling a reject.
This commit is contained in:
parent
c981555be4
commit
bc60f0d1f2
@ -16,18 +16,14 @@
|
||||
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.common.annotations.*;
|
||||
import com.google.common.base.*;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import org.bitcoinj.utils.*;
|
||||
import org.slf4j.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import javax.annotation.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a single transaction broadcast that we are performing. A broadcast occurs after a new transaction is created
|
||||
@ -49,8 +45,12 @@ public class TransactionBroadcast {
|
||||
/** Used for shuffling the peers before broadcast: unit tests can replace this to make themselves deterministic. */
|
||||
@VisibleForTesting
|
||||
public static Random random = new Random();
|
||||
|
||||
private Transaction pinnedTx;
|
||||
|
||||
// Tracks which nodes sent us a reject message about this broadcast, if any. Useful for debugging.
|
||||
private Map<Peer, RejectMessage> rejects = Collections.synchronizedMap(new HashMap<Peer, RejectMessage>());
|
||||
|
||||
// TODO: Context being owned by BlockChain isn't right w.r.t future intentions so it shouldn't really be optional here.
|
||||
TransactionBroadcast(PeerGroup peerGroup, @Nullable Context context, Transaction tx) {
|
||||
this.peerGroup = peerGroup;
|
||||
@ -73,8 +73,14 @@ public class TransactionBroadcast {
|
||||
if (m instanceof RejectMessage) {
|
||||
RejectMessage rejectMessage = (RejectMessage)m;
|
||||
if (tx.getHash().equals(rejectMessage.getRejectedObjectHash())) {
|
||||
future.setException(new RejectedTransactionException(tx, rejectMessage));
|
||||
peerGroup.removeEventListener(this);
|
||||
rejects.put(peer, rejectMessage);
|
||||
int size = rejects.size();
|
||||
long threshold = Math.round(numWaitingFor / 2.0);
|
||||
if (size > threshold) {
|
||||
log.warn("Threshold for considering broadcast rejected has been reached ({}/{})", size, threshold);
|
||||
future.setException(new RejectedTransactionException(tx, rejectMessage));
|
||||
peerGroup.removeEventListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
@ -147,7 +153,7 @@ public class TransactionBroadcast {
|
||||
@Override
|
||||
public void onConfidenceChanged(TransactionConfidence conf, ChangeReason reason) {
|
||||
// The number of peers that announced this tx has gone up.
|
||||
int numSeenPeers = conf.numBroadcastPeers();
|
||||
int numSeenPeers = conf.numBroadcastPeers() + rejects.size();
|
||||
boolean mined = tx.getAppearsInHashes() != null;
|
||||
log.info("broadcastTransaction: {}: TX {} seen by {} peers{}", reason, pinnedTx.getHashAsString(),
|
||||
numSeenPeers, mined ? " and mined" : "");
|
||||
|
@ -32,6 +32,7 @@ import org.junit.runners.Parameterized;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static org.bitcoinj.core.Coin.*;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -97,6 +98,27 @@ public class TransactionBroadcastTest extends TestWithPeerGroup {
|
||||
assertTrue(future.isDone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectHandling() throws Exception {
|
||||
InboundMessageQueuer[] channels = { connectPeer(0), connectPeer(1), connectPeer(2), connectPeer(3), connectPeer(4) };
|
||||
Transaction tx = new Transaction(params);
|
||||
TransactionBroadcast broadcast = new TransactionBroadcast(peerGroup, blockChain.getContext(), tx);
|
||||
ListenableFuture<Transaction> future = broadcast.broadcast();
|
||||
// 0 and 3 are randomly selected to receive the broadcast.
|
||||
assertEquals(tx, outbound(channels[1]));
|
||||
assertEquals(tx, outbound(channels[2]));
|
||||
assertEquals(tx, outbound(channels[4]));
|
||||
RejectMessage reject = new RejectMessage(params, RejectMessage.RejectCode.DUST, tx.getHash(), "tx", "dust");
|
||||
inbound(channels[1], reject);
|
||||
inbound(channels[4], reject);
|
||||
try {
|
||||
future.get();
|
||||
fail();
|
||||
} catch (ExecutionException e) {
|
||||
assertEquals(RejectedTransactionException.class, e.getCause().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retryFailedBroadcast() throws Exception {
|
||||
// If we create a spend, it's sent to a peer that swallows it, and the peergroup is removed/re-added then
|
||||
|
Loading…
x
Reference in New Issue
Block a user