# This file was autogenerated by some hot garbage in the `uniffi` crate.
# Trust me, you don't want to mess with it!

# Common helper code.
#
# Ideally this would live in a separate .py file where it can be unittested etc
# in isolation, and perhaps even published as a re-useable package.
#
# However, it's important that the details 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 Python
# helpers directly inline like we're doing here.

import os
import sys
import ctypes
import enum
import struct
import contextlib
import datetime


class RustBuffer(ctypes.Structure):
    _fields_ = [
        ("capacity", ctypes.c_int32),
        ("len", ctypes.c_int32),
        ("data", ctypes.POINTER(ctypes.c_char)),
    ]

    @staticmethod
    def alloc(size):
        return rust_call(_UniFFILib.ffi_bdk_b9b3_rustbuffer_alloc, size)

    @staticmethod
    def reserve(rbuf, additional):
        return rust_call(_UniFFILib.ffi_bdk_b9b3_rustbuffer_reserve, rbuf, additional)

    def free(self):
        return rust_call(_UniFFILib.ffi_bdk_b9b3_rustbuffer_free, self)

    def __str__(self):
        return "RustBuffer(capacity={}, len={}, data={})".format(
            self.capacity,
            self.len,
            self.data[0:self.len]
        )

    @contextlib.contextmanager
    def allocWithBuilder():
        """Context-manger to allocate a buffer using a RustBufferBuilder.

        The allocated buffer will be automatically freed if an error occurs, ensuring that
        we don't accidentally leak it.
        """
        builder = RustBufferBuilder()
        try:
            yield builder
        except:
            builder.discard()
            raise

    @contextlib.contextmanager
    def consumeWithStream(self):
        """Context-manager to consume a buffer using a RustBufferStream.

        The RustBuffer will be freed once the context-manager exits, ensuring that we don't
        leak it even if an error occurs.
        """
        try:
            s = RustBufferStream(self)
            yield s
            if s.remaining() != 0:
                raise RuntimeError("junk data left in buffer after consuming")
        finally:
            self.free()


class ForeignBytes(ctypes.Structure):
    _fields_ = [
        ("len", ctypes.c_int32),
        ("data", ctypes.POINTER(ctypes.c_char)),
    ]

    def __str__(self):
        return "ForeignBytes(len={}, data={})".format(self.len, self.data[0:self.len])


class RustBufferStream(object):
    """
    Helper for structured reading of bytes from a RustBuffer
    """

    def __init__(self, rbuf):
        self.rbuf = rbuf
        self.offset = 0

    def remaining(self):
        return self.rbuf.len - self.offset

    def _unpack_from(self, size, format):
        if self.offset + size > self.rbuf.len:
            raise InternalError("read past end of rust buffer")
        value = struct.unpack(format, self.rbuf.data[self.offset:self.offset+size])[0]
        self.offset += size
        return value

    def read(self, size):
        if self.offset + size > self.rbuf.len:
            raise InternalError("read past end of rust buffer")
        data = self.rbuf.data[self.offset:self.offset+size]
        self.offset += size
        return data

    def readI8(self):
        return self._unpack_from(1, ">b")

    def readU8(self):
        return self._unpack_from(1, ">B")

    def readI16(self):
        return self._unpack_from(2, ">h")

    def readU16(self):
        return self._unpack_from(2, ">H")

    def readI32(self):
        return self._unpack_from(4, ">i")

    def readU32(self):
        return self._unpack_from(4, ">I")

    def readI64(self):
        return self._unpack_from(8, ">q")

    def readU64(self):
        return self._unpack_from(8, ">Q")

    def readFloat(self):
        v = self._unpack_from(4, ">f")
        return v

    def readDouble(self):
        return self._unpack_from(8, ">d")


class RustBufferBuilder(object):
    """
    Helper for structured writing of bytes into a RustBuffer.
    """

    def __init__(self):
        self.rbuf = RustBuffer.alloc(16)
        self.rbuf.len = 0

    def finalize(self):
        rbuf = self.rbuf
        self.rbuf = None
        return rbuf

    def discard(self):
        if self.rbuf is not None:
            rbuf = self.finalize()
            rbuf.free()

    @contextlib.contextmanager
    def _reserve(self, numBytes):
        if self.rbuf.len + numBytes > self.rbuf.capacity:
            self.rbuf = RustBuffer.reserve(self.rbuf, numBytes)
        yield None
        self.rbuf.len += numBytes

    def _pack_into(self, size, format, value):
        with self._reserve(size):
            # XXX TODO: I feel like I should be able to use `struct.pack_into` here but can't figure it out.
            for i, byte in enumerate(struct.pack(format, value)):
                self.rbuf.data[self.rbuf.len + i] = byte

    def write(self, value):
        with self._reserve(len(value)):
            for i, byte in enumerate(value):
                self.rbuf.data[self.rbuf.len + i] = byte

    def writeI8(self, v):
        self._pack_into(1, ">b", v)

    def writeU8(self, v):
        self._pack_into(1, ">B", v)

    def writeI16(self, v):
        self._pack_into(2, ">h", v)

    def writeU16(self, v):
        self._pack_into(2, ">H", v)

    def writeI32(self, v):
        self._pack_into(4, ">i", v)

    def writeU32(self, v):
        self._pack_into(4, ">I", v)

    def writeI64(self, v):
        self._pack_into(8, ">q", v)

    def writeU64(self, v):
        self._pack_into(8, ">Q", v)

    def writeFloat(self, v):
        self._pack_into(4, ">f", v)

    def writeDouble(self, v):
        self._pack_into(8, ">d", v)
# 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.

class InternalError(Exception):
    pass

class RustCallStatus(ctypes.Structure):
    """
    Error runtime.
    """
    _fields_ = [
        ("code", ctypes.c_int8),
        ("error_buf", RustBuffer),
    ]

    # These match the values from the uniffi::rustcalls module
    CALL_SUCCESS = 0
    CALL_ERROR = 1
    CALL_PANIC = 2

    def __str__(self):
        if self.code == RustCallStatus.CALL_SUCCESS:
            return "RustCallStatus(CALL_SUCCESS)"
        elif self.code == RustCallStatus.CALL_ERROR:
            return "RustCallStatus(CALL_ERROR)"
        elif self.code == RustCallStatus.CALL_PANIC:
            return "RustCallStatus(CALL_SUCCESS)"
        else:
            return "RustCallStatus(<invalid code>)"

def rust_call(fn, *args):
    # Call a rust function
    return rust_call_with_error(None, fn, *args)

def rust_call_with_error(error_class, fn, *args):
    # Call a rust function and handle any errors
    #
    # This function is used for rust calls that return Result<> and therefore can set the CALL_ERROR status code.
    # error_class must be set to the error class that corresponds to the result.
    call_status = RustCallStatus(code=RustCallStatus.CALL_SUCCESS, error_buf=RustBuffer(0, 0, None))

    args_with_error = args + (ctypes.byref(call_status),)
    result = fn(*args_with_error)
    if call_status.code == RustCallStatus.CALL_SUCCESS:
        return result
    elif call_status.code == RustCallStatus.CALL_ERROR:
        if error_class is None:
            call_status.err_buf.contents.free()
            raise InternalError("rust_call_with_error: CALL_ERROR, but no error class set")
        else:
            raise error_class._lift(call_status.error_buf)
    elif call_status.code == RustCallStatus.CALL_PANIC:
        # 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 call_status.error_buf.len > 0:
            msg = FfiConverterString._lift(call_status.error_buf)
        else:
            msg = "Unknown rust panic"
        raise InternalError(msg)
    else:
        raise InternalError("Invalid RustCallStatus code: {}".format(
            call_status.code))

# A function pointer for a callback as defined by UniFFI.
# Rust definition `fn(handle: u64, method: u32, args: RustBuffer, buf_ptr: *mut RustBuffer) -> int`
FOREIGN_CALLBACK_T = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_ulonglong, ctypes.c_ulong, RustBuffer, ctypes.POINTER(RustBuffer))
# Types conforming to `Primitive` pass themselves directly over the FFI.
class Primitive:
    @classmethod
    def _lift(cls, value):
        return value

    @classmethod
    def _lower(cls, value):
        return value

# Helper class for new types that will always go through a RustBuffer.
# Classes should inherit from this and implement the `_read` static method
# and `_write` instance methods.
class ViaFfiUsingByteBuffer:
    @classmethod
    def _lift(cls, rbuf):
        with rbuf.consumeWithStream() as stream:
            return cls._read(stream)

    def _lower(self):
        with RustBuffer.allocWithBuilder() as builder:
            self._write(builder)
            return builder.finalize()

# Helper class for wrapper types that will always go through a RustBuffer.
# Classes should inherit from this and implement the `_read` and `_write` static methods.
class FfiConverterUsingByteBuffer:
    @classmethod
    def _lift(cls, rbuf):
        with rbuf.consumeWithStream() as stream:
            return cls._read(stream)

    @classmethod
    def _lower(cls, value):
        with RustBuffer.allocWithBuilder() as builder:
            cls._write(value, builder)
            return builder.finalize()

# Helpers for structural types.

class FfiConverterSequence:
    @staticmethod
    def _write(value, buf, writeItem):
        items = len(value)
        buf.writeI32(items)
        for item in value:
            writeItem(item, buf)

    @staticmethod
    def _read(buf, readItem):
        count = buf.readI32()
        if count < 0:
            raise InternalError("Unexpected negative sequence length")

        items = []
        while count > 0:
            items.append(readItem(buf))
            count -= 1
        return items

class FfiConverterOptional:
    @staticmethod
    def _write(value, buf, writeItem):
        if value is None:
            buf.writeU8(0)
            return

        buf.writeU8(1)
        writeItem(value, buf)

    @staticmethod
    def _read(buf, readItem):
        flag = buf.readU8()
        if flag == 0:
            return None
        elif flag == 1:
            return readItem(buf)
        else:
            raise InternalError("Unexpected flag byte for optional type")

class FfiConverterDictionary:
    @staticmethod
    def _write(items, buf, writeItem):
        buf.writeI32(len(items))
        for (key, value) in items.items():
            writeItem(key, value, buf)

    @staticmethod
    def _read(buf, readItem):
        count = buf.readI32()
        if count < 0:
            raise InternalError("Unexpected negative map size")
        items = {}
        while count > 0:
            key, value = readItem(buf)
            items[key] = value
            count -= 1
        return items

# Contains loading, initialization code,
# and the FFI Function declarations in a com.sun.jna.Library.
# This is how we find and load the dynamic library provided by the component.
# For now we just look it up by name.
#
# XXX TODO: This will probably grow some magic for resolving megazording in future.
# E.g. we might start by looking for the named component in `libuniffi.so` and if
# that fails, fall back to loading it separately from `lib${componentName}.so`.

import platform
def loadIndirect():
    if sys.platform == "linux":
        # libname = "lib{}.so"
        # libname = os.path.join(os.path.dirname(__file__), "lib{}.so")
        libname = os.path.join(os.path.dirname(__file__), "linux-x86_64/lib{}.so")
    elif sys.platform == "darwin":
        # libname = "lib{}.dylib"
        # libname = os.path.join(os.path.dirname(__file__), "lib{}.dylib")
        if platform.machine() == "arm64":
            libname = os.path.join(os.path.dirname(__file__), "darwin-arm64/lib{}.dylib")
        elif platform.machine() == "x86_64":
            libname = os.path.join(os.path.dirname(__file__), "darwin-x86_64/lib{}.dylib")
    elif sys.platform.startswith("win"):
        # As of python3.8, ctypes does not seem to search $PATH when loading DLLs.
        # We could use `os.add_dll_directory` to configure the search path, but
        # it doesn't feel right to mess with application-wide settings. Let's
        # assume that the `.dll` is next to the `.py` file and load by full path.
        libname = os.path.join(
            os.path.dirname(__file__),
            "{}.dll",
        )
    return getattr(ctypes.cdll, libname.format("bdkffi"))

# def loadIndirect():
#     if sys.platform == "linux":
#         # libname = "lib{}.so"
#         libname = os.path.join(os.path.dirname(__file__), "lib{}.so")
#     elif sys.platform == "darwin":
#         # libname = "lib{}.dylib"
#         libname = os.path.join(os.path.dirname(__file__), "lib{}.dylib")
#     elif sys.platform.startswith("win"):
#         # As of python3.8, ctypes does not seem to search $PATH when loading DLLs.
#         # We could use `os.add_dll_directory` to configure the search path, but
#         # it doesn't feel right to mess with application-wide settings. Let's
#         # assume that the `.dll` is next to the `.py` file and load by full path.
#         libname = os.path.join(
#             os.path.dirname(__file__),
#             "{}.dll",
#         )
#     return getattr(ctypes.cdll, libname.format("bdkffi"))

# A ctypes library to expose the extern-C FFI definitions.
# This is an implementation detail which will be called internally by the public API.

_UniFFILib = loadIndirect()
_UniFFILib.ffi_bdk_b9b3_OfflineWallet_object_free.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.ffi_bdk_b9b3_OfflineWallet_object_free.restype = None
_UniFFILib.bdk_b9b3_OfflineWallet_new.argtypes = (
    RustBuffer,
    RustBuffer,
    RustBuffer,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OfflineWallet_new.restype = ctypes.c_void_p
_UniFFILib.bdk_b9b3_OfflineWallet_get_new_address.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OfflineWallet_get_new_address.restype = RustBuffer
_UniFFILib.bdk_b9b3_OfflineWallet_get_last_unused_address.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OfflineWallet_get_last_unused_address.restype = RustBuffer
_UniFFILib.bdk_b9b3_OfflineWallet_get_balance.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OfflineWallet_get_balance.restype = ctypes.c_uint64
_UniFFILib.bdk_b9b3_OfflineWallet_sign.argtypes = (
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OfflineWallet_sign.restype = None
_UniFFILib.bdk_b9b3_OfflineWallet_get_transactions.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OfflineWallet_get_transactions.restype = RustBuffer
_UniFFILib.ffi_bdk_b9b3_OnlineWallet_object_free.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.ffi_bdk_b9b3_OnlineWallet_object_free.restype = None
_UniFFILib.bdk_b9b3_OnlineWallet_new.argtypes = (
    RustBuffer,
    RustBuffer,
    RustBuffer,
    RustBuffer,
    RustBuffer,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_new.restype = ctypes.c_void_p
_UniFFILib.bdk_b9b3_OnlineWallet_get_new_address.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_get_new_address.restype = RustBuffer
_UniFFILib.bdk_b9b3_OnlineWallet_get_last_unused_address.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_get_last_unused_address.restype = RustBuffer
_UniFFILib.bdk_b9b3_OnlineWallet_get_balance.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_get_balance.restype = ctypes.c_uint64
_UniFFILib.bdk_b9b3_OnlineWallet_sign.argtypes = (
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_sign.restype = None
_UniFFILib.bdk_b9b3_OnlineWallet_get_transactions.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_get_transactions.restype = RustBuffer
_UniFFILib.bdk_b9b3_OnlineWallet_get_network.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_get_network.restype = RustBuffer
_UniFFILib.bdk_b9b3_OnlineWallet_sync.argtypes = (
    ctypes.c_void_p,
    ctypes.c_uint64,
    RustBuffer,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_sync.restype = None
_UniFFILib.bdk_b9b3_OnlineWallet_broadcast.argtypes = (
    ctypes.c_void_p,
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_OnlineWallet_broadcast.restype = RustBuffer
_UniFFILib.ffi_bdk_b9b3_PartiallySignedBitcoinTransaction_object_free.argtypes = (
    ctypes.c_void_p,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.ffi_bdk_b9b3_PartiallySignedBitcoinTransaction_object_free.restype = None
_UniFFILib.bdk_b9b3_PartiallySignedBitcoinTransaction_new.argtypes = (
    ctypes.c_void_p,
    RustBuffer,
    ctypes.c_uint64,
    RustBuffer,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_PartiallySignedBitcoinTransaction_new.restype = ctypes.c_void_p
_UniFFILib.ffi_bdk_b9b3_BdkProgress_init_callback.argtypes = (
    FOREIGN_CALLBACK_T,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.ffi_bdk_b9b3_BdkProgress_init_callback.restype = None
_UniFFILib.bdk_b9b3_generate_extended_key.argtypes = (
    RustBuffer,
    RustBuffer,
    RustBuffer,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_generate_extended_key.restype = RustBuffer
_UniFFILib.bdk_b9b3_restore_extended_key.argtypes = (
    RustBuffer,
    RustBuffer,
    RustBuffer,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.bdk_b9b3_restore_extended_key.restype = RustBuffer
_UniFFILib.ffi_bdk_b9b3_rustbuffer_alloc.argtypes = (
    ctypes.c_int32,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.ffi_bdk_b9b3_rustbuffer_alloc.restype = RustBuffer
_UniFFILib.ffi_bdk_b9b3_rustbuffer_from_bytes.argtypes = (
    ForeignBytes,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.ffi_bdk_b9b3_rustbuffer_from_bytes.restype = RustBuffer
_UniFFILib.ffi_bdk_b9b3_rustbuffer_free.argtypes = (
    RustBuffer,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.ffi_bdk_b9b3_rustbuffer_free.restype = None
_UniFFILib.ffi_bdk_b9b3_rustbuffer_reserve.argtypes = (
    RustBuffer,
    ctypes.c_int32,
    ctypes.POINTER(RustCallStatus),
)
_UniFFILib.ffi_bdk_b9b3_rustbuffer_reserve.restype = RustBuffer

# Public interface members begin here.

import threading

class ConcurrentHandleMap:
    """
    A map where inserting, getting and removing data is synchronized with a lock.
    """

    def __init__(self):
        # type Handle = int
        self._left_map = {}  # type: Dict[Handle, Any]
        self._right_map = {}  # type: Dict[Any, Handle]

        self._lock = threading.Lock()
        self._current_handle = 0
        self._stride = 1


    def insert(self, obj):
        with self._lock:
            if obj in self._right_map:
                return self._right_map[obj]
            else:
                handle = self._current_handle
                self._current_handle += self._stride
                self._left_map[handle] = obj
                self._right_map[obj] = handle
                return handle

    def get(self, handle):
        with self._lock:
            return self._left_map.get(handle)

    def remove(self, handle):
        with self._lock:
            if handle in self._left_map:
                obj = self._left_map.pop(handle)
                del self._right_map[obj]
                return obj

# 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.
IDX_CALLBACK_FREE = 0

class FfiConverterCallbackInterface:
    _handle_map = ConcurrentHandleMap()

    def __init__(self, cb):
        self._foreign_callback = cb

    def drop(self, handle):
        self.__class__._handle_map.remove(handle)

    @classmethod
    def _lift(cls, handle):
        obj = cls._handle_map.get(handle)
        if not obj:
            raise InternalError("The object in the handle map has been dropped already")

        return obj

    @classmethod
    def _read(cls, buf):
        handle = buf.readU64()
        cls._lift(handle)

    @classmethod
    def _lower(cls, cb):
        handle = cls._handle_map.insert(cb)
        return handle

    @classmethod
    def _write(cls, cb, buf):
        buf.writeU64(cls._lower(cb))



class Network(ViaFfiUsingByteBuffer, enum.Enum):
    BITCOIN = 1
    TESTNET = 2
    SIGNET = 3
    REGTEST = 4
    

    @staticmethod
    def _read(buf):
        variant = buf.readI32()
        if variant == 1:
            return Network.BITCOIN
        if variant == 2:
            return Network.TESTNET
        if variant == 3:
            return Network.SIGNET
        if variant == 4:
            return Network.REGTEST
        
        raise InternalError("Raw enum value doesn't match any cases")

    def _write(self, buf):
        if self is Network.BITCOIN:
            i = 1
            buf.writeI32(1)
        if self is Network.TESTNET:
            i = 2
            buf.writeI32(2)
        if self is Network.SIGNET:
            i = 3
            buf.writeI32(3)
        if self is Network.REGTEST:
            i = 4
            buf.writeI32(4)
        






class DatabaseConfig(ViaFfiUsingByteBuffer, object):
    def __init__(self):
        raise RuntimeError("DatabaseConfig cannot be instantiated directly")

    # Each enum variant is a nested class of the enum itself.
    class MEMORY(object):
        def __init__(self,junk ):
            
            self.junk = junk
            

        def __str__(self):
            return "DatabaseConfig.MEMORY(junk={} )".format(self.junk )

        def __eq__(self, other):
            if not other.is_memory():
                return False
            if self.junk != other.junk:
                return False
            return True
    class SLED(object):
        def __init__(self,config ):
            
            self.config = config
            

        def __str__(self):
            return "DatabaseConfig.SLED(config={} )".format(self.config )

        def __eq__(self, other):
            if not other.is_sled():
                return False
            if self.config != other.config:
                return False
            return True
    

    # For each variant, we have an `is_NAME` method for easily checking
    # whether an instance is that variant.
    def is_memory(self):
        return isinstance(self, DatabaseConfig.MEMORY)
    def is_sled(self):
        return isinstance(self, DatabaseConfig.SLED)
    

    @classmethod
    def _read(cls, buf):
        variant = buf.readI32()

        if variant == 1:
            return cls.MEMORY(
                junk=FfiConverterString._read(buf),
            )
        if variant == 2:
            return cls.SLED(
                config=SledDbConfiguration._read(buf),
            )
        
        raise InternalError("Raw enum value doesn't match any cases")

    def _write(self, buf):
        if self.is_memory():
            buf.writeI32(1)
            FfiConverterString._write(self.junk, buf)
        if self.is_sled():
            buf.writeI32(2)
            self.config._write(buf)
        

# Now, a little trick - we make each nested variant class be a subclass of the main
# enum class, so that method calls and instance checks etc will work intuitively.
# We might be able to do this a little more neatly with a metaclass, but this'll do.
DatabaseConfig.MEMORY = type("DatabaseConfig.MEMORY", (DatabaseConfig.MEMORY, DatabaseConfig,), {})
DatabaseConfig.SLED = type("DatabaseConfig.SLED", (DatabaseConfig.SLED, DatabaseConfig,), {})







class Transaction(ViaFfiUsingByteBuffer, object):
    def __init__(self):
        raise RuntimeError("Transaction cannot be instantiated directly")

    # Each enum variant is a nested class of the enum itself.
    class UNCONFIRMED(object):
        def __init__(self,details ):
            
            self.details = details
            

        def __str__(self):
            return "Transaction.UNCONFIRMED(details={} )".format(self.details )

        def __eq__(self, other):
            if not other.is_unconfirmed():
                return False
            if self.details != other.details:
                return False
            return True
    class CONFIRMED(object):
        def __init__(self,details, confirmation ):
            
            self.details = details
            self.confirmation = confirmation
            

        def __str__(self):
            return "Transaction.CONFIRMED(details={}, confirmation={} )".format(self.details, self.confirmation )

        def __eq__(self, other):
            if not other.is_confirmed():
                return False
            if self.details != other.details:
                return False
            if self.confirmation != other.confirmation:
                return False
            return True
    

    # For each variant, we have an `is_NAME` method for easily checking
    # whether an instance is that variant.
    def is_unconfirmed(self):
        return isinstance(self, Transaction.UNCONFIRMED)
    def is_confirmed(self):
        return isinstance(self, Transaction.CONFIRMED)
    

    @classmethod
    def _read(cls, buf):
        variant = buf.readI32()

        if variant == 1:
            return cls.UNCONFIRMED(
                details=TransactionDetails._read(buf),
            )
        if variant == 2:
            return cls.CONFIRMED(
                details=TransactionDetails._read(buf),
                confirmation=Confirmation._read(buf),
            )
        
        raise InternalError("Raw enum value doesn't match any cases")

    def _write(self, buf):
        if self.is_unconfirmed():
            buf.writeI32(1)
            self.details._write(buf)
        if self.is_confirmed():
            buf.writeI32(2)
            self.details._write(buf)
            self.confirmation._write(buf)
        

# Now, a little trick - we make each nested variant class be a subclass of the main
# enum class, so that method calls and instance checks etc will work intuitively.
# We might be able to do this a little more neatly with a metaclass, but this'll do.
Transaction.UNCONFIRMED = type("Transaction.UNCONFIRMED", (Transaction.UNCONFIRMED, Transaction,), {})
Transaction.CONFIRMED = type("Transaction.CONFIRMED", (Transaction.CONFIRMED, Transaction,), {})







class BlockchainConfig(ViaFfiUsingByteBuffer, object):
    def __init__(self):
        raise RuntimeError("BlockchainConfig cannot be instantiated directly")

    # Each enum variant is a nested class of the enum itself.
    class ELECTRUM(object):
        def __init__(self,config ):
            
            self.config = config
            

        def __str__(self):
            return "BlockchainConfig.ELECTRUM(config={} )".format(self.config )

        def __eq__(self, other):
            if not other.is_electrum():
                return False
            if self.config != other.config:
                return False
            return True
    class ESPLORA(object):
        def __init__(self,config ):
            
            self.config = config
            

        def __str__(self):
            return "BlockchainConfig.ESPLORA(config={} )".format(self.config )

        def __eq__(self, other):
            if not other.is_esplora():
                return False
            if self.config != other.config:
                return False
            return True
    

    # For each variant, we have an `is_NAME` method for easily checking
    # whether an instance is that variant.
    def is_electrum(self):
        return isinstance(self, BlockchainConfig.ELECTRUM)
    def is_esplora(self):
        return isinstance(self, BlockchainConfig.ESPLORA)
    

    @classmethod
    def _read(cls, buf):
        variant = buf.readI32()

        if variant == 1:
            return cls.ELECTRUM(
                config=ElectrumConfig._read(buf),
            )
        if variant == 2:
            return cls.ESPLORA(
                config=EsploraConfig._read(buf),
            )
        
        raise InternalError("Raw enum value doesn't match any cases")

    def _write(self, buf):
        if self.is_electrum():
            buf.writeI32(1)
            self.config._write(buf)
        if self.is_esplora():
            buf.writeI32(2)
            self.config._write(buf)
        

# Now, a little trick - we make each nested variant class be a subclass of the main
# enum class, so that method calls and instance checks etc will work intuitively.
# We might be able to do this a little more neatly with a metaclass, but this'll do.
BlockchainConfig.ELECTRUM = type("BlockchainConfig.ELECTRUM", (BlockchainConfig.ELECTRUM, BlockchainConfig,), {})
BlockchainConfig.ESPLORA = type("BlockchainConfig.ESPLORA", (BlockchainConfig.ESPLORA, BlockchainConfig,), {})






class MnemonicType(ViaFfiUsingByteBuffer, enum.Enum):
    WORDS12 = 1
    WORDS15 = 2
    WORDS18 = 3
    WORDS21 = 4
    WORDS24 = 5
    

    @staticmethod
    def _read(buf):
        variant = buf.readI32()
        if variant == 1:
            return MnemonicType.WORDS12
        if variant == 2:
            return MnemonicType.WORDS15
        if variant == 3:
            return MnemonicType.WORDS18
        if variant == 4:
            return MnemonicType.WORDS21
        if variant == 5:
            return MnemonicType.WORDS24
        
        raise InternalError("Raw enum value doesn't match any cases")

    def _write(self, buf):
        if self is MnemonicType.WORDS12:
            i = 1
            buf.writeI32(1)
        if self is MnemonicType.WORDS15:
            i = 2
            buf.writeI32(2)
        if self is MnemonicType.WORDS18:
            i = 3
            buf.writeI32(3)
        if self is MnemonicType.WORDS21:
            i = 4
            buf.writeI32(4)
        if self is MnemonicType.WORDS24:
            i = 5
            buf.writeI32(5)
        




def generate_extended_key(network,mnemonic_type,password):
    network = network
    mnemonic_type = mnemonic_type
    password = (None if password is None else password)
    _retval = rust_call_with_error(BdkError,_UniFFILib.bdk_b9b3_generate_extended_key,network._lower(),mnemonic_type._lower(),FfiConverterOptionalString._lower(password))
    return ExtendedKeyInfo._lift(_retval)



def restore_extended_key(network,mnemonic,password):
    network = network
    mnemonic = mnemonic
    password = (None if password is None else password)
    _retval = rust_call_with_error(BdkError,_UniFFILib.bdk_b9b3_restore_extended_key,network._lower(),FfiConverterString._lower(mnemonic),FfiConverterOptionalString._lower(password))
    return ExtendedKeyInfo._lift(_retval)



class OfflineWallet(object):
    def __init__(self, descriptor,network,database_config):
        descriptor = descriptor
        network = network
        database_config = database_config
        self._pointer = rust_call_with_error(BdkError,_UniFFILib.bdk_b9b3_OfflineWallet_new,FfiConverterString._lower(descriptor),network._lower(),database_config._lower())

    def __del__(self):
        # In case of partial initialization of instances.
        pointer = getattr(self, "_pointer", None)
        if pointer is not None:
            rust_call(_UniFFILib.ffi_bdk_b9b3_OfflineWallet_object_free, pointer)

    # Used by alternative constructors or any methods which return this type.
    @classmethod
    def _make_instance_(cls, pointer):
        # Lightly yucky way to bypass the usual __init__ logic
        # and just create a new instance with the required pointer.
        inst = cls.__new__(cls)
        inst._pointer = pointer
        return inst

    

    def get_new_address(self, ):
        _retval = rust_call(_UniFFILib.bdk_b9b3_OfflineWallet_get_new_address,self._pointer,)
        return FfiConverterString._lift(_retval)
    
    def get_last_unused_address(self, ):
        _retval = rust_call(_UniFFILib.bdk_b9b3_OfflineWallet_get_last_unused_address,self._pointer,)
        return FfiConverterString._lift(_retval)
    
    def get_balance(self, ):
        _retval = rust_call_with_error(
    BdkError,_UniFFILib.bdk_b9b3_OfflineWallet_get_balance,self._pointer,)
        return FfiConverterUInt64._lift(_retval)
    
    def sign(self, psbt):
        psbt = psbt
        rust_call_with_error(
    BdkError,_UniFFILib.bdk_b9b3_OfflineWallet_sign,self._pointer,psbt._lower())
    
    def get_transactions(self, ):
        _retval = rust_call_with_error(
    BdkError,_UniFFILib.bdk_b9b3_OfflineWallet_get_transactions,self._pointer,)
        return FfiConverterSequenceEnumTransaction._lift(_retval)
    
    

    @classmethod
    def _read(cls, buf):
        ptr = buf.readU64()
        if ptr == 0:
            raise InternalError("Raw pointer value was null")
        return cls._lift(ptr)

    @classmethod
    def _write(cls, value, buf):
        if not isinstance(value, OfflineWallet):
            raise TypeError("Expected OfflineWallet instance, {} found".format(value.__class__.__name__))
        buf.writeU64(value._lower())

    @classmethod
    def _lift(cls, pointer):
        return cls._make_instance_(pointer)

    def _lower(self):
        return self._pointer


class OnlineWallet(object):
    def __init__(self, descriptor,change_descriptor,network,database_config,blockchain_config):
        descriptor = descriptor
        change_descriptor = (None if change_descriptor is None else change_descriptor)
        network = network
        database_config = database_config
        blockchain_config = blockchain_config
        self._pointer = rust_call_with_error(BdkError,_UniFFILib.bdk_b9b3_OnlineWallet_new,FfiConverterString._lower(descriptor),FfiConverterOptionalString._lower(change_descriptor),network._lower(),database_config._lower(),blockchain_config._lower())

    def __del__(self):
        # In case of partial initialization of instances.
        pointer = getattr(self, "_pointer", None)
        if pointer is not None:
            rust_call(_UniFFILib.ffi_bdk_b9b3_OnlineWallet_object_free, pointer)

    # Used by alternative constructors or any methods which return this type.
    @classmethod
    def _make_instance_(cls, pointer):
        # Lightly yucky way to bypass the usual __init__ logic
        # and just create a new instance with the required pointer.
        inst = cls.__new__(cls)
        inst._pointer = pointer
        return inst

    

    def get_new_address(self, ):
        _retval = rust_call(_UniFFILib.bdk_b9b3_OnlineWallet_get_new_address,self._pointer,)
        return FfiConverterString._lift(_retval)
    
    def get_last_unused_address(self, ):
        _retval = rust_call(_UniFFILib.bdk_b9b3_OnlineWallet_get_last_unused_address,self._pointer,)
        return FfiConverterString._lift(_retval)
    
    def get_balance(self, ):
        _retval = rust_call_with_error(
    BdkError,_UniFFILib.bdk_b9b3_OnlineWallet_get_balance,self._pointer,)
        return FfiConverterUInt64._lift(_retval)
    
    def sign(self, psbt):
        psbt = psbt
        rust_call_with_error(
    BdkError,_UniFFILib.bdk_b9b3_OnlineWallet_sign,self._pointer,psbt._lower())
    
    def get_transactions(self, ):
        _retval = rust_call_with_error(
    BdkError,_UniFFILib.bdk_b9b3_OnlineWallet_get_transactions,self._pointer,)
        return FfiConverterSequenceEnumTransaction._lift(_retval)
    
    def get_network(self, ):
        _retval = rust_call(_UniFFILib.bdk_b9b3_OnlineWallet_get_network,self._pointer,)
        return Network._lift(_retval)
    
    def sync(self, progress_update,max_address_param):
        progress_update = progress_update
        max_address_param = (None if max_address_param is None else int(max_address_param))
        rust_call_with_error(
    BdkError,_UniFFILib.bdk_b9b3_OnlineWallet_sync,self._pointer,FfiConverterCallbackInterfaceBdkProgress._lower(progress_update),FfiConverterOptionalUInt32._lower(max_address_param))
    
    def broadcast(self, psbt):
        psbt = psbt
        _retval = rust_call_with_error(
    BdkError,_UniFFILib.bdk_b9b3_OnlineWallet_broadcast,self._pointer,psbt._lower())
        return Transaction._lift(_retval)
    
    

    @classmethod
    def _read(cls, buf):
        ptr = buf.readU64()
        if ptr == 0:
            raise InternalError("Raw pointer value was null")
        return cls._lift(ptr)

    @classmethod
    def _write(cls, value, buf):
        if not isinstance(value, OnlineWallet):
            raise TypeError("Expected OnlineWallet instance, {} found".format(value.__class__.__name__))
        buf.writeU64(value._lower())

    @classmethod
    def _lift(cls, pointer):
        return cls._make_instance_(pointer)

    def _lower(self):
        return self._pointer


class PartiallySignedBitcoinTransaction(object):
    def __init__(self, wallet,recipient,amount,fee_rate):
        wallet = wallet
        recipient = recipient
        amount = int(amount)
        fee_rate = (None if fee_rate is None else float(fee_rate))
        self._pointer = rust_call_with_error(BdkError,_UniFFILib.bdk_b9b3_PartiallySignedBitcoinTransaction_new,wallet._lower(),FfiConverterString._lower(recipient),FfiConverterUInt64._lower(amount),FfiConverterOptionalFloat._lower(fee_rate))

    def __del__(self):
        # In case of partial initialization of instances.
        pointer = getattr(self, "_pointer", None)
        if pointer is not None:
            rust_call(_UniFFILib.ffi_bdk_b9b3_PartiallySignedBitcoinTransaction_object_free, pointer)

    # Used by alternative constructors or any methods which return this type.
    @classmethod
    def _make_instance_(cls, pointer):
        # Lightly yucky way to bypass the usual __init__ logic
        # and just create a new instance with the required pointer.
        inst = cls.__new__(cls)
        inst._pointer = pointer
        return inst

    

    

    @classmethod
    def _read(cls, buf):
        ptr = buf.readU64()
        if ptr == 0:
            raise InternalError("Raw pointer value was null")
        return cls._lift(ptr)

    @classmethod
    def _write(cls, value, buf):
        if not isinstance(value, PartiallySignedBitcoinTransaction):
            raise TypeError("Expected PartiallySignedBitcoinTransaction instance, {} found".format(value.__class__.__name__))
        buf.writeU64(value._lower())

    @classmethod
    def _lift(cls, pointer):
        return cls._make_instance_(pointer)

    def _lower(self):
        return self._pointer

class SledDbConfiguration(ViaFfiUsingByteBuffer, object):
    def __init__(self,path, tree_name ):
        self.path = path
        self.tree_name = tree_name

    def __str__(self):
        return "SledDbConfiguration(path={}, tree_name={} )".format(self.path, self.tree_name )

    def __eq__(self, other):
        if self.path != other.path:
            return False
        if self.tree_name != other.tree_name:
            return False
        return True

    @staticmethod
    def _read(buf):
        return SledDbConfiguration(
            path=FfiConverterString._read(buf),
            tree_name=FfiConverterString._read(buf)
        )

    def _write(self, buf):
        FfiConverterString._write(self.path, buf)
        FfiConverterString._write(self.tree_name, buf)

class TransactionDetails(ViaFfiUsingByteBuffer, object):
    def __init__(self,fees, received, sent, txid ):
        self.fees = fees
        self.received = received
        self.sent = sent
        self.txid = txid

    def __str__(self):
        return "TransactionDetails(fees={}, received={}, sent={}, txid={} )".format(self.fees, self.received, self.sent, self.txid )

    def __eq__(self, other):
        if self.fees != other.fees:
            return False
        if self.received != other.received:
            return False
        if self.sent != other.sent:
            return False
        if self.txid != other.txid:
            return False
        return True

    @staticmethod
    def _read(buf):
        return TransactionDetails(
            fees=FfiConverterOptionalUInt64._read(buf),
            received=FfiConverterUInt64._read(buf),
            sent=FfiConverterUInt64._read(buf),
            txid=FfiConverterString._read(buf)
        )

    def _write(self, buf):
        FfiConverterOptionalUInt64._write(self.fees, buf)
        FfiConverterUInt64._write(self.received, buf)
        FfiConverterUInt64._write(self.sent, buf)
        FfiConverterString._write(self.txid, buf)

class Confirmation(ViaFfiUsingByteBuffer, object):
    def __init__(self,height, timestamp ):
        self.height = height
        self.timestamp = timestamp

    def __str__(self):
        return "Confirmation(height={}, timestamp={} )".format(self.height, self.timestamp )

    def __eq__(self, other):
        if self.height != other.height:
            return False
        if self.timestamp != other.timestamp:
            return False
        return True

    @staticmethod
    def _read(buf):
        return Confirmation(
            height=FfiConverterUInt32._read(buf),
            timestamp=FfiConverterUInt64._read(buf)
        )

    def _write(self, buf):
        FfiConverterUInt32._write(self.height, buf)
        FfiConverterUInt64._write(self.timestamp, buf)

class ElectrumConfig(ViaFfiUsingByteBuffer, object):
    def __init__(self,url, socks5, retry, timeout, stop_gap ):
        self.url = url
        self.socks5 = socks5
        self.retry = retry
        self.timeout = timeout
        self.stop_gap = stop_gap

    def __str__(self):
        return "ElectrumConfig(url={}, socks5={}, retry={}, timeout={}, stop_gap={} )".format(self.url, self.socks5, self.retry, self.timeout, self.stop_gap )

    def __eq__(self, other):
        if self.url != other.url:
            return False
        if self.socks5 != other.socks5:
            return False
        if self.retry != other.retry:
            return False
        if self.timeout != other.timeout:
            return False
        if self.stop_gap != other.stop_gap:
            return False
        return True

    @staticmethod
    def _read(buf):
        return ElectrumConfig(
            url=FfiConverterString._read(buf),
            socks5=FfiConverterOptionalString._read(buf),
            retry=FfiConverterUInt8._read(buf),
            timeout=FfiConverterOptionalUInt8._read(buf),
            stop_gap=FfiConverterUInt64._read(buf)
        )

    def _write(self, buf):
        FfiConverterString._write(self.url, buf)
        FfiConverterOptionalString._write(self.socks5, buf)
        FfiConverterUInt8._write(self.retry, buf)
        FfiConverterOptionalUInt8._write(self.timeout, buf)
        FfiConverterUInt64._write(self.stop_gap, buf)

class EsploraConfig(ViaFfiUsingByteBuffer, object):
    def __init__(self,base_url, proxy, timeout_read, timeout_write, stop_gap ):
        self.base_url = base_url
        self.proxy = proxy
        self.timeout_read = timeout_read
        self.timeout_write = timeout_write
        self.stop_gap = stop_gap

    def __str__(self):
        return "EsploraConfig(base_url={}, proxy={}, timeout_read={}, timeout_write={}, stop_gap={} )".format(self.base_url, self.proxy, self.timeout_read, self.timeout_write, self.stop_gap )

    def __eq__(self, other):
        if self.base_url != other.base_url:
            return False
        if self.proxy != other.proxy:
            return False
        if self.timeout_read != other.timeout_read:
            return False
        if self.timeout_write != other.timeout_write:
            return False
        if self.stop_gap != other.stop_gap:
            return False
        return True

    @staticmethod
    def _read(buf):
        return EsploraConfig(
            base_url=FfiConverterString._read(buf),
            proxy=FfiConverterOptionalString._read(buf),
            timeout_read=FfiConverterUInt64._read(buf),
            timeout_write=FfiConverterUInt64._read(buf),
            stop_gap=FfiConverterUInt64._read(buf)
        )

    def _write(self, buf):
        FfiConverterString._write(self.base_url, buf)
        FfiConverterOptionalString._write(self.proxy, buf)
        FfiConverterUInt64._write(self.timeout_read, buf)
        FfiConverterUInt64._write(self.timeout_write, buf)
        FfiConverterUInt64._write(self.stop_gap, buf)

class ExtendedKeyInfo(ViaFfiUsingByteBuffer, object):
    def __init__(self,mnemonic, xprv, fingerprint ):
        self.mnemonic = mnemonic
        self.xprv = xprv
        self.fingerprint = fingerprint

    def __str__(self):
        return "ExtendedKeyInfo(mnemonic={}, xprv={}, fingerprint={} )".format(self.mnemonic, self.xprv, self.fingerprint )

    def __eq__(self, other):
        if self.mnemonic != other.mnemonic:
            return False
        if self.xprv != other.xprv:
            return False
        if self.fingerprint != other.fingerprint:
            return False
        return True

    @staticmethod
    def _read(buf):
        return ExtendedKeyInfo(
            mnemonic=FfiConverterString._read(buf),
            xprv=FfiConverterString._read(buf),
            fingerprint=FfiConverterString._read(buf)
        )

    def _write(self, buf):
        FfiConverterString._write(self.mnemonic, buf)
        FfiConverterString._write(self.xprv, buf)
        FfiConverterString._write(self.fingerprint, buf)

class BdkError(ViaFfiUsingByteBuffer):

    # Each variant is a nested class of the error itself.
    # It just carries a string error message, so no special implementation is necessary.
    class InvalidU32Bytes(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(1)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Generic(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(2)
            message = str(self)
            FfiConverterString._write(message, buf)
    class ScriptDoesntHaveAddressForm(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(3)
            message = str(self)
            FfiConverterString._write(message, buf)
    class NoRecipients(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(4)
            message = str(self)
            FfiConverterString._write(message, buf)
    class NoUtxosSelected(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(5)
            message = str(self)
            FfiConverterString._write(message, buf)
    class OutputBelowDustLimit(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(6)
            message = str(self)
            FfiConverterString._write(message, buf)
    class InsufficientFunds(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(7)
            message = str(self)
            FfiConverterString._write(message, buf)
    class BnBTotalTriesExceeded(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(8)
            message = str(self)
            FfiConverterString._write(message, buf)
    class BnBNoExactMatch(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(9)
            message = str(self)
            FfiConverterString._write(message, buf)
    class UnknownUtxo(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(10)
            message = str(self)
            FfiConverterString._write(message, buf)
    class TransactionNotFound(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(11)
            message = str(self)
            FfiConverterString._write(message, buf)
    class TransactionConfirmed(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(12)
            message = str(self)
            FfiConverterString._write(message, buf)
    class IrreplaceableTransaction(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(13)
            message = str(self)
            FfiConverterString._write(message, buf)
    class FeeRateTooLow(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(14)
            message = str(self)
            FfiConverterString._write(message, buf)
    class FeeTooLow(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(15)
            message = str(self)
            FfiConverterString._write(message, buf)
    class FeeRateUnavailable(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(16)
            message = str(self)
            FfiConverterString._write(message, buf)
    class MissingKeyOrigin(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(17)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Key(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(18)
            message = str(self)
            FfiConverterString._write(message, buf)
    class ChecksumMismatch(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(19)
            message = str(self)
            FfiConverterString._write(message, buf)
    class SpendingPolicyRequired(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(20)
            message = str(self)
            FfiConverterString._write(message, buf)
    class InvalidPolicyPathError(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(21)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Signer(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(22)
            message = str(self)
            FfiConverterString._write(message, buf)
    class InvalidNetwork(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(23)
            message = str(self)
            FfiConverterString._write(message, buf)
    class InvalidProgressValue(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(24)
            message = str(self)
            FfiConverterString._write(message, buf)
    class ProgressUpdateError(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(25)
            message = str(self)
            FfiConverterString._write(message, buf)
    class InvalidOutpoint(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(26)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Descriptor(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(27)
            message = str(self)
            FfiConverterString._write(message, buf)
    class AddressValidator(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(28)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Encode(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(29)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Miniscript(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(30)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Bip32(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(31)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Secp256k1(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(32)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Json(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(33)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Hex(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(34)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Psbt(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(35)
            message = str(self)
            FfiConverterString._write(message, buf)
    class PsbtParse(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(36)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Electrum(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(37)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Esplora(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(38)
            message = str(self)
            FfiConverterString._write(message, buf)
    class Sled(ViaFfiUsingByteBuffer, Exception):
        def _write(self, buf):
            buf.writeI32(39)
            message = str(self)
            FfiConverterString._write(message, buf)

    @classmethod
    def _read(cls, buf):
        variant = buf.readI32()
        if variant == 1:
            return cls.InvalidU32Bytes(FfiConverterString._read(buf))
        if variant == 2:
            return cls.Generic(FfiConverterString._read(buf))
        if variant == 3:
            return cls.ScriptDoesntHaveAddressForm(FfiConverterString._read(buf))
        if variant == 4:
            return cls.NoRecipients(FfiConverterString._read(buf))
        if variant == 5:
            return cls.NoUtxosSelected(FfiConverterString._read(buf))
        if variant == 6:
            return cls.OutputBelowDustLimit(FfiConverterString._read(buf))
        if variant == 7:
            return cls.InsufficientFunds(FfiConverterString._read(buf))
        if variant == 8:
            return cls.BnBTotalTriesExceeded(FfiConverterString._read(buf))
        if variant == 9:
            return cls.BnBNoExactMatch(FfiConverterString._read(buf))
        if variant == 10:
            return cls.UnknownUtxo(FfiConverterString._read(buf))
        if variant == 11:
            return cls.TransactionNotFound(FfiConverterString._read(buf))
        if variant == 12:
            return cls.TransactionConfirmed(FfiConverterString._read(buf))
        if variant == 13:
            return cls.IrreplaceableTransaction(FfiConverterString._read(buf))
        if variant == 14:
            return cls.FeeRateTooLow(FfiConverterString._read(buf))
        if variant == 15:
            return cls.FeeTooLow(FfiConverterString._read(buf))
        if variant == 16:
            return cls.FeeRateUnavailable(FfiConverterString._read(buf))
        if variant == 17:
            return cls.MissingKeyOrigin(FfiConverterString._read(buf))
        if variant == 18:
            return cls.Key(FfiConverterString._read(buf))
        if variant == 19:
            return cls.ChecksumMismatch(FfiConverterString._read(buf))
        if variant == 20:
            return cls.SpendingPolicyRequired(FfiConverterString._read(buf))
        if variant == 21:
            return cls.InvalidPolicyPathError(FfiConverterString._read(buf))
        if variant == 22:
            return cls.Signer(FfiConverterString._read(buf))
        if variant == 23:
            return cls.InvalidNetwork(FfiConverterString._read(buf))
        if variant == 24:
            return cls.InvalidProgressValue(FfiConverterString._read(buf))
        if variant == 25:
            return cls.ProgressUpdateError(FfiConverterString._read(buf))
        if variant == 26:
            return cls.InvalidOutpoint(FfiConverterString._read(buf))
        if variant == 27:
            return cls.Descriptor(FfiConverterString._read(buf))
        if variant == 28:
            return cls.AddressValidator(FfiConverterString._read(buf))
        if variant == 29:
            return cls.Encode(FfiConverterString._read(buf))
        if variant == 30:
            return cls.Miniscript(FfiConverterString._read(buf))
        if variant == 31:
            return cls.Bip32(FfiConverterString._read(buf))
        if variant == 32:
            return cls.Secp256k1(FfiConverterString._read(buf))
        if variant == 33:
            return cls.Json(FfiConverterString._read(buf))
        if variant == 34:
            return cls.Hex(FfiConverterString._read(buf))
        if variant == 35:
            return cls.Psbt(FfiConverterString._read(buf))
        if variant == 36:
            return cls.PsbtParse(FfiConverterString._read(buf))
        if variant == 37:
            return cls.Electrum(FfiConverterString._read(buf))
        if variant == 38:
            return cls.Esplora(FfiConverterString._read(buf))
        if variant == 39:
            return cls.Sled(FfiConverterString._read(buf))
        
        raise InternalError("Raw enum value doesn't match any cases")


# Declaration and FfiConverters for BdkProgress Callback Interface

class BdkProgress:
    def update(progress,message):
        raise NotImplementedError

    

def py_foreignCallbackCallbackInterfaceBdkProgress(handle, method, args, buf_ptr):
    
    def invoke_update(python_callback, args):
        rval = None
        with args.consumeWithStream() as buf:
            rval = python_callback.update(
                FfiConverterFloat._read(buf), 
                FfiConverterOptionalString._read(buf) 
                )
        return RustBuffer.alloc(0)
        # TODO catch errors and report them back to Rust.
        # https://github.com/mozilla/uniffi-rs/issues/351
    

    cb = FfiConverterCallbackInterfaceBdkProgress._lift(handle)
    if not cb:
        raise InternalError("No callback in handlemap; this is a Uniffi bug")

    if method == IDX_CALLBACK_FREE:
        FfiConverterCallbackInterfaceBdkProgress.drop(handle)
        # No return value.
        # See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
        return 0

    if method == 1:
        buf_ptr[0] = invoke_update(cb, args)
        # Value written to out buffer.
        # See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
        return 1
    

    # This should never happen, because an out of bounds method index won't
    # ever be used. Once we can catch errors, we should return an InternalException.
    # https://github.com/mozilla/uniffi-rs/issues/351

    # An unexpected error happened.
    # See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs`
    return -1

# We need to keep this function reference alive:
# if they get GC'd while in use then UniFFI internals could attempt to call a function
# that is in freed memory.
# That would be...uh...bad. Yeah, that's the word. Bad.
foreignCallbackCallbackInterfaceBdkProgress = FOREIGN_CALLBACK_T(py_foreignCallbackCallbackInterfaceBdkProgress)

# The FfiConverter which transforms the Callbacks in to Handles to pass to Rust.
rust_call(lambda err: _UniFFILib.ffi_bdk_b9b3_BdkProgress_init_callback(foreignCallbackCallbackInterfaceBdkProgress, err))
FfiConverterCallbackInterfaceBdkProgress = FfiConverterCallbackInterface(foreignCallbackCallbackInterfaceBdkProgress)
class FfiConverterUInt8(Primitive):
    @staticmethod
    def _read(buf):
        return FfiConverterUInt8._lift(buf.readU8())

    @staticmethod
    def _write(value, buf):
        buf.writeU8(FfiConverterUInt8._lower(value))
class FfiConverterUInt32(Primitive):
    @staticmethod
    def _read(buf):
        return FfiConverterUInt32._lift(buf.readU32())

    @staticmethod
    def _write(value, buf):
        buf.writeU32(FfiConverterUInt32._lower(value))
class FfiConverterUInt64(Primitive):
    @staticmethod
    def _read(buf):
        return FfiConverterUInt64._lift(buf.readU64())

    @staticmethod
    def _write(value, buf):
        buf.writeU64(FfiConverterUInt64._lower(value))
class FfiConverterFloat(Primitive):
    @staticmethod
    def _read(buf):
        return FfiConverterFloat._lift(buf.readFloat())

    @staticmethod
    def _write(value, buf):
        buf.writeFloat(FfiConverterFloat._lower(value))
class FfiConverterString:
    @staticmethod
    def _read(buf):
        size = buf.readI32()
        if size < 0:
            raise InternalError("Unexpected negative string length")
        utf8Bytes = buf.read(size)
        return utf8Bytes.decode("utf-8")

    @staticmethod
    def _write(value, buf):
        utf8Bytes = value.encode("utf-8")
        buf.writeI32(len(utf8Bytes))
        buf.write(utf8Bytes)

    @staticmethod
    def _lift(buf):
        with buf.consumeWithStream() as stream:
            return stream.read(stream.remaining()).decode("utf-8")

    @staticmethod
    def _lower(value):
        with RustBuffer.allocWithBuilder() as builder:
            builder.write(value.encode("utf-8"))
            return builder.finalize()
# Helper code for OfflineWallet class is found in ObjectTemplate.py
# Helper code for OnlineWallet class is found in ObjectTemplate.py
# Helper code for PartiallySignedBitcoinTransaction class is found in ObjectTemplate.py
# Helper code for Confirmation record is found in RecordTemplate.py
# Helper code for ElectrumConfig record is found in RecordTemplate.py
# Helper code for EsploraConfig record is found in RecordTemplate.py
# Helper code for ExtendedKeyInfo record is found in RecordTemplate.py
# Helper code for SledDbConfiguration record is found in RecordTemplate.py
# Helper code for TransactionDetails record is found in RecordTemplate.py
# Helper code for BlockchainConfig enum is found in EnumTemplate.py
# Helper code for DatabaseConfig enum is found in EnumTemplate.py
# Helper code for MnemonicType enum is found in EnumTemplate.py
# Helper code for Network enum is found in EnumTemplate.py
# Helper code for Transaction enum is found in EnumTemplate.py
# Helper code for BdkError error is found in ErrorTemplate.py


class FfiConverterOptionalUInt8(FfiConverterUsingByteBuffer):
    @staticmethod
    def _write(value, buf):
        FfiConverterOptional._write(value, buf, lambda v, buf: FfiConverterUInt8._write(v, buf))

    @staticmethod
    def _read(buf):
        return FfiConverterOptional._read(buf, lambda buf: FfiConverterUInt8._read(buf))


class FfiConverterOptionalUInt32(FfiConverterUsingByteBuffer):
    @staticmethod
    def _write(value, buf):
        FfiConverterOptional._write(value, buf, lambda v, buf: FfiConverterUInt32._write(v, buf))

    @staticmethod
    def _read(buf):
        return FfiConverterOptional._read(buf, lambda buf: FfiConverterUInt32._read(buf))


class FfiConverterOptionalUInt64(FfiConverterUsingByteBuffer):
    @staticmethod
    def _write(value, buf):
        FfiConverterOptional._write(value, buf, lambda v, buf: FfiConverterUInt64._write(v, buf))

    @staticmethod
    def _read(buf):
        return FfiConverterOptional._read(buf, lambda buf: FfiConverterUInt64._read(buf))


class FfiConverterOptionalFloat(FfiConverterUsingByteBuffer):
    @staticmethod
    def _write(value, buf):
        FfiConverterOptional._write(value, buf, lambda v, buf: FfiConverterFloat._write(v, buf))

    @staticmethod
    def _read(buf):
        return FfiConverterOptional._read(buf, lambda buf: FfiConverterFloat._read(buf))


class FfiConverterOptionalString(FfiConverterUsingByteBuffer):
    @staticmethod
    def _write(value, buf):
        FfiConverterOptional._write(value, buf, lambda v, buf: FfiConverterString._write(v, buf))

    @staticmethod
    def _read(buf):
        return FfiConverterOptional._read(buf, lambda buf: FfiConverterString._read(buf))


class FfiConverterSequenceEnumTransaction(FfiConverterUsingByteBuffer):
    @staticmethod
    def _write(value, buf):
        FfiConverterSequence._write(value, buf, lambda v, buf: v._write(buf))

    @staticmethod
    def _read(buf):
        return FfiConverterSequence._read(buf, lambda buf: Transaction._read(buf))

__all__ = [
    "InternalError",
    "Network",
    "DatabaseConfig",
    "Transaction",
    "BlockchainConfig",
    "MnemonicType",
    "SledDbConfiguration",
    "TransactionDetails",
    "Confirmation",
    "ElectrumConfig",
    "EsploraConfig",
    "ExtendedKeyInfo",
    "generate_extended_key",
    "restore_extended_key",
    "OfflineWallet",
    "OnlineWallet",
    "PartiallySignedBitcoinTransaction",
    "BdkError",
    "BdkProgress",
]

