public static ISourceBlock <ValidatableTx> ReplayRollbackUtxo(ICoreStorage coreStorage, IChainState chainState, ChainedHeader replayBlock, CancellationToken cancelToken = default(CancellationToken)) { // replaying rollback of an on-chain block, use the chainstate tx index for replay, same as replaying forward if (chainState.Chain.BlocksByHash.ContainsKey(replayBlock.Hash)) { return(ReplayFromTxIndex(coreStorage, chainState, replayBlock, replayForward: false, cancelToken: cancelToken)); } // replaying rollback of an off-chain (re-org) block, use the unminted information for replay else { IImmutableList <UnmintedTx> unmintedTxesList; if (!chainState.TryGetBlockUnmintedTxes(replayBlock.Hash, out unmintedTxesList)) { //TODO if a wallet/monitor were to see a chainstate block that wasn't flushed to disk yet, //TODO and if bitsharp crashed, and if the block was orphaned: then the orphaned block would //TODO not be present in the chainstate, and it would not get rolled back to generate unminted information. //TODO DeferredChainStateCursor should be used in order to re-org the chainstate in memory and calculate the unminted information throw new MissingDataException(replayBlock.Hash); } var unmintedTxes = ImmutableDictionary.CreateRange( unmintedTxesList.Select(x => new KeyValuePair <UInt256, UnmintedTx>(x.TxHash, x))); var lookupLoadingTx = new TransformBlock <DecodedBlockTx, ValidatableTx>( blockTx => { var tx = blockTx.Transaction; var txIndex = blockTx.Index; var prevTxOutputs = ImmutableArray.CreateBuilder <PrevTxOutput>(!blockTx.IsCoinbase ? tx.Inputs.Length : 0); if (!blockTx.IsCoinbase) { UnmintedTx unmintedTx; if (!unmintedTxes.TryGetValue(tx.Hash, out unmintedTx)) { throw new MissingDataException(replayBlock.Hash); } prevTxOutputs.AddRange(unmintedTx.PrevTxOutputs); } return(new ValidatableTx(blockTx, replayBlock, prevTxOutputs.MoveToImmutable())); }); IEnumerator <BlockTx> blockTxes; if (!coreStorage.TryReadBlockTransactions(replayBlock.Hash, out blockTxes)) { throw new MissingDataException(replayBlock.Hash); } var blockTxesBuffer = new BufferBlock <DecodedBlockTx>(); blockTxesBuffer.LinkTo(lookupLoadingTx, new DataflowLinkOptions { PropagateCompletion = true }); blockTxesBuffer.SendAndCompleteAsync(blockTxes.UsingAsEnumerable().Select(x => x.Decode()).Reverse(), cancelToken).Forget(); return(lookupLoadingTx); } }
private static ISourceBlock <ValidatableTx> ReplayFromTxIndex(ICoreStorage coreStorage, IChainState chainState, ChainedHeader replayBlock, bool replayForward, CancellationToken cancelToken = default(CancellationToken)) { //TODO use replayForward to retrieve blocks in reverse order //TODO also check that the block hasn't been pruned (that information isn't stored yet) IEnumerator <BlockTx> blockTxes; if (!coreStorage.TryReadBlockTransactions(replayBlock.Hash, out blockTxes)) { throw new MissingDataException(replayBlock.Hash); } var blockTxesBuffer = new BufferBlock <DecodedBlockTx>(); if (replayForward) { blockTxesBuffer.SendAndCompleteAsync(blockTxes.UsingAsEnumerable().Select(x => x.Decode()), cancelToken).Forget(); } else { blockTxesBuffer.SendAndCompleteAsync(blockTxes.UsingAsEnumerable().Select(x => x.Decode()).Reverse(), cancelToken).Forget(); } // begin looking up txes var lookupValidatableTx = InitLookupValidatableTx(chainState, replayBlock, cancelToken); blockTxesBuffer.LinkTo(lookupValidatableTx, new DataflowLinkOptions { PropagateCompletion = true }); return(lookupValidatableTx); }
public static async Task ValidateBlockAsync(ICoreStorage coreStorage, ICoreRules rules, Chain newChain, ISourceBlock<ValidatableTx> validatableTxes, CancellationToken cancelToken = default(CancellationToken)) { // tally transactions object finalTally = null; var txTallier = new TransformBlock<ValidatableTx, ValidatableTx>( validatableTx => { var runningTally = finalTally; rules.TallyTransaction(newChain, validatableTx, ref runningTally); finalTally = runningTally; return validatableTx; }); validatableTxes.LinkTo(txTallier, new DataflowLinkOptions { PropagateCompletion = true }); // validate transactions var txValidator = InitTxValidator(rules, newChain, cancelToken); // begin feeding the tx validator txTallier.LinkTo(txValidator, new DataflowLinkOptions { PropagateCompletion = true }); // validate scripts var scriptValidator = InitScriptValidator(rules, newChain, cancelToken); // begin feeding the script validator txValidator.LinkTo(scriptValidator, new DataflowLinkOptions { PropagateCompletion = true }); //TODO await PipelineCompletion.Create( new Task[] { }, new IDataflowBlock[] { validatableTxes, txTallier, txValidator, scriptValidator }); // validate overall block rules.PostValidateBlock(newChain, finalTally); }
public UnconfirmedTxesBuilder(ICoreDaemon coreDaemon, ICoreStorage coreStorage, IStorageManager storageManager) { this.coreDaemon = coreDaemon; this.coreStorage = coreStorage; this.storageManager = storageManager; this.chain = new Lazy<Chain>(() => LoadChain()); }
public UnconfirmedTxesBuilder(ICoreDaemon coreDaemon, ICoreStorage coreStorage, IStorageManager storageManager) { this.coreDaemon = coreDaemon; this.coreStorage = coreStorage; this.storageManager = storageManager; this.chain = new Lazy <Chain>(() => LoadChain()); }
public static ISourceBlock<ValidatableTx> ReplayRollbackUtxo(ICoreStorage coreStorage, IChainState chainState, ChainedHeader replayBlock, CancellationToken cancelToken = default(CancellationToken)) { // replaying rollback of an on-chain block, use the chainstate tx index for replay, same as replaying forward if (chainState.Chain.BlocksByHash.ContainsKey(replayBlock.Hash)) { return ReplayFromTxIndex(coreStorage, chainState, replayBlock, replayForward: false, cancelToken: cancelToken); } // replaying rollback of an off-chain (re-org) block, use the unminted information for replay else { IImmutableList<UnmintedTx> unmintedTxesList; if (!chainState.TryGetBlockUnmintedTxes(replayBlock.Hash, out unmintedTxesList)) { //TODO if a wallet/monitor were to see a chainstate block that wasn't flushed to disk yet, //TODO and if bitsharp crashed, and if the block was orphaned: then the orphaned block would //TODO not be present in the chainstate, and it would not get rolled back to generate unminted information. //TODO DeferredChainStateCursor should be used in order to re-org the chainstate in memory and calculate the unminted information throw new MissingDataException(replayBlock.Hash); } var unmintedTxes = ImmutableDictionary.CreateRange( unmintedTxesList.Select(x => new KeyValuePair<UInt256, UnmintedTx>(x.TxHash, x))); var lookupLoadingTx = new TransformBlock<DecodedBlockTx, ValidatableTx>( blockTx => { var tx = blockTx.Transaction; var txIndex = blockTx.Index; var prevTxOutputs = ImmutableArray.CreateBuilder<PrevTxOutput>(!blockTx.IsCoinbase ? tx.Inputs.Length : 0); if (!blockTx.IsCoinbase) { UnmintedTx unmintedTx; if (!unmintedTxes.TryGetValue(tx.Hash, out unmintedTx)) throw new MissingDataException(replayBlock.Hash); prevTxOutputs.AddRange(unmintedTx.PrevTxOutputs); } return new ValidatableTx(blockTx, replayBlock, prevTxOutputs.MoveToImmutable()); }); IEnumerator<BlockTx> blockTxes; if (!coreStorage.TryReadBlockTransactions(replayBlock.Hash, out blockTxes)) { throw new MissingDataException(replayBlock.Hash); } var blockTxesBuffer = new BufferBlock<DecodedBlockTx>(); blockTxesBuffer.LinkTo(lookupLoadingTx, new DataflowLinkOptions { PropagateCompletion = true }); blockTxesBuffer.SendAndCompleteAsync(blockTxes.UsingAsEnumerable().Select(x => x.Decode()).Reverse(), cancelToken).Forget(); return lookupLoadingTx; } }
public ChainStateBuilder(ICoreRules rules, ICoreStorage coreStorage, IStorageManager storageManager) { this.rules = rules; this.coreStorage = coreStorage; this.storageManager = storageManager; this.chain = new Lazy <Chain>(() => LoadChain()); this.stats = new ChainStateBuilderStats(); this.utxoBuilder = new UtxoBuilder(); }
public static ISourceBlock<ValidatableTx> ReplayBlock(ICoreStorage coreStorage, IChainState chainState, UInt256 blockHash, bool replayForward, CancellationToken cancelToken = default(CancellationToken)) { ChainedHeader replayBlock; if (!coreStorage.TryGetChainedHeader(blockHash, out replayBlock)) throw new MissingDataException(blockHash); // replay the validatable txes for this block, in reverse order for a rollback ISourceBlock<ValidatableTx> validatableTxes; if (replayForward) validatableTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage, chainState, replayBlock, cancelToken); else validatableTxes = UtxoReplayer.ReplayRollbackUtxo(coreStorage, chainState, replayBlock, cancelToken); return validatableTxes; }
public static ISourceBlock<LoadedTx> LoadTxes(ICoreStorage coreStorage, ISourceBlock<LoadingTx> loadingTxes, CancellationToken cancelToken = default(CancellationToken)) { // split incoming LoadingTx by its number of inputs var createTxInputList = InitCreateTxInputList(cancelToken); // link the loading txes to the input splitter loadingTxes.LinkTo(createTxInputList, new DataflowLinkOptions { PropagateCompletion = true }); // load each input, and return and fully loaded txes var loadTxInputAndReturnLoadedTx = InitLoadTxInputAndReturnLoadedTx(coreStorage, cancelToken); // link the input splitter to the input loader createTxInputList.LinkTo(loadTxInputAndReturnLoadedTx, new DataflowLinkOptions { PropagateCompletion = true }); return loadTxInputAndReturnLoadedTx; }
private static LoadedTx LoadTxInput(ICoreStorage coreStorage, ConcurrentDictionary <TxLookupKey, Lazy <BlockTx> > txCache, LoadingTx loadingTx, int inputIndex) { var txIndex = loadingTx.TxIndex; var transaction = loadingTx.Transaction; var chainedHeader = loadingTx.ChainedHeader; // load previous transaction for this input, unless this is a coinbase transaction if (!loadingTx.IsCoinbase) { var prevOutputTxKey = loadingTx.PrevOutputTxKeys[inputIndex]; var input = transaction.Inputs[inputIndex]; var inputPrevTxHash = input.PrevTxOutputKey.TxHash; var inputPrevTx = txCache.GetOrAdd(prevOutputTxKey, new Lazy <BlockTx>(() => { BlockTx tx; if (coreStorage.TryGetTransaction(prevOutputTxKey.BlockHash, prevOutputTxKey.TxIndex, out tx)) { return(tx); } else { throw new MissingDataException(prevOutputTxKey.BlockHash); } })).Value; if (input.PrevTxOutputKey.TxHash != inputPrevTx.Hash) { throw new Exception("TODO"); } if (loadingTx.InputTxes.TryComplete(inputIndex, inputPrevTx.Decode().DecodedTx)) { return(loadingTx.ToLoadedTx()); } else { return(null); } } else { Debug.Assert(inputIndex == -1); return(new LoadedTx(transaction, txIndex, ImmutableArray.Create <DecodedTx>())); } }
private static TransformManyBlock<Tuple<LoadingTx, int>, LoadedTx> InitLoadTxInputAndReturnLoadedTx(ICoreStorage coreStorage, CancellationToken cancelToken) { var txCache = new ConcurrentDictionary<TxLookupKey, Lazy<BlockTx>>(); return new TransformManyBlock<Tuple<LoadingTx, int>, LoadedTx>( tuple => { var loadingTx = tuple.Item1; var inputIndex = tuple.Item2; var loadedTx = LoadTxInput(coreStorage, txCache, loadingTx, inputIndex); if (loadedTx != null) return new[] { loadedTx }; else return new LoadedTx[0]; }, new ExecutionDataflowBlockOptions { CancellationToken = cancelToken, MaxDegreeOfParallelism = Environment.ProcessorCount }); }
private async Task ScanBlock(ICoreStorage coreStorage, IChainState chainState, ChainedHeader scanBlock, bool forward, CancellationToken cancelToken = default(CancellationToken)) { var replayTxes = BlockReplayer.ReplayBlock(coreStorage, chainState, scanBlock.Hash, forward, cancelToken); var txScanner = new ActionBlock <ValidatableTx>( validatableTx => { var tx = validatableTx.Transaction; var txIndex = validatableTx.Index; if (!validatableTx.IsCoinbase) { for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; var prevOutput = validatableTx.PrevTxOutputs[inputIndex]; var prevOutputScriptHash = new UInt256(SHA256Static.ComputeHash(prevOutput.ScriptPublicKey)); var chainPosition = ChainPosition.Fake(); var entryType = forward ? EnumWalletEntryType.Spend : EnumWalletEntryType.UnSpend; ScanForEntry(chainPosition, entryType, (TxOutput)prevOutput, prevOutputScriptHash); } } for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++) { var output = tx.Outputs[outputIndex]; var outputScriptHash = new UInt256(SHA256Static.ComputeHash(output.ScriptPublicKey)); var chainPosition = ChainPosition.Fake(); var entryType = validatableTx.IsCoinbase ? (forward ? EnumWalletEntryType.Mine : EnumWalletEntryType.UnMine) : (forward ? EnumWalletEntryType.Receive : EnumWalletEntryType.UnReceieve); ScanForEntry(chainPosition, entryType, output, outputScriptHash); } }); replayTxes.LinkTo(txScanner, new DataflowLinkOptions { PropagateCompletion = true }); await txScanner.Completion; }
public static async Task ValidateBlockAsync(ICoreStorage coreStorage, ICoreRules rules, Chain newChain, ISourceBlock <ValidatableTx> validatableTxes, CancellationToken cancelToken = default(CancellationToken)) { // tally transactions object finalTally = null; var txTallier = new TransformBlock <ValidatableTx, ValidatableTx>( validatableTx => { var runningTally = finalTally; rules.TallyTransaction(newChain, validatableTx, ref runningTally); finalTally = runningTally; return(validatableTx); }); validatableTxes.LinkTo(txTallier, new DataflowLinkOptions { PropagateCompletion = true }); // validate transactions var txValidator = InitTxValidator(rules, newChain, cancelToken); // begin feeding the tx validator txTallier.LinkTo(txValidator, new DataflowLinkOptions { PropagateCompletion = true }); // validate scripts var scriptValidator = InitScriptValidator(rules, newChain, cancelToken); // begin feeding the script validator txValidator.LinkTo(scriptValidator, new DataflowLinkOptions { PropagateCompletion = true }); //TODO await PipelineCompletion.Create( new Task[] { }, new IDataflowBlock[] { validatableTxes, txTallier, txValidator, scriptValidator }); // validate overall block rules.PostValidateBlock(newChain, finalTally); }
public static ISourceBlock <LoadedTx> LoadTxes(ICoreStorage coreStorage, ISourceBlock <LoadingTx> loadingTxes, CancellationToken cancelToken = default(CancellationToken)) { // split incoming LoadingTx by its number of inputs var createTxInputList = InitCreateTxInputList(cancelToken); // link the loading txes to the input splitter loadingTxes.LinkTo(createTxInputList, new DataflowLinkOptions { PropagateCompletion = true }); // load each input, and return and fully loaded txes var loadTxInputAndReturnLoadedTx = InitLoadTxInputAndReturnLoadedTx(coreStorage, cancelToken); // link the input splitter to the input loader createTxInputList.LinkTo(loadTxInputAndReturnLoadedTx, new DataflowLinkOptions { PropagateCompletion = true }); return(loadTxInputAndReturnLoadedTx); }
public static async Task ValidateBlockAsync(ICoreStorage coreStorage, IBlockchainRules rules, ChainedHeader chainedHeader, ISourceBlock<LoadedTx> loadedTxes, CancellationToken cancelToken = default(CancellationToken)) { // validate merkle root var merkleStream = new MerkleStream(); var merkleValidator = InitMerkleValidator(chainedHeader, merkleStream, cancelToken); // begin feeding the merkle validator loadedTxes.LinkTo(merkleValidator, new DataflowLinkOptions { PropagateCompletion = true }); // validate transactions var txValidator = InitTxValidator(rules, chainedHeader, cancelToken); // begin feeding the tx validator merkleValidator.LinkTo(txValidator, new DataflowLinkOptions { PropagateCompletion = true }); // validate scripts var scriptValidator = InitScriptValidator(rules, chainedHeader, cancelToken); // begin feeding the script validator txValidator.LinkTo(scriptValidator, new DataflowLinkOptions { PropagateCompletion = true }); await merkleValidator.Completion; await txValidator.Completion; await scriptValidator.Completion; if (!rules.BypassPrevTxLoading) { try { merkleStream.FinishPairing(); } //TODO catch (InvalidOperationException) { throw CreateMerkleRootException(chainedHeader); } if (merkleStream.RootNode.Hash != chainedHeader.MerkleRoot) throw CreateMerkleRootException(chainedHeader); } }
public static ISourceBlock <ValidatableTx> ReplayBlock(ICoreStorage coreStorage, IChainState chainState, UInt256 blockHash, bool replayForward, CancellationToken cancelToken = default(CancellationToken)) { ChainedHeader replayBlock; if (!coreStorage.TryGetChainedHeader(blockHash, out replayBlock)) { throw new MissingDataException(blockHash); } // replay the validatable txes for this block, in reverse order for a rollback ISourceBlock <ValidatableTx> validatableTxes; if (replayForward) { validatableTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage, chainState, replayBlock, cancelToken); } else { validatableTxes = UtxoReplayer.ReplayRollbackUtxo(coreStorage, chainState, replayBlock, cancelToken); } return(validatableTxes); }
private static ISourceBlock<ValidatableTx> ReplayFromTxIndex(ICoreStorage coreStorage, IChainState chainState, ChainedHeader replayBlock, bool replayForward, CancellationToken cancelToken = default(CancellationToken)) { //TODO use replayForward to retrieve blocks in reverse order //TODO also check that the block hasn't been pruned (that information isn't stored yet) IEnumerator<BlockTx> blockTxes; if (!coreStorage.TryReadBlockTransactions(replayBlock.Hash, out blockTxes)) { throw new MissingDataException(replayBlock.Hash); } var blockTxesBuffer = new BufferBlock<DecodedBlockTx>(); if (replayForward) blockTxesBuffer.SendAndCompleteAsync(blockTxes.UsingAsEnumerable().Select(x => x.Decode()), cancelToken).Forget(); else blockTxesBuffer.SendAndCompleteAsync(blockTxes.UsingAsEnumerable().Select(x => x.Decode()).Reverse(), cancelToken).Forget(); // begin looking up txes var lookupValidatableTx = InitLookupValidatableTx(chainState, replayBlock, cancelToken); blockTxesBuffer.LinkTo(lookupValidatableTx, new DataflowLinkOptions { PropagateCompletion = true }); return lookupValidatableTx; }
public static ISourceBlock<LoadedTx> ReplayBlock(ICoreStorage coreStorage, IChainState chainState, UInt256 blockHash, bool replayForward, CancellationToken cancelToken = default(CancellationToken)) { ChainedHeader replayBlock; if (!coreStorage.TryGetChainedHeader(blockHash, out replayBlock)) throw new MissingDataException(blockHash); // replay the loading txes for this block, in reverse order for a rollback ISourceBlock<LoadingTx> loadingTxes; if (replayForward) loadingTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage, chainState, replayBlock, cancelToken); else loadingTxes = UtxoReplayer.ReplayRollbackUtxo(coreStorage, chainState, replayBlock, cancelToken); // capture the original loading txes order var orderedLoadingTxes = OrderingBlock.CaptureOrder<LoadingTx, LoadedTx, UInt256>( loadingTxes, loadingTx => loadingTx.Transaction.Hash, cancelToken); // begin loading txes var loadedTxes = TxLoader.LoadTxes(coreStorage, orderedLoadingTxes, cancelToken); // return the loaded txes in original order return orderedLoadingTxes.ApplyOrder(loadedTxes, loadedTx => loadedTx.Transaction.Hash, cancelToken); }
private static LoadedTx LoadTxInput(ICoreStorage coreStorage, ConcurrentDictionary<TxLookupKey, Lazy<BlockTx>> txCache, LoadingTx loadingTx, int inputIndex) { var txIndex = loadingTx.TxIndex; var transaction = loadingTx.Transaction; var chainedHeader = loadingTx.ChainedHeader; // load previous transaction for this input, unless this is a coinbase transaction if (!loadingTx.IsCoinbase) { var prevOutputTxKey = loadingTx.PrevOutputTxKeys[inputIndex]; var input = transaction.Inputs[inputIndex]; var inputPrevTxHash = input.PrevTxOutputKey.TxHash; var inputPrevTx = txCache.GetOrAdd(prevOutputTxKey, new Lazy<BlockTx>(() => { BlockTx tx; if (coreStorage.TryGetTransaction(prevOutputTxKey.BlockHash, prevOutputTxKey.TxIndex, out tx)) return tx; else throw new MissingDataException(prevOutputTxKey.BlockHash); })).Value; if (input.PrevTxOutputKey.TxHash != inputPrevTx.Hash) throw new Exception("TODO"); if (loadingTx.InputTxes.TryComplete(inputIndex, inputPrevTx.Decode().DecodedTx)) return loadingTx.ToLoadedTx(); else return null; } else { Debug.Assert(inputIndex == -1); return new LoadedTx(transaction, txIndex, ImmutableArray.Create<DecodedTx>()); } }
public static ISourceBlock <ValidatableTx> ReplayCalculateUtxo(ICoreStorage coreStorage, IChainState chainState, ChainedHeader replayBlock, CancellationToken cancelToken = default(CancellationToken)) { return(ReplayFromTxIndex(coreStorage, chainState, replayBlock, replayForward: true, cancelToken: cancelToken)); }
public static ISourceBlock<ValidatableTx> ReplayCalculateUtxo(ICoreStorage coreStorage, IChainState chainState, ChainedHeader replayBlock, CancellationToken cancelToken = default(CancellationToken)) { return ReplayFromTxIndex(coreStorage, chainState, replayBlock, replayForward: true, cancelToken: cancelToken); }
private static TransformManyBlock <Tuple <LoadingTx, int>, LoadedTx> InitLoadTxInputAndReturnLoadedTx(ICoreStorage coreStorage, CancellationToken cancelToken) { var txCache = new ConcurrentDictionary <TxLookupKey, Lazy <BlockTx> >(); return(new TransformManyBlock <Tuple <LoadingTx, int>, LoadedTx>( tuple => { var loadingTx = tuple.Item1; var inputIndex = tuple.Item2; var loadedTx = LoadTxInput(coreStorage, txCache, loadingTx, inputIndex); if (loadedTx != null) { return new[] { loadedTx } } ; else { return new LoadedTx[0]; } }, new ExecutionDataflowBlockOptions { CancellationToken = cancelToken, MaxDegreeOfParallelism = Environment.ProcessorCount })); }
private static ISourceBlock<LoadingTx> ReplayFromTxIndex(ICoreStorage coreStorage, IChainState chainState, ChainedHeader replayBlock, bool replayForward, CancellationToken cancelToken = default(CancellationToken)) { //TODO use replayForward to retrieve blocks in reverse order //TODO also check that the block hasn't been pruned (that information isn't stored yet) IEnumerator<BlockTx> blockTxes; if (!coreStorage.TryReadBlockTransactions(replayBlock.Hash, /*requireTransaction:*/true, out blockTxes)) { throw new MissingDataException(replayBlock.Hash); } var blockTxesBuffer = new BufferBlock<BlockTx>(); if (replayForward) blockTxesBuffer.SendAndCompleteAsync(blockTxes.UsingAsEnumerable(), cancelToken).Forget(); else blockTxesBuffer.SendAndCompleteAsync(blockTxes.UsingAsEnumerable().Reverse(), cancelToken).Forget(); // capture the original block txes order var orderedBlockTxes = OrderingBlock.CaptureOrder<BlockTx, LoadingTx, UInt256>( blockTxesBuffer, blockTx => blockTx.Transaction.Hash, cancelToken); // begin looking up txes var lookupLoadingTx = InitLookupLoadingTx(chainState, replayBlock, cancelToken); orderedBlockTxes.LinkTo(lookupLoadingTx, new DataflowLinkOptions { PropagateCompletion = true }); // return the loading txes in original order return orderedBlockTxes.ApplyOrder(lookupLoadingTx, loadingTx => loadingTx.Transaction.Hash, cancelToken); ; }
private async Task ScanBlock(ICoreStorage coreStorage, IChainState chainState, ChainedHeader scanBlock, bool forward, CancellationToken cancelToken = default(CancellationToken)) { var replayTxes = BlockReplayer.ReplayBlock(coreStorage, chainState, scanBlock.Hash, forward, cancelToken); var txScanner = new ActionBlock<ValidatableTx>( validatableTx => { var tx = validatableTx.Transaction; var txIndex = validatableTx.Index; if (!validatableTx.IsCoinbase) { for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; var prevOutput = validatableTx.PrevTxOutputs[inputIndex]; var prevOutputScriptHash = new UInt256(SHA256Static.ComputeHash(prevOutput.ScriptPublicKey)); var chainPosition = ChainPosition.Fake(); var entryType = forward ? EnumWalletEntryType.Spend : EnumWalletEntryType.UnSpend; ScanForEntry(chainPosition, entryType, (TxOutput)prevOutput, prevOutputScriptHash); } } for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++) { var output = tx.Outputs[outputIndex]; var outputScriptHash = new UInt256(SHA256Static.ComputeHash(output.ScriptPublicKey)); var chainPosition = ChainPosition.Fake(); var entryType = validatableTx.IsCoinbase ? (forward ? EnumWalletEntryType.Mine : EnumWalletEntryType.UnMine) : (forward ? EnumWalletEntryType.Receive : EnumWalletEntryType.UnReceieve); ScanForEntry(chainPosition, entryType, output, outputScriptHash); } }); replayTxes.LinkTo(txScanner, new DataflowLinkOptions { PropagateCompletion = true }); await txScanner.Completion; }