In a previous post we introduced you to the Neutrino wallet, a lightweight SPV wallet with strong network level privacy (which is a port of the excellent work done by BTC devs including roasbeef). Past attempts to build SPV wallets tried to use bloom filters to protect user privacy but it turns out that it’s basically impossible to use bloom filters in such a way that it doesn’t leak most of the information about the addresses in your wallet to the nodes that you connect to.

Neutrino works similar to a normal SPV wallet in that it downloads the full chain of headers from genesis (or a later checkpoint). But rather than constructing a bloom filter containing all the addresses in the wallet and giving the filter to each node it’s connected to, the nodes are expected to instead construct a GCS (Golomb-Coded Set) filter for each block which serves as compact representation of all of the transactions in the block.

In addition to downloading the header chain neutrino downloads the filter for each block and tests each address against the filter wallet side to see if there are any matches. If there is a match, the neutrino wallet downloads the corresponding full block and parses it for its transaction(s). By doing the filtering wallet side rather than server (full node) side we can eliminate leaking information about our addresses to our peers and gain a much need privacy boost.

Neutrino At Scale

The primary downside to this approach is neutrino can be expected to use more bandwidth than a normal SPV because it occasionally downloads a full block. Today the amount of bandwidth neutrino uses is fairly trivial but in the future, when the average block size is much larger, it could be much more substantial. However, even at medium-large block sizes, say 128 MB, you’d be surprised to find that a mobile phone with a typical mobile data plan would likely still have enough bandwidth to run neutrino and have bandwidth left over to browse the internet. And, of course, you could always synchronize your wallet with the network when you’re connected to WiFi so you don’t waste your mobile data.

But looking past 128 MB we will likely start to have a big problem with bandwidth usage, especially for mobile wallets. Gigabyte blocks would likely be too much for a typical mobile device to process unless mobile bandwidth has increased substantially between now and whenever the network starts processing blocks of that size.

So does this mean that at some point neutrino is expected to stop working and we have to go back to the old way of doing SPV with poor privacy? Not necessarily.

Downloading Partial Blocks

Instead of downloading the full block every time we get a filter match we could instead request that our remote peers send us only the transactions in the block with transactions IDs within a given range. For example we could request the range {0x11…, 0x33…} and we would download roughly 12.5% of the block instead of the full block. The remote peer could also give us the SPV proofs for each transaction to prove to us that these transactions are indeed in the block.

But if this is all we do, we have a problem. The remote peers can more or less lie to us by omitting certain transactions from their response. Today this type of lie-by-omission is not possible in neutrino because we download the full block. Hence we’re guaranteed to see everything. But when downloading a partial block we re-introduce the ability of peers to lie by omission.

One possible solution is to just download the partial block from as many peers as possible so it is less likely that we’ve missed one of our transactions, however doing so would give right back whatever bandwidth savings we acquired by downloading a partial block in the first place. Not to mention we’d rather the system be based on cryptographic proof rather than trust that our peers are not withholding transactions.

This is where CTOR comes in. Because the last Bitcoin Cash upgrade now requires transactions in the block to be ordered by transaction ID, it is possible for the remote peer to give us cryptographic proof that no transactions were omitted from the portion of the block that we requested. Thus we don’t have query multiple peers for a partial block and can recognize a significant bandwidth savings.

Partial Blocks With Neutrino

Because blocks are sorted by transaction ID we’d need to know the IDs of the transactions we’re interested in. For outgoing spends this obviously works since we know the ID of all of the transactions that we personally create (assuming third-party malleability is fixed). But we’d still need to know the ID of our incoming transactions.

One possibility here is to have senders of transactions push their transaction directly to the recipient the same or similar way to how BIP70 currently works. The downside is doing so introduces additional networking difficulties but the upshot would be we would learn of all of our incoming transaction IDs which we could then use to download partial blocks. Receiving our transactions out of band would also dramatically reduce our bandwidth requirement as well as we would no longer have to listen on the network for unconfirmed transactions.

An alternative approach is we could have the sender of the transaction brute force the transaction ID to be within a requested range. This simplifies the networking but at the expense of a large increase the bandwidth requirement and it would erode privacy somewhat.

Restoring From Seed

The main downside to downloading partial blocks by transaction ID is you cannot restore a wallet from seed using this technique.

This might not be that big of a deal, however, as you could always still restore a wallet from seed by downloading full blocks as this isn’t something that you’d be expected to do very often. The wallet could simply tell you that you need to be connected to WiFi to perform the restore and that it is expected to take a while.

Alternatively you could use a cloud backup service from your wallet provider or a third party. In such a scenario every time your wallet’s state changes it would encrypt your wallet and upload it to the cloud. The service provider would never see the contents of your wallet, only an encrypted blob. And restoring from seed would only take a fraction of a second.

If the service provider ever disappears you could always still restore from chain using the bandwidth intensive method described above.

Conclusion

Neutrino holds great promise to improve privacy for SPV users but it presents unique scaling challenges for wallets as blocks start to get large. Fortunately the activation of CTOR means we can download partial blocks with strong cryptographic proof and recognize a bandwidth savings without sacrificing privacy or security.