refactor: whisper: Add type aliases and update rustdocs in message.rs (#10812)

* refactor: Add type aliases to Whisper and update rustdocs

* remove my question that was answered
This commit is contained in:
Luke Schoen 2019-07-04 21:45:56 +10:00 committed by David
parent 582a4ea339
commit bacc0f0b9a
3 changed files with 123 additions and 84 deletions

View File

@ -27,11 +27,38 @@ use tiny_keccak::{keccak256, Keccak};
#[cfg(not(time_checked_add))]
use time_utils::CheckedSystemTime;
/// Bloom of topics.
type Bloom = H512;
/// Topic data index within a bloom.
type BloomTopicIndex = usize;
/// List of envelope topics.
type EnvelopeTopics = SmallVec<[EnvelopeTopic; 4]>;
/// Envelope topic data.
type EnvelopeTopicData = u8;
/// List of envelope topics data.
type EnvelopeTopicsData = [EnvelopeTopicData; 4];
/// Expiry timestamp of an envelope.
type EnvelopeExpiryTimestamp = u64;
/// Message contained within an envelope
type EnvelopeMessage = Vec<u8>;
/// Arbitrary value used to target lower PoW hash.
type EnvelopeNonce = u64;
/// Envelope nonce in bytes.
type EnvelopeNonceBytes = [u8; 8];
/// Envelope proving work duration in milliseconds.
type EnvelopeProvingWorkDuration = u64;
/// Envelope message uniquely identifying proving hash.
type EnvelopeProvingHash = H256;
/// Envelope work that has been proved by the proving hash.
type EnvelopeProvenWork = f64;
/// Time-to-live of an envelope in seconds.
type EnvelopeTTLDuration = u64;
/// Work-factor proved. Takes 3 parameters: size of message, time to live,
/// and hash.
///
/// Panics if size or TTL is zero.
pub fn work_factor_proved(size: u64, ttl: u64, hash: H256) -> f64 {
pub fn work_factor_proved(size: u64, ttl: EnvelopeTTLDuration, hash: EnvelopeProvingHash) -> EnvelopeProvenWork {
assert!(size != 0 && ttl != 0);
let leading_zeros = {
@ -44,51 +71,51 @@ pub fn work_factor_proved(size: u64, ttl: u64, hash: H256) -> f64 {
2.0_f64.powi(leading_zeros as i32) / spacetime
}
/// A topic of a message.
/// A topic of a message. The topic is an abridged version of the first four bytes of the original topic's hash.
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Topic(pub [u8; 4]);
pub struct EnvelopeTopic(pub EnvelopeTopicsData);
impl From<[u8; 4]> for Topic {
fn from(x: [u8; 4]) -> Self {
Topic(x)
impl From<EnvelopeTopicsData> for EnvelopeTopic {
fn from(x: EnvelopeTopicsData) -> Self {
EnvelopeTopic(x)
}
}
impl Topic {
/// set up to three bits in the 64-byte bloom passed.
impl EnvelopeTopic {
/// Set up to three bits in the 64-byte bloom passed.
///
/// this takes 3 sets of 9 bits, treating each as an index in the range
/// This takes 3 sets of 9 bits, treating each as an index in the range
/// 0..512 into the bloom and setting the corresponding bit in the bloom to 1.
pub fn bloom_into(&self, bloom: &mut H512) {
pub fn bloom_into(&self, bloom: &mut Bloom) {
let data = &self.0;
let topics_data = &self.0;
for i in 0..3 {
let mut idx = data[i] as usize;
let mut topic_idx = topics_data[i] as BloomTopicIndex;
if data[3] & (1 << i) != 0 {
idx += 256;
if topics_data[3] & (1 << i) != 0 {
topic_idx += 256;
}
debug_assert!(idx <= 511);
bloom.as_bytes_mut()[idx / 8] |= 1 << (7 - idx % 8);
debug_assert!(topic_idx <= 511);
bloom.as_bytes_mut()[topic_idx / 8] |= 1 << (7 - topic_idx % 8);
}
}
/// Get bloom for single topic.
pub fn bloom(&self) -> H512 {
pub fn bloom(&self) -> Bloom {
let mut bloom = Default::default();
self.bloom_into(&mut bloom);
bloom
}
}
impl rlp::Encodable for Topic {
impl rlp::Encodable for EnvelopeTopic {
fn rlp_append(&self, s: &mut RlpStream) {
s.encoder().encode_value(&self.0);
}
}
impl rlp::Decodable for Topic {
impl rlp::Decodable for EnvelopeTopic {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
use std::cmp;
@ -96,16 +123,16 @@ impl rlp::Decodable for Topic {
cmp::Ordering::Less => Err(DecoderError::RlpIsTooShort),
cmp::Ordering::Greater => Err(DecoderError::RlpIsTooBig),
cmp::Ordering::Equal => {
let mut t = [0u8; 4];
let mut t: EnvelopeTopicsData = [0u8; 4];
t.copy_from_slice(bytes);
Ok(Topic(t))
Ok(EnvelopeTopic(t))
}
})
}
}
/// Calculate union of blooms for given topics.
pub fn bloom_topics(topics: &[Topic]) -> H512 {
pub fn bloom_topics(topics: &[EnvelopeTopic]) -> Bloom {
let mut bloom = H512::default();
for topic in topics {
topic.bloom_into(&mut bloom);
@ -143,7 +170,8 @@ impl fmt::Display for Error {
}
}
fn append_topics<'a>(s: &'a mut RlpStream, topics: &[Topic]) -> &'a mut RlpStream {
/// Append given topic(s) to RLP stream.
fn append_topics<'a>(s: &'a mut RlpStream, topics: &[EnvelopeTopic]) -> &'a mut RlpStream {
if topics.len() == 1 {
s.append(&topics[0])
} else {
@ -151,27 +179,27 @@ fn append_topics<'a>(s: &'a mut RlpStream, topics: &[Topic]) -> &'a mut RlpStrea
}
}
fn decode_topics(rlp: Rlp) -> Result<SmallVec<[Topic; 4]>, DecoderError> {
fn decode_topics(rlp: Rlp) -> Result<EnvelopeTopics, DecoderError> {
if rlp.is_list() {
rlp.iter().map(|r| r.as_val::<Topic>()).collect()
rlp.iter().map(|r| r.as_val::<EnvelopeTopic>()).collect()
} else {
rlp.as_val().map(|t| SmallVec::from_slice(&[t]))
}
}
// Raw envelope struct.
/// An `Envelope` instance is contained in each `Message`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Envelope {
/// Expiry timestamp
pub expiry: u64,
/// Time-to-live in seconds
pub ttl: u64,
/// series of 4-byte topics.
pub topics: SmallVec<[Topic; 4]>,
/// The message contained within.
pub data: Vec<u8>,
/// Expiry timestamp.
pub expiry: EnvelopeExpiryTimestamp,
/// Time-to-live in seconds.
pub ttl: EnvelopeTTLDuration,
/// Series of 4-byte topics.
pub topics: EnvelopeTopics,
/// The message contained within an envelope.
pub message_data: EnvelopeMessage,
/// Arbitrary value used to target lower PoW hash.
pub nonce: u64,
pub nonce: EnvelopeNonce,
}
impl Envelope {
@ -180,7 +208,8 @@ impl Envelope {
self.topics.len() != 1
}
fn proving_hash(&self) -> H256 {
// Generate the uniquely identifying proving hash for the message.
fn proving_hash(&self) -> EnvelopeProvingHash {
use byteorder::{BigEndian, ByteOrder};
let mut buf = [0; 32];
@ -189,7 +218,7 @@ impl Envelope {
stream.append(&self.expiry).append(&self.ttl);
append_topics(&mut stream, &self.topics)
.append(&self.data);
.append(&self.message_data);
let mut digest = Keccak::new_keccak256();
digest.update(&*stream.drain());
@ -212,7 +241,7 @@ impl rlp::Encodable for Envelope {
.append(&self.ttl);
append_topics(s, &self.topics)
.append(&self.data)
.append(&self.message_data)
.append(&self.nonce);
}
}
@ -225,7 +254,7 @@ impl rlp::Decodable for Envelope {
expiry: rlp.val_at(0)?,
ttl: rlp.val_at(1)?,
topics: decode_topics(rlp.at(2)?)?,
data: rlp.val_at(3)?,
message_data: rlp.val_at(3)?,
nonce: rlp.val_at(4)?,
})
}
@ -234,22 +263,22 @@ impl rlp::Decodable for Envelope {
/// Message creation parameters.
/// Pass this to `Message::create` to make a message.
pub struct CreateParams {
/// time-to-live in seconds.
pub ttl: u64,
/// payload data.
pub payload: Vec<u8>,
/// Topics. May not be empty.
pub topics: Vec<Topic>,
/// Envelope time-to-live in seconds.
pub ttl: EnvelopeTTLDuration,
/// Envelope payload of message data.
pub payload: EnvelopeMessage,
/// Envelope topics. Must not be empty.
pub topics: Vec<EnvelopeTopic>,
/// How many milliseconds to spend proving work.
pub work: u64,
pub work: EnvelopeProvingWorkDuration,
}
/// A whisper message. This is a checked message carrying around metadata.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Message {
envelope: Envelope,
bloom: H512,
hash: H256,
bloom: Bloom,
hash: EnvelopeProvingHash,
encoded_size: usize,
}
@ -270,6 +299,7 @@ impl Message {
assert!(params.ttl > 0);
// Expiry period since the last epoch rounded up to the nearest second.
let expiry = {
let since_epoch = SystemTime::now()
.checked_add(Duration::from_secs(params.ttl))
@ -277,10 +307,13 @@ impl Message {
.ok_or(Error::TimestampOverflow)?
.duration_since(time::UNIX_EPOCH).expect("time after now is after unix epoch; qed");
// round up the sub-second to next whole second.
// Round up the sub-second to next whole second.
since_epoch.as_secs() + if since_epoch.subsec_nanos() == 0 { 0 } else { 1 }
};
// Encrypt an RLP stream into a digest. Create the RLP stream by appending
// to it the envelope topics, envelope payload of message data,
// envelope ttl, and the expiry period since the last epoch.
let start_digest = {
let mut stream = RlpStream::new_list(4);
stream.append(&expiry).append(&params.ttl);
@ -291,8 +324,10 @@ impl Message {
digest
};
// Find the best nonce based on using updating the digest with
// randomly generated envelope nonce bytes
let mut buf = [0; 32];
let mut try_nonce = move |nonce: &[u8; 8]| {
let mut try_nonce = move |nonce: &EnvelopeNonceBytes| {
let mut digest = start_digest.clone();
digest.update(&nonce[..]);
digest.finalize(&mut buf[..]);
@ -300,9 +335,12 @@ impl Message {
buf.clone()
};
let mut nonce: [u8; 8] = rng.gen();
let mut nonce: EnvelopeNonceBytes = rng.gen();
let mut best_found = try_nonce(&nonce);
// Start proving work, which involves repeatedly trying to create another
// nonce hash that is better (lower PoW hash) than the latest best nonce,
// to replace it.
let start = Instant::now();
while start.elapsed() <= Duration::from_millis(params.work) {
@ -316,10 +354,10 @@ impl Message {
}
let envelope = Envelope {
expiry: expiry,
expiry,
ttl: params.ttl,
topics: params.topics.into_iter().collect(),
data: params.payload,
message_data: params.payload,
nonce: BigEndian::read_u64(&nonce[..]),
};
@ -344,9 +382,9 @@ impl Message {
Message::from_components(envelope, encoded_size, hash, now)
}
// create message from envelope, hash, and encoded size.
// does checks for validity.
fn from_components(envelope: Envelope, size: usize, hash: H256, now: SystemTime)
// Create message from envelope, hash, and encoded size.
// Does checks for validity.
fn from_components(envelope: Envelope, size: usize, hash: EnvelopeProvingHash, now: SystemTime)
-> Result<Self, Error>
{
const LEEWAY_SECONDS: u64 = 2;
@ -371,9 +409,9 @@ impl Message {
let bloom = bloom_topics(&envelope.topics);
Ok(Message {
envelope: envelope,
bloom: bloom,
hash: hash,
envelope,
bloom,
hash,
encoded_size: size,
})
}
@ -388,18 +426,18 @@ impl Message {
self.encoded_size
}
/// Get a uniquely identifying hash for the message.
pub fn hash(&self) -> &H256 {
/// Get a uniquely identifying proving hash for the message.
pub fn hash(&self) -> &EnvelopeProvingHash {
&self.hash
}
/// Get the bloom filter of the topics
/// Get the bloom filter of the topics.
pub fn bloom(&self) -> &H512 {
&self.bloom
}
/// Get the work proved by the hash.
pub fn work_proved(&self) -> f64 {
pub fn work_proved(&self) -> EnvelopeProvenWork {
let proving_hash = self.envelope.proving_hash();
work_factor_proved(self.encoded_size as _, self.envelope.ttl, proving_hash)
@ -411,13 +449,13 @@ impl Message {
}
/// Get the topics.
pub fn topics(&self) -> &[Topic] {
pub fn topics(&self) -> &[EnvelopeTopic] {
&self.envelope.topics
}
/// Get the message data.
pub fn data(&self) -> &[u8] {
&self.envelope.data
pub fn message_data(&self) -> &EnvelopeMessage {
&self.envelope.message_data
}
}
@ -438,7 +476,7 @@ mod tests {
assert!(Message::create(CreateParams {
ttl: 100,
payload: vec![1, 2, 3, 4],
topics: vec![Topic([1, 2, 1, 2])],
topics: vec![EnvelopeTopic([1, 2, 1, 2])],
work: 50,
}).is_ok());
}
@ -448,7 +486,7 @@ mod tests {
let envelope = Envelope {
expiry: 100_000,
ttl: 30,
data: vec![9; 256],
message_data: vec![9; 256],
topics: SmallVec::from_slice(&[Default::default()]),
nonce: 1010101,
};
@ -464,8 +502,8 @@ mod tests {
let envelope = Envelope {
expiry: 100_000,
ttl: 30,
data: vec![9; 256],
topics: SmallVec::from_slice(&[Default::default(), Topic([1, 2, 3, 4])]),
message_data: vec![9; 256],
topics: SmallVec::from_slice(&[Default::default(), EnvelopeTopic([1, 2, 3, 4])]),
nonce: 1010101,
};
@ -480,7 +518,7 @@ mod tests {
let envelope = Envelope {
expiry: 100_000,
ttl: 30,
data: vec![9; 256],
message_data: vec![9; 256],
topics: SmallVec::from_slice(&[Default::default()]),
nonce: 1010101,
};
@ -499,7 +537,7 @@ mod tests {
let envelope = Envelope {
expiry: 100_000,
ttl: 30,
data: vec![9; 256],
message_data: vec![9; 256],
topics: SmallVec::from_slice(&[Default::default()]),
nonce: 1010101,
};
@ -516,7 +554,7 @@ mod tests {
let envelope = Envelope {
expiry: 100_000,
ttl: 200_000,
data: vec![9; 256],
message_data: vec![9; 256],
topics: SmallVec::from_slice(&[Default::default()]),
nonce: 1010101,
};
@ -530,10 +568,10 @@ mod tests {
#[test]
fn work_factor() {
// 256 leading zeros -> 2^256 / 1
assert_eq!(work_factor_proved(1, 1, H256::zero()), 115792089237316200000000000000000000000000000000000000000000000000000000000000.0);
assert_eq!(work_factor_proved(1, 1, EnvelopeProvingHash::zero()), 115792089237316200000000000000000000000000000000000000000000000000000000000000.0);
// 255 leading zeros -> 2^255 / 1
assert_eq!(work_factor_proved(1, 1, H256::from_low_u64_be(1)), 57896044618658100000000000000000000000000000000000000000000000000000000000000.0);
assert_eq!(work_factor_proved(1, 1, EnvelopeProvingHash::from_low_u64_be(1)), 57896044618658100000000000000000000000000000000000000000000000000000000000000.0);
// 0 leading zeros -> 2^0 / 1
assert_eq!(work_factor_proved(1, 1, serde_json::from_str::<H256>("\"0xff00000000000000000000000000000000000000000000000000000000000000\"").unwrap()), 1.0);
assert_eq!(work_factor_proved(1, 1, serde_json::from_str::<EnvelopeProvingHash>("\"0xff00000000000000000000000000000000000000000000000000000000000000\"").unwrap()), 1.0);
}
}

View File

@ -24,7 +24,7 @@ use ethkey::Public;
use jsonrpc_pubsub::typed::{Subscriber, Sink};
use parking_lot::{Mutex, RwLock};
use message::{Message, Topic};
use message::{Message, EnvelopeTopic};
use super::{key_store::KeyStore, types::{self, FilterItem, HexEncode}};
/// Kinds of filters,
@ -198,7 +198,7 @@ impl Drop for Manager {
/// Filter incoming messages by critera.
pub struct Filter {
topics: Vec<(Vec<u8>, H512, Topic)>,
topics: Vec<(Vec<u8>, H512, EnvelopeTopic)>,
from: Option<Public>,
decrypt_with: Option<H256>,
}
@ -278,7 +278,7 @@ impl Filter {
}
};
let decrypted = match decrypt.decrypt(message.data()) {
let decrypted = match decrypt.decrypt(message.message_data()) {
Some(d) => d,
None => {
trace!(target: "whisper", "Failed to decrypt message with {} matching topics",
@ -317,7 +317,7 @@ impl Filter {
#[cfg(test)]
mod tests {
use message::{CreateParams, Message, Topic};
use message::{CreateParams, Message, EnvelopeTopic};
use rpc::types::{FilterRequest, HexEncode};
use rpc::abridge_topic;
use super::*;
@ -366,7 +366,7 @@ mod tests {
let message = Message::create(CreateParams {
ttl: 100,
payload: vec![1, 3, 5, 7, 9],
topics: vec![Topic([1, 8, 3, 99])],
topics: vec![EnvelopeTopic([1, 8, 3, 99])],
work: 0,
}).unwrap();

View File

@ -19,7 +19,7 @@
//! Manages standard message format decoding, ephemeral identities, signing,
//! encryption, and decryption.
//!
//! Provides an interface for using whisper to transmit data securely.
//! Provides an interface for using Whisper to transmit data securely.
use std::sync::Arc;
@ -35,7 +35,7 @@ use self::filter::Filter;
use self::key_store::{Key, KeyStore};
use self::types::HexEncode;
use message::{CreateParams, Message, Topic};
use message::{CreateParams, Message, EnvelopeTopic};
mod crypto;
mod filter;
@ -61,7 +61,7 @@ fn topic_hash(topic: &[u8]) -> H256 {
}
// abridge topic using first four bytes of hash.
fn abridge_topic(topic: &[u8]) -> Topic {
fn abridge_topic(topic: &[u8]) -> EnvelopeTopic {
let mut abridged = [0; 4];
let hash = topic_hash(topic).0;
abridged.copy_from_slice(&hash[..4]);
@ -99,6 +99,7 @@ pub trait Whisper {
#[rpc(name = "shh_getPrivateKey")]
fn get_private(&self, types::Identity) -> Result<types::Private, Error>;
/// Get symmetric key. Succeeds if identity has been stored.
#[rpc(name = "shh_getSymKey")]
fn get_symmetric(&self, types::Identity) -> Result<types::Symmetric, Error>;