-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathTransaction.cs
More file actions
187 lines (158 loc) · 6.11 KB
/
Copy pathTransaction.cs
File metadata and controls
187 lines (158 loc) · 6.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
using System;
using System.Runtime.InteropServices;
using BitcoinKernel.Exceptions;
using BitcoinKernel.Interop;
namespace BitcoinKernel.Primitives;
/// <summary>
/// Managed wrapper for Bitcoin transactions with automatic memory management.
/// Provides high-level APIs for transaction operations.
/// </summary>
public sealed class Transaction : IDisposable
{
private IntPtr _handle;
private bool _disposed;
private readonly bool _ownsHandle;
/// <summary>
/// Creates a transaction from serialized data.
/// </summary>
/// <param name="rawTransaction">The serialized transaction bytes.</param>
/// <exception cref="ArgumentNullException">Thrown when rawTransaction is null.</exception>
/// <exception cref="TransactionException">Thrown when transaction creation fails.</exception>
public Transaction(byte[] rawTransaction)
{
ArgumentNullException.ThrowIfNull(rawTransaction);
unsafe
{
fixed (byte* ptr = rawTransaction)
{
_handle = NativeMethods.TransactionCreate((IntPtr)ptr, (nuint)rawTransaction.Length);
if (_handle == IntPtr.Zero)
throw new TransactionException("Failed to create transaction from serialized data");
}
}
_ownsHandle = true;
}
public static Transaction FromBytes(byte[] rawTransaction)
{
return new Transaction(rawTransaction);
}
/// <summary>
/// Creates a transaction from a hex string.
/// </summary>
/// <param name="hexString">The transaction as a hex string.</param>
/// <exception cref="ArgumentNullException">Thrown when hexString is null or empty.</exception>
/// <exception cref="TransactionException">Thrown when transaction creation fails.</exception>
public Transaction(string hexString)
{
ArgumentNullException.ThrowIfNullOrEmpty(hexString);
var bytes = Convert.FromHexString(hexString);
unsafe
{
fixed (byte* ptr = bytes)
{
_handle = NativeMethods.TransactionCreate((IntPtr)ptr, (nuint)bytes.Length);
if (_handle == IntPtr.Zero)
throw new TransactionException("Failed to create transaction from hex string");
}
}
_ownsHandle = true;
}
public static Transaction FromHex(string hexString)
{
return new Transaction(hexString);
}
internal Transaction(IntPtr handle, bool ownsHandle = true)
{
_handle = handle;
_ownsHandle = ownsHandle;
}
/// <summary>
/// Gets the number of inputs in this transaction.
/// </summary>
/// <returns>The number of inputs as an integer.</returns>
public int InputCount => (int)NativeMethods.TransactionCountInputs(_handle);
/// <summary>
/// Gets the number of outputs in this transaction.
/// </summary>
/// <returns>The number of outputs as an integer.</returns>
public int OutputCount => (int)NativeMethods.TransactionCountOutputs(_handle);
/// <summary>
/// Gets the nLockTime value of this transaction.
/// </summary>
public uint LockTime => NativeMethods.TransactionGetLocktime(_handle);
/// <summary>
/// Gets the transaction ID (txid) as bytes.
/// </summary>
/// <returns>The transaction ID as a byte array.</returns>
public byte[] GetTxid()
{
IntPtr txidPtr = NativeMethods.TransactionGetTxid(_handle);
if (txidPtr == IntPtr.Zero)
throw new TransactionException("Failed to get transaction ID");
const int TxidSize = 32;
byte[] txid = new byte[TxidSize];
Marshal.Copy(txidPtr, txid, 0, TxidSize);
return txid;
}
/// <summary>
/// Gets the transaction ID (txid) as a hex string.
/// </summary>
/// <returns>The transaction ID as a hex string.</returns>
public string GetTxidHex()
{
byte[] txid = GetTxid();
return Convert.ToHexString(txid).ToLowerInvariant();
}
/// <exception cref="ArgumentOutOfRangeException">Thrown when index is out of range.</exception>
/// <exception cref="TransactionException">Thrown when input retrieval fails.</exception>
public TxIn GetInputAt(int index)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, InputCount);
IntPtr inputPtr = NativeMethods.TransactionGetInputAt(_handle, (nuint)index);
if (inputPtr == IntPtr.Zero)
throw new TransactionException($"Failed to get input at index {index}");
return new TxIn(inputPtr, ownsHandle: false);
}
/// <returns>The TxOut at the specified index.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when index is out of range.</exception>
/// <exception cref="TransactionException">Thrown when output retrieval fails.</exception>
public TxOut GetOutputAt(int index)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, OutputCount);
IntPtr outputPtr = NativeMethods.TransactionGetOutputAt(_handle, (nuint)index);
if (outputPtr == IntPtr.Zero)
throw new TransactionException($"Failed to get output at index {index}");
return new TxOut(outputPtr, ownsHandle: false);
}
/// <summary>
/// Creates a copy of this transaction.
/// </summary>
/// <returns>A new Transaction instance.</returns>
public Transaction Copy()
{
IntPtr copyHandle = NativeMethods.TransactionCopy(_handle);
if (copyHandle == IntPtr.Zero)
throw new TransactionException("Failed to copy transaction");
return new Transaction(copyHandle);
}
internal IntPtr Handle => _handle;
public void Dispose()
{
if (!_disposed && _handle != IntPtr.Zero)
{
if (_ownsHandle)
{
NativeMethods.TransactionDestroy(_handle);
}
_handle = IntPtr.Zero;
_disposed = true;
}
GC.SuppressFinalize(this);
}
~Transaction()
{
Dispose();
}
}