pub use crate::error::{Error, Result};
pub use alloy_primitives::Address as EvmAddress;
pub use bls::SecretKey;
pub use evmlib::common::U256;
pub use libp2p::Multiaddr;
pub use xor_name::XorName;
use crate::logging::{logging, LoggingHandle};
use alloy_primitives::Bytes as EvmBytes;
use autonomi::{
Client,
get_evm_network,
Wallet,
ClientConfig,
InitialPeersConfig,
GraphEntry,
pointer::PointerTarget,
PointerAddress,
GraphEntryAddress,
graph::GraphError,
Network as EvmNetwork,
client::{payment::PaymentOption, data::DataAddress},
};
use bytes::Bytes;
use std::str::FromStr;
use serde::{Serialize, Deserialize};
pub const ROOT_SK: &str = "160922b4d2b35fec6b7a36a54c9793bea0fdef00c2630b4361e7a92546f05993";
pub struct Safe {
evm_network: EvmNetwork,
client: Client,
wallet: Option<Wallet>,
sk: Option<SecretKey>,
log_handle: Option<LoggingHandle>,
}
impl std::fmt::Debug for Safe {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let walllet_debug: String = self.wallet.clone().map(
|w| format!("Some(Wallet {{ address: {:?} }})", w.address())
).unwrap_or("None".to_string());
f.debug_struct("Safe")
.field("evm_network", &format!("{:?}", self.evm_network))
.field("client", &"Client { ... }")
.field("wallet", &walllet_debug)
.field("sk", &format!("{:?}", self.sk))
.field("log_handle", match &self.log_handle { Some(_) => &"Some(LoggingHandle { ... })", None => &"None" })
.finish()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Network {
Alpha,
Mainnet,
Local(Vec<String>),
}
impl Safe {
pub async fn connect(
network: Network,
secret: Option<SecretKey>,
log_level: &str,
) -> Result<Safe> {
let (addrs, local, disable_mainnet_contacts, network_id, evm_network) = match network {
Network::Alpha => {
let mut peers: Vec<Multiaddr> = Vec::new();
peers.push("/ip4/206.189.96.49/udp/49841/quic-v1/p2p/12D3KooWQp3XJ6SRVLvLhezJQ7QgTQWFwDDVvwrXZQrFL4NfebWX".parse().expect("Alpha network Multiaddr parse."));
(peers, false, true, Some(2), EvmNetwork::ArbitrumSepoliaTest)
},
Network::Mainnet => {
(Vec::<Multiaddr>::new(), false, false, None, EvmNetwork::ArbitrumOne)
},
Network::Local(peers_str) => {
let mut peers: Vec<Multiaddr> = Vec::new();
for peer_str in &peers_str {
peers.push(
peer_str
.parse()
.map_err(|e| Error::Custom(format!("Local network peer Multiaddr \"{}\" parse: {}", peer_str, e)))?
);
}
let evm_network = get_evm_network(true)?;
(peers, true, true, None, evm_network)
},
};
let client = Client::init_with_config(ClientConfig {
init_peers_config: InitialPeersConfig {
addrs: addrs,
local: local,
disable_mainnet_contacts: disable_mainnet_contacts,
..Default::default()
},
evm_network: evm_network.clone(),
strategy: Default::default(),
network_id: network_id,
}).await?;
let log_handle = logging(log_level, None)?;
let mut safe = Safe {
evm_network: evm_network,
client: client,
wallet: None,
sk: None,
log_handle,
};
if let Some(sk) = secret {
safe.login(Some(sk))?;
}
Ok(safe)
}
pub fn login_with_eth(&mut self, eth_privkey: Option<String>) -> Result<()> {
let eth_pk = eth_privkey.unwrap_or(SecretKey::random().to_hex());
println!("\n\neth_pk: {:.4}(...)", eth_pk);
let wallet = Wallet::new_from_private_key(self.evm_network.clone(), ð_pk)?;
let eth_pk = EvmBytes::from_str(ð_pk)
.map_err(|e| Error::Custom(format!("Eth privkey parse: {}", e)))?;
let root_sk = SecretKey::from_hex(ROOT_SK).unwrap();
let sk = root_sk.derive_child(ð_pk);
self.wallet = Some(wallet);
self.sk = Some(sk);
Ok(())
}
pub fn login(&mut self, secret: Option<SecretKey>) -> Result<()> {
let secret = secret.unwrap_or(SecretKey::random());
self.login_with_eth(Some(SecretKey::to_hex(&secret)))?; self.sk = Some(secret);
Ok(())
}
pub fn log_level(&mut self, level: &str) -> Result<()> {
let _ = logging(level, self.log_handle.as_ref());
Ok(())
}
pub async fn reg_create(
&mut self,
data: &[u8],
name: &XorName,
) -> Result<()> {
println!("\n\nUploading data: {:?}...", data);
let data_address = self.upload(data).await?;
println!("\n\nCreating graph entry...");
let ge_name = XorNameBuilder::from(name).with_str("0").build();
let ge_key = self.sk.clone().ok_or(Error::NotLoggedIn)?.derive_child(&ge_name);
let ge = GraphEntry::new(
&ge_key,
vec![],
data_address.0,
vec![],
);
let (_attos, _xorname) = self.client.graph_entry_put(ge, PaymentOption::Wallet(self.wallet.clone().ok_or(Error::NotLoggedIn)?)).await?;
println!("\n\nCreating counter pointer...");
let pointer_name = XorNameBuilder::from(name)
.with_str("counter").build();
let pointer_key = self.sk.clone().ok_or(Error::NotLoggedIn)?.derive_child(&pointer_name);
let (_attos, _address) = self.client.pointer_create(
&pointer_key,
PointerTarget::PointerAddress(PointerAddress::new(pointer_key.public_key().derive_child("DUMMY".as_bytes()))),
PaymentOption::Wallet(self.wallet.clone().ok_or(Error::NotLoggedIn)?),
).await?;
Ok(())
}
pub async fn reg_write(
&self,
data: &[u8],
name: &XorName,
) -> Result<()> {
println!("\n\nGetting counter pointer...");
let pointer_name = XorNameBuilder::from(name)
.with_str("counter").build();
let pointer_key = self.sk.clone().ok_or(Error::NotLoggedIn)?.derive_child(&pointer_name);
let pointer = self.client.pointer_get(&PointerAddress::new(pointer_key.public_key())).await?;
println!("\n\nWriting data...");
let data_address = self.upload(data).await?;
println!("\n\nNew graph entry...");
let ge_index = pointer.counter() + 1;
println!("ge_index {}", ge_index);
let ge_name = XorNameBuilder::from(name)
.with_str(&format!("{}", ge_index)).build();
let ge_key = self.sk.clone().ok_or(Error::NotLoggedIn)?.derive_child(&ge_name);
let new_tail_ge = GraphEntry::new(
&ge_key,
vec![],
data_address.0,
vec![],
);
let (_attos, _xorname) = self.client.graph_entry_put(new_tail_ge, PaymentOption::Wallet(self.wallet.clone().ok_or(Error::NotLoggedIn)?)).await?;
println!("\n\nIncrementing counter pointer...");
self.client.pointer_update(
&pointer_key,
PointerTarget::PointerAddress(PointerAddress::new(pointer_key.public_key().derive_child("DUMMY".as_bytes()))),
).await?;
Ok(())
}
pub async fn read_reg(&self, name: &XorName, version: Option<u32>) -> Result<Vec<u8>> {
let version: u32 = if let Some(v) = version {
v
} else {
println!("\n\nReading counter pointer...");
let pointer_name = XorNameBuilder::from(name)
.with_str("counter").build();
let pointer_key = self.sk.clone().ok_or(Error::NotLoggedIn)?.derive_child(&pointer_name);
let pointer = self.client.pointer_get(&PointerAddress::new(pointer_key.public_key())).await?;
pointer.counter()
};
println!("\n\nReading graph entry...");
println!("version {}", version);
let ge_name = XorNameBuilder::from(name)
.with_str(&format!("{}", version)).build();
let ge_key = self.sk.clone().ok_or(Error::NotLoggedIn)?.derive_child(&ge_name);
let gentriess = self.client.graph_entry_get(&GraphEntryAddress::new(ge_key.public_key())).await;
let ge = match gentriess {
Ok(e) => e,
Err(GraphError::Fork(entries)) => {
entries.first().ok_or(Error::Custom(format!("No GraphEntry for version {}", version)))?.clone()
},
Err(e) => {
return Err(Error::Custom(format!("Error reading GraphEntry: {}", e)));
},
};
println!("\n\nReading data...");
let data_address = XorName(ge.content);
Ok(self.download(data_address).await?)
}
pub async fn upload(&self, data: &[u8]) -> Result<XorName> {
let (_attos, address) = self
.client
.data_put_public(
Bytes::copy_from_slice(data),
PaymentOption::Wallet(self.wallet.clone().ok_or(Error::NotLoggedIn)?),
)
.await?;
Ok(*address.xorname())
}
pub async fn download(&self, xorname: XorName) -> Result<Vec<u8>> {
let data = self.client.data_get_public(&DataAddress::new(xorname)).await?;
Ok(data.to_vec()) }
pub fn address(&self) -> Result<EvmAddress> {
self.wallet
.as_ref()
.ok_or(Error::NotLoggedIn)
.map(Wallet::address)
}
pub async fn balance(&self) -> Result<(U256, U256)> {
Ok(
(self
.wallet
.as_ref()
.ok_or(Error::NotLoggedIn)?
.balance_of_tokens()
.await?,
self
.wallet
.as_ref()
.ok_or(Error::NotLoggedIn)?
.balance_of_gas_tokens()
.await?
)
)
}
}
pub struct XorNameBuilder {
origin: XorName,
sources: Vec<Vec<u8>>,
}
impl XorNameBuilder {
pub fn from(xor_name: &XorName) -> Self {
Self {
origin: xor_name.clone(),
sources: vec![],
}
}
pub fn from_str(name: &str) -> Self {
Self {
origin: XorName::from_content(name.as_bytes()),
sources: vec![],
}
}
pub fn random() -> Self {
Self {
origin: XorName::random(&mut rand::thread_rng()),
sources: vec![],
}
}
pub fn with_bytes(mut self, name: &[u8]) -> Self {
self.sources.push(name.to_vec());
self
}
pub fn with_str(mut self, name: &str) -> Self {
self.sources.push(name.as_bytes().to_vec());
self
}
pub fn build(self) -> XorName {
let mut built = self.origin.0;
if !self.sources.is_empty() {
let other = XorName::from_content_parts(
Vec::from_iter(self.sources.iter().map(|v| v.as_slice())).as_slice(),
);
for i in 0..xor_name::XOR_NAME_LEN {
built[i] = built[i] ^ other.0[i];
}
}
XorName(built)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xor_builder() {
let x = XorNameBuilder::random().build();
let x1: XorName = XorNameBuilder::from(&x).build();
assert_eq!(x.0, x1.0);
let x2: XorName = XorNameBuilder::from(&x).with_str("test").build();
assert_ne!(x1.0, x2.0);
let x3: XorName = XorNameBuilder::from(&x1)
.with_bytes("test".as_bytes())
.build();
assert_eq!(x2.0, x3.0);
}
}