-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTransaction.swift
More file actions
407 lines (358 loc) · 15.8 KB
/
Copy pathTransaction.swift
File metadata and controls
407 lines (358 loc) · 15.8 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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
//
// Transaction.swift
// Cardano
//
// Created by hellc.
// Copyright © 2020-2026 KXP. All rights reserved.
// Licensed under the MIT License.
//
import Foundation
import CCardano
/// Represents the body of a Cardano transaction.
///
/// ### Example
/// ```swift
/// let body = try builder.build(changeAddress: myAddress)
/// let fee = try body.fee()
/// ```
public class TransactionBody {
internal let pointer: RPtr
internal init(pointer: RPtr) {
self.pointer = pointer
}
/// Initializes a new TransactionBody with inputs, outputs, fee, and optional parameters.
public init(
inputs: TransactionInputs,
outputs: TransactionOutputs,
fee: BigNum,
ttl: UInt64? = nil
) throws {
self.pointer = try CSL.callRPtr { csl_bridge_transaction_body_new(inputs.pointer, outputs.pointer, fee.pointer, $0, $1) }
}
/// Returns the inputs for this transaction body.
public func inputs() throws -> TransactionInputs {
let ptr = try CSL.callRPtr { csl_bridge_transaction_body_inputs(pointer, $0, $1) }
return TransactionInputs(pointer: ptr)
}
/// Returns the outputs for this transaction body.
public func outputs() throws -> TransactionOutputs {
let ptr = try CSL.callRPtr { csl_bridge_transaction_body_outputs(pointer, $0, $1) }
return TransactionOutputs(pointer: ptr)
}
/// Returns the fee for this transaction body.
public func fee() throws -> BigNum {
let ptr = try CSL.callRPtr { csl_bridge_transaction_body_fee(pointer, $0, $1) }
return BigNum(pointer: ptr)
}
deinit {
var p = pointer
csl_bridge_rptr_free(&p)
}
}
/// Represents an input to a Cardano transaction (UTXO reference).
public class TransactionInput {
internal let pointer: RPtr
internal init(pointer: RPtr) {
self.pointer = pointer
}
/// Initializes a TransactionInput from a transaction hash and output index.
/// - Parameter hash: The transaction hash (32 bytes) of the UTXO.
/// - Parameter index: The output index within the transaction.
/// - Throws: CardanoError if initialization fails.
public init(hash: Data, index: UInt32) throws {
let hashPtr = try hash.withUnsafeBytes { ptr in
try CSL.callRPtr { csl_bridge_transaction_hash_from_bytes(ptr.bindMemory(to: UInt8.self).baseAddress!, uintptr_t(hash.count), $0, $1) }
}
self.pointer = try CSL.callRPtr { csl_bridge_transaction_input_new(hashPtr, Int64(index), $0, $1) }
var p = hashPtr
csl_bridge_rptr_free(&p)
}
/// Returns the output index of this input.
/// - Returns: The output index.
/// - Throws: CardanoError if retrieval fails.
public func index() throws -> Int {
Int(try CSL.call { csl_bridge_transaction_input_index(pointer, $0, $1) })
}
/// Returns the transaction ID (hash) of this input.
/// - Returns: The transaction ID bytes.
/// - Throws: CardanoError if retrieval fails.
public func transactionId() throws -> Data {
let hashPtr = try CSL.callRPtr { csl_bridge_transaction_input_transaction_id(pointer, $0, $1) }
let data = try CSL.getData { csl_bridge_transaction_hash_to_bytes(hashPtr, $0, $1) }
var p = hashPtr
csl_bridge_rptr_free(&p)
return data
}
deinit {
var p = pointer
csl_bridge_rptr_free(&p)
}
}
/// Represents the witness set (signatures) for a Cardano transaction.
public class TransactionWitnessSet {
internal let pointer: RPtr
internal init(pointer: RPtr) {
self.pointer = pointer
}
/// Initializes a new empty TransactionWitnessSet.
/// - Returns: A new empty TransactionWitnessSet.
/// - Throws: CardanoError if initialization fails.
public init() throws {
self.pointer = try CSL.callRPtr { csl_bridge_transaction_witness_set_new($0, $1) }
}
/// Sets the Plutus scripts for smart contract validation.
/// - Parameter scripts: A PlutusScripts collection containing the scripts.
/// - Throws: CardanoError if setting scripts fails.
public func setPlutusScripts(scripts: PlutusScripts) throws {
try CSL.voidCall { csl_bridge_transaction_witness_set_set_plutus_scripts(pointer, scripts.pointer, $0) }
}
/// Sets the Plutus data (datums) required by the scripts.
/// - Parameter data: A PlutusList containing the datum objects.
/// - Throws: CardanoError if setting data fails.
public func setPlutusData(data: PlutusList) throws {
try CSL.voidCall { csl_bridge_transaction_witness_set_set_plutus_data(pointer, data.pointer, $0) }
}
/// Sets the redeemers (script execution arguments) for this transaction.
/// - Parameter redeemers: A Redeemers collection containing the redeemer objects.
/// - Throws: CardanoError if setting redeemers fails.
public func setRedeemers(redeemers: Redeemers) throws {
try CSL.voidCall { csl_bridge_transaction_witness_set_set_redeemers(pointer, redeemers.pointer, $0) }
}
deinit {
var p = pointer
csl_bridge_rptr_free(&p)
}
}
/// Represents a complete Cardano transaction, including the body and witness set (signatures).
///
/// ### Example
/// ```swift
/// let tx = try Transaction(body: body, witnessSet: witnesses)
/// let hex = try tx.toHex()
/// ```
public class Transaction {
/// Opaque pointer to the underlying Rust transaction object.
private let pointer: RPtr
/// Initializes a transaction with its components.
/// - Parameters:
/// - body: The `TransactionBody`.
/// - witnessSet: The `TransactionWitnessSet` containing signatures.
/// - auxiliaryData: Optional `AuxiliaryData`.
public init(body: TransactionBody, witnessSet: TransactionWitnessSet, auxiliaryData: AuxiliaryData? = nil) throws {
if let aux = auxiliaryData {
self.pointer = try CSL.callRPtr { csl_bridge_transaction_new_with_auxiliary_data(body.pointer, witnessSet.pointer, aux.pointer, $0, $1) }
} else {
self.pointer = try CSL.callRPtr { csl_bridge_transaction_new(body.pointer, witnessSet.pointer, $0, $1) }
}
}
deinit {
var p = pointer
csl_bridge_rptr_free(&p)
}
/// Deserializes a transaction from a Hexadecimal string.
/// - Parameter hex: The hex encoded CBOR of the transaction.
public static func fromHex(_ hex: String) throws -> Transaction {
let ptr = try CSL.callRPtr { csl_bridge_transaction_from_hex(hex, $0, $1) }
return Transaction(pointer: ptr)
}
/// Internal initializer for creating transactions from existing pointers.
private init(pointer: RPtr) {
self.pointer = pointer
}
/// Computes the transaction hash.
/// Uses a "fixed" transaction body bridge to ensure consistent hashing across different CSL versions.
/// - Returns: The transaction hash as a hexadecimal string.
/// - Throws: CardanoError if hashing fails.
public func hash() throws -> String {
let bodyPtr = try CSL.callRPtr { csl_bridge_transaction_body(pointer, $0, $1) }
let bodyData = try CSL.getData { csl_bridge_transaction_body_to_bytes(bodyPtr, $0, $1) }
let fixedBody = try CSL.callRPtr { (res: UnsafeMutablePointer<RPtr>, err: UnsafeMutablePointer<CharPtr?>) -> Bool in
bodyData.withUnsafeBytes { ptr in
csl_bridge_fixed_transaction_body_from_bytes(ptr.bindMemory(to: UInt8.self).baseAddress!, uintptr_t(bodyData.count), res, err)
}
}
let hash = try CSL.callRPtr { csl_bridge_fixed_transaction_body_tx_hash(fixedBody, $0, $1) }
let hex = try CSL.getString { csl_bridge_transaction_hash_to_hex(hash, $0, $1) }
var p1 = bodyPtr
var p2 = fixedBody
var p3 = hash
csl_bridge_rptr_free(&p1)
csl_bridge_rptr_free(&p2)
csl_bridge_rptr_free(&p3)
return hex
}
/// Returns the body for this transaction.
public func body() throws -> TransactionBody {
let ptr = try CSL.callRPtr { csl_bridge_transaction_body(pointer, $0, $1) }
return TransactionBody(pointer: ptr)
}
/// Returns the witness set for this transaction.
public func witnessSet() throws -> TransactionWitnessSet {
let ptr = try CSL.callRPtr { csl_bridge_transaction_witness_set(pointer, $0, $1) }
return TransactionWitnessSet(pointer: ptr)
}
/// Returns the auxiliary data (metadata) for this transaction, if present.
public func auxiliaryData() throws -> AuxiliaryData? {
let ptr = try CSL.callRPtr { csl_bridge_transaction_auxiliary_data(pointer, $0, $1) }
guard ptr._0 != nil else { return nil }
return AuxiliaryData(pointer: ptr)
}
/// Returns the Hexadecimal (CBOR) representation of the full transaction.
/// - Returns: The transaction encoded as a hex string.
/// - Throws: CardanoError if serialization fails.
public func toHex() throws -> String {
return try CSL.getString { csl_bridge_transaction_to_hex(pointer, $0, $1) }
}
/// Returns the raw bytes (CBOR) of the full transaction.
/// - Returns: The transaction bytes as an array of UInt8.
/// - Throws: CardanoError if serialization fails.
public func toBytes() throws -> [UInt8] {
let data = try CSL.getData { csl_bridge_transaction_to_bytes(pointer, $0, $1) }
return [UInt8](data)
}
// MARK: - Priority 3: Async Batch Operations
/// Asynchronously serializes multiple transactions to hex format in parallel
/// - Parameter transactions: Array of Transaction objects to serialize
/// - Returns: Array of hex-encoded transaction strings
public static func toHex(transactions: [Transaction]) async throws -> [String] {
return try await withThrowingTaskGroup(
of: (Int, String).self,
returning: [String].self
) { group in
for (index, tx) in transactions.enumerated() {
group.addTask {
let hex = try tx.toHex()
return (index, hex)
}
}
var results = Array(repeating: "", count: transactions.count)
for try await (index, hex) in group {
results[index] = hex
}
return results
}
}
/// Asynchronously parses multiple transactions from hex format in parallel
/// - Parameter hexStrings: Array of hex-encoded transaction strings
/// - Returns: Array of parsed Transaction objects
public static func fromHex(hexStrings: [String]) async throws -> [Transaction] {
return try await withThrowingTaskGroup(
of: (Int, Transaction).self,
returning: [Transaction].self
) { group in
for (index, hex) in hexStrings.enumerated() {
group.addTask {
let tx = try Transaction.fromHex(hex)
return (index, tx)
}
}
var tempResults: [Int: Transaction] = [:]
for try await (index, tx) in group {
tempResults[index] = tx
}
return (0..<hexStrings.count).compactMap { tempResults[$0] }
}
}
}
extension Wallet {
/// Signs a transaction body using the provided keychain.
/// - Parameters:
/// - transactionBody: The `TransactionBody` to sign.
/// - keychain: The derived keychain (usually at index m/1852'/1815'/0'/0/X) to sign with.
/// - Returns: A complete `Transaction` object ready for submission.
/// - Throws: CardanoError if signing fails.
public func sign(transactionBody: TransactionBody, keychain: Keychain) throws -> Transaction {
let bodyData = try CSL.getData { csl_bridge_transaction_body_to_bytes(transactionBody.pointer, $0, $1) }
let fixedBody = try CSL.callRPtr { (res: UnsafeMutablePointer<RPtr>, err: UnsafeMutablePointer<CharPtr?>) -> Bool in
bodyData.withUnsafeBytes { ptr in
csl_bridge_fixed_transaction_body_from_bytes(ptr.bindMemory(to: UInt8.self).baseAddress!, uintptr_t(bodyData.count), res, err)
}
}
let hash = try CSL.callRPtr { csl_bridge_fixed_transaction_body_tx_hash(fixedBody, $0, $1) }
let vkeyWitnesses = try CSL.callRPtr { csl_bridge_vkeywitnesses_new($0, $1) }
let privateKey = try keychain.privateKey()
let vkeyWitness = try CSL.callRPtr { csl_bridge_make_vkey_witness(hash, privateKey.pointer, $0, $1) }
_ = try CSL.call { (res: UnsafeMutablePointer<Bool>, err: UnsafeMutablePointer<CharPtr?>) -> Bool in
return csl_bridge_vkeywitnesses_add(vkeyWitnesses, vkeyWitness, res, err)
}
let witnessSet = try CSL.callRPtr { csl_bridge_transaction_witness_set_new($0, $1) }
try CSL.voidCall { csl_bridge_transaction_witness_set_set_vkeys(witnessSet, vkeyWitnesses, $0) }
var p1 = fixedBody
var p2 = hash
var p3 = vkeyWitnesses
var p4 = vkeyWitness
csl_bridge_rptr_free(&p1)
csl_bridge_rptr_free(&p2)
csl_bridge_rptr_free(&p3)
csl_bridge_rptr_free(&p4)
return try Transaction(body: transactionBody, witnessSet: TransactionWitnessSet(pointer: witnessSet), auxiliaryData: nil)
}
/// Asynchronously signs a transaction body using the provided keychain.
/// - Parameters:
/// - transactionBody: The `TransactionBody` to sign.
/// - keychain: The derived keychain (usually at index m/1852'/1815'/0'/0/X) to sign with.
/// - Returns: A complete `Transaction` object ready for submission.
/// - Throws: CardanoError if signing fails.
public func sign(transactionBody: TransactionBody, keychain: Keychain) async throws -> Transaction {
return try await Task.detached(priority: .userInitiated) {
try self.sign(transactionBody: transactionBody, keychain: keychain)
}.value
}
}
/// A collection of transaction inputs.
public class TransactionInputs {
internal let pointer: RPtr
internal init(pointer: RPtr) {
self.pointer = pointer
}
public init() throws {
self.pointer = try CSL.callRPtr { csl_bridge_transaction_inputs_new($0, $1) }
}
public func add(input: TransactionInput) throws {
_ = try CSL.call { (res: UnsafeMutablePointer<Bool>, err: UnsafeMutablePointer<CharPtr?>) -> Bool in
csl_bridge_transaction_inputs_add(pointer, input.pointer, res, err)
}
}
public func len() throws -> Int {
Int(try CSL.call { csl_bridge_transaction_inputs_len(pointer, $0, $1) })
}
deinit {
var p = pointer
csl_bridge_rptr_free(&p)
}
}
/// Represents an output of a Cardano transaction.
public class TransactionOutput {
internal let pointer: RPtr
internal init(pointer: RPtr) {
self.pointer = pointer
}
public init(address: Address, amount: Value) throws {
let valuePtr = try amount.toPointer()
self.pointer = try CSL.callRPtr { csl_bridge_transaction_output_new(address.pointer, valuePtr, $0, $1) }
}
deinit {
var p = pointer
csl_bridge_rptr_free(&p)
}
}
/// A collection of transaction outputs.
public class TransactionOutputs {
internal let pointer: RPtr
internal init(pointer: RPtr) {
self.pointer = pointer
}
public init() throws {
self.pointer = try CSL.callRPtr { csl_bridge_transaction_outputs_new($0, $1) }
}
public func add(output: TransactionOutput) throws {
try CSL.voidCall { csl_bridge_transaction_outputs_add(pointer, output.pointer, $0) }
}
public func len() throws -> Int {
Int(try CSL.call { csl_bridge_transaction_outputs_len(pointer, $0, $1) })
}
deinit {
var p = pointer
csl_bridge_rptr_free(&p)
}
}