1010public class Transactions {
1111
1212 // ===== Transaction =====
13- public static class Transaction {
14- private final MemorySegment inner ;
13+ public static class Transaction implements AutoCloseable {
14+ private MemorySegment inner ;
15+ private final Arena arena ;
1516
1617 Transaction (MemorySegment inner ) {
1718 if (inner == MemorySegment .NULL ) {
1819 throw new IllegalStateException ("Transaction Object cannot be null" );
1920 }
2021 this .inner = inner ;
22+ this .arena = null ;
23+ }
24+
25+ public Transaction (byte [] rawTransaction ) throws IllegalArgumentException {
26+ if (rawTransaction == null || rawTransaction .length == 0 ) {
27+ throw new IllegalArgumentException ("Raw transaction cannot be null or empty" );
28+ }
29+ this .arena = Arena .ofConfined ();
30+ MemorySegment txSegment = arena .allocateFrom (ValueLayout .JAVA_BYTE , rawTransaction );
31+ this .inner = btck_transaction_create (txSegment , rawTransaction .length );
32+ if (this .inner == MemorySegment .NULL ) {
33+ arena .close ();
34+ throw new IllegalArgumentException ("Failed to create Transaction from raw data" );
35+ }
36+ }
37+
38+ public Transaction copy () {
39+ checkClosed ();
40+ MemorySegment copied = btck_transaction_copy (inner );
41+ if (copied == MemorySegment .NULL ) {
42+ throw new RuntimeException ("Failed to copy Transaction" );
43+ }
44+ return new Transaction (copied );
2145 }
2246
2347 public long countInputs () {
@@ -58,73 +82,194 @@ void checkClosed() {
5882 MemorySegment getInner () {
5983 return inner ;
6084 }
85+
86+ @ Override
87+ public void close () throws Exception {
88+ if (inner != MemorySegment .NULL ) {
89+ btck_transaction_destroy (inner );
90+ inner = MemorySegment .NULL ;
91+ }
92+ if (arena != null ) {
93+ arena .close ();
94+ }
95+ }
6196 }
6297
6398 // ===== Transaction Input =====
64- public static class TransactionInput {
65- private final MemorySegment inner ;
99+ public static class TransactionInput implements AutoCloseable {
100+ private MemorySegment inner ;
101+ private final boolean ownsMemory ;
66102
67103 TransactionInput (MemorySegment inner ) {
68104 if (inner == MemorySegment .NULL ) {
69- throw new IllegalStateException ("Transasction Input object cannot be null" );
105+ throw new IllegalStateException ("Transaction Input object cannot be null" );
70106 }
71107 this .inner = inner ;
108+ this .ownsMemory = false ;
109+ }
110+
111+ private TransactionInput (MemorySegment inner , boolean ownsMemory ) {
112+ this .inner = inner ;
113+ this .ownsMemory = ownsMemory ;
114+ }
115+
116+ public TransactionInput copy () {
117+ checkClosed ();
118+ MemorySegment copied = btck_transaction_input_copy (inner );
119+ if (copied == MemorySegment .NULL ) {
120+ throw new RuntimeException ("Failed to copy TransactionInput" );
121+ }
122+ return new TransactionInput (copied , true );
72123 }
73124
74125 public TransactionOutPoint getOutPoint () {
126+ checkClosed ();
75127 MemorySegment outPointPtr = btck_transaction_input_get_out_point (inner );
76128 return new TransactionOutPoint (outPointPtr );
77129 }
78130
131+ private void checkClosed () {
132+ if (inner == MemorySegment .NULL ) {
133+ throw new IllegalStateException ("TransactionInput has been closed" );
134+ }
135+ }
136+
79137 MemorySegment getInner () {
80138 return inner ;
81139 }
140+
141+ @ Override
142+ public void close () throws Exception {
143+ if (inner != MemorySegment .NULL && ownsMemory ) {
144+ btck_transaction_input_destroy (inner );
145+ inner = MemorySegment .NULL ;
146+ }
147+ }
82148 }
83149
84150 // ===== Transaction OutPoint =====
85- public static class TransactionOutPoint {
86- private final MemorySegment inner ;
151+ public static class TransactionOutPoint implements AutoCloseable {
152+ private MemorySegment inner ;
153+ private final boolean ownsMemory ;
87154
88155 TransactionOutPoint (MemorySegment inner ) {
89156 if (inner == MemorySegment .NULL ) {
90157 throw new IllegalArgumentException ("TransactionOutPoint cannot be null" );
91158 }
92159 this .inner = inner ;
160+ this .ownsMemory = false ;
161+ }
162+
163+ private TransactionOutPoint (MemorySegment inner , boolean ownsMemory ) {
164+ this .inner = inner ;
165+ this .ownsMemory = ownsMemory ;
166+ }
167+
168+ public TransactionOutPoint copy () {
169+ checkClosed ();
170+ MemorySegment copied = btck_transaction_out_point_copy (inner );
171+ if (copied == MemorySegment .NULL ) {
172+ throw new RuntimeException ("Failed to copy TransactionOutPoint" );
173+ }
174+ return new TransactionOutPoint (copied , true );
93175 }
94176
95177 public long getIndex () {
178+ checkClosed ();
96179 return Integer .toUnsignedLong (btck_transaction_out_point_get_index (inner ));
97180 }
98181
99182 public Txid getTxid () {
100- MemorySegment txidPtr = btck_transaction_get_txid (inner );
183+ checkClosed ();
184+ MemorySegment txidPtr = btck_transaction_out_point_get_txid (inner );
101185 return new Txid (txidPtr );
102186 }
187+
188+ private void checkClosed () {
189+ if (inner == MemorySegment .NULL ) {
190+ throw new IllegalStateException ("TransactionOutPoint has been closed" );
191+ }
192+ }
193+
194+ MemorySegment getInner () {
195+ return inner ;
196+ }
197+
198+ @ Override
199+ public void close () throws Exception {
200+ if (inner != MemorySegment .NULL && ownsMemory ) {
201+ btck_transaction_out_point_destroy (inner );
202+ inner = MemorySegment .NULL ;
203+ }
204+ }
103205 }
104206
105207 // ===== Transaction Output =====
106- public static class TransactionOutput {
107- private final MemorySegment inner ;
208+ public static class TransactionOutput implements AutoCloseable {
209+ private MemorySegment inner ;
210+ private final boolean ownsMemory ;
108211
109212 TransactionOutput (MemorySegment inner ) {
110213 if (inner == MemorySegment .NULL ) {
111214 throw new IllegalArgumentException ("TransactionOutput cannot be null" );
112215 }
113216 this .inner = inner ;
217+ this .ownsMemory = false ;
218+ }
219+
220+ private TransactionOutput (MemorySegment inner , boolean ownsMemory ) {
221+ this .inner = inner ;
222+ this .ownsMemory = ownsMemory ;
223+ }
224+
225+ public TransactionOutput (ScriptPubkey scriptPubkey , long amount ) {
226+ if (scriptPubkey == null ) {
227+ throw new IllegalArgumentException ("ScriptPubkey cannot be null" );
228+ }
229+ this .inner = btck_transaction_output_create (scriptPubkey .getInner (), amount );
230+ if (this .inner == MemorySegment .NULL ) {
231+ throw new IllegalArgumentException ("Failed to create TransactionOutput" );
232+ }
233+ this .ownsMemory = true ;
234+ }
235+
236+ public TransactionOutput copy () {
237+ checkClosed ();
238+ MemorySegment copied = btck_transaction_output_copy (inner );
239+ if (copied == MemorySegment .NULL ) {
240+ throw new RuntimeException ("Failed to copy TransactionOutput" );
241+ }
242+ return new TransactionOutput (copied , true );
114243 }
115244
116245 public long getAmount () {
246+ checkClosed ();
117247 return btck_transaction_output_get_amount (inner );
118248 }
119249
120250 public ScriptPubkey getScriptPubKey () {
251+ checkClosed ();
121252 MemorySegment scriptPtr = btck_transaction_output_get_script_pubkey (inner );
122253 return new ScriptPubkey (scriptPtr );
123254 }
124255
256+ private void checkClosed () {
257+ if (inner == MemorySegment .NULL ) {
258+ throw new IllegalStateException ("TransactionOutput has been closed" );
259+ }
260+ }
261+
125262 MemorySegment getInner () {
126263 return inner ;
127264 }
265+
266+ @ Override
267+ public void close () throws Exception {
268+ if (inner != MemorySegment .NULL && ownsMemory ) {
269+ btck_transaction_output_destroy (inner );
270+ inner = MemorySegment .NULL ;
271+ }
272+ }
128273 }
129274
130275 // ===== Coin =====
@@ -190,17 +335,34 @@ public void close() throws Exception {
190335 }
191336
192337 // ===== Txid =====
193- public static class Txid {
194- private final MemorySegment inner ;
338+ public static class Txid implements AutoCloseable {
339+ private MemorySegment inner ;
340+ private final boolean ownsMemory ;
195341
196342 Txid (MemorySegment inner ) {
197343 if (inner == MemorySegment .NULL ) {
198344 throw new IllegalArgumentException ("Txid cannot be null" );
199345 }
200346 this .inner = inner ;
347+ this .ownsMemory = false ;
348+ }
349+
350+ private Txid (MemorySegment inner , boolean ownsMemory ) {
351+ this .inner = inner ;
352+ this .ownsMemory = ownsMemory ;
353+ }
354+
355+ public Txid copy () {
356+ checkClosed ();
357+ MemorySegment copied = btck_txid_copy (inner );
358+ if (copied == MemorySegment .NULL ) {
359+ throw new RuntimeException ("Failed to copy Txid" );
360+ }
361+ return new Txid (copied , true );
201362 }
202363
203364 public byte [] toBytes () {
365+ checkClosed ();
204366 try (var arena = Arena .ofConfined ()) {
205367 MemorySegment output = arena .allocate (32 );
206368 btck_txid_to_bytes (inner , output );
@@ -209,6 +371,11 @@ public byte[] toBytes() {
209371 }
210372
211373 public boolean equals (Txid other ) {
374+ checkClosed ();
375+ if (other == null ) {
376+ return false ;
377+ }
378+ other .checkClosed ();
212379 return btck_txid_equals (inner , other .getInner ()) != 0 ;
213380 }
214381
@@ -222,9 +389,23 @@ public int hashCode() {
222389 return result ;
223390 }
224391
392+ private void checkClosed () {
393+ if (inner == MemorySegment .NULL ) {
394+ throw new IllegalStateException ("Txid has been closed" );
395+ }
396+ }
397+
225398 MemorySegment getInner () {
226399 return inner ;
227400 }
401+
402+ @ Override
403+ public void close () throws Exception {
404+ if (inner != MemorySegment .NULL && ownsMemory ) {
405+ btck_txid_destroy (inner );
406+ inner = MemorySegment .NULL ;
407+ }
408+ }
228409 }
229410
230411 // ===== Transaction Spent Outputs
0 commit comments