Change Address Heuristics

BlockSci supports a number of different heuristics for determining the change address for a given transaction. The heuristics are sometimes contradictory and thus we provide users with the ability to choose which heuristics they wish to apply. Further, you can combine different change address heuristics through the various composition operators of the blocksci.heuristics.change.ChangeHeuristic class.

class blocksci.heuristics.change
class ChangeHeuristic

Class representing a change heuristic

__and__(other_heuristic: blocksci.heuristics.change.ChangeHeuristic)blocksci.heuristics.change.ChangeHeuristic

Return a new heuristic matching outputs that match both of the given heuristics

property __call__

Return all outputs matching the change heuristic

__or__(other_heuristic: blocksci.heuristics.change.ChangeHeuristic)blocksci.heuristics.change.ChangeHeuristic

Return a new heuristic matching outputs that match either of the given heuristics

__sub__(other_heuristic: blocksci.heuristics.change.ChangeHeuristic)blocksci.heuristics.change.ChangeHeuristic

Return a new heuristic matching outputs matched by the first heuristic unless they’re matched by the second heuristic

property unique_change

Return a new heuristic that will return a single output if it’s the only candidate output, and no outputs otherwise.

Static Heuristics

Static heuristics do not depend on the outputs being spent. They will always return the same result.

change.address_reuse = <blocksci.heuristics.change.ChangeHeuristic object>

If input addresses appear as an output address, the client might have reused addresses for change.

change.address_type = <blocksci.heuristics.change.ChangeHeuristic object>

If all inputs are of one address type (e.g., P2PKH or P2SH), it is likely that the change output has the same type.

change.optimal_change = <blocksci.heuristics.change.ChangeHeuristic object>

If there exists an output that is smaller than any of the inputs it is likely the change. If a change output was larger than the smallest input, then the coin selection algorithm wouldn’t need to add the input in the first place.

blocksci.heuristics.change.power_of_ten_value(digits, tx=None)

Detects possible change outputs by excluding output values that are multiples of 10^`digits`, as such values are unlikely to occur randomly.

Dynamic Heuristics

Dynamic heuristics depend on the state of the blockchain. If outputs of a transaction are spent, the results returned by the following heuristics can change.

change.client_change_address_behavior = <blocksci.heuristics.change.ChangeHeuristic object>

Most clients will generate a fresh address for the change. If an output is the first to send value to an address, it is potentially the change.

change.legacy = <blocksci.heuristics.change.ChangeHeuristic object>

The original change address heuristic that was the default used in BlockSci before version 0.6. It consists of the intersection of the optimal change heuristic and the client address behavior heuristic

change.locktime = <blocksci.heuristics.change.ChangeHeuristic object>

Bitcoin Core sets the locktime to the current block height to prevent fee sniping. If an output has been spent and it matches this transaction’s locktime behavior, it is possibly the change output. (This heuristic will return unspent outputs as potential candidates.)

change.peeling_chain = <blocksci.heuristics.change.ChangeHeuristic object>

If the transaction is a peeling chain, returns the outputs that continue the peeling chain. (This heuristic will return unspent outputs as potential candidates.)

Utility Heuristics

change.none = <blocksci.heuristics.change.ChangeHeuristic object>

This heuristics will never return any outputs, allowing it to be used to disable clustering based on change addresses.

change.spent = <blocksci.heuristics.change.ChangeHeuristic object>

Returns outputs that have been spent. Useful to exclude unspent outputs from dynamic heuristics that return unspent outputs as potential change outputs.

Using Composition Operators

You can use the union (|), intersection (&) and difference (-) operators to combine existing change address heuristics into new ones. This also works with unique_change. For example, the following heuristic would return a single change output only if the address reuse and the optimal change heuristics agree on a unique change output:

blocksci.heuristics.change.address_reuse.unique_change & blocksci.heuristics.change.optimal_change.unique_change

Similarly, if you want to remove unspent outputs from the output candidates returned by the blocksci.heuristics.change.locktime heuristics, you can do the following:

blocksci.heuristics.change.locktime & blocksci.heuristics.change.spent