commonMain.com.lightspark.sdk.crypto.internal.lightspark_crypto.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lightspark-crypto-jvm Show documentation
Show all versions of lightspark-crypto-jvm Show documentation
The Lightspark shared crypto library.
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
@file:Suppress("NAME_SHADOWING")
package com.lightspark.sdk.crypto.internal
// Common helper code.
//
// Ideally this would live in a separate .kt file where it can be unittested etc
// in isolation, and perhaps even published as a re-useable package.
//
// However, it's important that the detils of how this helper code works (e.g. the
// way that different builtin types are passed across the FFI) exactly match what's
// expected by the Rust code on the other side of the interface. In practice right
// now that means coming from the exact some version of `uniffi` that was used to
// compile the Rust component. The easiest way to ensure this is to bundle the Kotlin
// helpers directly inline like we're doing here.
import com.sun.jna.Library
import com.sun.jna.Native
import com.sun.jna.Pointer
import com.sun.jna.Structure
import com.sun.jna.ptr.ByReference
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
// This is a helper for safely working with byte buffers returned from the Rust code.
// A rust-owned buffer is represented by its capacity, its current length, and a
// pointer to the underlying data.
@Structure.FieldOrder("capacity", "len", "data")
open class RustBuffer : Structure() {
@JvmField var capacity: Int = 0
@JvmField var len: Int = 0
@JvmField var data: Pointer? = null
class ByValue : RustBuffer(), Structure.ByValue
class ByReference : RustBuffer(), Structure.ByReference
companion object {
internal fun alloc(size: Int = 0) =
rustCall { status ->
_UniFFILib.INSTANCE.ffi_lightspark_crypto_420_rustbuffer_alloc(size, status).also {
if (it.data == null) {
throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=$size)")
}
}
}
internal fun free(buf: RustBuffer.ByValue) =
rustCall { status ->
_UniFFILib.INSTANCE.ffi_lightspark_crypto_420_rustbuffer_free(buf, status)
}
}
@Suppress("TooGenericExceptionThrown")
fun asByteBuffer() =
this.data?.getByteBuffer(0, this.len.toLong())?.also {
it.order(ByteOrder.BIG_ENDIAN)
}
}
/**
* The equivalent of the `*mut RustBuffer` type.
* Required for callbacks taking in an out pointer.
*
* Size is the sum of all values in the struct.
*/
class RustBufferByReference : ByReference(16) {
/**
* Set the pointed-to `RustBuffer` to the given value.
*/
fun setValue(value: RustBuffer.ByValue) {
// NOTE: The offsets are as they are in the C-like struct.
val pointer = getPointer()
pointer.setInt(0, value.capacity)
pointer.setInt(4, value.len)
pointer.setPointer(8, value.data)
}
}
// This is a helper for safely passing byte references into the rust code.
// It's not actually used at the moment, because there aren't many things that you
// can take a direct pointer to in the JVM, and if we're going to copy something
// then we might as well copy it into a `RustBuffer`. But it's here for API
// completeness.
@Structure.FieldOrder("len", "data")
open class ForeignBytes : Structure() {
@JvmField var len: Int = 0
@JvmField var data: Pointer? = null
class ByValue : ForeignBytes(), Structure.ByValue
}
// The FfiConverter interface handles converter types to and from the FFI
//
// All implementing objects should be public to support external types. When a
// type is external we need to import it's FfiConverter.
public interface FfiConverter {
// Convert an FFI type to a Kotlin type
fun lift(value: FfiType): KotlinType
// Convert an Kotlin type to an FFI type
fun lower(value: KotlinType): FfiType
// Read a Kotlin type from a `ByteBuffer`
fun read(buf: ByteBuffer): KotlinType
// Calculate bytes to allocate when creating a `RustBuffer`
//
// This must return at least as many bytes as the write() function will
// write. It can return more bytes than needed, for example when writing
// Strings we can't know the exact bytes needed until we the UTF-8
// encoding, so we pessimistically allocate the largest size possible (3
// bytes per codepoint). Allocating extra bytes is not really a big deal
// because the `RustBuffer` is short-lived.
fun allocationSize(value: KotlinType): Int
// Write a Kotlin type to a `ByteBuffer`
fun write(
value: KotlinType,
buf: ByteBuffer,
)
// Lower a value into a `RustBuffer`
//
// This method lowers a value into a `RustBuffer` rather than the normal
// FfiType. It's used by the callback interface code. Callback interface
// returns are always serialized into a `RustBuffer` regardless of their
// normal FFI type.
fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue {
val rbuf = RustBuffer.alloc(allocationSize(value))
try {
val bbuf =
rbuf.data!!.getByteBuffer(0, rbuf.capacity.toLong()).also {
it.order(ByteOrder.BIG_ENDIAN)
}
write(value, bbuf)
rbuf.writeField("len", bbuf.position())
return rbuf
} catch (e: Throwable) {
RustBuffer.free(rbuf)
throw e
}
}
// Lift a value from a `RustBuffer`.
//
// This here mostly because of the symmetry with `lowerIntoRustBuffer()`.
// It's currently only used by the `FfiConverterRustBuffer` class below.
fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType {
val byteBuf = rbuf.asByteBuffer()!!
try {
val item = read(byteBuf)
if (byteBuf.hasRemaining()) {
throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!")
}
return item
} finally {
RustBuffer.free(rbuf)
}
}
}
// FfiConverter that uses `RustBuffer` as the FfiType
public interface FfiConverterRustBuffer : FfiConverter {
override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value)
override fun lower(value: KotlinType) = lowerIntoRustBuffer(value)
}
// A handful of classes and functions to support the generated data structures.
// This would be a good candidate for isolating in its own ffi-support lib.
// Error runtime.
@Structure.FieldOrder("code", "error_buf")
internal open class RustCallStatus : Structure() {
@JvmField var code: Int = 0
@JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue()
fun isSuccess(): Boolean {
return code == 0
}
fun isError(): Boolean {
return code == 1
}
fun isPanic(): Boolean {
return code == 2
}
}
class InternalException(message: String) : Exception(message)
// Each top-level error class has a companion object that can lift the error from the call status's rust buffer
interface CallStatusErrorHandler {
fun lift(error_buf: RustBuffer.ByValue): E
}
// Helpers for calling Rust
// In practice we usually need to be synchronized to call this safely, so it doesn't
// synchronize itself
// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err
private inline fun rustCallWithError(
errorHandler: CallStatusErrorHandler,
callback: (RustCallStatus) -> U,
): U {
var status = RustCallStatus()
val return_value = callback(status)
if (status.isSuccess()) {
return return_value
} else if (status.isError()) {
throw errorHandler.lift(status.error_buf)
} else if (status.isPanic()) {
// when the rust code sees a panic, it tries to construct a rustbuffer
// with the message. but if that code panics, then it just sends back
// an empty buffer.
if (status.error_buf.len > 0) {
throw InternalException(FfiConverterString.lift(status.error_buf))
} else {
throw InternalException("Rust panic")
}
} else {
throw InternalException("Unknown rust call status: $status.code")
}
}
// CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR
object NullCallStatusErrorHandler : CallStatusErrorHandler {
override fun lift(error_buf: RustBuffer.ByValue): InternalException {
RustBuffer.free(error_buf)
return InternalException("Unexpected CALL_ERROR")
}
}
// Call a rust function that returns a plain value
private inline fun rustCall(callback: (RustCallStatus) -> U): U {
return rustCallWithError(NullCallStatusErrorHandler, callback)
}
// Contains loading, initialization code,
// and the FFI Function declarations in a com.sun.jna.Library.
@Synchronized
private fun findLibraryName(componentName: String): String {
val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride")
if (libOverride != null) {
return libOverride
}
return "uniffi_lightspark_crypto"
}
private inline fun loadIndirect(componentName: String): Lib {
return Native.load(findLibraryName(componentName), Lib::class.java)
}
// A JNA Library to expose the extern-C FFI definitions.
// This is an implementation detail which will be called internally by the public API.
internal interface _UniFFILib : Library {
companion object {
internal val INSTANCE: _UniFFILib by lazy {
loadIndirect<_UniFFILib>(componentName = "lightspark_crypto")
.also { lib: _UniFFILib ->
FfiConverterTypeValidation.register(lib)
}
}
}
fun ffi_lightspark_crypto_420_KeyPair_object_free(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): Unit
fun lightspark_crypto_420_KeyPair_get_public_key(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_KeyPair_get_private_key(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun ffi_lightspark_crypto_420_Mnemonic_object_free(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): Unit
fun lightspark_crypto_420_Mnemonic_random(_uniffi_out_err: RustCallStatus): Pointer
fun lightspark_crypto_420_Mnemonic_from_entropy(
`entropy`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Pointer
fun lightspark_crypto_420_Mnemonic_from_phrase(
`phrase`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Pointer
fun lightspark_crypto_420_Mnemonic_as_string(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun ffi_lightspark_crypto_420_Seed_object_free(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): Unit
fun lightspark_crypto_420_Seed_new(
`seed`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Pointer
fun lightspark_crypto_420_Seed_from_mnemonic(
`mnemonic`: Pointer,
_uniffi_out_err: RustCallStatus,
): Pointer
fun lightspark_crypto_420_Seed_as_bytes(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun ffi_lightspark_crypto_420_InvoiceSignature_object_free(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): Unit
fun lightspark_crypto_420_InvoiceSignature_get_recovery_id(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): Int
fun lightspark_crypto_420_InvoiceSignature_get_signature(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun ffi_lightspark_crypto_420_LightsparkSigner_object_free(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): Unit
fun lightspark_crypto_420_LightsparkSigner_new(
`seed`: Pointer,
`network`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Pointer
fun lightspark_crypto_420_LightsparkSigner_from_bytes(
`seed`: RustBuffer.ByValue,
`network`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Pointer
fun lightspark_crypto_420_LightsparkSigner_get_master_public_key(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_derive_public_key(
`ptr`: Pointer,
`derivationPath`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_derive_public_key_hex(
`ptr`: Pointer,
`derivationPath`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_derive_private_key(
`ptr`: Pointer,
`derivationPath`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_ecdh(
`ptr`: Pointer,
`publicKey`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_sign_invoice(
`ptr`: Pointer,
`unsignedInvoice`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Pointer
fun lightspark_crypto_420_LightsparkSigner_sign_invoice_hash(
`ptr`: Pointer,
`unsignedInvoice`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Pointer
fun lightspark_crypto_420_LightsparkSigner_derive_key_and_sign(
`ptr`: Pointer,
`message`: RustBuffer.ByValue,
`derivationPath`: RustBuffer.ByValue,
`isRaw`: Byte,
`addTweak`: RustBuffer.ByValue,
`mulTweak`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_get_per_commitment_point(
`ptr`: Pointer,
`derivationPath`: RustBuffer.ByValue,
`perCommitmentPointIdx`: Long,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_release_per_commitment_secret(
`ptr`: Pointer,
`derivationPath`: RustBuffer.ByValue,
`perCommitmentPointIdx`: Long,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_generate_preimage_nonce(
`ptr`: Pointer,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_generate_preimage(
`ptr`: Pointer,
`nonce`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_LightsparkSigner_generate_preimage_hash(
`ptr`: Pointer,
`nonce`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun ffi_lightspark_crypto_420_Validation_init_callback(
`callbackStub`: ForeignCallback,
_uniffi_out_err: RustCallStatus,
): Unit
fun lightspark_crypto_420_sign_ecdsa(
`msg`: RustBuffer.ByValue,
`privateKeyBytes`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_verify_ecdsa(
`msg`: RustBuffer.ByValue,
`signatureBytes`: RustBuffer.ByValue,
`publicKeyBytes`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Byte
fun lightspark_crypto_420_encrypt_ecies(
`msg`: RustBuffer.ByValue,
`publicKeyBytes`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_decrypt_ecies(
`cipherText`: RustBuffer.ByValue,
`privateKeyBytes`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun lightspark_crypto_420_generate_keypair(_uniffi_out_err: RustCallStatus): Pointer
fun lightspark_crypto_420_handle_remote_signing_webhook_event(
`webhookData`: RustBuffer.ByValue,
`webhookSignature`: RustBuffer.ByValue,
`webhookSecret`: RustBuffer.ByValue,
`masterSeedBytes`: RustBuffer.ByValue,
`validation`: Long,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun ffi_lightspark_crypto_420_rustbuffer_alloc(
`size`: Int,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun ffi_lightspark_crypto_420_rustbuffer_from_bytes(
`bytes`: ForeignBytes.ByValue,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
fun ffi_lightspark_crypto_420_rustbuffer_free(
`buf`: RustBuffer.ByValue,
_uniffi_out_err: RustCallStatus,
): Unit
fun ffi_lightspark_crypto_420_rustbuffer_reserve(
`buf`: RustBuffer.ByValue,
`additional`: Int,
_uniffi_out_err: RustCallStatus,
): RustBuffer.ByValue
}
// Public interface members begin here.
public object FfiConverterUByte : FfiConverter {
override fun lift(value: Byte): UByte {
return value.toUByte()
}
override fun read(buf: ByteBuffer): UByte {
return lift(buf.get())
}
override fun lower(value: UByte): Byte {
return value.toByte()
}
override fun allocationSize(value: UByte) = 1
override fun write(
value: UByte,
buf: ByteBuffer,
) {
buf.put(value.toByte())
}
}
public object FfiConverterInt : FfiConverter {
override fun lift(value: Int): Int {
return value
}
override fun read(buf: ByteBuffer): Int {
return buf.getInt()
}
override fun lower(value: Int): Int {
return value
}
override fun allocationSize(value: Int) = 4
override fun write(
value: Int,
buf: ByteBuffer,
) {
buf.putInt(value)
}
}
public object FfiConverterULong : FfiConverter {
override fun lift(value: Long): ULong {
return value.toULong()
}
override fun read(buf: ByteBuffer): ULong {
return lift(buf.getLong())
}
override fun lower(value: ULong): Long {
return value.toLong()
}
override fun allocationSize(value: ULong) = 8
override fun write(
value: ULong,
buf: ByteBuffer,
) {
buf.putLong(value.toLong())
}
}
public object FfiConverterBoolean : FfiConverter {
override fun lift(value: Byte): Boolean {
return value.toInt() != 0
}
override fun read(buf: ByteBuffer): Boolean {
return lift(buf.get())
}
override fun lower(value: Boolean): Byte {
return if (value) 1.toByte() else 0.toByte()
}
override fun allocationSize(value: Boolean) = 1
override fun write(
value: Boolean,
buf: ByteBuffer,
) {
buf.put(lower(value))
}
}
public object FfiConverterString : FfiConverter {
// Note: we don't inherit from FfiConverterRustBuffer, because we use a
// special encoding when lowering/lifting. We can use `RustBuffer.len` to
// store our length and avoid writing it out to the buffer.
override fun lift(value: RustBuffer.ByValue): String {
try {
val byteArr = ByteArray(value.len)
value.asByteBuffer()!!.get(byteArr)
return byteArr.toString(Charsets.UTF_8)
} finally {
RustBuffer.free(value)
}
}
override fun read(buf: ByteBuffer): String {
val len = buf.getInt()
val byteArr = ByteArray(len)
buf.get(byteArr)
return byteArr.toString(Charsets.UTF_8)
}
override fun lower(value: String): RustBuffer.ByValue {
val byteArr = value.toByteArray(Charsets.UTF_8)
// Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us
// to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`.
val rbuf = RustBuffer.alloc(byteArr.size)
rbuf.asByteBuffer()!!.put(byteArr)
return rbuf
}
// We aren't sure exactly how many bytes our string will be once it's UTF-8
// encoded. Allocate 3 bytes per unicode codepoint which will always be
// enough.
override fun allocationSize(value: String): Int {
val sizeForLength = 4
val sizeForString = value.length * 3
return sizeForLength + sizeForString
}
override fun write(
value: String,
buf: ByteBuffer,
) {
val byteArr = value.toByteArray(Charsets.UTF_8)
buf.putInt(byteArr.size)
buf.put(byteArr)
}
}
// Interface implemented by anything that can contain an object reference.
//
// Such types expose a `destroy()` method that must be called to cleanly
// dispose of the contained objects. Failure to call this method may result
// in memory leaks.
//
// The easiest way to ensure this method is called is to use the `.use`
// helper method to execute a block and destroy the object at the end.
interface Disposable {
fun destroy()
companion object {
fun destroy(vararg args: Any?) {
args.filterIsInstance()
.forEach(Disposable::destroy)
}
}
}
inline fun T.use(block: (T) -> R) =
try {
block(this)
} finally {
try {
// N.B. our implementation is on the nullable type `Disposable?`.
this?.destroy()
} catch (e: Throwable) {
// swallow
}
}
// The base class for all UniFFI Object types.
//
// This class provides core operations for working with the Rust `Arc` pointer to
// the live Rust struct on the other side of the FFI.
//
// There's some subtlety here, because we have to be careful not to operate on a Rust
// struct after it has been dropped, and because we must expose a public API for freeing
// the Kotlin wrapper object in lieu of reliable finalizers. The core requirements are:
//
// * Each `FFIObject` instance holds an opaque pointer to the underlying Rust struct.
// Method calls need to read this pointer from the object's state and pass it in to
// the Rust FFI.
//
// * When an `FFIObject` is no longer needed, its pointer should be passed to a
// special destructor function provided by the Rust FFI, which will drop the
// underlying Rust struct.
//
// * Given an `FFIObject` instance, calling code is expected to call the special
// `destroy` method in order to free it after use, either by calling it explicitly
// or by using a higher-level helper like the `use` method. Failing to do so will
// leak the underlying Rust struct.
//
// * We can't assume that calling code will do the right thing, and must be prepared
// to handle Kotlin method calls executing concurrently with or even after a call to
// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`.
//
// * We must never allow Rust code to operate on the underlying Rust struct after
// the destructor has been called, and must never call the destructor more than once.
// Doing so may trigger memory unsafety.
//
// If we try to implement this with mutual exclusion on access to the pointer, there is the
// possibility of a race between a method call and a concurrent call to `destroy`:
//
// * Thread A starts a method call, reads the value of the pointer, but is interrupted
// before it can pass the pointer over the FFI to Rust.
// * Thread B calls `destroy` and frees the underlying Rust struct.
// * Thread A resumes, passing the already-read pointer value to Rust and triggering
// a use-after-free.
//
// One possible solution would be to use a `ReadWriteLock`, with each method call taking
// a read lock (and thus allowed to run concurrently) and the special `destroy` method
// taking a write lock (and thus blocking on live method calls). However, we aim not to
// generate methods with any hidden blocking semantics, and a `destroy` method that might
// block if called incorrectly seems to meet that bar.
//
// So, we achieve our goals by giving each `FFIObject` an associated `AtomicLong` counter to track
// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy`
// has been called. These are updated according to the following rules:
//
// * The initial value of the counter is 1, indicating a live object with no in-flight calls.
// The initial value for the flag is false.
//
// * At the start of each method call, we atomically check the counter.
// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted.
// If it is nonzero them we atomically increment it by 1 and proceed with the method call.
//
// * At the end of each method call, we atomically decrement and check the counter.
// If it has reached zero then we destroy the underlying Rust struct.
//
// * When `destroy` is called, we atomically flip the flag from false to true.
// If the flag was already true we silently fail.
// Otherwise we atomically decrement and check the counter.
// If it has reached zero then we destroy the underlying Rust struct.
//
// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works,
// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`.
//
// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been
// called *and* all in-flight method calls have completed, avoiding violating any of the expectations
// of the underlying Rust code.
//
// In the future we may be able to replace some of this with automatic finalization logic, such as using
// the new "Cleaner" functionaility in Java 9. The above scheme has been designed to work even if `destroy` is
// invoked by garbage-collection machinery rather than by calling code (which by the way, it's apparently also
// possible for the JVM to finalize an object while there is an in-flight call to one of its methods [1],
// so there would still be some complexity here).
//
// Sigh...all of this for want of a robust finalization mechanism.
//
// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219
//
abstract class FFIObject(
protected val pointer: Pointer,
) : Disposable, AutoCloseable {
private val wasDestroyed = AtomicBoolean(false)
private val callCounter = AtomicLong(1)
protected open fun freeRustArcPtr() {
// To be overridden in subclasses.
}
override fun destroy() {
// Only allow a single call to this method.
// TODO: maybe we should log a warning if called more than once?
if (this.wasDestroyed.compareAndSet(false, true)) {
// This decrement always matches the initial count of 1 given at creation time.
if (this.callCounter.decrementAndGet() == 0L) {
this.freeRustArcPtr()
}
}
}
@Synchronized
override fun close() {
this.destroy()
}
internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R {
// Check and increment the call counter, to keep the object alive.
// This needs a compare-and-set retry loop in case of concurrent updates.
do {
val c = this.callCounter.get()
if (c == 0L) {
throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed")
}
if (c == Long.MAX_VALUE) {
throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow")
}
} while (!this.callCounter.compareAndSet(c, c + 1L))
// Now we can safely do the method call without the pointer being freed concurrently.
try {
return block(this.pointer)
} finally {
// This decrement always matches the increment we performed above.
if (this.callCounter.decrementAndGet() == 0L) {
this.freeRustArcPtr()
}
}
}
}
public interface InvoiceSignatureInterface {
fun `getRecoveryId`(): Int
fun `getSignature`(): List
}
class InvoiceSignature(
pointer: Pointer,
) : FFIObject(pointer), InvoiceSignatureInterface {
/**
* Disconnect the object from the underlying Rust object.
*
* It can be called more than once, but once called, interacting with the object
* causes an `IllegalStateException`.
*
* Clients **must** call this method once done with the object, or cause a memory leak.
*/
protected override fun freeRustArcPtr() {
rustCall { status ->
_UniFFILib.INSTANCE.ffi_lightspark_crypto_420_InvoiceSignature_object_free(this.pointer, status)
}
}
override fun `getRecoveryId`(): Int =
callWithPointer {
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_InvoiceSignature_get_recovery_id(it, _status)
}
}.let {
FfiConverterInt.lift(it)
}
override fun `getSignature`(): List =
callWithPointer {
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_InvoiceSignature_get_signature(it, _status)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
}
public object FfiConverterTypeInvoiceSignature : FfiConverter {
override fun lower(value: InvoiceSignature): Pointer = value.callWithPointer { it }
override fun lift(value: Pointer): InvoiceSignature {
return InvoiceSignature(value)
}
override fun read(buf: ByteBuffer): InvoiceSignature {
// The Rust code always writes pointers as 8 bytes, and will
// fail to compile if they don't fit.
return lift(Pointer(buf.getLong()))
}
override fun allocationSize(value: InvoiceSignature) = 8
override fun write(
value: InvoiceSignature,
buf: ByteBuffer,
) {
// The Rust code always expects pointers written as 8 bytes,
// and will fail to compile if they don't fit.
buf.putLong(Pointer.nativeValue(lower(value)))
}
}
public interface KeyPairInterface {
fun `getPublicKey`(): List
fun `getPrivateKey`(): List
}
class KeyPair(
pointer: Pointer,
) : FFIObject(pointer), KeyPairInterface {
/**
* Disconnect the object from the underlying Rust object.
*
* It can be called more than once, but once called, interacting with the object
* causes an `IllegalStateException`.
*
* Clients **must** call this method once done with the object, or cause a memory leak.
*/
protected override fun freeRustArcPtr() {
rustCall { status ->
_UniFFILib.INSTANCE.ffi_lightspark_crypto_420_KeyPair_object_free(this.pointer, status)
}
}
override fun `getPublicKey`(): List =
callWithPointer {
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_KeyPair_get_public_key(it, _status)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
override fun `getPrivateKey`(): List =
callWithPointer {
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_KeyPair_get_private_key(it, _status)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
}
public object FfiConverterTypeKeyPair : FfiConverter {
override fun lower(value: KeyPair): Pointer = value.callWithPointer { it }
override fun lift(value: Pointer): KeyPair {
return KeyPair(value)
}
override fun read(buf: ByteBuffer): KeyPair {
// The Rust code always writes pointers as 8 bytes, and will
// fail to compile if they don't fit.
return lift(Pointer(buf.getLong()))
}
override fun allocationSize(value: KeyPair) = 8
override fun write(
value: KeyPair,
buf: ByteBuffer,
) {
// The Rust code always expects pointers written as 8 bytes,
// and will fail to compile if they don't fit.
buf.putLong(Pointer.nativeValue(lower(value)))
}
}
public interface LightsparkSignerInterface {
@Throws(LightsparkSignerException::class)
fun `getMasterPublicKey`(): String
@Throws(LightsparkSignerException::class)
fun `derivePublicKey`(`derivationPath`: String): String
@Throws(LightsparkSignerException::class)
fun `derivePublicKeyHex`(`derivationPath`: String): String
@Throws(LightsparkSignerException::class)
fun `derivePrivateKey`(`derivationPath`: String): String
@Throws(LightsparkSignerException::class)
fun `ecdh`(`publicKey`: List): List
@Throws(LightsparkSignerException::class)
fun `signInvoice`(`unsignedInvoice`: String): InvoiceSignature
@Throws(LightsparkSignerException::class)
fun `signInvoiceHash`(`unsignedInvoice`: List): InvoiceSignature
@Throws(LightsparkSignerException::class)
fun `deriveKeyAndSign`(
`message`: List,
`derivationPath`: String,
`isRaw`: Boolean,
`addTweak`: List?,
`mulTweak`: List?,
): List
@Throws(LightsparkSignerException::class)
fun `getPerCommitmentPoint`(
`derivationPath`: String,
`perCommitmentPointIdx`: ULong,
): List
@Throws(LightsparkSignerException::class)
fun `releasePerCommitmentSecret`(
`derivationPath`: String,
`perCommitmentPointIdx`: ULong,
): List
fun `generatePreimageNonce`(): List
@Throws(LightsparkSignerException::class)
fun `generatePreimage`(`nonce`: List): List
@Throws(LightsparkSignerException::class)
fun `generatePreimageHash`(`nonce`: List): List
}
class LightsparkSigner(
pointer: Pointer,
) : FFIObject(pointer), LightsparkSignerInterface {
constructor(`seed`: Seed, `network`: Network) :
this(
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_new(
FfiConverterTypeSeed.lower(`seed`),
FfiConverterTypeNetwork.lower(`network`),
_status,
)
},
)
/**
* Disconnect the object from the underlying Rust object.
*
* It can be called more than once, but once called, interacting with the object
* causes an `IllegalStateException`.
*
* Clients **must** call this method once done with the object, or cause a memory leak.
*/
protected override fun freeRustArcPtr() {
rustCall { status ->
_UniFFILib.INSTANCE.ffi_lightspark_crypto_420_LightsparkSigner_object_free(this.pointer, status)
}
}
@Throws(
LightsparkSignerException::class,
)
override fun `getMasterPublicKey`(): String =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_get_master_public_key(it, _status)
}
}.let {
FfiConverterString.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `derivePublicKey`(`derivationPath`: String): String =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_derive_public_key(
it,
FfiConverterString.lower(`derivationPath`),
_status,
)
}
}.let {
FfiConverterString.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `derivePublicKeyHex`(`derivationPath`: String): String =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_derive_public_key_hex(
it,
FfiConverterString.lower(`derivationPath`),
_status,
)
}
}.let {
FfiConverterString.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `derivePrivateKey`(`derivationPath`: String): String =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_derive_private_key(
it,
FfiConverterString.lower(`derivationPath`),
_status,
)
}
}.let {
FfiConverterString.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `ecdh`(`publicKey`: List): List =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_ecdh(it, FfiConverterSequenceUByte.lower(`publicKey`), _status)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `signInvoice`(`unsignedInvoice`: String): InvoiceSignature =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_sign_invoice(
it,
FfiConverterString.lower(`unsignedInvoice`),
_status,
)
}
}.let {
FfiConverterTypeInvoiceSignature.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `signInvoiceHash`(`unsignedInvoice`: List): InvoiceSignature =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_sign_invoice_hash(
it,
FfiConverterSequenceUByte.lower(`unsignedInvoice`),
_status,
)
}
}.let {
FfiConverterTypeInvoiceSignature.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `deriveKeyAndSign`(
`message`: List,
`derivationPath`: String,
`isRaw`: Boolean,
`addTweak`: List?,
`mulTweak`: List?,
): List =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_derive_key_and_sign(
it,
FfiConverterSequenceUByte.lower(`message`),
FfiConverterString.lower(`derivationPath`),
FfiConverterBoolean.lower(`isRaw`),
FfiConverterOptionalSequenceUByte.lower(`addTweak`),
FfiConverterOptionalSequenceUByte.lower(`mulTweak`),
_status,
)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `getPerCommitmentPoint`(
`derivationPath`: String,
`perCommitmentPointIdx`: ULong,
): List =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_get_per_commitment_point(
it,
FfiConverterString.lower(`derivationPath`),
FfiConverterULong.lower(`perCommitmentPointIdx`),
_status,
)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `releasePerCommitmentSecret`(
`derivationPath`: String,
`perCommitmentPointIdx`: ULong,
): List =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_release_per_commitment_secret(
it,
FfiConverterString.lower(`derivationPath`),
FfiConverterULong.lower(`perCommitmentPointIdx`),
_status,
)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
override fun `generatePreimageNonce`(): List =
callWithPointer {
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_generate_preimage_nonce(it, _status)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `generatePreimage`(`nonce`: List): List =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_generate_preimage(
it,
FfiConverterSequenceUByte.lower(`nonce`),
_status,
)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
@Throws(
LightsparkSignerException::class,
)
override fun `generatePreimageHash`(`nonce`: List): List =
callWithPointer {
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_generate_preimage_hash(
it,
FfiConverterSequenceUByte.lower(`nonce`),
_status,
)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
companion object {
fun `fromBytes`(
`seed`: List,
`network`: Network,
): LightsparkSigner =
LightsparkSigner(
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_LightsparkSigner_from_bytes(
FfiConverterSequenceUByte.lower(`seed`),
FfiConverterTypeNetwork.lower(`network`),
_status,
)
},
)
}
}
public object FfiConverterTypeLightsparkSigner : FfiConverter {
override fun lower(value: LightsparkSigner): Pointer = value.callWithPointer { it }
override fun lift(value: Pointer): LightsparkSigner {
return LightsparkSigner(value)
}
override fun read(buf: ByteBuffer): LightsparkSigner {
// The Rust code always writes pointers as 8 bytes, and will
// fail to compile if they don't fit.
return lift(Pointer(buf.getLong()))
}
override fun allocationSize(value: LightsparkSigner) = 8
override fun write(
value: LightsparkSigner,
buf: ByteBuffer,
) {
// The Rust code always expects pointers written as 8 bytes,
// and will fail to compile if they don't fit.
buf.putLong(Pointer.nativeValue(lower(value)))
}
}
public interface MnemonicInterface {
fun `asString`(): String
}
class Mnemonic(
pointer: Pointer,
) : FFIObject(pointer), MnemonicInterface {
/**
* Disconnect the object from the underlying Rust object.
*
* It can be called more than once, but once called, interacting with the object
* causes an `IllegalStateException`.
*
* Clients **must** call this method once done with the object, or cause a memory leak.
*/
protected override fun freeRustArcPtr() {
rustCall { status ->
_UniFFILib.INSTANCE.ffi_lightspark_crypto_420_Mnemonic_object_free(this.pointer, status)
}
}
override fun `asString`(): String =
callWithPointer {
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_Mnemonic_as_string(it, _status)
}
}.let {
FfiConverterString.lift(it)
}
companion object {
fun `random`(): Mnemonic =
Mnemonic(
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_Mnemonic_random(_status)
},
)
fun `fromEntropy`(`entropy`: List): Mnemonic =
Mnemonic(
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_Mnemonic_from_entropy(FfiConverterSequenceUByte.lower(`entropy`), _status)
},
)
fun `fromPhrase`(`phrase`: String): Mnemonic =
Mnemonic(
rustCallWithError(LightsparkSignerException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_Mnemonic_from_phrase(FfiConverterString.lower(`phrase`), _status)
},
)
}
}
public object FfiConverterTypeMnemonic : FfiConverter {
override fun lower(value: Mnemonic): Pointer = value.callWithPointer { it }
override fun lift(value: Pointer): Mnemonic {
return Mnemonic(value)
}
override fun read(buf: ByteBuffer): Mnemonic {
// The Rust code always writes pointers as 8 bytes, and will
// fail to compile if they don't fit.
return lift(Pointer(buf.getLong()))
}
override fun allocationSize(value: Mnemonic) = 8
override fun write(
value: Mnemonic,
buf: ByteBuffer,
) {
// The Rust code always expects pointers written as 8 bytes,
// and will fail to compile if they don't fit.
buf.putLong(Pointer.nativeValue(lower(value)))
}
}
public interface SeedInterface {
fun `asBytes`(): List
}
class Seed(
pointer: Pointer,
) : FFIObject(pointer), SeedInterface {
constructor(`seed`: List) :
this(
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_Seed_new(FfiConverterSequenceUByte.lower(`seed`), _status)
},
)
/**
* Disconnect the object from the underlying Rust object.
*
* It can be called more than once, but once called, interacting with the object
* causes an `IllegalStateException`.
*
* Clients **must** call this method once done with the object, or cause a memory leak.
*/
protected override fun freeRustArcPtr() {
rustCall { status ->
_UniFFILib.INSTANCE.ffi_lightspark_crypto_420_Seed_object_free(this.pointer, status)
}
}
override fun `asBytes`(): List =
callWithPointer {
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_Seed_as_bytes(it, _status)
}
}.let {
FfiConverterSequenceUByte.lift(it)
}
companion object {
fun `fromMnemonic`(`mnemonic`: Mnemonic): Seed =
Seed(
rustCall { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_Seed_from_mnemonic(FfiConverterTypeMnemonic.lower(`mnemonic`), _status)
},
)
}
}
public object FfiConverterTypeSeed : FfiConverter {
override fun lower(value: Seed): Pointer = value.callWithPointer { it }
override fun lift(value: Pointer): Seed {
return Seed(value)
}
override fun read(buf: ByteBuffer): Seed {
// The Rust code always writes pointers as 8 bytes, and will
// fail to compile if they don't fit.
return lift(Pointer(buf.getLong()))
}
override fun allocationSize(value: Seed) = 8
override fun write(
value: Seed,
buf: ByteBuffer,
) {
// The Rust code always expects pointers written as 8 bytes,
// and will fail to compile if they don't fit.
buf.putLong(Pointer.nativeValue(lower(value)))
}
}
data class RemoteSigningResponse(
var `query`: String,
var `variables`: String,
)
public object FfiConverterTypeRemoteSigningResponse : FfiConverterRustBuffer {
override fun read(buf: ByteBuffer): RemoteSigningResponse {
return RemoteSigningResponse(
FfiConverterString.read(buf),
FfiConverterString.read(buf),
)
}
override fun allocationSize(value: RemoteSigningResponse) =
(
FfiConverterString.allocationSize(value.`query`) +
FfiConverterString.allocationSize(value.`variables`)
)
override fun write(
value: RemoteSigningResponse,
buf: ByteBuffer,
) {
FfiConverterString.write(value.`query`, buf)
FfiConverterString.write(value.`variables`, buf)
}
}
enum class Network {
BITCOIN,
TESTNET,
REGTEST,
}
public object FfiConverterTypeNetwork : FfiConverterRustBuffer {
override fun read(buf: ByteBuffer) =
try {
Network.values()[buf.getInt() - 1]
} catch (e: IndexOutOfBoundsException) {
throw RuntimeException("invalid enum value, something is very wrong!!", e)
}
override fun allocationSize(value: Network) = 4
override fun write(
value: Network,
buf: ByteBuffer,
) {
buf.putInt(value.ordinal + 1)
}
}
sealed class CryptoException(message: String) : Exception(message) {
// Each variant is a nested class
// Flat enums carries a string error message, so no special implementation is necessary.
class Secp256k1Exception(message: String) : CryptoException(message)
class RustSecp256k1Exception(message: String) : CryptoException(message)
companion object ErrorHandler : CallStatusErrorHandler {
override fun lift(error_buf: RustBuffer.ByValue): CryptoException = FfiConverterTypeCryptoError.lift(error_buf)
}
}
public object FfiConverterTypeCryptoError : FfiConverterRustBuffer {
override fun read(buf: ByteBuffer): CryptoException {
return when (buf.getInt()) {
1 -> CryptoException.Secp256k1Exception(FfiConverterString.read(buf))
2 -> CryptoException.RustSecp256k1Exception(FfiConverterString.read(buf))
else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
}
}
override fun allocationSize(value: CryptoException): Int {
return 4
}
override fun write(
value: CryptoException,
buf: ByteBuffer,
) {
when (value) {
is CryptoException.Secp256k1Exception -> {
buf.putInt(1)
Unit
}
is CryptoException.RustSecp256k1Exception -> {
buf.putInt(2)
Unit
}
}.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
}
}
sealed class LightsparkSignerException(message: String) : Exception(message) {
// Each variant is a nested class
// Flat enums carries a string error message, so no special implementation is necessary.
class Bip39Exception(message: String) : LightsparkSignerException(message)
class Secp256k1Exception(message: String) : LightsparkSignerException(message)
class KeyDerivationException(message: String) : LightsparkSignerException(message)
class KeyTweakException(message: String) : LightsparkSignerException(message)
class EntropyLengthException(message: String) : LightsparkSignerException(message)
companion object ErrorHandler : CallStatusErrorHandler {
override fun lift(error_buf: RustBuffer.ByValue): LightsparkSignerException = FfiConverterTypeLightsparkSignerError.lift(error_buf)
}
}
public object FfiConverterTypeLightsparkSignerError : FfiConverterRustBuffer {
override fun read(buf: ByteBuffer): LightsparkSignerException {
return when (buf.getInt()) {
1 -> LightsparkSignerException.Bip39Exception(FfiConverterString.read(buf))
2 -> LightsparkSignerException.Secp256k1Exception(FfiConverterString.read(buf))
3 -> LightsparkSignerException.KeyDerivationException(FfiConverterString.read(buf))
4 -> LightsparkSignerException.KeyTweakException(FfiConverterString.read(buf))
5 -> LightsparkSignerException.EntropyLengthException(FfiConverterString.read(buf))
else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
}
}
override fun allocationSize(value: LightsparkSignerException): Int {
return 4
}
override fun write(
value: LightsparkSignerException,
buf: ByteBuffer,
) {
when (value) {
is LightsparkSignerException.Bip39Exception -> {
buf.putInt(1)
Unit
}
is LightsparkSignerException.Secp256k1Exception -> {
buf.putInt(2)
Unit
}
is LightsparkSignerException.KeyDerivationException -> {
buf.putInt(3)
Unit
}
is LightsparkSignerException.KeyTweakException -> {
buf.putInt(4)
Unit
}
is LightsparkSignerException.EntropyLengthException -> {
buf.putInt(5)
Unit
}
}.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
}
}
sealed class RemoteSigningException(message: String) : Exception(message) {
// Each variant is a nested class
// Flat enums carries a string error message, so no special implementation is necessary.
class WebhookParsingException(message: String) : RemoteSigningException(message)
class WebhookSignatureException(message: String) : RemoteSigningException(message)
class SignerCreationException(message: String) : RemoteSigningException(message)
class RemoteSigningHandlerException(message: String) : RemoteSigningException(message)
companion object ErrorHandler : CallStatusErrorHandler {
override fun lift(error_buf: RustBuffer.ByValue): RemoteSigningException = FfiConverterTypeRemoteSigningError.lift(error_buf)
}
}
public object FfiConverterTypeRemoteSigningError : FfiConverterRustBuffer {
override fun read(buf: ByteBuffer): RemoteSigningException {
return when (buf.getInt()) {
1 -> RemoteSigningException.WebhookParsingException(FfiConverterString.read(buf))
2 -> RemoteSigningException.WebhookSignatureException(FfiConverterString.read(buf))
3 -> RemoteSigningException.SignerCreationException(FfiConverterString.read(buf))
4 -> RemoteSigningException.RemoteSigningHandlerException(FfiConverterString.read(buf))
else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
}
}
override fun allocationSize(value: RemoteSigningException): Int {
return 4
}
override fun write(
value: RemoteSigningException,
buf: ByteBuffer,
) {
when (value) {
is RemoteSigningException.WebhookParsingException -> {
buf.putInt(1)
Unit
}
is RemoteSigningException.WebhookSignatureException -> {
buf.putInt(2)
Unit
}
is RemoteSigningException.SignerCreationException -> {
buf.putInt(3)
Unit
}
is RemoteSigningException.RemoteSigningHandlerException -> {
buf.putInt(4)
Unit
}
}.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
}
}
internal typealias Handle = Long
internal class ConcurrentHandleMap(
private val leftMap: MutableMap = mutableMapOf(),
private val rightMap: MutableMap = mutableMapOf(),
) {
private val lock = java.util.concurrent.locks.ReentrantLock()
private val currentHandle = AtomicLong(0L)
private val stride = 1L
fun insert(obj: T): Handle =
lock.withLock {
rightMap[obj]
?: currentHandle.getAndAdd(stride)
.also { handle ->
leftMap[handle] = obj
rightMap[obj] = handle
}
}
fun get(handle: Handle) =
lock.withLock {
leftMap[handle]
}
fun delete(handle: Handle) {
this.remove(handle)
}
fun remove(handle: Handle): T? =
lock.withLock {
leftMap.remove(handle)?.let { obj ->
rightMap.remove(obj)
obj
}
}
}
interface ForeignCallback : com.sun.jna.Callback {
public fun invoke(
handle: Handle,
method: Int,
args: RustBuffer.ByValue,
outBuf: RustBufferByReference,
): Int
}
// Magic number for the Rust proxy to call using the same mechanism as every other method,
// to free the callback once it's dropped by Rust.
internal const val IDX_CALLBACK_FREE = 0
public abstract class FfiConverterCallbackInterface(
protected val foreignCallback: ForeignCallback,
) : FfiConverter {
private val handleMap = ConcurrentHandleMap()
// Registers the foreign callback with the Rust side.
// This method is generated for each callback interface.
internal abstract fun register(lib: _UniFFILib)
fun drop(handle: Handle): RustBuffer.ByValue {
return handleMap.remove(handle).let { RustBuffer.ByValue() }
}
override fun lift(value: Handle): CallbackInterface {
return handleMap.get(value) ?: throw InternalException("No callback in handlemap; this is a Uniffi bug")
}
override fun read(buf: ByteBuffer) = lift(buf.getLong())
override fun lower(value: CallbackInterface) =
handleMap.insert(value).also {
assert(
handleMap.get(it) === value,
) { "Handle map is not returning the object we just placed there. This is a bug in the HandleMap." }
}
override fun allocationSize(value: CallbackInterface) = 8
override fun write(
value: CallbackInterface,
buf: ByteBuffer,
) {
buf.putLong(lower(value))
}
}
// Declaration and FfiConverters for Validation Callback Interface
public interface Validation {
fun `shouldSign`(`webhook`: String): Boolean
}
// The ForeignCallback that is passed to Rust.
internal class ForeignCallbackTypeValidation : ForeignCallback {
@Suppress("TooGenericExceptionCaught")
override fun invoke(
handle: Handle,
method: Int,
args: RustBuffer.ByValue,
outBuf: RustBufferByReference,
): Int {
val cb = FfiConverterTypeValidation.lift(handle)
return when (method) {
IDX_CALLBACK_FREE -> {
FfiConverterTypeValidation.drop(handle)
// No return value.
// See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
0
}
1 -> {
// Call the method, write to outBuf and return a status code
// See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs` for info
try {
val buffer = this.`invokeShouldSign`(cb, args)
// Success
outBuf.setValue(buffer)
1
} catch (e: Throwable) {
// Unexpected error
try {
// Try to serialize the error into a string
outBuf.setValue(FfiConverterString.lower(e.toString()))
} catch (e: Throwable) {
// If that fails, then it's time to give up and just return
}
-1
}
}
else -> {
// An unexpected error happened.
// See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
try {
// Try to serialize the error into a string
outBuf.setValue(FfiConverterString.lower("Invalid Callback index"))
} catch (e: Throwable) {
// If that fails, then it's time to give up and just return
}
-1
}
}
}
private fun `invokeShouldSign`(
kotlinCallbackInterface: Validation,
args: RustBuffer.ByValue,
): RustBuffer.ByValue =
try {
val buf = args.asByteBuffer() ?: throw InternalException("No ByteBuffer in RustBuffer; this is a Uniffi bug")
kotlinCallbackInterface.`shouldSign`(
FfiConverterString.read(buf),
)
.let {
FfiConverterBoolean.lowerIntoRustBuffer(it)
} // TODO catch errors and report them back to Rust.
// https://github.com/mozilla/uniffi-rs/issues/351
} finally {
RustBuffer.free(args)
}
}
// The ffiConverter which transforms the Callbacks in to Handles to pass to Rust.
public object FfiConverterTypeValidation : FfiConverterCallbackInterface(
foreignCallback = ForeignCallbackTypeValidation(),
) {
override fun register(lib: _UniFFILib) {
rustCall { status ->
lib.ffi_lightspark_crypto_420_Validation_init_callback(this.foreignCallback, status)
}
}
}
public object FfiConverterOptionalSequenceUByte : FfiConverterRustBuffer?> {
override fun read(buf: ByteBuffer): List? {
if (buf.get().toInt() == 0) {
return null
}
return FfiConverterSequenceUByte.read(buf)
}
override fun allocationSize(value: List?): Int {
if (value == null) {
return 1
} else {
return 1 + FfiConverterSequenceUByte.allocationSize(value)
}
}
override fun write(
value: List?,
buf: ByteBuffer,
) {
if (value == null) {
buf.put(0)
} else {
buf.put(1)
FfiConverterSequenceUByte.write(value, buf)
}
}
}
public object FfiConverterSequenceUByte : FfiConverterRustBuffer> {
override fun read(buf: ByteBuffer): List {
val len = buf.getInt()
return List(len) {
FfiConverterUByte.read(buf)
}
}
override fun allocationSize(value: List): Int {
val sizeForLength = 4
val sizeForItems = value.map { FfiConverterUByte.allocationSize(it) }.sum()
return sizeForLength + sizeForItems
}
override fun write(
value: List,
buf: ByteBuffer,
) {
buf.putInt(value.size)
value.forEach {
FfiConverterUByte.write(it, buf)
}
}
}
@Throws(CryptoException::class)
fun `signEcdsa`(
`msg`: List,
`privateKeyBytes`: List,
): List {
return FfiConverterSequenceUByte.lift(
rustCallWithError(CryptoException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_sign_ecdsa(
FfiConverterSequenceUByte.lower(`msg`),
FfiConverterSequenceUByte.lower(`privateKeyBytes`),
_status,
)
},
)
}
@Throws(CryptoException::class)
fun `verifyEcdsa`(
`msg`: List,
`signatureBytes`: List,
`publicKeyBytes`: List,
): Boolean {
return FfiConverterBoolean.lift(
rustCallWithError(CryptoException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_verify_ecdsa(
FfiConverterSequenceUByte.lower(`msg`),
FfiConverterSequenceUByte.lower(`signatureBytes`),
FfiConverterSequenceUByte.lower(`publicKeyBytes`),
_status,
)
},
)
}
@Throws(CryptoException::class)
fun `encryptEcies`(
`msg`: List,
`publicKeyBytes`: List,
): List {
return FfiConverterSequenceUByte.lift(
rustCallWithError(CryptoException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_encrypt_ecies(
FfiConverterSequenceUByte.lower(`msg`),
FfiConverterSequenceUByte.lower(`publicKeyBytes`),
_status,
)
},
)
}
@Throws(CryptoException::class)
fun `decryptEcies`(
`cipherText`: List,
`privateKeyBytes`: List,
): List {
return FfiConverterSequenceUByte.lift(
rustCallWithError(CryptoException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_decrypt_ecies(
FfiConverterSequenceUByte.lower(`cipherText`),
FfiConverterSequenceUByte.lower(`privateKeyBytes`),
_status,
)
},
)
}
@Throws(CryptoException::class)
fun `generateKeypair`(): KeyPair {
return FfiConverterTypeKeyPair.lift(
rustCallWithError(CryptoException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_generate_keypair(_status)
},
)
}
@Throws(RemoteSigningException::class)
fun `handleRemoteSigningWebhookEvent`(
`webhookData`: List,
`webhookSignature`: String,
`webhookSecret`: String,
`masterSeedBytes`: List,
`validation`: Validation,
): RemoteSigningResponse {
return FfiConverterTypeRemoteSigningResponse.lift(
rustCallWithError(RemoteSigningException) { _status ->
_UniFFILib.INSTANCE.lightspark_crypto_420_handle_remote_signing_webhook_event(
FfiConverterSequenceUByte.lower(`webhookData`),
FfiConverterString.lower(`webhookSignature`),
FfiConverterString.lower(`webhookSecret`),
FfiConverterSequenceUByte.lower(`masterSeedBytes`),
FfiConverterTypeValidation.lower(`validation`),
_status,
)
},
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy