public void TestCreateCoinbaseAndSpend() { var keyPair = TransactionManager.CreateKeyPair(); var privateKey = keyPair.Item1; var publicKey = keyPair.Item2; var coinbaseTx = TransactionManager.CreateCoinbaseTransaction(publicKey, Encoding.ASCII.GetBytes("coinbase text!")); var publicKeyScript = TransactionManager.CreatePublicKeyScript(publicKey); var privateKeyScript = TransactionManager.CreatePrivateKeyScript(coinbaseTx, 0, (byte)ScriptHashType.SIGHASH_ALL, privateKey, publicKey); var script = privateKeyScript.Concat(publicKeyScript); var scriptEngine = new ScriptEngine(); Assert.IsTrue(scriptEngine.VerifyScript(0, 0, publicKeyScript.ToArray(), coinbaseTx, 0, script)); }
//TODO utxo needs to be as-at transaction, with regards to a transaction being fully spent and added back in in the same block public virtual void ValidateTransactionScripts(Block block, ImmutableDictionary<UInt256, UnspentTx> utxo, ImmutableDictionary<UInt256, ImmutableHashSet<int>> newTransactions) { if (BypassExecuteScript) return; // lookup all previous outputs var prevOutputMissing = false; var previousOutputs = new Dictionary<TxOutputKey, Tuple<Transaction, TxInput, int, TxOutput>>(); for (var txIndex = 1; txIndex < block.Transactions.Length; txIndex++) { var tx = block.Transactions[txIndex]; for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; // find previous transaction var prevTx = GetPreviousTransaction(block, txIndex, input.PreviousTxOutputKey, utxo, newTransactions); // find previous transaction output if (input.PreviousTxOutputKey.TxOutputIndex >= prevTx.Outputs.Length) throw new ValidationException(); var prevOutput = prevTx.Outputs[input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked()]; previousOutputs.Add(input.PreviousTxOutputKey, Tuple.Create(tx, input, inputIndex, prevOutput)); } } if (prevOutputMissing) { throw new ValidationException(); } var exceptions = new ConcurrentBag<Exception>(); var scriptEngine = new ScriptEngine(); Parallel.ForEach(previousOutputs.Values, (tuple, loopState) => { try { var tx = tuple.Item1; var input = tuple.Item2; var inputIndex = tuple.Item3; var prevOutput = tuple.Item4; // create the transaction script from the input and output var script = input.ScriptSignature.AddRange(prevOutput.ScriptPublicKey); if (!scriptEngine.VerifyScript(0 /*TODO blockHash*/, 0 /*TODO txIndex*/, prevOutput.ScriptPublicKey.ToArray(), tx, inputIndex, script.ToArray())) { exceptions.Add(new ValidationException()); loopState.Break(); } } catch (Exception e) { exceptions.Add(e); loopState.Break(); } }); if (exceptions.Count > 0) throw new AggregateException(exceptions.ToArray()); }
private void TestTransactionVerifyScript(Transaction tx, IDictionary<UInt256, Transaction> txLookup) { var scriptEngine = new ScriptEngine(); for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; var prevOutput = txLookup[input.PreviousTxOutputKey.TxHash].Outputs[input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked()]; var script = GetScriptFromInputPrevOutput(input, prevOutput); var result = scriptEngine.VerifyScript(0 /*blockIndex*/, -1 /*txIndex*/, prevOutput.ScriptPublicKey.ToArray(), tx, inputIndex, script.ToArray()); Assert.IsTrue(result); } }