From 08f51c7d5ff1d9367c127b287ec5d6acc36d210f Mon Sep 17 00:00:00 2001 From: Jonathan Downing Date: Wed, 25 Sep 2024 13:07:52 -0500 Subject: [PATCH] Prevent worker from unintentionally double-spending UTXOs --- core/worker.go | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/core/worker.go b/core/worker.go index 91a9fd2d2..5627bcb86 100644 --- a/core/worker.go +++ b/core/worker.go @@ -1531,7 +1531,7 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment, parent *ty addresses := make(map[common.AddressBytes]struct{}) totalQitIn := big.NewInt(0) - utxosDelete := make(map[types.OutPoint]*types.UtxoEntry) + utxosDeleteHashes := make([]common.Hash, 0, len(tx.TxIn())) inputs := make(map[uint]uint64) for _, txIn := range tx.TxIn() { utxo := rawdb.GetUTXO(w.workerDb, txIn.PreviousOutPoint.TxHash, txIn.PreviousOutPoint.Index) @@ -1557,7 +1557,12 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment, parent *ty } addresses[common.AddressBytes(utxo.Address)] = struct{}{} totalQitIn.Add(totalQitIn, types.Denominations[denomination]) - utxosDelete[txIn.PreviousOutPoint] = utxo + utxoHash := types.UTXOHash(txIn.PreviousOutPoint.TxHash, txIn.PreviousOutPoint.Index, utxo) + if _, exists := env.deletedUtxos[utxoHash]; exists { + return fmt.Errorf("tx %032x double spends UTXO %032x:%d", tx.Hash(), txIn.PreviousOutPoint.TxHash, txIn.PreviousOutPoint.Index) + } + env.deletedUtxos[utxoHash] = struct{}{} + utxosDeleteHashes = append(utxosDeleteHashes, utxoHash) inputs[uint(denomination)]++ } var ETXRCount int @@ -1565,7 +1570,7 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment, parent *ty etxs := make([]*types.ExternalTx, 0) totalQitOut := big.NewInt(0) totalConvertQitOut := big.NewInt(0) - utxosCreate := make(map[types.OutPoint]*types.UtxoEntry) + utxosCreateHashes := make([]common.Hash, 0, len(tx.TxOut())) conversion := false var convertAddress common.Address outputs := make(map[uint]uint64) @@ -1650,7 +1655,7 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment, parent *ty } else { // This output creates a normal UTXO utxo := types.NewUtxoEntry(&txOut) - utxosCreate[types.OutPoint{TxHash: tx.Hash(), Index: uint16(txOutIdx)}] = utxo + utxosCreateHashes = append(utxosCreateHashes, types.UTXOHash(tx.Hash(), uint16(txOutIdx), utxo)) } } // Ensure the transaction does not spend more than its inputs. @@ -1708,18 +1713,10 @@ func (w *worker) processQiTx(tx *types.Transaction, env *environment, parent *ty } env.txs = append(env.txs, tx) env.utxoFees.Add(env.utxoFees, txFeeInQit) - for outpoint, utxo := range utxosDelete { - utxoHash := types.UTXOHash(outpoint.TxHash, outpoint.Index, utxo) - if _, exists := env.deletedUtxos[utxoHash]; exists { - return fmt.Errorf("tx %032x double spends UTXO %032x:%d", tx.Hash(), outpoint.TxHash, outpoint.Index) - } - env.deletedUtxos[utxoHash] = struct{}{} - env.utxosDelete = append(env.utxosDelete, utxoHash) - } - for outPoint, utxo := range utxosCreate { - utxoHash := types.UTXOHash(outPoint.TxHash, outPoint.Index, utxo) - env.utxosCreate = append(env.utxosCreate, utxoHash) - } + + env.utxosDelete = append(env.utxosDelete, utxosDeleteHashes...) + env.utxosCreate = append(env.utxosCreate, utxosCreateHashes...) + if err := CheckDenominations(inputs, outputs); err != nil { return err }