diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Cargo.lock | 40 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | TODO | 7 | ||||
-rw-r--r-- | src/bin/geniza-net.rs | 28 | ||||
-rw-r--r-- | src/bin/geniza.rs | 166 | ||||
-rw-r--r-- | src/bitfield.rs | 63 | ||||
-rw-r--r-- | src/helpers.rs | 61 | ||||
-rw-r--r-- | src/lib.rs | 27 | ||||
-rw-r--r-- | src/peer.rs | 31 | ||||
-rw-r--r-- | src/synchronizer.rs (renamed from src/node.rs) | 68 |
11 files changed, 390 insertions, 109 deletions
@@ -1,5 +1,6 @@ papers/ target/ +scratch/ **/*.rs.bk *.o *.a @@ -20,7 +20,7 @@ dependencies = [ "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "skeptic 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -90,9 +90,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -107,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" -version = "2.28.0" +version = "2.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -208,7 +208,7 @@ version = "0.1.0" dependencies = [ "assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.28.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.29.0 (registry+https://github.com/rust-lang/crates.io-index)", "data-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -408,7 +408,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -418,22 +418,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive_internals" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -442,13 +442,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -461,7 +461,7 @@ dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -473,7 +473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "libsodium-sys 0.0.16 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -621,7 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cargo_metadata 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f56ec3e469bca7c276f2eea015aa05c5e381356febdbb0683c2580189604537" "checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum clap 2.28.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc34bf7d5d66268b466b9852bca925ec1d2650654dab4da081e63fd230145c2e" +"checksum clap 2.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "110d43e343eb29f4f51c1db31beb879d546db27998577e5715270a54bcf41d3f" "checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" "checksum data-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "099d2591f809713931cd770f2bdf4b8a4d2eb7314bc762da4c375ecaa74af80f" "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" @@ -662,10 +662,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee2bc909ab2d8d60dab26e8cad85b25d795b14603a0dcb627b78b9d30b6454b" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a7c37d7f192f00041e8a613e936717923a71bc0c9051fc4425a49b104140f05" -"checksum serde_derive 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "0672de7300b02bac3f3689f8faea813c4a1ea9fe0cb49e80f714231d267518a2" -"checksum serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32f1926285523b2db55df263d2aa4eb69ddcfa7a7eade6430323637866b513ab" -"checksum serde_json 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ea28ea0cca944668919bec6af209864a8dfe769fd2b0b723f36b22e20c1bf69f" +"checksum serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1c57ab4ec5fa85d08aaf8ed9245899d9bbdd66768945b21113b84d5f595cb6a1" +"checksum serde_derive 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "02c92ea07b6e49b959c1481804ebc9bfd92d3c459f1274c9a9546829e42a66ce" +"checksum serde_derive_internals 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75c6aac7b99801a16db5b40b7bf0d7e4ba16e76fbf231e32a4677f271cac0603" +"checksum serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7cf5b0b5b4bd22eeecb7e01ac2e1225c7ef5e4272b79ee28a8392a8c8489c839" "checksum skeptic 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c8431f8fca168e2db4be547bd8329eac70d095dff1444fee4b0fa0fabc7df75a" "checksum sodiumoxide 0.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "eb5cb2f14f9a51352ad65e59257a0a9459d5a36a3615f3d53a974c82fdaaa00a" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" @@ -30,15 +30,16 @@ it will eat-your-data! - [x] import file to register - [x] export file from register - [x] import/export directories recursively -- [ ] Networking +- [ ] Protocol - [x] send/receive encrypted messages to a known host - - [ ] receive entire register from a known host - - [ ] share (upload) register to a known host - [ ] bitfields - [ ] Discovery - [x] centralized DNS - [ ] mDNS (local DNS) - [ ] DHT (distributed hash table) +- [ ] Peer Synchronization + - [ ] receive entire register from a known host + - [ ] share (upload) register to a known host - [ ] Wrapper commands - [ ] clone - [ ] share @@ -1,11 +1,10 @@ next: +- DatPeer wrapper API +- Synchronizer API - clean up node bitfield implementation - "insert" API for HyperRegister -- crude single-thread drive+network clone - sparse register clone -- "needed data chunks" and progress function? -- crude single-thread drive+network broadcast - 'geniza clone' command - 'geniza checkout' command - 'geniza init', 'geniza commit' commands @@ -28,7 +27,7 @@ Backburner: - API to link and run from, eg, python - uTP transport - in-memory storage backend -- WASM +- compile to WASM... play in browser? - multi-connection network sync (per-connection state, etc) - duplicate file/chunk optimizations - secret_key-in-home helpers (read/write) diff --git a/src/bin/geniza-net.rs b/src/bin/geniza-net.rs index 6d38eae..0c82e6d 100644 --- a/src/bin/geniza-net.rs +++ b/src/bin/geniza-net.rs @@ -12,22 +12,6 @@ use geniza::*; use std::path::Path; use clap::{App, SubCommand, Arg}; -fn parse_dat_key(raw_key: &str) -> Result<Vec<u8>> { - - if raw_key.len() != 32 * 2 { - bail!("dat key not correct length"); - } - let mut key_bytes = vec![]; - for i in 0..32 { - let r = u8::from_str_radix(&raw_key[2 * i..2 * i + 2], 16); - match r { - Ok(b) => key_bytes.push(b), - Err(e) => bail!("Problem with hex: {}", e), - }; - } - Ok(key_bytes) -} - fn run() -> Result<()> { env_logger::init().unwrap(); @@ -81,7 +65,7 @@ fn run() -> Result<()> { ("connect", Some(subm)) => { let host_port = subm.value_of("host_port").unwrap(); let dat_key = subm.value_of("dat_key").unwrap(); - let key_bytes = parse_dat_key(&dat_key)?; + let key_bytes = parse_dat_address(&dat_key)?; DatConnection::connect(host_port, &key_bytes, false)?; println!("Done!"); } @@ -89,7 +73,7 @@ fn run() -> Result<()> { let host_port = subm.value_of("host_port").unwrap(); let dat_key = subm.value_of("dat_key").unwrap(); let count: u64 = subm.value_of("count").unwrap().parse().unwrap(); - let key_bytes = parse_dat_key(&dat_key)?; + let key_bytes = parse_dat_address(&dat_key)?; let mut dc = DatConnection::connect(host_port, &key_bytes, false)?; dc.receive_some(false, count)?; dc.receive_some(true, count)?; @@ -97,7 +81,7 @@ fn run() -> Result<()> { } ("discovery-key", Some(subm)) => { let dat_key = subm.value_of("dat_key").unwrap(); - let key_bytes = parse_dat_key(&dat_key)?; + let key_bytes = parse_dat_address(&dat_key)?; let disc_key = make_discovery_key(&key_bytes); for b in disc_key { print!("{:02x}", b); @@ -106,7 +90,7 @@ fn run() -> Result<()> { } ("discovery-dns-name", Some(subm)) => { let dat_key = subm.value_of("dat_key").unwrap(); - let key_bytes = parse_dat_key(&dat_key)?; + let key_bytes = parse_dat_address(&dat_key)?; let disc_key = make_discovery_key(&key_bytes); for b in 0..20 { print!("{:02x}", disc_key[b]); @@ -115,7 +99,7 @@ fn run() -> Result<()> { } ("discover-dns", Some(subm)) => { let dat_key = subm.value_of("dat_key").unwrap(); - let key_bytes = parse_dat_key(&dat_key)?; + let key_bytes = parse_dat_address(&dat_key)?; let peers = discover_peers_dns(&key_bytes)?; if peers.len() == 0 { println!("No peers found!"); @@ -128,7 +112,7 @@ fn run() -> Result<()> { ("naive-clone", Some(subm)) => { let host_port = subm.value_of("host_port").unwrap(); let dat_key = subm.value_of("dat_key").unwrap(); - let key_bytes = parse_dat_key(&dat_key)?; + let key_bytes = parse_dat_address(&dat_key)?; let dir = Path::new(subm.value_of("dat-dir").unwrap()); let mut metadata = SleepDirRegister::create(&dir, "metadata")?; node_simple_clone(host_port, &key_bytes, &mut metadata, false)?; diff --git a/src/bin/geniza.rs b/src/bin/geniza.rs new file mode 100644 index 0000000..f5c73b0 --- /dev/null +++ b/src/bin/geniza.rs @@ -0,0 +1,166 @@ +// Free Software under GPL-3.0, see LICENSE +// Copyright 2017 Bryan Newbold + +extern crate clap; +extern crate env_logger; +#[macro_use] +extern crate error_chain; +extern crate geniza; + +// TODO: more careful import +use geniza::*; +use std::path::{Path, PathBuf}; +use clap::{App, SubCommand}; +use std::env::current_dir; + + +// Helper to find a dat directory somewhere in the parent to the current working directory (or None +// if not found) +fn find_dat_dir() -> Option<PathBuf> { + let mut here: &Path = ¤t_dir().unwrap(); + loop { + let check = here.join(".dat"); + if check.is_dir() && check.join("metadata.tree").is_file() { + return Some(check); + }; + here = match here.parent() { + None => return None, + Some(t) => t, + } + } +} + +fn run() -> Result<()> { + env_logger::init().unwrap(); + + let matches = App::new("geniza") + .version(env!("CARGO_PKG_VERSION")) + .subcommand( + SubCommand::with_name("clone") + .about("Finds and downloads a dat archive from the network into a given folder") + .arg_from_usage("<address> 'dat address (public key) to fetch'") + .arg_from_usage("[dir] 'directory to clone into'") + .arg_from_usage("--full 'pull and save complete history (not just latest version)'"), + ) + .subcommand( + SubCommand::with_name("init") + .about("Creates a data archive in the current directory") + .arg_from_usage("[dir] 'init somewhere other than current directory'"), + ) + .subcommand( + SubCommand::with_name("status") + .about("Displays current status of archive and checkout") + ) + .subcommand( + SubCommand::with_name("log") + .about("Displays version history of the archive") + ) + .subcommand( + SubCommand::with_name("checkout") + .about("Copies (or overwrites) files from dat archive") + .arg_from_usage("<path> 'relative path to checkout'"), + ) + .subcommand( + SubCommand::with_name("add") + .about("Adds a path to the current dat archive") + .arg_from_usage("<path> 'file to delete from dat archive'"), + ) + .subcommand( + SubCommand::with_name("rm") + .about("Removes a path from the current dat archive, and from disk (danger!)") + .arg_from_usage("<path> 'file to delete from dat archive'"), + ) + .subcommand( + SubCommand::with_name("ls") + .about("Lists contents of the archive") + .arg_from_usage("[path] 'path to display'") + .arg_from_usage("--recursive 'show directory recursively'"), + ) + .subcommand( + SubCommand::with_name("seed") + .about("Uploads indefinately to any peer") + ) + .subcommand( + SubCommand::with_name("pull") + .about("Pulls highest known version from all possible peers") + .arg_from_usage("--forever 'continues to search for updates forever'"), + ) + .get_matches(); + + match matches.subcommand() { + ("clone", Some(subm)) => { + let dat_key = subm.value_of("dat_key").unwrap(); + let _key_bytes = parse_dat_address(&dat_key)?; + unimplemented!(); + //let dir = Path::new(subm.value_of("dat-dir").unwrap()); + //let mut metadata = SleepDirRegister::create(&dir, "metadata")?; + //node_simple_clone(host_port, &key_bytes, &mut metadata, false)?; + } + ("init", Some(subm)) => { + let _dir = Path::new(subm.value_of("dir").unwrap()); + unimplemented!(); + } + ("status", Some(_subm)) => { + unimplemented!(); + } + ("log", Some(_subm)) => { + let dat_dir = match find_dat_dir() { + Some(p) => p, + None => { + println!("Couldn't find '.dat/' in the current or (any parent) directory."); + println!("Are you running from inside a Dat archive?"); + ::std::process::exit(-1); + } + }; + println!("{:?}", dat_dir); + let mut drive = DatDrive::open(dat_dir, false)?; + for entry in drive.history(0) { + let entry = entry?; + if let Some(stat) = entry.stat { + if stat.get_blocks() == 0 { + println!("{}\t[chg] {}", + entry.index, entry.path.display()); + } else { + println!("{}\t[put] {}\t{} bytes ({} blocks)", + entry.index, entry.path.display(), stat.get_size(), stat.get_blocks()); + } + } else { + println!("{}\t[del] {}", + entry.index, entry.path.display()); + } + } + } + ("checkout", Some(subm)) => { + let _path = Path::new(subm.value_of("path").unwrap()); + unimplemented!(); + } + ("add", Some(subm)) => { + let _path = Path::new(subm.value_of("path").unwrap()); + unimplemented!(); + } + ("rm", Some(subm)) => { + let _path = Path::new(subm.value_of("path").unwrap()); + unimplemented!(); + } + ("ls", Some(subm)) => { + let _path = Path::new(subm.value_of("path").unwrap()); + unimplemented!(); + } + ("seed", Some(subm)) => { + let _path = Path::new(subm.value_of("path").unwrap()); + unimplemented!(); + } + ("pull", Some(subm)) => { + let _path = Path::new(subm.value_of("path").unwrap()); + unimplemented!(); + } + _ => { + println!("Missing or unimplemented command!"); + println!("{}", matches.usage()); + ::std::process::exit(-1); + } + } + Ok(()) +} + +quick_main!(run); diff --git a/src/bitfield.rs b/src/bitfield.rs new file mode 100644 index 0000000..018ccbc --- /dev/null +++ b/src/bitfield.rs @@ -0,0 +1,63 @@ + +use errors::*; +use integer_encoding::VarInt; +use bit_field::BitArray; +use network_msgs::Have; + +pub struct Bitfield { + inner: Vec<u64>, +} + +impl Bitfield { + + pub fn from_have_msg(msg: &Have) -> Bitfield { + unimplemented!() + } + + pub fn get(&self, index: u64) -> Result<bool> { + unimplemented!() + } +} + +pub fn decode_bitfield(raw_bf: &[u8]) -> Result<Vec<u8>> { + let mut offset = 0; // byte offset that we have read up to + if raw_bf.len() < 1 { + bail!("Expected (varint-encoded) bitfield to have len>=1"); + } + let mut bit_array: Vec<u8> = vec![]; + while offset < raw_bf.len() { + let (header, inc): (u64, usize) = VarInt::decode_var(&raw_bf[offset..]); + offset += inc; + + if (header & 0x01) == 0x01 { + // compressed + let bit = (header & 0x02) == 0x02; + let run_len = header >> 2; + if bit { + bit_array.append(&mut vec![0xFF; run_len as usize]); + } else { + bit_array.append(&mut vec![0x00; run_len as usize]); + } + } else { + // uncompressed + let byte_count = header >> 1; + let mut data = raw_bf[offset..(offset + byte_count as usize)].to_vec(); + bit_array.append(&mut data); + offset += byte_count as usize; + } + } + // XXX: HACK + bit_array.reverse(); + return Ok(bit_array); +} + +/// Finds the index of the lowest bit +pub fn max_high_bit(bf: &[u8]) -> u64 { + // XXX: HACK, going backwards + for i in 0..bf.bit_length() { + if bf.get_bit(i) { + return (bf.bit_length() - i - 1) as u64; + } + } + return 0; +} diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000..32f0e25 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,61 @@ + +use errors::*; +use crypto::digest::Digest; +use crypto::blake2b::Blake2b; + +/// Helper to calculate a discovery key from a public key. 'key' should be 32 bytes; the returned +/// array will also be 32 bytes long. +/// +/// dat discovery keys are calculated as a BLAKE2b "keyed hash" (using the passed key) of the string +/// "hypercore" (with no trailing null byte). +pub fn make_discovery_key(key: &[u8]) -> Vec<u8> { + let mut discovery_key = [0; 32]; + let mut hash = Blake2b::new_keyed(32, key); + hash.input(&"hypercore".as_bytes()); + hash.result(&mut discovery_key); + discovery_key.to_vec() +} + +/// Helper to parse a dat address (aka, public key) in string format. +/// +/// Address can start with 'dat://'. It should contain 64 hexadecimal characters. +pub fn parse_dat_address(input: &str) -> Result<Vec<u8>> { + + let raw_key = if input.starts_with("dat://") { + &input[6..] + } else { + input + }; + if raw_key.len() != 32 * 2 { + bail!("dat key not correct length"); + } + let mut key_bytes = vec![]; + for i in 0..32 { + let r = u8::from_str_radix(&raw_key[2 * i..2 * i + 2], 16); + match r { + Ok(b) => key_bytes.push(b), + Err(e) => bail!("Problem with hex: {}", e), + }; + } + Ok(key_bytes) +} + +#[test] +fn test_parse_dat_address() { + + assert!(parse_dat_address( + "c7638882870abd4044d6467b0738f15e3a36f57c3a7f7f3417fd7e4e0841d597").is_ok()); + assert!(parse_dat_address( + "C7638882870ABD4044D6467B0738F15E3A36F57C3A7F7F3417FD7E4E0841D597").is_ok()); + assert!(parse_dat_address( + "dat://c7638882870abd4044d6467b0738f15e3a36f57c3a7f7f3417fd7e4e0841d597").is_ok()); + + assert!(parse_dat_address( + "c7638882870ab").is_err()); + assert!(parse_dat_address( + "g7638882870abd4044d6467b0738f15e3a36f57c3a7f7f3417fd7e4e0841d597").is_err()); + assert!(parse_dat_address( + "dat://c7638882870abd4044d6467b0738f15e3a36f57c3a7f7f3417fd7e4e0841d5970").is_err()); + assert!(parse_dat_address( + "dat://c7638882870abd4044d6467b0738f15e3a36f57c3a7f7f3417fd7e4e0841d59").is_err()); +} @@ -46,6 +46,10 @@ mod errors { pub use errors::*; // Organize code internally (files, modules), but pull it all into a flat namespace to export. +mod helpers; +pub use helpers::*; +mod bitfield; +pub use bitfield::*; mod sleep_file; pub use sleep_file::*; mod sleep_register; @@ -56,25 +60,10 @@ mod protocol; pub use protocol::*; pub mod network_msgs; pub mod metadata_msgs; -mod node; -pub use node::*; mod discovery; pub use discovery::*; - -// Shared functions -use crypto::digest::Digest; -use crypto::blake2b::Blake2b; - -/// Helper to calculate a discovery key from a public key. 'key' should be 32 bytes; the returned -/// array will also be 32 bytes long. -/// -/// dat discovery keys are calculated as a BLAKE2b "keyed hash" (using the passed key) of the string -/// "hypercore" (with no trailing null byte). -pub fn make_discovery_key(key: &[u8]) -> Vec<u8> { - let mut discovery_key = [0; 32]; - let mut hash = Blake2b::new_keyed(32, key); - hash.input(&"hypercore".as_bytes()); - hash.result(&mut discovery_key); - discovery_key.to_vec() -} +mod peer; +pub use peer::*; +mod synchronizer; +pub use synchronizer::*; diff --git a/src/peer.rs b/src/peer.rs new file mode 100644 index 0000000..29b90a2 --- /dev/null +++ b/src/peer.rs @@ -0,0 +1,31 @@ + +use errors::*; +use sleep_register::{HyperRegister, SleepDirRegister}; +use protocol::DatConnection; +use bitfield::Bitfield; + +pub struct DatPeer { + registers: Vec<SleepDirRegister>, + connection: DatConnection, + have_log: Vec<Vec<Bitfield>>, +} + +impl DatPeer { + + pub fn new(connection: DatConnection, registers: Vec<SleepDirRegister>) -> DatPeer { + DatPeer { + registers, + connection, + have_log: vec![], + } + } + + pub fn has(self, register: u64, index: u64) -> Result<bool> { + for bitfield in self.have_log[register as usize].iter() { + if bitfield.get(index)? { + return Ok(true) + } + } + Ok(false) + } +} diff --git a/src/node.rs b/src/synchronizer.rs index 582bf3d..4e803fb 100644 --- a/src/node.rs +++ b/src/synchronizer.rs @@ -1,60 +1,46 @@ use errors::*; use network_msgs::*; +use bitfield::*; use protocol::{DatNetMessage, DatConnection}; -use integer_encoding::VarInt; -use bit_field::BitArray; use sleep_register::HyperRegister; +use peer::DatPeer; +use sleep_register::SleepDirRegister; + +pub enum SyncMode { + RxMax, + RxEndless, + TxEndless, + RxTxEndless, +} -fn decode_bitfiled(raw_bf: &[u8]) -> Result<Vec<u8>> { - let mut offset = 0; // byte offset that we have read up to - if raw_bf.len() < 1 { - bail!("Expected (varint-encoded) bitfield to have len>=1"); - } - let mut bit_array: Vec<u8> = vec![]; - while offset < raw_bf.len() { - let (header, inc): (u64, usize) = VarInt::decode_var(&raw_bf[offset..]); - offset += inc; - - if (header & 0x01) == 0x01 { - // compressed - let bit = (header & 0x02) == 0x02; - let run_len = header >> 2; - if bit { - bit_array.append(&mut vec![0xFF; run_len as usize]); - } else { - bit_array.append(&mut vec![0x00; run_len as usize]); - } - } else { - // uncompressed - let byte_count = header >> 1; - let mut data = raw_bf[offset..(offset + byte_count as usize)].to_vec(); - bit_array.append(&mut data); - offset += byte_count as usize; - } - } - // XXX: HACK - bit_array.reverse(); - return Ok(bit_array); +pub struct Synchronizer { + peers: Vec<DatPeer>, + registers: Vec<SleepDirRegister>, + mode: SyncMode, + wanted: Bitfield, + inflight: Vec<Vec<u64>>, } -/// Finds the index of the lowest bit -fn max_high_bit(bf: &[u8]) -> u64 { - // XXX: HACK, going backwards - for i in 0..bf.bit_length() { - if bf.get_bit(i) { - return (bf.bit_length() - i - 1) as u64; - } +impl Synchronizer { + + pub fn next_wanted(&mut self, reg: u64) -> Option<(u64, u64)> { + // XXX + None + } + + pub fn tick(&mut self) -> Result<()> { + Ok(()) } - return 0; } + fn max_index(have_msg: &Have) -> Result<u64> { if have_msg.has_length() { return Ok(have_msg.get_start() + have_msg.get_length()); } else if have_msg.has_bitfield() { let raw_bf = have_msg.get_bitfield(); - let bf = decode_bitfiled(raw_bf)?; + let bf = decode_bitfield(raw_bf)?; trace!("decoded bitfield: {:?}", bf); return Ok(max_high_bit(&bf)); } else { |