Skip to content

Commit f8cace8

Browse files
committed
fix: some ctor for Transactions related classes
1 parent 64354c6 commit f8cace8

File tree

2 files changed

+194
-13
lines changed

2 files changed

+194
-13
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55

66
java {
77
toolchain {
8-
languageVersion = JavaLanguageVersion.of(25)
8+
languageVersion = JavaLanguageVersion.of(24)
99
}
1010
}
1111

src/main/java/org/bitcoinkernel/Transactions.java

Lines changed: 193 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,38 @@
1010
public 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

Comments
 (0)