Wallet Fingerprints: Detection & Analysis
Table of Contents
This summer, I worked on identifying fingerprints in Bitcoin transactions, and automating their detection. By implementing a few heuristics, I was able to guess the originating wallet for about 50% of recent transactions. A wallet fingerprint is an aspect of a broadcasted Bitcoin transaction that can aid in identifying the wallet used to create that transaction. Identifying the wallet that created a transaction could ultimately lead to learning private information about the creator of a transaction. For example, if someone is using Trezor, they are more likely to have a substantial amount of Bitcoin compared to someone using a hot wallet, making them a more promising hacking target.
A large part of pseudo-anonymity in Bitcoin means ensuring that an adversary can’t learn additional information about the person who created a transaction, beyond their public key. The presence of wallet fingerprints on transactions violates this privacy. In this work, I identify several fingerprints across a variety of wallet software and use them to demonstrate the extent to which wallet fingerprinting can be done effectively. Once fingerprints have been identified, automating their detection becomes trivial, and it is easy to collect information such as the following:
The Consequences of Effective Wallet Fingerprinting
You can learn a lot about a user by looking at the wallet software they use. For example, if someone is using Bitcoin Core, they are more likely to have a deeper understanding of Bitcoin than someone who uses a more mainstream wallet with fewer features. Additionally, if someone is using a hardware wallet, it indicates that they likely have more Bitcoin than someone who uses a mobile wallet. Furthermore, this information is accessible to anyone who sends you Bitcoin, like an employer or customer, and anyone who you send Bitcoin to. On a practical level, if an attacker can identify the wallet that you are using, it becomes much easier to decide if you are a good target and to exploit known vulnerabilities in that particular software.
There are also issues with privacy measures that arise when transactions can be easily fingerprinted. For example, sometimes users use the same wallet software on both sides of a coinjoin transaction. If one of the users in the inputs is using a specific version of some software, and there is an output that is spent using this same software, then this shows that this input and output belong to the same user. This not only renders the coin-join ineffective for them, but it also lowers the confidentiality of other users in the coin-join. Because knowing the wallet someone uses is a privacy leak, this can make other privacy-preserving techniques less effective in general.
Types of Fingerprints
I have identified a variety of fingerprints that can be combined to identify the wallet that created a given transaction. I have divided these fingerprints into four primary types: Independent, Probabilistic, Dependent, and Temporal. These groups are not definite, and some fingerprints from one group can exhibit a few characteristics of another group. These groups roughly show how difficult a fingerprint is to identify in a transaction, and how much weightage that fingerprint should be given for identifying an originating wallet.
Independent fingerprints can be seen directly in a transaction, and are independent from other aspects of the transaction. This is the easiest type of fingerprint to detect. These fingerprints are generally simple and include a transaction’s version number, and the types of outputs being sent.
With Probabilistic fingerprints, almost every wallet will occasionally produce transactions that exhibit the fingerprint. The more examples there are of a transaction exhibiting certain behavior, the more confident we can be that wallet software that leaves the given fingerprint intentionally was used. These fingerprints are also independent of other characteristics of the transaction. A useful example of this is the input and output order in a transaction. Some wallets order their inputs lexicographically and outputs by ascending amount according to BIP-69. While a wallet that implements BIP-69 will always order all their inputs and outputs accordingly, a wallet that does not implement BIP-69 may conform to its rule by chance.
Dependent fingerprints rely on other heuristic-based information having been deduced correctly, such as being able to detect the change output correctly. Detecting change outputs can be quite easy using a few simple heuristics, but it would still be possible to guess incorrectly, so there should typically be less confidence with these fingerprinting methods.
The most uncommon fingerprints are Temporal. They either need mempool data in order to be detected, or take into account the block heights of certain transactions. This makes them more difficult to identify. Temporal fingerprints typically relate to some sort of fee-bumping and/or unconfirmed inputs.
Previous Wallet Fingerprinting Research
Wallet fingerprinting is not an unknown concept to the Bitcoin community. It is typically discussed theoretically and is considered something that should generally be avoided. To the extent that fingerprinting research has been done, it has typically been focused on just one fingerprint or just a few wallets. Examples of this include achow101’s list of Bitcoin Core and Electrum fingerprints and 0xb10c’s blog post on using feerates to fingerprint blockchain.com transactions. In this work, I aimed at getting a wider understanding of fingerprinting, and the scale to which it can be done.
Because fingerprinting all versions of all available wallet software would be infeasible, there are certain things to keep in mind when looking at the following methodology and results. Firstly, when I refer to a certain wallet, I mean the version of that particular software listed below. It is likely that these fingerprints also apply to other versions of these wallets, but that has not been verified. Additionally, these fingerprints might not apply to wallets that are being run with settings different from the defaults. Finally, there are many wallets that I have not looked at that may have the same fingerprints as the wallets which I did look at, so none of the fingerprint combinations discussed here should be considered as belonging exclusively to a single wallet.
Looking at a Variety of Wallets
In order to represent different kinds of Bitcoin software I decided to look at eight wallets with varying security models and target users:
- Bitcoin Core (v. 25.0)
- Electrum (v. 4.4.5)
- Blue Wallet (v. 6.4.6 iOS)
- Exodus (v. 23.7.2 iOS)
- Trust Wallet (v. 9. 0 iOS)
- Coinbase Wallet (v. 28.53 iOS)
- Trezor Suite (v. 23.7.2)
- Ledger Live (v. 2.64.2)
Looking at Wallet Software
The majority of the investigated wallets are open-source (Bitcoin Core, Electrum, Blue Wallet, Trezor, Ledger). This not only allows for looking at the code and determining fingerprints from there, but also for looking at pull requests where fingerprints may have been added or issues where fingerprints have been reported and discussed. However, looking through the code and GitHub repositories for all five open-source wallets would take an unreasonable amount of time. I kept the repositories in mind as a backup, for confirming observed fingerprints, and to check if anyone had already reported them.
A faster method of finding fingerprints is to explore the wallet’s interface and options, and to create a few transactions using the software.
Exploring the Wallet Interface
Many simple fingerprints can be identified just by looking at the options that a wallet provides. This includes the possible spending addresses, whether having a wallet that spends multiple output types is possible, and whether spending to more than one recipient is possible.
When looking for fingerprints, not all transactions are created equal. The transactions created for this purpose should contain as much information as possible. For example, a transaction with a single input and single output doesn’t tell us much. It tells us nothing about the wallet’s typical change index, how the inputs and outputs are sorted, whether or not it reuses the input address as the change address, etc.
With this in mind, I created many transactions using these wallets and looked at them for fingerprints. I ended up creating more transactions for the wallets that were harder to fingerprint. After creating the first transaction, I started looking for Independent fingerprints. Then I tried to see if there were any patterns that could be identified, such as those related to input order and change index. Then, I created additional transactions to try to either confirm or disprove the presence of these patterns in all transactions of that kind created by this wallet. After finding these fingerprints, I wrote Python functions to verify them.
Here are my results shown as a table of possible wallet fingerprints by wallet software. The full explanation of each fingerprint can be found here.
There are some things here which I find particularly note-worthy:
- Ledger doesn’t seem to shuffle its inputs after it performs coin selection. Ledger’s default coin selection algorithm is historical (the oldest UTXOs get spent first).
- All of the Trezor transactions which I created followed BIP-69.
- Exodus only provides a single address and reuses that address as change, but if you go into the settings, there is the option to use multiple addresses. It is interesting that this is not the default.
- In general, it appears that wallets that also deal with altcoins (such as Exodus and Trust) only use a single address. This is likely because it is common in many altcoins to use a single address.
- Simple fingerprints can be very useful. For example support for spending multiple UTXO types or paying more than one recipient are simple to detect, but vary widely amongst wallets. For example, Bitcoin Core is one of the few wallets that allows for spending multiple UTXO types and sending to more than two outputs. Ledger, on the other hand, doesn’t allow for either of these things.
Automating the Detection of Fingerprints on Transactions
Using the fingerprints found above, it became simple to write a series of functions to A) detect fingerprints in transactions, B) Use the presence or absence of these fingerprints to rule out or substantiate wallets as potential transaction originators, and C) do this on a wider scale for large groups of transactions, such as those in a given block or in a mempool snapshot. I was able to write these functions in Python, and they can be found in this GitHub repository. They can be run locally (see repository for instructions) or on Google Colab.
Transactions and blocks are fetched using either Bitcoin Core or the Mempool Space REST API. Some of the functions take a deserialized transaction and check for the presence of a fingerprint or multiple fingerprints (for example it is easier to check for different types of output orders at once instead of doing it individually). There is a
detect_wallet(tx_hash) function which uses these functions to identify fingerprints and use them to determine the wallet.
It might seem intuitive that a wallet would be determined based on the presence of certain fingerprints. However, because of probabilistic fingerprints (the fingerprint can be present even if a wallet does not leave that fingerprint) it is actually more effective to rule out wallets based on the absence of fingerprints. For example, if a transaction does anti-fee-sniping (which is an independent fingerprint), but does not follow BIP-69 (which is probabilistic), then it was likely created by Bitcoin Core. Wallets are eliminated by the absence of fingerprints. If there is only one wallet left, it is likely that wallet was used to create the transaction. If there are no wallets left, it means that it could not have been created by one of the eight wallets. If multiple possible wallets remain, there is not enough information to determine the wallet.
detect_wallet can be used for all of the transactions in a block to produce a result as shown below, which provides a breakdown of how many transactions were likely created by each of the wallets.
Note that below the “Other” field indicates the number of transactions that could not have been created by one of the eight investigated wallets because they have a different combination of fingerprints. The “Unclear” field indicates the number of transactions that could have been created by multiple of the eight wallets, and there is not enough information to narrow it down to a single wallet.
There are many additional facets of wallet fingerprinting that can be looked into further. Sometimes there are not enough fingerprints present in a transaction to get a reasonable idea of the wallet that created it. To find more fingerprints, clustering algorithms could be used to find other addresses belonging to the same wallet. This would be helpful because then all related transactions can be searched for fingerprints. Having multiple related transactions is especially useful for detecting probabilistic fingerprints because there are more examples of certain behavior. Generally de-anonymization techniques are even more effective when multiple are used in tandem.
There is no clear cut solution to the issues discussed here. While some fingerprints would be trivial to eliminate, it will be difficult to eliminate fingerprinting entirely. Just because something is a fingerprint does not automatically mean that it should not be done by a wallet. For instance, all transactions of a wallet having a certain input order or change index should definitely be modified, but things like anti-fee-sniping and the spending of taproot UTXOs are still worthwhile.
Thanks to Clara Shikhelman and Mark Erhardt for their help discussing this project with me and reviewing this work. Thanks to Andrew Chow for giving me a few spare hardware wallets to look for fingerprints on. Any mistakes and opinions contained within are my own.
- Anti-Fee-Sniping: A mechanism to discourage chain reorgs. This consists of setting the
nLockTimevalue of the transaction to the current block height, so that the transaction can’t be included in an earlier block by miners trying to take fees from the previous miners of that block. Locking transactions to the upcoming block ensures that their fees can only be collected by the next block, or any blocks after that.
- Input Types: Some wallets only create addresses of certain types.
- Inputs of Multiple Types: Some wallets allow for multiple input types to be spent in the same transaction.
- Output Types: Some wallets only support sending to certain address / scriptPubKey types.
- Number of Outputs: Certain wallets only support sending to a single recipient (plus change). Some wallets try to create changeless transactions.
- Signaling Opt-in RBF: Certain wallets create transactions which opt-into RBF by default, or provide an option for the user to do so.
- Compressed/Uncompressed ECDSA Public Keys: Uncompressed ECDSA public keys are now non-standard in all output types, but previously they were allowed for P2PKH and P2SH (see Bitcoin Core #8499). This fingerprint is more useful with older transactions.
- Transaction Version: Only nVersion 1 and 2 are standard. Use of certain opcodes requires nVersion to be set to 2. Some wallets generally use version 2, while others still use version 1.
- BIP 69: The lexicographical ordering of inputs and outputs is specified by BIP 69. While this was created in order to mitigate privacy weaknesses in wallet software as a result of input and output ordering, this has ended up becoming a fingerprint in itself. If a minority of wallets implement an anti-fingerprinting measure, it turns into a fingerprint. This is probabilistic because it is possible for wallet software to unintentionally order the inputs and outputs lexicographically.
- Low-R-Grinding: In a signature, the r value is the x-coordinate of the secret nonce value. A low-r signature is a signature which has an r value that can be encoded with 32 bytes instead of 33 bytes. A few wallets generate signatures until they have a signature with a low-r value, creating a smaller overall transaction size, to save on fees and simplify fee estimation. This is probabilistic because it is possible for wallet software to unintentionally create signatures that all have low rs. On the other hand, the presence of a high r signature can be used to rule out all wallets that implement low-r-grinding.
- Input Order: Input order fingerprints can be a result of the wallet not shuffling the inputs after performing coin selection. This is probabilistic because it could seem like a wallet is ordering inputs a certain way, but the inputs just happened to be shuffled in that manner. The common cases are:
- Smallest UTXO (by value) first
- Largest UTXO (by value) first
- Oldest first
- Manual Entering of Fees/Feerate: Some signs of this fingerprint include:
- Round dollar amount
- Round satoshi amount
This is probabilistic because it is possible that a feerate recommendation provided a round dollar or satoshi amount.
- Change Position in Outputs: Certain wallets always use the same output index for change (usually the first or last index). Other wallets randomize the change index.
- Change Type:
- Does the change type match one of the output types?
- Does the change type match one of the input types instead?
- Spending Unconfirmed Outputs
- Are unconfirmed change outputs ever spent?
- Are all types of unconfirmed outputs spent?
- Submission of Replacement Transactions: This should not be confused with simply opting into RBF. This is when a user actually creates a replacement transaction for a transaction which has opted into RBF. There are wallets that create transactions which opt-in to RBF, but don’t actually allow users to fee-bump their transactions.
- Feerate Recommendations: Are fee recommendations being used by the wallet? If so, where does the wallet get its fee recommendations? If a transaction had the same feerate as that recommended by a certain wallet that uses somewhat unique fee recommendations, this is indicative of that wallet being used.
The following are transactions which demonstrate the presence of some of the fingerprints described above.