All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bitcoinj.core.TransactionConfidence Maven / Gradle / Ivy

There is a newer version: 0.15-cm06
Show newest version
/*
 * Copyright 2011 Google Inc.
 * Copyright 2014 Andreas Schildbach
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bitcoinj.core;

import com.google.common.collect.*;
import com.google.common.util.concurrent.*;

import org.bitcoinj.core.listeners.BlockChainListener;
import org.bitcoinj.utils.*;
import org.bitcoinj.wallet.Wallet;

import javax.annotation.*;
import java.util.*;
import java.util.concurrent.*;

import static com.google.common.base.Preconditions.*;

// TODO: Modify the getDepthInBlocks method to require the chain height to be specified, in preparation for ceasing to touch every tx on every block.

/**
 * 

A TransactionConfidence object tracks data you can use to make a confidence decision about a transaction. * It also contains some pre-canned rules for common scenarios: if you aren't really sure what level of confidence * you need, these should prove useful. You can get a confidence object using {@link Transaction#getConfidence()}. * They cannot be constructed directly.

* *

Confidence in a transaction can come in multiple ways:

* *
    *
  • Because you created it yourself and only you have the necessary keys.
  • *
  • Receiving it from a fully validating peer you know is trustworthy, for instance, because it's run by yourself.
  • *
  • Receiving it from a peer on the network you randomly chose. If your network connection is not being * intercepted, you have a pretty good chance of connecting to a node that is following the rules.
  • *
  • Receiving it from multiple peers on the network. If your network connection is not being intercepted, * hearing about a transaction from multiple peers indicates the network has accepted the transaction and * thus miners likely have too (miners have the final say in whether a transaction becomes valid or not).
  • *
  • Seeing the transaction appear appear in a block on the main chain. Your confidence increases as the transaction * becomes further buried under work. Work can be measured either in blocks (roughly, units of time), or * amount of work done.
  • *
* *

Alternatively, you may know that the transaction is "dead", that is, one or more of its inputs have * been double spent and will never confirm unless there is another re-org.

* *

TransactionConfidence is updated via the {@link org.bitcoinj.core.TransactionConfidence#incrementDepthInBlocks()} * method to ensure the block depth is up to date.

* To make a copy that won't be changed, use {@link org.bitcoinj.core.TransactionConfidence#duplicate()}. */ public class TransactionConfidence { /** * The peers that have announced the transaction to us. Network nodes don't have stable identities, so we use * 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 CopyOnWriteArrayList broadcastBy; /** The time the transaction was last announced to us. */ private Date lastBroadcastedAt; /** The Transaction that this confidence object is associated with. */ private final Sha256Hash hash; // Lazily created listeners array. private CopyOnWriteArrayList> listeners; // The depth of the transaction on the best chain in blocks. An unconfirmed block has depth 0. private int depth; /** Describes the state of the transaction in general terms. Properties can be read to learn specifics. */ public enum ConfidenceType { /** If BUILDING, then the transaction is included in the best chain and your confidence in it is increasing. */ BUILDING(1), /** * If PENDING, then the transaction is unconfirmed and should be included shortly, as long as it is being * announced and is considered valid by the network. A pending transaction will be announced if the containing * wallet has been attached to a live {@link PeerGroup} using {@link PeerGroup#addWallet(Wallet)}. * You can estimate how likely the transaction is to be included by connecting to a bunch of nodes then measuring * how many announce it, using {@link org.bitcoinj.core.TransactionConfidence#numBroadcastPeers()}. * Or if you saw it from a trusted peer, you can assume it's valid and will get mined sooner or later as well. */ PENDING(2), /** * If DEAD, then it means the transaction won't confirm unless there is another re-org, * because some other transaction is spending one of its inputs. Such transactions should be alerted to the user * so they can take action, eg, suspending shipment of goods if they are a merchant. * It can also mean that a coinbase transaction has been made dead from it being moved onto a side chain. */ DEAD(4), /** * If IN_CONFLICT, then it means there is another transaction (or several other transactions) spending one * (or several) of its inputs but nor this transaction nor the other/s transaction/s are included in the best chain. * The other/s transaction/s should be IN_CONFLICT too. * IN_CONFLICT can be thought as an intermediary state between a) PENDING and BUILDING or b) PENDING and DEAD. * Another common name for this situation is "double spend". */ IN_CONFLICT(5), /** * If a transaction hasn't been broadcast yet, or there's no record of it, its confidence is UNKNOWN. */ UNKNOWN(0); private int value; ConfidenceType(int value) { this.value = value; } public int getValue() { return value; } } private ConfidenceType confidenceType = ConfidenceType.UNKNOWN; private int appearedAtChainHeight = -1; // The transaction that double spent this one, if any. private Transaction overridingTransaction; /** * Information about where the transaction was first seen (network, sent direct from peer, created by ourselves). * Useful for risk analyzing pending transactions. Probably not that useful after a tx is included in the chain, * unless re-org double spends start happening frequently. */ public enum Source { /** We don't know where the transaction came from. */ UNKNOWN, /** We got this transaction from a network peer. */ NETWORK, /** This transaction was created by our own wallet, so we know it's not a double spend. */ SELF } private Source source = Source.UNKNOWN; public TransactionConfidence(Sha256Hash hash) { // Assume a default number of peers for our set. broadcastBy = new CopyOnWriteArrayList<>(); listeners = new CopyOnWriteArrayList<>(); this.hash = hash; } /** *

A confidence listener is informed when the level of {@link TransactionConfidence} is updated by something, like * for example a {@link Wallet}. You can add listeners to update your user interface or manage your order tracking * system when confidence levels pass a certain threshold. Note that confidence can go down as well as up. * For example, this can happen if somebody is doing a double-spend attack against you. Whilst it's unlikely, your * code should be able to handle that in order to be correct.

* *

During listener execution, it's safe to remove the current listener but not others.

*/ public interface Listener { /** An enum that describes why a transaction confidence listener is being invoked (i.e. the class of change). */ enum ChangeReason { /** * Occurs when the type returned by {@link org.bitcoinj.core.TransactionConfidence#getConfidenceType()} * has changed. For example, if a PENDING transaction changes to BUILDING or DEAD, then this reason will * be given. It's a high level summary. */ TYPE, /** * Occurs when a transaction that is in the best known block chain gets buried by another block. If you're * waiting for a certain number of confirmations, this is the reason to watch out for. */ DEPTH, /** * Occurs when a pending transaction (not in the chain) was announced by another connected peers. By * watching the number of peers that announced a transaction go up, you can see whether it's being * accepted by the network or not. If all your peers announce, it's a pretty good bet the transaction * is considered relayable and has thus reached the miners. */ SEEN_PEERS, } void onConfidenceChanged(TransactionConfidence confidence, ChangeReason reason); } // This is used to ensure that confidence objects which aren't referenced from anywhere but which have an event // listener set on them don't become eligible for garbage collection. Otherwise the TxConfidenceTable, which only // has weak references to these objects, would not be enough to keep the event listeners working as transactions // propagate around the network - it cannot know directly if the API user is interested in the object, so it uses // heap reachability as a proxy for interest. // // We add ourselves to this set when a listener is added and remove ourselves when the listener list is empty. private static final Set pinnedConfidenceObjects = Collections.synchronizedSet(new HashSet()); /** *

Adds an event listener that will be run when this confidence object is updated. The listener will be locked and * is likely to be invoked on a peer thread.

* *

Note that this is NOT called when every block arrives. Instead it is called when the transaction * transitions between confidence states, ie, from not being seen in the chain to being seen (not necessarily in * the best chain). If you want to know when the transaction gets buried under another block, consider using * a future from {@link #getDepthFuture(int)}.

*/ public void addEventListener(Executor executor, Listener listener) { checkNotNull(listener); listeners.addIfAbsent(new ListenerRegistration<>(listener, executor)); pinnedConfidenceObjects.add(this); } /** *

Adds an event listener that will be run when this confidence object is updated. The listener will be locked and * is likely to be invoked on a peer thread.

* *

Note that this is NOT called when every block arrives. Instead it is called when the transaction * transitions between confidence states, ie, from not being seen in the chain to being seen (not necessarily in * the best chain). If you want to know when the transaction gets buried under another block, implement a * {@link BlockChainListener}, attach it to a {@link BlockChain} and then use the getters on the * confidence object to determine the new depth.

*/ public void addEventListener(Listener listener) { addEventListener(Threading.USER_THREAD, listener); } public boolean removeEventListener(Listener listener) { checkNotNull(listener); boolean removed = ListenerRegistration.removeFromList(listener, listeners); if (listeners.isEmpty()) pinnedConfidenceObjects.remove(this); return removed; } /** * Returns the chain height at which the transaction appeared if confidence type is BUILDING. * @throws IllegalStateException if the confidence type is not BUILDING. */ public synchronized int getAppearedAtChainHeight() { if (getConfidenceType() != ConfidenceType.BUILDING) throw new IllegalStateException("Confidence type is " + getConfidenceType() + ", not BUILDING"); return appearedAtChainHeight; } /** * The chain height at which the transaction appeared, if it has been seen in the best chain. Automatically sets * the current type to {@link ConfidenceType#BUILDING} and depth to one. */ public synchronized void setAppearedAtChainHeight(int appearedAtChainHeight) { if (appearedAtChainHeight < 0) throw new IllegalArgumentException("appearedAtChainHeight out of range"); this.appearedAtChainHeight = appearedAtChainHeight; this.depth = 1; setConfidenceType(ConfidenceType.BUILDING); } /** * Returns a general statement of the level of confidence you can have in this transaction. */ public synchronized ConfidenceType getConfidenceType() { return confidenceType; } /** * Called by other objects in the system, like a {@link Wallet}, when new information about the confidence of a * transaction becomes available. */ public synchronized void setConfidenceType(ConfidenceType confidenceType) { if (confidenceType == this.confidenceType) return; this.confidenceType = confidenceType; if (confidenceType != ConfidenceType.DEAD) { overridingTransaction = null; } if (confidenceType == ConfidenceType.PENDING || confidenceType == ConfidenceType.IN_CONFLICT) { depth = 0; appearedAtChainHeight = -1; } } /** * Called by a {@link Peer} when a transaction is pending and announced by a peer. The more peers announce the * transaction, the more peers have validated it (assuming your internet connection is not being intercepted). * If confidence is currently unknown, sets it to {@link ConfidenceType#PENDING}. Does not run listeners. * * @param address IP address of the peer, used as a proxy for identity. * @return true if marked, false if this address was already seen */ public boolean markBroadcastBy(PeerAddress address) { lastBroadcastedAt = Utils.now(); if (!broadcastBy.addIfAbsent(address)) return false; // Duplicate. synchronized (this) { if (getConfidenceType() == ConfidenceType.UNKNOWN) { this.confidenceType = ConfidenceType.PENDING; } } return true; } /** * Returns how many peers have been passed to {@link TransactionConfidence#markBroadcastBy}. */ public int numBroadcastPeers() { return broadcastBy.size(); } /** * Returns a snapshot of {@link PeerAddress}es that announced the transaction. */ public Set getBroadcastBy() { ListIterator iterator = broadcastBy.listIterator(); return Sets.newHashSet(iterator); } /** Returns true if the given address has been seen via markBroadcastBy() */ public boolean wasBroadcastBy(PeerAddress address) { return broadcastBy.contains(address); } /** Return the time the transaction was last announced to us. */ public Date getLastBroadcastedAt() { return lastBroadcastedAt; } /** Set the time the transaction was last announced to us. */ public void setLastBroadcastedAt(Date lastBroadcastedAt) { this.lastBroadcastedAt = lastBroadcastedAt; } @Override public synchronized String toString() { StringBuilder builder = new StringBuilder(); int peers = numBroadcastPeers(); if (peers > 0) { builder.append("Seen by ").append(peers).append(peers > 1 ? " peers" : " peer"); if (lastBroadcastedAt != null) builder.append(" (most recently: ").append(Utils.dateTimeFormat(lastBroadcastedAt)).append(")"); builder.append(". "); } switch (getConfidenceType()) { case UNKNOWN: builder.append("Unknown confidence level."); break; case DEAD: builder.append("Dead: overridden by double spend and will not confirm."); break; case PENDING: builder.append("Pending/unconfirmed."); break; case IN_CONFLICT: builder.append("In conflict."); break; case BUILDING: builder.append(String.format(Locale.US, "Appeared in best chain at height %d, depth %d.", getAppearedAtChainHeight(), getDepthInBlocks())); break; } if (source != Source.UNKNOWN) builder.append(" Source: ").append(source); return builder.toString(); } /** * Called by the wallet when the tx appears on the best chain and a new block is added to the top. Updates the * internal counter that tracks how deeply buried the block is. * * @return the new depth */ public synchronized int incrementDepthInBlocks() { return ++this.depth; } /** *

Depth in the chain is an approximation of how much time has elapsed since the transaction has been confirmed. * On average there is supposed to be a new block every 10 minutes, but the actual rate may vary. Bitcoin Core * considers a transaction impractical to reverse after 6 blocks, but as of EOY 2011 network * security is high enough that often only one block is considered enough even for high value transactions. For low * value transactions like songs, or other cheap items, no blocks at all may be necessary.

* *

If the transaction appears in the top block, the depth is one. If it's anything else (pending, dead, unknown) * the depth is zero.

*/ public synchronized int getDepthInBlocks() { return depth; } /* * Set the depth in blocks. Having one block confirmation is a depth of one. */ public synchronized void setDepthInBlocks(int depth) { this.depth = depth; } /** * Erases the set of broadcast/seen peers. This cannot be called whilst the confidence is PENDING. It is useful * for saving memory and wallet space once a tx is buried so deep it doesn't seem likely to go pending again. */ public void clearBroadcastBy() { checkState(getConfidenceType() != ConfidenceType.PENDING); broadcastBy.clear(); lastBroadcastedAt = null; } /** * If this transaction has been overridden by a double spend (is dead), this call returns the overriding transaction. * Note that this call can return null if you have migrated an old wallet, as pre-Jan 2012 wallets did not * store this information. * * @return the transaction that double spent this one * @throws IllegalStateException if confidence type is not DEAD. */ public synchronized Transaction getOverridingTransaction() { if (getConfidenceType() != ConfidenceType.DEAD) throw new IllegalStateException("Confidence type is " + getConfidenceType() + ", not DEAD"); return overridingTransaction; } /** * Called when the transaction becomes newly dead, that is, we learn that one of its inputs has already been spent * in such a way that the double-spending transaction takes precedence over this one. It will not become valid now * unless there is a re-org. Automatically sets the confidence type to DEAD. The overriding transaction may not * directly double spend this one, but could also have double spent a dependency of this tx. */ public synchronized void setOverridingTransaction(@Nullable Transaction overridingTransaction) { this.overridingTransaction = overridingTransaction; setConfidenceType(ConfidenceType.DEAD); } /** Returns a copy of this object. Event listeners are not duplicated. */ public TransactionConfidence duplicate() { TransactionConfidence c = new TransactionConfidence(hash); c.broadcastBy.addAll(broadcastBy); c.lastBroadcastedAt = lastBroadcastedAt; synchronized (this) { c.confidenceType = confidenceType; c.overridingTransaction = overridingTransaction; c.appearedAtChainHeight = appearedAtChainHeight; } return c; } /** * Call this after adjusting the confidence, for cases where listeners should be notified. This has to be done * explicitly rather than being done automatically because sometimes complex changes to transaction states can * result in a series of confidence changes that are not really useful to see separately. By invoking listeners * explicitly, more precise control is available. Note that this will run the listeners on the user code thread. */ public void queueListeners(final Listener.ChangeReason reason) { for (final ListenerRegistration registration : listeners) { registration.executor.execute(new Runnable() { @Override public void run() { registration.listener.onConfidenceChanged(TransactionConfidence.this, reason); } }); } } /** * The source of a transaction tries to identify where it came from originally. For instance, did we download it * from the peer to peer network, or make it ourselves, or receive it via Bluetooth, or import it from another app, * and so on. This information is useful for {@link org.bitcoinj.wallet.CoinSelector} implementations to risk analyze * transactions and decide when to spend them. */ public synchronized Source getSource() { return source; } /** * The source of a transaction tries to identify where it came from originally. For instance, did we download it * from the peer to peer network, or make it ourselves, or receive it via Bluetooth, or import it from another app, * and so on. This information is useful for {@link org.bitcoinj.wallet.CoinSelector} implementations to risk analyze * transactions and decide when to spend them. */ public synchronized void setSource(Source source) { this.source = source; } /** * Returns a future that completes when the transaction has been confirmed by "depth" blocks. For instance setting * depth to one will wait until it appears in a block on the best chain, and zero will wait until it has been seen * on the network. */ public synchronized ListenableFuture getDepthFuture(final int depth, Executor executor) { final SettableFuture result = SettableFuture.create(); if (getDepthInBlocks() >= depth) { result.set(this); } addEventListener(executor, new Listener() { @Override public void onConfidenceChanged(TransactionConfidence confidence, ChangeReason reason) { if (getDepthInBlocks() >= depth) { removeEventListener(this); result.set(confidence); } } }); return result; } public synchronized ListenableFuture getDepthFuture(final int depth) { return getDepthFuture(depth, Threading.USER_THREAD); } public Sha256Hash getTransactionHash() { return hash; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy