keep-network / keep-core

The smart contracts and reference client behind the Keep network
https://keep.network
MIT License
118 stars 73 forks source link

Hardened wallet actions #3723

Closed lukasz-zimnoch closed 11 months ago

lukasz-zimnoch commented 11 months ago

Here we introduce some improvements regarding two mechanisms important for wallet actions execution:

Harden the wallet main UTXO lookup mechanism

The goal of that mechanism is to determine the plain-text wallet main UTXO whose hash is registered in the Bridge contract. The plain-text version is necessary to construct wallet transactions on Bitcoin.

The current version of the main UTXO lookup mechanism looks just at the last five confirmed transactions targeting the wallet public key hash to determine the plain text main UTXO of the wallet. This mechanism is not ideal as it doesn't recognize the difference between true wallet transactions and arbitrary transfers to the wallet public key hash that can be made by anyone. That means it is enough to craft several arbitrary transfers to block the main UTXO lookup and prevent the given wallet from performing actions requested by the wallet coordinator.

To address that problem, we are improving the mechanism to take the full transaction history into account. To make it efficient, we are taking just transaction hashes first and fetching full transaction data only for the latest transactions, where the chance to find the wallet UTXO is the highest.

Harden the wallet sync check mechanism

The goal of this mechanism is to ensure the previous wallet transaction on Bitcoin chain was properly proved to the Bridge contract. This must be ensured before initiating new wallet transactions in order to maintain proper Bitcoin transaction ordering enforced by the Bridge contract. (see https://github.com/keep-network/keep-core/pull/3559 for further reference)

The current version of this mechanism was a naive implementation that checked whether the wallet main UTXO comes from the latest Bitcoin transaction or, if there was no main UTXO, the wallet doesn't have a transaction history. Additionally, this implementation required the mempool to be empty for both cases. This logic is prone to spam transactions sending funds to wallet addresses arbitrarily. Such spam transactions can cause the wallet to abandon all actions proposed by the coordinator.

Here we fix that by using a more sophisticated mechanism:

For wallets having a registered main UTXO, it is enough to check whether their registered UTXO is still among the confirmed unspent outputs from the Bitcoin network standpoint. In order to do that check, we are leveraging ElectrumX listunspent method that returns outputs not used as inputs by any (either confirmed or mempool) transaction. If a wallet uses their main UTXO to produce another transaction, listunspent will not show it and EnsureWalletSyncedBetweenChain will detect this state drift preventing to start another action.

For fresh wallets which don't have main UTXO yet, the situation is more complicated. In that case, we are additionally taking mempool UTXOs into account. If there are no UTXOs at all, that implies the wallet has not produced any (either confirmed or mempool) Bitcoin transaction so far. If some UTXOs targets the wallet, we need to check whether they are spam or actually result of proper wallet transaction. We do this by checking the first input of each transaction. Very first transactions of wallets are always deposit sweeps and all their inputs must point to revealed deposit. If the first input refers to a deposit in that case, that means the wallet already produced their first transaction on Bitcoin and no other action should be taken until the corresponding SPV proof is submitted to the Bridge. Otherwise, such a transaction is spam. If all transactions are spam, the wallet can safely start the given action.