stellar / slingshot

A new blockchain architecture under active development, with a strong focus on scalability, privacy and safety
Apache License 2.0
414 stars 61 forks source link

node/wallet: utxo and tx state machine #479

Closed oleganza closed 4 years ago

oleganza commented 4 years ago

Problem

I'm in process of porting the wallet logic from the ./demo crate to ./node/wallet module and want to make sure the logic of tracking UTXOs is correct. The version in the demo is not well-specified and looks more like a patchwork and does not work reliably with respect to unconfirmed (mempool'ed) transactions.

Definitions

Tx: a ZkVM transaction

Output: location of the value locked in a contract of a transaction that can be spent by an input in the child tx.

UTXO: unspent tx output.

Confirmed: presence in the blockchain.

Unconfirmed: absence in the blockchain, but possible presence in the mempool.

Address: a key controlled by this wallet, derived from a single "account xpub".

Incoming: other party's payment towards an address controlled by this wallet.

Outgoing: tx or output created by this wallet.

Change: an output that sends the rest of the value back to the wallet.

Requirements

  1. The wallet must provide a list of spendable utxos through its API that consist of:
    • confirmed incoming payments,
    • confirmed change outputs, and
    • unconfirmed change outputs,
    • excluding unconfirmed incoming payments, as the wallet has no control over the chance of those not being double-spent in the future.
  2. When the wallet makes a payment:
    • it needs to store the unconfirmed tx, so it will attempt to re-submit it to mempool whenever it falls out of it,
    • remember the change output, so it can be spent in the next payment,
    • remember the annotation for both the change and destination output, so this data can be rendered in the UI.
  3. When the wallet receives a payment:
    • a newly observed (confirmed or unconfirmed) tx is checked against the list of known addresses (stored or on-the-fly derived from account xpub) to detect whether this tx is relevant for this wallet,
    • confirmed outputs (change + incoming) that can be spent by the wallet are added as confirmed spendable utxos.
    • unconfirmed outputs and their transactions are treated separately:
      • outgoing transactions have their change outputs marked as spendable.
      • incoming transactions have their payment output counting towards unconfirmed balance, but not used for spending.
  4. When the wallet adds unconfirmed tx to the mempool:
    • if it fails to be added, it can only be because it's expired or double-spent: then associated outputs are removed from the balance. Note: this can only happen with incoming txs, as outgoing ones are never intentionally double-spent by the wallet (and the maxtime on wallet's txs is set to infinity).
  5. Wallet can expect that the notifications about txs, from blocks or from mempool are coming in correct topological order: meaning, children come after parents. Likewise, attempts to re-add unconfirmed txs to mempool must happen parent-first.
  6. Since the incoming unconfirmed txs can be double-spent by the originator, those outputs must not be used for outgoing payments, nor should be stored alongside confirmed outputs, so we properly evict them when we evict the transaction.

Design sketch

API sketch

oleganza commented 4 years ago

Done in #473