<commit_before>using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using WalletWasabi.Helpers;
using WalletWasabi.JsonConverters;
using WalletWasabi.Logging;

namespace WalletWasabi.Models
{
	[JsonObject(MemberSerialization.OptIn)]
	public class SmartTransaction : IEquatable<SmartTransaction>
	{
		#region Members

		[JsonProperty]
		[JsonConverter(typeof(TransactionJsonConverter))]
		public Transaction Transaction { get; }

		[JsonProperty]
		[JsonConverter(typeof(HeightJsonConverter))]
		public Height Height { get; private set; }

		[JsonProperty]
		[JsonConverter(typeof(Uint256JsonConverter))]
		public uint256 BlockHash { get; private set; }

		[JsonProperty]
		public int BlockIndex { get; private set; }

		[JsonProperty]
		[JsonConverter(typeof(SmartLabelJsonConverter))]
		public SmartLabel Label { get; set; }

		[JsonProperty]
		[JsonConverter(typeof(DateTimeOffsetUnixSecondsConverter))]
		public DateTimeOffset FirstSeen { get; private set; }

		[JsonProperty(PropertyName = "FirstSeenIfMempoolTime")]
		[JsonConverter(typeof(BlockCypherDateTimeOffsetJsonConverter))]
		[Obsolete("This property exists only for json backwards compatibility. If someone tries to set it, it'll set the FirstSeen. https://stackoverflow.com/a/43715009/2061103", error: true)]
#pragma warning disable IDE0051 // Remove unused private members
		private DateTimeOffset? FirstSeenCompatibility
#pragma warning restore IDE0051 // Remove unused private members
		{
			set
			{
				// If it's null, let FirstSeen's default to be set.
				// If it's not null, then check if FirstSeen has just been recently set to utcnow which is its default.
				if (value.HasValue && (DateTimeOffset.UtcNow - FirstSeen) < TimeSpan.FromSeconds(1))
				{
					FirstSeen = value.Value;
				}
			}
		}

		[JsonProperty]
		public bool IsReplacement { get; private set; }

		public bool Confirmed => Height.Type == HeightType.Chain;

		public uint256 GetHash() => Transaction.GetHash();

		public int GetConfirmationCount(Height bestHeight) => Height == Height.Mempool ? 0 : bestHeight.Value - Height.Value + 1;

		/// <summary>
		/// A transaction can signal that is replaceable by fee in two ways:
		/// * Explicitly by using a nSequence &lt; (0xffffffff - 1) or,
		/// * Implicitly in case one of its unconfirmed ancestors are replaceable
		/// </summary>
		public bool IsRBF => !Confirmed && (Transaction.RBF || IsReplacement);

		#endregion Members

		#region Constructors

		public SmartTransaction(Transaction transaction, Height height, uint256 blockHash = null, int blockIndex = 0, SmartLabel label = null, bool isReplacement = false, DateTimeOffset firstSeen = default)
		{
			Transaction = transaction;
			Label = label ?? SmartLabel.Empty;

			Height = height;
			BlockHash = blockHash;
			BlockIndex = blockIndex;

			FirstSeen = firstSeen == default ? DateTimeOffset.UtcNow : firstSeen;

			IsReplacement = isReplacement;
		}

		#endregion Constructors

		/// <summary>
		/// Update the transaction with the data acquired from another transaction. (For example merge their labels.)
		/// </summary>
		/// <param name="canUnconfirm">By default confirmed transaction won't get unconfirmed. You can override this option.</param>
		public void Update(SmartTransaction tx, bool canUnconfirm = false)
		{
			// If this is not the same tx, then don't update.
			if (this != tx)
			{
				throw new InvalidOperationException($"{GetHash()} != {tx.GetHash()}");
			}

			// Set the height related properties.
			if (canUnconfirm)
			{
				Height = tx.Height;
				BlockHash = tx.BlockHash;
				BlockIndex = tx.BlockIndex;
			}
			else if (tx.Confirmed)
			{
				Height = tx.Height;
				BlockHash = tx.BlockHash;
				BlockIndex = tx.BlockIndex;
			}

			// Always the earlier seen is the firstSeen.
			if (tx.FirstSeen < FirstSeen)
			{
				FirstSeen = tx.FirstSeen;
			}
<commit_msg>Remove canUnconfirm<commit_after>using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using WalletWasabi.Helpers;
using WalletWasabi.JsonConverters;
using WalletWasabi.Logging;

namespace WalletWasabi.Models
{
	[JsonObject(MemberSerialization.OptIn)]
	public class SmartTransaction : IEquatable<SmartTransaction>
	{
		#region Members

		[JsonProperty]
		[JsonConverter(typeof(TransactionJsonConverter))]
		public Transaction Transaction { get; }

		[JsonProperty]
		[JsonConverter(typeof(HeightJsonConverter))]
		public Height Height { get; private set; }

		[JsonProperty]
		[JsonConverter(typeof(Uint256JsonConverter))]
		public uint256 BlockHash { get; private set; }

		[JsonProperty]
		public int BlockIndex { get; private set; }

		[JsonProperty]
		[JsonConverter(typeof(SmartLabelJsonConverter))]
		public SmartLabel Label { get; set; }

		[JsonProperty]
		[JsonConverter(typeof(DateTimeOffsetUnixSecondsConverter))]
		public DateTimeOffset FirstSeen { get; private set; }

		[JsonProperty(PropertyName = "FirstSeenIfMempoolTime")]
		[JsonConverter(typeof(BlockCypherDateTimeOffsetJsonConverter))]
		[Obsolete("This property exists only for json backwards compatibility. If someone tries to set it, it'll set the FirstSeen. https://stackoverflow.com/a/43715009/2061103", error: true)]
#pragma warning disable IDE0051 // Remove unused private members
		private DateTimeOffset? FirstSeenCompatibility
#pragma warning restore IDE0051 // Remove unused private members
		{
			set
			{
				// If it's null, let FirstSeen's default to be set.
				// If it's not null, then check if FirstSeen has just been recently set to utcnow which is its default.
				if (value.HasValue && (DateTimeOffset.UtcNow - FirstSeen) < TimeSpan.FromSeconds(1))
				{
					FirstSeen = value.Value;
				}
			}
		}

		[JsonProperty]
		public bool IsReplacement { get; private set; }

		public bool Confirmed => Height.Type == HeightType.Chain;

		public uint256 GetHash() => Transaction.GetHash();

		public int GetConfirmationCount(Height bestHeight) => Height == Height.Mempool ? 0 : bestHeight.Value - Height.Value + 1;

		/// <summary>
		/// A transaction can signal that is replaceable by fee in two ways:
		/// * Explicitly by using a nSequence &lt; (0xffffffff - 1) or,
		/// * Implicitly in case one of its unconfirmed ancestors are replaceable
		/// </summary>
		public bool IsRBF => !Confirmed && (Transaction.RBF || IsReplacement);

		#endregion Members

		#region Constructors

		public SmartTransaction(Transaction transaction, Height height, uint256 blockHash = null, int blockIndex = 0, SmartLabel label = null, bool isReplacement = false, DateTimeOffset firstSeen = default)
		{
			Transaction = transaction;
			Label = label ?? SmartLabel.Empty;

			Height = height;
			BlockHash = blockHash;
			BlockIndex = blockIndex;

			FirstSeen = firstSeen == default ? DateTimeOffset.UtcNow : firstSeen;

			IsReplacement = isReplacement;
		}

		#endregion Constructors

		/// <summary>
		/// Update the transaction with the data acquired from another transaction. (For example merge their labels.)
		/// </summary>
		public void Update(SmartTransaction tx)
		{
			// If this is not the same tx, then don't update.
			if (this != tx)
			{
				throw new InvalidOperationException($"{GetHash()} != {tx.GetHash()}");
			}

			// Set the height related properties, only if confirmed.
			if (tx.Confirmed)
			{
				Height = tx.Height;
				BlockHash = tx.BlockHash;
				BlockIndex = tx.BlockIndex;
			}

			// Always the earlier seen is the firstSeen.
			if (tx.FirstSeen < FirstSeen)
			{
				FirstSeen = tx.FirstSeen;
			}

{'fraction_non_alphanumeric': 0.09916699722332407, 'fraction_numerical': 0.010842258363083432, 'mean_word_length': 3.9730440499671267, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, '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': '2634924', 'n_tokens_mistral': 2571, 'n_tokens_neox': 2178, 'n_words': 669}