private IEnumerable <SlimChainedBlock> EnumerateToTip(BlockLocator fork, SlimChain chain) { var bh = chain.FindFork(fork); if (bh is null) { throw new InvalidOperationException("No fork found with the chain"); } int height = bh.Height + 1; var prev = bh.Hash; while (true) { bh = chain.GetBlock(height); if (bh is null) { yield break; } if (bh.Previous != prev) { yield break; } yield return(bh); height = bh.Height + 1; prev = bh.Hash; } }
private async Task UpdateRepository(RPCClient client, DerivationSchemeTrackedSource trackedSource, Repository repo, SlimChain chain, ScanTxoutOutput[] outputs, ScannedItems scannedItems, ScanUTXOProgress progressObj) { var data = outputs .GroupBy(o => o.Coin.Outpoint.Hash) .Select(o => (Coins: o.Select(c => c.Coin).ToList(), BlockId: chain.GetBlock(o.First().Height)?.Hash, TxId: o.Select(c => c.Coin.Outpoint.Hash).FirstOrDefault(), KeyPathInformations: o.Select(c => scannedItems.KeyPathInformations[c.Coin.ScriptPubKey]).ToList())) .Where(o => o.BlockId != null) .Select(o => { foreach (var keyInfo in o.KeyPathInformations) { var index = keyInfo.KeyPath.Indexes.Last(); var highest = progressObj.HighestKeyIndexFound[keyInfo.Feature]; if (highest == null || index > highest.Value) { progressObj.HighestKeyIndexFound[keyInfo.Feature] = (int)index; } } return(o); }).ToList(); var headers = new ConcurrentDictionary <uint256, BlockHeader>(); var clientBatch = client.PrepareBatch(); var gettingBlockHeaders = Task.WhenAll(data.Select(async o => { headers.TryAdd(o.BlockId, await clientBatch.GetBlockHeaderAsync(o.BlockId)); }).Concat(new[] { clientBatch.SendBatchAsync() }).ToArray()); await repo.SaveKeyInformations(scannedItems. KeyPathInformations. Select(p => p.Value). Where(p => { var highest = progressObj.HighestKeyIndexFound[p.Feature]; if (highest == null) { return(false); } return(p.KeyPath.Indexes.Last() <= highest.Value); }).ToArray()); await repo.UpdateAddressPool(trackedSource, progressObj.HighestKeyIndexFound); await gettingBlockHeaders; DateTimeOffset now = DateTimeOffset.UtcNow; await repo.SaveMatches(data.Select(o => new TrackedTransaction(new TrackedTransactionKey(o.TxId, o.BlockId, true), trackedSource, o.Coins, o.KeyPathInformations) { Inserted = now, FirstSeen = headers.TryGetValue(o.BlockId, out var header) && header != null ? header.BlockTime : NBitcoin.Utils.UnixTimeToDateTime(0) }).ToArray());
public AnnotatedTransaction(TrackedTransaction tracked, SlimChain chain) { Record = tracked; if (tracked.BlockHash == null) { Type = AnnotatedTransactionType.Unconfirmed; } else { var block = chain.GetBlock(tracked.BlockHash); Type = block == null ? AnnotatedTransactionType.Orphan : AnnotatedTransactionType.Confirmed; Height = block?.Height; } }
public void CanCreateBigSlimChain() { var main = new ConcurrentChain(LoadMainChain(), Network.Main); var c = new SlimChain(main.GetBlock(0).HashBlock); foreach (var item in main.EnumerateToTip(main.GetBlock(0).HashBlock)) { c.TrySetTip(item.HashBlock, item.Previous?.HashBlock); } Assert.Equal(main.Height, c.Height); Assert.Equal(main.Tip.HashBlock, c.Tip); // Can up the capacity without errors c.SetCapacity(main.Height + 3000); Assert.Equal(main.Height, c.Height); Assert.Equal(main.Tip.HashBlock, c.Tip); Assert.Equal(main.GetBlock(main.Tip.HashBlock).HashBlock, c.GetBlock(c.Tip).Hash); }
public AnnotatedTransaction(TrackedTransaction tracked, SlimChain chain) { if (tracked == null) { throw new ArgumentNullException(nameof(tracked)); } Record = tracked; if (tracked.BlockHash == null) { Type = AnnotatedTransactionType.Unconfirmed; } else { var block = chain?.GetBlock(tracked.BlockHash); Type = block == null ? AnnotatedTransactionType.Orphan : AnnotatedTransactionType.Confirmed; Height = block?.Height; } }
public static TransactionResult ToTransactionResult(SlimChain chain, Repository.SavedTransaction[] result) { var noDate = NBitcoin.Utils.UnixTimeToDateTime(0); var oldest = result .Where(o => o.Timestamp != noDate) .OrderBy(o => o.Timestamp).FirstOrDefault() ?? result.First(); var confBlock = result .Where(r => r.BlockHash != null) .Select(r => chain.GetBlock(r.BlockHash)) .Where(r => r != null) .FirstOrDefault(); var conf = confBlock == null ? 0 : chain.Height - confBlock.Height + 1; return(new TransactionResult() { Confirmations = conf, BlockId = confBlock?.Hash, Transaction = oldest.Transaction, TransactionHash = oldest.Transaction.GetHash(), Height = confBlock?.Height, Timestamp = oldest.Timestamp }); }
private async Task UpdateRepository(DerivationSchemeTrackedSource trackedSource, Repository repo, SlimChain chain, ScanTxoutOutput[] outputs, ScannedItems scannedItems, ScanUTXOProgress progressObj) { var data = outputs .GroupBy(o => o.Coin.Outpoint.Hash) .Select(o => (Coins: o.Select(c => c.Coin).ToList(), BlockId: chain.GetBlock(o.First().Height)?.Hash, TxId: o.Select(c => c.Coin.Outpoint.Hash).FirstOrDefault(), KeyPathInformations: o.Select(c => scannedItems.KeyPathInformations[c.Coin.ScriptPubKey]).ToList())) .Where(o => o.BlockId != null) .Select(o => { foreach (var keyInfo in o.KeyPathInformations) { var index = keyInfo.KeyPath.Indexes.Last(); var highest = progressObj.HighestKeyIndexFound[keyInfo.Feature]; if (highest == null || index > highest.Value) { progressObj.HighestKeyIndexFound[keyInfo.Feature] = (int)index; } } return(o); }).ToList(); await repo.SaveKeyInformations(scannedItems. KeyPathInformations. Select(p => p.Value). Where(p => { var highest = progressObj.HighestKeyIndexFound[p.Feature]; if (highest == null) { return(false); } return(p.KeyPath.Indexes.Last() <= highest.Value); }).ToArray()); await repo.UpdateAddressPool(trackedSource, progressObj.HighestKeyIndexFound); DateTimeOffset now = DateTimeOffset.UtcNow; await repo.SaveMatches(data.Select(o => new TrackedTransaction(new TrackedTransactionKey(o.TxId, o.BlockId, true), trackedSource, o.Coins, o.KeyPathInformations)).ToArray()); }
public void CanBuildSlimChain() { var b0 = RandomUInt256(); SlimChain chain = new SlimChain(b0); var b1 = RandomUInt256(); Assert.Throws <ArgumentException>(() => chain.TrySetTip(b0, b0)); Assert.True(chain.TrySetTip(b1, b0)); var b2 = RandomUInt256(); Assert.True(chain.TrySetTip(b2, b1)); Assert.True(chain.TrySetTip(b2, b1)); Assert.Equal(b0, chain.Genesis); Assert.Equal(b2, chain.Tip); Assert.True(chain.Contains(b2)); Assert.Equal(2, chain.Height); Assert.False(chain.TrySetTip(b1, b0, true)); Assert.True(chain.TrySetTip(b1, b0, false)); Assert.Equal(b1, chain.Tip); Assert.False(chain.TryGetHeight(b2, out int height)); Assert.False(chain.Contains(b2)); Assert.True(chain.TryGetHeight(b1, out height)); Assert.Equal(1, height); Assert.True(chain.TrySetTip(b2, b1)); Assert.Throws <ArgumentException>(() => chain.TrySetTip(b1, b2)); // Incoherent Assert.Throws <ArgumentException>(() => chain.TrySetTip(b0, b1, true)); // Genesis block should not have previosu Assert.Throws <ArgumentException>(() => chain.TrySetTip(b0, b1, false)); Assert.True(chain.TrySetTip(b0, null)); Assert.Equal(0, chain.Height); Assert.True(chain.TrySetTip(b1, b0, true)); Assert.True(chain.TrySetTip(b2, b1)); var b3 = RandomUInt256(); var block = chain.GetBlock(b2); Assert.Equal(b2, block.Hash); Assert.Equal(b1, block.Previous); Assert.Equal(2, block.Height); Assert.Null(chain.GetBlock(b3)); block = chain.GetBlock(2); Assert.Equal(b2, block.Hash); Assert.Equal(b1, block.Previous); Assert.Equal(2, block.Height); Assert.Null(chain.GetBlock(3)); Assert.Null(chain.GetBlock(-1)); block = chain.GetBlock(0); Assert.Equal(b0, block.Hash); Assert.Null(block.Previous); Assert.Equal(0, block.Height); var chain2 = new SlimChain(RandomUInt256()); var ms = new MemoryStream(); chain.Save(ms); ms.Position = 0; // Not good genesis Assert.Throws <InvalidOperationException>(() => chain2.Load(ms)); chain2 = new SlimChain(b0); ms.Position = 0; chain2.Load(ms); Assert.Equal(chain.Tip, chain2.Tip); Assert.Equal(2, chain2.Height); }