I’ve been thinking about ways to obfuscate a transaction before it is broadcasted. Many months ago, I came up with a decoy system that I never publicly shared because it was bloating the chain with kernels. I think I have found a way to reduce kernels to a single one. It still has some flaws, but I hope someone else finds a variant of this that is better.
What we end up with is a transaction where Alice is sending funds to Bob and the transaction has a bunch of input->output pairs that act as decoys. Bob doesn’t know anything apart from his output UTXO, each peer that contributed an input->output pair also does not know anything apart from their own inputs and outputs. This is information theoretical optima from Alice’s view. Alice can choose the number of decoy input->output pairs N and she blinds everyone else with 1/N probability which is somewhat similar to ring signatures from her view point (Monero has fixed N=11). This is why I called it ObscuroJoin because Alice knows everything, but she blindfolds everyone else.
As already mentioned, this system has some issues e.g. Alice could be asking for pairs just to gather info on fake outputs. I still hope someone ponders on this and finds improvements. It’s also possible to do analysis attacks on the previous spendings etc.
P.S. Bob can blindfold Alice as well by doing a similar dance on his end and contributing a set of inputs and outputs, which would mean that even Alice does not know which output belongs to Bob.
Edit: I’ve updated the incentive system with what John described below because it seems superior.
This reminds me of an idea I may have only shared in chats before (I couldn’t find it on this Forum):
Let’s say your wallet has some received outputs that you consider tainted by the potential of the sender identifying you as their owner. So you might want to make some 1-input 1-output transaction with them and have it aggregated with other random transactions to reduce the taint (mixing).
But how to ensure aggregation? One way is to attach this wallet to a grin node and wait for a dandelion stem path to come across your node, at which point you could aggregate it with the transaction passing by. You can avoid adding your own kernel by proper adjustment of the offset. This is not safe in general, but appears safe if your excess is between your inputs and outputs exclusively.
You can either pay the likely increase in required fee yourself, or wait for a fee overpaying transaction to come by.
This scheme shows that paying a high fee is not only useful to get priority in case of full blocks, but also as an incentive to get aggregated with other transactions. Fees pay for priority and/or privacy.
Both of those sound very interesting. Great work. It would also be a great way to incentivize adding nodes to network and possibly earn some grin for having grin (if you’re willing to dance)
@tromp’s idea is simpler and it has the property that people can’t spy on you because the addition of input->output to a tx happens locally. The difference is that it’s not guaranteed to have a merge in the stem phase though. His incentive system also shows that fees being paid could be enough, so maybe there’s no need for an additional UTXO that a dance peer gets paid. Instead you could simply ask nodes if they want to contribute a tx and you pay for their fees. This is probably a weaker incentive to participate, but a much healthier one for the network as it no longer encourages hoarding utxos just to get paid and thus doesn’t add dust outputs. It gets even simpler to show nobody can steal. The dance peer has nothing to steal and the peer using the dance peer input->output can only use it if they pay for the fees.
I’ve updated the original post to account for that in the updated version
I think we decided we could only pay the additional fee by including an additional kernel?
This could work but we’d need a full tx, not by adding a single input->output (plus offset adjustment).
out1->out2 can be subsequently (mischievously) reversed via out2->out1 if somebody sees the original tx and can subtract your offset adjustment to effectively “undo” it.
I think we decided funds are “safe” but not necessarily “stable” until spent via a real tx.
Good point; you cannot add any fee to a tx without adding a kernel (or modifying one of your own). So avoiding an extra kernel by modifying the offset only works if existing fees are sufficiently generous.
Another good point. It’s probably not wise to rely on network participants being well-behaved, so the prudent thing to do is just to add a kernel for your untainting transaction.
Great points . This also means that the updated obscuro version would either require a kernel for every peer contribution or have the vulnerability of undoing that antiochp mentioned. On top of that the undoing attack is even more harmful for my first version because Alice could steal back her fee outputs by adding a reverse transaction. This makes obscuro bloating with kernels again if it wants to be secure against undoing attack.
Yes. It seems like every time we find a way to “tweak” transactions without adding kernel bloat there is something in there that allows unfortunate side-effects.
Any operation on a transaction without adding a kernel ends up revealing some excess that can subsequently be reversed or replayed.
I believe currently the only way to prevent this is through an additional kernel (aka a full transaction).
I think @tromp’s idea is still useful for some cases. Let’s say I have a time stamping website and I expect at most 10 timestamps per minute. I create 10 outputs that hold very small v*H or 0*H. When someone wants to timestamp a wall of text, I run it through a hash function and use the hash for the r blinding factor to create the next output that I then submit on the chain after merging it with another tx by adjusting the kernel offset. I provide the user with a JSON that describes the block hash, output_mmr_root and the merkle proof for that UTXO that they can later paste into my form to validate that it really existed at a specific block. For this case, I don’t really care whether someone does the ‘undo attack’ on my output because my value is not really hidden on the output, but rather in the mmr_root and the merkle proof. This isn’t really a use case for cash which is the main goal of MW, but I find it interesting that Mimblewimble allows to store timestamps in a way that uses a constant storage size (effectively 0) for either 10 created proofs over the year or 10000.
Edit: I think it’s possible to have infinitely many timestamps using a single utxo by having the r be the merkle root hash of many timestamps that are being created. You just need to provide another merkle proof for that tree I think