1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! # Create IPNS Entries in Rust
//!
//! Create protobuf IPNS Entries in Rust. Follows the [IPNS Spec](https://specs.ipfs.tech/ipns/ipns-record/).
//!
//! Library includes CBOR and Protobuf modules that convert IPNS entry
//! to/from bytes that can be saved to the routing channels.
//!
//! The output from this library can be published to the IPFS DHT, Pubsub, or anywhere else.
//!
pub mod cbor;
pub mod entry;
pub mod signer;
use crate::entry::ValidityType;
use humantime::Rfc3339Timestamp;
use signer::Signables;
use std::time::Duration;
use std::time::SystemTime;
/// # Example
///
/// ```rust
/// # use std::result::Result;
/// # use libp2p_identity::SigningError;
///
/// # fn main() -> Result<(), SigningError> {
/// use ipns_entry::DataBuilder;
/// use ipns_entry::entry::IpnsEntry;
/// use std::time::SystemTime;
/// use std::time::Duration;
/// use ipns_entry::signer::{Signables, Signed, Signer};
/// use libp2p_identity::PeerId;
/// use libp2p_identity::PublicKey;
/// use libp2p_identity::ed25519;
///
/// let value = "QmWEekX7EZLUd9VXRNMRXW3LXe4F6x7mB8oPxY5XLptrBq";
/// let ttl = 60 * 60 * 48; // 48 hours, the default
/// let validity: SystemTime = SystemTime::now() + Duration::from_secs(ttl);
/// let sequence = 0;
///
/// let (data, signables) = DataBuilder::new(value).validity(validity).sequence(sequence).ttl(ttl).build();
///
/// // Provide a Signer (holding your private keys) that takes `Signables {v1, v2}` and returns `Signed {v1, v2, pub_key}`
/// let signer = Signer::default();
/// let signed: Signed = signer.sign(signables)?;
///
/// let entry = IpnsEntry::new(data, signed);
/// let routable_bytes = entry.to_bytes();
///
/// // Decode received bytes into an Entry
/// let rxd_entry = IpnsEntry::from_bytes(&routable_bytes).expect("Valid routable bytes");
///
/// // Validate Entry against the IPNS Name (PeerId)
/// let peer_id = PeerId::from_public_key(&signer.public());
/// let verified = rxd_entry.is_valid_for(&peer_id).expect("valid against our peer id");
/// assert!(verified);
///
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone)]
pub struct DataBuilder {
value: String,
validity: Rfc3339Timestamp,
validity_type: ValidityType,
sequence: u64,
ttl: u64,
}
impl DataBuilder {
/// Create a new DataBuilder with the required value.
/// The default ttl is 48 hours.
/// The default validity is 48 hours from now.
/// The default sequence is 0.
///
/// Customize the ttl, validity, and sequence with the builder methods.
///
/// When the DataBuilder is ready, call `signables()` to get the Signables {v1, v2}
/// which can be signed by the Signer.
pub fn new(value: &str) -> Self {
// default to 48 hours
let ttl = 60 * 60 * 48;
let validity: SystemTime = SystemTime::now() + Duration::from_secs(ttl);
let validity = humantime::format_rfc3339_nanos(validity);
DataBuilder {
value: value.to_string(),
validity,
validity_type: ValidityType::Eol,
sequence: 0,
ttl,
}
}
pub fn value(&mut self, value: &str) -> &mut DataBuilder {
self.value = value.to_string();
self
}
pub fn validity(&mut self, validity: SystemTime) -> &mut DataBuilder {
// Convert validity to nanoseconds of rfc3339
self.validity = humantime::format_rfc3339_nanos(validity);
self
}
pub fn sequence(&mut self, sequence: u64) -> &mut DataBuilder {
self.sequence = sequence;
self
}
pub fn increment_sequence(&mut self) -> &mut DataBuilder {
self.sequence += 1;
self
}
pub fn ttl(&mut self, ttl: u64) -> &mut DataBuilder {
self.ttl = ttl;
self
}
/// Terminal method which generates the Signables from the Builder
pub fn build(&self) -> (cbor::Data, Signables) {
let v1 = vec![
self.value.as_bytes(),
self.validity.to_string().as_bytes(),
&[ValidityType::Eol as u8],
]
.concat();
let data = cbor::Data {
value: self.value.as_bytes().to_vec(),
validity: self.validity.to_string().as_bytes().to_vec(),
validity_type: self.validity_type.into(),
sequence: self.sequence,
ttl: self.ttl,
};
let v2 = vec!["ipns-signature:".as_bytes(), &data.to_bytes()].concat();
(data, Signables { v1, v2 })
}
}