private void CachePrevOut(PrevTxOutput prevTxOutput) { var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSize(1) .SetSlidingExpiration(TimeSpan.FromMinutes(30)); prevTxOutputCache.Cache.Set <PrevTxOutput>($"{HelperTools.ByteToHexString(prevTxOutput.TxExternalId)}_{prevTxOutput.N}", prevTxOutput, cacheEntryOptions); }
public static byte[] EncodePrevTxOutput(PrevTxOutput txOutput) { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { EncodePrevTxOutput(writer, txOutput); return(stream.ToArray()); } }
public static void EncodePrevTxOutput(BinaryWriter writer, PrevTxOutput txOutput) { writer.WriteUInt64(txOutput.Value); writer.WriteVarBytes(txOutput.ScriptPublicKey.ToArray()); writer.WriteInt32(txOutput.BlockHeight); writer.WriteInt32(txOutput.TxIndex); writer.WriteUInt32(txOutput.TxVersion); writer.WriteBool(txOutput.IsCoinbase); }
public bool TryAddTransaction(DecodedTx decodedTx) { if (ContainsTransaction(decodedTx.Hash)) { // unconfirmed tx already exists return(false); } var tx = decodedTx.Transaction; UnconfirmedTx unconfirmedTx; // allow concurrent transaction adds if underlying storage supports it // in either case, lock waits for block add/rollback to finish if (storageManager.IsUnconfirmedTxesConcurrent) { updateLock.EnterReadLock(); } else { updateLock.EnterWriteLock(); } try { using (var chainState = coreDaemon.GetChainState()) { // verify each input is available to spend var prevTxOutputKeys = new HashSet <TxOutputKey>(); var prevTxOutputs = ImmutableArray.CreateBuilder <PrevTxOutput>(tx.Inputs.Length); var inputValue = 0UL; for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; if (!prevTxOutputKeys.Add(input.PrevTxOutputKey)) { // tx double spends one of its own inputs return(false); } UnspentTx unspentTx; if (!chainState.TryGetUnspentTx(input.PrevTxHash, out unspentTx)) { // input's prev output does not exist return(false); } if (input.PrevTxOutputIndex >= unspentTx.OutputStates.Length) { // input's prev output does not exist return(false); } if (unspentTx.OutputStates[(int)input.PrevTxOutputIndex] != OutputState.Unspent) { // input's prev output has already been spent return(false); } TxOutput txOutput; if (!chainState.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)) { // input's prev output does not exist return(false); } var prevTxOutput = new PrevTxOutput(txOutput, unspentTx); prevTxOutputs.Add(prevTxOutput); checked { inputValue += prevTxOutput.Value; } } var outputValue = 0UL; for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++) { var output = tx.Outputs[outputIndex]; checked { outputValue += output.Value; } } if (outputValue > inputValue) { // transaction spends more than its inputs return(false); } // validation passed // create the unconfirmed tx var blockTx = new DecodedBlockTx(-1, decodedTx); var validatableTx = new ValidatableTx(blockTx, null, prevTxOutputs.ToImmutable()); unconfirmedTx = new UnconfirmedTx(validatableTx, DateTimeOffset.Now); // add the unconfirmed tx using (var handle = storageManager.OpenUnconfirmedTxesCursor()) { var unconfirmedTxesCursor = handle.Item; unconfirmedTxesCursor.BeginTransaction(); if (unconfirmedTxesCursor.TryAddTransaction(unconfirmedTx)) { unconfirmedTxesCursor.CommitTransaction(); } else { // unconfirmed tx already exists return(false); } } } } finally { if (storageManager.IsUnconfirmedTxesConcurrent) { updateLock.ExitReadLock(); } else { updateLock.ExitWriteLock(); } } UnconfirmedTxAdded?.Invoke(this, new UnconfirmedTxAddedEventArgs(unconfirmedTx)); return(true); }
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); } }
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); } }