public override void ValidationTransactionScript(ChainedHeader chainedHeader, Transaction tx, int txIndex, TxInput txInput, int txInputIndex, TxOutput prevTxOutput) { if (ValidationTransactionScriptAction == null) base.ValidationTransactionScript(chainedHeader, tx, txIndex, txInput, txInputIndex, prevTxOutput); else ValidationTransactionScriptAction(chainedHeader, tx, txIndex, txInput, txInputIndex, prevTxOutput); }
public void ValidationTransactionScript(Chain newChain, BlockTx tx, TxInput txInput, int txInputIndex, PrevTxOutput prevTxOutput) { if (ValidationTransactionScriptAction == null) coreRules.ValidationTransactionScript(newChain, tx, txInput, txInputIndex, prevTxOutput); else ValidationTransactionScriptAction(newChain, tx, txInput, txInputIndex, prevTxOutput); }
public void TestReadOneLoadingTx() { var coreStorageMock = new Mock<ICoreStorage>(); // create a fake transaction with 4 inputs var prevTxCount = 4; var txIndex = 1; var chainedHeader = RandomData.RandomChainedHeader(); // create previous transactions for 4 inputs var prevTxes = new Transaction[prevTxCount]; var inputs = new TxInput[prevTxCount]; for (var i = 0; i < prevTxCount; i++) { var prevTx = RandomData.RandomTransaction(); var prevBlockTx = (BlockTx)BlockTx.Create(i, prevTx); prevTxes[i] = prevTx; inputs[i] = new TxInput(prevTx.Hash, 0, ImmutableArray.Create<byte>(), 0); // mock retrieval of the previous transaction coreStorageMock.Setup(coreStorage => coreStorage.TryGetTransaction(UInt256.Zero, i, out prevBlockTx)).Returns(true); } // create a loading tx with the 4 inputs referencing block hash 0 var tx = RandomData.RandomTransaction(new RandomDataOptions { TxOutputCount = 1 }) .CreateWith(Inputs: inputs.ToImmutableArray()).Transaction; var prevOutputTxKeys = ImmutableArray.CreateRange( Enumerable.Range(0, prevTxCount).Select(x => new TxLookupKey(UInt256.Zero, x))); var loadingTx = new LoadingTx(txIndex, tx, chainedHeader, prevOutputTxKeys); // begin queuing transactions to load var loadingTxes = new BufferBlock<LoadingTx>(); loadingTxes.Post(loadingTx); loadingTxes.Complete(); // begin transaction loading var txLoader = TxLoader.LoadTxes(coreStorageMock.Object, loadingTxes); // verify the loaded transaction var loadedTxesBuffer = new BufferBlock<LoadedTx>(); txLoader.LinkTo(loadedTxesBuffer, new DataflowLinkOptions { PropagateCompletion = true }); txLoader.Completion.Wait(); IList<LoadedTx> actualLoadedTxes; Assert.IsTrue(loadedTxesBuffer.TryReceiveAll(out actualLoadedTxes)); var actualLoadedTx = actualLoadedTxes.Single(); Assert.AreEqual(loadingTx.TxIndex, actualLoadedTx.TxIndex); Assert.AreEqual(loadingTx.Transaction, actualLoadedTx.Transaction); CollectionAssert.AreEqual(prevTxes.Select(x => x.Hash).ToArray(), actualLoadedTx.InputTxes.Select(x => x.Hash).ToArray()); }
public void TestDoubleSpend() { // prepare block var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); var chain = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder(); var emptyCoinbaseTx0 = new BlockTx(0, new Transaction(0, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); var emptyCoinbaseTx1 = new BlockTx(0, new Transaction(1, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); // initialize memory utxo builder storage var memoryStorage = new MemoryStorageManager(); var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item; memoryChainStateCursor.BeginTransaction(); // initialize utxo builder var utxoBuilder = new UtxoBuilder(); // prepare an unspent transaction var txHash = new UInt256(100); var unspentTx = new UnspentTx(txHash, chainedHeader1.Height, 0, 1, OutputState.Unspent); // add the unspent transaction memoryChainStateCursor.TryAddUnspentTx(unspentTx); // create an input to spend the unspent transaction var input = new TxInput(new TxOutputKey(txHash, txOutputIndex: 0), ImmutableArray.Create<byte>(), 0); var tx = new BlockTx(1, new Transaction(0, ImmutableArray.Create(input), ImmutableArray.Create<TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader1); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage UnspentTx actualUnspentTx; Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.IsFullySpent); // attempt to spend the input again, validation exception should be thrown chain.AddBlock(chainedHeader2); AssertMethods.AssertAggregateThrows<ValidationException>(() => utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx }.ToBufferBlock()).ToEnumerable().ToList()); }
public void TestSimpleSpend() { // prepare block var fakeHeaders = new FakeHeaders(); var chainedHeader0 = fakeHeaders.GenesisChained(); var chainedHeader1 = fakeHeaders.NextChained(); var chainedHeader2 = fakeHeaders.NextChained(); var chainedHeader3 = fakeHeaders.NextChained(); var chain = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder(); var emptyCoinbaseTx0 = BlockTx.Create(0, Transaction.Create(0, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); var emptyCoinbaseTx1 = BlockTx.Create(1, Transaction.Create(1, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); var emptyCoinbaseTx2 = BlockTx.Create(2, Transaction.Create(2, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0)); // initialize memory utxo builder storage var memoryStorage = new MemoryStorageManager(); var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item; memoryChainStateCursor.BeginTransaction(); // initialize utxo builder var utxoBuilder = new UtxoBuilder(); // prepare an unspent transaction var txHash = new UInt256(100); var unspentTx = new UnspentTx(txHash, chainedHeader1.Height, 0, 0, false, 3, OutputState.Unspent); var txOutput1Key = new TxOutputKey(txHash, 0); var txOutput1 = new TxOutput(0, ImmutableArray<byte>.Empty); var txOutput2Key = new TxOutputKey(txHash, 1); var txOutput2 = new TxOutput(1, ImmutableArray<byte>.Empty); var txOutput3Key = new TxOutputKey(txHash, 2); var txOutput3 = new TxOutput(2, ImmutableArray<byte>.Empty); // prepare unspent output var unspentTransactions = ImmutableDictionary.Create<UInt256, UnspentTx>().Add(txHash, unspentTx); // add the unspent transaction memoryChainStateCursor.TryAddUnspentTx(unspentTx); memoryChainStateCursor.TryAddUnspentTxOutput(txOutput1Key, txOutput1); memoryChainStateCursor.TryAddUnspentTxOutput(txOutput2Key, txOutput2); memoryChainStateCursor.TryAddUnspentTxOutput(txOutput3Key, txOutput3); // create an input to spend the unspent transaction's first output var input0 = new TxInput(txHash, 0, ImmutableArray.Create<byte>(), 0); var tx0 = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input0), ImmutableArray.Create<TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader1); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx0 }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage UnspentTx actualUnspentTx; TxOutput actualTxOutput; Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3); Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent); Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Unspent); Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent); Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput)); Assert.AreEqual(txOutput1, actualTxOutput); Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput2Key, out actualTxOutput)); Assert.AreEqual(txOutput2, actualTxOutput); Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput3Key, out actualTxOutput)); Assert.AreEqual(txOutput3, actualTxOutput); // create an input to spend the unspent transaction's second output var input1 = new TxInput(txHash, 1, ImmutableArray.Create<byte>(), 0); var tx1 = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input1), ImmutableArray.Create<TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader2); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx1 }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3); Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent); Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Spent); Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent); // create an input to spend the unspent transaction's third output var input2 = new TxInput(txHash, 2, ImmutableArray.Create<byte>(), 0); var tx2 = BlockTx.Create(2, Transaction.Create(0, ImmutableArray.Create(input2), ImmutableArray.Create<TxOutput>(), 0)); // spend the input chain.AddBlock(chainedHeader3); utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx2, tx2 }.ToBufferBlock()).ToEnumerable().ToList(); // verify utxo storage Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx)); Assert.IsTrue(actualUnspentTx.IsFullySpent); }
private static byte[] GetScriptFromInputPrevOutput(TxInput input, TxOutput prevOutput) { return input.ScriptSignature.Concat(prevOutput.ScriptPublicKey).ToArray(); }
public static byte[] EncodeTxInput(TxInput txInput) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { EncodeTxInput(writer, txInput); return stream.ToArray(); } }
public static void EncodeTxInput(BinaryWriter writer, TxInput txInput) { writer.WriteUInt256(txInput.PrevTxOutputKey.TxHash); writer.WriteUInt32(txInput.PrevTxOutputKey.TxOutputIndex); writer.WriteVarBytes(txInput.ScriptSignature.ToArray()); writer.WriteUInt32(txInput.Sequence); }
public virtual void ValidationTransactionScript(ChainedHeader chainedHeader, Transaction tx, int txIndex, TxInput txInput, int txInputIndex, TxOutput prevTxOutput) { var scriptEngine = new ScriptEngine(this.IgnoreSignatures); // create the transaction script from the input and output var script = txInput.ScriptSignature.Concat(prevTxOutput.ScriptPublicKey); if (!scriptEngine.VerifyScript(chainedHeader.Hash, txIndex, prevTxOutput.ScriptPublicKey.ToArray(), tx, txInputIndex, script.ToArray())) { logger.Debug($"Script did not pass in block: {chainedHeader.Hash}, tx: {txIndex}, {tx.Hash}, input: {txInputIndex}"); throw new ValidationException(chainedHeader.Hash); } }
private bool IsFinal(TxInput input) { return input.Sequence == uint.MaxValue; }
public void ValidationTransactionScript(Chain newChain, BlockTx tx, TxInput txInput, int txInputIndex, PrevTxOutput prevTxOutput) { var chainedHeader = newChain.LastBlock; // BIP16 didn't become active until Apr 1 2012 var nBIP16SwitchTime = DateTimeOffset.FromUnixTimeSeconds(1333238400U); var strictPayToScriptHash = chainedHeader.Time >= nBIP16SwitchTime; var flags = strictPayToScriptHash ? verify_flags_type.verify_flags_p2sh : verify_flags_type.verify_flags_none; // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, // when 75% of the network has upgraded: if (chainedHeader.Version >= 3 && IsSuperMajority(3, newChain, ChainParams.MajorityEnforceBlockUpgrade)) { flags |= verify_flags_type.verify_flags_dersig; } // Start enforcing CHECKLOCKTIMEVERIFY, (BIP65) for block.nVersion=4 // blocks, when 75% of the network has upgraded: if (chainedHeader.Version >= 4 && IsSuperMajority(4, newChain, ChainParams.MajorityEnforceBlockUpgrade)) { flags |= verify_flags_type.verify_flags_checklocktimeverify; } var result = LibbitcoinConsensus.VerifyScript( tx.TxBytes, prevTxOutput.ScriptPublicKey, txInputIndex, flags); if (!result) { logger.Debug($"Script did not pass in block: {chainedHeader.Hash}, tx: {tx.Index}, {tx.Hash}, input: {txInputIndex}"); throw new ValidationException(chainedHeader.Hash); } }
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); }