I hear a lot about how small point-of-sale (PoS) transactions are not possible, because it's too unsafe to accept them at the "0/Unconfirmed" state. However, I am not sure why this has to be a problem, and so far, I haven't been able to find any discussion about this. If my assumptions are correct, anyone can very reliably defend against a 0/Unconfirmed double-spend with nothing more than a simple client modification. Worst case, a slight protocol modification could make this attack vector nearly impossible.
I will describe the assumptions and attack vector, and then the solution after that.Assumptions:
- When I broadcast a transaction, it will immediately be checked by the full nodes and propagated if it is valid
- If the transaction is invalid, each full node will see it but silently reject it without forwarding it to its peers.
- If a node receives a second, conflicting transaction, regardless of whether the first one is confirmed yet, the node will consider the first one it sees to be valid and reject the second one
- Nodes will work to include the tx they see first into a block, and strike it from their queue if a new block is solved that conflicts with it.
Attacker puts special software on his phone which is used to pay merchants in PoS situations. This software is designed such that whenever he initiates a transaction, the phone will broadcast the "correct" tx to half of the nodes it is connected to, and then 0.1 seconds later broadcast a second, conflicting tx to the other half of the nodes. The second tx would be a transfer to himself using the same outputs as the first tx they saw.
The first transaction will be seen by ~70% of the network first, and the attacker hopes the merchant node will see it first, too. Miners in this 70% will consider this to be a real tx and start trying to include it in a block. The other 30% will see the second transaction first, consider it to be "real", start trying to include it in a block. Both groups of nodes will receive word of the other transaction when it is passed along, but will silently ignore it because it conflicts with the first transaction they received.
For the moment, forget about the 30% chance that the merchant sees the irrelevant transaction first (as the only person inconvenienced by this is the attacker). Assume the merchant is in the 70% group. The merchant will see the expected tx as 0/Unconfirmed, and the buyer takes his merchandise and walks out of the store. However, there's a 30% chance that the tx to himself ends up in the next block and the merchant's 0/Unconfirmed tx will be invalid. In this case, the customer is already out of the store, and his transaction is anonymous enough
that the merchant might just eat the $100 loss instead of trying to get it investigated.
In this sense, there is very low-risk for the buyer to do this, and might as well do it every chance he has. Although, I don't think it's actually that low-risk, because there's a lot of things that can go wrong for the attacker if the merchant is in the other 30%. But I don't want to discuss that here.The Defense:
In this situation, the merchant node has a 20+ peers, and with 30% of the network being convinced the other transaction is valid, at least one of those peers will send him the conflicting transaction. The way the client is written now, the transaction is simply rejected because it conflicts with the first. However, I don't see why the client software can't be designed to see that the second transaction is a threat to the first tx and, instead of rejecting it, alert the user that there's a threat to one of his own transactions. This is very reliable, because in order for the attack to have a chance of success, the attacker must broadcast the second tx immediately after the first, or else 99%+ of the network will see the first tx and the second one will be DOA and not propagate
. So, either the second tx doesn't propagate and the attack fails, or the merchant will immediately
see the conflicting transaction and take appropriate action: deny service, call the cops, keep the money, etc. I can't think of a justifiable reason why a conflicting tx would be propagated at the same time unless there was malicious intent.
Regardless of intent/legalities, the merchant has justification to deny service on the goods until the tx is
confirmed in a block. A customer can avoid being forced to wait for 1+ confirmation by just not attempting to double-spend. If there's no conflicting transaction, then the first/only transaction is nearly guaranteed to be included in a future block at 0/Unconfirmed, and the customer can do nothing to reverse it.
More far-reaching, but even more reliable, the client software could be set up with a new protocol message specifically for this event. If any
node receives a transaction that conflicts with a transaction currently in the block it's trying to solve, it simply broadcasts a new "dsalert" message that contains the two hashes of the conflicting transactions in question. Then, even if only 1% of nodes think the conflicting tx is valid and the merchant has no peers in this group, they will still become aware within seconds that they should wait for confirmation before allowing the customer to leave with his merchandise. All that is needed is for an extra loop in the code to check freshly received transactions against all unconfirmed transactions and notify the user/peers if it sees one. So?
If my assumptions and assessment is correct, it would seem that a merchant doesn't actually need a third-party to avoid 0/Unconfirmed double-spending. If the attack has any chance of success, then there would be immediate evidence that the customer is trying to defraud them. Where am I going wrong in all this?