private static MsgTx DecodeTransaction(byte[] rawTransaction) { var msgTx = new MsgTx(); msgTx.Decode(rawTransaction); return(msgTx); }
/// <summary> /// /// </summary> /// <param name="transaction">The transaction that the script applies to.</param> /// <param name="script"></param> /// <param name="options"></param> public ScriptEngine(MsgTx transaction, int txIndex, Script script, ScriptOptions options) { if (transaction == null) { throw new ArgumentNullException(nameof(transaction)); } if (txIndex < 0 || txIndex >= transaction.TxIn.Length) { throw new ArgumentOutOfRangeException(nameof(txIndex)); } if (script == null) { throw new ArgumentNullException(nameof(script)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } _transaction = transaction; _txIndex = txIndex; _script = script; _opCodeLookup = new Dictionary <OpCode, Action <ParsedOpCode> >(); Options = options; MainStack = new ScriptStack(Options.AssertScriptIntegerMinimalEncoding); AltStack = new ScriptStack(Options.AssertScriptIntegerMinimalEncoding); BranchStack = new BranchStack(); InitializeOpCodeDictionary(); }
public void OpCheckSig(ParsedOpCode op, MsgTx transaction) { try { var rawPublicKey = MainStack.Pop(); var rawSignature = MainStack.Pop(); if (rawSignature.Length < 1) { MainStack.Push(false); return; } var signature = rawSignature.Take(rawSignature.Length - 1).ToArray(); var signatureType = (SignatureHashType)rawSignature.Last(); AssertSignatureHashType(signatureType); AssertSignatureEncoding(signature); AssertPublicKeyEncoding(rawPublicKey); var subScript = _script.GetOpCodesWithoutData(rawSignature); var hash = CalculateSignatureHash(subScript, signatureType, (MsgTx)transaction.Clone(), _txIndex); var ecSignature = new ECSignature(signature); var securityService = new ECPublicSecurityService(rawPublicKey); var isValidSignature = securityService.VerifySignature(hash, ecSignature); MainStack.Push(isValidSignature); } catch (ScriptException) { MainStack.Push(false); } }
/// <summary> /// Broadcasts a signed transaction to the Decred network /// </summary> /// <param name="operationId"></param> /// <param name="hexTransaction"></param> /// <returns></returns> /// <exception cref="TransactionBroadcastException"></exception> public async Task Broadcast(Guid operationId, string hexTransaction) { if (operationId == Guid.Empty) { throw new BusinessException(ErrorReason.BadRequest, "Operation id is invalid"); } if (string.IsNullOrWhiteSpace(hexTransaction)) { throw new BusinessException(ErrorReason.BadRequest, "SignedTransaction is invalid"); } var txBytes = HexUtil.ToByteArray(hexTransaction); var msgTx = new MsgTx(); msgTx.Decode(txBytes); // If the operation exists in the cache, throw exception var cachedResult = await _broadcastTxRepo.GetAsync(operationId.ToString()); if (cachedResult != null) { throw new BusinessException(ErrorReason.DuplicateRecord, "Operation already broadcast"); } var txHash = HexUtil.FromByteArray(msgTx.GetHash().Reverse().ToArray()); var txWasMined = await _txRepo.GetTxInfoByHash(txHash, long.MaxValue) != null; if (txWasMined) { await SaveBroadcastedTransaction(new BroadcastedTransaction { OperationId = operationId, Hash = txHash, EncodedTransaction = hexTransaction }); throw new BusinessException(ErrorReason.DuplicateRecord, "Operation already broadcast"); } // Submit the transaction to the network via dcrd var result = await _dcrdClient.SendRawTransactionAsync(hexTransaction); if (result.Error != null) { throw new TransactionBroadcastException($"[{result.Error.Code}] {result.Error.Message}"); } await SaveBroadcastedTransaction(new BroadcastedTransaction { OperationId = operationId, Hash = txHash, EncodedTransaction = hexTransaction }); }
/// <summary> /// Calculates the hash of a transaction to be signed. /// </summary> /// <param name="transaction"></param> /// <returns></returns> private byte[] CalculateTxHash(MsgTx transaction) { var wbuf = new List <byte>(32 * 2 + 4); wbuf.AddRange(BitConverter.GetBytes((uint)1)); var prefixHash = transaction.GetHash(TxSerializeType.NoWitness); var witnessHash = transaction.GetHash(TxSerializeType.WitnessSigning); wbuf.AddRange(prefixHash); wbuf.AddRange(witnessHash); return(HashUtil.Blake256(wbuf.ToArray())); }
public void GetHash_GivenTestObjectWithKnownHash_ReturnsExpectedHash() { var msgTx = new MsgTx { Version = 1, SerializationType = TxSerializeType.Full, TxIn = new[] { txIn }, TxOut = new[] { txOut } }; var actualHash = msgTx.GetHash().Reverse().ToArray(); var actualHashString = Hex.ToHexString(actualHash); Assert.Equal(expectedHash, actualHashString); }
public void New_GivenSerializedValueWithMultipleTx_DeserializesInputAndReserializes() { var tests = new MsgTxTestSubject[] { new MultiTxTestSubject(), new NoTxTests() }; foreach (var test in tests) { var subject = new MsgTx(); subject.Decode(test.EncodedMessage); var deserialized = subject.Encode(); Assert.True(test.EncodedMessage.SequenceEqual(deserialized)); } }
/// <summary> /// Determines the state of a broadcasted transaction /// </summary> /// <param name="operationId"></param> /// <returns></returns> /// <exception cref="BusinessException"></exception> public async Task <BroadcastedSingleTransactionResponse> GetBroadcastedTxSingle(Guid operationId) { if (operationId == Guid.Empty) { throw new BusinessException(ErrorReason.BadRequest, "Operation id is invalid"); } // Retrieve the broadcasted transaction and deserialize it. var broadcastedTransaction = await GetBroadcastedTransaction(operationId); var transaction = new MsgTx(); transaction.Decode(HexUtil.ToByteArray(broadcastedTransaction.EncodedTransaction)); // Calculate the fee and total amount spent from the transaction. var fee = transaction.TxIn.Sum(t => t.ValueIn) - transaction.TxOut.Sum(t => t.Value); var amount = transaction.TxOut.Sum(t => t.Value); // Check to see if the transaction has been included in a block. var safeBlockHeight = await _dcrdClient.GetMaxConfirmedBlockHeight(); var knownTx = await _txRepo.GetTxInfoByHash(broadcastedTransaction.Hash, safeBlockHeight); var txState = knownTx == null ? BroadcastedTransactionState.InProgress : BroadcastedTransactionState.Completed; // If the tx has been included in a block, // use the block height + timestamp from the block var txBlockHeight = knownTx?.BlockHeight ?? safeBlockHeight; var timestamp = knownTx?.BlockTime.UtcDateTime ?? DateTime.UtcNow; return(new BroadcastedSingleTransactionResponse { Block = txBlockHeight, State = txState, Hash = broadcastedTransaction.Hash, Amount = amount.ToString(), Fee = fee.ToString(), Error = "", ErrorCode = null, OperationId = operationId, Timestamp = timestamp }); }
public MsgTx SpendingTx() { var coinbaseTx = new MsgTx { TxIn = new[] { new TxIn { PreviousOutPoint = new OutPoint(new byte[32], 0, TxTree.TxTreeRegular), SignatureScript = new[] { (byte)OpCode.OP_0, (byte)OpCode.OP_0 } } }, TxOut = new[] { new TxOut { Value = 0, PkScript = PublicKeyScript.Bytes } } }; var coinbaseTxHash = coinbaseTx.GetHash(); var spendingTx = new MsgTx { TxIn = new[] { new TxIn { PreviousOutPoint = new OutPoint(coinbaseTxHash, 0, TxTree.TxTreeRegular), SignatureScript = SignatureScript.Bytes } }, TxOut = new[] { new TxOut() } }; return(spendingTx); }
public void GetRoot_GivenTestnetGenesisTranasction_ReturnsExpectedMerkleRoot() { var testnetGenesisTx = new MsgTx { SerializationType = TxSerializeType.Full, Version = 1, TxIn = new[] { new TxIn { PreviousOutPoint = new OutPoint { Hash = new byte[32], Index = 0xffffffff, }, SignatureScript = new byte[] { 0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, 0x6e, 0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x72, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6c, 0x6f, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x73, }, Sequence = 0xffffffff }, }, TxOut = new TxOut[] { new TxOut() { Value = 0x00000000, PkScript = new byte[] { 0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, 0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, 0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, 0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, 0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, 0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, 0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0x5f, 0xac }, }, }, LockTime = 0, Expiry = 0, }; // For some reason, the testnet genesis tx is hashed differently than mainnet to // get the merkle root. var hash = testnetGenesisTx.GetHash(); var merkleTree = new MerkleTree(); var merkleRoot = merkleTree.GetRoot(new[] { hash }); // Assert.True(); }
public static byte[] CalculateSignatureHash(ParsedOpCode[] subScript, SignatureHashType hashType, MsgTx transaction, int index) { const SignatureHashType mask = (SignatureHashType)0x1f; if ((hashType & mask) == SignatureHashType.Single && index >= transaction.TxOut.Length) { throw new InvalidSignatureException("SignatureHashType.Single index out of range"); } // Clear out signature scripts for input transactions not at index // transactionIndex for (var i = 0; i < transaction.TxIn.Length; i++) { transaction.TxIn[i].SignatureScript = i == index? subScript.SelectMany(s => s.Serialize()).ToArray() : new byte[0]; } switch (hashType & mask) { case SignatureHashType.None: transaction.TxOut = new TxOut[0]; for (var i = 0; i < transaction.TxIn.Length; i++) { if (i != index) { transaction.TxIn[i].Sequence = 0; } } break; case SignatureHashType.Single: transaction.TxOut = new TxOut[index]; for (var i = 0; i < index; i++) { transaction.TxOut[i].Value = -1; transaction.TxOut[i].PkScript = null; } for (var i = 0; i < transaction.TxIn.Length; i++) { if (i != index) { transaction.TxIn[i].Sequence = 0; } } break; case SignatureHashType.Old: break; case SignatureHashType.All: break; case SignatureHashType.AllValue: break; case SignatureHashType.AnyOneCanPay: break; default: throw new ArgumentOutOfRangeException(); } if ((hashType & SignatureHashType.AnyOneCanPay) != 0) { transaction.TxIn = transaction.TxIn .Skip(index) .Take(1) .ToArray(); } var wbuf = new List <byte>(32 * 2 + 4); wbuf.AddRange(BitConverter.GetBytes((uint)hashType)); var prefixHash = transaction.GetHash(TxSerializeType.NoWitness); var witnessHash = transaction.GetHash( (hashType & mask) != SignatureHashType.All ? TxSerializeType.WitnessSigning : TxSerializeType.WitnessValueSigning ); wbuf.AddRange(prefixHash); wbuf.AddRange(witnessHash); return(HashUtil.Blake256(wbuf.ToArray())); }
private void OpCheckSigVerify(ParsedOpCode op, MsgTx transaction) { OpCheckSig(op, transaction); OpVerify(); }