mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-12 02:05:53 +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:
parent
9a65d4cab8
commit
916e33254f
@ -996,7 +996,7 @@ public class PeerGroup extends AbstractIdleService {
|
|||||||
tx.getConfidence().addEventListener(new TransactionConfidence.Listener() {
|
tx.getConfidence().addEventListener(new TransactionConfidence.Listener() {
|
||||||
public void onConfidenceChanged(Transaction tx) {
|
public void onConfidenceChanged(Transaction tx) {
|
||||||
// This will run in a peer thread.
|
// This will run in a peer thread.
|
||||||
final int numSeenPeers = tx.getConfidence().getBroadcastBy().size();
|
final int numSeenPeers = tx.getConfidence().numBroadcastPeers();
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
log.info("broadcastTransaction: TX {} seen by {} peers", pinnedTx.getHashAsString(),
|
log.info("broadcastTransaction: TX {} seen by {} peers", pinnedTx.getHashAsString(),
|
||||||
numSeenPeers);
|
numSeenPeers);
|
||||||
|
@ -22,8 +22,8 @@ import com.google.common.base.Preconditions;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.ListIterator;
|
||||||
import java.util.Set;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A TransactionConfidence object tracks data you can use to make a confidence decision about a transaction.
|
* <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
|
* 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.
|
* 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. */
|
/** The Transaction that this confidence object is associated with. */
|
||||||
private Transaction transaction;
|
private Transaction transaction;
|
||||||
// Lazily created listeners array.
|
// Lazily created listeners array.
|
||||||
@ -180,7 +180,7 @@ public class TransactionConfidence implements Serializable {
|
|||||||
|
|
||||||
public TransactionConfidence(Transaction tx) {
|
public TransactionConfidence(Transaction tx) {
|
||||||
// Assume a default number of peers for our set.
|
// Assume a default number of peers for our set.
|
||||||
broadcastBy = new HashSet<PeerAddress>(10);
|
broadcastBy = new CopyOnWriteArrayList<PeerAddress>();
|
||||||
transaction = tx;
|
transaction = tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,8 +234,9 @@ public class TransactionConfidence implements Serializable {
|
|||||||
* @param address IP address of the peer, used as a proxy for identity.
|
* @param address IP address of the peer, used as a proxy for identity.
|
||||||
*/
|
*/
|
||||||
public void markBroadcastBy(PeerAddress address) {
|
public void markBroadcastBy(PeerAddress address) {
|
||||||
|
if (!broadcastBy.addIfAbsent(address))
|
||||||
|
return; // Duplicate.
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
broadcastBy.add(address);
|
|
||||||
if (getConfidenceType() == ConfidenceType.UNKNOWN) {
|
if (getConfidenceType() == ConfidenceType.UNKNOWN) {
|
||||||
this.confidenceType = ConfidenceType.NOT_SEEN_IN_CHAIN;
|
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}.
|
* Returns how many peers have been passed to {@link TransactionConfidence#markBroadcastBy}.
|
||||||
*/
|
*/
|
||||||
public synchronized int numBroadcastPeers() {
|
public int numBroadcastPeers() {
|
||||||
return broadcastBy.size();
|
return broadcastBy.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a synchronized set of {@link PeerAddress}es that announced the transaction.
|
* Returns a synchronized set of {@link PeerAddress}es that announced the transaction.
|
||||||
*/
|
*/
|
||||||
public synchronized Set<PeerAddress> getBroadcastBy() {
|
public ListIterator<PeerAddress> getBroadcastBy() {
|
||||||
return broadcastBy;
|
return broadcastBy.listIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if the given address has been seen via markBroadcastBy() */
|
||||||
|
public boolean wasBroadcastBy(PeerAddress address) {
|
||||||
|
return broadcastBy.contains(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,10 +31,7 @@ import java.io.OutputStream;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize and de-serialize a wallet to a byte stream containing a
|
* Serialize and de-serialize a wallet to a byte stream containing a
|
||||||
@ -203,6 +200,7 @@ public class WalletProtobufSerializer {
|
|||||||
private static void writeConfidence(Protos.Transaction.Builder txBuilder,
|
private static void writeConfidence(Protos.Transaction.Builder txBuilder,
|
||||||
TransactionConfidence confidence,
|
TransactionConfidence confidence,
|
||||||
Protos.TransactionConfidence.Builder confidenceBuilder) {
|
Protos.TransactionConfidence.Builder confidenceBuilder) {
|
||||||
|
synchronized (confidence) {
|
||||||
confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue()));
|
confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue()));
|
||||||
if (confidence.getConfidenceType() == ConfidenceType.BUILDING) {
|
if (confidence.getConfidenceType() == ConfidenceType.BUILDING) {
|
||||||
confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight());
|
confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight());
|
||||||
@ -215,7 +213,9 @@ public class WalletProtobufSerializer {
|
|||||||
Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash();
|
Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash();
|
||||||
confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash));
|
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()
|
Protos.PeerAddress proto = Protos.PeerAddress.newBuilder()
|
||||||
.setIpAddress(ByteString.copyFrom(address.getAddr().getAddress()))
|
.setIpAddress(ByteString.copyFrom(address.getAddr().getAddress()))
|
||||||
.setPort(address.getPort())
|
.setPort(address.getPort())
|
||||||
|
@ -268,8 +268,8 @@ public class PeerGroupTest extends TestWithNetworkConnections {
|
|||||||
inbound(p1, tx); // returns nothing after a queue drain.
|
inbound(p1, tx); // returns nothing after a queue drain.
|
||||||
// Two peers saw this tx hash.
|
// Two peers saw this tx hash.
|
||||||
assertEquals(2, tx.getConfidence().numBroadcastPeers());
|
assertEquals(2, tx.getConfidence().numBroadcastPeers());
|
||||||
assertTrue(tx.getConfidence().getBroadcastBy().contains(peerOf(p1).getAddress()));
|
assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p1).getAddress()));
|
||||||
assertTrue(tx.getConfidence().getBroadcastBy().contains(peerOf(p2).getAddress()));
|
assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p2).getAddress()));
|
||||||
|
|
||||||
tx.getConfidence().addEventListener(new TransactionConfidence.Listener() {
|
tx.getConfidence().addEventListener(new TransactionConfidence.Listener() {
|
||||||
public void onConfidenceChanged(Transaction tx) {
|
public void onConfidenceChanged(Transaction tx) {
|
||||||
@ -280,7 +280,7 @@ public class PeerGroupTest extends TestWithNetworkConnections {
|
|||||||
inbound(p3, inv);
|
inbound(p3, inv);
|
||||||
assertEquals(tx, event[1]);
|
assertEquals(tx, event[1]);
|
||||||
assertEquals(3, tx.getConfidence().numBroadcastPeers());
|
assertEquals(3, tx.getConfidence().numBroadcastPeers());
|
||||||
assertTrue(tx.getConfidence().getBroadcastBy().contains(peerOf(p3).getAddress()));
|
assertTrue(tx.getConfidence().wasBroadcastBy(peerOf(p3).getAddress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user