diff --git a/Common/Class1.cs b/Common/Class1.cs deleted file mode 100644 index 4829b4a..0000000 --- a/Common/Class1.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Common -{ - public class Class1 - { - } -} diff --git a/Common/Common.csproj b/Common/Common.csproj deleted file mode 100644 index 711bf12..0000000 --- a/Common/Common.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - {44241912-4992-416E-A07B-4ABCA3C93826} - - - - false - - - false - - - false - - - false - - - Linked\%(RecursiveDir)%(Filename)%(Extension) - - - JsonConverters\%(Filename)%(Extension) - - - Models\%(Filename)%(Extension) - - - Serializer.cs - - - false - - - LinkedJson\%(RecursiveDir)%(Filename)%(Extension) - - - \ No newline at end of file diff --git a/QBitNinja/ActionDisposable.cs b/QBitNinja/ActionDisposable.cs index 9897e85..eab46fb 100644 --- a/QBitNinja/ActionDisposable.cs +++ b/QBitNinja/ActionDisposable.cs @@ -8,23 +8,24 @@ namespace QBitNinja { public class ActionDisposable : IDisposable { - Action _Act; + readonly Action _Act; + public ActionDisposable(Action act) { _Act = act; } + public ActionDisposable(Action start, Action act) { start(); _Act = act; } + #region IDisposable Members - public void Dispose() { _Act(); } - #endregion } } diff --git a/QBitNinja/CacheBlocksRepository.cs b/QBitNinja/CacheBlocksRepository.cs index 8bc25f2..8b01514 100644 --- a/QBitNinja/CacheBlocksRepository.cs +++ b/QBitNinja/CacheBlocksRepository.cs @@ -11,33 +11,35 @@ namespace QBitNinja { public class CacheBlocksRepository : IBlocksRepository { - private IBlocksRepository _Repo; + private readonly IBlocksRepository _Repo; public CacheBlocksRepository(IBlocksRepository repo) { - if(repo == null) - throw new ArgumentNullException("repo"); - this._Repo = repo; + this._Repo = repo ?? throw new ArgumentNullException("repo"); } - List> _LastAsked = new List>(); + List> _LastAsked = new List>(); // A cache of blocks. #region IBlocksRepository Members - int MaxBlocks = 70; + static readonly int MaxBlocks = 70; // The maximum number of blocks that may be cached. - public IEnumerable GetBlocks(IEnumerable hashes, CancellationToken cancellation) + public IEnumerable GetBlocks(IEnumerable hashes, CancellationToken cancellationToken) { var asked = hashes.ToList(); - if(asked.Count > MaxBlocks) - return _Repo.GetBlocks(hashes, cancellation); + + if (asked.Count > MaxBlocks) // Asked for more than fit in the cache size, no point in checking cache. + return _Repo.GetBlocks(hashes, cancellationToken); // Fetch from repo instead. + var lastAsked = _LastAsked; - if(lastAsked != null && asked.SequenceEqual(lastAsked.Select(a => a.Item1))) + if (lastAsked != null && asked.SequenceEqual(lastAsked.Select(a => a.Item1))) return lastAsked.Select(l=>l.Item2); - var blocks = _Repo.GetBlocks(hashes, cancellation).ToList(); - if(blocks.Count < 5) + + var blocks = _Repo.GetBlocks(hashes, cancellationToken).ToList(); + + if (blocks.Count < 5) // Shouldn't this number match 'MaxBlocks = 70' ? { _LastAsked = blocks.Select(b => Tuple.Create(b.GetHash(), b)).ToList(); } @@ -45,9 +47,9 @@ public CacheBlocksRepository(IBlocksRepository repo) { _LastAsked = null; } + return blocks; } - #endregion } } diff --git a/QBitNinja/ChainTable.cs b/QBitNinja/ChainTable.cs index 1362fd7..8cd6bf9 100644 --- a/QBitNinja/ChainTable.cs +++ b/QBitNinja/ChainTable.cs @@ -10,31 +10,27 @@ namespace QBitNinja { /// - /// Such table can store data keyed by Height/BlockId/TransactionId, and range query them + /// Wraps an Azure table that may store data keyed by Height/BlockId/TransactionId, and allows for range queries against it. /// public class ChainTable { private readonly JsonSerializerSettings serializerSettings; - readonly CloudTable _cloudTable; + readonly CloudTable _cloudTable; // An Azure table + public ChainTable(CloudTable cloudTable, Network network) { if(cloudTable == null) throw new ArgumentNullException("cloudTable"); if (network == null) throw new ArgumentNullException(nameof(network)); - JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); + + var serializerSettings = new JsonSerializerSettings(); QBitNinja.Serializer.RegisterFrontConverters(serializerSettings, network); this.serializerSettings = serializerSettings; _cloudTable = cloudTable; } - public CloudTable Table - { - get - { - return _cloudTable; - } - } + public CloudTable Table => _cloudTable; public Scope Scope { @@ -50,8 +46,6 @@ public void Create(ConfirmedBalanceLocator locator, T item) Table.Execute(TableOperation.InsertOrReplace(entity)); } - - public void Delete(ConfirmedBalanceLocator locator) { var entity = new DynamicTableEntity(Escape(Scope), Escape(locator)) @@ -60,15 +54,16 @@ public void Delete(ConfirmedBalanceLocator locator) }; Table.Execute(TableOperation.Delete(entity)); } + public void Delete() { - foreach(var entity in Table.ExecuteQuery(new TableQuery() + var tableQuery = new TableQuery() { FilterString = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, Escape(Scope)) - })) - { + }; + + foreach (var entity in Table.ExecuteQuery(tableQuery)) Table.Execute(TableOperation.Delete(entity)); - } } public IEnumerable Query(ChainBase chain, BalanceQuery query = null) @@ -95,6 +90,7 @@ private string ParseData(DynamicTableEntity entity) } return builder.ToString(); } + private void PutData(DynamicTableEntity entity, string str) { int i = 0; diff --git a/QBitNinja/CrudTable.cs b/QBitNinja/CrudTable.cs index 0388803..2569ed9 100644 --- a/QBitNinja/CrudTable.cs +++ b/QBitNinja/CrudTable.cs @@ -28,13 +28,14 @@ public CrudTableFactory(JsonSerializerSettings serializerSettings, Func _createTable; + public Scope Scope { get; private set; } - private JsonSerializerSettings serializerSettings; + private readonly JsonSerializerSettings serializerSettings; public CrudTable GetTable(string tableName) { @@ -45,6 +46,12 @@ public CrudTable GetTable(string tableName) }; } } + + /// + /// Wraps an Azure CloudTable, offering CRUD operations for objects of a specific type, automatically + /// serializing to and from JSON when querying the CloudTable. + /// + /// public class CrudTable { public CrudTable(CloudTable table, JsonSerializerSettings serializerSettings) @@ -53,7 +60,7 @@ public CrudTable(CloudTable table, JsonSerializerSettings serializerSettings) throw new ArgumentNullException("table"); if (serializerSettings == null) throw new ArgumentNullException(nameof(serializerSettings)); - _table = table; + Table = table; Scope = new Scope(); this.serializerSettings = serializerSettings; } @@ -64,14 +71,12 @@ public Scope Scope set; } - private JsonSerializerSettings serializerSettings; - private readonly CloudTable _table; + private readonly JsonSerializerSettings serializerSettings; + public CloudTable Table { - get - { - return _table; - } + get; + private set; } public async Task CreateAsync(string itemId, T item, bool orReplace = true) @@ -82,9 +87,9 @@ public async Task CreateAsync(string itemId, T item, bool orReplace = true var entity = new DynamicTableEntity(Escape(Scope), Escape(itemId)) { Properties = - { - new KeyValuePair("data",new EntityProperty(callbackStr)) - } + { + new KeyValuePair("data", new EntityProperty(callbackStr)) + } }; await Table.ExecuteAsync(orReplace ? TableOperation.InsertOrReplace(entity) : TableOperation.Insert(entity)).ConfigureAwait(false); } @@ -118,7 +123,7 @@ public void Delete() } } - void Ignore(int errorCode, Action act) + private void Ignore(int errorCode, Action act) { try { @@ -130,6 +135,7 @@ void Ignore(int errorCode, Action act) throw; } } + public bool Delete(string itemId, bool includeChildren = false) { try @@ -140,8 +146,8 @@ public bool Delete(string itemId, bool includeChildren = false) })); if (includeChildren) { - var children = Scope.GetChild(itemId); - foreach (var child in Table.ExecuteQuery(AllInScope(children))) + var childScope = Scope.GetChild(itemId); // 'itemId' can be a path. + foreach (var child in Table.ExecuteQuery(AllInScope(childScope))) { Ignore(404, () => Table.Execute(TableOperation.Delete(child))); } @@ -156,11 +162,11 @@ public bool Delete(string itemId, bool includeChildren = false) return true; } - private static TableQuery AllInScope(QBitNinja.Scope children) + private static TableQuery AllInScope(QBitNinja.Scope scope) { return new TableQuery() { - FilterString = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, Escape(children)) + FilterString = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, Escape(scope)) }; } @@ -185,8 +191,6 @@ private static string Escape(string key) return result; } - - public async Task ReadOneAsync(string item) { var e = (await Table.ExecuteAsync(TableOperation.Retrieve(Escape(Scope), Escape(item))).ConfigureAwait(false)).Result as DynamicTableEntity; @@ -195,12 +199,10 @@ public async Task ReadOneAsync(string item) return JsonConvert.DeserializeObject(e.Properties["data"].StringValue, serializerSettings); } - public T ReadOne(string item) { try { - return ReadOneAsync(item).Result; } catch (AggregateException aex) @@ -210,6 +212,9 @@ public T ReadOne(string item) } } + /// + /// Get a CrudTable that is scoped to some nested path. + /// public CrudTable GetChild(params string[] children) { return new CrudTable(Table, serializerSettings) diff --git a/QBitNinja/DefaultDataDirectory.cs b/QBitNinja/DefaultDataDirectory.cs index dcd37ee..5d0e984 100644 --- a/QBitNinja/DefaultDataDirectory.cs +++ b/QBitNinja/DefaultDataDirectory.cs @@ -5,13 +5,19 @@ namespace QBitNinja { - public class DefaultDataDirectory + public static class DefaultDataDirectory { + /// + /// Get or create a directory rooted in a location that is specified as an environment variable + /// named either 'HOME', or 'APPDATA' (fallback). + /// public static string GetDirectory(string appDirectory, string subDirectory, bool createIfNotExists = true) { string directory = null; + + // Find path to 'appDirectory'. var home = Environment.GetEnvironmentVariable("HOME"); - if(!string.IsNullOrEmpty(home)) + if (!string.IsNullOrEmpty(home)) { directory = home; directory = Path.Combine(directory, "." + appDirectory.ToLowerInvariant()); @@ -19,25 +25,30 @@ public static string GetDirectory(string appDirectory, string subDirectory, bool else { var localAppData = Environment.GetEnvironmentVariable("APPDATA"); - if(!string.IsNullOrEmpty(localAppData)) + if (!string.IsNullOrEmpty(localAppData)) { directory = localAppData; directory = Path.Combine(directory, appDirectory); } else { - throw new DirectoryNotFoundException("Could not find suitable datadir"); + throw new DirectoryNotFoundException("Could not find suitable datadir."); } } - if(!Directory.Exists(directory)) + + + if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } + + // Find path to 'subdirectory' and create it if needed. directory = Path.Combine(directory, subDirectory); - if(createIfNotExists && !Directory.Exists(directory)) + if (createIfNotExists && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } + return directory; } } diff --git a/QBitNinja/Extensions.cs b/QBitNinja/Extensions.cs index 9b9ee6b..d0b3995 100644 --- a/QBitNinja/Extensions.cs +++ b/QBitNinja/Extensions.cs @@ -26,10 +26,15 @@ public static WalletRuleEntry CreateWalletRuleEntry(this WalletAddress address, }); } + + /// + /// Get an instance of QBitNinjaConfiguration as provided by the HttpConfiguration's dependency resolver. + /// public static QBitNinjaConfiguration GetConfiguration(this HttpRequestContext ctx) { return ((QBitNinjaDependencyResolver)ctx.Configuration.DependencyResolver).Get(); } + public static T MinElement(this IEnumerable input, Func predicate) { int min = int.MaxValue; diff --git a/QBitNinja/InitialIndexer.cs b/QBitNinja/InitialIndexer.cs index cf9ed85..5e0154d 100644 --- a/QBitNinja/InitialIndexer.cs +++ b/QBitNinja/InitialIndexer.cs @@ -43,7 +43,6 @@ public override string ToString() } } - public class InitialIndexer { diff --git a/QBitNinja/ListenerTrace.cs b/QBitNinja/ListenerTrace.cs index a931a42..2f2d06f 100644 --- a/QBitNinja/ListenerTrace.cs +++ b/QBitNinja/ListenerTrace.cs @@ -8,13 +8,18 @@ namespace QBitNinja { + /// + /// Wrapper around a System.Diagnostics.TraceSource instance. + /// public static class ListenerTrace { static TraceSource _Source = new TraceSource("QBitNinja.Listener"); + public static void Error(string info, Exception ex) { _Source.TraceEvent(TraceEventType.Error, 0, info + "\r\n" + Utils.ExceptionToString(ex)); } + public static void Info(string info) { _Source.TraceInformation(info); diff --git a/QBitNinja/Models/WhatIsItModels.cs b/QBitNinja/Models/WhatIsItModels.cs index 37783be..21d65f4 100644 --- a/QBitNinja/Models/WhatIsItModels.cs +++ b/QBitNinja/Models/WhatIsItModels.cs @@ -266,12 +266,14 @@ public WhatIsExtPubKey(BitcoinExtPubKey data) : base(data) public string PubKey { - get; set; + get; + set; } public bool Hardened { - get; set; + get; + set; } public string ChainCode { @@ -305,11 +307,13 @@ public WhatIsExtKey(BitcoinExtKey data) : base(data) public string ExtPubKey { - get; set; + get; + set; } public string PrivateKey { - get; set; + get; + set; } } @@ -362,6 +366,7 @@ public string Hash get; set; } + public string ColoredAddress { get; @@ -373,11 +378,13 @@ public WhatIsScript ScriptPubKey get; set; } + public WhatIsScript RedeemScript { get; set; } + public WhatIsPublicKey PublicKey { get; @@ -390,6 +397,7 @@ public WhatIsBase58() { } + public WhatIsBase58(IBitcoinString data) { Data = data.ToString(); @@ -418,15 +426,19 @@ public string Data get; set; } + public string Type { get; set; } + public string StringType { - get; set; + get; + set; } + public Network Network { get; diff --git a/QBitNinja/ReaderWriterLock.cs b/QBitNinja/ReaderWriterLock.cs index f502c4e..dd862a9 100644 --- a/QBitNinja/ReaderWriterLock.cs +++ b/QBitNinja/ReaderWriterLock.cs @@ -7,6 +7,10 @@ namespace QBitNinja { + /// + /// Wrapper around ReaderWriterLockSlim, offering a scoped-behavior pattern where the lock + /// is exited at the end of a using statement block. + /// internal class ReaderWriterLock { ReaderWriterLockSlim @lock = new ReaderWriterLockSlim(); diff --git a/QBitNinja/Scope.cs b/QBitNinja/Scope.cs index 5609ffa..1c06fef 100644 --- a/QBitNinja/Scope.cs +++ b/QBitNinja/Scope.cs @@ -3,12 +3,24 @@ namespace QBitNinja { + /// + /// Represents a scope for some part of a hierarchy, where each scope is represented by a number of names that make up a path + /// within that hierarchy. A scope covers all children in that path. + /// public class Scope { + /// + /// Create a scope for an empty path. + /// public Scope() { - _parents = new string[0]; + Parents = new string[0]; } + + /// + /// Create a scope for a given path. + /// + /// A path represented by a number of names, the name at the first index being the root. public Scope(string[] parents) { if (parents == null) @@ -17,28 +29,37 @@ public Scope(string[] parents) { throw new ArgumentException("'/' is not authorized for naming a scope"); } - _parents = parents; + Parents = parents; } - private readonly string[] _parents; + /// + /// A path represented by a number of names, the name at the first index being the root. + /// public string[] Parents { - get - { - return _parents; - } + get; + private set; } + /// + /// Get a child scope, which will be represented by the path of the current scope, extended with + /// the given subpath. + /// + /// An array of names that make up a path rooted in the current scope. + /// public Scope GetChild(params string[] names) { - var parents = _parents.ToList(); + var parents = Parents.ToList(); parents.AddRange(names); return new Scope(parents.ToArray()); } + /// + /// Join the parts of the path with forward slashes to get a string representation of the scope. + /// public override string ToString() { - return String.Join("/", _parents); + return String.Join("/", Parents); } } } diff --git a/QBitNinja/Serializer.cs b/QBitNinja/Serializer.cs index 72762fd..80d1165 100644 --- a/QBitNinja/Serializer.cs +++ b/QBitNinja/Serializer.cs @@ -18,7 +18,10 @@ namespace QBitNinja namespace QBitNinja.Client #endif { - public class Serializer + /// + /// A JSON serializer that also has NBitcoin's front converters registered. + /// + public static class Serializer { #if !NOJSONNET public @@ -37,6 +40,7 @@ static void RegisterFrontConverters(JsonSerializerSettings settings, Network net #endif settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } + public static T ToObject(string data, Network network) { JsonSerializerSettings settings = new JsonSerializerSettings @@ -57,6 +61,7 @@ public static string ToString(T response, Network network) return JsonConvert.SerializeObject(response, settings); } #if !CLIENT + public static MediaTypeFormatter JsonMediaTypeFormatter(Network network) { if (network == null) diff --git a/QBitNinja/WalletRepository.cs b/QBitNinja/WalletRepository.cs index 94369d7..e5f58ff 100644 --- a/QBitNinja/WalletRepository.cs +++ b/QBitNinja/WalletRepository.cs @@ -25,6 +25,7 @@ public WalletRepository(IndexerClient indexer, throw new ArgumentNullException("tableFactory"); if(getBalancesCacheTable == null) throw new ArgumentNullException("getBalancesCacheTable"); + GetBalancesCacheTable = getBalancesCacheTable; _walletAddressesTable = tableFactory.GetTable("wa"); _walletTable = tableFactory.GetTable("wm"); @@ -34,9 +35,8 @@ public WalletRepository(IndexerClient indexer, _indexer = indexer; } - Func> GetBalancesCacheTable; + readonly Func> GetBalancesCacheTable; - public Scope Scope { get; @@ -44,49 +44,19 @@ public Scope Scope } private readonly CrudTable _walletAddressesTable; - public CrudTable WalletAddressesTable - { - get - { - return _walletAddressesTable; - } - } + public CrudTable WalletAddressesTable =>_walletAddressesTable; private readonly CrudTable _keyDataTable; - public CrudTable KeyDataTable - { - get - { - return _keyDataTable; - } - } + public CrudTable KeyDataTable =>_keyDataTable; private readonly CrudTable _keySetTable; - public CrudTable KeySetTable - { - get - { - return _keySetTable; - } - } + public CrudTable KeySetTable => _keySetTable; private readonly CrudTable _walletTable; - public CrudTable WalletTable - { - get - { - return _walletTable; - } - } + public CrudTable WalletTable =>_walletTable; private readonly IndexerClient _indexer; - public IndexerClient Indexer - { - get - { - return _indexer; - } - } + public IndexerClient Indexer => _indexer; public bool Create(WalletModel wallet) { @@ -153,6 +123,7 @@ public async Task> Scan(string walletName, KeySetData keyset SetKeySet(walletName, keysetData); return addedAddresses; } + private static void HackToPreventOOM(WalletAddress[] addresses, Network network) { var addr = addresses.FirstOrDefault(); @@ -175,6 +146,7 @@ public bool AddKeySet(string walletName, KeySetData keysetData) { return KeySetTable.GetChild(walletName).Create(keysetData.KeySet.Name, keysetData, false); } + public bool SetKeySet(string walletName, KeySetData keysetData) { return KeySetTable.GetChild(walletName).Create(keysetData.KeySet.Name, keysetData, true); @@ -199,13 +171,7 @@ private static string Encode(Script script) return Encoders.Hex.EncodeData(script.ToBytes(true)); } - public Network Network - { - get - { - return Indexer.Configuration.Network; - } - } + public Network Network => Indexer.Configuration.Network; private static KeyPath Inc(KeyPath keyPath) { @@ -223,6 +189,7 @@ internal HDKeyData[] GetKeys(string walletName, string keysetName) { return KeyDataTable.GetChild(walletName, keysetName).Read(); } + public async Task<(bool Added, bool Empty)> AddWalletAddress(WalletAddress address, bool mergePast) { var empty = true; @@ -248,7 +215,6 @@ internal HDKeyData[] GetKeys(string walletName, string keysetName) return (true, empty); } - public ChainTable GetBalanceSummaryCacheTable(string walletName, bool colored) { var balanceId = new BalanceId(walletName); diff --git a/QBitNinja/WhatIsIt.cs b/QBitNinja/WhatIsIt.cs index afcac4b..1f9427b 100644 --- a/QBitNinja/WhatIsIt.cs +++ b/QBitNinja/WhatIsIt.cs @@ -8,6 +8,9 @@ namespace QBitNinja { + /// + /// Utility class to aid in recognizing a few kinds of objects from some string representations. + /// public class WhatIsIt { public WhatIsIt(MainController controller) @@ -18,42 +21,39 @@ public WhatIsIt(MainController controller) public MainController Controller { get; - set; + set; } - public Network Network - { - get - { - return Controller.Network; - } - } + public Network Network => Controller.Network; public ConsensusFactory ConsensusFactory => Network.Consensus.ConsensusFactory; - public QBitNinjaConfiguration Configuration - { - get - { - return Controller.Configuration; - } - } - + public QBitNinjaConfiguration Configuration => Controller.Configuration; + /// + /// Try to interpret the given string in a few ways in order to detect what object it's supposed to represent. + /// + /// The object represented by the input string. This may be a Bitcoin address, a script, a signature, a public key, etc. public async Task Find(string data) { data = data.Trim(); + + + // Is it a Bitcoin address? var b58 = NoException(() => WhatIsBase58.GetFromBitcoinString(data)); + if (b58 != null) { - if (b58 is WhatIsAddress) + if (b58 is WhatIsAddress address) { - var address = (WhatIsAddress)b58; - await TryFetchRedeemOrPubKey(address); + await TryFetchRedeemOrPubKey(address); // Shouldn't the return value here be checked? } + return b58; } + + // Is it a transaction ID? if (data.Length == 0x40) { try @@ -62,45 +62,68 @@ public async Task Find(string data) } catch { + // Well, apparently it's not a transaction ID. } } + + + // Is it a block feature? var b = NoException(() => Controller.JsonBlock(BlockFeature.Parse(data), true, false)); + if (b != null) return b; - if (data.Length == 0x28) //Hash of pubkey or script + + // Is it the hash of a public key (modeled as KeyId in NBitcoin), or is it the hash of a script ID? + if (data.Length == 0x28) // Hash of pubkey or script { TxDestination dest = new KeyId(data); + var address = new WhatIsAddress(dest.GetAddress(Network)); + if (await TryFetchRedeemOrPubKey(address)) return address; dest = new ScriptId(data); address = new WhatIsAddress(dest.GetAddress(Network)); + if (await TryFetchRedeemOrPubKey(address)) return address; } + // Is it a script? var script = NoException(() => GetScriptFromBytes(data)); + if (script != null) return new WhatIsScript(script, Network); + script = NoException(() => GetScriptFromText(data)); + if (script != null) return new WhatIsScript(script, Network); + + // Is it a transaction signature? var sig = NoException(() => new TransactionSignature(Encoders.Hex.DecodeData(data))); + if (sig != null) return new WhatIsTransactionSignature(sig); + + // Is it a hexstring representing the bytes of a public key? var pubkeyBytes = NoException(() => Encoders.Hex.DecodeData(data)); + if (pubkeyBytes != null && PubKey.Check(pubkeyBytes, true)) { var pubKey = NoException(() => new PubKey(data)); + if (pubKey != null) return new WhatIsPublicKey(pubKey, Network); } + + // Is it a blockheader? if (data.Length == 80 * 2) { var blockHeader = NoException(() => @@ -109,9 +132,13 @@ public async Task Find(string data) h.ReadWrite(Encoders.Hex.DecodeData(data), ConsensusFactory); return h; }); + if (blockHeader != null) return new WhatIsBlockHeader(blockHeader); } + + + // No idea what this is. return null; } @@ -137,7 +164,6 @@ private static Script GetScriptFromBytes(string data) return !hasOps ? null : script; } - private async Task TryFetchRedeemOrPubKey(WhatIsAddress address) { if (address.IsP2SH) @@ -145,11 +171,13 @@ private async Task TryFetchRedeemOrPubKey(WhatIsAddress address) address.RedeemScript = await TryFetchRedeem(address); return address.RedeemScript != null; } - address.PublicKey = await TryFetchPublicKey(address); - return address.PublicKey != null; + else + { + address.PublicKey = await TryFetchPublicKey(address); + return address.PublicKey != null; + } } - private async Task