public void TestWireDecodeTransaction() { var actual = DataDecoder.DecodeTransaction(null, TRANSACTION_1_BYTES.ToArray()); var actualBytes = DataEncoder.EncodeTransaction(actual).TxBytes; CollectionAssert.AreEqual(TRANSACTION_1_BYTES.ToList(), actualBytes.ToList()); }
public async Task SendTransaction(Transaction transaction) { await Task.Yield(); var sendTxMessage = Messaging.ConstructMessage("tx", DataEncoder.EncodeTransaction(transaction)); await SendMessageAsync(sendTxMessage); }
public byte[] TxSignature(ImmutableArray <byte> scriptPubKey, Transaction tx, int inputIndex, byte hashType) { ///TODO Debug.Assert(inputIndex < tx.Inputs.Length); // Blank out other inputs' signatures var empty = ImmutableArray.Create <byte>(); var newInputs = ImmutableArray.CreateBuilder <TxInput>(tx.Inputs.Length); for (var i = 0; i < tx.Inputs.Length; i++) { var oldInput = tx.Inputs[i]; var newInput = oldInput.With(scriptSignature: i == inputIndex ? scriptPubKey : empty); newInputs.Add(newInput); } //// Blank out some of the outputs //if ((hashType & 0x1F) == (int)ScriptHashType.SIGHASH_NONE) //{ // //TODO // Debug.Assert(false); // // Wildcard payee // // Let the others update at will //} //else if ((hashType & 0x1F) == (int)ScriptHashType.SIGHASH_SINGLE) //{ // //TODO // Debug.Assert(false); // // Only lock-in the txout payee at same index as txin // // Let the others update at will //} //// Blank out other inputs completely, not recommended for open transactions //if ((hashType & 0x80) == (int)ScriptHashType.SIGHASH_ANYONECANPAY) //{ // //TODO // Debug.Assert(false); //} // create simplified transaction var newTx = tx.With(Inputs: newInputs.ToImmutable()); // return wire-encoded simplified transaction with the 4-byte hashType tacked onto the end using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { writer.WriteBytes(DataEncoder.EncodeTransaction(newTx)); writer.WriteUInt32(hashType); return(stream.ToArray()); } }
public static DecodedBlockTx Create(int txIndex, Transaction tx) { if (tx == null) { throw new ArgumentNullException(nameof(tx)); } var decodedTx = DataEncoder.EncodeTransaction(tx); return(new DecodedBlockTx(txIndex, decodedTx)); }
public bool TryAddBlockTransactions(UInt256 blockHash, IEnumerable <Transaction> blockTxes) { if (this.ContainsBlock(blockHash)) { return(false); } try { using (var handle = this.cursorCache.TakeItem()) { var cursor = handle.Item; using (var jetTx = cursor.jetSession.BeginTransaction()) { int blockId; using (var jetUpdate = cursor.jetSession.BeginUpdate(cursor.blockIdsTableId, JET_prep.Insert)) { blockId = Api.RetrieveColumnAsInt32(cursor.jetSession, cursor.blockIdsTableId, cursor.blockIdsIdColumnId, RetrieveColumnGrbit.RetrieveCopy).Value; Api.SetColumn(cursor.jetSession, cursor.blockIdsTableId, cursor.blockIdsHashColumnId, DbEncoder.EncodeUInt256(blockHash)); jetUpdate.Save(); } var txIndex = 0; foreach (var tx in blockTxes) { AddTransaction(blockId, txIndex, tx.Hash, DataEncoder.EncodeTransaction(tx), cursor); txIndex++; } // increase block count Api.EscrowUpdate(cursor.jetSession, cursor.globalsTableId, cursor.blockCountColumnId, +1); jetTx.CommitLazy(); return(true); } } } catch (EsentKeyDuplicateException) { return(false); } }
public TransactionStorage(string baseDirectory) : base(baseDirectory, "Transactions", tx => DataEncoder.EncodeTransaction(tx), (txHash, bytes) => DataEncoder.DecodeTransaction(bytes, txHash)) { }
public void TestWireDecodeTransaction() { var actual = DataEncoder.EncodeTransaction(DataEncoder.DecodeTransaction(TRANSACTION_1_BYTES.ToArray())); CollectionAssert.AreEqual(TRANSACTION_1_BYTES.ToList(), actual.ToList()); }
public static DecodedTx Create(UInt32 version, ImmutableArray <TxInput> inputs, ImmutableArray <TxOutput> outputs, UInt32 lockTime) { return(DataEncoder.EncodeTransaction(version, inputs, outputs, lockTime)); }
public void TallyTransaction(Chain newChain, ValidatableTx validatableTx, ref object runningTally) { var chainedHeader = newChain.LastBlock; if (runningTally == null) { var medianPrevHeaderTime = GetMedianPrevHeaderTime(newChain, chainedHeader.Height); var lockTimeFlags = 0; // TODO why is this used here? var lockTimeCutoff = ((lockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) != 0) ? GetMedianPrevHeaderTime(newChain, chainedHeader.Height).ToUnixTimeSeconds() : chainedHeader.Time.ToUnixTimeSeconds(); runningTally = new BlockTally { BlockSize = 80, LockTimeCutoff = lockTimeCutoff }; } var blockTally = (BlockTally)runningTally; var tx = validatableTx.Transaction; var txIndex = validatableTx.Index; // BIP16 didn't become active until Apr 1 2012 var nBIP16SwitchTime = DateTimeOffset.FromUnixTimeSeconds(1333238400U); var strictPayToScriptHash = chainedHeader.Time >= nBIP16SwitchTime; // first transaction must be coinbase if (validatableTx.Index == 0 && !tx.IsCoinbase) { throw new ValidationException(chainedHeader.Hash); } // all other transactions must not be coinbase else if (validatableTx.Index > 0 && tx.IsCoinbase) { throw new ValidationException(chainedHeader.Hash); } // must have inputs else if (tx.Inputs.Length == 0) { throw new ValidationException(chainedHeader.Hash); } // must have outputs else if (tx.Outputs.Length == 0) { throw new ValidationException(chainedHeader.Hash); } // coinbase scriptSignature length must be >= 2 && <= 100 else if (tx.IsCoinbase && (tx.Inputs[0].ScriptSignature.Length < 2 || tx.Inputs[0].ScriptSignature.Length > 100)) { throw new ValidationException(chainedHeader.Hash); } // all transactions must be finalized else if (!IsFinal(tx, chainedHeader.Height, blockTally.LockTimeCutoff)) { throw new ValidationException(chainedHeader.Hash); } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): if (tx.IsCoinbase && chainedHeader.Version >= 2 && IsSuperMajority(2, newChain, ChainParams.MajorityEnforceBlockUpgrade)) { var requiredScript = GetPushInt64Script(chainedHeader.Height); var actualScript = tx.Inputs[0].ScriptSignature; if (actualScript.Length < requiredScript.Length || !actualScript.Take(requiredScript.Length).SequenceEqual(requiredScript)) { throw new ValidationException(chainedHeader.Hash); } } // running input/output value tallies var txTotalInputValue = 0UL; var txTotalOutputValue = 0UL; var txSigOpCount = 0; // validate & tally inputs for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; if (!tx.IsCoinbase) { var prevOutput = validatableTx.PrevTxOutputs[inputIndex]; // if spending a coinbase, it must be mature if (prevOutput.IsCoinbase && chainedHeader.Height - prevOutput.BlockHeight < COINBASE_MATURITY) { throw new ValidationException(chainedHeader.Hash); } // non-coinbase txes must not have coinbase prev tx output key (txHash: 0, outputIndex: -1) if (input.PrevTxOutputKey.TxOutputIndex == uint.MaxValue && input.PrevTxOutputKey.TxHash == UInt256.Zero) { throw new ValidationException(chainedHeader.Hash); } // tally txTotalInputValue += prevOutput.Value; } // tally txSigOpCount += CountLegacySigOps(input.ScriptSignature, accurate: false); if (!tx.IsCoinbase && strictPayToScriptHash) { txSigOpCount += CountP2SHSigOps(validatableTx, inputIndex); } } // validate & tally outputs for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++) { var output = tx.Outputs[outputIndex]; // must not have any negative money value outputs if (unchecked ((long)output.Value) < 0) { throw new ValidationException(chainedHeader.Hash); } // must not have any outputs with a value greater than max money else if (output.Value > MAX_MONEY) { throw new ValidationException(chainedHeader.Hash); } // tally if (!tx.IsCoinbase) { txTotalOutputValue += output.Value; } txSigOpCount += CountLegacySigOps(output.ScriptPublicKey, accurate: false); } // must not have a total output value greater than max money if (txTotalOutputValue > MAX_MONEY) { throw new ValidationException(chainedHeader.Hash); } // validate non-coinbase fees long txFeeValue; if (!validatableTx.IsCoinbase) { // ensure that output amount isn't greater than input amount if (txTotalOutputValue > txTotalInputValue) { throw new ValidationException(chainedHeader.Hash, $"Failing tx {tx.Hash}: Transaction output value is greater than input value"); } // calculate fee value (unspent amount) txFeeValue = (long)txTotalInputValue - (long)txTotalOutputValue; Debug.Assert(txFeeValue >= 0); } else { txFeeValue = 0; } // block tallies if (validatableTx.IsCoinbase) { blockTally.CoinbaseTx = validatableTx; } blockTally.TxCount++; blockTally.TotalFees += txFeeValue; blockTally.TotalSigOpCount += txSigOpCount; // re-encode transaction for block size calculation so it is optimal length blockTally.BlockSize += DataEncoder.EncodeTransaction(validatableTx.Transaction).TxBytes.Length; //TODO if (blockTally.TotalSigOpCount > MAX_BLOCK_SIGOPS) { throw new ValidationException(chainedHeader.Hash); } //TODO else if (blockTally.BlockSize + DataEncoder.VarIntSize((uint)blockTally.TxCount) > MAX_BLOCK_SIZE) { throw new ValidationException(chainedHeader.Hash); } // all validation has passed }
public void TestWireEncodeTransaction() { var actual = DataEncoder.EncodeTransaction(TRANSACTION_1).TxBytes; CollectionAssert.AreEqual(TRANSACTION_1_BYTES.ToList(), actual.ToList()); }