"""
Copyright 2025 Biglup Labs.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from __future__ import annotations
from .._ffi import ffi, lib
from ..errors import CardanoError
from ..cbor.cbor_reader import CborReader
from ..cbor.cbor_writer import CborWriter
[docs]
class BootstrapWitness:
"""
Represents a bootstrap witness for Byron-era addresses.
Bootstrap witnesses are used to sign transactions that spend from
Byron-era addresses. They include additional information beyond
just the public key and signature, such as the chain code and
address attributes.
"""
[docs]
def __init__(self, ptr) -> None:
if ptr == ffi.NULL:
raise CardanoError("BootstrapWitness: invalid handle")
self._ptr = ptr
def __del__(self) -> None:
if getattr(self, "_ptr", ffi.NULL) not in (None, ffi.NULL):
ptr_ptr = ffi.new("cardano_bootstrap_witness_t**", self._ptr)
lib.cardano_bootstrap_witness_unref(ptr_ptr)
self._ptr = ffi.NULL
[docs]
def __enter__(self) -> BootstrapWitness:
return self
[docs]
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
pass
[docs]
def __repr__(self) -> str:
return "BootstrapWitness(...)"
[docs]
@classmethod
def new(
cls,
vkey: bytes,
signature: bytes,
chain_code: bytes,
attributes: bytes,
) -> BootstrapWitness:
"""
Creates a new bootstrap witness.
Args:
vkey: The verification key (32 bytes for Ed25519).
signature: The signature (64 bytes for Ed25519).
chain_code: The chain code (32 bytes).
attributes: The address attributes (CBOR encoded).
Returns:
A new BootstrapWitness instance.
Raises:
CardanoError: If creation fails.
"""
# Create ed25519_public_key
vkey_ptr = ffi.new("cardano_ed25519_public_key_t**")
err = lib.cardano_ed25519_public_key_from_bytes(vkey, len(vkey), vkey_ptr)
if err != 0:
raise CardanoError(f"Failed to create public key (error code: {err})")
# Create ed25519_signature
sig_ptr = ffi.new("cardano_ed25519_signature_t**")
err = lib.cardano_ed25519_signature_from_bytes(signature, len(signature), sig_ptr)
if err != 0:
lib.cardano_ed25519_public_key_unref(vkey_ptr)
raise CardanoError(f"Failed to create signature (error code: {err})")
# Create chain code buffer
chain_code_ptr = ffi.new("cardano_buffer_t**")
err = lib.cardano_buffer_new_from(chain_code, len(chain_code), chain_code_ptr)
if err != 0:
lib.cardano_ed25519_public_key_unref(vkey_ptr)
lib.cardano_ed25519_signature_unref(sig_ptr)
raise CardanoError(f"Failed to create chain code buffer (error code: {err})")
# Create attributes buffer
attr_ptr = ffi.new("cardano_buffer_t**")
err = lib.cardano_buffer_new_from(attributes, len(attributes), attr_ptr)
if err != 0:
lib.cardano_ed25519_public_key_unref(vkey_ptr)
lib.cardano_ed25519_signature_unref(sig_ptr)
lib.cardano_buffer_unref(chain_code_ptr)
raise CardanoError(f"Failed to create attributes buffer (error code: {err})")
# Create witness
out = ffi.new("cardano_bootstrap_witness_t**")
err = lib.cardano_bootstrap_witness_new(
vkey_ptr[0], sig_ptr[0], chain_code_ptr[0], attr_ptr[0], out
)
# Clean up intermediate objects
lib.cardano_ed25519_public_key_unref(vkey_ptr)
lib.cardano_ed25519_signature_unref(sig_ptr)
lib.cardano_buffer_unref(chain_code_ptr)
lib.cardano_buffer_unref(attr_ptr)
if err != 0:
raise CardanoError(f"Failed to create BootstrapWitness (error code: {err})")
return cls(out[0])
[docs]
@classmethod
def from_cbor(cls, reader: CborReader) -> BootstrapWitness:
"""
Deserializes a BootstrapWitness from CBOR data.
Args:
reader: A CborReader positioned at the witness data.
Returns:
A new BootstrapWitness deserialized from the CBOR data.
Raises:
CardanoError: If deserialization fails.
"""
out = ffi.new("cardano_bootstrap_witness_t**")
err = lib.cardano_bootstrap_witness_from_cbor(reader._ptr, out)
if err != 0:
raise CardanoError(
f"Failed to deserialize BootstrapWitness from CBOR (error code: {err})"
)
return cls(out[0])
[docs]
def to_cbor(self, writer: CborWriter) -> None:
"""
Serializes the witness to CBOR format.
Args:
writer: A CborWriter to write the serialized data to.
Raises:
CardanoError: If serialization fails.
"""
err = lib.cardano_bootstrap_witness_to_cbor(self._ptr, writer._ptr)
if err != 0:
raise CardanoError(
f"Failed to serialize BootstrapWitness to CBOR (error code: {err})"
)
@property
def vkey(self) -> bytes:
"""
The verification key (public key).
Returns:
The 32-byte Ed25519 public key.
"""
ptr = lib.cardano_bootstrap_witness_get_vkey(self._ptr)
if ptr == ffi.NULL:
raise CardanoError("Failed to get vkey")
lib.cardano_ed25519_public_key_ref(ptr)
data_ptr = lib.cardano_ed25519_public_key_get_data(ptr)
size = lib.cardano_ed25519_public_key_get_bytes_size(ptr)
result = bytes(ffi.buffer(data_ptr, size))
pk_ptr = ffi.new("cardano_ed25519_public_key_t**", ptr)
lib.cardano_ed25519_public_key_unref(pk_ptr)
return result
@vkey.setter
def vkey(self, value: bytes) -> None:
"""
Sets the verification key.
Args:
value: The 32-byte Ed25519 public key.
Raises:
CardanoError: If setting fails.
"""
vkey_ptr = ffi.new("cardano_ed25519_public_key_t**")
err = lib.cardano_ed25519_public_key_from_bytes(value, len(value), vkey_ptr)
if err != 0:
raise CardanoError(f"Failed to create public key (error code: {err})")
err = lib.cardano_bootstrap_witness_set_vkey(self._ptr, vkey_ptr[0])
lib.cardano_ed25519_public_key_unref(vkey_ptr)
if err != 0:
raise CardanoError(f"Failed to set vkey (error code: {err})")
@property
def signature(self) -> bytes:
"""
The signature.
Returns:
The 64-byte Ed25519 signature.
"""
ptr = lib.cardano_bootstrap_witness_get_signature(self._ptr)
if ptr == ffi.NULL:
raise CardanoError("Failed to get signature")
lib.cardano_ed25519_signature_ref(ptr)
data_ptr = lib.cardano_ed25519_signature_get_data(ptr)
size = lib.cardano_ed25519_signature_get_bytes_size(ptr)
result = bytes(ffi.buffer(data_ptr, size))
sig_ptr = ffi.new("cardano_ed25519_signature_t**", ptr)
lib.cardano_ed25519_signature_unref(sig_ptr)
return result
@signature.setter
def signature(self, value: bytes) -> None:
"""
Sets the signature.
Args:
value: The 64-byte Ed25519 signature.
Raises:
CardanoError: If setting fails.
"""
sig_ptr = ffi.new("cardano_ed25519_signature_t**")
err = lib.cardano_ed25519_signature_from_bytes(value, len(value), sig_ptr)
if err != 0:
raise CardanoError(f"Failed to create signature (error code: {err})")
err = lib.cardano_bootstrap_witness_set_signature(self._ptr, sig_ptr[0])
lib.cardano_ed25519_signature_unref(sig_ptr)
if err != 0:
raise CardanoError(f"Failed to set signature (error code: {err})")
@property
def chain_code(self) -> bytes:
"""
The chain code for key derivation.
Returns:
The 32-byte chain code.
"""
ptr = lib.cardano_bootstrap_witness_get_chain_code(self._ptr)
if ptr == ffi.NULL:
raise CardanoError("Failed to get chain_code")
lib.cardano_buffer_ref(ptr)
data_ptr = lib.cardano_buffer_get_data(ptr)
size = lib.cardano_buffer_get_size(ptr)
result = bytes(ffi.buffer(data_ptr, size))
buf_ptr = ffi.new("cardano_buffer_t**", ptr)
lib.cardano_buffer_unref(buf_ptr)
return result
@chain_code.setter
def chain_code(self, value: bytes) -> None:
"""
Sets the chain code.
Args:
value: The 32-byte chain code.
Raises:
CardanoError: If setting fails.
"""
buf_ptr = ffi.new("cardano_buffer_t**")
err = lib.cardano_buffer_new_from(value, len(value), buf_ptr)
if err != 0:
raise CardanoError(f"Failed to create buffer (error code: {err})")
err = lib.cardano_bootstrap_witness_set_chain_code(self._ptr, buf_ptr[0])
lib.cardano_buffer_unref(buf_ptr)
if err != 0:
raise CardanoError(f"Failed to set chain_code (error code: {err})")
@property
def attributes(self) -> bytes:
"""
The address attributes (CBOR encoded).
Returns:
The attributes bytes.
"""
ptr = lib.cardano_bootstrap_witness_get_attributes(self._ptr)
if ptr == ffi.NULL:
raise CardanoError("Failed to get attributes")
lib.cardano_buffer_ref(ptr)
data_ptr = lib.cardano_buffer_get_data(ptr)
size = lib.cardano_buffer_get_size(ptr)
result = bytes(ffi.buffer(data_ptr, size))
buf_ptr = ffi.new("cardano_buffer_t**", ptr)
lib.cardano_buffer_unref(buf_ptr)
return result
@attributes.setter
def attributes(self, value: bytes) -> None:
"""
Sets the address attributes.
Args:
value: The attributes bytes (CBOR encoded).
Raises:
CardanoError: If setting fails.
"""
buf_ptr = ffi.new("cardano_buffer_t**")
err = lib.cardano_buffer_new_from(value, len(value), buf_ptr)
if err != 0:
raise CardanoError(f"Failed to create buffer (error code: {err})")
err = lib.cardano_bootstrap_witness_set_attributes(self._ptr, buf_ptr[0])
lib.cardano_buffer_unref(buf_ptr)
if err != 0:
raise CardanoError(f"Failed to set attributes (error code: {err})")
[docs]
def to_cip116_json(self, writer: "JsonWriter") -> None:
"""
Serializes this object to CIP-116 compliant JSON.
Args:
writer: The JsonWriter to write the JSON to.
Raises:
CardanoError: If serialization fails.
"""
from ..json.json_writer import JsonWriter
if not isinstance(writer, JsonWriter):
raise TypeError("writer must be a JsonWriter instance")
err = lib.cardano_bootstrap_witness_to_cip116_json(self._ptr, writer._ptr)
if err != 0:
raise CardanoError(f"Failed to serialize to CIP-116 JSON (error code: {err})")