use std::{
path::PathBuf,
str::FromStr,
sync::Arc,
time::{Duration, SystemTime},
};
use ant_bootstrap::{Bootstrap, BootstrapConfig};
use ant_evm::{PaymentQuote, QuotingMetrics, RewardsAddress};
use ant_protocol::storage::DataTypes;
use bls::{PK_SIZE, PublicKey, SecretKey};
use bytes::Bytes;
use exponential_backoff::Backoff;
use libp2p::Multiaddr;
use pyo3::{
basic::CompareOp,
exceptions::{PyConnectionError, PyRuntimeError, PyValueError},
prelude::*,
};
use pyo3_async_runtimes::tokio::future_into_py;
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use tokio::sync::mpsc;
use xor_name::{XOR_NAME_LEN, XorName};
use crate::{
Amount, AttoTokens, Chunk, ChunkAddress, Client, ClientConfig, ClientOperatingStrategy,
GraphEntry, GraphEntryAddress, InitialPeersConfig, MaxFeePerGas, Network as EVMNetwork,
Pointer, PointerAddress, Scratchpad, ScratchpadAddress, Signature, TransactionConfig, Wallet,
client::{
ClientEvent, UploadSummary,
chunk::DataMapChunk,
data::DataAddress,
files::{archive_private::PrivateArchiveDataMap, archive_public::ArchiveAddress},
key_derivation::{
DerivationIndex, DerivedPubkey, DerivedSecretKey, MainPubkey, MainSecretKey,
},
payment::{PaymentOption, Receipt},
pointer::PointerTarget,
quote::{QuoteForAddress, StoreQuote},
vault::{UserData, VaultSecretKey},
},
files::{Metadata, PrivateArchive, PublicArchive},
networking::{PeerId, Quorum, RetryStrategy, Strategy},
register::{RegisterAddress, RegisterHistory},
};
fn scratchpad_error_to_py_err(
error: crate::client::data_types::scratchpad::ScratchpadError,
) -> PyErr {
use crate::client::data_types::scratchpad::ScratchpadError;
match error {
ScratchpadError::Fork(conflicting_scratchpads) => {
let py_scratchpads: Vec<PyScratchpad> = conflicting_scratchpads
.iter()
.map(|s| PyScratchpad { inner: s.clone() })
.collect();
let message = format!("{}", ScratchpadError::Fork(conflicting_scratchpads.clone()));
Python::with_gil(|py| {
let exception = PyRuntimeError::new_err(message);
if let Ok(exc_value) = exception
.value(py)
.downcast::<pyo3::exceptions::PyRuntimeError>()
{
let _ = exc_value.setattr("conflicting_scratchpads", py_scratchpads);
}
exception
})
}
_ => PyRuntimeError::new_err(format!("{error}")),
}
}
#[pyclass(name = "AttoTokens")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct PyAttoTokens {
inner: AttoTokens,
}
#[pymethods]
impl PyAttoTokens {
#[staticmethod]
fn zero() -> Self {
Self {
inner: AttoTokens::zero(),
}
}
fn is_zero(&self) -> bool {
self.inner.is_zero()
}
#[staticmethod]
fn from_atto(value: String) -> PyResult<Self> {
let amount = Amount::from_str(&value)
.map_err(|e| PyValueError::new_err(format!("Invalid amount: {e}")))?;
Ok(Self {
inner: AttoTokens::from_atto(amount),
})
}
#[staticmethod]
fn from_u64(value: u64) -> Self {
Self {
inner: AttoTokens::from_u64(value),
}
}
#[staticmethod]
fn from_u128(value: u128) -> Self {
Self {
inner: AttoTokens::from_u128(value),
}
}
fn as_atto(&self) -> String {
self.inner.as_atto().to_string()
}
fn checked_add(&self, rhs: &PyAttoTokens) -> Option<PyAttoTokens> {
self.inner
.checked_add(rhs.inner)
.map(|inner| PyAttoTokens { inner })
}
fn checked_sub(&self, rhs: &PyAttoTokens) -> Option<PyAttoTokens> {
self.inner
.checked_sub(rhs.inner)
.map(|inner| PyAttoTokens { inner })
}
fn as_bytes(&self) -> Vec<u8> {
self.inner.to_bytes()
}
#[staticmethod]
fn from_str(value_str: &str) -> PyResult<Self> {
AttoTokens::from_str(value_str)
.map(|inner| Self { inner })
.map_err(|e| PyValueError::new_err(format!("Failed to parse AttoTokens: {e}")))
}
fn __str__(&self) -> String {
self.inner.to_string()
}
fn __repr__(&self) -> String {
format!("AttoTokens('{}')", self.inner)
}
}
#[pyclass(name = "DataTypes", eq, eq_int)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum PyDataTypes {
Chunk = 0,
GraphEntry = 1,
Pointer = 2,
Scratchpad = 3,
}
impl From<PyDataTypes> for DataTypes {
fn from(py_data_type: PyDataTypes) -> Self {
match py_data_type {
PyDataTypes::Chunk => DataTypes::Chunk,
PyDataTypes::GraphEntry => DataTypes::GraphEntry,
PyDataTypes::Pointer => DataTypes::Pointer,
PyDataTypes::Scratchpad => DataTypes::Scratchpad,
}
}
}
#[pyclass(name = "Client")]
pub(crate) struct PyClient {
inner: Client,
}
#[pymethods]
impl PyClient {
#[staticmethod]
fn init(py: Python) -> PyResult<Bound<PyAny>> {
future_into_py(py, async {
let inner = Client::init()
.await
.map_err(|e| PyConnectionError::new_err(format!("Failed to connect: {e}")))?;
Ok(PyClient { inner })
})
}
#[staticmethod]
fn init_local(py: Python) -> PyResult<Bound<PyAny>> {
future_into_py(py, async {
let inner = Client::init_local()
.await
.map_err(|e| PyConnectionError::new_err(format!("Failed to connect: {e}")))?;
Ok(PyClient { inner })
})
}
#[staticmethod]
fn init_alpha(py: Python) -> PyResult<Bound<PyAny>> {
future_into_py(py, async {
let inner = Client::init_alpha()
.await
.map_err(|e| PyConnectionError::new_err(format!("Failed to connect: {e}")))?;
Ok(PyClient { inner })
})
}
#[staticmethod]
fn init_with_peers(py: Python, peers: Vec<String>) -> PyResult<Bound<PyAny>> {
let peers: Vec<Multiaddr> = peers
.iter()
.map(|p| Multiaddr::from_str(p))
.collect::<Result<_, _>>()
.map_err(|e| PyValueError::new_err(format!("Failed to parse peers: {e}")))?;
future_into_py(py, async {
let inner = Client::init_with_peers(peers)
.await
.map_err(|e| PyConnectionError::new_err(format!("Failed to connect: {e}")))?;
Ok(PyClient { inner })
})
}
#[staticmethod]
fn init_with_config(py: Python, config: PyClientConfig) -> PyResult<Bound<PyAny>> {
future_into_py(py, async {
let inner = Client::init_with_config(config.inner)
.await
.map_err(|e| PyConnectionError::new_err(format!("Failed to connect: {e}")))?;
Ok(PyClient { inner })
})
}
fn enable_client_events(&mut self) -> PyClientEventReceiver {
let receiver = self.inner.enable_client_events();
PyClientEventReceiver {
inner: Arc::new(futures::lock::Mutex::new(receiver)),
}
}
fn evm_network(&self) -> PyEVMNetwork {
PyEVMNetwork {
inner: self.inner.evm_network().clone(),
}
}
fn with_payment_mode(
mut slf: PyRefMut<'_, Self>,
payment_mode: PyPaymentMode,
) -> PyRefMut<'_, Self> {
let mode = match payment_mode {
PyPaymentMode::Standard => crate::client::quote::PaymentMode::Standard,
PyPaymentMode::SingleNode => crate::client::quote::PaymentMode::SingleNode,
};
slf.inner = slf.inner.clone().with_payment_mode(mode);
slf
}
fn chunk_cost<'a>(&self, py: Python<'a>, addr: PyChunkAddress) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let cost = client
.chunk_cost(&addr.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get chunk cost: {e}")))?;
Ok(cost.to_string())
})
}
fn chunk_get<'a>(&self, py: Python<'a>, addr: &PyChunkAddress) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let addr = addr.inner;
future_into_py(py, async move {
let chunk = client
.chunk_get(&addr)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get chunk: {e}")))?;
Ok(chunk.value.to_vec())
})
}
fn chunk_put<'a>(
&self,
py: Python<'a>,
data: Vec<u8>,
payment: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment.inner.clone();
let chunk = Chunk::new(Bytes::from(data));
future_into_py(py, async move {
let (cost, addr) = client
.chunk_put(&chunk, payment)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to put chunk: {e}")))?;
Ok((cost.to_string(), PyChunkAddress { inner: addr }))
})
}
fn graph_entry_get<'a>(
&self,
py: Python<'a>,
addr: PyGraphEntryAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let entry = client
.graph_entry_get(&addr.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get graph entry: {e}")))?;
Ok(PyGraphEntry { inner: entry })
})
}
fn graph_entry_check_existance<'a>(
&self,
py: Python<'a>,
addr: PyGraphEntryAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let exists = client
.graph_entry_check_existence(&addr.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get graph entry: {e}")))?;
Ok(exists)
})
}
fn graph_entry_put<'a>(
&self,
py: Python<'a>,
entry: PyGraphEntry,
payment_option: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment_option.inner.clone();
future_into_py(py, async move {
let (cost, addr) = client
.graph_entry_put(entry.inner, payment)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get graph entry: {e}")))?;
Ok((cost.to_string(), PyGraphEntryAddress { inner: addr }))
})
}
fn graph_entry_cost<'a>(&self, py: Python<'a>, key: PyPublicKey) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let cost = client.graph_entry_cost(&key.inner).await.map_err(|e| {
PyRuntimeError::new_err(format!("Failed to get graph entry cost: {e}"))
})?;
Ok(cost.to_string())
})
}
fn scratchpad_get_from_public_key<'a>(
&self,
py: Python<'a>,
public_key: PyPublicKey,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let scratchpad = client
.scratchpad_get_from_public_key(&public_key.inner)
.await
.map_err(scratchpad_error_to_py_err)?;
Ok(PyScratchpad { inner: scratchpad })
})
}
fn scratchpad_get<'a>(
&self,
py: Python<'a>,
addr: PyScratchpadAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let scratchpad = client
.scratchpad_get(&addr.inner)
.await
.map_err(scratchpad_error_to_py_err)?;
Ok(PyScratchpad { inner: scratchpad })
})
}
fn scratchpad_check_existance<'a>(
&self,
py: Python<'a>,
addr: PyScratchpadAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let exists = client
.scratchpad_check_existence(&addr.inner)
.await
.map_err(scratchpad_error_to_py_err)?;
Ok(exists)
})
}
fn scratchpad_put<'a>(
&self,
py: Python<'a>,
scratchpad: PyScratchpad,
payment_option: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment_option.inner.clone();
future_into_py(py, async move {
let (cost, addr) = client
.scratchpad_put(scratchpad.inner, payment)
.await
.map_err(scratchpad_error_to_py_err)?;
Ok((cost.to_string(), PyScratchpadAddress { inner: addr }))
})
}
fn scratchpad_put_update<'a>(
&self,
py: Python<'a>,
scratchpad: PyScratchpad,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
client
.scratchpad_put_update(scratchpad.inner)
.await
.map_err(scratchpad_error_to_py_err)?;
Ok(())
})
}
fn scratchpad_create<'a>(
&self,
py: Python<'a>,
owner: PySecretKey,
content_type: u64,
initial_data: Vec<u8>,
payment_option: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment_option.inner.clone();
future_into_py(py, async move {
let (cost, addr) = client
.scratchpad_create(
&owner.inner,
content_type,
&Bytes::from(initial_data),
payment,
)
.await
.map_err(scratchpad_error_to_py_err)?;
Ok((cost.to_string(), PyScratchpadAddress { inner: addr }))
})
}
fn scratchpad_update<'a>(
&self,
py: Python<'a>,
owner: PySecretKey,
content_type: u64,
data: Vec<u8>,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
client
.scratchpad_update(&owner.inner, content_type, &Bytes::from(data))
.await
.map_err(scratchpad_error_to_py_err)?;
Ok(())
})
}
fn scratchpad_update_from<'a>(
&self,
py: Python<'a>,
current: PyScratchpad,
owner: PySecretKey,
content_type: u64,
data: Vec<u8>,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let new_scratchpad = client
.scratchpad_update_from(
¤t.inner,
&owner.inner,
content_type,
&Bytes::from(data),
)
.await
.map_err(scratchpad_error_to_py_err)?;
Ok(PyScratchpad {
inner: new_scratchpad,
})
})
}
fn scratchpad_cost<'a>(
&self,
py: Python<'a>,
public_key: PyPublicKey,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let cost = client
.scratchpad_cost(&public_key.inner)
.await
.map_err(|e| {
PyRuntimeError::new_err(format!("Failed to get scratchpad cost: {e}"))
})?;
Ok(cost.to_string())
})
}
#[staticmethod]
fn scratchpad_verify(scratchpad: &PyScratchpad) -> PyResult<()> {
Client::scratchpad_verify(&scratchpad.inner).map_err(scratchpad_error_to_py_err)
}
fn archive_cost<'a>(
&self,
py: Python<'a>,
archive: PyPublicArchive,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let cost = client
.archive_cost(&archive.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get archive cost: {e}")))?;
Ok(cost.to_string())
})
}
fn archive_get<'a>(
&self,
py: Python<'a>,
data_map: &PyDataMapChunk,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let data_map = data_map.inner.clone();
future_into_py(py, async move {
let archive = client
.archive_get(&data_map)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get archive: {e}")))?;
Ok(PyPrivateArchive { inner: archive })
})
}
fn archive_put<'a>(
&self,
py: Python<'a>,
archive: PyPrivateArchive,
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let (cost, data_map) = client
.archive_put(&archive.inner, payment.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to put archive: {e}")))?;
Ok((cost.to_string(), PyDataMapChunk { inner: data_map }))
})
}
fn archive_put_public<'a>(
&self,
py: Python<'a>,
archive: PyPublicArchive,
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let (cost, addr) = client
.archive_put_public(&archive.inner, payment.inner)
.await
.map_err(|e| {
PyRuntimeError::new_err(format!("Failed to put public archive: {e}"))
})?;
Ok((cost.to_string(), PyArchiveAddress { inner: addr }))
})
}
fn file_cost<'a>(&self, py: Python<'a>, path: PathBuf) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let cost = client
.file_cost(&path)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get file cost: {e}")))?;
Ok(cost.to_string())
})
}
fn file_download<'a>(
&self,
py: Python<'a>,
data_map: PyDataMapChunk,
path: PathBuf,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
client
.file_download(&data_map.inner, path)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to download file: {e}")))?;
Ok(())
})
}
fn dir_download<'a>(
&self,
py: Python<'a>,
data_map: PyPrivateArchiveDataMap,
dir_path: PathBuf,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
client
.dir_download(&data_map.inner, dir_path)
.await
.map_err(|e| {
PyRuntimeError::new_err(format!("Failed to download directory: {e}"))
})?;
Ok(())
})
}
fn dir_content_upload<'a>(
&self,
py: Python<'a>,
dir_path: PathBuf,
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let (cost, archive) = client
.dir_content_upload(dir_path, payment.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to upload directory: {e}")))?;
Ok((cost.to_string(), PyPrivateArchive { inner: archive }))
})
}
fn file_download_public<'a>(
&self,
py: Python<'a>,
addr: &PyDataAddress,
path: PathBuf,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let addr = addr.inner;
future_into_py(py, async move {
client
.file_download_public(&addr, path)
.await
.map_err(|e| {
PyRuntimeError::new_err(format!("Failed to download public file: {e}"))
})?;
Ok(())
})
}
fn dir_upload<'a>(
&self,
py: Python<'a>,
dir_path: PathBuf,
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let (cost, data_map) = client
.dir_upload(dir_path, payment.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to upload directory: {e}")))?;
Ok((
cost.to_string(),
PyPrivateArchiveDataMap { inner: data_map },
))
})
}
fn file_content_upload<'a>(
&self,
py: Python<'a>,
path: PathBuf,
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let (cost, data_map) = client
.file_content_upload(path, payment.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to upload file: {e}")))?;
Ok((cost.to_string(), PyDataMapChunk { inner: data_map }))
})
}
fn file_content_upload_public<'a>(
&self,
py: Python<'a>,
path: PathBuf,
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let (cost, data_addr) = client
.file_content_upload_public(path, payment.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to upload file: {e}")))?;
Ok((cost.to_string(), PyDataAddress { inner: data_addr }))
})
}
fn data_put<'a>(
&self,
py: Python<'a>,
data: Vec<u8>,
payment: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment.inner.clone();
future_into_py(py, async move {
let (cost, data_map) = client
.data_put(Bytes::from(data), payment)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to put data: {e}")))?;
Ok((cost.to_string(), PyDataMapChunk { inner: data_map }))
})
}
fn data_get<'a>(&self, py: Python<'a>, access: &PyDataMapChunk) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let access = access.inner.clone();
future_into_py(py, async move {
let data = client
.data_get(&access)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get data: {e}")))?;
Ok(data.to_vec())
})
}
fn data_stream<'a>(
&self,
py: Python<'a>,
access: &PyDataMapChunk,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let access = access.inner.clone();
future_into_py(py, async move {
let stream = client
.data_stream(&access)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create stream: {e}")))?;
Ok(PyDataStream::new(stream))
})
}
fn data_cost<'a>(&self, py: Python<'a>, data: Vec<u8>) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let cost = client
.data_cost(Bytes::from(data))
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get data cost: {e}")))?;
Ok(cost.to_string())
})
}
fn data_put_public<'a>(
&self,
py: Python<'a>,
data: Vec<u8>,
payment: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment.inner.clone();
future_into_py(py, async move {
let (cost, addr) = client
.data_put_public(bytes::Bytes::from(data), payment)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to put data: {e}")))?;
Ok((cost.to_string(), PyDataAddress { inner: addr }))
})
}
fn data_get_public<'a>(
&self,
py: Python<'a>,
addr: &PyDataAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let addr = addr.inner;
future_into_py(py, async move {
let data = client
.data_get_public(&addr)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get data: {e}")))?;
Ok(data.to_vec())
})
}
fn data_stream_public<'a>(
&self,
py: Python<'a>,
addr: &PyDataAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let addr = addr.inner;
future_into_py(py, async move {
let stream = client
.data_stream_public(&addr)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create stream: {e}")))?;
Ok(PyDataStream::new(stream))
})
}
fn dir_upload_public<'a>(
&self,
py: Python<'a>,
dir_path: PathBuf,
payment: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment.inner.clone();
future_into_py(py, async move {
let (cost, addr) = client
.dir_upload_public(dir_path, payment)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to upload directory: {e}")))?;
Ok((cost.to_string(), PyArchiveAddress { inner: addr }))
})
}
fn dir_download_public<'a>(
&self,
py: Python<'a>,
addr: &PyArchiveAddress,
dir_path: PathBuf,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let addr = addr.inner;
future_into_py(py, async move {
client
.dir_download_public(&addr, dir_path)
.await
.map_err(|e| {
PyRuntimeError::new_err(format!("Failed to download directory: {e}"))
})?;
Ok(())
})
}
fn dir_content_upload_public<'a>(
&self,
py: Python<'a>,
dir_path: PathBuf,
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let (cost, archive) = client
.dir_content_upload_public(dir_path, payment.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to upload directory: {e}")))?;
Ok((cost.to_string(), PyPublicArchive { inner: archive }))
})
}
fn archive_get_public<'a>(
&self,
py: Python<'a>,
addr: &PyArchiveAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let addr = addr.inner;
future_into_py(py, async move {
let archive = client
.archive_get_public(&addr)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get archive: {e}")))?;
Ok(PyPublicArchive { inner: archive })
})
}
fn vault_cost<'a>(
&self,
py: Python<'a>,
key: &PyVaultSecretKey,
max_expected_size: u64,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let key = key.inner.clone();
future_into_py(py, async move {
let cost = client
.vault_cost(&key, max_expected_size)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get vault cost: {e}")))?;
Ok(cost.to_string())
})
}
fn vault_put<'a>(
&self,
py: Python<'a>,
data: Vec<u8>,
payment: &PyPaymentOption,
key: &PyVaultSecretKey,
content_type: u64,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment.inner.clone();
let key = key.inner.clone();
future_into_py(py, async move {
match client
.vault_put(bytes::Bytes::from(data), payment, &key, content_type)
.await
{
Ok(cost) => Ok(cost.to_string()),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to write to vault: {e}"
))),
}
})
}
fn register_history(&self, addr: String) -> PyResult<PyRegisterHistory> {
let client = self.inner.clone();
let addr = RegisterAddress::from_hex(&addr)
.map_err(|e| PyValueError::new_err(format!("Failed to parse address: {e}")))?;
let history = client.register_history(&addr);
Ok(PyRegisterHistory::new(history))
}
#[staticmethod]
fn register_key_from_name(owner: PySecretKey, name: &str) -> PyResult<PySecretKey> {
let key = Client::register_key_from_name(&owner.inner, name);
Ok(PySecretKey { inner: key })
}
#[staticmethod]
fn register_value_from_bytes(bytes: &[u8]) -> PyResult<[u8; 32]> {
let value = Client::register_value_from_bytes(bytes)
.map_err(|e| PyValueError::new_err(format!("`bytes` has invalid length: {e}")))?;
Ok(value)
}
fn register_create<'a>(
&self,
py: Python<'a>,
owner: PySecretKey,
value: [u8; 32],
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let (cost, addr) = client
.register_create(&owner.inner, value, payment.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create register: {e}")))?;
Ok((cost.to_string(), addr.to_hex()))
})
}
fn register_update<'a>(
&self,
py: Python<'a>,
owner: PySecretKey,
value: [u8; 32],
payment: PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let cost = client
.register_update(&owner.inner, value, payment.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to update register: {e}")))?;
Ok(cost.to_string())
})
}
fn register_get<'a>(&self, py: Python<'a>, addr: String) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let addr = RegisterAddress::from_hex(&addr)
.map_err(|e| PyValueError::new_err(format!("Failed to parse address: {e}")))?;
future_into_py(py, async move {
let data = client
.register_get(&addr)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get register: {e}")))?;
Ok(data)
})
}
fn register_cost<'a>(&self, py: Python<'a>, owner: PyPublicKey) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let cost = client.register_cost(&owner.inner).await.map_err(|e| {
PyRuntimeError::new_err(format!("Failed to get register cost: {e}"))
})?;
Ok(cost.to_string())
})
}
fn fetch_and_decrypt_vault<'a>(
&self,
py: Python<'a>,
key: &PyVaultSecretKey,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let key = key.inner.clone();
future_into_py(py, async move {
match client.vault_get(&key).await {
Ok((data, content_type)) => Ok((data.to_vec(), content_type)),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to fetch vault: {e}"
))),
}
})
}
fn vault_get_user_data<'a>(
&self,
py: Python<'a>,
key: &PyVaultSecretKey,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let key = key.inner.clone();
future_into_py(py, async move {
match client.vault_get_user_data(&key).await {
Ok(user_data) => Ok(PyUserData { inner: user_data }),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to get user data from vault: {e}"
))),
}
})
}
fn vault_put_user_data<'a>(
&self,
py: Python<'a>,
key: &PyVaultSecretKey,
payment: &PyPaymentOption,
user_data: &PyUserData,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let key = key.inner.clone();
let payment = payment.inner.clone();
let user_data = user_data.inner.clone();
future_into_py(py, async move {
match client.vault_put_user_data(&key, payment, user_data).await {
Ok(cost) => Ok(cost.to_string()),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to put user data: {e}"
))),
}
})
}
fn write_bytes_to_vault<'a>(
&self,
py: Python<'a>,
data: Vec<u8>,
payment: &PyPaymentOption,
key: &PyVaultSecretKey,
content_type: u64,
) -> PyResult<Bound<'a, PyAny>> {
self.vault_put(py, data, payment, key, content_type)
}
fn get_user_data_from_vault<'a>(
&self,
py: Python<'a>,
key: &PyVaultSecretKey,
) -> PyResult<Bound<'a, PyAny>> {
self.vault_get_user_data(py, key)
}
fn put_user_data_to_vault<'a>(
&self,
py: Python<'a>,
key: &PyVaultSecretKey,
payment: &PyPaymentOption,
user_data: &PyUserData,
) -> PyResult<Bound<'a, PyAny>> {
self.vault_put_user_data(py, key, payment, user_data)
}
fn pointer_get<'a>(
&self,
py: Python<'a>,
addr: PyPointerAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
match client.pointer_get(&addr.inner).await {
Ok(pointer) => Ok(PyPointer { inner: pointer }),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to get pointer: {e}"
))),
}
})
}
fn pointer_check_existance<'a>(
&self,
py: Python<'a>,
addr: PyPointerAddress,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let exists = client
.pointer_check_existence(&addr.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get pointer: {e}")))?;
Ok(exists)
})
}
fn pointer_put<'a>(
&self,
py: Python<'a>,
pointer: &PyPointer,
payment_option: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let pointer = pointer.inner.clone();
let payment = payment_option.inner.clone();
future_into_py(py, async move {
let (_cost, addr) = client
.pointer_put(pointer, payment)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to put pointer: {e}")))?;
Ok(PyPointerAddress { inner: addr })
})
}
fn pointer_create<'a>(
&self,
py: Python<'a>,
owner: PySecretKey,
target: PyPointerTarget,
payment_option: &PyPaymentOption,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let payment = payment_option.inner.clone();
future_into_py(py, async move {
let (cost, addr) = client
.pointer_create(&owner.inner, target.inner, payment)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create pointer: {e}")))?;
Ok((cost.to_string(), PyPointerAddress { inner: addr }))
})
}
fn pointer_update<'a>(
&self,
py: Python<'a>,
owner: PySecretKey,
target: PyPointerTarget,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
client
.pointer_update(&owner.inner, target.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to update pointer: {e}")))?;
Ok(())
})
}
fn pointer_update_from<'a>(
&self,
py: Python<'a>,
current: PyPointer,
owner: PySecretKey,
target: PyPointerTarget,
) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
let new_pointer = client
.pointer_update_from(¤t.inner, &owner.inner, target.inner)
.await
.map_err(|e| PyRuntimeError::new_err(format!("Failed to update pointer: {e}")))?;
Ok(PyPointer { inner: new_pointer })
})
}
fn pointer_cost<'a>(&self, py: Python<'a>, key: &PyPublicKey) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
let key = key.inner;
future_into_py(py, async move {
match client.pointer_cost(&key).await {
Ok(cost) => Ok(cost.to_string()),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to get pointer cost: {e}"
))),
}
})
}
#[staticmethod]
fn pointer_verify(pointer: &PyPointer) -> PyResult<()> {
Client::pointer_verify(&pointer.inner)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to verify pointer: {e}")))
}
fn get_raw_quotes<'a>(
&self,
py: Python<'a>,
data_type: PyDataTypes,
content_addrs: Vec<(PyXorName, usize)>,
) -> PyResult<Bound<'a, PyAny>> {
let data_type: DataTypes = data_type.into();
let client = self.inner.clone();
let content_addrs_iter = content_addrs
.into_iter()
.map(|(xor_name, size)| (xor_name.inner, size));
future_into_py(py, async move {
let results = client.get_raw_quotes(data_type, content_addrs_iter).await;
let py_results: Vec<_> = results
.into_iter()
.map(|result| match result {
Ok((xor_name, quotes)) => {
let py_xor_name = PyXorName { inner: xor_name };
let py_quotes: Vec<_> = quotes
.into_iter()
.map(|(peer_id, addresses, quote)| {
let peer_id_str = peer_id.to_string();
let addresses_str = addresses
.0
.iter()
.map(|addr| addr.to_string())
.collect::<Vec<_>>()
.join(",");
let py_payment_quote = PyPaymentQuote { inner: quote };
(peer_id_str, addresses_str, py_payment_quote)
})
.collect();
(py_xor_name, py_quotes)
}
Err(err) => {
error!("Error in get_raw_quotes: {}", err);
let empty_xor = PyXorName {
inner: XorName::default(),
};
let empty_quotes: Vec<(String, String, PyPaymentQuote)> = Vec::new();
(empty_xor, empty_quotes)
}
})
.collect();
Ok(py_results)
})
}
fn get_store_quotes<'a>(
&self,
py: Python<'a>,
data_type: PyDataTypes,
content_addrs: Vec<(PyXorName, usize)>,
) -> PyResult<Bound<'a, PyAny>> {
let data_type: DataTypes = data_type.into();
let client = self.inner.clone();
let content_addrs_iter = content_addrs
.into_iter()
.map(|(xor_name, size)| (xor_name.inner, size));
future_into_py(py, async move {
match client.get_store_quotes(data_type, content_addrs_iter).await {
Ok(quotes) => {
let py_store_quote = PyStoreQuote { inner: quotes };
Ok(py_store_quote)
}
Err(err) => Err(PyErr::new::<PyValueError, _>(format!("{err:?}"))),
}
})
}
}
#[pyclass(name = "ClientEvent")]
#[derive(Debug, Clone)]
pub struct PyClientEvent {
inner: ClientEvent,
}
#[pymethods]
impl PyClientEvent {
#[getter]
fn event_type(&self) -> &'static str {
match self.inner {
ClientEvent::UploadComplete(_) => "UploadComplete",
}
}
#[getter]
fn upload_summary(&self) -> Option<PyUploadSummary> {
match &self.inner {
ClientEvent::UploadComplete(summary) => Some(PyUploadSummary {
inner: summary.clone(),
}),
}
}
fn __str__(&self) -> PyResult<String> {
Ok(format!("{:?}", self.inner))
}
}
#[pyclass(name = "ClientEventReceiver")]
pub struct PyClientEventReceiver {
inner: Arc<futures::lock::Mutex<mpsc::Receiver<ClientEvent>>>,
}
#[pymethods]
impl PyClientEventReceiver {
fn recv<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
let inner = Arc::clone(&self.inner);
future_into_py(py, async move {
let mut receiver = inner.lock().await;
let result = receiver
.recv()
.await
.map(|event| PyClientEvent { inner: event });
Ok(result)
})
}
}
#[pyclass(name = "ClientOperatingStrategy")]
#[derive(Debug, Clone)]
pub struct PyClientOperatingStrategy {
inner: ClientOperatingStrategy,
}
#[pymethods]
impl PyClientOperatingStrategy {
#[new]
fn new() -> Self {
Self {
inner: ClientOperatingStrategy::new(),
}
}
#[getter]
fn get_chunks(&self) -> PyStrategy {
PyStrategy {
inner: self.inner.chunks.clone(),
}
}
#[getter]
fn get_graph_entry(&self) -> PyStrategy {
PyStrategy {
inner: self.inner.graph_entry.clone(),
}
}
#[getter]
fn get_pointer(&self) -> PyStrategy {
PyStrategy {
inner: self.inner.pointer.clone(),
}
}
#[getter]
fn get_scratchpad(&self) -> PyStrategy {
PyStrategy {
inner: self.inner.scratchpad.clone(),
}
}
fn __str__(&self) -> String {
format!("{:?}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "PointerAddress", eq, ord)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PyPointerAddress {
inner: PointerAddress,
}
#[pymethods]
impl PyPointerAddress {
#[new]
fn new(public_key: PyPublicKey) -> PyResult<Self> {
Ok(Self {
inner: PointerAddress::new(public_key.inner),
})
}
pub fn owner(&self) -> PyPublicKey {
PyPublicKey {
inner: *self.inner.owner(),
}
}
#[getter]
fn hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
Ok(Self {
inner: PointerAddress::from_hex(hex)
.map_err(|e| PyValueError::new_err(e.to_string()))?,
})
}
fn __str__(&self) -> PyResult<String> {
Ok(self.hex())
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("PointerAddress('{}')", self.hex()))
}
}
#[pyclass(name = "Pointer")]
#[derive(Debug, Clone)]
pub struct PyPointer {
inner: Pointer,
}
#[pymethods]
impl PyPointer {
#[new]
pub fn new(key: &PySecretKey, counter: u64, target: &PyPointerTarget) -> PyResult<Self> {
Ok(Self {
inner: Pointer::new(&key.inner, counter, target.inner.clone()),
})
}
pub fn address(&self) -> PyPointerAddress {
PyPointerAddress {
inner: self.inner.address(),
}
}
#[getter]
fn target(&self) -> PyPointerTarget {
PyPointerTarget {
inner: self.inner.target().clone(),
}
}
fn __str__(&self) -> PyResult<String> {
Ok(format!("Pointer('{}')", self.inner.address().to_hex()))
}
}
#[pyclass(name = "PointerTarget")]
#[derive(Debug, Clone)]
pub struct PyPointerTarget {
inner: PointerTarget,
}
#[pymethods]
impl PyPointerTarget {
#[staticmethod]
fn new_chunk(addr: PyChunkAddress) -> PyResult<Self> {
Ok(Self {
inner: PointerTarget::ChunkAddress(addr.inner),
})
}
#[staticmethod]
fn new_graph_entry(addr: PyGraphEntryAddress) -> PyResult<Self> {
Ok(Self {
inner: PointerTarget::GraphEntryAddress(addr.inner),
})
}
#[staticmethod]
fn new_pointer(addr: PyPointerAddress) -> PyResult<Self> {
Ok(Self {
inner: PointerTarget::PointerAddress(addr.inner),
})
}
#[staticmethod]
fn new_scratchpad(addr: PyScratchpadAddress) -> PyResult<Self> {
Ok(Self {
inner: PointerTarget::ScratchpadAddress(addr.inner),
})
}
#[getter]
fn target(&self) -> PyPointerTarget {
PyPointerTarget {
inner: PointerTarget::ChunkAddress(ChunkAddress::new(self.inner.xorname())),
}
}
#[getter]
fn hex(&self) -> String {
self.inner.to_hex()
}
fn __str__(&self) -> PyResult<String> {
Ok(format!("PointerTarget('{}')", self.hex()))
}
}
#[pyclass(name = "Chunk", eq, ord)]
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Debug)]
pub struct PyChunk {
inner: Chunk,
}
#[pymethods]
impl PyChunk {
#[new]
fn new(value: Vec<u8>) -> Self {
Self {
inner: Chunk::new(Bytes::from(value)),
}
}
#[getter]
fn value(&self) -> Vec<u8> {
self.inner.value().to_vec()
}
#[getter]
fn address(&self) -> PyChunkAddress {
PyChunkAddress {
inner: *self.inner.address(),
}
}
fn network_address(&self) -> String {
self.inner.network_address().to_string()
}
fn name(&self) -> PyXorName {
PyXorName {
inner: *self.inner.name(),
}
}
fn size(&self) -> usize {
self.inner.size()
}
fn is_too_big(&self) -> bool {
self.inner.is_too_big()
}
#[classattr]
fn max_raw_size() -> usize {
Chunk::MAX_RAW_SIZE
}
#[classattr]
fn max_size() -> usize {
Chunk::MAX_SIZE
}
fn __str__(&self) -> PyResult<String> {
Ok(format!("Chunk(size={})", self.inner.size()))
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!(
"Chunk(address='{}', size={})",
self.inner.address().to_hex(),
self.inner.size()
))
}
}
#[pyclass(name = "ChunkAddress", eq, ord)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PyChunkAddress {
inner: ChunkAddress,
}
#[pymethods]
impl PyChunkAddress {
#[new]
fn new(addr: PyXorName) -> PyResult<Self> {
Ok(Self {
inner: ChunkAddress::new(addr.inner),
})
}
pub fn xorname(&self) -> PyXorName {
PyXorName {
inner: *self.inner.xorname(),
}
}
#[staticmethod]
fn from_content(data: Vec<u8>) -> PyResult<Self> {
Ok(Self {
inner: ChunkAddress::new(XorName::from_content(&data[..])),
})
}
#[staticmethod]
fn random() -> PyResult<Self> {
Ok(Self {
inner: ChunkAddress::new(XorName::random(&mut rand::thread_rng())),
})
}
#[getter]
fn hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
Ok(Self {
inner: ChunkAddress::from_hex(hex).map_err(|e| PyValueError::new_err(e.to_string()))?,
})
}
fn __str__(&self) -> PyResult<String> {
Ok(self.hex())
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("ChunkAddress('{}')", self.hex()))
}
}
#[pyclass(name = "GraphEntryAddress", eq, ord)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PyGraphEntryAddress {
inner: GraphEntryAddress,
}
#[pymethods]
impl PyGraphEntryAddress {
#[staticmethod]
fn new(public_key: PyPublicKey) -> PyResult<Self> {
Ok(Self {
inner: GraphEntryAddress::new(public_key.inner),
})
}
#[getter]
fn hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
Ok(Self {
inner: GraphEntryAddress::from_hex(hex)
.map_err(|e| PyValueError::new_err(e.to_string()))?,
})
}
fn __str__(&self) -> PyResult<String> {
Ok(self.hex())
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("GraphEntryAddress('{}')", self.hex()))
}
}
#[pyclass(name = "BootstrapCacheConfig")]
#[derive(Debug, Clone, Default)]
pub struct PyBootstrapCacheConfig {
pub(crate) inner: BootstrapConfig,
}
#[pymethods]
impl PyBootstrapCacheConfig {
#[new]
fn new(local: bool) -> Self {
Self {
inner: BootstrapConfig::new(local),
}
}
fn with_cache_dir(&self, path: PathBuf) -> Self {
Self {
inner: self.inner.clone().with_cache_dir(path),
}
}
fn with_max_cached_peers(&self, max_peers: usize) -> Self {
Self {
inner: self.inner.clone().with_max_cached_peers(max_peers),
}
}
fn with_max_addrs_per_cached_peer(&self, max_addrs: usize) -> Self {
Self {
inner: self.inner.clone().with_max_addrs_per_cached_peer(max_addrs),
}
}
fn with_disable_cache_writing(&self, disable: bool) -> Self {
Self {
inner: self.inner.clone().with_disable_cache_writing(disable),
}
}
fn with_disable_cache_reading(&self, disable: bool) -> Self {
Self {
inner: self.inner.clone().with_disable_cache_reading(disable),
}
}
fn with_backwards_compatible_writes(&self, enable: bool) -> Self {
Self {
inner: self.inner.clone().with_backwards_compatible_writes(enable),
}
}
fn with_min_cache_save_duration(&self, seconds: u64) -> Self {
Self {
inner: self
.inner
.clone()
.with_min_cache_save_duration(Duration::from_secs(seconds)),
}
}
fn with_max_cache_save_duration(&self, seconds: u64) -> Self {
Self {
inner: self
.inner
.clone()
.with_max_cache_save_duration(Duration::from_secs(seconds)),
}
}
fn with_cache_save_scaling_factor(&self, factor: u32) -> Self {
Self {
inner: self.inner.clone().with_cache_save_scaling_factor(factor),
}
}
fn with_network_contacts_url(&self, urls: Vec<String>) -> Self {
Self {
inner: self.inner.clone().with_network_contacts_url(urls),
}
}
fn with_first(&self, first: bool) -> Self {
Self {
inner: self.inner.clone().with_first(first),
}
}
fn with_initial_peers(&self, peers: Vec<String>) -> PyResult<Self> {
let parsed = peers
.into_iter()
.map(|addr| addr.parse::<Multiaddr>())
.collect::<Result<Vec<_>, _>>()
.map_err(|err| PyValueError::new_err(format!("Invalid multiaddr: {err}")))?;
Ok(Self {
inner: self.inner.clone().with_initial_peers(parsed),
})
}
#[getter]
fn max_cached_peers(&self) -> usize {
self.inner.max_cached_peers
}
#[getter]
fn max_addrs_per_cached_peer(&self) -> usize {
self.inner.max_addrs_per_cached_peer
}
#[getter]
fn cache_dir(&self) -> PathBuf {
self.inner.cache_dir.clone()
}
#[getter]
fn disable_cache_writing(&self) -> bool {
self.inner.disable_cache_writing
}
#[getter]
fn disable_cache_reading(&self) -> bool {
self.inner.disable_cache_reading
}
#[getter]
fn backwards_compatible_writes(&self) -> bool {
self.inner.backwards_compatible_writes
}
#[getter]
fn min_cache_save_duration(&self) -> u64 {
self.inner.min_cache_save_duration.as_secs()
}
#[getter]
fn max_cache_save_duration(&self) -> u64 {
self.inner.max_cache_save_duration.as_secs()
}
#[getter]
fn cache_save_scaling_factor(&self) -> u32 {
self.inner.cache_save_scaling_factor
}
#[getter]
fn network_contacts_url(&self) -> Vec<String> {
self.inner.network_contacts_url.clone()
}
#[getter]
fn local(&self) -> bool {
self.inner.local
}
fn __str__(&self) -> String {
format!("{:?}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "InitialPeersConfig")]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct PyInitialPeersConfig {
inner: InitialPeersConfig,
}
#[pymethods]
impl PyInitialPeersConfig {
#[new]
fn new() -> Self {
Self {
inner: InitialPeersConfig::default(),
}
}
#[getter]
fn get_first(&self) -> bool {
self.inner.first
}
#[setter]
fn set_first(&mut self, value: bool) {
self.inner.first = value;
}
#[getter]
fn get_addrs(&self) -> Vec<String> {
self.inner
.addrs
.iter()
.map(|addr| addr.to_string())
.collect()
}
#[setter]
fn set_addrs(&mut self, addrs: Vec<String>) -> PyResult<()> {
self.inner.addrs = addrs
.iter()
.filter_map(|addr| Multiaddr::from_str(addr).ok())
.collect();
Ok(())
}
#[getter]
fn get_network_contacts_url(&self) -> Vec<String> {
self.inner.network_contacts_url.clone()
}
#[setter]
fn set_network_contacts_url(&mut self, urls: Vec<String>) {
self.inner.network_contacts_url = urls;
}
#[getter]
fn get_local(&self) -> bool {
self.inner.local
}
#[setter]
fn set_local(&mut self, value: bool) {
self.inner.local = value;
}
#[getter]
fn get_ignore_cache(&self) -> bool {
self.inner.ignore_cache
}
#[setter]
fn set_ignore_cache(&mut self, value: bool) {
self.inner.ignore_cache = value;
}
#[getter]
fn get_bootstrap_cache_dir(&self) -> Option<PathBuf> {
self.inner.bootstrap_cache_dir.clone()
}
#[setter]
fn set_bootstrap_cache_dir(&mut self, dir: Option<PathBuf>) {
self.inner.bootstrap_cache_dir = dir;
}
#[staticmethod]
fn read_bootstrap_addr_from_env() -> Vec<String> {
Bootstrap::fetch_from_env()
.into_iter()
.map(|addr| addr.to_string())
.collect()
}
fn __str__(&self) -> String {
format!("{:?}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "MainPubkey")]
#[derive(Copy, PartialEq, Eq, Ord, PartialOrd, Clone, Serialize, Deserialize, Hash)]
pub struct PyMainPubkey {
inner: MainPubkey,
}
#[pymethods]
impl PyMainPubkey {
#[new]
fn new(public_key: PyPublicKey) -> Self {
Self {
inner: MainPubkey::new(public_key.inner),
}
}
fn verify(&self, sig: &PySignature, msg: &[u8]) -> bool {
self.inner.verify(&sig.inner, msg)
}
fn derive_key(&self, index: &PyDerivationIndex) -> PyDerivedPubkey {
PyDerivedPubkey {
inner: self.inner.derive_key(&index.inner),
}
}
fn as_bytes(&self) -> [u8; PK_SIZE] {
self.inner.to_bytes()
}
fn as_hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex_str: &str) -> PyResult<Self> {
MainPubkey::from_hex(hex_str)
.map(|inner| Self { inner })
.map_err(|e| PyValueError::new_err(format!("Failed to parse hex: {e}")))
}
fn __str__(&self) -> String {
self.inner.to_hex()
}
fn __repr__(&self) -> String {
format!("MainPubkey('{}')", self.inner.to_hex())
}
}
#[pyclass(name = "MainSecretKey")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PyMainSecretKey {
inner: MainSecretKey,
}
#[pymethods]
impl PyMainSecretKey {
#[new]
fn new(secret_key: PySecretKey) -> Self {
Self {
inner: MainSecretKey::new(secret_key.inner),
}
}
fn public_key(&self) -> PyMainPubkey {
PyMainPubkey {
inner: self.inner.public_key(),
}
}
fn sign(&self, msg: &[u8]) -> PySignature {
PySignature {
inner: self.inner.sign(msg),
}
}
fn derive_key(&self, index: &PyDerivationIndex) -> PyDerivedSecretKey {
PyDerivedSecretKey {
inner: self.inner.derive_key(&index.inner),
}
}
fn to_bytes(&self) -> Vec<u8> {
self.inner.to_bytes()
}
#[staticmethod]
fn random() -> Self {
Self {
inner: MainSecretKey::random(),
}
}
fn random_derived_key(&self) -> PyDerivedSecretKey {
PyDerivedSecretKey {
inner: self.inner.random_derived_key(&mut rand::thread_rng()),
}
}
fn __repr__(&self) -> String {
format!("MainSecretKey(public_key={})", self.public_key().as_hex())
}
}
#[pyclass(name = "ScratchpadAddress", eq, ord)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PyScratchpadAddress {
inner: ScratchpadAddress,
}
#[pymethods]
impl PyScratchpadAddress {
#[new]
fn new(public_key: PyPublicKey) -> PyResult<Self> {
Ok(Self {
inner: ScratchpadAddress::new(public_key.inner),
})
}
pub fn owner(&self) -> PyPublicKey {
PyPublicKey {
inner: *self.inner.owner(),
}
}
#[getter]
fn hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
Ok(Self {
inner: ScratchpadAddress::from_hex(hex)
.map_err(|e| PyValueError::new_err(e.to_string()))?,
})
}
fn __str__(&self) -> PyResult<String> {
Ok(self.hex())
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("ScratchpadAddress('{}')", self.hex()))
}
}
#[pyclass(name = "DataAddress", eq, ord)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PyDataAddress {
inner: DataAddress,
}
#[pymethods]
impl PyDataAddress {
#[new]
fn new(xorname: PyXorName) -> PyResult<Self> {
Ok(Self {
inner: DataAddress::new(xorname.inner),
})
}
#[getter]
fn hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
Ok(Self {
inner: DataAddress::from_hex(hex).map_err(|e| PyValueError::new_err(e.to_string()))?,
})
}
fn __str__(&self) -> PyResult<String> {
Ok(self.hex())
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("DataAddress('{}')", self.hex()))
}
}
#[pyclass(name = "ArchiveAddress", eq, ord)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct PyArchiveAddress {
inner: ArchiveAddress,
}
#[pymethods]
impl PyArchiveAddress {
#[new]
fn new(xorname: PyXorName) -> PyResult<Self> {
Ok(Self {
inner: ArchiveAddress::new(xorname.inner),
})
}
#[getter]
fn hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
Ok(Self {
inner: ArchiveAddress::from_hex(hex)
.map_err(|e| PyValueError::new_err(e.to_string()))?,
})
}
fn __str__(&self) -> PyResult<String> {
Ok(self.hex())
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("ArchiveAddress('{}')", self.hex()))
}
}
#[pyclass(name = "PrivateArchiveDataMap", eq, ord)]
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct PyPrivateArchiveDataMap {
inner: PrivateArchiveDataMap,
}
#[pymethods]
impl PyPrivateArchiveDataMap {
#[getter]
fn hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
Ok(Self {
inner: PrivateArchiveDataMap::from_hex(hex)
.map_err(|e| PyValueError::new_err(e.to_string()))?,
})
}
fn __str__(&self) -> PyResult<String> {
Ok(self.hex())
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("PrivateArchiveDataMap('{}')", self.hex()))
}
}
#[pyclass(name = "Wallet")]
#[derive(Clone)]
pub struct PyWallet {
pub(crate) inner: Wallet,
}
#[pymethods]
impl PyWallet {
#[new]
fn new(private_key: String) -> PyResult<Self> {
let wallet = Wallet::new_from_private_key(
EVMNetwork::ArbitrumOne, &private_key,
)
.map_err(|e| PyValueError::new_err(format!("`private_key` invalid: {e}")))?;
Ok(Self { inner: wallet })
}
#[staticmethod]
fn new_with_random_wallet(network: PyEVMNetwork) -> Self {
Self {
inner: Wallet::new_with_random_wallet(network.inner),
}
}
#[staticmethod]
fn new_from_private_key(network: PyEVMNetwork, private_key: &str) -> PyResult<Self> {
let inner = Wallet::new_from_private_key(network.inner, private_key)
.map_err(|e| PyValueError::new_err(format!("`private_key` invalid: {e}")))?;
Ok(Self { inner })
}
fn address(&self) -> String {
self.inner.address().to_string()
}
fn network(&self) -> PyEVMNetwork {
PyEVMNetwork {
inner: self.inner.network().clone(),
}
}
fn balance<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
match client.balance_of_tokens().await {
Ok(balance) => Ok(balance.to_string()),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to get balance: {e}"
))),
}
})
}
fn balance_of_gas<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
let client = self.inner.clone();
future_into_py(py, async move {
match client.balance_of_gas_tokens().await {
Ok(balance) => Ok(balance.to_string()),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to get balance: {e}"
))),
}
})
}
#[staticmethod]
pub fn random_private_key() -> String {
Wallet::random_private_key()
}
fn set_transaction_config(&mut self, config: PyTransactionConfig) -> PyResult<()> {
self.inner.set_transaction_config(config.into());
Ok(())
}
}
#[pyclass(name = "TransactionConfig")]
#[derive(Clone, Debug)]
pub struct PyTransactionConfig {
pub max_fee_per_gas: PyMaxFeePerGas,
}
#[pymethods]
impl PyTransactionConfig {
#[new]
fn new(max_fee_per_gas: PyMaxFeePerGas) -> Self {
Self { max_fee_per_gas }
}
#[getter]
fn max_fee_per_gas(&self) -> PyMaxFeePerGas {
self.max_fee_per_gas.clone()
}
fn __str__(&self) -> String {
format!(
"{:?}",
std::convert::Into::<TransactionConfig>::into(self.clone())
)
}
}
#[allow(clippy::from_over_into)]
impl Into<TransactionConfig> for PyTransactionConfig {
fn into(self) -> TransactionConfig {
let max_fee_per_gas = match self.max_fee_per_gas {
PyMaxFeePerGas::Auto() => MaxFeePerGas::Auto,
PyMaxFeePerGas::LimitedAuto(limit) => MaxFeePerGas::LimitedAuto(limit),
PyMaxFeePerGas::Unlimited() => MaxFeePerGas::Unlimited,
PyMaxFeePerGas::Custom(limit) => MaxFeePerGas::Custom(limit),
};
TransactionConfig { max_fee_per_gas }
}
}
#[pyclass(name = "MaxFeePerGas")]
#[derive(Clone, Debug)]
pub enum PyMaxFeePerGas {
Auto(),
LimitedAuto(u128),
Unlimited(),
Custom(u128),
}
#[pymethods]
impl PyMaxFeePerGas {
#[staticmethod]
pub fn auto() -> Self {
Self::Auto()
}
#[staticmethod]
pub fn limited_auto(value: u128) -> Self {
Self::LimitedAuto(value)
}
#[staticmethod]
pub fn unlimited() -> Self {
Self::Unlimited()
}
#[staticmethod]
pub fn custom(value: u128) -> Self {
Self::Custom(value)
}
pub fn __str__(&self) -> String {
match self {
Self::Auto() => "Auto".to_string(),
Self::LimitedAuto(val) => format!("LimitedAuto({val})"),
Self::Unlimited() => "Unlimited".to_string(),
Self::Custom(val) => format!("Custom({val})"),
}
}
}
#[pyclass(name = "PaymentMode", eq, eq_int)]
#[derive(Clone, Copy, PartialEq)]
pub enum PyPaymentMode {
Standard = 0,
SingleNode = 1,
}
#[pyclass(name = "PaymentOption")]
#[derive(Clone)]
pub struct PyPaymentOption {
pub(crate) inner: PaymentOption,
}
#[pymethods]
impl PyPaymentOption {
#[staticmethod]
fn wallet(wallet: &PyWallet) -> Self {
Self {
inner: PaymentOption::Wallet(wallet.inner.clone()),
}
}
}
#[pyclass(name = "SecretKey")]
#[derive(Debug, Clone)]
pub struct PySecretKey {
inner: SecretKey,
}
#[pymethods]
impl PySecretKey {
#[new]
fn new() -> PyResult<Self> {
Ok(Self {
inner: SecretKey::random(),
})
}
#[staticmethod]
fn from_hex(hex_str: &str) -> PyResult<Self> {
SecretKey::from_hex(hex_str)
.map(|key| Self { inner: key })
.map_err(|e| PyValueError::new_err(format!("Invalid hex key: {e}")))
}
fn public_key(&self) -> PyPublicKey {
PyPublicKey {
inner: self.inner.public_key(),
}
}
fn hex(&self) -> String {
self.inner.to_hex()
}
}
#[pyclass(name = "Signature")]
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PySignature {
pub(crate) inner: Signature,
}
#[pymethods]
impl PySignature {
fn parity(&self) -> bool {
self.inner.parity()
}
#[staticmethod]
fn from_bytes(bytes: [u8; bls::SIG_SIZE]) -> PyResult<Self> {
Signature::from_bytes(bytes)
.map(|inner| Self { inner })
.map_err(|e| PyValueError::new_err(format!("Invalid signature: {e}")))
}
fn to_bytes(&self) -> [u8; bls::SIG_SIZE] {
self.inner.to_bytes()
}
fn __str__(&self) -> String {
hex::encode(self.inner.to_bytes())
}
fn __repr__(&self) -> String {
format!("Signature('{}')", hex::encode(self.inner.to_bytes()))
}
}
#[pyclass(name = "StoreQuote")]
pub struct PyStoreQuote {
inner: StoreQuote,
}
#[pymethods]
impl PyStoreQuote {
pub fn price(&self) -> String {
self.inner.price().to_string()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn payments(&self) -> Vec<(String, String, String)> {
self.inner
.payments()
.into_iter()
.map(|(hash, rewards_address, price)| {
(
format!("0x{}", hex::encode(hash.0)),
format!("0x{}", hex::encode(rewards_address.0)),
price.to_string(),
)
})
.collect()
}
}
#[pyclass(name = "Backoff")]
#[derive(Debug, Clone)]
pub struct PyBackoff {
inner: Backoff,
}
#[pymethods]
impl PyBackoff {
fn __str__(&self) -> String {
format!("{:?}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "RetryStrategy")]
#[derive(Clone, Debug, Copy, Default)]
pub struct PyRetryStrategy {
inner: RetryStrategy,
}
#[pymethods]
impl PyRetryStrategy {
#[staticmethod]
fn none() -> Self {
Self {
inner: RetryStrategy::None,
}
}
#[staticmethod]
fn quick() -> Self {
Self {
inner: RetryStrategy::Quick,
}
}
#[staticmethod]
fn balanced() -> Self {
Self {
inner: RetryStrategy::Balanced,
}
}
#[staticmethod]
fn persistent() -> Self {
Self {
inner: RetryStrategy::Persistent,
}
}
#[staticmethod]
fn default() -> Self {
Self {
inner: RetryStrategy::default(),
}
}
fn attempts(&self) -> usize {
self.inner.attempts()
}
fn backoff(&self) -> PyBackoff {
PyBackoff {
inner: self.inner.backoff(),
}
}
fn __str__(&self) -> String {
format!("{:?}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "Quorum")]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PyQuorum {
inner: Quorum,
}
#[pymethods]
impl PyQuorum {
fn __str__(&self) -> String {
match self.inner {
Quorum::One => "Quorum::One".to_string(),
Quorum::Majority => "Quorum::Majority".to_string(),
Quorum::All => "Quorum::All".to_string(),
Quorum::N(n) => format!("Quorum::N({n})"),
}
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "Strategy")]
#[derive(Debug, Clone)]
pub struct PyStrategy {
inner: Strategy,
}
#[pymethods]
impl PyStrategy {
#[getter]
fn get_put_quorum(&self) -> PyQuorum {
PyQuorum {
inner: self.inner.put_quorum,
}
}
#[getter]
fn get_put_retry(&self) -> PyRetryStrategy {
PyRetryStrategy {
inner: self.inner.put_retry,
}
}
#[getter]
fn get_verification_quorum(&self) -> PyQuorum {
PyQuorum {
inner: self.inner.verification_quorum,
}
}
#[getter]
fn get_get_quorum(&self) -> PyQuorum {
PyQuorum {
inner: self.inner.get_quorum,
}
}
#[getter]
fn get_get_retry(&self) -> PyRetryStrategy {
PyRetryStrategy {
inner: self.inner.get_retry,
}
}
fn __str__(&self) -> String {
format!("{:?}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "UploadSummary")]
#[derive(Debug, Clone)]
pub struct PyUploadSummary {
inner: UploadSummary,
}
#[pymethods]
impl PyUploadSummary {
#[getter]
fn records_paid(&self) -> usize {
self.inner.records_paid
}
#[getter]
fn records_already_paid(&self) -> usize {
self.inner.records_already_paid
}
#[getter]
fn tokens_spent(&self) -> String {
self.inner.tokens_spent.to_string()
}
fn __str__(&self) -> PyResult<String> {
Ok(format!(
"UploadSummary {{ records_paid: {}, records_already_paid: {}, tokens_spent: {} }}",
self.inner.records_paid, self.inner.records_already_paid, self.inner.tokens_spent
))
}
}
#[pyclass(name = "PublicKey")]
#[derive(Debug, Clone)]
pub struct PyPublicKey {
inner: PublicKey,
}
#[pymethods]
impl PyPublicKey {
#[staticmethod]
fn random() -> PyResult<Self> {
let secret = SecretKey::random();
Ok(Self {
inner: secret.public_key(),
})
}
#[staticmethod]
fn from_hex(hex_str: &str) -> PyResult<Self> {
PublicKey::from_hex(hex_str)
.map(|key| Self { inner: key })
.map_err(|e| PyValueError::new_err(format!("Invalid hex key: {e}")))
}
fn hex(&self) -> String {
self.inner.to_hex()
}
}
#[pyclass(name = "QuoteForAddress")]
pub struct PyQuoteForAddress {
inner: QuoteForAddress,
}
#[pymethods]
impl PyQuoteForAddress {
pub fn price(&self) -> String {
self.inner.price().to_string()
}
}
#[pyclass(name = "QuotingMetrics")]
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct PyQuotingMetrics {
inner: QuotingMetrics,
}
#[pymethods]
impl PyQuotingMetrics {
#[new]
#[allow(clippy::too_many_arguments)]
#[pyo3(signature = (data_type, data_size, close_records_stored, records_per_type, max_records, received_payment_count, live_time, network_density=None, network_size=None))]
fn new(
data_type: u32,
data_size: usize,
close_records_stored: usize,
records_per_type: Vec<(u32, u32)>,
max_records: usize,
received_payment_count: usize,
live_time: u64,
network_density: Option<Vec<u8>>,
network_size: Option<u64>,
) -> PyResult<Self> {
let network_density = if let Some(density) = network_density {
if density.len() != 32 {
return Err(PyValueError::new_err(
"network_density must be 32 bytes if provided",
));
}
let mut array = [0u8; 32];
array.copy_from_slice(&density);
Some(array)
} else {
None
};
Ok(Self {
inner: QuotingMetrics {
data_type,
data_size,
close_records_stored,
records_per_type,
max_records,
received_payment_count,
live_time,
network_density,
network_size,
},
})
}
#[getter]
fn data_type(&self) -> u32 {
self.inner.data_type
}
#[setter]
fn set_data_type(&mut self, value: u32) {
self.inner.data_type = value;
}
#[getter]
fn data_size(&self) -> usize {
self.inner.data_size
}
#[setter]
fn set_data_size(&mut self, value: usize) {
self.inner.data_size = value;
}
#[getter]
fn close_records_stored(&self) -> usize {
self.inner.close_records_stored
}
#[setter]
fn set_close_records_stored(&mut self, value: usize) {
self.inner.close_records_stored = value;
}
#[getter]
fn records_per_type(&self) -> Vec<(u32, u32)> {
self.inner.records_per_type.clone()
}
#[setter]
fn set_records_per_type(&mut self, value: Vec<(u32, u32)>) {
self.inner.records_per_type = value;
}
#[getter]
fn max_records(&self) -> usize {
self.inner.max_records
}
#[setter]
fn set_max_records(&mut self, value: usize) {
self.inner.max_records = value;
}
#[getter]
fn received_payment_count(&self) -> usize {
self.inner.received_payment_count
}
#[setter]
fn set_received_payment_count(&mut self, value: usize) {
self.inner.received_payment_count = value;
}
#[getter]
fn live_time(&self) -> u64 {
self.inner.live_time
}
#[setter]
fn set_live_time(&mut self, value: u64) {
self.inner.live_time = value;
}
#[getter]
fn network_density(&self) -> Option<Vec<u8>> {
self.inner.network_density.map(|array| array.to_vec())
}
#[setter]
fn set_network_density(&mut self, value: Option<Vec<u8>>) -> PyResult<()> {
self.inner.network_density = if let Some(density) = value {
if density.len() != 32 {
return Err(PyValueError::new_err(
"network_density must be 32 bytes if provided",
));
}
let mut array = [0u8; 32];
array.copy_from_slice(&density);
Some(array)
} else {
None
};
Ok(())
}
#[getter]
fn network_size(&self) -> Option<u64> {
self.inner.network_size
}
#[setter]
fn set_network_size(&mut self, value: Option<u64>) {
self.inner.network_size = value;
}
fn __str__(&self) -> String {
format!("{:?}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "PaymentQuote")]
#[derive(Clone)]
pub struct PyPaymentQuote {
inner: PaymentQuote,
}
#[pymethods]
impl PyPaymentQuote {
#[new]
#[pyo3(signature = (content, timestamp, quoting_metrics, rewards_address, pub_key, signature))]
fn new(
content: PyXorName,
timestamp: u64, quoting_metrics: PyQuotingMetrics,
rewards_address: String, pub_key: Vec<u8>,
signature: Vec<u8>,
) -> PyResult<Self> {
let timestamp = SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp);
let rewards_address = RewardsAddress::from_slice(
&hex::decode(rewards_address.trim_start_matches("0x"))
.map_err(|e| PyValueError::new_err(format!("Invalid rewards address: {e}")))?,
);
Ok(Self {
inner: PaymentQuote {
content: content.inner,
timestamp,
quoting_metrics: quoting_metrics.inner,
rewards_address,
pub_key,
signature,
},
})
}
fn hash(&self) -> String {
format!("0x{}", hex::encode(self.inner.hash().0))
}
#[staticmethod]
fn bytes_for_signing(
xorname: PyXorName,
timestamp: u64,
quoting_metrics: PyQuotingMetrics,
rewards_address: String,
) -> PyResult<Vec<u8>> {
let timestamp = SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp);
let rewards_address = RewardsAddress::from_slice(
&hex::decode(rewards_address.trim_start_matches("0x"))
.map_err(|e| PyValueError::new_err(format!("Invalid rewards address: {e}")))?,
);
Ok(PaymentQuote::bytes_for_signing(
xorname.inner,
timestamp,
"ing_metrics.inner,
&rewards_address,
))
}
fn bytes_for_sig(&self) -> Vec<u8> {
self.inner.bytes_for_sig()
}
fn peer_id(&self) -> PyResult<String> {
match self.inner.peer_id() {
Ok(peer_id) => Ok(peer_id.to_string()),
Err(e) => Err(PyRuntimeError::new_err(format!(
"Failed to get peer id: {e}"
))),
}
}
fn check_is_signed_by_claimed_peer(&self, claimed_peer: String) -> PyResult<bool> {
let peer_id = match PeerId::from_str(&claimed_peer) {
Ok(id) => id,
Err(e) => return Err(PyValueError::new_err(format!("Invalid peer ID: {e}"))),
};
Ok(self.inner.check_is_signed_by_claimed_peer(peer_id))
}
fn is_newer_than(&self, other: &PyPaymentQuote) -> bool {
self.inner.is_newer_than(&other.inner)
}
fn historical_verify(&self, other: &PyPaymentQuote) -> bool {
self.inner.historical_verify(&other.inner)
}
#[getter]
fn content(&self) -> PyXorName {
PyXorName {
inner: self.inner.content,
}
}
#[getter]
fn timestamp(&self) -> PyResult<u64> {
self.inner
.timestamp
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_secs())
.map_err(|e| PyRuntimeError::new_err(format!("Failed to get timestamp: {e}")))
}
#[getter]
fn quoting_metrics(&self) -> PyQuotingMetrics {
PyQuotingMetrics {
inner: self.inner.quoting_metrics.clone(),
}
}
#[getter]
fn rewards_address(&self) -> String {
format!("0x{}", hex::encode(self.inner.rewards_address.as_slice()))
}
#[getter]
fn pub_key(&self) -> Vec<u8> {
self.inner.pub_key.clone()
}
#[getter]
fn signature(&self) -> Vec<u8> {
self.inner.signature.clone()
}
fn __str__(&self) -> String {
format!(
"PaymentQuote(content={}, timestamp={})",
self.inner.content,
self.inner
.timestamp
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
#[pyclass(name = "Receipt")]
#[derive(Clone)]
pub struct PyReceipt {
pub(crate) inner: Receipt,
}
#[pymethods]
impl PyReceipt {
#[new]
fn new() -> Self {
Self {
inner: Receipt::new(),
}
}
fn len(&self) -> usize {
self.inner.len()
}
fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
#[pyclass(name = "RegisterAddress")]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, Debug)]
pub struct PyRegisterAddress {
inner: RegisterAddress,
}
#[pymethods]
impl PyRegisterAddress {
#[new]
fn new(owner: PyPublicKey) -> Self {
Self {
inner: RegisterAddress::new(owner.inner),
}
}
fn owner(&self) -> PyPublicKey {
PyPublicKey {
inner: self.inner.owner(),
}
}
fn as_underlying_graph_root(&self) -> PyGraphEntryAddress {
PyGraphEntryAddress {
inner: self.inner.to_underlying_graph_root(),
}
}
fn as_underlying_head_pointer(&self) -> PyPointerAddress {
PyPointerAddress {
inner: self.inner.to_underlying_head_pointer(),
}
}
fn as_hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
RegisterAddress::from_hex(hex)
.map(|addr| Self { inner: addr })
.map_err(|e| PyValueError::new_err(format!("Failed to parse hex: {e}")))
}
fn __str__(&self) -> String {
self.inner.to_string()
}
fn __repr__(&self) -> String {
format!("RegisterAddress('{}')", self.inner.to_hex())
}
}
#[pyclass(name = "VaultSecretKey")]
#[derive(Debug, Clone)]
pub struct PyVaultSecretKey {
inner: VaultSecretKey,
}
#[pymethods]
impl PyVaultSecretKey {
#[new]
fn new() -> PyResult<Self> {
Ok(Self {
inner: VaultSecretKey::random(),
})
}
#[staticmethod]
fn from_hex(hex_str: &str) -> PyResult<Self> {
VaultSecretKey::from_hex(hex_str)
.map(|key| Self { inner: key })
.map_err(|e| PyValueError::new_err(format!("Invalid hex key: {e}")))
}
fn hex(&self) -> String {
self.inner.to_hex()
}
}
#[pyclass(name = "UserData")]
#[derive(Debug, Clone)]
pub struct PyUserData {
inner: UserData,
}
#[pymethods]
impl PyUserData {
#[new]
fn new() -> Self {
Self {
inner: UserData::new(),
}
}
fn file_archives(&self) -> Vec<(String, String)> {
self.inner
.file_archives
.iter()
.map(|(addr, name)| (addr.to_hex(), name.clone()))
.collect()
}
fn private_file_archives(&self) -> Vec<(String, String)> {
self.inner
.private_file_archives
.iter()
.map(|(addr, name)| (addr.to_hex(), name.clone()))
.collect()
}
}
#[pyclass(name = "DataMapChunk")]
#[derive(Debug, Clone)]
pub struct PyDataMapChunk {
inner: DataMapChunk,
}
#[pymethods]
impl PyDataMapChunk {
#[staticmethod]
fn from_hex(hex: &str) -> PyResult<Self> {
DataMapChunk::from_hex(hex)
.map(|access| Self { inner: access })
.map_err(|e| PyValueError::new_err(format!("Invalid hex: {e}")))
}
fn hex(&self) -> String {
self.inner.to_hex()
}
fn address(&self) -> String {
self.inner.address().to_string()
}
}
#[pyclass(name = "DataStream")]
pub struct PyDataStream {
inner: Mutex<crate::client::data::DataStream>,
}
impl PyDataStream {
fn new(stream: crate::client::data::DataStream) -> Self {
Self {
inner: Mutex::new(stream),
}
}
}
#[pymethods]
impl PyDataStream {
fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}
fn __next__(&mut self) -> PyResult<Option<Vec<u8>>> {
let mut stream = self
.inner
.lock()
.map_err(|e| PyRuntimeError::new_err(format!("Lock error: {e}")))?;
match stream.next() {
Some(Ok(chunk)) => Ok(Some(chunk.to_vec())),
Some(Err(e)) => Err(PyRuntimeError::new_err(format!("Stream error: {e}"))),
None => Ok(None),
}
}
fn data_size(&self) -> PyResult<usize> {
let stream = self
.inner
.lock()
.map_err(|e| PyRuntimeError::new_err(format!("Lock error: {e}")))?;
Ok(stream.data_size())
}
fn get_range(&self, start: usize, len: usize) -> PyResult<Vec<u8>> {
let stream = self
.inner
.lock()
.map_err(|e| PyRuntimeError::new_err(format!("Lock error: {e}")))?;
let bytes = stream
.get_range(start, len)
.map_err(|e| PyRuntimeError::new_err(format!("Range access error: {e}")))?;
Ok(bytes.to_vec())
}
fn range(&self, start: usize, end: usize) -> PyResult<Vec<u8>> {
let stream = self
.inner
.lock()
.map_err(|e| PyRuntimeError::new_err(format!("Lock error: {e}")))?;
let bytes = stream
.range(start..end)
.map_err(|e| PyRuntimeError::new_err(format!("Range access error: {e}")))?;
Ok(bytes.to_vec())
}
fn range_from(&self, start: usize) -> PyResult<Vec<u8>> {
let stream = self
.inner
.lock()
.map_err(|e| PyRuntimeError::new_err(format!("Lock error: {e}")))?;
let bytes = stream
.range_from(start)
.map_err(|e| PyRuntimeError::new_err(format!("Range access error: {e}")))?;
Ok(bytes.to_vec())
}
fn range_to(&self, end: usize) -> PyResult<Vec<u8>> {
let stream = self
.inner
.lock()
.map_err(|e| PyRuntimeError::new_err(format!("Lock error: {e}")))?;
let bytes = stream
.range_to(end)
.map_err(|e| PyRuntimeError::new_err(format!("Range access error: {e}")))?;
Ok(bytes.to_vec())
}
fn range_all(&self) -> PyResult<Vec<u8>> {
let stream = self
.inner
.lock()
.map_err(|e| PyRuntimeError::new_err(format!("Lock error: {e}")))?;
let bytes = stream
.range_full()
.map_err(|e| PyRuntimeError::new_err(format!("Range access error: {e}")))?;
Ok(bytes.to_vec())
}
fn range_inclusive(&self, start: usize, end: usize) -> PyResult<Vec<u8>> {
let stream = self
.inner
.lock()
.map_err(|e| PyRuntimeError::new_err(format!("Lock error: {e}")))?;
let bytes = stream
.range_inclusive(start, end)
.map_err(|e| PyRuntimeError::new_err(format!("Range access error: {e}")))?;
Ok(bytes.to_vec())
}
}
#[pyfunction]
fn encrypt(data: Vec<u8>) -> PyResult<(Vec<u8>, Vec<Vec<u8>>)> {
let (data_map, chunks) = self_encryption::encrypt(Bytes::from(data))
.map_err(|e| PyRuntimeError::new_err(format!("Encryption failed: {e}")))?;
let data_map_bytes = rmp_serde::to_vec(&data_map)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to serialize datamap: {e}")))?;
let chunks_bytes: Vec<Vec<u8>> = chunks
.into_iter()
.map(|chunk| chunk.content.to_vec())
.collect();
Ok((data_map_bytes, chunks_bytes))
}
#[pyclass(name = "DerivationIndex")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
pub struct PyDerivationIndex {
inner: DerivationIndex,
}
#[pymethods]
impl PyDerivationIndex {
#[staticmethod]
fn random() -> Self {
let mut rng = rand::thread_rng();
Self {
inner: DerivationIndex::random(&mut rng),
}
}
fn as_bytes(&self) -> [u8; 32] {
*self.inner.as_bytes()
}
fn bytes_owned(&self) -> [u8; 32] {
self.inner.into_bytes()
}
#[staticmethod]
fn from_bytes(bytes: [u8; 32]) -> Self {
Self {
inner: DerivationIndex::from_bytes(bytes),
}
}
fn __str__(&self) -> String {
format!(
"{:02x}{:02x}{:02x}..",
self.inner.as_bytes()[0],
self.inner.as_bytes()[1],
self.inner.as_bytes()[2]
)
}
fn __repr__(&self) -> String {
format!(
"DerivationIndex({:02x}{:02x}{:02x}..)",
self.inner.as_bytes()[0],
self.inner.as_bytes()[1],
self.inner.as_bytes()[2]
)
}
}
#[pyclass(name = "DerivedPubkey")]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PyDerivedPubkey {
inner: DerivedPubkey,
}
#[pymethods]
impl PyDerivedPubkey {
#[new]
fn new(public_key: PyPublicKey) -> Self {
Self {
inner: DerivedPubkey::new(public_key.inner),
}
}
fn as_bytes(&self) -> [u8; bls::PK_SIZE] {
self.inner.to_bytes()
}
fn verify(&self, sig: &PySignature, msg: &[u8]) -> bool {
self.inner.verify(&sig.inner, msg)
}
fn as_hex(&self) -> String {
self.inner.to_hex()
}
#[staticmethod]
fn from_hex(hex_str: &str) -> PyResult<Self> {
DerivedPubkey::from_hex(hex_str)
.map(|inner| Self { inner })
.map_err(|e| PyValueError::new_err(format!("Failed to parse hex: {e}")))
}
fn __str__(&self) -> String {
self.inner.to_hex()
}
fn __repr__(&self) -> String {
format!("DerivedPubkey('{}')", self.inner.to_hex())
}
}
#[pyclass(name = "DerivedSecretKey")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PyDerivedSecretKey {
inner: DerivedSecretKey,
}
#[pymethods]
impl PyDerivedSecretKey {
#[new]
fn new(secret_key: PySecretKey) -> Self {
Self {
inner: DerivedSecretKey::new(secret_key.inner),
}
}
fn public_key(&self) -> PyDerivedPubkey {
PyDerivedPubkey {
inner: self.inner.public_key(),
}
}
fn sign(&self, msg: &[u8]) -> PySignature {
PySignature {
inner: self.inner.sign(msg),
}
}
fn __repr__(&self) -> String {
format!(
"DerivedSecretKey(public_key={})",
self.public_key().as_hex()
)
}
}
#[pyclass(name = "EVMNetwork", eq)]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct PyEVMNetwork {
inner: EVMNetwork,
}
#[pymethods]
impl PyEVMNetwork {
#[new]
fn new(local: bool) -> PyResult<Self> {
let inner =
EVMNetwork::new(local).map_err(|e| PyRuntimeError::new_err(format!("{e:?}")))?;
Ok(Self { inner })
}
#[staticmethod]
fn new_custom(rpc_url: &str, payment_token_addr: &str, data_payments_addr: &str) -> Self {
Self {
inner: EVMNetwork::new_custom(rpc_url, payment_token_addr, data_payments_addr, None),
}
}
fn identifier(&self) -> &str {
self.inner.identifier()
}
fn rpc_url(&self) -> String {
self.inner.rpc_url().as_str().to_string()
}
fn payment_token_address(&self) -> String {
self.inner.payment_token_address().to_string()
}
fn data_payments_address(&self) -> String {
self.inner.data_payments_address().to_string()
}
}
#[pyclass(name = "Metadata")]
#[derive(Debug, Clone)]
pub struct PyMetadata {
inner: Metadata,
}
#[pymethods]
impl PyMetadata {
#[new]
fn new(size: u64) -> Self {
Self {
inner: Metadata::new_with_size(size),
}
}
#[getter]
fn get_created(&self) -> u64 {
self.inner.created
}
#[setter]
fn set_created(&mut self, value: u64) {
self.inner.created = value;
}
#[getter]
fn get_modified(&self) -> u64 {
self.inner.modified
}
#[setter]
fn set_modified(&mut self, value: u64) {
self.inner.modified = value;
}
#[getter]
fn get_size(&self) -> u64 {
self.inner.size
}
#[setter]
fn set_size(&mut self, value: u64) {
self.inner.size = value;
}
}
#[pyclass(name = "PublicArchive")]
#[derive(Debug, Clone)]
pub struct PyPublicArchive {
inner: PublicArchive,
}
#[pymethods]
impl PyPublicArchive {
#[new]
fn new() -> Self {
Self {
inner: PublicArchive::new(),
}
}
fn rename_file(&mut self, old_path: PathBuf, new_path: PathBuf) -> PyResult<()> {
self.inner
.rename_file(&old_path, &new_path)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to rename file: {e}")))
}
fn add_file(&mut self, path: PathBuf, addr: &PyDataAddress, metadata: &PyMetadata) {
self.inner
.add_file(path, addr.inner, metadata.inner.clone());
}
fn files(&self) -> Vec<(PathBuf, PyMetadata)> {
self.inner
.files()
.into_iter()
.map(|(path, meta)| (path, PyMetadata { inner: meta }))
.collect()
}
fn addresses(&self) -> Vec<String> {
self.inner
.addresses()
.into_iter()
.map(|a| a.to_hex())
.collect()
}
}
#[pyclass(name = "PrivateArchive")]
#[derive(Debug, Clone)]
pub struct PyPrivateArchive {
inner: PrivateArchive,
}
#[pymethods]
impl PyPrivateArchive {
#[new]
fn new() -> Self {
Self {
inner: PrivateArchive::new(),
}
}
fn rename_file(&mut self, old_path: PathBuf, new_path: PathBuf) -> PyResult<()> {
self.inner
.rename_file(&old_path, &new_path)
.map_err(|e| PyRuntimeError::new_err(format!("Failed to rename file: {e}")))
}
fn add_file(&mut self, path: PathBuf, data_map: &PyDataMapChunk, metadata: &PyMetadata) {
self.inner
.add_file(path, data_map.inner.clone(), metadata.inner.clone());
}
fn files(&self) -> Vec<(PathBuf, PyMetadata)> {
self.inner
.files()
.into_iter()
.map(|(path, meta)| (path, PyMetadata { inner: meta }))
.collect()
}
fn data_maps(&self) -> Vec<PyDataMapChunk> {
self.inner
.data_maps()
.into_iter()
.map(|data_map| PyDataMapChunk { inner: data_map })
.collect()
}
}
#[pyclass(name = "GraphEntry", eq, ord)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct PyGraphEntry {
inner: GraphEntry,
}
#[pymethods]
impl PyGraphEntry {
#[new]
fn new(
owner: PySecretKey,
parents: Vec<PyPublicKey>,
content: [u8; 32],
descendants: Vec<(PyPublicKey, [u8; 32])>,
) -> PyResult<Self> {
Ok(Self {
inner: GraphEntry::new(
&owner.inner,
parents.into_iter().map(|p| p.inner).collect(),
content,
descendants.into_iter().map(|p| (p.0.inner, p.1)).collect(),
),
})
}
pub fn address(&self) -> PyGraphEntryAddress {
PyGraphEntryAddress {
inner: self.inner.address(),
}
}
pub fn content(&self) -> [u8; 32] {
self.inner.content
}
pub fn parents(&self) -> Vec<PyPublicKey> {
self.inner
.parents
.iter()
.map(|&p| PyPublicKey { inner: p })
.collect()
}
pub fn descendants(&self) -> Vec<(PyPublicKey, [u8; 32])> {
self.inner
.descendants
.iter()
.map(|&(pk, c)| (PyPublicKey { inner: pk }, c))
.collect()
}
}
#[pyclass(name = "Scratchpad", eq, ord)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct PyScratchpad {
inner: Scratchpad,
}
#[pymethods]
impl PyScratchpad {
#[new]
fn new(
owner: PySecretKey,
data_encoding: u64,
unencrypted_data: Vec<u8>,
counter: u64,
) -> PyResult<Self> {
Ok(Self {
inner: Scratchpad::new(
&owner.inner,
data_encoding,
&Bytes::from(unencrypted_data),
counter,
),
})
}
pub fn address(&self) -> PyScratchpadAddress {
PyScratchpadAddress {
inner: *self.inner.address(),
}
}
pub fn data_encoding(&self) -> u64 {
self.inner.data_encoding()
}
pub fn counter(&self) -> u64 {
self.inner.counter()
}
pub fn decrypt_data(&self, sk: PySecretKey) -> PyResult<Vec<u8>> {
let data = self
.inner
.decrypt_data(&sk.inner)
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))?;
Ok(data.to_vec())
}
pub fn owner(&self) -> PyPublicKey {
PyPublicKey {
inner: *self.inner.owner(),
}
}
pub fn signature(&self) -> PySignature {
PySignature {
inner: self.inner.signature().clone(),
}
}
pub fn scratchpad_hash(&self) -> String {
hex::encode(self.inner.scratchpad_hash().0)
}
pub fn encrypted_data_hash(&self) -> String {
hex::encode(self.inner.encrypted_data_hash())
}
pub fn encrypted_data(&self) -> Vec<u8> {
self.inner.encrypted_data().to_vec()
}
}
#[pyclass(name = "RegisterHistory")]
#[derive(Clone)]
pub struct PyRegisterHistory {
inner: Arc<futures::lock::Mutex<RegisterHistory>>,
}
impl PyRegisterHistory {
fn new(history: RegisterHistory) -> Self {
Self {
inner: Arc::new(futures::lock::Mutex::new(history)),
}
}
}
#[pymethods]
impl PyRegisterHistory {
fn next<'a>(&'a mut self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
let arc = Arc::clone(&self.inner);
future_into_py(py, async move {
let mut register_history = arc.lock().await;
let value = register_history
.next()
.await
.map_err(|e| PyRuntimeError::new_err(format!("history `next` failed: {e}")))?;
Ok(value)
})
}
fn collect<'a>(&'a mut self, py: Python<'a>) -> PyResult<Bound<'a, PyAny>> {
let arc = Arc::clone(&self.inner);
future_into_py(py, async move {
let mut register_history = arc.lock().await;
let values = register_history
.collect()
.await
.map_err(|e| PyRuntimeError::new_err(format!("history `collect` failed: {e}")))?;
Ok(values)
})
}
}
#[pyclass(name = "ClientConfig")]
#[derive(Debug, Clone)]
pub struct PyClientConfig {
inner: ClientConfig,
}
#[pymethods]
impl PyClientConfig {
#[new]
fn new() -> Self {
Self {
inner: ClientConfig::default(),
}
}
#[getter]
fn get_local(&self) -> bool {
self.inner.bootstrap_config.local
}
#[setter]
fn set_local(&mut self, value: bool) {
self.inner.bootstrap_config.local = value;
}
#[getter]
fn get_peers(&self) -> Vec<String> {
self.inner
.bootstrap_config
.initial_peers
.iter()
.map(|p| p.to_string())
.collect()
}
#[setter]
fn set_peers(&mut self, peers: Vec<String>) -> PyResult<()> {
if peers.is_empty() {
return Ok(());
}
let peers: Vec<Multiaddr> = peers
.iter()
.map(|p| Multiaddr::from_str(p))
.collect::<Result<_, _>>()
.map_err(|e| PyValueError::new_err(format!("Failed to parse peers: {e}")))?;
self.inner.bootstrap_config.initial_peers = peers;
Ok(())
}
#[getter]
fn get_network(&self) -> PyEVMNetwork {
PyEVMNetwork {
inner: self.inner.evm_network.clone(),
}
}
#[setter]
fn set_network(&mut self, network: PyEVMNetwork) {
self.inner.evm_network = network.inner;
}
}
#[pyclass(name = "XorName")]
#[derive(Eq, Copy, Clone, Default, Hash, Ord, PartialEq, PartialOrd)]
pub struct PyXorName {
pub(crate) inner: XorName,
}
#[pymethods]
impl PyXorName {
#[staticmethod]
fn from_content(content: &[u8]) -> Self {
Self {
inner: XorName::from_content(content),
}
}
#[staticmethod]
fn from_content_parts(content_parts: Vec<Vec<u8>>) -> Self {
let refs: Vec<&[u8]> = content_parts.iter().map(|v| v.as_slice()).collect();
Self {
inner: XorName::from_content_parts(&refs),
}
}
#[staticmethod]
fn random() -> Self {
Self {
inner: XorName::random(&mut rand::thread_rng()),
}
}
fn bit(&self, i: u8) -> bool {
self.inner.bit(i)
}
fn cmp_distance(&self, lhs: &PyXorName, rhs: &PyXorName) -> i32 {
match self.inner.cmp_distance(&lhs.inner, &rhs.inner) {
std::cmp::Ordering::Less => -1,
std::cmp::Ordering::Equal => 0,
std::cmp::Ordering::Greater => 1,
}
}
fn with_bit(&self, i: u8, bit: bool) -> Self {
Self {
inner: self.inner.with_bit(i, bit),
}
}
fn as_hex(&self) -> String {
hex::encode(self.inner.0)
}
#[staticmethod]
fn from_hex(hex_str: &str) -> PyResult<Self> {
if hex_str.len() != XOR_NAME_LEN * 2 {
return Err(PyValueError::new_err(format!(
"Hex string must be exactly {} characters",
XOR_NAME_LEN * 2
)));
}
let bytes = hex::decode(hex_str)
.map_err(|e| PyValueError::new_err(format!("Invalid hex string: {e}")))?;
let mut array = [0u8; XOR_NAME_LEN];
array.copy_from_slice(&bytes);
Ok(Self {
inner: XorName(array),
})
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
format!("XorName({})", hex::encode(&self.inner.0[..3]))
}
fn __richcmp__(&self, other: &PyXorName, op: CompareOp) -> bool {
match op {
CompareOp::Lt => self.inner < other.inner,
CompareOp::Le => self.inner <= other.inner,
CompareOp::Eq => self.inner == other.inner,
CompareOp::Ne => self.inner != other.inner,
CompareOp::Gt => self.inner > other.inner,
CompareOp::Ge => self.inner >= other.inner,
}
}
fn __hash__(&self) -> isize {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
std::hash::Hash::hash(&self.inner, &mut hasher);
std::hash::Hasher::finish(&hasher) as isize
}
}
#[pyfunction]
fn random_xor() -> PyXorName {
PyXorName {
inner: XorName::random(&mut rand::thread_rng()),
}
}
#[pymodule]
#[pyo3(name = "autonomi_client")]
fn autonomi_client_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<PyArchiveAddress>()?;
m.add_class::<PyAttoTokens>()?;
m.add_class::<PyBackoff>()?;
m.add_class::<PyBootstrapCacheConfig>()?;
m.add_class::<PyChunk>()?;
m.add_class::<PyChunkAddress>()?;
m.add_class::<PyClient>()?;
m.add_class::<PyClientConfig>()?;
m.add_class::<PyClientEvent>()?;
m.add_class::<PyClientEventReceiver>()?;
m.add_class::<PyClientOperatingStrategy>()?;
m.add_class::<PyDataAddress>()?;
m.add_class::<PyDataMapChunk>()?;
m.add_class::<PyDataStream>()?;
m.add_class::<PyDataTypes>()?;
m.add_class::<PyDerivationIndex>()?;
m.add_class::<PyDerivedPubkey>()?;
m.add_class::<PyDerivedSecretKey>()?;
m.add_class::<PyEVMNetwork>()?;
m.add_class::<PyGraphEntry>()?;
m.add_class::<PyGraphEntryAddress>()?;
m.add_class::<PyInitialPeersConfig>()?;
m.add_class::<PyMainPubkey>()?;
m.add_class::<PyMainSecretKey>()?;
m.add_class::<PyMaxFeePerGas>()?;
m.add_class::<PyMetadata>()?;
m.add_class::<PyPaymentMode>()?;
m.add_class::<PyPaymentOption>()?;
m.add_class::<PyPaymentQuote>()?;
m.add_class::<PyPointer>()?;
m.add_class::<PyPointerAddress>()?;
m.add_class::<PyPointerTarget>()?;
m.add_class::<PyPrivateArchive>()?;
m.add_class::<PyPrivateArchiveDataMap>()?;
m.add_class::<PyPublicArchive>()?;
m.add_class::<PyPublicKey>()?;
m.add_class::<PyQuorum>()?;
m.add_class::<PyQuoteForAddress>()?;
m.add_class::<PyQuotingMetrics>()?;
m.add_class::<PyReceipt>()?;
m.add_class::<PyRegisterAddress>()?;
m.add_class::<PyRegisterHistory>()?;
m.add_class::<PyRetryStrategy>()?;
m.add_class::<PyScratchpad>()?;
m.add_class::<PyScratchpadAddress>()?;
m.add_class::<PySecretKey>()?;
m.add_class::<PySignature>()?;
m.add_class::<PyStoreQuote>()?;
m.add_class::<PyStrategy>()?;
m.add_class::<PyTransactionConfig>()?;
m.add_class::<PyUploadSummary>()?;
m.add_class::<PyUserData>()?;
m.add_class::<PyVaultSecretKey>()?;
m.add_class::<PyWallet>()?;
m.add_class::<PyXorName>()?;
m.add_function(wrap_pyfunction!(encrypt, m)?)?;
m.add_function(wrap_pyfunction!(random_xor, m)?)?;
Ok(())
}