<commit_before>// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package udb

import (
	"bytes"
	"fmt"

	"github.com/decred/dcrd/blockchain/stake"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/wire"
	"github.com/decred/dcrwallet/apperrors"
	"github.com/decred/dcrwallet/walletdb"
)

// InsertMemPoolTx inserts a memory pool transaction record.  It also marks
// previous outputs referenced by its inputs as spent.  Errors with the
// apperrors.ErrDoubleSpend code if another unmined transaction is a double
// spend of this transaction.
func (s *Store) InsertMemPoolTx(ns walletdb.ReadWriteBucket, rec *TxRecord) error {
	_, recVal := latestTxRecord(ns, rec.Hash[:])
	if recVal != nil {
		const str = "transaction already exists mined"
		return apperrors.New(apperrors.ErrDuplicate, str)
	}

	v := existsRawUnmined(ns, rec.Hash[:])
	if v != nil {
		// TODO: compare serialized txs to ensure this isn't a hash collision?
		return nil
	}

	// Check for other unmined transactions which cause this tx to be a double
	// spend.  Unlike mined transactions that double spend an unmined tx,
	// existing unmined txs are not removed when inserting a double spending
	// unmined tx.
	//
	// An exception is made for this rule for revocations that double spend an
	// unmined vote that doesn't vote on the tip block.
	for _, input := range rec.MsgTx.TxIn {
		prevOut := &input.PreviousOutPoint
		k := canonicalOutPoint(&prevOut.Hash, prevOut.Index)
		if v := existsRawUnminedInput(ns, k); v != nil {
			var spenderHash chainhash.Hash
			readRawUnminedInputSpenderHash(v, &spenderHash)

			// A switch is used here instead of an if statement so it can be
			// broken out of to the error below.
		RevocationCheck:
			switch {
			case rec.TxType == stake.TxTypeSSRtx:
				spenderVal := existsRawUnmined(ns, spenderHash[:])
				spenderTxBytes := extractRawUnminedTx(spenderVal)
				var spenderTx wire.MsgTx
				err := spenderTx.Deserialize(bytes.NewReader(spenderTxBytes))
				if err != nil {
					str := fmt.Sprintf("failed to deserialize unmined "+
						"transaction %v", &spenderHash)
					return apperrors.Wrap(err, apperrors.ErrData, str)
				}
				if stake.DetermineTxType(&spenderTx) != stake.TxTypeSSGen {
					break RevocationCheck
				}
				votedBlock, _, err := stake.SSGenBlockVotedOn(&spenderTx)
				if err != nil {
					const str = "failed to determine voted block"
					return apperrors.Wrap(err, apperrors.ErrData, str)
				}
				tipBlock, _ := s.MainChainTip(ns)
				if votedBlock == tipBlock {
					const str = "revocation double spends unmined vote on " +
						"the tip block"
					return apperrors.New(apperrors.ErrDoubleSpend, str)
				}

				err = s.removeUnconfirmed(ns, &spenderTx, &spenderHash)
				if err != nil {
					return err
				}
				continue
			}

			str := fmt.Sprintf("unmined transaction %v is a double spend of "+
				"unmined transaction %v", &rec.Hash, &spenderHash)
			return apperrors.New(apperrors.ErrDoubleSpend, str)
		}
	}

	log.Infof("Inserting unconfirmed transaction %v", rec.Hash)
	v, err := valueTxRecord(rec)
	if err != nil {
		return err
	}
	err = putRawUnmined(ns, rec.Hash[:], v)
	if err != nil {
		return err
	}

	txType := stake.DetermineTxType(&rec.MsgTx)

	for i, input := range rec.MsgTx.TxIn {
		// Skip stakebases for votes.
		if i == 0 && txType == stake.TxTypeSSGen {<commit_msg>Allow votes to double spend other invalid unmined votes.<commit_after>// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package udb

import (
	"bytes"
	"fmt"

	"github.com/decred/dcrd/blockchain/stake"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/wire"
	"github.com/decred/dcrwallet/apperrors"
	"github.com/decred/dcrwallet/walletdb"
)

// InsertMemPoolTx inserts a memory pool transaction record.  It also marks
// previous outputs referenced by its inputs as spent.  Errors with the
// apperrors.ErrDoubleSpend code if another unmined transaction is a double
// spend of this transaction.
func (s *Store) InsertMemPoolTx(ns walletdb.ReadWriteBucket, rec *TxRecord) error {
	_, recVal := latestTxRecord(ns, rec.Hash[:])
	if recVal != nil {
		const str = "transaction already exists mined"
		return apperrors.New(apperrors.ErrDuplicate, str)
	}

	v := existsRawUnmined(ns, rec.Hash[:])
	if v != nil {
		// TODO: compare serialized txs to ensure this isn't a hash collision?
		return nil
	}

	// Check for other unmined transactions which cause this tx to be a double
	// spend.  Unlike mined transactions that double spend an unmined tx,
	// existing unmined txs are not removed when inserting a double spending
	// unmined tx.
	//
	// An exception is made for this rule for votes and revocations that double
	// spend an unmined vote that doesn't vote on the tip block.
	for _, input := range rec.MsgTx.TxIn {
		prevOut := &input.PreviousOutPoint
		k := canonicalOutPoint(&prevOut.Hash, prevOut.Index)
		if v := existsRawUnminedInput(ns, k); v != nil {
			var spenderHash chainhash.Hash
			readRawUnminedInputSpenderHash(v, &spenderHash)

			// A switch is used here instead of an if statement so it can be
			// broken out of to the error below.
		DoubleSpendVoteCheck:
			switch rec.TxType {
			case stake.TxTypeSSGen, stake.TxTypeSSRtx:
				spenderVal := existsRawUnmined(ns, spenderHash[:])
				spenderTxBytes := extractRawUnminedTx(spenderVal)
				var spenderTx wire.MsgTx
				err := spenderTx.Deserialize(bytes.NewReader(spenderTxBytes))
				if err != nil {
					str := fmt.Sprintf("failed to deserialize unmined "+
						"transaction %v", &spenderHash)
					return apperrors.Wrap(err, apperrors.ErrData, str)
				}
				if stake.DetermineTxType(&spenderTx) != stake.TxTypeSSGen {
					break DoubleSpendVoteCheck
				}
				votedBlock, _, err := stake.SSGenBlockVotedOn(&spenderTx)
				if err != nil {
					const str = "failed to determine voted block"
					return apperrors.Wrap(err, apperrors.ErrData, str)
				}
				tipBlock, _ := s.MainChainTip(ns)
				if votedBlock == tipBlock {
					const str = "vote or revocation double spends unmined vote " +
						"on the tip block"
					return apperrors.New(apperrors.ErrDoubleSpend, str)
				}

				err = s.removeUnconfirmed(ns, &spenderTx, &spenderHash)
				if err != nil {
					return err
				}
				continue
			}

			str := fmt.Sprintf("unmined transaction %v is a double spend of "+
				"unmined transaction %v", &rec.Hash, &spenderHash)
			return apperrors.New(apperrors.ErrDoubleSpend, str)
		}
	}

	log.Infof("Inserting unconfirmed transaction %v", rec.Hash)
	v, err := valueTxRecord(rec)
	if err != nil {
		return err
	}
	err = putRawUnmined(ns, rec.Hash[:], v)
	if err != nil {
		return err
	}

	txType := stake.DetermineTxType(&rec.MsgTx)

	for i, input := range rec.MsgTx.TxIn {
		// Skip stakebases for votes.
		if i == 0 && txType == stake.TxTypeSSGen {
{'fraction_non_alphanumeric': 0.10328307735662956, 'fraction_numerical': 0.004790756657742709, 'mean_word_length': 3.500951173113507, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2097449', 'n_tokens_mistral': 2655, 'n_tokens_neox': 2278, 'n_words': 719}