3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-30 23:02:15 +00:00

Resolve some race conditions.

It was possible that during saving of a wallet a network thread would update the confidence metrics due to broadcast announcements. This change makes TransactionConfidence use a COW list so the broadcast peer set can be iterated over safely. Resolves issue 277.
This commit is contained in:
Mike Hearn 2013-01-12 14:11:11 +01:00
parent 9a65d4cab8
commit 916e33254f
4 changed files with 33 additions and 27 deletions

View File

@ -996,7 +996,7 @@ public class PeerGroup extends AbstractIdleService {
tx.getConfidence().addEventListener(new TransactionConfidence.Listener() {
public void onConfidenceChanged(Transaction tx) {
// This will run in a peer thread.
final int numSeenPeers = tx.getConfidence().getBroadcastBy().size();
final int numSeenPeers = tx.getConfidence().numBroadcastPeers();
boolean done = false;
log.info("broadcastTransaction: TX {} seen by {} peers", pinnedTx.getHashAsString(),
numSeenPeers);

View File

@ -22,8 +22,8 @@ import com.google.common.base.Preconditions;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.ListIterator;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* <p>A TransactionConfidence object tracks data you can use to make a confidence decision about a transaction.
@ -61,7 +61,7 @@ public class TransactionConfidence implements Serializable {
* IP address as an approximation. It's obviously vulnerable to being gamed if we allow arbitrary people to connect
* to us, so only peers we explicitly connected to should go here.
*/
private Set<PeerAddress> broadcastBy;
private CopyOnWriteArrayList<PeerAddress> broadcastBy;
/** The Transaction that this confidence object is associated with. */
private Transaction transaction;
// Lazily created listeners array.
@ -180,7 +180,7 @@ public class TransactionConfidence implements Serializable {
public TransactionConfidence(Transaction tx) {
// Assume a default number of peers for our set.
broadcastBy = new HashSet<PeerAddress>(10);
broadcastBy = new CopyOnWriteArrayList<PeerAddress>();
transaction = tx;
}
@ -234,8 +234,9 @@ public class TransactionConfidence implements Serializable {
* @param address IP address of the peer, used as a proxy for identity.
*/
public void markBroadcastBy(PeerAddress address) {
if (!broadcastBy.addIfAbsent(address))
return; // Duplicate.
synchronized (this) {
broadcastBy.add(address);
if (getConfidenceType() == ConfidenceType.UNKNOWN) {
this.confidenceType = ConfidenceType.NOT_SEEN_IN_CHAIN;
}
@ -246,15 +247,20 @@ public class TransactionConfidence implements Serializable {
/**
* Returns how many peers have been passed to {@link TransactionConfidence#markBroadcastBy}.
*/
public synchronized int numBroadcastPeers() {
public int numBroadcastPeers() {
return broadcastBy.size();
}
/**
* Returns a synchronized set of {@link PeerAddress}es that announced the transaction.
*/
public synchronized Set<PeerAddress> getBroadcastBy() {
return broadcastBy;
public ListIterator<PeerAddress> getBroadcastBy() {
return broadcastBy.listIterator();
}
/** Returns true if the given address has been seen via markBroadcastBy() */
public boolean wasBroadcastBy(PeerAddress address) {
return broadcastBy.contains(address);
}
@Override

View File

@ -31,10 +31,7 @@ import java.io.OutputStream;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* Serialize and de-serialize a wallet to a byte stream containing a
@ -203,19 +200,22 @@ public class WalletProtobufSerializer {
private static void writeConfidence(Protos.Transaction.Builder txBuilder,
TransactionConfidence confidence,
Protos.TransactionConfidence.Builder confidenceBuilder) {
confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue()));
if (confidence.getConfidenceType() == ConfidenceType.BUILDING) {
confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight());
confidenceBuilder.setDepth(confidence.getDepthInBlocks());
if (confidence.getWorkDone() != null) {
confidenceBuilder.setWorkDone(confidence.getWorkDone().longValue());
synchronized (confidence) {
confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue()));
if (confidence.getConfidenceType() == ConfidenceType.BUILDING) {
confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight());
confidenceBuilder.setDepth(confidence.getDepthInBlocks());
if (confidence.getWorkDone() != null) {
confidenceBuilder.setWorkDone(confidence.getWorkDone().longValue());
}
}
if (confidence.getConfidenceType() == ConfidenceType.DEAD) {
Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash();
confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash));
}
}
if (confidence.getConfidenceType() == ConfidenceType.DEAD) {
Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash();
confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash));
}
for (PeerAddress address : confidence.getBroadcastBy()) {
for (ListIterator<PeerAddress> it = confidence.getBroadcastBy(); it.hasNext();) {
PeerAddress address = it.next();
Protos.PeerAddress proto = Protos.PeerAddress.newBuilder()
.setIpAddress(ByteString.copyFrom(address.getAddr().getAddress()))
.setPort(address.getPort())

View File

@ -268,8 +268,8 @@ public class PeerGroupTest extends TestWithNetworkConnections {
inbound(p1, tx); // returns nothing after a queue drain.
// Two peers saw this tx hash.
assertEquals(2, tx.getConfidence().numBroadcastPeers());
assertTrue(tx.getConfidence().getBroadcastBy().contains(peerOf(p1).getAddress()));
assertTrue(tx.getConfidence().getBroadcastBy().contains(peerOf(p2).getAddress()));
assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p1).getAddress()));
assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p2).getAddress()));
tx.getConfidence().addEventListener(new TransactionConfidence.Listener() {
public void onConfidenceChanged(Transaction tx) {
@ -280,7 +280,7 @@ public class PeerGroupTest extends TestWithNetworkConnections {
inbound(p3, inv);
assertEquals(tx, event[1]);
assertEquals(3, tx.getConfidence().numBroadcastPeers());
assertTrue(tx.getConfidence().getBroadcastBy().contains(peerOf(p3).getAddress()));
assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p3).getAddress()));
}
@Test