diff --git a/Scripts/STRAXSidechainRegistrationScript.ps1 b/Scripts/STRAXSidechainRegistrationScript.ps1 index 7794caecc4..1cea9e7447 100644 --- a/Scripts/STRAXSidechainRegistrationScript.ps1 +++ b/Scripts/STRAXSidechainRegistrationScript.ps1 @@ -188,7 +188,8 @@ While ( ( Get-MaxHeight ) -eq $null ) While ( ( Get-MaxHeight ) -gt ( Get-LocalIndexerHeight ) ) { $a = Get-MaxHeight - $b = Get-LocalIndexerHeight + $b = Get-LocalIndexerHeight + $c = $a - $b [int]$percentage = $b / $a * 100 "" Write-Host (Get-TimeStamp) "$percentage% Synced" -ForegroundColor Cyan diff --git a/src/FederationSetup/FederationSetup.csproj b/src/FederationSetup/FederationSetup.csproj index 3b7d953d51..69b5b66a07 100644 --- a/src/FederationSetup/FederationSetup.csproj +++ b/src/FederationSetup/FederationSetup.csproj @@ -3,7 +3,7 @@ Exe netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. diff --git a/src/FodyNlogAdapter/FodyNlogAdapter.csproj b/src/FodyNlogAdapter/FodyNlogAdapter.csproj index ab1d18a171..edfea59ee0 100644 --- a/src/FodyNlogAdapter/FodyNlogAdapter.csproj +++ b/src/FodyNlogAdapter/FodyNlogAdapter.csproj @@ -3,7 +3,7 @@ netcoreapp3.1 FodyNlogAdapter - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. Stratis.Utils.FodyNlogAdapter diff --git a/src/NBitcoin/BIP39/Mnemonic.cs b/src/NBitcoin/BIP39/Mnemonic.cs index 6b322a5905..ddfca69f04 100644 --- a/src/NBitcoin/BIP39/Mnemonic.cs +++ b/src/NBitcoin/BIP39/Mnemonic.cs @@ -27,7 +27,7 @@ public Mnemonic(string mnemonic, Wordlist wordlist = null) wordlist = Wordlist.AutoDetect(mnemonic) ?? Wordlist.English; string[] words = mnemonic.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); - _Mnemonic = string.Join(wordlist.Space.ToString(), words); + this._Mnemonic = string.Join(wordlist.Space.ToString(), words); //if the sentence is not at least 12 characters or cleanly divisible by 3, it is bad! if (!CorrectWordCount(words.Length)) @@ -54,7 +54,7 @@ public Mnemonic(Wordlist wordList, byte[] entropy = null) int i = Array.IndexOf(entArray, entropy.Length * 8); if(i == -1) - throw new ArgumentException("The length for entropy should be : " + String.Join(",", entArray), "entropy"); + throw new ArgumentException("The length for entropy should be : " + string.Join(",", entArray), "entropy"); int cs = csArray[i]; byte[] checksum = Hashes.SHA256(entropy); @@ -200,10 +200,10 @@ public ExtKey DeriveExtKey(string passphrase = null) return new ExtKey(DeriveSeed(passphrase)); } - private static Byte[] Concat(Byte[] source1, Byte[] source2) + private static byte[] Concat(byte[] source1, byte[] source2) { //Most efficient way to merge two arrays this according to http://stackoverflow.com/questions/415291/best-way-to-combine-two-or-more-byte-arrays-in-c-sharp - var buffer = new Byte[source1.Length + source2.Length]; + var buffer = new byte[source1.Length + source2.Length]; Buffer.BlockCopy(source1, 0, buffer, 0, source1.Length); Buffer.BlockCopy(source2, 0, buffer, source1.Length, source2.Length); diff --git a/src/NBitcoin/ConsensusOptions.cs b/src/NBitcoin/ConsensusOptions.cs index 7c7ead2f73..42e16f3bb0 100644 --- a/src/NBitcoin/ConsensusOptions.cs +++ b/src/NBitcoin/ConsensusOptions.cs @@ -13,7 +13,7 @@ public class ConsensusOptions /// public const int SerializeTransactionNoWitness = 0x40000000; - /// Maximum size for a block in bytes. + /// Maximum size for a block in bytes. public uint MaxBlockBaseSize { get; set; } /// The maximum allowed weight for a block, see BIP 141 (network rule) diff --git a/src/NBitcoin/Money.cs b/src/NBitcoin/Money.cs index 4fdeb62091..03f485c08d 100644 --- a/src/NBitcoin/Money.cs +++ b/src/NBitcoin/Money.cs @@ -857,7 +857,7 @@ public string Format(string format, object arg, IFormatProvider formatProvider) i++; } char unit = format[i]; - var unitToUseInCalc = MoneyUnit.BTC; + MoneyUnit unitToUseInCalc = MoneyUnit.BTC; switch (unit) { case 'B': diff --git a/src/NBitcoin/NBitcoin.csproj b/src/NBitcoin/NBitcoin.csproj index ac190dbe71..0f1180eea6 100644 --- a/src/NBitcoin/NBitcoin.csproj +++ b/src/NBitcoin/NBitcoin.csproj @@ -2,12 +2,12 @@ NStratis - Copyright © Stratis Platform SA 2020 + Copyright © Stratis Platform SA 2022 The C# Bitcoin Library based on NBitcoin - 4.0.0.83 + 4.0.0.84 diff --git a/src/NBitcoin/TransactionBuilder.cs b/src/NBitcoin/TransactionBuilder.cs index cb8cc12014..994aea9e4e 100644 --- a/src/NBitcoin/TransactionBuilder.cs +++ b/src/NBitcoin/TransactionBuilder.cs @@ -1584,7 +1584,7 @@ public int EstimateSize(Transaction tx, bool virtualSize) int witSize = 0; if (tx.HasWitness) witSize += 2; - foreach (var txin in tx.Inputs.AsIndexedInputs()) + foreach (IndexedTxIn txin in tx.Inputs.AsIndexedInputs()) { ICoin coin = FindSignableCoin(txin) ?? FindCoin(txin.PrevOut); if (coin == null) diff --git a/src/Stratis.Bitcoin.Api.Tests/ApiSettingsTest.cs b/src/Stratis.Bitcoin.Api.Tests/ApiSettingsTest.cs index 68e35b79a9..4ca9711a65 100644 --- a/src/Stratis.Bitcoin.Api.Tests/ApiSettingsTest.cs +++ b/src/Stratis.Bitcoin.Api.Tests/ApiSettingsTest.cs @@ -231,7 +231,7 @@ public void GivenUseHttps_ThenUsesTheCorrectProtocol(bool useHttps, string expec var nodeSettings = new NodeSettings(KnownNetworks.TestNet, args: new[] { $"-usehttps={useHttps}", "-certificatefilepath=nonNullValue" }); // Act. - var settings = FullNodeSetup(nodeSettings); + ApiSettings settings = FullNodeSetup(nodeSettings); // Assert. settings.UseHttps.Should().Be(useHttps); diff --git a/src/Stratis.Bitcoin.Cli/Stratis.Bitcoin.Cli.csproj b/src/Stratis.Bitcoin.Cli/Stratis.Bitcoin.Cli.csproj index cb03d021d6..6fef66ca86 100644 --- a/src/Stratis.Bitcoin.Cli/Stratis.Bitcoin.Cli.csproj +++ b/src/Stratis.Bitcoin.Cli/Stratis.Bitcoin.Cli.csproj @@ -3,7 +3,7 @@ Exe netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.Api/NodeController.cs b/src/Stratis.Bitcoin.Features.Api/NodeController.cs index 9a22d2ed89..17019e21c6 100644 --- a/src/Stratis.Bitcoin.Features.Api/NodeController.cs +++ b/src/Stratis.Bitcoin.Features.Api/NodeController.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; @@ -15,6 +14,7 @@ using Stratis.Bitcoin.Base; using Stratis.Bitcoin.Builder.Feature; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Connection; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Controllers.Models; @@ -27,7 +27,6 @@ using Stratis.Bitcoin.Utilities; using Stratis.Bitcoin.Utilities.JsonErrors; using Stratis.Bitcoin.Utilities.ModelStateErrors; -using ILogger = Microsoft.Extensions.Logging.ILogger; using LogLevel = NLog.LogLevel; using Target = NBitcoin.Target; @@ -221,33 +220,34 @@ public IActionResult Status([FromQuery] bool publish) /// The hash of the block to retrieve. /// A flag that specifies whether to return the block header in the JSON format. Defaults to true. A value of false is currently not supported. /// Json formatted . null if block not found. Returns formatted error if fails. - /// Thrown if isJsonFormat = false" - /// Thrown if hash is empty. - /// Thrown if logger is not provided. + /// Returns the blockheader if found. + /// Null hash provided, BlockHeader does not exist or if isJsonFormat = false>/response> /// Binary serialization is not supported with this method. [Route("getblockheader")] [HttpGet] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult GetBlockHeader([FromQuery] string hash, bool isJsonFormat = true) { try { - Guard.NotEmpty(hash, nameof(hash)); + if (string.IsNullOrEmpty(hash)) + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Error", "Null hash provided."); this.logger.LogDebug("GetBlockHeader {0}", hash); + if (!isJsonFormat) { this.logger.LogError("Binary serialization is not supported."); - throw new NotImplementedException(); + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Error", "Binary serialization is not supported."); } - BlockHeaderModel model = null; BlockHeader blockHeader = this.chainIndexer?.GetHeader(uint256.Parse(hash))?.Header; - if (blockHeader != null) - { - model = new BlockHeaderModel(blockHeader); - } + if (blockHeader == null) + return this.NotFound($"Block header for '{hash}' not found"); - return this.Json(model); + return this.Json(new BlockHeaderModel(blockHeader)); } catch (Exception e) { @@ -385,7 +385,7 @@ public IActionResult ValidateAddress([FromQuery] string address) if (result.IsValid) { - var scriptPubKey = BitcoinAddress.Create(address, this.network).ScriptPubKey; + NBitcoin.Script scriptPubKey = BitcoinAddress.Create(address, this.network).ScriptPubKey; result.ScriptPubKey = scriptPubKey.ToHex(); result.IsWitness = scriptPubKey.IsWitness(this.network); } @@ -453,7 +453,7 @@ public async Task GetTxOutAsync([FromQuery] string trxid, uint vo /// The hex-encoded merkle proof. [Route("gettxoutproof")] [HttpGet] - public async Task GetTxOutProofAsync([FromQuery] string[] txids, string blockhash = "") + public Task GetTxOutProofAsync([FromQuery] string[] txids, string blockhash = "") { List transactionIds = txids.Select(txString => uint256.Parse(txString)).ToList(); @@ -500,7 +500,7 @@ public async Task GetTxOutProofAsync([FromQuery] string[] txids, var result = new MerkleBlock(block.Block, transactionIds.ToArray()); - return this.Json(result); + return Task.FromResult(this.Json(result)); } /// @@ -529,6 +529,8 @@ public IActionResult Shutdown([FromBody] bool corsProtection = true) /// Signals the node to rewind to the specified height. /// This will be done via writing a flag to the .conf file so that on startup it be executed. /// + /// The rewind height. + /// A json text result indicating success or an indicating failure. [Route("rewind")] [HttpPut] [ProducesResponseType((int)HttpStatusCode.OK)] @@ -697,7 +699,7 @@ public IActionResult GetAsyncLoops() /// /// Schedules data folder storing chain state in the for deletion on the next graceful shutdown. /// - /// + /// Returns an . [HttpDelete] [Route("datafolder/chain")] public IActionResult DeleteChain() @@ -717,7 +719,7 @@ public IActionResult DeleteChain() /// Thrown if fullnode is not provided. internal ChainedHeader GetTransactionBlock(uint256 trxid, ChainIndexer chain) { - Guard.NotNull(fullNode, nameof(fullNode)); + Guard.NotNull(this.fullNode, nameof(this.fullNode)); ChainedHeader block = null; uint256 blockid = this.blockStore?.GetBlockIdByTransactionId(trxid); diff --git a/src/Stratis.Bitcoin.Features.Api/Startup.cs b/src/Stratis.Bitcoin.Features.Api/Startup.cs index 7136012856..9a838a9635 100644 --- a/src/Stratis.Bitcoin.Features.Api/Startup.cs +++ b/src/Stratis.Bitcoin.Features.Api/Startup.cs @@ -9,7 +9,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; -using Newtonsoft.Json.Converters; using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerUI; @@ -74,7 +73,9 @@ public void ConfigureServices(IServiceCollection services) { options.Filters.Add(typeof(LoggingActionFilter)); +#pragma warning disable ASP0000 // Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices' ServiceProvider serviceProvider = services.BuildServiceProvider(); +#pragma warning restore ASP0000 // Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices' var apiSettings = (ApiSettings)serviceProvider.GetRequiredService(typeof(ApiSettings)); if (apiSettings.KeepaliveTimer != null) { diff --git a/src/Stratis.Bitcoin.Features.Api/Stratis.Bitcoin.Features.Api.csproj b/src/Stratis.Bitcoin.Features.Api/Stratis.Bitcoin.Features.Api.csproj index 9024d02ed5..cbb1b4420d 100644 --- a/src/Stratis.Bitcoin.Features.Api/Stratis.Bitcoin.Features.Api.csproj +++ b/src/Stratis.Bitcoin.Features.Api/Stratis.Bitcoin.Features.Api.csproj @@ -6,7 +6,7 @@ Stratis.Bitcoin.Features.Api Library Stratis.Features.Api - 1.1.1.1 + 1.2.0.0 False library diff --git a/src/Stratis.Bitcoin.Features.BlockStore.Tests/AddressIndexerOutpointsRepositoryTests.cs b/src/Stratis.Bitcoin.Features.BlockStore.Tests/AddressIndexerOutpointsRepositoryTests.cs index 26e37e5e27..47939abe0d 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore.Tests/AddressIndexerOutpointsRepositoryTests.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore.Tests/AddressIndexerOutpointsRepositoryTests.cs @@ -4,7 +4,6 @@ using System.Runtime.InteropServices; using LiteDB; using NBitcoin; -using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.BlockStore.AddressIndexing; using Xunit; @@ -23,7 +22,7 @@ public AddressIndexerOutpointsRepositoryTests() FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; var db = new LiteDatabase(new ConnectionString() { Filename = this.RandomString(20) + ".litedb", Mode = fileMode }); - this.repository = new AddressIndexerOutpointsRepository(db, new ExtendedLoggerFactory(), this.maxItems); + this.repository = new AddressIndexerOutpointsRepository(db, this.maxItems); } [Fact] diff --git a/src/Stratis.Bitcoin.Features.BlockStore.Tests/AddressIndexerTests.cs b/src/Stratis.Bitcoin.Features.BlockStore.Tests/AddressIndexerTests.cs index 3cad2fc9f9..319db52591 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore.Tests/AddressIndexerTests.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore.Tests/AddressIndexerTests.cs @@ -7,7 +7,6 @@ using NBitcoin; using Stratis.Bitcoin.AsyncWork; using Stratis.Bitcoin.Configuration; -using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Controllers.Models; using Stratis.Bitcoin.Features.BlockStore.AddressIndexing; @@ -52,7 +51,7 @@ public AddressIndexerTests() var utxoIndexerMock = new Mock(); - this.addressIndexer = new AddressIndexer(storeSettings, dataFolder, new ExtendedLoggerFactory(), this.network, stats.Object, + this.addressIndexer = new AddressIndexer(storeSettings, dataFolder, this.network, stats.Object, this.consensusManagerMock.Object, this.asyncProviderMock.Object, indexer, new DateTimeProvider(), utxoIndexerMock.Object); this.genesisHeader = new ChainedHeader(this.network.GetGenesis().Header, this.network.GetGenesis().Header.GetHash(), 0); @@ -182,7 +181,7 @@ public void OutPointCacheCanRetrieveExisting() FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; var database = new LiteDatabase(new ConnectionString() { Filename = dbPath, Mode = fileMode }); - var cache = new AddressIndexerOutpointsRepository(database, new ExtendedLoggerFactory()); + var cache = new AddressIndexerOutpointsRepository(database); var outPoint = new OutPoint(uint256.Parse("0000af9ab2c8660481328d0444cf167dfd31f24ca2dbba8e5e963a2434cffa93"), 0); @@ -205,7 +204,7 @@ public void OutPointCacheCannotRetrieveNonexistent() FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; var database = new LiteDatabase(new ConnectionString() { Filename = dbPath, Mode = fileMode }); - var cache = new AddressIndexerOutpointsRepository(database, new ExtendedLoggerFactory()); + var cache = new AddressIndexerOutpointsRepository(database); Assert.False(cache.TryGetOutPointData(new OutPoint(uint256.Parse("0000af9ab2c8660481328d0444cf167dfd31f24ca2dbba8e5e963a2434cffa93"), 1), out OutPointData retrieved)); Assert.Null(retrieved); @@ -220,7 +219,7 @@ public void OutPointCacheEvicts() FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; var database = new LiteDatabase(new ConnectionString() { Filename = dbPath, Mode = fileMode }); - var cache = new AddressIndexerOutpointsRepository(database, new ExtendedLoggerFactory(), 2); + var cache = new AddressIndexerOutpointsRepository(database, 2); Assert.Equal(0, cache.Count); Assert.Equal(0, database.GetCollection(CollectionName).Count()); @@ -271,7 +270,7 @@ public void AddressCacheCanRetrieveExisting() FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; var database = new LiteDatabase(new ConnectionString() { Filename = dbPath, Mode = fileMode }); - var cache = new AddressIndexRepository(database, new ExtendedLoggerFactory()); + var cache = new AddressIndexRepository(database); string address = "xyz"; var balanceChanges = new List(); @@ -300,7 +299,7 @@ public void AddressCacheRetrievesBlankRecordForNonexistent() FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; var database = new LiteDatabase(new ConnectionString() { Filename = dbPath, Mode = fileMode }); - var cache = new AddressIndexRepository(database, new ExtendedLoggerFactory()); + var cache = new AddressIndexRepository(database); AddressIndexerData retrieved = cache.GetOrCreateAddress("xyz"); @@ -319,7 +318,7 @@ public void AddressCacheEvicts() FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; var database = new LiteDatabase(new ConnectionString() { Filename = dbPath, Mode = fileMode }); - var cache = new AddressIndexRepository(database, new ExtendedLoggerFactory(), 4); + var cache = new AddressIndexRepository(database, 4); // Recall, each index entry counts as 1 and each balance change associated with it is an additional 1. Assert.Equal(0, database.GetCollection(CollectionName).Count()); diff --git a/src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreControllerTests.cs b/src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreControllerTests.cs index 612f5cd55a..55730a8d4b 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreControllerTests.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreControllerTests.cs @@ -68,9 +68,9 @@ public void Get_Block_When_Hash_Is_Not_Found_Should_Return_OkResult_WithMessage( IActionResult response = controller.GetBlock(new SearchByHashRequest() { Hash = new uint256(1).ToString(), OutputJson = true }); - response.Should().BeOfType(); - var result = (OkObjectResult)response; - result.StatusCode.Should().Be((int)HttpStatusCode.OK); + response.Should().BeOfType(); + var result = (NotFoundObjectResult)response; + result.StatusCode.Should().Be((int)HttpStatusCode.NotFound); result.Value.Should().Be("Block not found"); } diff --git a/src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreTests.cs b/src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreTests.cs index e34b8ff765..f48bdabdc7 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreTests.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore.Tests/BlockStoreTests.cs @@ -333,6 +333,7 @@ public async Task ReorgedBlocksAreNotSavedAsync() /// /// Tests reorgs inside the batch and inside the database at the same time. /// + /// The asynchronous task. [Fact] [Trait("Unstable", "True")] public async Task ReorgedBlocksAreDeletedFromRepositoryIfReorgDetectedAsync() diff --git a/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexRepository.cs b/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexRepository.cs index a202e8e4e6..7ba776766d 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexRepository.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexRepository.cs @@ -3,6 +3,7 @@ using System.Linq; using LiteDB; using Microsoft.Extensions.Logging; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Controllers.Models; using Stratis.Bitcoin.Utilities; @@ -17,9 +18,9 @@ public class AddressIndexRepository : MemorySizeCache(DbAddressDataKey); this.addressIndexerDataCollection.EnsureIndex("BalanceChangedHeightIndex", "$.BalanceChanges[*].BalanceChangedHeight", false); } @@ -80,10 +81,17 @@ public void SaveAllItems() lock (this.LockObject) { CacheItem[] dirtyItems = this.Keys.Where(x => x.Dirty).ToArray(); - this.addressIndexerDataCollection.Upsert(dirtyItems.Select(x => x.Value)); + this.logger.LogDebug("Saving {0} dirty items.", dirtyItems.Length); + + List toUpsert = dirtyItems.Select(x => x.Value).ToList(); + + this.addressIndexerDataCollection.Upsert(toUpsert); + foreach (CacheItem dirtyItem in dirtyItems) dirtyItem.Dirty = false; + + this.logger.LogDebug("Saved dirty items."); } } } diff --git a/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexer.cs b/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexer.cs index 0faabfba2d..5bd1d73a1c 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexer.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexer.cs @@ -40,6 +40,7 @@ public interface IAddressIndexer : IDisposable /// Returns verbose balances data. /// The set of addresses that will be queried. + /// See . VerboseAddressBalancesResult GetAddressIndexerState(string[] addresses); IFullNodeFeature InitializingFeature { set; } @@ -82,7 +83,9 @@ public class AddressIndexer : IAddressIndexer /// private const int DelayTimeMs = 2000; - private const int CompactingThreshold = 50; + private int compactionThreshold; + + private int compactionAmount => this.compactionThreshold / 2; /// Max distance between consensus and indexer tip to consider indexer synced. private const int ConsiderSyncedMaxDistance = 10; @@ -103,9 +106,7 @@ public class AddressIndexer : IAddressIndexer private readonly object lockObject; private readonly CancellationTokenSource cancellation; - - private readonly ILoggerFactory loggerFactory; - + private readonly ChainIndexer chainIndexer; private readonly AverageCalculator averageTimePerBlock; @@ -138,10 +139,10 @@ public class AddressIndexer : IAddressIndexer /// We assume that nodes usually don't have view that is different from other nodes by that constant of blocks. /// public const int SyncBuffer = 50; - + public IFullNodeFeature InitializingFeature { get; set; } - public AddressIndexer(StoreSettings storeSettings, DataFolder dataFolder, ILoggerFactory loggerFactory, Network network, INodeStats nodeStats, + public AddressIndexer(StoreSettings storeSettings, DataFolder dataFolder, Network network, INodeStats nodeStats, IConsensusManager consensusManager, IAsyncProvider asyncProvider, ChainIndexer chainIndexer, IDateTimeProvider dateTimeProvider, IUtxoIndexer utxoIndexer) { this.storeSettings = storeSettings; @@ -152,7 +153,6 @@ public AddressIndexer(StoreSettings storeSettings, DataFolder dataFolder, ILogge this.asyncProvider = asyncProvider; this.dateTimeProvider = dateTimeProvider; this.utxoIndexer = utxoIndexer; - this.loggerFactory = loggerFactory; this.scriptAddressReader = new ScriptAddressReader(); this.lockObject = new object(); @@ -160,15 +160,18 @@ public AddressIndexer(StoreSettings storeSettings, DataFolder dataFolder, ILogge this.lastFlushTime = this.dateTimeProvider.GetUtcNow(); this.cancellation = new CancellationTokenSource(); this.chainIndexer = chainIndexer; - this.logger = loggerFactory.CreateLogger(this.GetType().FullName); + this.logger = LogManager.GetCurrentClassLogger(); + this.compactionThreshold = storeSettings.AddressIndexerCompactionThreshold; this.averageTimePerBlock = new AverageCalculator(200); int maxReorgLength = GetMaxReorgOrFallbackMaxReorg(this.network); this.compactionTriggerDistance = maxReorgLength * 2 + SyncBuffer + 1000; } - /// Returns maxReorg of in case maxReorg is 0. + /// Gets the maxReorg of in case maxReorg is 0. + /// The network to get the value for. + /// Returns the maxReorg or value. public static int GetMaxReorgOrFallbackMaxReorg(Network network) { int maxReorgLength = network.Consensus.MaxReorgLength == 0 ? FallBackMaxReorg : (int)network.Consensus.MaxReorgLength; @@ -190,7 +193,7 @@ public void Initialize() FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; this.db = new LiteDatabase(new ConnectionString() { Filename = dbPath, Mode = fileMode }); - this.addressIndexRepository = new AddressIndexRepository(this.db, this.loggerFactory); + this.addressIndexRepository = new AddressIndexRepository(this.db); this.logger.LogDebug("Address indexing is enabled."); @@ -216,7 +219,7 @@ public void Initialize() } } - this.outpointsRepository = new AddressIndexerOutpointsRepository(this.db, this.loggerFactory); + this.outpointsRepository = new AddressIndexerOutpointsRepository(this.db); this.RewindAndSave(this.IndexerTip); @@ -247,7 +250,10 @@ private async Task IndexAddressesContinuouslyAsync() } if (this.cancellation.IsCancellationRequested) + { + this.logger.LogDebug("Cancelled loop."); break; + } ChainedHeader nextHeader = this.consensusManager.Tip.GetAncestor(this.IndexerTip.Height + 1); @@ -332,7 +338,7 @@ private async Task IndexAddressesContinuouslyAsync() this.IndexerTip = nextHeader; } - + this.SaveAll(); } @@ -369,7 +375,10 @@ private void SaveAll() lock (this.lockObject) { + this.logger.LogDebug("Saving addr indexer repo."); this.addressIndexRepository.SaveAllItems(); + + this.logger.LogDebug("Saving outpoints repo."); this.outpointsRepository.SaveAllItems(); AddressIndexerTipData tipData = this.tipDataStore.FindAll().FirstOrDefault(); @@ -380,6 +389,8 @@ private void SaveAll() tipData.Height = this.IndexerTip.Height; tipData.TipHashBytes = this.IndexerTip.HashBlock.ToBytes(); + this.logger.LogDebug("Saving tip data."); + this.tipDataStore.Upsert(tipData); this.lastSavedHeight = this.IndexerTip.Height; } @@ -390,7 +401,7 @@ private void SaveAll() private void AddInlineStats(StringBuilder benchLog) { benchLog.AppendLine("AddressIndexer Height".PadRight(LoggingConfiguration.ColumnLength) + $": {this.IndexerTip.Height}".PadRight(9) + - "AddressCache%: " + this.addressIndexRepository.GetLoadPercentage().ToString().PadRight(8) + + " AddressCache%: " + this.addressIndexRepository.GetLoadPercentage().ToString().PadRight(8) + "OutPointCache%: " + this.outpointsRepository.GetLoadPercentage().ToString().PadRight(8) + $"Ms/block: {Math.Round(this.averageTimePerBlock.Average, 2)}"); } @@ -401,6 +412,8 @@ private void AddInlineStats(StringBuilder benchLog) /// true if block was sucessfully processed. private bool ProcessBlock(Block block, ChainedHeader header) { + this.logger.LogTrace("Processing block " + header.ToString()); + lock (this.lockObject) { // Record outpoints. @@ -430,8 +443,8 @@ private bool ProcessBlock(Block block, ChainedHeader header) // Process inputs. var inputs = new List(); - // Collect all inputs excluding coinbases. - foreach (TxInList inputsCollection in block.Transactions.Where(x => !x.IsCoinBase).Select(x => x.Inputs)) + // Collect all inputs. + foreach (TxInList inputsCollection in block.Transactions.Select(x => x.Inputs)) inputs.AddRange(inputsCollection); lock (this.lockObject) @@ -442,6 +455,10 @@ private bool ProcessBlock(Block block, ChainedHeader header) { OutPoint consumedOutput = input.PrevOut; + // Ignore coinbase. + if (consumedOutput.Hash == uint256.Zero) + continue; + if (!this.outpointsRepository.TryGetOutPointData(consumedOutput, out OutPointData consumedOutputData)) { this.logger.LogError("Missing outpoint data for {0}.", consumedOutput); @@ -503,10 +520,11 @@ private bool ProcessBlock(Block block, ChainedHeader header) } // Remove outpoints that were consumed. - foreach (OutPoint consumedOutPoint in inputs.Select(x => x.PrevOut)) + foreach (OutPoint consumedOutPoint in inputs.Where(x => x.PrevOut.Hash != uint256.Zero).Select(x => x.PrevOut)) this.outpointsRepository.RemoveOutPointData(consumedOutPoint); } + this.logger.LogTrace("Block processed."); return true; } @@ -531,16 +549,17 @@ private void ProcessBalanceChangeLocked(int height, string address, Money amount // Anything less than that should be compacted. int heightThreshold = this.consensusManager.Tip.Height - this.compactionTriggerDistance; - bool compact = (indexData.BalanceChanges.Count > CompactingThreshold) && - (indexData.BalanceChanges[1].BalanceChangedHeight < heightThreshold); + bool compact = (indexData.BalanceChanges.Count > this.compactionThreshold) && (indexData.BalanceChanges[1].BalanceChangedHeight < heightThreshold); if (!compact) { + this.addressIndexRepository.AddOrUpdate(indexData.Address, indexData, indexData.BalanceChanges.Count + 1); + this.logger.LogTrace("(-)[TOO_FEW_CHANGE_RECORDS]"); return; } - var compacted = new List(CompactingThreshold / 2) + var compacted = new List(this.compactionThreshold / 2) { new AddressBalanceChange() { @@ -550,9 +569,11 @@ private void ProcessBalanceChangeLocked(int height, string address, Money amount } }; - foreach (AddressBalanceChange change in indexData.BalanceChanges) + for (int i = 0; i < indexData.BalanceChanges.Count; i++) { - if (change.BalanceChangedHeight < heightThreshold) + AddressBalanceChange change = indexData.BalanceChanges[i]; + + if ((change.BalanceChangedHeight) < heightThreshold && i < this.compactionAmount) { this.logger.LogDebug("Balance change: {0} was selected for compaction. Compacted balance now: {1}.", change, compacted[0].Satoshi); @@ -731,11 +752,15 @@ public LastBalanceDecreaseTransactionModel GetLastBalanceDecreaseTransaction(str /// public void Dispose() { + this.logger.LogDebug("Disposing."); + this.cancellation.Cancel(); this.indexingTask?.GetAwaiter().GetResult(); this.db?.Dispose(); + + this.logger.LogDebug("Disposed."); } } } diff --git a/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexerOutpointsRepository.cs b/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexerOutpointsRepository.cs index 73b7f93f31..852f486aed 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexerOutpointsRepository.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/AddressIndexing/AddressIndexerOutpointsRepository.cs @@ -4,6 +4,7 @@ using LiteDB; using Microsoft.Extensions.Logging; using NBitcoin; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Utilities; namespace Stratis.Bitcoin.Features.BlockStore.AddressIndexing @@ -27,9 +28,9 @@ public sealed class AddressIndexerOutpointsRepository : MemoryCache(DbOutputsDataKey); this.addressIndexerRewindData = db.GetCollection(DbOutputsRewindDataKey); this.maxCacheItems = maxItems; @@ -92,6 +93,7 @@ public bool TryGetOutPointData(OutPoint outPoint, out OutPointData outPointData) public void SaveAllItems() { + this.logger.LogDebug("Saving all items."); lock (this.LockObject) { CacheItem[] dirtyItems = this.Keys.Where(x => x.Dirty).ToArray(); @@ -99,7 +101,10 @@ public void SaveAllItems() foreach (CacheItem dirtyItem in dirtyItems) dirtyItem.Dirty = false; + + this.logger.LogDebug("{0} items saved.", dirtyItems.Length); } + } /// Persists rewind data into the repository. @@ -118,15 +123,9 @@ public void PurgeOldRewindData(int height) { lock (this.LockObject) { - var itemsToPurge = this.addressIndexerRewindData.Find(x => x.BlockHeight < height).ToArray(); - - for (int i = 0; i < itemsToPurge.Count(); i++) - { - this.addressIndexerRewindData.Delete(itemsToPurge[i].BlockHash); - - if (i % 100 == 0) - this.logger.LogInformation("Purging {0}/{1} rewind data items.", i, itemsToPurge.Count()); - } + this.logger.LogInformation("AddressIndexer: started purging rewind data items."); + int purgedCount = this.addressIndexerRewindData.Delete(x => x.BlockHeight < height); + this.logger.LogInformation("AddressIndexer: Purged {0} rewind data items.", purgedCount); } } diff --git a/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs b/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs index e9c40b12ef..ae245d367e 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreQueue.cs @@ -54,6 +54,8 @@ public class BlockStoreQueue : IBlockStoreQueue private readonly IInitialBlockDownloadState initialBlockDownloadState; +#pragma warning disable SA1648 + /// private readonly ILogger logger; private readonly IBlockStoreQueueFlushCondition blockStoreQueueFlushCondition; @@ -70,6 +72,8 @@ public class BlockStoreQueue : IBlockStoreQueue /// private readonly IBlockRepository blockRepository; +#pragma warning restore SA1648 + private readonly IAsyncProvider asyncProvider; /// Queue which contains blocks that should be saved to the database. @@ -399,6 +403,7 @@ public List GetBlocks(List blockHashes) } /// Sets the internal store tip and exposes the store tip to other components through the chain state. + /// The new store tip to set. private void SetStoreTip(ChainedHeader newTip) { this.storeTip = newTip; @@ -408,6 +413,7 @@ private void SetStoreTip(ChainedHeader newTip) /// /// Sets block store tip to the last block that exists both in the repository and in the . /// + /// The store tip set by this method. private ChainedHeader RecoverStoreTip() { ChainedHeader blockStoreTip = this.chainIndexer.GetHeader(this.blockRepository.TipHashAndHeight.Hash); @@ -496,6 +502,7 @@ public void AddToPending(ChainedHeaderBlock chainedHeaderBlock) /// Dequeues the blocks continuously and saves them to the database when max batch size is reached or timer ran out. /// /// Batch is always saved on shutdown. + /// The asynchronous task. private async Task DequeueBlocksContinuouslyAsync() { Task dequeueTask = null; diff --git a/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreSignaled.cs b/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreSignaled.cs index 580e7f9f8e..039bd2c60b 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreSignaled.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/BlockStoreSignaled.cs @@ -130,6 +130,7 @@ protected virtual void AddBlockToQueue(ChainedHeaderBlock blockPair, bool isIBD) /// Continuously dequeues items from and sends /// them to the peers after the timer runs out or if the last item is a tip. /// + /// The asynchronous task. private async Task DequeueContinuouslyAsync() { var batch = new List(); @@ -186,6 +187,8 @@ private async Task DequeueContinuouslyAsync() /// /// A method that relays blocks found in to connected peers on the network. /// + /// The batch containing the blocks to relay. + /// The asynchronous task. /// /// /// The list contains hashes of blocks that were validated by the consensus rules. diff --git a/src/Stratis.Bitcoin.Features.BlockStore/Controllers/BlockStoreController.cs b/src/Stratis.Bitcoin.Features.BlockStore/Controllers/BlockStoreController.cs index d7c5500ef9..a413d28f13 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/Controllers/BlockStoreController.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/Controllers/BlockStoreController.cs @@ -122,6 +122,7 @@ public IActionResult GetAddressIndexerTip() [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult GetBlock([FromQuery] SearchByHashRequest query) { if (!this.ModelState.IsValid) @@ -134,14 +135,14 @@ public IActionResult GetBlock([FromQuery] SearchByHashRequest query) ChainedHeader chainedHeader = this.chainIndexer.GetHeader(blockId); if (chainedHeader == null) - return this.Ok("Block not found"); + return this.NotFound("Block not found"); Block block = chainedHeader.Block ?? this.blockStore.GetBlock(blockId); // In rare occasions a block that is found in the // indexer may not have been pushed to the store yet. if (block == null) - return this.Ok("Block not found"); + return this.NotFound("Block not found"); if (!query.OutputJson) { diff --git a/src/Stratis.Bitcoin.Features.BlockStore/IBlockRepository.cs b/src/Stratis.Bitcoin.Features.BlockStore/IBlockRepository.cs index 03b8e126f1..37c50287dd 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/IBlockRepository.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/IBlockRepository.cs @@ -34,13 +34,6 @@ public interface IBlockRepository : IBlockStore /// Blocks to be inserted. void PutBlocks(HashHeightPair newTip, List blocks); - /// - /// Get the blocks from the database by using block hashes. - /// - /// A list of unique block hashes. - /// The blocks (or null if not found) in the same order as the hashes on input. - List GetBlocks(List hashes); - /// /// Wipe out blocks and their transactions then replace with a new block. /// diff --git a/src/Stratis.Bitcoin.Features.BlockStore/Repositories/LevelDbBlockRepository.cs b/src/Stratis.Bitcoin.Features.BlockStore/Repositories/LevelDbBlockRepository.cs index d6ec63f360..52dc357c29 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/Repositories/LevelDbBlockRepository.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/Repositories/LevelDbBlockRepository.cs @@ -6,9 +6,10 @@ using System.Threading; using DBreeze.Utils; using LevelDB; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Persistence; using Stratis.Bitcoin.Utilities; @@ -85,7 +86,7 @@ public Transaction GetTransactionById(uint256 trxid) if (!this.TxIndex) { - this.logger.Trace("(-)[TX_INDEXING_DISABLED]:null"); + this.logger.LogTrace("(-)[TX_INDEXING_DISABLED]:null"); return default(Transaction); } @@ -101,7 +102,7 @@ public Transaction GetTransactionById(uint256 trxid) if (transactionRow == null) { - this.logger.Trace("(-)[NO_BLOCK]:null"); + this.logger.LogTrace("(-)[NO_BLOCK]:null"); return null; } @@ -122,7 +123,7 @@ public Transaction GetTransactionById(uint256 trxid) { if (!this.TxIndex) { - this.logger.Trace("(-)[TX_INDEXING_DISABLED]:null"); + this.logger.LogTrace("(-)[TX_INDEXING_DISABLED]:null"); return null; } @@ -138,7 +139,7 @@ public Transaction GetTransactionById(uint256 trxid) if (alreadyFetched) { - this.logger.Debug("Duplicated transaction encountered. Tx id: '{0}'.", trxids[i]); + this.logger.LogDebug("Duplicated transaction encountered. Tx id: '{0}'.", trxids[i]); txes[i] = txes.First(x => x.GetHash() == trxids[i]); continue; @@ -153,7 +154,7 @@ public Transaction GetTransactionById(uint256 trxid) byte[] transactionRow = this.leveldb.Get(BlockRepositoryConstants.TransactionTableName, trxids[i].ToBytes()); if (transactionRow == null) { - this.logger.Trace("(-)[NO_TX_ROW]:null"); + this.logger.LogTrace("(-)[NO_TX_ROW]:null"); return null; } @@ -161,7 +162,7 @@ public Transaction GetTransactionById(uint256 trxid) if (blockRow != null) { - this.logger.Trace("(-)[NO_BLOCK]:null"); + this.logger.LogTrace("(-)[NO_BLOCK]:null"); return null; } @@ -182,7 +183,7 @@ public uint256 GetBlockIdByTransactionId(uint256 trxid) if (!this.TxIndex) { - this.logger.Trace("(-)[NO_TXINDEX]:null"); + this.logger.LogTrace("(-)[NO_TXINDEX]:null"); return default(uint256); } @@ -298,7 +299,7 @@ public void ReIndex() warningMessage.AppendLine("".PadRight(133, '=')); warningMessage.AppendLine(); - this.logger.Info(warningMessage.ToString()); + this.logger.LogInformation(warningMessage.ToString()); using (var batch = new WriteBatch()) { var enumerator = this.leveldb.GetEnumerator(); @@ -315,7 +316,7 @@ public void ReIndex() // inform the user about the ongoing operation if (++rowCount % 1000 == 0) { - this.logger.Info("Reindex in process... {0}/{1} blocks processed.", rowCount, totalBlocksCount); + this.logger.LogInformation("Reindex in process... {0}/{1} blocks processed.", rowCount, totalBlocksCount); } } } @@ -323,7 +324,7 @@ public void ReIndex() this.leveldb.Write(batch, new WriteOptions() { Sync = true }); } - this.logger.Info("Reindex completed successfully."); + this.logger.LogInformation("Reindex completed successfully."); } else { @@ -498,13 +499,13 @@ public List GetBlocksFromHashes(List hashes) { results[key.Item1] = this.dBreezeSerializer.Deserialize(blockRow); - this.logger.Debug("Block hash '{0}' loaded from the store.", key.Item1); + this.logger.LogDebug("Block hash '{0}' loaded from the store.", key.Item1); } else { results[key.Item1] = null; - this.logger.Debug("Block hash '{0}' not found in the store.", key.Item1); + this.logger.LogDebug("Block hash '{0}' not found in the store.", key.Item1); } } diff --git a/src/Stratis.Bitcoin.Features.BlockStore/Repositories/RocksDbBlockRepository.cs b/src/Stratis.Bitcoin.Features.BlockStore/Repositories/RocksDbBlockRepository.cs index d1d8b227b5..4b9fc03490 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/Repositories/RocksDbBlockRepository.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/Repositories/RocksDbBlockRepository.cs @@ -5,10 +5,11 @@ using System.Text; using System.Threading; using DBreeze.Utils; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using RocksDbSharp; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Persistence; using Stratis.Bitcoin.Utilities; @@ -82,7 +83,7 @@ public Transaction GetTransactionById(uint256 trxid) if (!this.TxIndex) { - this.logger.Trace("(-)[TX_INDEXING_DISABLED]:null"); + this.logger.LogTrace("(-)[TX_INDEXING_DISABLED]:null"); return default; } @@ -98,7 +99,7 @@ public Transaction GetTransactionById(uint256 trxid) if (transactionRow == null) { - this.logger.Trace("(-)[NO_BLOCK]:null"); + this.logger.LogTrace("(-)[NO_BLOCK]:null"); return null; } @@ -119,7 +120,7 @@ public Transaction GetTransactionById(uint256 trxid) { if (!this.TxIndex) { - this.logger.Trace("(-)[TX_INDEXING_DISABLED]:null"); + this.logger.LogTrace("(-)[TX_INDEXING_DISABLED]:null"); return null; } @@ -135,7 +136,7 @@ public Transaction GetTransactionById(uint256 trxid) if (alreadyFetched) { - this.logger.Debug("Duplicated transaction encountered. Tx id: '{0}'.", trxids[i]); + this.logger.LogDebug("Duplicated transaction encountered. Tx id: '{0}'.", trxids[i]); txes[i] = txes.First(x => x.GetHash() == trxids[i]); continue; @@ -150,7 +151,7 @@ public Transaction GetTransactionById(uint256 trxid) byte[] transactionRow = this.rocksDb.Get(BlockRepositoryConstants.TransactionTableName, trxids[i].ToBytes()); if (transactionRow == null) { - this.logger.Trace("(-)[NO_TX_ROW]:null"); + this.logger.LogTrace("(-)[NO_TX_ROW]:null"); return null; } @@ -158,7 +159,7 @@ public Transaction GetTransactionById(uint256 trxid) if (blockRow != null) { - this.logger.Trace("(-)[NO_BLOCK]:null"); + this.logger.LogTrace("(-)[NO_BLOCK]:null"); return null; } @@ -179,7 +180,7 @@ public uint256 GetBlockIdByTransactionId(uint256 trxid) if (!this.TxIndex) { - this.logger.Trace("(-)[NO_TXINDEX]:null"); + this.logger.LogTrace("(-)[NO_TXINDEX]:null"); return default; } @@ -294,7 +295,7 @@ public void ReIndex() warningMessage.AppendLine("".PadRight(133, '=')); warningMessage.AppendLine(); - this.logger.Info(warningMessage.ToString()); + this.logger.LogInformation(warningMessage.ToString()); using (var batch = new WriteBatch()) { var enumerator = this.rocksDb.NewIterator(); @@ -311,7 +312,7 @@ public void ReIndex() // inform the user about the ongoing operation if (++rowCount % 1000 == 0) { - this.logger.Info("Reindex in process... {0}/{1} blocks processed.", rowCount, totalBlocksCount); + this.logger.LogInformation("Reindex in process... {0}/{1} blocks processed.", rowCount, totalBlocksCount); } } } @@ -319,7 +320,7 @@ public void ReIndex() this.rocksDb.Write(batch); } - this.logger.Info("Reindex completed successfully."); + this.logger.LogInformation("Reindex completed successfully."); } else { @@ -494,13 +495,13 @@ public List GetBlocksFromHashes(List hashes) { results[key.Item1] = this.dBreezeSerializer.Deserialize(blockRow); - this.logger.Debug("Block hash '{0}' loaded from the store.", key.Item1); + this.logger.LogDebug("Block hash '{0}' loaded from the store.", key.Item1); } else { results[key.Item1] = null; - this.logger.Debug("Block hash '{0}' not found in the store.", key.Item1); + this.logger.LogDebug("Block hash '{0}' not found in the store.", key.Item1); } } diff --git a/src/Stratis.Bitcoin.Features.BlockStore/StoreSettings.cs b/src/Stratis.Bitcoin.Features.BlockStore/StoreSettings.cs index 4faf502a86..103428c0cc 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/StoreSettings.cs +++ b/src/Stratis.Bitcoin.Features.BlockStore/StoreSettings.cs @@ -35,6 +35,9 @@ public class StoreSettings /// true to maintain a full addresses index. public bool AddressIndex { get; set; } + /// How many balance changes per address should be accumulated before compaction happens. Compaction compacts half of the balance changes. + public int AddressIndexerCompactionThreshold { get; set; } + /// Calculates minimum amount of blocks we need to keep during pruning. private int GetMinPruningAmount() { @@ -73,6 +76,7 @@ public StoreSettings(NodeSettings nodeSettings) this.ReIndex = config.GetOrDefault("reindex", false, this.logger); this.ReIndexChain = config.GetOrDefault("reindex-chain", false, this.logger); this.AddressIndex = config.GetOrDefault("addressindex", false, this.logger); + this.AddressIndexerCompactionThreshold = config.GetOrDefault("compactionthreshold", 8000, this.logger); if (this.PruningEnabled && this.TxIndex) throw new ConfigurationException("Prune mode is incompatible with -txindex"); @@ -90,6 +94,7 @@ public static void PrintHelp(Network network) builder.AppendLine($"-reindex=<0 or 1> Rebuild store with tx index from block data files on disk."); builder.AppendLine($"-reindex-chain=<0 or 1> Rebuild the coindb from block data files on disk."); builder.AppendLine($"-addressindex=<0 or 1> Enable to maintain a full address index."); + builder.AppendLine($"-compactionthreshold= Specify address indexer compaction threshold."); NodeSettings.Default(network).Logger.LogInformation(builder.ToString()); } diff --git a/src/Stratis.Bitcoin.Features.BlockStore/Stratis.Bitcoin.Features.BlockStore.csproj b/src/Stratis.Bitcoin.Features.BlockStore/Stratis.Bitcoin.Features.BlockStore.csproj index 3cd4063a2f..3901f059f1 100644 --- a/src/Stratis.Bitcoin.Features.BlockStore/Stratis.Bitcoin.Features.BlockStore.csproj +++ b/src/Stratis.Bitcoin.Features.BlockStore/Stratis.Bitcoin.Features.BlockStore.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.ColdStaking.Tests/ColdStakingControllerTest.cs b/src/Stratis.Bitcoin.Features.ColdStaking.Tests/ColdStakingControllerTest.cs index 3bb7994cc2..8709fa2ab7 100644 --- a/src/Stratis.Bitcoin.Features.ColdStaking.Tests/ColdStakingControllerTest.cs +++ b/src/Stratis.Bitcoin.Features.ColdStaking.Tests/ColdStakingControllerTest.cs @@ -810,6 +810,7 @@ public void GetColdStakingInfoOnlyConfirmAccountExistenceOnceCreated() /// Adds a spendable cold staking transaction to a wallet. /// /// Wallet to add the transaction to. + /// Set to true to output to the . /// The spendable transaction that was added to the wallet. private Transaction AddSpendableColdstakingTransactionToWallet(Wallet.Wallet wallet, bool script = false) { @@ -855,6 +856,7 @@ private Transaction AddSpendableColdstakingTransactionToWallet(Wallet.Wallet wal /// Adds a spendable cold staking transaction to a normal account, as oppose to dedicated special account. /// /// Wallet to add the transaction to. + /// Set to true to output to the . /// The spendable transaction that was added to the wallet. private Transaction AddSpendableColdstakingTransactionToNormalWallet(Wallet.Wallet wallet, bool script = false) { diff --git a/src/Stratis.Bitcoin.Features.ColdStaking.Tests/ColdStakingManagerTest.cs b/src/Stratis.Bitcoin.Features.ColdStaking.Tests/ColdStakingManagerTest.cs index 94e40b8d9d..5054535c2c 100644 --- a/src/Stratis.Bitcoin.Features.ColdStaking.Tests/ColdStakingManagerTest.cs +++ b/src/Stratis.Bitcoin.Features.ColdStaking.Tests/ColdStakingManagerTest.cs @@ -145,7 +145,7 @@ public void ProcessTransactionWithValidColdStakingSetupLoadsTransactionsIntoWall Assert.Single(spendingAddress.Transactions); Assert.Equal(transaction.GetHash(), spentAddressResult.Transactions.ElementAt(0).SpendingDetails.TransactionId); Assert.Equal(2, transaction.Outputs.Count); - Assert.True(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments.Any(p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value)); + Assert.Contains(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments, p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value); Assert.Single(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Transactions); TransactionData changeAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Transactions.ElementAt(0); diff --git a/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingManager.cs b/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingManager.cs index b82bbe1d67..567923967f 100644 --- a/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingManager.cs +++ b/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingManager.cs @@ -68,6 +68,7 @@ public class ColdStakingManager : WalletManager, IWalletManager /// A reader for extracting an address from a . /// The logger factory to use to create the custom logger. /// Provider of time functions. + /// The wallet repository. /// The broadcaster manager. public ColdStakingManager( Network network, @@ -194,6 +195,7 @@ internal HdAccount GetColdStakingAccount(Wallet.Wallet wallet, bool isColdWallet /// The name of the wallet where we wish to create the account. /// Indicates whether we need the cold wallet account (versus the hot wallet account). /// The wallet password which will be used to create the account. + /// The of the wallet account. Can be null for watch-only wallets. /// The new or existing cold staking account. internal HdAccount GetOrCreateColdStakingAccount(string walletName, bool isColdWalletAccount, string walletPassword, ExtPubKey extPubKey) { @@ -457,6 +459,14 @@ internal Money EstimateSetupTransactionFee(IWalletTransactionHandler walletTrans /// Builds an unsigned transaction template for a cold staking withdrawal transaction. /// This requires specialised logic due to the lack of a private key for the cold account. /// + /// The . + /// The receiving address. + /// The spending wallet name. + /// The spending account name. + /// The amount to spend. + /// The fee amount. + /// Set to true to subtract the from the . + /// See . public BuildOfflineSignResponse BuildOfflineColdStakingWithdrawalRequest(IWalletTransactionHandler walletTransactionHandler, string receivingAddress, string walletName, string accountName, Money amount, Money feeAmount, bool subtractFeeFromAmount) { @@ -552,6 +562,7 @@ private TransactionBuildContext GetOfflineWithdrawalBuildContext(string receivin /// The wallet password. /// The amount to remove from cold staking. /// The fee to pay for cold staking transaction withdrawal. + /// Set to true to subtract the from the . /// The for cold staking withdrawal. /// Thrown if the receiving address is in a cold staking account in this wallet. /// Thrown if the receiving address is invalid. diff --git a/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingScriptTemplate.cs b/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingScriptTemplate.cs index 8f8d16b328..62570ef92c 100644 --- a/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingScriptTemplate.cs +++ b/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingScriptTemplate.cs @@ -100,9 +100,11 @@ public Script GenerateScriptPubKey(KeyId hotPubKeyHash, KeyId coldPubKeyHash) return new Script( // Duplicates the last stack entry resulting in: // 0/1 . +#pragma warning disable SA1114 // Parameter list must follow declaration OP_DUP, - // Replaces the last stack entry with its hash resulting in: - // 0/1 . +#pragma warning restore SA1114 // Parameter list must follow declaration + // Replaces the last stack entry with its hash resulting in: + // 0/1 . OP_HASH160, // Rotates the top 3 stack entries resulting in: // 0/1. diff --git a/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingWalletService.cs b/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingWalletService.cs index 7d8c51cc88..e2ae5ea5a5 100644 --- a/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingWalletService.cs +++ b/src/Stratis.Bitcoin.Features.ColdStaking/ColdStakingWalletService.cs @@ -76,17 +76,17 @@ public override async Task OfflineSignRequest(Offli $"Could not resolve address from UTXO's scriptPubKey '{scriptPubKey.ToHex()}'."); } - var accounts = this.walletManager.GetAccounts(request.WalletName); - var addresses = accounts.SelectMany(hdAccount => hdAccount.GetCombinedAddresses()); + IEnumerable accounts = this.walletManager.GetAccounts(request.WalletName); + IEnumerable addresses = accounts.SelectMany(hdAccount => hdAccount.GetCombinedAddresses()); HdAddress hdAddress = addresses.FirstOrDefault(a => a.Address == address || a.Bech32Address == address); if (coldStakingWithdrawal && hdAddress == null) { var coldStakingManager = this.walletManager as ColdStakingManager; - var wallet = coldStakingManager.GetWallet(request.WalletName); - var coldAccount = coldStakingManager.GetColdStakingAccount(wallet, true); - var coldAccountAddresses = coldAccount.GetCombinedAddresses(); + Wallet.Wallet wallet = coldStakingManager.GetWallet(request.WalletName); + HdAccount coldAccount = coldStakingManager.GetColdStakingAccount(wallet, true); + IEnumerable coldAccountAddresses = coldAccount.GetCombinedAddresses(); hdAddress = coldAccountAddresses.FirstOrDefault(a => a.Address == address || a.Bech32Address == address); } diff --git a/src/Stratis.Bitcoin.Features.ColdStaking/Stratis.Bitcoin.Features.ColdStaking.csproj b/src/Stratis.Bitcoin.Features.ColdStaking/Stratis.Bitcoin.Features.ColdStaking.csproj index b2c27d0c19..423c446b78 100644 --- a/src/Stratis.Bitcoin.Features.ColdStaking/Stratis.Bitcoin.Features.ColdStaking.csproj +++ b/src/Stratis.Bitcoin.Features.ColdStaking/Stratis.Bitcoin.Features.ColdStaking.csproj @@ -7,7 +7,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.Consensus.Tests/CoinViews/CoinviewTests.cs b/src/Stratis.Bitcoin.Features.Consensus.Tests/CoinViews/CoinviewTests.cs index d42f0204e1..8f882403a4 100644 --- a/src/Stratis.Bitcoin.Features.Consensus.Tests/CoinViews/CoinviewTests.cs +++ b/src/Stratis.Bitcoin.Features.Consensus.Tests/CoinViews/CoinviewTests.cs @@ -176,7 +176,7 @@ private void SaveChanges(List unspent, int height) this.cachedCoinView.SaveChanges(unspent, new HashHeightPair(previous), new HashHeightPair(current)); } - private async Task ValidateCoinviewIntegrityAsync(List expectedAvailableOutPoints) + private Task ValidateCoinviewIntegrityAsync(List expectedAvailableOutPoints) { FetchCoinsResponse result = this.cachedCoinView.FetchCoins(expectedAvailableOutPoints.ToArray()); @@ -197,6 +197,8 @@ private async Task ValidateCoinviewIntegrityAsync(List expectedAvailab { Assert.Contains(referenceOutPoint, availableOutPoints); } + + return Task.CompletedTask; } private void Shuffle(IList list) diff --git a/src/Stratis.Bitcoin.Features.Consensus.Tests/Rules/CommonRules/PosCoinViewRuleTest.cs b/src/Stratis.Bitcoin.Features.Consensus.Tests/Rules/CommonRules/PosCoinViewRuleTest.cs index 4948d6612b..c01c9e6246 100644 --- a/src/Stratis.Bitcoin.Features.Consensus.Tests/Rules/CommonRules/PosCoinViewRuleTest.cs +++ b/src/Stratis.Bitcoin.Features.Consensus.Tests/Rules/CommonRules/PosCoinViewRuleTest.cs @@ -121,6 +121,7 @@ private async Task CreateConsensusManagerAsync(Dictionary /// Tests whether an error is raised when a miner attempts to stake an output which he can't spend with his private key. /// + /// The asynchronous task. /// /// Create a "previous transaction" with 2 outputs. The first output is sent to miner 2 and the second output is sent to miner 1. /// Now miner 2 creates a proof of stake block with coinstake transaction which will have two inputs corresponding to both the diff --git a/src/Stratis.Bitcoin.Features.Consensus.Tests/Rules/CommonRules/StraxCoinViewRuleTests.cs b/src/Stratis.Bitcoin.Features.Consensus.Tests/Rules/CommonRules/StraxCoinViewRuleTests.cs index 669d820221..3ca882d4aa 100644 --- a/src/Stratis.Bitcoin.Features.Consensus.Tests/Rules/CommonRules/StraxCoinViewRuleTests.cs +++ b/src/Stratis.Bitcoin.Features.Consensus.Tests/Rules/CommonRules/StraxCoinViewRuleTests.cs @@ -121,6 +121,7 @@ private async Task CreateConsensusManagerAsync(Dictionary /// Tests whether an error is raised when a miner attempts to stake an output which he can't spend with his private key. /// + /// The asynchronous task. /// /// Create a "previous transaction" with 2 outputs. The first output is sent to miner 2 and the second output is sent to miner 1. /// Now miner 2 creates a proof of stake block with coinstake transaction which will have two inputs corresponding to both the diff --git a/src/Stratis.Bitcoin.Features.Consensus.Tests/TestChainFactory.cs b/src/Stratis.Bitcoin.Features.Consensus.Tests/TestChainFactory.cs index 59d4265931..a63f00becf 100644 --- a/src/Stratis.Bitcoin.Features.Consensus.Tests/TestChainFactory.cs +++ b/src/Stratis.Bitcoin.Features.Consensus.Tests/TestChainFactory.cs @@ -97,6 +97,10 @@ internal class TestChainFactory /// /// Creates test chain with a consensus loop. /// + /// The network context. + /// The data directory. + /// A mock . + /// The asynchronous task returning a . public static async Task CreateAsync(Network network, string dataDir, Mock mockPeerAddressManager = null) { var testChainContext = new TestChainContext() { Network = network }; @@ -180,6 +184,11 @@ public static async Task> MineBlocksAsync(TestChainContext testChain /// /// Mine new blocks in to the consensus database and the chain. /// + /// See . + /// The number of blocks to mine. + /// Script that explains what conditions must be met to claim ownership of the mined coins. + /// Indicates whether mutated blocks should be built. + /// A list of mined entries. private static async Task> MineBlocksAsync(TestChainContext testChainContext, int count, Script receiver, bool mutateLastBlock) { var blockPolicyEstimator = new BlockPolicyEstimator(new MempoolSettings(testChainContext.NodeSettings), testChainContext.LoggerFactory, testChainContext.NodeSettings); @@ -209,7 +218,7 @@ private static async Task MineBlockAsync(TestChainContext testCha TryFindNonceForProofOfWork(testChainContext, newBlock); - if (!getMutatedBlock) await ValidateBlock(testChainContext, newBlock); + if (!getMutatedBlock) await ValidateBlockAsync(testChainContext, newBlock); else CheckBlockIsMutated(newBlock); return newBlock; @@ -219,7 +228,7 @@ private static BlockTemplate CreateBlockTemplate(TestChainContext testChainConte TxMempool mempool, MempoolSchedulerLock mempoolLock) { PowBlockDefinition blockAssembler = new PowBlockDefinition(testChainContext.Consensus, - testChainContext.DateTimeProvider, testChainContext.LoggerFactory as LoggerFactory, mempool, mempoolLock, + testChainContext.DateTimeProvider, testChainContext.LoggerFactory as ILoggerFactory, mempool, mempoolLock, new MinerSettings(testChainContext.NodeSettings), testChainContext.Network, testChainContext.ConsensusRules, new NodeDeployments(testChainContext.Network, testChainContext.ChainIndexer)); BlockTemplate newBlock = blockAssembler.Build(testChainContext.ChainIndexer.Tip, scriptPubKey); @@ -260,7 +269,7 @@ private static void CheckBlockIsMutated(BlockTemplate newBlock) isMutated.Should().Be(true); } - private static async Task ValidateBlock(TestChainContext testChainContext, BlockTemplate newBlock) + private static async Task ValidateBlockAsync(TestChainContext testChainContext, BlockTemplate newBlock) { var res = await testChainContext.Consensus.BlockMinedAsync(newBlock.Block); Assert.NotNull(res); diff --git a/src/Stratis.Bitcoin.Features.Consensus/CoinViews/CachedCoinView.cs b/src/Stratis.Bitcoin.Features.Consensus/CoinViews/CachedCoinView.cs index e9b6a95edc..dff4762122 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/CoinViews/CachedCoinView.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/CoinViews/CachedCoinView.cs @@ -94,8 +94,10 @@ public long GetScriptSize /// All access to this list has to be protected by . private readonly Dictionary cachedRewindData; +#pragma warning disable SA1648 // inheritdoc must be used with inheriting class /// public ICoindb ICoindb => this.coindb; +#pragma warning restore SA1648 // inheritdoc must be used with inheriting class /// Storage of POS block information. private readonly StakeChainStore stakeChainStore; diff --git a/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/ICoindb.cs b/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/ICoindb.cs index ed15db0d4f..c596f9c8db 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/ICoindb.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/ICoindb.cs @@ -61,6 +61,7 @@ public interface ICoindb /// Gets the rewind data by block height. /// /// The height of the block. + /// See . RewindData GetRewindData(int height); /// Gets the minimum rewind height. diff --git a/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/LeveldbCoindb.cs b/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/LeveldbCoindb.cs index 17bc6a0a3f..0011906fe8 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/LeveldbCoindb.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/LeveldbCoindb.cs @@ -3,9 +3,10 @@ using System.Linq; using System.Text; using LevelDB; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Utilities; namespace Stratis.Bitcoin.Features.Consensus.CoinViews @@ -85,7 +86,7 @@ public void Initialize(ChainedHeader chainTip) byte[] row2 = (current.Height > 1) ? this.leveldb.Get(new byte[] { rewindTable }.Concat(BitConverter.GetBytes(current.Height - 1)).ToArray()) : new byte[] { }; if (row2 != null) { - this.logger.Info("Fixing the coin db."); + this.logger.LogInformation("Fixing the coin db."); var rows = new Dictionary(); @@ -133,12 +134,12 @@ public void Initialize(ChainedHeader chainTip) if (this.GetTipHash() == null) this.SetBlockHash(new HashHeightPair(genesis.GetHash(), 0)); - this.logger.Info("Coinview initialized with tip '{0}'.", this.persistedCoinviewTip); + this.logger.LogInformation("Coinview initialized with tip '{0}'.", this.persistedCoinviewTip); } private void EnsureCoinDatabaseIntegrity(ChainedHeader chainTip) { - this.logger.Info("Checking coin database integrity..."); + this.logger.LogInformation("Checking coin database integrity..."); var heightToCheck = chainTip.Height; @@ -157,13 +158,13 @@ private void EnsureCoinDatabaseIntegrity(ChainedHeader chainTip) { for (int height = heightToCheck - 1; height > chainTip.Height; height--) { - this.logger.Info($"Fixing coin database, deleting rewind data at height {height} above tip '{chainTip}'."); + this.logger.LogInformation($"Fixing coin database, deleting rewind data at height {height} above tip '{chainTip}'."); RewindInternal(batch, height); } } - this.logger.Info("Coin database integrity good."); + this.logger.LogInformation("Coin database integrity good."); } private void SetBlockHash(HashHeightPair nextBlockHash) @@ -200,7 +201,7 @@ public FetchCoinsResponse FetchCoins(OutPoint[] utxos) byte[] row = this.leveldb.Get(new byte[] { coinsTable }.Concat(outPoint.ToBytes()).ToArray()); Coins outputs = row != null ? this.dBreezeSerializer.Deserialize(row) : null; - this.logger.Debug("Outputs for '{0}' were {1}.", outPoint, outputs == null ? "NOT loaded" : "loaded"); + this.logger.LogDebug("Outputs for '{0}' were {1}.", outPoint, outputs == null ? "NOT loaded" : "loaded"); res.UnspentOutputs.Add(outPoint, new UnspentOutput(outPoint, outputs)); } @@ -220,7 +221,7 @@ public void SaveChanges(IList unspentOutputs, HashHeightPair oldB HashHeightPair current = this.GetTipHash(); if (current != oldBlockHash) { - this.logger.Trace("(-)[BLOCKHASH_MISMATCH]"); + this.logger.LogTrace("(-)[BLOCKHASH_MISMATCH]"); throw new InvalidOperationException("Invalid oldBlockHash"); } @@ -231,7 +232,7 @@ public void SaveChanges(IList unspentOutputs, HashHeightPair oldB { if (coin.Coins == null) { - this.logger.Debug("Outputs of transaction ID '{0}' are prunable and will be removed from the database.", coin.OutPoint); + this.logger.LogDebug("Outputs of transaction ID '{0}' are prunable and will be removed from the database.", coin.OutPoint); batch.Delete(new byte[] { coinsTable }.Concat(coin.OutPoint.ToBytes()).ToArray()); } else @@ -245,7 +246,7 @@ public void SaveChanges(IList unspentOutputs, HashHeightPair oldB for (int i = 0; i < toInsert.Count; i++) { var coin = toInsert[i]; - this.logger.Debug("Outputs of transaction ID '{0}' are NOT PRUNABLE and will be inserted into the database. {1}/{2}.", coin.OutPoint, i, toInsert.Count); + this.logger.LogDebug("Outputs of transaction ID '{0}' are NOT PRUNABLE and will be inserted into the database. {1}/{2}.", coin.OutPoint, i, toInsert.Count); batch.Put(new byte[] { coinsTable }.Concat(coin.OutPoint.ToBytes()).ToArray(), this.dBreezeSerializer.Serialize(coin.Coins)); } @@ -256,7 +257,7 @@ public void SaveChanges(IList unspentOutputs, HashHeightPair oldB { var nextRewindIndex = rewindData.PreviousBlockHash.Height + 1; - this.logger.Debug("Rewind state #{0} created.", nextRewindIndex); + this.logger.LogDebug("Rewind state #{0} created.", nextRewindIndex); batch.Put(new byte[] { rewindTable }.Concat(BitConverter.GetBytes(nextRewindIndex).Reverse()).ToArray(), this.dBreezeSerializer.Serialize(rewindData)); } @@ -314,13 +315,13 @@ private HashHeightPair RewindInternal(WriteBatch batch, int height) foreach (OutPoint outPoint in rewindData.OutputsToRemove) { - this.logger.Debug("Outputs of outpoint '{0}' will be removed.", outPoint); + this.logger.LogDebug("Outputs of outpoint '{0}' will be removed.", outPoint); batch.Delete(new byte[] { coinsTable }.Concat(outPoint.ToBytes()).ToArray()); } foreach (RewindDataOutput rewindDataOutput in rewindData.OutputsToRestore) { - this.logger.Debug("Outputs of outpoint '{0}' will be restored.", rewindDataOutput.OutPoint); + this.logger.LogDebug("Outputs of outpoint '{0}' will be restored.", rewindDataOutput.OutPoint); batch.Put(new byte[] { coinsTable }.Concat(rewindDataOutput.OutPoint.ToBytes()).ToArray(), this.dBreezeSerializer.Serialize(rewindDataOutput.Coins)); } @@ -366,7 +367,7 @@ public void GetStake(IEnumerable blocklist) { foreach (StakeItem blockStake in blocklist) { - this.logger.Trace("Loading POS block hash '{0}' from the database.", blockStake.BlockId); + this.logger.LogTrace("Loading POS block hash '{0}' from the database.", blockStake.BlockId); byte[] stakeRow = this.leveldb.Get(new byte[] { stakeTable }.Concat(blockStake.BlockId.ToBytes(false)).ToArray()); if (stakeRow != null) diff --git a/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/RocksDbCoindb.cs b/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/RocksDbCoindb.cs index bd849584c4..4f715b61fb 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/RocksDbCoindb.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/CoinViews/Coindb/RocksDbCoindb.cs @@ -2,10 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using RocksDbSharp; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Utilities; namespace Stratis.Bitcoin.Features.Consensus.CoinViews @@ -70,7 +71,7 @@ public void Initialize(ChainedHeader chainTip) byte[] row2 = (current.Height > 1) ? this.rocksDb.Get(new byte[] { rewindTable }.Concat(BitConverter.GetBytes(current.Height - 1)).ToArray()) : new byte[] { }; if (row2 != null) { - this.logger.Info("Fixing the coin db."); + this.logger.LogInformation("Fixing the coin db."); var rows = new Dictionary(); @@ -118,12 +119,12 @@ public void Initialize(ChainedHeader chainTip) if (this.GetTipHash() == null) this.SetBlockHash(new HashHeightPair(genesis.GetHash(), 0)); - this.logger.Info("Coinview initialized with tip '{0}'.", this.persistedCoinviewTip); + this.logger.LogInformation("Coinview initialized with tip '{0}'.", this.persistedCoinviewTip); } private void EnsureCoinDatabaseIntegrity(ChainedHeader chainTip) { - this.logger.Info("Checking coin database integrity..."); + this.logger.LogInformation("Checking coin database integrity..."); var heightToCheck = chainTip.Height; @@ -142,12 +143,12 @@ private void EnsureCoinDatabaseIntegrity(ChainedHeader chainTip) { for (int height = heightToCheck - 1; height > chainTip.Height; height--) { - this.logger.Info($"Fixing coin database, deleting rewind data at height {height} above tip '{chainTip}'."); + this.logger.LogInformation($"Fixing coin database, deleting rewind data at height {height} above tip '{chainTip}'."); RewindInternal(batch, height); } } - this.logger.Info("Coin database integrity good."); + this.logger.LogInformation("Coin database integrity good."); } private void SetBlockHash(HashHeightPair nextBlockHash) @@ -184,7 +185,7 @@ public FetchCoinsResponse FetchCoins(OutPoint[] utxos) byte[] row = this.rocksDb.Get(new byte[] { coinsTable }.Concat(outPoint.ToBytes()).ToArray()); Coins outputs = row != null ? this.dBreezeSerializer.Deserialize(row) : null; - this.logger.Debug("Outputs for '{0}' were {1}.", outPoint, outputs == null ? "NOT loaded" : "loaded"); + this.logger.LogDebug("Outputs for '{0}' were {1}.", outPoint, outputs == null ? "NOT loaded" : "loaded"); res.UnspentOutputs.Add(outPoint, new UnspentOutput(outPoint, outputs)); } @@ -204,7 +205,7 @@ public void SaveChanges(IList unspentOutputs, HashHeightPair oldB HashHeightPair current = this.GetTipHash(); if (current != oldBlockHash) { - this.logger.Error("(-)[BLOCKHASH_MISMATCH]"); + this.logger.LogError("(-)[BLOCKHASH_MISMATCH]"); throw new InvalidOperationException("Invalid oldBlockHash"); } @@ -215,7 +216,7 @@ public void SaveChanges(IList unspentOutputs, HashHeightPair oldB { if (coin.Coins == null) { - this.logger.Debug("Outputs of transaction ID '{0}' are prunable and will be removed from the database.", coin.OutPoint); + this.logger.LogDebug("Outputs of transaction ID '{0}' are prunable and will be removed from the database.", coin.OutPoint); batch.Delete(new byte[] { coinsTable }.Concat(coin.OutPoint.ToBytes()).ToArray()); } else @@ -229,7 +230,7 @@ public void SaveChanges(IList unspentOutputs, HashHeightPair oldB for (int i = 0; i < toInsert.Count; i++) { var coin = toInsert[i]; - this.logger.Debug("Outputs of transaction ID '{0}' are NOT PRUNABLE and will be inserted into the database. {1}/{2}.", coin.OutPoint, i, toInsert.Count); + this.logger.LogDebug("Outputs of transaction ID '{0}' are NOT PRUNABLE and will be inserted into the database. {1}/{2}.", coin.OutPoint, i, toInsert.Count); batch.Put(new byte[] { coinsTable }.Concat(coin.OutPoint.ToBytes()).ToArray(), this.dBreezeSerializer.Serialize(coin.Coins)); } @@ -240,7 +241,7 @@ public void SaveChanges(IList unspentOutputs, HashHeightPair oldB { var nextRewindIndex = rewindData.PreviousBlockHash.Height + 1; - this.logger.Debug("Rewind state #{0} created.", nextRewindIndex); + this.logger.LogDebug("Rewind state #{0} created.", nextRewindIndex); batch.Put(new byte[] { rewindTable }.Concat(BitConverter.GetBytes(nextRewindIndex).Reverse()).ToArray(), this.dBreezeSerializer.Serialize(rewindData)); } @@ -299,13 +300,13 @@ private HashHeightPair RewindInternal(WriteBatch batch, int height) foreach (OutPoint outPoint in rewindData.OutputsToRemove) { - this.logger.Debug("Outputs of outpoint '{0}' will be removed.", outPoint); + this.logger.LogDebug("Outputs of outpoint '{0}' will be removed.", outPoint); batch.Delete(new byte[] { coinsTable }.Concat(outPoint.ToBytes()).ToArray()); } foreach (RewindDataOutput rewindDataOutput in rewindData.OutputsToRestore) { - this.logger.Debug("Outputs of outpoint '{0}' will be restored.", rewindDataOutput.OutPoint); + this.logger.LogDebug("Outputs of outpoint '{0}' will be restored.", rewindDataOutput.OutPoint); batch.Put(new byte[] { coinsTable }.Concat(rewindDataOutput.OutPoint.ToBytes()).ToArray(), this.dBreezeSerializer.Serialize(rewindDataOutput.Coins)); } @@ -351,7 +352,7 @@ public void GetStake(IEnumerable blocklist) { foreach (StakeItem blockStake in blocklist) { - this.logger.Debug("Loading POS block hash '{0}' from the database.", blockStake.BlockId); + this.logger.LogDebug("Loading POS block hash '{0}' from the database.", blockStake.BlockId); byte[] stakeRow = this.rocksDb.Get(new byte[] { stakeTable }.Concat(blockStake.BlockId.ToBytes(false)).ToArray()); if (stakeRow != null) diff --git a/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs b/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs index c3ab53856f..3d8e54559f 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs @@ -156,5 +156,33 @@ public IActionResult GetBlockHashAPI([FromQuery] int height) return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } + + /// + /// Returns the current tip of consensus. + /// + /// Json formatted hash and height of the block at the consensus tip. Returns formatted error if fails. + /// Returns the tip hash and height. + /// Unexpected exception occurred + [Route("api/[controller]/tip")] + [HttpGet] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public IActionResult ConsensusTip() + { + try + { + if (this.ConsensusManager.Tip == null) + return this.Json("Consensus is not initialized."); + + var tip = this.ConsensusManager.Tip; + + return this.Json(new { TipHash = tip.HashBlock, TipHeight = tip.Height }); + } + catch (Exception e) + { + this.logger.LogError("Exception occurred: {0}", e.ToString()); + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); + } + } } } diff --git a/src/Stratis.Bitcoin.Features.Consensus/ProvenBlockHeaders/RocksDbProvenBlockHeaderRepository.cs b/src/Stratis.Bitcoin.Features.Consensus/ProvenBlockHeaders/RocksDbProvenBlockHeaderRepository.cs index cab244d8fe..52803ffcc6 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/ProvenBlockHeaders/RocksDbProvenBlockHeaderRepository.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/ProvenBlockHeaders/RocksDbProvenBlockHeaderRepository.cs @@ -3,10 +3,11 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using RocksDbSharp; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Interfaces; using Stratis.Bitcoin.Persistence; using Stratis.Bitcoin.Utilities; @@ -116,7 +117,7 @@ public Task PutAsync(SortedDictionary headers, HashHeigh Task task = Task.Run(() => { - this.logger.Debug("({0}.Count():{1})", nameof(headers), headers.Count()); + this.logger.LogDebug("({0}.Count():{1})", nameof(headers), headers.Count()); this.InsertHeaders(headers); diff --git a/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/CoinviewRule.cs b/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/CoinviewRule.cs index 9bbd88f2ba..05c8617d84 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/CoinviewRule.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/CoinviewRule.cs @@ -148,6 +148,9 @@ protected virtual void AllowSpend(TxOut prevOut, Transaction spendingTx) protected abstract Money GetTransactionFee(UnspentOutputSet view, Transaction tx); /// Checks if transaction if final. + /// See . + /// See . + /// true if the transaction is final and false otherwise. protected virtual bool IsTxFinal(Transaction transaction, RuleContext context) { return transaction.IsFinal(context.ValidationContext.ChainedHeaderToValidate); @@ -197,6 +200,8 @@ protected void UpdateUTXOSet(RuleContext context, Transaction transaction) /// Refer to . /// /// + /// See . + /// See . public abstract void UpdateCoinView(RuleContext context, Transaction transaction); /// @@ -230,11 +235,12 @@ public void CheckCoinbaseMaturity(UnspentOutput coins, int spendHeight) } /// - /// Network specific logic that checks the maturity of UTXO sets. - /// - /// Refer to . - /// + /// Network-specific logic that checks the maturity of UTXOs. /// + /// UTXOs to check the maturity of. + /// Height at which coins are attempted to be spent. + /// Thrown if transaction tries to spend coinbase coins that are not mature. + /// Thrown if transaction tries to spend coinstake coins that are not mature. public abstract void CheckMaturity(UnspentOutput coins, int spendHeight); /// diff --git a/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/HeaderVersionRule.cs b/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/HeaderVersionRule.cs index e8a63edec3..4917caa455 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/HeaderVersionRule.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/HeaderVersionRule.cs @@ -15,6 +15,7 @@ public abstract class HeaderVersionRule : HeaderValidationConsensusRule /// This method is currently used during block creation only. Different nodes may not implement /// BIP9, or may disagree about what the current valid set of deployments are. It is therefore not strictly /// possible to validate a block version number in anything more than general terms. + /// The block version. public int ComputeBlockVersion(ChainedHeader prevChainedHeader) { uint version = ThresholdConditionCache.VersionbitsTopBits; diff --git a/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/SetActivationDeploymentsPartialValidationRule.cs b/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/SetActivationDeploymentsPartialValidationRule.cs index 8d5f9640c5..832a00b189 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/SetActivationDeploymentsPartialValidationRule.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/SetActivationDeploymentsPartialValidationRule.cs @@ -17,7 +17,8 @@ public override Task RunAsync(RuleContext context) } } - //TODO merge those 2 classes into 1 after activation + // TODO: Merge these 2 classes into one after activation. + /// Set the property that defines what deployments have been activated. public class SetActivationDeploymentsFullValidationRule : FullValidationConsensusRule { diff --git a/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/WitnessCommitmentsRule.cs b/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/WitnessCommitmentsRule.cs index 1294634b68..d7c0f59857 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/WitnessCommitmentsRule.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/Rules/CommonRules/WitnessCommitmentsRule.cs @@ -27,6 +27,9 @@ public override Task RunAsync(RuleContext context) /// /// Validation of the witness commitment if its found. /// + /// The including the block to validate. + /// The nwetwork to validate the commitment for. + /// The asynchronous task. public Task ValidateWitnessCommitment(RuleContext context, Network network) { if (context.SkipValidation) @@ -114,6 +117,7 @@ private bool EqualsArray(byte[] a, byte[] b, int length) /// /// Gets commitment in the last coinbase transaction output with SegWit flag. /// + /// The network context. /// Block which coinbase transaction's outputs will be checked for SegWit flags. /// /// null if no SegWit flags were found. @@ -154,6 +158,8 @@ public static Script GetWitnessCommitment(Network network, Block block) /// /// Clear all witness commitments from the block. /// + /// The network context. + /// The block to clear commitments from. public static void ClearWitnessCommitment(Network network, Block block) { if (network.Consensus.IsProofOfStake && network.Consensus.PosEmptyCoinbase) @@ -185,6 +191,8 @@ public static void ClearWitnessCommitment(Network network, Block block) /// /// Create a witness commitment based from the given block. /// + /// The network context. + /// The block to create commitments in. public static void CreateWitnessCommitment(Network network, Block block) { var wtxidCoinbase = new byte[32]; // The wtxid of the coinbase transaction is defined as to be 0x0000....0000. diff --git a/src/Stratis.Bitcoin.Features.Consensus/StakeValidator.cs b/src/Stratis.Bitcoin.Features.Consensus/StakeValidator.cs index 5ac3672441..ea196cd9ab 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/StakeValidator.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/StakeValidator.cs @@ -49,7 +49,8 @@ public class StakeValidator : IStakeValidator /// When checking the POS block signature this determines the maximum push data (public key) size following the OP_RETURN in the nonspendable output. private const int MaxPushDataSize = 40; - // TODO: move this to IConsensus + // TODO: Move this to IConsensus. + /// Time interval in minutes that is used in the retarget calculation. private const uint RetargetIntervalMinutes = 16; @@ -65,6 +66,9 @@ public class StakeValidator : IStakeValidator /// Consensus' view of UTXO set. private readonly ICoinView coinView; + +#pragma warning disable SA1648 // inheritdoc must be used with inheriting class + /// private readonly Network network; @@ -83,6 +87,8 @@ public StakeValidator(Network network, IStakeChain stakeChain, ChainIndexer chai this.network = network; } +#pragma warning restore SA1648 // inheritdoc must be used with inheriting class + /// public ChainedHeader GetLastPowPosChainedBlock(IStakeChain stakeChain, ChainedHeader startChainedHeader, bool proofOfStake) { diff --git a/src/Stratis.Bitcoin.Features.Consensus/Stratis.Bitcoin.Features.Consensus.csproj b/src/Stratis.Bitcoin.Features.Consensus/Stratis.Bitcoin.Features.Consensus.csproj index 67c52f6438..ed474bb294 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/Stratis.Bitcoin.Features.Consensus.csproj +++ b/src/Stratis.Bitcoin.Features.Consensus/Stratis.Bitcoin.Features.Consensus.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.Dns/DnsFeature.cs b/src/Stratis.Bitcoin.Features.Dns/DnsFeature.cs index 0a4efff82c..e34509213c 100644 --- a/src/Stratis.Bitcoin.Features.Dns/DnsFeature.cs +++ b/src/Stratis.Bitcoin.Features.Dns/DnsFeature.cs @@ -132,6 +132,7 @@ public DnsFeature(IDnsServer dnsServer, IWhitelistManager whitelistManager, ILog /// /// Initializes the DNS feature. /// + /// The asynchronous task. public override Task InitializeAsync() { // Create long running task for DNS service. diff --git a/src/Stratis.Bitcoin.Features.Dns/DnsSeedServer.cs b/src/Stratis.Bitcoin.Features.Dns/DnsSeedServer.cs index 3954a58a60..a94ac41593 100644 --- a/src/Stratis.Bitcoin.Features.Dns/DnsSeedServer.cs +++ b/src/Stratis.Bitcoin.Features.Dns/DnsSeedServer.cs @@ -119,8 +119,10 @@ public class DnsSeedServer : IDnsServer /// The UDP client to use to receive DNS requests and send DNS responses. /// The initial DNS masterfile. /// The async loop factory. + /// The . /// The logger factory. /// The provider. + /// The . /// The data folders of the system. public DnsSeedServer(IUdpClient client, IMasterFile masterFile, IAsyncProvider asyncProvider, INodeLifetime nodeLifetime, ILoggerFactory loggerFactory, IDateTimeProvider dateTimeProvider, DnsSettings dnsSettings, DataFolder dataFolders) { @@ -362,6 +364,7 @@ private IResponse Resolve(Request request) /// Handles a DNS request received by the UDP client. /// /// The DNS request received from the UDP client. + /// The asynchronous task. private async Task HandleRequestAsync(Tuple udpRequest) { Request request = null; @@ -430,7 +433,7 @@ private async Task HandleRequestAsync(Tuple udpRequest) /// /// Seeds the given masterfile with the SOA and NS DNS records with the DNS specific settings. /// - /// + /// The . private void SeedMasterFile(IMasterFile masterFile) { this.logger.LogInformation("Seeding DNS masterfile with SOA and NS resource records: Host = {0}, Nameserver = {1}, Mailbox = {2}", this.dnsSettings.DnsHostName, this.dnsSettings.DnsNameServer, this.dnsSettings.DnsMailBox); @@ -441,7 +444,7 @@ private void SeedMasterFile(IMasterFile masterFile) /// /// Gets the peer count of IP v4 and v6 addresses in the DNS masterfile. /// - /// + /// The peer count. private int GetPeerCount() { int count = this.MasterFile.Get(new Question(new Domain(this.dnsSettings.DnsHostName), RecordType.A)).Count; diff --git a/src/Stratis.Bitcoin.Features.Dns/Stratis.Bitcoin.Features.Dns.csproj b/src/Stratis.Bitcoin.Features.Dns/Stratis.Bitcoin.Features.Dns.csproj index 746bf4f8bc..5ad3c3077f 100644 --- a/src/Stratis.Bitcoin.Features.Dns/Stratis.Bitcoin.Features.Dns.csproj +++ b/src/Stratis.Bitcoin.Features.Dns/Stratis.Bitcoin.Features.Dns.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/CoinGeckoClient.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/CoinGeckoClient.cs index 9f1ff07c08..788cf08539 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/CoinGeckoClient.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/CoinGeckoClient.cs @@ -6,8 +6,14 @@ namespace Stratis.Bitcoin.Features.ExternalApi.ApiClients { + /// + /// Retrieves price data from Coin Gecko. + /// public class CoinGeckoClient : IDisposable { + /// + /// The user-agent used by this class when retrieving price data. + /// public const string DummyUserAgent = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"; private readonly ExternalApiSettings externalApiSettings; @@ -16,6 +22,10 @@ public class CoinGeckoClient : IDisposable private decimal stratisPrice = -1; private decimal ethereumPrice = -1; + /// + /// Class constructor. + /// + /// The . public CoinGeckoClient(ExternalApiSettings externalApiSettings) { this.externalApiSettings = externalApiSettings; @@ -23,16 +33,28 @@ public CoinGeckoClient(ExternalApiSettings externalApiSettings) this.client = new HttpClient(); } + /// + /// Gets the most recently retrieved Stratis price. + /// + /// The Stratis price. public decimal GetStratisPrice() { return this.stratisPrice; } + /// + /// Gets the most recently retrieved Ethereum price. + /// + /// The Ethereum price. public decimal GetEthereumPrice() { return this.ethereumPrice; } + /// + /// Retrieves price data for Stratis and Ethereum from Coin Gecko. + /// + /// The . public async Task PriceDataRetrievalAsync() { var targetUri = new Uri(this.externalApiSettings.PriceUrl); @@ -55,6 +77,9 @@ public async Task PriceDataRetrievalAsync() return response; } + /// + /// Disposes instances of this class. + /// public void Dispose() { this.client?.Dispose(); diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/EtherscanClient.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/EtherscanClient.cs index c9f52b5a0d..3213b10410 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/EtherscanClient.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/ApiClients/EtherscanClient.cs @@ -6,6 +6,9 @@ namespace Stratis.Bitcoin.Features.ExternalApi.ApiClients { + /// + /// Retrieves gas prices from Etherscan. + /// public class EtherscanClient : IDisposable { private readonly ExternalApiSettings externalApiSettings; @@ -18,6 +21,10 @@ public class EtherscanClient : IDisposable private bool sampled = false; private int samplePointer = 0; + /// + /// The class constructor. + /// + /// The . public EtherscanClient(ExternalApiSettings externalApiSettings) { this.externalApiSettings = externalApiSettings; @@ -58,7 +65,11 @@ public int GetGasPrice() return (int)Math.Ceiling((((totalFast * fastWeighting) + (totalProposed * proposedWeighting) + (totalSafe * safeWeighting)) / this.fastSamples.Length)); } - public async Task GasOracle(bool recordSamples) + /// + /// Samples the gas price from Etherscan. + /// + /// The . + public async Task GasOracleAsync() { string content = await this.client.GetStringAsync(this.externalApiSettings.EtherscanGasOracleUrl).ConfigureAwait(false); @@ -85,23 +96,23 @@ public async Task GasOracle(bool recordSamples) return response; } - if (recordSamples) - { - this.fastSamples[this.samplePointer] = response.result.FastGasPrice; - this.proposeSamples[this.samplePointer] = response.result.ProposeGasPrice; - this.safeSamples[this.samplePointer] = response.result.SafeGasPrice; + this.fastSamples[this.samplePointer] = response.result.FastGasPrice; + this.proposeSamples[this.samplePointer] = response.result.ProposeGasPrice; + this.safeSamples[this.samplePointer] = response.result.SafeGasPrice; - this.samplePointer++; + this.samplePointer++; - if (this.samplePointer >= this.fastSamples.Length) - { - this.samplePointer = 0; - } + if (this.samplePointer >= this.fastSamples.Length) + { + this.samplePointer = 0; } return response; } + /// + /// Disposes instances of this class. + /// public void Dispose() { this.client?.Dispose(); diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/Controllers/ExternalApiController.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/Controllers/ExternalApiController.cs index 7682b6edeb..8c1d776a9c 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/Controllers/ExternalApiController.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/Controllers/ExternalApiController.cs @@ -1,32 +1,63 @@ using System; using System.Net; using Microsoft.AspNetCore.Mvc; -using NLog; +using Microsoft.Extensions.Logging; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Utilities.JsonErrors; namespace Stratis.Features.ExternalApi.Controllers { + /// + /// Controller for the External Api. + /// [ApiVersion("1")] [Route("api/[controller]")] public class ExternalApiController : Controller { + /// + /// Name of api method which estimates the total gas a conversion will require. + /// public const string EstimateConversionGasEndpoint = "estimateconversiongas"; + + /// + /// Name of api method which estimates the conversion fee (in STRAX). + /// public const string EstimateConversionFeeEndpoint = "estimateconversionfee"; + + /// + /// Name of api method which estimates a recommended gas price based on historical measured samples. + /// public const string GasPriceEndpoint = "gasprice"; + + /// + /// Name of api method which returns the most recently retrieved Stratis price. + /// public const string StratisPriceEndpoint = "stratisprice"; + + /// + /// Name of api method which returns the most recently retrieved Ethereum price. + /// public const string EthereumPriceEndpoint = "ethereumprice"; private readonly IExternalApiPoller externalApiPoller; private readonly ILogger logger; + /// + /// The class constructor. + /// + /// The . public ExternalApiController(IExternalApiPoller externalApiPoller) { this.externalApiPoller = externalApiPoller; this.logger = LogManager.GetCurrentClassLogger(); } + /// + /// Estimates the total gas a conversion will require. + /// + /// The total gas a conversion will require or an . [Route(EstimateConversionGasEndpoint)] [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] @@ -40,11 +71,15 @@ public IActionResult EstimateConversionGas() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } + /// + /// Estimates the conversion fee (in STRAX). + /// + /// The conversion fee or and . [Route(EstimateConversionFeeEndpoint)] [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] @@ -58,11 +93,15 @@ public IActionResult EstimateConversionFee() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } + /// + /// Estimates a recommended gas price based on historical samples. + /// + /// The recommended gas price or an . [Route(GasPriceEndpoint)] [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] @@ -76,11 +115,15 @@ public IActionResult GasPrice() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } + /// + /// Returns the most recently retrieved Stratis price. + /// + /// The most recently retrieved Stratis price or an . [Route(StratisPriceEndpoint)] [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] @@ -94,11 +137,15 @@ public IActionResult StratisPrice() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } + /// + /// Returns the most recently retrieved Ethereum price. + /// + /// The most recently retrieved Ethereum price or an . [Route(EthereumPriceEndpoint)] [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] @@ -112,7 +159,7 @@ public IActionResult EthereumPrice() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiClient.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiClient.cs index 0ed60eb4eb..11691ebe43 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiClient.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiClient.cs @@ -13,12 +13,22 @@ namespace Stratis.Bitcoin.Features.ExternalApi /// public interface IExternalApiClient { + /// + /// Estimates the conversion fee (in STRAX). + /// + /// . + /// The conversion fee. Task EstimateConversionTransactionFeeAsync(CancellationToken cancellation = default); } /// public sealed class ExternalApiClient : RestApiClientBase, IExternalApiClient { + /// + /// The class constructor. + /// + /// The . + /// The . public ExternalApiClient(ICounterChainSettings counterChainSettings, IHttpClientFactory httpClientFactory) : base(httpClientFactory, counterChainSettings.CounterChainApiPort, "ExternalApi", $"http://{counterChainSettings.CounterChainApiHost}") { diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiFeature.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiFeature.cs index d976b50157..dc253247d0 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiFeature.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiFeature.cs @@ -6,15 +6,26 @@ namespace Stratis.Bitcoin.Features.ExternalApi { + /// + /// The External API Feature. + /// public sealed class ExternalApiFeature : FullNodeFeature { private readonly IExternalApiPoller externalApiPoller; + /// + /// The class constructor. + /// + /// The . public ExternalApiFeature(IExternalApiPoller externalApiPoller) { this.externalApiPoller = externalApiPoller; } + /// + /// Initializes the instance. + /// + /// The asynchronous task. public override Task InitializeAsync() { this.externalApiPoller?.Initialize(); @@ -22,14 +33,25 @@ public override Task InitializeAsync() return Task.CompletedTask; } + /// + /// Disposes the instance. + /// public override void Dispose() { this.externalApiPoller?.Dispose(); } } + /// + /// extensions. + /// public static partial class IFullNodeBuilderExtensions { + /// + /// Add the External Api feature to the node. + /// + /// Implicitly passed . + /// The . public static IFullNodeBuilder AddExternalApi(this IFullNodeBuilder fullNodeBuilder) { LoggingConfiguration.RegisterFeatureNamespace("externalapi"); diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiPoller.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiPoller.cs index 60bed7e0cb..0493667fc8 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiPoller.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiPoller.cs @@ -9,26 +9,63 @@ namespace Stratis.Bitcoin.Features.ExternalApi { + /// + /// External API poller. + /// public interface IExternalApiPoller : IDisposable { + /// + /// Initialize the instance. + /// void Initialize(); + /// + /// Gets the Stratis price. + /// + /// The Stratis price. decimal GetStratisPrice(); + /// + /// Gets the Ethereum price. + /// + /// The Ethereum price. decimal GetEthereumPrice(); + /// + /// Gets the gas price. + /// + /// The gas price. int GetGasPrice(); + /// Estimates the total conversion gas costs. + /// The estimated total amount of gas a conversion transaction will require. + /// The decimal type is acceptable here because it supports sufficiently large numbers for most conceivable gas calculations. decimal EstimateConversionTransactionGas(); + /// + /// Estimates the conversion transaction fee. + /// + /// The estimated conversion transaction fee, converted from the USD total to the equivalent STRAX amount. decimal EstimateConversionTransactionFee(); } + /// public class ExternalApiPoller : IExternalApiPoller { + /// + /// The quorum size. + /// // TODO: This should be linked to the setting in the interop feature public const int QuorumSize = 6; + + /// + /// One Ether. + /// public const decimal OneEther = 1_000_000_000_000_000_000; + + /// + /// One Gwei. + /// public const decimal OneGwei = 1_000_000_000; private readonly IAsyncProvider asyncProvider; @@ -41,6 +78,13 @@ public class ExternalApiPoller : IExternalApiPoller private IAsyncLoop gasPriceLoop; private IAsyncLoop priceLoop; + /// + /// The instance constructor. + /// + /// The . + /// The . + /// The . + /// The . public ExternalApiPoller(NodeSettings nodeSettings, IAsyncProvider asyncProvider, INodeLifetime nodeLifetime, @@ -54,6 +98,9 @@ public ExternalApiPoller(NodeSettings nodeSettings, this.coinGeckoClient = new CoinGeckoClient(this.externalApiSettings); } + /// + /// Inializes the instance. + /// public void Initialize() { this.logger.LogInformation($"External API feature enabled, initializing periodic loops."); @@ -68,7 +115,7 @@ public void Initialize() try { - await this.etherscanClient.GasOracle(true).ConfigureAwait(false); + await this.etherscanClient.GasOracleAsync().ConfigureAwait(false); } catch (HttpRequestException e2) when (e2.InnerException is SocketException socketException && socketException.ErrorCode == 11001) { @@ -115,23 +162,25 @@ public void Initialize() } } + /// public decimal GetStratisPrice() { return this.coinGeckoClient.GetStratisPrice(); } + /// public decimal GetEthereumPrice() { return this.coinGeckoClient.GetEthereumPrice(); } + /// public int GetGasPrice() { return this.etherscanClient.GetGasPrice(); } - /// The decimal type is acceptable here because it supports sufficiently large numbers for most conceivable gas calculations. - /// The estimated total amount of gas a conversion transaction will require. + /// public decimal EstimateConversionTransactionGas() { // The cost of submitting a multisig ERC20 transfer to the multisig contract. @@ -152,7 +201,7 @@ public decimal EstimateConversionTransactionGas() return totalGas * gasPrice * OneGwei; } - /// The estimated conversion transaction fee, converted from the USD total to the equivalent STRAX amount. + /// public decimal EstimateConversionTransactionFee() { // The approximate USD fee that will be applied to conversion transactions, over and above the computed gas cost. @@ -173,6 +222,7 @@ public decimal EstimateConversionTransactionFee() return (overallGasUsd / stratisPriceUsd) + (ConversionTransactionFee / stratisPriceUsd); } + /// public void Dispose() { this.gasPriceLoop?.Dispose(); diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiSettings.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiSettings.cs index f488e3ef41..5727ad43c4 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiSettings.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/ExternalApiSettings.cs @@ -2,28 +2,65 @@ namespace Stratis.Bitcoin.Features.ExternalApi { + /// + /// External Api Settings. + /// public class ExternalApiSettings { + /// + /// The key for the Etherscan api key. + /// public const string EtherscanApiKeyKey = "etherscanapikey"; + /// + /// The Etherscan gas oracle key. + /// public const string EtherscanGasOracleUrlKey = "etherscangasoracle"; + /// + /// The Ethereum gas price tracking key. + /// public const string EthereumGasPriceTrackingKey = "ethereumgaspricetracking"; + /// + /// The Ethereum price url key. + /// public const string PriceUrlKey = "ethereumpriceurl"; + /// + /// The key for a flag indicating whether price tracking is enabled. + /// public const string PriceTrackingKey = "pricetracking"; + /// + /// The Etherscan api key. + /// public string EtherscanApiKey { get; set; } + /// + /// The Etherscan gas oracle url and key. + /// public string EtherscanGasOracleUrl { get; set; } + /// + /// Indicates whether Ethereum gas price tracking is enabled. + /// public bool EthereumGasPriceTracking { get; set; } + /// + /// Url for retrieving Stratis and Ethereum prices. + /// public string PriceUrl { get; set; } + /// + /// A flag indicating whether price tracking is enabled. + /// public bool PriceTracking { get; set; } + /// + /// The instance constructor. + /// + /// The . public ExternalApiSettings(NodeSettings nodeSettings) { // To avoid any rate limiting by Etherscan it is better to have an API key defined, but the API is still supposed to work to a limited extent without one. diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/Models/CoinGeckoResponse.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/Models/CoinGeckoResponse.cs index b6863cc173..bc908bfae2 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/Models/CoinGeckoResponse.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/Models/CoinGeckoResponse.cs @@ -1,14 +1,29 @@ namespace Stratis.Bitcoin.Features.ExternalApi.Models { + /// + /// CoinGecko price data as included in . + /// public class CoinGeckoPriceData { + /// + /// The price in usd. + /// public decimal usd { get; set; } } + /// + /// CoinGecko response. + /// public class CoinGeckoResponse { + /// + /// The Stratis price. + /// public CoinGeckoPriceData stratis { get; set; } + /// + /// The Ethereum price. + /// public CoinGeckoPriceData ethereum { get; set; } } } diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/Models/EtherscanGasOracleResponse.cs b/src/Stratis.Bitcoin.Features.ExternalAPI/Models/EtherscanGasOracleResponse.cs index 1bc915796e..2993bc7f78 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/Models/EtherscanGasOracleResponse.cs +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/Models/EtherscanGasOracleResponse.cs @@ -1,22 +1,49 @@ namespace Stratis.Bitcoin.Features.ExternalApi.Models { + /// + /// Etherscan gas oracle response. + /// public class EtherscanGasOracleResponse { + /// + /// Status. + /// public string status { get; set; } + /// + /// Message. + /// public string message { get; set; } + /// + /// See . + /// public EtherscanGasOracleResponseResult result { get; set; } } + /// + /// Etherscan gas oracle response result as included in . + /// public class EtherscanGasOracleResponseResult { + /// + /// The last block. + /// public int LastBlock { get; set; } + /// + /// The safe gas price. + /// public int SafeGasPrice { get; set; } + /// + /// The proposed gas price. + /// public int ProposeGasPrice { get; set; } + /// + /// The fast gas price. + /// public int FastGasPrice { get; set; } } } diff --git a/src/Stratis.Bitcoin.Features.ExternalAPI/Stratis.Bitcoin.Features.ExternalApi.csproj b/src/Stratis.Bitcoin.Features.ExternalAPI/Stratis.Bitcoin.Features.ExternalApi.csproj index 88f2172402..8c8a96a208 100644 --- a/src/Stratis.Bitcoin.Features.ExternalAPI/Stratis.Bitcoin.Features.ExternalApi.csproj +++ b/src/Stratis.Bitcoin.Features.ExternalAPI/Stratis.Bitcoin.Features.ExternalApi.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. Stratis.Features.ExternalAPI Stratis.Features.ExternalAPI diff --git a/src/Stratis.Bitcoin.Features.Interop/Controllers/InteropController.cs b/src/Stratis.Bitcoin.Features.Interop/Controllers/InteropController.cs index 784f4fc63b..646640311e 100644 --- a/src/Stratis.Bitcoin.Features.Interop/Controllers/InteropController.cs +++ b/src/Stratis.Bitcoin.Features.Interop/Controllers/InteropController.cs @@ -5,9 +5,10 @@ using System.Numerics; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using NBitcoin; using NBitcoin.DataEncoders; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.Interop.ETHClient; using Stratis.Bitcoin.Features.Interop.Models; using Stratis.Bitcoin.Features.PoA; @@ -83,7 +84,7 @@ public IActionResult InteropStatusBurnRequests() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -123,7 +124,7 @@ public IActionResult InteropStatusMintRequests() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -159,7 +160,7 @@ public IActionResult InteropStatusVotes() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -187,7 +188,7 @@ public async Task OwnersAsync(DestinationChain destinationChain) } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } @@ -223,7 +224,7 @@ public async Task AddOwnerAsync(DestinationChain destinationChain } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } @@ -259,7 +260,7 @@ public async Task RemoveOwnerAsync(DestinationChain destinationCh } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } @@ -292,7 +293,7 @@ public async Task ConfirmTransactionAsync(DestinationChain destin } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } @@ -330,7 +331,7 @@ public async Task ChangeRequirementAsync(DestinationChain destina } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } @@ -374,7 +375,7 @@ public async Task MultisigTransactionAsync(DestinationChain desti } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } @@ -416,7 +417,7 @@ public async Task MultisigConfirmationsAsync(DestinationChain des } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } @@ -446,7 +447,7 @@ public async Task BalanceAsync(DestinationChain destinationChain, } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } @@ -486,7 +487,7 @@ public IActionResult SetOriginatorForRequest([FromBody] string requestId) } catch (Exception e) { - this.logger.Error("Exception setting conversion request '{0}' to {1} : {2}.", requestId, e.ToString(), ConversionRequestStatus.OriginatorNotSubmitted); + this.logger.LogError("Exception setting conversion request '{0}' to {1} : {2}.", requestId, e.ToString(), ConversionRequestStatus.OriginatorNotSubmitted); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Error", e.Message); } @@ -510,7 +511,7 @@ public IActionResult ResetConversionRequestAsNotOriginator([FromBody] string req } catch (Exception e) { - this.logger.Error("Exception setting conversion request '{0}' to {1} : {2}.", requestId, e.ToString(), ConversionRequestStatus.NotOriginator); + this.logger.LogError("Exception setting conversion request '{0}' to {1} : {2}.", requestId, e.ToString(), ConversionRequestStatus.NotOriginator); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Error", e.Message); } @@ -530,13 +531,13 @@ public IActionResult ReprocessBurnRequest([FromBody] ReprocessBurnRequestModel m try { this.conversionRequestRepository.ReprocessBurnRequest(model.RequestId, model.BlockHeight, ConversionRequestStatus.Unprocessed); - this.logger.Info($"Burn request '{model.RequestId}' will be reprocessed at height {model.BlockHeight}."); + this.logger.LogInformation($"Burn request '{model.RequestId}' will be reprocessed at height {model.BlockHeight}."); return this.Json($"Burn request '{model.RequestId}' will be reprocessed at height {model.BlockHeight}."); } catch (Exception e) { - this.logger.Error("Exception setting burn request '{0}' to be reprocessed : {1}.", model.RequestId, e.ToString()); + this.logger.LogError("Exception setting burn request '{0}' to be reprocessed : {1}.", model.RequestId, e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Error", e.Message); } @@ -561,7 +562,7 @@ public IActionResult PushVoteManually([FromBody] PushManualVoteForRequest model) } catch (Exception e) { - this.logger.Error("Exception manual pushing vote for conversion request '{0}' : {1}.", model.RequestId, e.ToString()); + this.logger.LogError("Exception manual pushing vote for conversion request '{0}' : {1}.", model.RequestId, e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Error", e.Message); } diff --git a/src/Stratis.Bitcoin.Features.Interop/GlobalSuppressions.cs b/src/Stratis.Bitcoin.Features.Interop/GlobalSuppressions.cs new file mode 100644 index 0000000000..21cb2606e7 --- /dev/null +++ b/src/Stratis.Bitcoin.Features.Interop/GlobalSuppressions.cs @@ -0,0 +1,13 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +// Temporary fix to reduce the clutter of warnings. +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements must be documented", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.Interop")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1615:Element return value must be documented", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.Interop")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1611:The documentation for parameter is missing", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.Interop")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements must be documented", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.Interop")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1614:Element parameter documentation must have text", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.Interop")] diff --git a/src/Stratis.Bitcoin.Features.Interop/InteropBehavior.cs b/src/Stratis.Bitcoin.Features.Interop/InteropBehavior.cs index 33f9c4add8..2e867e9d2f 100644 --- a/src/Stratis.Bitcoin.Features.Interop/InteropBehavior.cs +++ b/src/Stratis.Bitcoin.Features.Interop/InteropBehavior.cs @@ -1,8 +1,9 @@ using System; using System.Numerics; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.Interop.ETHClient; using Stratis.Bitcoin.Features.Interop.Payloads; using Stratis.Bitcoin.Features.PoA; @@ -54,14 +55,14 @@ public override object Clone() /// protected override void AttachCore() { - this.logger.Debug("Attaching behaviour for {0}", this.AttachedPeer.PeerEndPoint.Address); + this.logger.LogDebug("Attaching behaviour for {0}", this.AttachedPeer.PeerEndPoint.Address); this.AttachedPeer.MessageReceived.Register(this.OnMessageReceivedAsync, true); } /// protected override void DetachCore() { - this.logger.Debug("Detaching behaviour for {0}", this.AttachedPeer.PeerEndPoint.Address); + this.logger.LogDebug("Detaching behaviour for {0}", this.AttachedPeer.PeerEndPoint.Address); this.AttachedPeer.MessageReceived.Unregister(this.OnMessageReceivedAsync); } @@ -73,12 +74,12 @@ private async Task OnMessageReceivedAsync(INetworkPeer peer, IncomingMessage mes } catch (OperationCanceledException) { - this.logger.Trace("(-)[CANCELED_EXCEPTION]"); + this.logger.LogTrace("(-)[CANCELED_EXCEPTION]"); return; } catch (Exception ex) { - this.logger.Error("Exception occurred: {0}", ex.ToString()); + this.logger.LogError("Exception occurred: {0}", ex.ToString()); throw; } } @@ -104,7 +105,7 @@ private async Task ProcessMessageAsync(INetworkPeer peer, IncomingMessage messag } catch (OperationCanceledException) { - this.logger.Trace("(-)[CANCELED_EXCEPTION]"); + this.logger.LogTrace("(-)[CANCELED_EXCEPTION]"); } } @@ -113,7 +114,7 @@ private async Task ProcessConversionRequestPayloadAsync(INetworkPeer peer, Conve if (!this.federationManager.IsFederationMember) return; - this.logger.Debug("Conversion request payload request for id '{0}' received from '{1}':'{2}' proposing transaction ID '{4}'.", payload.RequestId, peer.PeerEndPoint.Address, peer.RemoteSocketEndpoint.Address, payload.RequestId, payload.TransactionId); + this.logger.LogDebug("Conversion request payload request for id '{0}' received from '{1}':'{2}' proposing transaction ID '{4}'.", payload.RequestId, peer.PeerEndPoint.Address, peer.RemoteSocketEndpoint.Address, payload.RequestId, payload.TransactionId); if (payload.TransactionId == BigInteger.MinusOne) return; @@ -127,14 +128,14 @@ private async Task ProcessConversionRequestPayloadAsync(INetworkPeer peer, Conve if (!this.federationManager.IsMultisigMember(pubKey)) { - this.logger.Warn("Conversion request payload for '{0}'. Computed pubkey '{1}'.", payload.RequestId, pubKey?.ToHex()); + this.logger.LogWarning("Conversion request payload for '{0}'. Computed pubkey '{1}'.", payload.RequestId, pubKey?.ToHex()); return; } } catch (Exception) { - this.logger.Warn("Received malformed conversion request payload for '{0}'.", payload.RequestId); + this.logger.LogWarning("Received malformed conversion request payload for '{0}'.", payload.RequestId); return; } @@ -153,7 +154,7 @@ private async Task ProcessConversionRequestPayloadAsync(INetworkPeer peer, Conve // We presume that the initial submitter of the transaction must have at least confirmed it. Otherwise just ignore this coordination attempt. if (confirmationCount < 1) { - this.logger.Info("Multisig wallet transaction {0} has no confirmations.", payload.TransactionId); + this.logger.LogInformation("Multisig wallet transaction {0} has no confirmations.", payload.TransactionId); return; } @@ -183,13 +184,13 @@ private async Task ProcessFeeProposalAsync(FeeProposalPayload payload) if (!this.federationManager.IsMultisigMember(pubKey)) { - this.logger.Warn("Received unverified fee proposal payload for '{0}' from pubkey '{1}'.", payload.RequestId, pubKey?.ToHex()); + this.logger.LogWarning("Received unverified fee proposal payload for '{0}' from pubkey '{1}'.", payload.RequestId, pubKey?.ToHex()); return; } } catch (Exception) { - this.logger.Warn("Received malformed fee proposal payload for '{0}'.", payload.RequestId); + this.logger.LogWarning("Received malformed fee proposal payload for '{0}'.", payload.RequestId); return; } @@ -213,13 +214,13 @@ private async Task ProcessFeeAgreeAsync(FeeAgreePayload payload) if (!this.federationManager.IsMultisigMember(pubKey)) { - this.logger.Warn("Received unverified fee vote payload for '{0}' from pubkey '{1}'.", payload.RequestId, pubKey?.ToHex()); + this.logger.LogWarning("Received unverified fee vote payload for '{0}' from pubkey '{1}'.", payload.RequestId, pubKey?.ToHex()); return; } } catch (Exception) { - this.logger.Warn("Received malformed fee vote payload for '{0}'.", payload.RequestId); + this.logger.LogWarning("Received malformed fee vote payload for '{0}'.", payload.RequestId); return; } diff --git a/src/Stratis.Bitcoin.Features.Interop/InteropPoller.cs b/src/Stratis.Bitcoin.Features.Interop/InteropPoller.cs index a21ad94145..4b69c8b7ef 100644 --- a/src/Stratis.Bitcoin.Features.Interop/InteropPoller.cs +++ b/src/Stratis.Bitcoin.Features.Interop/InteropPoller.cs @@ -5,14 +5,15 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; using NBitcoin.Crypto; using Nethereum.RPC.Eth.DTOs; using Nethereum.Util; using Nethereum.Web3; -using NLog; using Stratis.Bitcoin.AsyncWork; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Features.Interop.ETHClient; using Stratis.Bitcoin.Features.Interop.Exceptions; @@ -126,17 +127,17 @@ public async Task InitializeAsync() if (!this.ethClientProvider.GetAllSupportedChains().Any()) { // There are no chains that are supported and enabled, exit. - this.logger.Debug("Interop disabled."); + this.logger.LogDebug("Interop disabled."); return; } if (!this.federationManager.IsFederationMember) { - this.logger.Debug("Not a federation member."); + this.logger.LogDebug("Not a federation member."); return; } - this.logger.Info($"Interoperability enabled, initializing periodic loop."); + this.logger.LogInformation($"Interoperability enabled, initializing periodic loop."); // Initialize the interop polling loop, to check for interop contract requests. this.interopLoop = this.asyncProvider.CreateAndRunAsyncLoop("PeriodicCheckInterop", async (cancellation) => @@ -144,7 +145,7 @@ public async Task InitializeAsync() if (this.initialBlockDownloadState.IsInitialBlockDownload()) return; - this.logger.Trace("Beginning interop loop."); + this.logger.LogTrace("Beginning interop loop."); try { @@ -152,10 +153,10 @@ public async Task InitializeAsync() } catch (Exception e) { - this.logger.Warn("Exception raised when checking interop requests. {0}", e); + this.logger.LogWarning("Exception raised when checking interop requests. {0}", e); } - this.logger.Trace("Finishing interop loop."); + this.logger.LogTrace("Finishing interop loop."); }, this.nodeLifetime.ApplicationStopping, repeatEvery: TimeSpans.TenSeconds, @@ -167,7 +168,7 @@ public async Task InitializeAsync() if (this.initialBlockDownloadState.IsInitialBlockDownload()) return; - this.logger.Trace("Beginning conversion processing loop."); + this.logger.LogTrace("Beginning conversion processing loop."); try { @@ -175,10 +176,10 @@ public async Task InitializeAsync() } catch (Exception e) { - this.logger.Warn($"Exception raised when checking conversion requests. {e}"); + this.logger.LogWarning($"Exception raised when checking conversion requests. {e}"); } - this.logger.Trace("Finishing conversion processing loop."); + this.logger.LogTrace("Finishing conversion processing loop."); }, this.nodeLifetime.ApplicationStopping, repeatEvery: TimeSpans.TenSeconds, @@ -194,7 +195,7 @@ public async Task InitializeAsync() if (this.initialBlockDownloadState.IsInitialBlockDownload()) return; - this.logger.Debug("Beginning conversion burn transaction polling loop."); + this.logger.LogDebug("Beginning conversion burn transaction polling loop."); try { @@ -208,10 +209,10 @@ public async Task InitializeAsync() } catch (Exception e) { - this.logger.Warn($"Exception raised when polling for conversion burn transactions. {e}"); + this.logger.LogWarning($"Exception raised when polling for conversion burn transactions. {e}"); } - this.logger.Debug("Finishing conversion burn transaction polling loop."); + this.logger.LogDebug("Finishing conversion burn transaction polling loop."); }, this.nodeLifetime.ApplicationStopping, repeatEvery: TimeSpans.TenSeconds, @@ -233,14 +234,14 @@ private async Task LoadLastPolledBlockAsync() else this.lastPolledBlock[supportedChain.Key] = loaded; - this.logger.Info($"Last polled block for {supportedChain.Key} set to {this.lastPolledBlock[supportedChain.Key]}."); + this.logger.LogInformation($"Last polled block for {supportedChain.Key} set to {this.lastPolledBlock[supportedChain.Key]}."); } } private void SaveLastPolledBlock(DestinationChain destinationChain) { this.keyValueRepository.SaveValueJson(string.Format(LastPolledBlockKey, destinationChain), this.lastPolledBlock[destinationChain]); - this.logger.Info($"Last polled block for {destinationChain} saved as {this.lastPolledBlock[destinationChain]}."); + this.logger.LogInformation($"Last polled block for {destinationChain} saved as {this.lastPolledBlock[destinationChain]}."); } /// @@ -263,7 +264,7 @@ private async Task EnsureLastPolledBlockIsSyncedWithChainAsync() private async Task PollBlockForBurnRequestsAsync(KeyValuePair supportedChain, BigInteger blockHeight) { - this.logger.Info("Polling {0} block at height {1} for burn transactions.", supportedChain.Key, blockHeight); + this.logger.LogInformation("Polling {0} block at height {1} for burn transactions.", supportedChain.Key, blockHeight); BlockWithTransactions block = await supportedChain.Value.GetBlockAsync(this.lastPolledBlock[supportedChain.Key]).ConfigureAwait(false); List<(string TransactionHash, BurnFunction Burn)> burns = await supportedChain.Value.GetBurnsFromBlock(block).ConfigureAwait(false); @@ -291,11 +292,11 @@ private async Task CheckInteropNodesAsync() // TODO Add back or refactor so that this is specific per chain (if applicable) // BigInteger balance = await this.ETHClientBase.GetBalanceAsync(this.interopSettings.ETHAccount).ConfigureAwait(false); - this.logger.Info("Current {0} node block height is {1}.", clientForChain.Key, blockHeight); + this.logger.LogInformation("Current {0} node block height is {1}.", clientForChain.Key, blockHeight); } catch (Exception e) { - this.logger.Error("Error checking {0} node status: {1}", clientForChain.Key, e); + this.logger.LogError("Error checking {0} node status: {1}", clientForChain.Key, e); } } } @@ -305,24 +306,24 @@ private async Task CheckInteropNodesAsync() /// private void ProcessBurn(string blockHash, string transactionHash, BurnFunction burn) { - this.logger.Info("Conversion burn transaction '{0}' received from polled block '{1}', sender {2}.", transactionHash, blockHash, burn.FromAddress); + this.logger.LogInformation("Conversion burn transaction '{0}' received from polled block '{1}', sender {2}.", transactionHash, blockHash, burn.FromAddress); lock (this.repositoryLock) { if (this.conversionRequestRepository.Get(transactionHash) != null) { - this.logger.Info("Conversion burn transaction '{0}' already exists, ignoring.", transactionHash); + this.logger.LogInformation("Conversion burn transaction '{0}' already exists, ignoring.", transactionHash); return; } } - this.logger.Info("Conversion burn transaction '{0}' has value {1}.", transactionHash, burn.Amount); + this.logger.LogInformation("Conversion burn transaction '{0}' has value {1}.", transactionHash, burn.Amount); // Get the destination address recorded in the contract call itself. This has the benefit that subsequent burn calls from the same account providing different addresses will not interfere with this call. string destinationAddress = burn.StraxAddress; - this.logger.Info("Conversion burn transaction '{0}' has destination address {1}.", transactionHash, destinationAddress); + this.logger.LogInformation("Conversion burn transaction '{0}' has destination address {1}.", transactionHash, destinationAddress); // Validate that it is a mainchain address here before bothering to add it to the repository. try @@ -331,7 +332,7 @@ private void ProcessBurn(string blockHash, string transactionHash, BurnFunction } catch (Exception) { - this.logger.Warn("Error validating destination address '{0}' for transaction '{1}'.", destinationAddress, transactionHash); + this.logger.LogWarning("Error validating destination address '{0}' for transaction '{1}'.", destinationAddress, transactionHash); return; } @@ -387,18 +388,18 @@ private async Task ProcessConversionRequestsAsync() if (mintRequests == null) { - this.logger.Debug("There are no requests."); + this.logger.LogDebug("There are no requests."); return; } - this.logger.Info("There are {0} unprocessed conversion mint requests.", mintRequests.Count); + this.logger.LogInformation("There are {0} unprocessed conversion mint requests.", mintRequests.Count); foreach (ConversionRequest request in mintRequests) { // Ignore old conversion requests for the time being. if (request.RequestStatus == ConversionRequestStatus.Unprocessed && (this.chainIndexer.Tip.Height - request.BlockHeight) > this.network.Consensus.MaxReorgLength) { - this.logger.Info("Ignoring old conversion mint request '{0}' with status {1} from block height {2}.", request.RequestId, request.RequestStatus, request.BlockHeight); + this.logger.LogInformation("Ignoring old conversion mint request '{0}' with status {1} from block height {2}.", request.RequestId, request.RequestStatus, request.BlockHeight); request.Processed = true; @@ -410,7 +411,7 @@ private async Task ProcessConversionRequestsAsync() continue; } - this.logger.Info("Processing conversion mint request {0} on {1} chain.", request.RequestId, request.DestinationChain); + this.logger.LogInformation("Processing conversion mint request {0} on {1} chain.", request.RequestId, request.DestinationChain); IETHClient clientForDestChain = this.ethClientProvider.GetClientForChain(request.DestinationChain); @@ -439,13 +440,13 @@ private async Task ProcessConversionRequestsAsync() if (originator) { // If this node is the designated transaction originator, it must create and submit the transaction to the multisig. - this.logger.Info("This node selected as originator for transaction '{0}'.", request.RequestId); + this.logger.LogInformation("This node selected as originator for transaction '{0}'.", request.RequestId); request.RequestStatus = ConversionRequestStatus.OriginatorNotSubmitted; } else { - this.logger.Info("This node was not selected as the originator for transaction '{0}'. The originator is: '{1}'.", request.RequestId, designatedMember == null ? "N/A (Overridden)" : designatedMember.PubKey?.ToHex()); + this.logger.LogInformation("This node was not selected as the originator for transaction '{0}'. The originator is: '{1}'.", request.RequestId, designatedMember == null ? "N/A (Overridden)" : designatedMember.PubKey?.ToHex()); request.RequestStatus = ConversionRequestStatus.NotOriginator; } @@ -455,7 +456,7 @@ private async Task ProcessConversionRequestsAsync() case ConversionRequestStatus.OriginatorNotSubmitted: { - this.logger.Info("Conversion not yet submitted, checking which gas price to use."); + this.logger.LogInformation("Conversion not yet submitted, checking which gas price to use."); // First construct the necessary transfer() transaction data, utilising the ABI of the wrapped STRAX ERC20 contract. // When this constructed transaction is actually executed, the transfer's source account will be the account executing the transaction i.e. the multisig contract address. @@ -467,7 +468,7 @@ private async Task ProcessConversionRequestsAsync() if (gasPrice == -1) gasPrice = this.interopSettings.GetSettingsByChain(request.DestinationChain).GasPrice; - this.logger.Info("Originator will use a gas price of {0} to submit the transaction.", gasPrice); + this.logger.LogInformation("Originator will use a gas price of {0} to submit the transaction.", gasPrice); // Submit the unconfirmed transaction data to the multisig contract, returning a transactionId used to refer to it. // Once sufficient multisig owners have confirmed the transaction the multisig contract will execute it. @@ -488,12 +489,12 @@ private async Task ProcessConversionRequestsAsync() case ConversionRequestStatus.OriginatorSubmitting: { (BigInteger confirmationCount, string blockHash) = await this.ethClientProvider.GetClientForChain(request.DestinationChain).GetConfirmationsAsync(request.ExternalChainTxHash).ConfigureAwait(false); - this.logger.Info($"Originator confirming transaction id '{request.ExternalChainTxHash}' '({request.ExternalChainTxEventId})' before broadcasting; confirmations: {confirmationCount}; Block Hash {blockHash}."); + this.logger.LogInformation($"Originator confirming transaction id '{request.ExternalChainTxHash}' '({request.ExternalChainTxEventId})' before broadcasting; confirmations: {confirmationCount}; Block Hash {blockHash}."); if (confirmationCount < this.SubmissionConfirmationThreshold) break; - this.logger.Info("Originator submitted transaction to multisig in transaction '{0}' and was allocated transactionId '{1}'.", request.ExternalChainTxHash, request.ExternalChainTxEventId); + this.logger.LogInformation("Originator submitted transaction to multisig in transaction '{0}' and was allocated transactionId '{1}'.", request.ExternalChainTxHash, request.ExternalChainTxEventId); this.conversionRequestCoordinationService.AddVote(request.RequestId, BigInteger.Parse(request.ExternalChainTxEventId), this.federationManager.CurrentFederationKey.PubKey); @@ -520,7 +521,7 @@ private async Task ProcessConversionRequestsAsync() if (agreedTransactionId != BigInteger.MinusOne) { - this.logger.Info("Transaction '{0}' has received sufficient votes, it should now start getting confirmed by each peer.", agreedTransactionId); + this.logger.LogInformation("Transaction '{0}' has received sufficient votes, it should now start getting confirmed by each peer.", agreedTransactionId); request.RequestStatus = ConversionRequestStatus.VoteFinalised; } @@ -541,7 +542,7 @@ private async Task ProcessConversionRequestsAsync() if (confirmationCount >= this.interopSettings.GetSettingsByChain(request.DestinationChain).MultisigWalletQuorum) { - this.logger.Info("Transaction '{0}' has received at least {1} confirmations, it will be automatically executed by the multisig contract.", transactionId3, this.interopSettings.GetSettingsByChain(request.DestinationChain).MultisigWalletQuorum); + this.logger.LogInformation("Transaction '{0}' has received at least {1} confirmations, it will be automatically executed by the multisig contract.", transactionId3, this.interopSettings.GetSettingsByChain(request.DestinationChain).MultisigWalletQuorum); request.RequestStatus = ConversionRequestStatus.Processed; request.Processed = true; @@ -551,7 +552,7 @@ private async Task ProcessConversionRequestsAsync() } else { - this.logger.Info("Transaction '{0}' has finished voting but does not yet have {1} confirmations, re-broadcasting votes to peers.", transactionId3, this.interopSettings.GetSettingsByChain(request.DestinationChain).MultisigWalletQuorum); + this.logger.LogInformation("Transaction '{0}' has finished voting but does not yet have {1} confirmations, re-broadcasting votes to peers.", transactionId3, this.interopSettings.GetSettingsByChain(request.DestinationChain).MultisigWalletQuorum); // There are not enough confirmations yet. // Even though the vote is finalised, other nodes may come and go. So we re-broadcast the finalised votes to all federation peers. // Nodes will simply ignore the messages if they are not relevant. @@ -577,7 +578,7 @@ private async Task ProcessConversionRequestsAsync() { // TODO: Should we check the number of confirmations for the submission transaction here too? - this.logger.Info("Quorum reached for conversion transaction '{0}' with transactionId '{1}', submitting confirmation to contract.", request.RequestId, agreedUponId); + this.logger.LogInformation("Quorum reached for conversion transaction '{0}' with transactionId '{1}', submitting confirmation to contract.", request.RequestId, agreedUponId); int gasPrice = this.externalApiPoller.GetGasPrice(); @@ -585,7 +586,7 @@ private async Task ProcessConversionRequestsAsync() if (gasPrice == -1) gasPrice = this.interopSettings.GetSettingsByChain(request.DestinationChain).GasPrice; - this.logger.Info("The non-originator will use a gas price of {0} to confirm the transaction.", gasPrice); + this.logger.LogInformation("The non-originator will use a gas price of {0} to confirm the transaction.", gasPrice); // Once a quorum is reached, each node confirms the agreed transactionId. // If the originator or some other nodes renege on their vote, the current node will not re-confirm a different transactionId. @@ -593,7 +594,7 @@ private async Task ProcessConversionRequestsAsync() request.ExternalChainTxHash = confirmationHash; - this.logger.Info("The hash of the confirmation transaction for conversion transaction '{0}' was '{1}'.", request.RequestId, confirmationHash); + this.logger.LogInformation("The hash of the confirmation transaction for conversion transaction '{0}' was '{1}'.", request.RequestId, confirmationHash); request.RequestStatus = ConversionRequestStatus.VoteFinalised; } @@ -603,7 +604,7 @@ private async Task ProcessConversionRequestsAsync() if (transactionId4 != BigInteger.MinusOne) { - this.logger.Debug("Broadcasting vote (transactionId '{0}') for conversion transaction '{1}'.", transactionId4, request.RequestId); + this.logger.LogDebug("Broadcasting vote (transactionId '{0}') for conversion transaction '{1}'.", transactionId4, request.RequestId); this.conversionRequestCoordinationService.AddVote(request.RequestId, transactionId4, this.federationManager.CurrentFederationKey.PubKey); @@ -660,7 +661,7 @@ private bool DetermineConversionRequestOriginator(int blockHeight, out IFederati // We are not able to simply use the entire federation member list, as only multisig nodes can be transaction originators. List federation = this.federationHistory.GetFederationForBlock(this.chainIndexer.GetHeader(blockHeight)); - this.logger.Info($"Federation retrieved at height '{blockHeight}', size {federation.Count} members."); + this.logger.LogInformation($"Federation retrieved at height '{blockHeight}', size {federation.Count} members."); var multisig = new List(); @@ -687,9 +688,10 @@ private bool DetermineConversionRequestOriginator(int blockHeight, out IFederati } /// - /// Wait for the submission to be well-confirmed before initial vote & broadcast. + /// Wait for the submission to be well-confirmed before initial vote and broadcast. /// /// The transaction information to check. + /// /// The caller that is waiting on the submission transaction's confirmation count. /// True if it succeeded, false if the node is stopping. private async Task WaitForReplenishmentToBeConfirmedAsync(MultisigTransactionIdentifiers identifiers, DestinationChain destinationChain, [CallerMemberName] string caller = null) @@ -700,7 +702,7 @@ private async Task WaitForReplenishmentToBeConfirmedAsync(MultisigTransact return false; (BigInteger confirmationCount, string blockHash) = await this.ethClientProvider.GetClientForChain(destinationChain).GetConfirmationsAsync(identifiers.TransactionHash).ConfigureAwait(false); - this.logger.Info($"[{caller}] Originator confirming transaction id '{identifiers.TransactionHash}' '({identifiers.TransactionId})' before broadcasting; confirmations: {confirmationCount}; Block Hash {blockHash}."); + this.logger.LogInformation($"[{caller}] Originator confirming transaction id '{identifiers.TransactionHash}' '({identifiers.TransactionId})' before broadcasting; confirmations: {confirmationCount}; Block Hash {blockHash}."); if (confirmationCount >= this.SubmissionConfirmationThreshold) break; @@ -730,7 +732,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg if (originator) { - this.logger.Info("Insufficient reserve balance remaining, initiating mint transaction to replenish reserve."); + this.logger.LogInformation("Insufficient reserve balance remaining, initiating mint transaction to replenish reserve."); // By minting the request amount + the reserve requirement, we cater for arbitrarily large amounts in the request. string mintData = this.ethClientProvider.GetClientForChain(request.DestinationChain).EncodeMintParams(this.interopSettings.GetSettingsByChain(request.DestinationChain).MultisigWalletAddress, amountInWei + this.ReserveBalanceTarget); @@ -741,7 +743,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg if (gasPrice == -1) gasPrice = this.interopSettings.GetSettingsByChain(request.DestinationChain).GasPrice; - this.logger.Info("Originator will use a gas price of {0} to submit the mint replenishment transaction.", gasPrice); + this.logger.LogInformation("Originator will use a gas price of {0} to submit the mint replenishment transaction.", gasPrice); MultisigTransactionIdentifiers identifiers = await this.ethClientProvider.GetClientForChain(request.DestinationChain).SubmitTransactionAsync(this.interopSettings.GetSettingsByChain(request.DestinationChain).WrappedStraxContractAddress, 0, mintData, gasPrice).ConfigureAwait(false); @@ -757,7 +759,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg return; } - this.logger.Info("Originator adding its vote for mint transaction id: {0}", mintTransactionId); + this.logger.LogInformation("Originator adding its vote for mint transaction id: {0}", mintTransactionId); this.conversionRequestCoordinationService.AddVote(mintRequestId, mintTransactionId, this.federationManager.CurrentFederationKey.PubKey); @@ -767,7 +769,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg await this.BroadcastCoordinationVoteRequestAsync(mintRequestId, mintTransactionId, request.DestinationChain).ConfigureAwait(false); } else - this.logger.Info("Insufficient reserve balance remaining, waiting for originator to initiate mint transaction to replenish reserve."); + this.logger.LogInformation("Insufficient reserve balance remaining, waiting for originator to initiate mint transaction to replenish reserve."); BigInteger agreedTransactionId; @@ -781,7 +783,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg agreedTransactionId = this.conversionRequestCoordinationService.GetAgreedTransactionId(mintRequestId, this.interopSettings.GetSettingsByChain(request.DestinationChain).MultisigWalletQuorum); - this.logger.Debug("Agreed transaction id '{0}'.", agreedTransactionId); + this.logger.LogDebug("Agreed transaction id '{0}'.", agreedTransactionId); if (agreedTransactionId != BigInteger.MinusOne) break; @@ -789,7 +791,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg // Just re-broadcast. if (originator) { - this.logger.Debug("Originator broadcasting id {0}.", mintTransactionId); + this.logger.LogDebug("Originator broadcasting id {0}.", mintTransactionId); await this.BroadcastCoordinationVoteRequestAsync(mintRequestId, mintTransactionId, request.DestinationChain).ConfigureAwait(false); } @@ -798,7 +800,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg if (ourTransactionId == BigInteger.MinusOne) ourTransactionId = this.conversionRequestCoordinationService.GetCandidateTransactionId(mintRequestId); - this.logger.Debug("Non-originator broadcasting id {0}.", ourTransactionId); + this.logger.LogDebug("Non-originator broadcasting id {0}.", ourTransactionId); if (ourTransactionId != BigInteger.MinusOne) { @@ -814,7 +816,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false); } - this.logger.Info("Agreed transaction ID for replenishment transaction: {0}", agreedTransactionId); + this.logger.LogInformation("Agreed transaction ID for replenishment transaction: {0}", agreedTransactionId); if (!originator) { @@ -824,11 +826,11 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg if (gasPrice == -1) gasPrice = this.interopSettings.GetSettingsByChain(request.DestinationChain).GasPrice; - this.logger.Info("Non-originator will use a gas price of {0} to confirm the mint replenishment transaction.", gasPrice); + this.logger.LogInformation("Non-originator will use a gas price of {0} to confirm the mint replenishment transaction.", gasPrice); string confirmation = await this.ethClientProvider.GetClientForChain(request.DestinationChain).ConfirmTransactionAsync(agreedTransactionId, gasPrice).ConfigureAwait(false); - this.logger.Info("ID of confirmation transaction: {0}", confirmation); + this.logger.LogInformation("ID of confirmation transaction: {0}", confirmation); } while (true) @@ -838,7 +840,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg BigInteger confirmationCount = await this.ethClientProvider.GetClientForChain(request.DestinationChain).GetMultisigConfirmationCountAsync(agreedTransactionId).ConfigureAwait(false); - this.logger.Info("Waiting for confirmation of mint replenishment transaction {0}, current count {1}.", mintRequestId, confirmationCount); + this.logger.LogInformation("Waiting for confirmation of mint replenishment transaction {0}, current count {1}.", mintRequestId, confirmationCount); if (confirmationCount >= this.interopSettings.GetSettingsByChain(request.DestinationChain).MultisigWalletQuorum) break; @@ -847,7 +849,7 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false); } - this.logger.Info("Mint replenishment transaction {0} fully confirmed.", mintTransactionId); + this.logger.LogInformation("Mint replenishment transaction {0} fully confirmed.", mintTransactionId); while (true) { @@ -858,11 +860,11 @@ private async Task PerformReplenishmentAsync(ConversionRequest request, BigInteg if (balance > startBalance) { - this.logger.Info("The contract's balance has been replenished, new balance {0}.", balance); + this.logger.LogInformation("The contract's balance has been replenished, new balance {0}.", balance); break; } else - this.logger.Info("The contract's balance is unchanged at {0}.", balance); + this.logger.LogInformation("The contract's balance is unchanged at {0}.", balance); await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false); } diff --git a/src/Stratis.Bitcoin.Features.Interop/Stratis.Bitcoin.Features.Interop.csproj b/src/Stratis.Bitcoin.Features.Interop/Stratis.Bitcoin.Features.Interop.csproj index a51fa86041..adb2719f73 100644 --- a/src/Stratis.Bitcoin.Features.Interop/Stratis.Bitcoin.Features.Interop.csproj +++ b/src/Stratis.Bitcoin.Features.Interop/Stratis.Bitcoin.Features.Interop.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. Stratis.Features.Interop Stratis.Features.Interop @@ -10,6 +10,7 @@ bin\Debug\netcoreapp3.1\Stratis.Bitcoin.Features.Interop.xml + 1701;1702;1591 diff --git a/src/Stratis.Bitcoin.Features.LightWallet/Stratis.Bitcoin.Features.LightWallet.csproj b/src/Stratis.Bitcoin.Features.LightWallet/Stratis.Bitcoin.Features.LightWallet.csproj index 8908f0672d..c689b947a8 100644 --- a/src/Stratis.Bitcoin.Features.LightWallet/Stratis.Bitcoin.Features.LightWallet.csproj +++ b/src/Stratis.Bitcoin.Features.LightWallet/Stratis.Bitcoin.Features.LightWallet.csproj @@ -7,7 +7,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.MemoryPool.Tests/MempoolValidatorTest.cs b/src/Stratis.Bitcoin.Features.MemoryPool.Tests/MempoolValidatorTest.cs index f26c6ed35c..34728fa25e 100644 --- a/src/Stratis.Bitcoin.Features.MemoryPool.Tests/MempoolValidatorTest.cs +++ b/src/Stratis.Bitcoin.Features.MemoryPool.Tests/MempoolValidatorTest.cs @@ -180,6 +180,7 @@ public async Task AcceptToMemoryPool_WithValidP2PKHTxn_IsSuccessfulAsync() /// Validate multi input/output P2PK, P2PKH transactions in memory pool. /// Transaction scenario adapted from code project article referenced below. /// + /// The asynchronous task. /// [Fact] public async Task AcceptToMemoryPool_WithMultiInOutValidTxns_IsSuccessfulAsync() @@ -244,6 +245,7 @@ public async Task AcceptToMemoryPool_WithMultiInOutValidTxns_IsSuccessfulAsync() /// Validate multi sig transactions in memory pool. /// Transaction scenario adapted from code project article referenced below. /// + /// The asynchronous task. /// [Fact] public async Task AcceptToMemoryPool_WithMultiSigValidTxns_IsSuccessfulAsync() @@ -312,6 +314,7 @@ public async Task AcceptToMemoryPool_WithMultiSigValidTxns_IsSuccessfulAsync() /// Validate P2SH transaction in memory pool. /// Transaction scenario adapted from code project article referenced below. /// + /// The asynchronous task. /// [Fact] public async Task AcceptToMemoryPool_WithP2SHValidTxns_IsSuccessfulAsync() @@ -373,6 +376,7 @@ public async Task AcceptToMemoryPool_WithP2SHValidTxns_IsSuccessfulAsync() /// /// Validate P2WPKH transaction in memory pool. /// + /// The asynchronous task. [Fact] public async Task AcceptToMemoryPool_WithP2WPKHValidTxns_IsSuccessfulAsync() { @@ -517,6 +521,7 @@ public async Task AcceptToMemoryPool_SpendingP2WPKH_WithMissingWitnessData_Retur /// /// Validate P2WSH transaction in memory pool. /// + /// The asynchronous task. [Fact] public async Task AcceptToMemoryPool_WithP2WSHValidTxns_IsSuccessfulAsync() { @@ -1034,7 +1039,7 @@ public async Task AcceptToMemoryPool_NonBIP68CanMine_ReturnsFalseAsync() } [Fact] - public async Task AcceptToMemoryPool_NonStandardBareMultiSig_ReturnsFalse() + public async Task AcceptToMemoryPool_NonStandardBareMultiSig_ReturnsFalseAsync() { string dataDir = GetTestDirectoryPath(this); @@ -1686,9 +1691,10 @@ public async Task AcceptToMemoryPool_TxPowConsensusCheckInputBadTransactionInBel } [Fact(Skip = "Not clear how to test this without triggering In Below Out check instead.")] - public async Task AcceptToMemoryPool_TxPowConsensusCheckInputNegativeFee_ReturnsFalseAsync() + public Task AcceptToMemoryPool_TxPowConsensusCheckInputNegativeFee_ReturnsFalse() { // TODO: Execute failure case for CheckAllInputs CheckInputs PowCoinViewRule.CheckInputs NegativeFee + return Task.CompletedTask; } [Fact(Skip = "Making transactions with very large inputs/outputs pass validation is difficult, WIP.")] diff --git a/src/Stratis.Bitcoin.Features.MemoryPool/BlocksDisconnectedSignaled.cs b/src/Stratis.Bitcoin.Features.MemoryPool/BlocksDisconnectedSignaled.cs index f70667fe77..bd55d61843 100644 --- a/src/Stratis.Bitcoin.Features.MemoryPool/BlocksDisconnectedSignaled.cs +++ b/src/Stratis.Bitcoin.Features.MemoryPool/BlocksDisconnectedSignaled.cs @@ -53,6 +53,7 @@ private void OnBlockDisconnected(BlockDisconnected blockDisconnected) /// /// This could potentially be optimized. with an async queue. /// The disconnected block containing the transactions. + /// The asynchronous task. private async Task AddBackToMempoolAsync(Block block) { var state = new MempoolValidationState(true); @@ -78,6 +79,8 @@ await this.mempoolLock.WriteAsync(async () => /// /// If there are any transactions in the mempool that depend on transactions no longer in the chain, remove them. /// + /// The block containing the coinbase that should not be spent. + /// The asynchronous task. private async Task RemoveInvalidTransactionsAsync(Block block) { // TODO: This was initially implemented only to fix a known issue on Cirrus. @@ -96,7 +99,7 @@ private async Task RemoveInvalidTransactionsAsync(Block block) // Invalid transactions would have spent the coinbase. The other transactions can be put back into the mempool and be fine. uint256 coinbaseId = block.Transactions[0].GetHash(); - await this.mempoolLock.WriteAsync(async () => + await this.mempoolLock.WriteAsync(() => { foreach (TxMempoolEntry mempoolEntry in this.mempool.MapTx.SpendsCoinbase.ToList()) { diff --git a/src/Stratis.Bitcoin.Features.MemoryPool/MempoolFeature.cs b/src/Stratis.Bitcoin.Features.MemoryPool/MempoolFeature.cs index 607284ea37..2159ad3e8b 100644 --- a/src/Stratis.Bitcoin.Features.MemoryPool/MempoolFeature.cs +++ b/src/Stratis.Bitcoin.Features.MemoryPool/MempoolFeature.cs @@ -52,6 +52,7 @@ public class MempoolFeature : FullNodeFeature /// Memory pool node behavior for managing attached node messages. /// Memory pool manager for managing external access to memory pool. /// Logger factory for creating instance logger. + /// See . public MempoolFeature( IConnectionManager connectionManager, MempoolSignaled mempoolSignaled, @@ -142,7 +143,7 @@ public static class FullNodeBuilderMempoolExtension /// Include the memory pool feature and related services in the full node. /// /// Full node builder. - /// Full node builder. + /// The full node builder. public static IFullNodeBuilder UseMempool(this IFullNodeBuilder fullNodeBuilder) { LoggingConfiguration.RegisterFeatureNamespace("mempool"); diff --git a/src/Stratis.Bitcoin.Features.MemoryPool/Rules/StraxCoinViewMempoolRule.cs b/src/Stratis.Bitcoin.Features.MemoryPool/Rules/StraxCoinViewMempoolRule.cs index e1cc5e170e..6421bc30f9 100644 --- a/src/Stratis.Bitcoin.Features.MemoryPool/Rules/StraxCoinViewMempoolRule.cs +++ b/src/Stratis.Bitcoin.Features.MemoryPool/Rules/StraxCoinViewMempoolRule.cs @@ -20,6 +20,7 @@ public StraxCoinViewMempoolRule(Network network, { } + /// /// Also see > public override void CheckTransaction(MempoolValidationContext context) { diff --git a/src/Stratis.Bitcoin.Features.MemoryPool/Stratis.Bitcoin.Features.MemoryPool.csproj b/src/Stratis.Bitcoin.Features.MemoryPool/Stratis.Bitcoin.Features.MemoryPool.csproj index c51a7792a7..b419d37d2c 100644 --- a/src/Stratis.Bitcoin.Features.MemoryPool/Stratis.Bitcoin.Features.MemoryPool.csproj +++ b/src/Stratis.Bitcoin.Features.MemoryPool/Stratis.Bitcoin.Features.MemoryPool.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False library Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.Miner/BlockDefinition.cs b/src/Stratis.Bitcoin.Features.Miner/BlockDefinition.cs index 04bb3cf238..d3123ab871 100644 --- a/src/Stratis.Bitcoin.Features.Miner/BlockDefinition.cs +++ b/src/Stratis.Bitcoin.Features.Miner/BlockDefinition.cs @@ -266,11 +266,13 @@ public virtual void BlockModified(ChainedHeader chainTip, Block block) /// /// Network specific logic to add a transaction to the block from a given mempool entry. /// + /// The . public abstract void AddToBlock(TxMempoolEntry mempoolEntry); /// /// Adds a transaction to the block and updates the and values. /// + /// The . protected void AddTransactionToBlock(Transaction transaction) { this.block.AddTransaction(transaction); @@ -285,6 +287,7 @@ protected void AddTransactionToBlock(Transaction transaction) /// The block's and values are adjusted. /// /// + /// The . protected void UpdateBlockStatistics(TxMempoolEntry mempoolEntry) { this.BlockSigOpsCost += mempoolEntry.SigOpCost; @@ -295,6 +298,7 @@ protected void UpdateBlockStatistics(TxMempoolEntry mempoolEntry) /// /// Updates the total fee amount for this block. /// + /// The fee to add to the total fee amount for the block. protected void UpdateTotalFees(Money fee) { this.fees += fee; @@ -316,6 +320,8 @@ protected void UpdateTotalFees(Money fee) /// mapModifiedTxs with the next transaction in the mempool to decide what /// transaction package to work on next. /// + /// Updated packages selected statistic. + /// Updated descendants updated statistic. protected virtual void AddTransactions(out int nPackagesSelected, out int nDescendantsUpdated) { nPackagesSelected = 0; @@ -488,6 +494,7 @@ protected virtual void AddTransactions(out int nPackagesSelected, out int nDesce /// /// Remove confirmed entries from given set. /// + /// The confirmed . private void OnlyUnconfirmed(TxMempool.SetEntries testSet) { foreach (TxMempoolEntry setEntry in testSet.ToList()) @@ -503,6 +510,10 @@ private void OnlyUnconfirmed(TxMempool.SetEntries testSet) /// /// Test if a new package would "fit" in the block. /// + /// The . + /// The package size. + /// The package sigops cost. + /// True if the package would fit in the block or false otherwise. protected virtual bool TestPackage(TxMempoolEntry entry, long packageSize, long packageSigOpsCost) { // TODO: Switch to weight-based accounting for packages instead of vsize-based accounting. @@ -532,6 +543,8 @@ protected virtual bool TestPackage(TxMempoolEntry entry, long packageSize, long /// /// /// + /// The package. + /// True if the checks have passed or false otherwise. private bool TestPackageTransactions(TxMempool.SetEntries package) { foreach (TxMempoolEntry it in package) @@ -561,6 +574,9 @@ private bool TestPackageTransactions(TxMempool.SetEntries package) /// state updated assuming given transactions are inBlock. Returns number /// of updated descendants. /// + /// The . + /// The map to add the descendants to. + /// The number of updated descendants. private int UpdatePackagesForAdded(TxMempool.SetEntries alreadyAdded, Dictionary mapModifiedTx) { int descendantsUpdated = 0; @@ -605,6 +621,9 @@ private int UpdatePackagesForAdded(TxMempool.SetEntries alreadyAdded, Dictionary } /// Network specific logic specific as to how the block will be built. + /// The chain tip. + /// The scriptpubkey. + /// The . public abstract BlockTemplate Build(ChainedHeader chainTip, Script scriptPubKey); /// Update the block's header information. diff --git a/src/Stratis.Bitcoin.Features.Miner/BlockDefinitionOptions.cs b/src/Stratis.Bitcoin.Features.Miner/BlockDefinitionOptions.cs index 11b4646600..6f02e00cb2 100644 --- a/src/Stratis.Bitcoin.Features.Miner/BlockDefinitionOptions.cs +++ b/src/Stratis.Bitcoin.Features.Miner/BlockDefinitionOptions.cs @@ -31,6 +31,8 @@ public BlockDefinitionOptions(uint blockMaxWeight, uint blockMaxSize) /// Restrict the options to within those allowed by network consensus rules. /// If set values are outside those allowed by consensus, set to nearest allowed value (minimum or maximum). /// + /// The network context. + /// The . public BlockDefinitionOptions RestrictForNetwork(Network network) { uint minAllowedBlockWeight = MinBlockSize * (uint) network.Consensus.Options.WitnessScaleFactor; diff --git a/src/Stratis.Bitcoin.Features.Miner/BlockProvider.cs b/src/Stratis.Bitcoin.Features.Miner/BlockProvider.cs index fde2519c0a..260897b369 100644 --- a/src/Stratis.Bitcoin.Features.Miner/BlockProvider.cs +++ b/src/Stratis.Bitcoin.Features.Miner/BlockProvider.cs @@ -19,6 +19,8 @@ public sealed class BlockProvider : IBlockProvider /// Defines how proof of work blocks are built on a Proof-of-Stake network. private readonly PosPowBlockDefinition posPowBlockDefinition; + /// The instance constructor. + /// The network context. /// A list of block definitions that the builder can utilize. public BlockProvider(Network network, IEnumerable definitions) { diff --git a/src/Stratis.Bitcoin.Features.Miner/MiningFeature.cs b/src/Stratis.Bitcoin.Features.Miner/MiningFeature.cs index 9fc1f7cf08..b9d309e701 100644 --- a/src/Stratis.Bitcoin.Features.Miner/MiningFeature.cs +++ b/src/Stratis.Bitcoin.Features.Miner/MiningFeature.cs @@ -260,6 +260,7 @@ public static IFullNodeBuilder AddMining(this IFullNodeBuilder fullNodeBuilder) /// Adds POW and POS miner components to the node, so that it can mine or stake. /// /// The object used to build the current node. + /// Indicates whether the class should be used. /// The full node builder, enriched with the new component. public static IFullNodeBuilder AddPowPosMining(this IFullNodeBuilder fullNodeBuilder, bool straxMode) { diff --git a/src/Stratis.Bitcoin.Features.Miner/PowMining.cs b/src/Stratis.Bitcoin.Features.Miner/PowMining.cs index 929c43548c..3b80384b0b 100644 --- a/src/Stratis.Bitcoin.Features.Miner/PowMining.cs +++ b/src/Stratis.Bitcoin.Features.Miner/PowMining.cs @@ -192,6 +192,8 @@ public List GenerateBlocks(ReserveScript reserveScript, ulong amountOfB /// /// Ensures that the node is synced before mining is allowed to start. /// + /// The . + /// True if the node is synced and false otherwise. private bool ConsensusIsAtTip(MineBlockContext context) { this.miningCancellationTokenSource.Token.ThrowIfCancellationRequested(); @@ -219,6 +221,8 @@ private bool ConsensusIsAtTip(MineBlockContext context) /// generation of blocks inside tests, where it is possible to generate multiple blocks within one second. /// /// + /// The . + /// True if the block was successfully built or false otherwise. private bool BuildBlock(MineBlockContext context) { context.BlockTemplate = this.blockProvider.BuildPowBlock(context.ChainTip, context.ReserveScript.ReserveFullNodeScript); @@ -235,6 +239,8 @@ private bool BuildBlock(MineBlockContext context) /// /// Executes until the required work (difficulty) has been reached. This is the "mining" process. /// + /// The . + /// True if the block was successfully mined or false otherwise. private bool MineBlock(MineBlockContext context) { if (this.network.Consensus.LastPOWBlock != 0 && context.ChainTip.Height > this.network.Consensus.LastPOWBlock) @@ -263,6 +269,8 @@ private bool MineBlock(MineBlockContext context) /// /// Ensures that the block was properly mined by checking the block's work against the next difficulty target. /// + /// The . + /// True if the block passed validation or false otherwise. private bool ValidateMinedBlock(MineBlockContext context) { if (context.BlockTemplate.Block.Header.Nonce == InnerLoopCount) @@ -281,6 +289,8 @@ private bool ValidateMinedBlock(MineBlockContext context) /// On successful block validation the block will be connected to the chain. /// /// + /// The . + /// True if the block was successfully validated and connected or false otherwise. private bool ValidateAndConnectBlock(MineBlockContext context) { ChainedHeader chainedHeader = this.consensusManager.BlockMinedAsync(context.BlockTemplate.Block).GetAwaiter().GetResult(); diff --git a/src/Stratis.Bitcoin.Features.Miner/Stratis.Bitcoin.Features.Miner.csproj b/src/Stratis.Bitcoin.Features.Miner/Stratis.Bitcoin.Features.Miner.csproj index 938e15a118..8a4a2276df 100644 --- a/src/Stratis.Bitcoin.Features.Miner/Stratis.Bitcoin.Features.Miner.csproj +++ b/src/Stratis.Bitcoin.Features.Miner/Stratis.Bitcoin.Features.Miner.csproj @@ -1,4 +1,4 @@ - + Stratis Bitcoin Features Miner @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.Notifications/Stratis.Bitcoin.Features.Notifications.csproj b/src/Stratis.Bitcoin.Features.Notifications/Stratis.Bitcoin.Features.Notifications.csproj index d157857348..2a08f51253 100644 --- a/src/Stratis.Bitcoin.Features.Notifications/Stratis.Bitcoin.Features.Notifications.csproj +++ b/src/Stratis.Bitcoin.Features.Notifications/Stratis.Bitcoin.Features.Notifications.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.PoA.IntegrationTests.Common/Stratis.Bitcoin.Features.PoA.IntegrationTests.Common.csproj b/src/Stratis.Bitcoin.Features.PoA.IntegrationTests.Common/Stratis.Bitcoin.Features.PoA.IntegrationTests.Common.csproj index 6a689853df..4816b77229 100644 --- a/src/Stratis.Bitcoin.Features.PoA.IntegrationTests.Common/Stratis.Bitcoin.Features.PoA.IntegrationTests.Common.csproj +++ b/src/Stratis.Bitcoin.Features.PoA.IntegrationTests.Common/Stratis.Bitcoin.Features.PoA.IntegrationTests.Common.csproj @@ -13,7 +13,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False diff --git a/src/Stratis.Bitcoin.Features.PoA.IntegrationTests/VotingAndMiningTests.cs b/src/Stratis.Bitcoin.Features.PoA.IntegrationTests/VotingAndMiningTests.cs index 3bdb73dbe3..2ac9f23f68 100644 --- a/src/Stratis.Bitcoin.Features.PoA.IntegrationTests/VotingAndMiningTests.cs +++ b/src/Stratis.Bitcoin.Features.PoA.IntegrationTests/VotingAndMiningTests.cs @@ -393,7 +393,7 @@ public async Task TransactionSentFeesReceivedByMinerAsync() Assert.True(context.TransactionBuilder.Verify(trx, out _)); - await nodeA.FullNode.NodeController().SendTransaction(new SendTransactionRequest(trx.ToHex())); + await nodeA.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(trx.ToHex())); TestBase.WaitLoop(() => nodeA.CreateRPCClient().GetRawMempool().Length == 1 && nodeB.CreateRPCClient().GetRawMempool().Length == 1); @@ -451,7 +451,7 @@ public async Task CanMineVotingRequestTransactionAsync() var encoder = new JoinFederationRequestEncoder(); JoinFederationRequestResult result = JoinFederationRequestBuilder.BuildTransaction(nodeA.FullNode.WalletTransactionHandler(), this.poaNetwork, request, encoder, walletName, walletAccount, walletPassword); - await nodeA.FullNode.NodeController().SendTransaction(new SendTransactionRequest(result.Transaction.ToHex())); + await nodeA.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(result.Transaction.ToHex())); TestBase.WaitLoop(() => nodeA.CreateRPCClient().GetRawMempool().Length == 1 && nodeB.CreateRPCClient().GetRawMempool().Length == 1); diff --git a/src/Stratis.Bitcoin.Features.PoA.Tests/PoATestsBase.cs b/src/Stratis.Bitcoin.Features.PoA.Tests/PoATestsBase.cs index 0bdd24def6..f0429d0fa4 100644 --- a/src/Stratis.Bitcoin.Features.PoA.Tests/PoATestsBase.cs +++ b/src/Stratis.Bitcoin.Features.PoA.Tests/PoATestsBase.cs @@ -59,7 +59,7 @@ public PoATestsBase(TestPoANetwork network = null) (this.federationManager, this.federationHistory) = CreateFederationManager(this, this.network, this.loggerFactory, this.signals); - this.slotsManager = new SlotsManager(this.network, this.federationManager, this.ChainIndexer, this.loggerFactory); + this.slotsManager = new SlotsManager(this.network, this.federationManager, this.federationHistory, this.ChainIndexer); this.poaHeaderValidator = new PoABlockHeaderValidator(this.loggerFactory); this.asyncProvider = new AsyncProvider(this.loggerFactory, this.signals); @@ -120,7 +120,7 @@ public static (IFederationManager federationManager, IFederationHistory federati return members[chainedHeader.Height % members.Count]; }); - federationHistory.Setup(x => x.GetFederationForBlock(It.IsAny())).Returns((chainedHeader) => + federationHistory.Setup(x => x.GetFederationForBlock(It.IsAny(), It.IsAny())).Returns((chainedHeader, offset) => { return ((PoAConsensusOptions)network.Consensus.Options).GenesisFederationMembers; }); diff --git a/src/Stratis.Bitcoin.Features.PoA.Tests/Rules/PoAVotingCoinbaseOutputFormatRuleTests.cs b/src/Stratis.Bitcoin.Features.PoA.Tests/Rules/PoAVotingCoinbaseOutputFormatRuleTests.cs index 2c1b9fb729..8a714a09ff 100644 --- a/src/Stratis.Bitcoin.Features.PoA.Tests/Rules/PoAVotingCoinbaseOutputFormatRuleTests.cs +++ b/src/Stratis.Bitcoin.Features.PoA.Tests/Rules/PoAVotingCoinbaseOutputFormatRuleTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using NBitcoin; -using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Consensus.Rules; using Stratis.Bitcoin.Features.PoA.Voting; diff --git a/src/Stratis.Bitcoin.Features.PoA.Tests/SlotsManagerTests.cs b/src/Stratis.Bitcoin.Features.PoA.Tests/SlotsManagerTests.cs index fcf198562b..e7d29c9e67 100644 --- a/src/Stratis.Bitcoin.Features.PoA.Tests/SlotsManagerTests.cs +++ b/src/Stratis.Bitcoin.Features.PoA.Tests/SlotsManagerTests.cs @@ -15,6 +15,7 @@ public class SlotsManagerTests private TestPoANetwork network; private readonly PoAConsensusOptions consensusOptions; private readonly IFederationManager federationManager; + private readonly IFederationHistory federationHistory; private Mock chainIndexer; public SlotsManagerTests() @@ -22,9 +23,9 @@ public SlotsManagerTests() this.network = new TestPoANetwork(); this.consensusOptions = this.network.ConsensusOptions; - this.federationManager = PoATestsBase.CreateFederationManager(this).federationManager; + (this.federationManager, this.federationHistory) = PoATestsBase.CreateFederationManager(this); this.chainIndexer = new Mock(); - this.slotsManager = new SlotsManager(this.network, this.federationManager, this.chainIndexer.Object, new LoggerFactory()); + this.slotsManager = new SlotsManager(this.network, this.federationManager, this.federationHistory, this.chainIndexer.Object); } [Fact] @@ -45,10 +46,10 @@ public void GetMiningTimestamp() Key key = tool.GeneratePrivateKey(); this.network = new TestPoANetwork(new List() { tool.GeneratePrivateKey().PubKey, key.PubKey, tool.GeneratePrivateKey().PubKey }); - IFederationManager fedManager = PoATestsBase.CreateFederationManager(this, this.network, new ExtendedLoggerFactory(), new Signals.Signals(new LoggerFactory(), null)).federationManager; + (IFederationManager fedManager, IFederationHistory federationHistory) = PoATestsBase.CreateFederationManager(this, this.network, new ExtendedLoggerFactory(), new Signals.Signals(new LoggerFactory(), null)); var header = new BlockHeader(); this.chainIndexer.Setup(x => x.Tip).Returns(new ChainedHeader(header, header.GetHash(), 0)); - this.slotsManager = new SlotsManager(this.network, fedManager, this.chainIndexer.Object, new LoggerFactory()); + this.slotsManager = new SlotsManager(this.network, fedManager, federationHistory, this.chainIndexer.Object); List federationMembers = fedManager.GetFederationMembers(); uint roundStart = this.consensusOptions.TargetSpacingSeconds * (uint)federationMembers.Count * 5; @@ -81,8 +82,13 @@ public void GetMiningTimestamp() Time = thisTurnTimestamp }; + Mock.Get(federationHistory).Setup(x => x.GetFederationMemberForBlock(It.IsAny())).Returns((chainedHeader) => + { + return federationMembers[1]; + }); + this.chainIndexer.Setup(x => x.Tip).Returns(new ChainedHeader(header, header.GetHash(), 0)); - this.slotsManager = new SlotsManager(this.network, fedManager, this.chainIndexer.Object, new LoggerFactory()); + this.slotsManager = new SlotsManager(this.network, fedManager, federationHistory, this.chainIndexer.Object); Assert.Equal(nextTurnTimestamp, this.slotsManager.GetMiningTimestamp(thisTurnTimestamp + 1)); } diff --git a/src/Stratis.Bitcoin.Features.PoA.Tests/VotingDataEncoderTests.cs b/src/Stratis.Bitcoin.Features.PoA.Tests/VotingDataEncoderTests.cs index a628902223..18707d543d 100644 --- a/src/Stratis.Bitcoin.Features.PoA.Tests/VotingDataEncoderTests.cs +++ b/src/Stratis.Bitcoin.Features.PoA.Tests/VotingDataEncoderTests.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Text; using NBitcoin; -using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Features.PoA.Voting; using Xunit; diff --git a/src/Stratis.Bitcoin.Features.PoA/BasePoAFeatureConsensusRules/PoAHeaderSignatureRule.cs b/src/Stratis.Bitcoin.Features.PoA/BasePoAFeatureConsensusRules/PoAHeaderSignatureRule.cs index 3181205fa2..1055896814 100644 --- a/src/Stratis.Bitcoin.Features.PoA/BasePoAFeatureConsensusRules/PoAHeaderSignatureRule.cs +++ b/src/Stratis.Bitcoin.Features.PoA/BasePoAFeatureConsensusRules/PoAHeaderSignatureRule.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NBitcoin; @@ -34,15 +36,15 @@ public override void Initialize() this.federationHistory = engine.FederationHistory; this.validator = engine.PoaHeaderValidator; - var lastCheckPoint = engine.Network.Checkpoints.LastOrDefault(); + KeyValuePair lastCheckPoint = engine.Network.Checkpoints.LastOrDefault(); this.lastCheckPoint = (lastCheckPoint.Value != null) ? new HashHeightPair(lastCheckPoint.Value.Hash, lastCheckPoint.Key) : null; } - public override async Task RunAsync(RuleContext context) + public override Task RunAsync(RuleContext context) { // Only start validating at the last checkpoint block. if (context.ValidationContext.ChainedHeaderToValidate.Height < (this.lastCheckPoint?.Height ?? 0)) - return; + return Task.CompletedTask; ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate; @@ -71,12 +73,12 @@ public override async Task RunAsync(RuleContext context) } // Look at the last round of blocks to find the previous time that the miner mined. - var roundTime = this.slotsManager.GetRoundLength(this.federationHistory.GetFederationForBlock(chainedHeader).Count); + TimeSpan roundTime = this.slotsManager.GetRoundLength(this.federationHistory.GetFederationForBlock(chainedHeader).Count); // Quick check for optimisation. this.federationHistory.GetLastActiveTime(federationMember, chainedHeader.Previous, out uint lastActiveTime); if ((chainedHeader.Header.Time - lastActiveTime) >= roundTime.TotalSeconds) - return; + return Task.CompletedTask; int blockCounter = 0; @@ -103,6 +105,8 @@ public override async Task RunAsync(RuleContext context) this.Logger.LogTrace("(-)[TIME_TOO_EARLY]"); ConsensusErrors.BlockTimestampTooEarly.Throw(); } + + return Task.CompletedTask; } } } \ No newline at end of file diff --git a/src/Stratis.Bitcoin.Features.PoA/Collateral/CollateralHeightCommitmentEncoder.cs b/src/Stratis.Bitcoin.Features.PoA/Collateral/CollateralHeightCommitmentEncoder.cs index 6de818eadd..fdf9a0a662 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Collateral/CollateralHeightCommitmentEncoder.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Collateral/CollateralHeightCommitmentEncoder.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; namespace Stratis.Features.PoA.Collateral { @@ -40,7 +41,7 @@ public byte[] EncodeCommitmentHeight(int height) byte[] commitmentData = null; byte[] magic = null; - this.logger.Debug("Transaction contains {0} OP_RETURN outputs.", opReturnOutputs.Count()); + this.logger.LogDebug("Transaction contains {0} OP_RETURN outputs.", opReturnOutputs.Count()); foreach (Script script in opReturnOutputs) { @@ -55,7 +56,7 @@ public byte[] EncodeCommitmentHeight(int height) if (!correctPrefix) { - this.logger.Debug("Push data contains incorrect prefix for height commitment."); + this.logger.LogDebug("Push data contains incorrect prefix for height commitment."); continue; } diff --git a/src/Stratis.Bitcoin.Features.PoA/FederationHistory.cs b/src/Stratis.Bitcoin.Features.PoA/FederationHistory.cs index a74dacda09..a74e0ed72f 100644 --- a/src/Stratis.Bitcoin.Features.PoA/FederationHistory.cs +++ b/src/Stratis.Bitcoin.Features.PoA/FederationHistory.cs @@ -27,8 +27,9 @@ public interface IFederationHistory /// Gets the federation for a specified block. /// Identifies the block and timestamp. + /// Identifies a block relative to /// The federation member or null if the member could not be determined. - List GetFederationForBlock(ChainedHeader chainedHeader); + List GetFederationForBlock(ChainedHeader chainedHeader, int offset = 0); /// /// Determines when a federation member was last active. This includes mining or joining. @@ -96,11 +97,12 @@ public bool CanGetFederationForBlock(ChainedHeader chainedHeader) } /// - public List GetFederationForBlock(ChainedHeader chainedHeader) + public List GetFederationForBlock(ChainedHeader chainedHeader, int offset = 0) { lock (this.lockObject) { - if ((this.lastActiveTip == chainedHeader || this.lastActiveTip?.FindFork(chainedHeader)?.Height >= chainedHeader.Height) && + if (this.lastFederationTip >= (chainedHeader.Height + offset) && + (this.lastActiveTip == chainedHeader || this.lastActiveTip?.FindFork(chainedHeader)?.Height >= chainedHeader.Height) && this.federationHistory.TryGetValue(chainedHeader.Height, out (List modifiedFederation, HashSet whoJoined, IFederationMember miner) item)) { return item.modifiedFederation; @@ -347,7 +349,7 @@ ChainedHeader GetHeader(int height) return blockHeader.GetAncestor(height); } - + // Find the first block with Time >= minTime. We're not interested in re-reading any blocks below or at the last active tip though. int startHeight = (this.lastActiveTip?.Height ?? -1) + 1; startHeight = BinarySearch.BinaryFindFirst(n => GetHeader(n).Header.Time >= minTime, startHeight, blockHeader.Height - startHeight + 1); @@ -373,7 +375,8 @@ ChainedHeader GetHeader(int height) foreach (ChainedHeader header in blockHeader.EnumerateToGenesis().Take(miners.Length).Reverse()) { - var history = this.federationHistory[header.Height]; + // Add the miner to the history. + (List members, HashSet joined, IFederationMember miner) history = this.federationHistory[header.Height]; history.miner = miners[header.Height - startHeight]; this.federationHistory[header.Height] = history; diff --git a/src/Stratis.Bitcoin.Features.PoA/FederationManager.cs b/src/Stratis.Bitcoin.Features.PoA/FederationManager.cs index 6a6d321775..dde8319dac 100644 --- a/src/Stratis.Bitcoin.Features.PoA/FederationManager.cs +++ b/src/Stratis.Bitcoin.Features.PoA/FederationManager.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Features.PoA.Events; using Stratis.Bitcoin.Features.PoA.Voting; @@ -31,11 +32,12 @@ public interface IFederationManager void Initialize(); - /// Provides up to date list of federation members. + /// Provides up-to-date list of federation members. /// /// Blocks that are not signed with private keys that correspond /// to public keys from this list are considered to be invalid. /// + /// An up-to-date list of federation members. List GetFederationMembers(); bool IsMultisigMember(PubKey pubKey); @@ -101,19 +103,19 @@ public void Initialize() { var genesisFederation = new List(this.network.ConsensusOptions.GenesisFederationMembers); - this.logger.Info("Genesis federation contains {0} members. Their public keys are: {1}", genesisFederation.Count, $"{Environment.NewLine}{string.Join(Environment.NewLine, genesisFederation)}"); + this.logger.LogInformation("Genesis federation contains {0} members. Their public keys are: {1}", genesisFederation.Count, $"{Environment.NewLine}{string.Join(Environment.NewLine, genesisFederation)}"); // Load federation from the db. this.LoadFederation(); if (this.federationMembers == null) { - this.logger.Debug("Federation members are not stored in the database, using genesis federation members."); + this.logger.LogDebug("Federation members are not stored in the database, using genesis federation members."); this.federationMembers = genesisFederation; } // Display federation. - this.logger.Info("Current federation contains {0} members. Their public keys are: {1}", this.federationMembers.Count, Environment.NewLine + string.Join(Environment.NewLine, this.federationMembers)); + this.logger.LogInformation("Current federation contains {0} members. Their public keys are: {1}", this.federationMembers.Count, Environment.NewLine + string.Join(Environment.NewLine, this.federationMembers)); // Set the current federation member's key. if (!InitializeFederationMemberKey()) @@ -122,14 +124,14 @@ public void Initialize() // Loaded key has to be a key for current federation. if (!this.federationMembers.Any(x => x.PubKey == this.CurrentFederationKey.PubKey)) { - this.logger.Warn("Key provided is not registered on the network."); + this.logger.LogWarning("Key provided is not registered on the network."); } // TODO This will be removed once we remove the distinction between FederationMember and CollateralFederationMember if (this.federationMembers.Any(f => f is CollateralFederationMember)) CheckCollateralMembers(); - this.logger.Info("Federation key pair was successfully loaded. Your public key is: '{0}'.", this.CurrentFederationKey.PubKey); + this.logger.LogInformation("Federation key pair was successfully loaded. Your public key is: '{0}'.", this.CurrentFederationKey.PubKey); } private bool InitializeFederationMemberKey() @@ -140,7 +142,7 @@ private bool InitializeFederationMemberKey() Key key = new KeyTool(this.nodeSettings.DataFolder).LoadPrivateKey(); if (key == null) { - this.logger.Warn("No federation key was loaded from 'federationKey.dat'."); + this.logger.LogWarning("No federation key was loaded from 'federationKey.dat'."); return false; } @@ -149,7 +151,7 @@ private bool InitializeFederationMemberKey() if (this.CurrentFederationKey == null) { - this.logger.Trace("(-)[NOT_FED_MEMBER]"); + this.logger.LogTrace("(-)[NOT_FED_MEMBER]"); return false; } @@ -285,20 +287,22 @@ public void AddFederationMember(IFederationMember federationMember) this.signals.Publish(new FedMemberAdded(federationMember)); } - /// Should be protected by . + /// Adds a federation member to the internal list of federation members. + /// The to add. + /// Should be protected by . private void AddFederationMemberLocked(IFederationMember federationMember) { if (federationMember is CollateralFederationMember collateralFederationMember) { if (this.federationMembers.IsCollateralAddressRegistered(collateralFederationMember.CollateralMainchainAddress)) { - this.logger.Warn($"Federation member with address '{collateralFederationMember.CollateralMainchainAddress}' already exists."); + this.logger.LogWarning($"Federation member with address '{collateralFederationMember.CollateralMainchainAddress}' already exists."); return; } if (this.federationMembers.Contains(federationMember)) { - this.logger.Trace("(-)[ALREADY_EXISTS]"); + this.logger.LogTrace("(-)[ALREADY_EXISTS]"); return; } } @@ -307,7 +311,7 @@ private void AddFederationMemberLocked(IFederationMember federationMember) this.SetIsFederationMember(); - this.logger.Info("Federation member '{0}' was added.", federationMember); + this.logger.LogInformation("Federation member '{0}' was added.", federationMember); } public void RemoveFederationMember(IFederationMember federationMember) @@ -318,7 +322,7 @@ public void RemoveFederationMember(IFederationMember federationMember) this.SetIsFederationMember(); - this.logger.Info("Federation member '{0}' was removed.", federationMember); + this.logger.LogInformation("Federation member '{0}' was removed.", federationMember); } this.signals.Publish(new FedMemberKicked(federationMember)); diff --git a/src/Stratis.Bitcoin.Features.PoA/PoAConsensusOptions.cs b/src/Stratis.Bitcoin.Features.PoA/PoAConsensusOptions.cs index 6899e76a8b..145d7741e3 100644 --- a/src/Stratis.Bitcoin.Features.PoA/PoAConsensusOptions.cs +++ b/src/Stratis.Bitcoin.Features.PoA/PoAConsensusOptions.cs @@ -15,6 +15,9 @@ public class PoAConsensusOptions : ConsensusOptions /// public List GenesisFederationMembers { get; protected set; } + /// + /// The number of elapsed seconds required between mined block. + /// public uint TargetSpacingSeconds { get; protected set; } /// Adds capability of voting for adding\kicking federation members and other things. @@ -50,6 +53,13 @@ public class PoAConsensusOptions : ConsensusOptions /// public int InterFluxV2MainChainActivationHeight { get; set; } + /// + /// The height at which inituitive mining slots become active. + /// Legacy mining slots are determined by mining_slot = block_height % number_of_federation_members. + /// Once the specified height is reached there should no longer be a shift in mining slots when new federation members are added/removed. + /// + public int GetMiningTimestampV2ActivationHeight { get; set; } + /// /// Logic related to release 1.1.0.0 will activate at this height, this includes Poll Expiry and the Join Federation Voting Request consensus rule. /// @@ -68,6 +78,16 @@ public class PoAConsensusOptions : ConsensusOptions public int ContractSerializerV2ActivationHeight { get; set; } /// Initializes values for networks that use block size rules. + /// See . + /// See . + /// See . + /// See . + /// See . + /// See . + /// See . + /// See . + /// See . + /// See . public PoAConsensusOptions( uint maxBlockBaseSize, int maxStandardVersion, diff --git a/src/Stratis.Bitcoin.Features.PoA/PoAFeature.cs b/src/Stratis.Bitcoin.Features.PoA/PoAFeature.cs index 3342f9260d..08c469ff2f 100644 --- a/src/Stratis.Bitcoin.Features.PoA/PoAFeature.cs +++ b/src/Stratis.Bitcoin.Features.PoA/PoAFeature.cs @@ -153,6 +153,7 @@ public override Task InitializeAsync() } /// Replaces default with . + /// See . private void ReplaceConsensusManagerBehavior(NetworkPeerConnectionParameters connectionParameters) { INetworkPeerBehavior defaultConsensusManagerBehavior = connectionParameters.TemplateBehaviors.FirstOrDefault(behavior => behavior is ConsensusManagerBehavior); @@ -167,6 +168,7 @@ private void ReplaceConsensusManagerBehavior(NetworkPeerConnectionParameters con } /// Replaces default with . + /// See . private void ReplaceBlockStoreBehavior(NetworkPeerConnectionParameters connectionParameters) { INetworkPeerBehavior defaultBlockStoreBehavior = connectionParameters.TemplateBehaviors.FirstOrDefault(behavior => behavior is BlockStoreBehavior); diff --git a/src/Stratis.Bitcoin.Features.PoA/PoAMiner.cs b/src/Stratis.Bitcoin.Features.PoA/PoAMiner.cs index b348a801ef..330579660f 100644 --- a/src/Stratis.Bitcoin.Features.PoA/PoAMiner.cs +++ b/src/Stratis.Bitcoin.Features.PoA/PoAMiner.cs @@ -177,10 +177,31 @@ public virtual void InitializeMining() return Task.CompletedTask; }, this.nodeLifetime.ApplicationStopping, - repeatEvery: TimeSpans.Minute, + repeatEvery: TimeSpans.TenSeconds, startAfter: TimeSpans.TenSeconds); } + private IFederationMember DetermineExpectedMinerForTimestamp(uint headerTime) + { + // When blocks are missing for given timestamps we have to determine whom was supposed to mine + // by looking at the federation make-up at the time and whom mined last. + + ChainedHeader prevBlockMined = this.consensusManager.Tip; + while (prevBlockMined.Header.Time > headerTime) + prevBlockMined = prevBlockMined.Previous; + + IFederationMember minerForBlock = this.federationHistory.GetFederationMemberForBlock(prevBlockMined); + List federationAtBlock = this.federationHistory.GetFederationForBlock(prevBlockMined); + + int offset = (int)((headerTime - prevBlockMined.Header.Time) / this.network.ConsensusOptions.TargetSpacingSeconds); + + int minerForBlockIndex = federationAtBlock.TakeWhile(m => m.PubKey != minerForBlock.PubKey).Count(); + + int expectedIndex = (minerForBlockIndex + offset) % federationAtBlock.Count; + + return federationAtBlock[expectedIndex]; + } + private void GatherMiningStatistics() { var log = new StringBuilder(); @@ -188,6 +209,17 @@ private void GatherMiningStatistics() log.AppendLine(">> Miner"); log.AppendLine(); + if (!this.federationManager.IsFederationMember) + { + log.AppendLine("Mining information is not available for non federation members."); + log.AppendLine("It is possible that your node was kicked from the federation due to inactivity."); + log.AppendLine(); + + this.miningStatisticsLog = log.ToString(); + + return; + } + if (this.ibdState.IsInitialBlockDownload()) { log.AppendLine("Mining information is not available whilst the node is syncing."); @@ -209,49 +241,59 @@ private void GatherMiningStatistics() int maxDepth = modifiedFederation.Count; + // TODO: Make this a command line option. + bool includeHeight = false; + log.AppendLine($"Mining information for the last { maxDepth } blocks."); - log.AppendLine("Note that '<' and '>' surrounds a slot where a miner didn't produce a block."); + if (includeHeight) + log.AppendLine("Note 'MISS' indicates a slot where a miner didn't produce a block."); + else + log.AppendLine("Note that '<' and '>' surrounds a slot where a miner didn't produce a block."); - uint timeHeader = (uint)this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp(); - timeHeader -= timeHeader % this.network.ConsensusOptions.TargetSpacingSeconds; - if (timeHeader < currentHeader.Header.Time) - timeHeader += this.network.ConsensusOptions.TargetSpacingSeconds; + uint currentSlotTime = (uint)this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp(); + currentSlotTime -= currentSlotTime % this.network.ConsensusOptions.TargetSpacingSeconds; + // Determine the public key of the current slot. + string pubKeyRepresentation; // Iterate mining slots. - for (int i = 0; i < maxDepth; i++) + for (int i = 0; i < maxDepth; i++, currentSlotTime -= this.network.ConsensusOptions.TargetSpacingSeconds) { - int headerSlot = (int)(timeHeader / this.network.ConsensusOptions.TargetSpacingSeconds) % modifiedFederation.Count; - - PubKey pubKey = modifiedFederation[headerSlot].PubKey; - - string pubKeyRepresentation = pubKey.ToString().Substring(0, pubKeyTakeCharacters); - if (pubKey == this.federationManager.CurrentFederationKey?.PubKey) - pubKeyRepresentation = "█████"; + // Find the chained header for this slot. + while (currentHeader.Header.Time > currentSlotTime) + currentHeader = currentHeader.Previous; // Mined in this slot? - if (timeHeader == currentHeader.Header.Time) - { - log.Append($"[{ pubKeyRepresentation }] "); + bool minedInThisSlot = currentHeader.Header.Time == currentSlotTime; - currentHeader = currentHeader.Previous; - hitCount++; + PubKey pubKey = (minedInThisSlot ? + this.federationHistory.GetFederationMemberForBlock(currentHeader) : + DetermineExpectedMinerForTimestamp(currentSlotTime)).PubKey; - modifiedFederation = this.federationHistory.GetFederationForBlock(currentHeader); - if (pubKey == this.federationManager.CurrentFederationKey?.PubKey) - this.miningStatistics.ProducedBlockInLastRound = true; + if (pubKey == this.federationManager.CurrentFederationKey?.PubKey) + { + pubKeyRepresentation = "█████"; + this.miningStatistics.ProducedBlockInLastRound = minedInThisSlot; } else { - log.Append($"<{ pubKeyRepresentation }> "); + pubKeyRepresentation = pubKey.ToHex().Substring(0, pubKeyTakeCharacters); + } - if (pubKey == this.federationManager.CurrentFederationKey?.PubKey) - this.miningStatistics.ProducedBlockInLastRound = false; + if (includeHeight) + { + string strHeight = minedInThisSlot ? currentHeader.Height.ToString().PadLeft(7) : "---MISS"; + log.Append($"{strHeight}:{ pubKeyRepresentation } "); + } + else + { + log.Append(minedInThisSlot ? $"[{pubKeyRepresentation}] " : $"<{pubKeyRepresentation}> "); } - timeHeader -= this.network.ConsensusOptions.TargetSpacingSeconds; + if (minedInThisSlot) + hitCount++; - if ((i % 20) == 19) + if (((i + 1) % (includeHeight ? 10 : 20)) == 0) log.AppendLine(); } @@ -360,38 +402,32 @@ private async Task CreateBlocksAsync() private async Task WaitUntilMiningSlotAsync() { - uint? myTimestamp = null; - while (!this.cancellation.IsCancellationRequested) { uint timeNow = (uint)this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp(); if (timeNow <= this.consensusManager.Tip.Header.Time) { - await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); + await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false); continue; } - if (myTimestamp == null) + try { - try - { - myTimestamp = this.slotsManager.GetMiningTimestamp(timeNow); - } - catch (NotAFederationMemberException) - { - this.logger.LogWarning("This node is no longer a federation member!"); + uint myTimestamp = this.slotsManager.GetMiningTimestamp(timeNow); - throw new OperationCanceledException(); - } - } + if (myTimestamp <= timeNow) + return myTimestamp; - int estimatedWaitingTime = (int)(myTimestamp - timeNow) - 1; + } + catch (NotAFederationMemberException) + { + this.logger.LogWarning("This node is no longer a federation member!"); - if (estimatedWaitingTime <= 0) - return myTimestamp.Value; + throw new OperationCanceledException(); + } - await Task.Delay(TimeSpan.FromMilliseconds(500), this.cancellation.Token).ConfigureAwait(false); + await Task.Delay(TimeSpan.FromMilliseconds(100), this.cancellation.Token).ConfigureAwait(false); } throw new OperationCanceledException(); @@ -401,7 +437,7 @@ protected async Task MineBlockAtTimestampAsync(uint timestamp) { ChainedHeader tip = this.consensusManager.Tip; - // Timestamp should always be greater than prev one. + // Timestamp should always be greater than prev one. if (timestamp <= tip.Header.Time) { // Can happen only when target spacing had crazy low value or key was compromised and someone is mining with our key. @@ -479,6 +515,8 @@ protected async Task MineBlockAtTimestampAsync(uint timestamp) } /// Fills block template with custom non-standard data. + /// See . + /// Indicates whether the template should be dropped. protected virtual void FillBlockTemplate(BlockTemplate blockTemplate, out bool dropTemplate) { if (this.network.ConsensusOptions.VotingEnabled) @@ -496,7 +534,8 @@ protected virtual void FillBlockTemplate(BlockTemplate blockTemplate, out bool d dropTemplate = false; } - /// Gets scriptPubKey from the wallet. + /// Gets the first address from the first account of the first wallet - if any. Returns null otherwise. + /// See private HdAddress GetMiningAddressFromWallet() { string walletName = this.walletManager.GetWalletsNames().FirstOrDefault(); @@ -515,6 +554,7 @@ private HdAddress GetMiningAddressFromWallet() } /// Adds OP_RETURN output to a coinbase transaction which contains encoded voting data. + /// See . /// If there are no votes scheduled output will not be added. private void AddVotingData(BlockTemplate blockTemplate) { diff --git a/src/Stratis.Bitcoin.Features.PoA/SlotsManager.cs b/src/Stratis.Bitcoin.Features.PoA/SlotsManager.cs index 1e1c22bcd6..d8449916c0 100644 --- a/src/Stratis.Bitcoin.Features.PoA/SlotsManager.cs +++ b/src/Stratis.Bitcoin.Features.PoA/SlotsManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Microsoft.Extensions.Logging; using NBitcoin; using Stratis.Bitcoin.Utilities; @@ -15,9 +14,13 @@ public interface ISlotsManager /// Gets next timestamp at which current node can produce a block. /// Thrown if this node is not a federation member. + /// The current unix timestamp. + /// The next timestamp at which current node can produce a block. uint GetMiningTimestamp(uint currentTime); /// Determines whether timestamp is valid according to the network rules. + /// The unix timstamp of a block header. + /// True if the timestamp is valid and false otherwise. bool IsValidTimestamp(uint headerUnixTimestamp); TimeSpan GetRoundLength(int? federationMembersCount = null); @@ -29,21 +32,85 @@ public class SlotsManager : ISlotsManager private readonly IFederationManager federationManager; - private readonly ChainIndexer chainIndexer; + private readonly IFederationHistory federationHistory; - private readonly ILogger logger; + private readonly ChainIndexer chainIndexer; - public SlotsManager(Network network, IFederationManager federationManager, ChainIndexer chainIndexer, ILoggerFactory loggerFactory) + public SlotsManager(Network network, IFederationManager federationManager, IFederationHistory federationHistory, ChainIndexer chainIndexer) { Guard.NotNull(network, nameof(network)); + this.federationManager = Guard.NotNull(federationManager, nameof(federationManager)); + this.federationHistory = federationHistory; this.chainIndexer = chainIndexer; this.consensusOptions = (network as PoANetwork).ConsensusOptions; - this.logger = loggerFactory.CreateLogger(this.GetType().FullName); } - /// public uint GetMiningTimestamp(uint currentTime) + { + /* + A miner can calculate when its expected to mine by looking at the ordered list of federation members + and the last block that was mined and by whom. It can count the number of mining slots from that member + to itself and multiply that with the target spacing to arrive at its mining timestamp. + The fact that the federation can change at any time adds extra complexity to this basic approach. + The miner that mined the last block may no longer exist when the next block is about to be mined. As such + we may need to look a bit further back to find a "reference miner" that still occurs in the latest federation. + */ + ChainedHeader tip = this.chainIndexer.Tip; + if (tip.Height < this.consensusOptions.GetMiningTimestampV2ActivationHeight) + return GetMiningTimestampLegacy(currentTime); + + List federationMembers = this.federationHistory.GetFederationForBlock(tip, 1); + if (federationMembers == null) + throw new Exception($"Could not determine the federation at block { tip.Height } + 1."); + + int myIndex = federationMembers.FindIndex(m => m.PubKey == this.federationManager.CurrentFederationKey?.PubKey); + if (myIndex < 0) + throw new NotAFederationMemberException(); + + // Find a "reference miner" to determine our slot against. + ChainedHeader referenceMinerBlock = tip; + IFederationMember referenceMiner = null; + int referenceMinerIndex = -1; + int referenceMinerDepth = 0; + for (int i = 0; i < federationMembers.Count; i++, referenceMinerDepth++) + { + referenceMiner = this.federationHistory.GetFederationMemberForBlock(referenceMinerBlock); + referenceMinerIndex = federationMembers.FindIndex(m => m.PubKey == referenceMiner.PubKey); + if (referenceMinerIndex >= 0) + break; + } + + if (referenceMinerIndex < 0) + throw new Exception("Could not find a member in common between the old and new federation"); + + // Found a reference miner that also occurs in the latest federation. + // Determine how many blocks before our mining slot. + int blocksFromTipToMiningSlot = myIndex - referenceMinerIndex - referenceMinerDepth; + while (blocksFromTipToMiningSlot < 0) + blocksFromTipToMiningSlot += federationMembers.Count; + + // Round length in seconds. + uint roundTime = (uint)this.GetRoundLength(federationMembers.Count).TotalSeconds; + + // Get the tip time and make is a valid time if required. + uint tipTime = tip.Header.Time; + if (!IsValidTimestamp(tipTime)) + tipTime += (this.consensusOptions.TargetSpacingSeconds - tipTime % this.consensusOptions.TargetSpacingSeconds); + + // Check if we have missed our turn for this round. + // We still consider ourselves "in a turn" if we are in the first half of the turn and we haven't mined there yet. + // This might happen when starting the node for the first time or if there was a problem when mining. + + uint nextTimestampForMining = (uint)(tipTime + blocksFromTipToMiningSlot * this.consensusOptions.TargetSpacingSeconds); + while (currentTime > nextTimestampForMining + (this.consensusOptions.TargetSpacingSeconds / 2) // We are closer to the next turn than our own + || tipTime == nextTimestampForMining) + nextTimestampForMining += roundTime; + + return nextTimestampForMining; + } + + private uint GetMiningTimestampLegacy(uint currentTime) { if (!this.federationManager.IsFederationMember) throw new NotAFederationMemberException(); diff --git a/src/Stratis.Bitcoin.Features.PoA/Stratis.Bitcoin.Features.PoA.csproj b/src/Stratis.Bitcoin.Features.PoA/Stratis.Bitcoin.Features.PoA.csproj index 2d2a061c37..15faa5571b 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Stratis.Bitcoin.Features.PoA.csproj +++ b/src/Stratis.Bitcoin.Features.PoA/Stratis.Bitcoin.Features.PoA.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/FederationController.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/FederationController.cs index 41f4ae13c5..db647bb47f 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/FederationController.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/FederationController.cs @@ -4,8 +4,9 @@ using System.Linq; using System.Net; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.PoA.Models; using Stratis.Bitcoin.Utilities.JsonErrors; @@ -45,7 +46,7 @@ public FederationController( } /// - /// Signals the node to rebuild the federation via cleabning and rebuilding executed polls. + /// Signals the node to rebuild the federation via cleaning and rebuilding executed polls. /// This will be done via writing a flag to the .conf file so that on startup it be executed. /// /// See response codes @@ -65,7 +66,7 @@ public IActionResult Reconstruct() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -126,12 +127,10 @@ public IActionResult GetCurrentMemberInfo() federationMemberModel.PollWillFinishInBlocks = 0; else federationMemberModel.PollWillFinishInBlocks = (poll.PollVotedInFavorBlockData.Height + this.network.Consensus.MaxReorgLength) - chainTip.Height; - } - // Has the poll executed? - poll = this.votingManager.GetExecutedPolls().MemberPolls().OrderByDescending(p => p.PollExecutedBlockData.Height).FirstOrDefault(p => this.votingManager.GetMemberVotedOn(p.VotingData).PubKey == this.federationManager.CurrentFederationKey.PubKey); - if (poll != null) - federationMemberModel.PollExecutedBlockHeight = poll.PollExecutedBlockData.Height; + // Has the poll executed? + federationMemberModel.PollExecutedBlockHeight = poll.PollExecutedBlockData?.Height; + } federationMemberModel.RewardEstimatePerBlock = 9d / this.federationManager.GetFederationMembers().Count; @@ -141,7 +140,7 @@ public IActionResult GetCurrentMemberInfo() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -181,7 +180,7 @@ public IActionResult GetMembers() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -208,7 +207,7 @@ public IActionResult GetPubkeyAtHeight([FromQuery(Name = "blockHeight")] int blo } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -241,7 +240,7 @@ public IActionResult GetFederationAtHeight([FromQuery(Name = "blockHeight")] int } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/JoinFederationRequestEncoder.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/JoinFederationRequestEncoder.cs index a229421249..c72aa1d5d3 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/JoinFederationRequestEncoder.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/JoinFederationRequestEncoder.cs @@ -1,8 +1,9 @@ using System; using System.IO; using DBreeze.Utils; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.PoA; namespace Stratis.Bitcoin.PoA.Features.Voting @@ -18,7 +19,9 @@ public JoinFederationRequestEncoder() this.logger = LogManager.GetCurrentClassLogger(); } - /// Encodes voting request data. + /// Encodes voting request data as an array of bytes. + /// See . + /// An array of bytes encoding the voting request. public byte[] Encode(JoinFederationRequest votingRequestData) { using (var memoryStream = new MemoryStream()) @@ -32,8 +35,10 @@ public byte[] Encode(JoinFederationRequest votingRequestData) } } - /// Decodes the voting request. + /// Decodes a voting request that had previously been encoded as an array of bytes. /// Thrown in case voting data format is invalid. + /// An array of bytes encoding the voting request. + /// The . public JoinFederationRequest Decode(byte[] votingRequestDataBytes) { try @@ -55,7 +60,7 @@ public JoinFederationRequest Decode(byte[] votingRequestDataBytes) if (deserializeStream.ProcessedBytes != votingRequestDataBytes.Length) { - this.logger.Trace("(-)[INVALID_SIZE]"); + this.logger.LogTrace("(-)[INVALID_SIZE]"); PoAConsensusErrors.VotingRequestInvalidFormat.Throw(); } @@ -64,8 +69,8 @@ public JoinFederationRequest Decode(byte[] votingRequestDataBytes) } catch (Exception e) { - this.logger.Debug("Exception during deserialization: '{0}'.", e.ToString()); - this.logger.Trace("(-)[DESERIALIZING_EXCEPTION]"); + this.logger.LogDebug("Exception during deserialization: '{0}'.", e.ToString()); + this.logger.LogTrace("(-)[DESERIALIZING_EXCEPTION]"); PoAConsensusErrors.VotingRequestInvalidFormat.Throw(); return null; diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/JoinFederationRequestService.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/JoinFederationRequestService.cs index d3276e7b3f..1c5f8bf839 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/JoinFederationRequestService.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/JoinFederationRequestService.cs @@ -49,6 +49,23 @@ public JoinFederationRequestService(ICounterChainSettings counterChainSettings, this.votingManager = votingManager; } + public static byte[] GetFederationMemberBytes(JoinFederationRequest joinRequest, Network network, Network counterChainNetwork) + { + Script collateralScript = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(joinRequest.CollateralMainchainAddress); + BitcoinAddress bitcoinAddress = collateralScript.GetDestinationAddress(counterChainNetwork); + var collateralFederationMember = new CollateralFederationMember(joinRequest.PubKey, false, joinRequest.CollateralAmount, bitcoinAddress.ToString()); + + return (network.Consensus.ConsensusFactory as CollateralPoAConsensusFactory).SerializeFederationMember(collateralFederationMember); + } + + public static void SetLastRemovalEventId(JoinFederationRequest joinRequest, byte[] federationMemberBytes, VotingManager votingManager) + { + Poll poll = votingManager.GetExecutedPolls().OrderByDescending(p => p.PollExecutedBlockData.Height).FirstOrDefault(x => + x.VotingData.Key == VoteKey.KickFederationMember && x.VotingData.Data.SequenceEqual(federationMemberBytes)); + + joinRequest.RemovalEventId = (poll == null) ? Guid.Empty : new Guid(poll.PollExecutedBlockData.Hash.ToBytes().TakeLast(16).ToArray()); + } + public async Task JoinFederationAsync(JoinFederationRequestModel request, CancellationToken cancellationToken) { // Wait until the node is synced before joining. @@ -74,13 +91,7 @@ public async Task JoinFederationAsync(JoinFederationRequestModel request var joinRequest = new JoinFederationRequest(minerKey.PubKey, new Money(expectedCollateralAmount, MoneyUnit.BTC), addressKey); // Populate the RemovalEventId. - var collateralFederationMember = new CollateralFederationMember(minerKey.PubKey, false, joinRequest.CollateralAmount, request.CollateralAddress); - - byte[] federationMemberBytes = (this.network.Consensus.ConsensusFactory as CollateralPoAConsensusFactory).SerializeFederationMember(collateralFederationMember); - Poll poll = this.votingManager.GetApprovedPolls().FirstOrDefault(x => x.IsExecuted && - x.VotingData.Key == VoteKey.KickFederationMember && x.VotingData.Data.SequenceEqual(federationMemberBytes)); - - joinRequest.RemovalEventId = (poll == null) ? Guid.Empty : new Guid(poll.PollExecutedBlockData.Hash.ToBytes().TakeLast(16).ToArray()); + SetLastRemovalEventId(joinRequest, GetFederationMemberBytes(joinRequest, this.network, this.counterChainSettings.CounterChainNetwork), this.votingManager); // Get the signature by calling the counter-chain "signmessage" API. var signMessageRequest = new SignMessageRequest() diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/PollResultExecutor.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/PollResultExecutor.cs index 644ef6fc7f..2e10360f99 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/PollResultExecutor.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/PollResultExecutor.cs @@ -7,12 +7,16 @@ namespace Stratis.Bitcoin.Features.PoA.Voting public interface IPollResultExecutor { /// Applies effect of . + /// See . void ApplyChange(VotingData data); /// Reverts effect of . + /// See . void RevertChange(VotingData data); /// Converts to a human readable format. + /// See . + /// The string representation of the . string ConvertToString(VotingData data); } diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/PollsCollection.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/PollsCollection.cs index 1c2880cf69..209d61253c 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/PollsCollection.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/PollsCollection.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; -using NLog; +using Microsoft.Extensions.Logging; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Utilities; namespace Stratis.Bitcoin.Features.PoA.Voting @@ -39,7 +40,7 @@ public void Add(Poll poll) { if (this.polls.Contains(poll)) { - this.logger.Warn("The poll already exists: '{0}'.", poll); + this.logger.LogWarning("The poll already exists: '{0}'.", poll); return; } diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/PollsRepository.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/PollsRepository.cs index 38b4e877e5..000a263c9e 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/PollsRepository.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/PollsRepository.cs @@ -5,9 +5,10 @@ using DBreeze; using DBreeze.DataTypes; using DBreeze.Utils; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Utilities; namespace Stratis.Bitcoin.Features.PoA.Voting @@ -65,7 +66,7 @@ public void Initialize() var uniquePolls = new HashSet(polls); if (uniquePolls.Count != polls.Count) { - this.logger.Warn("The polls repository contains {0} duplicate polls, it will be rebuilt.", polls.Count - uniquePolls.Count); + this.logger.LogWarning("The polls repository contains {0} duplicate polls, it will be rebuilt.", polls.Count - uniquePolls.Count); this.ResetLocked(transaction); transaction.Commit(); @@ -76,7 +77,7 @@ public void Initialize() Row rowTip = transaction.Select(DataTable, RepositoryTipKey); if (!rowTip.Exists) { - this.logger.Info("The polls repository tip is unknown, it will be rebuilt."); + this.logger.LogInformation("The polls repository tip is unknown, it will be rebuilt."); this.ResetLocked(transaction); transaction.Commit(); return; @@ -89,11 +90,11 @@ public void Initialize() if (chainedHeaderTip != null) { this.highestPollId = (polls.Count > 0) ? polls.Max(p => p.Id) : -1; - this.logger.Info("Polls repository tip exists on chain; initializing at height {0}; highest poll id: {1}.", this.CurrentTip.Height, this.highestPollId); + this.logger.LogInformation("Polls repository tip exists on chain; initializing at height {0}; highest poll id: {1}.", this.CurrentTip.Height, this.highestPollId); return; } - this.logger.Info("The polls repository tip {0} was not found in the consensus chain, determining fork.", this.CurrentTip); + this.logger.LogInformation("The polls repository tip {0} was not found in the consensus chain, determining fork.", this.CurrentTip); // The polls repository tip could not be found in the chain. // Look at all other known hash/height pairs to find something in common with the consensus chain. @@ -111,7 +112,7 @@ public void Initialize() if (maxGoodHeight == -1) { - this.logger.Info("No common blocks found; the repo will be rebuilt from scratch."); + this.logger.LogInformation("No common blocks found; the repo will be rebuilt from scratch."); this.ResetLocked(transaction); transaction.Commit(); return; @@ -119,7 +120,7 @@ public void Initialize() this.CurrentTip = new HashHeightPair(this.chainIndexer.GetHeader(maxGoodHeight)); - this.logger.Info("Common block found at height {0}; the repo will be rebuilt from there.", this.CurrentTip.Height); + this.logger.LogInformation("Common block found at height {0}; the repo will be rebuilt from there.", this.CurrentTip.Height); // Trim polls to tip. HashSet pollsToDelete = new HashSet(); @@ -128,7 +129,7 @@ public void Initialize() if (poll.PollStartBlockData.Height > this.CurrentTip.Height) { pollsToDelete.Add(poll); - this.logger.Debug("Poll {0} will be deleted.", poll.Id); + this.logger.LogDebug("Poll {0} will be deleted.", poll.Id); continue; } @@ -156,7 +157,7 @@ public void Initialize() // if so un-expire it. if (poll.IsExpired && !IsPollExpiredAt(poll, chainedHeaderTip, this.network)) { - this.logger.Debug("Un-expiring poll {0}.", poll.Id); + this.logger.LogDebug("Un-expiring poll {0}.", poll.Id); poll.IsExpired = false; modified = true; @@ -170,11 +171,11 @@ public void Initialize() SaveCurrentTip(transaction, this.CurrentTip); transaction.Commit(); - this.logger.Info("Polls repository initialized at height {0}; highest poll id: {1}.", this.CurrentTip.Height, this.highestPollId); + this.logger.LogInformation("Polls repository initialized at height {0}; highest poll id: {1}.", this.CurrentTip.Height, this.highestPollId); } catch (Exception err) when (err.Message == "No more byte to read") { - this.logger.Warn("There was an error reading the polls repository, it will be rebuild."); + this.logger.LogWarning("There was an error reading the polls repository, it will be rebuild."); this.ResetLocked(transaction); transaction.Commit(); } @@ -219,6 +220,7 @@ public void SaveCurrentTip(DBreeze.Transactions.Transaction transaction, HashHei } /// Provides Id of the most recently added poll. + /// Id of the most recently added poll. public int GetHighestPollId() { return this.highestPollId; @@ -233,6 +235,8 @@ public void Synchronous(Action action) } /// Removes polls for the provided ids. + /// See . + /// The ids of the polls to remove. public void DeletePollsAndSetHighestPollId(DBreeze.Transactions.Transaction transaction, params int[] ids) { lock (this.lockObject) @@ -248,6 +252,8 @@ public void DeletePollsAndSetHighestPollId(DBreeze.Transactions.Transaction tran } /// Removes polls under provided ids. + /// See . + /// The ids of the polls to remove. public void RemovePolls(DBreeze.Transactions.Transaction transaction, params int[] ids) { lock (this.lockObject) @@ -294,7 +300,9 @@ public void WithTransaction(Action action) } } - /// Adds new poll. + /// Adds new polls. + /// See . + /// The polls to add. public void AddPolls(DBreeze.Transactions.Transaction transaction, params Poll[] polls) { lock (this.lockObject) @@ -314,6 +322,8 @@ public void AddPolls(DBreeze.Transactions.Transaction transaction, params Poll[] } /// Updates existing poll. + /// See . + /// The poll to update. public void UpdatePoll(DBreeze.Transactions.Transaction transaction, Poll poll) { lock (this.lockObject) @@ -325,6 +335,9 @@ public void UpdatePoll(DBreeze.Transactions.Transaction transaction, Poll poll) } /// Loads polls under provided keys from the database. + /// See . + /// The ids of the polls to retrieve. + /// A list of retrieved entries. public List GetPolls(DBreeze.Transactions.Transaction transaction, params int[] ids) { lock (this.lockObject) @@ -348,6 +361,8 @@ public List GetPolls(DBreeze.Transactions.Transaction transaction, params } /// Loads all polls from the database. + /// See . + /// A list of retrieved entries. public List GetAllPolls(DBreeze.Transactions.Transaction transaction) { lock (this.lockObject) diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/VotingController.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/VotingController.cs index 861ee078c9..9f8fa1bcaa 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/VotingController.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/VotingController.cs @@ -3,8 +3,9 @@ using System.Linq; using System.Net; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.PoA.Models; using Stratis.Bitcoin.Features.Wallet.Models; using Stratis.Bitcoin.Utilities; @@ -72,7 +73,7 @@ public IActionResult GetPollsRepositoryTip() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -80,6 +81,8 @@ public IActionResult GetPollsRepositoryTip() /// /// Retrieves a list of pending or "active" polls. /// + /// See 0 = Kick Member, 1 - Add Member, 2 = Whitelist Hash, 3 = Remove Hash + /// The public key of the member being voted on (in hexadecimal). If omitted or empty then all polls are returned. /// Active polls /// Returns the active polls /// Unexpected exception occurred @@ -101,7 +104,7 @@ public IActionResult GetPendingPolls([FromQuery] VoteKey voteType, [FromQuery] s } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -109,6 +112,8 @@ public IActionResult GetPendingPolls([FromQuery] VoteKey voteType, [FromQuery] s /// /// Retrieves a list of finished polls. /// + /// See 0 = Kick Member, 1 - Add Member, 2 = Whitelist Hash, 3 = Remove Hash + /// The public key of the member being voted on (in hexadecimal). If omitted or empty then all polls are returned. /// Finished polls /// Returns the finished polls /// Unexpected exception occurred @@ -130,7 +135,7 @@ public IActionResult GetFinishedPolls([FromQuery] VoteKey voteType, [FromQuery] } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -138,6 +143,8 @@ public IActionResult GetFinishedPolls([FromQuery] VoteKey voteType, [FromQuery] /// /// Retrieves a list of executed polls. /// + /// See 0 = Kick Member, 1 - Add Member, 2 = Whitelist Hash, 3 = Remove Hash + /// The public key of the member being voted on (in hexadecimal). If omitted or empty then all polls are returned. /// Finished polls /// Returns the finished polls /// Unexpected exception occurred @@ -159,7 +166,7 @@ public IActionResult GetExecutedPolls([FromQuery] VoteKey voteType, [FromQuery] } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -185,7 +192,7 @@ public IActionResult GetExpiredPollsMembers() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -211,7 +218,7 @@ public IActionResult GetExpiredPollsWhitelist() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -236,7 +243,7 @@ public IActionResult GetWhitelistedHashes() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -244,6 +251,7 @@ public IActionResult GetWhitelistedHashes() /// /// Votes to add a hash to the whitelist. /// + /// See . /// The HTTP response /// Voted to add hash to whitelist /// Invalid request, node is not a federation member, or an unexpected exception occurred @@ -261,6 +269,7 @@ public IActionResult VoteWhitelistHash([FromBody] HashModel request) /// /// Votes to remove a hash from the whitelist. /// + /// See . /// The HTTP response /// Voted to remove hash from whitelist /// Invalid request, node is not a federation member, or an unexpected exception occurred @@ -299,7 +308,7 @@ private IActionResult VoteWhitelistRemoveHashMember(HashModel request, bool whit } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "There was a problem executing a command.", e.ToString()); } } @@ -326,7 +335,7 @@ public IActionResult GetScheduledVotes() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -334,6 +343,7 @@ public IActionResult GetScheduledVotes() /// /// Votes to kick/remove a member from the federation. /// + /// See . /// The HTTP response /// Voted to remove a member from the federation. /// Invalid request, node is not a federation member, or an unexpected exception occurred @@ -376,7 +386,7 @@ public IActionResult VoteKickFederationMember([FromBody] KickFederationMemberMod } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "There was a problem executing a vote to be scheduled.", e.ToString()); } } diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/VotingDataEncoder.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/VotingDataEncoder.cs index 3f5e26e72e..9dabf40952 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/VotingDataEncoder.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/VotingDataEncoder.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; namespace Stratis.Bitcoin.Features.PoA.Voting { @@ -29,7 +30,7 @@ public List Decode(byte[] votingDataBytes) { if (votingDataBytes.Length > VotingDataMaxSerializedSize) { - this.logger.Trace("(-)[INVALID_SIZE]"); + this.logger.LogTrace("(-)[INVALID_SIZE]"); PoAConsensusErrors.VotingDataInvalidFormat.Throw(); } @@ -46,8 +47,8 @@ public List Decode(byte[] votingDataBytes) } catch (Exception e) { - this.logger.Debug("Exception during deserialization: '{0}'.", e.ToString()); - this.logger.Trace("(-)[DESERIALIZING_EXCEPTION]"); + this.logger.LogDebug("Exception during deserialization: '{0}'.", e.ToString()); + this.logger.LogTrace("(-)[DESERIALIZING_EXCEPTION]"); PoAConsensusErrors.VotingDataInvalidFormat.Throw(); return null; @@ -93,7 +94,7 @@ public byte[] ExtractRawVotingData(Transaction tx) if (votingData != null) { - this.logger.Trace("(-)[TOO_MANY_VOTING_OUTPUTS]"); + this.logger.LogTrace("(-)[TOO_MANY_VOTING_OUTPUTS]"); PoAConsensusErrors.TooManyVotingOutputs.Throw(); } diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/VotingManager.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/VotingManager.cs index 72fb813e61..a64c30c5ac 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/VotingManager.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/VotingManager.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Text; using ConcurrentCollections; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.EventBus; @@ -114,10 +114,11 @@ public void Initialize(IFederationHistory federationHistory, IIdleFederationMemb this.isInitialized = true; - this.logger.Debug("VotingManager initialized."); + this.logger.LogDebug("VotingManager initialized."); } /// Schedules a vote for the next time when the block will be mined. + /// See . /// Thrown in case caller is not a federation member. public void ScheduleVote(VotingData votingData) { @@ -125,7 +126,7 @@ public void ScheduleVote(VotingData votingData) if (!this.federationManager.IsFederationMember) { - this.logger.Trace("(-)[NOT_FED_MEMBER]"); + this.logger.LogTrace("(-)[NOT_FED_MEMBER]"); throw new InvalidOperationException("Not a federation member!"); } @@ -137,10 +138,11 @@ public void ScheduleVote(VotingData votingData) this.SanitizeScheduledPollsLocked(); } - this.logger.Debug("Vote was scheduled with key: {0}.", votingData.Key); + this.logger.LogDebug("Vote was scheduled with key: {0}.", votingData.Key); } /// Provides a copy of scheduled voting data. + /// A list of . public List GetScheduledVotes() { this.EnsureInitialized(); @@ -154,6 +156,7 @@ public List GetScheduledVotes() } /// Provides scheduled voting data and removes all items that were provided. + /// A list of . /// Used by miner. public List GetAndCleanScheduledVotes() { @@ -168,7 +171,7 @@ public List GetAndCleanScheduledVotes() this.scheduledVotingData = new List(); if (votingData.Count > 0) - this.logger.Debug("{0} scheduled votes were taken.", votingData.Count); + this.logger.LogDebug("{0} scheduled votes were taken.", votingData.Count); return votingData; } @@ -205,6 +208,7 @@ bool IsValid(VotingData currentScheduledData) } /// Provides a collection of polls that are currently active. + /// A list of items. public List GetPendingPolls() { this.EnsureInitialized(); @@ -217,6 +221,7 @@ public List GetPendingPolls() } /// Provides a collection of polls that are approved but not executed yet. + /// A list of items. public List GetApprovedPolls() { this.EnsureInitialized(); @@ -228,6 +233,7 @@ public List GetApprovedPolls() } /// Provides a collection of polls that are approved and their results applied. + /// A list of items. public List GetExecutedPolls() { this.EnsureInitialized(); @@ -239,6 +245,7 @@ public List GetExecutedPolls() } /// Provides a collection of polls that are expired. + /// A list of items. public List GetExpiredPolls() { this.EnsureInitialized(); @@ -250,8 +257,11 @@ public List GetExpiredPolls() } /// - /// Tells us whether we have already voted to boot a federation member. + /// Tells us whether we have already voted on this federation member. /// + /// See . + /// The bytes to compare against. + /// True if we have already voted or false otherwise. public bool AlreadyVotingFor(VoteKey voteKey, byte[] federationMemberBytes) { List approvedPolls = this.GetApprovedPolls(); @@ -306,7 +316,7 @@ public List GetFederationFromExecutedPolls() { if (federationMember is CollateralFederationMember colMember2 && federation.IsCollateralAddressRegistered(colMember2.CollateralMainchainAddress)) { - this.logger.Debug("Not adding member '{0}' with duplicate collateral address '{1}'.", federationMember.PubKey.ToHex(), colMember2.CollateralMainchainAddress); + this.logger.LogDebug("Not adding member '{0}' with duplicate collateral address '{1}'.", federationMember.PubKey.ToHex(), colMember2.CollateralMainchainAddress); continue; } @@ -404,7 +414,7 @@ public List GetModifiedFederation(ChainedHeader chainedHeader { if (modifiedFederation.IsCollateralAddressRegistered(collateralFederationMember.CollateralMainchainAddress)) { - this.logger.Debug("Not adding member '{0}' with duplicate collateral address '{1}'.", collateralFederationMember.PubKey.ToHex(), collateralFederationMember.CollateralMainchainAddress); + this.logger.LogDebug("Not adding member '{0}' with duplicate collateral address '{1}'.", collateralFederationMember.PubKey.ToHex(), collateralFederationMember.CollateralMainchainAddress); continue; } @@ -466,7 +476,7 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH foreach (Poll poll in this.GetPendingPolls().Where(p => PollsRepository.IsPollExpiredAt(p, chBlock.ChainedHeader, this.network as PoANetwork)).ToList()) { - this.logger.Debug("Expiring poll '{0}'.", poll); + this.logger.LogDebug("Expiring poll '{0}'.", poll); // Flag the poll as expired. The "PollVotedInFavorBlockData" will always be null at this point due to the "GetPendingPolls" filter above. // The value of the hash is not significant but we set it to a non-zero value to prevent the field from being de-serialized as null. @@ -481,7 +491,7 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH if (poll.IsExpired || chBlock.ChainedHeader.Height != (poll.PollVotedInFavorBlockData.Height + this.network.Consensus.MaxReorgLength)) continue; - this.logger.Debug("Applying poll '{0}'.", poll); + this.logger.LogDebug("Applying poll '{0}'.", poll); this.pollResultExecutor.ApplyChange(poll.VotingData); poll.PollExecutedBlockData = new HashHeightPair(chBlock.ChainedHeader); @@ -504,8 +514,8 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH IFederationMember member = this.federationHistory.GetFederationMemberForBlock(chBlock.ChainedHeader); if (member == null) { - this.logger.Error("The block was mined by a non-federation-member!"); - this.logger.Trace("(-)[ALIEN_BLOCK]"); + this.logger.LogError("The block was mined by a non-federation-member!"); + this.logger.LogTrace("(-)[ALIEN_BLOCK]"); this.PollsRepository.SaveCurrentTip(pollsRepositoryModified ? transaction : null, chBlock.ChainedHeader); return; @@ -517,7 +527,7 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH List votingDataList = this.votingDataEncoder.Decode(rawVotingData); - this.logger.Debug("Applying {0} voting data items included in a block by '{1}'.", votingDataList.Count, fedMemberKeyHex); + this.logger.LogDebug("Applying {0} voting data items included in a block by '{1}'.", votingDataList.Count, fedMemberKeyHex); lock (this.locker) { @@ -556,7 +566,7 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH this.PollsRepository.AddPolls(transaction, poll); pollsRepositoryModified = true; - this.logger.Debug("New poll was created: '{0}'.", poll); + this.logger.LogDebug("New poll was created: '{0}'.", poll); }); } else if (!poll.PubKeysHexVotedInFavor.Any(v => v.PubKey == fedMemberKeyHex)) @@ -565,11 +575,11 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH this.PollsRepository.UpdatePoll(transaction, poll); pollsRepositoryModified = true; - this.logger.Debug("Voted on existing poll: '{0}'.", poll); + this.logger.LogDebug("Voted on existing poll: '{0}'.", poll); } else { - this.logger.Debug("Fed member '{0}' already voted for this poll. Ignoring his vote. Poll: '{1}'.", fedMemberKeyHex, poll); + this.logger.LogDebug("Fed member '{0}' already voted for this poll. Ignoring his vote. Poll: '{1}'.", fedMemberKeyHex, poll); } List modifiedFederation = this.federationManager.GetFederationMembers(); @@ -586,7 +596,7 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH chainedHeader = this.chainIndexer.GetHeader(poll.PollStartBlockData.Hash); if (chainedHeader == null) { - this.logger.Warn("Couldn't retrieve header for block at height-hash: {0}-{1}.", poll.PollStartBlockData.Height, poll.PollStartBlockData.Hash?.ToString()); + this.logger.LogWarning("Couldn't retrieve header for block at height-hash: {0}-{1}.", poll.PollStartBlockData.Height, poll.PollStartBlockData.Hash?.ToString()); Guard.NotNull(chainedHeader, nameof(chainedHeader)); Guard.NotNull(chainedHeader.Header, nameof(chainedHeader.Header)); @@ -608,7 +618,7 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH int requiredVotesCount = (fedMembersHex.Count / 2) + 1; - this.logger.Debug("Fed members count: {0}, valid votes count: {1}, required votes count: {2}.", fedMembersHex.Count, validVotesCount, requiredVotesCount); + this.logger.LogDebug("Fed members count: {0}, valid votes count: {1}, required votes count: {2}.", fedMembersHex.Count, validVotesCount, requiredVotesCount); if (validVotesCount < requiredVotesCount) continue; @@ -626,7 +636,7 @@ private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedH } catch (Exception ex) { - this.logger.Error(ex, ex.ToString()); + this.logger.LogError(ex, ex.ToString()); throw; } finally @@ -646,7 +656,7 @@ private void UnProcessBlock(DBreeze.Transactions.Transaction transaction, Chaine { foreach (Poll poll in this.polls.Where(x => !x.IsPending && x.PollExecutedBlockData?.Hash == chBlock.ChainedHeader.HashBlock).ToList()) { - this.logger.Debug("Reverting poll execution '{0}'.", poll); + this.logger.LogDebug("Reverting poll execution '{0}'.", poll); this.pollResultExecutor.RevertChange(poll.VotingData); poll.PollExecutedBlockData = null; @@ -656,7 +666,7 @@ private void UnProcessBlock(DBreeze.Transactions.Transaction transaction, Chaine foreach (Poll poll in this.polls.Where(x => x.IsExpired && !PollsRepository.IsPollExpiredAt(x, chBlock.ChainedHeader.Previous, this.network as PoANetwork)).ToList()) { - this.logger.Debug("Reverting poll expiry '{0}'.", poll); + this.logger.LogDebug("Reverting poll expiry '{0}'.", poll); // Revert back to null as this field would have been when the poll was expired. poll.IsExpired = false; @@ -673,7 +683,7 @@ private void UnProcessBlock(DBreeze.Transactions.Transaction transaction, Chaine if (rawVotingData == null) { - this.logger.Trace("(-)[NO_VOTING_DATA]"); + this.logger.LogTrace("(-)[NO_VOTING_DATA]"); this.PollsRepository.SaveCurrentTip(pollsRepositoryModified ? transaction : null, chBlock.ChainedHeader.Previous); return; @@ -698,7 +708,7 @@ private void UnProcessBlock(DBreeze.Transactions.Transaction transaction, Chaine targetPoll = this.polls.Last(x => x.VotingData == votingData); } - this.logger.Debug("Reverting poll voting in favor: '{0}'.", targetPoll); + this.logger.LogDebug("Reverting poll voting in favor: '{0}'.", targetPoll); if (targetPoll.PollVotedInFavorBlockData == new HashHeightPair(chBlock.ChainedHeader)) { @@ -722,7 +732,7 @@ private void UnProcessBlock(DBreeze.Transactions.Transaction transaction, Chaine this.PollsRepository.RemovePolls(transaction, targetPoll.Id); pollsRepositoryModified = true; - this.logger.Debug("Poll with Id {0} was removed.", targetPoll.Id); + this.logger.LogDebug("Poll with Id {0} was removed.", targetPoll.Id); } } } @@ -809,7 +819,7 @@ internal bool Synchronize(ChainedHeader newTip) { if (this.nodeLifetime.ApplicationStopping.IsCancellationRequested) { - this.logger.Trace("(-)[NODE_DISPOSED]"); + this.logger.LogTrace("(-)[NODE_DISPOSED]"); this.PollsRepository.SaveCurrentTip(currentTransaction); currentTransaction.Commit(); currentTransaction.Dispose(); @@ -827,7 +837,7 @@ internal bool Synchronize(ChainedHeader newTip) var progress = (int)((decimal)header.Height / this.chainIndexer.Tip.Height * 100); var progressString = $"Synchronizing voting data at height {header.Height} / {this.chainIndexer.Tip.Height} ({progress} %)."; - this.logger.Info(progressString); + this.logger.LogInformation(progressString); this.signals.Publish(new FullNodeEvent() { Message = progressString, State = FullNodeState.Initializing.ToString() }); this.PollsRepository.SaveCurrentTip(currentTransaction); diff --git a/src/Stratis.Bitcoin.Features.PoA/Voting/WhitelistedHashesRepository.cs b/src/Stratis.Bitcoin.Features.PoA/Voting/WhitelistedHashesRepository.cs index 56771d6a4e..cd8ac70124 100644 --- a/src/Stratis.Bitcoin.Features.PoA/Voting/WhitelistedHashesRepository.cs +++ b/src/Stratis.Bitcoin.Features.PoA/Voting/WhitelistedHashesRepository.cs @@ -24,16 +24,18 @@ public WhitelistedHashesRepository(ILoggerFactory loggerFactory, IKeyValueReposi this.locker = new object(); this.logger = loggerFactory.CreateLogger(this.GetType().FullName); - } - public void Initialize() - { + // Load this before initialize to ensure its available to when the Mempool feature initializes. lock (this.locker) { this.whitelistedHashes = this.kvRepository.LoadValueJson>(dbKey) ?? new List(); } } + public void Initialize() + { + } + private void SaveHashes() { lock (this.locker) diff --git a/src/Stratis.Bitcoin.Features.RPC.Tests/RPCSettingsTest.cs b/src/Stratis.Bitcoin.Features.RPC.Tests/RPCSettingsTest.cs index 3addf0ffd3..1a3d76af06 100644 --- a/src/Stratis.Bitcoin.Features.RPC.Tests/RPCSettingsTest.cs +++ b/src/Stratis.Bitcoin.Features.RPC.Tests/RPCSettingsTest.cs @@ -269,8 +269,8 @@ public void Load_ValidNodeSettings_UpdatesRpcSettingsFromNodeSettings_Empty_User Assert.True(rpcSettings.Server); Assert.Equal(1378, rpcSettings.RPCPort); - Assert.Equal(null, rpcSettings.RpcUser); - Assert.Equal(null, rpcSettings.RpcPassword); + Assert.Null(rpcSettings.RpcUser); + Assert.Null(rpcSettings.RpcPassword); Assert.NotEmpty(rpcSettings.Bind); Assert.Equal("127.0.0.1:1378", rpcSettings.Bind[0].ToString()); Assert.NotEmpty(rpcSettings.DefaultBindings); diff --git a/src/Stratis.Bitcoin.Features.RPC/Controllers/FullNodeController.cs b/src/Stratis.Bitcoin.Features.RPC/Controllers/FullNodeController.cs index fa0c2a1b36..4e9887ee3c 100644 --- a/src/Stratis.Bitcoin.Features.RPC/Controllers/FullNodeController.cs +++ b/src/Stratis.Bitcoin.Features.RPC/Controllers/FullNodeController.cs @@ -99,6 +99,7 @@ public FullNodeController( /// /// Stops the full node. /// + /// The asynchronous task. [ActionName("stop")] [ActionDescription("Stops the full node.")] public Task Stop() @@ -254,7 +255,7 @@ public async Task GetTxOutAsync(string txid, uint vout, bool incl /// The hex-encoded merkle proof. [ActionName("gettxoutproof")] [ActionDescription("Checks if transactions are within block. Returns a merkle proof of transaction inclusion.")] - public async Task GetTxOutProofAsync(string[] txids, string blockhash = "") + public Task GetTxOutProofAsync(string[] txids, string blockhash = "") { List transactionIds = txids.Select(txString => uint256.Parse(txString)).ToList(); @@ -306,7 +307,7 @@ public async Task GetTxOutProofAsync(string[] txids, string blockha var result = new MerkleBlock(block.Block, transactionIds.ToArray()); - return result; + return Task.FromResult(result); } /// diff --git a/src/Stratis.Bitcoin.Features.RPC/Stratis.Bitcoin.Features.RPC.csproj b/src/Stratis.Bitcoin.Features.RPC/Stratis.Bitcoin.Features.RPC.csproj index 4d74f02c2f..c6188bec38 100644 --- a/src/Stratis.Bitcoin.Features.RPC/Stratis.Bitcoin.Features.RPC.csproj +++ b/src/Stratis.Bitcoin.Features.RPC/Stratis.Bitcoin.Features.RPC.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.SignalR/EventSubscriptionService.cs b/src/Stratis.Bitcoin.Features.SignalR/EventSubscriptionService.cs index cd984367bd..b5c0ea48cb 100644 --- a/src/Stratis.Bitcoin.Features.SignalR/EventSubscriptionService.cs +++ b/src/Stratis.Bitcoin.Features.SignalR/EventSubscriptionService.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using NLog; +using Microsoft.Extensions.Logging; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.EventBus; using Stratis.Bitcoin.Signals; @@ -35,7 +36,7 @@ public void Init() { foreach (IClientEvent eventToHandle in this.options.EventsToHandle) { - this.logger.Debug("Create subscription for {0}", eventToHandle.NodeEventType); + this.logger.LogDebug("Create subscription for {0}", eventToHandle.NodeEventType); async Task callback(EventBase eventBase) { diff --git a/src/Stratis.Bitcoin.Features.SignalR/EventsHub.cs b/src/Stratis.Bitcoin.Features.SignalR/EventsHub.cs index 3d3bc07c3a..dfce4fa0df 100644 --- a/src/Stratis.Bitcoin.Features.SignalR/EventsHub.cs +++ b/src/Stratis.Bitcoin.Features.SignalR/EventsHub.cs @@ -3,10 +3,14 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; -using NLog; +using Microsoft.Extensions.Logging; +using Stratis.Bitcoin.Configuration.Logging; namespace Stratis.Bitcoin.Features.SignalR { + /// + /// SignalR message. + /// public class SignalRMessageArgs { public string Target { get; set; } @@ -30,17 +34,18 @@ public EventsHub() public override Task OnConnectedAsync() { - this.logger.Debug("New client with id {id} connected", this.Context.ConnectionId); + this.logger.LogDebug("New client with id {id} connected", this.Context.ConnectionId); return base.OnConnectedAsync(); } public override Task OnDisconnectedAsync(Exception exception) { - this.logger.Debug("Client with id {id} disconnected", this.Context.ConnectionId); + this.logger.LogDebug("Client with id {id} disconnected", this.Context.ConnectionId); return base.OnDisconnectedAsync(exception); } - /// Called using reflection from SignalR + /// Called using reflection from SignalR + /// See . public void SendMessage(SignalRMessageArgs message) { try @@ -55,7 +60,7 @@ public void SendMessage(SignalRMessageArgs message) } catch (Exception e) { - this.logger.Error("Error SendMessage", e); + this.logger.LogError("Error SendMessage", e); } } @@ -89,7 +94,7 @@ public async Task SendToClientsAsync(IClientEvent @event) } catch (Exception ex) { - this.logger.Error(ex, "Error sending to clients"); + this.logger.LogError(ex, "Error sending to clients"); } } } diff --git a/src/Stratis.Bitcoin.Features.SignalR/SignalRFeature.cs b/src/Stratis.Bitcoin.Features.SignalR/SignalRFeature.cs index 31fc049147..2a1ec89454 100644 --- a/src/Stratis.Bitcoin.Features.SignalR/SignalRFeature.cs +++ b/src/Stratis.Bitcoin.Features.SignalR/SignalRFeature.cs @@ -23,7 +23,7 @@ public class SignalRFeature : FullNodeFeature private readonly IEnumerable eventBroadcasters; private readonly IEventsSubscriptionService eventsSubscriptionService; private IWebHost webHost; - private readonly ILogger logger; + private readonly ILogger logger; public SignalRFeature( FullNode fullNode, diff --git a/src/Stratis.Bitcoin.Features.SignalR/Stratis.Bitcoin.Features.SignalR.csproj b/src/Stratis.Bitcoin.Features.SignalR/Stratis.Bitcoin.Features.SignalR.csproj index 81f203751b..fb71d5c61c 100644 --- a/src/Stratis.Bitcoin.Features.SignalR/Stratis.Bitcoin.Features.SignalR.csproj +++ b/src/Stratis.Bitcoin.Features.SignalR/Stratis.Bitcoin.Features.SignalR.csproj @@ -1,7 +1,7 @@  netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis.Features.SignalR Stratis.Features.SignalR Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/GlobalSuppressions.cs b/src/Stratis.Bitcoin.Features.SmartContracts/GlobalSuppressions.cs new file mode 100644 index 0000000000..fef494d847 --- /dev/null +++ b/src/Stratis.Bitcoin.Features.SmartContracts/GlobalSuppressions.cs @@ -0,0 +1,16 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +// Temporary fix to reduce the clutter of warnings. +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements must be documented", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.SmartContracts")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1615:Element return value must be documented", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.SmartContracts")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1611:The documentation for parameter is missing", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.SmartContracts")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1614:Element parameter documentation must have text", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.SmartContracts")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Element items must be documented", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.SmartContracts")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements must be documented", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.SmartContracts")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1616:Element return value documentation must have text", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.SmartContracts")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1627:The documentation text within the 'exception' tag must not be empty", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.Bitcoin.Features.SmartContracts")] diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/ContractSchemaFactory.cs b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/ContractSchemaFactory.cs index 39f28a3fee..d5058c4e3d 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/ContractSchemaFactory.cs +++ b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/ContractSchemaFactory.cs @@ -47,7 +47,7 @@ public IDictionary Map(IContractAssembly assembly) /// /// Maps a type to its schemas. /// - /// + /// /// public IDictionary Map(IEnumerable methods) { @@ -65,7 +65,7 @@ public OpenApiSchema Map(MethodInfo method) schema.Properties = new Dictionary(); schema.Title = method.Name; - foreach (var parameter in method.GetParameters()) + foreach (ParameterInfo parameter in method.GetParameters()) { // Default to string. OpenApiSchema paramSchema = PrimitiveTypeMap.ContainsKey(parameter.ParameterType) diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/ContractSwaggerController.cs b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/ContractSwaggerController.cs index e8d3a8fae2..8bdcaa938a 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/ContractSwaggerController.cs +++ b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/ContractSwaggerController.cs @@ -50,11 +50,11 @@ public ContractSwaggerController( /// Dynamically generates a swagger document for the contract at the given address. /// /// The contract's address. - /// A model. + /// A swagger document serialized as an OpenApi 3.0 json string. /// [Route("swagger/contracts/{address}")] [HttpGet] - public async Task ContractSwaggerDoc(string address) + public Task ContractSwaggerDoc(string address) { var code = this.stateRepository.GetCode(address.ToUint160(this.network)); @@ -82,7 +82,7 @@ public async Task ContractSwaggerDoc(string address) // TODO confirm v2/v3 var outputString = doc.Serialize(OpenApiSpecVersion.OpenApi3_0, OpenApiFormat.Json); - return Ok(outputString); + return Task.FromResult(Ok(outputString)); } /// @@ -92,7 +92,7 @@ public async Task ContractSwaggerDoc(string address) /// A success response. [HttpPost] [Route("api/swagger/contracts")] - public async Task AddContractToSwagger([FromBody] AddContractRequest address) + public Task AddContractToSwagger([FromBody] AddContractRequest address) { // Check that the contract exists var code = this.stateRepository.GetCode(address.Address.ToUint160(this.network)); @@ -111,7 +111,7 @@ public async Task AddContractToSwagger([FromBody] AddContractRequ this.uiOptions.ConfigObject.Urls = newUrls; - return Ok(); + return Task.FromResult(Ok()); } public class AddContractRequest diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/DynamicContractController.cs b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/DynamicContractController.cs index baafbda91b..42dc7fd444 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/DynamicContractController.cs +++ b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/DynamicContractController.cs @@ -62,7 +62,7 @@ public DynamicContractController( /// [Route("api/contract/{address}/method/{method}")] [HttpPost] - public async Task CallMethod([FromRoute] string address, [FromRoute] string method, [FromBody] JObject requestData) + public Task CallMethod([FromRoute] string address, [FromRoute] string method, [FromBody] JObject requestData) { var contractCode = this.stateRoot.GetCode(address.ToUint160(this.network)); @@ -88,7 +88,7 @@ public async Task CallMethod([FromRoute] string address, [FromRou BuildCallContractTransactionRequest request = this.MapCallRequest(address, method, methodParams, this.Request.Headers); // Proxy to the actual SC controller. - return this.smartContractWalletController.Call(request); + return Task.FromResult(this.smartContractWalletController.Call(request)); } /// diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/SmartContractsController.cs b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/SmartContractsController.cs index cb02a86981..436ad4ba94 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/SmartContractsController.cs +++ b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Controllers/SmartContractsController.cs @@ -190,7 +190,7 @@ public IActionResult GetStorage([FromQuery] GetStorageRequest request) if (storageValue == null) { - return this.Json(new + return this.NotFound(new { Message = string.Format("No data at storage with key {0}", request.StorageKey) }); @@ -300,16 +300,16 @@ public List ReceiptSearch(string contractAddress, string eventN /// A list of receipts for transactions relating to a specific smart contract and a specific event in that smart contract. [Route("api/[controller]/receipt-search")] [HttpGet] - public async Task ReceiptSearchAPI([FromQuery] string contractAddress, [FromQuery] string eventName, [FromQuery] List topics = null, [FromQuery] int fromBlock = 0, [FromQuery] int? toBlock = null) + public Task ReceiptSearchAPI([FromQuery] string contractAddress, [FromQuery] string eventName, [FromQuery] List topics = null, [FromQuery] int fromBlock = 0, [FromQuery] int? toBlock = null) { List result = this.smartContractTransactionService.ReceiptSearch(contractAddress, eventName, topics, fromBlock, toBlock); if (result == null) { - return ErrorHelpers.BuildErrorResponse(HttpStatusCode.InternalServerError, "No code exists", $"No contract execution code exists at {contractAddress}"); + return Task.FromResult(ErrorHelpers.BuildErrorResponse(HttpStatusCode.InternalServerError, "No code exists", $"No contract execution code exists at {contractAddress}")); } - return this.Json(result); + return Task.FromResult(this.Json(result)); } /// @@ -366,7 +366,7 @@ public IActionResult BuildCallSmartContractTransaction([FromBody] BuildCallContr if (!this.ModelState.IsValid) return ModelStateErrors.BuildErrorResponse(this.ModelState); - var response = this.smartContractTransactionService.BuildCallTx(request); + BuildCallContractTransactionResponse response = this.smartContractTransactionService.BuildCallTx(request); if (!response.Success) return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, response.Message, string.Empty); diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Swagger/ContractSwaggerDocGenerator.cs b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Swagger/ContractSwaggerDocGenerator.cs index 0459937117..72850e5d80 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Swagger/ContractSwaggerDocGenerator.cs +++ b/src/Stratis.Bitcoin.Features.SmartContracts/ReflectionExecutor/Swagger/ContractSwaggerDocGenerator.cs @@ -267,7 +267,6 @@ private List GetCallMetadataHeaderParams() /// The name of the swagger document to use. /// /// - /// /// public OpenApiDocument GetSwagger(string documentName, string host = null, string basePath = null) { diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/SmartContractFeature.cs b/src/Stratis.Bitcoin.Features.SmartContracts/SmartContractFeature.cs index 90000154f2..39c3e0acae 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/SmartContractFeature.cs +++ b/src/Stratis.Bitcoin.Features.SmartContracts/SmartContractFeature.cs @@ -18,7 +18,6 @@ using Stratis.Bitcoin.Features.SmartContracts.PoW; using Stratis.Bitcoin.Features.SmartContracts.ReflectionExecutor.Controllers; using Stratis.Bitcoin.Features.SmartContracts.Wallet; -using Stratis.Bitcoin.Interfaces; using Stratis.Bitcoin.Utilities; using Stratis.SmartContracts; using Stratis.SmartContracts.CLR; diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/Stratis.Bitcoin.Features.SmartContracts.csproj b/src/Stratis.Bitcoin.Features.SmartContracts/Stratis.Bitcoin.Features.SmartContracts.csproj index af83e6538e..c558051c57 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/Stratis.Bitcoin.Features.SmartContracts.csproj +++ b/src/Stratis.Bitcoin.Features.SmartContracts/Stratis.Bitcoin.Features.SmartContracts.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. Stratis.Features.SmartContracts Stratis.Features.SmartContracts @@ -10,6 +10,7 @@ bin\Debug\netcoreapp3.1\Stratis.Bitcoin.Features.SmartContracts.xml + 1701;1702;1591;1572;1573 diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/Wallet/SmartContractWalletController.cs b/src/Stratis.Bitcoin.Features.SmartContracts/Wallet/SmartContractWalletController.cs index 26255afe8d..f21b591f19 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/Wallet/SmartContractWalletController.cs +++ b/src/Stratis.Bitcoin.Features.SmartContracts/Wallet/SmartContractWalletController.cs @@ -121,9 +121,7 @@ public IActionResult GetAccountAddresses(string walletName) /// The function can be used to query the balance at a smart contract account address /// supplied by /api/SmartContractWallet/account-addresses. /// - /// - /// The address at which to retrieve the balance. - /// + /// The address at which to retrieve the balance. /// The balance at a specific wallet address in STRAT (or the sidechain coin). /// Returns address balance [Route("address-balance")] @@ -147,8 +145,7 @@ public IActionResult GetAddressBalance(string address) /// a list of all smart contract interactions for a wallet will be returned. /// /// - /// The name of the wallet holding the address. - /// The address to retrieve the history for. + /// See . /// A list of smart contract create and call transaction items as well as transaction items at a specific wallet address. /// Returns transaction history /// Invalid request or unexpected exception occurred @@ -230,7 +227,7 @@ public IActionResult GetHistory(GetHistoryRequest request) transactionItems.Add(result); } - return this.Json(transactionItems.OrderByDescending(x => x.BlockHeight ?? Int32.MaxValue)); + return this.Json(transactionItems.OrderByDescending(x => x.BlockHeight ?? int.MaxValue)); } catch (Exception e) { diff --git a/src/Stratis.Bitcoin.Features.SmartContracts/Wallet/SmartContractWalletFeature.cs b/src/Stratis.Bitcoin.Features.SmartContracts/Wallet/SmartContractWalletFeature.cs index b102535cd4..f7bd563423 100644 --- a/src/Stratis.Bitcoin.Features.SmartContracts/Wallet/SmartContractWalletFeature.cs +++ b/src/Stratis.Bitcoin.Features.SmartContracts/Wallet/SmartContractWalletFeature.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; using NBitcoin.Policy; -using NLog; using Stratis.Bitcoin.Builder; using Stratis.Bitcoin.Builder.Feature; using Stratis.Bitcoin.Configuration.Logging; @@ -27,7 +27,7 @@ public override Task InitializeAsync() { ILogger logger = LogManager.GetCurrentClassLogger(); - logger.Info("Smart Contract Feature Wallet Injected."); + logger.LogInformation("Smart Contract Feature Wallet Injected."); return Task.CompletedTask; } diff --git a/src/Stratis.Bitcoin.Features.Wallet.Tests/WalletControllerTest.cs b/src/Stratis.Bitcoin.Features.Wallet.Tests/WalletControllerTest.cs index b299424ed3..6c21602770 100644 --- a/src/Stratis.Bitcoin.Features.Wallet.Tests/WalletControllerTest.cs +++ b/src/Stratis.Bitcoin.Features.Wallet.Tests/WalletControllerTest.cs @@ -52,7 +52,7 @@ public async Task GenerateMnemonicWithoutParametersCreatesMnemonicWithDefaultsAs { var controller = this.GetWalletController(); - IActionResult result = await controller.GenerateMnemonic(); + IActionResult result = await controller.GenerateMnemonicAsync(); var viewResult = Assert.IsType(result); @@ -71,7 +71,7 @@ public async Task GenerateMnemonicWithDifferentWordCountCreatesMnemonicWithCorre { var controller = this.GetWalletController(); - IActionResult result = await controller.GenerateMnemonic(wordCount: 24); + IActionResult result = await controller.GenerateMnemonicAsync(wordCount: 24); var viewResult = Assert.IsType(result); @@ -94,7 +94,7 @@ public async Task GenerateMnemonicWithStrangeLanguageCasingReturnsCorrectMnemoni var controller = this.GetWalletController(); var wordList = (Wordlist)WordLists[language].GetValue(null, null); - IActionResult result = await controller.GenerateMnemonic(language); + IActionResult result = await controller.GenerateMnemonicAsync(language); var viewResult = Assert.IsType(result); @@ -110,7 +110,7 @@ public async Task GenerateMnemonicWithUnknownLanguageReturnsBadRequestAsync() { var controller = this.GetWalletController(); - IActionResult result = await controller.GenerateMnemonic("invalidlanguage"); + IActionResult result = await controller.GenerateMnemonicAsync("invalidlanguage"); var errorResult = Assert.IsType(result); var errorResponse = Assert.IsType(errorResult.Value); @@ -138,7 +138,7 @@ public async Task CreateWalletSuccessfullyReturnsMnemonicAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.Create(new WalletCreationRequest + IActionResult result = await controller.CreateAsync(new WalletCreationRequest { Name = "myName", Password = "", @@ -158,7 +158,7 @@ public async Task CreateWalletWithInvalidModelStateReturnsBadRequestAsync() controller.ModelState.AddModelError("Name", "Name cannot be empty."); - IActionResult result = await controller.Create(new WalletCreationRequest + IActionResult result = await controller.CreateAsync(new WalletCreationRequest { Name = "", Password = "", @@ -189,7 +189,7 @@ public async Task CreateWalletWithInvalidOperationExceptionReturnsConflictAsync( var controller = this.GetWalletController(); - IActionResult result = await controller.Create(new WalletCreationRequest + IActionResult result = await controller.CreateAsync(new WalletCreationRequest { Name = "myName", Password = "", @@ -221,7 +221,7 @@ public async Task CreateWalletWithNotSupportedExceptionExceptionReturnsBadReques var controller = this.GetWalletController(); - IActionResult result = await controller.Create(new WalletCreationRequest + IActionResult result = await controller.CreateAsync(new WalletCreationRequest { Name = "myName", Password = "", @@ -260,7 +260,7 @@ public async Task RecoverWalletSuccessfullyReturnsWalletModelAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.Recover(new WalletRecoveryRequest + IActionResult result = await controller.RecoverAsync(new WalletRecoveryRequest { Name = "myWallet", Password = "", @@ -277,6 +277,7 @@ public async Task RecoverWalletSuccessfullyReturnsWalletModelAsync() /// and the user recovers a new wallet at height X + Y. /// The wallet should continue syncing from X without jumpoing forward. /// + /// The asynchronous task. [Fact] public async Task RecoverWalletWithDatedAfterCurrentSyncHeightDoesNotMoveSyncHeightAsync() { @@ -303,7 +304,7 @@ public async Task RecoverWalletWithDatedAfterCurrentSyncHeightDoesNotMoveSyncHei var controller = this.GetWalletController(); - IActionResult result = await controller.Recover(new WalletRecoveryRequest + IActionResult result = await controller.RecoverAsync(new WalletRecoveryRequest { Name = "myWallet", Password = "", @@ -324,7 +325,7 @@ public async Task RecoverWalletWithInvalidModelStateReturnsBadRequestAsync() controller.ModelState.AddModelError("Password", "A password is required."); - IActionResult result = await controller.Recover(new WalletRecoveryRequest + IActionResult result = await controller.RecoverAsync(new WalletRecoveryRequest { Name = "myWallet", Password = "", @@ -355,7 +356,7 @@ public async Task RecoverWalletWithInvalidOperationExceptionReturnsConflictAsync var controller = this.GetWalletController(); - IActionResult result = await controller.Recover(new WalletRecoveryRequest + IActionResult result = await controller.RecoverAsync(new WalletRecoveryRequest { Name = "myWallet", Password = "", @@ -385,7 +386,7 @@ public async Task RecoverWalletWithFileNotFoundExceptionReturnsNotFoundAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.Recover(new WalletRecoveryRequest + IActionResult result = await controller.RecoverAsync(new WalletRecoveryRequest { Name = "myWallet", Password = "", @@ -416,7 +417,7 @@ public async Task RecoverWalletWithExceptionReturnsBadRequestAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.Recover(new WalletRecoveryRequest + IActionResult result = await controller.RecoverAsync(new WalletRecoveryRequest { Name = "myWallet", Password = "", @@ -473,7 +474,7 @@ private async Task RecoverWithExtPubAndCheckSuccessfulResponseAsync(string walle var controller = this.GetWalletController(); - IActionResult result = await controller.RecoverViaExtPubKey(new WalletExtPubRecoveryRequest + IActionResult result = await controller.RecoverViaExtPubKeyAsync(new WalletExtPubRecoveryRequest { Name = walletName, ExtPubKey = extPubKey, @@ -522,7 +523,7 @@ public async Task RecoverWalletWithExtPubDatedAfterCurrentSyncHeightDoesNotMoveS var controller = this.GetWalletController(); - IActionResult result = await controller.RecoverViaExtPubKey(new WalletExtPubRecoveryRequest + IActionResult result = await controller.RecoverViaExtPubKeyAsync(new WalletExtPubRecoveryRequest { Name = walletName, ExtPubKey = extPubKey, @@ -549,7 +550,7 @@ public async Task LoadWalletSuccessfullyReturnsWalletModelAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.Load(new WalletLoadRequest + IActionResult result = await controller.LoadAsync(new WalletLoadRequest { Name = "myWallet", Password = "" @@ -567,7 +568,7 @@ public async Task LoadWalletWithInvalidModelReturnsBadRequestAsync() controller.ModelState.AddModelError("Password", "A password is required."); - IActionResult result = await controller.Load(new WalletLoadRequest + IActionResult result = await controller.LoadAsync(new WalletLoadRequest { Name = "myWallet", Password = "" @@ -591,7 +592,7 @@ public async Task LoadWalletWithFileNotFoundExceptionandReturnsNotFoundAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.Load(new WalletLoadRequest + IActionResult result = await controller.LoadAsync(new WalletLoadRequest { Name = "myName", Password = "" @@ -617,7 +618,7 @@ public async Task LoadWalletWithSecurityExceptionandReturnsForbiddenAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.Load(new WalletLoadRequest + IActionResult result = await controller.LoadAsync(new WalletLoadRequest { Name = "myName", Password = "" @@ -643,7 +644,7 @@ public async Task LoadWalletWithOtherExceptionandReturnsBadRequestAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.Load(new WalletLoadRequest + IActionResult result = await controller.LoadAsync(new WalletLoadRequest { Name = "myName", Password = "" @@ -847,7 +848,7 @@ public async Task GetBalanceWithValidModelStateReturnsWalletBalanceModelAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.GetBalance(new WalletBalanceRequest + IActionResult result = await controller.GetBalanceAsync(new WalletBalanceRequest { WalletName = "myWallet" }); @@ -876,7 +877,7 @@ public async Task GetBalanceWithValidModelStateReturnsWalletBalanceModelAsync() } [Fact] - public async Task WalletSyncFromDateReturnsOK() + public async Task WalletSyncFromDateReturnsOKAsync() { string walletName = "myWallet"; DateTime syncDate = DateTime.Now.Subtract(new TimeSpan(1)).Date; @@ -888,7 +889,7 @@ public async Task WalletSyncFromDateReturnsOK() var controller = this.GetWalletController(); - IActionResult result = await controller.SyncFromDate(new WalletSyncRequest + IActionResult result = await controller.SyncFromDateAsync(new WalletSyncRequest { WalletName = walletName, Date = DateTime.Now.Subtract(new TimeSpan(1)).Date @@ -897,11 +898,11 @@ public async Task WalletSyncFromDateReturnsOK() var viewResult = Assert.IsType(result); mockWalletSyncManager.Verify(); Assert.NotNull(viewResult); - Assert.NotNull(viewResult.StatusCode == (int)HttpStatusCode.OK); + Assert.Equal((int)HttpStatusCode.OK, viewResult.StatusCode); } [Fact] - public async Task WalletSyncAllReturnsOK() + public async Task WalletSyncAllReturnsOKAsync() { string walletName = "myWallet"; @@ -912,7 +913,7 @@ public async Task WalletSyncAllReturnsOK() var controller = this.GetWalletController(); - IActionResult result = await controller.SyncFromDate(new WalletSyncRequest + IActionResult result = await controller.SyncFromDateAsync(new WalletSyncRequest { WalletName = walletName, All = true @@ -921,11 +922,11 @@ public async Task WalletSyncAllReturnsOK() var viewResult = Assert.IsType(result); mockWalletSyncManager.Verify(); Assert.NotNull(viewResult); - Assert.NotNull(viewResult.StatusCode == (int)HttpStatusCode.OK); + Assert.Equal((int)HttpStatusCode.OK, viewResult.StatusCode); } [Fact] - public async Task GetBalanceWithEmptyListOfAccountsReturnsWalletBalanceModel() + public async Task GetBalanceWithEmptyListOfAccountsReturnsWalletBalanceModelAsync() { var accounts = new List(); var mockWalletManager = new Mock(); @@ -934,7 +935,7 @@ public async Task GetBalanceWithEmptyListOfAccountsReturnsWalletBalanceModel() var controller = this.GetWalletController(); - IActionResult result = await controller.GetBalance(new WalletBalanceRequest + IActionResult result = await controller.GetBalanceAsync(new WalletBalanceRequest { WalletName = "myWallet", AccountName = null @@ -948,12 +949,12 @@ public async Task GetBalanceWithEmptyListOfAccountsReturnsWalletBalanceModel() } [Fact] - public async Task GetBalanceWithInvalidValidModelStateReturnsBadRequest() + public async Task GetBalanceWithInvalidValidModelStateReturnsBadRequestAsync() { var controller = this.GetWalletController(); controller.ModelState.AddModelError("WalletName", "A walletname is required."); - IActionResult result = await controller.GetBalance(new WalletBalanceRequest + IActionResult result = await controller.GetBalanceAsync(new WalletBalanceRequest { WalletName = "" }); @@ -968,7 +969,7 @@ public async Task GetBalanceWithInvalidValidModelStateReturnsBadRequest() } [Fact] - public async Task GetBalanceWithExceptionReturnsBadRequest() + public async Task GetBalanceWithExceptionReturnsBadRequestAsync() { var mockWalletManager = this.ConfigureMock(); mockWalletManager.Setup(m => m.GetBalances("myWallet", WalletManager.DefaultAccount, It.IsAny())) @@ -976,7 +977,7 @@ public async Task GetBalanceWithExceptionReturnsBadRequest() var controller = this.GetWalletController(); - IActionResult result = await controller.GetBalance(new WalletBalanceRequest + IActionResult result = await controller.GetBalanceAsync(new WalletBalanceRequest { WalletName = "myWallet" }); @@ -992,7 +993,7 @@ public async Task GetBalanceWithExceptionReturnsBadRequest() } [Fact] - public async Task GetAddressBalanceWithValidModelStateReturnsAddressBalanceModel() + public async Task GetAddressBalanceWithValidModelStateReturnsAddressBalanceModelAsync() { HdAccount account = WalletTestsHelpers.CreateAccount("account 1"); HdAddress accountAddress = WalletTestsHelpers.CreateAddress(true); @@ -1011,7 +1012,7 @@ public async Task GetAddressBalanceWithValidModelStateReturnsAddressBalanceModel var controller = this.GetWalletController(); - IActionResult result = await controller.GetReceivedByAddress(new ReceivedByAddressRequest + IActionResult result = await controller.GetReceivedByAddressAsync(new ReceivedByAddressRequest { Address = accountAddress.Address }); @@ -1027,7 +1028,7 @@ public async Task GetAddressBalanceWithValidModelStateReturnsAddressBalanceModel } [Fact] - public async Task GetAddressBalanceWithExceptionReturnsBadRequest() + public async Task GetAddressBalanceWithExceptionReturnsBadRequestAsync() { var mockWalletManager = this.ConfigureMock(); mockWalletManager.Setup(m => m.GetAddressBalance("MyAddress")) @@ -1035,7 +1036,7 @@ public async Task GetAddressBalanceWithExceptionReturnsBadRequest() var controller = this.GetWalletController(); - IActionResult result = await controller.GetReceivedByAddress(new ReceivedByAddressRequest + IActionResult result = await controller.GetReceivedByAddressAsync(new ReceivedByAddressRequest { Address = "MyAddress" }); @@ -1051,12 +1052,12 @@ public async Task GetAddressBalanceWithExceptionReturnsBadRequest() } [Fact] - public async Task GetAddressBalanceWithInvalidModelStateReturnsBadRequest() + public async Task GetAddressBalanceWithInvalidModelStateReturnsBadRequestAsync() { var controller = this.GetWalletController(); controller.ModelState.AddModelError("Address", "An address is required."); - IActionResult result = await controller.GetReceivedByAddress(new ReceivedByAddressRequest + IActionResult result = await controller.GetReceivedByAddressAsync(new ReceivedByAddressRequest { Address = "" }); @@ -1071,7 +1072,7 @@ public async Task GetAddressBalanceWithInvalidModelStateReturnsBadRequest() } [Fact] - public async Task BuildTransactionWithValidRequestAllowingUnconfirmedReturnsWalletBuildTransactionModel() + public async Task BuildTransactionWithValidRequestAllowingUnconfirmedReturnsWalletBuildTransactionModelAsync() { var mockWalletTransactionHandler = this.ConfigureMock(); @@ -1082,7 +1083,7 @@ public async Task BuildTransactionWithValidRequestAllowingUnconfirmedReturnsWall var controller = this.GetWalletController(); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { AccountName = "Account 1", AllowUnconfirmed = true, @@ -1108,7 +1109,7 @@ public async Task BuildTransactionWithValidRequestAllowingUnconfirmedReturnsWall } [Fact] - public async Task BuildTransactionWithCustomFeeAmountAndFeeTypeReturnsWalletBuildTransactionModelWithFeeAmount() + public async Task BuildTransactionWithCustomFeeAmountAndFeeTypeReturnsWalletBuildTransactionModelWithFeeAmountAsync() { var key = new Key(); this.ConfigureMock(mock => @@ -1120,7 +1121,7 @@ public async Task BuildTransactionWithCustomFeeAmountAndFeeTypeReturnsWalletBuil var controller = this.GetWalletController(); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { AccountName = "Account 1", AllowUnconfirmed = true, @@ -1147,7 +1148,7 @@ public async Task BuildTransactionWithCustomFeeAmountAndFeeTypeReturnsWalletBuil [Fact] public async Task - BuildTransactionWithCustomFeeAmountAndNoFeeTypeReturnsWalletBuildTransactionModelWithFeeAmount() + BuildTransactionWithCustomFeeAmountAndNoFeeTypeReturnsWalletBuildTransactionModelWithFeeAmountAsync() { var key = new Key(); this.ConfigureMock(mock => @@ -1159,7 +1160,7 @@ public async Task var controller = this.GetWalletController(); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { AccountName = "Account 1", AllowUnconfirmed = true, @@ -1184,7 +1185,7 @@ public async Task } [Fact] - public async Task BuildTransactionWithValidRequestNotAllowingUnconfirmedReturnsWalletBuildTransactionModel() + public async Task BuildTransactionWithValidRequestNotAllowingUnconfirmedReturnsWalletBuildTransactionModelAsync() { var key = new Key(); var sentTrx = new Transaction(); @@ -1196,7 +1197,7 @@ public async Task BuildTransactionWithValidRequestNotAllowingUnconfirmedReturnsW var controller = this.GetWalletController(); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { AccountName = "Account 1", AllowUnconfirmed = false, @@ -1222,7 +1223,7 @@ public async Task BuildTransactionWithValidRequestNotAllowingUnconfirmedReturnsW } [Fact] - public async Task BuildTransactionWithChangeAddressReturnsWalletBuildTransactionModel() + public async Task BuildTransactionWithChangeAddressReturnsWalletBuildTransactionModelAsync() { string walletName = "myWallet"; @@ -1245,7 +1246,7 @@ public async Task BuildTransactionWithChangeAddressReturnsWalletBuildTransaction var controller = this.GetWalletController(); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { AccountName = "Account 0", Recipients = new List() { new RecipientModel() { Amount = "1.0", DestinationAddress = new Key().PubKey.Hash.GetAddress(this.Network).ToString() } }, @@ -1265,7 +1266,7 @@ public async Task BuildTransactionWithChangeAddressReturnsWalletBuildTransaction } [Fact] - public async Task BuildTransactionWithChangeAddressNotInWalletReturnsBadRequest() + public async Task BuildTransactionWithChangeAddressNotInWalletReturnsBadRequestAsync() { string walletName = "myWallet"; @@ -1281,7 +1282,7 @@ public async Task BuildTransactionWithChangeAddressNotInWalletReturnsBadRequest( var controller = this.GetWalletController(); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { AccountName = "Account 0", Recipients = new List() { new RecipientModel() { Amount = "1.0", DestinationAddress = new Key().PubKey.Hash.GetAddress(this.Network).ToString() } }, @@ -1303,7 +1304,7 @@ public async Task BuildTransactionWithChangeAddressNotInWalletReturnsBadRequest( } [Fact] - public async Task BuildTransactionWithChangeAddressAccountNotInWalletReturnsBadRequest() + public async Task BuildTransactionWithChangeAddressAccountNotInWalletReturnsBadRequestAsync() { string walletName = "myWallet"; @@ -1319,7 +1320,7 @@ public async Task BuildTransactionWithChangeAddressAccountNotInWalletReturnsBadR var controller = this.GetWalletController(); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { AccountName = "Account 0", Recipients = new List() { new RecipientModel() { Amount = "1.0", DestinationAddress = new Key().PubKey.Hash.GetAddress(this.Network).ToString() } }, @@ -1341,12 +1342,12 @@ public async Task BuildTransactionWithChangeAddressAccountNotInWalletReturnsBadR } [Fact] - public async Task BuildTransactionWithInvalidModelStateReturnsBadRequest() + public async Task BuildTransactionWithInvalidModelStateReturnsBadRequestAsync() { var controller = this.GetWalletController(); controller.ModelState.AddModelError("WalletName", "A walletname is required."); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { WalletName = "" }); @@ -1361,7 +1362,7 @@ public async Task BuildTransactionWithInvalidModelStateReturnsBadRequest() } [Fact] - public async Task BuildTransactionWithExceptionReturnsBadRequest() + public async Task BuildTransactionWithExceptionReturnsBadRequestAsync() { var key = new Key(); this.ConfigureMock(mock => @@ -1372,7 +1373,7 @@ public async Task BuildTransactionWithExceptionReturnsBadRequest() var controller = this.GetWalletController(); - IActionResult result = await controller.BuildTransaction(new BuildTransactionRequest + IActionResult result = await controller.BuildTransactionAsync(new BuildTransactionRequest { AccountName = "Account 1", AllowUnconfirmed = false, @@ -1400,7 +1401,7 @@ public async Task BuildTransactionWithExceptionReturnsBadRequest() } [Fact] - public async Task SendTransactionSuccessfulReturnsWalletSendTransactionModelResponse() + public async Task SendTransactionSuccessfulReturnsWalletSendTransactionModelResponseAsync() { string transactionHex = "010000000189c041f79aac3aa7e7a72804a9a55cd9eceba41a0586640f602eb9823540ce89010000006b483045022100ab9597b37cb8796aefa30b207abb248c8003d4d153076997e375b0daf4f9f7050220546397fee1cefe54c49210ea653e9e61fb88adf51b68d2c04ad6d2b46ddf97a30121035cc9de1f233469dad8a3bbd1e61b699a7dd8e0d8370c6f3b1f2a16167da83546ffffffff02f6400a00000000001976a914accf603142aaa5e22dc82500d3e187caf712f11588ac3cf61700000000001976a91467872601dda216fbf4cab7891a03ebace87d8e7488ac00000000"; @@ -1417,7 +1418,7 @@ public async Task SendTransactionSuccessfulReturnsWalletSendTransactionModelResp var controller = this.GetWalletController(); - IActionResult result = await controller.SendTransaction(new SendTransactionRequest(transactionHex)); + IActionResult result = await controller.SendTransactionAsync(new SendTransactionRequest(transactionHex)); var viewResult = Assert.IsType(result); var model = viewResult.Value as WalletSendTransactionModel; @@ -1441,7 +1442,7 @@ public async Task SendTransactionFailedBecauseNoNodesConnectedAsync() var controller = this.GetWalletController(); - IActionResult result = await controller.SendTransaction(new SendTransactionRequest(new uint256(15555).ToString())); + IActionResult result = await controller.SendTransactionAsync(new SendTransactionRequest(new uint256(15555).ToString())); var errorResult = Assert.IsType(result); var errorResponse = Assert.IsType(errorResult.Value); @@ -1453,12 +1454,12 @@ public async Task SendTransactionFailedBecauseNoNodesConnectedAsync() } [Fact] - public async Task SendTransactionWithInvalidModelStateReturnsBadRequest() + public async Task SendTransactionWithInvalidModelStateReturnsBadRequestAsync() { var controller = this.GetWalletController(); controller.ModelState.AddModelError("Hex", "Hex required."); - IActionResult result = await controller.SendTransaction(new SendTransactionRequest("")); + IActionResult result = await controller.SendTransactionAsync(new SendTransactionRequest("")); var errorResult = Assert.IsType(result); var errorResponse = Assert.IsType(errorResult.Value); @@ -1470,9 +1471,8 @@ public async Task SendTransactionWithInvalidModelStateReturnsBadRequest() } [Fact] - public async Task ListWalletFilesWithExistingWalletFilesReturnsWalletFileModel() + public async Task ListWalletFilesWithExistingWalletFilesReturnsWalletFileModelAsync() { - string walletPath = "walletPath"; var walletManager = this.ConfigureMock(); walletManager.Setup(m => m.GetWalletsNames()) .Returns(new[] { "wallet1.wallet.json", "wallet2.wallet.json" }); @@ -1481,7 +1481,7 @@ public async Task ListWalletFilesWithExistingWalletFilesReturnsWalletFileModel() var controller = this.GetWalletController(); - IActionResult result = await controller.ListWallets(); + IActionResult result = await controller.ListWalletsAsync(); var viewResult = Assert.IsType(result); var model = viewResult.Value as WalletInfoModel; @@ -1493,7 +1493,7 @@ public async Task ListWalletFilesWithExistingWalletFilesReturnsWalletFileModel() } [Fact] - public async Task ListWalletFilesWithoutExistingWalletFilesReturnsWalletFileModel() + public async Task ListWalletFilesWithoutExistingWalletFilesReturnsWalletFileModelAsync() { var walletManager = this.ConfigureMock(); @@ -1502,7 +1502,7 @@ public async Task ListWalletFilesWithoutExistingWalletFilesReturnsWalletFileMode var controller = this.GetWalletController(); - IActionResult result = await controller.ListWallets(); + IActionResult result = await controller.ListWalletsAsync(); var viewResult = Assert.IsType(result); var model = viewResult.Value as WalletInfoModel; @@ -1512,7 +1512,7 @@ public async Task ListWalletFilesWithoutExistingWalletFilesReturnsWalletFileMode } [Fact] - public async Task ListWalletFilesWithExceptionReturnsBadRequest() + public async Task ListWalletFilesWithExceptionReturnsBadRequestAsync() { var walletManager = this.ConfigureMock(); walletManager.Setup(m => m.GetWalletsNames()) @@ -1520,7 +1520,7 @@ public async Task ListWalletFilesWithExceptionReturnsBadRequest() var controller = this.GetWalletController(); - IActionResult result = await controller.ListWallets(); + IActionResult result = await controller.ListWalletsAsync(); var errorResult = Assert.IsType(result); var errorResponse = Assert.IsType(errorResult.Value); @@ -1532,7 +1532,7 @@ public async Task ListWalletFilesWithExceptionReturnsBadRequest() } [Fact] - public async Task CreateNewAccountWithValidModelReturnsAccountName() + public async Task CreateNewAccountWithValidModelReturnsAccountNameAsync() { var mockWalletManager = this.ConfigureMock(); mockWalletManager.Setup(m => m.GetUnusedAccount("myWallet", "test")) @@ -1540,7 +1540,7 @@ public async Task CreateNewAccountWithValidModelReturnsAccountName() var controller = this.GetWalletController(); - IActionResult result = await controller.CreateNewAccount(new GetUnusedAccountModel + IActionResult result = await controller.CreateNewAccountAsync(new GetUnusedAccountModel { WalletName = "myWallet", Password = "test" @@ -1551,7 +1551,7 @@ public async Task CreateNewAccountWithValidModelReturnsAccountName() } [Fact] - public async Task CreateNewAccountWithInvalidValidModelReturnsBadRequest() + public async Task CreateNewAccountWithInvalidValidModelReturnsBadRequestAsync() { var mockWalletManager = new Mock(); @@ -1559,7 +1559,7 @@ public async Task CreateNewAccountWithInvalidValidModelReturnsBadRequest() controller.ModelState.AddModelError("Password", "A password is required."); - IActionResult result = await controller.CreateNewAccount(new GetUnusedAccountModel + IActionResult result = await controller.CreateNewAccountAsync(new GetUnusedAccountModel { WalletName = "myWallet", Password = "" @@ -1575,7 +1575,7 @@ public async Task CreateNewAccountWithInvalidValidModelReturnsBadRequest() } [Fact] - public async Task CreateNewAccountWithExceptionReturnsBadRequest() + public async Task CreateNewAccountWithExceptionReturnsBadRequestAsync() { var mockWalletManager = this.ConfigureMock(); mockWalletManager.Setup(m => m.GetUnusedAccount("myWallet", "test")) @@ -1583,7 +1583,7 @@ public async Task CreateNewAccountWithExceptionReturnsBadRequest() var controller = this.GetWalletController(); - IActionResult result = await controller.CreateNewAccount(new GetUnusedAccountModel + IActionResult result = await controller.CreateNewAccountAsync(new GetUnusedAccountModel { WalletName = "myWallet", Password = "test" @@ -1600,7 +1600,7 @@ public async Task CreateNewAccountWithExceptionReturnsBadRequest() } [Fact] - public async Task ListAccountsWithValidModelStateReturnsAccounts() + public async Task ListAccountsWithValidModelStateReturnsAccountsAsync() { string walletName = "wallet 1"; Wallet wallet = WalletTestsHelpers.CreateWallet(walletName); @@ -1614,7 +1614,7 @@ public async Task ListAccountsWithValidModelStateReturnsAccounts() var controller = this.GetWalletController(); - IActionResult result = await controller.ListAccounts(new ListAccountsModel + IActionResult result = await controller.ListAccountsAsync(new ListAccountsModel { WalletName = "wallet 1" }); @@ -1629,13 +1629,13 @@ public async Task ListAccountsWithValidModelStateReturnsAccounts() } [Fact] - public async Task ListAccountsWithInvalidModelReturnsBadRequest() + public async Task ListAccountsWithInvalidModelReturnsBadRequestAsync() { var controller = this.GetWalletController(); controller.ModelState.AddModelError("WalletName", "A wallet name is required."); - IActionResult result = await controller.ListAccounts(new ListAccountsModel + IActionResult result = await controller.ListAccountsAsync(new ListAccountsModel { WalletName = "" }); @@ -1650,7 +1650,7 @@ public async Task ListAccountsWithInvalidModelReturnsBadRequest() } [Fact] - public async Task ListAccountsWithExceptionReturnsBadRequest() + public async Task ListAccountsWithExceptionReturnsBadRequestAsync() { var mockWalletManager = this.ConfigureMock(); mockWalletManager.Setup(m => m.GetAccounts("wallet 0")) @@ -1658,7 +1658,7 @@ public async Task ListAccountsWithExceptionReturnsBadRequest() var controller = this.GetWalletController(); - IActionResult result = await controller.ListAccounts(new ListAccountsModel + IActionResult result = await controller.ListAccountsAsync(new ListAccountsModel { WalletName = "wallet 0", }); @@ -1674,7 +1674,7 @@ public async Task ListAccountsWithExceptionReturnsBadRequest() } [Fact] - public async Task GetUnusedAddressWithValidModelReturnsUnusedAddress() + public async Task GetUnusedAddressWithValidModelReturnsUnusedAddressAsync() { HdAddress address = WalletTestsHelpers.CreateAddress(); var mockWalletManager = this.ConfigureMock(); @@ -1683,7 +1683,7 @@ public async Task GetUnusedAddressWithValidModelReturnsUnusedAddress() var controller = this.GetWalletController(); - IActionResult result = await controller.GetUnusedAddress(new GetUnusedAddressModel + IActionResult result = await controller.GetUnusedAddressAsync(new GetUnusedAddressModel { WalletName = "myWallet", AccountName = "Account 1" @@ -1694,13 +1694,13 @@ public async Task GetUnusedAddressWithValidModelReturnsUnusedAddress() } [Fact] - public async Task GetUnusedAddressWithInvalidValidModelReturnsBadRequest() + public async Task GetUnusedAddressWithInvalidValidModelReturnsBadRequestAsync() { var controller = this.GetWalletController(); controller.ModelState.AddModelError("AccountName", "An account name is required."); - IActionResult result = await controller.GetUnusedAddress(new GetUnusedAddressModel + IActionResult result = await controller.GetUnusedAddressAsync(new GetUnusedAddressModel { WalletName = "myWallet", AccountName = "" @@ -1716,7 +1716,7 @@ public async Task GetUnusedAddressWithInvalidValidModelReturnsBadRequest() } [Fact] - public async Task GetUnusedAddressWithExceptionReturnsBadRequest() + public async Task GetUnusedAddressWithExceptionReturnsBadRequestAsync() { var mockWalletManager = this.ConfigureMock(); mockWalletManager.Setup(m => m.GetUnusedAddress(new WalletAccountReference("myWallet", "Account 1"))) @@ -1724,7 +1724,7 @@ public async Task GetUnusedAddressWithExceptionReturnsBadRequest() var controller = this.GetWalletController(); - IActionResult result = await controller.GetUnusedAddress(new GetUnusedAddressModel + IActionResult result = await controller.GetUnusedAddressAsync(new GetUnusedAddressModel { WalletName = "myWallet", AccountName = "Account 1" @@ -1741,7 +1741,7 @@ public async Task GetUnusedAddressWithExceptionReturnsBadRequest() } [Fact] - public async Task GetAllAddressesWithValidModelReturnsAllAddresses() + public async Task GetAllAddressesWithValidModelReturnsAllAddressesAsync() { string walletName = "myWallet"; @@ -1787,7 +1787,7 @@ public async Task GetAllAddressesWithValidModelReturnsAllAddresses() var controller = this.GetWalletController(); - IActionResult result = await controller.GetAllAddresses(new GetAllAddressesModel + IActionResult result = await controller.GetAllAddressesAsync(new GetAllAddressesModel { WalletName = "myWallet", AccountName = "Account 0" }); var viewResult = Assert.IsType(result); @@ -1824,13 +1824,13 @@ public async Task GetAllAddressesWithValidModelReturnsAllAddresses() } [Fact] - public async Task GetMaximumBalanceWithValidModelStateReturnsMaximumBalance() + public async Task GetMaximumBalanceWithValidModelStateReturnsMaximumBalanceAsync() { var controller = this.GetWalletController(); controller.ModelState.AddModelError("Error in model", "There was an error in the model."); - IActionResult result = await controller.GetMaximumSpendableBalance(new WalletMaximumBalanceRequest + IActionResult result = await controller.GetMaximumSpendableBalanceAsync(new WalletMaximumBalanceRequest { WalletName = "myWallet", AccountName = "account 1", @@ -1849,7 +1849,7 @@ public async Task GetMaximumBalanceWithValidModelStateReturnsMaximumBalance() } [Fact] - public async Task GetMaximumBalanceSuccessfullyReturnsMaximumBalanceAndFee() + public async Task GetMaximumBalanceSuccessfullyReturnsMaximumBalanceAndFeeAsync() { var mockWalletTransactionHandler = this.ConfigureMock(); mockWalletTransactionHandler @@ -1858,7 +1858,7 @@ public async Task GetMaximumBalanceSuccessfullyReturnsMaximumBalanceAndFee() var controller = this.GetWalletController(); - IActionResult result = await controller.GetMaximumSpendableBalance(new WalletMaximumBalanceRequest + IActionResult result = await controller.GetMaximumSpendableBalanceAsync(new WalletMaximumBalanceRequest { WalletName = "myWallet", AccountName = "account 1", @@ -1875,7 +1875,7 @@ public async Task GetMaximumBalanceSuccessfullyReturnsMaximumBalanceAndFee() } [Fact] - public async Task GetMaximumBalanceWithExceptionReturnsBadRequest() + public async Task GetMaximumBalanceWithExceptionReturnsBadRequestAsync() { var mockWalletTransactionHandler = this.ConfigureMock(); mockWalletTransactionHandler @@ -1884,7 +1884,7 @@ public async Task GetMaximumBalanceWithExceptionReturnsBadRequest() var controller = this.GetWalletController(); - IActionResult result = await controller.GetMaximumSpendableBalance(new WalletMaximumBalanceRequest + IActionResult result = await controller.GetMaximumSpendableBalanceAsync(new WalletMaximumBalanceRequest { WalletName = "myWallet", AccountName = "account 1", @@ -1900,7 +1900,7 @@ public async Task GetMaximumBalanceWithExceptionReturnsBadRequest() } [Fact] - public async Task GetTransactionFeeEstimateWithValidRequestReturnsFee() + public async Task GetTransactionFeeEstimateWithValidRequestReturnsFeeAsync() { var mockWalletManager = this.ConfigureMock(); var mockWalletTransactionHandler = this.ConfigureMock(); @@ -1911,7 +1911,7 @@ public async Task GetTransactionFeeEstimateWithValidRequestReturnsFee() var controller = this.GetWalletController(); - IActionResult result = await controller.GetTransactionFeeEstimate(new TxFeeEstimateRequest + IActionResult result = await controller.GetTransactionFeeEstimateAsync(new TxFeeEstimateRequest { AccountName = "Account 1", Recipients = new List @@ -1934,7 +1934,7 @@ public async Task GetTransactionFeeEstimateWithValidRequestReturnsFee() } [Fact] - public async Task RemoveAllTransactionsWithSyncEnabledSyncsAfterRemoval() + public async Task RemoveAllTransactionsWithSyncEnabledSyncsAfterRemovalAsync() { // Arrange. string walletName = "wallet1"; @@ -1963,7 +1963,7 @@ public async Task RemoveAllTransactionsWithSyncEnabledSyncsAfterRemoval() }; // Act. - IActionResult result = await controller.RemoveTransactions(requestModel); + IActionResult result = await controller.RemoveTransactionsAsync(requestModel); // Assert. walletManager.VerifyAll(); @@ -1979,7 +1979,7 @@ public async Task RemoveAllTransactionsWithSyncEnabledSyncsAfterRemoval() } [Fact] - public async Task RemoveAllTransactionsWithSyncDisabledDoesNotSyncAfterRemoval() + public async Task RemoveAllTransactionsWithSyncDisabledDoesNotSyncAfterRemovalAsync() { // Arrange. string walletName = "wallet1"; @@ -2004,7 +2004,7 @@ public async Task RemoveAllTransactionsWithSyncDisabledDoesNotSyncAfterRemoval() }; // Act. - IActionResult result = await controller.RemoveTransactions(requestModel); + IActionResult result = await controller.RemoveTransactionsAsync(requestModel); // Assert. walletManager.VerifyAll(); @@ -2020,7 +2020,7 @@ public async Task RemoveAllTransactionsWithSyncDisabledDoesNotSyncAfterRemoval() } [Fact] - public async Task RemoveTransactionsWithIdsRemovesAllTransactionsByIds() + public async Task RemoveTransactionsWithIdsRemovesAllTransactionsByIdsAsync() { // Arrange. string walletName = "wallet1"; @@ -2048,7 +2048,7 @@ public async Task RemoveTransactionsWithIdsRemovesAllTransactionsByIds() }; // Act. - IActionResult result = await controller.RemoveTransactions(requestModel); + IActionResult result = await controller.RemoveTransactionsAsync(requestModel); // Assert. walletManager.VerifyAll(); diff --git a/src/Stratis.Bitcoin.Features.Wallet.Tests/WalletManagerTest.cs b/src/Stratis.Bitcoin.Features.Wallet.Tests/WalletManagerTest.cs index 5564e1d267..1ff9a5af7c 100644 --- a/src/Stratis.Bitcoin.Features.Wallet.Tests/WalletManagerTest.cs +++ b/src/Stratis.Bitcoin.Features.Wallet.Tests/WalletManagerTest.cs @@ -1432,7 +1432,7 @@ public void ProcessTransactionWithValidTransactionLoadsTransactionsIntoWalletIfM HdAddress spentAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0); Assert.Single(spendingAddress.Transactions); Assert.Equal(transaction.GetHash(), spentAddressResult.Transactions.ElementAt(0).SpendingDetails.TransactionId); - Assert.True(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments.Any(p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value)); + Assert.Contains(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments, p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value); Assert.Single(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(1).Transactions); TransactionData destinationAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(1).Transactions.ElementAt(0); @@ -1494,7 +1494,7 @@ public void ProcessTransactionWithValidSegwitTransactionLoadsTransactionsIntoWal HdAddress spentAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0); Assert.Single(spendingAddress.Transactions); Assert.Equal(transaction.GetHash(), spentAddressResult.Transactions.ElementAt(0).SpendingDetails.TransactionId); - Assert.True(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments.Any(p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value)); + Assert.Contains(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments, p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value); Assert.Single(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(1).Transactions); TransactionData destinationAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(1).Transactions.ElementAt(0); @@ -1668,7 +1668,7 @@ public void ProcessTransactionWithDestinationAsMultisigAddTransactionAsPayment() HdAddress spentAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0); Assert.Single(spendingAddress.Transactions); Assert.Equal(transaction.GetHash(), spentAddressResult.Transactions.ElementAt(0).SpendingDetails.TransactionId); - Assert.True(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments.Any(p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value)); + Assert.Contains(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments, p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value); Assert.Single(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Transactions); TransactionData changeAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Transactions.ElementAt(0); @@ -1731,7 +1731,7 @@ public void ProcessTransactionWithBlockHeightSetsBlockHeightOnTransactionData() HdAddress spentAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0); Assert.Single(spendingAddress.Transactions); Assert.Equal(transaction.GetHash(), spentAddressResult.Transactions.ElementAt(0).SpendingDetails.TransactionId); - Assert.True(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments.Any(p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value)); + Assert.Contains(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments, p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value); Assert.Equal(blockHeight - 1, spentAddressResult.Transactions.ElementAt(0).BlockHeight); Assert.Single(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(1).Transactions); @@ -1798,7 +1798,7 @@ public void ProcessTransactionWithBlockSetsBlockHash() HdAddress spentAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0); Assert.Single(spendingAddress.Transactions); Assert.Equal(transaction.GetHash(), spentAddressResult.Transactions.ElementAt(0).SpendingDetails.TransactionId); - Assert.True(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments.Any(p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value)); + Assert.Contains(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments, p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value); Assert.Equal(chainInfo.block.GetHash(), spentAddressResult.Transactions.ElementAt(0).BlockHash); Assert.Single(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(1).Transactions); @@ -1816,12 +1816,14 @@ public void ProcessTransactionWithBlockSetsBlockHash() Assert.Equal(transaction.Outputs[0].ScriptPubKey, changeAddressResult.ScriptPubKey); } + /* /// /// TODO: [SENDTRANSACTION] Conceptual changes had been introduced to tx sending. /// /// These tests don't make sense anymore, it must be either removed or refactored. /// /// + */ //[Fact(Skip = "See TODO")] //public void SendTransactionWithoutMempoolValidatorProcessesTransactionAndBroadcastsTransactionToConnectionManagerNodes() //{ @@ -1929,12 +1931,14 @@ public void ProcessTransactionWithBlockSetsBlockHash() //} //} + /* /// /// TODO: [SENDTRANSACTION] Conceptual changes had been introduced to tx sending. /// /// These tests don't make sense anymore, it must be either removed or refactored. /// /// + */ //[Fact(Skip = "See TODO")] //public void SendTransactionWithMempoolValidatorWithAcceptToMemoryPoolSuccessProcessesTransaction() //{ @@ -2045,12 +2049,14 @@ public void ProcessTransactionWithBlockSetsBlockHash() //} //} + /* /// /// TODO: [SENDTRANSACTION] Conceptual changes had been introduced to tx sending. /// /// These tests don't make sense anymore, it must be either removed or refactored. /// /// + */ //[Fact(Skip = "See TODO")] //public void SendTransactionWithMempoolValidatorWithAcceptToMemoryPoolFailedDoesNotProcessesTransaction() //{ @@ -2147,8 +2153,7 @@ public void ProcessTransactionWithBlockSetsBlockHash() // Assert.Equal(0, payloads.Count); // } //} - //} - + //} private void AdvanceWalletTipToChainTip(IWalletRepository walletRepository, ChainIndexer concurrentchain) { var block0 = this.Network.CreateBlock(); @@ -2314,7 +2319,7 @@ public void ProcessBlockWithWalletsProcessesTransactionsOfBlockToWallet() HdAddress spentAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0); Assert.Single(spendingAddress.Transactions); Assert.Equal(transaction.GetHash(), spentAddressResult.Transactions.ElementAt(0).SpendingDetails.TransactionId); - Assert.True(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments.Any(p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value)); + Assert.Contains(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments, p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value); Assert.Single(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(1).Transactions); TransactionData destinationAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(1).Transactions.ElementAt(0); diff --git a/src/Stratis.Bitcoin.Features.Wallet/Controllers/WalletClient.cs b/src/Stratis.Bitcoin.Features.Wallet/Controllers/WalletClient.cs index 06aecbf1a6..d576473051 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/Controllers/WalletClient.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/Controllers/WalletClient.cs @@ -9,7 +9,7 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers /// Rest client for . public interface IWalletClient : IRestApiClientBase { - /// + /// Task SignMessageAsync(SignMessageRequest request, CancellationToken cancellation = default); } diff --git a/src/Stratis.Bitcoin.Features.Wallet/Controllers/WalletController.cs b/src/Stratis.Bitcoin.Features.Wallet/Controllers/WalletController.cs index 7589161c57..4a82b28ae1 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/Controllers/WalletController.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/Controllers/WalletController.cs @@ -53,7 +53,7 @@ public WalletController( [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public async Task GenerateMnemonic([FromQuery] string language = "English", int wordCount = 12, + public async Task GenerateMnemonicAsync([FromQuery] string language = "English", int wordCount = 12, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(new { Language = language, WordCount = wordCount }, @@ -78,10 +78,10 @@ public async Task GenerateMnemonic([FromQuery] string language = [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.Conflict)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task Create([FromBody] WalletCreationRequest request, + public async Task CreateAsync([FromBody] WalletCreationRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.CreateWallet(req, token))); } @@ -99,7 +99,7 @@ public async Task Create([FromBody] WalletCreationRequest request [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task SignMessage([FromBody] SignMessageRequest request, + public async Task SignMessageAsync([FromBody] SignMessageRequest request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -124,7 +124,7 @@ public async Task SignMessage([FromBody] SignMessageRequest reque [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task GetPubKey([FromBody] PubKeyRequest request, + public async Task GetPubKeyAsync([FromBody] PubKeyRequest request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -149,7 +149,7 @@ public async Task GetPubKey([FromBody] PubKeyRequest request, [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task VerifyMessage([FromBody] VerifyRequest request, + public async Task VerifyMessageAsync([FromBody] VerifyRequest request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -178,10 +178,10 @@ public async Task VerifyMessage([FromBody] VerifyRequest request, [ProducesResponseType((int)HttpStatusCode.Forbidden)] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task Load([FromBody] WalletLoadRequest request, + public async Task LoadAsync([FromBody] WalletLoadRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, async (req, token) => + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => { await this.walletService.LoadWallet(req, token); return Ok(); @@ -206,10 +206,10 @@ public async Task Load([FromBody] WalletLoadRequest request, [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Conflict)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task Recover([FromBody] WalletRecoveryRequest request, + public async Task RecoverAsync([FromBody] WalletRecoveryRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, async (req, token) => + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => { await this.walletService.RecoverWallet(req, token); return Ok(); @@ -235,10 +235,10 @@ public async Task Recover([FromBody] WalletRecoveryRequest reques [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Conflict)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task RecoverViaExtPubKey([FromBody] WalletExtPubRecoveryRequest request, + public async Task RecoverViaExtPubKeyAsync([FromBody] WalletExtPubRecoveryRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, async (req, token) => + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => { await this.walletService.RecoverViaExtPubKey(req, token); return Ok(); @@ -264,7 +264,7 @@ public async Task RecoverViaExtPubKey([FromBody] WalletExtPubReco public Task GetGeneralInfo([FromQuery] WalletName request, CancellationToken cancellationToken = default(CancellationToken)) { - return this.Execute(request, cancellationToken, async (req, token) => + return this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.GetWalletGeneralInfo(req.Name, token))); } @@ -276,7 +276,7 @@ public Task GetGeneralInfo([FromQuery] WalletName request, /// Transaction Count [Route("transactionCount")] [HttpGet] - public async Task GetTransactionCount([FromQuery] WalletTransactionCountRequest request, + public async Task GetTransactionCountAsync([FromQuery] WalletTransactionCountRequest request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, @@ -313,10 +313,10 @@ public IActionResult GetHistory([FromQuery] WalletHistoryRequest request) /// Request is null [Route("balance")] [HttpGet] - public async Task GetBalance([FromQuery] WalletBalanceRequest request, + public async Task GetBalanceAsync([FromQuery] WalletBalanceRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.GetBalance(req.WalletName, req.AccountName, req.IncludeBalanceByAddress, token)) ); @@ -336,10 +336,10 @@ public async Task GetBalance([FromQuery] WalletBalanceRequest req /// Request is null [Route("received-by-address")] [HttpGet] - public async Task GetReceivedByAddress([FromQuery] ReceivedByAddressRequest request, + public async Task GetReceivedByAddressAsync([FromQuery] ReceivedByAddressRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.GetReceivedByAddress(request.Address, cancellationToken))); } @@ -357,10 +357,10 @@ public async Task GetReceivedByAddress([FromQuery] ReceivedByAddr /// Request is null [Route("maxbalance")] [HttpGet] - public async Task GetMaximumSpendableBalance([FromQuery] WalletMaximumBalanceRequest request, + public async Task GetMaximumSpendableBalanceAsync([FromQuery] WalletMaximumBalanceRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.GetMaximumSpendableBalance(request, cancellationToken))); } @@ -381,10 +381,10 @@ public async Task GetMaximumSpendableBalance([FromQuery] WalletMa [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task GetSpendableTransactions([FromQuery] SpendableTransactionsRequest request, + public async Task GetSpendableTransactionsAsync([FromQuery] SpendableTransactionsRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => Json(await this.walletService.GetSpendableTransactions(req, token))); } @@ -405,10 +405,10 @@ public async Task GetSpendableTransactions([FromQuery] SpendableT [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task GetTransactionFeeEstimate([FromBody] TxFeeEstimateRequest request, + public async Task GetTransactionFeeEstimateAsync([FromBody] TxFeeEstimateRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => Json(await this.walletService.GetTransactionFeeEstimate(req, token))); } @@ -427,16 +427,18 @@ public async Task GetTransactionFeeEstimate([FromBody] TxFeeEstim [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task BuildTransaction([FromBody] BuildTransactionRequest request, + public async Task BuildTransactionAsync([FromBody] BuildTransactionRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => Json(await this.walletService.BuildTransaction(req, token))); } /// - /// Same as but overrides OP_RETURN data and encodes destination chain and address for InterFlux transaction. + /// Same as but overrides OP_RETURN data and encodes destination chain and address for InterFlux transaction. /// + /// See . + /// The asynchronous task returning an . [Route("build-interflux-transaction")] [HttpPost] [ProducesResponseType((int)HttpStatusCode.OK)] @@ -456,7 +458,7 @@ public async Task BuildInterFluxTransactionAsync([FromBody] Build request.OpReturnData = InterFluxOpReturnEncoder.Encode((DestinationChain)request.DestinationChain, request.DestinationAddress); - return await this.Execute(request, default, async (req, token) => Json(await this.walletService.BuildTransaction(req, token))); + return await this.ExecuteAsync(request, default, async (req, token) => Json(await this.walletService.BuildTransaction(req, token))); } /// @@ -476,10 +478,10 @@ public async Task BuildInterFluxTransactionAsync([FromBody] Build [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task SendTransaction([FromBody] SendTransactionRequest request, + public async Task SendTransactionAsync([FromBody] SendTransactionRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => Json(await this.walletService.SendTransaction(req, token))); } @@ -491,7 +493,7 @@ public async Task SendTransaction([FromBody] SendTransactionReque /// The Cancellation Token [Route("list-wallets")] [HttpGet] - public async Task ListWallets(CancellationToken cancellationToken = default(CancellationToken)) + public async Task ListWalletsAsync(CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync((object)null, cancellationToken, (req, token) => this.Json(new WalletInfoModel(this.walletManager.GetWalletsNames(), this.walletManager.GetWatchOnlyWalletsNames())), false); @@ -522,7 +524,7 @@ public async Task SendTransaction([FromBody] SendTransactionReque [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task CreateNewAccount([FromBody] GetUnusedAccountModel request, + public async Task CreateNewAccountAsync([FromBody] GetUnusedAccountModel request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -554,7 +556,7 @@ public async Task CreateNewAccount([FromBody] GetUnusedAccountMod [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task ListAccounts([FromQuery] ListAccountsModel request, + public async Task ListAccountsAsync([FromQuery] ListAccountsModel request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -580,7 +582,7 @@ public async Task ListAccounts([FromQuery] ListAccountsModel requ [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task GetUnusedAddress([FromQuery] GetUnusedAddressModel request, + public async Task GetUnusedAddressAsync([FromQuery] GetUnusedAddressModel request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -609,7 +611,7 @@ public async Task GetUnusedAddress([FromQuery] GetUnusedAddressMo [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task GetUnusedAddresses([FromQuery] GetUnusedAddressesModel request, + public async Task GetUnusedAddressesAsync([FromQuery] GetUnusedAddressesModel request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -640,7 +642,7 @@ public async Task GetUnusedAddresses([FromQuery] GetUnusedAddress [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task GetNewAddresses([FromQuery] GetNewAddressesModel request, + public async Task GetNewAddressesAsync([FromQuery] GetNewAddressesModel request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -665,10 +667,10 @@ public async Task GetNewAddresses([FromQuery] GetNewAddressesMode /// Request is null [Route("addresses")] [HttpGet] - public async Task GetAllAddresses([FromQuery] GetAllAddressesModel request, + public async Task GetAllAddressesAsync([FromQuery] GetAllAddressesModel request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.GetAllAddresses(req, token))); } @@ -694,19 +696,19 @@ public async Task GetAllAddresses([FromQuery] GetAllAddressesMode /// Request is null [Route("remove-transactions")] [HttpDelete] - public async Task RemoveTransactions([FromQuery] RemoveTransactionsModel request, + public async Task RemoveTransactionsAsync([FromQuery] RemoveTransactionsModel request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.RemoveTransactions(req, token))); } [Route("remove-wallet")] [HttpDelete] - public async Task RemoveWallet([FromQuery] RemoveWalletModel request, + public async Task RemoveWalletAsync([FromQuery] RemoveWalletModel request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => { await this.walletService.RemoveWallet(req, token); @@ -726,7 +728,7 @@ public async Task RemoveWallet([FromQuery] RemoveWalletModel requ /// Request is null [Route("extpubkey")] [HttpGet] - public async Task GetExtPubKey([FromQuery] GetExtPubKeyModel request, + public async Task GetExtPubKeyAsync([FromQuery] GetExtPubKeyModel request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, @@ -746,7 +748,7 @@ public async Task GetExtPubKey([FromQuery] GetExtPubKeyModel requ /// Request is null [Route("privatekey")] [HttpPost] - public async Task RetrievePrivateKey([FromBody] RetrievePrivateKeyModel request, + public async Task RetrievePrivateKeyAsync([FromBody] RetrievePrivateKeyModel request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, @@ -766,7 +768,7 @@ public async Task RetrievePrivateKey([FromBody] RetrievePrivateKe [Route("sync")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public async Task Sync([FromBody] HashModel model, + public async Task SyncAsync([FromBody] HashModel model, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(model, cancellationToken, (req, token) => @@ -796,7 +798,7 @@ public async Task Sync([FromBody] HashModel model, /// Invalid request [HttpPost] [Route("sync-from-date")] - public async Task SyncFromDate([FromBody] WalletSyncRequest request, + public async Task SyncFromDateAsync([FromBody] WalletSyncRequest request, CancellationToken cancellationToken = default(CancellationToken)) { return await this.ExecuteAsAsync(request, cancellationToken, (req, token) => @@ -818,16 +820,17 @@ public async Task SyncFromDate([FromBody] WalletSyncRequest reque /// Retrieves information about the wallet /// /// Parameters to request wallet stats + /// See . /// Stats about the wallet /// Returns wallet stats /// Invalid request, or unexpected exception occurred /// Request is null [Route("wallet-stats")] [HttpGet] - public async Task WalletStats([FromQuery] WalletStatsRequest request, + public async Task WalletStatsAsync([FromQuery] WalletStatsRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.GetWalletStats(req, token))); } @@ -841,10 +844,10 @@ public async Task WalletStats([FromQuery] WalletStatsRequest requ /// Request is null [HttpPost] [Route("splitcoins")] - public async Task SplitCoins([FromBody] SplitCoinsRequest request, + public async Task SplitCoinsAsync([FromBody] SplitCoinsRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.SplitCoins(req, token))); } @@ -854,46 +857,46 @@ public async Task SplitCoins([FromBody] SplitCoinsRequest request /// The Cancellation Token [HttpPost] [Route("distribute-utxos")] - public async Task DistributeUtxos([FromBody] DistributeUtxosRequest request, + public async Task DistributeUtxosAsync([FromBody] DistributeUtxosRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.DistributeUtxos(req, token))); } [HttpPost] [Route("sweep")] - public async Task Sweep([FromBody] SweepRequest request, + public async Task SweepAsync([FromBody] SweepRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.Sweep(req, token))); } [Route("build-offline-sign-request")] [HttpPost] - public async Task BuildOfflineSignRequest([FromBody] BuildOfflineSignRequest request, + public async Task BuildOfflineSignRequestAsync([FromBody] BuildOfflineSignRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.BuildOfflineSignRequest(req, token))); } // TODO: Make this support PSBT directly? [Route("offline-sign-request")] [HttpPost] - public async Task OfflineSignRequest([FromBody] OfflineSignRequest request, + public async Task OfflineSignRequestAsync([FromBody] OfflineSignRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, async (req, token) => this.Json(await this.walletService.OfflineSignRequest(req, token))); + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.OfflineSignRequest(req, token))); } [HttpPost] [Route("consolidate")] - public async Task Consolidate([FromBody] ConsolidationRequest request, + public async Task ConsolidateAsync([FromBody] ConsolidationRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.Execute(request, cancellationToken, + return await this.ExecuteAsync(request, cancellationToken, async (req, token) => this.Json(await this.walletService.Consolidate(req, token))); } } diff --git a/src/Stratis.Bitcoin.Features.Wallet/Interfaces/IWalletManager.cs b/src/Stratis.Bitcoin.Features.Wallet/Interfaces/IWalletManager.cs index 6a80e3352f..7013768cca 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/Interfaces/IWalletManager.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/Interfaces/IWalletManager.cs @@ -273,6 +273,14 @@ public interface IWalletManager /// The list of accounts in the specified wallet. IEnumerable GetAccounts(string walletName); + /// + /// Gets a list of accounts. + /// + /// The name of the wallet to look into. + /// Optional filter for the accounts to return. Defaults to returning normal accounts only. + /// The list of accounts in the specified wallet. + IEnumerable GetAccounts(string walletName, Func accountFilter); + /// /// Gets a list of all the accounts in all wallets. /// diff --git a/src/Stratis.Bitcoin.Features.Wallet/Interfaces/IWalletTransactionHandler.cs b/src/Stratis.Bitcoin.Features.Wallet/Interfaces/IWalletTransactionHandler.cs index 2abd265982..e66fb9fde3 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/Interfaces/IWalletTransactionHandler.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/Interfaces/IWalletTransactionHandler.cs @@ -2,6 +2,9 @@ namespace Stratis.Bitcoin.Features.Wallet.Interfaces { + /// + /// A handler that has various functionalities related to transaction operations. + /// public interface IWalletTransactionHandler { /// diff --git a/src/Stratis.Bitcoin.Features.Wallet/Models/UnspentCoinModel.cs b/src/Stratis.Bitcoin.Features.Wallet/Models/UnspentCoinModel.cs index 61e6f480c9..7da4bd8864 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/Models/UnspentCoinModel.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/Models/UnspentCoinModel.cs @@ -4,6 +4,7 @@ namespace Stratis.Bitcoin.Features.Wallet.Models { + // TODO: This is similar to the UnspentCoin class in the RPC feature; perhaps one of the two should be removed? /// /// Model for Json response for listunspent RPC call. /// @@ -46,6 +47,12 @@ public class UnspentCoinModel [JsonProperty(PropertyName = "redeemScript")] public string RedeemScriptHex { get; set; } + /// + /// If the output is a P2WSH or P2SH-P2WSH whose script belongs to this wallet, this is the redeem script. + /// + [JsonProperty(PropertyName = "witnessScript")] + public string WitnessScriptHex { get; set; } + /// /// The transaction amount. /// Serialized in coins (BTC). diff --git a/src/Stratis.Bitcoin.Features.Wallet/Services/WalletService.cs b/src/Stratis.Bitcoin.Features.Wallet/Services/WalletService.cs index 43a1754955..895365503b 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/Services/WalletService.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/Services/WalletService.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NBitcoin; -using NBitcoin.DataEncoders; using NBitcoin.Policy; using Stratis.Bitcoin.Builder.Feature; using Stratis.Bitcoin.Configuration; diff --git a/src/Stratis.Bitcoin.Features.Wallet/Stratis.Bitcoin.Features.Wallet.csproj b/src/Stratis.Bitcoin.Features.Wallet/Stratis.Bitcoin.Features.Wallet.csproj index d826867cc8..d67e9be299 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/Stratis.Bitcoin.Features.Wallet.csproj +++ b/src/Stratis.Bitcoin.Features.Wallet/Stratis.Bitcoin.Features.Wallet.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Features.Wallet/WalletFeature.cs b/src/Stratis.Bitcoin.Features.Wallet/WalletFeature.cs index 068264bd80..48dda593df 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/WalletFeature.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/WalletFeature.cs @@ -54,6 +54,8 @@ public class WalletFeature : BaseWalletFeature /// The address book manager. /// The connection manager. /// The broadcaster behavior. + /// See . + /// See . public WalletFeature( IWalletSyncManager walletSyncManager, IWalletManager walletManager, diff --git a/src/Stratis.Bitcoin.Features.Wallet/WalletManager.cs b/src/Stratis.Bitcoin.Features.Wallet/WalletManager.cs index 1bbf0362fa..870fae42a1 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/WalletManager.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/WalletManager.cs @@ -955,6 +955,12 @@ public AddressBalance GetAddressBalance(string address) /// public IEnumerable GetAccounts(string walletName) + { + return GetAccounts(walletName, null); + } + + /// + public IEnumerable GetAccounts(string walletName, Func accountFilter) { Guard.NotEmpty(walletName, nameof(walletName)); @@ -963,7 +969,7 @@ public IEnumerable GetAccounts(string walletName) HdAccount[] res = null; lock (this.lockObject) { - res = wallet.GetAccounts().ToArray(); + res = wallet.GetAccounts(accountFilter).ToArray(); } return res; @@ -1017,6 +1023,7 @@ public void AddWatchOnlyAddress(string walletName, string accountName, PubKey[] this.WalletRepository.AddWatchOnlyAddresses(walletName, accountName, 0, pubKeys.Select(pubKey => new HdAddress() { Pubkey = pubKey.ScriptPubKey }).ToList()); } + // TODO: Not sure if the intention was to return special accounts too. If not, this method doesn't have much purpose and is essentially a duplicate of GetAccounts(), albeit across all wallets public IEnumerable GetAllAccounts() { HdAccount[] res = null; diff --git a/src/Stratis.Bitcoin.Features.Wallet/WalletRPCController.cs b/src/Stratis.Bitcoin.Features.Wallet/WalletRPCController.cs index cee13e9a05..1572567a54 100644 --- a/src/Stratis.Bitcoin.Features.Wallet/WalletRPCController.cs +++ b/src/Stratis.Bitcoin.Features.Wallet/WalletRPCController.cs @@ -126,8 +126,6 @@ public bool LockWallet() [ActionDescription("Sends money to an address. Requires wallet to be unlocked using walletpassphrase.")] public async Task SendToAddressAsync(BitcoinAddress address, decimal amount, string commentTx, string commentDest) { - WalletAccountReference account = this.GetWalletAccountReference(); - TransactionBuildContext context = new TransactionBuildContext(this.FullNode.Network) { AccountReference = this.GetWalletAccountReference(), @@ -154,7 +152,7 @@ public async Task SendToAddressAsync(BitcoinAddress address, decimal am [ActionName("fundrawtransaction")] [ActionDescription("Add inputs to a transaction until it has enough in value to meet its out value. Note that signing is performed separately.")] - public async Task FundRawTransactionAsync(string rawHex, FundRawTransactionOptions options = null, bool? isWitness = null) + public Task FundRawTransactionAsync(string rawHex, FundRawTransactionOptions options = null, bool? isWitness = null) { try { @@ -300,12 +298,12 @@ public async Task FundRawTransactionAsync(string raw } } - return new FundRawTransactionResponse() + return Task.FromResult(new FundRawTransactionResponse() { ChangePos = foundChange, Fee = context.TransactionFee, Transaction = rawTx - }; + }); } catch (SecurityException) { @@ -325,7 +323,7 @@ public async Task FundRawTransactionAsync(string raw /// The hex format of the transaction once it has been signed. [ActionName("signrawtransaction")] [ActionDescription("Sign inputs for raw transaction. Requires all affected wallets to be unlocked using walletpassphrase.")] - public async Task SignRawTransactionAsync(string rawHex) + public Task SignRawTransactionAsync(string rawHex) { try { @@ -372,11 +370,11 @@ public async Task SignRawTransactionAsync(string raw builder.AddKeys(signingKeys.ToArray()); builder.SignTransactionInPlace(rawTx); - return new SignRawTransactionResponse() + return Task.FromResult(new SignRawTransactionResponse() { Transaction = rawTx, Complete = true - }; + }); } catch (SecurityException) { @@ -461,10 +459,11 @@ public decimal GetBalance(string accountName, int minConfirmations = 0) /// Uses the default wallet if specified, or the first wallet found. /// /// Identifier of the transaction to find. + /// Set to true to search the watch-only account. /// Transaction information. [ActionName("gettransaction")] [ActionDescription("Get detailed information about an in-wallet transaction.")] - public async Task GetTransaction(string txid, bool include_watchonly = false) + public Task GetTransaction(string txid, bool include_watchonly = false) { if (!uint256.TryParse(txid, out uint256 trxid)) throw new ArgumentException(nameof(txid)); @@ -473,7 +472,7 @@ public async Task GetTransaction(string txid, bool include_watchonly = f { WalletHistoryModel history = GetWatchOnlyTransaction(trxid); if ((history?.AccountsHistoryModel?.FirstOrDefault()?.TransactionsHistory?.Count ?? 0) != 0) - return history; + return Task.FromResult(history); } // First check the regular wallet accounts. @@ -637,13 +636,15 @@ bool IsChangeAddress(Script scriptPubKey) model.Amount = model.Details.Sum(d => d.Amount); model.Fee = model.Details.FirstOrDefault(d => d.Category == GetTransactionDetailsCategoryModel.Send)?.Fee; - return model; + return Task.FromResult(model); } /// /// We get the details via the wallet service's history method. /// + /// The hash of the transaction to get. + /// See . private WalletHistoryModel GetWatchOnlyTransaction(uint256 trxid) { var accountReference = this.GetWatchOnlyWalletAccountReference(); @@ -760,30 +761,41 @@ public UnspentCoinModel[] ListUnspent(int minConfirmations = 1, int maxConfirmat JsonConvert.DeserializeObject>(addressesJson).ForEach(i => addresses.Add(BitcoinAddress.Create(i, this.FullNode.Network))); } - WalletAccountReference accountReference = this.GetWalletAccountReference(); - IEnumerable spendableTransactions = this.walletManager.GetSpendableTransactionsInAccount(accountReference, minConfirmations); - + string walletName = this.GetWallet(); + var accounts = this.walletManager.GetAccounts(walletName, Wallet.AllAccounts); var unspentCoins = new List(); - foreach (var spendableTx in spendableTransactions) + + foreach (var account in accounts) { - if (spendableTx.Confirmations <= maxConfirmations) + // The intention here is to filter out cold staking accounts. The watch only account can be included. + if (!account.IsNormalAccount() && account.Name != Wallet.WatchOnlyAccountName) + continue; + + WalletAccountReference accountReference = new WalletAccountReference(walletName, account.Name); + + IEnumerable spendableTransactions = this.walletManager.GetSpendableTransactionsInAccount(accountReference, minConfirmations); + + foreach (var spendableTx in spendableTransactions) { - if (!addresses.Any() || addresses.Contains(BitcoinAddress.Create(spendableTx.Address.Address, this.FullNode.Network))) + if (spendableTx.Confirmations > maxConfirmations) + continue; + + if (addresses.Any() && !addresses.Contains(BitcoinAddress.Create(spendableTx.Address.Address, this.FullNode.Network))) + continue; + + unspentCoins.Add(new UnspentCoinModel() { - unspentCoins.Add(new UnspentCoinModel() - { - Account = accountReference.AccountName, - Address = spendableTx.Address.Address, - Id = spendableTx.Transaction.Id, - Index = spendableTx.Transaction.Index, - Amount = spendableTx.Transaction.Amount, - ScriptPubKeyHex = spendableTx.Transaction.ScriptPubKey.ToHex(), - RedeemScriptHex = null, // TODO: Currently don't support P2SH wallet addresses, review if we do. - Confirmations = spendableTx.Confirmations, - IsSpendable = !spendableTx.Transaction.IsSpent(), - IsSolvable = !spendableTx.Transaction.IsSpent() // If it's spendable we assume it's solvable. - }); - } + Account = accountReference.AccountName, + Address = spendableTx.Address.Address, + Id = spendableTx.Transaction.Id, + Index = spendableTx.Transaction.Index, + Amount = spendableTx.Transaction.Amount, + ScriptPubKeyHex = spendableTx.Transaction.ScriptPubKey.ToHex(), + RedeemScriptHex = null, // TODO: Currently don't support P2SH wallet addresses, review if we do. + Confirmations = spendableTx.Confirmations, + IsSpendable = (!spendableTx.Transaction.IsSpent()) && (spendableTx.Account.Name != Wallet.WatchOnlyAccountName), + IsSolvable = (!spendableTx.Transaction.IsSpent()) && (spendableTx.Account.Name != Wallet.WatchOnlyAccountName) // If it's spendable we assume it's solvable. + }); } } @@ -931,11 +943,10 @@ private int GetConfirmationCount(TransactionData transaction) } /// - /// Gets the first account from the "default" wallet if it is specified, - /// otherwise returns the first available account in the existing wallets. + /// Gets the name of the wallet currently associated with RPC. This can be changed via the 'setwallet' RPC command. /// - /// Reference to the default wallet account, or the first available if no default wallet is specified. - private WalletAccountReference GetWalletAccountReference() + /// The active wallet name. + private string GetWallet() { string walletName = null; @@ -958,6 +969,18 @@ private WalletAccountReference GetWalletAccountReference() if (walletName == null) throw new RPCServerException(RPCErrorCode.RPC_INVALID_REQUEST, "No wallet found"); + return walletName; + } + + /// + /// Gets the first account from the "default" wallet if it is specified, + /// otherwise returns the first available account in the existing wallets. + /// + /// Reference to the default wallet account, or the first available if no default wallet is specified. + private WalletAccountReference GetWalletAccountReference() + { + string walletName = GetWallet(); + HdAccount account = this.walletManager.GetAccounts(walletName).FirstOrDefault(); if (account == null) @@ -973,23 +996,7 @@ private WalletAccountReference GetWalletAccountReference() /// Reference to the default wallet watch only account, or the first available if no default wallet is specified. private WalletAccountReference GetWatchOnlyWalletAccountReference() { - string walletName = null; - - if (string.IsNullOrWhiteSpace(WalletRPCController.CurrentWalletName)) - { - if (this.walletSettings.IsDefaultWalletEnabled()) - walletName = this.walletManager.GetWalletsNames().FirstOrDefault(w => w == this.walletSettings.DefaultWalletName); - else - { - // TODO: Support multi wallet like core by mapping passed RPC credentials to a wallet/account - walletName = this.walletManager.GetWalletsNames().FirstOrDefault(); - } - } - else - { - // Read the wallet name from the class instance. - walletName = WalletRPCController.CurrentWalletName; - } + string walletName = GetWallet(); if (walletName == null) throw new RPCServerException(RPCErrorCode.RPC_INVALID_REQUEST, "No wallet found"); diff --git a/src/Stratis.Bitcoin.Features.WatchOnlyWallet/Stratis.Bitcoin.Features.WatchOnlyWallet.csproj b/src/Stratis.Bitcoin.Features.WatchOnlyWallet/Stratis.Bitcoin.Features.WatchOnlyWallet.csproj index 639e2839f6..751c7ce4ed 100644 --- a/src/Stratis.Bitcoin.Features.WatchOnlyWallet/Stratis.Bitcoin.Features.WatchOnlyWallet.csproj +++ b/src/Stratis.Bitcoin.Features.WatchOnlyWallet/Stratis.Bitcoin.Features.WatchOnlyWallet.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.IntegrationTests.Common/EnvironmentMockUpHelpers/CoreNode.cs b/src/Stratis.Bitcoin.IntegrationTests.Common/EnvironmentMockUpHelpers/CoreNode.cs index ca1ae1195a..bc49d5f25b 100644 --- a/src/Stratis.Bitcoin.IntegrationTests.Common/EnvironmentMockUpHelpers/CoreNode.cs +++ b/src/Stratis.Bitcoin.IntegrationTests.Common/EnvironmentMockUpHelpers/CoreNode.cs @@ -36,6 +36,9 @@ namespace Stratis.Bitcoin.IntegrationTests.Common.EnvironmentMockUpHelpers { + /// + /// Full node wrapper for testing purposes. + /// public class CoreNode { private readonly NetworkCredential creds; @@ -483,8 +486,8 @@ public void Broadcast(Transaction transaction) /// /// Emit a ping and wait the pong. /// - /// - /// + /// . + /// See . /// Latency. public async Task PingPongAsync(INetworkPeer peer, CancellationToken cancellation = default(CancellationToken)) { @@ -595,7 +598,7 @@ public async Task GenerateAsync(int blockCount, bool includeUnbroadcast /// /// Peer to get chain from. /// The highest block wanted. - /// + /// See . /// The chain of headers. private ChainIndexer GetChain(INetworkPeer peer, uint256 hashStop = null, CancellationToken cancellationToken = default(CancellationToken)) { @@ -610,8 +613,8 @@ public async Task GenerateAsync(int blockCount, bool includeUnbroadcast /// Node to synchronize the chain for. /// The chain to synchronize. /// The location until which it synchronize. - /// - /// + /// See . + /// An enumeration of objects added to the fork point. private IEnumerable SynchronizeChain(INetworkPeer peer, ChainIndexer chain, uint256 hashStop = null, CancellationToken cancellationToken = default(CancellationToken)) { ChainedHeader oldTip = chain.Tip; diff --git a/src/Stratis.Bitcoin.IntegrationTests.Common/Stratis.Bitcoin.IntegrationTests.Common.csproj b/src/Stratis.Bitcoin.IntegrationTests.Common/Stratis.Bitcoin.IntegrationTests.Common.csproj index c2e3ef116f..d42ca8c63a 100644 --- a/src/Stratis.Bitcoin.IntegrationTests.Common/Stratis.Bitcoin.IntegrationTests.Common.csproj +++ b/src/Stratis.Bitcoin.IntegrationTests.Common/Stratis.Bitcoin.IntegrationTests.Common.csproj @@ -13,7 +13,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False diff --git a/src/Stratis.Bitcoin.IntegrationTests.Common/TestHelper.cs b/src/Stratis.Bitcoin.IntegrationTests.Common/TestHelper.cs index 2a8a5bfe2a..09fe7fc22f 100644 --- a/src/Stratis.Bitcoin.IntegrationTests.Common/TestHelper.cs +++ b/src/Stratis.Bitcoin.IntegrationTests.Common/TestHelper.cs @@ -525,7 +525,7 @@ public static void SendCoins(CoreNode miner, CoreNode sender, CoreNode[] receive var transaction = sender.FullNode.WalletTransactionHandler().BuildTransaction(context); - sender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(transaction.ToHex())).GetAwaiter().GetResult(); + sender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(transaction.ToHex())).GetAwaiter().GetResult(); MineBlocks(miner, 1); diff --git a/src/Stratis.Bitcoin.IntegrationTests/API/ApiSteps.cs b/src/Stratis.Bitcoin.IntegrationTests/API/ApiSteps.cs index 04943f0f28..e68e672e0b 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/API/ApiSteps.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/API/ApiSteps.cs @@ -533,14 +533,14 @@ private async Task SendTransaction(IActionResult transactionResult) var walletTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; this.transaction = this.firstStratisPowApiNode.FullNode.Network.CreateTransaction(walletTransactionModel.Hex); await this.firstStratisPowApiNode.FullNode.NodeController() - .SendTransaction(new SendTransactionRequest(walletTransactionModel.Hex)); + .SendTransactionAsync(new SendTransactionRequest(walletTransactionModel.Hex)); } private async Task BuildTransaction() { IActionResult transactionResult = await this.firstStratisPowApiNode.FullNode .NodeController() - .BuildTransaction(new BuildTransactionRequest + .BuildTransactionAsync(new BuildTransactionRequest { AccountName = WalletAccountName, AllowUnconfirmed = true, diff --git a/src/Stratis.Bitcoin.IntegrationTests/BlockStore/BlockStoreSignaledTests.cs b/src/Stratis.Bitcoin.IntegrationTests/BlockStore/BlockStoreSignaledTests.cs index 278b3c83d2..dc243643fa 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/BlockStore/BlockStoreSignaledTests.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/BlockStore/BlockStoreSignaledTests.cs @@ -48,12 +48,14 @@ private async Task OnMessageReceivedAsync(INetworkPeer peer, IncomingMessage mes } } - private async Task ProcessMessageAsync(INetworkPeer peer, IncomingMessage message) + private Task ProcessMessageAsync(INetworkPeer peer, IncomingMessage message) { if (!this.receivedMessageTracker.ContainsKey(message.Message.Payload.Command)) this.receivedMessageTracker[message.Message.Payload.Command] = new List(); this.receivedMessageTracker[message.Message.Payload.Command].Add(message); + + return Task.CompletedTask; } public override object Clone() diff --git a/src/Stratis.Bitcoin.IntegrationTests/BlockStore/ProofOfWorkSpendingSteps.cs b/src/Stratis.Bitcoin.IntegrationTests/BlockStore/ProofOfWorkSpendingSteps.cs index beb87a1096..c23a47e913 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/BlockStore/ProofOfWorkSpendingSteps.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/BlockStore/ProofOfWorkSpendingSteps.cs @@ -91,7 +91,7 @@ private async Task spending_the_coins_from_original_block() .BuildTransaction(transactionBuildContext); await this.sendingStratisBitcoinNode.FullNode.NodeController() - .SendTransaction(new SendTransactionRequest(this.lastTransaction.ToHex())); + .SendTransactionAsync(new SendTransactionRequest(this.lastTransaction.ToHex())); } catch (Exception exception) { diff --git a/src/Stratis.Bitcoin.IntegrationTests/BlockStore/ReorgToLongestChainSteps.cs b/src/Stratis.Bitcoin.IntegrationTests/BlockStore/ReorgToLongestChainSteps.cs index 57e4048aec..4f46848d1a 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/BlockStore/ReorgToLongestChainSteps.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/BlockStore/ReorgToLongestChainSteps.cs @@ -100,7 +100,7 @@ private void bob_creates_a_transaction_and_broadcasts() this.shorterChainTransaction = this.bobNode.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); Money shortChainTransactionFee = this.bobNode.FullNode.WalletTransactionHandler().EstimateFee(transactionBuildContext); - this.bobNode.FullNode.NodeController().SendTransaction(new SendTransactionRequest(this.shorterChainTransaction.ToHex())); + this.bobNode.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(this.shorterChainTransaction.ToHex())); } private HdAddress GetSecondUnusedAddressToAvoidClashWithMiningAddress(CoreNode node) diff --git a/src/Stratis.Bitcoin.IntegrationTests/BlockStore/RetrieveFromBlockStoreSteps.cs b/src/Stratis.Bitcoin.IntegrationTests/BlockStore/RetrieveFromBlockStoreSteps.cs index 68d9646b73..31a021a8d8 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/BlockStore/RetrieveFromBlockStoreSteps.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/BlockStore/RetrieveFromBlockStoreSteps.cs @@ -114,7 +114,7 @@ private async Task a_real_transaction() this.transaction = this.node.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); await this.node.FullNode.NodeController() - .SendTransaction(new SendTransactionRequest(this.transaction.ToHex())); + .SendTransactionAsync(new SendTransactionRequest(this.transaction.ToHex())); } private void the_block_with_the_transaction_is_mined() diff --git a/src/Stratis.Bitcoin.IntegrationTests/MinerTests.cs b/src/Stratis.Bitcoin.IntegrationTests/MinerTests.cs index cde8ac0094..1fae271277 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/MinerTests.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/MinerTests.cs @@ -181,7 +181,7 @@ public async Task InitializeAsync() foreach (var ruleType in this.network.Consensus.ConsensusRules.HeaderValidationRules) consensusRulesContainer.HeaderValidationRules.Add(Activator.CreateInstance(ruleType) as HeaderValidationConsensusRule); - foreach (var ruleType in network.Consensus.ConsensusRules.FullValidationRules) + foreach (var ruleType in this.network.Consensus.ConsensusRules.FullValidationRules) { FullValidationConsensusRule rule = null; if (ruleType == typeof(FlushCoinviewRule)) diff --git a/src/Stratis.Bitcoin.IntegrationTests/Miners/ProofOfStakeMintCoinsTest.cs b/src/Stratis.Bitcoin.IntegrationTests/Miners/ProofOfStakeMintCoinsTest.cs index 0d29b70dab..31a782901d 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/Miners/ProofOfStakeMintCoinsTest.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/Miners/ProofOfStakeMintCoinsTest.cs @@ -124,6 +124,7 @@ public void Staking_Wallet_Can_Mint_New_Coins() /// /// Returns a snapshot of the current transactions by coin type in the first wallet. /// + /// See . /// A list of TransactionData. private List GetTransactionsSnapshot(CoreNode node) { diff --git a/src/Stratis.Bitcoin.IntegrationTests/RPC/ListAddressGroupingsTest.cs b/src/Stratis.Bitcoin.IntegrationTests/RPC/ListAddressGroupingsTest.cs index 0d03030a8e..52a9a4bf3b 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/RPC/ListAddressGroupingsTest.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/RPC/ListAddressGroupingsTest.cs @@ -135,7 +135,7 @@ private void SendCoins(CoreNode from, CoreNode to, Money coins, HdAddress toAddr // Send 10 coins to node. var transaction = from.FullNode.WalletTransactionHandler().BuildTransaction(WalletTests.CreateContext(from.FullNode.Network, new WalletAccountReference(walletName, accountName), password, toAddress.ScriptPubKey, coins, FeeType.Medium, 10)); - from.FullNode.NodeController().SendTransaction(new SendTransactionRequest(transaction.ToHex())); + from.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(transaction.ToHex())); TestBase.WaitLoop(() => from.CreateRPCClient().GetRawMempool().Length > 0); diff --git a/src/Stratis.Bitcoin.IntegrationTests/RPC/RPCTestsMutable.cs b/src/Stratis.Bitcoin.IntegrationTests/RPC/RPCTestsMutable.cs index 46ece73aa7..a5c0c65f5e 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/RPC/RPCTestsMutable.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/RPC/RPCTestsMutable.cs @@ -72,6 +72,64 @@ public void TestRpcImportPubkeyIsSuccessful() TestHelper.ConnectAndSync(node, node2); + UnspentOutputReference tx = node2.FullNode.WalletManager().GetUnspentTransactionsInWallet("mywallet", 0, Features.Wallet.Wallet.NormalAccounts).First(); + + RPCClient rpc = node.CreateRPCClient(); + + PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(tx.Address.Pubkey); + PubKey pubKey2 = new Key().PubKey; + + uint256 blockHash = rpc.GenerateToAddress(1, pubKey2.GetAddress(rpc.Network)).First(); + Block block = rpc.GetBlock(blockHash); + uint256 tx2 = block.Transactions.First().GetHash(); + + Assert.Throws(() => rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), true)); + Assert.Throws(() => rpc.SendCommand(RPCOperations.gettransaction, tx2.ToString(), true));; + + // Test that adding the same pubkey twice doesn't throw. + rpc.ImportPubKey(pubKey.ToHex()); + rpc.ImportPubKey(pubKey.ToHex()); + + // Add a second pubkey and ensure it doesn't throw. + rpc.ImportPubKey(pubKey2.ToHex()); + + // Add an arbitrary pubkey and ensure it doesn't throw. + rpc.ImportPubKey(new Key().PubKey.ToHex()); + + TestBase.WaitLoop(() => node.FullNode.WalletManager().WalletTipHeight == node2.FullNode.WalletManager().WalletTipHeight); + + TestBase.WaitLoop(() => + { + try + { + // Check if gettransaction can now find the transactions in the watch only account. + RPCResponse walletTx = rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), true); + RPCResponse walletTx2 = rpc.SendCommand(RPCOperations.gettransaction, tx2.ToString(), true); + + return walletTx != null && walletTx2 != null; + } + catch (RPCException) + { + return false; + } + }); + + // Check that when include_watchonly is not set, the watched addresses' transactions cannot be located in the normal wallet accounts. + Assert.Throws(() => rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), false)); + Assert.Throws(() => rpc.SendCommand(RPCOperations.gettransaction, tx2.ToString(), false)); + } + } + + [Fact] + public void TestRpcListUnspentForWatchOnlyIsSuccessful() + { + using (NodeBuilder builder = NodeBuilder.Create(this)) + { + CoreNode node = builder.CreateStratisPowNode(new BitcoinRegTest()).AlwaysFlushBlocks().WithWallet().Start(); + CoreNode node2 = builder.CreateStratisPowNode(new BitcoinRegTest()).WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner).Start(); + + TestHelper.ConnectAndSync(node, node2); + UnspentOutputReference tx = node2.FullNode.WalletManager().GetUnspentTransactionsInWallet("mywallet", 0, Features.Wallet.Wallet.NormalAccounts).First(); RPCClient rpc = node.CreateRPCClient(); @@ -82,25 +140,39 @@ public void TestRpcImportPubkeyIsSuccessful() rpc.ImportPubKey(pubKey.ToHex()); + // ListUnspent will not regard the outputs as spendable if they are not sufficiently mature. + rpc.Generate((int)node.FullNode.Network.Consensus.CoinbaseMaturity); + TestBase.WaitLoop(() => node.FullNode.WalletManager().WalletTipHeight == node2.FullNode.WalletManager().WalletTipHeight); TestBase.WaitLoop(() => { try { - // Check if gettransaction can now find the transaction in the watch only account. + // Wait until gettransaction can find the transaction in the watch only account. RPCResponse walletTx = rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), true); return walletTx != null; } - catch (RPCException e) + catch (RPCException) { return false; } }); - // Check that when include_watchonly is not set, the watched transaction cannot be located in the normal wallet accounts. - Assert.Throws(() => rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), false)); + UnspentCoin[] unspent = rpc.ListUnspent(1, 9999999); + + bool found = false; + + foreach (UnspentCoin coin in unspent) + { + if (coin.OutPoint == tx.ToOutPoint()) + found = true; + + Assert.Equal(coin.Account, Features.Wallet.Wallet.WatchOnlyAccountName); + } + + Assert.True(found); } } diff --git a/src/Stratis.Bitcoin.IntegrationTests/SegWitTests.cs b/src/Stratis.Bitcoin.IntegrationTests/SegWitTests.cs index 3aa8a83476..6c1b25465c 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/SegWitTests.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/SegWitTests.cs @@ -44,6 +44,7 @@ public StraxOverrideRegTest() : base() } // TODO: This is also used in the block store integration tests, perhaps move it into the common namespace + /// /// Used for recording messages coming into a test node. Does not respond to them in any way. /// @@ -72,12 +73,14 @@ private async Task OnMessageReceivedAsync(INetworkPeer peer, IncomingMessage mes } } - private async Task ProcessMessageAsync(INetworkPeer peer, IncomingMessage message) + private Task ProcessMessageAsync(INetworkPeer peer, IncomingMessage message) { if (!this.receivedMessageTracker.ContainsKey(message.Message.Payload.Command)) this.receivedMessageTracker[message.Message.Payload.Command] = new List(); this.receivedMessageTracker[message.Message.Payload.Command].Add(message); + + return Task.CompletedTask; } public override object Clone() @@ -556,7 +559,7 @@ public void SegwitWalletTransactionBuildingAndPropagationTest() var witAddress = destinationAddress.Bech32Address; IActionResult transactionResult = node.FullNode.NodeController() - .BuildTransaction(new BuildTransactionRequest + .BuildTransactionAsync(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, @@ -568,7 +571,7 @@ public void SegwitWalletTransactionBuildingAndPropagationTest() var walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; - node.FullNode.NodeController().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); + _ = node.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(walletBuildTransactionModel.Hex)); Transaction witFunds = node.FullNode.Network.CreateTransaction(walletBuildTransactionModel.Hex); uint witIndex = witFunds.Outputs.AsIndexedOutputs().First(o => o.TxOut.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)).N; @@ -595,7 +598,7 @@ public void SegwitWalletTransactionBuildingAndPropagationTest() // Send a transaction that has a segwit input, to a segwit address. transactionResult = node.FullNode.NodeController() - .BuildTransaction(new BuildTransactionRequest + .BuildTransactionAsync(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, @@ -608,7 +611,7 @@ public void SegwitWalletTransactionBuildingAndPropagationTest() walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; - node.FullNode.NodeController().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); + _ = node.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(walletBuildTransactionModel.Hex)); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); @@ -663,7 +666,7 @@ public void SegwitWalletTransactionBuildingTest_SpendP2WPKHAndNormalUTXOs() var p2wpkhAmount = Money.Coins(1); IActionResult transactionResult = node.FullNode.NodeController() - .BuildTransaction(new BuildTransactionRequest + .BuildTransactionAsync(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, @@ -675,7 +678,7 @@ public void SegwitWalletTransactionBuildingTest_SpendP2WPKHAndNormalUTXOs() var walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; - node.FullNode.NodeController().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); + _ = node.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(walletBuildTransactionModel.Hex)); Transaction witFunds = node.FullNode.Network.CreateTransaction(walletBuildTransactionModel.Hex); uint witIndex = witFunds.Outputs.AsIndexedOutputs().First(o => o.TxOut.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)).N; @@ -696,7 +699,7 @@ public void SegwitWalletTransactionBuildingTest_SpendP2WPKHAndNormalUTXOs() // By sending more than the size of the P2WPKH UTXO, we guarantee that at least one non-P2WPKH UTXO gets included transactionResult = node.FullNode.NodeController() - .BuildTransaction(new BuildTransactionRequest + .BuildTransactionAsync(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, @@ -712,7 +715,7 @@ public void SegwitWalletTransactionBuildingTest_SpendP2WPKHAndNormalUTXOs() walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; - node.FullNode.NodeController().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); + _ = node.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(walletBuildTransactionModel.Hex)); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); @@ -748,7 +751,7 @@ public void SegwitWalletTransactionBuildingTest_SendToBech32AndNormalDestination var nonWitAddress = destinationAddress.Address; IActionResult transactionResult = node.FullNode.NodeController() - .BuildTransaction(new BuildTransactionRequest + .BuildTransactionAsync(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, @@ -760,7 +763,7 @@ public void SegwitWalletTransactionBuildingTest_SendToBech32AndNormalDestination var walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; - node.FullNode.NodeController().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); + _ = node.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(walletBuildTransactionModel.Hex)); Transaction witFunds = node.FullNode.Network.CreateTransaction(walletBuildTransactionModel.Hex); uint witIndex = witFunds.Outputs.AsIndexedOutputs().First(o => o.TxOut.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)).N; @@ -779,7 +782,7 @@ public void SegwitWalletTransactionBuildingTest_SendToBech32AndNormalDestination // By sending more than the size of the P2WPKH UTXO, we guarantee that at least one non-P2WPKH UTXO gets included transactionResult = node.FullNode.NodeController() - .BuildTransaction(new BuildTransactionRequest + .BuildTransactionAsync(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, @@ -796,7 +799,7 @@ public void SegwitWalletTransactionBuildingTest_SendToBech32AndNormalDestination walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; - node.FullNode.NodeController().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); + _ = node.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(walletBuildTransactionModel.Hex)); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); diff --git a/src/Stratis.Bitcoin.IntegrationTests/Wallet/ColdWalletTests.cs b/src/Stratis.Bitcoin.IntegrationTests/Wallet/ColdWalletTests.cs index ae6c9a6196..43dbf82c30 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/Wallet/ColdWalletTests.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/Wallet/ColdWalletTests.cs @@ -108,6 +108,7 @@ private CoreNode CreatePowPosMiningNode(NodeBuilder nodeBuilder, Network network /// the cold staking setup using a cold staking address obtained from the cold wallet node. /// Success is determined by whether the balance in the cold wallet increases. /// + /// The asynchronous task. [Fact] [Trait("Unstable", "True")] public async Task WalletCanMineWithColdWalletCoinsAsync() @@ -151,7 +152,7 @@ public async Task WalletCanMineWithColdWalletCoinsAsync() Transaction transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, amountToSend, FeeType.Medium, 1)); // Broadcast to the other node - await stratisSender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(transaction1.ToHex())); + await stratisSender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(transaction1.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => stratisHotStake.CreateRPCClient().GetRawMempool().Length > 0); @@ -168,7 +169,7 @@ public async Task WalletCanMineWithColdWalletCoinsAsync() coldWalletAddress.Address, hotWalletAddress.Address, WalletName, Account, Password, amountToSend2, new Money(0.02m, MoneyUnit.BTC), false, false, 1, false); // Broadcast to the other node - await stratisHotStake.FullNode.NodeController().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); + await stratisHotStake.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(transaction2.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).Any()); @@ -233,7 +234,7 @@ public async Task CanRetrieveFilteredUtxosAsync() Transaction transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, coldWalletAddress.ScriptPubKey, amountToSend, FeeType.Medium, 1)); // Broadcast to the other nodes. - await stratisSender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(transaction1.ToHex())); + await stratisSender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(transaction1.ToHex())); // Wait for the transaction to arrive. TestBase.WaitLoop(() => stratisColdStake.CreateRPCClient().GetRawMempool().Length > 0); diff --git a/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingStakedCoinsBeforeMaturity_Steps.cs b/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingStakedCoinsBeforeMaturity_Steps.cs index e2c31d8209..c1caca5694 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingStakedCoinsBeforeMaturity_Steps.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingStakedCoinsBeforeMaturity_Steps.cs @@ -73,13 +73,13 @@ private IActionResult SendTransaction(IActionResult transactionResult) return null; return this.proofOfStakeSteps.PremineNodeWithCoins.FullNode.NodeController() - .SendTransaction(new SendTransactionRequest(walletTransactionModel.Hex)).GetAwaiter().GetResult(); + .SendTransactionAsync(new SendTransactionRequest(walletTransactionModel.Hex)).GetAwaiter().GetResult(); } private IActionResult BuildTransaction() { IActionResult transactionResult = this.proofOfStakeSteps.PremineNodeWithCoins.FullNode.NodeController() - .BuildTransaction(new BuildTransactionRequest + .BuildTransactionAsync(new BuildTransactionRequest { AccountName = this.proofOfStakeSteps.PremineWalletAccount, AllowUnconfirmed = true, diff --git a/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingTransactionOverPolicyByteLimitSpecification_Steps.cs b/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingTransactionOverPolicyByteLimitSpecification_Steps.cs index bf785619bd..1c73e2e56f 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingTransactionOverPolicyByteLimitSpecification_Steps.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingTransactionOverPolicyByteLimitSpecification_Steps.cs @@ -80,7 +80,7 @@ private void node1_builds_oversize_tx_to_send_to_node2() private void sending_the_transaction() { - this.firstNode.FullNode.NodeController().SendTransaction(new SendTransactionRequest(this.transaction.ToHex(this.firstNode.FullNode.Network))); + this.firstNode.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(this.transaction.ToHex(this.firstNode.FullNode.Network))); } private void Node1BuildsTransactionToSendToNode2(int txoutputs) diff --git a/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingTxWithDoubleSpendSpecification_Steps.cs b/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingTxWithDoubleSpendSpecification_Steps.cs index 4c7e59e8de..9aa92f28f5 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingTxWithDoubleSpendSpecification_Steps.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/Wallet/SendingTxWithDoubleSpendSpecification_Steps.cs @@ -68,7 +68,7 @@ private void coins_first_sent_to_receiving_wallet() this.transaction = this.stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(WalletTests.CreateContext(this.stratisSender.FullNode.Network, new WalletAccountReference(Name, AccountName), Password, this.receivingAddress.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); - this.stratisSender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(this.transaction.ToHex())); + this.stratisSender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(this.transaction.ToHex())); TestBase.WaitLoop(() => this.stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); TestBase.WaitLoop(() => this.stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(Name).Any()); diff --git a/src/Stratis.Bitcoin.IntegrationTests/Wallet/WalletAddressGenerationAndFundsVisibility_Steps.cs b/src/Stratis.Bitcoin.IntegrationTests/Wallet/WalletAddressGenerationAndFundsVisibility_Steps.cs index 32f845b8d7..4ca3f5d3fd 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/Wallet/WalletAddressGenerationAndFundsVisibility_Steps.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/Wallet/WalletAddressGenerationAndFundsVisibility_Steps.cs @@ -97,7 +97,7 @@ private async Task a_wallet_with_funds_at_index_20_which_is_beyond_default_gap_l .BuildTransaction(transactionBuildContext); await this.sendingStratisBitcoinNode.FullNode.NodeController() - .SendTransaction(new SendTransactionRequest(transaction.ToHex())); + .SendTransactionAsync(new SendTransactionRequest(transaction.ToHex())); TestHelper.MineBlocks(this.sendingStratisBitcoinNode, 1); diff --git a/src/Stratis.Bitcoin.IntegrationTests/Wallet/WalletTests.cs b/src/Stratis.Bitcoin.IntegrationTests/Wallet/WalletTests.cs index 2cc4e94482..ab69e7e4ab 100644 --- a/src/Stratis.Bitcoin.IntegrationTests/Wallet/WalletTests.cs +++ b/src/Stratis.Bitcoin.IntegrationTests/Wallet/WalletTests.cs @@ -56,7 +56,7 @@ public async Task WalletCanReceiveAndSendCorrectlyAsync() new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // Broadcast to the other node - await stratisSender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(trx.ToHex())); + await stratisSender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(trx.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); @@ -108,7 +108,7 @@ public void WalletBalanceCorrectWhenOnlySomeUnconfirmedAreIncludedInABlock() new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // Broadcast to the other node - stratisSender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(trx.ToHex())); + stratisSender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(trx.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); @@ -126,7 +126,7 @@ public void WalletBalanceCorrectWhenOnlySomeUnconfirmedAreIncludedInABlock() sendto = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction testTx1 = stratisReceiver.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 0)); - stratisReceiver.FullNode.NodeController().SendTransaction(new SendTransactionRequest(testTx1.ToHex())); + stratisReceiver.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(testTx1.ToHex())); TestBase.WaitLoop(() => stratisSender.CreateRPCClient().GetRawMempool().Length > 0); // Disconnect so the first node doesn't get any more transactions. @@ -228,7 +228,7 @@ public void WalletCanReorg() Transaction transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // Broadcast to the other node. - stratisSender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(transaction1.ToHex())); + stratisSender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(transaction1.ToHex())); // Wait for the transaction to arrive. TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); @@ -261,7 +261,7 @@ public void WalletCanReorg() // Send more coins to the wallet sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction transaction2 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 101)); - stratisSender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); + stratisSender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(transaction2.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); @@ -305,7 +305,7 @@ public void WalletCanReorg() // ReBuild Transaction 2. // After the reorg transaction2 was returned back to mempool. - stratisSender.FullNode.NodeController().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); + stratisSender.FullNode.NodeController().SendTransactionAsync(new SendTransactionRequest(transaction2.ToHex())); TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); // Mine the transaction again. @@ -359,7 +359,7 @@ public void BuildTransaction_From_ManyUtxos_EnoughFundsForFee() Assert.Equal(utxosToSend * howManyTimes, transactionsToSpend.Count()); // Firstly, build a tx with value 1. Previously this would fail as the WalletTransactionHandler didn't pass enough UTXOs. - IActionResult result = node2.FullNode.NodeController().BuildTransaction( + IActionResult result = node2.FullNode.NodeController().BuildTransactionAsync( new BuildTransactionRequest { WalletName = WalletName, @@ -543,7 +543,7 @@ private static Result SendManyUtxosTransaction(CoreN // Broadcast to the other node. IActionResult result = node.FullNode.NodeController() - .SendTransaction(new SendTransactionRequest(trx.ToHex())).GetAwaiter().GetResult(); + .SendTransactionAsync(new SendTransactionRequest(trx.ToHex())).GetAwaiter().GetResult(); if (result is ErrorResult errorResult) { var errorResponse = (ErrorResponse)errorResult.Value; diff --git a/src/Stratis.Bitcoin.Networks/Stratis.Bitcoin.Networks.csproj b/src/Stratis.Bitcoin.Networks/Stratis.Bitcoin.Networks.csproj index e74cb1bc2b..769c576832 100644 --- a/src/Stratis.Bitcoin.Networks/Stratis.Bitcoin.Networks.csproj +++ b/src/Stratis.Bitcoin.Networks/Stratis.Bitcoin.Networks.csproj @@ -14,9 +14,9 @@ false false false - 1.1.1.1 - 1.1.1.1 - 1.1.1.1 + 1.2.0.0 + 1.2.0.0 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin.Networks/StraxMain.cs b/src/Stratis.Bitcoin.Networks/StraxMain.cs index 8fcd763833..12e729b574 100644 --- a/src/Stratis.Bitcoin.Networks/StraxMain.cs +++ b/src/Stratis.Bitcoin.Networks/StraxMain.cs @@ -167,7 +167,8 @@ public StraxMain() { 300_000, new CheckpointInfo(new uint256("0x35cb635c4f286b233fab6252c30f3df7813c0a76ca7ea2a90249cad73958e2d3"), new uint256("0x42e5a29b035296e3dee4f675f92c5790e0ac6cd0c9390fcf6bac9ac28ccaa850")) }, { 450_000, new CheckpointInfo(new uint256("0xc08db6151e2f341360a28e6a796d9c4356e14085e81aed2338c05f1964ef3e27"), new uint256("0x0cfc40a07819297a39be5460f805ce391d7f9b8d5794b18c97384a6b832deb4b")) }, { 600_000, new CheckpointInfo(new uint256("0xde6e45862b53aa12e68ebe1ce58962a35dc44c9b6357d6137d6f4d72a7799262"), new uint256("0xc1b1e2c3417c1d41e906a53028421bec3a1f8969cf38516671433c2c85ef09d3")) }, - { 700_000, new CheckpointInfo(new uint256("0x9383c8d2cb72273ec784c8bca40fd8aedb5014080b30e664e7025f9733e28cd4"), new uint256("0x2a90ab7ce85e1733631282f9cc392aa8b6f8a352234a8c992d38ff1651b039af")) } + { 700_000, new CheckpointInfo(new uint256("0x9383c8d2cb72273ec784c8bca40fd8aedb5014080b30e664e7025f9733e28cd4"), new uint256("0x2a90ab7ce85e1733631282f9cc392aa8b6f8a352234a8c992d38ff1651b039af")) }, + { 750_000, new CheckpointInfo(new uint256("0x16a24a00b59bf1f0a366be26f6da9bc12814f315cbaac6b536494555f065f5d0"), new uint256("0x54426d468d84dd4b54adbedf62458c175235e23af537876150853738b6adfacf")) } }; this.Bech32Encoders = new Bech32Encoder[2]; diff --git a/src/Stratis.Bitcoin.Networks/StraxTest.cs b/src/Stratis.Bitcoin.Networks/StraxTest.cs index 53a28f46dc..9aeffdcc57 100644 --- a/src/Stratis.Bitcoin.Networks/StraxTest.cs +++ b/src/Stratis.Bitcoin.Networks/StraxTest.cs @@ -155,6 +155,7 @@ public StraxTest() { 500_000, new CheckpointInfo(new uint256("0xda5da5c0ac8f34e89d6d308e1a046e98e46080941670e327d9eb84dc859d153f"), new uint256("0x1f73717627345bdc6d7b9b521dcea85df2586208a6d3a90fcd2efd16dcf9c591")) }, { 650_000, new CheckpointInfo(new uint256("0x50b2ddb88c5efe942d8bf6a07bed996f44b3b663df0f77d5d88ad1adba48329b"), new uint256("0xb507c86a412b9e50d0bed3be52a9042c2dbaca6653ff6ccb3e2e355c24c73a70")) }, { 750_000, new CheckpointInfo(new uint256("0x592842f3e5af517b0ce6f451f6b61738a6dea1007ccbaab39f22878de8de78dc"), new uint256("0x6ee053737f80a3a5173c10a507b1d1ea2ec9f6fa6be07b2b9d26558e4622f4a4")) }, + { 800_000, new CheckpointInfo(new uint256("0x70c51661a4b358c42984019ee5dac9faee2f724860f651f8d78ed309e137f957"), new uint256("0x82ddde3f4e73a2eb69907b3f07f1f70f4c3b7e95812590fdef1050afb253cf84")) }, }; this.Bech32Encoders = new Bech32Encoder[2]; diff --git a/src/Stratis.Bitcoin.Tests.Common/ReflectionExtensions.cs b/src/Stratis.Bitcoin.Tests.Common/ReflectionExtensions.cs index c677c4a895..3df78c9d47 100644 --- a/src/Stratis.Bitcoin.Tests.Common/ReflectionExtensions.cs +++ b/src/Stratis.Bitcoin.Tests.Common/ReflectionExtensions.cs @@ -30,7 +30,11 @@ public static object GetMemberValue(this object obj, string memberName) throw new Exception(); } - /// Gets private constant member of specified type. + /// Gets a static private constant member of specified type. + /// The type containing the static private constant. + /// The name of the static private constant. + /// The type of the static private constant. + /// The value of the static private constant. public static T GetPrivateConstantValue(this Type type, string constantName) { T value = type @@ -71,6 +75,11 @@ private static MemberInfo GetMemberInfo(object obj, string memberName) } /// Calls private method using reflection. + /// The object containing the method to invoke. + /// The name of the method to invoke. + /// The arguments to be passed to . + /// The type of the object containing the method to invoke. + /// An object containing the return value of the invoked method, or null in the case of a constructor. public static object InvokeMethod(this T obj, string methodName, params object[] args) { Type type = typeof(T); diff --git a/src/Stratis.Bitcoin.Tests.Common/Stratis.Bitcoin.Tests.Common.csproj b/src/Stratis.Bitcoin.Tests.Common/Stratis.Bitcoin.Tests.Common.csproj index 43125911d7..44f9525e99 100644 --- a/src/Stratis.Bitcoin.Tests.Common/Stratis.Bitcoin.Tests.Common.csproj +++ b/src/Stratis.Bitcoin.Tests.Common/Stratis.Bitcoin.Tests.Common.csproj @@ -13,7 +13,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False diff --git a/src/Stratis.Bitcoin.Tests.Common/TestBase.cs b/src/Stratis.Bitcoin.Tests.Common/TestBase.cs index 517f31855a..74619dccb9 100644 --- a/src/Stratis.Bitcoin.Tests.Common/TestBase.cs +++ b/src/Stratis.Bitcoin.Tests.Common/TestBase.cs @@ -20,6 +20,7 @@ public class TestBase /// /// Initializes logger factory for inherited tests. /// + /// The network context. public TestBase(Network network) { this.Network = network; @@ -37,6 +38,7 @@ public static DirectoryInfo AssureEmptyDir(string dir) /// /// The calling object, from which we derive the namespace in which the test is contained. /// The name of the test being executed. A directory with the same name will be created. + /// The network context. /// The that was initialized. public static DataFolder CreateDataFolder(object caller, [System.Runtime.CompilerServices.CallerMemberName] string callingMethod = "", Network network = null) { @@ -153,6 +155,7 @@ public ProvenBlockHeader CreateNewProvenBlockHeaderMock(PosBlock posBlock = null /// Creates a list of Proof of Stake blocks. /// /// The amount of blocks to create. + /// The list of Pos entries. public List CreatePosBlocks(int amount) { var blocks = new List(); diff --git a/src/Stratis.Bitcoin.Tests/Base/ChainRepositoryTest.cs b/src/Stratis.Bitcoin.Tests/Base/ChainRepositoryTest.cs index 26a3c968b2..a6a406cb78 100644 --- a/src/Stratis.Bitcoin.Tests/Base/ChainRepositoryTest.cs +++ b/src/Stratis.Bitcoin.Tests/Base/ChainRepositoryTest.cs @@ -74,8 +74,7 @@ public void LoadChainFromDisk() foreach (ChainedHeader block in blocks) { batch.Put(1, BitConverter.GetBytes(block.Height), - new ChainRepository.ChainRepositoryData() - { Hash = block.HashBlock, Work = block.ChainWorkBytes } + new ChainRepository.ChainRepositoryData(block.HashBlock, block.ChainWorkBytes) .ToBytes(this.Network.Consensus.ConsensusFactory)); ConsensusFactory consensusFactory = KnownNetworks.StraxRegTest.Consensus.ConsensusFactory; diff --git a/src/Stratis.Bitcoin.Tests/BlockPulling/BlockPullerTestsHelper.cs b/src/Stratis.Bitcoin.Tests/BlockPulling/BlockPullerTestsHelper.cs index c1dd127997..d1695052df 100644 --- a/src/Stratis.Bitcoin.Tests/BlockPulling/BlockPullerTestsHelper.cs +++ b/src/Stratis.Bitcoin.Tests/BlockPulling/BlockPullerTestsHelper.cs @@ -50,12 +50,18 @@ public BlockPullerTestsHelper() } /// Creates a peer with extended puller behavior. + /// The . + /// Iff true overrides the version with . + /// The . public INetworkPeer CreatePeer(out ExtendedBlockPullerBehavior mockedBehavior, bool notSupportedVersion = false) { return this.CreatePeerMock(out mockedBehavior, notSupportedVersion).Object; } /// Creates a peer with extended puller behavior. + /// The . + /// Iff true overrides the version with . + /// The mocked . public Mock CreatePeerMock(out ExtendedBlockPullerBehavior mockedBehavior, bool notSupportedVersion = false) { var peer = new Mock(); @@ -100,6 +106,8 @@ private ExtendedBlockPullerBehavior CreateBlockPullerBehavior() } /// Creates a new block with mocked serialized size. + /// The value for . + /// The . public Block GenerateBlock(long size) { Block block = new StraxMain().Consensus.ConsensusFactory.CreateBlock(); diff --git a/src/Stratis.Bitcoin.Tests/Consensus/CheckpointsTest.cs b/src/Stratis.Bitcoin.Tests/Consensus/CheckpointsTest.cs index df42af9f72..32d8550539 100644 --- a/src/Stratis.Bitcoin.Tests/Consensus/CheckpointsTest.cs +++ b/src/Stratis.Bitcoin.Tests/Consensus/CheckpointsTest.cs @@ -74,7 +74,7 @@ public void GetLastCheckPointHeight_StraxMainnet_ReturnsLastCheckPointHeight() int result = checkpoints.GetLastCheckpointHeight(); - Assert.Equal(700_000, result); + Assert.Equal(750_000, result); } [Fact] @@ -84,7 +84,7 @@ public void GetLastCheckPointHeight_StraxTestnet_ReturnsLastCheckPointHeight() int result = checkpoints.GetLastCheckpointHeight(); - Assert.Equal(750_000, result); + Assert.Equal(800_000, result); } [Fact] diff --git a/src/Stratis.Bitcoin.Tests/Controllers/NodeControllerTest.cs b/src/Stratis.Bitcoin.Tests/Controllers/NodeControllerTest.cs index df7a3dba37..8b6c538072 100644 --- a/src/Stratis.Bitcoin.Tests/Controllers/NodeControllerTest.cs +++ b/src/Stratis.Bitcoin.Tests/Controllers/NodeControllerTest.cs @@ -526,7 +526,7 @@ public void GetBlockHeader_NotUsingJsonFormat_ThrowsNotImplementedException() Assert.Single(errorResponse.Errors); ErrorModel error = errorResponse.Errors[0]; Assert.Equal(400, error.Status); - Assert.StartsWith("System.NotImplementedException", error.Description); + Assert.StartsWith("Binary serialization is not", error.Description); } [Fact] @@ -555,10 +555,7 @@ public void GetBlockHeader_BlockHeaderNotFound_ReturnsNull() string hash = new uint256(2562).ToString(); bool isJsonFormat = true; - var json = (JsonResult)this.controller.GetBlockHeader(hash, isJsonFormat); - var resultModel = (BlockHeaderModel)json.Value; - - Assert.Null(resultModel); + Assert.IsType(this.controller.GetBlockHeader(hash, isJsonFormat)); } [Fact] diff --git a/src/Stratis.Bitcoin.Tests/Utilities/AsyncProviderTest.cs b/src/Stratis.Bitcoin.Tests/Utilities/AsyncProviderTest.cs index cc258e15b2..30533be405 100644 --- a/src/Stratis.Bitcoin.Tests/Utilities/AsyncProviderTest.cs +++ b/src/Stratis.Bitcoin.Tests/Utilities/AsyncProviderTest.cs @@ -36,6 +36,7 @@ public AsyncProviderTest() /// /// Tests that triggers cancellation inside the on-enqueue callback. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_DelegateDequeuer_DisposeCancelsEnqueueAsync() { @@ -63,6 +64,7 @@ public async Task AsyncProvider_DelegateDequeuer_DisposeCancelsEnqueueAsync() /// Tests that waits until the on-enqueue callback (and the consumer task) /// are finished before returning to the caller. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_DelegateDequeuer_DisposeCancelsAndWaitsEnqueueAsync() { @@ -89,6 +91,7 @@ public async Task AsyncProvider_DelegateDequeuer_DisposeCancelsAndWaitsEnqueueAs /// Tests the guarantee of that only one instance of the callback is executed at the moment /// regardless of how many enqueue operations occur. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_DelegateDequeuer_OnlyOneInstanceOfCallbackExecutesAsync() { @@ -165,6 +168,7 @@ public void AsyncProvider_DelegateDequeuer_EnqueueOrderPreservedInCallbacks() /// /// Tests that if the queue is disposed, not all items are necessarily processed. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_DelegateDequeuer_DisposeCanDiscardItemsAsync() { @@ -194,6 +198,7 @@ public async Task AsyncProvider_DelegateDequeuer_DisposeCanDiscardItemsAsync() /// Tests that throws cancellation exception /// when the passed cancellation token is cancelled. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_AsyncQueue_DequeueCancellationAsync() { @@ -243,6 +248,7 @@ public async Task AsyncProvider_AsyncQueue_DequeueCancellationAsync() /// Tests that provides items in correct order /// and that it throws cancellation exception when the queue is disposed. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_AsyncQueue_DequeueAndDisposeAsync() { @@ -303,6 +309,7 @@ public async Task AsyncProvider_AsyncQueue_DequeueAndDisposeAsync() /// Tests that throws cancellation exception /// if it is called after the queue was disposed. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_AsyncQueue_DequeueThrowsAfterDisposeAsync() { @@ -332,6 +339,7 @@ public void AsyncProvider_AsyncQueue_DequeueBlocksOnEmptyQueue() /// Tests that can be used by /// two different threads safely. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_AsyncQueue_DequeueParallelAsync() { @@ -389,6 +397,7 @@ public async Task AsyncProvider_AsyncQueue_DequeueParallelAsync() /// List to add consumed items to. /// Value of the last item that will be added to the queue. /// Cancellation source to cancel when we are done. + /// The asynchronous task. private async Task AsyncQueue_DequeueParallelAsync_WorkerAsync(IAsyncQueue asyncQueue, List list, int lastItem, CancellationTokenSource cts) { while (true) @@ -418,13 +427,14 @@ private async Task AsyncQueue_DequeueParallelAsync_WorkerAsync(IAsyncQueue /// /// Tests that can be called from a callback. /// + /// The asynchronous task. [Fact] public async Task AsyncProvider_AsyncQueue_CanDisposeFromCallback_Async() { bool firstRun = true; bool shouldBeFalse = false; - var asyncQueue = this.asyncProvider.CreateAndRunAsyncDelegateDequeuer(this.GetType().Name, async (item, cancellation) => + var asyncQueue = this.asyncProvider.CreateAndRunAsyncDelegateDequeuer(this.GetType().Name, (item, cancellation) => { if (firstRun) { @@ -436,6 +446,8 @@ public async Task AsyncProvider_AsyncQueue_CanDisposeFromCallback_Async() // This should not happen. shouldBeFalse = true; } + + return Task.CompletedTask; }); asyncQueue.Enqueue(asyncQueue); @@ -452,9 +464,9 @@ public async Task AsyncProvider_AsyncQueue_CanDisposeFromCallback_Async() [Fact] - public async Task AsyncProvider_AsyncLoop_ExceptionInLoopThrowsCriticalException() + public async Task AsyncProvider_AsyncLoop_ExceptionInLoopThrowsCriticalExceptionAsync() { - var asyncLoop = this.asyncProvider.CreateAndRunAsyncLoop("TestLoop", async token => + var asyncLoop = this.asyncProvider.CreateAndRunAsyncLoop("TestLoop", token => { throw new Exception("Exception Test."); }, CancellationToken.None); diff --git a/src/Stratis.Bitcoin/Base/BaseFeature.cs b/src/Stratis.Bitcoin/Base/BaseFeature.cs index 2b71cf5c88..31f50d4ab6 100644 --- a/src/Stratis.Bitcoin/Base/BaseFeature.cs +++ b/src/Stratis.Bitcoin/Base/BaseFeature.cs @@ -109,6 +109,8 @@ public sealed class BaseFeature : FullNodeFeature /// Provider of IBD state. private readonly IInitialBlockDownloadState initialBlockDownloadState; +#pragma warning disable SA1648 + /// private readonly Network network; private readonly INodeStats nodeStats; @@ -127,6 +129,8 @@ public sealed class BaseFeature : FullNodeFeature /// private readonly IPartialValidator partialValidator; +#pragma warning restore SA1648 + public BaseFeature(NodeSettings nodeSettings, DataFolder dataFolder, INodeLifetime nodeLifetime, diff --git a/src/Stratis.Bitcoin/Base/ChainRepository.cs b/src/Stratis.Bitcoin/Base/ChainRepository.cs index 24cfb4a387..ea64f7c9bf 100644 --- a/src/Stratis.Bitcoin/Base/ChainRepository.cs +++ b/src/Stratis.Bitcoin/Base/ChainRepository.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.EventBus; using Stratis.Bitcoin.Signals; using Stratis.Bitcoin.Utilities; @@ -23,7 +24,7 @@ public interface IChainRepository : IDisposable public class ChainRepository : IChainRepository { private readonly IChainStore chainStore; - private readonly Logger logger; + private readonly ILogger logger; private readonly ISignals signals; private BlockLocator locator; @@ -71,7 +72,7 @@ public Task LoadAsync(ChainedHeader genesisHeader) if (this.signals != null) this.signals.Publish(new FullNodeEvent() { Message = $"Loading chain at height {height}.", State = FullNodeState.Initializing.ToString() }); - this.logger.Info($"Loading chain at height {height}."); + this.logger.LogInformation($"Loading chain at height {height}."); } height++; @@ -135,28 +136,38 @@ public void Dispose() public class ChainRepositoryData : IBitcoinSerializable { - public uint256 Hash; - public byte[] Work; + private uint256 hash; + private byte[] work; + + public uint256 Hash => this.hash; + + public byte[] Work => this.work; public ChainRepositoryData() { } + public ChainRepositoryData(uint256 hash, byte[] work) : this() + { + this.hash = hash; + this.work = work; + } + public void ReadWrite(BitcoinStream stream) { - stream.ReadWrite(ref this.Hash); + stream.ReadWrite(ref this.hash); if (stream.Serializing) { - int len = this.Work.Length; + int len = this.work.Length; stream.ReadWrite(ref len); - stream.ReadWrite(ref this.Work); + stream.ReadWrite(ref this.work); } else { int len = 0; stream.ReadWrite(ref len); - this.Work = new byte[len]; - stream.ReadWrite(ref this.Work); + this.work = new byte[len]; + stream.ReadWrite(ref this.work); } } } diff --git a/src/Stratis.Bitcoin/BlockPulling/BlockPuller.cs b/src/Stratis.Bitcoin/BlockPulling/BlockPuller.cs index 2002a03dc8..790e290ee5 100644 --- a/src/Stratis.Bitcoin/BlockPulling/BlockPuller.cs +++ b/src/Stratis.Bitcoin/BlockPulling/BlockPuller.cs @@ -4,8 +4,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.Base; using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Configuration.Logging; @@ -184,6 +184,8 @@ public class BlockPuller : IBlockPuller /// This object has to be protected by . private bool isIbd; +#pragma warning disable SA1648 + /// private readonly ILogger logger; @@ -200,6 +202,8 @@ public class BlockPuller : IBlockPuller /// private readonly Random random; +#pragma warning restore SA1648 + /// Loop that assigns download jobs to the peers. private Task assignerLoop; @@ -268,7 +272,7 @@ public void RequestPeerServices(NetworkPeerServices services) if ((peer == null) || !this.networkPeerRequirement.Check(peer.PeerVersion, peer.Inbound, out reason)) { - this.logger.Debug("Peer Id {0} does not meet requirements, reason: {1}", peerIdToBehavior.Key, reason); + this.logger.LogDebug("Peer Id {0} does not meet requirements, reason: {1}", peerIdToBehavior.Key, reason); peerIdsToRemove.Add(peerIdToBehavior.Key); } } @@ -321,7 +325,7 @@ public void NewPeerTipClaimed(INetworkPeer peer, ChainedHeader newTip) if (this.pullerBehaviorsByPeerId.TryGetValue(peerId, out IBlockPullerBehavior behavior)) { behavior.Tip = newTip; - this.logger.Debug("Tip for peer with ID {0} was changed to '{1}'.", peerId, newTip); + this.logger.LogDebug("Tip for peer with ID {0} was changed to '{1}'.", peerId, newTip); } else { @@ -333,10 +337,10 @@ public void NewPeerTipClaimed(INetworkPeer peer, ChainedHeader newTip) behavior.Tip = newTip; this.pullerBehaviorsByPeerId.Add(peerId, behavior); - this.logger.Debug("New peer with ID {0} and tip '{1}' was added.", peerId, newTip); + this.logger.LogDebug("New peer with ID {0} and tip '{1}' was added.", peerId, newTip); } else - this.logger.Debug("Peer ID {0} was discarded since he doesn't support the requirements, reason: {1}", peerId, reason); + this.logger.LogDebug("Peer ID {0} was discarded since he doesn't support the requirements, reason: {1}", peerId, reason); } } } @@ -370,7 +374,7 @@ public void RequestBlocksDownload(List headers, bool highPriority Id = jobId }); - this.logger.Debug("{0} blocks were requested from puller. Job ID {1} was created.", headers.Count, jobId); + this.logger.LogDebug("{0} blocks were requested from puller. Job ID {1} was created.", headers.Count, jobId); this.processQueuesSignal.Set(); } @@ -387,7 +391,7 @@ private async Task AssignerLoopAsync() } catch (OperationCanceledException) { - this.logger.Trace("(-)[CANCELLED]"); + this.logger.LogTrace("(-)[CANCELLED]"); return; } @@ -406,7 +410,7 @@ private async Task StallingLoopAsync() } catch (OperationCanceledException) { - this.logger.Trace("(-)[CANCELLED]"); + this.logger.LogTrace("(-)[CANCELLED]"); return; } @@ -437,27 +441,27 @@ private async Task AssignDownloadJobsAsync() if (emptySlots >= slotsThreshold) this.ProcessQueueLocked(this.downloadJobsQueue, newAssignments, failedHashes, emptySlots); else - this.logger.Debug("Slots threshold is not met, queue will not be processed. There are {0} empty slots, threshold is {1}.", emptySlots, slotsThreshold); + this.logger.LogDebug("Slots threshold is not met, queue will not be processed. There are {0} empty slots, threshold is {1}.", emptySlots, slotsThreshold); this.processQueuesSignal.Reset(); } if (newAssignments.Count != 0) { - this.logger.Debug("Total amount of downloads assigned in this iteration is {0}.", newAssignments.Count); + this.logger.LogDebug("Total amount of downloads assigned in this iteration is {0}.", newAssignments.Count); await this.AskPeersForBlocksAsync(newAssignments).ConfigureAwait(false); } // Call callbacks with null since puller failed to deliver requested blocks. if (failedHashes.Count != 0) - this.logger.Debug("{0} jobs partially or fully failed.", failedHashes.Count); + this.logger.LogDebug("{0} jobs partially or fully failed.", failedHashes.Count); foreach (uint256 failedJob in failedHashes) { // Avoid calling callbacks on shutdown. if (this.cancellationSource.IsCancellationRequested) { - this.logger.Debug("Callbacks won't be called because component is being disposed."); + this.logger.LogDebug("Callbacks won't be called because component is being disposed."); break; } @@ -483,7 +487,7 @@ private void ProcessQueueLocked(Queue jobsQueue, List assignments) if (!success) { - this.logger.Debug("Failed to ask peer {0} for {1} blocks.", peerId, hashes.Count); + this.logger.LogDebug("Failed to ask peer {0} for {1} blocks.", peerId, hashes.Count); this.PeerDisconnected(peerId); } } @@ -606,7 +610,7 @@ private async Task AskPeersForBlocksAsync(List assignments) /// Distributes download job's headers to peers that can provide blocks represented by those headers. /// - /// If some of the blocks from the job can't be provided by any peer those headers will be added to a . + /// If some of the blocks from the job can't be provided by any peer those headers will be added to a . /// /// Have to be locked by . /// @@ -633,7 +637,7 @@ private List DistributeHeadersLocked(DownloadJob downloadJob, if (peerBehaviors.Count == 0) { - this.logger.Debug("There are no peers that can participate in download job distribution! Job ID {0} failed.", downloadJob.Id); + this.logger.LogDebug("There are no peers that can participate in download job distribution! Job ID {0} failed.", downloadJob.Id); jobFailed = true; } @@ -679,7 +683,7 @@ private List DistributeHeadersLocked(DownloadJob downloadJob, lastSucceededIndex = index; - this.logger.Debug("Block '{0}' was assigned to peer ID {1}.", header.HashBlock, peerId); + this.logger.LogDebug("Block '{0}' was assigned to peer ID {1}.", header.HashBlock, peerId); break; } else @@ -691,7 +695,7 @@ private List DistributeHeadersLocked(DownloadJob downloadJob, continue; jobFailed = true; - this.logger.Debug("Job {0} failed because there is no peer claiming header '{1}'.", downloadJob.Id, header); + this.logger.LogDebug("Job {0} failed because there is no peer claiming header '{1}'.", downloadJob.Id, header); } } } @@ -717,7 +721,7 @@ private List DistributeHeadersLocked(DownloadJob downloadJob, private void CheckStalling() { int lastImportantHeight = this.chainState.ConsensusTip.Height + ImportantHeightMargin; - this.logger.Debug("Blocks up to height {0} are considered to be important.", lastImportantHeight); + this.logger.LogDebug("Blocks up to height {0} are considered to be important.", lastImportantHeight); var allReleasedAssignments = new List>>(); @@ -751,7 +755,7 @@ private void CheckStalling() int assignedCount = this.assignedHeadersByPeerId[peerId].Count; - this.logger.Debug("Peer {0} failed to deliver {1} blocks from which some were important.", peerId, assignedCount); + this.logger.LogDebug("Peer {0} failed to deliver {1} blocks from which some were important.", peerId, assignedCount); lock (this.peerLock) { @@ -793,15 +797,15 @@ public void PushBlock(uint256 blockHash, Block block, int peerId) { if (!this.assignedDownloadsByHash.TryGetValue(blockHash, out assignedDownload)) { - this.logger.Trace("(-)[BLOCK_NOT_REQUESTED]"); + this.logger.LogTrace("(-)[BLOCK_NOT_REQUESTED]"); return; } - this.logger.Debug("Assignment '{0}' for peer ID {1} was delivered by peer ID {2}.", blockHash, assignedDownload.PeerId, peerId); + this.logger.LogDebug("Assignment '{0}' for peer ID {1} was delivered by peer ID {2}.", blockHash, assignedDownload.PeerId, peerId); if (assignedDownload.PeerId != peerId) { - this.logger.Trace("(-)[WRONG_PEER_DELIVERED]"); + this.logger.LogTrace("(-)[WRONG_PEER_DELIVERED]"); return; } @@ -809,7 +813,7 @@ public void PushBlock(uint256 blockHash, Block block, int peerId) } double deliveredInSeconds = (this.dateTimeProvider.GetUtcNow() - assignedDownload.AssignedTime).TotalSeconds; - this.logger.Debug("Peer {0} delivered block '{1}' in {2} seconds.", assignedDownload.PeerId, blockHash, deliveredInSeconds); + this.logger.LogDebug("Peer {0} delivered block '{1}' in {2} seconds.", assignedDownload.PeerId, blockHash, deliveredInSeconds); lock (this.peerLock) { @@ -855,7 +859,7 @@ private void RecalculateQualityScoreLocked(IBlockPullerBehavior pullerBehavior, } else { - this.logger.Debug("Peer ID {0} is the fastest peer. Recalculating quality score of all peers.", peerId); + this.logger.LogDebug("Peer ID {0} is the fastest peer. Recalculating quality score of all peers.", peerId); // This is the best peer. Recalculate quality score for everyone. foreach (IBlockPullerBehavior peerPullerBehavior in this.pullerBehaviorsByPeerId.Values) @@ -877,7 +881,7 @@ private void RecalculateMaxBlocksBeingDownloadedLocked() if (this.maxBlocksBeingDownloaded < MinimalCountOfBlocksBeingDownloaded) this.maxBlocksBeingDownloaded = MinimalCountOfBlocksBeingDownloaded; - this.logger.Debug("Max number of blocks that can be downloaded at the same time is set to {0}.", this.maxBlocksBeingDownloaded); + this.logger.LogDebug("Max number of blocks that can be downloaded at the same time is set to {0}.", this.maxBlocksBeingDownloaded); } /// @@ -928,7 +932,7 @@ private Dictionary> ReleaseAssignmentsLocked(int peerId assignmentsToRemove.Add(assignment); - this.logger.Debug("Header '{0}' for job ID {1} was released from peer ID {2}.", header, assignment.JobId, peerId); + this.logger.LogDebug("Header '{0}' for job ID {1} was released from peer ID {2}.", header, assignment.JobId, peerId); } foreach (AssignedDownload assignment in assignmentsToRemove) diff --git a/src/Stratis.Bitcoin/Builder/Feature/FeatureControllerBase.cs b/src/Stratis.Bitcoin/Builder/Feature/FeatureControllerBase.cs index 6e2407be3c..975bae6350 100644 --- a/src/Stratis.Bitcoin/Builder/Feature/FeatureControllerBase.cs +++ b/src/Stratis.Bitcoin/Builder/Feature/FeatureControllerBase.cs @@ -17,7 +17,7 @@ namespace Stratis.Bitcoin.Builder.Feature /// public abstract class FeatureControllerBase : Controller { - public readonly ILogger Logger; + protected ILogger Logger { get; private set; } protected FeatureControllerBase(ILogger logger) { @@ -49,7 +49,7 @@ protected IActionResult Execute(TRequest request, Func Execute(TRequest request, CancellationToken token, Func> action, bool checkModelState = true) + protected async Task ExecuteAsync(TRequest request, CancellationToken token, Func> action, bool checkModelState = true) { if (checkModelState && !this.ModelState.IsValid) { @@ -78,7 +78,7 @@ protected Task ExecuteAsAsync(TRequest request, CancellationToken cancellationToken, Func action, bool checkModelState = true) { - return this.Execute(request, cancellationToken, (req, token) + return this.ExecuteAsync(request, cancellationToken, (req, token) => Task.Run(() => action(req, token), token), checkModelState); } } diff --git a/src/Stratis.Bitcoin/Builder/FullNodeFeatureExecutor.cs b/src/Stratis.Bitcoin/Builder/FullNodeFeatureExecutor.cs index c9092af431..84b6c49873 100644 --- a/src/Stratis.Bitcoin/Builder/FullNodeFeatureExecutor.cs +++ b/src/Stratis.Bitcoin/Builder/FullNodeFeatureExecutor.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using NLog; +using Microsoft.Extensions.Logging; using Stratis.Bitcoin.Builder.Feature; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.EventBus; using Stratis.Bitcoin.Signals; using Stratis.Bitcoin.Utilities; @@ -53,10 +54,10 @@ public void Initialize() { try { - this.logger.Info("Validating feature dependencies."); + this.logger.LogInformation("Validating feature dependencies."); this.Execute(feature => feature.ValidateDependencies(this.fullNode.Services)); - this.logger.Info("Initializing fullnode features."); + this.logger.LogInformation("Initializing fullnode features."); this.Execute(feature => { this.signals.Publish(new FullNodeEvent() { Message = $"Initializing feature '{feature.GetType().Name}'.", State = FullNodeState.Initializing.ToString() }); @@ -68,8 +69,8 @@ public void Initialize() } catch (Exception ex) { - this.logger.Error($"An error occurred starting the application: {ex}"); - this.logger.Trace("(-)[INITIALIZE_EXCEPTION]"); + this.logger.LogError($"An error occurred starting the application: {ex}"); + this.logger.LogTrace("(-)[INITIALIZE_EXCEPTION]"); throw; } } @@ -88,8 +89,8 @@ public void Dispose() } catch { - this.logger.Error("An error occurred stopping the application."); - this.logger.Trace("(-)[DISPOSE_EXCEPTION]"); + this.logger.LogError("An error occurred stopping the application."); + this.logger.LogTrace("(-)[DISPOSE_EXCEPTION]"); throw; } } @@ -104,7 +105,7 @@ private void Execute(Action callback, bool disposing = false) { if (this.fullNode.Services == null) { - this.logger.Trace("(-)[NO_SERVICES]"); + this.logger.LogTrace("(-)[NO_SERVICES]"); return; } @@ -153,7 +154,7 @@ private void Execute(Action callback, bool disposing = false) // Throw an aggregate exception if there were any exceptions. if (exceptions != null) { - this.logger.Trace("(-)[EXECUTION_FAILED]"); + this.logger.LogTrace("(-)[EXECUTION_FAILED]"); throw new AggregateException(exceptions); } } @@ -165,7 +166,7 @@ private void LogAndAddException(IFullNodeFeature feature, bool disposing, List public void DeleteChainDirectories() { - var dirsForDeletion = new string[] { BlockPath, ChainPath, CoindbPath, KeyValueRepositoryPath, SmartContractStatePath, ProvenBlockHeaderPath }; + var dirsForDeletion = new string[] { this.BlockPath, this.ChainPath, this.CoindbPath, this.KeyValueRepositoryPath, this.SmartContractStatePath, this.ProvenBlockHeaderPath }; foreach (var dir in dirsForDeletion.Select(dir => new DirectoryInfo(dir))) { diff --git a/src/Stratis.Bitcoin/Configuration/Logging/ILogger.cs b/src/Stratis.Bitcoin/Configuration/Logging/ILogger.cs new file mode 100644 index 0000000000..1c7945bcb6 --- /dev/null +++ b/src/Stratis.Bitcoin/Configuration/Logging/ILogger.cs @@ -0,0 +1,46 @@ +using System; +using Microsoft.Extensions.Logging; +using Stratis.Bitcoin.Utilities; + +namespace Stratis.Bitcoin.Configuration.Logging +{ + public class Logger : ILogger + { + private NLog.Logger logger; + + public Logger(NLog.Logger logger = null) + { + this.logger = logger ?? NLog.LogManager.GetCurrentClassLogger(); + } + + /// + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!this.IsEnabled(logLevel)) + return; + + if (formatter == null) + throw new ArgumentNullException(nameof(formatter)); + + string message = formatter(state, exception); + + NLog.LogEventInfo eventInfo = NLog.LogEventInfo.Create(logLevel.ToNLogLevel(), this.logger.Name, message, exception); + this.logger.Log(typeof(Logger), eventInfo); + } + + /// + public bool IsEnabled(LogLevel logLevel) + { + return this.logger.IsEnabled(logLevel.ToNLogLevel()); + } + + /// + public IDisposable BeginScope(TState state) + { + if (state == null) + throw new ArgumentNullException(nameof(state)); + + return NLog.NestedDiagnosticsLogicalContext.Push(state); + } + } +} diff --git a/src/Stratis.Bitcoin/Configuration/Logging/LogManager.cs b/src/Stratis.Bitcoin/Configuration/Logging/LogManager.cs new file mode 100644 index 0000000000..c7f9e14e42 --- /dev/null +++ b/src/Stratis.Bitcoin/Configuration/Logging/LogManager.cs @@ -0,0 +1,64 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using Microsoft.Extensions.Logging; + +namespace Stratis.Bitcoin.Configuration.Logging +{ + public static class LogManager + { + // Declare the delegate (if using non-generic pattern). + public delegate void ConfigurationReloadedEventHandler(object sender, NLog.Config.LoggingConfigurationReloadedEventArgs e); + + // Declare the event. + public static event ConfigurationReloadedEventHandler ConfigurationReloaded; + + static LogManager() + { + NLog.LogManager.ConfigurationReloaded += LogManager_ConfigurationReloaded; + } + + private static void LogManager_ConfigurationReloaded(object sender, NLog.Config.LoggingConfigurationReloadedEventArgs e) + { + // Raise the event in a thread-safe manner using the ?. operator. + ConfigurationReloaded?.Invoke(sender, e); + } + + public static ILogger GetCurrentClassLogger() + { + try + { + var stackTrace = new StackTrace(); + if (stackTrace.FrameCount > 1) + { + + MethodBase methodInfo = stackTrace.GetFrame(1).GetMethod(); + return new Logger(NLog.LogManager.GetLogger(methodInfo.ReflectedType.ToString())); + } + } + catch (Exception) + { + } + + return new Logger(); + } + + public static void ReconfigExistingLoggers() + { + NLog.LogManager.ReconfigExistingLoggers(); + } + + public static NLog.Config.LoggingConfiguration Configuration + { + get + { + return NLog.LogManager.Configuration; + } + + set + { + NLog.LogManager.Configuration = value; + } + } + } +} diff --git a/src/Stratis.Bitcoin/Configuration/Logging/LoggerFactory.cs b/src/Stratis.Bitcoin/Configuration/Logging/LoggerFactory.cs new file mode 100644 index 0000000000..04b4a59821 --- /dev/null +++ b/src/Stratis.Bitcoin/Configuration/Logging/LoggerFactory.cs @@ -0,0 +1,33 @@ +using System; +using System.Diagnostics; +using Microsoft.Extensions.Logging; + +namespace Stratis.Bitcoin.Configuration.Logging +{ + public class CustomLoggerFactory : ILoggerFactory + { + public ILogger CreateLogger(string name) + { + return new Logger(NLog.LogManager.LogFactory.GetLogger(name)); + } + + public ILogger CreateLogger(Type type) + { + return new Logger(NLog.LogManager.LogFactory.GetLogger(type.FullName)); + } + + public ILogger CreateLogger() + { + return new Logger(NLog.LogManager.LogFactory.GetLogger(typeof(T).FullName)); + } + + public void AddProvider(ILoggerProvider loggerProvider) + { + throw new NotImplementedException(); + } + + public void Dispose() + { + } + } +} diff --git a/src/Stratis.Bitcoin/Configuration/Logging/LoggingConfiguration.cs b/src/Stratis.Bitcoin/Configuration/Logging/LoggingConfiguration.cs index a991c32951..a513db4bd0 100644 --- a/src/Stratis.Bitcoin/Configuration/Logging/LoggingConfiguration.cs +++ b/src/Stratis.Bitcoin/Configuration/Logging/LoggingConfiguration.cs @@ -7,12 +7,10 @@ using Microsoft.Extensions.Logging.Console; using NLog; using NLog.Config; -using NLog.Extensions.Logging; using NLog.LayoutRenderers; using NLog.Targets; using NLog.Targets.Wrappers; using Stratis.Bitcoin.Configuration.Settings; -using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace Stratis.Bitcoin.Configuration.Logging { @@ -29,36 +27,17 @@ public class ExtendedLoggerFactory : LoggerFactory public static ILoggerFactory Create() { - return Create(builder => - { - builder - .AddFilter("Default", LogLevel.Information) - .AddFilter("System", LogLevel.Warning) - .AddFilter("Microsoft", LogLevel.Warning) - .AddFilter("Microsoft.AspNetCore", LogLevel.Error); - } - ); + return new CustomLoggerFactory(); } /// Loads the NLog.config file from the , if it exists. public static ILoggerFactory Create(LogSettings settings, DataFolder dataFolder) { - return Create(builder => - { - LoggingConfiguration.ConfigureConsoleFilters(builder, settings); - - builder - .AddFilter("Default", LogLevel.Information) - .AddFilter("System", LogLevel.Warning) - .AddFilter("Microsoft", LogLevel.Warning) - .AddFilter("Microsoft.AspNetCore", LogLevel.Error); - - string configPath = Path.Combine(dataFolder.RootPath, LoggingConfiguration.NLogConfigFileName); - if (File.Exists(configPath)) - builder.AddNLog(configPath); - else - builder.AddNLog(); - }); + string configPath = Path.Combine(dataFolder.RootPath, LoggingConfiguration.NLogConfigFileName); + if (File.Exists(configPath)) + NLog.LogManager.Configuration = new XmlLoggingConfiguration(configPath, true); + + return new CustomLoggerFactory(); } } @@ -131,17 +110,6 @@ static LoggingConfiguration() LogManager.ConfigurationReloaded += NLogConfigurationReloaded; } - /// Loads the NLog.config file from the , if it exists. - public static void LoadNLogConfiguration(this ILoggerFactory loggerFactory, DataFolder dataFolder) - { - if (dataFolder == null) - return; - - string configPath = Path.Combine(dataFolder.RootPath, NLogConfigFileName); - if (File.Exists(configPath)) - loggerFactory.ConfigureNLog(configPath); - } - /// /// Event handler to be called when logging gets reloaded. /// @@ -192,7 +160,10 @@ private static void AddFilters(LogSettings settings = null, DataFolder dataFolde LogManager.Configuration.LoggingRules.Remove(nullPreInitRule); - LayoutRenderer.Register("message", (logEvent) => ((logEvent.Parameters == null) ? logEvent.Message : string.Format(logEvent.Message, logEvent.Parameters)).Replace("\n", "\n\t")); + LayoutRenderer.Register("message", (logEvent) => + { + return ((logEvent.Parameters == null) ? logEvent.Message : string.Format(logEvent.Message, logEvent.Parameters)).Replace("\n", "\n\t"); + }); var consoleTarget = new ColoredConsoleTarget { @@ -200,7 +171,7 @@ private static void AddFilters(LogSettings settings = null, DataFolder dataFolde Layout = "[${level:lowercase=true}]\t${logger}\n\t${message}", AutoFlush = true, }; - + consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule("level == LogLevel.Info", ConsoleOutputColor.Gray, ConsoleOutputColor.Black)); consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule("level == LogLevel.Warn", ConsoleOutputColor.Gray, ConsoleOutputColor.Black)); consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule("level == LogLevel.Error", ConsoleOutputColor.Gray, ConsoleOutputColor.Black)); @@ -212,6 +183,10 @@ private static void AddFilters(LogSettings settings = null, DataFolder dataFolde // Logging level for console is always Info. LogManager.Configuration.LoggingRules.Add(new LoggingRule($"{nameof(Stratis)}.*", NLog.LogLevel.Info, consoleTarget)); + LogManager.Configuration.LoggingRules.Add(new LoggingRule("Default", NLog.LogLevel.Warn, consoleTarget)); + LogManager.Configuration.LoggingRules.Add(new LoggingRule($"{nameof(System)}", NLog.LogLevel.Warn, consoleTarget)); + LogManager.Configuration.LoggingRules.Add(new LoggingRule($"{nameof(Microsoft)}", NLog.LogLevel.Warn, consoleTarget)); + LogManager.Configuration.LoggingRules.Add(new LoggingRule($"{nameof(Microsoft)}.{nameof(Microsoft.AspNetCore)}", NLog.LogLevel.Error, consoleTarget)); // Configure main file target, configured using command line or node configuration file settings. var mainTarget = new FileTarget @@ -230,6 +205,10 @@ private static void AddFilters(LogSettings settings = null, DataFolder dataFolde // Default logging level is Info for all components. var defaultRule = new LoggingRule($"{nameof(Stratis)}.*", settings.LogLevel, mainTarget); + LogManager.Configuration.LoggingRules.Add(new LoggingRule("Default", NLog.LogLevel.Warn, mainTarget)); + LogManager.Configuration.LoggingRules.Add(new LoggingRule($"{nameof(System)}", NLog.LogLevel.Warn, mainTarget)); + LogManager.Configuration.LoggingRules.Add(new LoggingRule($"{nameof(Microsoft)}", NLog.LogLevel.Warn, mainTarget)); + LogManager.Configuration.LoggingRules.Add(new LoggingRule($"{nameof(Microsoft)}.{nameof(Microsoft.AspNetCore)}", NLog.LogLevel.Error, mainTarget)); if (settings.DebugArgs.Any() && settings.DebugArgs[0] != "1") { @@ -249,7 +228,7 @@ private static void AddFilters(LogSettings settings = null, DataFolder dataFolde if (!usedCategories.Contains(category)) { usedCategories.Add(category); - var rule = new LoggingRule(category, settings.LogLevel, mainTarget); + var rule = new LoggingRule(category, NLog.LogLevel.Debug, mainTarget); LogManager.Configuration.LoggingRules.Add(rule); } } diff --git a/src/Stratis.Bitcoin/Configuration/Settings/LogSettings.cs b/src/Stratis.Bitcoin/Configuration/Settings/LogSettings.cs index d6909a8d24..9897761c2f 100644 --- a/src/Stratis.Bitcoin/Configuration/Settings/LogSettings.cs +++ b/src/Stratis.Bitcoin/Configuration/Settings/LogSettings.cs @@ -33,7 +33,7 @@ public void Load(TextFileConfiguration config) this.DebugArgs = config.GetOrDefault("debug", string.Empty).Split(',').Where(s => !string.IsNullOrEmpty(s)).ToList(); // Get the minimum log level. The default is either Information or Debug depending on the DebugArgs. - this.LogLevel = this.DebugArgs.Any() ? NLog.LogLevel.Debug : NLog.LogLevel.Info; + this.LogLevel = (this.DebugArgs.Any() && this.DebugArgs[0] == "1") ? NLog.LogLevel.Debug : NLog.LogLevel.Info; string logLevelArg = config.GetOrDefault("loglevel", string.Empty); if (!string.IsNullOrEmpty(logLevelArg)) diff --git a/src/Stratis.Bitcoin/Connection/ConnectionManager.cs b/src/Stratis.Bitcoin/Connection/ConnectionManager.cs index bc0d6021c3..c144fa0a72 100644 --- a/src/Stratis.Bitcoin/Connection/ConnectionManager.cs +++ b/src/Stratis.Bitcoin/Connection/ConnectionManager.cs @@ -530,7 +530,7 @@ public async Task ConnectAsync(IPEndPoint ipEndpoint) if (existingConnection != null) { - this.logger.LogDebug("{0} is already connected."); + this.logger.LogDebug("{0} is already connected.", ipEndpoint); return existingConnection; } diff --git a/src/Stratis.Bitcoin/Connection/ConnectionManagerBehavior.cs b/src/Stratis.Bitcoin/Connection/ConnectionManagerBehavior.cs index dbb063ba1d..76ac297eb6 100644 --- a/src/Stratis.Bitcoin/Connection/ConnectionManagerBehavior.cs +++ b/src/Stratis.Bitcoin/Connection/ConnectionManagerBehavior.cs @@ -71,7 +71,7 @@ protected override void AttachCore() } } - private async Task OnStateChangedAsync(INetworkPeer peer, NetworkPeerState oldState) + private Task OnStateChangedAsync(INetworkPeer peer, NetworkPeerState oldState) { try { @@ -93,6 +93,8 @@ private async Task OnStateChangedAsync(INetworkPeer peer, NetworkPeerState oldSt catch (OperationCanceledException) { } + + return Task.CompletedTask; } [NoTrace] diff --git a/src/Stratis.Bitcoin/Consensus/ConsensusManager.cs b/src/Stratis.Bitcoin/Consensus/ConsensusManager.cs index ffd7ea7f7e..194b97b257 100644 --- a/src/Stratis.Bitcoin/Consensus/ConsensusManager.cs +++ b/src/Stratis.Bitcoin/Consensus/ConsensusManager.cs @@ -692,7 +692,7 @@ private async Task FullyValidateLockedAsync(ChainedHeader n // Add peers that needed to be banned as a result of a failure to connect blocks. // Otherwise they get lost as we are returning a different ConnnectBlocksResult. // We also need to set the ban reason and ban time otherwise it is not known why - // connecting the new chain failed and hence why the peer is being disconnected in + // connecting the new chain failed and hence why the peer is being disconnected in // peer banning. reconnectionResult.BanReason = connectBlockResult.BanReason; reconnectionResult.BanDurationSeconds = connectBlockResult.BanDurationSeconds; @@ -820,7 +820,7 @@ private async Task ConnectChainAsync(ListProvider of IBD state. protected IInitialBlockDownloadState InitialBlockDownloadState { get; private set; } +#pragma warning disable SA1648 + /// protected IConsensusManager ConsensusManager { get; private set; } @@ -32,6 +34,8 @@ public class ConsensusManagerBehavior : NetworkPeerBehavior /// protected IPeerBanning PeerBanning { get; private set; } +#pragma warning restore SA1648 + /// Factory for creating loggers. protected ILoggerFactory LoggerFactory { get; private set; } @@ -573,7 +577,7 @@ public void UpdateBestSentHeader(ChainedHeader header) } /// Tries to sync the chain with the peer by sending it in case peer's state is . - public async Task ResyncAsync() + public Task ResyncAsync() { if (this.cachedHeaders.Any()) { @@ -589,7 +593,7 @@ public async Task ResyncAsync() if (getHeadersPayload == null) { this.logger.LogDebug("Ignoring sync request, headersPayload is null."); - return; + return Task.CompletedTask; } try @@ -605,6 +609,8 @@ public async Task ResyncAsync() } else this.logger.LogDebug("Can't sync. Peer's state is not handshaked or peer was not attached."); + + return Task.CompletedTask; } /// diff --git a/src/Stratis.Bitcoin/Consensus/ConsensusRuleEngine.cs b/src/Stratis.Bitcoin/Consensus/ConsensusRuleEngine.cs index 50b79f5cd9..e5ebbc3a71 100644 --- a/src/Stratis.Bitcoin/Consensus/ConsensusRuleEngine.cs +++ b/src/Stratis.Bitcoin/Consensus/ConsensusRuleEngine.cs @@ -48,6 +48,8 @@ public abstract class ConsensusRuleEngine : IConsensusRuleEngine /// State of the current chain that hold consensus tip. public IChainState ChainState { get; } +#pragma warning disable SA1648 + /// private readonly IInvalidBlockHashStore invalidBlockHashStore; @@ -56,6 +58,8 @@ public abstract class ConsensusRuleEngine : IConsensusRuleEngine /// private ConsensusRulesPerformanceCounter performanceCounter; +#pragma warning restore SA1648 + protected ConsensusRuleEngine( Network network, ILoggerFactory loggerFactory, diff --git a/src/Stratis.Bitcoin/Consensus/FinalizedBlockInfoRepository.cs b/src/Stratis.Bitcoin/Consensus/FinalizedBlockInfoRepository.cs index 14dbbbf35e..7ae67c8e01 100644 --- a/src/Stratis.Bitcoin/Consensus/FinalizedBlockInfoRepository.cs +++ b/src/Stratis.Bitcoin/Consensus/FinalizedBlockInfoRepository.cs @@ -2,9 +2,10 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Persistence; using Stratis.Bitcoin.Utilities; @@ -52,7 +53,6 @@ public class FinalizedBlockInfoRepository : IFinalizedBlockInfoRepository /// Height and hash of a block that can't be reorged away from. private HashHeightPair finalizedBlockInfo; - private readonly INodeStats nodeStats; /// Queue of finalized infos to save. /// All access should be protected by . @@ -91,7 +91,7 @@ public FinalizedBlockInfoRepository(IKeyValueRepository keyValueRepo, IAsyncProv /// public void Initialize(ChainedHeader chainTip) { - // If the node shut down unexpectedly, it is possible that the finalized height could be + // If the node shut down unexpectedly, it is possible that the finalized height could be // higher than the chain tip. In this case we have to set the finalized height back to the chain's tip. if (this.GetFinalizedBlockInfo()?.Height > chainTip.Height) { @@ -99,10 +99,10 @@ public void Initialize(ChainedHeader chainTip) this.keyValueRepo.SaveValue(FinalizedBlockKey, resetFinalization); this.finalizedBlockInfo = resetFinalization; - this.logger.Info("Finalized block info reset."); + this.logger.LogInformation("Finalized block info reset."); } - this.logger.Info("Finalized block info initialized at '{0}'.", this.finalizedBlockInfo); + this.logger.LogInformation("Finalized block info initialized at '{0}'.", this.finalizedBlockInfo); this.finalizedBlockInfoPersistingTask = this.PersistFinalizedBlockInfoContinuouslyAsync(); this.asyncProvider.RegisterTask($"{nameof(FinalizedBlockInfoRepository)}.{nameof(this.finalizedBlockInfoPersistingTask)}", this.finalizedBlockInfoPersistingTask); @@ -138,7 +138,7 @@ private async Task PersistFinalizedBlockInfoContinuouslyAsync() this.keyValueRepo.SaveValue(FinalizedBlockKey, lastFinalizedBlock); - this.logger.Debug("Finalized info saved: '{0}'.", lastFinalizedBlock); + this.logger.LogDebug("Finalized info saved: '{0}'.", lastFinalizedBlock); } } @@ -169,7 +169,7 @@ public bool SaveFinalizedBlockHashAndHeight(uint256 hash, int height) { if (this.finalizedBlockInfo != null && height <= this.finalizedBlockInfo.Height) { - this.logger.Trace("(-)[CANT_GO_BACK]:false"); + this.logger.LogTrace("(-)[CANT_GO_BACK]:false"); return false; } diff --git a/src/Stratis.Bitcoin/Consensus/PerformanceCounters/Rules/ConsensusRulesPerformanceCounter.cs b/src/Stratis.Bitcoin/Consensus/PerformanceCounters/Rules/ConsensusRulesPerformanceCounter.cs index e4ca938314..0845137632 100644 --- a/src/Stratis.Bitcoin/Consensus/PerformanceCounters/Rules/ConsensusRulesPerformanceCounter.cs +++ b/src/Stratis.Bitcoin/Consensus/PerformanceCounters/Rules/ConsensusRulesPerformanceCounter.cs @@ -51,10 +51,7 @@ public IDisposable MeasureRuleExecutionTime(IConsensusRuleBase rule) { var stopwatch = new StopwatchDisposable(elapsedTicks => { - RulePerformance performance = this.currentSnapshot.PerformanceInfo[rule]; - - Interlocked.Increment(ref performance.CalledTimes); - Interlocked.Add(ref performance.ExecutionTimesTicks, elapsedTicks); + this.currentSnapshot.PerformanceInfo[rule].AddTicks(elapsedTicks); }); return stopwatch; diff --git a/src/Stratis.Bitcoin/Consensus/PerformanceCounters/Rules/ConsensusRulesPerformanceSnapshot.cs b/src/Stratis.Bitcoin/Consensus/PerformanceCounters/Rules/ConsensusRulesPerformanceSnapshot.cs index 1083bfe315..a46b7cb1c5 100644 --- a/src/Stratis.Bitcoin/Consensus/PerformanceCounters/Rules/ConsensusRulesPerformanceSnapshot.cs +++ b/src/Stratis.Bitcoin/Consensus/PerformanceCounters/Rules/ConsensusRulesPerformanceSnapshot.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using NBitcoin.Rules; using TracerAttributes; @@ -89,9 +90,12 @@ internal class RuleItem /// internal class RulePerformance : RuleItem { - public int CalledTimes; + private int calledTimes; + private long executionTimesTicks; - public long ExecutionTimesTicks; + public int CalledTimes => this.calledTimes; + + public long ExecutionTimesTicks => this.executionTimesTicks; public RulePerformance(RuleItem rule) { @@ -99,8 +103,14 @@ public RulePerformance(RuleItem rule) this.RuleType = rule.RuleType; this.RuleReferenceInstance = rule.RuleReferenceInstance; - this.CalledTimes = 0; - this.ExecutionTimesTicks = 0; + this.calledTimes = 0; + this.executionTimesTicks = 0; + } + + public void AddTicks(long elapsedTicks) + { + Interlocked.Increment(ref this.calledTimes); + Interlocked.Add(ref this.executionTimesTicks, elapsedTicks); } } diff --git a/src/Stratis.Bitcoin/Controllers/Models/TransactionModel.cs b/src/Stratis.Bitcoin/Controllers/Models/TransactionModel.cs index fe051b8787..24f61bbe0d 100644 --- a/src/Stratis.Bitcoin/Controllers/Models/TransactionModel.cs +++ b/src/Stratis.Bitcoin/Controllers/Models/TransactionModel.cs @@ -346,6 +346,7 @@ protected string GetScriptType(ScriptTemplate template) { if (template == null) return "nonstandard"; + switch (template.Type) { case TxOutType.TX_PUBKEY: diff --git a/src/Stratis.Bitcoin/Controllers/RestApiClientBase.cs b/src/Stratis.Bitcoin/Controllers/RestApiClientBase.cs index 6998173a3f..6e8bff62cb 100644 --- a/src/Stratis.Bitcoin/Controllers/RestApiClientBase.cs +++ b/src/Stratis.Bitcoin/Controllers/RestApiClientBase.cs @@ -5,10 +5,11 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; -using NLog; using Polly; using Polly.Retry; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Utilities; using Stratis.Bitcoin.Utilities.JsonErrors; @@ -81,28 +82,28 @@ protected async Task SendPostRequestAsync(Model requ // Retry the following call according to the policy. await this.policy.ExecuteAsync(async token => { - this.logger.Debug("Sending request of type '{0}' to Uri '{1}'.", + this.logger.LogDebug("Sending request of type '{0}' to Uri '{1}'.", requestModel.GetType().FullName, publicationUri); response = await client.PostAsync(publicationUri, request, cancellation).ConfigureAwait(false); - this.logger.Debug("Response received: {0}", response); + this.logger.LogDebug("Response received: {0}", response); }, cancellation); } catch (OperationCanceledException) { - this.logger.Debug("Operation canceled."); - this.logger.Trace("(-)[CANCELLED]:null"); + this.logger.LogDebug("Operation canceled."); + this.logger.LogTrace("(-)[CANCELLED]:null"); return null; } catch (HttpRequestException ex) { - this.logger.Error("Target node is not ready to receive API calls at this time on {0}. Reason: {1}.", this.EndpointUrl, ex.Message); - this.logger.Debug("Failed to send a message. Exception: '{0}'.", ex); + this.logger.LogError("Target node is not ready to receive API calls at this time on {0}. Reason: {1}.", this.EndpointUrl, ex.Message); + this.logger.LogDebug("Failed to send a message. Exception: '{0}'.", ex); return new HttpResponseMessage() { ReasonPhrase = ex.Message, StatusCode = HttpStatusCode.InternalServerError }; } } - this.logger.Trace("(-)[SUCCESS]"); + this.logger.LogTrace("(-)[SUCCESS]"); return response; } @@ -131,19 +132,19 @@ private async Task ParseHttpResponseMessageAsync(HttpRespons { if (httpResponse == null) { - this.logger.Trace("(-)[NO_RESPONSE]:null"); + this.logger.LogTrace("(-)[NO_RESPONSE]:null"); return null; } if (!httpResponse.IsSuccessStatusCode) { - this.logger.Trace("(-)[NOT_SUCCESS_CODE]:null"); + this.logger.LogTrace("(-)[NOT_SUCCESS_CODE]:null"); return null; } if (httpResponse.Content == null) { - this.logger.Trace("(-)[NO_CONTENT]:null"); + this.logger.LogTrace("(-)[NO_CONTENT]:null"); return null; } @@ -152,13 +153,13 @@ private async Task ParseHttpResponseMessageAsync(HttpRespons if (successJson == null) { - this.logger.Trace("(-)[JSON_PARSING_FAILURE]:null"); + this.logger.LogTrace("(-)[JSON_PARSING_FAILURE]:null"); return null; } Response responseModel = JsonConvert.DeserializeObject(successJson); - this.logger.Trace("(-)[SUCCESS]"); + this.logger.LogTrace("(-)[SUCCESS]"); return responseModel; } @@ -182,35 +183,35 @@ protected async Task SendGetRequestAsync(string apiMethodNa // Retry the following call according to the policy. await this.policy.ExecuteAsync(async token => { - this.logger.Debug("Sending request to Url '{1}'.", url); + this.logger.LogDebug("Sending request to Url '{1}'.", url); response = await client.GetAsync(url, cancellation).ConfigureAwait(false); if (response != null) - this.logger.Debug("Response received: {0}", response); + this.logger.LogDebug("Response received: {0}", response); }, cancellation); } catch (OperationCanceledException) { - this.logger.Debug("Operation canceled."); - this.logger.Trace("(-)[CANCELLED]:null"); + this.logger.LogDebug("Operation canceled."); + this.logger.LogTrace("(-)[CANCELLED]:null"); return null; } catch (HttpRequestException ex) { - this.logger.Error("Target node is not ready to receive API calls at this time ({0})", this.EndpointUrl); - this.logger.Debug("Failed to send a message to '{0}'. Exception: '{1}'.", url, ex); + this.logger.LogError("Target node is not ready to receive API calls at this time ({0})", this.EndpointUrl); + this.logger.LogDebug("Failed to send a message to '{0}'. Exception: '{1}'.", url, ex); return new HttpResponseMessage() { ReasonPhrase = ex.Message, StatusCode = HttpStatusCode.InternalServerError }; } } - this.logger.Trace("(-)[SUCCESS]"); + this.logger.LogTrace("(-)[SUCCESS]"); return response; } protected virtual void OnRetry(Exception exception, TimeSpan delay) { - this.logger.Debug("Exception while calling API method: {0}. Retrying...", exception.ToString()); + this.logger.LogDebug("Exception while calling API method: {0}. Retrying...", exception.ToString()); } } diff --git a/src/Stratis.Bitcoin/EventBus/FullNodeEvent.cs b/src/Stratis.Bitcoin/EventBus/FullNodeEvent.cs index 493fe861a9..3a38e994ac 100644 --- a/src/Stratis.Bitcoin/EventBus/FullNodeEvent.cs +++ b/src/Stratis.Bitcoin/EventBus/FullNodeEvent.cs @@ -10,6 +10,7 @@ public sealed class FullNodeEvent : EventBase { public string Message { get; set; } + public string State { get; set; } } } diff --git a/src/Stratis.Bitcoin/EventBus/InMemoryEventBus.cs b/src/Stratis.Bitcoin/EventBus/InMemoryEventBus.cs index e26aba30e2..90eda81a08 100644 --- a/src/Stratis.Bitcoin/EventBus/InMemoryEventBus.cs +++ b/src/Stratis.Bitcoin/EventBus/InMemoryEventBus.cs @@ -28,9 +28,13 @@ public class InMemoryEventBus : IEventBus /// private readonly object subscriptionsLock = new object(); +#pragma warning disable SA1648 + /// private readonly InMemoryEventBusPerformanceCounter performanceCounter; +#pragma warning restore SA1648 + /// /// Initializes a new instance of the class. /// diff --git a/src/Stratis.Bitcoin/OpReturnDataReader.cs b/src/Stratis.Bitcoin/OpReturnDataReader.cs index 9f7f879c24..92b851ca1a 100644 --- a/src/Stratis.Bitcoin/OpReturnDataReader.cs +++ b/src/Stratis.Bitcoin/OpReturnDataReader.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using TracerAttributes; namespace Stratis.Bitcoin @@ -108,7 +109,7 @@ private string TryConvertValidOpReturnDataToAddress(byte[] data) } catch (Exception ex) { - this.logger.Debug("Address {destination} could not be converted to a valid address. Reason {message}.", destination, ex.Message); + this.logger.LogDebug("Address {destination} could not be converted to a valid address. Reason {message}.", destination, ex.Message); return null; } } @@ -123,7 +124,7 @@ private string TryConvertValidOpReturnDataToHash(byte[] data) } catch (Exception ex) { - this.logger.Debug("Candidate hash {data} could not be converted to a valid uint256. Reason {message}.", data, ex.Message); + this.logger.LogDebug("Candidate hash {data} could not be converted to a valid uint256. Reason {message}.", data, ex.Message); return null; } } diff --git a/src/Stratis.Bitcoin/P2P/Peer/NetworkPeerConnection.cs b/src/Stratis.Bitcoin/P2P/Peer/NetworkPeerConnection.cs index 971be97d6a..cc1be9b429 100644 --- a/src/Stratis.Bitcoin/P2P/Peer/NetworkPeerConnection.cs +++ b/src/Stratis.Bitcoin/P2P/Peer/NetworkPeerConnection.cs @@ -155,7 +155,7 @@ private async Task ReceiveMessagesAsync() } catch (Exception ex) when (ex is IOException || ex is OperationCanceledException || ex is ObjectDisposedException) { - this.logger.LogDebug("The node stopped receiving messages, exception: {1}", ex.ToString()); + this.logger.LogDebug("The node stopped receiving messages, exception: {0}", ex.ToString()); this.peer.Disconnect("The node stopped receiving messages.", ex); } catch (Exception ex) @@ -468,7 +468,7 @@ private async Task ReadMagicAsync(byte[] magic, CancellationToken cancellation) message = Message.ReadNext(memoryStream, this.network, protocolVersion, cancellation, this.payloadProvider, out PerformanceCounter counter); } - return(message, rawMessage.Length); + return (message, rawMessage.Length); } /// diff --git a/src/Stratis.Bitcoin/P2P/Protocol/Behaviors/EnforcePeerVersionCheckBehavior.cs b/src/Stratis.Bitcoin/P2P/Protocol/Behaviors/EnforcePeerVersionCheckBehavior.cs index ee4a5a14ca..5ba4193c8a 100644 --- a/src/Stratis.Bitcoin/P2P/Protocol/Behaviors/EnforcePeerVersionCheckBehavior.cs +++ b/src/Stratis.Bitcoin/P2P/Protocol/Behaviors/EnforcePeerVersionCheckBehavior.cs @@ -16,25 +16,25 @@ namespace Stratis.Bitcoin.P2P.Protocol.Behaviors public class EnforcePeerVersionCheckBehavior : NetworkPeerBehavior { /// An indexer that provides methods to query the best chain (the chain that is validated by the full consensus rules) - protected readonly ChainIndexer ChainIndexer; + protected ChainIndexer ChainIndexer { get; private set; } /// User defined node settings. - protected readonly NodeSettings NodeSettings; + protected NodeSettings NodeSettings { get; private set; } /// Specification of the network the node runs on - regtest/testnet/mainnet. - protected readonly Network Network; + protected Network Network { get; private set; } /// Logger factory used while cloning the object. - protected readonly ILoggerFactory LoggerFactory; + protected ILoggerFactory LoggerFactory { get; private set; } /// Instance logger. - protected readonly ILogger Logger; + protected ILogger Logger { get; private set; } /// /// Set to true if the attached peer callbacks have been registered and they should be unregistered, /// false if the callbacks are not registered. /// - protected bool CallbacksRegistered; + protected bool CallbacksRegistered { get; private set; } /// /// Initializes an instance of the object for outbound network peers. diff --git a/src/Stratis.Bitcoin/P2P/Protocol/Payloads/GetHeadersPayload.cs b/src/Stratis.Bitcoin/P2P/Protocol/Payloads/GetHeadersPayload.cs index d5cec87870..9b543e3d20 100644 --- a/src/Stratis.Bitcoin/P2P/Protocol/Payloads/GetHeadersPayload.cs +++ b/src/Stratis.Bitcoin/P2P/Protocol/Payloads/GetHeadersPayload.cs @@ -18,7 +18,9 @@ public ProtocolVersion Version set => this.version = (uint)value; } +#pragma warning disable SA1401 // Fields must be private protected BlockLocator blockLocator; +#pragma warning restore SA1401 // Fields must be private /// /// Gets a block locator which represents a compact structure of one's chain position which can be used to find @@ -31,7 +33,9 @@ public BlockLocator BlockLocator set => this.blockLocator = value; } +#pragma warning disable SA1401 // Fields must be private protected uint256 hashStop = uint256.Zero; +#pragma warning restore SA1401 // Fields must be private /// /// Gets a hash after which no new headers should be sent withing the same message. diff --git a/src/Stratis.Bitcoin/P2P/Protocol/Payloads/HeadersPayload.cs b/src/Stratis.Bitcoin/P2P/Protocol/Payloads/HeadersPayload.cs index 4d1d6f1ab2..348f165b50 100644 --- a/src/Stratis.Bitcoin/P2P/Protocol/Payloads/HeadersPayload.cs +++ b/src/Stratis.Bitcoin/P2P/Protocol/Payloads/HeadersPayload.cs @@ -14,7 +14,9 @@ public class HeadersPayload : Payload { private class BlockHeaderWithTxCount : IBitcoinSerializable { - internal BlockHeader Header; + private BlockHeader header; + + public BlockHeader Header => this.header; public BlockHeaderWithTxCount() { @@ -22,13 +24,13 @@ public BlockHeaderWithTxCount() public BlockHeaderWithTxCount(BlockHeader header) { - this.Header = header; + this.header = header; } [NoTrace] public void ReadWrite(BitcoinStream stream) { - stream.ReadWrite(ref this.Header); + stream.ReadWrite(ref this.header); var txCount = new VarInt(0); stream.ReadWrite(ref txCount); diff --git a/src/Stratis.Bitcoin/Properties/AssemblyInfo.cs b/src/Stratis.Bitcoin/Properties/AssemblyInfo.cs index d34838694d..1bbcd41b73 100644 --- a/src/Stratis.Bitcoin/Properties/AssemblyInfo.cs +++ b/src/Stratis.Bitcoin/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Stratis Group Ltd.")] [assembly: AssemblyProduct("Stratis Full Node")] -[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,6 +32,6 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.1.1")] -[assembly: AssemblyFileVersion("1.1.1.1")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.0.0")] [assembly: InternalsVisibleTo("Stratis.Bitcoin.Tests")] \ No newline at end of file diff --git a/src/Stratis.Bitcoin/Stratis.Bitcoin.csproj b/src/Stratis.Bitcoin/Stratis.Bitcoin.csproj index cdae41486f..7d9e8abb3c 100644 --- a/src/Stratis.Bitcoin/Stratis.Bitcoin.csproj +++ b/src/Stratis.Bitcoin/Stratis.Bitcoin.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False ..\Stratis.ruleset Stratis Group Ltd. diff --git a/src/Stratis.Bitcoin/Utilities/DBreezeRetryOptions.cs b/src/Stratis.Bitcoin/Utilities/DBreezeRetryOptions.cs deleted file mode 100644 index aec1dfc3c3..0000000000 --- a/src/Stratis.Bitcoin/Utilities/DBreezeRetryOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using DBreeze.Exceptions; - -namespace Stratis.Bitcoin.Utilities -{ - public class DBreezeRetryOptions : RetryOptions - { - public DBreezeRetryOptions(RetryStrategyType type = RetryStrategyType.Simple) - : base(1, TimeSpan.FromMilliseconds(100), type, typeof(TableNotOperableException)) - { - } - - public static RetryOptions Default => new DBreezeRetryOptions(); - } -} \ No newline at end of file diff --git a/src/Stratis.Bitcoin/Utilities/HashHeightPair.cs b/src/Stratis.Bitcoin/Utilities/HashHeightPair.cs index b28c259548..f1f98bdcd3 100644 --- a/src/Stratis.Bitcoin/Utilities/HashHeightPair.cs +++ b/src/Stratis.Bitcoin/Utilities/HashHeightPair.cs @@ -89,5 +89,10 @@ public static HashHeightPair Load(byte[] bytes, ConsensusFactory consensusFactor hashHeight.ReadWrite(bytes, consensusFactory); return hashHeight; } + + public override int GetHashCode() + { + return this.hash.GetHashCode() ^ this.height; + } } } diff --git a/src/Stratis.Bitcoin/Utilities/JsonConverters/Serializer.cs b/src/Stratis.Bitcoin/Utilities/JsonConverters/Serializer.cs index 4b29aa5342..f92aec0a2b 100644 --- a/src/Stratis.Bitcoin/Utilities/JsonConverters/Serializer.cs +++ b/src/Stratis.Bitcoin/Utilities/JsonConverters/Serializer.cs @@ -9,7 +9,7 @@ namespace Stratis.Bitcoin.Utilities.JsonConverters /// public class Serializer { - public static void RegisterFrontConverters(JsonSerializerSettings settings, Network network = null) + public static void RegisterFrontConverters(JsonSerializerSettings settings, Network network = null, bool allowOverrideContractResolver = true) { settings.Converters.Add(new MoneyJsonConverter()); settings.Converters.Add(new KeyJsonConverter()); @@ -27,7 +27,8 @@ public static void RegisterFrontConverters(JsonSerializerSettings settings, Netw settings.Converters.Add(new LockTimeJsonConverter()); settings.Converters.Add(new BitcoinStringJsonConverter(network)); - settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + if (allowOverrideContractResolver) + settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } public static T ToObject(string data, Network network = null) diff --git a/src/Stratis.Bitcoin/Utilities/NodeStats.cs b/src/Stratis.Bitcoin/Utilities/NodeStats.cs index 6346678e68..646502d732 100644 --- a/src/Stratis.Bitcoin/Utilities/NodeStats.cs +++ b/src/Stratis.Bitcoin/Utilities/NodeStats.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using NLog; +using Microsoft.Extensions.Logging; using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Interfaces; @@ -14,7 +14,6 @@ namespace Stratis.Bitcoin.Utilities { public interface INodeStats { - /// /// A flag indicating whether or not to display bench stats on the console output. /// @@ -126,11 +125,11 @@ public string GetStats() } catch (OperationCanceledException) { - this.logger.Warn("{0} failed to provide inline statistics after {1} seconds, please investigate...", inlineStatItem.ComponentName, ComponentStatsWaitSeconds); + this.logger.LogWarning("{0} failed to provide inline statistics after {1} seconds, please investigate...", inlineStatItem.ComponentName, ComponentStatsWaitSeconds); } catch (Exception ex) { - this.logger.Warn("{0} failed to provide inline statistics: {1}", inlineStatItem.ComponentName, ex.ToString()); + this.logger.LogWarning("{0} failed to provide inline statistics: {1}", inlineStatItem.ComponentName, ex.ToString()); } }); @@ -155,11 +154,11 @@ public string GetStats() } catch (OperationCanceledException) { - this.logger.Warn("{0} failed to provide statistics after {1} seconds, please investigate...", componentStatItem.ComponentName, ComponentStatsWaitSeconds); + this.logger.LogWarning("{0} failed to provide statistics after {1} seconds, please investigate...", componentStatItem.ComponentName, ComponentStatsWaitSeconds); } catch (Exception ex) { - this.logger.Warn("{0} failed to provide statistics: {1}", componentStatItem.ComponentName, ex.ToString()); + this.logger.LogWarning("{0} failed to provide statistics: {1}", componentStatItem.ComponentName, ex.ToString()); } }); diff --git a/src/Stratis.Bitcoin/Utilities/RetryOptions.cs b/src/Stratis.Bitcoin/Utilities/RetryOptions.cs index 0cc8eee005..a0012dbc59 100644 --- a/src/Stratis.Bitcoin/Utilities/RetryOptions.cs +++ b/src/Stratis.Bitcoin/Utilities/RetryOptions.cs @@ -38,8 +38,6 @@ public RetryOptions(short retryCount, TimeSpan delay, RetryStrategyType type = R this.Type = type; } - public static RetryOptions Default => new RetryOptions(); - public RetryStrategyType Type { get; } public short RetryCount { get; } diff --git a/src/Stratis.Bitcoin/Utilities/Stopwatch.cs b/src/Stratis.Bitcoin/Utilities/Stopwatch.cs index be0128cee4..3bb5cfec61 100644 --- a/src/Stratis.Bitcoin/Utilities/Stopwatch.cs +++ b/src/Stratis.Bitcoin/Utilities/Stopwatch.cs @@ -25,7 +25,7 @@ namespace Stratis.Bitcoin.Utilities public class StopwatchDisposable : IDisposable { /// Stopwatch to measure elapsed ticks of the code block. - public readonly Stopwatch watch; + private readonly Stopwatch watch; /// /// Action to execute when the measurement is done. diff --git a/src/Stratis.CirrusD/Properties/launchSettings.json b/src/Stratis.CirrusD/Properties/launchSettings.json index 055d83c539..ffc6738c73 100644 --- a/src/Stratis.CirrusD/Properties/launchSettings.json +++ b/src/Stratis.CirrusD/Properties/launchSettings.json @@ -1,4 +1,4 @@ -{ +{ "profiles": { "Stratis.CirrusD": { "commandName": "Project" diff --git a/src/Stratis.CirrusD/Stratis.CirrusD.csproj b/src/Stratis.CirrusD/Stratis.CirrusD.csproj index 3e7e1694e9..ca43f19481 100644 --- a/src/Stratis.CirrusD/Stratis.CirrusD.csproj +++ b/src/Stratis.CirrusD/Stratis.CirrusD.csproj @@ -3,7 +3,7 @@ Exe netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. Stratis Group Ltd. diff --git a/src/Stratis.CirrusDnsD/Stratis.CirrusDnsD.csproj b/src/Stratis.CirrusDnsD/Stratis.CirrusDnsD.csproj index 58cd63e2eb..7d124bc800 100644 --- a/src/Stratis.CirrusDnsD/Stratis.CirrusDnsD.csproj +++ b/src/Stratis.CirrusDnsD/Stratis.CirrusDnsD.csproj @@ -17,7 +17,7 @@ latest Stratis Group Ltd. - 1.1.1.1 + 1.2.0.0 diff --git a/src/Stratis.CirrusMinerD/Program.cs b/src/Stratis.CirrusMinerD/Program.cs index d17b33cbd9..7c2be7789a 100644 --- a/src/Stratis.CirrusMinerD/Program.cs +++ b/src/Stratis.CirrusMinerD/Program.cs @@ -148,6 +148,8 @@ private static IFullNode BuildDevCirrusMiningNode(string[] args) /// /// Returns a standard Stratis node. Just like StratisD. /// + /// The command-line arguments. + /// See . private static IFullNode BuildStraxNode(string[] args) { // TODO: Hardcode -addressindex for better user experience diff --git a/src/Stratis.CirrusMinerD/Stratis.CirrusMinerD.csproj b/src/Stratis.CirrusMinerD/Stratis.CirrusMinerD.csproj index 665a8d3ecc..6724c1d22b 100644 --- a/src/Stratis.CirrusMinerD/Stratis.CirrusMinerD.csproj +++ b/src/Stratis.CirrusMinerD/Stratis.CirrusMinerD.csproj @@ -3,7 +3,7 @@ Exe netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. Stratis Group Ltd. diff --git a/src/Stratis.CirrusPegD/Stratis.CirrusPegD.csproj b/src/Stratis.CirrusPegD/Stratis.CirrusPegD.csproj index 7f2d98c280..ab3b0097eb 100644 --- a/src/Stratis.CirrusPegD/Stratis.CirrusPegD.csproj +++ b/src/Stratis.CirrusPegD/Stratis.CirrusPegD.csproj @@ -3,7 +3,7 @@ Exe netcoreapp3.1 - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. diff --git a/src/Stratis.Features.Collateral/CollateralChecker.cs b/src/Stratis.Features.Collateral/CollateralChecker.cs index 03e8daaf43..189daabb64 100644 --- a/src/Stratis.Features.Collateral/CollateralChecker.cs +++ b/src/Stratis.Features.Collateral/CollateralChecker.cs @@ -4,9 +4,10 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Controllers.Models; using Stratis.Bitcoin.EventBus; using Stratis.Bitcoin.Features.BlockStore.AddressIndexing; @@ -27,6 +28,7 @@ public interface ICollateralChecker : IDisposable /// Checks if given federation member fulfills the collateral requirement. /// The federation member whose collateral will be checked. /// Counter chain height at which collateral should be checked. + /// True if the collateral requirement is fulfilled and false otherwise. bool CheckCollateral(IFederationMember federationMember, int heightToCheckAt); int GetCounterChainConsensusHeight(); @@ -92,7 +94,7 @@ public async Task InitializeAsync() foreach (CollateralFederationMember federationMember in this.federationManager.GetFederationMembers() .Cast().Where(x => x.CollateralAmount != null && x.CollateralAmount > 0)) { - this.logger.Info("Initializing federation member {0} with amount {1}.", federationMember.CollateralMainchainAddress, federationMember.CollateralAmount); + this.logger.LogInformation("Initializing federation member {0} with amount {1}.", federationMember.CollateralMainchainAddress, federationMember.CollateralAmount); this.balancesDataByAddress.Add(federationMember.CollateralMainchainAddress, null); } @@ -103,7 +105,7 @@ public async Task InitializeAsync() if (this.collateralUpdated) break; - this.logger.Warn("Node initialization will not continue until the gateway node responds."); + this.logger.LogWarning("Node initialization will not continue until the gateway node responds."); await this.DelayCollateralCheckAsync().ConfigureAwait(false); } @@ -123,6 +125,7 @@ public int GetCounterChainConsensusHeight() } /// Continuously updates info about money deposited to fed member's addresses. + /// The asynchronous task. private async Task UpdateCollateralInfoContinuouslyAsync() { while (!this.cancellationSource.IsCancellationRequested) @@ -136,6 +139,7 @@ private async Task UpdateCollateralInfoContinuouslyAsync() /// /// Delay checking the federation member's collateral with seconds. /// + /// The asynchronous task. private async Task DelayCollateralCheckAsync() { try @@ -144,7 +148,7 @@ private async Task DelayCollateralCheckAsync() } catch (OperationCanceledException) { - this.logger.Trace("(-)[CANCELLED]"); + this.logger.LogTrace("(-)[CANCELLED]"); } } @@ -159,33 +163,33 @@ private async Task UpdateCollateralInfoAsync(CancellationToken cancellation) if (addressesToCheck.Count == 0) { - this.logger.Info("None of the federation members has a collateral requirement configured."); + this.logger.LogInformation("None of the federation members has a collateral requirement configured."); } - this.logger.Debug("Addresses to check {0}.", addressesToCheck.Count); + this.logger.LogDebug("Addresses to check {0}.", addressesToCheck.Count); VerboseAddressBalancesResult verboseAddressBalanceResult = await this.blockStoreClient.GetVerboseAddressesBalancesDataAsync(addressesToCheck, cancellation).ConfigureAwait(false); if (verboseAddressBalanceResult == null) { - this.logger.Warn("Failed to update collateral, please ensure that the mainnet gateway node is running and it's API feature is enabled."); - this.logger.Trace("(-)[CALL_RETURNED_NULL_RESULT]:false"); + this.logger.LogWarning("Failed to update collateral, please ensure that the mainnet gateway node is running and it's API feature is enabled."); + this.logger.LogTrace("(-)[CALL_RETURNED_NULL_RESULT]:false"); return; } if (!string.IsNullOrEmpty(verboseAddressBalanceResult.Reason)) { - this.logger.Warn("Failed to fetch address balances from the counter chain node : {0}", verboseAddressBalanceResult.Reason); - this.logger.Trace("(-)[FAILED]:{0}", verboseAddressBalanceResult.Reason); + this.logger.LogWarning("Failed to fetch address balances from the counter chain node : {0}", verboseAddressBalanceResult.Reason); + this.logger.LogTrace("(-)[FAILED]:{0}", verboseAddressBalanceResult.Reason); return; } - this.logger.Debug("Addresses received {0}.", verboseAddressBalanceResult.BalancesData.Count); + this.logger.LogDebug("Addresses received {0}.", verboseAddressBalanceResult.BalancesData.Count); if (verboseAddressBalanceResult.BalancesData.Count != addressesToCheck.Count) { - this.logger.Debug("Expected {0} data entries but received {1}.", addressesToCheck.Count, verboseAddressBalanceResult.BalancesData.Count); - this.logger.Trace("(-)[CALL_RETURNED_INCONSISTENT_DATA]:false"); + this.logger.LogDebug("Expected {0} data entries but received {1}.", addressesToCheck.Count, verboseAddressBalanceResult.BalancesData.Count); + this.logger.LogTrace("(-)[CALL_RETURNED_INCONSISTENT_DATA]:false"); return; } @@ -193,7 +197,7 @@ private async Task UpdateCollateralInfoAsync(CancellationToken cancellation) { foreach (AddressIndexerData balanceData in verboseAddressBalanceResult.BalancesData) { - this.logger.Debug("Updating federation member address {0}.", balanceData.Address); + this.logger.LogDebug("Updating federation member address {0}.", balanceData.Address); this.balancesDataByAddress[balanceData.Address] = balanceData; } @@ -208,7 +212,7 @@ public bool CheckCollateral(IFederationMember federationMember, int heightToChec { if (!this.collateralUpdated) { - this.logger.Debug("(-)[NOT_INITIALIZED]"); + this.logger.LogDebug("(-)[NOT_INITIALIZED]"); throw new Exception("Component is not initialized!"); } @@ -216,7 +220,7 @@ public bool CheckCollateral(IFederationMember federationMember, int heightToChec if (member == null) { - this.logger.Debug("(-)[WRONG_TYPE]"); + this.logger.LogDebug("(-)[WRONG_TYPE]"); throw new ArgumentException($"{nameof(federationMember)} should be of type: {nameof(CollateralFederationMember)}."); } @@ -224,14 +228,14 @@ public bool CheckCollateral(IFederationMember federationMember, int heightToChec { if (heightToCheckAt > this.counterChainConsensusTipHeight - this.maxReorgLength) { - this.logger.Debug("(-)[HEIGHTTOCHECK_HIGHER_THAN_COUNTER_TIP_LESS_MAXREORG]:{0}={1}, {2}={3}:false", nameof(heightToCheckAt), heightToCheckAt, nameof(this.counterChainConsensusTipHeight), this.counterChainConsensusTipHeight); + this.logger.LogDebug("(-)[HEIGHTTOCHECK_HIGHER_THAN_COUNTER_TIP_LESS_MAXREORG]:{0}={1}, {2}={3}:false", nameof(heightToCheckAt), heightToCheckAt, nameof(this.counterChainConsensusTipHeight), this.counterChainConsensusTipHeight); return false; } } if ((member.CollateralAmount == null) || (member.CollateralAmount == 0)) { - this.logger.Debug("(-)[NO_COLLATERAL_REQUIREMENT]:true"); + this.logger.LogDebug("(-)[NO_COLLATERAL_REQUIREMENT]:true"); return true; } @@ -242,13 +246,13 @@ public bool CheckCollateral(IFederationMember federationMember, int heightToChec if (balanceData == null) { // No data. Assume collateral is 0. It's ok if there is no collateral set for that fed member. - this.logger.Debug("(-)[NO_DATA]:{0}={1}", nameof(this.balancesDataByAddress.Count), this.balancesDataByAddress.Count); + this.logger.LogDebug("(-)[NO_DATA]:{0}={1}", nameof(this.balancesDataByAddress.Count), this.balancesDataByAddress.Count); return 0 >= member.CollateralAmount; } long balance = balanceData.BalanceChanges.Where(x => x.BalanceChangedHeight <= heightToCheckAt).CalculateBalance(); - this.logger.Info("Calculated balance for '{0}' at {1} is {2}, collateral requirement is {3}.", member.CollateralMainchainAddress, heightToCheckAt, Money.Satoshis(balance).ToUnit(MoneyUnit.BTC), member.CollateralAmount); + this.logger.LogInformation("Calculated balance for '{0}' at {1} is {2}, collateral requirement is {3}.", member.CollateralMainchainAddress, heightToCheckAt, Money.Satoshis(balance).ToUnit(MoneyUnit.BTC), member.CollateralAmount); return balance >= member.CollateralAmount.Satoshi; } @@ -260,15 +264,15 @@ private void OnFedMemberKicked(FedMemberKicked fedMemberKicked) { if (fedMemberKicked.KickedMember is CollateralFederationMember collateralFederationMember) { - this.logger.Debug("Removing federation member '{0}' with collateral address '{1}'.", collateralFederationMember.PubKey, collateralFederationMember.CollateralMainchainAddress); + this.logger.LogDebug("Removing federation member '{0}' with collateral address '{1}'.", collateralFederationMember.PubKey, collateralFederationMember.CollateralMainchainAddress); if (!string.IsNullOrEmpty(collateralFederationMember.CollateralMainchainAddress)) this.balancesDataByAddress.Remove(collateralFederationMember.CollateralMainchainAddress); else - this.logger.Debug("(-)[NO_COLLATERAL_ADDRESS]:{0}='{1}'", nameof(fedMemberKicked.KickedMember.PubKey), fedMemberKicked.KickedMember.PubKey); + this.logger.LogDebug("(-)[NO_COLLATERAL_ADDRESS]:{0}='{1}'", nameof(fedMemberKicked.KickedMember.PubKey), fedMemberKicked.KickedMember.PubKey); } else { - this.logger.Error("(-)[NOT_A_COLLATERAL_MEMBER]:{0}='{1}'", nameof(fedMemberKicked.KickedMember.PubKey), fedMemberKicked.KickedMember.PubKey); + this.logger.LogError("(-)[NOT_A_COLLATERAL_MEMBER]:{0}='{1}'", nameof(fedMemberKicked.KickedMember.PubKey), fedMemberKicked.KickedMember.PubKey); } } } @@ -281,19 +285,19 @@ private void OnFedMemberAdded(FedMemberAdded fedMemberAdded) { if (string.IsNullOrEmpty(collateralFederationMember.CollateralMainchainAddress)) { - this.logger.Debug("(-)[NO_COLLATERAL_ADDRESS]:{0}='{1}'", nameof(fedMemberAdded.AddedMember.PubKey), fedMemberAdded.AddedMember.PubKey); + this.logger.LogDebug("(-)[NO_COLLATERAL_ADDRESS]:{0}='{1}'", nameof(fedMemberAdded.AddedMember.PubKey), fedMemberAdded.AddedMember.PubKey); return; } if (!this.balancesDataByAddress.ContainsKey(collateralFederationMember.CollateralMainchainAddress)) { - this.logger.Debug("Adding federation member '{0}' with collateral address '{1}'.", collateralFederationMember.PubKey, collateralFederationMember.CollateralMainchainAddress); + this.logger.LogDebug("Adding federation member '{0}' with collateral address '{1}'.", collateralFederationMember.PubKey, collateralFederationMember.CollateralMainchainAddress); this.balancesDataByAddress.Add(collateralFederationMember.CollateralMainchainAddress, null); } } else { - this.logger.Error("(-)[NOT_A_COLLATERAL_MEMBER]:{0}='{1}'", nameof(fedMemberAdded.AddedMember.PubKey), fedMemberAdded.AddedMember.PubKey); + this.logger.LogError("(-)[NOT_A_COLLATERAL_MEMBER]:{0}='{1}'", nameof(fedMemberAdded.AddedMember.PubKey), fedMemberAdded.AddedMember.PubKey); } } } diff --git a/src/Stratis.Features.Collateral/CollateralController.cs b/src/Stratis.Features.Collateral/CollateralController.cs index e3a6db8f64..4e258d889b 100644 --- a/src/Stratis.Features.Collateral/CollateralController.cs +++ b/src/Stratis.Features.Collateral/CollateralController.cs @@ -3,8 +3,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.PoA; using Stratis.Bitcoin.Utilities; using Stratis.Bitcoin.Utilities.JsonErrors; @@ -56,7 +57,7 @@ public async Task JoinFederationAsync([FromBody] JoinFederationRe // Checks that the request is valid. if (!this.ModelState.IsValid) { - this.logger.Trace("(-)[MODEL_STATE_INVALID]"); + this.logger.LogTrace("(-)[MODEL_STATE_INVALID]"); return ModelStateErrors.BuildErrorResponse(this.ModelState); } @@ -72,13 +73,13 @@ public async Task JoinFederationAsync([FromBody] JoinFederationRe MinerPublicKey = minerPubKey.ToHex() }; - this.logger.Trace("(-):'{0}'", model); + this.logger.LogTrace("(-):'{0}'", model); return this.Json(model); } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); - this.logger.Trace("(-)[ERROR]"); + this.logger.LogError("Exception occurred: {0}", e.ToString()); + this.logger.LogTrace("(-)[ERROR]"); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } diff --git a/src/Stratis.Features.Collateral/CollateralPoAMiner.cs b/src/Stratis.Features.Collateral/CollateralPoAMiner.cs index 72bb32362f..bf7f59b33f 100644 --- a/src/Stratis.Features.Collateral/CollateralPoAMiner.cs +++ b/src/Stratis.Features.Collateral/CollateralPoAMiner.cs @@ -8,6 +8,7 @@ using Stratis.Bitcoin.Connection; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Consensus.Validators; +using Stratis.Bitcoin.EventBus.CoreEvents; using Stratis.Bitcoin.Features.BlockStore.AddressIndexing; using Stratis.Bitcoin.Features.Miner; using Stratis.Bitcoin.Features.PoA; @@ -127,25 +128,21 @@ private void OnBeforeFillBlockTemplate() pendingAddFederationMemberPolls = pendingAddFederationMemberPolls.Where(p => !p.PubKeysHexVotedInFavor.Any(v => v.PubKey == this.federationManager.CurrentFederationKey.PubKey.ToString())).ToList(); if (!pendingAddFederationMemberPolls.Any()) + { + this.logger.LogDebug("There are no outstanding add member polls for this node to vote on."); return; - - IFederationMember collateralFederationMember = this.federationManager.GetCurrentFederationMember(); - - var poaConsensusFactory = this.network.Consensus.ConsensusFactory as PoAConsensusFactory; + } foreach (Poll poll in pendingAddFederationMemberPolls) { + this.logger.LogDebug($"Attempting to cast outstanding vote on poll '{poll.Id}'."); + ChainedHeader pollStartHeader = this.chainIndexer.GetHeader(poll.PollStartBlockData.Hash); ChainedHeader votingRequestHeader = pollStartHeader.Previous; - // Already checked? - if (this.joinFederationRequestMonitor.AlreadyChecked(votingRequestHeader.HashBlock)) - continue; - ChainedHeaderBlock blockData = this.consensusManager.GetBlockData(votingRequestHeader.HashBlock); - this.joinFederationRequestMonitor.OnBlockConnected(new Bitcoin.EventBus.CoreEvents.BlockConnected( - new ChainedHeaderBlock(blockData.Block, votingRequestHeader))); + this.joinFederationRequestMonitor.OnBlockConnected(new BlockConnected(new ChainedHeaderBlock(blockData.Block, votingRequestHeader))); } return; diff --git a/src/Stratis.Features.Collateral/ConsensusRules/MandatoryCollateralMemberVotingRule.cs b/src/Stratis.Features.Collateral/ConsensusRules/MandatoryCollateralMemberVotingRule.cs index 90dc0cbca5..6b3305841a 100644 --- a/src/Stratis.Features.Collateral/ConsensusRules/MandatoryCollateralMemberVotingRule.cs +++ b/src/Stratis.Features.Collateral/ConsensusRules/MandatoryCollateralMemberVotingRule.cs @@ -30,6 +30,8 @@ public override void Initialize() } /// Checks that whomever mined this block is participating in any pending polls to vote-in new federation members. + /// See . + /// The asynchronous task. public override Task RunAsync(RuleContext context) { // "AddFederationMember" polls, that were started at or before this height, that are still pending, which this node has voted in favor of. diff --git a/src/Stratis.Features.Collateral/JoinFederationRequestMonitor.cs b/src/Stratis.Features.Collateral/JoinFederationRequestMonitor.cs index 745d4d336f..d48b552e9f 100644 --- a/src/Stratis.Features.Collateral/JoinFederationRequestMonitor.cs +++ b/src/Stratis.Features.Collateral/JoinFederationRequestMonitor.cs @@ -2,15 +2,16 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; -using Stratis.Bitcoin.EventBus; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.EventBus.CoreEvents; using Stratis.Bitcoin.Features.PoA; using Stratis.Bitcoin.Features.PoA.Voting; using Stratis.Bitcoin.PoA.Features.Voting; using Stratis.Bitcoin.Signals; using Stratis.Features.Collateral.CounterChain; +using Stratis.Features.PoA.Voting; namespace Stratis.Features.Collateral { @@ -18,12 +19,10 @@ public class JoinFederationRequestMonitor { private readonly ILogger logger; private readonly ISignals signals; - private SubscriptionToken blockConnectedToken; private readonly VotingManager votingManager; private readonly Network network; private readonly Network counterChainNetwork; private readonly IFederationManager federationManager; - private readonly HashSet pollsCheckedWithJoinFederationRequestMonitor; public JoinFederationRequestMonitor(VotingManager votingManager, Network network, CounterChainNetworkWrapper counterChainNetworkWrapper, IFederationManager federationManager, ISignals signals) { @@ -33,14 +32,11 @@ public JoinFederationRequestMonitor(VotingManager votingManager, Network network this.network = network; this.counterChainNetwork = counterChainNetworkWrapper.CounterChainNetwork; this.federationManager = federationManager; - this.pollsCheckedWithJoinFederationRequestMonitor = new HashSet(); } - public bool AlreadyChecked(uint256 hash) => this.pollsCheckedWithJoinFederationRequestMonitor.Contains(hash); - public Task InitializeAsync() { - this.blockConnectedToken = this.signals.Subscribe(this.OnBlockConnected); + this.signals.Subscribe(this.OnBlockConnected); return Task.CompletedTask; } @@ -56,8 +52,6 @@ public void OnBlockConnected(BlockConnected blockConnectedData) List modifiedFederation = null; - this.pollsCheckedWithJoinFederationRequestMonitor.Add(blockConnectedData.ConnectedBlock.ChainedHeader.HashBlock); - List transactions = blockConnectedData.ConnectedBlock.Block.Transactions; var encoder = new JoinFederationRequestEncoder(); @@ -78,9 +72,12 @@ public void OnBlockConnected(BlockConnected blockConnectedData) continue; // Only mining federation members vote to include new members. - modifiedFederation = modifiedFederation ?? this.votingManager.GetModifiedFederation(blockConnectedData.ConnectedBlock.ChainedHeader); + modifiedFederation ??= this.votingManager.GetModifiedFederation(blockConnectedData.ConnectedBlock.ChainedHeader); if (!modifiedFederation.Any(m => m.PubKey == this.federationManager.CurrentFederationKey.PubKey)) + { + this.logger.LogDebug($"Ignoring as member '{this.federationManager.CurrentFederationKey.PubKey}' is not part of the federation at block '{blockConnectedData.ConnectedBlock.ChainedHeader}'."); return; + } // Check if the collateral amount is valid. decimal collateralAmount = request.CollateralAmount.ToDecimal(MoneyUnit.BTC); @@ -88,42 +85,34 @@ public void OnBlockConnected(BlockConnected blockConnectedData) if (collateralAmount != expectedCollateralAmount) { - this.logger.Debug("Ignoring voting collateral amount '{0}', when expecting '{1}'.", collateralAmount, expectedCollateralAmount); + this.logger.LogDebug("Ignoring voting collateral amount '{0}', when expecting '{1}'.", collateralAmount, expectedCollateralAmount); continue; } // Fill in the request.removalEventId (if any). - Script collateralScript = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(request.CollateralMainchainAddress); - - var collateralFederationMember = new CollateralFederationMember(request.PubKey, false, request.CollateralAmount, collateralScript.GetDestinationAddress(this.counterChainNetwork).ToString()); - - byte[] federationMemberBytes = consensusFactory.SerializeFederationMember(collateralFederationMember); + byte[] federationMemberBytes = JoinFederationRequestService.GetFederationMemberBytes(request, this.network, this.counterChainNetwork); // Nothing to do if already voted. if (this.votingManager.AlreadyVotingFor(VoteKey.AddFederationMember, federationMemberBytes)) { - this.logger.Debug("Skipping because already voted for adding '{0}'.", request.PubKey.ToHex()); - + this.logger.LogDebug("Skipping because already voted for adding '{0}'.", request.PubKey.ToHex()); continue; } // Populate the RemovalEventId. - Poll poll = this.votingManager.GetApprovedPolls().FirstOrDefault(x => x.IsExecuted && - x.VotingData.Key == VoteKey.KickFederationMember && x.VotingData.Data.SequenceEqual(federationMemberBytes)); - - request.RemovalEventId = (poll == null) ? Guid.Empty : new Guid(poll.PollExecutedBlockData.Hash.ToBytes().TakeLast(16).ToArray()); + JoinFederationRequestService.SetLastRemovalEventId(request, federationMemberBytes, this.votingManager); // Check the signature. PubKey key = PubKey.RecoverFromMessage(request.SignatureMessage, request.Signature); if (key.Hash != request.CollateralMainchainAddress) { - this.logger.Debug("Invalid collateral address validation signature for joining federation via transaction '{0}'", tx.GetHash()); + this.logger.LogDebug("Invalid collateral address validation signature for joining federation via transaction '{0}'", tx.GetHash()); continue; } // Vote to add the member. - this.logger.Debug("Voting to add federation member '{0}'.", request.PubKey.ToHex()); + this.logger.LogDebug("Voting to add federation member '{0}'.", request.PubKey.ToHex()); this.votingManager.ScheduleVote(new VotingData() { @@ -134,7 +123,7 @@ public void OnBlockConnected(BlockConnected blockConnectedData) } catch (Exception err) { - this.logger.Error(err.Message); + this.logger.LogError(err.Message); } } } diff --git a/src/Stratis.Features.Collateral/Stratis.Features.Collateral.csproj b/src/Stratis.Features.Collateral/Stratis.Features.Collateral.csproj index 8ff29cbc2b..ae9a2f6b16 100644 --- a/src/Stratis.Features.Collateral/Stratis.Features.Collateral.csproj +++ b/src/Stratis.Features.Collateral/Stratis.Features.Collateral.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 4.0.4.0 + 4.0.5.0 Stratis Group Ltd. Stratis.Features.Collateral diff --git a/src/Stratis.Features.Diagnostic/Controllers/DiagnosticController.cs b/src/Stratis.Features.Diagnostic/Controllers/DiagnosticController.cs index e658f900e2..81d77167cd 100644 --- a/src/Stratis.Features.Diagnostic/Controllers/DiagnosticController.cs +++ b/src/Stratis.Features.Diagnostic/Controllers/DiagnosticController.cs @@ -23,6 +23,12 @@ public class DiagnosticController : FeatureController { private readonly PeerStatisticsCollector peerStatisticsCollector; + /// + /// Constructs an instance of this class. + /// + /// See . + /// See . + /// See . public DiagnosticController(PeerStatisticsCollector peerStatisticsCollector, IConnectionManager connectionManager, IConsensusManager consensusManager) : base(connectionManager: connectionManager, consensusManager: consensusManager) { @@ -30,8 +36,9 @@ public DiagnosticController(PeerStatisticsCollector peerStatisticsCollector, ICo } /// - /// Returns the connected peers with some information + /// Returns the connected peers with some information. /// + /// The connected peers with some information. /// Returns connected peers information /// Unexpected exception occurred [HttpGet] diff --git a/src/Stratis.Features.Diagnostic/Controllers/Models/PeerStatisticsModel.cs b/src/Stratis.Features.Diagnostic/Controllers/Models/PeerStatisticsModel.cs index 9b5634ba9f..53a852756a 100644 --- a/src/Stratis.Features.Diagnostic/Controllers/Models/PeerStatisticsModel.cs +++ b/src/Stratis.Features.Diagnostic/Controllers/Models/PeerStatisticsModel.cs @@ -4,24 +4,56 @@ namespace Stratis.Features.Diagnostic.Controllers.Models { + /// + /// Records perr statistics. + /// public class PeerStatisticsModel { + /// + /// The peer endpoint. + /// public string PeerEndPoint { get; set; } + /// + /// Indicates whether the peer is connected. + /// public bool Connected { get; set; } + /// + /// Indicates whether this is an inbound peer. + /// public bool Inbound { get; set; } + /// + /// The number of bytes sent to the peer. + /// public long BytesSent { get; set; } + /// + /// The number of bytes received from the peer. + /// public long BytesReceived { get; set; } + /// + /// The number of messages received from the peer. + /// public int ReceivedMessages { get; set; } + /// + /// The number of messages sent to the peer. + /// public int SentMessages { get; set; } + /// + /// The list of latest events. + /// public List LatestEvents { get; set; } + /// + /// Class instance constructor. + /// + /// See . + /// Indicates whether the peer is connected. public PeerStatisticsModel(PeerStatistics peer, bool connected) { this.LatestEvents = new List(); diff --git a/src/Stratis.Features.Diagnostic/DiagnosticFeature.cs b/src/Stratis.Features.Diagnostic/DiagnosticFeature.cs index 13e7359f00..65f52e6bcb 100644 --- a/src/Stratis.Features.Diagnostic/DiagnosticFeature.cs +++ b/src/Stratis.Features.Diagnostic/DiagnosticFeature.cs @@ -24,6 +24,12 @@ public class DiagnosticFeature : FullNodeFeature private readonly DiagnosticSettings diagnosticSettings; private readonly PeerStatisticsCollector peerStatisticsCollector; + /// + /// The class instance constructor. + /// + /// See . + /// See . + /// See . public DiagnosticFeature(ISignals signals, DiagnosticSettings diagnosticSettings, PeerStatisticsCollector peerStatisticsCollector) { this.signals = Guard.NotNull(signals, nameof(signals)); @@ -31,6 +37,10 @@ public DiagnosticFeature(ISignals signals, DiagnosticSettings diagnosticSettings this.peerStatisticsCollector = Guard.NotNull(peerStatisticsCollector, nameof(peerStatisticsCollector)); } + /// + /// Initializes the instance. + /// + /// The asynchronous task. public override Task InitializeAsync() { this.peerStatisticsCollector.Initialize(); @@ -46,14 +56,25 @@ public static void PrintHelp(Network network) DiagnosticSettings.PrintHelp(network); } + /// + /// Disposes the instance. + /// public override void Dispose() { this.peerStatisticsCollector.Dispose(); } } + /// + /// Extension for adding the feature to the node. + /// public static class DiagnosticFeatureExtension { + /// + /// Adds the feature to the node. + /// + /// See . + /// The . public static IFullNodeBuilder UseDiagnosticFeature(this IFullNodeBuilder fullNodeBuilder) { LoggingConfiguration.RegisterFeatureNamespace("diagnostic"); diff --git a/src/Stratis.Features.Diagnostic/PeerDiagnostic/PeerStatistics.cs b/src/Stratis.Features.Diagnostic/PeerDiagnostic/PeerStatistics.cs index 93c0fb1cc3..e2816f54a7 100644 --- a/src/Stratis.Features.Diagnostic/PeerDiagnostic/PeerStatistics.cs +++ b/src/Stratis.Features.Diagnostic/PeerDiagnostic/PeerStatistics.cs @@ -10,16 +10,34 @@ namespace Stratis.Features.Diagnostic.PeerDiagnostic /// public class PeerStatistics { + /// + /// The peer endpoint. + /// public IPEndPoint PeerEndPoint { get; set; } + /// + /// Indicates whether this is an inbound peer. + /// public bool Inbound { get; set; } + /// + /// The number of bytes sent to the peer. + /// public long BytesSent { get; set; } + /// + /// The number of bytes received from the peer. + /// public long BytesReceived { get; set; } + /// + /// The number of messages received from the peer. + /// public int ReceivedMessages { get; set; } + /// + /// The number of messages sent to the peer. + /// public int SentMessages { get; set; } /// @@ -41,6 +59,10 @@ public PeerStatistics(int maxLoggedEvents, IPEndPoint peerEndPoint) this.LatestEvents = new ConcurrentFixedSizeQueue(maxLoggedEvents); } + /// + /// Logs an event. + /// + /// The text to log. public void LogEvent(string loggedText) { this.LatestEvents.Enqueue($"[{DateTime.UtcNow}] {loggedText}"); diff --git a/src/Stratis.Features.Diagnostic/PeerDiagnostic/PeerStatisticsCollector.cs b/src/Stratis.Features.Diagnostic/PeerDiagnostic/PeerStatisticsCollector.cs index 91465c42eb..ba49e0abda 100644 --- a/src/Stratis.Features.Diagnostic/PeerDiagnostic/PeerStatisticsCollector.cs +++ b/src/Stratis.Features.Diagnostic/PeerDiagnostic/PeerStatisticsCollector.cs @@ -37,6 +37,13 @@ public sealed class PeerStatisticsCollector : IDisposable /// Holds a list of event subscriptions. private readonly List eventSubscriptions; + /// + /// Class instance constructor. + /// + /// See . + /// See . + /// See . + /// See . public PeerStatisticsCollector(IAsyncProvider asyncProvider, ISignals signals, DiagnosticSettings diagnosticSettings, INodeLifetime nodeLifetime) { this.asyncProvider = asyncProvider; @@ -50,6 +57,9 @@ public PeerStatisticsCollector(IAsyncProvider asyncProvider, ISignals signals, D this.lockStartStopCollecting = new object(); } + /// + /// Initializes a class instance. + /// public void Initialize() { this.Enabled = this.diagnosticSettings.PeersStatisticsCollectorEnabled; @@ -116,7 +126,9 @@ private PeerStatistics GetPeerStatistics(IPEndPoint peerEndPoint) return statistics; } - + /// + /// Starts collecting peer statistics. + /// public void StartCollecting() { lock (this.lockStartStopCollecting) @@ -138,6 +150,9 @@ public void StartCollecting() } } + /// + /// Stops collecting peer statistics. + /// public void StopCollecting() { lock (this.lockStartStopCollecting) @@ -155,11 +170,18 @@ public void StopCollecting() } } + /// + /// Gets peer statistics. + /// + /// A list of entries. internal List GetStatistics() { return this.peersStatistics.Values.ToList(); } + /// + /// Disposes a class instance. + /// public void Dispose() { this.StopCollecting(); diff --git a/src/Stratis.Features.Diagnostic/Stratis.Features.Diagnostic.csproj b/src/Stratis.Features.Diagnostic/Stratis.Features.Diagnostic.csproj index 58fc742309..4b53ffb476 100644 --- a/src/Stratis.Features.Diagnostic/Stratis.Features.Diagnostic.csproj +++ b/src/Stratis.Features.Diagnostic/Stratis.Features.Diagnostic.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 ..\None.ruleset true - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. diff --git a/src/Stratis.Features.Diagnostic/Utils/ConcurrentFixedSizeQueue.cs b/src/Stratis.Features.Diagnostic/Utils/ConcurrentFixedSizeQueue.cs index 4d4a42eeba..2a08696288 100644 --- a/src/Stratis.Features.Diagnostic/Utils/ConcurrentFixedSizeQueue.cs +++ b/src/Stratis.Features.Diagnostic/Utils/ConcurrentFixedSizeQueue.cs @@ -19,12 +19,27 @@ public class ConcurrentFixedSizeQueue : IReadOnlyCollection, ICollection private readonly ConcurrentQueue concurrentQueue; private readonly int maxSize; + /// + /// Number of items in the queue. + /// public int Count => this.concurrentQueue.Count; + /// + /// Indicates whether the queue is empty. + /// public bool IsEmpty => this.concurrentQueue.IsEmpty; + /// + /// Class instance constructor. + /// + /// Initial maximum size. public ConcurrentFixedSizeQueue(int maxSize) : this(Array.Empty(), maxSize) { } + /// + /// Class instance constructor. + /// + /// The intial collection. + /// Initial maximum size. public ConcurrentFixedSizeQueue(IEnumerable initialCollection, int maxSize) { if (initialCollection == null) @@ -36,6 +51,10 @@ public ConcurrentFixedSizeQueue(IEnumerable initialCollection, int maxSize) this.maxSize = maxSize; } + /// + /// Adds an item to the queue. + /// + /// The item to add to the queue. public void Enqueue(T item) { this.concurrentQueue.Enqueue(item); @@ -47,30 +66,81 @@ public void Enqueue(T item) } } + /// + /// Tries to return an object from the begginning of the queue without removing it. + /// + /// The object from the beginning of the queue (if any). public void TryPeek(out T result) => this.concurrentQueue.TryPeek(out result); + /// + /// Tries to remove an object from the beginning of the queue (if any). + /// + /// The object from the beginning of the queue (if any). + /// True if an element was removed and false otherwise. public bool TryDequeue(out T result) => this.concurrentQueue.TryDequeue(out result); + /// + /// Copies the queue elements to an array starting at the given index. + /// + /// The array to copy the elements to. + /// The index in the array to start copying to. public void CopyTo(T[] array, int index) => this.concurrentQueue.CopyTo(array, index); + /// + /// Converts the queue elements to an array. + /// + /// The array containing the queue elements. public T[] ToArray() => this.concurrentQueue.ToArray(); + /// + /// Gets an enumerator for iterating the queue elements. + /// + /// The enumerator for iterating the queue elements. public IEnumerator GetEnumerator() => this.concurrentQueue.GetEnumerator(); + /// + /// Gets an enumerator for iterating the queue elements. + /// + /// The enumerator for iterating the queue elements. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #region Explicit ICollection implementations. + + /// + /// Copies the queue elements to an array starting at the given index. + /// + /// The array to copy the elements to. + /// The index in the array to start copying to. void ICollection.CopyTo(Array array, int index) => ((ICollection)this.concurrentQueue).CopyTo(array, index); + /// + /// Gets an object that can be used to synchronize access to the collection. + /// object ICollection.SyncRoot => ((ICollection)this.concurrentQueue).SyncRoot; + /// + /// Gets a value indicating whether access to the collection is synchronized (thread-safe). + /// bool ICollection.IsSynchronized => ((ICollection)this.concurrentQueue).IsSynchronized; #endregion + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. public override int GetHashCode() => this.concurrentQueue.GetHashCode(); + /// + /// Determines if the specified object is equal to the current object. + /// + /// The specified object. + /// true if the specified object is equal to the current object; otherwise, false. public override bool Equals(object obj) => this.concurrentQueue.Equals(obj); + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. public override string ToString() => this.concurrentQueue.ToString(); } } diff --git a/src/Stratis.Features.Diagnostic/Utils/ReflectionExtensionsion.cs b/src/Stratis.Features.Diagnostic/Utils/ReflectionExtensionsion.cs index b951552c5d..b285b4722a 100644 --- a/src/Stratis.Features.Diagnostic/Utils/ReflectionExtensionsion.cs +++ b/src/Stratis.Features.Diagnostic/Utils/ReflectionExtensionsion.cs @@ -3,6 +3,9 @@ namespace Stratis.Features.Diagnostic.Utils { + /// + /// Provides an extension with methods to access private properties. + /// internal static class ReflectionExtension { /// diff --git a/src/Stratis.Features.FederatedPeg.Tests/CrossChainTransferStoreTests.cs b/src/Stratis.Features.FederatedPeg.Tests/CrossChainTransferStoreTests.cs index a2a22a5fa0..aa24b9026f 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/CrossChainTransferStoreTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/CrossChainTransferStoreTests.cs @@ -101,6 +101,7 @@ public void StartSynchronizesWithWalletAndSurvivesRestart() /// /// Recording deposits when the wallet UTXOs are sufficient succeeds with deterministic transactions. /// + /// The asynchronous task. [Fact] public async Task StoringDepositsWhenWalletBalanceSufficientSucceedsWithDeterministicTransactionsAsync() { @@ -249,6 +250,7 @@ public async void StoringDepositsWhenTargetIsContractFailsWithRejectedTransferAs /// /// Recording deposits when the wallet UTXOs are sufficient succeeds with deterministic transactions. /// + /// The asynchronous task. [Fact] public async Task StoringDepositsWhenWalletBalanceInSufficientSucceedsWithSuspendStatusAsync() { @@ -370,6 +372,7 @@ public async Task StoringDepositsWhenWalletBalanceInSufficientSucceedsWithSuspen /// /// Test that if one transaction is set to suspended then all following transactions will be too to maintain deterministic order. /// + /// The asynchronous task. [Fact] public async Task SetAllAfterSuspendedToSuspendedAsync() { @@ -433,7 +436,8 @@ public async Task SetAllAfterSuspendedToSuspendedAsync() /// /// Tests whether the store merges signatures as expected. - /// + /// The asynchronous task. [Fact] public async Task StoreMergesSignaturesAsExpectedAsync() { @@ -530,6 +534,7 @@ public async Task StoreMergesSignaturesAsExpectedAsync() /// /// Check that partial transactions present in the store cause partial transaction requests made to peers. /// + /// The asynchronous task. [Fact] public async Task StoredPartialTransactionsTriggerSignatureRequestAsync() { @@ -586,7 +591,7 @@ public async Task StoredPartialTransactionsTriggerSignatureRequestAsync() } [Fact(Skip = "Requires main chain user to be running.")] - public async Task DoTest() + public async Task DoTestAsync() { var transactionRequest = new BuildTransactionRequest() { @@ -634,6 +639,7 @@ public async Task DoTest() /// Simulates the behaviour if someone were to come on the network and broadcast their own message /// with bogus information. /// + /// The asynchronous task. [Fact] public async Task AttemptFederationInvalidWithdrawalAsync() { @@ -708,6 +714,7 @@ public async Task AttemptFederationInvalidWithdrawalAsync() /// /// Recording deposits when the wallet UTXOs are sufficient succeeds with deterministic transactions. /// + /// The asynchronous task. [Fact] public async Task StoringDepositsAfterRewindIsPrecededByClearingInvalidTransientsAndSettingNextMatureDepositHeightCorrectlyAsync() { @@ -797,6 +804,7 @@ public async Task StoringDepositsAfterRewindIsPrecededByClearingInvalidTransient /// Test demonstrates what happens when there is a reorg. Specifically, that no FullySigned transactions are maintained, /// even though we previously tried to do so. /// + /// The asynchronous task. [Fact] public async Task ReorgSetsAllInProgressToSuspendedAsync() { @@ -1194,6 +1202,7 @@ private Q Post(string url, T body) /// /// Recording deposits when the target is our multisig is ignored, but a different multisig is allowed. /// + /// The asynchronous task. [Fact] public async Task StoringDepositsWhenTargetIsMultisigIsIgnoredIffOurMultisigAsync() { diff --git a/src/Stratis.Features.FederatedPeg.Tests/Distribution/RewardClaimerTests.cs b/src/Stratis.Features.FederatedPeg.Tests/Distribution/RewardClaimerTests.cs index 750118e66a..3bcc6123e6 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/Distribution/RewardClaimerTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/Distribution/RewardClaimerTests.cs @@ -6,7 +6,6 @@ using Stratis.Bitcoin; using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Consensus; -using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Features.Wallet.Interfaces; using Stratis.Bitcoin.Interfaces; using Stratis.Bitcoin.Networks; diff --git a/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs b/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs index 4a1081aaf6..064bff3a4c 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs @@ -130,6 +130,7 @@ public async Task GetMaturedBlocksReturnsDepositsAsync() /// Returns 0 normal deposits /// Returns 4 faster deposits /// + /// The asynchronous task. [Fact] public async Task RetrieveDeposits_ReturnsDataToAdvanceNextMaturedBlockHeightAsync() { @@ -189,8 +190,9 @@ public async Task RetrieveDeposits_ReturnsDataToAdvanceNextMaturedBlockHeightAsy /// Returns 6 normal deposits /// Returns 4 faster deposits /// + /// The asynchronous task. [Fact] - public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario2() + public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario2_Async() { // Create a "chain" of 30 blocks. this.blocks = ChainedHeadersHelper.CreateConsecutiveHeadersAndBlocks(30, true, this.mainChainNetwork); @@ -247,8 +249,9 @@ public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario2() /// Returns 6 faster deposits /// Returns 5 normal deposits /// + /// The asynchronous task. [Fact] - public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario3() + public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario3_Async() { // Create a "chain" of 30 blocks. this.blocks = ChainedHeadersHelper.CreateConsecutiveHeadersAndBlocks(30, true, this.mainChainNetwork); @@ -297,8 +300,9 @@ public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario3() /// Returns 6 normal deposits /// Returns 0 small deposits /// + /// The asynchronous task. [Fact] - public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario4() + public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario4_Async() { // Create a "chain" of 20 blocks. this.blocks = ChainedHeadersHelper.CreateConsecutiveHeadersAndBlocks(30, true, this.mainChainNetwork); @@ -353,8 +357,9 @@ public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario4() /// Returns 0 normal deposits /// Returns 0 small deposits /// + /// The asynchronous task. [Fact] - public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario5() + public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario5_Async() { // Create a "chain" of 20 blocks. this.blocks = ChainedHeadersHelper.CreateConsecutiveHeadersAndBlocks(20, true, this.mainChainNetwork); @@ -407,8 +412,9 @@ public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario5() /// Returns 0 normal deposits /// Returns 0 large deposits /// + /// The asynchronous task. [Fact] - public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario6() + public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario6_Async() { // Create a "chain" of 20 blocks. this.blocks = ChainedHeadersHelper.CreateConsecutiveHeadersAndBlocks(20, true, this.mainChainNetwork); @@ -462,8 +468,9 @@ public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario6() /// Returns 6 normal deposits /// Returns 6 large deposits /// + /// The asynchronous task. [Fact] - public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario7() + public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario7_Async() { // Create a "chain" of 40 blocks. this.blocks = ChainedHeadersHelper.CreateConsecutiveHeadersAndBlocks(40, true, this.mainChainNetwork); @@ -531,8 +538,9 @@ public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario7() /// Returns 6 normal deposits /// Returns 7 large deposits /// + /// The asynchronous task. [Fact] - public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario8() + public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario8_Async() { // Create a "chain" of 40 blocks. this.blocks = ChainedHeadersHelper.CreateConsecutiveHeadersAndBlocks(40, true, this.mainChainNetwork); diff --git a/src/Stratis.Features.FederatedPeg.Tests/PartialTransactionsRequesterTests.cs b/src/Stratis.Features.FederatedPeg.Tests/PartialTransactionsRequesterTests.cs index c164631df2..239b43e783 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/PartialTransactionsRequesterTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/PartialTransactionsRequesterTests.cs @@ -42,7 +42,7 @@ public PartialTransactionsRequesterTests() } [Fact] - public async Task DoesntBroadcastInIBD() + public async Task DoesntBroadcastInIBDAsync() { this.ibdState.IsInitialBlockDownload().Returns(true); @@ -61,7 +61,7 @@ public async Task DoesntBroadcastInIBD() } [Fact] - public async Task DoesntBroadcastWithInactiveFederation() + public async Task DoesntBroadcastWithInactiveFederationAsync() { this.federationWalletManager.IsFederationWalletActive().Returns(false); diff --git a/src/Stratis.Features.FederatedPeg.Tests/RestClientsTests/FederationGatewayClientTests.cs b/src/Stratis.Features.FederatedPeg.Tests/RestClientsTests/FederationGatewayClientTests.cs index 9aafba195d..d177b5f4af 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/RestClientsTests/FederationGatewayClientTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/RestClientsTests/FederationGatewayClientTests.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using Stratis.Bitcoin.Configuration; -using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Controllers; using Stratis.Bitcoin.Networks; using Stratis.Bitcoin.Utilities; diff --git a/src/Stratis.Features.FederatedPeg/Controllers/CollateralVotingController.cs b/src/Stratis.Features.FederatedPeg/Controllers/CollateralVotingController.cs index 176b4b1ec5..a2a22ac252 100644 --- a/src/Stratis.Features.FederatedPeg/Controllers/CollateralVotingController.cs +++ b/src/Stratis.Features.FederatedPeg/Controllers/CollateralVotingController.cs @@ -1,8 +1,9 @@ using System; using System.Net; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.PoA; using Stratis.Bitcoin.Features.PoA.Voting; using Stratis.Bitcoin.Utilities; @@ -32,8 +33,11 @@ public CollateralVotingController(IFederationManager fedManager, VotingManager v this.logger = LogManager.GetCurrentClassLogger(); } + /// Schedules a vote to kick a federation member. + /// See . /// Not yet implemented /// Request is null + /// See . [Route("schedulevote-kickfedmember")] [HttpPost] [ProducesResponseType((int)HttpStatusCode.BadRequest)] @@ -74,7 +78,7 @@ private IActionResult VoteAddKickFedMember(CollateralFederationMemberModel reque } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "There was a problem executing a command.", e.ToString()); } } diff --git a/src/Stratis.Features.FederatedPeg/Controllers/FederationGatewayController.cs b/src/Stratis.Features.FederatedPeg/Controllers/FederationGatewayController.cs index 0d1b34cbce..18829d8565 100644 --- a/src/Stratis.Features.FederatedPeg/Controllers/FederationGatewayController.cs +++ b/src/Stratis.Features.FederatedPeg/Controllers/FederationGatewayController.cs @@ -4,10 +4,11 @@ using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Connection; using Stratis.Bitcoin.Controllers.Models; using Stratis.Bitcoin.Features.PoA; @@ -112,7 +113,7 @@ public async Task GetMaturedBlockDepositsAsync([FromQuery(Name = } catch (Exception e) { - this.logger.Error("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.GetMaturedBlockDeposits, e.Message); + this.logger.LogError("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.GetMaturedBlockDeposits, e.Message); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, $"Could not re-sync matured block deposits: {e.Message}", e.ToString()); } } @@ -242,7 +243,7 @@ public IActionResult GetFederationMemberInfo() } catch (Exception e) { - this.logger.Error("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.GetFederationMemberInfo, e.Message); + this.logger.LogError("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.GetFederationMemberInfo, e.Message); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -285,7 +286,7 @@ public IActionResult GetInfo() } catch (Exception e) { - this.logger.Error("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.GetFederationInfo, e.Message); + this.logger.LogError("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.GetFederationInfo, e.Message); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -318,7 +319,7 @@ public IActionResult AddFederationMemberIp([FromBody] FederationMemberIpModel mo } catch (Exception e) { - this.logger.Error("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.FederationMemberIpAdd, e.Message); + this.logger.LogError("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.FederationMemberIpAdd, e.Message); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -351,7 +352,7 @@ public IActionResult RemoveFederationMemberIp([FromBody] FederationMemberIpModel } catch (Exception e) { - this.logger.Error("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.FederationMemberIpRemove, e.Message); + this.logger.LogError("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.FederationMemberIpRemove, e.Message); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -391,7 +392,7 @@ public IActionResult ReplaceFederationMemberIp([FromBody] ReplaceFederationMembe } catch (Exception e) { - this.logger.Error("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.FederationMemberIpReplace, e.Message); + this.logger.LogError("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.FederationMemberIpReplace, e.Message); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } diff --git a/src/Stratis.Features.FederatedPeg/Controllers/FederationWalletController.cs b/src/Stratis.Features.FederatedPeg/Controllers/FederationWalletController.cs index 75e735e316..56cf390d87 100644 --- a/src/Stratis.Features.FederatedPeg/Controllers/FederationWalletController.cs +++ b/src/Stratis.Features.FederatedPeg/Controllers/FederationWalletController.cs @@ -4,8 +4,9 @@ using System.Net; using System.Threading; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Connection; using Stratis.Bitcoin.Features.Wallet; using Stratis.Bitcoin.Features.Wallet.Models; @@ -100,7 +101,7 @@ public IActionResult GetGeneralInfo() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -143,7 +144,7 @@ public IActionResult GetBalance() } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -151,6 +152,7 @@ public IActionResult GetBalance() /// /// Retrieves withdrawal history for the wallet /// + /// The maximum number of history items to return. /// HTTP response /// Returns wallet history /// Unexpected exception occurred @@ -174,7 +176,7 @@ public IActionResult GetHistory([FromQuery] int maxEntriesToReturn) } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -251,7 +253,7 @@ public IActionResult EnableFederation([FromBody] EnableFederationRequest request } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } @@ -312,7 +314,7 @@ public IActionResult RemoveTransactions([FromQuery] RemoveFederationTransactions } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } diff --git a/src/Stratis.Features.FederatedPeg/Controllers/MultisigController.cs b/src/Stratis.Features.FederatedPeg/Controllers/MultisigController.cs index c73a389655..c8dbf57b34 100644 --- a/src/Stratis.Features.FederatedPeg/Controllers/MultisigController.cs +++ b/src/Stratis.Features.FederatedPeg/Controllers/MultisigController.cs @@ -2,8 +2,9 @@ using System.Linq; using System.Net; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.Wallet.Models; using Stratis.Bitcoin.Utilities; using Stratis.Bitcoin.Utilities.JsonErrors; @@ -85,7 +86,7 @@ public IActionResult BuildTransaction([FromBody] BuildMultisigTransactionRequest } catch (Exception e) { - this.logger.Error("Exception occurred: {0}", e.ToString()); + this.logger.LogError("Exception occurred: {0}", e.ToString()); return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); } } diff --git a/src/Stratis.Features.FederatedPeg/Coordination/ConversionRequestCoordinationService.cs b/src/Stratis.Features.FederatedPeg/Coordination/ConversionRequestCoordinationService.cs index 09bba77999..71ddca6296 100644 --- a/src/Stratis.Features.FederatedPeg/Coordination/ConversionRequestCoordinationService.cs +++ b/src/Stratis.Features.FederatedPeg/Coordination/ConversionRequestCoordinationService.cs @@ -2,8 +2,9 @@ using System.Linq; using System.Numerics; using System.Text; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Utilities; namespace Stratis.Features.FederatedPeg.Coordination @@ -85,7 +86,7 @@ public void AddVote(string requestId, BigInteger transactionId, PubKey pubKey) // Check if the pubkey node has voted for this request. if (!voted.Contains(pubKey)) { - this.logger.Debug("Adding vote for request '{0}' (transactionId '{1}') from pubkey {2}.", requestId, transactionId, pubKey.ToHex()); + this.logger.LogDebug("Adding vote for request '{0}' (transactionId '{1}') from pubkey {2}.", requestId, transactionId, pubKey.ToHex()); voted.Add(pubKey); diff --git a/src/Stratis.Features.FederatedPeg/Coordination/ConversionRequestFeeService.cs b/src/Stratis.Features.FederatedPeg/Coordination/ConversionRequestFeeService.cs index 5a545cde9a..4306c4dd63 100644 --- a/src/Stratis.Features.FederatedPeg/Coordination/ConversionRequestFeeService.cs +++ b/src/Stratis.Features.FederatedPeg/Coordination/ConversionRequestFeeService.cs @@ -3,9 +3,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; using Newtonsoft.Json; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Features.PoA; using Stratis.Bitcoin.Utilities; @@ -126,7 +127,7 @@ public async Task AgreeFeeForConversionRequestAsync { if (conversionRequestSyncStart.AddMinutes(2) <= this.dateTimeProvider.GetUtcNow()) { - this.logger.Warn($"A fee for conversion request '{requestId}' failed to reach consensus after 2 minutes, ignoring."); + this.logger.LogWarning($"A fee for conversion request '{requestId}' failed to reach consensus after 2 minutes, ignoring."); interopConversionRequestFee.State = InteropFeeState.FailRevertToFallback; this.interopRequestKeyValueStore.SaveValueJson(requestId, interopConversionRequestFee); break; @@ -182,7 +183,7 @@ private InteropConversionRequestFee CreateInteropConversionRequestFeeLocked(stri { interopConversionRequest = new InteropConversionRequestFee() { RequestId = requestId, BlockHeight = blockHeight, State = InteropFeeState.ProposalInProgress }; this.interopRequestKeyValueStore.SaveValueJson(requestId, interopConversionRequest); - this.logger.Debug($"InteropConversionRequestFee object for request '{requestId}' has been created."); + this.logger.LogDebug($"InteropConversionRequestFee object for request '{requestId}' has been created."); } return interopConversionRequest; @@ -204,10 +205,10 @@ private async Task SubmitProposalForInteropFeeForConversionRequestLockedAsync(In interopConversionRequestFee.FeeProposals.Add(new InterOpFeeToMultisig() { BlockHeight = interopConversionRequestFee.BlockHeight, PubKey = this.federationManager.CurrentFederationKey.PubKey.ToHex(), FeeAmount = candidateFee }); this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); - this.logger.Debug($"Adding this node's proposal fee of {candidateFee} for conversion request id '{interopConversionRequestFee.RequestId}'."); + this.logger.LogDebug($"Adding this node's proposal fee of {candidateFee} for conversion request id '{interopConversionRequestFee.RequestId}'."); } - this.logger.Debug($"{interopConversionRequestFee.FeeProposals.Count} node(s) has proposed a fee for conversion request id '{interopConversionRequestFee.RequestId}'."); + this.logger.LogDebug($"{interopConversionRequestFee.FeeProposals.Count} node(s) has proposed a fee for conversion request id '{interopConversionRequestFee.RequestId}'."); if (HasFeeProposalBeenConcluded(interopConversionRequestFee)) { @@ -218,7 +219,7 @@ private async Task SubmitProposalForInteropFeeForConversionRequestLockedAsync(In this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); IEnumerable values = interopConversionRequestFee.FeeProposals.Select(s => Convert.ToInt64(s.FeeAmount)); - this.logger.Debug($"Proposal fee for request id '{interopConversionRequestFee.RequestId}' has concluded, average amount: {values.Average()}"); + this.logger.LogDebug($"Proposal fee for request id '{interopConversionRequestFee.RequestId}' has concluded, average amount: {values.Average()}"); } } @@ -237,7 +238,7 @@ private async Task AgreeOnInteropFeeForConversionRequestLockedAsync(InteropConve { if (!HasFeeProposalBeenConcluded(interopConversionRequestFee)) { - this.logger.Error($"Cannot vote on fee proposal for request id '{interopConversionRequestFee.RequestId}' as it hasn't concluded yet."); + this.logger.LogError($"Cannot vote on fee proposal for request id '{interopConversionRequestFee.RequestId}' as it hasn't concluded yet."); return; } @@ -250,10 +251,10 @@ private async Task AgreeOnInteropFeeForConversionRequestLockedAsync(InteropConve interopConversionRequestFee.FeeVotes.Add(interOpFeeToMultisig); this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); - this.logger.Debug($"Creating fee vote for conversion request id '{interopConversionRequestFee.RequestId}' with a fee amount of {new Money(candidateFee)}."); + this.logger.LogDebug($"Creating fee vote for conversion request id '{interopConversionRequestFee.RequestId}' with a fee amount of {new Money(candidateFee)}."); } - this.logger.Debug($"{interopConversionRequestFee.FeeVotes.Count} node(s) has voted on a fee for conversion request id '{interopConversionRequestFee.RequestId}'."); + this.logger.LogDebug($"{interopConversionRequestFee.FeeVotes.Count} node(s) has voted on a fee for conversion request id '{interopConversionRequestFee.RequestId}'."); if (HasFeeVoteBeenConcluded(interopConversionRequestFee)) ConcludeInteropConversionRequestFee(interopConversionRequestFee); @@ -285,7 +286,7 @@ public async Task MultiSigMemberProposedInteropFeeAsync(stri interopConversionRequestFee.FeeProposals.Add(new InterOpFeeToMultisig() { BlockHeight = interopConversionRequestFee.BlockHeight, PubKey = pubKey.ToHex(), FeeAmount = feeAmount }); this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); - this.logger.Debug($"Received conversion request proposal '{requestId}' from '{pubKey}', proposing fee of {new Money(feeAmount)}."); + this.logger.LogDebug($"Received conversion request proposal '{requestId}' from '{pubKey}', proposing fee of {new Money(feeAmount)}."); } // This node would have proposed this fee if the InteropConversionRequestFee object exists. @@ -311,7 +312,7 @@ public async Task MultiSigMemberAgreedOnInteropFeeAsync(string interopConversionRequestFee.FeeVotes.Add(new InterOpFeeToMultisig() { BlockHeight = interopConversionRequestFee.BlockHeight, PubKey = pubKey.ToHex(), FeeAmount = feeAmount }); this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); - this.logger.Debug($"Received conversion request fee vote '{requestId}' from '{pubKey} for a fee of {new Money(feeAmount)}."); + this.logger.LogDebug($"Received conversion request fee vote '{requestId}' from '{pubKey} for a fee of {new Money(feeAmount)}."); } // This node would have voted on this if the InteropConversionRequestFee object exists. @@ -338,7 +339,7 @@ private bool IsFeeWithinAcceptableRange(List proposals, st if (feeAmount < (currentAverage - (currentAverage * FeeProposalRange)) || feeAmount > (currentAverage + (currentAverage * FeeProposalRange))) { - this.logger.Debug($"Conversion request '{requestId}' received from pubkey '{pubKey}' with amount {feeAmount} is out of range of the current average of {currentAverage}, skipping."); + this.logger.LogDebug($"Conversion request '{requestId}' received from pubkey '{pubKey}' with amount {feeAmount} is out of range of the current average of {currentAverage}, skipping."); return false; } @@ -352,7 +353,7 @@ private bool EstimateConversionTransactionFee(out ulong candidateFee) var conversionTransactionFee = this.externalApiPoller.EstimateConversionTransactionFee(); if (conversionTransactionFee == -1) { - this.logger.Debug("External poller returned -1, will retry."); + this.logger.LogDebug("External poller returned -1, will retry."); return false; } @@ -378,7 +379,7 @@ private void ConcludeInteropConversionRequestFee(InteropConversionRequestFee int foreach (InterOpFeeToMultisig vote in interopConversionRequestFee.FeeVotes) { - this.logger.Debug($"Pubkey '{vote.PubKey}' voted for {new Money(vote.FeeAmount)}."); + this.logger.LogDebug($"Pubkey '{vote.PubKey}' voted for {new Money(vote.FeeAmount)}."); } // Try and find the majority vote @@ -392,7 +393,7 @@ private void ConcludeInteropConversionRequestFee(InteropConversionRequestFee int interopConversionRequestFee.State = InteropFeeState.AgreeanceConcluded; this.interopRequestKeyValueStore.SaveValueJson(interopConversionRequestFee.RequestId, interopConversionRequestFee, true); - this.logger.Debug($"Voting on fee for request id '{interopConversionRequestFee.RequestId}' has concluded, amount: {new Money(interopConversionRequestFee.Amount)}"); + this.logger.LogDebug($"Voting on fee for request id '{interopConversionRequestFee.RequestId}' has concluded, amount: {new Money(interopConversionRequestFee.Amount)}"); } private void AddComponentStats(StringBuilder benchLog) diff --git a/src/Stratis.Features.FederatedPeg/Distribution/RewardClaimer.cs b/src/Stratis.Features.FederatedPeg/Distribution/RewardClaimer.cs index 859611150b..124bdc8717 100644 --- a/src/Stratis.Features.FederatedPeg/Distribution/RewardClaimer.cs +++ b/src/Stratis.Features.FederatedPeg/Distribution/RewardClaimer.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Logging; using NBitcoin; using NBitcoin.Policy; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.EventBus; using Stratis.Bitcoin.EventBus.CoreEvents; @@ -85,7 +86,7 @@ public Transaction BuildRewardTransaction(bool batchRewards) var startFromHeight = (this.lastDistributionHeight + 1) - minStakeConfirmations; - this.logger.Info($"[Reward Batching] Calculating rewards from height {startFromHeight} to {startFromHeight + this.network.RewardClaimerBlockInterval} (last distribution [{this.lastDistributionHeight + 1}] less minimum stake confirmations [{minStakeConfirmations}])."); + this.logger.LogInformation($"[Reward Batching] Calculating rewards from height {startFromHeight} to {startFromHeight + this.network.RewardClaimerBlockInterval} (last distribution [{this.lastDistributionHeight + 1}] less minimum stake confirmations [{minStakeConfirmations}])."); for (int height = startFromHeight; height < startFromHeight + this.network.RewardClaimerBlockInterval; height++) { // Get the block that is minStakeConfirmations behind the current tip. @@ -169,13 +170,13 @@ private Transaction BuildRewardTransaction(List coins) if (errors.Any()) { foreach (TransactionPolicyError error in errors) - this.logger.Warn("Unable to validate reward claim transaction '{0}', error: {1}", builtTransaction.ToHex(), error.ToString()); + this.logger.LogWarning("Unable to validate reward claim transaction '{0}', error: {1}", builtTransaction.ToHex(), error.ToString()); // Not much further can be done at this point. return null; } - this.logger.Info($"Reward distribution transaction '{builtTransaction.GetHash()}' built; sending {builtTransaction.TotalOut} to federation '{this.network.Federations.GetOnlyFederation().MultisigScript.PaymentScript}'."); + this.logger.LogInformation($"Reward distribution transaction '{builtTransaction.GetHash()}' built; sending {builtTransaction.TotalOut} to federation '{this.network.Federations.GetOnlyFederation().MultisigScript.PaymentScript}'."); return builtTransaction; } @@ -190,7 +191,7 @@ private Block GetMaturedBlock(int applicableHeight) // If we still don't have the block data, just return. if (maturedBlock == null) { - this.logger.Debug("Consensus does not have the block data for '{0}'", chainedHeader); + this.logger.LogDebug("Consensus does not have the block data for '{0}'", chainedHeader); return null; } @@ -214,7 +215,7 @@ private void OnBlockConnected(BlockConnected blockConnected) // This is could happen due to a reorg and therefore we do nothing. if (blockConnected.ConnectedBlock.ChainedHeader.Height <= (this.lastDistributionHeight + 1)) { - this.logger.Info($"Reward claiming skipped as block window already processed; Block connected at {blockConnected.ConnectedBlock.ChainedHeader.Height}; Last distribution at {this.lastDistributionHeight}."); + this.logger.LogInformation($"Reward claiming skipped as block window already processed; Block connected at {blockConnected.ConnectedBlock.ChainedHeader.Height}; Last distribution at {this.lastDistributionHeight}."); return; } @@ -228,19 +229,19 @@ private void OnBlockConnected(BlockConnected blockConnected) if (blockConnected.ConnectedBlock.ChainedHeader.Height - (this.lastDistributionHeight + 1) > this.network.RewardClaimerBlockInterval) { this.lastDistributionHeight = blockConnected.ConnectedBlock.ChainedHeader.Height - this.network.RewardClaimerBlockInterval - 1; - this.logger.Info($"[Reward Batching] The last reward window was skipped, resetting to {this.lastDistributionHeight}."); + this.logger.LogInformation($"[Reward Batching] The last reward window was skipped, resetting to {this.lastDistributionHeight}."); } - this.logger.Info($"[Reward Batching] Triggered at height {blockConnected.ConnectedBlock.ChainedHeader.Height}."); + this.logger.LogInformation($"[Reward Batching] Triggered at height {blockConnected.ConnectedBlock.ChainedHeader.Height}."); BuildAndCompleteRewardClaim(true, this.lastDistributionHeight + this.network.RewardClaimerBlockInterval); } else - this.logger.Info($"[Reward Batching] The next distribution will be triggered at block {this.lastDistributionHeight + 1 + this.network.RewardClaimerBlockInterval}."); + this.logger.LogInformation($"[Reward Batching] The next distribution will be triggered at block {this.lastDistributionHeight + 1 + this.network.RewardClaimerBlockInterval}."); } else { - this.logger.Info($"Per block reward claiming in effect until block {this.network.RewardClaimerBatchActivationHeight} (rewards are not batched)."); + this.logger.LogInformation($"Per block reward claiming in effect until block {this.network.RewardClaimerBatchActivationHeight} (rewards are not batched)."); BuildAndCompleteRewardClaim(false, blockConnected.ConnectedBlock.ChainedHeader.Height); } } @@ -274,13 +275,13 @@ private void LoadLastDistributionHeight() if (this.lastDistributionHeight == 0) this.lastDistributionHeight = this.network.RewardClaimerBatchActivationHeight; - this.logger.Info($"Last reward distribution height set to {this.lastDistributionHeight}."); + this.logger.LogInformation($"Last reward distribution height set to {this.lastDistributionHeight}."); } private void SaveLastDistributionHeight() { this.keyValueRepository.SaveValueJson(LastDistributionHeightKey, this.lastDistributionHeight); - this.logger.Info($"Last reward distribution saved as {this.lastDistributionHeight}."); + this.logger.LogInformation($"Last reward distribution saved as {this.lastDistributionHeight}."); } public void Dispose() diff --git a/src/Stratis.Features.FederatedPeg/Distribution/RewardDistributionManager.cs b/src/Stratis.Features.FederatedPeg/Distribution/RewardDistributionManager.cs index 3637dbe22a..80240f7973 100644 --- a/src/Stratis.Features.FederatedPeg/Distribution/RewardDistributionManager.cs +++ b/src/Stratis.Features.FederatedPeg/Distribution/RewardDistributionManager.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Features.PoA; using Stratis.Features.FederatedPeg.Wallet; @@ -109,7 +110,7 @@ public List DistributeToMultisigNodes(int blockHeight, Money fee) multiSigMinerScripts.Add(minerScript); } - this.logger.Info("Fee reward to multisig node at main chain height {0} will distribute {1} STRAX between {2} multisig mining keys.", blockHeight, fee, multiSigMinerScripts.Count); + this.logger.LogInformation("Fee reward to multisig node at main chain height {0} will distribute {1} STRAX between {2} multisig mining keys.", blockHeight, fee, multiSigMinerScripts.Count); Money feeReward = fee / multiSigMinerScripts.Count; @@ -124,12 +125,12 @@ public List DistributeToMultisigNodes(int blockHeight, Money fee) multiSigRecipients.Add(new Recipient() { Amount = feeReward, ScriptPubKey = p2pkh }); - this.logger.Debug($"Paying multisig member '{pubKey}' {feeReward} STRAX."); + this.logger.LogDebug($"Paying multisig member '{pubKey}' {feeReward} STRAX."); } else { multiSigRecipients.Add(new Recipient() { Amount = feeReward, ScriptPubKey = multiSigMinerScript }); - this.logger.Debug($"Paying multisig member '{multiSigMinerScript.ToHex()}' (hex) {feeReward} STRAX."); + this.logger.LogDebug($"Paying multisig member '{multiSigMinerScript.ToHex()}' (hex) {feeReward} STRAX."); } } @@ -142,7 +143,7 @@ public List Distribute(int heightOfRecordedDistributionDeposit, Money { // First determine the main chain blockheight of the recorded deposit less max reorg * 2 (epoch window) var applicableMainChainDepositHeight = heightOfRecordedDistributionDeposit - this.epochWindow; - this.logger.Trace("{0} : {1}", nameof(applicableMainChainDepositHeight), applicableMainChainDepositHeight); + this.logger.LogTrace("{0} : {1}", nameof(applicableMainChainDepositHeight), applicableMainChainDepositHeight); // Then find the header on the sidechain that contains the applicable commitment height. int sidechainTipHeight = this.chainIndexer.Tip.Height; @@ -174,7 +175,7 @@ public List Distribute(int heightOfRecordedDistributionDeposit, Money if (commitmentHeightToCheck != null) { - this.logger.Trace("{0} : {1}={2}", currentHeader, nameof(commitmentHeightToCheck), commitmentHeightToCheck); + this.logger.LogTrace("{0} : {1}={2}", currentHeader, nameof(commitmentHeightToCheck), commitmentHeightToCheck); if (commitmentHeightToCheck <= applicableMainChainDepositHeight) break; @@ -187,7 +188,7 @@ public List Distribute(int heightOfRecordedDistributionDeposit, Money // Get the set of miners (more specifically, the scriptPubKeys they generated blocks with) to distribute rewards to. // Based on the computed 'common block height' we define the distribution epoch: int sidechainStartHeight = currentHeader.Height; - this.logger.Trace("Initial {0} : {1}", nameof(sidechainStartHeight), sidechainStartHeight); + this.logger.LogTrace("Initial {0} : {1}", nameof(sidechainStartHeight), sidechainStartHeight); // This is a special case which will not be the case on the live network. if (sidechainStartHeight < this.epoch) @@ -197,7 +198,7 @@ public List Distribute(int heightOfRecordedDistributionDeposit, Money if (sidechainStartHeight > this.epoch) sidechainStartHeight -= this.epoch; - this.logger.Trace("Adjusted {0} : {1}", nameof(sidechainStartHeight), sidechainStartHeight); + this.logger.LogTrace("Adjusted {0} : {1}", nameof(sidechainStartHeight), sidechainStartHeight); // Ensure that the dictionary is cleared on every run. // As this is a static class, new instances of this dictionary will @@ -237,7 +238,7 @@ private long CalculateBlocksMinedPerMiner(int sidechainStartHeight, int sidechai totalBlocks++; } else - this.logger.Trace("A block was mined with an empty script at height '{0}' (the miner probably did not have a wallet at the time.", currentHeight); + this.logger.LogTrace("A block was mined with an empty script at height '{0}' (the miner probably did not have a wallet at the time.", currentHeight); } /* @@ -291,7 +292,7 @@ private List ConstructRecipients(int heightOfRecordedDistributionDepo this.logger.LogDebug(recipientLog.ToString()); */ - this.logger.Info("Reward distribution at main chain height {0} will distribute {1} STRAX between {2} mining keys.", heightOfRecordedDistributionDeposit, totalReward, recipients.Count); + this.logger.LogInformation("Reward distribution at main chain height {0} will distribute {1} STRAX between {2} mining keys.", heightOfRecordedDistributionDeposit, totalReward, recipients.Count); return recipients; } diff --git a/src/Stratis.Features.FederatedPeg/FederatedPegFeature.cs b/src/Stratis.Features.FederatedPeg/FederatedPegFeature.cs index b1dc05752d..9c2be2b48d 100644 --- a/src/Stratis.Features.FederatedPeg/FederatedPegFeature.cs +++ b/src/Stratis.Features.FederatedPeg/FederatedPegFeature.cs @@ -5,8 +5,8 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin; using Stratis.Bitcoin.Builder; using Stratis.Bitcoin.Builder.Feature; @@ -16,7 +16,6 @@ using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Features.Notifications; using Stratis.Bitcoin.Features.SmartContracts; -using Stratis.Bitcoin.Features.Wallet; using Stratis.Bitcoin.P2P.Peer; using Stratis.Bitcoin.P2P.Protocol.Payloads; using Stratis.Bitcoin.Utilities; @@ -182,7 +181,7 @@ private void AddComponentStats(StringBuilder benchLog) } catch (Exception e) { - this.logger.Error(e.ToString()); + this.logger.LogError(e.ToString()); } } diff --git a/src/Stratis.Features.FederatedPeg/InputConsolidation/InputConsolidator.cs b/src/Stratis.Features.FederatedPeg/InputConsolidation/InputConsolidator.cs index 78d885fad0..6580744d9c 100644 --- a/src/Stratis.Features.FederatedPeg/InputConsolidation/InputConsolidator.cs +++ b/src/Stratis.Features.FederatedPeg/InputConsolidation/InputConsolidator.cs @@ -3,9 +3,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.Wallet.Interfaces; using Stratis.Bitcoin.Primitives; using Stratis.Bitcoin.Signals; @@ -92,17 +93,17 @@ public void StartConsolidation(WalletNeedsConsolidation trigger) if (this.ConsolidationTransactions != null) return; - this.logger.Info("Building consolidation transactions for federation wallet inputs."); + this.logger.LogInformation("Building consolidation transactions for federation wallet inputs."); this.ConsolidationTransactions = this.CreateRequiredConsolidationTransactions(trigger.Amount); if (this.ConsolidationTransactions == null) { - this.logger.Warn("Failed to build condensing transactions."); + this.logger.LogWarning("Failed to build condensing transactions."); return; } - this.logger.Info("Successfully built {0} consolidating transactions.", this.ConsolidationTransactions.Count); + this.logger.LogInformation("Successfully built {0} consolidating transactions.", this.ConsolidationTransactions.Count); } }); @@ -131,18 +132,18 @@ public ConsolidationSignatureResult CombineSignatures(Transaction incomingPartia var builder = new TransactionBuilder(this.network); Transaction oldTransaction = inMemoryTransaction.PartialTransaction; - this.logger.Debug("Attempting to merge signatures for '{0}' and '{1}'.", inMemoryTransaction.PartialTransaction.GetHash(), incomingPartialTransaction.GetHash()); + this.logger.LogDebug("Attempting to merge signatures for '{0}' and '{1}'.", inMemoryTransaction.PartialTransaction.GetHash(), incomingPartialTransaction.GetHash()); Transaction newTransaction = SigningUtils.CheckTemplateAndCombineSignatures(builder, inMemoryTransaction.PartialTransaction, new[] { incomingPartialTransaction }); if (oldTransaction.GetHash() == newTransaction.GetHash()) { // Signing didn't work if the hash is still the same - this.logger.Debug("Signing failed."); + this.logger.LogDebug("Signing failed."); return ConsolidationSignatureResult.Failed(); } - this.logger.Debug("Successfully signed transaction."); + this.logger.LogDebug("Successfully signed transaction."); inMemoryTransaction.PartialTransaction = newTransaction; // NOTE: We don't need to reserve the transaction. The wallet will be at a standstill whilst this is happening. @@ -151,12 +152,12 @@ public ConsolidationSignatureResult CombineSignatures(Transaction incomingPartia if (this.walletManager.ValidateConsolidatingTransaction(inMemoryTransaction.PartialTransaction, true)) { inMemoryTransaction.Status = ConsolidationTransactionStatus.FullySigned; - this.logger.Debug("Consolidation transaction is fully signed. Broadcasting '{0}'", inMemoryTransaction.PartialTransaction.GetHash()); + this.logger.LogDebug("Consolidation transaction is fully signed. Broadcasting '{0}'", inMemoryTransaction.PartialTransaction.GetHash()); this.broadcasterManager.BroadcastTransactionAsync(inMemoryTransaction.PartialTransaction).GetAwaiter().GetResult(); return ConsolidationSignatureResult.Succeeded(inMemoryTransaction.PartialTransaction); } - this.logger.Debug("Consolidation transaction not fully signed yet."); + this.logger.LogDebug("Consolidation transaction not fully signed yet."); return ConsolidationSignatureResult.Succeeded(inMemoryTransaction.PartialTransaction); } @@ -165,6 +166,11 @@ public ConsolidationSignatureResult CombineSignatures(Transaction incomingPartia /// /// Builds a list of consolidation transactions that will need to pass before the next withdrawal transaction can come through. /// + /// + /// Gathers consolidation transactions until we find a group of UTXO's summing up to + /// the or there are less than that number of UTXO's left. + /// + /// The consolidation amount. /// A list of consolidation transactions. public List CreateRequiredConsolidationTransactions(Money amount) { @@ -178,7 +184,7 @@ public List CreateRequiredConsolidationTransactions(Mo // We shouldn't be consolidating transactions if we have less than 50 UTXOs to spend. if (unspentOutputs.Count < FederatedPegSettings.MaxInputs) { - this.logger.Info($"Not enough UTXOs '[{unspentOutputs.Count}]' to trigger consolidation transactions."); + this.logger.LogInformation($"Not enough UTXOs '[{unspentOutputs.Count}]' to trigger consolidation transactions."); return null; } @@ -196,7 +202,7 @@ public List CreateRequiredConsolidationTransactions(Mo // Something went wrong building transaction - start over. We will want to build them all from scratch in case wallet has changed state. if (transaction == null) { - this.logger.Warn("Failure building specific consolidating transaction."); + this.logger.LogWarning("Failure building specific consolidating transaction."); return null; } @@ -209,7 +215,7 @@ public List CreateRequiredConsolidationTransactions(Mo // We found a set of 50 that is worth enough so no more consolidation needed. if (oneRound.Sum(x => x.Transaction.Amount) >= amount + FederatedPegSettings.ConsolidationFee) { - this.logger.Info($"Input consolidation threshold reached '{Money.Satoshis(oneRound.Sum(x => x.Transaction.Amount)).ToUnit(MoneyUnit.BTC)}'; consolidation amount '{amount}'."); + this.logger.LogInformation($"Input consolidation threshold reached '{Money.Satoshis(oneRound.Sum(x => x.Transaction.Amount)).ToUnit(MoneyUnit.BTC)}'; consolidation amount '{amount}'."); break; } @@ -229,6 +235,8 @@ public List CreateRequiredConsolidationTransactions(Mo /// /// Build a consolidating transaction. /// + /// The selected inputs for the consolidation transaction. + /// The consolidation transaction. private Transaction BuildConsolidatingTransaction(List selectedInputs) { try @@ -254,13 +262,13 @@ private Transaction BuildConsolidatingTransaction(List s Transaction transaction = this.transactionHandler.BuildTransaction(multiSigContext); - this.logger.Info("Consolidating transaction = {0}", transaction.ToString(this.network, RawFormat.BlockExplorer)); + this.logger.LogInformation("Consolidating transaction = {0}", transaction.ToString(this.network, RawFormat.BlockExplorer)); return transaction; } catch (Exception e) { - this.logger.Warn("Exception when building consolidating transaction. Wallet state likely changed before calling: " + e); + this.logger.LogWarning("Exception when building consolidating transaction. Wallet state likely changed before calling: " + e); return null; } } @@ -287,7 +295,7 @@ public void ProcessTransaction(Transaction transaction) if (inMemoryTransaction != null && inMemoryTransaction.Status == ConsolidationTransactionStatus.Partial) { - this.logger.Info("Saw consolidating transaction {0} in mempool, updating its status to FullySigned", transaction.GetHash()); + this.logger.LogInformation("Saw consolidating transaction {0} in mempool, updating its status to FullySigned", transaction.GetHash()); inMemoryTransaction.Status = ConsolidationTransactionStatus.FullySigned; inMemoryTransaction.PartialTransaction = transaction; } @@ -319,7 +327,7 @@ private Task ProcessBlockInternal(ChainedHeaderBlock chainedHeaderBlock, Cancell if (inMemoryTransaction != null) { - this.logger.Info("Saw condensing transaction {0}, updating status to SeenInBlock", + this.logger.LogInformation("Saw condensing transaction {0}, updating status to SeenInBlock", transaction.GetHash()); inMemoryTransaction.Status = ConsolidationTransactionStatus.SeenInBlock; } @@ -338,8 +346,7 @@ private Task ProcessBlockInternal(ChainedHeaderBlock chainedHeaderBlock, Cancell if (!this.walletManager.ValidateConsolidatingTransaction(cTransaction.PartialTransaction)) { // If we find an invalid one, everything will need redoing! - this.logger.Info( - "Consolidation transaction {0} failed validation, resetting InputConsolidator", + this.logger.LogInformation("Consolidation transaction {0} failed validation, resetting InputConsolidator", cTransaction.PartialTransaction.GetHash()); this.ConsolidationTransactions = null; return Task.CompletedTask; @@ -357,6 +364,8 @@ private Task ProcessBlockInternal(ChainedHeaderBlock chainedHeaderBlock, Cancell /// /// Discerns whether an incoming transaction is a consolidating transaction. /// + /// The transaction to inspect. + /// Determines if a transaction is a consolidation transaction. private bool IsConsolidatingTransaction(Transaction transaction) { return transaction.Inputs.Count == FederatedPegSettings.MaxInputs @@ -367,6 +376,8 @@ private bool IsConsolidatingTransaction(Transaction transaction) /// /// Gets the equivalent transaction on this node for any incoming transaction. /// + /// The incoming transaction. + /// The equivalent . private ConsolidationTransaction GetInMemoryConsolidationTransaction(Transaction toMatch) { if (toMatch?.Inputs == null || !toMatch.Inputs.Any()) diff --git a/src/Stratis.Features.FederatedPeg/Interfaces/IWithdrawal.cs b/src/Stratis.Features.FederatedPeg/Interfaces/IWithdrawal.cs index 698e631514..7bf06ce4cf 100644 --- a/src/Stratis.Features.FederatedPeg/Interfaces/IWithdrawal.cs +++ b/src/Stratis.Features.FederatedPeg/Interfaces/IWithdrawal.cs @@ -45,8 +45,9 @@ public interface IWithdrawal uint256 BlockHash { get; } /// - /// Abbreviated information about the withdrawal. + /// Gets abbreviated information about the withdrawal. /// + /// The abbreviated information. string GetInfo(); } } diff --git a/src/Stratis.Features.FederatedPeg/PartialTransactionsBehavior.cs b/src/Stratis.Features.FederatedPeg/PartialTransactionsBehavior.cs index 364e61ab45..b66011d903 100644 --- a/src/Stratis.Features.FederatedPeg/PartialTransactionsBehavior.cs +++ b/src/Stratis.Features.FederatedPeg/PartialTransactionsBehavior.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.P2P.Peer; using Stratis.Bitcoin.P2P.Protocol; using Stratis.Bitcoin.P2P.Protocol.Behaviors; @@ -9,7 +10,6 @@ using Stratis.Features.FederatedPeg.Interfaces; using Stratis.Features.FederatedPeg.Payloads; using TracerAttributes; -using ILogger = NLog.ILogger; namespace Stratis.Features.FederatedPeg { @@ -56,13 +56,13 @@ public override object Clone() protected override void AttachCore() { - this.logger.Debug("Attaching behaviour for {0}", this.AttachedPeer.PeerEndPoint.Address); + this.logger.LogDebug("Attaching behaviour for {0}", this.AttachedPeer.PeerEndPoint.Address); this.AttachedPeer.MessageReceived.Register(this.OnMessageReceivedAsync, true); } protected override void DetachCore() { - this.logger.Debug("Detaching behaviour for {0}", this.AttachedPeer.PeerEndPoint.Address); + this.logger.LogDebug("Detaching behaviour for {0}", this.AttachedPeer.PeerEndPoint.Address); this.AttachedPeer.MessageReceived.Unregister(this.OnMessageReceivedAsync); } @@ -70,9 +70,10 @@ protected override void DetachCore() /// Broadcast the partial transaction request to federation members. /// /// The payload to broadcast. + /// The asynchronous task. private async Task BroadcastAsync(RequestPartialTransactionPayload payload) { - this.logger.Debug("Broadcasting to {0}", this.AttachedPeer.PeerEndPoint.Address); + this.logger.LogDebug("Broadcasting to {0}", this.AttachedPeer.PeerEndPoint.Address); await this.AttachedPeer.SendMessageAsync(payload).ConfigureAwait(false); } @@ -84,19 +85,19 @@ private async Task OnMessageReceivedAsync(INetworkPeer peer, IncomingMessage mes // Don't process payloads whilst the federation wallet and cross chain store is syncing. if (!this.federationWalletManager.IsSyncedWithChain()) { - this.logger.Debug($"Federation payloads will only be processed once the federation wallet is synced; current height {this.federationWalletManager.WalletTipHeight}"); + this.logger.LogDebug($"Federation payloads will only be processed once the federation wallet is synced; current height {this.federationWalletManager.WalletTipHeight}"); return; } // Is a consolidation request. if (payload.DepositId == RequestPartialTransactionPayload.ConsolidationDepositId) { - this.logger.Debug("Received request to sign consolidation transaction."); + this.logger.LogDebug("Received request to sign consolidation transaction."); await this.HandleConsolidationTransactionRequestAsync(peer, payload); return; } - this.logger.Debug("{0} with deposit Id '{1}' received from '{2}':'{3}'.", nameof(RequestPartialTransactionPayload), payload.DepositId, peer.PeerEndPoint.Address, peer.RemoteSocketAddress); + this.logger.LogDebug("{0} with deposit Id '{1}' received from '{2}':'{3}'.", nameof(Payloads.RequestPartialTransactionPayload), payload.DepositId, peer.PeerEndPoint.Address, peer.RemoteSocketAddress); ICrossChainTransfer[] transfer = await this.crossChainTransferStore.GetAsync(new[] { payload.DepositId }).ConfigureAwait(false); @@ -105,25 +106,25 @@ private async Task OnMessageReceivedAsync(INetworkPeer peer, IncomingMessage mes // on chain and as such the store was not able to sync. if (transfer == null) { - this.logger.Debug("{0}: Unable to retrieve transfers for deposit {1} at this time, the store is not synced.", nameof(this.OnMessageReceivedAsync), payload.DepositId); + this.logger.LogDebug("{0}: Unable to retrieve transfers for deposit {1} at this time, the store is not synced.", nameof(this.OnMessageReceivedAsync), payload.DepositId); return; } if (transfer[0] == null) { - this.logger.Debug("{0}: Deposit {1} does not exist.", nameof(this.OnMessageReceivedAsync), payload.DepositId); + this.logger.LogDebug("{0}: Deposit {1} does not exist.", nameof(this.OnMessageReceivedAsync), payload.DepositId); return; } if (transfer[0].Status != CrossChainTransferStatus.Partial) { - this.logger.Debug("{0}: Deposit {1} is {2}.", nameof(this.OnMessageReceivedAsync), payload.DepositId, transfer[0].Status); + this.logger.LogDebug("{0}: Deposit {1} is {2}.", nameof(this.OnMessageReceivedAsync), payload.DepositId, transfer[0].Status); return; } if (transfer[0].PartialTransaction == null) { - this.logger.Debug("{0}: Deposit {1}, PartialTransaction not found.", nameof(this.OnMessageReceivedAsync), payload.DepositId); + this.logger.LogDebug("{0}: Deposit {1}, PartialTransaction not found.", nameof(this.OnMessageReceivedAsync), payload.DepositId); return; } @@ -133,20 +134,20 @@ private async Task OnMessageReceivedAsync(INetworkPeer peer, IncomingMessage mes if (signedTransaction == null) { - this.logger.Debug("{0}: Deposit {1}, signedTransaction not found.", nameof(this.OnMessageReceivedAsync), payload.DepositId); + this.logger.LogDebug("{0}: Deposit {1}, signedTransaction not found.", nameof(this.OnMessageReceivedAsync), payload.DepositId); return; } if (oldHash != signedTransaction.GetHash()) { - this.logger.Debug("Signed transaction (deposit={0}) to produce {1} from {2}.", payload.DepositId, signedTransaction.GetHash(), oldHash); + this.logger.LogDebug("Signed transaction (deposit={0}) to produce {1} from {2}.", payload.DepositId, signedTransaction.GetHash(), oldHash); // Respond back to the peer that requested a signature. await this.BroadcastAsync(payload.AddPartial(signedTransaction)).ConfigureAwait(false); } else { - this.logger.Debug("The old and signed hash matches '{0}'.", oldHash); + this.logger.LogDebug("The old and signed hash matches '{0}'.", oldHash); } } @@ -156,7 +157,7 @@ private async Task HandleConsolidationTransactionRequestAsync(INetworkPeer peer, if (result.Signed) { - this.logger.Debug("Signed consolidating transaction to produce {0} from {1}", result.TransactionResult.GetHash(), payload.PartialTransaction.GetHash()); + this.logger.LogDebug("Signed consolidating transaction to produce {0} from {1}", result.TransactionResult.GetHash(), payload.PartialTransaction.GetHash()); await this.BroadcastAsync(payload.AddPartial(result.TransactionResult)).ConfigureAwait(false); } } diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/DepositExtractor.cs b/src/Stratis.Features.FederatedPeg/SourceChain/DepositExtractor.cs index 590e8c0f7e..a10c5d42cb 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/DepositExtractor.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/DepositExtractor.cs @@ -2,9 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.Wallet; using Stratis.Features.FederatedPeg.Conversion; using Stratis.Features.FederatedPeg.Interfaces; @@ -120,7 +121,7 @@ private void ProcessInterFluxBurnRequests(List deposits, int inspectFo { if (inspectForDepositsAtHeight == burnRequest.BlockHeight) { - this.logger.Info($"Processing burn request '{burnRequest.RequestId}' to '{burnRequest.DestinationAddress}' for {new Money(burnRequest.Amount)} STRAX at height {inspectForDepositsAtHeight}."); + this.logger.LogInformation($"Processing burn request '{burnRequest.RequestId}' to '{burnRequest.DestinationAddress}' for {new Money(burnRequest.Amount)} STRAX at height {inspectForDepositsAtHeight}."); Deposit deposit = CreateDeposit(burnRequest, inspectForDepositsAtHeight); if (deposit == null) @@ -149,7 +150,7 @@ private void ProcessInterFluxBurnRequests(List deposits, int inspectFo this.conversionRequestRepository.Save(burnRequest); - this.logger.Info($"Marking burn request '{burnRequest.RequestId}' to '{burnRequest.DestinationAddress}' as processed at height {inspectForDepositsAtHeight}."); + this.logger.LogInformation($"Marking burn request '{burnRequest.RequestId}' to '{burnRequest.DestinationAddress}' as processed at height {inspectForDepositsAtHeight}."); continue; } @@ -165,7 +166,7 @@ private void ProcessInterFluxBurnRequests(List deposits, int inspectFo if (this.depositsBeingProcessedWithinMaturingWindow.Contains(deposit.Id)) { - this.logger.Debug($"Burn request '{burnRequest.RequestId}' is already being processed within the maturity window."); + this.logger.LogDebug($"Burn request '{burnRequest.RequestId}' is already being processed within the maturity window."); continue; } @@ -173,7 +174,7 @@ private void ProcessInterFluxBurnRequests(List deposits, int inspectFo this.depositsBeingProcessedWithinMaturingWindow.Add(deposit.Id); - this.logger.Info($"Re-injecting burn request '{burnRequest.RequestId}' to '{burnRequest.DestinationAddress}' that was processed at {burnRequest.BlockHeight} and will mature at {burnRequest.BlockHeight + requiredConfirmations}."); + this.logger.LogInformation($"Re-injecting burn request '{burnRequest.RequestId}' to '{burnRequest.DestinationAddress}' that was processed at {burnRequest.BlockHeight} and will mature at {burnRequest.BlockHeight + requiredConfirmations}."); continue; } @@ -209,15 +210,15 @@ private Deposit CreateDeposit(ConversionRequest burnRequest, int inspectForDepos } /// - public async Task ExtractDepositFromTransaction(Transaction transaction, int blockHeight, uint256 blockHash) + public Task ExtractDepositFromTransaction(Transaction transaction, int blockHeight, uint256 blockHash) { // If there are no deposits to the multsig (i.e. cross chain transfers) do nothing. if (!DepositValidationHelper.TryGetDepositsToMultisig(this.network, transaction, FederatedPegSettings.CrossChainTransferMinimum, out List depositsToMultisig)) - return null; + return Task.FromResult((IDeposit)null); // If there are deposits to the multsig (i.e. cross chain transfers), try and extract and validate the address by the specfied destination chain. if (!DepositValidationHelper.TryGetTarget(transaction, this.opReturnDataReader, out bool conversionTransaction, out string targetAddress, out int targetChain)) - return null; + return Task.FromResult((IDeposit)null); Money amount = depositsToMultisig.Sum(o => o.Value); @@ -227,11 +228,11 @@ public async Task ExtractDepositFromTransaction(Transaction transactio { if (this.federatedPegSettings.IsMainChain && amount < DepositValidationHelper.ConversionTransactionMinimum) { - this.logger.Warn($"Ignoring conversion transaction '{transaction.GetHash()}' with amount {amount} which is below the threshold of {DepositValidationHelper.ConversionTransactionMinimum}."); - return null; + this.logger.LogWarning($"Ignoring conversion transaction '{transaction.GetHash()}' with amount {amount} which is below the threshold of {DepositValidationHelper.ConversionTransactionMinimum}."); + return Task.FromResult((IDeposit)null); } - this.logger.Info("Received conversion deposit transaction '{0}' for an amount of {1}.", transaction.GetHash(), amount); + this.logger.LogInformation("Received conversion deposit transaction '{0}' for an amount of {1}.", transaction.GetHash(), amount); if (amount > this.federatedPegSettings.NormalDepositThresholdAmount) depositRetrievalType = DepositRetrievalType.ConversionLarge; @@ -250,7 +251,7 @@ public async Task ExtractDepositFromTransaction(Transaction transactio } } - return new Deposit(transaction.GetHash(), depositRetrievalType, amount, targetAddress, (DestinationChain)targetChain, blockHeight, blockHash); + return Task.FromResult((IDeposit)new Deposit(transaction.GetHash(), depositRetrievalType, amount, targetAddress, (DestinationChain)targetChain, blockHeight, blockHash)); } private DepositRetrievalType DetermineDepositRetrievalType(Money amount) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs b/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs index 463ddb4dd6..ceea5ba2c9 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs @@ -4,8 +4,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Controllers; using Stratis.Bitcoin.Primitives; @@ -112,7 +113,7 @@ public async Task>> RetrieveD // Don't process blocks below the requested maturity height. if (chainedHeaderBlock.ChainedHeader.Height < maturityHeight) { - this.logger.Debug("{0} below maturity height of {1}.", chainedHeaderBlock.ChainedHeader, maturityHeight); + this.logger.LogDebug("{0} below maturity height of {1}.", chainedHeaderBlock.ChainedHeader, maturityHeight); continue; } @@ -127,7 +128,7 @@ public async Task>> RetrieveD maturedDeposits.AddRange(this.RecallBlockDeposits(chainedHeaderBlock.ChainedHeader.Height - requiredConfirmations, retrievalType)); } - this.logger.Debug("{0} mature deposits retrieved from block '{1}'.", maturedDeposits.Count, chainedHeaderBlock.ChainedHeader); + this.logger.LogDebug("{0} mature deposits retrieved from block '{1}'.", maturedDeposits.Count, chainedHeaderBlock.ChainedHeader); result.Value.Add(new MaturedBlockDepositsModel(new MaturedBlockInfoModel() { @@ -183,13 +184,13 @@ private async Task RecordBlockDepositsAsync(ChainedHeaderBlock chainedHeaderBloc // Already have this recorded? if (this.deposits.TryGetValue(chainedHeaderBlock.ChainedHeader.Height, out BlockDeposits blockDeposits) && blockDeposits.BlockHash == chainedHeaderBlock.ChainedHeader.HashBlock) { - this.logger.Debug("Deposits already recorded for '{0}'.", chainedHeaderBlock.ChainedHeader); + this.logger.LogDebug("Deposits already recorded for '{0}'.", chainedHeaderBlock.ChainedHeader); return; } IReadOnlyList deposits = await this.depositExtractor.ExtractDepositsFromBlock(chainedHeaderBlock.Block, chainedHeaderBlock.ChainedHeader.Height, retrievalTypes).ConfigureAwait(false); - this.logger.Debug("{0} potential deposits extracted from block '{1}'.", deposits.Count, chainedHeaderBlock.ChainedHeader); + this.logger.LogDebug("{0} potential deposits extracted from block '{1}'.", deposits.Count, chainedHeaderBlock.ChainedHeader); this.deposits[chainedHeaderBlock.ChainedHeader.Height] = new BlockDeposits() { diff --git a/src/Stratis.Features.FederatedPeg/Stratis.Features.FederatedPeg.csproj b/src/Stratis.Features.FederatedPeg/Stratis.Features.FederatedPeg.csproj index 90577bd3cc..af3926d170 100644 --- a/src/Stratis.Features.FederatedPeg/Stratis.Features.FederatedPeg.csproj +++ b/src/Stratis.Features.FederatedPeg/Stratis.Features.FederatedPeg.csproj @@ -12,7 +12,7 @@ Full ..\None.ruleset Stratis Group Ltd. - 4.0.4.0 + 4.0.5.0 diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/CrossChainTransferStore.cs b/src/Stratis.Features.FederatedPeg/TargetChain/CrossChainTransferStore.cs index 60f97523aa..5b040b5f0f 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/CrossChainTransferStore.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/CrossChainTransferStore.cs @@ -8,8 +8,8 @@ using DBreeze; using DBreeze.DataTypes; using DBreeze.Utils; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.BlockStore; @@ -226,7 +226,7 @@ private ICrossChainTransfer[] ValidateCrossChainTransfers(ICrossChainTransfer[] List<(Transaction transaction, IWithdrawal withdrawal)> walletData = this.federationWalletManager.FindWithdrawalTransactions(partialTransfer.DepositTransactionId); - this.logger.Trace("DepositTransactionId:{0}; {1}:{2}", partialTransfer.DepositTransactionId, nameof(walletData), walletData.Count); + this.logger.LogTrace("DepositTransactionId:{0}; {1}:{2}", partialTransfer.DepositTransactionId, nameof(walletData), walletData.Count); if (walletData.Count == 1 && this.ValidateTransaction(walletData[0].transaction)) { @@ -248,7 +248,7 @@ private ICrossChainTransfer[] ValidateCrossChainTransfers(ICrossChainTransfer[] continue; } - this.logger.Debug("Templates don't match for {0} and {1}.", walletTran.GetHash(), partialTransfer.PartialTransaction.GetHash()); + this.logger.LogDebug("Templates don't match for {0} and {1}.", walletTran.GetHash(), partialTransfer.PartialTransaction.GetHash()); } // The chain may have been rewound so that this transaction or its UTXO's have been lost. @@ -256,14 +256,14 @@ private ICrossChainTransfer[] ValidateCrossChainTransfers(ICrossChainTransfer[] if (partialTransfer.DepositHeight < newChainATip) newChainATip = partialTransfer.DepositHeight ?? newChainATip; - this.logger.Debug("Setting DepositId {0} to Suspended", partialTransfer.DepositTransactionId); + this.logger.LogDebug("Setting DepositId {0} to Suspended", partialTransfer.DepositTransactionId); tracker.SetTransferStatus(partialTransfer, CrossChainTransferStatus.Suspended); } if (tracker.Count == 0) { - this.logger.Trace("(-)[NO_CHANGES_IN_TRACKER]"); + this.logger.LogTrace("(-)[NO_CHANGES_IN_TRACKER]"); return crossChainTransfers; } @@ -316,7 +316,7 @@ private ICrossChainTransfer[] ValidateCrossChainTransfers(ICrossChainTransfer[] /// Short reason/context code of failure. private void RollbackAndThrowTransactionError(DBreeze.Transactions.Transaction dbreezeTransaction, Exception exception, string reason = "FAILED_TRANSACTION") { - this.logger.Error("Error during database update: {0}, reason: {1}", exception.Message, reason); + this.logger.LogError("Error during database update: {0}, reason: {1}", exception.Message, reason); dbreezeTransaction.Rollback(); throw exception; @@ -375,7 +375,7 @@ public void RejectTransfer(ICrossChainTransfer crossChainTransfer) } catch (Exception err) { - this.logger.Error("An error occurred when processing deposits {0}", err); + this.logger.LogError("An error occurred when processing deposits {0}", err); this.RollbackAndThrowTransactionError(dbreezeTransaction, err, "REJECT_ERROR"); } @@ -402,13 +402,13 @@ public Task RecordLatestMatureDepositsAsync(IL if (maturedBlockDeposits.Count == 0 || maturedBlockDeposits.First().BlockInfo.BlockHeight != this.NextMatureDepositHeight) { - this.logger.Debug($"No viable blocks to process; {nameof(maturedBlockDeposits)}={maturedBlockDeposits.Count};{nameof(this.NextMatureDepositHeight)}={this.NextMatureDepositHeight}"); + this.logger.LogDebug($"No viable blocks to process; {nameof(maturedBlockDeposits)}={maturedBlockDeposits.Count};{nameof(this.NextMatureDepositHeight)}={this.NextMatureDepositHeight}"); return new RecordLatestMatureDepositsResult().Succeeded(); } if (maturedBlockDeposits.Last().BlockInfo.BlockHeight != this.NextMatureDepositHeight + maturedBlockDeposits.Count - 1) { - this.logger.Debug("(-)[DUPLICATE_BLOCKS]:true"); + this.logger.LogDebug("(-)[DUPLICATE_BLOCKS]:true"); return new RecordLatestMatureDepositsResult().Succeeded(); } @@ -419,7 +419,7 @@ public Task RecordLatestMatureDepositsAsync(IL { this.NextMatureDepositHeight += maturedBlockDeposits.Count; - this.logger.Debug("(-)[NO_DEPOSITS]:true"); + this.logger.LogDebug("(-)[NO_DEPOSITS]:true"); return new RecordLatestMatureDepositsResult().Succeeded(); } @@ -430,8 +430,8 @@ public Task RecordLatestMatureDepositsAsync(IL if (!this.Synchronize()) return; - this.logger.Info($"{maturedBlockDeposits.Count} blocks received, containing a total of {maturedBlockDeposits.SelectMany(d => d.Deposits).Where(a => a.Amount > 0).Count()} deposits."); - this.logger.Info($"Block Range : {maturedBlockDeposits.Min(a => a.BlockInfo.BlockHeight)} to {maturedBlockDeposits.Max(a => a.BlockInfo.BlockHeight)}."); + this.logger.LogInformation($"{maturedBlockDeposits.Count} blocks received, containing a total of {maturedBlockDeposits.SelectMany(d => d.Deposits).Where(a => a.Amount > 0).Count()} deposits."); + this.logger.LogInformation($"Block Range : {maturedBlockDeposits.Min(a => a.BlockInfo.BlockHeight)} to {maturedBlockDeposits.Max(a => a.BlockInfo.BlockHeight)}."); foreach (MaturedBlockDepositsModel maturedDeposit in maturedBlockDeposits) { @@ -447,7 +447,7 @@ public Task RecordLatestMatureDepositsAsync(IL if (!this.federationWalletManager.IsFederationWalletActive()) { - this.logger.Error("The store can't persist mature deposits while the federation is inactive."); + this.logger.LogError("The store can't persist mature deposits while the federation is inactive."); continue; } @@ -494,14 +494,14 @@ public Task RecordLatestMatureDepositsAsync(IL if (invalidRecipient) { - this.logger.Info("Invalid recipient."); + this.logger.LogInformation("Invalid recipient."); status = CrossChainTransferStatus.Rejected; } else if ((tracker.Count(t => t.Value == CrossChainTransferStatus.Partial) + this.depositsIdsByStatus[CrossChainTransferStatus.Partial].Count) >= this.settings.MaximumPartialTransactionThreshold) { haveSuspendedTransfers = true; - this.logger.Warn($"Partial transaction limit of {this.settings.MaximumPartialTransactionThreshold} reached, processing of deposits will continue once the partial transaction count falls below this value."); + this.logger.LogWarning($"Partial transaction limit of {this.settings.MaximumPartialTransactionThreshold} reached, processing of deposits will continue once the partial transaction count falls below this value."); } else { @@ -514,7 +514,7 @@ public Task RecordLatestMatureDepositsAsync(IL if (!this.ValidateTransaction(transaction)) { - this.logger.Info("Suspending transfer for deposit '{0}' to retry invalid transaction later.", deposit.Id); + this.logger.LogInformation("Suspending transfer for deposit '{0}' to retry invalid transaction later.", deposit.Id); this.federationWalletManager.RemoveWithdrawalTransactions(deposit.Id); haveSuspendedTransfers = true; @@ -528,27 +528,27 @@ public Task RecordLatestMatureDepositsAsync(IL } else { - this.logger.Info("Unable to build withdrawal transaction, suspending."); + this.logger.LogInformation("Unable to build withdrawal transaction, suspending."); haveSuspendedTransfers = true; } } } else { - this.logger.Info("Suspended flag set: '{0}'", deposit); + this.logger.LogInformation("Suspended flag set: '{0}'", deposit); } if (transfers[i] == null || transaction == null) { transfers[i] = new CrossChainTransfer(status, deposit.Id, scriptPubKey, deposit.Amount, maturedDeposit.BlockInfo.BlockHeight, transaction, null, null); tracker.SetTransferStatus(transfers[i]); - this.logger.Debug("Set {0} to {1}.", transfers[i]?.DepositTransactionId, status); + this.logger.LogDebug("Set {0} to {1}.", transfers[i]?.DepositTransactionId, status); } else { transfers[i].SetPartialTransaction(transaction); tracker.SetTransferStatus(transfers[i], CrossChainTransferStatus.Partial); - this.logger.Debug("Set {0} to Partial.", transfers[i]?.DepositTransactionId); + this.logger.LogDebug("Set {0} to Partial.", transfers[i]?.DepositTransactionId); } } @@ -581,7 +581,7 @@ public Task RecordLatestMatureDepositsAsync(IL } catch (Exception err) { - this.logger.Error("An error occurred when processing deposits {0}", err); + this.logger.LogError("An error occurred when processing deposits {0}", err); // Undo reserved UTXO's. if (walletUpdated) @@ -631,55 +631,55 @@ public Transaction MergeTransactionSignatures(uint256 depositId, Transaction[] p if (transfer == null) { - this.logger.Debug("(-)[MERGE_NOT_FOUND]:null"); + this.logger.LogDebug("(-)[MERGE_NOT_FOUND]:null"); return null; } if (transfer.Status != CrossChainTransferStatus.Partial) { - this.logger.Debug("(-)[MERGE_BAD_STATUS]:{0}={1}", nameof(transfer.Status), transfer.Status); + this.logger.LogDebug("(-)[MERGE_BAD_STATUS]:{0}={1}", nameof(transfer.Status), transfer.Status); return transfer.PartialTransaction; } try { - this.logger.Debug("Partial Transaction inputs:{0}", partialTransactions[0].Inputs.Count); - this.logger.Debug("Partial Transaction outputs:{0}", partialTransactions[0].Outputs.Count); + this.logger.LogDebug("Partial Transaction inputs:{0}", partialTransactions[0].Inputs.Count); + this.logger.LogDebug("Partial Transaction outputs:{0}", partialTransactions[0].Outputs.Count); for (int i = 0; i < partialTransactions[0].Inputs.Count; i++) { TxIn input = partialTransactions[0].Inputs[i]; - this.logger.Debug("Partial Transaction Input N:{0} : Hash:{1}", input.PrevOut.N, input.PrevOut.Hash); + this.logger.LogDebug("Partial Transaction Input N:{0} : Hash:{1}", input.PrevOut.N, input.PrevOut.Hash); } for (int i = 0; i < partialTransactions[0].Outputs.Count; i++) { TxOut output = partialTransactions[0].Outputs[i]; - this.logger.Debug("Partial Transaction Output Value:{0} : ScriptPubKey:{1}", output.Value, output.ScriptPubKey); + this.logger.LogDebug("Partial Transaction Output Value:{0} : ScriptPubKey:{1}", output.Value, output.ScriptPubKey); } - this.logger.Debug("Transfer Partial Transaction inputs:{0}", transfer.PartialTransaction.Inputs.Count); - this.logger.Debug("Transfer Partial Transaction outputs:{0}", transfer.PartialTransaction.Outputs.Count); + this.logger.LogDebug("Transfer Partial Transaction inputs:{0}", transfer.PartialTransaction.Inputs.Count); + this.logger.LogDebug("Transfer Partial Transaction outputs:{0}", transfer.PartialTransaction.Outputs.Count); for (int i = 0; i < transfer.PartialTransaction.Inputs.Count; i++) { TxIn transferInput = transfer.PartialTransaction.Inputs[i]; - this.logger.Debug("Transfer Partial Transaction Input N:{0} : Hash:{1}", transferInput.PrevOut.N, transferInput.PrevOut.Hash); + this.logger.LogDebug("Transfer Partial Transaction Input N:{0} : Hash:{1}", transferInput.PrevOut.N, transferInput.PrevOut.Hash); } for (int i = 0; i < transfer.PartialTransaction.Outputs.Count; i++) { TxOut transferOutput = transfer.PartialTransaction.Outputs[i]; - this.logger.Debug("Transfer Partial Transaction Output Value:{0} : ScriptPubKey:{1}", transferOutput.Value, transferOutput.ScriptPubKey); + this.logger.LogDebug("Transfer Partial Transaction Output Value:{0} : ScriptPubKey:{1}", transferOutput.Value, transferOutput.ScriptPubKey); } } catch (Exception err) { - this.logger.Debug("Failed to log transactions: {0}.", err.Message); + this.logger.LogDebug("Failed to log transactions: {0}.", err.Message); } - this.logger.Debug("Merging signatures for deposit : {0}", depositId); + this.logger.LogDebug("Merging signatures for deposit : {0}", depositId); var builder = new TransactionBuilder(this.network); Transaction oldTransaction = transfer.PartialTransaction; @@ -691,7 +691,7 @@ public Transaction MergeTransactionSignatures(uint256 depositId, Transaction[] p // We will finish dealing with the request here if an invalid signature is sent. // The incoming partial transaction will not have the same inputs / outputs as what our node has generated // so would have failed CrossChainTransfer.TemplatesMatch() and leave through here. - this.logger.Debug("(-)[MERGE_UNCHANGED_TX_HASHES_MATCH]"); + this.logger.LogDebug("(-)[MERGE_UNCHANGED_TX_HASHES_MATCH]"); return transfer.PartialTransaction; } @@ -706,13 +706,13 @@ public Transaction MergeTransactionSignatures(uint256 depositId, Transaction[] p if (this.ValidateTransaction(transfer.PartialTransaction, true)) { - this.logger.Debug("Deposit: {0} collected enough signatures and is FullySigned", transfer.DepositTransactionId); + this.logger.LogDebug("Deposit: {0} collected enough signatures and is FullySigned", transfer.DepositTransactionId); transfer.SetStatus(CrossChainTransferStatus.FullySigned); this.signals.Publish(new CrossChainTransferTransactionFullySigned(transfer)); } else { - this.logger.Debug("Deposit: {0} did not collect enough signatures and is Partial", transfer.DepositTransactionId); + this.logger.LogDebug("Deposit: {0} did not collect enough signatures and is Partial", transfer.DepositTransactionId); } this.PutTransfer(dbreezeTransaction, transfer); @@ -724,7 +724,7 @@ public Transaction MergeTransactionSignatures(uint256 depositId, Transaction[] p } catch (Exception err) { - this.logger.Error("Error: {0} ", err); + this.logger.LogError("Error: {0} ", err); // Restore expected store state in case the calling code retries / continues using the store. transfer.SetPartialTransaction(oldTransaction); @@ -747,10 +747,11 @@ public Transaction MergeTransactionSignatures(uint256 depositId, Transaction[] p /// identified in the blocks. /// /// The blocks used to update the store. Must be sorted by ascending height leading up to the new tip. + /// The chained headers corresponding to the blocks. private void Put(List blocks, Dictionary chainedHeadersSnapshot) { if (blocks.Count == 0) - this.logger.Trace("(-)[NO_BLOCKS]:0"); + this.logger.LogTrace("(-)[NO_BLOCKS]:0"); Dictionary transferLookup; Dictionary allWithdrawals; @@ -772,7 +773,7 @@ private void Put(List blocks, Dictionary chainedH // Exiting here and saving the tip after the sync. this.TipHashAndHeight = chainedHeadersSnapshot[blocks.Last().GetHash()]; - this.logger.Trace("(-)[NO_DEPOSIT_IDS]"); + this.logger.LogTrace("(-)[NO_DEPOSIT_IDS]"); return; } @@ -857,7 +858,7 @@ private void RewindIfRequiredLocked() { if (this.TipHashAndHeight == null) { - this.logger.Trace("(-)[CCTS_TIP_NOT_SET]"); + this.logger.LogTrace("(-)[CCTS_TIP_NOT_SET]"); return; } @@ -866,7 +867,7 @@ private void RewindIfRequiredLocked() // Indicates that the CCTS is synchronized with the Federation Wallet. if (this.TipHashAndHeight.HashBlock == tipToChase.Hash) { - this.logger.Trace("(-)[SYNCHRONIZED]"); + this.logger.LogTrace("(-)[SYNCHRONIZED]"); return; } @@ -954,8 +955,8 @@ private bool Synchronize() { if (this.TipHashAndHeight == null) { - this.logger.Error("Synchronization failed as the store's tip is null."); - this.logger.Trace("(-)[CCTS_TIP_NOT_SET]:false"); + this.logger.LogError("Synchronization failed as the store's tip is null."); + this.logger.LogTrace("(-)[CCTS_TIP_NOT_SET]:false"); return false; } @@ -964,15 +965,15 @@ private bool Synchronize() // Check if the federation wallet's tip is on chain, if not exit. if (this.chainIndexer.GetHeader(federationWalletTip.Hash) == null) { - this.logger.Debug("Synchronization failed as the federation wallet tip is not on chain; {0}='{1}', {2}='{3}'", nameof(this.chainIndexer.Tip), this.chainIndexer.Tip, nameof(federationWalletTip), federationWalletTip); - this.logger.Trace("(-)[FED_WALLET_TIP_NOT_ONCHAIN]:false"); + this.logger.LogDebug("Synchronization failed as the federation wallet tip is not on chain; {0}='{1}', {2}='{3}'", nameof(this.chainIndexer.Tip), this.chainIndexer.Tip, nameof(federationWalletTip), federationWalletTip); + this.logger.LogTrace("(-)[FED_WALLET_TIP_NOT_ONCHAIN]:false"); return false; } // If the federation wallet tip matches the store's tip, exit. if (federationWalletTip.Hash == this.TipHashAndHeight.HashBlock) { - this.logger.Trace("(-)[SYNCHRONIZED]:true"); + this.logger.LogTrace("(-)[SYNCHRONIZED]:true"); return true; } @@ -987,7 +988,7 @@ private bool Synchronize() } catch (Exception ex) { - this.logger.Error($"An error occurred whilst synchronizing the store: {ex}."); + this.logger.LogError($"An error occurred whilst synchronizing the store: {ex}."); throw ex; } } @@ -1042,7 +1043,7 @@ private bool SynchronizeBatch() { // If the federation tip is found to be not on chain, we need to throw an // exception to ensure that we exit the synchronization process. - this.logger.Trace("(-)[FEDERATION_WALLET_TIP_NOT_ON CHAIN]:{0}='{1}', {2}='{3}'", nameof(this.chainIndexer.Tip), this.chainIndexer.Tip, nameof(federationWalletTip), federationWalletTip); + this.logger.LogTrace("(-)[FEDERATION_WALLET_TIP_NOT_ON CHAIN]:{0}='{1}', {2}='{3}'", nameof(this.chainIndexer.Tip), this.chainIndexer.Tip, nameof(federationWalletTip), federationWalletTip); throw new FederationWalletTipNotOnChainException(); } @@ -1071,7 +1072,7 @@ private bool SynchronizeBatch() { Block lastBlock = blocks[availableBlocks - 1]; this.Put(blocks.GetRange(0, availableBlocks), chainedHeadersSnapshot); - this.logger.Info("Synchronized {0} blocks with cross-chain store to advance tip to block {1}", availableBlocks, this.TipHashAndHeight?.Height); + this.logger.LogInformation("Synchronized {0} blocks with cross-chain store to advance tip to block {1}", availableBlocks, this.TipHashAndHeight?.Height); } bool done = availableBlocks < SynchronizationBatchSize; @@ -1328,7 +1329,7 @@ private StatusChangeTracker OnDeleteBlocks(DBreeze.Transactions.Transaction dbre { // Transaction is no longer seen and the FederationWalletManager is going to remove the transaction anyhow // So don't prolong - just set to Suspended now. - this.logger.Debug("Setting DepositId {0} to Suspended", transfer.DepositTransactionId); + this.logger.LogDebug("Setting DepositId {0} to Suspended", transfer.DepositTransactionId); tracker.SetTransferStatus(transfer, CrossChainTransferStatus.Suspended); // Write the transfer status to the database. @@ -1489,7 +1490,7 @@ private void AddComponentStats(StringBuilder benchLog) { benchLog.AppendLine("--- Pending Withdrawals ---"); benchLog.AppendLine("Failed to retrieve data"); - this.logger.Error("Exception occurred while getting pending withdrawals: '{0}'.", exception.ToString()); + this.logger.LogError("Exception occurred while getting pending withdrawals: '{0}'.", exception.ToString()); } } @@ -1516,7 +1517,7 @@ public int DeleteSuspendedTransfers() { this.DeleteTransfer(dbreezeTransaction, transfer); this.depositsIdsByStatus[CrossChainTransferStatus.Suspended].Clear(); - this.logger.Debug($"Suspended transfer with deposit id '{transfer.DepositTransactionId}' deleted."); + this.logger.LogDebug($"Suspended transfer with deposit id '{transfer.DepositTransactionId}' deleted."); } dbreezeTransaction.Commit(); diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/FederatedPegBroadcaster.cs b/src/Stratis.Features.FederatedPeg/TargetChain/FederatedPegBroadcaster.cs index c3f35069cf..a1bc00b2c1 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/FederatedPegBroadcaster.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/FederatedPegBroadcaster.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using NLog; +using Microsoft.Extensions.Logging; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Connection; using Stratis.Bitcoin.P2P.Peer; using Stratis.Bitcoin.P2P.Protocol.Payloads; @@ -26,17 +27,17 @@ public FederatedPegBroadcaster( } /// - public async Task BroadcastAsync(Payload payload) + public Task BroadcastAsync(Payload payload) { IEnumerable connectedPeers = this.connectionManager.ConnectedPeers.Where(peer => (peer?.IsConnected ?? false) && this.federatedPegSettings.FederationNodeIpAddresses.Contains(peer.PeerEndPoint.Address)); - this.logger.Trace($"Sending {payload.GetType()} to {connectedPeers.Count()} peers."); + this.logger.LogTrace($"Sending {payload.GetType()} to {connectedPeers.Count()} peers."); Parallel.ForEach(connectedPeers, async (INetworkPeer peer) => { try { - this.logger.Trace($"Sending {payload.GetType()} to {peer.RemoteSocketAddress}"); + this.logger.LogTrace($"Sending {payload.GetType()} to {peer.RemoteSocketAddress}"); await peer.SendMessageAsync(payload).ConfigureAwait(false); } catch (OperationCanceledException) @@ -44,9 +45,11 @@ public async Task BroadcastAsync(Payload payload) } catch (Exception ex) { - this.logger.Error($"Error sending {payload.GetType().Name} to {peer.PeerEndPoint.Address}:{ex.ToString()}"); + this.logger.LogError($"Error sending {payload.GetType().Name} to {peer.PeerEndPoint.Address}:{ex.ToString()}"); } }); + + return Task.CompletedTask; } } } diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 10f2e84ce9..e80cd98f6d 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -2,9 +2,10 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Features.PoA; using Stratis.Bitcoin.Interfaces; @@ -28,6 +29,7 @@ namespace Stratis.Features.FederatedPeg.TargetChain public interface IMaturedBlocksSyncManager : IDisposable { /// Starts requesting blocks from another chain. + /// The asynchronous task. Task StartAsync(); } @@ -120,37 +122,37 @@ protected async Task SyncDepositsAsync() // First ensure that the federation wallet is active. if (!this.federationWalletManager.IsFederationWalletActive()) { - this.logger.Info("The CCTS will start processing deposits once the federation wallet has been activated."); + this.logger.LogInformation("The CCTS will start processing deposits once the federation wallet has been activated."); return true; } // Then ensure that the node is out of IBD. if (this.initialBlockDownloadState.IsInitialBlockDownload()) { - this.logger.Info("The CCTS will start processing deposits once the node is out of IBD."); + this.logger.LogInformation("The CCTS will start processing deposits once the node is out of IBD."); return true; } // Then ensure that the federation wallet is synced with the chain. if (!this.federationWalletManager.IsSyncedWithChain()) { - this.logger.Info($"The CCTS will start processing deposits once the federation wallet is synced with the chain; height {this.federationWalletManager.WalletTipHeight}"); + this.logger.LogInformation($"The CCTS will start processing deposits once the federation wallet is synced with the chain; height {this.federationWalletManager.WalletTipHeight}"); return true; } - this.logger.Info($"Requesting deposits from counterchain node."); + this.logger.LogInformation($"Requesting deposits from counterchain node."); SerializableResult> matureBlockDeposits = await this.federationGatewayClient.GetMaturedBlockDepositsAsync(this.crossChainTransferStore.NextMatureDepositHeight, this.nodeLifetime.ApplicationStopping).ConfigureAwait(false); if (matureBlockDeposits == null) { - this.logger.Debug("Failed to fetch normal deposits from counter chain node; {0} didn't respond.", this.federationGatewayClient.EndpointUrl); + this.logger.LogDebug("Failed to fetch normal deposits from counter chain node; {0} didn't respond.", this.federationGatewayClient.EndpointUrl); return true; } if (matureBlockDeposits.Value == null) { - this.logger.Debug("Failed to fetch normal deposits from counter chain node; {0} didn't reply with any deposits; Message: {1}", this.federationGatewayClient.EndpointUrl, matureBlockDeposits.Message ?? "none"); + this.logger.LogDebug("Failed to fetch normal deposits from counter chain node; {0} didn't reply with any deposits; Message: {1}", this.federationGatewayClient.EndpointUrl, matureBlockDeposits.Message ?? "none"); return true; } @@ -162,7 +164,7 @@ private async Task ProcessMatureBlockDepositsAsync(SerializableResult ProcessMatureBlockDepositsAsync(SerializableResult ProcessMatureBlockDepositsAsync(SerializableResult(); if (maturedBlockDeposit.Deposits.Count > 0) - this.logger.Debug("Matured deposit count for block {0} height {1}: {2}.", maturedBlockDeposit.BlockInfo.BlockHash, maturedBlockDeposit.BlockInfo.BlockHeight, maturedBlockDeposit.Deposits.Count); + this.logger.LogDebug("Matured deposit count for block {0} height {1}: {2}.", maturedBlockDeposit.BlockInfo.BlockHash, maturedBlockDeposit.BlockInfo.BlockHeight, maturedBlockDeposit.Deposits.Count); foreach (IDeposit potentialConversionTransaction in maturedBlockDeposit.Deposits) { @@ -194,24 +196,24 @@ private async Task ProcessMatureBlockDepositsAsync(SerializableResult ProcessMatureBlockDepositsAsync(SerializableResult= potentialConversionTransaction.Amount) { - this.logger.Warn("Conversion transaction '{0}' is no longer large enough to cover the fee.", potentialConversionTransaction.Id); + this.logger.LogWarning("Conversion transaction '{0}' is no longer large enough to cover the fee.", potentialConversionTransaction.Id); continue; } // We insert the fee distribution as a deposit to be processed, albeit with a special address. // Deposits with this address as their destination will be distributed between the multisig members. // Note that it will be actioned immediately as a matured deposit. - this.logger.Info("Adding conversion fee distribution for transaction '{0}' to deposit list.", potentialConversionTransaction.Id); + this.logger.LogInformation("Adding conversion fee distribution for transaction '{0}' to deposit list.", potentialConversionTransaction.Id); // Instead of being a conversion deposit, the fee distribution is translated to its non-conversion equivalent. DepositRetrievalType depositType = DepositRetrievalType.Small; @@ -268,7 +270,7 @@ private async Task ProcessMatureBlockDepositsAsync(SerializableResult ProcessMatureBlockDepositsAsync(SerializableResult CompletedTransactions(IEnumerable } } - private async Task CleanMempoolAsync() + private Task CleanMempoolAsync() { List transactionsToCheck = this.mempoolOrphans.OrphansList().Select(i => i.Tx).ToList(); if (transactionsToCheck.Count == 0) - return; + return Task.CompletedTask; List completedTransactions = null; @@ -126,8 +127,10 @@ private async Task CleanMempoolAsync() { this.mempoolOrphans.RemoveForBlock(transactionsToRemove); - this.logger.Debug("Removed {0} transactions from mempool", transactionsToRemove.Count); + this.logger.LogDebug("Removed {0} transactions from mempool", transactionsToRemove.Count); } + + return Task.CompletedTask; } /// diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/PartialTransactionRequester.cs b/src/Stratis.Features.FederatedPeg/TargetChain/PartialTransactionRequester.cs index 732195777c..ac9d8bdcf4 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/PartialTransactionRequester.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/PartialTransactionRequester.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using NLog; +using Microsoft.Extensions.Logging; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Interfaces; using Stratis.Bitcoin.Utilities; using Stratis.Features.FederatedPeg.InputConsolidation; @@ -70,19 +71,19 @@ public async Task BroadcastPartialTransactionsAsync() { if (this.ibdState.IsInitialBlockDownload() || !this.federationWalletManager.IsFederationWalletActive()) { - this.logger.Info("Federation wallet isn't active or in IBD. Not attempting to request transaction signatures."); + this.logger.LogInformation("Federation wallet isn't active or in IBD. Not attempting to request transaction signatures."); return; } // Broadcast the partial transaction with the earliest inputs. IEnumerable partialtransfers = this.crossChainTransferStore.GetTransfersByStatus(new[] { CrossChainTransferStatus.Partial }, true); - this.logger.Info($"Requesting partial templates for {partialtransfers.Count()} transfers."); + this.logger.LogInformation($"Requesting partial templates for {partialtransfers.Count()} transfers."); foreach (ICrossChainTransfer transfer in partialtransfers) { await this.federatedPegBroadcaster.BroadcastAsync(new RequestPartialTransactionPayload(transfer.DepositTransactionId).AddPartial(transfer.PartialTransaction)).ConfigureAwait(false); - this.logger.Debug("Partial template requested for deposit Id '{0}'", transfer.DepositTransactionId); + this.logger.LogDebug("Partial template requested for deposit Id '{0}'", transfer.DepositTransactionId); } // If we don't have any broadcastable transactions, check if we have any consolidating transactions to sign. @@ -97,7 +98,7 @@ public async Task BroadcastPartialTransactionsAsync() if (toSign != null) { await this.federatedPegBroadcaster.BroadcastAsync(new RequestPartialTransactionPayload(RequestPartialTransactionPayload.ConsolidationDepositId).AddPartial(toSign.PartialTransaction)).ConfigureAwait(false); - this.logger.Debug("Partial consolidating transaction requested for '{0}'.", toSign.PartialTransaction.GetHash()); + this.logger.LogDebug("Partial consolidating transaction requested for '{0}'.", toSign.PartialTransaction.GetHash()); } } } diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/SignedMultisigTransactionBroadcaster.cs b/src/Stratis.Features.FederatedPeg/TargetChain/SignedMultisigTransactionBroadcaster.cs index 457c5e0742..fd8406d060 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/SignedMultisigTransactionBroadcaster.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/SignedMultisigTransactionBroadcaster.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; -using NLog; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.MemoryPool; using Stratis.Bitcoin.Features.Wallet.Broadcasting; using Stratis.Bitcoin.Features.Wallet.Interfaces; @@ -22,7 +23,7 @@ public interface ISignedMultisigTransactionBroadcaster : IDisposable /// /// Enables the node operator to try and manually push fully signed transactions. /// - /// + /// The asynchronous task returning . Task BroadcastFullySignedTransfersAsync(); /// @@ -87,7 +88,7 @@ public async Task BroadcastFullySigned if (fullySignedTransfers.Length == 0) { - this.logger.Debug("There are no fully signed transactions to broadcast."); + this.logger.LogDebug("There are no fully signed transactions to broadcast."); return new SignedMultisigTransactionBroadcastResult() { Message = "There are no fully signed transactions to broadcast." }; } @@ -112,12 +113,12 @@ private async Task BroadcastFullyS TxMempoolInfo txMempoolInfo = await this.mempoolManager.InfoAsync(crossChainTransfer.PartialTransaction.GetHash()).ConfigureAwait(false); if (txMempoolInfo != null) { - this.logger.Info("Deposit '{0}' already in the mempool.", crossChainTransfer.DepositTransactionId); + this.logger.LogInformation("Deposit '{0}' already in the mempool.", crossChainTransfer.DepositTransactionId); transferItem.ItemMessage = $"Deposit '{crossChainTransfer.DepositTransactionId}' already in the mempool."; return transferItem; } - this.logger.Info("Broadcasting deposit '{0}', a signed multisig transaction '{1}' to the network.", crossChainTransfer.DepositTransactionId, crossChainTransfer.PartialTransaction.GetHash()); + this.logger.LogInformation("Broadcasting deposit '{0}', a signed multisig transaction '{1}' to the network.", crossChainTransfer.DepositTransactionId, crossChainTransfer.PartialTransaction.GetHash()); await this.broadcasterManager.BroadcastTransactionAsync(crossChainTransfer.PartialTransaction).ConfigureAwait(false); @@ -132,7 +133,7 @@ private async Task BroadcastFullyS if (transactionBroadCastEntry.TransactionBroadcastState == TransactionBroadcastState.CantBroadcast && !CrossChainTransferStore.IsMempoolErrorRecoverable(transactionBroadCastEntry.MempoolError)) { - this.logger.Warn("Deposit '{0}' rejected: '{1}'.", crossChainTransfer.DepositTransactionId, transactionBroadCastEntry.ErrorMessage); + this.logger.LogWarning("Deposit '{0}' rejected: '{1}'.", crossChainTransfer.DepositTransactionId, transactionBroadCastEntry.ErrorMessage); this.crossChainTransferStore.RejectTransfer(crossChainTransfer); transferItem.ItemMessage = $"Deposit '{crossChainTransfer.DepositTransactionId}' rejected: '{transactionBroadCastEntry.ErrorMessage}'."; return transferItem; diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/WithdrawalTransactionBuilder.cs b/src/Stratis.Features.FederatedPeg/TargetChain/WithdrawalTransactionBuilder.cs index 3c442e6eab..6576252630 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/WithdrawalTransactionBuilder.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/WithdrawalTransactionBuilder.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.Wallet; using Stratis.Bitcoin.Signals; using Stratis.Features.FederatedPeg.Distribution; @@ -64,7 +65,7 @@ public Transaction BuildWithdrawalTransaction(int blockHeight, uint256 depositId { try { - this.logger.Debug("BuildDeterministicTransaction depositId(opReturnData)={0}; recipient.ScriptPubKey={1}; recipient.Amount={2}; height={3}", depositId, recipient.ScriptPubKey, recipient.Amount, blockHeight); + this.logger.LogDebug("BuildDeterministicTransaction depositId(opReturnData)={0}; recipient.ScriptPubKey={1}; recipient.Amount={2}; height={3}", depositId, recipient.ScriptPubKey, recipient.Amount, blockHeight); // Build the multisig transaction template. uint256 opReturnData = depositId; @@ -99,7 +100,7 @@ public Transaction BuildWithdrawalTransaction(int blockHeight, uint256 depositId if (recipient.ScriptPubKey == this.conversionTransactionFeeDistributionScriptPubKey) { - this.logger.Debug("Generating recipient list for conversion transaction fee distribution."); + this.logger.LogDebug("Generating recipient list for conversion transaction fee distribution."); multiSigContext.Recipients = this.distributionManager.DistributeToMultisigNodes(blockHeight, recipient.WithPaymentReducedByFee(FederatedPegSettings.CrossChainTransferFee).Amount); } @@ -111,9 +112,9 @@ public Transaction BuildWithdrawalTransaction(int blockHeight, uint256 depositId if (coins.Count > FederatedPegSettings.MaxInputs) { - this.logger.Debug("Too many inputs. Triggering the consolidation process."); + this.logger.LogDebug("Too many inputs. Triggering the consolidation process."); this.signals.Publish(new WalletNeedsConsolidation(recipient.Amount)); - this.logger.Trace("(-)[CONSOLIDATING_INPUTS]"); + this.logger.LogTrace("(-)[CONSOLIDATING_INPUTS]"); return null; } @@ -123,7 +124,7 @@ public Transaction BuildWithdrawalTransaction(int blockHeight, uint256 depositId // Build the transaction. Transaction transaction = this.federationWalletTransactionHandler.BuildTransaction(multiSigContext); - this.logger.Debug("transaction = {0}", transaction.ToString(this.network, RawFormat.BlockExplorer)); + this.logger.LogDebug("transaction = {0}", transaction.ToString(this.network, RawFormat.BlockExplorer)); return transaction; } @@ -133,15 +134,15 @@ public Transaction BuildWithdrawalTransaction(int blockHeight, uint256 depositId (walletException.Message == FederationWalletTransactionHandler.NoSpendableTransactionsMessage || walletException.Message == FederationWalletTransactionHandler.NotEnoughFundsMessage)) { - this.logger.Warn("Not enough spendable transactions in the wallet. Should be resolved when a pending transaction is included in a block."); + this.logger.LogWarning("Not enough spendable transactions in the wallet. Should be resolved when a pending transaction is included in a block."); } else { - this.logger.Error("Could not create transaction for deposit {0}: {1}", depositId, error.Message); + this.logger.LogError("Could not create transaction for deposit {0}: {1}", depositId, error.Message); } } - this.logger.Trace("(-)[FAIL]"); + this.logger.LogTrace("(-)[FAIL]"); return null; } } diff --git a/src/Stratis.Features.FederatedPeg/Wallet/BlockQueueProcessor.cs b/src/Stratis.Features.FederatedPeg/Wallet/BlockQueueProcessor.cs index e4a6c6921d..651298aeb4 100644 --- a/src/Stratis.Features.FederatedPeg/Wallet/BlockQueueProcessor.cs +++ b/src/Stratis.Features.FederatedPeg/Wallet/BlockQueueProcessor.cs @@ -1,9 +1,10 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.Wallet; using Stratis.Bitcoin.Utilities; @@ -51,7 +52,7 @@ private async Task OnProcessBlockAsync(Block block, CancellationToken cancellati { long currentBlockQueueSize = Interlocked.Add(ref this.blocksQueueSize, -block.BlockSize.Value); - this.logger.Debug("Block '{0}' queued for processing. Queue size changed to {1} bytes.", block.GetHash(), currentBlockQueueSize); + this.logger.LogDebug("Block '{0}' queued for processing. Queue size changed to {1} bytes.", block.GetHash(), currentBlockQueueSize); await this.callback(block, cancellationToken).ConfigureAwait(false); } @@ -72,7 +73,7 @@ public bool TryEnqueue(Block block) if (this.blocksQueueSize >= this.MaxQueueSize) { this.maxQueueSizeReached = true; - this.logger.Trace("(-)[REACHED_MAX_QUEUE_SIZE]"); + this.logger.LogTrace("(-)[REACHED_MAX_QUEUE_SIZE]"); } } else @@ -84,7 +85,7 @@ public bool TryEnqueue(Block block) if (!this.maxQueueSizeReached) { long currentBlockQueueSize = Interlocked.Add(ref this.blocksQueueSize, block.BlockSize.Value); - this.logger.Debug("Queue sized changed to {0} bytes.", currentBlockQueueSize); + this.logger.LogDebug("Queue sized changed to {0} bytes.", currentBlockQueueSize); this.blocksQueue.Enqueue(block); diff --git a/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletManager.cs b/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletManager.cs index 693b2a8125..35ab18c06a 100644 --- a/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletManager.cs +++ b/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletManager.cs @@ -4,9 +4,9 @@ using System.Security; using System.Text; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; using NBitcoin.Policy; -using NLog; using Stratis.Bitcoin.AsyncWork; using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Configuration.Logging; @@ -186,6 +186,7 @@ private Dictionary GetSpendingTransactions(IEnumer { SpendingDetails spendingDetail = transactionData.SpendingDetails; + // There is no transaction or its already resolved. if (spendingDetail?.TransactionId == null || spendingDetail.Transaction != null) { res.Add(transactionData, spendingDetail?.Transaction); @@ -283,7 +284,7 @@ public void Start() this.asyncLoop = this.asyncProvider.CreateAndRunAsyncLoop("wallet persist job", token => { this.SaveWallet(); - this.logger.Info("Wallets saved to file at {0}.", this.dateTimeProvider.GetUtcNow()); + this.logger.LogInformation("Wallets saved to file at {0}.", this.dateTimeProvider.GetUtcNow()); return Task.CompletedTask; }, @@ -310,13 +311,13 @@ public HashHeightPair LastBlockSyncedHashHeight() { if (!this.IsWalletActive()) { - this.logger.Trace("(-)[NO_WALLET]:{0}='{1}'", nameof(this.chainIndexer.Tip), this.chainIndexer.Tip); + this.logger.LogTrace("(-)[NO_WALLET]:{0}='{1}'", nameof(this.chainIndexer.Tip), this.chainIndexer.Tip); return new HashHeightPair(this.chainIndexer.Tip); } if (this.Wallet.LastBlockSyncedHash == null && this.Wallet.LastBlockSyncedHeight == null) { - this.logger.Trace("(-)[WALLET_SYNC_BLOCK_NOT_SET]:{0}='{1}'", nameof(this.chainIndexer.Tip), this.chainIndexer.Tip); + this.logger.LogTrace("(-)[WALLET_SYNC_BLOCK_NOT_SET]:{0}='{1}'", nameof(this.chainIndexer.Tip), this.chainIndexer.Tip); return new HashHeightPair(this.chainIndexer.Tip); } @@ -345,13 +346,13 @@ public void RemoveBlocks(ChainedHeader fork) lock (this.lockObject) { - this.logger.Debug("Removing blocks back to height {0} from {1}.", fork.Height, this.LastBlockSyncedHashHeight().Height); + this.logger.LogDebug("Removing blocks back to height {0} from {1}.", fork.Height, this.LastBlockSyncedHashHeight().Height); // Remove all the UTXO that have been reorged. IEnumerable makeUnspendable = this.Wallet.MultiSigAddress.Transactions.Where(w => w.BlockHeight > fork.Height).ToList(); foreach (TransactionData transactionData in makeUnspendable) { - this.logger.Debug("Removing reorged tx '{0}'.", transactionData.Id); + this.logger.LogDebug("Removing reorged tx '{0}'.", transactionData.Id); this.Wallet.MultiSigAddress.Transactions.Remove(transactionData); if (transactionData.SpendingDetails != null) @@ -362,7 +363,7 @@ public void RemoveBlocks(ChainedHeader fork) IEnumerable makeSpendable = this.Wallet.MultiSigAddress.Transactions.Where(w => (w.SpendingDetails != null) && (w.SpendingDetails.BlockHeight > fork.Height)); foreach (TransactionData transactionData in makeSpendable) { - this.logger.Debug("Unspend transaction '{0}'.", transactionData.Id); + this.logger.LogDebug("Unspend transaction '{0}'.", transactionData.Id); transactionData.SpendingDetails = null; } @@ -402,20 +403,20 @@ public void ProcessBlock(Block block, ChainedHeader chainedHeader) if (!this.IsWalletActive(chainedHeader.Height)) { this.WalletTipHash = chainedHeader.HashBlock; - this.logger.Trace("(-)[NO_WALLET]"); + this.logger.LogTrace("(-)[NO_WALLET]"); return; } // Is this the next block. if (chainedHeader.Header.HashPrevBlock != this.WalletTipHash) { - this.logger.Debug("New block's previous hash '{0}' does not match current wallet's tip hash '{1}'.", chainedHeader.Header.HashPrevBlock, this.WalletTipHash); + this.logger.LogDebug("New block's previous hash '{0}' does not match current wallet's tip hash '{1}'.", chainedHeader.Header.HashPrevBlock, this.WalletTipHash); // The block coming in to the wallet should never be ahead of the wallet. // If the block is behind, let it pass. if (chainedHeader.Height > this.WalletTipHeight) { - this.logger.Trace("(-)[BLOCK_TOO_FAR]"); + this.logger.LogTrace("(-)[BLOCK_TOO_FAR]"); throw new WalletException("block too far in the future has arrived to the wallet"); } } @@ -448,7 +449,7 @@ public bool ProcessTransaction(Transaction transaction, int? blockHeight = null, { if (!this.IsWalletActive()) { - this.logger.Trace("(-)"); + this.logger.LogTrace("(-)"); return false; } @@ -479,14 +480,14 @@ public bool ProcessTransaction(Transaction transaction, int? blockHeight = null, List<(Transaction transaction, IWithdrawal withdrawal)> walletData = this.FindWithdrawalTransactions(withdrawal.DepositId); if ((walletData.Count == 1) && (walletData[0].withdrawal.BlockNumber != 0)) { - this.logger.Debug("Deposit '{0}' already included in block.", withdrawal.DepositId); + this.logger.LogDebug("Deposit '{0}' already included in block.", withdrawal.DepositId); return false; } // Remove this to prevent duplicates if the transaction hash has changed. if (walletData.Count != 0) { - this.logger.Debug("Removing duplicates for '{0}'.", withdrawal.DepositId); + this.logger.LogDebug("Removing duplicates for '{0}'.", withdrawal.DepositId); this.RemoveWithdrawalTransactions(withdrawal.DepositId); } } @@ -547,7 +548,7 @@ public bool CleanTransactionsPastMaxReorg(int crossChainTransferStoreTip) if (this.network.Consensus.MaxReorgLength == 0 || this.Wallet.MultiSigAddress.Transactions.Count <= MinimumRetainedTransactions) { - this.logger.Debug("Skipping clean up of federation wallet. {0}={1};{2}={3}", nameof(this.network.Consensus.MaxReorgLength), this.network.Consensus.MaxReorgLength, nameof(this.Wallet.MultiSigAddress.Transactions), this.Wallet.MultiSigAddress.Transactions.Count); + this.logger.LogDebug("Skipping clean up of federation wallet. {0}={1};{2}={3}", nameof(this.network.Consensus.MaxReorgLength), this.network.Consensus.MaxReorgLength, nameof(this.Wallet.MultiSigAddress.Transactions), this.Wallet.MultiSigAddress.Transactions.Count); return walletUpdated; } @@ -572,7 +573,7 @@ public bool CleanTransactionsPastMaxReorg(int crossChainTransferStoreTip) break; } - this.logger.Debug("Cleaned {0} transactions older than the CCTS tip less max reorg of {1}.", transactionsPastMaxReorg.Count, crossChainTransferStoreTip); + this.logger.LogDebug("Cleaned {0} transactions older than the CCTS tip less max reorg of {1}.", transactionsPastMaxReorg.Count, crossChainTransferStoreTip); return walletUpdated; } @@ -595,7 +596,7 @@ private bool RemoveTransaction(Transaction transaction) if (spentTransaction.SpendingDetails != null) { // Get the transaction being spent and unspend it. - this.logger.Debug("Unspending transaction {0}-{1}.", spentTransaction.Id, spentTransaction.Index); + this.logger.LogDebug("Unspending transaction {0}-{1}.", spentTransaction.Id, spentTransaction.Index); this.RemoveAssociatedUnconfirmedSpentByTransaction(spentTransaction.SpendingDetails.TransactionId); spentTransaction.SpendingDetails = null; updatedWallet = true; @@ -612,7 +613,7 @@ private bool RemoveTransaction(Transaction transaction) // Remove any UTXO's that were provided by this transaction from wallet. if (this.Wallet.MultiSigAddress.Transactions.TryGetTransaction(hash, index, out TransactionData foundTransaction)) { - this.logger.Debug("Removing transaction {0}-{1}.", foundTransaction.Id, foundTransaction.Index); + this.logger.LogDebug("Removing transaction {0}-{1}.", foundTransaction.Id, foundTransaction.Index); this.Wallet.MultiSigAddress.Transactions.Remove(foundTransaction); updatedWallet = true; } @@ -630,18 +631,18 @@ private void RemoveAssociatedUnconfirmedSpentByTransaction(uint256 transactionId { if (!this.Wallet.MultiSigAddress.Transactions.TryGetTransaction(transactionId, 0, out TransactionData transactionData)) { - this.logger.Debug("Spending transaction '{0}' does not exist.", transactionId); + this.logger.LogDebug("Spending transaction '{0}' does not exist.", transactionId); return; } if (transactionData.IsConfirmed()) { - this.logger.Debug("Spending transaction '{0}' was not removed as it is already confirmed.", transactionId); + this.logger.LogDebug("Spending transaction '{0}' was not removed as it is already confirmed.", transactionId); return; } this.Wallet.MultiSigAddress.Transactions.Remove(transactionData); - this.logger.Debug("'{0}' was removed.", transactionId); + this.logger.LogDebug("'{0}' was removed.", transactionId); } /// @@ -687,7 +688,7 @@ private void AddTransactionToWallet(Transaction transaction, TxOut utxo, int? bl int index = transaction.Outputs.IndexOf(utxo); if (!this.Wallet.MultiSigAddress.Transactions.TryGetTransaction(transactionHash, index, out TransactionData foundTransaction)) { - this.logger.Debug("Transaction '{0}-{1}' not found, creating. BlockHeight={2}, BlockHash={3}", transactionHash, index, blockHeight, blockHash); + this.logger.LogDebug("Transaction '{0}-{1}' not found, creating. BlockHeight={2}, BlockHash={3}", transactionHash, index, blockHeight, blockHash); TransactionData newTransaction = new TransactionData { @@ -704,7 +705,7 @@ private void AddTransactionToWallet(Transaction transaction, TxOut utxo, int? bl } else { - this.logger.Debug("Transaction '{0}-{1}' found, updating. BlockHeight={2}, BlockHash={3}.", transactionHash, index, blockHeight, blockHash); + this.logger.LogDebug("Transaction '{0}-{1}' found, updating. BlockHeight={2}, BlockHash={3}.", transactionHash, index, blockHeight, blockHash); // Update the block height and block hash. if ((foundTransaction.BlockHeight == null) && (blockHeight != null)) @@ -732,6 +733,7 @@ private void AddTransactionToWallet(Transaction transaction, TxOut utxo, int? bl /// Height of the block. /// Hash of the block. /// The block containing the transaction to add. + /// The withdrawal (if any), otherwise null. private void AddSpendingTransactionToWalletLocked(Transaction transaction, IEnumerable paidToOutputs, uint256 spendingTransactionId, @@ -749,7 +751,7 @@ private void AddSpendingTransactionToWalletLocked(Transaction transaction, if (!this.Wallet.MultiSigAddress.Transactions.TryGetTransaction(spendingTransactionId, spendingTransactionIndex, out TransactionData spendingTransaction)) { // Strange, why would it be null? - this.logger.Trace("(-)[TX_NULL]"); + this.logger.LogTrace("(-)[TX_NULL]"); return; } @@ -757,19 +759,19 @@ private void AddSpendingTransactionToWalletLocked(Transaction transaction, { // If the spending tx's spending details are confirmed and this is coming in unconfirmed, ignore. // This is probably an unlucky concurrency issues, e.g. tx from mempool coming in after confirmed in a block. - this.logger.Debug("Unconfirmed spending UTXO '{0}-{1}' is being ignored as it is already confirmed in block {2}", spendingTransactionId, spendingTransactionIndex, spendingTransaction.SpendingDetails.BlockHeight); + this.logger.LogDebug("Unconfirmed spending UTXO '{0}-{1}' is being ignored as it is already confirmed in block {2}", spendingTransactionId, spendingTransactionIndex, spendingTransaction.SpendingDetails.BlockHeight); return; } // If spending details is null, always set new spending details. if (spendingTransaction.SpendingDetails == null) - this.logger.Debug("Spending UTXO '{0}-{1}' is new at height {2}, spending with tx '{3}'.", spendingTransactionId, spendingTransactionIndex, blockHeight, transaction.GetHash()); + this.logger.LogDebug("Spending UTXO '{0}-{1}' is new at height {2}, spending with tx '{3}'.", spendingTransactionId, spendingTransactionIndex, blockHeight, transaction.GetHash()); // If there are unconfirmed existing spending details, always overwrite with new one. // Could be a "more" signed tx, a FullySigned mempool tx or a confirmed block tx. if (spendingTransaction.SpendingDetails != null && spendingTransaction.SpendingDetails.BlockHeight == null) { - this.logger.Debug("Spending UTXO '{0}-{1}' has unconfirmed spending details at height {2}, spending with tx '{3}'.", spendingTransactionId, spendingTransactionIndex, blockHeight, transaction.GetHash()); + this.logger.LogDebug("Spending UTXO '{0}-{1}' has unconfirmed spending details at height {2}, spending with tx '{3}'.", spendingTransactionId, spendingTransactionIndex, blockHeight, transaction.GetHash()); //If we are overwriting existing spending details, remove the associated transaction as well. this.RemoveAssociatedUnconfirmedSpentByTransaction(spendingTransaction.SpendingDetails.TransactionId); @@ -777,14 +779,22 @@ private void AddSpendingTransactionToWalletLocked(Transaction transaction, // If the spending details are confirmed and this is also coming in confirmed, then update the spending details. if (spendingTransaction.SpendingDetails != null && spendingTransaction.SpendingDetails.BlockHeight != null && blockHeight != null) - this.logger.Debug("Spending UTXO '{0}-{1}' has confirmed spending details height {2}, spending with tx '{3}'.", spendingTransactionId, spendingTransactionIndex, blockHeight, transaction.GetHash()); + this.logger.LogDebug("Spending UTXO '{0}-{1}' has confirmed spending details height {2}, spending with tx '{3}'.", spendingTransactionId, spendingTransactionIndex, blockHeight, transaction.GetHash()); spendingTransaction.SpendingDetails = this.BuildSpendingDetails(transaction, paidToOutputs, blockHeight, blockHash, block, withdrawal); } /// - /// Creates a SpendingDetails object that we can spend an existing transaction with. + /// Creates a object for a spending transaction to be associated with a object. + /// The information includes selected transaction outputs as entries. Adds the block information if it has been included in a block. /// + /// The transaction providing the outputs. + /// A filtered list of the transaction outputs. + /// The block height of the transaction if its included in a block. + /// The block hash of the transaction if its included in a block. + /// The block of the transaction if its included in a block. + /// The withdrawal details if its a withdrawal transaction, otherwise null. + /// See . private SpendingDetails BuildSpendingDetails(Transaction transaction, IEnumerable paidToOutputs, int? blockHeight = null, @@ -836,6 +846,7 @@ private SpendingDetails BuildSpendingDetails(Transaction transaction, CreationTime = DateTimeOffset.FromUnixTimeSeconds(block?.Header.Time ?? this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp()), BlockHeight = blockHeight, BlockHash = blockHash, + // Only recording this if it can't be found in a block. Transaction = (blockHeight > 0) ? null : transaction, IsCoinStake = transaction.IsCoinStake == false ? (bool?)null : true }; @@ -902,7 +913,7 @@ public bool RemoveUnconfirmedTransactionData() /// public bool RemoveWithdrawalTransactions(uint256 depositId) { - this.logger.Debug("Removing transient transactions for depositId '{0}'.", depositId); + this.logger.LogDebug("Removing transient transactions for depositId '{0}'.", depositId); lock (this.lockObject) { @@ -949,7 +960,7 @@ private OutPoint EarliestOutput(Transaction transaction) if (spendingDetail.WithdrawalDetails == null) { - this.logger.Error($"Spending detail with txId '{spendingDetail.TransactionId}' has null withdrawal details, deposit id '{depositId}'"); + this.logger.LogError($"Spending detail with txId '{spendingDetail.TransactionId}' has null withdrawal details, deposit id '{depositId}'"); } var withdrawal = new Withdrawal( @@ -1047,7 +1058,7 @@ public ValidateTransactionResult ValidateTransaction(Transaction transaction, bo // Verify that the transaction has valid UTXOs. if (!this.TransactionHasValidUTXOs(transaction, coins)) { - this.logger.Error($"Transaction '{transaction.GetHash()}' does not have valid UTXOs."); + this.logger.LogError($"Transaction '{transaction.GetHash()}' does not have valid UTXOs."); return ValidateTransactionResult.Failed("Transaction does not have valid UTXOs."); } @@ -1063,7 +1074,7 @@ public ValidateTransactionResult ValidateTransaction(Transaction transaction, bo .FirstOrDefault(); if (oldestInput != null && DeterministicCoinOrdering.CompareTransactionData(earliestUnspent, oldestInput) < 0) { - this.logger.Error($"Earlier unspent UTXOs exist for tx '{transaction.GetHash()}'"); + this.logger.LogError($"Earlier unspent UTXOs exist for tx '{transaction.GetHash()}'"); return ValidateTransactionResult.Failed("Earlier unspent UTXOs exist."); } } @@ -1089,7 +1100,7 @@ public ValidateTransactionResult ValidateTransaction(Transaction transaction, bo // Trace the reason validation failed. Note that failure here doesn't mean an error necessarily. Just that the transaction is not fully signed. foreach (TransactionPolicyError transactionPolicyError in filteredErrors) { - this.logger.Debug("{0} FAILED - {1}", nameof(TransactionBuilder.Verify), transactionPolicyError.ToString()); + this.logger.LogDebug("{0} FAILED - {1}", nameof(TransactionBuilder.Verify), transactionPolicyError.ToString()); errorList.Add(transactionPolicyError.ToString()); } @@ -1124,7 +1135,7 @@ public bool ValidateConsolidatingTransaction(Transaction transaction, bool check // Trace the reason validation failed. Note that failure here doesn't mean an error necessarily. Just that the transaction is not fully signed. foreach (TransactionPolicyError transactionPolicyError in errors) { - this.logger.Info("{0} FAILED - {1}", nameof(TransactionBuilder.Verify), transactionPolicyError.ToString()); + this.logger.LogInformation("{0} FAILED - {1}", nameof(TransactionBuilder.Verify), transactionPolicyError.ToString()); } return false; @@ -1164,7 +1175,7 @@ public void UpdateLastBlockSyncedHeight(ChainedHeader chainedHeader) /// Thrown if wallet cannot be created. private FederationWallet GenerateWallet() { - this.logger.Debug("Generating the federation wallet file."); + this.logger.LogDebug("Generating the federation wallet file."); int lastBlockSyncedHeight = Math.Max(0, this.federatedPegSettings.WalletSyncFromHeight - 1); uint256 lastBlockSyncedHash = (lastBlockSyncedHeight <= this.chainIndexer.Height) ? this.chainIndexer[lastBlockSyncedHeight].HashBlock : null; @@ -1187,7 +1198,7 @@ private FederationWallet GenerateWallet() } }; - this.logger.Trace("(-)"); + this.logger.LogTrace("(-)"); return wallet; } @@ -1201,7 +1212,7 @@ public void EnableFederationWallet(string password, string mnemonic = null, stri // Protect against de-activation if the federation is already active. if (this.isFederationActive) { - this.logger.Warn("(-):[FEDERATION_ALREADY_ACTIVE]"); + this.logger.LogWarning("(-):[FEDERATION_ALREADY_ACTIVE]"); return; } @@ -1218,8 +1229,8 @@ public void EnableFederationWallet(string password, string mnemonic = null, stri } catch (NotSupportedException ex) { - this.logger.Debug("Exception occurred: {0}", ex.ToString()); - this.logger.Trace("(-)[EXCEPTION]"); + this.logger.LogDebug("Exception occurred: {0}", ex.ToString()); + this.logger.LogTrace("(-)[EXCEPTION]"); if (ex.Message == "Unknown") throw new WalletException("Please make sure you enter valid mnemonic words."); diff --git a/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletSyncManager.cs b/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletSyncManager.cs index c599ab65fe..9ad936ef3f 100644 --- a/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletSyncManager.cs @@ -3,9 +3,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NBitcoin; -using NLog; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.BlockStore; using Stratis.Bitcoin.Features.Wallet; using Stratis.Bitcoin.Interfaces; @@ -73,7 +74,7 @@ public void Initialize() if (this.storeSettings.PruningEnabled) throw new WalletException("Wallet can not yet run on a pruned node"); - this.logger.Info("WalletSyncManager initialized. Wallet at block {0}.", this.federationWalletManager.LastBlockSyncedHashHeight().Height); + this.logger.LogInformation("WalletSyncManager initialized. Wallet at block {0}.", this.federationWalletManager.LastBlockSyncedHashHeight().Height); this.walletTip = this.chain.GetHeader(this.federationWalletManager.WalletTipHash); if (this.walletTip == null) @@ -109,19 +110,19 @@ private async Task OnProcessBlockWrapperAsync(Block block, CancellationToken can } catch (Exception e) { - this.logger.Error(e.ToString()); + this.logger.LogError(e.ToString()); } } - private async Task OnProcessBlockAsync(Block block, CancellationToken cancellationToken) + private Task OnProcessBlockAsync(Block block, CancellationToken cancellationToken) { Guard.NotNull(block, nameof(block)); ChainedHeader newTip = this.chain.GetHeader(block.GetHash()); if (newTip == null) { - this.logger.Trace("(-)[NEW_TIP_REORG]"); - return; + this.logger.LogTrace("(-)[NEW_TIP_REORG]"); + return Task.CompletedTask; } // If the new block's previous hash is the same as the @@ -141,12 +142,12 @@ private async Task OnProcessBlockAsync(Block block, CancellationToken cancellati while (this.chain.GetHeader(fork.HashBlock) == null) fork = fork.Previous; - this.logger.Info("Reorg detected, going back from '{0}' to '{1}'.", this.walletTip, fork); + this.logger.LogInformation("Reorg detected, going back from '{0}' to '{1}'.", this.walletTip, fork); this.federationWalletManager.RemoveBlocks(fork); this.walletTip = fork; - this.logger.Debug("Wallet tip set to '{0}'.", this.walletTip); + this.logger.LogDebug("Wallet tip set to '{0}'.", this.walletTip); } // The new tip can be ahead or behind the wallet. @@ -157,11 +158,11 @@ private async Task OnProcessBlockAsync(Block block, CancellationToken cancellati ChainedHeader findTip = newTip.FindAncestorOrSelf(this.walletTip); if (findTip == null) { - this.logger.Trace("(-)[NEW_TIP_AHEAD_NOT_IN_WALLET]"); - return; + this.logger.LogTrace("(-)[NEW_TIP_AHEAD_NOT_IN_WALLET]"); + return Task.CompletedTask; } - this.logger.Debug("Wallet tip '{0}' is behind the new tip '{1}'.", this.walletTip, newTip); + this.logger.LogDebug("Wallet tip '{0}' is behind the new tip '{1}'.", this.walletTip, newTip); ChainedHeader next = this.walletTip; while (next != newTip) @@ -181,8 +182,8 @@ private async Task OnProcessBlockAsync(Block block, CancellationToken cancellati { if (cancellationToken.IsCancellationRequested) { - this.logger.Trace("(-)[CANCELLATION_REQUESTED]"); - return; + this.logger.LogTrace("(-)[CANCELLATION_REQUESTED]"); + return Task.CompletedTask; } nextblock = this.blockStore.GetBlock(next.HashBlock); @@ -193,13 +194,13 @@ private async Task OnProcessBlockAsync(Block block, CancellationToken cancellati index++; if (index > 10) { - this.logger.Trace("(-)[WALLET_CATCHUP_INDEX_MAX]"); - return; + this.logger.LogTrace("(-)[WALLET_CATCHUP_INDEX_MAX]"); + return Task.CompletedTask; } // Really ugly hack to let store catch up. // This will block the entire consensus pulling. - this.logger.Warn("Wallet is behind the best chain and the next block is not found in store."); + this.logger.LogWarning("Wallet is behind the best chain and the next block is not found in store."); Thread.Sleep(100); continue; } @@ -216,17 +217,19 @@ private async Task OnProcessBlockAsync(Block block, CancellationToken cancellati ChainedHeader findTip = this.walletTip.FindAncestorOrSelf(newTip); if (findTip == null) { - this.logger.Trace("(-)[NEW_TIP_BEHIND_NOT_IN_WALLET]"); - return; + this.logger.LogTrace("(-)[NEW_TIP_BEHIND_NOT_IN_WALLET]"); + return Task.CompletedTask; } - this.logger.Debug("Wallet tip '{0}' is ahead or equal to the new tip '{1}'.", this.walletTip, newTip); + this.logger.LogDebug("Wallet tip '{0}' is ahead or equal to the new tip '{1}'.", this.walletTip, newTip); } } - else this.logger.Debug("New block follows the previously known block '{0}'.", this.walletTip); + else this.logger.LogDebug("New block follows the previously known block '{0}'.", this.walletTip); this.walletTip = newTip; this.federationWalletManager.ProcessBlock(block, newTip); + + return Task.CompletedTask; } /// @@ -242,7 +245,7 @@ public virtual void ProcessTransaction(Transaction transaction) { Guard.NotNull(transaction, nameof(transaction)); - this.logger.Debug("Processing transaction from mempool: {0}", transaction.GetHash()); + this.logger.LogDebug("Processing transaction from mempool: {0}", transaction.GetHash()); if (this.federationWalletManager.ProcessTransaction(transaction)) this.federationWalletManager.SaveWallet(); diff --git a/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletTransactionHandler.cs b/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletTransactionHandler.cs index 1b6f0ce4ce..d172c109fa 100644 --- a/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletTransactionHandler.cs +++ b/src/Stratis.Features.FederatedPeg/Wallet/FederationWalletTransactionHandler.cs @@ -3,9 +3,10 @@ using System.Linq; using System.Security; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; using NBitcoin; using NBitcoin.Policy; -using NLog; +using Stratis.Bitcoin.Configuration.Logging; using Stratis.Bitcoin.Features.Wallet; using Stratis.Bitcoin.Features.Wallet.Interfaces; using Stratis.Bitcoin.Utilities; @@ -103,7 +104,7 @@ public Transaction BuildTransaction(TransactionBuildContext context) if (!transactionBuilder.Verify(transaction, out TransactionPolicyError[] errors)) { string errorsMessage = string.Join(" - ", errors.Select(s => s.ToString())); - this.logger.Error($"Build transaction failed: {errorsMessage}"); + this.logger.LogError($"Build transaction failed: {errorsMessage}"); throw new WalletException($"Could not build the transaction. Details: {errorsMessage}"); } } @@ -115,6 +116,7 @@ public Transaction BuildTransaction(TransactionBuildContext context) /// Initializes the context transaction builder from information in . /// /// Transaction build context. + /// See . private TransactionBuilder InitializeTransactionBuilder(TransactionBuildContext context) { Guard.NotNull(context, nameof(context)); @@ -147,7 +149,7 @@ private TransactionBuilder InitializeTransactionBuilder(TransactionBuildContext /// /// Loads the private key for the multisig address. /// - /// + /// . /// The context associated with the current transaction being built. private void AddSecrets(TransactionBuilder transactionBuilder, TransactionBuildContext context) { @@ -180,7 +182,7 @@ private void AddSecrets(TransactionBuilder transactionBuilder, TransactionBuildC /// /// Find the next available change address. /// - /// + /// /// The context associated with the current transaction being built. private void FindChangeAddress(TransactionBuilder transactionBuilder, TransactionBuildContext context) { @@ -193,7 +195,7 @@ private void FindChangeAddress(TransactionBuilder transactionBuilder, Transactio /// Find all available outputs (UTXO's) that belong to the multisig address. /// Then add them to the or . /// - /// + /// . /// The context associated with the current transaction being built. private void AddCoins(TransactionBuilder transactionBuilder, TransactionBuildContext context) { @@ -215,6 +217,7 @@ private void AddCoins(TransactionBuilder transactionBuilder, TransactionBuildCon /// The federation wallet manager. /// The network. /// The transacion build context. + /// See . /// The coins and unspent outputs that will be used. public static (List, List) DetermineCoins(IFederationWalletManager walletManager, Network network, TransactionBuildContext context, IFederatedPegSettings settings) { @@ -271,7 +274,7 @@ public static (List, List) DetermineCoins(IFederat /// /// Add recipients to the . /// - /// + /// . /// The context associated with the current transaction being built. /// /// Add outputs to the based on the list. @@ -288,7 +291,7 @@ private void AddRecipients(TransactionBuilder transactionBuilder, TransactionBui /// /// Use the from the . /// - /// + /// . /// The context associated with the current transaction being built. private void AddFee(TransactionBuilder transactionBuilder, TransactionBuildContext context) { @@ -321,7 +324,7 @@ private void AddFee(TransactionBuilder transactionBuilder, TransactionBuildConte /// /// Add extra unspendable output to the transaction if there is anything in OpReturnData. /// - /// + /// . /// The context associated with the current transaction being built. private void AddOpReturnOutput(TransactionBuilder transactionBuilder, TransactionBuildContext context) { @@ -347,6 +350,7 @@ public TransactionBuildContext(List recipients) : this(recipients, st /// /// The target recipients to send coins to. /// The password that protects the member's seed. + /// Optional data to be added as an extra OP_RETURN transaction output with Money.Zero value. public TransactionBuildContext(List recipients, string walletPassword = "", byte[] opReturnData = null) { Guard.NotNull(recipients, nameof(recipients)); @@ -481,6 +485,8 @@ public class Recipient /// /// We need to reduce the amount being withdrawn by the fees our transaction is going to have. /// + /// The transaction fee. + /// See . public Recipient WithPaymentReducedByFee(Money transactionFee) { Money newAmount = this.Amount - transactionFee; diff --git a/src/Stratis.Features.FederatedPeg/Wallet/SpendingDetails.cs b/src/Stratis.Features.FederatedPeg/Wallet/SpendingDetails.cs index 18a30236f5..3d5115c228 100644 --- a/src/Stratis.Features.FederatedPeg/Wallet/SpendingDetails.cs +++ b/src/Stratis.Features.FederatedPeg/Wallet/SpendingDetails.cs @@ -66,6 +66,7 @@ public SpendingDetails() /// /// Determines whether this transaction being spent is confirmed. /// + /// True if the transaction is confirmed and false otherwise. public bool IsSpentConfirmed() { return this.BlockHeight != null; diff --git a/src/Stratis.Features.SQLiteWalletRepository/DBConnection.cs b/src/Stratis.Features.SQLiteWalletRepository/DBConnection.cs index 188e89bda3..637a6583da 100644 --- a/src/Stratis.Features.SQLiteWalletRepository/DBConnection.cs +++ b/src/Stratis.Features.SQLiteWalletRepository/DBConnection.cs @@ -296,40 +296,46 @@ internal void AddTransactions(HDAccount account, HdAddress address, ICollection< } } - internal List CreateWatchOnlyAddresses(HDAccount account, int addressType, List hdAddresses, bool force = false) + internal List CreateWatchOnlyAddresses(WalletAccountReference accountReference, HDAccount account, int addressType, List hdAddresses, bool force = false) { var addresses = new List(); int addressIndex = HDAddress.GetAddressCount(this.SQLiteConnection, account.WalletId, account.AccountIndex, addressType); + + IEnumerable existingAddresses = this.Repository.GetAccountAddresses(accountReference, addressType, int.MaxValue); + + var existingPubKeys = new HashSet(); + + foreach (HdAddress existing in existingAddresses) + { + existingPubKeys.Add(PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(existing.Pubkey)); + } foreach (HdAddress hdAddress in hdAddresses) { - try - { - var pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(hdAddress.Pubkey); - HDAddress address = this.Repository.CreateAddress(account, addressType, addressIndex, pubKey); + var pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(hdAddress.Pubkey); - if (force || this.Repository.TestMode) - { - // Allow greater control over field values for legacy tests. - address.Address = hdAddress?.Address; - address.ScriptPubKey = hdAddress.ScriptPubKey?.ToHex(); - address.PubKey = hdAddress.Pubkey?.ToHex(); - this.Insert(address); - this.AddTransactions(account, hdAddress, hdAddress.Transactions); - } - else - { - this.Insert(address); - } + if (existingPubKeys.Contains(pubKey)) + continue; - addresses.Add(address); + HDAddress address = this.Repository.CreateAddress(account, addressType, addressIndex, pubKey); - addressIndex++; + if (force || this.Repository.TestMode) + { + // Allow greater control over field values for legacy tests. + address.Address = hdAddress?.Address; + address.ScriptPubKey = hdAddress.ScriptPubKey?.ToHex(); + address.PubKey = hdAddress.Pubkey?.ToHex(); + this.Insert(address); + this.AddTransactions(account, hdAddress, hdAddress.Transactions); } - catch (Exception) + else { - throw; + this.Insert(address); } + + addresses.Add(address); + + addressIndex++; } return addresses; @@ -608,6 +614,7 @@ AND SpendTxId IS NOT NULL /// /// The wallet. /// The last block synced to set. + /// An enumeration of transactions that were removed as tuples of transaction id (string) and transaction creation time (long). internal IEnumerable<(string txId, long creationTime)> SetLastBlockSynced(HDWallet wallet, ChainedHeader lastBlockSynced) { if (this.IsInTransaction) diff --git a/src/Stratis.Features.SQLiteWalletRepository/SQLiteWalletRepository.cs b/src/Stratis.Features.SQLiteWalletRepository/SQLiteWalletRepository.cs index e080230b0e..5753fbefab 100644 --- a/src/Stratis.Features.SQLiteWalletRepository/SQLiteWalletRepository.cs +++ b/src/Stratis.Features.SQLiteWalletRepository/SQLiteWalletRepository.cs @@ -620,7 +620,7 @@ public void AddWatchOnlyAddresses(string walletName, string accountName, int add if (!force && !this.TestMode && account.ExtPubKey != null) throw new Exception("Addresses can only be added to watch-only accounts."); - conn.CreateWatchOnlyAddresses(account, addressType, addresses, force); + conn.CreateWatchOnlyAddresses(new WalletAccountReference(walletName, accountName), account, addressType, addresses, force); conn.Commit(); walletContainer.AddressesOfInterest.AddAll(account.WalletId, account.AccountIndex); @@ -1307,27 +1307,47 @@ public IEnumerable GetSpendableTransactionsInAccount(Wal Wallet hdWallet = this.GetWallet(walletAccountReference.WalletName); HdAccount hdAccount = this.GetAccounts(hdWallet, walletAccountReference.AccountName).FirstOrDefault(); - var extPubKey = ExtPubKey.Parse(hdAccount.ExtendedPubKey, this.Network); - var spendable = conn.GetSpendableOutputs(walletContainer.Wallet.WalletId, hdAccount.Index, currentChainHeight, coinBaseMaturity ?? this.Network.Consensus.CoinbaseMaturity, confirmations); + ExtPubKey extPubKey = null; + + // The extPubKey will be null if this is a watch only account. + if (hdAccount.ExtendedPubKey != null) + { + extPubKey = ExtPubKey.Parse(hdAccount.ExtendedPubKey, this.Network); + } + var cachedPubKeys = new Dictionary(); foreach (HDTransactionData transactionData in spendable) { - var keyPath = new KeyPath($"{transactionData.AddressType}/{transactionData.AddressIndex}"); - PubKey pubKey = null; - if (!cachedPubKeys.TryGetValue(keyPath, out pubKey)) + int tdConfirmations = (transactionData.OutputBlockHeight == null) ? 0 : (currentChainHeight + 1) - (int)transactionData.OutputBlockHeight; + + PubKey pubKey; + + if (extPubKey == null) { - pubKey = extPubKey.Derive(keyPath).PubKey; - cachedPubKeys.Add(keyPath, pubKey); + if (!walletContainer.AddressesOfInterest.Contains(Script.FromHex(transactionData.RedeemScript), out AddressIdentifier addressIdentifier)) + continue; + + Script p2pkScript = Script.FromHex(addressIdentifier.PubKeyScript); + + pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(p2pkScript); + } + else + { + var keyPath = new KeyPath($"{transactionData.AddressType}/{transactionData.AddressIndex}"); + + if (!cachedPubKeys.TryGetValue(keyPath, out pubKey)) + { + pubKey = extPubKey.Derive(keyPath).PubKey; + cachedPubKeys.Add(keyPath, pubKey); + } } Script scriptPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(pubKey); Script witScriptPubKey = PayToWitPubKeyHashTemplate.Instance.GenerateScriptPubKey(pubKey); - int tdConfirmations = (transactionData.OutputBlockHeight == null) ? 0 : (currentChainHeight + 1) - (int)transactionData.OutputBlockHeight; - // We do not use the address from the transaction (i.e. UTXO) data here in case it is a segwit (P2WPKH) UTXO. // That is because the bech32 functionality is somewhat bolted onto the HdAddress, so we need to return an HdAddress augmented with bech32 data rather than only bech32 data. HdAddress hdAddress = this.ToHdAddress(new HDAddress() @@ -1346,11 +1366,11 @@ public IEnumerable GetSpendableTransactionsInAccount(Wal TransactionData txData = this.ToTransactionData(transactionData, hdAddress.Transactions); // Check if this wallet is a normal purpose wallet (not cold staking, etc). - if (hdAccount.IsNormalAccount()) + if (hdAccount.IsNormalAccount() || hdAccount.Name == Wallet.WatchOnlyAccountName) { bool isColdCoinStake = txData.IsColdCoinStake ?? false; - // Skip listing the UTXO if this is a normal wallet, and the UTXO is marked as an cold coin stake. + // Skip listing the UTXO if this is a normal wallet, and the UTXO is marked as a cold coin stake. if (isColdCoinStake) { continue; @@ -1600,11 +1620,23 @@ public IEnumerable GetTransactionInputs(HdAccount hdAccount, Da if (!addressDict.TryGetValue(addressIdentifier, out HdAddress hdAddress)) { - ExtPubKey extPubKey = ExtPubKey.Parse(hdAccount.ExtendedPubKey, this.Network); + PubKey pubKey; + + if (hdAccount.ExtendedPubKey != null) + { + ExtPubKey extPubKey = ExtPubKey.Parse(hdAccount.ExtendedPubKey, this.Network); - var keyPath = new KeyPath($"{tranData.AddressType}/{tranData.AddressIndex}"); + var keyPath = new KeyPath($"{tranData.AddressType}/{tranData.AddressIndex}"); - PubKey pubKey = extPubKey.Derive(keyPath).PubKey; + pubKey = extPubKey.Derive(keyPath).PubKey; + } + else + { + // TODO: Verify if PubKeyScript is getting populated correctly for TransactionsOfInterest-derived AddressIdentifiers + Script p2pkScript = Script.FromHex(addressIdentifier.PubKeyScript); + + pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(p2pkScript); + } hdAddress = this.ToHdAddress(new HDAddress() { diff --git a/src/Stratis.Features.SQLiteWalletRepository/SQLiteWalletRepositoryFeature.cs b/src/Stratis.Features.SQLiteWalletRepository/SQLiteWalletRepositoryFeature.cs index 8444ed8c5c..95496aa6fe 100644 --- a/src/Stratis.Features.SQLiteWalletRepository/SQLiteWalletRepositoryFeature.cs +++ b/src/Stratis.Features.SQLiteWalletRepository/SQLiteWalletRepositoryFeature.cs @@ -2,41 +2,22 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using NBitcoin; using Stratis.Bitcoin.Builder; using Stratis.Bitcoin.Builder.Feature; -using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Features.Wallet.Interfaces; namespace Stratis.Features.SQLiteWalletRepository { public class SQLiteWalletRepositoryFeature : FullNodeFeature - { - /// The settings for the node. - private readonly NodeSettings nodeSettings; - - /// The logger factory used to create instance loggers. - private readonly ILoggerFactory loggerFactory; - - /// The instance logger. - private readonly ILogger logger; - - /// The SQLite wallet repository. - private readonly SQLiteWalletRepository sqliteWalletRepository; - + { /// /// Initializes a new instance of the class. /// /// The settings for the node. /// The factory used to create instance loggers. - public SQLiteWalletRepositoryFeature( - NodeSettings nodeSettings, - ILoggerFactory loggerFactory) + public SQLiteWalletRepositoryFeature() { - this.logger = loggerFactory.CreateLogger(this.GetType().FullName); - this.loggerFactory = loggerFactory; - this.nodeSettings = nodeSettings; } /// diff --git a/src/Stratis.Features.SQLiteWalletRepository/Stratis.Features.SQLiteWalletRepository.csproj b/src/Stratis.Features.SQLiteWalletRepository/Stratis.Features.SQLiteWalletRepository.csproj index 116a5cab84..2c298fe571 100644 --- a/src/Stratis.Features.SQLiteWalletRepository/Stratis.Features.SQLiteWalletRepository.csproj +++ b/src/Stratis.Features.SQLiteWalletRepository/Stratis.Features.SQLiteWalletRepository.csproj @@ -14,7 +14,7 @@ false false false - 1.1.1.1 + 1.2.0.0 False Stratis Group Ltd. diff --git a/src/Stratis.Features.SQLiteWalletRepository/Tables/HDTransactionData.cs b/src/Stratis.Features.SQLiteWalletRepository/Tables/HDTransactionData.cs index a919972b36..8cb3a6d06f 100644 --- a/src/Stratis.Features.SQLiteWalletRepository/Tables/HDTransactionData.cs +++ b/src/Stratis.Features.SQLiteWalletRepository/Tables/HDTransactionData.cs @@ -230,6 +230,12 @@ AND AccountIndex IN (SELECT AccountIndex FROM HDAccount WHERE WalletId = {st /// Connection to the database engine. /// The wallet we are retrieving history for. /// The account index in question. + /// The maximum number of history items to return. + /// The number of history items to skip. + /// A transaction id filter or a null string. + /// An address filter or a null string. + /// Set to true if its a smart contract history. + /// Set to true if its for Cirrus. /// An unpaged set of wallet transaction history items internal static IEnumerable GetHistory(DBConnection conn, int walletId, int accountIndex, int limit, int offset, string txId, string address, bool forSmartContracts = false, bool forCirrus = false) { diff --git a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs index a8b8dcff05..259004efa1 100644 --- a/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs +++ b/src/Stratis.Features.Unity3dApi/Controllers/Unity3dController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Net; @@ -71,9 +71,12 @@ public class Unity3dController : Controller private readonly ILocalExecutor localExecutor; + private readonly INFTTransferIndexer NFTTransferIndexer; + public Unity3dController(ILoggerFactory loggerFactory, IAddressIndexer addressIndexer, - IBlockStore blockStore, IChainState chainState, Network network, ICoinView coinView, WalletController walletController, ChainIndexer chainIndexer, IStakeChain stakeChain = null, - IContractPrimitiveSerializer primitiveSerializer = null, IStateRepositoryRoot stateRoot = null, IContractAssemblyCache contractAssemblyCache = null, + IBlockStore blockStore, IChainState chainState, Network network, ICoinView coinView, WalletController walletController, ChainIndexer chainIndexer, INFTTransferIndexer NFTTransferIndexer, + IStakeChain stakeChain = null, + IContractPrimitiveSerializer primitiveSerializer = null, IStateRepositoryRoot stateRoot = null, IContractAssemblyCache contractAssemblyCache = null, IReceiptRepository receiptRepository = null, ISmartContractTransactionService smartContractTransactionService = null, ILocalExecutor localExecutor = null) { Guard.NotNull(loggerFactory, nameof(loggerFactory)); @@ -86,6 +89,7 @@ public Unity3dController(ILoggerFactory loggerFactory, IAddressIndexer addressIn this.walletController = Guard.NotNull(walletController, nameof(walletController)); this.chainIndexer = Guard.NotNull(chainIndexer, nameof(chainIndexer)); this.stakeChain = stakeChain; + this.NFTTransferIndexer = NFTTransferIndexer; this.primitiveSerializer = primitiveSerializer; this.stateRoot = stateRoot; @@ -98,6 +102,7 @@ public Unity3dController(ILoggerFactory loggerFactory, IAddressIndexer addressIn /// /// Gets UTXOs for specified address. /// + /// See . /// Address to get UTXOs for. [Route("getutxosforaddress")] [HttpGet] @@ -135,7 +140,7 @@ public GetUTXOsResponseModel GetUTXOsForAddress([FromQuery] string address) foreach (List txList in blocks.Select(x => x.Transactions)) { - foreach (Transaction transaction in txList.Where(x => !x.IsCoinBase && !x.IsCoinStake)) + foreach (Transaction transaction in txList) { for (int i = 0; i < transaction.Outputs.Count; i++) { @@ -155,6 +160,8 @@ public GetUTXOsResponseModel GetUTXOsForAddress([FromQuery] string address) UTXOs = new List() }; + Money totalM = Money.Zero; + foreach (KeyValuePair unspentOutput in fetchCoinsResponse.UnspentOutputs) { if (unspentOutput.Value.Coins == null) @@ -162,10 +169,14 @@ public GetUTXOsResponseModel GetUTXOsForAddress([FromQuery] string address) OutPoint outPoint = unspentOutput.Key; Money value = unspentOutput.Value.Coins.TxOut.Value; + totalM += value; response.UTXOs.Add(new UTXOModel(outPoint, value)); } + if (totalM != balanceSat) + this.logger.LogError(string.Format("Should be {0}, is: {1}", new Money(balanceSat), totalM)); + return response; } @@ -178,18 +189,30 @@ public GetUTXOsResponseModel GetUTXOsForAddress([FromQuery] string address) [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public long GetAddressBalance(string address) + public IActionResult GetAddressBalance(string address) { + long money = -1; try { - AddressBalancesResult result = this.addressIndexer.GetAddressBalances(new []{address}, 1); - - return result.Balances.First().Balance.Satoshi; + AddressBalancesResult result = this.addressIndexer.GetAddressBalances(new[] { address }, 1); + money = result.Balances.First().Balance.Satoshi; } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); - return -1; + } + + if (this.Request.Headers["Accept"] == "application/json") + { + GetBalanceResponseModel response = new GetBalanceResponseModel() + { + Balance = money + }; + return Ok(response); + } + else + { + return Ok(money); } } @@ -285,10 +308,10 @@ public RawTxModel GetRawTransaction([FromQuery] string trxid) [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public async Task SendTransaction([FromBody] SendTransactionRequest request, + public async Task SendTransactionAsync([FromBody] SendTransactionRequest request, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.walletController.SendTransaction(request, cancellationToken).ConfigureAwait(false); + return await this.walletController.SendTransactionAsync(request, cancellationToken).ConfigureAwait(false); } /// @@ -344,7 +367,7 @@ public ValidatedAddress ValidateAddress([FromQuery] string address) if (result.IsValid) { - var scriptPubKey = BitcoinAddress.Create(address, this.network).ScriptPubKey; + NBitcoin.Script scriptPubKey = BitcoinAddress.Create(address, this.network).ScriptPubKey; result.ScriptPubKey = scriptPubKey.ToHex(); result.IsWitness = scriptPubKey.IsWitness(this.network); } @@ -452,7 +475,7 @@ public TipModel GetTip() /// The receipt for the smart contract. /// Returns transaction receipt /// Transaction not found - [Route("api/[controller]/receipt")] + [Route("receipt")] [HttpGet] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] @@ -492,10 +515,11 @@ public ReceiptResponse GetReceiptAPI([FromQuery] string txHash) /// /// An object containing the necessary parameters to build the transaction. /// The result of the local call to the smart contract method. + /// The . /// Returns call response /// Invalid request /// Unable to deserialize method parameters - [Route("api/[controller]/local-call")] + [Route("local-call")] [HttpPost] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] @@ -549,18 +573,55 @@ public IActionResult LocalCallSmartContractTransaction([FromBody] LocalCallContr /// The block number where searching finishes. /// /// A list of receipts for transactions relating to a specific smart contract and a specific event in that smart contract. - [Route("api/[controller]/receipt-search")] + [Route("receipt-search")] [HttpGet] - public async Task> ReceiptSearchAPI([FromQuery] string contractAddress, [FromQuery] string eventName, [FromQuery] List topics = null, [FromQuery] int fromBlock = 0, [FromQuery] int? toBlock = null) + public Task> ReceiptSearchAPI([FromQuery] string contractAddress, [FromQuery] string eventName, [FromQuery] List topics = null, [FromQuery] int fromBlock = 0, [FromQuery] int? toBlock = null) { List result = this.smartContractTransactionService.ReceiptSearch(contractAddress, eventName, topics, fromBlock, toBlock); - return result; + return Task.FromResult(result); + } + + [Route("watch-nft-contract")] + [HttpGet] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public void WatchNFTContract([FromQuery] string contractAddress) + { + this.NFTTransferIndexer.WatchNFTContract(contractAddress); + } + + [Route("get-watched-nft-contracts")] + [HttpGet] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public List GetWatchedNFTContracts() + { + return this.NFTTransferIndexer.GetWatchedNFTContracts(); + } + + [Route("get-owned-nfts")] + [HttpGet] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public OwnedNFTsModel GetOwnedNFTs([FromQuery] string ownerAddress) + { + return this.NFTTransferIndexer.GetOwnedNFTs(ownerAddress); + } + + [Route("get-all-nft-owners-by-contract-address")] + [HttpGet] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public NFTContractModel GetAllNFTOwnersByContractAddress([FromQuery] string contractAddress) + { + return this.NFTTransferIndexer.GetAllNFTOwnersByContractAddress(contractAddress); } /// /// If the call is to a property, rewrites the method name to the getter method's name. /// + /// See . private void RewritePropertyGetterName(LocalCallContractRequest request) { // Don't rewrite if there are params diff --git a/src/Stratis.Features.Unity3dApi/NFTTransferIndexer.cs b/src/Stratis.Features.Unity3dApi/NFTTransferIndexer.cs new file mode 100644 index 0000000000..912dca7811 --- /dev/null +++ b/src/Stratis.Features.Unity3dApi/NFTTransferIndexer.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using LiteDB; +using Microsoft.Extensions.Logging; +using NBitcoin; +using Newtonsoft.Json; +using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Configuration; +using Stratis.Bitcoin.Features.BlockStore.AddressIndexing; +using Stratis.Bitcoin.Features.SmartContracts.Models; +using Stratis.Bitcoin.Features.SmartContracts.Wallet; +using Stratis.Bitcoin.Utilities; +using FileMode = LiteDB.FileMode; + +namespace Stratis.Features.Unity3dApi +{ + public interface INFTTransferIndexer : IDisposable + { + /// Initialized NFT indexer. + void Initialize(); + + /// Adds NFT contract to watch list. Only contracts from the watch list are being indexed. + void WatchNFTContract(string contractAddress); + + /// Provides a list of all nft contract addresses that are being tracked. + List GetWatchedNFTContracts(); + + /// Provides collection of NFT ids that belong to provided user's address for watched contracts. + OwnedNFTsModel GetOwnedNFTs(string address); + + /// Returns collection of all users that own nft. + NFTContractModel GetAllNFTOwnersByContractAddress(string contractAddress); + } + + /// This component maps addresses to NFT Ids they own. + public class NFTTransferIndexer : INFTTransferIndexer + { + public ChainedHeader IndexerTip { get; private set; } + + private const string DatabaseFilename = "NFTTransferIndexer.litedb"; + private const string DbOwnedNFTsKey = "OwnedNfts"; + private const int SyncBufferBlocks = 50; + + private readonly DataFolder dataFolder; + private readonly ILogger logger; + private readonly ChainIndexer chainIndexer; + private readonly IAsyncProvider asyncProvider; + private readonly ISmartContractTransactionService smartContractTransactionService; + private readonly Network network; + + private LiteDatabase db; + private LiteCollection NFTContractCollection; + private CancellationTokenSource cancellation; + private Task indexingTask; + + public NFTTransferIndexer(DataFolder dataFolder, ILoggerFactory loggerFactory, IAsyncProvider asyncProvider, + ChainIndexer chainIndexer, Network network, ISmartContractTransactionService smartContractTransactionService = null) + { + this.network = network; + this.dataFolder = dataFolder; + this.cancellation = new CancellationTokenSource(); + this.asyncProvider = asyncProvider; + this.chainIndexer = chainIndexer; + this.smartContractTransactionService = smartContractTransactionService; + + this.logger = loggerFactory.CreateLogger(this.GetType().FullName); + } + + /// + public void Initialize() + { + if (this.db != null) + throw new Exception("NFTTransferIndexer already initialized!"); + + string dbPath = Path.Combine(this.dataFolder.RootPath, DatabaseFilename); + + FileMode fileMode = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? FileMode.Exclusive : FileMode.Shared; + this.db = new LiteDatabase(new ConnectionString() { Filename = dbPath, Mode = fileMode }); + this.NFTContractCollection = this.db.GetCollection(DbOwnedNFTsKey); + + this.indexingTask = Task.Run(async () => await this.IndexNFTsContinuouslyAsync().ConfigureAwait(false)); + this.asyncProvider.RegisterTask($"{nameof(AddressIndexer)}.{nameof(this.indexingTask)}", this.indexingTask); + } + + /// + public void WatchNFTContract(string contractAddress) + { + int watchFromHeight = this.network.IsTest() ? 3000000 : 3400000; + + if (!this.NFTContractCollection.Exists(x => x.ContractAddress == contractAddress)) + { + NFTContractModel model = new NFTContractModel() + { + ContractAddress = contractAddress, + LastUpdatedBlock = watchFromHeight, + OwnedIDsByAddress = new Dictionary>() + }; + + this.NFTContractCollection.Upsert(model); + } + } + + /// + public List GetWatchedNFTContracts() + { + return this.NFTContractCollection.FindAll().Select(x => x.ContractAddress).ToList(); + } + + /// + public OwnedNFTsModel GetOwnedNFTs(string address) + { + List NFTContractModels = this.NFTContractCollection.FindAll().Where(x => x.OwnedIDsByAddress.ContainsKey(address)).ToList(); + + OwnedNFTsModel output = new OwnedNFTsModel() { OwnedIDsByContractAddress = new Dictionary>() }; + + foreach (NFTContractModel contractModel in NFTContractModels) + { + List ids = contractModel.OwnedIDsByAddress[address]; + output.OwnedIDsByContractAddress.Add(contractModel.ContractAddress, ids); + } + + return output; + } + + public NFTContractModel GetAllNFTOwnersByContractAddress(string contractAddress) + { + NFTContractModel currentContract = this.NFTContractCollection.FindOne(x => x.ContractAddress == contractAddress); + return currentContract; + } + + private async Task IndexNFTsContinuouslyAsync() + { + await Task.Delay(1); + + try + { + while (!this.cancellation.Token.IsCancellationRequested) + { + List contracts = this.NFTContractCollection.FindAll().Select(x => x.ContractAddress).ToList(); + + foreach (string contractAddr in contracts) + { + if (this.cancellation.Token.IsCancellationRequested) + break; + + NFTContractModel currentContract = this.NFTContractCollection.FindOne(x => x.ContractAddress == contractAddr); + + ChainedHeader chainTip = this.chainIndexer.Tip; + + List receipts = this.smartContractTransactionService.ReceiptSearch( + contractAddr, "TransferLog", null, currentContract.LastUpdatedBlock + 1, null); + + if ((receipts == null) || (receipts.Count == 0)) + { + currentContract.LastUpdatedBlock = chainTip.Height; + this.NFTContractCollection.Upsert(currentContract); + continue; + } + + int lastReceiptHeight = 0; + if (receipts.Any()) + lastReceiptHeight = (int)receipts.Last().BlockNumber.Value; + + currentContract.LastUpdatedBlock = new List() { chainTip.Height, lastReceiptHeight }.Max(); + + List transferLogs = new List(receipts.Count); + + foreach (ReceiptResponse receiptRes in receipts) + { + LogData log = receiptRes.Logs.First().Log; + string jsonLog = JsonConvert.SerializeObject(log); + + TransferLogRoot infoObj = JsonConvert.DeserializeObject(jsonLog); + transferLogs.Add(infoObj.Data); + } + + foreach (TransferLog transferInfo in transferLogs) + { + if ((transferInfo.From != null) && currentContract.OwnedIDsByAddress.ContainsKey(transferInfo.From)) + { + currentContract.OwnedIDsByAddress[transferInfo.From].Remove(transferInfo.TokenId); + + if (currentContract.OwnedIDsByAddress[transferInfo.From].Count == 0) + currentContract.OwnedIDsByAddress.Remove(transferInfo.From); + } + + if (!currentContract.OwnedIDsByAddress.ContainsKey(transferInfo.To)) + currentContract.OwnedIDsByAddress.Add(transferInfo.To, new List()); + + currentContract.OwnedIDsByAddress[transferInfo.To].Add(transferInfo.TokenId); + } + + this.NFTContractCollection.Upsert(currentContract); + } + + try + { + await Task.Delay(TimeSpan.FromSeconds(3), this.cancellation.Token); + } + catch (TaskCanceledException) + { + } + } + } + catch (Exception e) + { + this.logger.LogError(e.ToString()); + } + } + + public void Dispose() + { + this.cancellation.Cancel(); + this.indexingTask?.GetAwaiter().GetResult(); + this.db?.Dispose(); + } + } + + public class NFTContractModel + { + public int Id { get; set; } + + public string ContractAddress { get; set; } + + // Key is nft owner address, value is list of NFT IDs + public Dictionary> OwnedIDsByAddress { get; set; } + + public int LastUpdatedBlock { get; set; } + } + + public class OwnedNFTsModel + { + public Dictionary> OwnedIDsByContractAddress { get; set; } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class TransferLogRoot + { + public string Event { get; set; } + public TransferLog Data { get; set; } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.3.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class TransferLog + { + [JsonProperty("From", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + public string From { get; set; } + + [JsonProperty("To", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + public string To { get; set; } + + [JsonProperty("TokenId", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + public long TokenId { get; set; } + } +} diff --git a/src/Stratis.Features.Unity3dApi/ResponseModels.cs b/src/Stratis.Features.Unity3dApi/ResponseModels.cs index 3666597c3b..563da8166e 100644 --- a/src/Stratis.Features.Unity3dApi/ResponseModels.cs +++ b/src/Stratis.Features.Unity3dApi/ResponseModels.cs @@ -12,6 +12,11 @@ public class GetUTXOsResponseModel public string Reason; } + public class GetBalanceResponseModel + { + public long Balance; + } + public class UTXOModel { public UTXOModel() diff --git a/src/Stratis.Features.Unity3dApi/Startup.cs b/src/Stratis.Features.Unity3dApi/Startup.cs index bdb4d2fdc8..446a76afe0 100644 --- a/src/Stratis.Features.Unity3dApi/Startup.cs +++ b/src/Stratis.Features.Unity3dApi/Startup.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Newtonsoft.Json.Serialization; using Stratis.Bitcoin; using Stratis.Bitcoin.Features.Api; using Swashbuckle.AspNetCore.SwaggerGen; @@ -74,7 +75,9 @@ public void ConfigureServices(IServiceCollection services) { options.Filters.Add(typeof(LoggingActionFilter)); +#pragma warning disable ASP0000 // Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices' ServiceProvider serviceProvider = services.BuildServiceProvider(); +#pragma warning restore ASP0000 // Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices' var apiSettings = (ApiSettings)serviceProvider.GetRequiredService(typeof(ApiSettings)); if (apiSettings.KeepaliveTimer != null) { @@ -82,8 +85,10 @@ public void ConfigureServices(IServiceCollection services) } }) // add serializers for NBitcoin objects - .AddNewtonsoftJson(options => { - Stratis.Bitcoin.Utilities.JsonConverters.Serializer.RegisterFrontConverters(options.SerializerSettings); + .AddNewtonsoftJson(options => + { + options.SerializerSettings.ContractResolver = new DefaultContractResolver(); + Stratis.Bitcoin.Utilities.JsonConverters.Serializer.RegisterFrontConverters(options.SerializerSettings, null, false); }) .AddControllers(this.fullNode.Services.Features, services) .ConfigureApplicationPartManager(a => @@ -146,17 +151,21 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF }); // Enable middleware to serve generated Swagger as a JSON endpoint. - app.UseSwagger(); - + app.UseSwagger(c => + { + c.RouteTemplate = "swagger/unity/{documentname}/swagger.json"; + }); + // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.) app.UseSwaggerUI(c => { + c.RoutePrefix = "swagger/unity"; c.DefaultModelRendering(ModelRendering.Model); // Build a swagger endpoint for each discovered API version foreach (ApiVersionDescription description in provider.ApiVersionDescriptions) { - c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); + c.SwaggerEndpoint($"/swagger/unity/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant()); } // Hack to be able to access and modify the options object configured here diff --git a/src/Stratis.Features.Unity3dApi/Unity3dApiFeature.cs b/src/Stratis.Features.Unity3dApi/Unity3dApiFeature.cs index 3d1ae63128..52ebf86a1e 100644 --- a/src/Stratis.Features.Unity3dApi/Unity3dApiFeature.cs +++ b/src/Stratis.Features.Unity3dApi/Unity3dApiFeature.cs @@ -34,17 +34,21 @@ public sealed class Unity3dApiFeature : FullNodeFeature private readonly ICertificateStore certificateStore; + private readonly INFTTransferIndexer NFTTransferIndexer; + public Unity3dApiFeature( IFullNodeBuilder fullNodeBuilder, FullNode fullNode, Unity3dApiSettings apiSettings, ILoggerFactory loggerFactory, - ICertificateStore certificateStore) + ICertificateStore certificateStore, + INFTTransferIndexer NFTTransferIndexer) { this.fullNodeBuilder = fullNodeBuilder; this.fullNode = fullNode; this.apiSettings = apiSettings; this.certificateStore = certificateStore; + this.NFTTransferIndexer = NFTTransferIndexer; this.logger = loggerFactory.CreateLogger(this.GetType().FullName); this.InitializeBeforeBase = true; @@ -61,6 +65,8 @@ public override Task InitializeAsync() this.logger.LogInformation("Unity API starting on URL '{0}'.", this.apiSettings.ApiUri); this.webHost = Program.Initialize(this.fullNodeBuilder.Services, this.fullNode, this.apiSettings, this.certificateStore, new WebHostBuilder()); + this.NFTTransferIndexer.Initialize(); + if (this.apiSettings.KeepaliveTimer == null) { this.logger.LogTrace("(-)[KEEPALIVE_DISABLED]"); @@ -120,6 +126,8 @@ public override void Dispose() this.webHost.StopAsync(TimeSpan.FromSeconds(ApiStopTimeoutSeconds)).Wait(); this.webHost = null; } + + this.NFTTransferIndexer.Dispose(); } } @@ -139,6 +147,7 @@ public static IFullNodeBuilder UseUnity3dApi(this IFullNodeBuilder fullNodeBuild services.AddSingleton(fullNodeBuilder); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // Controller services.AddTransient(); diff --git a/src/Stratis.Sidechains.Networks/CirrusMain.cs b/src/Stratis.Sidechains.Networks/CirrusMain.cs index 3568f341e3..ebebb0e1f9 100644 --- a/src/Stratis.Sidechains.Networks/CirrusMain.cs +++ b/src/Stratis.Sidechains.Networks/CirrusMain.cs @@ -180,6 +180,7 @@ public CirrusMain() VotingManagerV2ActivationHeight = 1_683_000, // Tuesday, 12 January 2021 9:00:00 AM (Estimated) Release1100ActivationHeight = 3_426_950, // Monday, 20 December 2021 10:00:00 AM (Estimated) PollExpiryBlocks = 50_000, // Roughly 9 days + GetMiningTimestampV2ActivationHeight = 3_709_000, // Monday 14 February 00:00:00 (Estimated) ContractSerializerV2ActivationHeight = 3_386_335 // Monday 13 December 16:00:00 (Estimated) }; @@ -275,7 +276,8 @@ public CirrusMain() { 2_500_000, new CheckpointInfo(new uint256("0x2853be7b7224840d3d4b60427ea832e9bd67d8fc6bfcd4956b8c6b2414cf8fc2")) }, { 2_827_550, new CheckpointInfo(new uint256("0xcf0ebdd99ec04ef260d22befe70ef7b948e50b5fcc18d9d37376d49e872372a0")) }, { 3_000_000, new CheckpointInfo(new uint256("0x79afa4a91a24b5e72632ad01d2a18330aecd1bc2cd4eea82eda5e3945fb0b238")) }, - { 3_200_000, new CheckpointInfo(new uint256("0x6ec55b3b252f45e6677abf553601fb7bc97637319a9646e84d787769afe65988")) } + { 3_200_000, new CheckpointInfo(new uint256("0x6ec55b3b252f45e6677abf553601fb7bc97637319a9646e84d787769afe65988")) }, + { 3_500_000, new CheckpointInfo(new uint256("0x1772356d6498935ab93cbd5eaf1b868c5265480edeef2b5fec133fbc21b292cb")) } }; this.DNSSeeds = new List diff --git a/src/Stratis.Sidechains.Networks/CirrusRegTest.cs b/src/Stratis.Sidechains.Networks/CirrusRegTest.cs index a6f844d142..865b5070cc 100644 --- a/src/Stratis.Sidechains.Networks/CirrusRegTest.cs +++ b/src/Stratis.Sidechains.Networks/CirrusRegTest.cs @@ -109,6 +109,7 @@ public CirrusRegTest() votingEnabled: true, autoKickIdleMembers: true) { + GetMiningTimestampV2ActivationHeight = 100, PollExpiryBlocks = 450 // 2 hours }; diff --git a/src/Stratis.Sidechains.Networks/CirrusTest.cs b/src/Stratis.Sidechains.Networks/CirrusTest.cs index 3fa2e5a0f5..234ffff935 100644 --- a/src/Stratis.Sidechains.Networks/CirrusTest.cs +++ b/src/Stratis.Sidechains.Networks/CirrusTest.cs @@ -122,7 +122,7 @@ public CirrusTest() targetSpacingSeconds: 16, votingEnabled: true, autoKickIdleMembers: true, - federationMemberMaxIdleTimeSeconds: 60 * 60 * 3 // 3 Hours + federationMemberMaxIdleTimeSeconds: 60 * 30 // 30 minutes ) { InterFluxV2MainChainActivationHeight = 500_000, @@ -130,8 +130,9 @@ public CirrusTest() EnforcedMinProtocolVersion = ProtocolVersion.CIRRUS_VERSION, // minimum protocol version which will be enforced at block height defined in EnforceMinProtocolVersionAtBlockHeight VotingManagerV2ActivationHeight = 1_999_500, Release1100ActivationHeight = 2_796_000, - PollExpiryBlocks = 450, // 2 hours - ContractSerializerV2ActivationHeight = 2842681 + PollExpiryBlocks = 450, // 2 hours, + GetMiningTimestampV2ActivationHeight = 3_000_000, // 15 January 2022 + ContractSerializerV2ActivationHeight = 2_842_681 }; var buriedDeployments = new BuriedDeploymentsArray @@ -228,6 +229,7 @@ public CirrusTest() { 2_300_000, new CheckpointInfo(new uint256("0x8e189e0c38cb55c795276d13cc7f6d9c6825eb85324f38ec94a9d4df5d5b5938")) }, { 2_600_000, new CheckpointInfo(new uint256("0x272a6bd353d794bdbf0ebfe2846fb45ed63bc0073202fcfbbd0d6820bf2370c4")) }, { 2_800_000, new CheckpointInfo(new uint256("0xec789f5b9dec0245b3c81fe9279b403dd233dfb4f4361a18386152c66f88cb87")) }, + { 2_900_000, new CheckpointInfo(new uint256("0x37650f6df2f43fa6e00eb628f5aa5b728fc80dcabc18a9d071bbf171db4a2f7c")) }, }; this.DNSSeeds = new List diff --git a/src/Stratis.Sidechains.Networks/Stratis.Sidechains.Networks.csproj b/src/Stratis.Sidechains.Networks/Stratis.Sidechains.Networks.csproj index fe4d686ee3..dd9f983814 100644 --- a/src/Stratis.Sidechains.Networks/Stratis.Sidechains.Networks.csproj +++ b/src/Stratis.Sidechains.Networks/Stratis.Sidechains.Networks.csproj @@ -5,7 +5,7 @@ Full ..\None.ruleset - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. Stratis.Sidechains.Networks diff --git a/src/Stratis.SmartContracts.CLR.Tests/CallDataSerializerTests.cs b/src/Stratis.SmartContracts.CLR.Tests/CallDataSerializerTests.cs index caf7edb633..0b8e6e870f 100644 --- a/src/Stratis.SmartContracts.CLR.Tests/CallDataSerializerTests.cs +++ b/src/Stratis.SmartContracts.CLR.Tests/CallDataSerializerTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Text; +using CSharpFunctionalExtensions; using NBitcoin; using Stratis.SmartContracts.CLR.Serialization; using Stratis.SmartContracts.Networks; @@ -31,8 +32,8 @@ public void TestMethod() ); var contractTxData = new ContractTxData(1, 1, (RuntimeObserver.Gas)5000, contractExecutionCode, signatures: new[] { Convert.ToBase64String(new byte[] { 1, 2, 3 }), Convert.ToBase64String(new byte[] { 7, 8, 9 }) }); - var callDataResult = this.Serializer.Deserialize(this.Serializer.Serialize(contractTxData)); - var callData = callDataResult.Value; + Result callDataResult = this.Serializer.Deserialize(this.Serializer.Serialize(contractTxData)); + ContractTxData callData = callDataResult.Value; Assert.True((bool) callDataResult.IsSuccess); Assert.Equal(1, callData.VmVersion); @@ -73,8 +74,8 @@ public void TestMethod(int orders, bool canOrder) var contractTxData = new ContractTxData(1, 1, (RuntimeObserver.Gas)5000, contractExecutionCode, methodParameters); - var callDataResult = this.Serializer.Deserialize(this.Serializer.Serialize(contractTxData)); - var callData = callDataResult.Value; + Result callDataResult = this.Serializer.Deserialize(this.Serializer.Serialize(contractTxData)); + ContractTxData callData = callDataResult.Value; Assert.True((bool) callDataResult.IsSuccess); Assert.Equal(contractTxData.VmVersion, callData.VmVersion); @@ -106,8 +107,8 @@ public void SmartContract_CanSerialize_OP_CALLCONTRACT_WithoutMethodParameters() { var contractTxData = new ContractTxData(1, 1, (RuntimeObserver.Gas)5000, 100, "Execute"); - var callDataResult = this.Serializer.Deserialize(this.Serializer.Serialize(contractTxData)); - var callData = callDataResult.Value; + Result callDataResult = this.Serializer.Deserialize(this.Serializer.Serialize(contractTxData)); + ContractTxData callData = callDataResult.Value; Assert.True((bool) callDataResult.IsSuccess); Assert.Equal(contractTxData.VmVersion, callData.VmVersion); @@ -135,8 +136,8 @@ public void SmartContract_CanSerialize_OP_CALLCONTRACT_WithMethodParameters() }; var contractTxData = new ContractTxData(1, 1, (RuntimeObserver.Gas)5000, 100, "Execute", methodParameters); - var callDataResult = this.Serializer.Deserialize(this.Serializer.Serialize(contractTxData)); - var callData = callDataResult.Value; + Result callDataResult = this.Serializer.Deserialize(this.Serializer.Serialize(contractTxData)); + ContractTxData callData = callDataResult.Value; Assert.True((bool) callDataResult.IsSuccess); diff --git a/src/Stratis.SmartContracts.CLR.Tests/ContractTests.cs b/src/Stratis.SmartContracts.CLR.Tests/ContractTests.cs index be5fcd5879..da03b3ecde 100644 --- a/src/Stratis.SmartContracts.CLR.Tests/ContractTests.cs +++ b/src/Stratis.SmartContracts.CLR.Tests/ContractTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using Moq; using NBitcoin; using Stratis.SmartContracts.CLR.Exceptions; @@ -94,7 +95,7 @@ public string TestOptionalParam(string optional = "DefaultValue") public int Param { get; set; } - public ISmartContractState State { get; } + public new ISmartContractState State { get; } public int ConstructorCalledCount { get; } } @@ -268,9 +269,9 @@ public void Constructor_Does_Not_Exist_Tests() [Fact] public void HasReceive_Returns_Correct_Receive() { - var receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); + IContract receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); - var receiveMethod = ((Contract) receiveContract).ReceiveHandler; + MethodInfo receiveMethod = ((Contract) receiveContract).ReceiveHandler; Assert.NotNull(receiveMethod); } @@ -278,10 +279,10 @@ public void HasReceive_Returns_Correct_Receive() [Fact] public void HasNoReceive_Returns_Correct_Receive() { - var receiveContract = Contract.CreateUninitialized(typeof(HasNoReceive), this.state, this.address); + IContract receiveContract = Contract.CreateUninitialized(typeof(HasNoReceive), this.state, this.address); // ReceiveHandler should be null here because we set the binding flags to only resolve methods on the declared type - var receiveMethod = ((Contract)receiveContract).ReceiveHandler; + MethodInfo receiveMethod = ((Contract)receiveContract).ReceiveHandler; Assert.Null(receiveMethod); } @@ -289,11 +290,11 @@ public void HasNoReceive_Returns_Correct_Receive() [Fact] public void EmptyMethodName_Invokes_Receive() { - var receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); + IContract receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); var receiveInstance = (HasReceive) receiveContract.GetPrivateFieldValue("instance"); var methodCall = MethodCall.Receive(); - var result = receiveContract.Invoke(methodCall); + IContractInvocationResult result = receiveContract.Invoke(methodCall); Assert.True(result.IsSuccess); Assert.True(receiveInstance.ReceiveInvoked); @@ -302,10 +303,10 @@ public void EmptyMethodName_Invokes_Receive() [Fact] public void EmptyMethodName_DoesNot_Invoke_Receive() { - var receiveContract = Contract.CreateUninitialized(typeof(HasNoReceive), this.state, this.address); + IContract receiveContract = Contract.CreateUninitialized(typeof(HasNoReceive), this.state, this.address); var methodCall = MethodCall.Receive(); - var result = receiveContract.Invoke(methodCall); + IContractInvocationResult result = receiveContract.Invoke(methodCall); Assert.False(result.IsSuccess); } @@ -313,12 +314,12 @@ public void EmptyMethodName_DoesNot_Invoke_Receive() [Fact] public void EmptyMethodName_WithParams_DoesNot_Invoke_Receive() { - var receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); + IContract receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); var receiveInstance = (HasReceive)receiveContract.GetPrivateFieldValue("instance"); var parameters = new object[] { 1, "1" }; var methodCall = new MethodCall(MethodCall.ExternalReceiveHandlerName, parameters); - var result = receiveContract.Invoke(methodCall); + IContractInvocationResult result = receiveContract.Invoke(methodCall); Assert.False(result.IsSuccess); Assert.False(receiveInstance.ReceiveInvoked); @@ -328,11 +329,11 @@ public void EmptyMethodName_WithParams_DoesNot_Invoke_Receive() [Fact] public void Non_Existent_Method_DoesNot_Invoke_Receive() { - var receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); + IContract receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); var receiveInstance = (HasReceive)receiveContract.GetPrivateFieldValue("instance"); var methodCall = new MethodCall("DoesntExist"); - var result = receiveContract.Invoke(methodCall); + IContractInvocationResult result = receiveContract.Invoke(methodCall); Assert.False(result.IsSuccess); Assert.False(receiveInstance.ReceiveInvoked); @@ -343,7 +344,7 @@ public void Non_Existent_Method_DoesNot_Invoke_Receive() public void Invoke_Receive_Method_Sets_State() { var methodCall = MethodCall.Receive(); - var receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); + IContract receiveContract = Contract.CreateUninitialized(typeof(HasReceive), this.state, this.address); var receiveInstance = (HasReceive)receiveContract.GetPrivateFieldValue("instance"); receiveContract.Invoke(methodCall); diff --git a/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/ByteArrayConversion.cs b/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/ByteArrayConversion.cs index 688f57fdd5..9f065373b8 100644 --- a/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/ByteArrayConversion.cs +++ b/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/ByteArrayConversion.cs @@ -21,14 +21,14 @@ public static uint BytesToUInt(byte[] value) // Note: Use The Serializer/Converter property on SmartContract instead. public static ulong BytesToULong(byte[] value) { - return (ulong) value[0] - | (ulong)(value[1] << 8) - | (ulong)(value[2] << 16) - | (ulong)(value[3] << 24) - | (ulong)(value[4] << 32) - | (ulong)(value[5] << 40) - | (ulong)(value[6] << 48) - | (ulong)(value[7] << 56); + return (ulong)value[0] + | ((ulong)value[1] << 8) + | ((ulong)value[2] << 16) + | ((ulong)value[3] << 24) + | ((ulong)value[4] << 32) + | ((ulong)value[5] << 40) + | ((ulong)value[6] << 48) + | ((ulong)value[7] << 56); } // Note: Use The Serializer/Converter property on SmartContract instead. diff --git a/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/TryCatch.cs b/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/TryCatch.cs index f8bf67c3f2..26ab8afadc 100644 --- a/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/TryCatch.cs +++ b/src/Stratis.SmartContracts.CLR.Tests/SmartContracts/TryCatch.cs @@ -13,7 +13,6 @@ public bool Test(string input) try { throw new Exception(); - return true; } catch (ArgumentException e) { diff --git a/src/Stratis.SmartContracts.Core.Tests/Receipts/ReceiptSerializationTest.cs b/src/Stratis.SmartContracts.Core.Tests/Receipts/ReceiptSerializationTest.cs index f8f12f7bc2..a8dd18ef1c 100644 --- a/src/Stratis.SmartContracts.Core.Tests/Receipts/ReceiptSerializationTest.cs +++ b/src/Stratis.SmartContracts.Core.Tests/Receipts/ReceiptSerializationTest.cs @@ -111,8 +111,8 @@ private void TestStorageSerialize(Receipt receipt) /// /// Serializes a receipt without including the method name. For backwards compatibility testing. /// - /// - /// + /// See . + /// A byte array which is the serialized receipt. public byte[] ToStorageBytesRlp_NoMethodName(Receipt receipt) { IList encodedLogs = receipt.Logs.Select(x => RLP.EncodeElement(x.ToBytesRlp())).ToList(); @@ -136,9 +136,11 @@ public byte[] ToStorageBytesRlp_NoMethodName(Receipt receipt) } /// - /// Ensures 2 receipts and all their properties are equal. + /// Ensures that two receipts and all their properties are equal. /// - public static void TestStorageReceiptEquality(Receipt receipt1, Receipt receipt2) + /// The first receipt to compare. + /// The second receipt to compare. + internal static void TestStorageReceiptEquality(Receipt receipt1, Receipt receipt2) { Assert.Equal(receipt1.PostState, receipt2.PostState); Assert.Equal(receipt1.GasUsed, receipt2.GasUsed); diff --git a/src/Stratis.SmartContracts.IntegrationTests/ContractBehaviourTests.cs b/src/Stratis.SmartContracts.IntegrationTests/ContractBehaviourTests.cs index 6c1c74fc8a..36a0a9c4f2 100644 --- a/src/Stratis.SmartContracts.IntegrationTests/ContractBehaviourTests.cs +++ b/src/Stratis.SmartContracts.IntegrationTests/ContractBehaviourTests.cs @@ -234,13 +234,75 @@ public void Local_Call_Should_Produce_Logs_And_Transfers() } [Fact] - public void Local_Call_Returns_Correctly_Formatted_Value() + public void Local_Internal_Call_Should_Transfer_Value() { - var network = this.mockChain.Nodes[0].CoreNode.FullNode.Network; + // Ref: https://github.com/stratisproject/StratisFullNode/pull/662#issuecomment-902379004 + var localExecutor = this.mockChain.Nodes[0].CoreNode.FullNode.NodeService(); // Ensure fixture is funded. this.mockChain.MineBlocks(1); + // Deploy contract 1 + ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/ValueTransferTest.cs"); + + Assert.True(compilationResult.Success); + BuildCreateContractTransactionResponse valueTransferContractResponse = this.node1.SendCreateContractTransaction(compilationResult.Compilation, 0); + this.mockChain.WaitAllMempoolCount(1); + this.mockChain.MineBlocks(1); + + Assert.NotNull(this.node1.GetCode(valueTransferContractResponse.NewContractAddress)); + Assert.Equal(0UL, this.node1.GetContractBalance(valueTransferContractResponse.NewContractAddress)); + + ReceiptResponse receipt = this.node1.GetReceipt(valueTransferContractResponse.TransactionId.ToString()); + Assert.True(receipt.Success); + + // Deploy contract 2 + var compilationResult2 = ContractCompiler.CompileFile("SmartContracts/ValueTransferRecipient.cs"); + + Assert.True(compilationResult2.Success); + var recipientResponse = this.node1.SendCreateContractTransaction(compilationResult2.Compilation, 0); + this.mockChain.WaitAllMempoolCount(1); + this.mockChain.MineBlocks(1); + + Assert.NotNull(this.node1.GetCode(recipientResponse.NewContractAddress)); + Assert.Equal(0UL, this.node1.GetContractBalance(recipientResponse.NewContractAddress)); + + ReceiptResponse receipt2 = this.node1.GetReceipt(recipientResponse.TransactionId.ToString()); + Assert.True(receipt2.Success); + + uint256 currentHash = this.node1.GetLastBlock().GetHash(); + + NBitcoin.Block lastBlock = this.node1.GetLastBlock(); + + // Create a log in a local call. + var parameters = new object[] + { + recipientResponse.NewContractAddress.ToAddress(this.node1.CoreNode.FullNode.Network) + }; + + var call = new ContractTxData(1, 100, (Gas)250000, valueTransferContractResponse.NewContractAddress.ToUint160(this.node1.CoreNode.FullNode.Network), nameof(ValueTransferTest.CanForwardValueCall), parameters); + var internalTransferResult = localExecutor.Execute((ulong)this.node1.CoreNode.FullNode.ChainIndexer.Height, this.mockChain.Nodes[0].MinerAddress.Address.ToUint160(this.node1.CoreNode.FullNode.Network), 10, call); + + Assert.False(internalTransferResult.Revert); + + //Assert.NotEmpty(createLogResult.Logs); + //RLPCollection collection = (RLPCollection)RLP.Decode(createLogResult.Logs[0].Data); + //var loggedData = Encoding.UTF8.GetString(collection[0].RLPData); + //Assert.Equal(nameof(LocalCallTests.CreateLog), loggedData); + + //// Create a transfer in a local call + //call = new ContractTxData(1, 100, (Gas)250000, preResponse.NewContractAddress.ToUint160(this.node1.CoreNode.FullNode.Network), nameof(LocalCallTests.CreateTransfer)); + //var createTransferResult = localExecutor.Execute((ulong)this.node1.CoreNode.FullNode.ChainIndexer.Height, this.mockChain.Nodes[0].MinerAddress.Address.ToUint160(this.node1.CoreNode.FullNode.Network), 0, call); + + ////Assert.NotEmpty(createTransferResult.InternalTransfers); + ////Assert.Equal(Address.Zero.ToUint160(), createTransferResult.InternalTransfers[0].To); + ////Assert.Equal(1UL, createTransferResult.InternalTransfers[0].Value); + } + + [Fact] + public void Local_Call_Returns_Correctly_Formatted_Value() + { + var network = this.mockChain.Nodes[0].CoreNode.FullNode.Network; // Deploy contract ContractCompilationResult compilationResult = ContractCompiler.CompileFile("SmartContracts/LocalCallTests.cs"); diff --git a/src/Stratis.SmartContracts.IntegrationTests/ContractParameterSerializationTests.cs b/src/Stratis.SmartContracts.IntegrationTests/ContractParameterSerializationTests.cs index b9eb17192b..36cbc7849c 100644 --- a/src/Stratis.SmartContracts.IntegrationTests/ContractParameterSerializationTests.cs +++ b/src/Stratis.SmartContracts.IntegrationTests/ContractParameterSerializationTests.cs @@ -43,10 +43,10 @@ public void CreateContract_OneOfEachParameterType() string testAddressBase58 = new uint160("0x0000000000000000000000000000000000000001").ToBase58Address(this.node1.CoreNode.FullNode.Network); Address testAddress = testAddressBase58.ToAddress(this.node1.CoreNode.FullNode.Network); const bool testBool = true; - const int testInt = Int32.MaxValue; - const long testLong = Int64.MaxValue; - const uint testUint = UInt32.MaxValue; - const ulong testUlong = UInt64.MaxValue; + const int testInt = int.MaxValue; + const long testLong = long.MaxValue; + const uint testUint = uint.MaxValue; + const ulong testUlong = ulong.MaxValue; UInt128 testUInt128 = UInt128.MaxValue; UInt256 testUInt256 = UInt256.MaxValue; const string testString = "The quick brown fox jumps over the lazy dog"; @@ -148,10 +148,10 @@ public void CallContract_SerializeEachParameterType() const char testChar = 'c'; string testAddress = new uint160("0x0000000000000000000000000000000000000001").ToBase58Address(this.node1.CoreNode.FullNode.Network); const bool testBool = true; - const int testInt = Int32.MaxValue; - const long testLong = Int64.MaxValue; - const uint testUint = UInt32.MaxValue; - const ulong testUlong = UInt64.MaxValue; + const int testInt = int.MaxValue; + const long testLong = long.MaxValue; + const uint testUint = uint.MaxValue; + const ulong testUlong = ulong.MaxValue; UInt128 testUInt128 = UInt128.MaxValue; UInt256 testUInt256 = UInt256.MaxValue; const string testString = "The quick brown fox jumps over the lazy dog"; @@ -216,10 +216,10 @@ public void Internal_CallContract_SerializeEachParameterType() const char testChar = 'c'; string testAddressBase58 = new uint160("0x0000000000000000000000000000000000000001").ToBase58Address(this.node1.CoreNode.FullNode.Network); const bool testBool = true; - const int testInt = Int32.MaxValue; - const long testLong = Int64.MaxValue; - const uint testUint = UInt32.MaxValue; - const ulong testUlong = UInt64.MaxValue; + const int testInt = int.MaxValue; + const long testLong = long.MaxValue; + const uint testUint = uint.MaxValue; + const ulong testUlong = ulong.MaxValue; UInt128 testUInt128 = UInt128.MaxValue; UInt256 testUInt256 = UInt256.MaxValue; const string testString = "The quick brown fox jumps over the lazy dog"; diff --git a/src/Stratis.SmartContracts.IntegrationTests/GlobalSuppressions.cs b/src/Stratis.SmartContracts.IntegrationTests/GlobalSuppressions.cs new file mode 100644 index 0000000000..5a75be4c8a --- /dev/null +++ b/src/Stratis.SmartContracts.IntegrationTests/GlobalSuppressions.cs @@ -0,0 +1,11 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE0009:Member access should be qualified.", Justification = "", Scope = "type", Target = "~T:StratisCollectible")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1611:Element parameters must be documented", Justification = "", Scope = "type", Target = "~T:StratisCollectible")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1615:Element return value must be documented", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.SmartContracts.IntegrationTests")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1611:The documentation for parameter is missing", Justification = "Temporary", Scope = "namespaceanddescendants", Target = "~N:Stratis.SmartContracts.IntegrationTests")] \ No newline at end of file diff --git a/src/Stratis.SmartContracts.IntegrationTests/PoW/SmartContractMinerTests.cs b/src/Stratis.SmartContracts.IntegrationTests/PoW/SmartContractMinerTests.cs index c3c2f7ef78..0e687e853c 100644 --- a/src/Stratis.SmartContracts.IntegrationTests/PoW/SmartContractMinerTests.cs +++ b/src/Stratis.SmartContracts.IntegrationTests/PoW/SmartContractMinerTests.cs @@ -256,7 +256,7 @@ public async Task InitializeAsync([CallerMemberName] string callingMethod = "") this.cachedCoinView, chainState, new InvalidBlockHashStore(dateTimeProvider), - new NodeStats(dateTimeProvider, NodeSettings.Default(network), new Mock().Object), + new NodeStats(dateTimeProvider, NodeSettings.Default(this.network), new Mock().Object), asyncProvider, consensusRulesContainer) .SetupRulesEngineParent(); @@ -357,6 +357,7 @@ private void InitializeSmartContractComponents(string callingMethod) /// /// Tests creation of a simple token contract /// + /// The asynchronous task. [Fact] public async Task SmartContracts_CreateTokenContract_Async() { @@ -384,6 +385,7 @@ public async Task SmartContracts_CreateTokenContract_Async() /// /// Try and spend outputs we don't own /// + /// The asynchronous task. [Fact] public async Task SmartContracts_TrySpendingFundsThatArentOurs_Async() { @@ -446,6 +448,7 @@ await Assert.ThrowsAsync(async () => /// /// Test that contracts correctly send funds to one person /// + /// The asynchronous task. [Fact] public async Task SmartContracts_TransferFundsToSingleRecipient_Async() { @@ -498,6 +501,7 @@ public async Task SmartContracts_TransferFundsToSingleRecipient_Async() /// /// Send funds with create /// + /// The asynchronous task. [Fact] public async Task SmartContracts_CreateWithFunds_Success_Async() { @@ -524,6 +528,7 @@ public async Task SmartContracts_CreateWithFunds_Success_Async() /// /// Test that contract correctly send funds to 2 people inside one contract call /// + /// The asynchronous task. [Fact] public async Task SmartContracts_TransferFundsToMultipleRecipients_Async() { @@ -578,6 +583,7 @@ public async Task SmartContracts_TransferFundsToMultipleRecipients_Async() /// /// Tests that contracts manage their UTXOs correctly when not sending funds or receiving funds. /// + /// The asynchronous task. [Fact] public async Task SmartContracts_SendValue_NoTransfers_Async() { @@ -608,6 +614,7 @@ public async Task SmartContracts_SendValue_NoTransfers_Async() /// /// Tests that contracts manage their UTXOs correctly when not sending funds or receiving funds. /// + /// The asynchronous task. [Fact] public async Task SmartContracts_NoTransfers_Async() { @@ -652,6 +659,7 @@ public async Task SmartContracts_NoTransfers_Async() /// /// Should deploy 2 contracts, and then send funds from one to the other and end up with correct balances for all. /// + /// The asynchronous task. [Fact] public async Task SmartContracts_TransferToP2PKH_Async() { @@ -688,6 +696,7 @@ public async Task SmartContracts_TransferToP2PKH_Async() /// /// Should deploy 2 contracts, and then send funds from one to the other and end up with correct balances for all. /// + /// The asynchronous task. [Fact] public async Task SmartContracts_TransferBetweenContracts_Async() { @@ -745,6 +754,7 @@ public async Task SmartContracts_TransferBetweenContracts_Async() /// /// Should deploy 2 contracts, invoke a method on one, get the value from it, and persist it /// + /// The asynchronous task. [Fact] public async Task SmartContracts_InvokeContract_Async() { @@ -837,8 +847,9 @@ public async Task SmartContracts_TransferBetweenContracts_WithException_Async() /// /// Can execute a smart contract transaction referencing a P2PKH that's in the same block, above it. /// + /// The asynchronous task. [Fact] - public async Task SmartContract_ReferencingInputInSameBlock() + public async Task SmartContract_ReferencingInputInSameBlock_Async() { TestContext context = new TestContext(); await context.InitializeAsync(); @@ -874,6 +885,7 @@ public async Task SmartContract_ReferencingInputInSameBlock() /// /// Should send funds to another contract, causing the contract's ReceiveHandler function to be invoked. /// + /// The asynchronous task. [Fact] public async Task SmartContracts_TransferFunds_Invokes_Receive_Async() { diff --git a/src/Stratis.SmartContracts.IntegrationTests/PoW/SmartContractWalletTests.cs b/src/Stratis.SmartContracts.IntegrationTests/PoW/SmartContractWalletTests.cs index f3fd993764..fe4da3c21e 100644 --- a/src/Stratis.SmartContracts.IntegrationTests/PoW/SmartContractWalletTests.cs +++ b/src/Stratis.SmartContracts.IntegrationTests/PoW/SmartContractWalletTests.cs @@ -412,7 +412,7 @@ public void MockChain_NonFungibleToken() returnedAddressUint160 = ((Address)result.Return).ToUint160(); Assert.Equal(receiverAddressUint160, returnedAddressUint160); - IList receipts = node1.GetReceipts(response.NewContractAddress, "Transfer"); + IList receipts = node1.GetReceipts(response.NewContractAddress, "Transfer2"); Assert.Single(receipts); } } diff --git a/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/LargeStruct.cs b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/LargeStruct.cs deleted file mode 100644 index 6734608659..0000000000 --- a/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/LargeStruct.cs +++ /dev/null @@ -1,5162 +0,0 @@ -using Stratis.SmartContracts; - -#pragma warning disable 169 -#pragma warning disable IDE0044 // Add readonly modifier - -[Deploy] -public class F : SmartContract -{ - public F(ISmartContractState state) : base(state) - { - var s = new S(); - } - struct S - { - ulong F504069340; - ulong F155929222; - ulong F505060916; - ulong F1127146635; - ulong F43019765; - ulong F328960678; - ulong F1538960101; - ulong F1317393637; - ulong F250561939; - ulong F1631640650; - ulong F105938986; - ulong F836389238; - ulong F509403677; - ulong F67301662; - ulong F318132372; - ulong F1079270728; - ulong F1902848148; - ulong F1406696182; - ulong F572151116; - ulong F1017842440; - ulong F1784406495; - ulong F870715689; - ulong F802953906; - ulong F797969038; - ulong F308225960; - ulong F246518021; - ulong F1189198687; - ulong F1861930460; - ulong F2021663448; - ulong F1183334297; - ulong F1096927839; - ulong F1914039313; - ulong F1847245389; - ulong F561899085; - ulong F700403041; - ulong F345256201; - ulong F1134427571; - ulong F1750254034; - ulong F259087125; - ulong F1344691675; - ulong F1600329807; - ulong F520927207; - ulong F765043337; - ulong F1602847676; - ulong F1923678952; - ulong F1093307720; - ulong F1820458936; - ulong F1569592508; - ulong F1588755505; - ulong F1217903335; - ulong F1571008935; - ulong F276004635; - ulong F1166993579; - ulong F842399901; - ulong F1907497284; - ulong F1780837298; - ulong F1500458963; - ulong F1854575525; - ulong F818920675; - ulong F1943985391; - ulong F1287245638; - ulong F1824513288; - ulong F1443213836; - ulong F1214711289; - ulong F534712811; - ulong F339383320; - ulong F1136627496; - ulong F2094988239; - ulong F1514382268; - ulong F2120359818; - ulong F2092326804; - ulong F152594114; - ulong F1147609057; - ulong F1374943088; - ulong F1564996280; - ulong F1263479288; - ulong F105672352; - ulong F1347589877; - ulong F1021773733; - ulong F1362401887; - ulong F573542732; - ulong F1767089826; - ulong F273174955; - ulong F803760113; - ulong F1759809009; - ulong F820923204; - ulong F747045734; - ulong F1004845488; - ulong F801885448; - ulong F1067049390; - ulong F992280885; - ulong F1427335693; - ulong F931333359; - ulong F462585381; - ulong F57446037; - ulong F1923300166; - ulong F1225197018; - ulong F1697815695; - ulong F1068134865; - ulong F1584295632; - ulong F2104163871; - ulong F1872954344; - ulong F55210240; - ulong F1615879334; - ulong F1273060178; - ulong F1418414821; - ulong F1275879225; - ulong F1939534138; - ulong F1424887268; - ulong F644017996; - ulong F1675164946; - ulong F152869086; - ulong F832801792; - ulong F1604002435; - ulong F1370442659; - ulong F1667639459; - ulong F1551338333; - ulong F639453723; - ulong F1602385927; - ulong F1861273254; - ulong F1739821233; - ulong F131782008; - ulong F1293102791; - ulong F447332878; - ulong F1128078933; - ulong F664991111; - ulong F1368744402; - ulong F685023676; - ulong F1317497051; - ulong F1789179761; - ulong F38282270; - ulong F555340304; - ulong F279455012; - ulong F1584961748; - ulong F1405721663; - ulong F848072035; - ulong F1711879586; - ulong F804779268; - ulong F1678183582; - ulong F341394188; - ulong F1692527626; - ulong F954995243; - ulong F1727441867; - ulong F157867452; - ulong F1539368091; - ulong F839411799; - ulong F594533901; - ulong F1474814571; - ulong F1239626369; - ulong F537290225; - ulong F371961833; - ulong F585743295; - ulong F95429768; - ulong F1354345258; - ulong F1991958046; - ulong F1972381863; - ulong F579851553; - ulong F1755361009; - ulong F487800401; - ulong F608069067; - ulong F49670419; - ulong F590855549; - ulong F622037087; - ulong F1783191154; - ulong F605735726; - ulong F1119824642; - ulong F2020897721; - ulong F1395323691; - ulong F198280772; - ulong F522370623; - ulong F2103243520; - ulong F746559065; - ulong F1108753788; - ulong F1260991739; - ulong F168745628; - ulong F784825990; - ulong F551823788; - ulong F1135235339; - ulong F1055448434; - ulong F288667134; - ulong F70457210; - ulong F2041413478; - ulong F1592880954; - ulong F780206826; - ulong F1417217928; - ulong F1600022622; - ulong F459910536; - ulong F1072593401; - ulong F1740487349; - ulong F1580823447; - ulong F268220482; - ulong F2104002224; - ulong F316978867; - ulong F1070114515; - ulong F291723769; - ulong F1101672077; - ulong F332958156; - ulong F2091734360; - ulong F1699615373; - ulong F419543449; - ulong F965997725; - ulong F1346693857; - ulong F1276533799; - ulong F717255745; - ulong F581530352; - ulong F1772886415; - ulong F1624473154; - ulong F981921676; - ulong F1185599630; - ulong F1207132056; - ulong F1420558075; - ulong F1592099861; - ulong F699912575; - ulong F199133267; - ulong F537611857; - ulong F155740588; - ulong F1145458242; - ulong F1989313908; - ulong F365973226; - ulong F1153196751; - ulong F659914106; - ulong F948304320; - ulong F1802319989; - ulong F764940972; - ulong F254150142; - ulong F2146724943; - ulong F429580198; - ulong F38639273; - ulong F969267970; - ulong F1214557198; - ulong F451867834; - ulong F607573075; - ulong F1583103613; - ulong F635904985; - ulong F1470153056; - ulong F871247000; - ulong F764879679; - ulong F875625209; - ulong F198676474; - ulong F1791815160; - ulong F2123033115; - ulong F1625472507; - ulong F2034477418; - ulong F533355293; - ulong F160265372; - ulong F823604268; - ulong F1404089649; - ulong F117845600; - ulong F532502658; - ulong F135983181; - ulong F2103697482; - ulong F491127895; - ulong F1725761134; - ulong F546418622; - ulong F1907112990; - ulong F17693405; - ulong F1691857515; - ulong F511592827; - ulong F463105603; - ulong F582289056; - ulong F1343306217; - ulong F1585833881; - ulong F12653705; - ulong F2118526079; - ulong F755264222; - ulong F812985000; - ulong F8996248; - ulong F64007590; - ulong F876463858; - ulong F1813848504; - ulong F1538344556; - ulong F269833033; - ulong F1790637434; - ulong F721641713; - ulong F1177647283; - ulong F1181925246; - ulong F1061310549; - ulong F1268964696; - ulong F604675600; - ulong F1578029521; - ulong F742635294; - ulong F311734598; - ulong F1653620262; - ulong F833284789; - ulong F1258343363; - ulong F2108223586; - ulong F1029295588; - ulong F1036684991; - ulong F876275642; - ulong F1452459651; - ulong F1326873132; - ulong F253286852; - ulong F412519606; - ulong F1763871065; - ulong F448508943; - ulong F537199234; - ulong F1612818801; - ulong F2063434986; - ulong F1925574718; - ulong F1494764019; - ulong F814608020; - ulong F1340082059; - ulong F1388865389; - ulong F866137801; - ulong F745122272; - ulong F1833864449; - ulong F847974108; - ulong F1004119421; - ulong F1516254986; - ulong F725187744; - ulong F1103866503; - ulong F422892819; - ulong F2054400874; - ulong F1032559729; - ulong F1987137409; - ulong F1031571619; - ulong F2079697266; - ulong F1326852564; - ulong F860182716; - ulong F794524283; - ulong F1931173059; - ulong F1119794904; - ulong F1335215595; - ulong F1571487854; - ulong F486975372; - ulong F1285057704; - ulong F2004797074; - ulong F26766369; - ulong F273132770; - ulong F640448049; - ulong F1716590092; - ulong F1145359210; - ulong F1490873625; - ulong F1257395228; - ulong F763421501; - ulong F1550036882; - ulong F1070352856; - ulong F787482461; - ulong F88162517; - ulong F1571962561; - ulong F1260249478; - ulong F25176167; - ulong F1667913652; - ulong F151087898; - ulong F348593148; - ulong F903980313; - ulong F346369625; - ulong F1527443524; - ulong F1924217303; - ulong F1564420971; - ulong F604985615; - ulong F285966237; - ulong F1203252270; - ulong F1131050435; - ulong F1711074607; - ulong F1842296763; - ulong F4866464; - ulong F1964861182; - ulong F379162429; - ulong F1607548215; - ulong F1976551022; - ulong F821207739; - ulong F730986651; - ulong F875806937; - ulong F1156081299; - ulong F2105990940; - ulong F1079502841; - ulong F797005646; - ulong F269138228; - ulong F437100527; - ulong F2108702410; - ulong F1292214805; - ulong F1238690047; - ulong F1435703802; - ulong F1681758452; - ulong F1905996892; - ulong F1599364899; - ulong F1184127697; - ulong F1222894706; - ulong F1730478706; - ulong F938688079; - ulong F477353550; - ulong F250032713; - ulong F856195446; - ulong F35462434; - ulong F1430623855; - ulong F2089590587; - ulong F359823190; - ulong F1693804268; - ulong F1068608385; - ulong F1545170418; - ulong F1252975321; - ulong F408320032; - ulong F628097949; - ulong F1742895186; - ulong F439041739; - ulong F1441673163; - ulong F792106715; - ulong F1142490246; - ulong F390085855; - ulong F1971961119; - ulong F1696847626; - ulong F1258305296; - ulong F1487116776; - ulong F1603202208; - ulong F1460254457; - ulong F1194759837; - ulong F1915032115; - ulong F1596775630; - ulong F1952561362; - ulong F242931864; - ulong F968222414; - ulong F741966476; - ulong F796167370; - ulong F668860136; - ulong F1499197472; - ulong F571175026; - ulong F2022274852; - ulong F840344503; - ulong F1872941091; - ulong F16400353; - ulong F719679651; - ulong F1250685025; - ulong F1348013490; - ulong F1039413756; - ulong F855727089; - ulong F883894773; - ulong F610592098; - ulong F1840292263; - ulong F1242716713; - ulong F464323729; - ulong F807258184; - ulong F41637451; - ulong F832808851; - ulong F1906001234; - ulong F1389324100; - ulong F1366531901; - ulong F910399584; - ulong F1400476885; - ulong F722691624; - ulong F235864018; - ulong F174558472; - ulong F910531207; - ulong F1888726553; - ulong F825676521; - ulong F576948004; - ulong F511008845; - ulong F1759636309; - ulong F2106721460; - ulong F243697714; - ulong F2015350360; - ulong F1566881958; - ulong F2099245859; - ulong F1417032802; - ulong F373685502; - ulong F1252281468; - ulong F446162601; - ulong F2057775453; - ulong F447703020; - ulong F747475119; - ulong F576359684; - ulong F584167739; - ulong F74739852; - ulong F354058917; - ulong F1488237633; - ulong F1583157327; - ulong F926584963; - ulong F2056641272; - ulong F1037649783; - ulong F1427019683; - ulong F132665571; - ulong F1808259089; - ulong F621797967; - ulong F117652879; - ulong F1637077073; - ulong F1989325528; - ulong F1956632091; - ulong F1509442119; - ulong F522336969; - ulong F462465752; - ulong F344718244; - ulong F1271742111; - ulong F651354285; - ulong F1596594549; - ulong F1374850000; - ulong F1044925418; - ulong F855495972; - ulong F772088296; - ulong F459123349; - ulong F653719766; - ulong F943161499; - ulong F1456240095; - ulong F462696564; - ulong F653001766; - ulong F146331940; - ulong F1799179926; - ulong F99818620; - ulong F556472290; - ulong F400488920; - ulong F1390002841; - ulong F1797846688; - ulong F601851220; - ulong F721986526; - ulong F679701777; - ulong F111032143; - ulong F207091271; - ulong F945083991; - ulong F1981592980; - ulong F1927439376; - ulong F531843621; - ulong F1443133024; - ulong F1084204129; - ulong F1535438484; - ulong F2132720915; - ulong F402756875; - ulong F1452101220; - ulong F2080297101; - ulong F625628950; - ulong F1126692564; - ulong F443312215; - ulong F727661355; - ulong F154496667; - ulong F1597517923; - ulong F383930017; - ulong F483858184; - ulong F823909123; - ulong F1345562525; - ulong F2116279848; - ulong F2118804586; - ulong F1985380794; - ulong F1889506908; - ulong F1400159801; - ulong F1108953199; - ulong F1279817775; - ulong F812102711; - ulong F1890350671; - ulong F549755585; - ulong F2119136155; - ulong F1485562406; - ulong F1167758729; - ulong F99841427; - ulong F1021386639; - ulong F992132567; - ulong F2074763375; - ulong F1358070389; - ulong F2006441017; - ulong F2068285258; - ulong F477459296; - ulong F250244891; - ulong F841714367; - ulong F1866366472; - ulong F1621673317; - ulong F1577263373; - ulong F2104660352; - ulong F662341486; - ulong F1643350021; - ulong F1151816944; - ulong F338056509; - ulong F195843593; - ulong F1434606667; - ulong F1009012393; - ulong F976287790; - ulong F2010272041; - ulong F2089542229; - ulong F789820360; - ulong F42973223; - ulong F2122734577; - ulong F255620709; - ulong F1320618204; - ulong F659889851; - ulong F902345635; - ulong F2108644593; - ulong F1287550191; - ulong F2106417482; - ulong F343470788; - ulong F1853758363; - ulong F1309847747; - ulong F1670238195; - ulong F1173343275; - ulong F624900814; - ulong F903107512; - ulong F868103229; - ulong F1866034957; - ulong F1277090219; - ulong F119014322; - ulong F267833591; - ulong F1970380075; - ulong F1151776494; - ulong F617476289; - ulong F1316236337; - ulong F738533727; - ulong F211699076; - ulong F1923292562; - ulong F50955739; - ulong F158746336; - ulong F1271037284; - ulong F1158598245; - ulong F1050073985; - ulong F1284943015; - ulong F1315097166; - ulong F2031190087; - ulong F1812664549; - ulong F1304324739; - ulong F1737838687; - ulong F2086852379; - ulong F1905205526; - ulong F334123126; - ulong F1618329538; - ulong F1761189564; - ulong F956066770; - ulong F333502274; - ulong F1629062396; - ulong F1312196881; - ulong F1718426426; - ulong F531499155; - ulong F140909164; - ulong F1257736480; - ulong F733181822; - ulong F1970527907; - ulong F521986769; - ulong F220076795; - ulong F970958083; - ulong F1785628067; - ulong F4381867; - ulong F2068839771; - ulong F690646559; - ulong F185352031; - ulong F1236594452; - ulong F1947671146; - ulong F1219917151; - ulong F695160118; - ulong F259773762; - ulong F385295180; - ulong F2005729756; - ulong F741194374; - ulong F1237926610; - ulong F1711262137; - ulong F128196270; - ulong F1337721487; - ulong F361292443; - ulong F2081194112; - ulong F352050537; - ulong F1538070577; - ulong F1808893166; - ulong F982734063; - ulong F1256954978; - ulong F1046985842; - ulong F204866136; - ulong F1666940231; - ulong F17837171; - ulong F13300804; - ulong F425416423; - ulong F1227029725; - ulong F762956246; - ulong F1095020371; - ulong F1060232004; - ulong F27036482; - ulong F1299942872; - ulong F1816482563; - ulong F1396205820; - ulong F1719853495; - ulong F1245012321; - ulong F1818142039; - ulong F541272413; - ulong F260906652; - ulong F73728512; - ulong F1243767216; - ulong F1453950772; - ulong F977232052; - ulong F1441056192; - ulong F577130674; - ulong F1129540210; - ulong F1542943982; - ulong F1609235464; - ulong F588276304; - ulong F2015509905; - ulong F1580371153; - ulong F2124218548; - ulong F1169131451; - ulong F811884793; - ulong F1791144364; - ulong F2127969542; - ulong F1717137868; - ulong F1929833974; - ulong F1206616347; - ulong F269743695; - ulong F1180227684; - ulong F1769822581; - ulong F910709385; - ulong F1828446017; - ulong F1210890128; - ulong F411319265; - ulong F459197354; - ulong F2088999314; - ulong F788922595; - ulong F836181791; - ulong F681392145; - ulong F996798164; - ulong F1547986514; - ulong F909005551; - ulong F13187761; - ulong F1740518717; - ulong F1375117731; - ulong F225884039; - ulong F1588190145; - ulong F1031244241; - ulong F1029956088; - ulong F1765277908; - ulong F174679942; - ulong F1226994113; - ulong F1627344498; - ulong F50301581; - ulong F130811421; - ulong F1004597770; - ulong F1752545103; - ulong F1739367600; - ulong F1675358100; - ulong F2035791712; - ulong F1482139713; - ulong F2138646604; - ulong F1040984475; - ulong F1621428282; - ulong F543241387; - ulong F1296269682; - ulong F230166064; - ulong F165811409; - ulong F670342856; - ulong F1601428315; - ulong F820312869; - ulong F1899578160; - ulong F1334117760; - ulong F583572989; - ulong F576232034; - ulong F260125900; - ulong F798697031; - ulong F50625647; - ulong F752851811; - ulong F1491253829; - ulong F341643829; - ulong F175372106; - ulong F1387271254; - ulong F1562433423; - ulong F1595142639; - ulong F1831198919; - ulong F201101519; - ulong F1160588547; - ulong F280507844; - ulong F1602083231; - ulong F336454211; - ulong F1197038642; - ulong F1308307338; - ulong F793084080; - ulong F1662142098; - ulong F1556823557; - ulong F2015504723; - ulong F539243127; - ulong F1197277330; - ulong F78848049; - ulong F2143201622; - ulong F1422378736; - ulong F360901385; - ulong F1576011420; - ulong F944965039; - ulong F422585429; - ulong F2040360000; - ulong F1043771509; - ulong F1621553194; - ulong F2018169168; - ulong F205900739; - ulong F1701919456; - ulong F986515789; - ulong F184104271; - ulong F1694147883; - ulong F1306767607; - ulong F751375350; - ulong F1626034699; - ulong F26285643; - ulong F859526115; - ulong F1095168163; - ulong F1217061164; - ulong F2032787212; - ulong F1215743272; - ulong F1264974104; - ulong F1770757874; - ulong F591270822; - ulong F541033680; - ulong F1068914538; - ulong F1166892124; - ulong F392104824; - ulong F259453904; - ulong F1000831964; - ulong F674003762; - ulong F1495535854; - ulong F1066748740; - ulong F1961954368; - ulong F1958743481; - ulong F617468384; - ulong F1172557210; - ulong F1938322566; - ulong F1304813657; - ulong F1686519000; - ulong F409822323; - ulong F1396182492; - ulong F782018402; - ulong F210522853; - ulong F1124203067; - ulong F1246419844; - ulong F355374491; - ulong F805448207; - ulong F389470024; - ulong F512957484; - ulong F337751215; - ulong F1131163533; - ulong F926140458; - ulong F1537075171; - ulong F1292641760; - ulong F311037316; - ulong F1321690812; - ulong F1978798254; - ulong F1499326320; - ulong F2122340618; - ulong F454661070; - ulong F1626064344; - ulong F2093930482; - ulong F701087492; - ulong F312512027; - ulong F836052064; - ulong F627399143; - ulong F1492296886; - ulong F940115516; - ulong F1008566315; - ulong F1001212080; - ulong F1068687196; - ulong F1937838153; - ulong F1678025811; - ulong F1622964889; - ulong F1967044427; - ulong F482955702; - ulong F1560235021; - ulong F1614551402; - ulong F1442097483; - ulong F713540047; - ulong F361443917; - ulong F2634800; - ulong F1893980067; - ulong F663080749; - ulong F1690323876; - ulong F569395396; - ulong F1677157216; - ulong F669312608; - ulong F1647706165; - ulong F1443261219; - ulong F1341242603; - ulong F438996246; - ulong F1329956686; - ulong F1231857930; - ulong F931241626; - ulong F1449735657; - ulong F80930910; - ulong F2045494473; - ulong F288151003; - ulong F619020701; - ulong F1010561252; - ulong F2012816338; - ulong F1528387356; - ulong F1659229051; - ulong F1416547666; - ulong F1340809027; - ulong F1395598294; - ulong F2061593929; - ulong F1473080980; - ulong F1975565261; - ulong F1908939438; - ulong F364246852; - ulong F57228837; - ulong F1408800571; - ulong F93217153; - ulong F1623429544; - ulong F199950415; - ulong F38006743; - ulong F769671798; - ulong F266656668; - ulong F1097725574; - ulong F822984278; - ulong F1439892998; - ulong F1712788743; - ulong F1807453124; - ulong F629690950; - ulong F607881467; - ulong F446167881; - ulong F691723263; - ulong F517308770; - ulong F402024792; - ulong F1662224195; - ulong F1326400399; - ulong F823076782; - ulong F1850462442; - ulong F496111226; - ulong F621731091; - ulong F234751016; - ulong F1394016730; - ulong F349514849; - ulong F1321280749; - ulong F1763046934; - ulong F1343715275; - ulong F1819624551; - ulong F1681805428; - ulong F976995751; - ulong F381767409; - ulong F2068639762; - ulong F1138640777; - ulong F1455295729; - ulong F1249785242; - ulong F42924167; - ulong F1275822675; - ulong F21494335; - ulong F1668778774; - ulong F187576974; - ulong F572923340; - ulong F1963082260; - ulong F1999259574; - ulong F786856716; - ulong F732927560; - ulong F949430413; - ulong F1369870666; - ulong F955772210; - ulong F1573540469; - ulong F246715243; - ulong F1185330100; - ulong F1381635702; - ulong F1705821776; - ulong F1744589574; - ulong F1001698453; - ulong F2112683046; - ulong F791473660; - ulong F420156949; - ulong F1092859566; - ulong F1482162287; - ulong F1626752650; - ulong F1767752094; - ulong F30983315; - ulong F830457373; - ulong F247923541; - ulong F686725352; - ulong F1455010751; - ulong F1383911181; - ulong F1415007175; - ulong F359100625; - ulong F386401520; - ulong F1304906064; - ulong F1301781655; - ulong F1662885468; - ulong F2070671533; - ulong F806132478; - ulong F382975089; - ulong F607160014; - ulong F1764070936; - ulong F371850336; - ulong F393176268; - ulong F387943065; - ulong F246084082; - ulong F1435090185; - ulong F1939149298; - ulong F1147615354; - ulong F362817986; - ulong F1541534850; - ulong F453597276; - ulong F1284585843; - ulong F1398934154; - ulong F855665726; - ulong F1076118416; - ulong F186616487; - ulong F708307971; - ulong F952654893; - ulong F1932098945; - ulong F1168802201; - ulong F538933175; - ulong F46202208; - ulong F1641903309; - ulong F2133443132; - ulong F1688248682; - ulong F1214439844; - ulong F2007797370; - ulong F2027907683; - ulong F79854047; - ulong F42936308; - ulong F1821401688; - ulong F195565975; - ulong F1729707957; - ulong F184313646; - ulong F803569660; - ulong F721009230; - ulong F1088986019; - ulong F1238809585; - ulong F1521668012; - ulong F743376777; - ulong F1038791722; - ulong F1247791834; - ulong F323907366; - ulong F2060959548; - ulong F930313905; - ulong F130421332; - ulong F1107650118; - ulong F1678219441; - ulong F228787648; - ulong F1115165168; - ulong F954577497; - ulong F1118016640; - ulong F1021517180; - ulong F1361656535; - ulong F68226839; - ulong F1717868728; - ulong F877430674; - ulong F407216783; - ulong F847178030; - ulong F1179127885; - ulong F1574776462; - ulong F2058725262; - ulong F1067761307; - ulong F319881678; - ulong F1867616809; - ulong F258031301; - ulong F1702361533; - ulong F1214620508; - ulong F52096066; - ulong F355109186; - ulong F1245114115; - ulong F1616982033; - ulong F1578470528; - ulong F1188722168; - ulong F130010479; - ulong F1438624988; - ulong F1869778489; - ulong F1728427408; - ulong F1203129227; - ulong F1557827350; - ulong F106789726; - ulong F329577929; - ulong F1799120035; - ulong F1112172526; - ulong F1235842458; - ulong F703385048; - ulong F1321532442; - ulong F368051422; - ulong F116086807; - ulong F1233184579; - ulong F1991062203; - ulong F681769236; - ulong F391631555; - ulong F342540127; - ulong F1316083962; - ulong F1127550107; - ulong F180030527; - ulong F4025688; - ulong F193342739; - ulong F672282604; - ulong F575543446; - ulong F2040513257; - ulong F1626123375; - ulong F2021162109; - ulong F2017534700; - ulong F1485079111; - ulong F1687029759; - ulong F1980278659; - ulong F1231646056; - ulong F777085498; - ulong F1995573886; - ulong F1296486913; - ulong F1351571203; - ulong F1436834327; - ulong F1072338159; - ulong F1245198533; - ulong F259605227; - ulong F2103072428; - ulong F1231522867; - ulong F1164231761; - ulong F1083982506; - ulong F1334310111; - ulong F1098533701; - ulong F966395134; - ulong F511530630; - ulong F563344879; - ulong F1225350478; - ulong F1235930401; - ulong F2020121853; - ulong F1149944019; - ulong F1258594461; - ulong F1865752801; - ulong F1535084669; - ulong F530846623; - ulong F982283903; - ulong F213760116; - ulong F850938201; - ulong F1925441573; - ulong F1242121473; - ulong F1898246994; - ulong F1163838936; - ulong F1488737430; - ulong F1283889013; - ulong F1486484956; - ulong F1385094340; - ulong F694575290; - ulong F1477681680; - ulong F1102280875; - ulong F1417685615; - ulong F70885429; - ulong F867944880; - ulong F224441746; - ulong F919986468; - ulong F1176594625; - ulong F1735783745; - ulong F1388716982; - ulong F941979556; - ulong F659728241; - ulong F1509631479; - ulong F1454189821; - ulong F259728633; - ulong F451099358; - ulong F2107640453; - ulong F81702037; - ulong F1665974684; - ulong F129821085; - ulong F1908885891; - ulong F820724580; - ulong F454550423; - ulong F858578043; - ulong F394260332; - ulong F481647301; - ulong F860950955; - ulong F1480759520; - ulong F392825; - ulong F1742728723; - ulong F50421098; - ulong F1759532392; - ulong F1728784441; - ulong F1964438987; - ulong F1233146846; - ulong F123069603; - ulong F1965728433; - ulong F1949236424; - ulong F281999139; - ulong F1034152715; - ulong F945766333; - ulong F358490044; - ulong F942546525; - ulong F1741050569; - ulong F1419264207; - ulong F191209960; - ulong F415810094; - ulong F1935415299; - ulong F1638518361; - ulong F712739578; - ulong F1528580624; - ulong F1202186976; - ulong F1967993919; - ulong F1255273255; - ulong F933173046; - ulong F656957100; - ulong F647730452; - ulong F559107572; - ulong F1824108744; - ulong F386297579; - ulong F1510974438; - ulong F1586710595; - ulong F1176201800; - ulong F2140538669; - ulong F1338295884; - ulong F1329930811; - ulong F1078427447; - ulong F1692676139; - ulong F221042975; - ulong F136659030; - ulong F632854572; - ulong F158404029; - ulong F1947186545; - ulong F631821969; - ulong F1331538399; - ulong F1550395847; - ulong F2025661702; - ulong F860983501; - ulong F1586797483; - ulong F203050372; - ulong F65837207; - ulong F1073019303; - ulong F1989724806; - ulong F1435136894; - ulong F214148099; - ulong F995717769; - ulong F1939022120; - ulong F473511186; - ulong F1031265941; - ulong F576189746; - ulong F1622822798; - ulong F1406620861; - ulong F125127680; - ulong F2043185207; - ulong F1670661924; - ulong F1506539385; - ulong F1329771891; - ulong F949491503; - ulong F402754685; - ulong F89333396; - ulong F1260266160; - ulong F870617602; - ulong F1714372324; - ulong F1501859331; - ulong F79885006; - ulong F1370176595; - ulong F1402484078; - ulong F1336171950; - ulong F2071218503; - ulong F1530260846; - ulong F778779045; - ulong F1934230598; - ulong F1119793736; - ulong F1621058372; - ulong F320460372; - ulong F437955135; - ulong F1744469436; - ulong F1888548553; - ulong F1926390570; - ulong F342578115; - ulong F1538392338; - ulong F604916261; - ulong F661410198; - ulong F1792336876; - ulong F661319879; - ulong F1373717358; - ulong F33276349; - ulong F2051484985; - ulong F1108643692; - ulong F1972482661; - ulong F220623956; - ulong F1076170199; - ulong F458228816; - ulong F1497464087; - ulong F1090267859; - ulong F1342703252; - ulong F1506130626; - ulong F487865475; - ulong F1355251888; - ulong F991455151; - ulong F1740717338; - ulong F602850170; - ulong F549776330; - ulong F1648488742; - ulong F1944894348; - ulong F1836075847; - ulong F286827125; - ulong F651552955; - ulong F1722724835; - ulong F1232706789; - ulong F1909553596; - ulong F1588706985; - ulong F1170584580; - ulong F60176570; - ulong F698424705; - ulong F655349899; - ulong F209207404; - ulong F2069519095; - ulong F840539452; - ulong F853651295; - ulong F1336900246; - ulong F1498482740; - ulong F227528258; - ulong F98735842; - ulong F1309636890; - ulong F1850092493; - ulong F1476001782; - ulong F1769813296; - ulong F530790513; - ulong F1125240767; - ulong F1079308156; - ulong F1256603961; - ulong F533296665; - ulong F934935419; - ulong F749344424; - ulong F935542168; - ulong F55139931; - ulong F1160405103; - ulong F1994926175; - ulong F972727679; - ulong F1086890233; - ulong F1529207041; - ulong F328760150; - ulong F2023420550; - ulong F62929065; - ulong F779400618; - ulong F2053069266; - ulong F398052246; - ulong F799039382; - ulong F434917960; - ulong F1133495848; - ulong F1584095178; - ulong F1794809670; - ulong F501600593; - ulong F1802038552; - ulong F242234598; - ulong F375321912; - ulong F451040488; - ulong F338851852; - ulong F94801855; - ulong F360074065; - ulong F664497476; - ulong F120762442; - ulong F597484068; - ulong F153398633; - ulong F652949635; - ulong F1055410320; - ulong F235649161; - ulong F1458315793; - ulong F1910366184; - ulong F600209968; - ulong F1196285948; - ulong F74592920; - ulong F2015295420; - ulong F1914244709; - ulong F1955176852; - ulong F1169722590; - ulong F351591355; - ulong F35806777; - ulong F530236272; - ulong F1944506874; - ulong F1077949536; - ulong F970773914; - ulong F95872553; - ulong F2139228566; - ulong F1642696625; - ulong F1609277938; - ulong F31696072; - ulong F1280380514; - ulong F507109826; - ulong F560220256; - ulong F1751583090; - ulong F821553251; - ulong F1900124320; - ulong F612653614; - ulong F422392757; - ulong F1408444599; - ulong F1878759729; - ulong F1870021917; - ulong F1557463077; - ulong F1871473945; - ulong F1817420105; - ulong F1087220100; - ulong F1036156845; - ulong F1982191639; - ulong F2084693547; - ulong F1509502258; - ulong F1926997897; - ulong F734839531; - ulong F1994345347; - ulong F1219995655; - ulong F23730557; - ulong F415233711; - ulong F1956099227; - ulong F297778628; - ulong F1429608176; - ulong F1841207209; - ulong F24889889; - ulong F605739149; - ulong F658185655; - ulong F1191155344; - ulong F1023714248; - ulong F1102752294; - ulong F951205967; - ulong F1350145928; - ulong F996110525; - ulong F374732697; - ulong F321952247; - ulong F1402641806; - ulong F1491851952; - ulong F546732253; - ulong F1438446508; - ulong F629053085; - ulong F625827347; - ulong F806245974; - ulong F127086769; - ulong F2138213083; - ulong F2082100716; - ulong F261164561; - ulong F54535019; - ulong F133194367; - ulong F1829763688; - ulong F1444340188; - ulong F1433518814; - ulong F1434597818; - ulong F536489699; - ulong F1336349379; - ulong F1012937671; - ulong F1602345692; - ulong F1330529085; - ulong F728669195; - ulong F1383554710; - ulong F1273020580; - ulong F1211836262; - ulong F366307733; - ulong F847759697; - ulong F714667811; - ulong F136014133; - ulong F1833494564; - ulong F986081114; - ulong F1709960850; - ulong F1187550011; - ulong F524356091; - ulong F1390471226; - ulong F1447613094; - ulong F1929032794; - ulong F1542161119; - ulong F1936890011; - ulong F1149853253; - ulong F170691859; - ulong F1438878740; - ulong F1906590140; - ulong F1911208975; - ulong F551204130; - ulong F524991288; - ulong F1508875303; - ulong F1726857707; - ulong F1816717127; - ulong F1664091796; - ulong F813656229; - ulong F1807244793; - ulong F1509278673; - ulong F867090202; - ulong F72112721; - ulong F763182757; - ulong F1310661190; - ulong F165425928; - ulong F1564700470; - ulong F259519614; - ulong F2105969924; - ulong F1559902605; - ulong F2002198950; - ulong F248606152; - ulong F1422567094; - ulong F492057816; - ulong F1093128003; - ulong F1305407597; - ulong F53868962; - ulong F2133389367; - ulong F1653048671; - ulong F1141812227; - ulong F1546943015; - ulong F2010568065; - ulong F1431653833; - ulong F2039133992; - ulong F969562702; - ulong F1619829382; - ulong F721816450; - ulong F686844974; - ulong F1004916077; - ulong F1268385637; - ulong F1045434331; - ulong F619405984; - ulong F1019838335; - ulong F1326319968; - ulong F200682177; - ulong F320459809; - ulong F452243370; - ulong F627288469; - ulong F136951904; - ulong F1763606866; - ulong F2124944296; - ulong F1677370397; - ulong F1191366976; - ulong F758272901; - ulong F1584163437; - ulong F1657983988; - ulong F488641881; - ulong F59146314; - ulong F1579346932; - ulong F203467706; - ulong F1672988745; - ulong F1830811407; - ulong F11043125; - ulong F1819327649; - ulong F260301778; - ulong F1646194255; - ulong F1582920016; - ulong F180462376; - ulong F1941103702; - ulong F1838315455; - ulong F1591093125; - ulong F877855496; - ulong F1402087184; - ulong F837584287; - ulong F514468274; - ulong F1382792966; - ulong F1376251464; - ulong F96247126; - ulong F291375639; - ulong F772668194; - ulong F853164227; - ulong F1574064140; - ulong F1996437463; - ulong F2036925452; - ulong F1164351578; - ulong F2017056265; - ulong F819201089; - ulong F673380932; - ulong F454970555; - ulong F1459062361; - ulong F1131187501; - ulong F662670136; - ulong F1254981689; - ulong F801448371; - ulong F1742880539; - ulong F1362106571; - ulong F608362859; - ulong F1347994333; - ulong F1066018190; - ulong F701971569; - ulong F885023440; - ulong F271780994; - ulong F833668414; - ulong F446120096; - ulong F172513741; - ulong F1247088800; - ulong F275283213; - ulong F353782689; - ulong F243804627; - ulong F201370471; - ulong F281732524; - ulong F392394755; - ulong F1915254322; - ulong F806678738; - ulong F1497787126; - ulong F98924605; - ulong F1981857591; - ulong F121601320; - ulong F654976071; - ulong F390729160; - ulong F826993166; - ulong F909539084; - ulong F1872975468; - ulong F482041341; - ulong F707127954; - ulong F928422989; - ulong F1770357454; - ulong F600638813; - ulong F1242187395; - ulong F1299845350; - ulong F774430107; - ulong F28257131; - ulong F1177712583; - ulong F1736887717; - ulong F2035128401; - ulong F581383233; - ulong F740395726; - ulong F1550317367; - ulong F1864411711; - ulong F2064746425; - ulong F1741773052; - ulong F465418400; - ulong F429576305; - ulong F253600084; - ulong F1177329837; - ulong F738792746; - ulong F894899461; - ulong F448302951; - ulong F1451144892; - ulong F1643955934; - ulong F1527732627; - ulong F486761539; - ulong F693018262; - ulong F675289030; - ulong F2022462050; - ulong F2122968003; - ulong F546289173; - ulong F351627073; - ulong F1886475789; - ulong F1391574399; - ulong F1624214993; - ulong F1822128047; - ulong F1259078941; - ulong F1091442924; - ulong F1574424011; - ulong F253475393; - ulong F1362165819; - ulong F178366605; - ulong F919033983; - ulong F916403893; - ulong F1506012526; - ulong F431540224; - ulong F404673256; - ulong F737713293; - ulong F796439755; - ulong F361574766; - ulong F479962779; - ulong F1619375384; - ulong F1452195151; - ulong F2115818855; - ulong F33523528; - ulong F1322054503; - ulong F1296977568; - ulong F1745715108; - ulong F1919596370; - ulong F287668568; - ulong F1482722516; - ulong F502423553; - ulong F1861909314; - ulong F2059644045; - ulong F35094060; - ulong F388768653; - ulong F1811325225; - ulong F472837312; - ulong F440531432; - ulong F2067128652; - ulong F1353823106; - ulong F1485617028; - ulong F826659720; - ulong F923854444; - ulong F1524110574; - ulong F716532856; - ulong F1676752614; - ulong F534740999; - ulong F137943408; - ulong F1096192403; - ulong F82088283; - ulong F2102788616; - ulong F2026332922; - ulong F1660887284; - ulong F1643005224; - ulong F1074397436; - ulong F1046915569; - ulong F1918140581; - ulong F1358050871; - ulong F302160490; - ulong F525150479; - ulong F1660847480; - ulong F1319330201; - ulong F1286755443; - ulong F918236524; - ulong F859742266; - ulong F463940938; - ulong F1006873586; - ulong F881309833; - ulong F1117243873; - ulong F767698646; - ulong F2079319591; - ulong F297181861; - ulong F876794750; - ulong F1155235307; - ulong F1141829398; - ulong F792715664; - ulong F528340707; - ulong F591708281; - ulong F1464474319; - ulong F1792785536; - ulong F762236569; - ulong F1607771700; - ulong F823403967; - ulong F205580285; - ulong F1527417547; - ulong F623574278; - ulong F201022030; - ulong F416638821; - ulong F1108180271; - ulong F1489336731; - ulong F2040668291; - ulong F1262270088; - ulong F138370942; - ulong F1541978173; - ulong F1840459273; - ulong F166286827; - ulong F1687387924; - ulong F5617920; - ulong F664368308; - ulong F252591918; - ulong F669879028; - ulong F1800914813; - ulong F1168183182; - ulong F328493757; - ulong F150252339; - ulong F1805606755; - ulong F1149538172; - ulong F505651977; - ulong F501175826; - ulong F281681772; - ulong F518574862; - ulong F1326432300; - ulong F2041060199; - ulong F656858601; - ulong F1910397557; - ulong F53075780; - ulong F495926234; - ulong F1081175158; - ulong F1538302624; - ulong F236167988; - ulong F262918908; - ulong F590234765; - ulong F1920613209; - ulong F1775390789; - ulong F874514002; - ulong F817049503; - ulong F158810919; - ulong F1482300224; - ulong F1462259681; - ulong F975542571; - ulong F1252811387; - ulong F522722787; - ulong F2074823620; - ulong F1211882401; - ulong F1122906508; - ulong F1108805403; - ulong F439588518; - ulong F494910210; - ulong F55327946; - ulong F1869294439; - ulong F1621519753; - ulong F1842853700; - ulong F2062946642; - ulong F826498499; - ulong F970761869; - ulong F714235991; - ulong F1368693536; - ulong F1628995988; - ulong F1779064263; - ulong F1787383493; - ulong F1817844240; - ulong F606212766; - ulong F614798943; - ulong F428200320; - ulong F2137156657; - ulong F79644263; - ulong F2027785251; - ulong F1540276040; - ulong F1601463402; - ulong F1480686483; - ulong F1646795836; - ulong F1814721595; - ulong F1190875943; - ulong F1673116902; - ulong F1176354032; - ulong F2143335722; - ulong F1399092327; - ulong F829177798; - ulong F1681435740; - ulong F801592154; - ulong F1760970909; - ulong F1016024; - ulong F1025847212; - ulong F1816491832; - ulong F762131882; - ulong F567548855; - ulong F674771770; - ulong F1094114710; - ulong F804628920; - ulong F160278011; - ulong F1595839614; - ulong F677298578; - ulong F1850719608; - ulong F1822359835; - ulong F1305181978; - ulong F646598621; - ulong F2055407491; - ulong F1646623300; - ulong F1222209391; - ulong F1043262245; - ulong F1228503799; - ulong F1046796125; - ulong F1040930455; - ulong F722125110; - ulong F222498603; - ulong F1954281805; - ulong F651977757; - ulong F389829740; - ulong F1797628114; - ulong F974909794; - ulong F1462627311; - ulong F539515738; - ulong F2095043895; - ulong F977472109; - ulong F26412584; - ulong F1816828216; - ulong F1727849201; - ulong F945790758; - ulong F1813552085; - ulong F1569607802; - ulong F1552356140; - ulong F933670541; - ulong F735647120; - ulong F1441185391; - ulong F2032330516; - ulong F969497258; - ulong F2111485634; - ulong F1515999755; - ulong F367934924; - ulong F529755411; - ulong F87928231; - ulong F1899952674; - ulong F1754452054; - ulong F638173495; - ulong F1720572002; - ulong F714174784; - ulong F1107569216; - ulong F303722102; - ulong F1593993229; - ulong F955333724; - ulong F2063054745; - ulong F284942030; - ulong F1443970243; - ulong F1977202773; - ulong F845134347; - ulong F1056323876; - ulong F729738330; - ulong F873247499; - ulong F1795947251; - ulong F1635837409; - ulong F1066233067; - ulong F1109616733; - ulong F1980554862; - ulong F1800085236; - ulong F1638389752; - ulong F294833258; - ulong F311149005; - ulong F1747228711; - ulong F837278241; - ulong F1400484992; - ulong F1990279818; - ulong F1283461649; - ulong F21894816; - ulong F1267872703; - ulong F886981563; - ulong F1710158284; - ulong F932547331; - ulong F1456870400; - ulong F1404383754; - ulong F1459721447; - ulong F709259000; - ulong F1424127099; - ulong F1499281176; - ulong F858218361; - ulong F1654036704; - ulong F1267414110; - ulong F1637183945; - ulong F905927994; - ulong F596051044; - ulong F976006640; - ulong F239758928; - ulong F1238238135; - ulong F1867536151; - ulong F879581162; - ulong F1611005991; - ulong F1125795145; - ulong F2066881459; - ulong F2101850465; - ulong F1147267390; - ulong F1425738744; - ulong F403025779; - ulong F1507824152; - ulong F1613927508; - ulong F193508237; - ulong F1112537553; - ulong F779593096; - ulong F263047214; - ulong F176097540; - ulong F1090221210; - ulong F1282459710; - ulong F123776545; - ulong F1420351577; - ulong F1616347391; - ulong F336225804; - ulong F926578409; - ulong F1789589615; - ulong F1757819204; - ulong F1122336501; - ulong F146048532; - ulong F370975642; - ulong F805132960; - ulong F1552704658; - ulong F1151177667; - ulong F2008755248; - ulong F1160726064; - ulong F752041683; - ulong F1563409145; - ulong F1289797301; - ulong F1804350359; - ulong F1908670065; - ulong F1790760472; - ulong F978180513; - ulong F309603010; - ulong F2126128657; - ulong F1056695668; - ulong F1348918495; - ulong F1957683238; - ulong F1305772939; - ulong F1893164455; - ulong F874443608; - ulong F1004366896; - ulong F1461086405; - ulong F1963190431; - ulong F1461074981; - ulong F852230095; - ulong F966890998; - ulong F1769374390; - ulong F1531310347; - ulong F2100486399; - ulong F1968900023; - ulong F1515459588; - ulong F944544958; - ulong F1955801933; - ulong F776291748; - ulong F620605784; - ulong F997804768; - ulong F356646485; - ulong F1752655907; - ulong F1180265820; - ulong F360495870; - ulong F1363667598; - ulong F1120733560; - ulong F519230828; - ulong F1329034792; - ulong F1639182885; - ulong F1293079679; - ulong F1110748567; - ulong F1637702382; - ulong F1427013783; - ulong F1725143561; - ulong F1979390024; - ulong F452046265; - ulong F1376655693; - ulong F1419088571; - ulong F1514092393; - ulong F1491530202; - ulong F1736997874; - ulong F1837586333; - ulong F1156525153; - ulong F193835066; - ulong F1130150940; - ulong F32098798; - ulong F1336794548; - ulong F1982933983; - ulong F393210477; - ulong F846215514; - ulong F1169862227; - ulong F1680794909; - ulong F1505522873; - ulong F58890900; - ulong F992272010; - ulong F205027331; - ulong F125507119; - ulong F1532668585; - ulong F1658259657; - ulong F2031116983; - ulong F941855577; - ulong F634155639; - ulong F1969375743; - ulong F1706634063; - ulong F2003626078; - ulong F131672008; - ulong F104296564; - ulong F375342839; - ulong F2136993646; - ulong F1063413323; - ulong F1715372912; - ulong F536713362; - ulong F1409683002; - ulong F1276559229; - ulong F1408290541; - ulong F666543799; - ulong F596130754; - ulong F986430754; - ulong F1377828577; - ulong F1331568800; - ulong F1931422659; - ulong F683780492; - ulong F935824315; - ulong F792967371; - ulong F123217452; - ulong F1577437305; - ulong F132179509; - ulong F1368122883; - ulong F732871551; - ulong F1774362693; - ulong F326539146; - ulong F1991470755; - ulong F1908312561; - ulong F1630459057; - ulong F549674625; - ulong F1102842235; - ulong F2015694237; - ulong F1597374737; - ulong F337692635; - ulong F998478932; - ulong F2075285881; - ulong F961451709; - ulong F1993423984; - ulong F1477280801; - ulong F1278326249; - ulong F633148865; - ulong F271111907; - ulong F228963644; - ulong F798084006; - ulong F325728211; - ulong F1756380224; - ulong F1286560012; - ulong F154840008; - ulong F326690857; - ulong F99694324; - ulong F258075085; - ulong F1845814971; - ulong F1176408372; - ulong F1583416611; - ulong F426188773; - ulong F2146976146; - ulong F883657328; - ulong F1789954935; - ulong F362630953; - ulong F736874177; - ulong F1871385804; - ulong F775884448; - ulong F1926707592; - ulong F726884604; - ulong F305448306; - ulong F798333209; - ulong F1146239664; - ulong F648738119; - ulong F379349645; - ulong F1403766566; - ulong F969970950; - ulong F837840155; - ulong F1606027161; - ulong F1662124769; - ulong F1637552234; - ulong F1306325398; - ulong F2050699512; - ulong F570038877; - ulong F407143340; - ulong F17982469; - ulong F1187462781; - ulong F1836630747; - ulong F1581621704; - ulong F1530764733; - ulong F291599540; - ulong F1404510911; - ulong F839285865; - ulong F13958126; - ulong F2058987509; - ulong F998986433; - ulong F1191628553; - ulong F1318980421; - ulong F1630793031; - ulong F740406624; - ulong F1554424092; - ulong F2004748064; - ulong F491887962; - ulong F1649562687; - ulong F492635700; - ulong F1674878649; - ulong F610140560; - ulong F637821893; - ulong F1922974010; - ulong F1070407938; - ulong F1277207021; - ulong F1567718577; - ulong F239787810; - ulong F1661767250; - ulong F2093348024; - ulong F1267347022; - ulong F96276634; - ulong F313618451; - ulong F1382811595; - ulong F344648484; - ulong F1696895043; - ulong F34755057; - ulong F1341746391; - ulong F395942859; - ulong F435285064; - ulong F1048421042; - ulong F2106530991; - ulong F1132281538; - ulong F737234257; - ulong F1527846859; - ulong F212138013; - ulong F1798474176; - ulong F1354530771; - ulong F865620537; - ulong F107700677; - ulong F1780287817; - ulong F814437436; - ulong F401136825; - ulong F77403177; - ulong F879748338; - ulong F1555325556; - ulong F549640888; - ulong F2061140384; - ulong F511213766; - ulong F253557712; - ulong F871364610; - ulong F1164723101; - ulong F1325002262; - ulong F68093749; - ulong F791640487; - ulong F902709799; - ulong F878010102; - ulong F2083652473; - ulong F1286144547; - ulong F1190995228; - ulong F1519669035; - ulong F663001673; - ulong F95945103; - ulong F1214277623; - ulong F1591698305; - ulong F1715831305; - ulong F1625342669; - ulong F2048071283; - ulong F395127151; - ulong F858269925; - ulong F1626216492; - ulong F213187806; - ulong F1521650920; - ulong F1554066573; - ulong F313060207; - ulong F452909586; - ulong F1842623456; - ulong F236215274; - ulong F503063257; - ulong F936806575; - ulong F1147254155; - ulong F121098319; - ulong F830532625; - ulong F142385147; - ulong F1711404101; - ulong F2031181588; - ulong F781528729; - ulong F1064187789; - ulong F2093077417; - ulong F625137060; - ulong F1481611558; - ulong F1862305350; - ulong F68386224; - ulong F1822108956; - ulong F735515289; - ulong F1117286144; - ulong F718492333; - ulong F1334342849; - ulong F633188519; - ulong F1311400680; - ulong F2077466534; - ulong F649053252; - ulong F1666013233; - ulong F1800427488; - ulong F774824867; - ulong F658176804; - ulong F1790555828; - ulong F1918419336; - ulong F1902517189; - ulong F338730901; - ulong F1207569990; - ulong F641794828; - ulong F1580589216; - ulong F349337972; - ulong F43741073; - ulong F1398570715; - ulong F1979952695; - ulong F2101043603; - ulong F1650357169; - ulong F1708000364; - ulong F934302576; - ulong F561154880; - ulong F2102477513; - ulong F1917473738; - ulong F1524142014; - ulong F1911394789; - ulong F144801582; - ulong F1847025611; - ulong F818551284; - ulong F1343257710; - ulong F1881900900; - ulong F508280607; - ulong F1750510402; - ulong F1339146224; - ulong F1006823688; - ulong F498200903; - ulong F602568734; - ulong F1177588784; - ulong F1515043927; - ulong F1053227297; - ulong F240625760; - ulong F1010593040; - ulong F1309154247; - ulong F1754346516; - ulong F1565050717; - ulong F839816730; - ulong F281716134; - ulong F1866531899; - ulong F1778367883; - ulong F1484428221; - ulong F1284817096; - ulong F764932377; - ulong F1831469327; - ulong F1072671802; - ulong F377098104; - ulong F1516311654; - ulong F694059386; - ulong F1896023142; - ulong F276285474; - ulong F1010913725; - ulong F513375222; - ulong F2091013864; - ulong F1099868052; - ulong F559259479; - ulong F604313648; - ulong F699289383; - ulong F1038768073; - ulong F241442992; - ulong F1489997931; - ulong F1693023817; - ulong F796001981; - ulong F802363911; - ulong F585999676; - ulong F597129872; - ulong F1467374604; - ulong F2071193183; - ulong F1399484280; - ulong F348130997; - ulong F352423021; - ulong F684325284; - ulong F1629678655; - ulong F425753330; - ulong F68657728; - ulong F1481606710; - ulong F58440614; - ulong F1116968523; - ulong F824294927; - ulong F677838600; - ulong F962048120; - ulong F1637995681; - ulong F1951625164; - ulong F854029239; - ulong F901303310; - ulong F504130202; - ulong F539852075; - ulong F297095543; - ulong F2058208635; - ulong F749894768; - ulong F1150032868; - ulong F865761334; - ulong F1948532304; - ulong F40273142; - ulong F376533968; - ulong F85344066; - ulong F688426240; - ulong F482453185; - ulong F178932701; - ulong F1234339455; - ulong F1752780845; - ulong F453388568; - ulong F116827374; - ulong F345928389; - ulong F1543600121; - ulong F1739443837; - ulong F1528718717; - ulong F87621892; - ulong F2022356136; - ulong F1765744989; - ulong F500818865; - ulong F1634828772; - ulong F2022478103; - ulong F360929473; - ulong F1426878519; - ulong F1999485897; - ulong F1888882300; - ulong F2089456389; - ulong F2048544248; - ulong F81869474; - ulong F57277797; - ulong F1170279061; - ulong F12984548; - ulong F649589512; - ulong F1345581776; - ulong F1634145334; - ulong F883276627; - ulong F1589405513; - ulong F49219362; - ulong F2130797309; - ulong F793180470; - ulong F1723471076; - ulong F938035822; - ulong F1737439119; - ulong F1072541402; - ulong F508659552; - ulong F1521168307; - ulong F1605696775; - ulong F1457912765; - ulong F1309343120; - ulong F1122895132; - ulong F452230183; - ulong F422223054; - ulong F292463646; - ulong F249075903; - ulong F1662687743; - ulong F990766878; - ulong F1587602831; - ulong F760878270; - ulong F524531718; - ulong F343945413; - ulong F746453498; - ulong F581392584; - ulong F97063227; - ulong F1177061658; - ulong F582501784; - ulong F440404020; - ulong F1614721509; - ulong F1147830260; - ulong F2056938434; - ulong F856167210; - ulong F2086796851; - ulong F38402530; - ulong F2039042474; - ulong F972564519; - ulong F924831436; - ulong F696792950; - ulong F285038984; - ulong F1435871718; - ulong F918218967; - ulong F478317590; - ulong F283185525; - ulong F631543624; - ulong F739201128; - ulong F1106457989; - ulong F1752531261; - ulong F748056007; - ulong F1868004549; - ulong F400513609; - ulong F1830377680; - ulong F643378456; - ulong F1443157443; - ulong F828527243; - ulong F1672171291; - ulong F1786851896; - ulong F46726972; - ulong F1142078492; - ulong F840972595; - ulong F560377461; - ulong F490039618; - ulong F68255532; - ulong F2053930445; - ulong F457866515; - ulong F1548457978; - ulong F453175910; - ulong F1183581928; - ulong F413827653; - ulong F530664227; - ulong F1467382774; - ulong F1471728114; - ulong F965894793; - ulong F705727894; - ulong F151731113; - ulong F1990142950; - ulong F46214128; - ulong F60759888; - ulong F114909874; - ulong F1989675103; - ulong F1138088885; - ulong F1572014044; - ulong F1981929424; - ulong F719883118; - ulong F1214207900; - ulong F1464936227; - ulong F1413559978; - ulong F1560493414; - ulong F1258269608; - ulong F513714886; - ulong F252190578; - ulong F925837547; - ulong F1930236591; - ulong F2003304002; - ulong F1872145170; - ulong F945832100; - ulong F849963435; - ulong F571870792; - ulong F1972802657; - ulong F1230569293; - ulong F286025218; - ulong F2070359708; - ulong F1338703608; - ulong F217391780; - ulong F400621775; - ulong F1076269142; - ulong F864482887; - ulong F2085134209; - ulong F1291426330; - ulong F985867940; - ulong F1625957163; - ulong F1726092008; - ulong F2079300745; - ulong F1299887036; - ulong F1850367357; - ulong F1135847064; - ulong F655593841; - ulong F1495856061; - ulong F839722545; - ulong F1140413935; - ulong F134898000; - ulong F1040166143; - ulong F2072795967; - ulong F2047596414; - ulong F278473649; - ulong F541545227; - ulong F1688975170; - ulong F1110074438; - ulong F981066371; - ulong F1353382660; - ulong F1140179515; - ulong F1621826983; - ulong F235440878; - ulong F1031824228; - ulong F1703649885; - ulong F1215212824; - ulong F233310436; - ulong F1764537644; - ulong F319261343; - ulong F137938758; - ulong F600453340; - ulong F1475909416; - ulong F269067084; - ulong F272401668; - ulong F1035241370; - ulong F673582217; - ulong F994020449; - ulong F630349555; - ulong F152936645; - ulong F736298106; - ulong F290238259; - ulong F1501591021; - ulong F1879631894; - ulong F832388722; - ulong F1095671293; - ulong F1393342722; - ulong F2145047388; - ulong F1438590841; - ulong F2086401778; - ulong F2006560195; - ulong F1534777619; - ulong F1901892096; - ulong F1104067838; - ulong F2085527317; - ulong F1993172072; - ulong F4130180; - ulong F1490651130; - ulong F1047476517; - ulong F1743720798; - ulong F635154533; - ulong F902536628; - ulong F1038539844; - ulong F1176594718; - ulong F701783787; - ulong F539960595; - ulong F806472231; - ulong F771099059; - ulong F1800394299; - ulong F1012355044; - ulong F1752375079; - ulong F1695008425; - ulong F1058625615; - ulong F957137793; - ulong F244768265; - ulong F1063144401; - ulong F1786072141; - ulong F1889678736; - ulong F1550535803; - ulong F2083636582; - ulong F310307163; - ulong F1217649083; - ulong F942203242; - ulong F1825619513; - ulong F460184795; - ulong F750644786; - ulong F846044891; - ulong F371841578; - ulong F331023414; - ulong F426713243; - ulong F1031111190; - ulong F1330414734; - ulong F2094027579; - ulong F1034112404; - ulong F1665265759; - ulong F1981245125; - ulong F1399182062; - ulong F324996303; - ulong F1177848107; - ulong F292428127; - ulong F289199062; - ulong F622243663; - ulong F344653089; - ulong F426235797; - ulong F334026699; - ulong F311551770; - ulong F476152004; - ulong F944754303; - ulong F859299573; - ulong F1022382916; - ulong F207099931; - ulong F261935091; - ulong F2087598974; - ulong F1111323582; - ulong F1433413635; - ulong F1564989097; - ulong F2107817033; - ulong F1360403978; - ulong F716409923; - ulong F2098622648; - ulong F1841399351; - ulong F434630653; - ulong F440075645; - ulong F1373681056; - ulong F2128727501; - ulong F421960345; - ulong F1748464493; - ulong F24513211; - ulong F1439355681; - ulong F411006787; - ulong F1811445986; - ulong F1461075838; - ulong F711830629; - ulong F1258107676; - ulong F1794437520; - ulong F1835547147; - ulong F872995994; - ulong F515967445; - ulong F1491592814; - ulong F148633025; - ulong F274492782; - ulong F2048774235; - ulong F1660025652; - ulong F1456124145; - ulong F219613312; - ulong F769176099; - ulong F1390299407; - ulong F982703997; - ulong F1748182416; - ulong F100276662; - ulong F2020911739; - ulong F38778084; - ulong F1756070027; - ulong F1226709106; - ulong F598512423; - ulong F2002052056; - ulong F182168018; - ulong F1118455680; - ulong F444991943; - ulong F2059550001; - ulong F710570924; - ulong F451638793; - ulong F1652882269; - ulong F448292786; - ulong F1358420577; - ulong F893507740; - ulong F1697588109; - ulong F829491298; - ulong F1464369709; - ulong F1745350135; - ulong F691993103; - ulong F1591849588; - ulong F2016294811; - ulong F567776898; - ulong F1824129866; - ulong F1940108763; - ulong F922088648; - ulong F1131435147; - ulong F1154067744; - ulong F1359551402; - ulong F1179144585; - ulong F765760496; - ulong F423814442; - ulong F1339079019; - ulong F537578695; - ulong F1772667902; - ulong F1852489458; - ulong F1632605170; - ulong F659595253; - ulong F1939869111; - ulong F1653379129; - ulong F1902023961; - ulong F70975502; - ulong F1579526460; - ulong F1585545748; - ulong F1970337636; - ulong F395891966; - ulong F1211732866; - ulong F97703567; - ulong F1473589219; - ulong F1219071637; - ulong F560808109; - ulong F1665817935; - ulong F2832281; - ulong F1555767206; - ulong F429062151; - ulong F169966920; - ulong F1188293129; - ulong F1550062887; - ulong F805887307; - ulong F1079963408; - ulong F1198216518; - ulong F2111871583; - ulong F1232924188; - ulong F880405416; - ulong F2092294075; - ulong F27824351; - ulong F313803250; - ulong F2058197738; - ulong F1733236322; - ulong F1188501929; - ulong F64982939; - ulong F169896045; - ulong F1671984245; - ulong F91971006; - ulong F937452789; - ulong F1520874086; - ulong F436768351; - ulong F1129714797; - ulong F2001275877; - ulong F1544216797; - ulong F1857839429; - ulong F1033731579; - ulong F1827962172; - ulong F140479765; - ulong F618336476; - ulong F1247426208; - ulong F420982161; - ulong F1930795460; - ulong F108516544; - ulong F1602700982; - ulong F664196329; - ulong F82542283; - ulong F2001191593; - ulong F859905703; - ulong F455162611; - ulong F1937636025; - ulong F985534961; - ulong F699121044; - ulong F1640735320; - ulong F1942513285; - ulong F82088716; - ulong F1301018775; - ulong F511950893; - ulong F285087290; - ulong F1154088698; - ulong F390912063; - ulong F2141317337; - ulong F2058344922; - ulong F618314417; - ulong F1055671712; - ulong F1880682216; - ulong F58578332; - ulong F1696270657; - ulong F1409154157; - ulong F1369607626; - ulong F164484939; - ulong F283909411; - ulong F1092444423; - ulong F262068940; - ulong F844867867; - ulong F1754325837; - ulong F530491437; - ulong F1949681194; - ulong F130535340; - ulong F524305600; - ulong F2129924303; - ulong F316188099; - ulong F812078542; - ulong F1784292042; - ulong F1147300411; - ulong F535339125; - ulong F1885130954; - ulong F1636463124; - ulong F58762592; - ulong F1462128081; - ulong F556820654; - ulong F521780686; - ulong F1542874882; - ulong F1133874714; - ulong F227424412; - ulong F1253592518; - ulong F510120886; - ulong F1312481043; - ulong F1200328479; - ulong F1869502413; - ulong F605617997; - ulong F533755273; - ulong F592037436; - ulong F1637781724; - ulong F290677672; - ulong F1653726614; - ulong F2040574185; - ulong F437052104; - ulong F795867453; - ulong F188187448; - ulong F1699080926; - ulong F1498821228; - ulong F381415553; - ulong F1908265337; - ulong F1171648042; - ulong F74723965; - ulong F1329238795; - ulong F274052880; - ulong F1618497653; - ulong F520332587; - ulong F2143034909; - ulong F569598855; - ulong F1637508065; - ulong F2094509723; - ulong F812786972; - ulong F1790187900; - ulong F888518176; - ulong F2106053356; - ulong F34644528; - ulong F1738758996; - ulong F1244204951; - ulong F1365494041; - ulong F749352715; - ulong F408516574; - ulong F2066171250; - ulong F1596169030; - ulong F1871634310; - ulong F1321780465; - ulong F1493614370; - ulong F1641057444; - ulong F642248587; - ulong F1448078850; - ulong F840595671; - ulong F2018058791; - ulong F1910530802; - ulong F1205483073; - ulong F140365133; - ulong F1782093192; - ulong F2109710319; - ulong F152700447; - ulong F2071837370; - ulong F236068006; - ulong F1841467037; - ulong F679995892; - ulong F1873951151; - ulong F36019142; - ulong F1043730855; - ulong F645011360; - ulong F824994752; - ulong F647973419; - ulong F765208438; - ulong F2082004476; - ulong F402407576; - ulong F1204592104; - ulong F1091466144; - ulong F333586885; - ulong F749468513; - ulong F2120382626; - ulong F1989577734; - ulong F1722962659; - ulong F350573302; - ulong F7458330; - ulong F927922157; - ulong F2124923856; - ulong F2025567647; - ulong F694956059; - ulong F1876486831; - ulong F1766932921; - ulong F183978921; - ulong F1754787546; - ulong F1649822767; - ulong F1253908631; - ulong F2143826684; - ulong F2029427728; - ulong F1814405273; - ulong F1008136945; - ulong F1671510651; - ulong F69356823; - ulong F682049070; - ulong F2030152108; - ulong F552438175; - ulong F1226622950; - ulong F496785713; - ulong F845640951; - ulong F875849006; - ulong F707727758; - ulong F1045671274; - ulong F1783487214; - ulong F926592647; - ulong F1576943917; - ulong F456014560; - ulong F167466154; - ulong F1939999105; - ulong F386747660; - ulong F1949610792; - ulong F2064379040; - ulong F1455629496; - ulong F1864026828; - ulong F801911892; - ulong F1178995092; - ulong F307015958; - ulong F1424281581; - ulong F461032439; - ulong F1217690853; - ulong F1145634299; - ulong F1658783454; - ulong F2085661439; - ulong F520463495; - ulong F1537670478; - ulong F83329199; - ulong F809559881; - ulong F680111690; - ulong F1438333556; - ulong F2106909273; - ulong F1170524484; - ulong F1271433999; - ulong F1658156264; - ulong F82281206; - ulong F1249074850; - ulong F1317839889; - ulong F1796768432; - ulong F92999617; - ulong F840340274; - ulong F754518651; - ulong F1298772986; - ulong F1482356613; - ulong F1461393173; - ulong F1757079024; - ulong F79816936; - ulong F1897509880; - ulong F1699991096; - ulong F1954967470; - ulong F1414928578; - ulong F1650537625; - ulong F1723136150; - ulong F1275640241; - ulong F765590511; - ulong F1426578507; - ulong F1847490299; - ulong F1364549199; - ulong F769549966; - ulong F525207779; - ulong F245816735; - ulong F843263448; - ulong F767384036; - ulong F1923386517; - ulong F876616245; - ulong F1980573479; - ulong F1363706823; - ulong F678176793; - ulong F406222776; - ulong F1373348290; - ulong F614951978; - ulong F1631555650; - ulong F1529710307; - ulong F214016341; - ulong F583941307; - ulong F1853997435; - ulong F2066401514; - ulong F1810761333; - ulong F197390281; - ulong F328582415; - ulong F440646559; - ulong F1787644245; - ulong F530821750; - ulong F1002076058; - ulong F1412666759; - ulong F1935279578; - ulong F383773123; - ulong F2042367890; - ulong F505843488; - ulong F231577757; - ulong F382274554; - ulong F2032009298; - ulong F548289923; - ulong F1271560653; - ulong F1994666528; - ulong F2144560473; - ulong F2134618262; - ulong F1522870116; - ulong F605740368; - ulong F1628303341; - ulong F393372201; - ulong F1549123790; - ulong F1491287104; - ulong F326642806; - ulong F1340015492; - ulong F1930856575; - ulong F120827318; - ulong F1509119809; - ulong F691698934; - ulong F1059076723; - ulong F1507660640; - ulong F36728966; - ulong F1167158918; - ulong F440967551; - ulong F84561220; - ulong F605656138; - ulong F312441698; - ulong F1912791625; - ulong F510719758; - ulong F1088820314; - ulong F1596800356; - ulong F1468822580; - ulong F172333305; - ulong F174645019; - ulong F991073736; - ulong F730426327; - ulong F1083265727; - ulong F258149654; - ulong F366833460; - ulong F586864481; - ulong F1866862820; - ulong F543531398; - ulong F1205020965; - ulong F716570587; - ulong F2082693861; - ulong F1039006416; - ulong F296357141; - ulong F204178944; - ulong F1809544213; - ulong F1629293831; - ulong F1814452260; - ulong F1022136961; - ulong F1350668956; - ulong F1594250412; - ulong F871400764; - ulong F345545588; - ulong F864850380; - ulong F107322372; - ulong F1186999433; - ulong F1389010390; - ulong F1832118775; - ulong F221826637; - ulong F1012150358; - ulong F1664403701; - ulong F31502985; - ulong F1072033268; - ulong F1376790485; - ulong F1316642085; - ulong F1483052717; - ulong F609589165; - ulong F847590848; - ulong F2010161311; - ulong F1142286349; - ulong F104834453; - ulong F1339697550; - ulong F964129242; - ulong F979191648; - ulong F450588331; - ulong F505757337; - ulong F1193038451; - ulong F309298997; - ulong F108262754; - ulong F103247412; - ulong F1028909574; - ulong F1421851701; - ulong F574663395; - ulong F118153624; - ulong F725566540; - ulong F1450727902; - ulong F645528148; - ulong F2013059594; - ulong F975943355; - ulong F1218633868; - ulong F1125306717; - ulong F902229353; - ulong F1645036183; - ulong F1678864687; - ulong F1688100911; - ulong F685067602; - ulong F1010660593; - ulong F1809699578; - ulong F1127198703; - ulong F868609874; - ulong F1199955048; - ulong F781702983; - ulong F1951774596; - ulong F2027334259; - ulong F1245834503; - ulong F254552862; - ulong F2054755169; - ulong F1513837587; - ulong F414262049; - ulong F1749048682; - ulong F2141444629; - ulong F1079711393; - ulong F1723856021; - ulong F118579225; - ulong F2130724431; - ulong F242552000; - ulong F1604323237; - ulong F953879644; - ulong F651223945; - ulong F2013397830; - ulong F837524569; - ulong F744013218; - ulong F2019131140; - ulong F791527443; - ulong F16979632; - ulong F1350088747; - ulong F1842145014; - ulong F1432748202; - ulong F1438574384; - ulong F1913004376; - ulong F1642580391; - ulong F1530822520; - ulong F1329583941; - ulong F1387136527; - ulong F1050776011; - ulong F247206591; - ulong F1617560752; - ulong F694812783; - ulong F1019802768; - ulong F471013678; - ulong F1543456380; - ulong F1279174208; - ulong F1598797545; - ulong F1374378320; - ulong F1224672886; - ulong F45595324; - ulong F1325856979; - ulong F1526456958; - ulong F1695623903; - ulong F1445548911; - ulong F1228228012; - ulong F56780949; - ulong F1158475633; - ulong F1261284520; - ulong F31085305; - ulong F455941830; - ulong F910055490; - ulong F1160247153; - ulong F2010354627; - ulong F2043229403; - ulong F559891495; - ulong F622006967; - ulong F75263203; - ulong F648741320; - ulong F106468291; - ulong F610622109; - ulong F1897611099; - ulong F336719494; - ulong F1215286861; - ulong F1883517840; - ulong F772474895; - ulong F909510454; - ulong F2081560523; - ulong F180210267; - ulong F469941450; - ulong F1705834008; - ulong F1292699320; - ulong F644752820; - ulong F1714338204; - ulong F2118867955; - ulong F24231768; - ulong F315688056; - ulong F1884607946; - ulong F2140509120; - ulong F684776364; - ulong F1585799442; - ulong F372346887; - ulong F68299421; - ulong F1356051222; - ulong F594834181; - ulong F1484634748; - ulong F457313599; - ulong F831941803; - ulong F1124057012; - ulong F2058605830; - ulong F921449413; - ulong F1203911005; - ulong F950056225; - ulong F1267910029; - ulong F614050777; - ulong F295467872; - ulong F989137485; - ulong F311170097; - ulong F1959589710; - ulong F673074016; - ulong F318717558; - ulong F122704073; - ulong F978265366; - ulong F791343070; - ulong F472734944; - ulong F1310726157; - ulong F265302670; - ulong F1593392596; - ulong F2038970319; - ulong F2018997635; - ulong F244203439; - ulong F884882668; - ulong F82237730; - ulong F2111448603; - ulong F668152496; - ulong F238275222; - ulong F1829311678; - ulong F1128151919; - ulong F620452680; - ulong F398883092; - ulong F315161296; - ulong F77568651; - ulong F957503511; - ulong F269088084; - ulong F1695975684; - ulong F501923003; - ulong F342643095; - ulong F1524326438; - ulong F1100287427; - ulong F1823400083; - ulong F1182577930; - ulong F4517959; - ulong F2072501883; - ulong F1467435104; - ulong F366058806; - ulong F1463095369; - ulong F1541565168; - ulong F1424439998; - ulong F883316278; - ulong F1431591671; - ulong F1219332078; - ulong F1011404650; - ulong F940455131; - ulong F1252543024; - ulong F1814402391; - ulong F36566745; - ulong F1121673275; - ulong F986091269; - ulong F599757533; - ulong F375775555; - ulong F613639841; - ulong F2008469213; - ulong F1838201064; - ulong F1560706618; - ulong F357912720; - ulong F241148907; - ulong F1312684209; - ulong F709177282; - ulong F1242851033; - ulong F2118295588; - ulong F968083062; - ulong F888459879; - ulong F493105169; - ulong F215570236; - ulong F836419705; - ulong F239685480; - ulong F959864432; - ulong F762286273; - ulong F1745389797; - ulong F1352540774; - ulong F844193701; - ulong F404871680; - ulong F244835641; - ulong F1336344656; - ulong F1327034661; - ulong F1451240293; - ulong F1284597167; - ulong F1852444134; - ulong F602169340; - ulong F1659408939; - ulong F1527733375; - ulong F1504035473; - ulong F924568905; - ulong F724511871; - ulong F1209760242; - ulong F1321592364; - ulong F313800542; - ulong F511795265; - ulong F1109522384; - ulong F124909899; - ulong F150411160; - ulong F832387886; - ulong F181588965; - ulong F912504337; - ulong F463508609; - ulong F330872199; - ulong F518299481; - ulong F724884895; - ulong F416123319; - ulong F1574716911; - ulong F1224185960; - ulong F359387002; - ulong F1388185119; - ulong F1394700406; - ulong F1679065501; - ulong F208768161; - ulong F1763633572; - ulong F501856408; - ulong F233671957; - ulong F1054156074; - ulong F1104035387; - ulong F1607723722; - ulong F107007942; - ulong F1730925741; - ulong F590562213; - ulong F1611531236; - ulong F2111374621; - ulong F1916076944; - ulong F1153293641; - ulong F1662310988; - ulong F2073368585; - ulong F448069167; - ulong F1800247536; - ulong F1620479898; - ulong F1202129614; - ulong F11805815; - ulong F223282715; - ulong F1479814951; - ulong F872836047; - ulong F996162462; - ulong F932940812; - ulong F559712272; - ulong F1436320815; - ulong F1174936076; - ulong F435222979; - ulong F1168346373; - ulong F115850354; - ulong F1677352146; - ulong F1192930018; - ulong F1000992081; - ulong F1705442439; - ulong F1959427781; - ulong F278123308; - ulong F55366310; - ulong F1168358159; - ulong F690171085; - ulong F725379944; - ulong F598146871; - ulong F321942124; - ulong F999461020; - ulong F366981225; - ulong F749706184; - ulong F1719074901; - ulong F901295978; - ulong F1648831973; - ulong F776116793; - ulong F706623113; - ulong F1915188868; - ulong F192570792; - ulong F1667259686; - ulong F2132969093; - ulong F283818621; - ulong F1776504008; - ulong F1384993142; - ulong F121215262; - ulong F544323115; - ulong F171402907; - ulong F1079555513; - ulong F1295702762; - ulong F1569699487; - ulong F1495680882; - ulong F434022475; - ulong F723146926; - ulong F152301560; - ulong F2104352196; - ulong F113940804; - ulong F169945859; - ulong F1744881226; - ulong F452121739; - ulong F511958529; - ulong F1433909518; - ulong F1772619491; - ulong F1157872827; - ulong F2020858674; - ulong F629181237; - ulong F183234628; - ulong F988121018; - ulong F535024837; - ulong F1673587750; - ulong F1806589833; - ulong F461723260; - ulong F348145133; - ulong F1484781354; - ulong F1673153979; - ulong F1015506635; - ulong F1421623818; - ulong F182923773; - ulong F1040613813; - ulong F2081634695; - ulong F624035044; - ulong F518768178; - ulong F1793308078; - ulong F1449927756; - ulong F899726284; - ulong F1651263785; - ulong F2080442397; - ulong F26559258; - ulong F1566773341; - ulong F944427429; - ulong F1534891169; - ulong F606170934; - ulong F1109225534; - ulong F1463067129; - ulong F1828095910; - ulong F233350168; - ulong F360349602; - ulong F1273429441; - ulong F1903128981; - ulong F755811905; - ulong F2085464281; - ulong F1703685744; - ulong F1783861717; - ulong F1553451410; - ulong F1636596576; - ulong F1107976227; - ulong F1147535749; - ulong F1096724768; - ulong F1197476594; - ulong F1284278572; - ulong F682728378; - ulong F2078500678; - ulong F1276815693; - ulong F1810730178; - ulong F1975570342; - ulong F2140673998; - ulong F1788085087; - ulong F322691735; - ulong F258146543; - ulong F369594889; - ulong F696222487; - ulong F156675370; - ulong F1568831324; - ulong F1738081055; - ulong F138696581; - ulong F1200418899; - ulong F1499981373; - ulong F1032561651; - ulong F1804169091; - ulong F1439803811; - ulong F655157033; - ulong F148194377; - ulong F427278439; - ulong F284801908; - ulong F2143654061; - ulong F1067832947; - ulong F882390108; - ulong F239856668; - ulong F1960814827; - ulong F1939233704; - ulong F503728036; - ulong F983717629; - ulong F976566311; - ulong F282494769; - ulong F261699051; - ulong F1603874138; - ulong F1476838888; - ulong F1445979003; - ulong F1634980434; - ulong F1834905559; - ulong F592748728; - ulong F37657867; - ulong F1015282898; - ulong F1533534092; - ulong F59589418; - ulong F1928788911; - ulong F134854420; - ulong F45780662; - ulong F1414754829; - ulong F436177677; - ulong F1755478501; - ulong F114974098; - ulong F1440039324; - ulong F1905156430; - ulong F629121539; - ulong F534534001; - ulong F1651222239; - ulong F992013785; - ulong F1814559764; - ulong F907737395; - ulong F1258283890; - ulong F1548228419; - ulong F509360555; - ulong F466396486; - ulong F2013350500; - ulong F1859988505; - ulong F1327592706; - ulong F1286336555; - ulong F1476382004; - ulong F682306090; - ulong F1871063658; - ulong F54002370; - ulong F1545064864; - ulong F2116747179; - ulong F847055083; - ulong F617499166; - ulong F1280395126; - ulong F1041227994; - ulong F225212490; - ulong F214865150; - ulong F932978527; - ulong F836609446; - ulong F972585486; - ulong F1524637150; - ulong F183755203; - ulong F388753938; - ulong F1691161952; - ulong F1218893528; - ulong F1800856877; - ulong F1874648697; - ulong F2100135546; - ulong F484825103; - ulong F1778902886; - ulong F727243039; - ulong F576621669; - ulong F1192003956; - ulong F1675780959; - ulong F548886412; - ulong F1667667239; - ulong F347084560; - ulong F601196205; - ulong F996001512; - ulong F716882305; - ulong F732448739; - ulong F712597666; - ulong F1701476131; - ulong F717392881; - ulong F1470775792; - ulong F1058101347; - ulong F11622373; - ulong F1401622522; - ulong F609994245; - ulong F766801295; - ulong F1599694614; - ulong F2122242515; - ulong F421674444; - ulong F575642933; - ulong F1132207052; - ulong F282641283; - ulong F1624596562; - ulong F168826553; - ulong F108699178; - ulong F1632963325; - ulong F1749216954; - ulong F729654191; - ulong F1386238555; - ulong F422583131; - ulong F817821825; - ulong F1540125510; - ulong F1802534774; - ulong F1089201854; - ulong F731508714; - ulong F1521044402; - ulong F2025611577; - ulong F1761152592; - ulong F2084460662; - ulong F119727141; - ulong F240136747; - ulong F812039484; - ulong F629762719; - ulong F1818844704; - ulong F220386160; - ulong F160792181; - ulong F1789234504; - ulong F473026175; - ulong F1490141301; - ulong F1865507455; - ulong F179208272; - ulong F752484171; - ulong F154947225; - ulong F616361023; - ulong F543573907; - ulong F266245129; - ulong F43070677; - ulong F178258007; - ulong F492497027; - ulong F1510521834; - ulong F1115148998; - ulong F2794548; - ulong F1473842758; - ulong F1278893000; - ulong F2047054703; - ulong F2078133929; - ulong F1403050220; - ulong F1069904166; - ulong F670113808; - ulong F1236433490; - ulong F888673365; - ulong F1986025669; - ulong F37781853; - ulong F301947303; - ulong F335506186; - ulong F320167568; - ulong F1800362211; - ulong F1953235505; - ulong F2095924040; - ulong F2095390644; - ulong F1991212468; - ulong F1276190779; - ulong F1386996537; - ulong F1668214747; - ulong F243374859; - ulong F65337654; - ulong F1385178285; - ulong F1186173751; - ulong F545627947; - ulong F465263585; - ulong F1477973725; - ulong F1847353570; - ulong F1268655565; - ulong F573938828; - ulong F1152061790; - ulong F237342199; - ulong F1485680373; - ulong F1498353366; - ulong F1919273648; - ulong F289735878; - ulong F905225608; - ulong F719330338; - ulong F1950396014; - ulong F253707811; - ulong F976834090; - ulong F340666250; - ulong F714702318; - ulong F2000483569; - ulong F280854837; - ulong F223406339; - ulong F613366565; - ulong F237318819; - ulong F229817614; - ulong F544590030; - ulong F1666793013; - ulong F1986441866; - ulong F763281658; - ulong F1953111658; - ulong F1035518141; - ulong F1981717049; - ulong F692955644; - ulong F216876469; - ulong F524276219; - ulong F204850223; - ulong F1905943412; - ulong F1188803442; - ulong F717370104; - ulong F1611326672; - ulong F1297369160; - ulong F98163987; - ulong F981970842; - ulong F302008845; - ulong F33961857; - ulong F1806188162; - ulong F1190165036; - ulong F1271882130; - ulong F1473278412; - ulong F1133288726; - ulong F691380657; - ulong F2050192256; - ulong F1498118983; - ulong F1532178363; - ulong F905318914; - ulong F322221608; - ulong F1999380667; - ulong F1240654906; - ulong F1617535956; - ulong F724065535; - ulong F1054629462; - ulong F1313103571; - ulong F1621544188; - ulong F1680052362; - ulong F462835225; - ulong F2085040246; - ulong F1744263881; - ulong F688349139; - ulong F195054119; - ulong F1745545791; - ulong F495248046; - ulong F1935514295; - ulong F1770779793; - ulong F1250859293; - ulong F703114409; - ulong F182690850; - ulong F1388919144; - ulong F311357720; - ulong F203356962; - ulong F571113099; - ulong F1501908641; - ulong F394910883; - ulong F513163454; - ulong F1777476579; - ulong F1261731001; - ulong F1132809532; - ulong F483598066; - ulong F1308260928; - ulong F1459041202; - ulong F202054611; - ulong F352953203; - ulong F665288506; - ulong F1718751133; - ulong F2140788216; - ulong F556697210; - ulong F2131749236; - ulong F624103446; - ulong F1449402127; - ulong F1986657267; - ulong F96405258; - ulong F61924281; - ulong F501815897; - ulong F1076828011; - ulong F1875216268; - ulong F638040680; - ulong F903350009; - ulong F279412463; - ulong F247259690; - ulong F829063954; - ulong F722628064; - ulong F1080786111; - ulong F1688022947; - ulong F1037297944; - ulong F1046422857; - ulong F1369640541; - ulong F659718579; - ulong F799940117; - ulong F1991551256; - ulong F418321361; - ulong F1477509340; - ulong F1601442180; - ulong F436002953; - ulong F1376791584; - ulong F2140483155; - ulong F1392592588; - ulong F1977443187; - ulong F216763162; - ulong F1777475224; - ulong F694162083; - ulong F718848820; - ulong F1706071051; - ulong F2087000664; - ulong F472184100; - ulong F106951704; - ulong F509188818; - ulong F1000092744; - ulong F1465566519; - ulong F785430833; - ulong F1139435899; - ulong F358380992; - ulong F853397069; - ulong F236338376; - ulong F479196974; - ulong F736413138; - ulong F1268752147; - ulong F812413903; - ulong F1775474209; - ulong F672328276; - ulong F771147675; - ulong F2044462278; - ulong F1331809119; - ulong F780035837; - ulong F1031080766; - ulong F509147927; - ulong F642446725; - ulong F1773404975; - ulong F1272507960; - ulong F1083828503; - ulong F482623680; - ulong F808081140; - ulong F686586847; - ulong F649420886; - ulong F1700581254; - ulong F110215134; - ulong F1164040660; - ulong F1141269094; - ulong F1215838847; - ulong F930346240; - ulong F537234039; - ulong F369547797; - ulong F1341635707; - ulong F14509284; - ulong F852115357; - ulong F59940369; - ulong F624112271; - ulong F1365103804; - ulong F2104289626; - ulong F640378446; - ulong F871731008; - ulong F580178685; - ulong F201968978; - ulong F1691918533; - ulong F1006327549; - ulong F797183452; - ulong F1534523348; - ulong F926035214; - ulong F1055919898; - ulong F2110519820; - ulong F1611988626; - ulong F883267490; - ulong F1875068431; - ulong F381738016; - ulong F302807153; - ulong F331354759; - ulong F1819277792; - ulong F203976183; - ulong F683240769; - ulong F368981840; - ulong F1719856125; - ulong F127483053; - ulong F1744058703; - ulong F845127969; - ulong F135094237; - ulong F401599878; - ulong F702826571; - ulong F1317299835; - ulong F2075404127; - ulong F971140397; - ulong F2032519303; - ulong F1424826568; - ulong F1816598996; - ulong F632129514; - ulong F212097495; - ulong F2049928642; - ulong F606112162; - ulong F1142151961; - ulong F1790576984; - ulong F903397802; - ulong F723175433; - ulong F238005446; - ulong F85349196; - ulong F1252802674; - ulong F1465841261; - ulong F1801450196; - ulong F641963013; - ulong F959897691; - ulong F1859185778; - ulong F520760598; - ulong F388146224; - ulong F420136088; - ulong F681863035; - ulong F1735307786; - ulong F1068005968; - ulong F744247955; - ulong F983603629; - ulong F1504324656; - ulong F1556824296; - ulong F604727671; - ulong F94356881; - ulong F217223513; - ulong F998114734; - ulong F84779501; - ulong F78000517; - ulong F187162058; - ulong F1214152141; - ulong F1242938917; - ulong F169640521; - ulong F400362158; - ulong F1872726244; - ulong F677125831; - ulong F560882846; - ulong F1927326614; - ulong F1793290054; - ulong F1481850679; - ulong F42133857; - ulong F491256029; - ulong F1526770355; - ulong F481127688; - ulong F1907120512; - ulong F1890412527; - ulong F1605597704; - ulong F1554643529; - ulong F582994173; - ulong F1612383215; - ulong F742963533; - ulong F81291210; - ulong F1711607193; - ulong F1615333187; - ulong F1066325013; - ulong F1249271153; - ulong F1732811312; - ulong F1185849313; - ulong F809040921; - ulong F505951920; - ulong F1387374359; - ulong F569695; - ulong F1174802157; - ulong F1278679203; - ulong F587298055; - ulong F1546507743; - ulong F790257170; - ulong F1458823620; - ulong F795518001; - ulong F1858504040; - ulong F2006736889; - ulong F902020068; - ulong F2089501379; - ulong F1733638936; - ulong F702114098; - ulong F492347600; - ulong F2125037948; - ulong F1075696608; - ulong F845090806; - ulong F351428001; - ulong F759109456; - ulong F1590954852; - ulong F1649268975; - ulong F613100949; - ulong F1591682172; - ulong F1132860931; - ulong F1678815371; - ulong F701790981; - ulong F1481520792; - ulong F623455091; - ulong F1091798166; - ulong F1522517180; - ulong F1118285693; - ulong F1287338134; - ulong F94476320; - ulong F41564162; - ulong F1463937519; - ulong F248091152; - ulong F2041313280; - ulong F360612769; - ulong F1100155357; - ulong F146774084; - ulong F759125528; - ulong F871973780; - ulong F1753129973; - ulong F1988427112; - ulong F139273478; - ulong F2125451904; - ulong F913219089; - ulong F573977413; - ulong F1271716852; - ulong F657114704; - ulong F340758507; - ulong F457612920; - ulong F1894326111; - ulong F1943903154; - ulong F498784367; - ulong F561701208; - ulong F1834480678; - ulong F1601920771; - ulong F2015176019; - ulong F88466189; - ulong F2124786475; - ulong F172062910; - ulong F766705874; - ulong F484219709; - ulong F1931218022; - ulong F802163245; - ulong F1639162616; - ulong F660549936; - ulong F1175893728; - ulong F1876946796; - ulong F1181866975; - ulong F484478037; - ulong F1398756291; - ulong F612335372; - ulong F831829324; - ulong F777295195; - ulong F1007454623; - ulong F1750738707; - ulong F993587453; - ulong F1700847114; - ulong F1936055539; - ulong F907543379; - ulong F1499221886; - ulong F434683462; - ulong F1181758673; - ulong F660672773; - ulong F1540495670; - ulong F298056813; - ulong F1690263442; - ulong F902236311; - ulong F561094121; - ulong F439392509; - ulong F492920397; - ulong F1011689168; - ulong F169471256; - ulong F587062618; - ulong F105267906; - ulong F1268910264; - ulong F57209090; - ulong F1484593880; - ulong F486289288; - ulong F252669153; - ulong F1545567332; - ulong F1542253703; - ulong F1622731376; - ulong F2003764117; - ulong F1206340276; - ulong F1281990739; - ulong F1112073830; - ulong F1868972819; - ulong F1701730232; - ulong F83741971; - ulong F608333318; - ulong F314328905; - ulong F299894297; - ulong F1217243096; - ulong F820324671; - ulong F332022412; - ulong F1449944683; - ulong F1270545249; - ulong F1409151222; - ulong F1341105803; - ulong F1117770141; - ulong F273657417; - ulong F1315852675; - ulong F742474466; - ulong F2139041287; - ulong F387067123; - ulong F442864116; - ulong F244766706; - ulong F672027289; - ulong F1886028006; - ulong F1693529617; - ulong F1656477220; - ulong F1214557826; - ulong F1683386386; - ulong F1509459694; - ulong F2104451830; - ulong F959435733; - ulong F1325478203; - ulong F1601816144; - ulong F258504931; - ulong F1333466630; - ulong F1968774270; - ulong F1347989726; - ulong F477352150; - ulong F1978542838; - ulong F178591492; - ulong F711794871; - ulong F1099711807; - ulong F1914221594; - ulong F1920729141; - ulong F1966449228; - ulong F934147488; - ulong F75442658; - ulong F1292667132; - ulong F1282382659; - ulong F1271909915; - ulong F226401028; - ulong F880256910; - ulong F2012206477; - ulong F819273153; - ulong F839126623; - ulong F867307124; - ulong F1196945530; - ulong F1963185873; - ulong F537696001; - ulong F1099339745; - ulong F1247254726; - ulong F763991558; - ulong F1855267049; - ulong F863356488; - ulong F1520070326; - ulong F124466480; - ulong F1816212752; - ulong F1150646291; - ulong F7639172; - ulong F1296479518; - ulong F1073151338; - ulong F838500525; - ulong F911415275; - ulong F1960449795; - ulong F1822755899; - ulong F1490635956; - ulong F478028759; - ulong F898781795; - ulong F2067062425; - ulong F759382129; - ulong F1581034562; - ulong F2069374341; - ulong F401003727; - ulong F237549779; - ulong F1878050802; - ulong F79178823; - ulong F1460755373; - ulong F782542991; - ulong F1566861955; - ulong F466159506; - ulong F771828740; - ulong F1532287500; - ulong F2087139796; - ulong F879203093; - ulong F1078820413; - ulong F2095286960; - ulong F1391928405; - ulong F1050865106; - ulong F400658815; - ulong F1841982748; - ulong F1265418383; - ulong F1072280014; - ulong F1285027959; - ulong F2133386788; - ulong F198758577; - ulong F1535384150; - ulong F2116325282; - ulong F51756682; - ulong F1144000901; - ulong F1495974314; - ulong F389278365; - ulong F298163735; - ulong F2043607095; - ulong F1925797519; - ulong F1665788830; - ulong F1325364032; - ulong F362987831; - ulong F1617717270; - ulong F1132789333; - ulong F1440891503; - ulong F811194754; - ulong F1033669761; - ulong F1731267983; - ulong F1688963314; - ulong F524650778; - ulong F1688347485; - ulong F898844376; - ulong F32212182; - ulong F881629382; - ulong F1874952586; - ulong F98707551; - ulong F1574647300; - ulong F498122980; - ulong F225079677; - ulong F1641447393; - ulong F508754548; - ulong F784346382; - ulong F415100586; - ulong F38791202; - ulong F342666652; - ulong F110337188; - ulong F1408998691; - ulong F1786025737; - ulong F70887641; - ulong F76881141; - ulong F473665005; - ulong F1636164052; - ulong F161342277; - ulong F1360897910; - ulong F1900940028; - ulong F1732299129; - ulong F1921694782; - ulong F2065559420; - ulong F1107250959; - ulong F1030787994; - ulong F231748622; - ulong F1488495678; - ulong F1743548292; - ulong F1608736010; - ulong F657894739; - ulong F636539774; - ulong F2084113100; - ulong F1317610947; - ulong F1416531962; - ulong F1397266763; - ulong F962114712; - ulong F1947524402; - ulong F1818527418; - ulong F284350126; - ulong F1157034282; - ulong F541017650; - ulong F2095370892; - ulong F1578926068; - ulong F790122681; - ulong F1330554315; - ulong F1549679710; - ulong F1395127671; - ulong F1660380342; - ulong F1612082173; - ulong F50985773; - ulong F52183433; - ulong F737502099; - ulong F818797919; - ulong F1128173001; - ulong F142653457; - ulong F324496416; - ulong F1656571527; - ulong F1538355668; - ulong F1341775330; - ulong F1409698771; - ulong F1167742517; - ulong F1188281737; - ulong F953848223; - ulong F1528380110; - ulong F1853610525; - ulong F173707735; - ulong F91387744; - ulong F369493775; - ulong F821104525; - ulong F1262250076; - ulong F673624250; - ulong F1965120281; - ulong F2024475798; - ulong F203863628; - ulong F1359922378; - ulong F1784411884; - ulong F342768714; - ulong F1275436739; - ulong F1924180291; - ulong F1628591931; - ulong F984104598; - ulong F1975598983; - ulong F131466119; - ulong F1557750237; - ulong F605711306; - ulong F2046521322; - ulong F1265315181; - ulong F189437946; - ulong F1273878505; - ulong F1072770347; - ulong F1453026832; - ulong F409168734; - ulong F476752088; - ulong F1022135002; - ulong F2136775412; - ulong F1500219560; - ulong F1141522669; - ulong F50545958; - ulong F1083995803; - ulong F1156846580; - ulong F1458291966; - ulong F1025633896; - ulong F839275817; - ulong F349832097; - ulong F1524845170; - ulong F234546799; - ulong F860509948; - ulong F614934291; - ulong F1915734270; - ulong F505725220; - ulong F2129211349; - ulong F381134788; - ulong F1761659024; - ulong F1860667046; - ulong F425594173; - ulong F1339627181; - ulong F1056815618; - ulong F1543581633; - ulong F922668804; - ulong F1954572850; - ulong F1055876201; - ulong F2049433445; - ulong F1243098917; - ulong F1895817825; - ulong F1956706891; - ulong F264455516; - ulong F1488368193; - ulong F1002340796; - ulong F214571863; - ulong F2007186465; - ulong F642889215; - ulong F292222756; - ulong F191440936; - ulong F767333711; - ulong F170299965; - ulong F2105954349; - ulong F1136323166; - ulong F1929117669; - ulong F32905067; - ulong F371164507; - ulong F1186011374; - ulong F650380890; - ulong F421187323; - ulong F768153285; - ulong F1091042645; - ulong F1071892044; - ulong F794993357; - ulong F763568689; - ulong F596540829; - ulong F797148231; - ulong F443403942; - ulong F1745424683; - ulong F1275360801; - ulong F1276906600; - ulong F100970379; - ulong F1556342168; - ulong F1930018626; - ulong F1090941639; - ulong F540608853; - ulong F1260389654; - ulong F893662253; - ulong F2005652799; - ulong F400362428; - ulong F2056031452; - ulong F2010319652; - ulong F1836988593; - ulong F189693852; - ulong F994325313; - ulong F1690367081; - ulong F467123471; - ulong F203304015; - ulong F1275181596; - ulong F1510676566; - ulong F551504297; - ulong F768561476; - ulong F405495311; - ulong F1628246122; - ulong F474945632; - ulong F804775180; - ulong F884814847; - ulong F1616945806; - ulong F724799504; - ulong F405799967; - ulong F1564907279; - ulong F1563782523; - ulong F1044948179; - ulong F1164345602; - ulong F1062017983; - ulong F666363332; - ulong F761441444; - ulong F175935723; - ulong F45381527; - ulong F1388508816; - ulong F919999060; - ulong F1624985901; - ulong F1327842222; - ulong F250018462; - ulong F512639518; - ulong F905317280; - ulong F1401537699; - ulong F882198192; - ulong F1948151691; - ulong F1220685255; - ulong F129417358; - ulong F593844216; - ulong F1315705993; - ulong F234748117; - ulong F723856504; - ulong F508345124; - ulong F1842958715; - ulong F2075579693; - ulong F1455072994; - ulong F286166459; - ulong F1803277653; - ulong F1790927495; - ulong F168862749; - ulong F1599852832; - ulong F982938796; - ulong F492248929; - ulong F965371473; - ulong F672642991; - ulong F1275159516; - ulong F327961981; - ulong F928925637; - ulong F291187748; - ulong F157922488; - ulong F2034156427; - ulong F590677506; - ulong F1074002043; - ulong F1588202901; - ulong F155476849; - ulong F1115606604; - ulong F1717111999; - ulong F1550721128; - ulong F2616655; - ulong F1816277762; - ulong F1651597896; - ulong F276382609; - ulong F971063063; - ulong F248076530; - ulong F810200062; - ulong F440489098; - ulong F553672859; - ulong F970888264; - ulong F833345398; - ulong F868346376; - ulong F1906698715; - ulong F1732714810; - ulong F1276555212; - ulong F1456123152; - ulong F1875473037; - ulong F1414563313; - ulong F20390589; - ulong F2087429454; - ulong F728894708; - ulong F1754522323; - ulong F1620189710; - ulong F291759618; - ulong F1985713257; - ulong F435921727; - ulong F1429033213; - ulong F1791554258; - ulong F1797338108; - ulong F1067625870; - ulong F1687481866; - ulong F959973089; - ulong F1885444642; - ulong F882928978; - ulong F1800660998; - ulong F2122133380; - ulong F664748500; - ulong F1323470223; - ulong F11875733; - ulong F244172399; - ulong F155171411; - ulong F232153893; - ulong F721486657; - ulong F1504557364; - ulong F95580239; - ulong F1570325019; - ulong F398707420; - ulong F301441617; - ulong F1461605941; - ulong F1765362538; - ulong F1860213511; - ulong F888397183; - ulong F1095216015; - ulong F1777166192; - ulong F821826420; - ulong F395577979; - ulong F196088052; - ulong F1359838278; - ulong F438152999; - ulong F535141335; - ulong F966526964; - ulong F1166129451; - ulong F790634637; - ulong F1633530636; - ulong F1430890045; - ulong F2020855956; - ulong F1130385381; - ulong F1023769737; - ulong F2079537459; - ulong F1301905479; - ulong F791374652; - ulong F552002814; - ulong F1402687580; - ulong F1923701837; - ulong F1932258043; - ulong F496740815; - ulong F1033035666; - ulong F115632346; - ulong F196179379; - ulong F415388238; - ulong F37214308; - ulong F1127591596; - ulong F329948317; - ulong F31975570; - ulong F1354896006; - ulong F799084683; - ulong F2012240721; - ulong F108278450; - ulong F61102558; - ulong F1405083019; - ulong F1926045328; - ulong F1452393869; - ulong F885317224; - ulong F1624218045; - ulong F1425129082; - ulong F1136525607; - ulong F1589002903; - ulong F1235439668; - ulong F73667319; - ulong F222207930; - ulong F439939638; - ulong F1522421330; - ulong F369387805; - ulong F159700462; - ulong F973987886; - ulong F1308210697; - ulong F1633193250; - ulong F1318997825; - ulong F1992391796; - ulong F325085605; - ulong F1510025960; - ulong F80455706; - ulong F1163658899; - ulong F22764761; - ulong F497927027; - ulong F1986419015; - ulong F836181134; - ulong F758659067; - ulong F278634630; - ulong F631805362; - ulong F8615235; - ulong F1022106931; - ulong F962667179; - ulong F674454440; - ulong F1523343798; - ulong F1486464430; - ulong F1814169237; - ulong F1925953182; - ulong F498572755; - ulong F795732436; - ulong F1055221559; - ulong F1945079645; - ulong F41965027; - ulong F2121455096; - ulong F2122932247; - ulong F662276625; - ulong F758203791; - ulong F170247855; - ulong F1205471331; - ulong F46685309; - ulong F1313375080; - ulong F693242896; - ulong F263370301; - ulong F1883500600; - ulong F2042540706; - ulong F1845589622; - ulong F288734970; - ulong F862552463; - ulong F1126291018; - ulong F1586193714; - ulong F300344473; - ulong F830343836; - ulong F956805038; - ulong F1589345604; - ulong F213592695; - ulong F1565316354; - ulong F559754151; - ulong F1842417012; - ulong F783840311; - ulong F1635007103; - ulong F1641525107; - ulong F1854723715; - ulong F820425070; - ulong F1196659360; - ulong F1417347693; - ulong F1712429962; - ulong F38490679; - ulong F1189687450; - ulong F47316161; - ulong F1983134049; - ulong F1228215224; - ulong F665933279; - ulong F1700671383; - ulong F231949321; - ulong F1465913929; - ulong F1462855986; - ulong F758736630; - ulong F1226650226; - ulong F779397381; - ulong F1825237823; - ulong F1197729460; - ulong F951616774; - ulong F799662164; - ulong F1059862688; - ulong F495387963; - ulong F224877723; - ulong F988274607; - ulong F600103070; - ulong F1907862401; - ulong F557615893; - ulong F102522474; - ulong F1063270426; - ulong F1533891191; - ulong F1717947875; - ulong F552643849; - ulong F1606135012; - ulong F2020301473; - ulong F1214194588; - ulong F466152907; - ulong F330110744; - ulong F1807098943; - ulong F1246531167; - ulong F815236302; - ulong F1290640616; - ulong F357978490; - ulong F1781894841; - ulong F1277156100; - ulong F724855717; - ulong F123431675; - ulong F898220356; - ulong F806579724; - ulong F1480587572; - ulong F1063019631; - ulong F1106086135; - ulong F437277643; - ulong F689908333; - ulong F1055061551; - ulong F1908046029; - ulong F701271397; - ulong F1192469970; - ulong F724155355; - ulong F1585871256; - ulong F1429308696; - ulong F1637183915; - ulong F1880611575; - ulong F164944798; - ulong F1279525735; - ulong F2130207155; - ulong F1826789119; - ulong F2007262564; - ulong F1590038160; - ulong F1692025689; - ulong F760497319; - ulong F449286637; - ulong F18138880; - ulong F2098681940; - ulong F136380472; - ulong F1656505195; - ulong F701884198; - ulong F860976769; - ulong F1095205270; - ulong F263418890; - ulong F476671395; - ulong F1009642045; - ulong F1898519816; - ulong F769418549; - ulong F250795; - ulong F427805056; - ulong F1280670232; - ulong F2010219163; - ulong F551073461; - ulong F112255444; - ulong F512923191; - ulong F1421166584; - ulong F1753439036; - ulong F221227687; - ulong F1964706118; - ulong F1325536034; - ulong F1557512688; - ulong F193033692; - ulong F502369106; - ulong F1294432592; - ulong F1045550245; - ulong F263652758; - ulong F1455665843; - ulong F1262037682; - ulong F720090253; - ulong F613732994; - ulong F1087947255; - ulong F486079350; - ulong F553527861; - ulong F1546040003; - ulong F1206161831; - ulong F1987778275; - ulong F97264700; - ulong F460736465; - ulong F1109199861; - ulong F419666651; - ulong F1886147746; - ulong F1111193026; - ulong F164694003; - ulong F851720679; - ulong F849536923; - ulong F1964053603; - ulong F1456189103; - ulong F1477782716; - ulong F1179102498; - ulong F1486814382; - ulong F843331248; - ulong F1944394840; - ulong F133975822; - ulong F958328085; - ulong F98992507; - ulong F508850506; - ulong F358607663; - ulong F1948256325; - ulong F1365352292; - ulong F213018637; - ulong F1701459849; - ulong F636482134; - ulong F49328295; - ulong F1534001448; - ulong F1487341448; - ulong F794590882; - ulong F1456691302; - ulong F1152517105; - ulong F1053577260; - ulong F672628563; - ulong F1323901884; - ulong F1292702571; - ulong F1259511473; - ulong F1545039467; - ulong F1586871935; - ulong F446319662; - ulong F28339689; - ulong F1798132074; - ulong F444895669; - ulong F1228980289; - ulong F954947302; - ulong F2125366774; - ulong F82935183; - ulong F1380759518; - ulong F1917885393; - ulong F1291036062; - ulong F352103528; - ulong F1742683423; - ulong F1447047496; - ulong F697311325; - ulong F1629170612; - ulong F296492022; - ulong F1242867820; - ulong F896181224; - ulong F865690449; - ulong F1249665612; - ulong F1061864730; - ulong F778176202; - ulong F1511862878; - ulong F54946041; - ulong F507362301; - ulong F303671998; - ulong F424205456; - ulong F506473935; - ulong F162912498; - ulong F1698112324; - ulong F684883367; - ulong F736420002; - ulong F1518939797; - ulong F1800156492; - ulong F480510817; - ulong F707959236; - ulong F1503360656; - ulong F136372003; - ulong F1405554982; - ulong F1723576722; - ulong F553546950; - ulong F816052425; - ulong F1763599702; - ulong F196305386; - ulong F442487354; - ulong F1861491526; - ulong F1852953256; - ulong F356265935; - ulong F1190941598; - ulong F1027409862; - ulong F49834751; - ulong F363330249; - ulong F679349018; - ulong F337206323; - ulong F1531938579; - ulong F1397647134; - ulong F286269196; - ulong F389949628; - ulong F721617988; - ulong F651275304; - ulong F1701161318; - ulong F1723944896; - ulong F1217847020; - ulong F219773069; - ulong F606152695; - ulong F1763167173; - ulong F223743626; - ulong F1794374651; - ulong F216800508; - ulong F921211376; - ulong F940615013; - ulong F1106495817; - ulong F1638109889; - ulong F1289597374; - ulong F696118662; - ulong F245812305; - ulong F1162060147; - ulong F1315557492; - ulong F1759942334; - ulong F793354422; - ulong F598202389; - ulong F67939521; - ulong F1463015984; - ulong F1282986283; - ulong F1648277573; - ulong F321553118; - ulong F57070983; - ulong F1181733474; - ulong F268217913; - ulong F1230347330; - ulong F421690040; - ulong F1113411028; - ulong F1562237662; - ulong F754279678; - ulong F22415404; - ulong F977085701; - ulong F1745689052; - ulong F1543826633; - ulong F1737636338; - ulong F826803828; - ulong F1637747900; - ulong F58578605; - ulong F139465427; - ulong F269730222; - ulong F86794849; - ulong F1090822581; - ulong F872704007; - ulong F1537235291; - ulong F1788571308; - ulong F1286126274; - ulong F235586987; - ulong F1118195351; - ulong F777490941; - ulong F2075747213; - ulong F53072915; - ulong F1633221797; - ulong F260928912; - ulong F2082344384; - ulong F718979143; - ulong F284599577; - ulong F1706096189; - ulong F1189493799; - ulong F1526156738; - ulong F1133936825; - ulong F499521336; - ulong F1974687632; - ulong F1691741802; - ulong F883830211; - ulong F1267181970; - ulong F1866516608; - ulong F647606900; - ulong F1765717161; - ulong F1725404801; - ulong F933138506; - ulong F1303090169; - ulong F539623784; - ulong F2075957741; - ulong F1193285762; - ulong F1196191434; - ulong F557454992; - ulong F1596332758; - ulong F667319340; - ulong F1540645813; - ulong F1129575286; - ulong F994760343; - ulong F1450978336; - ulong F335920087; - ulong F1633974096; - ulong F701206763; - ulong F536677254; - ulong F716156789; - ulong F1810828315; - ulong F824847490; - ulong F1453036761; - ulong F1268191286; - ulong F448254101; - ulong F679905514; - ulong F1153012249; - ulong F1917692533; - ulong F259590864; - ulong F1546564426; - ulong F2136357443; - ulong F270053321; - ulong F2069538347; - ulong F638519374; - ulong F617353473; - ulong F1540274197; - ulong F1991836082; - ulong F772657044; - ulong F1660932778; - ulong F1704747703; - ulong F1215126797; - ulong F886152950; - ulong F161524151; - ulong F835750466; - ulong F1038776849; - ulong F1796331633; - ulong F396581452; - ulong F139176482; - ulong F1196026647; - ulong F1638767545; - ulong F57767706; - ulong F182623448; - ulong F730504716; - ulong F1150359819; - ulong F984262232; - ulong F940869671; - ulong F272368040; - ulong F1812430867; - ulong F854836068; - ulong F2007201917; - ulong F922945492; - ulong F1423076876; - ulong F936600570; - ulong F1158374213; - ulong F1607458962; - ulong F397266019; - ulong F1618591113; - ulong F491055912; - ulong F377406870; - ulong F2058187786; - ulong F491567652; - ulong F861317052; - ulong F1187757632; - ulong F979413198; - ulong F1648513639; - ulong F924675365; - ulong F663323339; - ulong F617286295; - ulong F229414437; - ulong F799406115; - ulong F283324062; - ulong F1013835767; - ulong F721665886; - ulong F768306966; - ulong F1488796720; - ulong F1953733995; - ulong F1687032252; - ulong F919178528; - ulong F1801740789; - ulong F1823967449; - ulong F1267906157; - ulong F179405215; - ulong F2065304623; - ulong F1801214508; - ulong F781802211; - ulong F1939533568; - ulong F2097036027; - ulong F1150633585; - ulong F1375775151; - ulong F641510830; - ulong F177740520; - ulong F2053009187; - ulong F1909253259; - ulong F1285322508; - ulong F1147199893; - ulong F1343934301; - ulong F1142349463; - ulong F1898575165; - ulong F1649329827; - ulong F59586867; - ulong F277546332; - ulong F1802565392; - ulong F1583016430; - ulong F55429953; - ulong F1723877855; - ulong F2056593372; - ulong F701410990; - ulong F168293604; - ulong F1817061140; - ulong F1801208614; - ulong F857717414; - ulong F699412585; - ulong F836798770; - ulong F700923068; - ulong F790281629; - ulong F312162437; - ulong F943496076; - ulong F1534026771; - ulong F197610987; - ulong F1856463718; - ulong F975122985; - ulong F1660173401; - ulong F1388994791; - ulong F1735387254; - ulong F621665595; - ulong F377798522; - ulong F1252066155; - ulong F1583827025; - ulong F1768590720; - ulong F144862419; - ulong F811384532; - ulong F1935940734; - ulong F1417332348; - ulong F1742153922; - ulong F1546421117; - ulong F1612824412; - ulong F743872432; - ulong F2009874670; - ulong F77336653; - ulong F872692486; - ulong F1238122578; - ulong F1928742423; - ulong F1481056092; - ulong F1722050184; - ulong F1931277063; - ulong F1625811582; - ulong F1216210417; - ulong F1208330191; - ulong F495040879; - ulong F835037456; - ulong F400438225; - ulong F1755806339; - ulong F1700964178; - ulong F1940349756; - ulong F1231947529; - ulong F764856578; - ulong F413570601; - ulong F1995112823; - ulong F1581248005; - ulong F1346079333; - ulong F804527217; - ulong F1265067612; - ulong F547186531; - ulong F1672198721; - ulong F989824082; - ulong F1069260327; - ulong F1429563884; - ulong F1242128495; - ulong F1301985598; - ulong F1324940864; - ulong F1715773652; - ulong F1081105053; - ulong F1456690118; - ulong F1472402148; - ulong F618341140; - ulong F1193864209; - ulong F179117309; - ulong F1814428254; - ulong F1951593838; - ulong F1143337660; - ulong F1309071752; - ulong F43735964; - ulong F1088786146; - ulong F933553264; - ulong F1891907841; - ulong F1203061840; - ulong F234976556; - ulong F1624466239; - ulong F510206393; - ulong F781564539; - ulong F1199253811; - ulong F896243256; - ulong F428626665; - ulong F878740967; - ulong F68165269; - ulong F2120538613; - ulong F1381555892; - ulong F1956341018; - ulong F732226102; - ulong F862016736; - ulong F196247698; - ulong F2121565569; - ulong F2053828240; - ulong F1317583662; - ulong F1266747451; - ulong F1466816819; - ulong F299116221; - ulong F228562030; - ulong F1322008616; - ulong F38083320; - ulong F585739269; - ulong F746625994; - ulong F43518985; - ulong F437910345; - ulong F37007581; - ulong F760791253; - ulong F176281466; - ulong F1761116914; - ulong F1927774527; - ulong F1934245889; - ulong F834283771; - ulong F1952581292; - ulong F731922102; - ulong F520421059; - ulong F125687053; - ulong F819530396; - ulong F652478388; - ulong F577949151; - ulong F1404236879; - ulong F645286174; - ulong F1959791964; - ulong F370259938; - ulong F1082202152; - ulong F1089577102; - ulong F947089962; - ulong F1334989830; - ulong F137391371; - ulong F1918686131; - ulong F1814289460; - ulong F425091022; - ulong F903945619; - ulong F6414526; - ulong F302457623; - ulong F472123073; - ulong F195825270; - ulong F452627817; - ulong F852724271; - ulong F2138199967; - ulong F841733386; - ulong F1454857663; - ulong F1944257147; - ulong F1767922625; - ulong F28566491; - ulong F945463860; - ulong F27732965; - ulong F391150053; - ulong F1389643467; - ulong F1533407181; - ulong F1191896609; - ulong F447217055; - ulong F814338431; - ulong F1868650717; - ulong F971808798; - ulong F676722442; - ulong F225775003; - ulong F215479331; - ulong F1811907489; - ulong F1101425530; - ulong F1638304030; - ulong F849501398; - ulong F623399882; - ulong F405078982; - ulong F2094311101; - ulong F1502683505; - ulong F1030300270; - ulong F827869245; - ulong F1650123669; - ulong F259799029; - ulong F324595789; - ulong F1820542883; - ulong F2114289772; - ulong F661762068; - ulong F1883699412; - ulong F2096862863; - ulong F848512674; - ulong F191869339; - ulong F341693447; - ulong F136738292; - ulong F1061844137; - ulong F555939909; - ulong F2092830010; - ulong F751467837; - ulong F726789522; - ulong F1367072405; - ulong F1758236238; - ulong F1182778549; - ulong F1182089375; - ulong F1773218828; - ulong F246348070; - ulong F2127829586; - ulong F788203975; - ulong F1898782388; - ulong F499895937; - ulong F2139715635; - ulong F831457781; - ulong F1539178165; - ulong F1821095171; - ulong F673366633; - ulong F2062647237; - ulong F1347347367; - ulong F888510031; - ulong F1129844438; - ulong F1208811392; - ulong F1518837373; - ulong F480410930; - ulong F152576363; - ulong F2132434952; - ulong F1022429582; - ulong F1975693415; - ulong F33905664; - ulong F2021269531; - ulong F1675169197; - ulong F39581393; - ulong F1082364121; - ulong F904155035; - ulong F2019415692; - ulong F1825773107; - ulong F727238696; - ulong F1891930914; - ulong F1995005368; - ulong F1793263517; - ulong F2024388488; - ulong F13450959; - ulong F344249850; - ulong F1032338908; - ulong F215507384; - ulong F161866131; - ulong F1891467424; - ulong F1265405082; - ulong F1456818156; - ulong F518257815; - ulong F1815810461; - ulong F221574702; - ulong F1861980417; - ulong F1814913525; - ulong F962985572; - ulong F1690140092; - ulong F1355435796; - ulong F886661475; - ulong F1605659875; - ulong F1197827244; - ulong F159659793; - ulong F1945009060; - ulong F212442406; - ulong F106560055; - ulong F1260518425; - ulong F1859200995; - ulong F1565015463; - ulong F1235560600; - ulong F959525736; - ulong F1860888705; - ulong F1093856475; - ulong F928919366; - ulong F67641869; - ulong F1701567497; - ulong F1011605190; - ulong F1116393479; - ulong F864561542; - ulong F486498465; - ulong F264903546; - ulong F2138193879; - ulong F240967528; - ulong F1904508147; - ulong F518875259; - ulong F1663131496; - ulong F205459070; - ulong F1453594495; - ulong F325084623; - ulong F1414934243; - ulong F2088653110; - ulong F329275600; - ulong F470337311; - ulong F1988060868; - ulong F286271039; - ulong F797178124; - ulong F1633603724; - ulong F79379428; - ulong F1948492200; - ulong F237689795; - ulong F1919304130; - ulong F503790036; - ulong F744334315; - ulong F655906824; - ulong F305879346; - ulong F1743413098; - ulong F1571884987; - ulong F886891095; - ulong F153932833; - ulong F160412920; - ulong F803308335; - ulong F1994075740; - ulong F825578550; - ulong F868937331; - ulong F621757929; - ulong F1614949643; - ulong F956859716; - ulong F402635293; - ulong F1426133801; - ulong F696794557; - ulong F2048584632; - ulong F1954407577; - ulong F1534116372; - ulong F150081220; - ulong F1294391137; - ulong F630250136; - ulong F1390551394; - ulong F1253279254; - ulong F642648327; - ulong F1417947391; - ulong F67963773; - ulong F932225762; - ulong F1315384926; - ulong F626871747; - ulong F714677982; - ulong F1908597157; - ulong F1393859564; - ulong F1732544351; - ulong F1598628801; - ulong F922945808; - ulong F91246509; - ulong F1466051622; - ulong F1299661662; - ulong F164671703; - ulong F611625908; - ulong F94577370; - ulong F1651180697; - ulong F1748883627; - ulong F1366302939; - ulong F818805043; - ulong F1987802055; - ulong F1230968431; - ulong F800729274; - ulong F1251697643; - ulong F336588810; - ulong F2112380200; - ulong F1117157311; - ulong F594253095; - ulong F1508999334; - ulong F1823112857; - ulong F352861704; - ulong F318605733; - ulong F244242768; - ulong F883469088; - ulong F92449147; - ulong F2018566220; - ulong F678690814; - ulong F198706803; - ulong F154259349; - } -} \ No newline at end of file diff --git a/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/NonFungibleToken.cs b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/NonFungibleToken.cs index 97c66356f2..e0402c2d68 100644 --- a/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/NonFungibleToken.cs +++ b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/NonFungibleToken.cs @@ -46,7 +46,7 @@ public void TransferFrom(Address from, Address to, ulong tokenId) AddTokenTo(to, tokenId); RemoveTokenFrom(from); - Log(new Transfer{From = from, To = to, TokenId = tokenId}); + Log(new Transfer2 { From = from, To = to, TokenId = tokenId }); } private void AddTokenTo(Address to, ulong tokenId) @@ -60,7 +60,7 @@ private void RemoveTokenFrom(Address from) SetBalance(from, BalanceOf(from) - 1); } - public struct Transfer + public struct Transfer2 { [Index] public ulong TokenId; @@ -71,5 +71,4 @@ public struct Transfer [Index] public Address To; } - } \ No newline at end of file diff --git a/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/ValueTransferRecipient.cs b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/ValueTransferRecipient.cs new file mode 100644 index 0000000000..104c83afbc --- /dev/null +++ b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/ValueTransferRecipient.cs @@ -0,0 +1,12 @@ +using Stratis.SmartContracts; + +public class ValueTransferRecipient : SmartContract +{ + public ValueTransferRecipient(ISmartContractState state) : base(state) { } + + public void CanReceiveValue() + { + Assert(Message.Value > 0); + } +} + diff --git a/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/ValueTransferTest.cs b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/ValueTransferTest.cs new file mode 100644 index 0000000000..1be8ae590c --- /dev/null +++ b/src/Stratis.SmartContracts.IntegrationTests/SmartContracts/ValueTransferTest.cs @@ -0,0 +1,20 @@ +using Stratis.SmartContracts; + +public class ValueTransferTest : SmartContract +{ + public ValueTransferTest(ISmartContractState state) : base(state) { } + + public void CanReceiveValue() + { + Assert(Message.Value > 0); + } + + public void CanForwardValueCall(Address other) + { + Assert(Message.Value > 0); + + var result = Call(other, Message.Value, "CanReceiveValue"); + + Assert(result.Success); + } +} \ No newline at end of file diff --git a/src/Stratis.SmartContracts.IntegrationTests/Stratis.SmartContracts.IntegrationTests.csproj b/src/Stratis.SmartContracts.IntegrationTests/Stratis.SmartContracts.IntegrationTests.csproj index 3910ccb5ad..b538256a41 100644 --- a/src/Stratis.SmartContracts.IntegrationTests/Stratis.SmartContracts.IntegrationTests.csproj +++ b/src/Stratis.SmartContracts.IntegrationTests/Stratis.SmartContracts.IntegrationTests.csproj @@ -174,6 +174,12 @@ Always + + Always + + + Always + diff --git a/src/Stratis.SmartContracts.Tests.Common/SmartContractNodeBuilder.cs b/src/Stratis.SmartContracts.Tests.Common/SmartContractNodeBuilder.cs index c62debede1..08740c1718 100644 --- a/src/Stratis.SmartContracts.Tests.Common/SmartContractNodeBuilder.cs +++ b/src/Stratis.SmartContracts.Tests.Common/SmartContractNodeBuilder.cs @@ -50,7 +50,7 @@ public CoreNode CreateSmartContractPowNode() return CreateNode(new StratisSmartContractNode(this.GetNextDataFolderName(), network), "stratis.conf"); } - public static SmartContractNodeBuilder Create(object caller, [CallerMemberName] string callingMethod = null) + public static new SmartContractNodeBuilder Create(object caller, [CallerMemberName] string callingMethod = null) { string testFolderPath = TestBase.CreateTestDir(caller, callingMethod); var builder = new SmartContractNodeBuilder(testFolderPath); diff --git a/src/Stratis.StraxD/Stratis.StraxD.csproj b/src/Stratis.StraxD/Stratis.StraxD.csproj index 9393922f8c..50a37dba71 100644 --- a/src/Stratis.StraxD/Stratis.StraxD.csproj +++ b/src/Stratis.StraxD/Stratis.StraxD.csproj @@ -16,7 +16,7 @@ latest - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd. diff --git a/src/Stratis.StraxDnsD/Stratis.StraxDnsD.csproj b/src/Stratis.StraxDnsD/Stratis.StraxDnsD.csproj index ce7fc35a20..62a114fb7b 100644 --- a/src/Stratis.StraxDnsD/Stratis.StraxDnsD.csproj +++ b/src/Stratis.StraxDnsD/Stratis.StraxDnsD.csproj @@ -16,7 +16,7 @@ latest - 1.1.1.1 + 1.2.0.0 Stratis Group Ltd.