private PrevTxOutput Unspend(IChainStateCursor chainStateCursor, TxInput input, ChainedHeader chainedHeader) { UnspentTx unspentTx; if (!chainStateCursor.TryGetUnspentTx(input.PrevTxOutputKey.TxHash, out unspentTx)) { // unable to rollback, the unspent tx has been pruned //TODO better exception throw new InvalidOperationException(); } // retrieve previous output index var outputIndex = unchecked ((int)input.PrevTxOutputKey.TxOutputIndex); if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length) { throw new Exception("TODO - corruption"); } // check that output isn't already considered unspent if (unspentTx.OutputStates[outputIndex] == OutputState.Unspent) { throw new ValidationException(chainedHeader.Hash); } var wasFullySpent = unspentTx.IsFullySpent; // mark output as unspent unspentTx = unspentTx.SetOutputState(outputIndex, OutputState.Unspent); // increment unspent output count chainStateCursor.UnspentOutputCount++; // update storage var wasUpdated = chainStateCursor.TryUpdateUnspentTx(unspentTx); if (!wasUpdated) { throw new ValidationException(chainedHeader.Hash); } // increment unspent tx count if (wasFullySpent) { chainStateCursor.UnspentTxCount++; } TxOutput txOutput; if (!chainStateCursor.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)) { // output missing throw new ValidationException(chainedHeader.Hash); } return(new PrevTxOutput(txOutput, unspentTx)); }
private PrevTxOutput Spend(IChainStateCursor chainStateCursor, int txIndex, Transaction tx, int inputIndex, TxInput input, ChainedHeader chainedHeader, BlockSpentTxesBuilder blockSpentTxes) { UnspentTx unspentTx; if (!chainStateCursor.TryGetUnspentTx(input.PrevTxOutputKey.TxHash, out unspentTx)) { // output wasn't present in utxo, invalid block throw new ValidationException(chainedHeader.Hash); } var outputIndex = unchecked ((int)input.PrevTxOutputKey.TxOutputIndex); if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length) { // output was out of bounds throw new ValidationException(chainedHeader.Hash); } if (unspentTx.OutputStates[outputIndex] == OutputState.Spent) { // output was already spent throw new ValidationException(chainedHeader.Hash); } // update output states unspentTx = unspentTx.SetOutputState(outputIndex, OutputState.Spent); // decrement unspent output count chainStateCursor.UnspentOutputCount--; // update transaction output states in the utxo var wasUpdated = chainStateCursor.TryUpdateUnspentTx(unspentTx); if (!wasUpdated) { throw new ValidationException(chainedHeader.Hash); } // store pruning information for a fully spent transaction if (unspentTx.IsFullySpent) { blockSpentTxes.AddSpentTx(unspentTx.ToSpentTx()); // decrement unspent tx count chainStateCursor.UnspentTxCount--; } TxOutput txOutput; if (!chainStateCursor.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)) { // output missing throw new ValidationException(chainedHeader.Hash); } return(new PrevTxOutput(txOutput, unspentTx)); }
public DeferredChainStateCursor(IChainState chainState, IStorageManager storageManager) { this.chainState = chainState; this.storageManager = storageManager; headers = new DeferredDictionary <UInt256, ChainedHeader>( blockHash => { ChainedHeader header; return(Tuple.Create(chainState.TryGetHeader(blockHash, out header), header)); }); unspentTxes = new WorkQueueDictionary <UInt256, UnspentTx>( txHash => { UnspentTx unspentTx; return(Tuple.Create(chainState.TryGetUnspentTx(txHash, out unspentTx), unspentTx)); }); unspentTxOutputs = new WorkQueueDictionary <TxOutputKey, TxOutput>( txOutputKey => { TxOutput txOutput; return(Tuple.Create(chainState.TryGetUnspentTxOutput(txOutputKey, out txOutput), txOutput)); }); blockSpentTxes = new DeferredDictionary <int, BlockSpentTxes>( blockHeight => { BlockSpentTxes spentTxes; return(Tuple.Create(chainState.TryGetBlockSpentTxes(blockHeight, out spentTxes), spentTxes)); }); blockUnmintedTxes = new DeferredDictionary <UInt256, IImmutableList <UnmintedTx> >( blockHash => { IImmutableList <UnmintedTx> unmintedTxes; return(Tuple.Create(chainState.TryGetBlockUnmintedTxes(blockHash, out unmintedTxes), unmintedTxes)); }); utxoApplier = new ActionBlock <WorkQueueDictionary <UInt256, UnspentTx> .WorkItem>( workItem => { workItem.Consume( (operation, unspentTxHash, unspentTx) => { lock (parentCursor) switch (operation) { case WorkQueueOperation.Nothing: break; case WorkQueueOperation.Add: if (!parentCursor.TryAddUnspentTx(unspentTx)) { throw new InvalidOperationException(); } break; case WorkQueueOperation.Update: if (!parentCursor.TryUpdateUnspentTx(unspentTx)) { throw new InvalidOperationException(); } break; case WorkQueueOperation.Remove: if (!parentCursor.TryRemoveUnspentTx(unspentTxHash)) { throw new InvalidOperationException(); } break; default: throw new InvalidOperationException(); } }); }); unspentTxes.WorkQueue.LinkTo(utxoApplier, new DataflowLinkOptions { PropagateCompletion = true }); utxoApplier2 = new ActionBlock <WorkQueueDictionary <TxOutputKey, TxOutput> .WorkItem>( workItem => { workItem.Consume( (operation, txOutputKey, txOutput) => { lock (parentCursor) switch (operation) { case WorkQueueOperation.Nothing: break; case WorkQueueOperation.Add: if (!parentCursor.TryAddUnspentTxOutput(txOutputKey, txOutput)) { throw new InvalidOperationException(); } break; case WorkQueueOperation.Update: throw new InvalidOperationException(); case WorkQueueOperation.Remove: if (!parentCursor.TryRemoveUnspentTxOutput(txOutputKey)) { throw new InvalidOperationException(); } break; default: throw new InvalidOperationException(); } }); }); unspentTxOutputs.WorkQueue.LinkTo(utxoApplier2, new DataflowLinkOptions { PropagateCompletion = true }); }
public void BeginTransaction(bool readOnly, bool pruneOnly) { if (inTransaction) throw new InvalidOperationException(); parentHandle = storageManager.OpenChainStateCursor(); parentCursor = parentHandle.Item; changesApplied = false; try { parentCursor.BeginTransaction(); this.ChainTip = parentCursor.ChainTip; this.UnspentTxCount = UnspentTxCount; this.UnspentOutputCount = UnspentOutputCount; this.TotalTxCount = TotalTxCount; this.TotalInputCount = TotalInputCount; this.TotalOutputCount = TotalOutputCount; utxoApplier = new ActionBlock<WorkQueueDictionary<UInt256, UnspentTx>.WorkItem>( workItem => { workItem.Consume( (operation, unspentTxHash, unspentTx) => { switch (operation) { case WorkQueueOperation.Nothing: break; case WorkQueueOperation.Add: if (!parentCursor.TryAddUnspentTx(unspentTx)) throw new InvalidOperationException(); break; case WorkQueueOperation.Update: if (!parentCursor.TryUpdateUnspentTx(unspentTx)) throw new InvalidOperationException(); break; case WorkQueueOperation.Remove: if (!parentCursor.TryRemoveUnspentTx(unspentTxHash)) throw new InvalidOperationException(); break; default: throw new InvalidOperationException(); } }); }); unspentTxes.WorkQueue.LinkTo(utxoApplier, new DataflowLinkOptions { PropagateCompletion = true }); inTransaction = true; } finally { if (!inTransaction) { parentHandle.Dispose(); parentHandle = null; parentCursor = null; utxoApplier = null; } } }
private PrevTxOutput Unspend(IChainStateCursor chainStateCursor, TxInput input, ChainedHeader chainedHeader) { UnspentTx unspentTx; if (!chainStateCursor.TryGetUnspentTx(input.PrevTxOutputKey.TxHash, out unspentTx)) { // unable to rollback, the unspent tx has been pruned //TODO better exception throw new InvalidOperationException(); } // retrieve previous output index var outputIndex = unchecked((int)input.PrevTxOutputKey.TxOutputIndex); if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length) throw new Exception("TODO - corruption"); // check that output isn't already considered unspent if (unspentTx.OutputStates[outputIndex] == OutputState.Unspent) throw new ValidationException(chainedHeader.Hash); var wasFullySpent = unspentTx.IsFullySpent; // mark output as unspent unspentTx = unspentTx.SetOutputState(outputIndex, OutputState.Unspent); // increment unspent output count chainStateCursor.UnspentOutputCount++; // update storage var wasUpdated = chainStateCursor.TryUpdateUnspentTx(unspentTx); if (!wasUpdated) throw new ValidationException(chainedHeader.Hash); // increment unspent tx count if (wasFullySpent) chainStateCursor.UnspentTxCount++; TxOutput txOutput; if (!chainStateCursor.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)) // output missing throw new ValidationException(chainedHeader.Hash); return new PrevTxOutput(txOutput, unspentTx); }
private PrevTxOutput Spend(IChainStateCursor chainStateCursor, int txIndex, Transaction tx, int inputIndex, TxInput input, ChainedHeader chainedHeader, BlockSpentTxesBuilder blockSpentTxes) { UnspentTx unspentTx; if (!chainStateCursor.TryGetUnspentTx(input.PrevTxOutputKey.TxHash, out unspentTx)) { // output wasn't present in utxo, invalid block throw new ValidationException(chainedHeader.Hash); } var outputIndex = unchecked((int)input.PrevTxOutputKey.TxOutputIndex); if (outputIndex < 0 || outputIndex >= unspentTx.OutputStates.Length) { // output was out of bounds throw new ValidationException(chainedHeader.Hash); } if (unspentTx.OutputStates[outputIndex] == OutputState.Spent) { // output was already spent throw new ValidationException(chainedHeader.Hash); } // update output states unspentTx = unspentTx.SetOutputState(outputIndex, OutputState.Spent); // decrement unspent output count chainStateCursor.UnspentOutputCount--; // update transaction output states in the utxo var wasUpdated = chainStateCursor.TryUpdateUnspentTx(unspentTx); if (!wasUpdated) throw new ValidationException(chainedHeader.Hash); // store pruning information for a fully spent transaction if (unspentTx.IsFullySpent) { blockSpentTxes.AddSpentTx(unspentTx.ToSpentTx()); // decrement unspent tx count chainStateCursor.UnspentTxCount--; } TxOutput txOutput; if (!chainStateCursor.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)) // output missing throw new ValidationException(chainedHeader.Hash); return new PrevTxOutput(txOutput, unspentTx); }