Fix datetime of SmartTransaction
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/HistoryTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/HistoryTabViewModel.cs
@@ -165,7 +165,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
else
{
- dateTime = foundTransaction.FirstSeenIfMempoolTime ?? DateTimeOffset.UtcNow;
+ dateTime = foundTransaction.FirstSeen;
}
if (found != default) // if found
@@ -215,7 +215,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
else
{
- dateTime = foundSpenderTransaction.FirstSeenIfMempoolTime ?? DateTimeOffset.UtcNow;
+ dateTime = foundSpenderTransaction.FirstSeen;
}
var foundSpenderCoin = txRecordList.FirstOrDefault(x => x.transactionId == coin.SpenderTransactionId);

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
@@ -407,7 +407,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
IsHardwareBusy = false;
}
- signedTransaction = signedPsbt.ExtractSmartTransaction(result.Transaction.Height, result.Transaction.BlockHash, result.Transaction.BlockIndex, result.Transaction.Label, result.Transaction.FirstSeenIfMempoolTime, result.Transaction.IsReplacement);
+ signedTransaction = signedPsbt.ExtractSmartTransaction(result.Transaction.Height, result.Transaction.BlockHash, result.Transaction.BlockIndex, result.Transaction.Label, result.Transaction.FirstSeen, result.Transaction.IsReplacement);
}
MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusBarStatus.BroadcastingTransaction);

MODIFY
WalletWasabi.Tests/UnitTests/SmartTransactionTests.cs
WalletWasabi.Tests/UnitTests/SmartTransactionTests.cs
@@ -2,6 +2,7 @@ using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Text;
using WalletWasabi.Helpers;
@@ -73,5 +74,54 @@ namespace WalletWasabi.Tests.UnitTests
var deserializedWithoutLabel = JsonConvert.DeserializeObject<SmartTransaction>(serializedWithoutLabel);
Assert.Empty(deserializedWithoutLabel.Label);
}
+
+ [Fact]
+ public void FirstSeenBackwardsCompatibility()
+ {
+ var now = DateTimeOffset.UtcNow;
+ DateTimeOffset twoThousandEight = new DateTimeOffset(2008, 1, 1, 0, 0, 0, TimeSpan.Zero);
+ DateTimeOffset twoThousandNine = new DateTimeOffset(2009, 1, 1, 0, 0, 0, TimeSpan.Zero);
+
+ //// Compatbile with FirstSeenIfMempoolTime json property.
+ //// FirstSeenIfMempoolTime is null.
+ //var serialized = "{\"FirstSeenIfMempoolTime\": null, \"Transaction\":\"02000000040aa8d0af84518df6e3a60c5bb19d9c3fcc3dc6e26b2f2449e8d7bf8d3fe84b87010000006a473044022018dfe9216c1209dd6c2b6c1607dbac4e499c1fce4878bc7d5d83fccbf3e24c9402202cac351c9c6a2b5eef338cbf0ec000d8de1c05e96a904cbba2b9e6ffc2d4e19501210364cc39da1091b1a9c12ec905a14a9e8478f951f7a1accdabeb40180533f2eaa5feffffff112c07d0f5e0617d720534f0b2b84dc0d5b7314b358c3ab338823b9e5bfbddf5010000006b483045022100ec155e7141e74661ee511ae980150a6c89261f31070999858738369afc28f2b6022006230d2aa24fac110b74ef15b84371486cf76c539b335a253c14462447912a300121020c2f41390f031d471b22abdb856e6cdbe0f4d74e72c197469bfd54e5a08f7e67feffffff38e799b8f6cf04fd021a9b135cdcd347da7aac4fd8bb8d0da9316a9fb228bb6e000000006b483045022100fc1944544a3f96edd8c8a9795c691e2725612b5ab2e1c999be11a2a4e3f841f1022077b2e088877829edeada0c707a9bb577aa79f26dafacba3d1d2d047f52524296012102e6015963dff9826836400cf8f45597c0705757d5dcdc6bf734f661c7dab89e69feffffff64c3f0377e86625123f2f1ee229319ed238e8ca8b7dda5bc080a2c5ecb984629000000006a47304402204233a90d6296182914424fd2901e16e6f5b13b451b67b0eec25a5eaacc5033c902203d8a13ef0b494c12009663475458e51da6bd55cc67688264230ece81d3eeca24012102f806d7152da2b52c1d9ad928e4a6253ccba080a5b9ab9efdd80e37274ac67f9bfeffffff0290406900000000001976a91491ac4e49b66f845180d98d8f8be6121588be6e3b88ac52371600000000001976a9142f44ed6749e8c84fd476e4440741f7e6f55542fa88acadd30700\",\"Height\":\"2147483646\"}";
+ //var deserialized = JsonConvert.DeserializeObject<SmartTransaction>(serialized);
+ //Assert.Equal(now.UtcDateTime, deserialized.FirstSeen.UtcDateTime, TimeSpan.FromSeconds(1));
+
+ //// FirstSeenIfMempoolTime is empty.
+ //serialized = "{\"FirstSeenIfMempoolTime\": \"\", \"Transaction\":\"02000000040aa8d0af84518df6e3a60c5bb19d9c3fcc3dc6e26b2f2449e8d7bf8d3fe84b87010000006a473044022018dfe9216c1209dd6c2b6c1607dbac4e499c1fce4878bc7d5d83fccbf3e24c9402202cac351c9c6a2b5eef338cbf0ec000d8de1c05e96a904cbba2b9e6ffc2d4e19501210364cc39da1091b1a9c12ec905a14a9e8478f951f7a1accdabeb40180533f2eaa5feffffff112c07d0f5e0617d720534f0b2b84dc0d5b7314b358c3ab338823b9e5bfbddf5010000006b483045022100ec155e7141e74661ee511ae980150a6c89261f31070999858738369afc28f2b6022006230d2aa24fac110b74ef15b84371486cf76c539b335a253c14462447912a300121020c2f41390f031d471b22abdb856e6cdbe0f4d74e72c197469bfd54e5a08f7e67feffffff38e799b8f6cf04fd021a9b135cdcd347da7aac4fd8bb8d0da9316a9fb228bb6e000000006b483045022100fc1944544a3f96edd8c8a9795c691e2725612b5ab2e1c999be11a2a4e3f841f1022077b2e088877829edeada0c707a9bb577aa79f26dafacba3d1d2d047f52524296012102e6015963dff9826836400cf8f45597c0705757d5dcdc6bf734f661c7dab89e69feffffff64c3f0377e86625123f2f1ee229319ed238e8ca8b7dda5bc080a2c5ecb984629000000006a47304402204233a90d6296182914424fd2901e16e6f5b13b451b67b0eec25a5eaacc5033c902203d8a13ef0b494c12009663475458e51da6bd55cc67688264230ece81d3eeca24012102f806d7152da2b52c1d9ad928e4a6253ccba080a5b9ab9efdd80e37274ac67f9bfeffffff0290406900000000001976a91491ac4e49b66f845180d98d8f8be6121588be6e3b88ac52371600000000001976a9142f44ed6749e8c84fd476e4440741f7e6f55542fa88acadd30700\",\"Height\":\"2147483646\"}";
+ //deserialized = JsonConvert.DeserializeObject<SmartTransaction>(serialized);
+ //Assert.Equal(now.UtcDateTime, deserialized.FirstSeen.UtcDateTime, TimeSpan.FromSeconds(1));
+
+ //// FirstSeenIfMempoolTime is value.
+ //serialized = "{\"FirstSeenIfMempoolTime\": \"" + twoThousandEight.ToString(CultureInfo.InvariantCulture) + "\", \"Transaction\":\"02000000040aa8d0af84518df6e3a60c5bb19d9c3fcc3dc6e26b2f2449e8d7bf8d3fe84b87010000006a473044022018dfe9216c1209dd6c2b6c1607dbac4e499c1fce4878bc7d5d83fccbf3e24c9402202cac351c9c6a2b5eef338cbf0ec000d8de1c05e96a904cbba2b9e6ffc2d4e19501210364cc39da1091b1a9c12ec905a14a9e8478f951f7a1accdabeb40180533f2eaa5feffffff112c07d0f5e0617d720534f0b2b84dc0d5b7314b358c3ab338823b9e5bfbddf5010000006b483045022100ec155e7141e74661ee511ae980150a6c89261f31070999858738369afc28f2b6022006230d2aa24fac110b74ef15b84371486cf76c539b335a253c14462447912a300121020c2f41390f031d471b22abdb856e6cdbe0f4d74e72c197469bfd54e5a08f7e67feffffff38e799b8f6cf04fd021a9b135cdcd347da7aac4fd8bb8d0da9316a9fb228bb6e000000006b483045022100fc1944544a3f96edd8c8a9795c691e2725612b5ab2e1c999be11a2a4e3f841f1022077b2e088877829edeada0c707a9bb577aa79f26dafacba3d1d2d047f52524296012102e6015963dff9826836400cf8f45597c0705757d5dcdc6bf734f661c7dab89e69feffffff64c3f0377e86625123f2f1ee229319ed238e8ca8b7dda5bc080a2c5ecb984629000000006a47304402204233a90d6296182914424fd2901e16e6f5b13b451b67b0eec25a5eaacc5033c902203d8a13ef0b494c12009663475458e51da6bd55cc67688264230ece81d3eeca24012102f806d7152da2b52c1d9ad928e4a6253ccba080a5b9ab9efdd80e37274ac67f9bfeffffff0290406900000000001976a91491ac4e49b66f845180d98d8f8be6121588be6e3b88ac52371600000000001976a9142f44ed6749e8c84fd476e4440741f7e6f55542fa88acadd30700\",\"Height\":\"2147483646\"}";
+ //deserialized = JsonConvert.DeserializeObject<SmartTransaction>(serialized);
+ //Assert.Equal(twoThousandEight.UtcDateTime, deserialized.FirstSeen.UtcDateTime, TimeSpan.FromSeconds(1));
+
+ //// FirstSeen is null.
+ //serialized = "{\"FirstSeen\": \"" + twoThousandEight.ToUnixTimeSeconds() + "\", \"Transaction\":\"02000000040aa8d0af84518df6e3a60c5bb19d9c3fcc3dc6e26b2f2449e8d7bf8d3fe84b87010000006a473044022018dfe9216c1209dd6c2b6c1607dbac4e499c1fce4878bc7d5d83fccbf3e24c9402202cac351c9c6a2b5eef338cbf0ec000d8de1c05e96a904cbba2b9e6ffc2d4e19501210364cc39da1091b1a9c12ec905a14a9e8478f951f7a1accdabeb40180533f2eaa5feffffff112c07d0f5e0617d720534f0b2b84dc0d5b7314b358c3ab338823b9e5bfbddf5010000006b483045022100ec155e7141e74661ee511ae980150a6c89261f31070999858738369afc28f2b6022006230d2aa24fac110b74ef15b84371486cf76c539b335a253c14462447912a300121020c2f41390f031d471b22abdb856e6cdbe0f4d74e72c197469bfd54e5a08f7e67feffffff38e799b8f6cf04fd021a9b135cdcd347da7aac4fd8bb8d0da9316a9fb228bb6e000000006b483045022100fc1944544a3f96edd8c8a9795c691e2725612b5ab2e1c999be11a2a4e3f841f1022077b2e088877829edeada0c707a9bb577aa79f26dafacba3d1d2d047f52524296012102e6015963dff9826836400cf8f45597c0705757d5dcdc6bf734f661c7dab89e69feffffff64c3f0377e86625123f2f1ee229319ed238e8ca8b7dda5bc080a2c5ecb984629000000006a47304402204233a90d6296182914424fd2901e16e6f5b13b451b67b0eec25a5eaacc5033c902203d8a13ef0b494c12009663475458e51da6bd55cc67688264230ece81d3eeca24012102f806d7152da2b52c1d9ad928e4a6253ccba080a5b9ab9efdd80e37274ac67f9bfeffffff0290406900000000001976a91491ac4e49b66f845180d98d8f8be6121588be6e3b88ac52371600000000001976a9142f44ed6749e8c84fd476e4440741f7e6f55542fa88acadd30700\",\"Height\":\"2147483646\"}";
+ //deserialized = JsonConvert.DeserializeObject<SmartTransaction>(serialized);
+ //Assert.Equal(twoThousandEight.UtcDateTime, deserialized.FirstSeen.UtcDateTime, TimeSpan.FromSeconds(1));
+
+ //// FirstSeen is empty.
+ //serialized = "{\"FirstSeen\": \"\", \"Transaction\":\"02000000040aa8d0af84518df6e3a60c5bb19d9c3fcc3dc6e26b2f2449e8d7bf8d3fe84b87010000006a473044022018dfe9216c1209dd6c2b6c1607dbac4e499c1fce4878bc7d5d83fccbf3e24c9402202cac351c9c6a2b5eef338cbf0ec000d8de1c05e96a904cbba2b9e6ffc2d4e19501210364cc39da1091b1a9c12ec905a14a9e8478f951f7a1accdabeb40180533f2eaa5feffffff112c07d0f5e0617d720534f0b2b84dc0d5b7314b358c3ab338823b9e5bfbddf5010000006b483045022100ec155e7141e74661ee511ae980150a6c89261f31070999858738369afc28f2b6022006230d2aa24fac110b74ef15b84371486cf76c539b335a253c14462447912a300121020c2f41390f031d471b22abdb856e6cdbe0f4d74e72c197469bfd54e5a08f7e67feffffff38e799b8f6cf04fd021a9b135cdcd347da7aac4fd8bb8d0da9316a9fb228bb6e000000006b483045022100fc1944544a3f96edd8c8a9795c691e2725612b5ab2e1c999be11a2a4e3f841f1022077b2e088877829edeada0c707a9bb577aa79f26dafacba3d1d2d047f52524296012102e6015963dff9826836400cf8f45597c0705757d5dcdc6bf734f661c7dab89e69feffffff64c3f0377e86625123f2f1ee229319ed238e8ca8b7dda5bc080a2c5ecb984629000000006a47304402204233a90d6296182914424fd2901e16e6f5b13b451b67b0eec25a5eaacc5033c902203d8a13ef0b494c12009663475458e51da6bd55cc67688264230ece81d3eeca24012102f806d7152da2b52c1d9ad928e4a6253ccba080a5b9ab9efdd80e37274ac67f9bfeffffff0290406900000000001976a91491ac4e49b66f845180d98d8f8be6121588be6e3b88ac52371600000000001976a9142f44ed6749e8c84fd476e4440741f7e6f55542fa88acadd30700\",\"Height\":\"2147483646\"}";
+ //deserialized = JsonConvert.DeserializeObject<SmartTransaction>(serialized);
+ //Assert.Equal(now.UtcDateTime, deserialized.FirstSeen.UtcDateTime, TimeSpan.FromSeconds(1));
+
+ //// FirstSeen is real value.
+ //serialized = "{\"FirstSeen\": \"" + twoThousandEight.ToUnixTimeSeconds() + "\", \"Transaction\":\"02000000040aa8d0af84518df6e3a60c5bb19d9c3fcc3dc6e26b2f2449e8d7bf8d3fe84b87010000006a473044022018dfe9216c1209dd6c2b6c1607dbac4e499c1fce4878bc7d5d83fccbf3e24c9402202cac351c9c6a2b5eef338cbf0ec000d8de1c05e96a904cbba2b9e6ffc2d4e19501210364cc39da1091b1a9c12ec905a14a9e8478f951f7a1accdabeb40180533f2eaa5feffffff112c07d0f5e0617d720534f0b2b84dc0d5b7314b358c3ab338823b9e5bfbddf5010000006b483045022100ec155e7141e74661ee511ae980150a6c89261f31070999858738369afc28f2b6022006230d2aa24fac110b74ef15b84371486cf76c539b335a253c14462447912a300121020c2f41390f031d471b22abdb856e6cdbe0f4d74e72c197469bfd54e5a08f7e67feffffff38e799b8f6cf04fd021a9b135cdcd347da7aac4fd8bb8d0da9316a9fb228bb6e000000006b483045022100fc1944544a3f96edd8c8a9795c691e2725612b5ab2e1c999be11a2a4e3f841f1022077b2e088877829edeada0c707a9bb577aa79f26dafacba3d1d2d047f52524296012102e6015963dff9826836400cf8f45597c0705757d5dcdc6bf734f661c7dab89e69feffffff64c3f0377e86625123f2f1ee229319ed238e8ca8b7dda5bc080a2c5ecb984629000000006a47304402204233a90d6296182914424fd2901e16e6f5b13b451b67b0eec25a5eaacc5033c902203d8a13ef0b494c12009663475458e51da6bd55cc67688264230ece81d3eeca24012102f806d7152da2b52c1d9ad928e4a6253ccba080a5b9ab9efdd80e37274ac67f9bfeffffff0290406900000000001976a91491ac4e49b66f845180d98d8f8be6121588be6e3b88ac52371600000000001976a9142f44ed6749e8c84fd476e4440741f7e6f55542fa88acadd30700\",\"Height\":\"2147483646\"}";
+ //deserialized = JsonConvert.DeserializeObject<SmartTransaction>(serialized);
+ //Assert.Equal(twoThousandEight.UtcDateTime, deserialized.FirstSeen.UtcDateTime, TimeSpan.FromSeconds(1));
+
+ //// FirstSeen and FirstSeenIfMempoolTime are both missing.
+ //serialized = "{\"Transaction\":\"02000000040aa8d0af84518df6e3a60c5bb19d9c3fcc3dc6e26b2f2449e8d7bf8d3fe84b87010000006a473044022018dfe9216c1209dd6c2b6c1607dbac4e499c1fce4878bc7d5d83fccbf3e24c9402202cac351c9c6a2b5eef338cbf0ec000d8de1c05e96a904cbba2b9e6ffc2d4e19501210364cc39da1091b1a9c12ec905a14a9e8478f951f7a1accdabeb40180533f2eaa5feffffff112c07d0f5e0617d720534f0b2b84dc0d5b7314b358c3ab338823b9e5bfbddf5010000006b483045022100ec155e7141e74661ee511ae980150a6c89261f31070999858738369afc28f2b6022006230d2aa24fac110b74ef15b84371486cf76c539b335a253c14462447912a300121020c2f41390f031d471b22abdb856e6cdbe0f4d74e72c197469bfd54e5a08f7e67feffffff38e799b8f6cf04fd021a9b135cdcd347da7aac4fd8bb8d0da9316a9fb228bb6e000000006b483045022100fc1944544a3f96edd8c8a9795c691e2725612b5ab2e1c999be11a2a4e3f841f1022077b2e088877829edeada0c707a9bb577aa79f26dafacba3d1d2d047f52524296012102e6015963dff9826836400cf8f45597c0705757d5dcdc6bf734f661c7dab89e69feffffff64c3f0377e86625123f2f1ee229319ed238e8ca8b7dda5bc080a2c5ecb984629000000006a47304402204233a90d6296182914424fd2901e16e6f5b13b451b67b0eec25a5eaacc5033c902203d8a13ef0b494c12009663475458e51da6bd55cc67688264230ece81d3eeca24012102f806d7152da2b52c1d9ad928e4a6253ccba080a5b9ab9efdd80e37274ac67f9bfeffffff0290406900000000001976a91491ac4e49b66f845180d98d8f8be6121588be6e3b88ac52371600000000001976a9142f44ed6749e8c84fd476e4440741f7e6f55542fa88acadd30700\",\"Height\":\"2147483646\"}";
+ //deserialized = JsonConvert.DeserializeObject<SmartTransaction>(serialized);
+ //Assert.Equal(now.UtcDateTime, deserialized.FirstSeen.UtcDateTime, TimeSpan.FromSeconds(1));
+
+ // FirstSeen and FirstSeenIfMempoolTime are both provided.
+ var serialized = "{\"FirstSeen\": \"" + twoThousandEight.ToUnixTimeSeconds() + "\", \"FirstSeenIfMempoolTime\": \"" + twoThousandNine.ToString(CultureInfo.InvariantCulture) + "\", \"Transaction\":\"02000000040aa8d0af84518df6e3a60c5bb19d9c3fcc3dc6e26b2f2449e8d7bf8d3fe84b87010000006a473044022018dfe9216c1209dd6c2b6c1607dbac4e499c1fce4878bc7d5d83fccbf3e24c9402202cac351c9c6a2b5eef338cbf0ec000d8de1c05e96a904cbba2b9e6ffc2d4e19501210364cc39da1091b1a9c12ec905a14a9e8478f951f7a1accdabeb40180533f2eaa5feffffff112c07d0f5e0617d720534f0b2b84dc0d5b7314b358c3ab338823b9e5bfbddf5010000006b483045022100ec155e7141e74661ee511ae980150a6c89261f31070999858738369afc28f2b6022006230d2aa24fac110b74ef15b84371486cf76c539b335a253c14462447912a300121020c2f41390f031d471b22abdb856e6cdbe0f4d74e72c197469bfd54e5a08f7e67feffffff38e799b8f6cf04fd021a9b135cdcd347da7aac4fd8bb8d0da9316a9fb228bb6e000000006b483045022100fc1944544a3f96edd8c8a9795c691e2725612b5ab2e1c999be11a2a4e3f841f1022077b2e088877829edeada0c707a9bb577aa79f26dafacba3d1d2d047f52524296012102e6015963dff9826836400cf8f45597c0705757d5dcdc6bf734f661c7dab89e69feffffff64c3f0377e86625123f2f1ee229319ed238e8ca8b7dda5bc080a2c5ecb984629000000006a47304402204233a90d6296182914424fd2901e16e6f5b13b451b67b0eec25a5eaacc5033c902203d8a13ef0b494c12009663475458e51da6bd55cc67688264230ece81d3eeca24012102f806d7152da2b52c1d9ad928e4a6253ccba080a5b9ab9efdd80e37274ac67f9bfeffffff0290406900000000001976a91491ac4e49b66f845180d98d8f8be6121588be6e3b88ac52371600000000001976a9142f44ed6749e8c84fd476e4440741f7e6f55542fa88acadd30700\",\"Height\":\"2147483646\"}";
+ var deserialized = JsonConvert.DeserializeObject<SmartTransaction>(serialized);
+ Assert.Equal(twoThousandEight.UtcDateTime, deserialized.FirstSeen.UtcDateTime, TimeSpan.FromSeconds(1));
+ }
}
}

MODIFY
WalletWasabi/Extensions/LinqExtensions.cs
WalletWasabi/Extensions/LinqExtensions.cs
@@ -134,6 +134,6 @@ namespace System.Linq
=> me
.OrderBy(x => x.Height)
.ThenBy(x => x.BlockIndex)
- .ThenBy(x => x.FirstSeenIfMempoolTime ?? DateTime.UtcNow);
+ .ThenBy(x => x.FirstSeen);
}
}

MODIFY
WalletWasabi/Extensions/NBitcoinExtensions.cs
WalletWasabi/Extensions/NBitcoinExtensions.cs
@@ -253,10 +253,10 @@ namespace NBitcoin
return psbt.ExtractSmartTransaction(Height.Unknown);
}
- public static SmartTransaction ExtractSmartTransaction(this PSBT psbt, Height height, uint256 blockHash = null, int blockIndex = 0, string label = "", DateTimeOffset? firstSeenIfMempoolTime = null, bool isReplacement = false)
+ public static SmartTransaction ExtractSmartTransaction(this PSBT psbt, Height height, uint256 blockHash = null, int blockIndex = 0, string label = "", DateTimeOffset firstSeen = default, bool isReplacement = false)
{
var extractedTx = psbt.ExtractTransaction();
- return new SmartTransaction(extractedTx, height, blockHash, blockIndex, label, firstSeenIfMempoolTime, isReplacement);
+ return new SmartTransaction(extractedTx, height, blockHash, blockIndex, label, isReplacement, firstSeen);
}
public static void SortByAmount(this TxOutList list)

ADD
WalletWasabi/JsonConverters/DateTimeOffsetUnixSecondsConverter.cs
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace WalletWasabi.JsonConverters
+{
+ public class DateTimeOffsetUnixSecondsConverter : JsonConverter
+ {
+ /// <inheritdoc />
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(DateTimeOffset);
+ }
+
+ /// <inheritdoc />
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var stringValue = reader.Value as string;
+ long value;
+ if (string.IsNullOrWhiteSpace(stringValue))
+ {
+ value = default(DateTimeOffset).ToUnixTimeSeconds();
+ }
+ else
+ {
+ value = long.Parse(stringValue);
+ }
+ return DateTimeOffset.FromUnixTimeSeconds(value);
+ }
+
+ /// <inheritdoc />
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ writer.WriteValue(((DateTimeOffset)value).ToUnixTimeSeconds());
+ }
+ }
+}

MODIFY
WalletWasabi/Models/SmartTransaction.cs
WalletWasabi/Models/SmartTransaction.cs
@@ -36,14 +36,27 @@ namespace WalletWasabi.Models
public int GetConfirmationCount(Height bestHeight) => Height == Height.Mempool ? 0 : bestHeight.Value - Height.Value + 1;
- /// <summary>
- /// if Height is Mempool it's first seen, else null,
- /// only exists in memory,
- /// does not affect equality
- /// </summary>
[JsonProperty]
+ [JsonConverter(typeof(DateTimeOffsetUnixSecondsConverter))]
+ public DateTimeOffset FirstSeen { get; private set; }
+
+ [JsonProperty(PropertyName = "FirstSeenIfMempoolTime")]
[JsonConverter(typeof(BlockCypherDateTimeOffsetJsonConverter))]
- public DateTimeOffset? FirstSeenIfMempoolTime { get; private set; }
+ [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 FirsSeen 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; }
@@ -59,17 +72,15 @@ namespace WalletWasabi.Models
#region Constructors
- [JsonConstructor]
- public SmartTransaction(Transaction transaction, Height height, uint256 blockHash = null, int blockIndex = 0, string label = "", DateTimeOffset? firstSeenIfMempoolTime = null, bool isReplacement = false)
+ public SmartTransaction(Transaction transaction, Height height, uint256 blockHash = null, int blockIndex = 0, string label = "", bool isReplacement = false, DateTimeOffset firstSeen = default)
{
Transaction = transaction;
Label = Guard.Correct(label);
SetHeight(height, blockHash, blockIndex);
- if (firstSeenIfMempoolTime != null)
- {
- FirstSeenIfMempoolTime = firstSeenIfMempoolTime;
- }
+
+ FirstSeen = firstSeen == default ? DateTimeOffset.UtcNow : firstSeen;
+
IsReplacement = isReplacement;
}
@@ -78,15 +89,6 @@ namespace WalletWasabi.Models
public void SetHeight(Height height, uint256 blockHash = null, int blockIndex = 0)
{
Height = height;
- if (height == Height.Mempool)
- {
- FirstSeenIfMempoolTime = DateTimeOffset.UtcNow;
- }
- else
- {
- FirstSeenIfMempoolTime = null;
- }
-
BlockHash = blockHash;
BlockIndex = blockIndex;
}
@@ -118,7 +120,7 @@ namespace WalletWasabi.Models
return blockIndexCompareResult;
}
- var firstSeenCompareResult = (a.FirstSeenIfMempoolTime ?? DateTime.UtcNow).CompareTo(b.FirstSeenIfMempoolTime ?? DateTime.UtcNow);
+ var firstSeenCompareResult = a.FirstSeen.CompareTo(b.FirstSeen);
return firstSeenCompareResult;
});
}

Fix datetime of SmartTransaction